Move Ansel to Horde-Hatchery
authorMichael J. Rubinsky <mrubinsk@horde.org>
Thu, 30 Jul 2009 20:12:03 +0000 (16:12 -0400)
committerMichael J. Rubinsky <mrubinsk@horde.org>
Thu, 30 Jul 2009 20:12:03 +0000 (16:12 -0400)
348 files changed:
ansel/.htaccess [new file with mode: 0755]
ansel/COPYING [new file with mode: 0644]
ansel/README [new file with mode: 0644]
ansel/browse.php [new file with mode: 0644]
ansel/browse_edit.php [new file with mode: 0644]
ansel/config/.cvsignore [new file with mode: 0755]
ansel/config/conf.xml [new file with mode: 0644]
ansel/config/hooks.php.dist [new file with mode: 0644]
ansel/config/prefs.php.dist [new file with mode: 0644]
ansel/config/styles.php.dist [new file with mode: 0644]
ansel/disclamer.php [new file with mode: 0644]
ansel/docs/CHANGES [new file with mode: 0644]
ansel/docs/CREDITS [new file with mode: 0644]
ansel/docs/INSTALL [new file with mode: 0644]
ansel/docs/RELEASE_NOTES [new file with mode: 0644]
ansel/docs/TODO [new file with mode: 0755]
ansel/docs/lighttpd-ansel.conf [new file with mode: 0755]
ansel/edit_dates.php [new file with mode: 0644]
ansel/faces/claim.php [new file with mode: 0644]
ansel/faces/custom.php [new file with mode: 0644]
ansel/faces/delete.php [new file with mode: 0644]
ansel/faces/face.php [new file with mode: 0644]
ansel/faces/gallery.php [new file with mode: 0644]
ansel/faces/image.php [new file with mode: 0644]
ansel/faces/img.php [new file with mode: 0644]
ansel/faces/name.php [new file with mode: 0644]
ansel/faces/report.php [new file with mode: 0644]
ansel/faces/savecustom.php [new file with mode: 0644]
ansel/faces/search/all.php [new file with mode: 0644]
ansel/faces/search/image.php [new file with mode: 0644]
ansel/faces/search/image_define.php [new file with mode: 0644]
ansel/faces/search/image_save.php [new file with mode: 0644]
ansel/faces/search/image_search.php [new file with mode: 0644]
ansel/faces/search/img.php [new file with mode: 0644]
ansel/faces/search/name.php [new file with mode: 0644]
ansel/faces/search/named.php [new file with mode: 0644]
ansel/faces/search/owner.php [new file with mode: 0644]
ansel/faces/search/tabs.php [new file with mode: 0644]
ansel/gallery.php [new file with mode: 0644]
ansel/gallery/captions.php [new file with mode: 0644]
ansel/gallery/delete.php [new file with mode: 0644]
ansel/gallery/index.php [new file with mode: 0755]
ansel/gallery/sort.php [new file with mode: 0644]
ansel/group.php [new file with mode: 0644]
ansel/image.php [new file with mode: 0644]
ansel/img/download.php [new file with mode: 0644]
ansel/img/ecard.php [new file with mode: 0644]
ansel/img/full.php [new file with mode: 0644]
ansel/img/index.php [new file with mode: 0644]
ansel/img/mini.php [new file with mode: 0644]
ansel/img/prettythumb.php [new file with mode: 0644]
ansel/img/screen.php [new file with mode: 0644]
ansel/img/thumb.php [new file with mode: 0644]
ansel/img/upload.php [new file with mode: 0644]
ansel/img/upload_preview.php [new file with mode: 0644]
ansel/index.php [new file with mode: 0644]
ansel/js/src/builder.js [new file with mode: 0755]
ansel/js/src/carousel.js [new file with mode: 0644]
ansel/js/src/cropper.js [new file with mode: 0644]
ansel/js/src/editcaption.js [new file with mode: 0644]
ansel/js/src/editfaces.js [new file with mode: 0644]
ansel/js/src/embed.js [new file with mode: 0755]
ansel/js/src/googlemap.js [new file with mode: 0644]
ansel/js/src/googlemap_edit.js [new file with mode: 0644]
ansel/js/src/imagefaces.js [new file with mode: 0644]
ansel/js/src/lightbox.js [new file with mode: 0755]
ansel/js/src/slideshow.js [new file with mode: 0755]
ansel/js/src/slugcheck.js [new file with mode: 0755]
ansel/js/src/tagactions.js [new file with mode: 0755]
ansel/js/src/togglewidget.js [new file with mode: 0755]
ansel/lib/Ajax/Imple/EditCaption.php [new file with mode: 0644]
ansel/lib/Ajax/Imple/EditFaces.php [new file with mode: 0644]
ansel/lib/Ajax/Imple/Embed.php [new file with mode: 0644]
ansel/lib/Ajax/Imple/GallerySlugCheck.php [new file with mode: 0644]
ansel/lib/Ajax/Imple/ImageSaveGeotag.php [new file with mode: 0644]
ansel/lib/Ajax/Imple/LocationAutoCompleter.php [new file with mode: 0644]
ansel/lib/Ajax/Imple/TagActions.php [new file with mode: 0644]
ansel/lib/Ajax/Imple/ToggleGalleryActions.php [new file with mode: 0644]
ansel/lib/Ajax/Imple/ToggleOtherGalleries.php [new file with mode: 0644]
ansel/lib/Ansel.php [new file with mode: 0644]
ansel/lib/Block/cloud.php [new file with mode: 0644]
ansel/lib/Block/gallery.php [new file with mode: 0644]
ansel/lib/Block/my_galleries.php [new file with mode: 0644]
ansel/lib/Block/random_photo.php [new file with mode: 0644]
ansel/lib/Block/recent_comments.php [new file with mode: 0644]
ansel/lib/Block/recent_faces.php [new file with mode: 0644]
ansel/lib/Block/recently_added.php [new file with mode: 0644]
ansel/lib/Block/recently_added_geodata.php [new file with mode: 0644]
ansel/lib/Exif.php [new file with mode: 0644]
ansel/lib/Exif/canon.php [new file with mode: 0644]
ansel/lib/Exif/fujifilm.php [new file with mode: 0644]
ansel/lib/Exif/gps.php [new file with mode: 0644]
ansel/lib/Exif/nikon.php [new file with mode: 0644]
ansel/lib/Exif/olympus.php [new file with mode: 0644]
ansel/lib/Exif/panasonic.php [new file with mode: 0644]
ansel/lib/Exif/sanyo.php [new file with mode: 0644]
ansel/lib/Faces.php [new file with mode: 0755]
ansel/lib/Faces/facedetect.php [new file with mode: 0644]
ansel/lib/Faces/opencv.php [new file with mode: 0644]
ansel/lib/Forms/Ecard.php [new file with mode: 0644]
ansel/lib/Forms/Image.php [new file with mode: 0644]
ansel/lib/Forms/ImageDate.php [new file with mode: 0644]
ansel/lib/Forms/Upload.php [new file with mode: 0644]
ansel/lib/Forms/Watermark.php [new file with mode: 0644]
ansel/lib/GalleryMode/Date.php [new file with mode: 0644]
ansel/lib/GalleryMode/Normal.php [new file with mode: 0644]
ansel/lib/GalleryMode/RemoteVimeo.php [new file with mode: 0644]
ansel/lib/ImageView.php [new file with mode: 0644]
ansel/lib/ImageView/mini.php [new file with mode: 0755]
ansel/lib/ImageView/plainstack.php [new file with mode: 0644]
ansel/lib/ImageView/polaroidstack.php [new file with mode: 0644]
ansel/lib/ImageView/polaroidthumb.php [new file with mode: 0644]
ansel/lib/ImageView/prettythumb.php [new file with mode: 0644]
ansel/lib/ImageView/roundedstack.php [new file with mode: 0644]
ansel/lib/ImageView/screen.php [new file with mode: 0755]
ansel/lib/ImageView/shadowsharpthumb.php [new file with mode: 0644]
ansel/lib/ImageView/thumb.php [new file with mode: 0755]
ansel/lib/ImageView/vimeothumb.php [new file with mode: 0644]
ansel/lib/Report.php [new file with mode: 0644]
ansel/lib/Report/letter.php [new file with mode: 0755]
ansel/lib/Report/mail.php [new file with mode: 0644]
ansel/lib/Report/tickets.php [new file with mode: 0755]
ansel/lib/Search.php [new file with mode: 0644]
ansel/lib/Search/exif.php [new file with mode: 0644]
ansel/lib/Tags.php [new file with mode: 0644]
ansel/lib/Tile/DateGallery.php [new file with mode: 0644]
ansel/lib/Tile/Gallery.php [new file with mode: 0644]
ansel/lib/Tile/Image.php [new file with mode: 0644]
ansel/lib/Views/Abstract.php [new file with mode: 0644]
ansel/lib/Views/Embedded.php [new file with mode: 0644]
ansel/lib/Views/EmbeddedRenderers/Carousel.php [new file with mode: 0644]
ansel/lib/Views/EmbeddedRenderers/GalleryLink.php [new file with mode: 0644]
ansel/lib/Views/EmbeddedRenderers/Mini.php [new file with mode: 0644]
ansel/lib/Views/EmbeddedRenderers/Slideshow.php [new file with mode: 0644]
ansel/lib/Views/Gallery.php [new file with mode: 0644]
ansel/lib/Views/GalleryRenderer.php [new file with mode: 0644]
ansel/lib/Views/GalleryRenderers/Gallery.php [new file with mode: 0644]
ansel/lib/Views/GalleryRenderers/GalleryLightbox.php [new file with mode: 0644]
ansel/lib/Views/GalleryRenderers/GalleryVimeo.php [new file with mode: 0644]
ansel/lib/Views/Image.php [new file with mode: 0644]
ansel/lib/Views/List.php [new file with mode: 0644]
ansel/lib/Views/Results.php [new file with mode: 0644]
ansel/lib/Views/Slideshow.php [new file with mode: 0644]
ansel/lib/Widget.php [new file with mode: 0644]
ansel/lib/Widget/Actions.php [new file with mode: 0644]
ansel/lib/Widget/Base.php [new file with mode: 0644]
ansel/lib/Widget/GalleryFaces.php [new file with mode: 0644]
ansel/lib/Widget/Geodata.php [new file with mode: 0644]
ansel/lib/Widget/ImageFaces.php [new file with mode: 0644]
ansel/lib/Widget/Links.php [new file with mode: 0644]
ansel/lib/Widget/OtherGalleries.php [new file with mode: 0644]
ansel/lib/Widget/OwnerFaces.php [new file with mode: 0644]
ansel/lib/Widget/SimilarPhotos.php [new file with mode: 0755]
ansel/lib/Widget/Tags.php [new file with mode: 0644]
ansel/lib/XPPublisher.php [new file with mode: 0644]
ansel/lib/api.php [new file with mode: 0644]
ansel/lib/base.php [new file with mode: 0644]
ansel/lib/prefs.php [new file with mode: 0644]
ansel/lib/version.php [new file with mode: 0755]
ansel/locale/de_DE/LC_MESSAGES/ansel.mo [new file with mode: 0644]
ansel/locale/en_US/help.xml [new file with mode: 0755]
ansel/locale/es_ES/LC_MESSAGES/ansel.mo [new file with mode: 0755]
ansel/locale/es_ES/help.xml [new file with mode: 0755]
ansel/locale/fi_FI/LC_MESSAGES/ansel.mo [new file with mode: 0755]
ansel/locale/fi_FI/help.xml [new file with mode: 0755]
ansel/locale/it_IT/LC_MESSAGES/ansel.mo [new file with mode: 0644]
ansel/locale/ja_JP/LC_MESSAGES/ansel.mo [new file with mode: 0755]
ansel/locale/lt_LT/LC_MESSAGES/ansel.mo [new file with mode: 0755]
ansel/locale/sl_SI/LC_MESSAGES/ansel.mo [new file with mode: 0755]
ansel/locale/sv_SE/LC_MESSAGES/ansel.mo [new file with mode: 0755]
ansel/locale/tr_TR/LC_MESSAGES/ansel.mo [new file with mode: 0644]
ansel/locale/zh_TW/LC_MESSAGES/ansel.mo [new file with mode: 0755]
ansel/map_edit.php [new file with mode: 0644]
ansel/perms.php [new file with mode: 0644]
ansel/po/.cvsignore [new file with mode: 0755]
ansel/po/README [new file with mode: 0755]
ansel/po/ansel.pot [new file with mode: 0644]
ansel/po/de_DE.po [new file with mode: 0644]
ansel/po/es_ES.po [new file with mode: 0644]
ansel/po/fi_FI.po [new file with mode: 0644]
ansel/po/it_IT.po [new file with mode: 0644]
ansel/po/ja_JP.po [new file with mode: 0644]
ansel/po/lt_LT.po [new file with mode: 0644]
ansel/po/sl_SI.po [new file with mode: 0644]
ansel/po/sv_SE.po [new file with mode: 0644]
ansel/po/tr_TR.po [new file with mode: 0644]
ansel/po/zh_TW.po [new file with mode: 0644]
ansel/preview.php [new file with mode: 0644]
ansel/protect.php [new file with mode: 0644]
ansel/report.php [new file with mode: 0644]
ansel/rss.php [new file with mode: 0644]
ansel/scripts/.htaccess [new file with mode: 0755]
ansel/scripts/AnselPublish.scpt [new file with mode: 0644]
ansel/scripts/all_images_exif_to_tags.php [new file with mode: 0755]
ansel/scripts/ansel.php [new file with mode: 0755]
ansel/scripts/garbage_collection.php [new file with mode: 0755]
ansel/scripts/recursive_import.php [new file with mode: 0755]
ansel/scripts/remote_import.php [new file with mode: 0755]
ansel/scripts/sql/ansel.pgsql.sql [new file with mode: 0644]
ansel/scripts/sql/ansel.sql [new file with mode: 0644]
ansel/scripts/upgrades/1.0_to_1.1.php [new file with mode: 0644]
ansel/scripts/upgrades/2008-06-04-faces.sql [new file with mode: 0755]
ansel/scripts/upgrades/2008-06-17_fix_varchar_lengths.sql [new file with mode: 0755]
ansel/scripts/upgrades/2008-09-13_add_image_original_date.sql [new file with mode: 0644]
ansel/scripts/upgrades/2008-09-16_add_original_date_values.php [new file with mode: 0755]
ansel/scripts/upgrades/2008-09-23_fix_group_uid.sql [new file with mode: 0644]
ansel/scripts/upgrades/2008-12-5_add_geolocation_tables.sql [new file with mode: 0644]
ansel/scripts/upgrades/2009-01-10_fix_view_mode.sql [new file with mode: 0644]
ansel/scripts/upgrades/2009-04-14_fix_view_mode.pgsql.sql [new file with mode: 0644]
ansel/scripts/upgrades/2009-06-14_fix_geolocation_values.php [new file with mode: 0755]
ansel/scripts/upgrades/2009-06-22_add_geolocation_fields.sql [new file with mode: 0644]
ansel/scripts/upgrades/2009-06-22_move_geolocation_values.php [new file with mode: 0644]
ansel/scripts/upgrades/2009-07-06_add_geolocation_timestamp.sql [new file with mode: 0644]
ansel/templates/captions/captions.inc [new file with mode: 0644]
ansel/templates/common-header.inc [new file with mode: 0644]
ansel/templates/faces/custom.inc [new file with mode: 0755]
ansel/templates/faces/define.inc [new file with mode: 0755]
ansel/templates/faces/face.inc [new file with mode: 0755]
ansel/templates/faces/faces.inc [new file with mode: 0755]
ansel/templates/faces/gallery.inc [new file with mode: 0644]
ansel/templates/faces/image.inc [new file with mode: 0755]
ansel/templates/faces/index.inc [new file with mode: 0755]
ansel/templates/faces/search.inc [new file with mode: 0755]
ansel/templates/gallery/delete_confirmation.inc [new file with mode: 0644]
ansel/templates/gallery/gallery.inc [new file with mode: 0644]
ansel/templates/group/category.inc [new file with mode: 0644]
ansel/templates/group/footer.inc [new file with mode: 0755]
ansel/templates/group/header.inc [new file with mode: 0644]
ansel/templates/group/owner.inc [new file with mode: 0644]
ansel/templates/group/pager.inc [new file with mode: 0755]
ansel/templates/image/crop_image.inc [new file with mode: 0644]
ansel/templates/image/edit_image.inc [new file with mode: 0644]
ansel/templates/image/preview_cropimage.inc [new file with mode: 0644]
ansel/templates/image/preview_image.inc [new file with mode: 0644]
ansel/templates/image/resize_image.inc [new file with mode: 0644]
ansel/templates/image/upload.inc [new file with mode: 0644]
ansel/templates/list/footer.inc [new file with mode: 0755]
ansel/templates/list/header.inc [new file with mode: 0644]
ansel/templates/list/pager.inc [new file with mode: 0755]
ansel/templates/menu.inc [new file with mode: 0644]
ansel/templates/prefs/default_category_select.inc [new file with mode: 0644]
ansel/templates/prefs/default_gallerystyle_select.inc [new file with mode: 0644]
ansel/templates/rss/rss.inc [new file with mode: 0644]
ansel/templates/rss/rss2.inc [new file with mode: 0755]
ansel/templates/tile/dategallery.inc [new file with mode: 0644]
ansel/templates/tile/face.inc [new file with mode: 0755]
ansel/templates/tile/gallery.inc [new file with mode: 0755]
ansel/templates/tile/gallerymini.inc [new file with mode: 0755]
ansel/templates/tile/image.inc [new file with mode: 0644]
ansel/templates/view/gallery.inc [new file with mode: 0644]
ansel/templates/view/gallerylightbox.inc [new file with mode: 0644]
ansel/templates/view/galleryvimeo.inc [new file with mode: 0644]
ansel/templates/view/image.inc [new file with mode: 0644]
ansel/templates/view/list.inc [new file with mode: 0644]
ansel/templates/view/results.inc [new file with mode: 0644]
ansel/templates/view/slideshow.inc [new file with mode: 0644]
ansel/templates/xppublish/javascript.inc [new file with mode: 0755]
ansel/templates/xppublish/list.inc [new file with mode: 0755]
ansel/templates/xppublish/login.inc [new file with mode: 0755]
ansel/templates/xppublish/new.inc [new file with mode: 0755]
ansel/test.php [new file with mode: 0644]
ansel/themes/cropper.css [new file with mode: 0755]
ansel/themes/embed.css [new file with mode: 0755]
ansel/themes/feed-rss.xsl [new file with mode: 0755]
ansel/themes/graphics/add.png [new file with mode: 0644]
ansel/themes/graphics/ansel.png [new file with mode: 0644]
ansel/themes/graphics/arrow_switch.png [new file with mode: 0644]
ansel/themes/graphics/browse.png [new file with mode: 0644]
ansel/themes/graphics/down.png [new file with mode: 0755]
ansel/themes/graphics/favicon.ico [new file with mode: 0644]
ansel/themes/graphics/galleries.png [new file with mode: 0644]
ansel/themes/graphics/gallery-locked-mini.png [new file with mode: 0644]
ansel/themes/graphics/gallery-locked.png [new file with mode: 0644]
ansel/themes/graphics/image_add.png [new file with mode: 0755]
ansel/themes/graphics/lightbox/bullet.gif [new file with mode: 0644]
ansel/themes/graphics/lightbox/close.gif [new file with mode: 0644]
ansel/themes/graphics/lightbox/closelabel.gif [new file with mode: 0644]
ansel/themes/graphics/lightbox/loading.gif [new file with mode: 0644]
ansel/themes/graphics/lightbox/nextlabel.gif [new file with mode: 0644]
ansel/themes/graphics/lightbox/prevlabel.gif [new file with mode: 0644]
ansel/themes/graphics/loading.gif [new file with mode: 0644]
ansel/themes/graphics/mini-error.png [new file with mode: 0644]
ansel/themes/graphics/minus.png [new file with mode: 0755]
ansel/themes/graphics/mygalleries.png [new file with mode: 0644]
ansel/themes/graphics/plus.png [new file with mode: 0755]
ansel/themes/graphics/point.png [new file with mode: 0644]
ansel/themes/graphics/prettythumb-error.png [new file with mode: 0755]
ansel/themes/graphics/problem.png [new file with mode: 0755]
ansel/themes/graphics/resize.png [new file with mode: 0644]
ansel/themes/graphics/scaler_slider.gif [new file with mode: 0755]
ansel/themes/graphics/scaler_slider_track.gif [new file with mode: 0755]
ansel/themes/graphics/screen-error.png [new file with mode: 0644]
ansel/themes/graphics/slideshow_next.png [new file with mode: 0644]
ansel/themes/graphics/slideshow_pause.png [new file with mode: 0644]
ansel/themes/graphics/slideshow_play.png [new file with mode: 0644]
ansel/themes/graphics/slideshow_prev.png [new file with mode: 0644]
ansel/themes/graphics/success.png [new file with mode: 0755]
ansel/themes/graphics/text.png [new file with mode: 0644]
ansel/themes/graphics/thumb-error.png [new file with mode: 0644]
ansel/themes/graphics/up.png [new file with mode: 0755]
ansel/themes/lightbox.css [new file with mode: 0644]
ansel/themes/screen.css [new file with mode: 0644]
ansel/themes/silver/graphics/add.png [new file with mode: 0644]
ansel/themes/silver/graphics/ansel.png [new file with mode: 0644]
ansel/themes/silver/graphics/arrow_switch.png [new file with mode: 0644]
ansel/themes/silver/graphics/browse.png [new file with mode: 0644]
ansel/themes/silver/graphics/galleries.png [new file with mode: 0644]
ansel/themes/silver/graphics/image_add.png [new file with mode: 0644]
ansel/themes/silver/graphics/lightbox/bullet.gif [new file with mode: 0755]
ansel/themes/silver/graphics/lightbox/close.gif [new file with mode: 0755]
ansel/themes/silver/graphics/lightbox/closelabel.gif [new file with mode: 0755]
ansel/themes/silver/graphics/lightbox/loading.gif [new file with mode: 0755]
ansel/themes/silver/graphics/lightbox/nextlabel.gif [new file with mode: 0755]
ansel/themes/silver/graphics/lightbox/prevlabel.gif [new file with mode: 0755]
ansel/themes/silver/graphics/mini-error.png [new file with mode: 0644]
ansel/themes/silver/graphics/mygalleries.png [new file with mode: 0644]
ansel/themes/silver/graphics/slideshow_next.png [new file with mode: 0755]
ansel/themes/silver/graphics/slideshow_pause.png [new file with mode: 0755]
ansel/themes/silver/graphics/slideshow_play.png [new file with mode: 0755]
ansel/themes/silver/graphics/slideshow_prev.png [new file with mode: 0755]
ansel/themes/silver/graphics/text.png [new file with mode: 0644]
ansel/themes/silver/themed_graphics [new file with mode: 0644]
ansel/themes/tango-blue/graphics/add.png [new file with mode: 0644]
ansel/themes/tango-blue/graphics/ansel.png [new file with mode: 0644]
ansel/themes/tango-blue/graphics/browse.png [new file with mode: 0644]
ansel/themes/tango-blue/graphics/down.png [new file with mode: 0755]
ansel/themes/tango-blue/graphics/galleries.png [new file with mode: 0644]
ansel/themes/tango-blue/graphics/image_add.png [new file with mode: 0755]
ansel/themes/tango-blue/graphics/lightbox/bullet.gif [new file with mode: 0755]
ansel/themes/tango-blue/graphics/lightbox/close.gif [new file with mode: 0755]
ansel/themes/tango-blue/graphics/lightbox/closelabel.gif [new file with mode: 0755]
ansel/themes/tango-blue/graphics/lightbox/loading.gif [new file with mode: 0755]
ansel/themes/tango-blue/graphics/lightbox/nextlabel.gif [new file with mode: 0755]
ansel/themes/tango-blue/graphics/lightbox/prevlabel.gif [new file with mode: 0755]
ansel/themes/tango-blue/graphics/mini-error.png [new file with mode: 0755]
ansel/themes/tango-blue/graphics/mygalleries.png [new file with mode: 0644]
ansel/themes/tango-blue/graphics/prettythumb-error.png [new file with mode: 0755]
ansel/themes/tango-blue/graphics/slideshow_next.png [new file with mode: 0755]
ansel/themes/tango-blue/graphics/slideshow_pause.png [new file with mode: 0755]
ansel/themes/tango-blue/graphics/slideshow_play.png [new file with mode: 0755]
ansel/themes/tango-blue/graphics/slideshow_prev.png [new file with mode: 0755]
ansel/themes/tango-blue/graphics/text.png [new file with mode: 0644]
ansel/themes/tango-blue/graphics/thumb-error.png [new file with mode: 0755]
ansel/themes/tango-blue/graphics/up.png [new file with mode: 0755]
ansel/themes/tango-blue/screen.css [new file with mode: 0644]
ansel/themes/tango-blue/themed_graphics [new file with mode: 0644]
ansel/view.php [new file with mode: 0644]
ansel/xppublish.php [new file with mode: 0644]

diff --git a/ansel/.htaccess b/ansel/.htaccess
new file mode 100755 (executable)
index 0000000..3a722c2
--- /dev/null
@@ -0,0 +1,18 @@
+<IfModule mod_rewrite.c>
+    RewriteEngine On
+    RewriteRule   ^user/?$ group.php?groupby=owner [QSA,L]
+    RewriteRule   ^category/?$ group.php?groupby=category [QSA,L]
+    RewriteRule   ^all/?$ view.php?view=List&groupby=none [QSA,L]
+    RewriteRule   ^user/([@a-zA-Z0-9%_+.!*',()~-]*)/rss/?$ rss.php?stream_type=user&id=$1 [L]
+    RewriteRule   ^user/([@a-zA-Z0-9%_+.!*',()~-]*)/?$ view.php?view=List&groupby=owner&owner=$1 [QSA,L]
+    RewriteRule   ^gallery/id/([0-9]+)/rss/?$ rss.php?stream_type=gallery&id=$1 [QSA,L]
+    RewriteRule   ^gallery/id/([0-9]+)/([0-9]+)/? view.php?view=Image&gallery=$1&image=$2 [QSA,L]
+    RewriteRule   ^gallery/id/([0-9]+)/?$ view.php?view=Gallery&gallery=$1 [QSA,L]
+    RewriteRule   ^gallery/([a-zA-Z0-9_@]+)/rss/?$ rss.php?stream_type=gallery&slug=$1 [L]
+    RewriteRule   ^gallery/([a-zA-Z0-9_@]+)/([0-9]+)/? view.php?view=Image&slug=$1&image=$2 [QSA,L]
+    RewriteRule   ^gallery/([a-zA-Z0-9_@]+)/?$ view.php?view=Gallery&slug=$1 [QSA,L]
+    RewriteRule   ^category/([@a-zA-Z0-9%_+.!*',()~-]*)/?$ view.php?view=List&groupby=category&category=$1 [QSA,L]
+    RewriteRule   ^tag/?$ view.php?view=Results [QSA,L]
+    RewriteRule   ^tag/([a-zA-Z0-9%_+.!*',()~-]*)/rss/?$ rss.php?stream_type=tag&id=$1 [QSA,L]
+    RewriteRule   ^tag/([a-zA-Z0-9%_+.!*',()~-]*)/?$ view.php?view=Results&tag=$1 [QSA,L]
+</IfModule>
diff --git a/ansel/COPYING b/ansel/COPYING
new file mode 100644 (file)
index 0000000..a6b6756
--- /dev/null
@@ -0,0 +1,280 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
diff --git a/ansel/README b/ansel/README
new file mode 100644 (file)
index 0000000..95d9730
--- /dev/null
@@ -0,0 +1,121 @@
+What is Ansel?
+==============
+
+:Last update:   $Date: 2008/10/27 19:57:04 $
+:Revision:      $Revision: 1.5 $
+
+.. contents:: Contents
+.. section-numbering::
+
+Ansel is the Horde photo management application. It makes use of the Horde frame
+work and integrates with other Horde applications. Ansel is nearing 1.0 quality,
+but is not yet officially released software. It can be obtained by downloading
+a CVS snapshot , or by checking it out from anonymous CVS.
+
+Ansel is a full featured photo management application. With it, you can create
+any number of galleries and subgalleries, share galleries among other Horde
+users or even make them public. You can upload images either one at a time, as a
+zip archive, or even upload them via Windows XP's "Publish to Web"
+functionality. Once in Ansel, you can browse your images, download originals,
+view galleries as a slideshow, add comments to images, send an 'ecard' to a
+friend, and more. You can crop, resize and perform other image maniputlation
+functions.  You can even have Ansel try to recognize people in your photos
+with it's support for face detection. Ansel will read and store EXIF data
+associated with uploaded images and can even auto rotate your images to the
+proper position if the image contains the proper EXIF properties.
+
+Ansel supports photo and gallery tagging - allowing you to browse your photos by
+tags just like you were browsing a directory. You can also choose to have
+certain EXIF tags automatically added to an image on upload.
+
+Mulitple gallery themes are available. You can create thumbnails that have
+rounded corners with shadows, sharp corners with shadows, or even make your
+thumbnails look like Polaroids! Image viewing can use a lightbox-type overlay
+on the gallery page, or can be a seperate image view page that can display
+image attributes, comments and other types of information.
+
+There are multiple ways to organize your photos within galleries. You can create
+subgalleries to further organize photos and you can also have Ansel
+automatically display a gallery's photos to you grouped by date.
+
+Ansel supports mulitiple ways to access your photos from outside of Ansel. There
+are mutlitple types of RSS feeds available, including most recently added images
+for a particular user, gallery or tag. The ability to embed widgets of your
+photos into external websites, such as blogs, is also available.
+
+For developers, Ansel also offers an external api that may be accessed via
+Horde's RPC server or directly through the Horde Registry.  There are api
+methods that make it extremely easy to embed gallery views on your own website.
+
+This software is OSI Certified Open Source Software. OSI Certified is a
+certification mark of the `Open Source Initiative`_.
+
+.. _`Open Source Initiative`: http://www.opensource.org/
+
+
+Obtaining Ansel
+---------------
+
+Further information on Ansel and the latest version can be obtained at
+
+  http://www.horde.org/ansel/
+
+
+Documentation
+-------------
+
+The following documentation is available in the Ansel distribution:
+
+:README_:            This file
+:COPYING_:           Copyright and license information
+:`docs/CHANGES`_:    Changes by release
+:`docs/CREDITS`_:    Project developers
+:`docs/INSTALL`_:    Installation instructions and notes
+:`docs/TODO`_:       Development TODO list
+
+
+Installation
+------------
+
+Instructions for installing Ansel can be found in the file INSTALL_ in the
+``docs/`` directory of the Ansel distribution.
+
+
+Assistance
+----------
+
+If you encounter problems with Ansel, help is available!
+
+The Horde Frequently Asked Questions List (FAQ), available on the Web at
+
+  http://www.horde.org/faq/
+
+The Horde Project runs a number of mailing lists, for individual applications
+and for issues relating to the project as a whole. Information, archives, and
+subscription information can be found at
+
+  http://www.horde.org/mail/
+
+Lastly, Horde developers, contributors and users also make occasional
+appearances on IRC, on the channel #horde on the freenode Network
+(irc.freenode.net).
+
+
+Licensing
+---------
+
+For licensing and copyright information, please see the file COPYING_ in the
+Ansel distribution.
+
+Thanks,
+
+The Ansel team
+
+
+.. _README: ?f=README.html
+.. _COPYING: http://www.horde.org/licenses/gpl.php
+.. _docs/CHANGES: ?f=CHANGES.html
+.. _docs/CREDITS: ?f=CREDITS.html
+.. _INSTALL:
+.. _docs/INSTALL: ?f=INSTALL.html
+.. _docs/TODO: ?f=TODO.html
diff --git a/ansel/browse.php b/ansel/browse.php
new file mode 100644 (file)
index 0000000..72dd3f8
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+/**
+ * $Horde: ansel/browse.php,v 1.12 2009/01/06 17:48:49 jan Exp $
+ *
+ * Copyright 2001-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ */
+
+require_once dirname(__FILE__) . '/lib/base.php';
+require_once ANSEL_BASE . '/lib/Tags.php';
+require_once 'Horde/Block/Layout/View.php';
+
+Horde::addScriptFile('prototype.js', 'horde', true);
+$layout = new Horde_Block_Layout_View(
+    @unserialize($prefs->getValue('myansel_layout')),
+    Horde::applicationUrl('browse_edit.php'),
+    Horde::applicationUrl('browse.php', true));
+
+$layout_html = $layout->toHtml();
+$title = _("Photo Galleries");
+Ansel_Tags::clearSearch();
+require ANSEL_BASE . '/templates/common-header.inc';
+require ANSEL_TEMPLATES . '/menu.inc';
+echo '<div id="menuBottom"><a href="' . Horde::applicationUrl('browse_edit.php') . '">' . _("Add Content") . '</a></div><div class="clear">&nbsp;</div>';
+echo $layout_html;
+require $registry->get('templates', 'horde') . '/common-footer.inc';
diff --git a/ansel/browse_edit.php b/ansel/browse_edit.php
new file mode 100644 (file)
index 0000000..f31ac71
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+/**
+ * $Horde: ansel/browse_edit.php,v 1.8 2009/06/10 00:33:01 mrubinsk Exp $
+ *
+ * Copyright 1999-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html
+ */
+
+require_once dirname(__FILE__) . '/lib/base.php';
+require_once 'Horde/Block/Collection.php';
+require_once 'Horde/Block/Layout/Manager.php';
+
+// Instantiate the blocks objects.
+$blocks = &Horde_Block_Collection::singleton('myphotos', array('ansel'));
+$layout = &Horde_Block_Layout_Manager::singleton('myphotos', $blocks, @unserialize($prefs->getValue('myansel_layout')));
+
+// Handle requested actions.
+$layout->handle(Horde_Util::getFormData('action'),
+                (int)Horde_Util::getFormData('row'),
+                (int)Horde_Util::getFormData('col'),
+                Horde_Util::getFormData('url'));
+if ($layout->updated()) {
+    $prefs->setValue('myansel_layout', $layout->serialize());
+}
+
+$title = _("My Photos :: Add Content");
+require ANSEL_TEMPLATES . '/common-header.inc';
+echo '<div id="menu">' . Ansel::getMenu('string') . '</div>';
+$notification->notify(array('listeners' => 'status'));
+require $registry->get('templates', 'horde') . '/portal/edit.inc';
+require $registry->get('templates', 'horde') . '/common-footer.inc';
diff --git a/ansel/config/.cvsignore b/ansel/config/.cvsignore
new file mode 100755 (executable)
index 0000000..cd8ce97
--- /dev/null
@@ -0,0 +1,5 @@
+conf.php
+conf.bak.php
+hooks.php
+prefs.php
+styles.php
diff --git a/ansel/config/conf.xml b/ansel/config/conf.xml
new file mode 100644 (file)
index 0000000..7326e03
--- /dev/null
@@ -0,0 +1,259 @@
+<?xml version="1.0"?>
+<!-- $Horde: ansel/config/conf.xml,v 1.60 2009/05/15 19:44:03 mrubinsk Exp $ -->
+<configuration>
+
+ <configtab name="menu" desc="General Settings and Menu">
+  <configheader>User Interface</configheader>
+  <configsection name="gallery">
+   <configinteger name="listlimit" desc="On installations with large
+   numbers of galleries, it can be useful to disallow using select lists for
+   gallery selection in blocks. Set this to the maximum number of galleries
+   that can be displayed in a gallery drop down list. If more than this number
+   of galleries would appear, then selection list will be disabled."
+   required="false"/>
+   <configinteger name="numlimit" desc="On installations with large
+   numbers of galleries, it can be useful when displaying gallery lists to guest
+   users to display only galleries with a certain number of photos. For example
+   to avoid listing empty galleries. Enter the minimum number of photos you
+   would like to require to be displayed in guest gallery lists. Note that this
+   setting will be ignored when specifically requesting all of a particular
+   user's galleries." required="false"/>
+   <configboolean name="downloadzip" desc="Allow the ability to download an
+   entire gallery as a .zip file? This is a potential performance hit, and will
+   likely require a larger memory_limit value.">false</configboolean>
+   <configboolean name="customlabel" desc="Allow the ability to query the
+   preference backend for other user's identities? This is a performance hit,
+   but will allow for displaying something like
+   &quot;John Smith's Galleries&quot; instead of &quot;jsmith's Galleries&quot;.
+   It also allows a user to specify a custom description to be displayed. So,
+   for example instead of &quot;John Smith's Galleries&quot;, the user could use
+   &quot;John's Family Photos&quot;. This requires a preference backend that
+   doesn't require an explicit login by the user whose preferences we are
+   trying to read. If you are getting errors regarding unloadable preferences
+   when loading Ansel, try turning this off.">true</configboolean>
+  </configsection>
+  <configsection name="menu">
+   <configheader>Menu Settings</configheader>
+   <configboolean name="print" desc="Should we display a Print link in Ansel's
+   menu?">true</configboolean>
+   <configmultienum name="apps" desc="Select any applications that should be
+   linked in Ansel's menu">
+    <values>
+     <configspecial name="list-horde-apps" />
+    </values>
+   </configmultienum>
+  </configsection>
+ </configtab>
+
+ <configtab name="vfs" desc="VFS and Photo Options">
+  <configheader>VFS Options</configheader>
+  <configsection name="images">
+   <configvfs switchname="type" default="horde"/>
+  </configsection>
+  <configsection name="vfs">
+   <configswitch name="src" desc="How should Horde serve the photo files from
+   the VFS? If choosing the Sendfile option, you will need a webserver that
+   supports either the X-Sendfile or the X-LIGHTTPD-send-file headers. If
+   choosing the VFS-Direct option, be aware that this will bypass
+   any permissions checking. Anyone that knows or can guess the URL can view
+   the photo. This option is also only useable with a file based VFS. The PHP
+   option streams the photo through PHP and is the least efficient, but most
+   compatible, choice.">
+     <case name="php" desc="PHP" />
+     <case name="sendfile" desc="Sendfile"/>
+     <case name="direct" desc="VFS Direct">
+      <configstring desc="Specify an alias pointing to the right place on your
+      filesystem defined in your webserver. Eg. '/ansel-images/'"
+      name="path"></configstring>
+     </case>
+    </configswitch>
+  </configsection>
+  <configsection name="image">
+   <configheader>Photo Generation Options</configheader>
+   <configenum name="type" desc="What format should photos be generated in?
+   Make sure that your server supports whichever format you choose!">
+    <values>
+     <value desc="JPEG">jpeg</value>
+     <value desc="PNG">png</value>
+    </values>
+   </configenum>
+   <configstring name="autogen" desc="Should Ansel automatically generate the
+   screen, thumb, and mini photo views immediately after upload? This will only
+   work when uploading from Ansel's UI and not when uploading via the api or
+   mechanisms such as XP Publish. Set this to the maximum number of photos that
+   may be automatically generated.  A value of zero will disable the feature">
+   0</configstring>
+   <configboolean name="prettythumbs" desc="Allow the selection of gallery
+   themes and generation of 'pretty' thumbnails?  This can be a performance
+    hit the first time the thumbnails are generated.">false</configboolean>
+   <configstring name="num_uploads" desc="How many photos should a user be
+   allowed to upload at once?">5</configstring>
+   <configstring name="font" required="false" desc="What font would you like
+   to use when watermarking photos? If you leave this blank, the default for
+   the particular photo manipulation functions will be used. Make sure you
+   select a font that is present on your system!"/>
+  </configsection>
+ </configtab>
+
+ <configtab name="thumbnail" desc="Thumbnail Settings">
+  <configsection name="thumbnail">
+   <configheader>Thumbnail Settings</configheader>
+   <configstring name="perpage" desc="Maximum allowed thumbnails per
+   page">30</configstring>
+   <configstring name="width" desc="Thumbnail width">150</configstring>
+   <configstring name="height" desc="Thumbnail
+   height">150</configstring>
+  </configsection>
+ </configtab>
+
+ <configtab name="screen" desc="Screen Photo Settings">
+  <configsection name="screen">
+   <configheader>Screen Image Settings</configheader>
+   <configstring name="width" desc="Screen photo
+   width">800</configstring>
+   <configstring name="height" desc="Screen photo
+   height">600</configstring>
+  </configsection>
+ </configtab>
+
+ <configtab name="ecard" desc="Ecard Settings">
+  <configsection name="ecard">
+   <configheader>Ecard Settings</configheader>
+   <configboolean name="enable" desc="Allow users to send photos as
+   ecards?">false</configboolean>
+  </configsection>
+ </configtab>
+
+ <configtab name="comments" desc="Photo Comments">
+  <configsection name="comments">
+   <configheader>Photo Comments</configheader>
+    <configenum name="allow" desc="Can users comment on photos? Note that this
+    functionality requires the presence of an application that provides the
+    forums API (such as Agora).">never
+     <values>
+      <value desc="Authenticated users">authenticated</value>
+      <value desc="No one">never</value>
+      <value desc="All">all</value>
+     </values>
+    </configenum>
+  </configsection>
+ </configtab>
+
+ <configtab name="cache" desc="Cache">
+  <configsection name="ansel_cache">
+   <configheader>Cache Settings</configheader>
+   <configboolean name="usecache" desc="Should we save frequently run queries
+   and permissions checks in the cache?  This will reduce load on your
+   database server, but could delay the appearance of newly tagged photos
+   while browsing tags.">false</configboolean>
+  </configsection>
+ </configtab>
+
+ <configtab name="tags" desc="Tags">
+  <configsection name="tags">
+   <configheader>Tag Settings</configheader>
+   <configboolean name="relatedtags" desc="Allow the display of related tags?
+   Related tags allow you to easily browse a tag hierarchy, but could be a
+   performance hit on sites with a large number of similarly tagged photos
+   since an additional SQL query is performed per related
+   tag.">false</configboolean>
+  </configsection>
+ </configtab>
+
+ <configtab name="report_content" desc="Content Reporting">
+  <configsection name="report_content">
+   <configheader>Content Reporting Settings</configheader>
+   <configswitch name="driver" desc="Driver to use">false
+    <case name="false" desc="Disable">
+    </case>
+    <case name="tickets" desc="Create tickets (Whups)">
+     <configphp name="ticket_params" desc="Enter as a PHP array any
+     additional arguments for the created ticket (queue, state, priority,
+     etc). Example for Whups: array('queue' => 2, 'state' => 1, 'priority' => 1,
+     'type' => 1). You will need to look up the appropriate IDs in Whups's
+     database, or whatever is appropriate for the ticket system you are
+     using.">array()</configphp>
+     <configenum name="allow" desc="Who should be allowed to report
+     inappropriate content?">all
+      <values>
+       <value desc="All users">all</value>
+       <value desc="Authenticated users">authenticated</value>
+       <value desc="Never">never</value>
+      </values>
+     </configenum>
+    </case>
+    <case name="mail" desc="Email">
+     <configstring name="email" desc="Where should problem report emails be
+     sent?">webmaster@example.com</configstring>
+     <configstring name="maildomain" desc="If Horde cannot determine a user's
+     email address, this domain will be appended to their
+     username.">example.com</configstring>
+     <configstring name="username" required="false" desc="If requiring SMTP
+     authentication without a global SMTP user (in section &quot;Mailer&quot;
+     and allowing problem reporting for not authenticated users (in section
+     &quot;Menu Settings&quot;), what username should be use for SMTP
+     authentication?"/>
+     <configstring name="password" required="false" desc="The password to use"/>
+     <configenum name="allow" desc="Who should be allowed to report problems?">all
+      <values>
+       <value desc="All users">all</value>
+       <value desc="Authenticated users">authenticated</value>
+       <value desc="Never">never</value>
+      </values>
+     </configenum>
+    </case>
+    <case name="letter" desc="Send internal messages (Letter)">
+     <configlist name="users" desc="Username to send messages to. If none, ansel admins will be used." />
+     <configenum name="allow" desc="Who should be allowed to report problems?">all
+      <values>
+       <value desc="All users">all</value>
+       <value desc="Authenticated users">authenticated</value>
+       <value desc="Never">never</value>
+      </values>
+     </configenum>
+    </case>
+   </configswitch>
+  </configsection>
+ </configtab>
+
+ <configtab name="ages" desc="Age">
+  <configsection name="ages">
+   <configheader>Age Settings</configheader>
+    <configlist name="limits" required="false" desc="Age content limitations
+    (ex 15,18)"/>
+    <configboolean name="hook" desc="Call hook to get the user
+    age?">false</configboolean>
+  </configsection>
+ </configtab>
+
+ <configtab name="faces" desc="Face Detection">
+  <configsection name="faces">
+   <configheader>Face Detection</configheader>
+   <configswitch name="driver">user
+     <case name="false" desc="Disable" />
+     <case name="user" desc="Manual selection of faces" />
+     <case name="opencv" desc="OpenCV PECL extension">
+       <configstring name="defs" desc="Path to frontalface XML definition file" >/usr/share/opencv/haarcascades/haarcascade_frontalface_alt.xml</configstring>
+     </case>
+     <case name="facedetect" desc="Face Detect PECL extension">
+       <configstring name="defs" desc="Path to frontalface XML definition file" >/usr/share/opencv/haarcascades/haarcascade_frontalface_alt.xml</configstring>
+     </case>
+  </configswitch>
+    <configstring name="search" desc="Enable the similar photo functionality for
+    finding similar faces? Set to 0 to disable, or enter the size, in bytes,
+    that should be used as the photo's index length.  If you don't know what
+    this  is, the default value should be OK for most people. You need the
+    libpuzzle pecl extension to use this feature." >7</configstring>
+  </configsection>
+ </configtab>
+
+ <configtab name="hooks" desc="Custom Hooks">
+  <configsection name="hooks">
+    <configboolean name="postupload" required="false" desc="Should we use a
+       custom post upload hook? If so, make sure you define _ansel_hook_postupload()
+       in hooks.php. The hook will be called after the user successfully uploads
+       a set of images and can be used for things like sending notifications to
+       external websites/APIs.">false</configboolean>
+   </configsection>
+  </configtab>
+</configuration>
diff --git a/ansel/config/hooks.php.dist b/ansel/config/hooks.php.dist
new file mode 100644 (file)
index 0000000..761c4ef
--- /dev/null
@@ -0,0 +1,96 @@
+<?php
+/**
+ * Ansel Hooks configuration file.
+ *
+ * THE HOOKS PROVIDED IN THIS FILE ARE EXAMPLES ONLY.  DO NOT ENABLE THEM
+ * BLINDLY IF YOU DO NOT KNOW WHAT YOU ARE DOING.  YOU HAVE TO CUSTOMIZE THEM
+ * TO MATCH YOUR SPECIFIC NEEDS AND SYSTEM ENVIRONMENT.
+ *
+ * $Horde: ansel/config/hooks.php.dist,v 1.8 2009/07/27 14:52:51 mrubinsk Exp $
+ */
+
+class Ansel_Hooks
+{
+    /**
+     * Here is an example _ansel_hook_user_age(). is called to get the user age
+     * when he/she tries to view an age limited content. This function must 
+     * return an integer value of user age
+     */
+//    function user_age()
+//     {
+//         $query  = 'SELECT age FROM user WHERE user_uid = ' . $GLOBALS['ansel_db']->quote(Horde_Auth::getAuth());
+//         $result = $GLOBALS['ansel_db']->queryOne($query);
+//         if (is_a($result, 'PEAR_Error')) {
+//               throw new Horde_Exception($result);
+//         }
+//
+//         return (int)$result;
+//     }
+
+
+    /**
+     * Example hook for sending a stream notification to facebook after a user
+     * uploads images to Ansel. This will likely go away, being replaced by
+     * by an remote image import/export functionality.
+     */
+//   function postupload($image_ids)
+//   {
+//       $context = array('http_client' => new Horde_Http_Client(),
+//                        'http_request' => new Horde_Controller_Request_Http(),);
+//       $facebook = new Horde_Service_Facebook($GLOBALS['conf']['facebook']['key'],
+//                                              $GLOBALS['conf']['facebook']['secret'],
+//                                              $context);
+//        $fbp = unserialize($GLOBALS['prefs']->getValue('facebook'));
+//
+//        // If no prefs exist just exit since there's nowhere to publish to.
+//        if (empty($fbp['sid'])) {
+//            return;
+//        } else {
+//            $facebook->auth->setUser($fbp['uid'], $fbp['sid'], 0);
+//        }
+//         // Limit of the number of images to include. This should really come
+//         // from a configuration parameter or at the very least, a user pref.
+//         // (5 is the facebook api's max)
+//         $limit = min(5, (count($image_ids)));
+//         $images = $GLOBALS['ansel_storage']->getImages(array_slice($image_ids, 0, $limit));
+//         $perms = array();
+//         $media = array();
+//         foreach ($images as $image) {
+//             // Only the gallery owner should be able to publish news about the
+//             // gallery, and only public galleries with no passwd or age checks
+//             // should be considered as well since the links on facebook would
+//             // be useless.
+//             if (!isset($perms[$image->gallery])) {
+//                 $g = $GLOBALS['ansel_storage']->getGallery($image->gallery);
+//                 $pwd = $g->get('passwd');
+//                 $no_agelimit = empty($GLOBALS['conf']['ages']['limits']) || $g->get('age') == 0;
+//                 if ($g->get('owner') == Horde_Auth::getAuth() && empty($pwd) && $no_agelimit) {
+//                     $perms[$image->gallery] = true;
+//                 } else {
+//                     $perms[$image->gallery] = false;
+//                 }
+//             }
+//             if ($perms[$image->gallery]) {
+//                $media[] = array('type' => 'image',
+//                                 'href' => Ansel::getUrlFor('view',array('view' => 'Image', 'image' => $image->id, 'gallery' => $image->gallery), true, -1),
+//                                 'src' => Ansel::getImageUrl($image->id, 'thumb', true));
+//
+//             }
+//         }
+//         // For this example, just use the last image's gallery title and description
+//         $attachment = array('name' => $g->get('name'), 'caption' => $g->get('description'), 'media' => $media);
+//
+//         //Do it.
+//         try {
+//             $facebook->streams->publish(sprintf("just uploaded these pictures to %s", $GLOBALS['registry']->get('name')), $attachment);
+//         } catch (Horde_Service_Facebook_Exception $e) {
+//             // For now, just pass back as a pear error...needs to be cleaned up
+//             $GLOBALS['notification']->push('Horde_Service_Facebook: ' . $e->getMessage(), 'horde.err');
+//             throw new Horde_Exception($e);
+//         }
+//         if (!empty($GLOBALS['notification'])) {
+//             $GLOBALS['notification']->push('Notification published to Facebook.', 'horde.success');
+//         }
+//     }
+
+}
diff --git a/ansel/config/prefs.php.dist b/ansel/config/prefs.php.dist
new file mode 100644 (file)
index 0000000..701e6be
--- /dev/null
@@ -0,0 +1,273 @@
+<?php
+/**
+ * $Horde: ansel/config/prefs.php.dist,v 1.72 2009/01/26 17:29:23 mrubinsk Exp $
+ *
+ * See horde/config/prefs.php for documentation on the structure of this file.
+ */
+
+// Make sure that constants are defined.
+require_once dirname(__FILE__) . '/../lib/Ansel.php';
+
+$prefGroups['display'] = array(
+    'column' => _("General Options"),
+    'label' => _("Display Options"),
+    'desc' => _("Change display options such as which view to display by default, how many photos to display on a page, and the default gallery style to use."),
+    'members' => array('grouptitle', 'defaultview', 'tilesperrow',
+                       'tilesperpage', 'facesperpage', 'groupby',
+                       'groupsperpage',
+                       'default_gallerystyle_select', 'default_category_select')
+);
+
+$prefGroups['metadata'] = array(
+    'column' => _("General Options"),
+    'label' => _("Tags and EXIF Options"),
+    'desc' => _("Change options dealing with tags and exif data."),
+    'members' => array('showexif', 'exif_tags')
+);
+
+$prefGroups['perms'] = array(
+    'column' => _("General Options"),
+    'label' => _("Permission Options"),
+    'desc' => _("Change your user permission options such as who can download original photos, and what permissions newly created galleries should have by default."),
+    'members' => array('default_download', 'default_permissions', 'group_permissions', 'guest_permissions')
+);
+
+$prefGroups['watermark'] = array(
+    'column' => _("General Options"),
+    'label' => _("Watermark Options"),
+    'desc' => _("Change your watermark options."),
+    'members' => array('watermark_text', 'watermark_vertical', 'watermark_horizontal',
+                       'watermark_font', 'watermark_auto')
+);
+
+/* Note that for the following to work, your pref backend must support
+   retrieving prefs for other users (such as the SQL backend) */
+$_prefs['grouptitle'] = array(
+    'value' => '',
+    'locked' => false,
+    'shared' => false,
+    'type' => 'text',
+    'desc' => _("Custom text to display describing your galleries. This will be displayed in place of your username when grouping galleries by username."));
+
+$_prefs['defaultview'] = array(
+    'value' => 'galleries',
+    'locked' => false,
+    'shared' => false,
+    'type' => 'enum',
+    'enum' => array('browse' => _("Browse"),
+                    'galleries' => _("Galleries"),
+                    'mygalleries' => _("My Galleries")
+                    ),
+    'desc' => _("View to display by default")
+);
+
+$_prefs['groupby'] = array(
+    'value' => 'none',
+    'locked' => false,
+    'shared' => false,
+    'type' => 'enum',
+    'enum' => array('owner' => _("Owner"),
+                    'category' => _("Category"),
+                    'none' => _("None")),
+    'desc' => _("Group galleries by")
+);
+
+// number of photos on each row in the gallery view
+$_prefs['tilesperrow'] = array(
+    'value' => 3,
+    'locked' => false,
+    'shared' => false,
+    'type' => 'number',
+    'desc' => _("Number of tiles per row")
+);
+
+$_prefs['tilesperpage'] = array(
+    'value' => 9,
+    'locked' => false,
+    'shared' => false,
+    'type' => 'number',
+    'desc' => _("Number of tiles per page")
+);
+
+$_prefs['facesperpage'] = array(
+    'value' => '20',
+    'locked' => !$GLOBALS['conf']['faces']['driver'],
+    'shared' => false,
+    'type' => 'number',
+    'desc' => _("Number of faces per page")
+);
+
+$_prefs['groupsperpage'] = array(
+    'value' => 9,
+    'locked' => false,
+    'shared' => false,
+    'type' => 'number',
+    'desc' => _("Number of groups per page")
+);
+
+$_prefs['showexif'] = array(
+    'value' => false,
+    'locked' => false,
+    'shared' => false,
+    'type' => 'checkbox',
+    'desc' => _("Show EXIF data")
+);
+
+$_prefs['watermark'] = array(
+    'value' => '',
+    'locked' => false,
+    'shared' => false,
+    'type' => 'text',
+    'desc' => _("Custom watermark to use for photos")
+);
+
+$_prefs['myansel_layout'] = array(
+    'value' => 'a:1:{i:0;a:3:{i:0;a:4:{s:3:"app";s:5:"ansel";s:6:"height";i:1;s:5:"width";i:1;s:6:"params";a:2:{s:4:"type";s:5:"cloud";s:6:"params";a:1:{s:5:"count";s:2:"20";}}}i:1;a:4:{s:3:"app";s:5:"ansel";s:6:"height";i:1;s:5:"width";i:1;s:6:"params";a:2:{s:4:"type";s:12:"my_galleries";s:6:"params";a:0:{}}}i:2;a:4:{s:3:"app";s:5:"ansel";s:6:"height";i:1;s:5:"width";i:1;s:6:"params";a:2:{s:4:"type";s:14:"recently_added";s:6:"params";a:2:{s:7:"gallery";s:3:"all";s:5:"limit";s:2:"10";}}}}}',
+    'locked' => false,
+    'shared' => false,
+    'type' => 'implicit'
+);
+
+$_prefs['default_gallerystyle'] = array(
+    'value' => 'ansel_default',
+    'locked' => false,
+    'shared' => false,
+    'type' => 'implicit'
+);
+$_prefs['default_gallerystyle_select'] = array(
+    'type' => 'special'
+);
+
+// Default category
+$_prefs['default_category'] = array(
+    'value' => '',
+    'locked' => false,
+    'shared' => false,
+    'type' => 'implicit'
+);
+
+// Default category
+$_prefs['default_category_select'] = array(
+    'type' => 'special'
+);
+
+$_prefs['show_actions'] = array(
+    'value' => 0,
+    'locked' => false,
+    'shared' => false,
+    'type' => 'implicit',
+);
+
+$_prefs['show_othergalleries'] = array(
+    'value' => 0,
+    'locked' => false,
+    'shared' => false,
+    'type' => 'implicit'
+);
+
+
+$_prefs['watermark_text'] = array(
+    'value' => '',
+    'locked' => false,
+    'shared' => false,
+    'type' => 'text',
+    'desc' => _("Custom watermark to use for photos")
+);
+
+$_prefs['watermark_horizontal'] = array(
+    'value' => 'left',
+    'locked' => false,
+    'shared' => false,
+    'type' => 'enum',
+    'enum' => array('left' => _("Left"),
+                    'center' => _("Center"),
+                    'right' => _("Right")),
+    'desc' => _("Horizontal Alignment")
+);
+
+$_prefs['watermark_vertical'] = array(
+    'value' => 'bottom',
+    'locked' => false,
+    'shared' => false,
+    'type' => 'enum',
+    'enum' => array('top' => _("Top"),
+                    'center' => _("Center"),
+                    'bottom' => _("Bottom")),
+    'desc' => _("Vertical Alignment")
+);
+
+$_prefs['watermark_font'] = array(
+    'value' => 'bottom',
+    'locked' => false,
+    'shared' => false,
+    'type' => 'enum',
+    'enum' => array('tiny' => _("Tiny"),
+                    'small' => _("Small"),
+                    'medium' => _("Medium"),
+                    'large' => _("Large"),
+                    'giant' => _("Giant")),
+    'desc' => _("Vertical Alignment")
+);
+
+$_prefs['watermark_auto'] = array(
+    'value' => 0,
+    'locked' => false,
+    'shared' => false,
+    'type' => 'enum',
+    'enum' => array(_("No"), _("Yes")),
+    'desc' => _("Automatically watermark photos?")
+);
+
+$_prefs['default_download'] = array(
+    'value' => 'edit',
+    'locked' => false,
+    'shared' => false,
+    'type' => 'enum',
+    'enum' => array('all' => _("Anyone"),
+                    'edit' => _("Authenticated users"),
+                    'authenticated' => _("Users with edit permissions")),
+    'desc' => _("Who should be allowed to download original photos")
+);
+
+$_prefs['default_permissions'] = array(
+    'value' => 'read',
+    'locked' => false,
+    'shared' => false,
+    'type' => 'enum',
+    'desc' => _("When a new gallery is created, what permissions should be given to authenticated users by default?"),
+    'enum' => array('none' => _("None"),
+                    'read' => _("Read-only"),
+                    'edit' => _("Read and write"))
+);
+
+$_prefs['guest_permissions'] = array(
+    'value' => 'read',
+    'locked' => false,
+    'shared' => false,
+    'type' => 'enum',
+    'desc' => _("When a new gallery is created, what permissions should be given to guests by default?"),
+    'enum' => array('none' => _("None (Owner only)"),
+                    'read' => _("Read-only"))
+);
+
+$_prefs['group_permissions'] = array(
+    'value' => 'none',
+    'locked' => false,
+    'shared' => false,
+    'type' => 'enum',
+    'desc' => _("When a new gallery is created, what default permissions should be given to groups that the user is a member of?"),
+    'enum' => array('none' => _("None"),
+                    'read' => _("Read-only"),
+                    'edit' => _("Read and write"),
+                    'delete' => _("Read, write, and delete"))
+);
+
+$_prefs['exif_tags'] = array(
+    'value' => 'a:0{}',
+    'locked' => false,
+    'shared' => false,
+    'type' => 'multienum',
+    'desc' => _("Which EXIF fields should we automatically add as photo tags during upload?"),
+    'enum' => array('DateTimeOriginal' => _("Date Photo Taken"),
+                    'ExposureTime' => _("Exposure Time"))
+);
\ No newline at end of file
diff --git a/ansel/config/styles.php.dist b/ansel/config/styles.php.dist
new file mode 100644 (file)
index 0000000..2e827cf
--- /dev/null
@@ -0,0 +1,190 @@
+<?php
+/**
+ * $Horde: ansel/config/styles.php.dist,v 1.8 2009/06/15 16:01:33 mrubinsk Exp $
+ *
+ * Configuration for the available gallery style
+ *
+ * Each entry *must* have:
+ *
+ * 'name'        =  The internal name of the style (this should match the
+ *                  hash key).
+ *
+ * 'title'       =  This is the title to be displayed to the users.
+ *
+ * 'thumbstyle'  =  This is the type of thumbnail to use. This must match
+ *                  an available Ansel_ImageView object in lib/ImageView.
+ *
+ * 'background'  =  The desired background color of the style. This will
+ *                  set the background of both the photo display area inside
+ *                  of Ansel as well as the background color of any generated
+ *                  photos. This is useful for installations not using PNG.
+ *
+ * The following are optional:
+ *
+ * requires_png               =  Specifies that the style should only be
+ *                               available if both the Horde install uses PNG
+ *                               photos *and* the currently requesting browser
+ *                               has PNG transparency support.
+ *
+ * default_galleryimage_type  =  How to represent each gallery's default photo.
+ *                               This must match an available Ansel_ImageView
+ *                               object in lib/ImageView
+ *                          create_serialized_styles.php
+ * gallery_view               =  The Ansel_View_* subclass to use for gallery
+ *                               views.
+ *
+ * widgets                    =  An array describing any Ansel_Widgets to
+ *                               display on this gallery along with any
+ *                               parameters the widget may need.
+ *
+ * hide                      =   Hide this style in the gallery propery page.
+ *
+ * falback                   -   Fall back to this gallery style if unable to
+ *                               use this style (like if we don't have PNG
+ *                               support)
+ */
+
+// Just a time saver...
+$widgets = array('Actions' => array(),
+                 'Tags' => array('view' => 'gallery'),
+                 'OtherGalleries' => array(),
+                 'Geodata' => array(),
+                 'Links' => array(),
+                 'GalleryFaces' => array(),
+                 'OwnerFaces' => array());
+
+
+$styles['ansel_default'] = array(
+    'name' => 'ansel_default',
+    'title' => _("Default"),
+    'thumbstyle' => 'thumb',
+    'background' => 'none',
+    'widgets' => $widgets,
+);
+
+$styles['ansel_prettythumbs'] = array(
+    'name' => 'ansel_prettythumbs',
+    'title' => _("Pretty Thumbnails (No Background)"),
+    'thumbstyle' => 'prettythumb',
+    'requires_png' => true,
+    // This really only looks good with ImageMagick, not GD.
+    'default_galleryimage_type' => 'roundedstack',
+    'background' => 'none',
+    'widgets' => $widgets,
+);
+
+$styles['ansel_blackonwhite'] = array(
+    'name' => 'ansel_blackonwhite',
+    'title' => _("Pretty Thumbnails (White Background)"),
+    'default_galleryimage_type' => 'roundedstack',
+    'thumbstyle' => 'prettythumb',
+    'background' => 'white',
+    'widgets' => $widgets,
+);
+
+$styles['ansel_sharpshadowed'] = array(
+    'name' => 'ansel_sharpshadowed',
+    'title' => _("Shadowed Thumbnails (White Background)"),
+    'thumbstyle' => 'shadowsharpthumb',
+    'background' => 'white',
+    'default_galleryimage_type' => 'plainstack',
+    'widgets' => $widgets
+);
+
+/* Polaoid style thumbnails and stacks */
+$styles['ansel_polaroid'] = array(
+    'name' => 'ansel_polaroid',
+    'title' => _("Polaroid Style Thumbnails (White Background)"),
+    'thumbstyle' => 'polaroidthumb',
+    'background' => 'white',
+    'default_galleryimage_type' => 'polaroidstack',
+    'widgets' => $widgets,
+);
+
+/* Lightbox image views */
+$styles['ansel_lightbox'] = array(
+    'name' => 'ansel_lightbox',
+    'title' => _("A Lightbox Inspired Style (White Background)"),
+    'thumbstyle' => 'thumb',
+    'background' => 'white',
+    'gallery_view' => 'GalleryLightbox',
+    'widgets' => $widgets,
+);
+
+
+/* Lightbox image views with no background
+ * (requires PNG) */
+$styles['ansel_lightbox_png'] = array(
+    'name' => 'ansel_lightbox_png',
+    'title' => _("A Lightbox Inspired Style (No Background)"),
+    'thumbstyle' => 'thumb',
+    'background' => 'none',
+    'requires_png' => true,
+    'gallery_view' => 'GalleryLightbox',
+    'widgets' => $widgets,
+);
+
+/* Lightbox image views with no background and shadowed thumbs
+ * (requires PNG) */
+$styles['ansel_lightbox_shadowed_png'] = array(
+    'name' => 'ansel_lightbox_shadowed_png',
+    'title' => _("A Lightbox Inspired Style (Drop Shadows, No Background)"),
+    'thumbstyle' => 'shadowsharpthumb',
+    'background' => 'none',
+    'requires_png' => true,
+    'gallery_view' => 'GalleryLightbox',
+    'widgets' => $widgets,
+    'fallback' => 'ansel_lightbox'
+);
+
+
+/* Same as above, but with Polaroid thumbnails/stacks
+ * and no background (so required png support) */
+$styles['ansel_lightbox_polaroid'] = array(
+    'name' => 'ansel_lightbox_polaroid',
+    'title' => _("Lightbox with Polaroids (No Background)"),
+    'thumbstyle' => 'polaroidthumb',
+    'background' => 'none',
+    'requires_png' => true,
+    'default_galleryimage_type' => 'polaroidstack',
+    'gallery_view' => 'GalleryLightbox',
+    'widgets' => $widgets,
+);
+
+/* Simple styles with no Ansel_Widgets useful for rendering  on external sites
+ * via the API. Note that some require PNG support, but fallback to ansel_simple
+ * if no PNG support is found. You could also create your own simple style with
+ * no PNG support required and an appropriate background color for your site
+ * indicated */
+$styles['ansel_simple'] = array(
+    'name' => 'ansel_simple',
+    'title' => _("Simple"),
+    'thumbstyle' => 'thumb',
+    'background' => 'none',
+    'hide' => true,
+);
+
+/* An API friendly lightbox style */
+$styles['ansel_lightbox_simple'] = array(
+    'name' => 'ansel_lightbox_simple',
+    'title' => _("Simple Lightbox"),
+    'thumbstyle' => 'thumb',
+    'background' => 'none',
+    'requires_png' => true,
+    'gallery_view' => 'GalleryLightbox',
+    'hide' => true,
+    'fallback' => 'ansel_simple'
+);
+
+/* Same as above, but with polaroid thumbnails */
+$styles['ansel_lightbox_simple_polaroid'] = array(
+    'name' => 'ansel_lightbox_polaroid',
+    'title' => _("Lightbox with Polaroids (No Background)"),
+    'thumbstyle' => 'polaroidthumb',
+    'background' => 'none',
+    'requires_png' => true,
+    'default_galleryimage_type' => 'polaroidstack',
+    'gallery_view' => 'GalleryLightbox',
+    'hide' => true,
+    'fallback' => 'ansel_simple'
+);
diff --git a/ansel/disclamer.php b/ansel/disclamer.php
new file mode 100644 (file)
index 0000000..0023b2b
--- /dev/null
@@ -0,0 +1,52 @@
+<?php
+/**
+ * $Horde: ansel/disclamer.php,v 1.6 2009/06/10 00:33:01 mrubinsk Exp $
+ *
+ * Copyright 2007-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Jan Zagar <jan.zagar@siol.net>
+ */
+
+require_once dirname(__FILE__) . '/lib/base.php';
+require_once 'Horde/Form.php';
+
+$vars = Horde_Variables::getDefaultVariables();
+$gallery = $ansel_storage->getGallery($vars->get('gallery'));
+if (is_a($gallery, 'PEAR_Error')) {
+    $notification->push($gallery->getMessage());
+    header('Location: ' . Horde::applicationUrl('view.php?view=List', true));
+    exit;
+}
+$url = $vars->get('url');
+
+
+$form = new Horde_Form($vars, _("Content Disclaimer"), 'disclamer');
+$form->addVariable($gallery->get('name'), 'name', 'description', false);
+$form->addVariable($gallery->get('desc'), 'desc', 'description', false);
+$form->addHidden('', 'url', 'text', true);
+$form->addHidden('', 'gallery', 'int', true);
+$msg = sprintf(_("Photo content may be offensive. You must be over %d to continue."), $gallery->get('age'));
+$form->addVariable($msg, 'warning', 'description', false);
+$form->setButtons(array(sprintf(_("Continue - I'm over %d"), $gallery->get('age')), _("Cancel")));
+
+if ($form->isSubmitted()) {
+    if (Horde_Util::getFormData('submitbutton') == _("Cancel")) {
+        $notification->push("You are not authorised to view this photo.", 'horde.warning');
+        header('Location: ' . Horde::applicationUrl('view.php?view=List', true));
+        exit;
+    } else {
+        $_SESSION['ansel']['user_age'] = (int)$gallery->get('age');
+        header('Location: ' . $url, true);
+        exit;
+    }
+}
+
+require ANSEL_TEMPLATES . '/common-header.inc';
+require ANSEL_TEMPLATES . '/menu.inc';
+
+$form->renderActive(null, null, null, 'post');
+
+require $registry->get('templates', 'horde') . '/common-footer.inc';
diff --git a/ansel/docs/CHANGES b/ansel/docs/CHANGES
new file mode 100644 (file)
index 0000000..32b0dee
--- /dev/null
@@ -0,0 +1,129 @@
+--------
+v1.1-cvs
+--------
+
+[mjr] Fix issue with uploading images to galleries in `Browse by Date` mode
+      causing the mode to be reset to `Normal`.
+[mjr] Return to the previously viewed page number when manually selecting
+      face ranges (Bug: 8402).
+[mjr] Significantly reduce the number of javascript errors due to IE issues.
+[mjr] Allow the addition of geolocation data on images that do not contain any
+      and allow the relocating of images that do have the data.
+[mjr] Fix display of EXIF date values (Bug: 8352).
+[mjr] Display a Google Map with thumbnail markers for image location(s) if
+      location data is available.
+[mjr] Add configuration switch to disallow querying the preference backend for
+      other user's identities and preferences (Bug: 8269).
+[mjr] Remove broken implementation of the 'numlimit' configuration option
+      (See Bug: 8139).
+[jan] Fix icon URLs if cookies are turned off.
+[jan] Fix ansel_shares table definition for PostgreSQL.
+[mjr] Fix issue that caused screen image creation to fail when using gd
+      Horde_Image driver.
+[mjr] Fix issue with screen images being generated as PNGs if not using VFS
+      Direct serving.
+
+----
+v1.0
+----
+
+[mjr] Force screen images to be jpegs to reduce image size.
+[mjr] Add a new api method for obtaining the available gallery styles.
+[mjr] Add a widget to the gallery view for showing faces that the gallery owner
+      has tagged.
+[mjr] Allow embedding screen size images in external sites as well.
+[mjr] Add configuration parameter to require a minimum number of
+      images to be present in a gallery before it's displayed in a
+      gallery list. (Duck <duck@obala.net>, Request # 7949)
+[mjr] Add a Link widget to the Image View.
+[mjr] Correct some undefined variable warnings. (Duck <duck@obala.net>)
+[mjr] Fix default parameters for the ansel tag cloud.
+
+--------
+v1.0-RC1
+--------
+
+[jan] Add Turkish translation (Akif Dinc <akif@dinc.org>).
+[jan] Change group field in shares table to work with LDAP groups (Bug #6883).
+[jan] Add WebDAV interface.
+[jan] Add Japanese translation (Takeshi Taguchi <taguchi@tcltk.jp>).
+[mjr] The xppublish feature now uses the correct (not temporary) filenames.
+[cjh] Use the zip extension to read uploaded ZIP files if it's available.
+[mjr] Added rounded, shadowed thumbnails and a 'Polaroid-like' stack of images
+      to use as gallery default images.
+[mjr] Add RSS feeds that can publish feeds across all images, for specific
+      user, specific gallery, and specific tag.
+[mjr] Implement a searchTag api so other applications can list tags and perform
+      tag searches of Ansel resources.
+[mjr] Add support for image and gallery tags.
+[mjr] New blocks for listing recently added images and image comments.
+[mjr] Image comments, titles, and links now correctly update during slideshow
+      and image navigation.
+[cjh] Give Ansel its own Share implementation for now as hierarchical shares
+      are being removed from the main Horde_Share class.
+[cjh] Merge in updates from Exifer 1.5 (final Exifer release), along with some
+      related code cleanup.
+[cjh] Fix screen-size previews of edit operations.
+[cjh] Move images from the DataTree to their own SQL table.
+[jan] Add Slovenian translation (Duck <duck@obala.net>).
+[mjr] Add an addGallery() method to the external api.
+[cjh] Integrate slideshow and image views (Bug #3785).
+[cjh] Add creation of subgalleries, setting gallery owners, and setting image
+      captions to the ansel.php CLI (dorm@dorm.org, Request #3986).
+[cjh] Implement drag-and-drop sorting of images.
+[mas] Fix undefined index warning if no galleries are defined (Bug #2172).
+[jan] Add Swedish translation (Andreas Dahlén <andreas@dahlen.ws>).
+[jan] Add Finnish translation (Leena Heino <liinu@uta.fi>).
+[jan] Add Lithuanian translation (Vilius Sumskas <vilius@lnk.lt>).
+[cjh] Use new Horde_Menu CSS styled menu.
+[cjh] Remove view/ directory as it wasn't being maintained.
+[cjh] Add automatic rotation of images according to Exif information
+      (Brian Templeton <kaetzchen@gmail.com>).
+[cjh] Add API methods (Duck <duck@obala.net>, Roel Gloudemans
+      <roel@gloudemans.info>).
+[cjh] Images can now have categories using the Horde-wide category system
+      (Duck <duck@obala.net>).
+[jan] Add option to not prompt for gallery ID (Duck <duck@obala.net>).
+[cjh] The default gallery "list" format now looks like the gallery image
+      listing view (Duck <duck@obala.net>).
+[jan] Add comments on images.
+[jan] Recursively delete complete directories when deleting images.
+[jan] Don't convert image format when downloading the full image.
+[jan] Store images with correct file extension in the VFS backend.
+[mms] Allow images to be sent as an ecard.
+[jan] Add Spanish translation (Manuel Perez Ayala <mperaya@alcazaba.unex.es>).
+[jan] Add Traditional Chinese translation (David Chang <david@tmv.gov.tw>).
+[cjh] Command line interface to Ansel (Vijay Mahrra
+      <vijay.mahrra@es.easynet.net>).
+[cjh] Store EXIF data for uploaded images into the backend on upload.
+      ("Heath S. Hendrickson" <heath@outerspaceconsultants.com>).
+[cjh] Allow re-ordering images in a gallery.
+      ("Heath S. Hendrickson" <heath@outerspaceconsultants.com>).
+[cjh] Add placeholder tokens in watermark strings
+      ("Heath S. Hendrickson" <heath@outerspaceconsultants.com>).
+[cjh] Allow custom watermark text
+      ("Heath S. Hendrickson" <heath@outerspaceconsultants.com>).
+[cjh] Link for showing exif data in a popup window
+      ("Heath S. Hendrickson" <heath@outerspaceconsultants.com>).
+[cjh] Preference for whether or not to show exif data in the image
+      view ("Heath S. Hendrickson" <heath@outerspaceconsultants.com>).
+[cjh] Support for PHP's native exif_read_data() function
+      ("Heath S. Hendrickson" <heath@outerspaceconsultants.com>).
+[cjh] Add batch-setting of captions.
+[cjh] Add previous and next buttons for navigation within galleries
+      (Brian Keifer <brian@valinor.net>).
+[cjh] Define minimum, maximum, and default sizes for thumbnail and screen
+      images, and allow setting those values in the min-max range on a
+      per-gallery basis (Ben Chavet <ben@chavet.net>).
+[cjh] Authenticated users can now download a zip file containing all images in
+      a gallery.
+[cjh] Add creation and display of subgalleries.
+[cjh] Let users enter the short ids for galleries, instead of using 32
+      character md5 strings.
+[cjh] Add slideshows.
+[cjh] Implement support for Windows XP's Publish To Web wizard.
+[cjh] Add display of embedded exif information in images. Adapted from Exifer,
+      by Jake Olefsky <jake@olefsky.com>.
+[cjh] Show tooltips containing filename and image descriptions.
+[jan] Add German translation.
+[cjh] Initial Ansel code.
diff --git a/ansel/docs/CREDITS b/ansel/docs/CREDITS
new file mode 100644 (file)
index 0000000..a6d7bd3
--- /dev/null
@@ -0,0 +1,36 @@
+========================
+ Ansel Development Team
+========================
+
+
+Core Developers
+===============
+
+- Chuck Hagenbuch <chuck@horde.org>
+- Michael J. Rubinsky <mrubinsk@horde.org>
+
+
+Features
+========
+
+The Exif-reading code was written by Jake Olefsky <jake@olefsky.com>.
+
+The original Exifer is available from
+http://www.offsky.com/software/exif/index.php, and Jake has given his
+permission for the library to be integrated into Ansel.
+
+
+Localization
+============
+
+======================  ===============================================
+Chinese (Traditional)   David Chang <david@tmv.gov.tw>
+Finnish                 Leena Heino <liinu@uta.fi>
+German                  Jan Schneider <jan@horde.org>
+Japanese                Takeshi Taguchi <taguchi@tcltk.jp>
+Lithuanian              Vilius Šumskas <vilius@lnk.lt>
+Slovenian               Duck <duck@obala.net>
+Spanish                 Manuel Perez Ayala <mperaya@alcazaba.unex.es>
+Swedish                 Andreas Dahlén <andreas@dahlen.ws>
+Turkish                 Akif Dinc <akif@dinc.org>
+======================  ===============================================
diff --git a/ansel/docs/INSTALL b/ansel/docs/INSTALL
new file mode 100644 (file)
index 0000000..8cfdaeb
--- /dev/null
@@ -0,0 +1,301 @@
+======================
+ Installing Ansel 1.0
+======================
+
+:Last update:   $Date: 2009/05/15 19:48:54 $
+:Revision:      $Revision: 1.13 $
+
+.. contents:: Contents
+.. section-numbering::
+
+This document contains instructions for installing the Ansel Photo Manager.
+
+For information on the capabilities and features of Ansel, see the file
+README_ in the top-level directory of the Ansel distribution.
+
+
+Obtaining Ansel
+===============
+
+Ansel can be obtained from the Horde website and FTP server, at
+
+   http://www.horde.org/ansel/
+
+   ftp://ftp.horde.org/pub/ansel/
+
+Or use the mirror closest to you:
+
+   http://www.horde.org/mirrors.php
+
+Bleeding-edge development versions of Ansel are available via CVS; see the
+file `horde/docs/HACKING`_ in the Horde distribution, or the website
+http://www.horde.org/source/, for information on accessing the Horde CVS
+repository.
+
+
+Prerequisites
+=============
+
+To function properly, Ansel **requires** the following:
+
+1. A working Horde installation.
+
+   Ansel runs within the `Horde Application Framework`_, a set of common
+   tools for Web applications written in PHP. You must install Horde before
+   installing Ansel.
+
+   .. Important:: Ansel 1.0 requires version 3.2.2 or greater of the
+                  Horde Framework - earlier versions of Horde will **not** work.
+
+   .. _`Horde Application Framework`: http://www.horde.org/horde/
+
+   The Horde Framework can be obtained from the Horde website and FTP server,
+   at
+
+      http://www.horde.org/horde/
+
+      ftp://ftp.horde.org/pub/horde/
+
+   Many of Ansel's prerequisites are also Horde prerequisites.
+
+   .. Important:: Be sure to have completed all of the steps in the
+                  `horde/docs/INSTALL`_ file for the Horde Framework before
+                  installing Ansel.
+
+2. The following PHP capabilities:
+
+   a. _`GD` support ``--with-gd``
+
+      The GD extension provides functionality for image manipulation in
+      PHP. You can alternatively use the imagick_ extension or the
+      ImageMagick_ software, but one of these is **required**.
+
+3. A database:
+
+   Ansel needs a database to store information about images and galleries. Any
+   database supported by MDB2 should work; MySQL and PostgreSQL are the most
+   tested. If you use MySQL, you need at least version 5.0.
+
+4. The following PEAR packages:
+   (See `horde/docs/INSTALL`_ for instructions on installing PEAR packages)
+
+   a. MDB2
+
+      Ansel uses the MDB2 classes for database access.  In addition, you will
+      need to ensure you have the proper MDB2_Driver package for your specific
+      database backend.
+
+5. The following PECL modules:
+   (See `horde/docs/INSTALL`_ for instructions on installing PECL modules)
+
+   a. _`imagick`
+
+      The imagick module provides a quicker, more efficient interface to
+      ImageMagick's capabilities and is used if Horde finds it available on
+      your system.  It provides better results when doing things like
+      generating thumbnails with rounded corners and drop shadows. To use the
+      imagick module, make sure you still set the path to ``convert`` as
+      described below. You can alternatively use the GD_ extension or the
+      ImageMagick_ software, but one of these is **required**.
+
+   b. _`OpenCV`
+
+      The opencv module provides the ability to detect human faces in images.
+      If you would like to be able to have Ansel automatically detect faces in
+      your images, you will need to install the opencv module. You will still be
+      able to manually select faces from your images if you do not have opencv
+      installed.
+
+      .. _ `OpenCV library`: http://sourceforge.net/projects/opencvlibrary/
+      .. _ `PHP module`: http://d.hatena.ne.jp/moriyoshi/20070924/1190595577
+
+
+      Alternatively, you may use the php-facedetect module instead of the
+      php-opencv module. You will still need to install the OpenCV library.
+
+      .. _ `PHP Module`: http://www.xarg.org/project/php-facedetect/
+
+   c. _`libpuzzle`
+
+      The libpuzzle module provides the ability to determine similarities
+      between images based on the image content. If you would like Ansel to be
+      able to provide recognition of similar images and similar faces, you will
+      need to install this module.
+
+      .. _ `libpuzzle library`: http://libpuzzle.pureftpd.org/project/libpuzzle
+
+6. The _`ImageMagick` software.
+
+   If using the `ImageMagick software`_ you need to set the path to the
+   ``convert`` utility in Horde's setup screen
+   (``Administration/Setup/Horde/Image Manipulation``). You can alternatively
+   use the GD_ extension or the imagick_ extension, but one of these is
+   **required**.
+
+   .. _`ImageMagick software`: http://www.imagemagick.org/
+
+7. _`Agora`, the Horde forums application.
+
+   `Agora`_ provides the ability for comments to be added to photos via the
+   forums API. If you want your users to be able to comment on photos, an
+   application that provides the forums API is **required**.  `Agora`_ is
+   currently the only such Horde application, but please be aware that currently
+   `Agora`_ is not officially released in a stable version, so use at your own
+   risk.
+
+   .. _ 'Horde Agora': http://www.horde.org/agora
+
+Installing Ansel
+================
+
+Ansel is written in PHP, and must be installed in a web-accessible
+directory. The precise location of this directory will differ from system to
+system. Conventionally, Ansel is installed directly underneath Horde in the
+web server's document tree.
+
+Since Ansel is written in PHP, there is no compilation necessary; simply
+expand the distribution where you want it to reside and rename the root
+directory of the distribution to whatever you wish to appear in the URL. For
+example, with the Apache web server's default document root of
+``/usr/local/apache/htdocs``, you would type::
+
+   cd /usr/local/apache/htdocs/horde
+   tar zxvf /path/to/ansel-h3-x.y.z.tar.gz
+   mv ansel-h3-x.y.z ansel
+
+and would then find Ansel at the URL::
+
+   http://your-server/horde/ansel/
+
+
+Configuring Ansel
+=================
+
+1. Configuring Horde for Ansel
+
+   a. Register the application
+
+      In ``horde/config/registry.php``, find the ``applications['ansel']``
+      stanza. The default settings here should be okay, but you can change
+      them if desired. If you have changed the location of Ansel relative
+      to Horde, either in the URL, in the filesystem or both, you must update
+      the ``fileroot`` and ``webroot`` settings to their correct values.
+
+2. Creating the database tables
+
+   The specific steps to create Ansel's database tables depend on which
+   database you've chosen to use.
+
+   First, look in ``scripts/sql/`` to see if a script already exists for your
+   database type. If so, you should be able to simply execute that script as
+   superuser in your database. (Note that executing the script as the "horde"
+   user will probably fail when granting privileges.)
+
+   If such a script does not exist, you'll need to build your own, using the
+   file ``ansel.sql`` as a starting point.  If you need assistance in creating
+   database tables, you may wish to let us know on the Ansel mailing list.
+
+   You will also need to make sure that the "horde" user in your database has
+   table-creation privileges, so that the tables that `PEAR MDB2`_ uses to
+   provide portable sequences can be created.
+
+   .. _`PEAR MDB2`: http://pear.php.net/MDB2
+
+3. Configuring Ansel
+
+   To configure Ansel, change to the ``config/`` directory of the installed
+   distribution, and make copies of all of the configuration ``dist`` files
+   without the ``dist`` suffix::
+
+      cd config/
+      for foo in *.dist; do cp $foo `basename $foo .dist`; done
+
+   Or on Windows::
+
+      copy *.dist *.
+
+   Documentation on the format and purpose of those files can be found in each
+   file. You may edit these files if you wish to customize Ansel's appearance
+   and behavior.
+
+   You must login to Horde as a Horde Administrator to finish the
+   configuration of Ansel. Use the Horde ``Administration`` menu item to get
+   to the administration page, and then click on the ``Configuration`` icon to
+   get the configuration page. Select ``Photos`` from the selection list of
+   applications. Fill in or change any configuration values as needed. When
+   done click on ``Generate Photos Configuration`` to generate the
+   ``conf.php`` file. If your web server doesn't have write permissions to the
+   Ansel configuration directory or file, it will not be able to write the
+   file. In this case, go back to ``Configuration`` and choose one of the
+   other methods to create the configuration file ``ansel/config/conf.php``.
+
+   Note for international users: Ansel uses GNU gettext to provide local
+   translations of text displayed by applications; the translations are found
+   in the ``po/`` directory. If a translation is not yet available for your
+   locale (and you wish to create one), see the ``horde/po/README`` file, or
+   if you're having trouble using a provided translation, please see the
+   `horde/docs/TRANSLATIONS`_ file for instructions.
+
+   Note for users of Lighttpd web server: The pretty url generation relies on
+   rewrite rules being configured on your web server. Since lighttpd does not
+   support htaccess files like Apache, there is also a configuration file for
+   lighttpd included that contains the necessary rewrite rules. This file is::
+
+       docs/lighttpd-ansel.conf
+
+   Instructions for using it are included in the file.
+
+
+4. Testing Ansel
+
+   Once you have configured Ansel, bring up the included test page in your Web
+   browser to ensure that all necessary prerequisites have been met. See the
+   `horde/docs/INSTALL`_ document for further details on Horde test
+   scripts. If you installed Ansel as described above, the URL to the test
+   page would be::
+
+      http://your-server/horde/ansel/test.php
+
+   Next, use Ansel to create galleries and photos. Test at least the
+   following:
+
+   - Generate galleries
+   - Upload photos
+   - Edit photo properties
+   - Edit photos
+   - Delete photos
+   - Delete galleries
+
+
+Obtaining Support
+=================
+
+If you encounter problems with Ansel, help is available!
+
+The Horde Frequently Asked Questions List (FAQ), available on the Web at
+
+  http://www.horde.org/faq/
+
+The Horde Project runs a number of mailing lists, for individual applications
+and for issues relating to the project as a whole. Information, archives, and
+subscription information can be found at
+
+  http://www.horde.org/mail/
+
+Lastly, Horde developers, contributors and users may also be found on IRC, on
+the channel #horde on the Freenode Network (irc.freenode.net).
+
+Please keep in mind that Ansel is free software written by volunteers.
+For information on reasonable support expectations, please read
+
+  http://www.horde.org/support.php
+
+Thanks for using Ansel!
+
+The Ansel team
+
+
+.. _README: ?f=README.html
+.. _`horde/docs/HACKING`: ../../horde/docs/?f=HACKING.html
+.. _`horde/docs/INSTALL`: ../../horde/docs/?f=INSTALL.html
+.. _`horde/docs/TRANSLATIONS`: ../../horde/docs/?f=TRANSLATIONS.html
diff --git a/ansel/docs/RELEASE_NOTES b/ansel/docs/RELEASE_NOTES
new file mode 100644 (file)
index 0000000..41fb2c0
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+/**
+ * Release focus. Possible values:
+ * 0 - N/A
+ * 1 - Initial freshmeat announcement
+ * 2 - Documentation
+ * 3 - Code cleanup
+ * 4 - Minor feature enhancements
+ * 5 - Major feature enhancements
+ * 6 - Minor bugfixes
+ * 7 - Major bugfixes
+ * 8 - Minor security fixes
+ * 9 - Major security fixes
+ */
+$this->notes['fm']['focus'] = 1;
+
+/* Mailing list release notes. */
+$this->notes['ml']['changes'] = <<<ML
+
+The Horde Team is pleased to announce the final release of the Ansel Photo
+Management and Web Gallery application version H3 (1.0).
+
+Ansel is a full featured photo management application. With it, you can organize
+your photos in any number of galleries and subgalleries, share galleries among
+other Horde users or even make them public. Ansel supports a wide range of
+features. See http://horde.org/ansel for more information.
+ML;
+
+/* Freshmeat release notes, not more than 600 characters. */
+$this->notes['fm']['changes'] = <<<FM
+This is the first stable release.
+FM;
+
+$this->notes['name'] = 'Ansel';
+$this->notes['list'] = 'horde';
+$this->notes['fm']['project'] = 'horde-ansel';
+$this->notes['fm']['branch'] = 'Default';
diff --git a/ansel/docs/TODO b/ansel/docs/TODO
new file mode 100755 (executable)
index 0000000..2095628
--- /dev/null
@@ -0,0 +1,17 @@
+=============================
+ Ansel Development TODO List
+=============================
+
+:Last update:   $Date: 2008/01/16 01:30:57 $
+:Revision:      $Revision: 1.43 $
+:Contact:       ansel@lists.horde.org
+
+- Further abstract out all database access from Ansel_Gallery/Ansel_Image and
+  place in Ansel_Storage. Likewise, consolidate the static Ansel_Tags methods
+  into the Ansel_Storage class.
+
+- A command line script for batch importing/exporting/management of galleries
+  (importing is done).
+
+- Complete XMLRPC/SOAP API that remote programs can use to upload/edit/manage
+  images.
diff --git a/ansel/docs/lighttpd-ansel.conf b/ansel/docs/lighttpd-ansel.conf
new file mode 100755 (executable)
index 0000000..016aa08
--- /dev/null
@@ -0,0 +1,36 @@
+## This file should be reviewed prior to inclusion in your lighttpd
+## configuration.  Specifically, if you have ansel somewhere other than
+## /horde/ansel you will need to edit the following rules to match your server
+## configuration.
+
+## This file should be included in your lighttpd.conf file with the "include"
+## directive. Example:
+##   include "path/to/lighttpd-ansel.conf"
+## The exact path you use will of course depend on your specific configuration.
+
+url.rewrite-once += (
+        ## General Groupings
+        "^/horde/ansel/user/?(?:\?(.*))?$" => "/horde/ansel/group.php?groupby=owner&$1",
+        "^/horde/ansel/category/?(?:\?(.*))?$" => "/horde/ansel/group.php?groupby=category&$1",
+        "^/horde/ansel/all/?(?:\?(.*))?$" => "/horde/ansel/view.php?view=List&groupby=none&$1",
+
+        ## This might need some work to catch all possible usernames.
+        "^/horde/ansel/user/([@a-zA-Z0-9%_+.!*',()~-]*)/rss/?$" => "/horde/ansel/rss.php?stream_type=user&id=$1",
+        "^/horde/ansel/user/([@a-zA-Z0-9%_+.!*',()~-]*)/?(?:\?(.*))?$" => "/horde/ansel/view.php?view=List&groupby=owner&owner=$1&$2",
+        "^/horde/ansel/category/([@a-zA-Z0-9%_+.!*',()~-]*)/?(?:\?(.*))?$" => "/horde/ansel/view.php?view=List&groupby=category&category=$1&$2",
+
+        ## Galleries by ID
+        "^/horde/ansel/gallery/id/([0-9]+)/([0-9]+)/?(?:\?(.*))?$" => "/horde/ansel/view.php?view=Image&gallery=$1&image=$2&$3",
+        "^/horde/ansel/gallery/id/([0-9]+)/rss/?$" => "/horde/ansel/rss.php?stream_type=gallery&id=$1",
+        "^/horde/ansel/gallery/id/([0-9]+)/?(?:\?(.*))?$" => "/horde/ansel/view.php?view=Gallery&gallery=$1&$2",
+
+        ## Galleries by slug
+        "^/horde/ansel/gallery/([a-zA-Z0-9_@]+)/([0-9]+)/?(?:\?(.*))?$" => "/horde/ansel/view.php?view=Image&slug=$1&image=$2&$3",
+        "^/horde/ansel/gallery/([a-zA-Z0-9_@]+)/rss/?$" => "/horde/ansel/rss.php?stream_type=gallery&slug=$1",
+        "^/horde/ansel/gallery/([a-zA-Z0-9_@]+)/?(?:\?(.*))?$" => "/horde/ansel/view.php?view=Gallery&slug=$1&$2",
+
+        ## Tag browsing
+        "^/horde/ansel/tag/?(?:\?(.*))?$" => "/horde/ansel/view.php?view=Results&$1",
+        "^/horde/ansel/tag/([a-zA-Z0-9%_+.!*',()~-]*)/rss/?$" => "/horde/ansel/rss.php?stream_type=tag&id=$1",
+        "^/horde/ansel/tag/([a-zA-Z0-9%_+.!*',()~-]*)/?(?:\?(.*))?$" => "/horde/ansel/view.php?view=Results&tag=$1&$2"
+)
diff --git a/ansel/edit_dates.php b/ansel/edit_dates.php
new file mode 100644 (file)
index 0000000..f729e93
--- /dev/null
@@ -0,0 +1,99 @@
+<?php
+/**
+ * $Horde: ansel/edit_dates.php,v 1.8 2009/07/13 17:18:38 mrubinsk Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ */
+
+@define('ANSEL_BASE', dirname(__FILE__));
+require_once ANSEL_BASE . '/lib/base.php';
+
+$images = Horde_Util::getFormData('image', array());
+$actionID = Horde_Util::getFormData('actionID');
+$gallery_id = Horde_Util::getFormData('gallery');
+$page = Horde_Util::getFormData('page', 0);
+
+/* If we have a single gallery, check perms now */
+if (!empty($gallery_id)) {
+    $gallery = $ansel_storage->getGallery($gallery_id);
+    if (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+        $notification->push(_("You are not allowed to edit these photos."), 'horde.error');
+        Horde_Util::closeWindowJS('window.opener.location.href = window.opener.location.href; window.close();');
+        exit;
+    }
+} else {
+    // TODO - right now we should *always* have a gallery_id. If we get here
+    //        from a results view, we may not, but that's not implemented yet.
+}
+
+/* Make sure we have at least one image */
+if (!count($images)) {
+    echo $notification->push(_("You must select at least on photo to edit."), 'horde.error');
+    Horde_Util::closeWindowJS('window.opener.location.href = window.opener.location.href; window.close();');
+    exit;
+}
+
+/* Includes */
+require_once ANSEL_BASE . '/lib/Forms/ImageDate.php';
+require_once 'Horde/Form/Renderer.php';
+
+/* Set up the form */
+$vars = Horde_Variables::getDefaultVariables();
+$form = new ImageDateForm($vars, _("Edit Dates"));
+/* Are we doing the edit now? */
+if ($actionID == 'edit_dates') {
+    $count = 0;
+    foreach (array_keys($images) as $image_id) {
+        $image = $ansel_storage->getImage($image_id);
+        if (!is_a($image, 'PEAR_Error')) {
+            if (empty($gallery_id)) {
+                // Images might be from different galleries
+                $gallery = $ansel_storage->getGallery($image->gallery);
+                if (is_a($gallery, 'PEAR_Error') ||
+                    !$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+                    continue;
+                }
+            }
+            require_once 'Horde/Date.php';
+            $newDate = new Horde_Date($vars->get('image_originalDate'));
+            $image->originalDate = (int)$newDate->timestamp();
+            $image->save();
+            ++$count;
+        } else {
+           $notification->push(sprintf(_("There was an error editing the dates: %s"), $image->getMessage()), 'horde.error');
+           Horde_Util::closeWindowJS('window.opener.location.href = window.opener.location.href; window.close();');
+           exit;
+        }
+
+    }
+
+    $notification->push(sprintf(_("Successfully modified the date on %d photos."), $count), 'horde.success');
+    Horde_Util::closeWindowJS('window.opener.location.href = window.opener.location.href; window.close();');
+    exit;
+}
+
+$keys = array_keys($images);
+$html = '';
+foreach ($keys as $key) {
+    $html .= '<img src="' . Ansel::getImageUrl($key, 'mini', false) . '" style="margin:2px;" alt="[thumbnail]" />';
+}
+$image = $ansel_storage->getImage(array_pop($keys));
+/* Display the form */
+$vars->set('image', $images);
+$vars->set('gallery', $gallery_id);
+$vars->set('page', $page);
+$vars->set('actionID', 'edit_dates');
+$vars->set('image_list', $html);
+$vars->set('image_originalDate', $image->originalDate);
+$renderer = new Horde_Form_Renderer();
+$count = count($images);
+include ANSEL_TEMPLATES . '/common-header.inc';
+$form->renderActive($renderer, $vars, null, 'post');
+// Needed to ensure the body element is large enough to hold the pop up calendar
+echo '<br /><br /><br />';
+require $registry->get('templates', 'horde') . '/common-footer.inc';
diff --git a/ansel/faces/claim.php b/ansel/faces/claim.php
new file mode 100644 (file)
index 0000000..06c8204
--- /dev/null
@@ -0,0 +1,78 @@
+<?php
+/**
+ * Identify a person in a photo
+ *
+ * $Horde: ansel/faces/claim.php,v 1.9 2009/06/10 00:33:02 mrubinsk Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Duck <duck@obala.net>
+ */
+define('ANSEL_BASE', dirname(__FILE__) . '/../');
+require_once ANSEL_BASE . '/lib/base.php';
+require_once ANSEL_BASE . '/lib/Faces.php';
+
+require_once 'Horde/Form.php';
+
+$faces = Ansel_Faces::factory();
+
+$face_id = Horde_Util::getFormData('face');
+$face = $faces->getFaceById($face_id);
+
+if (is_a($face, 'PEAR_Error')) {
+    $notification->push($face->getMessage());
+    header('Location: ' . Horde::applicationUrl('faces/search/all.php'));
+    exit;
+}
+
+$title = _("Tell us who is in this photo");
+
+$vars = Horde_Variables::getDefaultVariables();
+$form = new Horde_Form($vars, $title);
+$form->addHidden('', 'face', 'int', true);
+$form->addVariable(_("Person"), 'person', 'text', true);
+$form->setButtons($title);
+if ($form->validate()) {
+    if (Horde_Util::getFormData('submitbutton') == _("Cancel")) {
+        $notification->push(_("Action was cancelled."), 'horde.warning');
+    } else {
+        require ANSEL_BASE . '/lib/Report.php';
+        $report = Ansel_Report::factory();
+        $gallery = $ansel_storage->getGallery($face['gallery_id']);
+
+        $face_link = Horde_Util::addParameter(
+            Horde::applicationUrl('faces/custom.php', true),
+            array('name' => $vars->get('person'),
+                  'face' => $face_id,
+                  'image' => $face['image_id']), null, false);
+
+        $title = _("I know who is on one of your photos");
+        $body = _("Gallery Name") . ': ' . $gallery->get('name') . "\n"
+                . _("Gallery Description") . ': ' . $gallery->get('desc') . "\n\n"
+                . $title . "\n"
+                . _("Person") . ': ' . $vars->get('person') . "\n"
+                . _("Face") . ': ' . $face_link;
+
+        $report->setTitle($title);
+        $result = $report->report($body, $gallery->get('owner'));
+        if (is_a($result, 'PEAR_Error')) {
+            $notification->push(_("Face name was not reported.") . ' ' .
+                                $result->getMessage(), 'horde.error');
+        } else {
+            $notification->push(_("The owner of the photo, who will delegate the face name, was notified."), 'horde.success');
+        }
+    }
+
+    header('Location: ' . $faces->getLink($face));
+    exit;
+}
+
+require ANSEL_TEMPLATES . '/common-header.inc';
+require ANSEL_TEMPLATES . '/menu.inc';
+
+$form->renderActive(null, null, null, 'post');
+
+require $registry->get('templates', 'horde') . '/common-footer.inc';
\ No newline at end of file
diff --git a/ansel/faces/custom.php b/ansel/faces/custom.php
new file mode 100644 (file)
index 0000000..8d03f62
--- /dev/null
@@ -0,0 +1,87 @@
+<?php
+/**
+ * Explicitly add/edit a face range to an image.
+ *
+ * $Horde: ansel/faces/custom.php,v 1.14 2009/07/08 18:28:41 slusarz Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Duck <duck@obala.net>
+ */
+require_once dirname(__FILE__) . '/../lib/base.php';
+
+$image_id = (int)Horde_Util::getFormData('image');
+$face_id = (int)Horde_Util::getFormData('face');
+$page = Horde_Util::getFormData('page', 0);
+$url = Horde_Util::getFormData('url');
+$urlparams = array('page' => $page);
+if (!empty($url)) {
+    $urlparams['url'] = $url;
+}
+$form_post = Horde_Util::addParameter(Horde::applicationUrl('faces/savecustom.php'), $urlparams);
+
+$image = &$ansel_storage->getImage($image_id);
+if (is_a($image, 'PEAR_Error')) {
+    $notification->push($image);
+    header('Location: ' . Horde::applicationUrl('list.php'));
+    exit;
+}
+
+$gallery = $ansel_storage->getGallery($image->gallery);
+if (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+    $notification->push(_("Access denied editing the photo."));
+    header('Location: ' . Ansel::getUrlFor('view', array('gallery' => $image->gallery)));
+    exit;
+}
+
+$x1 = 0;
+$y1 = 0;
+$x2 = $conf['screen']['width'];
+$y2 = $conf['screen']['width'];
+$name = Horde_Util::getFormData('name');
+
+if ($face_id) {
+    require_once ANSEL_BASE . '/lib/Faces.php';
+    $faces = Ansel_Faces::factory();
+
+    if (is_a($faces, 'PEAR_Error')) {
+        $notification->push($faces);
+        header('Location: ' . $back_url);
+        exit;
+    }
+
+    $face = $faces->getFaceById($face_id, true);
+    if (is_a($face, 'PEAR_Error')) {
+        $notification->push($face);
+    } else {
+        $x1 = $face['face_x1'];
+        $y1 = $face['face_y1'];
+        $x2 = $face['face_x2'];
+        $y2 = $face['face_y2'];
+        if (!empty($face['face_name'])) {
+            $name = $face['face_name'];
+        }
+    }
+
+}
+
+$height = $x2 - $x1;
+$width = $y2 - $y1;
+
+$title = _("Create a new face");
+
+Horde::addScriptFile('prototype.js', 'horde', true);
+Horde::addScriptFile('builder.js');
+Horde::addScriptFile('effects.js', 'horde', true);
+Horde::addScriptFile('controls.js', 'horde', true);
+Horde::addScriptFile('dragdrop.js', 'horde', true);
+Horde::addScriptFile('cropper.js');
+Horde::addScriptFile('stripe.js', 'horde', true);
+
+require ANSEL_TEMPLATES . '/common-header.inc';
+require ANSEL_TEMPLATES . '/menu.inc';
+require ANSEL_TEMPLATES . '/faces/custom.inc';
+require $registry->get('templates', 'horde') . '/common-footer.inc';
diff --git a/ansel/faces/delete.php b/ansel/faces/delete.php
new file mode 100644 (file)
index 0000000..7f6e826
--- /dev/null
@@ -0,0 +1,38 @@
+<?php
+/**
+ * Delete a face from an image.
+ *
+ * $Horde: ansel/faces/delete.php,v 1.10 2009/07/30 18:02:13 mrubinsk Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Duck <duck@obala.net>
+ */
+require_once dirname(__FILE__) . '/../lib/base.php';
+require_once ANSEL_BASE . '/lib/Faces.php';
+
+$image_id = (int)Horde_Util::getFormData('image');
+$face_id = (int)Horde_Util::getFormData('face');
+
+$image = &$ansel_storage->getImage($image_id);
+if (is_a($image, 'PEAR_Error')) {
+    die($image->getMessage());
+}
+
+$gallery = &$ansel_storage->getGallery($image->gallery);
+if (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+    die(_("Access denied editing the photo."));
+}
+
+$faces = Ansel_Faces::factory();
+if (is_a($faces, 'PEAR_Error')) {
+    die($faces->getMessage());
+}
+
+$result = $faces->delete($image, $face_id);
+if (is_a($result, 'PEAR_Error')) {
+    die($result->getMessage());
+}
diff --git a/ansel/faces/face.php b/ansel/faces/face.php
new file mode 100644 (file)
index 0000000..8f159a8
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+/**
+ * Shows all images that the supplied, named face appears on?
+ *
+ * TODO: Maybe incorporate this into some kind of generic "result" view?
+ * At least, we need to rename this to something other that image.php to
+ * reflect what it's used for.
+ *
+ * $Horde: ansel/faces/face.php,v 1.6 2009/06/10 00:33:02 mrubinsk Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Duck <duck@obala.net>
+ */
+require_once dirname(__FILE__) . '/../lib/base.php';
+require_once ANSEL_BASE . '/lib/Faces.php';
+
+$faces = Ansel_Faces::factory();
+if (is_a($faces, 'PEAR_Error')) {
+    die($faces->getMessage());
+}
+
+$face_id = Horde_Util::getFormData('face');
+$face = $faces->getFaceById($face_id);
+if (is_a($face, 'PEAR_Error')) {
+    $notification->push($face->getMessage());
+    header('Location: ' . Horde::applicationUrl('faces/index.php'));
+    exit;
+}
+
+$title = _("Face") . ' :: ' . $face['face_name'];
+
+require ANSEL_TEMPLATES . '/common-header.inc';
+require ANSEL_TEMPLATES . '/menu.inc';
+
+require_once ANSEL_TEMPLATES . '/faces/face.inc';
+
+require $registry->get('templates', 'horde') . '/common-footer.inc';
\ No newline at end of file
diff --git a/ansel/faces/gallery.php b/ansel/faces/gallery.php
new file mode 100644 (file)
index 0000000..7694cd7
--- /dev/null
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Find faces and display faces UI for entire gallery.
+ *
+ * TODO: Turn this into an Ansel_View::
+ *
+ * $Horde: ansel/faces/gallery.php,v 1.16 2009/07/13 21:33:53 mrubinsk Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Duck <duck@obala.net>
+ */
+require_once dirname(__FILE__) . '/../lib/base.php';
+require_once ANSEL_BASE . '/lib/Faces.php';
+require_once 'Horde/Serialize.php';
+require_once 'Horde/UI/Pager.php';
+
+$gallery_id = (int)Horde_Util::getFormData('gallery');
+if (empty($gallery_id)) {
+    $notification->push(_("No gallery specified"), 'horde.error');
+    header('Location: ' . Ansel::getUrlFor('default_view', array()));
+    exit;
+}
+$gallery = $ansel_storage->getGallery($gallery_id);
+if (is_a($gallery, 'PEAR_Error')) {
+    $notification->push($gallery->getMessage(), 'horde.error');
+    header('Location: ' . Ansel::getUrlFor('view', array('gallery' => $gallery_id)));
+    exit;
+} elseif (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+    $notification->push(sprintf(_("Access denied editing gallery \"%s\"."), $gallery->get('name')), 'horde.error');
+    header('Location: ' . Ansel::getUrlFor('view', array('gallery' => $gallery_id)));
+    exit;
+}
+$gallery->setDate(Ansel::getDateParameter());
+$page = Horde_Util::getFormData('page', 0);
+$perpage = min($prefs->getValue('tilesperpage'), $conf['thumbnail']['perpage']);
+$images = $gallery->getImages($page * $perpage, $perpage);
+
+$reloadimage = $registry->getImageDir('horde') . '/reload.png';
+$customimage = $registry->getImageDir('horde') . '/layout.png';
+$customurl = Horde_Util::addParameter(Horde::applicationUrl('faces/custom.php'), 'page', $page);
+$autogenerate = Ansel_Faces::autogenerate();
+
+$vars = Horde_Variables::getDefaultVariables();
+$pager = new Horde_UI_Pager(
+    'page', $vars,
+    array('num' => $gallery->countImages(),
+          'url' => 'faces/gallery.php',
+          'perpage' => $perpage));
+$pager->preserve('gallery',  $gallery_id);
+
+$title = sprintf(_("Searching for faces in %s"), Horde::link(Ansel::getUrlFor('view', array('gallery' => $gallery_id, 'view' => 'Gallery'))) . $gallery->get('name') . '</a>');
+Horde::addScriptFile('prototype.js', 'horde', true);
+Horde::addScriptFile('stripe.js', 'horde', true);
+require ANSEL_TEMPLATES . '/common-header.inc';
+require ANSEL_TEMPLATES . '/menu.inc';
+require ANSEL_TEMPLATES . '/faces/gallery.inc';
+require $registry->get('templates', 'horde') . '/common-footer.inc';
diff --git a/ansel/faces/image.php b/ansel/faces/image.php
new file mode 100644 (file)
index 0000000..a7786d9
--- /dev/null
@@ -0,0 +1,53 @@
+<?php
+/**
+ * Process an single image (to be called via Ajax)
+ *
+ * $Horde: ansel/faces/image.php,v 1.10 2009/06/10 00:33:02 mrubinsk Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Duck <duck@obala.net>
+ */
+require_once dirname(__FILE__) . '/../lib/base.php';
+require_once ANSEL_BASE . '/lib/Faces.php';
+
+$faces = Ansel_Faces::factory();
+if (is_a($faces, 'PEAR_Error')) {
+    die($faces->getMessage());
+}
+
+$name = '';
+$autocreate = true;
+$image_id = (int)Horde_Util::getFormData('image');
+$reload = (int)Horde_Util::getFormData('reload');
+$result = $faces->getImageFacesData($image_id);
+
+// Attempt to get faces from the picture if we don't already have results,
+// or if we were asked to explicitly try again.
+if (($reload || empty($result))) {
+    $image = &$ansel_storage->getImage($image_id);
+    if (is_a($image, 'PEAR_Error')) {
+        exit;
+    }
+
+    $result = $image->createView('screen');
+    if (is_a($result, 'PEAR_Error')) {
+        exit;
+    }
+
+    $result = $faces->getFromPicture($image_id, $autocreate);
+    if (is_a($result, 'PEAR_Error')) {
+        exit;
+    }
+}
+
+if (!empty($result)) {
+    $imgdir = $registry->getImageDir('horde');
+    $customurl = Horde::applicationUrl('faces/custom.php');
+    require_once ANSEL_TEMPLATES . '/faces/image.inc';
+} else {
+    echo _("No faces found");
+}
\ No newline at end of file
diff --git a/ansel/faces/img.php b/ansel/faces/img.php
new file mode 100644 (file)
index 0000000..23387ab
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+/**
+ * Fetch face image for display
+ *
+ * $Horde: ansel/faces/img.php,v 1.11 2009/06/10 00:33:02 mrubinsk Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Duck <duck@obala.net>
+ */
+require_once dirname(__FILE__) . '/../lib/base.php';
+require_once ANSEL_BASE . '/lib/Faces.php';
+
+$face_id = Horde_Util::getFormData('face');
+$faces = Ansel_Faces::factory();
+if (is_a($faces, 'PEAR_Error')) {
+    Horde::logMessage($faces, __FILE__, __LINE__, PEAR_LOG_ERR);
+    exit;
+}
+
+// Sendfile support. Lighttpd < 1.5 only understands the X-LIGHTTPD-send-file
+// header
+if ($conf['vfs']['src'] == 'sendfile') {
+    $face = $faces->getFaceById($face_id);
+
+    // Make sure the view exists
+    if (!$faces->viewExists($face['image_id'], $face_id, true)) {
+        Horde::logMessage(sprintf('Unable to locate or create face_id %u.',
+                                  $face_id));
+        exit;
+    }
+
+    // We definitely have an image for the face.
+    $filename = $ansel_vfs->readFile(
+        $faces->getVFSPath($face['image_id']) . 'faces',
+        $face_id . $faces->getExtension());
+    if (is_a($filename, 'PEAR_ERROR')) {
+        Horde::logMessage($filename, __FILE__, __LINE__, PEAR_LOG_ERR);
+        exit;
+    }
+    header('Content-type: image/' . $GLOBALS['conf']['image']['type']);
+    header('X-LIGHTTPD-send-file: ' . $filename);
+    header('X-Sendfile: ' . $filename);
+    exit;
+}
+
+// Run it through PHP
+$img = $faces->getFaceImageObject($face_id);
+if (is_a($img, 'PEAR_Error')) {
+    exit;
+}
+header('Content-type: image/' . $GLOBALS['conf']['image']['type']);
+echo $img->raw();
diff --git a/ansel/faces/name.php b/ansel/faces/name.php
new file mode 100644 (file)
index 0000000..d451845
--- /dev/null
@@ -0,0 +1,39 @@
+<?php
+/**
+ * Set the name of a single image via Ajax
+ *
+ * $Horde: ansel/faces/name.php,v 1.9 2009/07/08 18:28:41 slusarz Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Duck <duck@obala.net>
+ */
+require_once dirname(__FILE__) . '/../lib/base.php';
+require_once ANSEL_BASE . '/lib/Faces.php';
+
+$image_id = (int)Horde_Util::getFormData('image');
+$face_id = (int)Horde_Util::getFormData('face');
+$name = Horde_Util::getFormData('name');
+
+$image = &$ansel_storage->getImage($image_id);
+if (is_a($image, 'PEAR_Error')) {
+    die($image->getMessage());
+}
+
+$gallery = &$ansel_storage->getGallery($image->gallery);
+if (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+    die(_("Access denied editing the photo."));
+}
+
+$faces = Ansel_Faces::factory();
+if (is_a($faces, 'PEAR_Error')) {
+    die($faces->getMessage());
+}
+
+$result = $faces->setName($face_id, $name);
+if (is_a($result, 'PEAR_Error')) {
+    die($result->getDebugInfo());
+}
\ No newline at end of file
diff --git a/ansel/faces/report.php b/ansel/faces/report.php
new file mode 100644 (file)
index 0000000..666b2f4
--- /dev/null
@@ -0,0 +1,81 @@
+<?php
+/**
+ * Process an single image (to be called by ajax)
+ *
+ * $Horde: ansel/faces/report.php,v 1.9 2009/06/10 00:33:02 mrubinsk Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Duck <duck@obala.net>
+ */
+require_once dirname(__FILE__) . '/../lib/base.php';
+require_once ANSEL_BASE . '/lib/Faces.php';
+
+require_once 'Horde/Form.php';
+
+$faces = Ansel_Faces::factory();
+
+$face_id = Horde_Util::getFormData('face');
+$face = $faces->getFaceById($face_id);
+
+if (is_a($face, 'PEAR_Error')) {
+    $notification->push($face->getMessage());
+    header('Location: ' . Horde::applicationUrl('faces/search/all.php'));
+    exit;
+}
+
+$title = _("Report face");
+
+$vars = Horde_Variables::getDefaultVariables();
+$form = new Horde_Form($vars, $title);
+$form->addHidden('', 'face', 'int', true);
+$form->addVariable(_("Reason"), 'reason', 'longtext', true, false, _("Please describe the reasons. For example, you don't want to be mentioned etc..."));
+$form->setButtons($title);
+
+if ($form->validate()) {
+
+    if (Horde_Util::getFormData('submitbutton') == _("Cancel")) {
+
+        $notification->push(_("Action was cancelled."), 'horde.warning');
+
+    } else {
+
+        require ANSEL_BASE . '/lib/Report.php';
+        $report = Ansel_Report::factory();
+        $gallery = $ansel_storage->getGallery($face['gallery_id']);
+
+        $face_link = Horde_Util::addParameter(Horde::applicationUrl('faces/face.php', true),
+                        array('name' => $vars->get('person'),
+                              'face' => $face_id,
+                                'image' => $face['image_id']), null, false);
+
+        $body = _("Gallery Name") . ': ' . $gallery->get('name') . "\n"
+                . _("Gallery Description") . ': ' . $gallery->get('desc') . "\n\n"
+                . $title . "\n"
+                . _("Reason") . ': ' . $vars->get('reason') . "\n"
+                . _("Face") . ': ' . $face_link;
+
+        $report->setTitle($title);
+        $result = $report->report($body, $gallery->get('owner'));
+        if (is_a($result, 'PEAR_Error')) {
+            $notification->push(_("Face name was not reported.") . ' ' .
+                                $result->getMessage(), 'horde.error');
+        } else {
+            $notification->push(_("The owner of the photo was notified."), 'horde.success');
+        }
+
+    }
+
+    header('Location: ' . $faces->getLink($face));
+    exit;
+}
+
+require ANSEL_TEMPLATES . '/common-header.inc';
+require ANSEL_TEMPLATES . '/menu.inc';
+
+$form->renderActive(null, null, null, 'post');
+
+require $registry->get('templates', 'horde') . '/common-footer.inc';
\ No newline at end of file
diff --git a/ansel/faces/savecustom.php b/ansel/faces/savecustom.php
new file mode 100644 (file)
index 0000000..4ac7011
--- /dev/null
@@ -0,0 +1,62 @@
+<?php
+/**
+ * Process an single image (to be called by ajax)
+ *
+ * $Horde: ansel/faces/savecustom.php,v 1.10 2009/07/06 15:50:34 mrubinsk Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Duck <duck@obala.net>
+ */
+require_once dirname(__FILE__) . '/../lib/base.php';
+require_once ANSEL_BASE . '/lib/Faces.php';
+
+$image_id = (int)Horde_Util::getFormData('image_id');
+$gallery_id = (int)Horde_Util::getFormData('gallery_id');
+$face_id = (int)Horde_Util::getFormData('face_id');
+$url = Horde_Util::getFormData('url');
+$page = Horde_Util::getFormData('page', 0);
+
+$back_url = empty($url) ?
+    Horde_Util::addParameter(Horde::applicationUrl('faces/gallery.php'),
+                             array('gallery' => $gallery_id,
+                                   'page' => $page), null, false) :
+    $url;
+
+if (Horde_Util::getPost('submit') == _("Cancel")) {
+    $notification->push(_("Changes cancelled."), 'horde.warning');
+    header('Location: ' . $back_url);
+    exit;
+}
+
+$faces = Ansel_Faces::factory();
+if (is_a($faces, 'PEAR_Error')) {
+    $notification->push($faces);
+    header('Location: ' . $back_url);
+    exit;
+}
+
+$result = $faces->saveCustomFace($face_id,
+                           $image_id,
+                           (int)Horde_Util::getFormData('x1'),
+                           (int)Horde_Util::getFormData('y1'),
+                           (int)Horde_Util::getFormData('x2'),
+                           (int)Horde_Util::getFormData('y2'),
+                           Horde_Util::getFormData('name'));
+
+if (is_a($result, 'PEAR_Error')) {
+    $notification->push($result);
+    $notification->push($result->getDebugInfo());
+    header('Location: ' . $back_url);
+    exit;
+} elseif ($face_id == 0) {
+    $notification->push(_("Face successfuly created"), 'horde.success');
+} else {
+    $notification->push(_("Face successfuly updated"), 'horde.success');
+}
+
+header('Location: ' . $back_url);
+exit;
diff --git a/ansel/faces/search/all.php b/ansel/faces/search/all.php
new file mode 100644 (file)
index 0000000..f363ab2
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+/**
+ * Process an single image (to be called by ajax)
+ *
+ * $Horde: ansel/faces/search/all.php,v 1.5 2009/06/10 00:33:02 mrubinsk Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Duck <duck@obala.net>
+ */
+require_once 'tabs.php';
+require_once 'Horde/UI/Pager.php';
+
+$title = _("All faces");
+$page = Horde_Util::getFormData('page', 0);
+$perpage = $prefs->getValue('facesperpage');
+
+$count = $faces->countAllFaces();
+if (is_a($count, 'PEAR_Error')) {
+    $notification->push($count->getDebugInfo());
+    $count = 0;
+    $results = array();
+} else {
+    $results = $faces->allFaces($page * $perpage, $perpage);
+}
+
+$vars = Horde_Variables::getDefaultVariables();
+$pager = new Horde_UI_Pager(
+    'page', $vars,
+    array('num' => $count,
+          'url' => 'faces/search/all.php',
+          'perpage' => $perpage));
+
+require ANSEL_TEMPLATES . '/common-header.inc';
+require ANSEL_TEMPLATES . '/menu.inc';
+include ANSEL_TEMPLATES . '/faces/faces.inc';
+require $registry->get('templates', 'horde') . '/common-footer.inc';
diff --git a/ansel/faces/search/image.php b/ansel/faces/search/image.php
new file mode 100644 (file)
index 0000000..1a0f54c
--- /dev/null
@@ -0,0 +1,81 @@
+<?php
+/**
+ * Process an single image (to be called by ajax)
+ *
+ * $Horde: ansel/faces/search/image.php,v 1.11 2009/07/08 18:28:41 slusarz Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Duck <duck@obala.net>
+ */
+require_once 'tabs.php';
+require_once 'Horde/Form.php';
+require_once 'Horde/Image.php';
+
+/* Search from */
+$form = new Horde_Form($vars);
+$msg = _("Please upload photo with the face to search for. You can search only one face per time.");
+$form->addVariable(_("Face to search for"), 'image', 'image', true, false, $msg, array(false));
+$form->setButtons(_("Upload"));
+
+if ($form->validate()) {
+
+    $form->getInfo(null, $info);
+
+    $tmp = Horde::getTempDir();
+    $driver = empty($conf['image']['convert']) ? 'gd' : 'im';
+    $img = Ansel::getImageObject();
+    $result = $img->loadFile($info['image']['file']);
+    if (is_a($result, 'PEAR_Error')) {
+        $notification->push($result->getMessage());
+        header('Location: ' . Horde::applicationUrl('faces/search/image.php'));
+        exit;
+    }
+
+    $dimensions = $img->getDimensions();
+    if ($dimensions['width'] < 50 || $dimensions['height'] < 50) {
+        $notification->push(_("Photo is too small. Search photo must be at least 50x50 pixels."));
+        header('Location: ' . Horde::applicationUrl('faces/search/image.php'));
+        exit;
+    }
+
+    $result = $img->resize(min($conf['screen']['width'], $dimensions['width']),
+                            min($conf['screen']['height'], $dimensions['height']));
+    if (is_a($result, 'PEAR_Error')) {
+        $notification->push($result->getMessage());
+        header('Location: ' . Horde::applicationUrl('faces/search/image.php'));
+        exit;
+    }
+
+    $path = $tmp . '/search_face_' . Horde_Auth::getAuth() . Ansel_Faces::getExtension();
+    if (file_put_contents($path, $img->raw())) {
+        header('Location: ' . Horde::applicationUrl('faces/search/image_define.php'));
+    } else {
+        $notification->push(_("Cannot store search photo"));
+        header('Location: ' . Horde::applicationUrl('faces/search/image.php'));
+    }
+    exit;
+
+}
+
+$title = _("Upload face photo");
+require ANSEL_TEMPLATES . '/common-header.inc';
+require ANSEL_TEMPLATES . '/menu.inc';
+
+echo $tabs->render(Horde_Util::getGet('search_faces', 'image'));
+$form->renderActive(null, null, null, 'post');
+
+if (empty($name)) {
+    // Do noting
+} elseif (empty($results)) {
+    echo _("No faces found");
+} else {
+    foreach ($results as $face_id => $face) {
+        include ANSEL_TEMPLATES . '/tile/face.inc';
+    }
+}
+
+require $registry->get('templates', 'horde') . '/common-footer.inc';
\ No newline at end of file
diff --git a/ansel/faces/search/image_define.php b/ansel/faces/search/image_define.php
new file mode 100644 (file)
index 0000000..c3ae534
--- /dev/null
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Process an single image (to be called by ajax)
+ *
+ * $Horde: ansel/faces/search/image_define.php,v 1.8 2009/07/08 18:28:41 slusarz Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Duck <duck@obala.net>
+ */
+require_once 'tabs.php';
+
+/* check if image exists */
+$tmp = Horde::getTempDir();
+$path = $tmp . '/search_face_' . Horde_Auth::getAuth() . $faces->getExtension();
+
+if (file_exists($path) !== true) {
+    $notification->push(_("You must upload the search photo first"));
+    header('Location: ' . Horde::applicationUrl('faces/search/image.php'));
+}
+
+$title = _("Create a new face");
+
+$x1 = 0;
+$y1 = 0;
+$x2 = 0;
+$y2 = 0;
+
+$faces = $faces->getFaces($path);
+if (is_a($faces, 'PEAR_Error')) {
+    exit;
+}
+
+if (count($faces) > 1) {
+    $notification->push(_("More then one face found in photo. Please note that you can search only one face at a time."));
+} elseif (empty($faces)) {
+    $notification->push(_("No faces found. Define you own."));
+} else {
+    $x1 = $faces[0]['x'];
+    $y1 = $faces[0]['y'];
+    $x2 = $faces[0]['x'] + $faces[0]['width'];
+    $y2 = $faces[0]['y'] + $faces[0]['height'];
+}
+
+$height = $x2 - $x1;
+$width = $y2 - $y1;
+
+Horde::addScriptFile('prototype.js', 'horde', true);
+Horde::addScriptFile('scriptaculous.js', 'horde', true);
+Horde::addScriptFile('builder.js', 'ansel', true);
+Horde::addScriptFile('cropper.js', 'ansel', true);
+Horde::addScriptFile('stripe.js', 'horde', true);
+
+require ANSEL_TEMPLATES . '/common-header.inc';
+require ANSEL_TEMPLATES . '/menu.inc';
+require ANSEL_TEMPLATES . '/faces/define.inc';
+
+require $registry->get('templates', 'horde') . '/common-footer.inc';
\ No newline at end of file
diff --git a/ansel/faces/search/image_save.php b/ansel/faces/search/image_save.php
new file mode 100644 (file)
index 0000000..cdc0528
--- /dev/null
@@ -0,0 +1,86 @@
+<?php
+/**
+ * Process an single image
+ *
+ * $Horde: ansel/faces/search/image_save.php,v 1.12 2009/07/08 18:28:41 slusarz Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Duck <duck@obala.net>
+ */
+require_once 'tabs.php';
+require_once 'Horde/Image.php';
+
+/* Check if image exists. */
+$tmp = Horde::getTempDir();
+$path = $tmp . '/search_face_' . Horde_Auth::getAuth() . $faces->getExtension();
+
+if (!file_exists($path)) {
+    $notification->push(_("You must upload the search photo first"));
+    header('Location: ' . Horde::applicationUrl('faces/search/image.php'));
+}
+
+$x1 = (int)Horde_Util::getFormData('x1');
+$y1 = (int)Horde_Util::getFormData('y1');
+$x2 = (int)Horde_Util::getFormData('x2');
+$y2 = (int)Horde_Util::getFormData('y2');
+
+if ($x2 - $x1 < 50 || $y2 - $y1 < 50) {
+    $notification->push(_("Photo is too small. Search photo must be at least 50x50 pixels."));
+    header('Location: ' . Horde::applicationUrl('faces/search/image.php'));
+    exit;
+}
+
+/* Create Horde_Image driver. */
+$img = Ansel::getImageObject();
+$driver = empty($conf['image']['convert']) ? 'Gd' : 'Im';
+$result = $img->loadFile($path);
+if (is_a($result, 'PEAR_Error')) {
+    $notification->push($result->getMessage());
+    header('Location: ' . Horde::applicationUrl('faces/search/image.php'));
+    exit;
+}
+
+/* Crop image. */
+$result = $img->crop($x1, $y1, $x2, $y2);
+if (is_a($result, 'PEAR_Error')) {
+    $notification->push($result->getMessage());
+    header('Location: ' . Horde::applicationUrl('faces/search/image.php'));
+    exit;
+}
+
+/* Resize image. */
+$img->getDimensions();
+if ($img->_width >= 50) {
+    $img->resize(min(50, $img->_width), min(50, $img->_height), true);
+}
+
+/* Save image. */
+$path = $tmp . '/search_face_thumb_' . Horde_Auth::getAuth() . $faces->getExtension();
+if (!file_put_contents($path, $img->raw())) {
+    $notification->push(_("Cannot store search photo"));
+    header('Location: ' . Horde::applicationUrl('faces/search/image.php'));
+    exit;
+}
+
+/* Get original signature. */
+$signature = $faces->getSignatureFromFile($path);
+if (empty($signature)) {
+    $notification->push(_("Cannot read photo signature"));
+    header('Location: ' . Horde::applicationUrl('faces/search/image.php'));
+    exit;
+}
+
+/* Save signature. */
+$path = $tmp . '/search_face_' . Horde_Auth::getAuth() . '.sig';
+if (file_put_contents($path, $signature)) {
+    header('Location: ' . Horde::applicationUrl('faces/search/image.php'));
+    exit;
+}
+
+$notification->push(_("Cannot save photo signature"));
+header('Location: ' . Horde::applicationUrl('faces/search/image.php'));
+exit;
\ No newline at end of file
diff --git a/ansel/faces/search/image_search.php b/ansel/faces/search/image_search.php
new file mode 100644 (file)
index 0000000..262de19
--- /dev/null
@@ -0,0 +1,54 @@
+<?php
+/**
+ * Image search
+ *
+ * $Horde: ansel/faces/search/image_search.php,v 1.6 2009/07/08 18:28:41 slusarz Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see /var/www/www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Duck <duck@obala.net>
+ */
+require_once 'tabs.php';
+require_once 'Horde/UI/Pager.php';
+
+$page = Horde_Util::getFormData('page', 0);
+$perpage = $prefs->getValue('facesperpage');
+
+if (($face_id = Horde_Util::getGet('face_id')) !== null) {
+    $face = $faces->getFaceById($face_id, true);
+    if (is_a($face, 'PEAR_Error')) {
+        $notification->push($face->getMessage());
+        header('Location: ' . Horde::applicationUrl('faces/search/image.php'));
+    }
+    $signature = $face['face_signature'];
+    $results = $faces->getSignatureMatches($signature, $face_id, $perpage * $page, $perpage);
+} else {
+    $tmp = Horde::getTempDir();
+    $path = $tmp . '/search_face_' . Horde_Auth::getAuth() . '.sig';
+    if (file_exists($path) !== true) {
+        $notification->push(_("You must upload the search photo first"));
+        header('Location: ' . Horde::applicationUrl('faces/search/image.php'));
+    }
+    $signature = file_get_contents($path);
+    $results = $faces->getSignatureMatches($signature, 0, $perpage * $page, $perpage);
+}
+if (is_a($results, 'PEAR_Error')) {
+    $notification->push($results);
+    $results = array();
+}
+
+$title = _("Photo search");
+$vars = Horde_Variables::getDefaultVariables();
+$pager = new Horde_UI_Pager(
+    'page', $vars,
+    array('num' => count($results),
+          'url' => 'faces/search/image_search.php',
+          'perpage' => $perpage));
+
+require ANSEL_TEMPLATES . '/common-header.inc';
+require ANSEL_TEMPLATES . '/menu.inc';
+require ANSEL_TEMPLATES . '/faces/search.inc';
+require $registry->get('templates', 'horde') . '/common-footer.inc';
\ No newline at end of file
diff --git a/ansel/faces/search/img.php b/ansel/faces/search/img.php
new file mode 100644 (file)
index 0000000..b3baf08
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+/**
+ * Process an single image (to be called by ajax)
+ *
+ * $Horde: ansel/faces/search/img.php,v 1.8 2009/07/08 18:28:41 slusarz Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Duck <duck@obala.net>
+ */
+require_once dirname(__FILE__) . '/../../lib/base.php';
+require_once ANSEL_BASE . '/lib/Faces.php';
+
+/* Face search is allowd only to  */
+if (!Horde_Auth::isauthenticated()) {
+    exit;
+}
+
+$thumb = Horde_Util::getGet('thumb');
+$tmp = Horde::getTempDir();
+$path = $tmp . '/search_face_' . ($thumb ? 'thumb_' : '') .  Horde_Auth::getAuth() . Ansel_Faces::getExtension();
+
+header('Content-type: image/' . $conf['image']['type']);
+readfile($path);
\ No newline at end of file
diff --git a/ansel/faces/search/name.php b/ansel/faces/search/name.php
new file mode 100644 (file)
index 0000000..54bebd5
--- /dev/null
@@ -0,0 +1,53 @@
+<?php
+/**
+ * Process an single image (to be called by ajax)
+ *
+ * $Horde: ansel/faces/search/name.php,v 1.4 2009/06/10 00:33:02 mrubinsk Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Duck <duck@obala.net>
+ */
+require_once 'tabs.php';
+require_once 'Horde/Form.php';
+require_once 'Horde/UI/Pager.php';
+
+/* Search from */
+$form = new Horde_Form($vars);
+$form->addVariable(_("Face name to search"), 'face_name', 'text', true);
+$form->setButtons(_("Search"));
+
+$page = Horde_Util::getFormData('page', 0);
+$perpage = $prefs->getValue('facesperpage');
+
+$name = Horde_Util::getFormData('face_name');
+if (!empty($name)) {
+    $page = Horde_Util::getFormData('page', 0);
+    $perpage = $prefs->getValue('faceperpage');
+    $count = $faces->countSearchFaces($name);
+    if ($count) {
+        $results = $faces->searchFaces($name, $page * $perpage, $perpage);
+    }
+} else {
+    $page = 0;
+    $perpage = 0;
+    $count = 0;
+}
+
+$vars = Horde_Variables::getDefaultVariables();
+$pager = new Horde_UI_Pager(
+    'page', $vars,
+    array('num' => $count,
+            'url' => 'faces/search/name.php',
+            'perpage' => $perpage));
+
+$title = _("Search by name");
+require ANSEL_TEMPLATES . '/common-header.inc';
+require ANSEL_TEMPLATES . '/menu.inc';
+
+include ANSEL_TEMPLATES . '/faces/faces.inc';
+
+require $registry->get('templates', 'horde') . '/common-footer.inc';
\ No newline at end of file
diff --git a/ansel/faces/search/named.php b/ansel/faces/search/named.php
new file mode 100644 (file)
index 0000000..2085a91
--- /dev/null
@@ -0,0 +1,39 @@
+<?php
+/**
+ * Show all named faces.
+ *
+ * $Horde: ansel/faces/search/named.php,v 1.5 2009/06/10 00:33:02 mrubinsk Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Duck <duck@obala.net>
+ */
+require_once 'tabs.php';
+require_once 'Horde/UI/Pager.php';
+
+$title = _("Named faces");
+$page = Horde_Util::getFormData('page', 0);
+$perpage = $prefs->getValue('facesperpage');
+$results = array();
+$count = $faces->countNamedFaces();
+if (is_a($count, 'PEAR_Error')) {
+    $notification->push($count->getDebugInfo());
+    $count = 0;
+} elseif ($count > 0) {
+    $results = $faces->namedFaces($page * $perpage, $perpage);
+}
+
+$vars = Horde_Variables::getDefaultVariables();
+$pager = new Horde_UI_Pager(
+    'page', $vars,
+    array('num' => $count,
+          'url' => 'faces/search/named.php',
+          'perpage' => $perpage));
+
+require ANSEL_TEMPLATES . '/common-header.inc';
+require ANSEL_TEMPLATES . '/menu.inc';
+include ANSEL_TEMPLATES . '/faces/faces.inc';
+require $registry->get('templates', 'horde') . '/common-footer.inc';
diff --git a/ansel/faces/search/owner.php b/ansel/faces/search/owner.php
new file mode 100644 (file)
index 0000000..86a79c8
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+/**
+ * Process an single image (to be called by ajax)
+ *
+ * $Horde: ansel/faces/search/owner.php,v 1.6 2009/07/08 18:28:41 slusarz Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Duck <duck@obala.net>
+ */
+require_once 'tabs.php';
+require_once 'Horde/UI/Pager.php';
+
+$page = Horde_Util::getFormData('page', 0);
+$perpage = $prefs->getValue('facesperpage');
+$owner = Horde_Util::getGet('owner', Horde_Auth::getAuth());
+if ($owner == Horde_Auth::getAuth()) {
+    $title = _("From my galleries");
+} else {
+    $title = sprintf(_("From galleries of %s"));
+}
+
+$count = $faces->countOwnerFaces($owner);
+if (is_a($count, 'PEAR_Error')) {
+    $notification->push($count);
+    $results = array();
+    $count = 0;
+} else {
+    $results = $faces->ownerFaces($owner, $page * $perpage, $perpage);
+}
+
+$vars = Horde_Variables::getDefaultVariables();
+$pager = new Horde_UI_Pager(
+    'page', $vars,
+    array('num' => $count,
+            'url' => 'faces/search/owner.php',
+            'perpage' => $perpage));
+$pager->preserve('owner', $owner);
+
+require ANSEL_TEMPLATES . '/common-header.inc';
+require ANSEL_TEMPLATES . '/menu.inc';
+
+include ANSEL_TEMPLATES . '/faces/faces.inc';
+
+require $registry->get('templates', 'horde') . '/common-footer.inc';
\ No newline at end of file
diff --git a/ansel/faces/search/tabs.php b/ansel/faces/search/tabs.php
new file mode 100644 (file)
index 0000000..1618417
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+/**
+ * Process an single photo (to be called by ajax)
+ *
+ * $Horde: ansel/faces/search/tabs.php,v 1.10 2009/07/22 21:08:09 mrubinsk Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Duck <duck@obala.net>
+ */
+require_once dirname(__FILE__) . '/../../lib/base.php';
+require_once ANSEL_BASE . '/lib/Faces.php';
+require_once 'Horde/UI/Tabs.php';
+
+$faces = Ansel_Faces::factory();
+if (is_a($faces, 'PEAR_Error')) {
+    die($faces->getMessage());
+}
+
+/* Face search is allowed only to authenticated users */
+if (!Horde_Auth::isauthenticated()) {
+    Horde_Auth::authenticationFailureRedirect();
+}
+
+/* Show tabs */
+$vars = Horde_Variables::getDefaultVariables();
+$tabs = new Horde_UI_Tabs('search_faces', $vars);
+$tabs->addTab(_("All faces"), Horde::applicationUrl('faces/search/all.php'), 'all');
+$tabs->addTab(_("From my galleries"), Horde::applicationUrl('faces/search/owner.php'), 'owner');
+$tabs->addTab(_("Named faces"), Horde::applicationUrl('faces/search/named.php'), 'named');
+$tabs->addTab(_("Search by name"), Horde::applicationUrl('faces/search/name.php'), 'name');
+if ($conf['faces']['search']) {
+    $tabs->addTab(_("Search by photo"), Horde::applicationUrl('faces/search/image.php'), 'image');
+}
diff --git a/ansel/gallery.php b/ansel/gallery.php
new file mode 100644 (file)
index 0000000..6656f72
--- /dev/null
@@ -0,0 +1,395 @@
+<?php
+/**
+ * $Horde: ansel/gallery.php,v 1.148 2009/07/30 18:02:13 mrubinsk Exp $
+ *
+ * Copyright 2001-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ */
+
+require_once dirname(__FILE__) . '/lib/base.php';
+require_once 'Horde/Prefs/CategoryManager.php';
+
+// Redirect to the gallery list if no action has been requested.
+$actionID = Horde_Util::getFormData('actionID');
+if (is_null($actionID)) {
+    header('Location: ' . Horde::applicationUrl('view.php?view=List', true));
+    exit;
+}
+
+// Run through the action handlers.
+switch ($actionID) {
+case 'add':
+    // Set up the gallery attributes.
+    $gallery_name = '';
+    $gallery_desc = '';
+    $gallery_category = $prefs->getValue('default_category');
+    $gallery_tags = '';
+    $gallery_thumbstyle = '';
+    $gallery_slug = '';
+    $gallery_age = 0;
+    $gallery_download = $prefs->getValue('default_download');
+    $gallery_parent = null;
+    $galleryId = null;
+    $gallery_mode = 'Normal';
+    $gallery_passwd = '';
+
+    $notification->push('document.gallery.gallery_name.focus();', 'javascript');
+
+    $title = _("Adding A New Gallery");
+    break;
+
+case 'addchild':
+    // Get the parent and make sure that it exists and that we have
+    // permissions to add to it.
+    $parentId = Horde_Util::getFormData('gallery');
+    $parent = $ansel_storage->getGallery($parentId);
+    if (is_a($parent, 'PEAR_Error')) {
+        $notification->push($parent->getMessage(), 'horde.error');
+        header('Location: ' . Horde::applicationUrl('view.php?view=List', true));
+        exit;
+    } elseif (!$parent->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+        $notification->push(
+            sprintf(_("Access denied adding a gallery to \"%s\"."),
+                    $parent->get('name')), 'horde.error');
+        header('Location: ' . Horde::applicationUrl('view.php?view=List', true));
+        exit;
+    }
+
+    // Set up the gallery attributes.
+    $gallery_name = '';
+    $gallery_desc = '';
+    $gallery_category = $prefs->getValue('default_category');
+    $gallery_tags = '';
+    $gallery_slug = '';
+    $gallery_age = 0;
+    $gallery_thumbstyle = $parent->get('style');
+    $gallery_download = $prefs->getValue('default_download');
+    $gallery_parent = $parentId;
+    $galleryId = null;
+    $gallery_mode = 'Normal';
+    $gallery_passwd = '';
+
+    $notification->push('document.gallery.gallery_name.focus();', 'javascript');
+
+    $title = sprintf(_("Adding A Subgallery to %s"), $parent->get('name'));
+    break;
+
+case 'downloadzip':
+    $galleryId = Horde_Util::getFormData('gallery');
+    $gallery = $ansel_storage->getGallery($galleryId);
+    if (!Horde_Auth::getAuth() || is_a($gallery, 'PEAR_Error') ||
+        !$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_READ)) {
+
+        $name = is_a($gallery, 'PEAR_Error')
+                     ? $galleryId
+                     : $gallery->get('name');
+        $notification->push(sprintf(_("Access denied downloading photos from \"%s\"."), $name),
+                            'horde.error');
+        header('Location: ' . Horde::applicationUrl('view.php?view=List', true));
+        exit;
+    }
+
+    Ansel::downloadImagesAsZip($gallery);
+    exit;
+
+case 'modify':
+    $galleryId = Horde_Util::getFormData('gallery');
+    $gallery = $ansel_storage->getGallery($galleryId);
+    if (!is_a($gallery, 'PEAR_Error')) {
+        // Set up the gallery attributes.
+        $gallery_name = $gallery->get('name');
+        $gallery_desc = $gallery->get('desc');
+        $gallery_category = $gallery->get('category');
+        $gallery_tags = implode(',', $gallery->getTags());
+        $gallery_thumbstyle = $gallery->get('style');
+        $gallery_slug = $gallery->get('slug');
+        $gallery_age = (int)$gallery->get('age');
+        $gallery_download = $gallery->get('download');
+        $title = sprintf(_("Modifying: %s"), $gallery_name);
+        $gallery_parent = $gallery->getParent();
+        if (!is_null($gallery_parent)) {
+            $gallery_parent = $gallery_parent->getId();
+        }
+        $gallery_mode = $gallery->get('view_mode');
+        $gallery_passwd = $gallery->get('passwd');
+    } else {
+        $title = _("Unknown gallery");
+    }
+    break;
+
+case 'save':
+    // Check general permissions.
+    if (!Horde_Auth::isAdmin() &&
+        ($GLOBALS['perms']->exists('ansel') &&
+         !$GLOBALS['perms']->hasPermission('ansel', Horde_Auth::getAuth(), PERMS_EDIT))) {
+        $notification->push(_("Access denied editing galleries."), 'horde.error');
+        header('Location: ' . Horde::applicationUrl('view.php?view=List', true));
+        exit;
+    }
+
+    // Get the form values.
+    $galleryId = Horde_Util::getFormData('gallery');
+    $gallery_name = Horde_Util::getFormData('gallery_name');
+    $gallery_desc = Horde_Util::getFormData('gallery_desc');
+    $gallery_slug = Horde_Util::getFormData('gallery_slug');
+    $gallery_age = (int)Horde_Util::getFormData('gallery_age', 0);
+    $gallery_download = Horde_Util::getFormData('gallery_download');
+    $gallery_mode = Horde_Util::getFormData('view_mode', 'Normal');
+    $gallery_passwd = Horde_Util::getFormData('gallery_passwd');
+    if ($new_category = Horde_Util::getFormData('new_category')) {
+        $cManager = new Prefs_CategoryManager();
+        $new_category = $cManager->add($new_category);
+        if ($new_category) {
+            $gallery_category = $new_category;
+        }
+    } else {
+        $gallery_category = Horde_Util::getFormData('gallery_category');
+    }
+
+    $gallery_tags = Horde_Util::getFormData('gallery_tags');
+    $gallery_thumbstyle = Horde_Util::getFormData('gallery_style');
+    $gallery_parent = Horde_Util::getFormData('gallery_parent');
+    // Double check for an empty string instead of null
+    if (empty($gallery_parent)) {
+        $gallery_parent = null;
+    }
+    if ($galleryId &&
+        ($exists = $ansel_storage->galleryExists($galleryId)) === true) {
+
+        // Modifying an existing gallery.
+        $gallery = $ansel_storage->getGallery($galleryId);
+        if (is_a($gallery, 'PEAR_Error') ||
+            !$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+
+            $name = is_a($gallery, 'PEAR_Error')
+                    ? $galleryId
+                    : $gallery->get('name');
+
+            $notification->push(sprintf(_("Access denied saving gallery \"%s\"."),
+                                        $name), 'horde.error');
+        } else {
+            // Don't allow the display name to be nulled out.
+            if ($gallery_name) {
+                $gallery->set('name', $gallery_name);
+            }
+
+            $gallery->set('desc', $gallery_desc);
+            $gallery->set('category', $gallery_category);
+            $gallery->setTags(explode(',', $gallery_tags));
+            $gallery->set('style', $gallery_thumbstyle);
+            $gallery->set('slug', $gallery_slug);
+            $gallery->set('age', $gallery_age);
+            $gallery->set('download', $gallery_download);
+            $gallery->set('view_mode', $gallery_mode);
+            if ($gallery->get('owner') == Horde_Auth::getAuth()) {
+                $gallery->set('passwd', $gallery_passwd);
+            }
+
+            // Did the parent change?
+            $old_parent = $gallery->getParent();
+            if (!is_null($old_parent)) {
+                $old_parent_id = $old_parent->getId();
+            } else {
+                $old_parent_id = null;
+            }
+            if ($gallery_parent != $old_parent_id) {
+                if (!is_null($gallery_parent)) {
+                    $new_parent = $ansel_storage->getGallery($gallery_parent);
+                    if (is_a($new_parent, 'PEAR_Error')) {
+                        return $new_parent;
+                    }
+                } else {
+                    $new_parent = null;
+                }
+                $result = $gallery->setParent($new_parent);
+                if (is_a($result, 'PEAR_Error')) {
+                    $notification->push($result->getMessage(), 'horde.error');
+                    header('Location: ' . Horde::applicationUrl(
+                        Ansel::getUrlFor('view', array('view' => 'List'), true)));
+                    exit;
+                }
+            }
+            $result = $gallery->save();
+            if (is_a($result, 'PEAR_Error')) {
+                $notification->push($result->getMessage(), 'horde.error');
+            } else {
+                $notification->push(_("The gallery was saved."),
+                                    'horde.success');
+            }
+        }
+    } else {
+        // Is this a new subgallery?
+        if ($gallery_parent) {
+            $parent = $ansel_storage->getGallery($gallery_parent);
+            if (is_a($parent, 'PEAR_Error')) {
+                $notification->push($parent->getMessage(), 'horde.error');
+                header('Location: ' . Horde::applicationUrl(
+                    Ansel::getUrlFor('view', array('view' => 'List'), true)));
+                exit;
+            } elseif (!$parent->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+                $notification->push(sprintf(
+                    _("You do not have permission to add children to %s."),
+                    $parent->get('name')), 'horde.error');
+
+                header('Location: ' . Horde::applicationUrl(
+                    Ansel::getUrlFor('view', array('view' => 'List'), true)));
+                exit;
+            }
+        }
+
+        // Require a display name.
+        if (!$gallery_name) {
+            $notification->push(
+                _("You must provide a display name for your new gallery."),
+                'horde.warning');
+            $actionId = 'add';
+            $title = _("Adding A New Gallery");
+            break;
+        }
+
+        // Create the new gallery.
+        $perm = (!empty($parent)) ? $parent->getPermission() : null;
+        $parent = (!empty($gallery_parent)) ? $gallery_parent : null;
+
+        $gallery = $ansel_storage->createGallery(array('name' => $gallery_name,
+                                              'desc' => $gallery_desc,
+                                              'category' => $gallery_category,
+                                              'tags' => explode(',', $gallery_tags),
+                                              'style' => $gallery_thumbstyle,
+                                              'slug' => $gallery_slug,
+                                              'age' => $gallery_age,
+                                              'download' => $gallery_download,
+                                              'view_mode' => $gallery_mode,
+                                              'passwd' => $gallery_passwd,
+                                              ), $perm, $parent);
+        if (is_a($gallery, 'PEAR_Error')) {
+            $galleryId = null;
+            $error = sprintf(_("The gallery \"%s\" couldn't be created: %s"),
+                             $gallery_name, $gallery->getMessage());
+            Horde::logMessage($error, __FILE__, __LINE__, PEAR_LOG_ERR);
+            $notification->push($error, 'horde.error');
+        } else {
+            $galleryId = $gallery->getId();
+            $msg = sprintf(_("The gallery \"%s\" was created successfully."),
+                           $gallery_name);
+            Horde::logMessage($msg, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+            $notification->push($msg, 'horde.success');
+        }
+    }
+
+    // Clear the OtherGalleries widget cache
+    if ($GLOBALS['conf']['ansel_cache']['usecache']) {
+        $GLOBALS['cache']->expire('Ansel_OtherGalleries' . $gallery->get('owner'));
+    }
+    // Return to the last view.
+    $url = Horde_Util::getFormData('url');
+    if (empty($url) && empty($exists) && !is_a($gallery, 'PEAR_Error')) {
+        // Redirect to the images upload page for newly creted galleries
+        $url = Horde_Util::addParameter(Horde::applicationUrl('img/upload.php'),
+                                  'gallery', $galleryId);
+    } elseif (empty($url)) {
+        $url = Horde::applicationUrl('index.php', true);
+    }
+    header('Location: ' . $url);
+    exit;
+
+case 'delete':
+case 'empty':
+    // Print the confirmation screen.
+    $galleryId = Horde_Util::getFormData('gallery');
+    if ($galleryId) {
+        $gallery = $ansel_storage->getGallery($galleryId);
+        if (is_a($gallery, 'PEAR_Error')) {
+            $notification->push($gallery->getMessage(), 'horde.error');
+        } else {
+            require ANSEL_TEMPLATES . '/common-header.inc';
+            require ANSEL_TEMPLATES . '/menu.inc';
+            require ANSEL_TEMPLATES . '/gallery/delete_confirmation.inc';
+            require $registry->get('templates', 'horde') . '/common-footer.inc';
+            exit;
+        }
+    }
+
+    // Return to the gallery list.
+    header('Location: ' . Horde::applicationUrl(
+        Ansel::getUrlFor('view', array('view' => 'List'), true)));
+    exit;
+
+case 'generateDefault':
+    // Re-generate the default pretty gallery image.
+    $galleryId = Horde_Util::getFormData('gallery');
+    $gallery = $ansel_storage->getGallery($galleryId);
+    if (is_a($gallery, 'PEAR_Error')) {
+        $notification->push($gallery->getMessage(), 'horde.error');
+        header('Location: ' . Horde::applicationUrl('index.php', true));
+        exit;
+    }
+    $gallery->clearStacks();
+    $notification->push(
+        _("The gallery's default photo has successfully been reset."),
+        'horde.success');
+    $url = Horde_Util::addParameter('view.php', 'gallery', $galleryId);
+    header('Location: ' . Horde::applicationUrl($url, true));
+    exit;
+
+case 'generateThumbs':
+    // Re-generate all of this gallery's prettythumbs.
+    $galleryId = Horde_Util::getFormData('gallery');
+    $gallery = $ansel_storage->getGallery($galleryId);
+    if (is_a($gallery, 'PEAR_Error')) {
+        $notification->push($gallery->getMessage(), 'horde.error');
+        header('Location: ' . Horde::applicationUrl('index.php', true));
+        exit;
+    }
+    $gallery->clearThumbs();
+    $notification->push(
+        _("The gallery's thumbnails have successfully been reset."),
+        'horde.success');
+    $url = Horde_Util::addParameter('view.php', 'gallery', $galleryId);
+    header('Location: ' . Horde::applicationUrl($url, true));
+    exit;
+
+case 'deleteCache':
+    // Delete all cached image views.  This will NOT immediately regenerate
+    // all views that existed prior to this action.  However it will remove all
+    // cached views (leaving the originals) which will be generated on demand
+    // by users browsing the site.  Note that the first time each image is
+    // viewed there will be a delay as the view is generated and cached.
+    // This can be useful when changing the configured "screen" size in Ansel's
+    // configuration.
+    $galleryId = Horde_Util::getFormData('gallery');
+    $gallery = $ansel_storage->getGallery($galleryId);
+    if (is_a($gallery, 'PEAR_Error')) {
+        $notification->push($gallery->getMessage(), 'horde.error');
+        header('Location: ' . Horde::applicationUrl('index.php', true));
+        exit;
+    }
+    $gallery->clearViews();
+    $notification->push(
+        _("The gallery's views have successfully been reset."),
+        'horde.success');
+    $url = Horde_Util::addParameter('view.php', 'gallery', $galleryId);
+    header('Location: ' . Horde::applicationUrl($url, true));
+    exit;
+
+default:
+    header('Location: ' . Horde::applicationUrl(
+        Ansel::getUrlFor('view', array('view' => 'List'), true)));
+    exit;
+}
+
+Horde::addScriptFile('stripe.js', 'horde', true);
+require ANSEL_TEMPLATES . '/common-header.inc';
+
+/* Attach the slug check action to the form */
+$imple = Horde_Ajax_Imple::factory(array('ansel', 'GallerySlugCheck'),
+                                   array('slug' => $gallery_slug,
+                                         'bindTo' => 'gallery_slug'));
+$imple->attach();
+require ANSEL_TEMPLATES . '/menu.inc';
+require ANSEL_TEMPLATES . '/gallery/gallery.inc';
+require $registry->get('templates', 'horde') . '/common-footer.inc';
diff --git a/ansel/gallery/captions.php b/ansel/gallery/captions.php
new file mode 100644 (file)
index 0000000..585c994
--- /dev/null
@@ -0,0 +1,69 @@
+<?php
+/**
+ * $Horde: ansel/gallery/captions.php,v 1.19 2009/07/08 18:28:42 slusarz Exp $
+ *
+ * Copyright 2001-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ */
+
+@define('ANSEL_BASE', dirname(__FILE__) . '/..');
+require_once ANSEL_BASE . '/lib/base.php';
+
+$galleryId = Horde_Util::getFormData('gallery');
+if (!$galleryId) {
+    header('Location: ' . Ansel::getUrlFor('view', array('view' => 'List'),
+                                           true));
+    exit;
+}
+
+$gallery = $ansel_storage->getGallery($galleryId);
+if (is_a($gallery, 'PEAR_Error')) {
+    $notification->push(sprintf(_("Error accessing %s: %s"), $galleryId, $gallery->getMessage()), 'horde.error');
+    header('Location: ' . Ansel::getUrlFor('view', array('view' => 'List'),
+                                           true));
+    exit;
+}
+
+if (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+    $notification->push(sprintf(_("Access denied setting captions for %s."), $gallery->get('name')), 'horde.error');
+    header('Location: ' . Ansel::getUrlFor('view', array('view' => 'List'),
+                                           true));
+    exit;
+}
+
+/* We might be browsing by date */
+$date = Ansel::getDateParameter();
+$gallery->setDate($date);
+
+/* Run through the action handlers. */
+$do = Horde_Util::getFormData('do');
+switch ($do) {
+case 'save':
+    /* Save a batch of captions. */
+    $images = $gallery->getImages();
+    foreach ($images as $image) {
+        if (($caption = Horde_Util::getFormData('img' . $image->id)) !== null) {
+            $image->caption = $caption;
+            $image->save();
+        }
+    }
+
+    $notification->push(_("Captions Saved."), 'horde.success');
+    $style = $gallery->getStyle();
+    header('Location: ' . Ansel::getUrlFor('view', array_merge(
+                                           array('gallery' => $galleryId,
+                                                 'slug' => $gallery->get('slug'),
+                                                 'view' => 'Gallery'),
+                                           $date), true));
+    exit;
+}
+
+$title = _("Caption Editor");
+require ANSEL_TEMPLATES . '/common-header.inc';
+require ANSEL_TEMPLATES . '/menu.inc';
+require ANSEL_TEMPLATES . '/captions/captions.inc';
+require $registry->get('templates', 'horde') . '/common-footer.inc';
diff --git a/ansel/gallery/delete.php b/ansel/gallery/delete.php
new file mode 100644 (file)
index 0000000..243e8b4
--- /dev/null
@@ -0,0 +1,72 @@
+<?php
+/**
+ * $Horde: ansel/gallery/delete.php,v 1.23 2009/07/08 18:28:42 slusarz Exp $
+ *
+ * Copyright 2001-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ */
+
+@define('ANSEL_BASE', dirname(__FILE__) . '/..');
+require_once ANSEL_BASE . '/lib/base.php';
+
+// Delete/empty the gallery if we're provided with a valid galleryId.
+$actionID = Horde_Util::getPost('action');
+$galleryId = Horde_Util::getPost('gallery');
+
+if ($galleryId) {
+    $gallery = $ansel_storage->getGallery($galleryId);
+
+    switch ($actionID) {
+    case 'delete':
+        if (is_a($gallery, 'PEAR_Error')) {
+            $notification->push($gallery->getMessage(), 'horde.error');
+        } elseif (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_DELETE)) {
+            $notification->push(sprintf(_("Access denied deleting gallery \"%s\"."),
+                                        $gallery->get('name')), 'horde.error');
+        } else {
+            $result = $ansel_storage->removeGallery($gallery);
+            if (is_a($result, 'PEAR_Error')) {
+                $notification->push(sprintf(
+                    _("There was a problem deleting %s: %s"),
+                    $gallery->get('name'), $result->getMessage()),
+                    'horde.error');
+            } else {
+                $notification->push(sprintf(
+                    _("Successfully deleted %s."),
+                    $gallery->get('name')), 'horde.success');
+            }
+        }
+
+        // Clear the OtherGalleries widget cache
+        if ($GLOBALS['conf']['ansel_cache']['usecache']) {
+            $GLOBALS['cache']->expire('Ansel_OtherGalleries' . $gallery->get('owner'));
+        }
+
+        // Return to the default view.
+        header('Location: ' . Ansel::getUrlFor('default_view', array()));
+        exit;
+
+    case 'empty':
+        if (is_a($gallery, 'PEAR_Error')) {
+            $notification->push($gallery->getMessage(), 'horde.error');
+        } elseif (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_DELETE)) {
+            $notification->push(sprintf(_("Access denied deleting gallery \"%s\"."),
+                                        $gallery->get('name')),
+                                'horde.error');
+        } else {
+            $ansel_storage->emptyGallery($gallery);
+            $notification->push(sprintf(_("Successfully emptied \"%s\""), $gallery->get('name')));
+        }
+        header('Location: '
+               . Ansel::getUrlFor('view',
+                                  array('view' => 'Gallery',
+                                        'gallery' => $galleryId,
+                                        'slug' => $gallery->get('slug')),
+                                  true));
+        exit;
+    }
+}
\ No newline at end of file
diff --git a/ansel/gallery/index.php b/ansel/gallery/index.php
new file mode 100755 (executable)
index 0000000..e69de29
diff --git a/ansel/gallery/sort.php b/ansel/gallery/sort.php
new file mode 100644 (file)
index 0000000..2187c29
--- /dev/null
@@ -0,0 +1,98 @@
+<?php
+/**
+ * $Horde: ansel/gallery/sort.php,v 1.31 2009/07/13 17:18:39 mrubinsk Exp $
+ *
+ * Copyright 2001-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ */
+
+@define('ANSEL_BASE', dirname(__FILE__) . '/..');
+require_once ANSEL_BASE . '/lib/base.php';
+
+/* If we aren't provided with a gallery, redirect to the gallery
+ * list. */
+$galleryId = Horde_Util::getFormData('gallery');
+if (!isset($galleryId)) {
+    header('Location: ' . Ansel::getUrlFor('view', array('view' => 'List'),
+                                           true));
+    exit;
+}
+
+$gallery = $ansel_storage->getGallery($galleryId);
+if (is_a($gallery, 'PEAR_Error')) {
+    $notification->push(_("There was an error accessing the gallery."), 'horde.error');
+    header('Location: ' . Ansel::getUrlFor('view', array('view' => 'List'),
+                                           true));
+    exit;
+} elseif (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+    $notification->push(sprintf(_("Access denied editing gallery \"%s\"."), $gallery->get('name')), 'horde.error');
+    header('Location: ' . Ansel::getUrlFor('view', array('view' => 'List'),
+                                           true));
+    exit;
+}
+$style = $gallery->getStyle();
+$date = Ansel::getDateParameter();
+$gallery->setDate($date);
+
+switch (Horde_Util::getPost('action')) {
+case 'Sort':
+    parse_str(Horde_Util::getPost('order'), $order);
+    $order = $order['order'];
+    foreach ($order as $pos => $id) {
+        $gallery->setImageOrder($id, $pos);
+    }
+
+    $notification->push(_("Gallery sorted."), 'horde.success');
+    $style = $gallery->getStyle();
+
+    header('Location: ' . Ansel::getUrlFor('view', array_merge(
+                                           array('view' => 'Gallery',
+                                                 'gallery' => $galleryId,
+                                                 'slug' => $gallery->get('slug')),
+                                           $date),
+                                           true));
+    exit;
+}
+
+Horde::addScriptFile('prototype.js', 'horde', true);
+Horde::addScriptFile('effects.js', 'horde', true);
+Horde::addScriptFile('dragdrop.js', 'horde', true);
+$title = sprintf(_("%s :: Sort"), $gallery->get('name'));
+require ANSEL_TEMPLATES . '/common-header.inc';
+require ANSEL_TEMPLATES . '/menu.inc';
+?>
+<h1 class="header"><?php echo htmlspecialchars($title, ENT_COMPAT, Horde_Nls::getCharset()) ?></h1>
+<div class="instructions">
+ <form action="sort.php" method="post">
+  <?php echo Horde_Util::formInput() ?>
+  <input type="hidden" name="gallery" value="<?php echo (int)$galleryId ?>" />
+  <input type="hidden" name="action" value="Sort" />
+  <input type="hidden" name="order" id="order" />
+  <input type="hidden" name="year" value="<?php echo $date['year'] ?>" />
+  <input type="hidden" name="month" value="<?php echo $date['month'] ?>" />
+  <input type="hidden" name="day" value="<?php echo $date['day'] ?>" />
+  <p>
+   <?php echo _("Drag photos to the desired sort position.") ?>
+   <input type="submit" onclick="$('order').value = Sortable.serialize('sortContainer', { name: 'order' });" class="button" value="<?php echo _("Done") ?>" />
+  </p>
+ </form>
+</div>
+
+<div id="sortContainer" style="background:<?php echo $style['background'] ?>">
+
+<?php
+$images = $gallery->getImages();
+foreach ($images as $image) {
+    $caption = empty($image->caption) ? htmlspecialchars($image->filename, ENT_COMPAT, Horde_Nls::getCharset()) : htmlspecialchars($image->caption, ENT_COMPAT, Horde_Nls::getCharset());
+    echo '<div id="o_' . (int)$image->id . '"><a title="'
+        . $caption . '" href="#">'
+        . '<img src="' . Ansel::getImageUrl($image->id, 'thumb', false, $style['name']) . '" alt="' . htmlspecialchars($image->filename) . '" />'
+        . '</a></div>';
+}
+echo '</div>';
+$notification->push('Sortable.create(\'sortContainer\', {tag: \'div\', overlap: \'horizontal\', constraint: false })', 'javascript');
+require $registry->get('templates', 'horde') . '/common-footer.inc';
diff --git a/ansel/group.php b/ansel/group.php
new file mode 100644 (file)
index 0000000..0d13ed9
--- /dev/null
@@ -0,0 +1,107 @@
+<?php
+/**
+ * $Horde: ansel/group.php,v 1.23 2009/06/10 00:33:01 mrubinsk Exp $
+ *
+ * Copyright 2005-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @author Ben Chavet <ben@horde.org>
+ */
+
+@define('ANSEL_BASE', dirname(__FILE__));
+require_once ANSEL_BASE . '/lib/base.php';
+
+// check for grouping
+$groupby = basename(Horde_Util::getFormData('groupby', $prefs->getValue('groupby')));
+
+// check for pref update
+$actionID = Horde_Util::getFormData('actionID');
+if ($actionID == 'groupby' &&
+    ($groupby == 'owner' || $groupby == 'category' || $groupby == 'none')) {
+    $prefs->setValue('groupby', $groupby);
+}
+
+// If we aren't supplied with a page number, default to page 0.
+$gbpage = Horde_Util::getFormData('gbpage', 0);
+$groups_perpage = $prefs->getValue('groupsperpage');
+
+switch ($groupby) {
+case 'category':
+    $num_groups = $ansel_storage->countCategories(PERMS_SHOW);
+    if (is_a($num_groups, 'PEAR_Error')) {
+        $notification->push($num_groups);
+        $num_groups = 0;
+        $groups = array();
+    } elseif ($num_groups) {
+        $groups = $ansel_storage->listCategories(PERMS_SHOW,
+                                                 $gbpage * $groups_perpage,
+                                                 $groups_perpage);
+    } else {
+        $groups = array();
+    }
+    break;
+
+case 'owner':
+    require_once 'Horde/Identity.php';
+    $num_groups = $ansel_storage->shares->countOwners(PERMS_SHOW, null,
+                                                      false);
+    if (is_a($num_groups, 'PEAR_Error')) {
+        $notification->push($num_groups);
+        $num_groups = 0;
+        $groups = array();
+    } elseif ($num_groups) {
+        $groups = $ansel_storage->shares->listOwners(PERMS_SHOW, null,
+                                                     false,
+                                                     $gbpage * $groups_perpage,
+                                                     $groups_perpage);
+    } else {
+        $groups = array();
+    }
+    break;
+
+default:
+    header('Location: ' . Ansel::getUrlFor('view',
+                                           array('view' => 'List',
+                                                 'groupby' => $groupby),
+                                           true));
+    exit;
+}
+
+// Set up pager.
+require_once 'Horde/UI/Pager.php';
+$vars = Horde_Variables::getDefaultVariables();
+$group_pager = new Horde_UI_Pager('gbpage', $vars,
+                                  array('num' => $num_groups,
+                                        'url' => 'group.php',
+                                        'perpage' => $groups_perpage));
+
+$min = $gbpage * $groups_perpage;
+$max = $min + $groups_perpage;
+if ($max > $num_groups) {
+    $max = $num_groups - $min;
+}
+$start = $min + 1;
+$end = min($num_groups, $min + $groups_perpage);
+$count = 0;
+$groupby_links = array();
+if ($groupby !== 'owner') {
+    $groupby_links[] = Horde::link(Ansel::getUrlFor('group', array('actionID' => 'groupby', 'groupby' => 'owner'))) . _("owner") . '</a>';
+} elseif ($groupby !== 'category') {
+    $groupby_links[] = Horde::link(Ansel::getUrlFor('group', array('actionID' => 'groupby', 'groupby' => 'category'))) . _("category") . '</a>';
+}
+if ($groupby !== 'none') {
+    $groupby_links[] = Horde::link(Ansel::getUrlFor('group', array('actionID' => 'groupby', 'groupby' => 'none'))) . _("none") . '</a>';
+}
+
+require ANSEL_TEMPLATES . '/common-header.inc';
+require ANSEL_TEMPLATES . '/menu.inc';
+require ANSEL_TEMPLATES . '/group/header.inc';
+foreach ($groups as $group) {
+    require ANSEL_TEMPLATES . '/group/' . $groupby . '.inc';
+}
+require ANSEL_TEMPLATES . '/group/footer.inc';
+require ANSEL_TEMPLATES . '/group/pager.inc';
+require $registry->get('templates', 'horde') . '/common-footer.inc';
diff --git a/ansel/image.php b/ansel/image.php
new file mode 100644 (file)
index 0000000..0a9692d
--- /dev/null
@@ -0,0 +1,779 @@
+<?php
+/**
+ * Responsible for making changes to image properties as well as making,
+ * previewing and saving changes to the image.
+ *
+ * $Horde: ansel/image.php,v 1.176 2009/07/08 18:28:40 slusarz Exp $
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ */
+
+@define('ANSEL_BASE', dirname(__FILE__));
+require_once ANSEL_BASE . '/lib/base.php';
+require_once 'Horde/Form/Renderer.php';
+
+/* Get all the form data */
+$actionID = Horde_Util::getFormData('actionID');
+$gallery_id = Horde_Util::getFormData('gallery');
+$image_id = Horde_Util::getFormData('image');
+$page = Horde_Util::getFormData('page', 0);
+$watermark_font = Horde_Util::getFormData('font');
+$watermark_halign = Horde_Util::getFormData('whalign');
+$watermark_valign = Horde_Util::getFormData('wvalign');
+$watermark = Horde_Util::getFormData('watermark', $prefs->getValue('watermark'));
+$date = Ansel::getDateParameter();
+
+/* Are we watermarking the image? */
+if ($watermark) {
+    require_once 'Horde/Identity.php';
+    $identity = &Identity::singleton();
+    $name = $identity->getValue('fullname');
+    if (empty($name)) {
+        $name = Horde_Auth::getAuth();
+    }
+
+    // Set up array of possible substitutions.
+    $watermark_array = array('%N' => $name,            // User's fullname.
+                             '%L' => Horde_Auth::getAuth()); // User login.
+    $watermark = str_replace(array_keys($watermark_array),
+                             array_values($watermark_array), $watermark);
+    $watermark = strftime($watermark);
+}
+
+/* See if any tags were passed in to add (when js not present) */
+$tags = Horde_Util::getFormData('addtag');
+
+/* Redirect to the image list if no other action has been requested. */
+if (is_null($actionID) && is_null($tags)) {
+    header('Location: ' . Ansel::getUrlFor('view', array('view' => 'List'),
+                                           true));
+    exit;
+}
+
+/* Get the gallery object and style information */
+$gallery = $ansel_storage->getGallery($gallery_id);
+if (is_a($gallery, 'PEAR_Error')) {
+    $notification->push(sprintf(_("Gallery %s not found."), $gallery_id), 'horde.error');
+    header('Location: ' . Ansel::getUrlFor('view', array('view' => 'List'), true));
+    exit;
+}
+
+/* Do we have tags to update? */
+if (!is_null($tags) && strlen($tags)) {
+    $tags = explode(',', $tags);
+    if (!empty($image_id)) {
+        $resource = &$ansel_storage->getImage($image_id);
+    } else {
+        $resource = $gallery;
+    }
+    $existingTags = $resource->getTags();
+    $tags = array_merge($existingTags, $tags);
+    $result = $resource->setTags($tags);
+    // If no other action requested, redirect back to the appropriate view
+    if (empty($actionID)) {
+        if (empty($image_id)) {
+            $url = Ansel::getUrlFor('view', array_merge(
+                                            array('view' => 'Gallery',
+                                                  'gallery' => $gallery_id,
+                                                  'slug' => $gallery->get('slug')),
+                                            $date),
+                                    true);
+
+        } else {
+            $url = Ansel::getUrlFor('view', array_merge(
+                                            array('view' => 'Image',
+                                                  'gallery' => $gallery_id,
+                                                  'image' => $image_id,
+                                                  'slug' => $gallery->get('slug')),
+                                            $date),
+
+                                    true);
+        }
+        header('Location: ' . $url);
+        exit;
+    }
+}
+
+/* Run through the action handlers. */
+switch ($actionID) {
+case 'deletetags':
+    $tag = Horde_Util::getFormData('tag');
+    if (!empty($image_id)) {
+        $resource = &$ansel_storage->getImage($image_id);
+        $page = Horde_Util::getFormData('page', 0);
+        $url = Ansel::getUrlFor('view', array_merge(
+                                        array('view' => 'Image',
+                                              'gallery' => $gallery_id,
+                                              'image' => $image_id,
+                                              'page' => $page),
+                                        $date),
+                                true);
+    } else {
+        $resource = $gallery;
+        $url = Ansel::getUrlFor('view', array_merge(
+                                        array('view' => 'Gallery',
+                                              'gallery' => $gallery_id),
+                                        $date),
+                                true);
+    }
+    $eTags = $resource->getTags();
+    unset($eTags[$tag]);
+    $resource->setTags($eTags);
+    header('Location: ' . $url);
+    exit;
+
+case 'modify':
+    $image = &$ansel_storage->getImage($image_id);
+    $ret = Horde_Util::getFormData('ret', 'gallery');
+
+    if (is_a($image, 'PEAR_Error')) {
+        $notification->push(_("Photo not found."), 'horde.error');
+        header('Location: ' . Ansel::getUrlFor('view', array('view' => 'List'), true));
+        exit;
+    }
+
+    $title = sprintf(_("Edit properties :: %s"), $image->filename);
+
+    /* Set up the form object. */
+    require_once ANSEL_BASE . '/lib/Forms/Image.php';
+    $vars = Horde_Variables::getDefaultVariables();
+    if ($ret == 'gallery') {
+        $vars->set('actionID', 'saveclose');
+    } else {
+        $vars->set('actionID', 'savecloseimage');
+    }
+    $form = new ImageForm($vars, $title);
+    $renderer = new Horde_Form_Renderer();
+
+    /* Set up the gallery attributes. */
+    $vars->set('image_default', $image->id == $gallery->get('default'));
+    $vars->set('image_desc', $image->caption);
+    $vars->set('image_tags', implode(', ', $image->getTags()));
+    $vars->set('image_originalDate', $image->originalDate);
+    $vars->set('image_uploaded', $image->uploaded);
+
+    require ANSEL_TEMPLATES . '/common-header.inc';
+    $form->renderActive($renderer, $vars, 'image.php', 'post',
+                        'multipart/form-data');
+
+    require $registry->get('templates', 'horde') . '/common-footer.inc';
+    exit;
+
+case 'savecloseimage':
+case 'saveclose':
+case 'save':
+    $title = _("Save Photo");
+    if (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+        $notification->push(sprintf(_("Access denied saving photo to \"%s\"."), $gallery->get('name')),
+                            'horde.error');
+        $imageurl = Ansel::getUrlFor('view', array_merge(
+                array('gallery' => $gallery_id,
+                      'slug' => $gallery->get('slug'),
+                      'view' => 'Gallery',
+                      'page' => $page),
+                $date),
+            true);
+        header('Location: ' . $imageurl);
+        exit;
+    }
+
+    /* Validate the form object. */
+    require_once ANSEL_BASE . '/lib/Forms/Image.php';
+    $vars = Horde_Variables::getDefaultVariables();
+    $vars->set('actionID', 'save');
+    $renderer = new Horde_Form_Renderer();
+    $form = new ImageForm($vars, _("Edit a photo"));
+
+    /* Update existing image. */
+    if ($form->validate($vars)) {
+         $form->getInfo($vars, $info);
+        /* See if we were replacing photo */
+        if (!empty($info['file0']['file']) &&
+            !is_a(Horde_Browser::wasFileUploaded('file0'), 'PEAR_Error') &&
+            filesize($info['file0']['file'])) {
+
+            /* Read in the uploaded data. */
+            $data = file_get_contents($info['file0']['file']);
+
+            /* Try and make sure the image is in a recognizeable
+             * format. */
+            if (getimagesize($info['file0']['file']) === false) {
+                $notification->push(_("The file you uploaded does not appear to be a valid photo."), 'horde.error');
+                unset($data);
+            }
+        }
+
+        $image = &$ansel_storage->getImage($image_id);
+        $image->caption = $vars->get('image_desc');
+        $image->setTags(explode(',' , $vars->get('image_tags')));
+
+        require_once 'Horde/Date.php';
+        $newDate = new Horde_Date($vars->get('image_originalDate'));
+        $image->originalDate = (int)$newDate->timestamp();
+
+        if (!empty($data)) {
+            $result = $image->replace($data);
+            if (is_a($result, 'PEAR_Error')) {
+                $notification->push(_("There was an error replacing the photo."), 'horde.error');
+            }
+        }
+        $image->save();
+
+        if ($vars->get('image_default')) {
+            if ($gallery->get('default') != $image_id) {
+                // Changing default - force refresh of stack
+                // If we have a default-pretty already, make sure we delete it
+                $ids = unserialize($gallery->get('default_prettythumb'));
+                if (is_array($ids)) {
+                    foreach ($ids as $imageId) {
+                        $gallery->removeImage($imageId, true);
+                    }
+                }
+                $gallery->set('default_prettythumb', '');
+            }
+            $gallery->set('default', $image_id);
+            $gallery->set('default_type', 'manual');
+        } elseif ($gallery->get('default') == $image_id) {
+            // Currently set as default, but we no longer wish it.
+            $gallery->set('default', 0);
+            $gallery->set('default_type', 'auto');
+            // If we have a default-pretty already, make sure we delete it
+            $ids = unserialize($gallery->get('default_prettythumb'));
+            if (is_array($ids)) {
+                foreach ($ids as $imageId) {
+                    $gallery->removeImage($imageId);
+                }
+            }
+            $gallery->set('default_prettythumb', '');
+        }
+
+        $gallery->save();
+        $imageurl = Ansel::getUrlFor('view', array_merge(
+                                             array('gallery' => $gallery_id,
+                                                   'image' => $image_id,
+                                                   'view' => 'Image',
+                                                   'page' => $page),
+                                             $date),
+                                     true);
+        if ($actionID == 'save') {
+            /* Return to the image view. */
+            header('Location: ' . $imageurl);
+        } elseif ($actionID == 'saveclose') {
+            Horde_Util::closeWindowJS('window.opener.location.href = window.opener.location.href; window.close();');
+        } else {
+            Horde_Util::closeWindowJS('window.opener.location.href = \'' . $imageurl . '\'; window.close();');
+        }
+        exit;
+    }
+    break;
+
+case 'editimage':
+case 'cropedit':
+case 'resizeedit':
+    $imageview_url = Ansel::getUrlFor('view', array_merge(
+                                     array('gallery' => $gallery_id,
+                                           'image' => $image_id,
+                                           'view' => 'Image',
+                                           'page' => $page),
+                                     $date),
+                                     true);
+    $imageurl = Horde_Util::addParameter('image.php', array_merge(
+                                            array('gallery' => $gallery_id,
+                                                  'slug' => $gallery->get('slug'),
+                                                  'image' => $image_id,
+                                                  'page' => $page),
+                                            $date));
+
+    $galleryurl = Ansel::getUrlFor('view', array_merge(
+                                       array('gallery' => $gallery_id,
+                                             'page' => $page,
+                                             'view' => 'Gallery',
+                                             'slug' => $gallery->get('slug')),
+                                       $date));
+
+    if (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+        $notification->push(_("Access denied editing the photo."),
+                            'horde.error');
+
+        /* Return to the image view. */
+        header('Location: ' . $imageview_url);
+        exit;
+    }
+
+    /* Retrieve image details. */
+    $image = &$ansel_storage->getImage($image_id);
+    $title = sprintf(_("Edit %s :: %s"), $gallery->get('name'),
+                     $image->filename);
+
+    if ($actionID == 'cropedit') {
+        $geometry = $image->getDimensions('full');
+        $x1 = 0;
+        $y1 = 0;
+        $x2 = $geometry['width'];
+        $y2 = $geometry['height'];
+
+        /* js and css files */
+        Horde::addScriptFile('prototype.js');
+        Horde::addScriptFile('builder.js');
+        Horde::addScriptFile('effects.js', 'horde', true);
+        Horde::addScriptFile('controls.js', 'horde', true);
+        Horde::addScriptFile('dragdrop.js', 'horde', true);
+        Horde::addScriptFile('cropper.js');
+        Ansel::attachStylesheet('cropper.css');
+    } elseif ($actionID == 'resizeedit') {
+        /* js and css files */
+        // TODO: Combine these cases
+        $geometry = $image->getDimensions('full');
+        Horde::addScriptFile('prototype.js');
+        Horde::addScriptFile('builder.js');
+        Horde::addScriptFile('effects.js', 'horde', true);
+        Horde::addScriptFile('slider.js', 'horde', true);
+        Horde::addScriptFile('dragdrop.js', 'horde', true);
+   }
+
+    require ANSEL_TEMPLATES . '/common-header.inc';
+    require ANSEL_TEMPLATES . '/menu.inc';
+
+    if ($actionID == 'cropedit') {
+        require ANSEL_TEMPLATES . '/image/crop_image.inc';
+    } elseif ($actionID == 'resizeedit') {
+        require ANSEL_TEMPLATES . '/image/resize_image.inc';
+    } else {
+        require ANSEL_TEMPLATES . '/image/edit_image.inc';
+    }
+    require $registry->get('templates', 'horde') . '/common-footer.inc';
+    exit;
+
+case 'watermark':
+    if (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+        $notification->push(sprintf(_("Access denied saving photo to \"%s\"."),
+                                    $gallery->get('name')),
+                            'horde.error');
+        /* Return to the image view. */
+        $imageurl = Ansel::getUrlFor('view', array_merge(
+                                     array('gallery' => $gallery_id,
+                                           'image' => $image_id,
+                                           'view' => 'Image',
+                                           'page' => $page,
+                                           'slug' => $gallery->get('slug')),
+                                     $date),
+                                     true);
+        header('Location: ' . $imageurl);
+        exit;
+    } else {
+        // @TODO: Writing directly to VFS here since we only watermark the
+        //        screen images, need to refactor so that Ansel_Image can
+        //        update image data for only a specific view.
+        $image = &$ansel_storage->getImage($image_id);
+        $image->watermark('screen', $watermark, $watermark_halign,
+                                $watermark_valign, $watermark_font);
+        $image->updateData($image->raw('screen'), 'screen');
+        $imageurl = Horde_Util::addParameter('image.php',array_merge(
+                                       array('gallery' => $gallery_id,
+                                             'image' => $image_id,
+                                             'actionID' => 'editimage',
+                                             'page' => $page),
+                                       $date));
+
+        header('Location: ' . Horde::applicationUrl($imageurl, true));
+        exit;
+    }
+
+case 'rotate90':
+case 'rotate180':
+case 'rotate270':
+case 'flip':
+case 'mirror':
+case 'grayscale':
+case 'crop':
+case 'resize':
+    if (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+        $notification->push(sprintf(_("Access denied saving photo to \"%s\"."),
+                                    $gallery->get('name')),
+                            'horde.error');
+    } else {
+        $image = &$ansel_storage->getImage($image_id);
+        if (is_a($image, 'PEAR_Error')) {
+            $notification->push($image->getMessage(), 'horde.error');
+            header('Location: ' . Ansel::getUrlFor('view', array('view' => 'List'), true));
+            exit;
+        }
+
+        switch ($actionID) {
+        case 'rotate90':
+        case 'rotate180':
+        case 'rotate270':
+            $angle = intval(substr($actionID, 6));
+            $image->rotate('full', $angle);
+            break;
+
+        case 'flip':
+            $image->flip('full');
+            break;
+
+        case 'mirror':
+            $image->mirror('full');
+            break;
+
+        case 'grayscale':
+            $image->grayscale('full');
+            break;
+
+        case 'crop':
+            $image->load('full');
+            $params = Horde_Util::getFormData('params');
+            list($x1, $y1, $x2, $y2) = explode('.', $params);
+            $result = $image->crop($x1, $y1, $x2, $y2);
+            if (is_a($result, 'PEAR_Error')) {
+                Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+                $notification->push($result->getMessage(), 'horde.error');
+                $error = true;
+            }
+            break;
+        case 'resize':
+            $image->load('full');
+            $width = Horde_Util::getFormData('width');
+            $height = Horde_Util::getFormData('height');
+            $result = $image->_image->resize($width, $height, true);
+            break;
+        }
+        if (empty($error)) {
+            $image->updateData($image->_image->raw());
+        }
+    }
+
+    $imageurl = Horde_Util::addParameter('image.php', array_merge(
+                                                array('gallery' => $gallery_id,
+                                                      'image' => $image_id,
+                                                      'actionID' => 'editimage',
+                                                      'page' => $page),
+                                                $date));
+    header('Location: ' . Horde::applicationUrl($imageurl, true));
+    exit;
+
+case 'setwatermark':
+    $title = _("Watermark");
+    $image = &$ansel_storage->getImage($image_id);
+    if (is_a($image, 'PEAR_Error')) {
+        $notification->push($image->getMessage(), 'horde.error');
+        header('Location: ' . Ansel::getUrlFor('view', array('view' => 'List'), true));
+        exit;
+    }
+    /* Set up the form object. */
+    require_once ANSEL_BASE . '/lib/Forms/Watermark.php';
+    $vars = Horde_Variables::getDefaultVariables();
+    $vars->set('actionID', 'previewcustomwatermark');
+    $form = new WatermarkForm($vars, _("Watermark"));
+    $renderer = new Horde_Form_Renderer();
+
+    require ANSEL_TEMPLATES . '/common-header.inc';
+    $form->renderActive($renderer, $vars, 'image.php', 'post');
+    require $registry->get('templates', 'horde') . '/common-footer.inc';
+    exit;
+
+case 'previewcustomwatermark':
+    $imageurl = Horde_Util::addParameter('image.php', array_merge(
+                                   array('gallery' => $gallery_id,
+                                         'image' => $image_id,
+                                         'page' => $page,
+                                         'watermark' => $watermark,
+                                         'font' => $watermark_font,
+                                         'whalign' => $watermark_halign,
+                                         'wvalign' => $watermark_valign,
+                                         'actionID' => 'previewwatermark'),
+                                   $date));
+
+    $url = Horde::applicationUrl($imageurl);
+    $url = str_replace('&amp;', '&', $url);
+    Horde_Util::closeWindowJS('window.opener.location.href = "' . $url . '";');
+    exit;
+
+case 'previewgrayscale':
+case 'previewwatermark':
+case 'previewflip':
+case 'previewmirror':
+case 'previewrotate90':
+case 'previewrotate180':
+case 'previewrotate270':
+    $title = _("Edit Photo");
+    $action = substr($actionID, 7);
+
+    /* Retrieve image details. */
+    $image = &$ansel_storage->getImage($image_id);
+    $title = sprintf(_("Preview changes for %s :: %s"),
+                     $gallery->get('name'),
+                     $image->filename);
+
+    require ANSEL_TEMPLATES . '/common-header.inc';
+    require ANSEL_TEMPLATES . '/menu.inc';
+    require ANSEL_TEMPLATES . '/image/preview_image.inc';
+    require $registry->get('templates', 'horde') . '/common-footer.inc';
+    exit;
+
+    break;
+
+case 'imagerotate90':
+case 'imagerotate180':
+case 'imagerotate270':
+    $view = Horde_Util::getFormData('view');
+    $angle = intval(substr($actionID, 11));
+    $image = &$ansel_storage->getImage($image_id);
+    $image->rotate($view, $angle);
+    $image->display($view);
+    exit;
+
+case 'imageflip':
+    $view = Horde_Util::getFormData('view');
+    $image = &$ansel_storage->getImage($image_id);
+    $image->flip($view);
+    $image->display($view);
+    exit;
+
+case 'imagemirror':
+    $view = Horde_Util::getFormData('view');
+    $image = &$ansel_storage->getImage($image_id);
+    $image->mirror($view);
+    $image->display($view);
+    exit;
+
+case 'imagegrayscale':
+    $view = Horde_Util::getFormData('view');
+    $image = &$ansel_storage->getImage($image_id);
+    $image->grayscale($view);
+    $image->display($view);
+    exit;
+
+case 'imagewatermark':
+    $view = Horde_Util::getFormData('view');
+    $image = &$ansel_storage->getImage($image_id);
+    $image->watermark($view, $watermark, $watermark_halign, $watermark_valign,
+                      $watermark_font);
+    $image->display($view);
+    exit;
+
+case 'delete':
+    if (is_array($image_id)) {
+        $images = array_keys($image_id);
+    } else {
+        $images = array($image_id);
+    }
+
+    /* Delete the images if we're provided with a valid image ID. */
+    if (count($images)) {
+        if (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_DELETE)) {
+            $notification->push(sprintf(_("Access denied deleting photos from \"%s\"."), $gallery->get('name')), 'horde.error');
+        } else {
+            foreach ($images as $image) {
+                $result = $gallery->removeImage($image);
+                if (is_a($result, 'PEAR_Error')) {
+                    $notification->push(
+                        sprintf(_("There was a problem deleting photos: %s"),
+                                $result->getMessage()), 'horde.error');
+                } else {
+                    $notification->push(_("Deleted the photo."),
+                                        'horde.success');
+                }
+            }
+        }
+    }
+
+    /* Recalculate the number of pages, since it might have changed */
+    $children = $gallery->countGalleryChildren(PERMS_SHOW);
+    $perpage = min($prefs->getValue('tilesperpage'),
+                   $conf['thumbnail']['perpage']);
+    $pages = ceil($children / $perpage);
+    if ($page > $pages) {
+        $page = $pages;
+    }
+
+    /* Return to the image list. */
+    $imageurl = Ansel::getUrlFor('view', array_merge(
+                                 array('gallery' => $gallery_id,
+                                       'view' => 'Gallery',
+                                       'page' => $page,
+                                       'slug' => $gallery->get('slug')),
+                                 $date),
+                                 true);
+    header('Location: ' . $imageurl);
+    exit;
+
+case 'move':
+    if (is_array($image_id)) {
+        $images = array_keys($image_id);
+    } else {
+        $images = array($image_id);
+    }
+
+    /* Move the images if we're provided with at least one valid image_id. */
+    $newGallery = Horde_Util::getFormData('new_gallery');
+    if ($images && $newGallery) {
+        $newGallery = $ansel_storage->getGallery($newGallery);
+        if (is_a($newGallery, 'PEAR_Error')) {
+            $notification->push(_("Bad input."), 'horde.error');
+        } else {
+            $result = $gallery->moveImagesTo($images, $newGallery);
+            if (is_a($result, 'PEAR_Error')) {
+                $notification->push($result, 'horde.error');
+            } else {
+                $notification->push(
+                    sprintf(ngettext("Moved %d photo from \"%s\" to \"%s\"",
+                                     "Moved %d photos from \"%s\" to \"%s\"",
+                                     $result),
+                            $result, $gallery->get('name'), $newGallery->get('name')),
+                    'horde.success');
+            }
+        }
+    }
+
+    /* Recalculate the number of pages, since it might have changed */
+    $children = $gallery->countGalleryChildren(PERMS_SHOW);
+    $perpage = min($prefs->getValue('tilesperpage'),
+                   $conf['thumbnail']['perpage']);
+    $pages = ceil($children / $perpage);
+    if ($page > $pages) {
+        $page = $pages;
+    }
+
+    /* Return to the image list. */
+    $imageurl = Ansel::getUrlFor('view', array_merge(
+                                 array('gallery' => $gallery_id,
+                                       'view' => 'Gallery',
+                                       'page' => $page,
+                                       'slug' => $gallery->get('slug')),
+                                 $date),
+                                 true);
+    header('Location: ' . $imageurl);
+    exit;
+
+case 'copy':
+    if (is_array($image_id)) {
+        $images = array_keys($image_id);
+    } else {
+        $images = array($image_id);
+    }
+
+    /* Move the images if we're provided with at least one valid image
+     * ID. */
+    $newGallery = Horde_Util::getFormData('new_gallery');
+    if ($images && $newGallery) {
+        $newGallery = $ansel_storage->getGallery($newGallery);
+        if (is_a($newGallery, 'PEAR_Error')) {
+            $notification->push(_("Bad input."), 'horde.error');
+        } else {
+            $result = $gallery->copyImagesTo($images, $newGallery);
+            if (is_a($result, 'PEAR_Error')) {
+                $notification->push($result, 'horde.error');
+            } else {
+                $notification->push(
+                    sprintf(ngettext("Copied %d photo to %s",
+                                     "Copied %d photos to %s", $result),
+                            $result, $newGallery->get('name')),
+                    'horde.success');
+            }
+        }
+    }
+
+    /* Return to the image list. */
+    $imageurl = Ansel::getUrlFor('view', array_merge(
+                                 array('gallery' => $gallery_id,
+                                       'view' => 'Gallery',
+                                       'page' => $page,
+                                       'slug' => $gallery->get('slug')),
+                                 $date),
+                                 true);
+    header('Location: ' . $imageurl);
+    exit;
+
+case 'downloadzip':
+    $galleryId = Horde_Util::getFormData('gallery');
+    if ($galleryId) {
+        $gallery = $ansel_storage->getGallery($galleryId);
+        if (!Horde_Auth::getAuth() || is_a($gallery, 'PEAR_Error') ||
+            !$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_READ) ||
+            $gallery->hasPasswd() || !$gallery->isOldEnough()) {
+
+            $name = is_a($gallery, 'PEAR_Error') ? $galleryId : $gallery->get('name');
+            $notification->push(sprintf(_("Access denied downloading photos from \"%s\"."), $name),
+                                'horde.error');
+            header('Location: ' . Horde::applicationUrl('view.php?view=List', true));
+            exit;
+        }
+    }
+    if (count($image_id)) {
+        Ansel::downloadImagesAsZip(null, array_keys($image_id));
+    } else {
+        $notification->push(_("You must select images to download."), 'horde.error');
+        if ($galleryId) {
+            $url = Ansel::getUrlFor('view', array('gallery' => $galleryId,
+                                                  'view' => 'Gallery',
+                                                  'page' => $page,
+                                                  'slug' => $gallery->get('slug')));
+        } else {
+            $url = Ansel::getUrlFor('view', array('view' => 'List'));
+        }
+        header('Location: ' . $url);
+        exit;
+    }
+    exit;
+    break;
+
+case 'previewcrop':
+
+    if (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+        $notification->push(_("Access denied editing the photo."), 'horde.error');
+        $imageurl = Ansel::getUrlFor(
+            'view', array('gallery' => $gallery_id,
+                          'image' => $image_id,
+                          'view' => 'Image',
+                          'page' => $page));
+        header('Location: ' . $imageurl);
+    } else {
+        $x1 = (int)Horde_Util::getFormData('x1');
+        $y1 = (int)Horde_Util::getFormData('y1');
+        $x2 = (int)Horde_Util::getFormData('x2');
+        $y2 = (int)Horde_Util::getFormData('y2');
+        $title = _("Crop");
+        $action = substr($actionID, 7);
+
+        /* Retrieve image details. */
+        $image = &$ansel_storage->getImage($image_id);
+        $title = sprintf(_("Preview changes for %s :: %s"),
+                         $gallery->get('name'),
+                         $image->filename);
+
+        $params = $x1 . '.' . $y1 . '.' . $x2 . '.' . $y2;
+
+        require ANSEL_TEMPLATES . '/common-header.inc';
+        require ANSEL_TEMPLATES . '/menu.inc';
+        require ANSEL_TEMPLATES . '/image/preview_cropimage.inc';
+        require $registry->get('templates', 'horde') . '/common-footer.inc';
+    }
+    exit;
+
+case 'imagecrop':
+        if ($gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+            $params = Horde_Util::getFormData('params');
+            list($x1, $y1, $x2, $y2) = explode('.', $params);
+            $image = &$ansel_storage->getImage($image_id);
+            $image->load('full');
+            $image->crop($x1, $y1, $x2, $y2);
+            $image->_image->display();
+        }
+        exit;
+
+default:
+    header('Location: ' . Ansel::getUrlFor('default_view', array()));
+    exit;
+}
+
+require ANSEL_TEMPLATES . '/common-header.inc';
+require ANSEL_TEMPLATES . '/menu.inc';
+$form->renderActive($renderer, $vars, 'image.php', 'post',
+                    'multipart/form-data');
+require $registry->get('templates', 'horde') . '/common-footer.inc';
diff --git a/ansel/img/download.php b/ansel/img/download.php
new file mode 100644 (file)
index 0000000..bea9252
--- /dev/null
@@ -0,0 +1,42 @@
+<?php
+/**
+ * $Horde: ansel/img/download.php,v 1.15 2009/07/08 18:28:42 slusarz Exp $
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ */
+
+require_once dirname(__FILE__) . '/../lib/base.php';
+
+$id = Horde_Util::getFormData('image');
+$image = &$ansel_storage->getImage($id);
+if (is_a($image, 'PEAR_Error')) {
+    Horde::fatal($image, __FILE__, __LINE__);
+}
+$gallery = $ansel_storage->getGallery($image->gallery);
+if (is_a($gallery, 'PEAR_Error')) {
+    Horde::fatal($gallery, __FILE__, __LINE__);
+}
+if (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_READ) ||
+    !$gallery->canDownload()) {
+    Horde::fatal(_("Access denied viewing this photo."), __FILE__, __LINE__);
+}
+
+$image->downloadHeaders();
+
+/* Sendfile support. Lighttpd < 1.5 only understands the X-LIGHTTPD-send-file header */
+if ($conf['vfs']['src'] == 'sendfile') {
+    $filename = $ansel_vfs->readFile($image->getVFSPath('full'), $image->getVFSName('full'));
+    header('Content-Type: ' . $image->getType('full'));
+    header('X-LIGHTTPD-send-file: ' . $filename);
+    header('X-Sendfile: ' . $filename);
+    exit;
+}
+
+if (is_a($result = $image->display('full'), 'PEAR_Error')) {
+    Horde::fatal($result, __FILE__, __LINE__);
+}
diff --git a/ansel/img/ecard.php b/ansel/img/ecard.php
new file mode 100644 (file)
index 0000000..b7ce91f
--- /dev/null
@@ -0,0 +1,118 @@
+<?php
+/**
+ * $Horde: ansel/img/ecard.php,v 1.20 2009/07/13 14:29:03 mrubinsk Exp $
+ *
+ * Copyright 2004-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ */
+
+require_once dirname(__FILE__) . '/../lib/base.php';
+require_once ANSEL_BASE . '/lib/Forms/Ecard.php';
+require_once 'Horde/Form/Renderer.php';
+
+/* Abort if ecard sending is disabled. */
+if (empty($conf['ecard']['enable'])) {
+    exit;
+}
+
+/* Get the gallery and the image, and abort if either fails. */
+$gallery = $ansel_storage->getGallery(Horde_Util::getFormData('gallery'));
+if (is_a($gallery, 'PEAR_Error')) {
+    exit;
+}
+$image = &$gallery->getImage(Horde_Util::getFormData('image'));
+if (is_a($image, 'PEAR_Error')) {
+    exit;
+}
+
+/* Run through the action handlers. */
+switch (Horde_Util::getFormData('actionID')) {
+case 'send':
+    /* Check for required elements. */
+    $from = Horde_Util::getFormData('ecard_retaddr');
+    if (empty($from)) {
+        $notification->push(_("You must enter your e-mail address."), 'horde.error');
+        break;
+    }
+    $to = Horde_Util::getFormData('ecard_addr');
+    if (empty($to)) {
+        $notification->push(_("You must enter an e-mail address to send the message to."), 'horde.error');
+        break;
+    }
+
+    $charset = Horde_Nls::getCharset();
+
+    /* Create the text part. */
+    $textpart = new Horde_Mime_Part();
+    $textpart->setType('text/plain');
+    $textpart->setCharset($charset);
+    $textpart->setContents(_("You have been sent an Ecard. To view the Ecard, you must be able to view text/html messages in your mail reader. If you are viewing this message, then most likely your mail reader does not support viewing text/html messages."));
+
+    /* Create the multipart/related part. */
+    $related = new Horde_Mime_Part();
+    $related->setType('multipart/related');
+
+    /* Create the HTML part. */
+    $htmlpart = new Horde_Mime_Part();
+    $htmlpart->setType('text/html');
+    $htmlpart->setCharset($charset);
+
+    /* The image part */
+    $imgpart = new Horde_Mime_Part();
+    $imgpart->setType($image->getType('screen'));
+    $imgpart->setContents($image->raw('screen'));
+    $img_tag = '<img src="cid:' . $imgpart->setContentID() . '" /><p />';
+    $comments = $htmlpart->replaceEOL(Horde_Util::getFormData('ecard_comments'));
+    if (!Horde_Util::getFormData('rtemode')) {
+        $comments = '<pre>' . htmlspecialchars($comments, ENT_COMPAT, Horde_Nls::getCharset()) . '</pre>';
+    }
+    $htmlpart->setContents('<html>' . $img_tag . $comments . '</html>');
+    $related->setContentTypeParameter('start', $htmlpart->setContentID());
+    $related->addPart($htmlpart);
+    $related->addPart($imgpart);
+
+    /* Create the multipart/alternative part. */
+    $alternative = new Horde_Mime_Part();
+    $alternative->setType('multipart/alternative');
+    $alternative->addPart($textpart);
+    $alternative->addPart($related);
+
+    /* Add them to the mail message */
+    $alt = new Horde_Mime_Mail(_("Ecard - ") . Horde_Util::getFormData('image_desc'), null, $to, $from, $charset);
+    $alt->setBasePart($alternative);
+
+    /* Send. */
+    list($mail_driver, $mail_params) = Horde::getMailerConfig();
+    $result = $alt->send($mail_driver, $mail_params);
+    if (is_a($result, 'PEAR_Error')) {
+        $notification->push(sprintf(_("There was an error sending your message: %s"), $result->getMessage()), 'horde.error');
+    } else {
+        Horde_Util::closeWindowJS();
+        exit;
+    }
+}
+
+$title = sprintf(_("Send Ecard :: %s"), $image->filename);
+
+/* Set up the form object. */
+$vars = Horde_Variables::getDefaultVariables();
+$vars->set('actionID', 'send');
+$vars->set('image_desc', strlen($image->caption) ? $image->caption : $image->filename);
+$form = new EcardForm($vars, $title);
+$renderer = new Horde_Form_Renderer();
+
+if ($browser->hasFeature('rte')) {
+    require_once 'Horde/Editor.php';
+    $editor = Horde_Editor::factory('xinha', array('id' => 'ecard_comments'));
+    $vars->set('rtemode', 1);
+    $form->addHidden('', 'rtemode', 'text', false);
+}
+
+require ANSEL_TEMPLATES . '/common-header.inc';
+$notification->notify(array('listeners' => 'status'));
+$form->renderActive($renderer, $vars, 'ecard.php', 'post', 'multipart/form-data');
+require $registry->get('templates', 'horde') . '/common-footer.inc';
diff --git a/ansel/img/full.php b/ansel/img/full.php
new file mode 100644 (file)
index 0000000..cb60ff2
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+/**
+ * $Horde: ansel/img/full.php,v 1.19 2009/07/08 18:28:42 slusarz Exp $
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ */
+
+require_once dirname(__FILE__) . '/../lib/base.php';
+
+$id = Horde_Util::getFormData('image');
+$image = &$ansel_storage->getImage($id);
+if (is_a($image, 'PEAR_Error')) {
+    Horde::fatal($image, __FILE__, __LINE__);
+}
+$gallery = $ansel_storage->getGallery($image->gallery);
+if (is_a($gallery, 'PEAR_Error')) {
+    Horde::fatal($gallery, __FILE__, __LINE__);
+}
+if (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_READ) ||
+    !$gallery->canDownload()) {
+    Horde::fatal(_("Access denied viewing this photo."), __FILE__, __LINE__);
+}
+
+/* Sendfile support. Lighttpd < 1.5 only understands the X-LIGHTTPD-send-file header */
+if ($conf['vfs']['src'] == 'sendfile') {
+    $filename = $ansel_vfs->readFile($image->getVFSPath('full'), $image->getVFSName('full'));
+    header('Content-Type: ' . $image->getType('full'));
+    header('X-LIGHTTPD-send-file: ' . $filename);
+    header('X-Sendfile: ' . $filename);
+    exit;
+}
+
+if (is_a($result = $image->display('full'), 'PEAR_Error')) {
+    Horde::fatal($result, __FILE__, __LINE__);
+}
diff --git a/ansel/img/index.php b/ansel/img/index.php
new file mode 100644 (file)
index 0000000..cdeceb6
--- /dev/null
@@ -0,0 +1,39 @@
+<?php
+/**
+ * $Horde: ansel/img/index.php,v 1.16 2009/07/08 18:28:42 slusarz Exp $
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ */
+
+require_once dirname(__FILE__) . '/../lib/base.php';
+
+$id = Horde_Util::getFormData('image');
+$image = &$ansel_storage->getImage($id);
+if (is_a($image, 'PEAR_Error')) {
+    Horde::fatal($image, __FILE__, __LINE__);
+}
+$gallery = $ansel_storage->getGallery($image->gallery);
+if (is_a($gallery, 'PEAR_Error')) {
+    Horde::fatal($gallery, __FILE__, __LINE__);
+}
+if (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_READ)) {
+    Horde::fatal(_("Access denied viewing this photo."), __FILE__, __LINE__);
+}
+
+/* Sendfile support. Lighttpd < 1.5 only understands the X-LIGHTTPD-send-file header */
+if ($conf['vfs']['src'] == 'sendfile') {
+    $filename = $ansel_vfs->readFile($image->getVFSPath('screen'), $image->getVFSName('screen'));
+    header('Content-Type: ' . $image->getType('screen'));
+    header('X-LIGHTTPD-send-file: ' . $filename);
+    header('X-Sendfile: ' . $filename);
+    exit;
+}
+
+if (is_a($result = $image->display('screen'), 'PEAR_Error')) {
+    Horde::fatal($result, __FILE__, __LINE__);
+}
diff --git a/ansel/img/mini.php b/ansel/img/mini.php
new file mode 100644 (file)
index 0000000..638ce00
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+/**
+ * $Horde: ansel/img/mini.php,v 1.21 2009/07/08 18:28:42 slusarz Exp $
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ */
+
+require_once dirname(__FILE__) . '/../lib/base.php';
+
+$id = Horde_Util::getFormData('image');
+$image = &$ansel_storage->getImage($id);
+if (is_a($image, 'PEAR_Error')) {
+    Horde::fatal($image, __FILE__, __LINE__);
+}
+$gallery = $ansel_storage->getGallery(abs($image->gallery));
+if (is_a($gallery, 'PEAR_Error')) {
+    Horde::fatal($gallery, __FILE__, __LINE__);
+}
+if (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_READ)) {
+    Horde::fatal(_("Access denied viewing this photo."), __FILE__, __LINE__);
+}
+
+/* Sendfile support. Lighttpd < 1.5 only understands the X-LIGHTTPD-send-file header */
+if ($conf['vfs']['src'] == 'sendfile') {
+    /* Need to ensure the file exists */
+    $result = $image->createView('mini', 'ansel_default');
+    if (is_a($result, 'PEAR_Error')) {
+        Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+        exit;
+    }
+    $filename = $ansel_vfs->readFile($image->getVFSPath('mini'), $image->getVFSName('mini'));
+    header('Content-Type: ' . $image->getType('mini'));
+    header('X-LIGHTTPD-send-file: ' . $filename);
+    header('X-Sendfile: ' . $filename);
+    exit;
+}
+
+if (is_a($result = $image->display('mini'), 'PEAR_Error')) {
+    Horde::fatal($result, __FILE__, __LINE__);
+}
diff --git a/ansel/img/prettythumb.php b/ansel/img/prettythumb.php
new file mode 100644 (file)
index 0000000..24e7258
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+/**
+ * $Horde: ansel/img/prettythumb.php,v 1.16 2009/07/08 18:28:42 slusarz Exp $
+ *
+ * Copyright 2007-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ */
+
+require_once dirname(__FILE__) . '/../lib/base.php';
+
+$style = Horde_Util::getFormData('style');
+$id = Horde_Util::getFormData('image');
+$image = &$ansel_storage->getImage($id);
+if (is_a($image, 'PEAR_Error')) {
+    Horde::fatal($image, __FILE__, __LINE__);
+}
+$gallery = $ansel_storage->getGallery(abs($image->gallery));
+if (is_a($gallery, 'PEAR_Error')) {
+    Horde::fatal($gallery, __FILE__, __LINE__);
+}
+if (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_READ)) {
+    Horde::fatal(_("Access denied viewing this photo."), __FILE__, __LINE__);
+}
+
+/* Sendfile support. Lighttpd < 1.5 only understands the X-LIGHTTPD-send-file header */
+if ($conf['vfs']['src'] == 'sendfile') {
+    /* Need to ensure the file exists */
+    $result = $image->createView('prettythumb', $style);
+    if (is_a($result, 'PEAR_Error')) {
+        Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+        exit;
+    }
+    $filename = $ansel_vfs->readFile($image->getVFSPath('prettythumb', $style), $image->getVFSName('prettythumb'));
+    header('Content-Type: ' . $image->getType('prettythumb'));
+    header('X-LIGHTTPD-send-file: ' . $filename);
+    header('X-Sendfile: ' . $filename);
+    exit;
+}
+
+if (is_a($result = $image->display('prettythumb', $style), 'PEAR_Error')) {
+    Horde::fatal($result, __FILE__, __LINE__);
+}
diff --git a/ansel/img/screen.php b/ansel/img/screen.php
new file mode 100644 (file)
index 0000000..691a006
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+/**
+ * $Horde: ansel/img/screen.php,v 1.20 2009/07/08 18:28:42 slusarz Exp $
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ */
+
+require_once dirname(__FILE__) . '/../lib/base.php';
+
+$id = Horde_Util::getFormData('image');
+$image = &$ansel_storage->getImage($id);
+if (is_a($image, 'PEAR_Error')) {
+    Horde::fatal($image, __FILE__, __LINE__);
+}
+$gallery = $ansel_storage->getGallery($image->gallery);
+if (is_a($gallery, 'PEAR_Error')) {
+    Horde::fatal($gallery, __FILE__, __LINE__);
+}
+if (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_READ)) {
+    Horde::fatal(_("Access denied viewing this photo."), __FILE__, __LINE__);
+}
+
+/* Sendfile support. Lighttpd < 1.5 only understands the X-LIGHTTPD-send-file header */
+if ($conf['vfs']['src'] == 'sendfile') {
+    /* Need to ensure the file exists */
+    $result = $image->createView('screen', 'ansel_default');
+    if (is_a($result, 'PEAR_Error')) {
+        Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+        exit;
+    }
+    $filename = $ansel_vfs->readFile($image->getVFSPath('screen'), $image->getVFSName('screen'));
+    header('Content-Type: ' . $image->getType('screen'));
+    header('X-LIGHTTPD-send-file: ' . $filename);
+    header('X-Sendfile: ' . $filename);
+    exit;
+}
+
+if (is_a($result = $image->display('screen'), 'PEAR_Error')) {
+    Horde::fatal($result, __FILE__, __LINE__);
+}
diff --git a/ansel/img/thumb.php b/ansel/img/thumb.php
new file mode 100644 (file)
index 0000000..28bf484
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+/**
+ * $Horde: ansel/img/thumb.php,v 1.21 2009/07/08 18:28:42 slusarz Exp $
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ */
+
+require_once dirname(__FILE__) . '/../lib/base.php';
+
+$id = Horde_Util::getFormData('image');
+$image = &$ansel_storage->getImage($id);
+if (is_a($image, 'PEAR_Error')) {
+    Horde::fatal($image, __FILE__, __LINE__);
+}
+$gallery = $ansel_storage->getGallery(abs($image->gallery));
+if (is_a($gallery, 'PEAR_Error')) {
+    Horde::fatal($gallery, __FILE__, __LINE__);
+}
+if (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_READ)) {
+    Horde::fatal(_("Access denied viewing this photo."), __FILE__, __LINE__);
+}
+
+/* Sendfile support. Lighttpd < 1.5 only understands the X-LIGHTTPD-send-file header */
+if ($conf['vfs']['src'] == 'sendfile') {
+    /* Need to ensure the file exists */
+    $result = $image->createView('thumb', 'ansel_default');
+    if (is_a($result, 'PEAR_Error')) {
+        Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+        exit;
+    }
+    $filename = $ansel_vfs->readFile($image->getVFSPath('thumb'), $image->getVFSName('thumb'));
+    header('Content-Type: ' . $image->getType('thumb'));
+    header('X-LIGHTTPD-send-file: ' . $filename);
+    header('X-Sendfile: ' . $filename);
+    exit;
+}
+
+if (is_a($result = $image->display('thumb'), 'PEAR_Error')) {
+    Horde::fatal($result, __FILE__, __LINE__);
+}
diff --git a/ansel/img/upload.php b/ansel/img/upload.php
new file mode 100644 (file)
index 0000000..480f3be
--- /dev/null
@@ -0,0 +1,255 @@
+<?php
+/**
+ * $Horde: ansel/img/upload.php,v 1.46 2009/07/27 14:52:52 mrubinsk Exp $
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ */
+
+require_once dirname(__FILE__) . '/../lib/base.php';
+require_once ANSEL_BASE . '/lib/Forms/Upload.php';
+require_once 'Horde/Form/Renderer.php';
+
+$gallery_id = Horde_Util::getFormData('gallery');
+$gallery = &$ansel_storage->getGallery($gallery_id);
+if (is_a($gallery, 'PEAR_Error')) {
+    $notification->push(sprintf(_("Gallery %s not found."), $gallery_id), 'horde.error');
+    header('Location: ' . Ansel::getUrlFor('view', array('view' => 'List'), true));
+    exit;
+}
+
+$page = Horde_Util::getFormData('page', 0);
+$vars = Horde_Variables::getDefaultVariables();
+
+$form = new UploadForm($vars, _("Upload photos"));
+if ($form->validate($vars)) {
+    $valid = true;
+    $uploaded = 0;
+    $form->getInfo($vars, $info);
+
+    /* Remember the ids of the images we uploaded so we can autogen */
+    $image_ids = array();
+    for ($i = 0; $i <= $conf['image']['num_uploads'] + 1; ++$i) {
+        if (empty($info['file' . $i]['file'])) {
+            continue;
+        }
+
+        /* Save new image. */
+        if (!is_a(Horde_Browser::wasFileUploaded('file' . $i), 'PEAR_Error') &&
+            filesize($info['file' . $i]['file'])) {
+
+            /* Check for a compressed file. */
+            require_once 'Horde/Mime/Magic.php';
+            if (in_array($info['file' . $i]['type'],
+                         array('x-extension/zip',
+                               'application/x-compressed',
+                               'application/x-zip-compressed',
+                               'application/zip')) ||
+                Horde_Mime_Magic::filenameToMime($info['file' . $i]['name']) == 'application/zip') {
+
+                /* See if we can use the zip extension for reading the file. */
+                if (Horde_Util::extensionExists('zip')) {
+                    $zip = new ZipArchive();
+                    if ($zip->open($info['file' . $i]['file']) !== true) {
+                        $notification->push(sprintf(_("There was an error processing the uploaded archive: %s"), $info['file' . $i]['file']), 'horde.error');
+                        continue;
+                    }
+
+                    for ($z = 0; $z < $zip->numFiles; $z++) {
+                        $zinfo = $zip->statIndex($z);
+
+                        /* Skip some known metadata files. */
+                        $len = strlen($zinfo['name']);
+                        if ($zinfo['name'][$len - 1] == '/') {
+                            continue;
+                        }
+                        if ($zinfo['name'] == 'Thumbs.db') {
+                            continue;
+                        }
+                        if (strrpos($zinfo['name'], '.DS_Store') == ($len - 9)) {
+                            continue;
+                        }
+                        if (strrpos($zinfo['name'], '.localized') == ($len - 10)) {
+                            continue;
+                        }
+                        if (strpos($zinfo['name'], '__MACOSX/') !== false) {
+                            continue;
+                        }
+
+                        $stream = $zip->getStream($zinfo['name']);
+                        $zdata = stream_get_contents($stream);
+                        if (!strlen($zdata)) {
+                            $notification->push(sprintf(_("There was an error processing the uploaded archive: %s"), $zinfo['name']), 'horde.error');
+                            break;
+                        }
+
+                        /* If we successfully got data, try adding the
+                         * image to the gallery. */
+                        $image_id = $gallery->addImage(array(
+                            'image_filename' => $zinfo['name'],
+                            'image_caption' => '',
+                            'data' => $zdata,
+                        ));
+                        unset($zdata);
+                        if (!is_a($image_id, 'PEAR_Error')) {
+                            ++$uploaded;
+                            if ($conf['image']['autogen'] > count($image_ids)) {
+                                $image_ids[] = $image_id;
+                            }
+                        } else {
+                            $notification->push(sprintf(_("There was a problem saving the photo: %s"), $image_id), 'horde.error');
+                        }
+                    }
+
+                    $zip->close();
+                    unset($zip);
+                } else {
+                    /* Read in the uploaded data. */
+                    $data = file_get_contents($info['file' . $i]['file']);
+
+                    /* Get the list of files in the zipfile. */
+                    require_once 'Horde/Compress.php';
+                    $zip = Horde_Compress::factory('zip');
+                    $files = $zip->decompress($data, array('action' => Horde_Compress::ZIP_LIST));
+
+                    if (is_a($files, 'PEAR_Error')) {
+                        $notification->push(sprintf(_("There was an error processing the uploaded archive: %s"), $files->getMessage()), 'horde.error');
+                        continue;
+                    }
+
+                    foreach ($files as $key => $zinfo) {
+                        /* Skip some known metadata files. */
+                        $len = strlen($zinfo['name']);
+                        if ($zinfo['name'][$len - 1] == '/') {
+                            continue;
+                        }
+                        if ($zinfo['name'] == 'Thumbs.db') {
+                            continue;
+                        }
+                        if (strrpos($zinfo['name'], '.DS_Store') == ($len - 9)) {
+                            continue;
+                        }
+                        if (strrpos($zinfo['name'], '.localized') == ($len - 10)) {
+                            continue;
+                        }
+                        if (strpos($zinfo['name'], '__MACOSX/') !== false) {
+                            continue;
+                        }
+
+                        $zdata = $zip->decompress($data, array('action' => Horde_Compress::ZIP_DATA,
+                                                               'info' => $files,
+                                                               'key' => $key));
+                        if (is_a($zdata, 'PEAR_Error')) {
+                            $notification->push(sprintf(_("There was an error processing the uploaded archive: %s"), $zdata->getMessage()), 'horde.error');
+                            break;
+                        }
+
+                        /* If we successfully got data, try adding the
+                         * image to the gallery. */
+                        $image_id = $gallery->addImage(array(
+                            'image_filename' => $zinfo['name'],
+                            'image_caption' => '',
+                            'data' => $zdata,
+                        ));
+                        unset($zdata);
+                        if (!is_a($image_id, 'PEAR_Error')) {
+                            ++$uploaded;
+                            if ($conf['image']['autogen'] > count($image_ids)) {
+                                $image_ids[] = $image_id;
+                            }
+                        } else {
+                            $notification->push(sprintf(_("There was a problem saving the photo: %s"), $image_id), 'horde.error');
+                        }
+                    }
+
+                    unset($zip);
+                    unset($data);
+                }
+            } else {
+                /* Read in the uploaded data. */
+                $data = file_get_contents($info['file' . $i]['file']);
+
+                /* Try and make sure the image is in a recognizeable
+                 * format. */
+                if (getimagesize($info['file' . $i]['file']) === false) {
+                    $notification->push(_("The file you uploaded does not appear to be a valid photo."), 'horde.error');
+                    continue;
+                }
+
+                /* Add the image to the gallery */
+                $image_data = array('image_filename' => $info['file' . $i]['name'],
+                                    'image_caption' => $vars->get('image' . $i . '_desc'),
+                                    'image_type' => $info['file' . $i]['type'],
+                                    'data' => $data,
+                                    'tags' => (isset($info['image' . $i . '_tags']) ? explode(',', $info['image' . $i . '_tags']) : array()));
+                $image_id = $gallery->addImage($image_data, (bool)$vars->get('image' . $i . '_default'));
+                unset($data);
+                if (is_a($image_id, 'PEAR_Error')) {
+                    $notification->push(sprintf(_("There was a problem saving the photo: %s"), $image_id->getMessage()), 'horde.error');
+                    $valid = false;
+                } else {
+                    ++$uploaded;
+                    $image_ids[] = $image_id;
+                }
+            }
+        } else {
+            if (!empty($info['file' . $i]['error'])) {
+                $notification->push(sprintf(_("There was a problem uploading the photo: %s"), $info['file' . $i]['error']), 'horde.error');
+            } elseif (!filesize($info['file' . $i]['file'])) {
+                $notification->push(_("The uploaded file appears to be empty. It may not exist on your computer."), 'horde.error');
+            }
+            $valid = false;
+        }
+    }
+
+    /* Try to autogenerate some views and tell the user what happened. */
+    if ($uploaded) {
+        $cnt = count($image_ids);
+        for ($i = 0; $i < $conf['image']['autogen'] && $cnt > $i; $i++) {
+            $image_id = $image_ids[$i];
+            $image = &$GLOBALS['ansel_storage']->getImage($image_id);
+            $image->createView('screen');
+            $image->createView('thumb');
+            $image->createView('mini');
+            unset($image);
+        }
+
+        // postupload hook if needed
+        try {
+            Horde::callHook('postupload', array($image_ids));
+        } catch (Horde_Exception $e) {
+            // Error from within the hook.
+        } catch (Horde_Exception_HookNotSet $e) {}
+        $notification->push(sprintf(ngettext("%d photo was uploaded.", "%d photos were uploaded.", $uploaded), $uploaded), 'horde.success');
+    } elseif ($vars->get('submitbutton') != _("Cancel")) {
+        $notification->push(_("You did not select any photos to upload."), 'horde.error');
+    }
+
+    if ($valid) {
+        /* Return to the gallery view. */
+        $imageurl = Ansel::getUrlFor('view',
+                                     array('gallery' => $gallery_id,
+                                           'slug' => $gallery->get('slug'),
+                                           'view' => 'Gallery',
+                                           'page' => $page),
+                                     true);
+
+        header('Location: ' . $imageurl);
+        exit;
+    }
+}
+///* Preview existing images */
+if ($gallery->countImages() && $browser->hasFeature('javascript')) {
+    $haveImages = true;
+}
+
+$breadcrumbs = Ansel::getBreadCrumbs(' &raquo; ', $gallery);
+$title = _("Add Photo");
+require ANSEL_TEMPLATES . '/common-header.inc';
+require ANSEL_TEMPLATES . '/menu.inc';
+require ANSEL_TEMPLATES . '/image/upload.inc';
+require $registry->get('templates', 'horde') . '/common-footer.inc';
diff --git a/ansel/img/upload_preview.php b/ansel/img/upload_preview.php
new file mode 100644 (file)
index 0000000..d8fe9eb
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+/**
+ * $Horde: ansel/img/upload_preview.php,v 1.9 2009/07/13 17:18:39 mrubinsk Exp $
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ */
+
+require_once dirname(__FILE__) . '/../lib/base.php';
+
+$gallery_id = (int)Horde_Util::getFormData('gallery');
+$gallery = $ansel_storage->getGallery($gallery_id);
+if (is_a($gallery, 'PEAR_Error') ||
+    !$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_READ)) {
+    die(sprintf(_("Gallery %s not found."), $gallery_id));
+}
+
+$from = (int)Horde_Util::getFormData('from');
+$to = (int)Horde_Util::getFormData('to');
+$count = $to - $from + 1;
+
+$images = $gallery->getImages($from, $count);
+if (is_a($images, 'PEAR_Error')) {
+    die($images->getError());
+}
+
+foreach ($images as $image) {
+    echo  '<li class="small">';
+    echo '<div style="width:90px;">';
+    $alt = htmlspecialchars($image->filename);
+    echo '<img onclick="ansel_lb.start(' . $image->id . ')" src="' . Ansel::getImageUrl($image->id, 'mini') . '" alt="' . $alt . '" title="' . $alt . '" />';
+    echo '</div></li>' . "\n";
+}
diff --git a/ansel/index.php b/ansel/index.php
new file mode 100644 (file)
index 0000000..1cdffd0
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+/**
+ * $Horde: ansel/index.php,v 1.22 2009/01/06 17:48:49 jan Exp $
+ *
+ * Copyright 2001-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ */
+
+define('ANSEL_BASE', dirname(__FILE__));
+$ansel_configured = (is_readable(ANSEL_BASE . '/config/conf.php') &&
+                     is_readable(ANSEL_BASE . '/config/prefs.php') &&
+                     is_readable(ANSEL_BASE . '/config/styles.php'));
+
+if (!$ansel_configured) {
+    require ANSEL_BASE . '/../lib/Test.php';
+    Horde_Test::configFilesMissing('Ansel', ANSEL_BASE,
+        array('conf.php', 'prefs.php'),
+        array('styles.php' => 'This file controls the available gallery styles for Ansel.'));
+}
+
+require_once ANSEL_BASE . '/lib/base.php';
+header('Location: ' . Ansel::getUrlFor('default_view', array()));
+exit;
diff --git a/ansel/js/src/builder.js b/ansel/js/src/builder.js
new file mode 100755 (executable)
index 0000000..f25e996
--- /dev/null
@@ -0,0 +1,20 @@
+
+var Builder={NODEMAP:{AREA:'map',CAPTION:'table',COL:'table',COLGROUP:'table',LEGEND:'fieldset',OPTGROUP:'select',OPTION:'select',PARAM:'object',TBODY:'table',TD:'table',TFOOT:'table',TH:'table',THEAD:'table',TR:'table'},node:function(elementName){elementName=elementName.toUpperCase();var parentTag=this.NODEMAP[elementName]||'div';var parentElement=document.createElement(parentTag);try{parentElement.innerHTML="<"+elementName+"></"+elementName+">";}catch(e){}
+var element=parentElement.firstChild||null;if(element&&(element.tagName.toUpperCase()!=elementName))
+element=element.getElementsByTagName(elementName)[0];if(!element)element=document.createElement(elementName);if(!element)return;if(arguments[1])
+if(this._isStringOrNumber(arguments[1])||(arguments[1]instanceof Array)||arguments[1].tagName){this._children(element,arguments[1]);}else{var attrs=this._attributes(arguments[1]);if(attrs.length){try{parentElement.innerHTML="<"+elementName+" "+
+attrs+"></"+elementName+">";}catch(e){}
+element=parentElement.firstChild||null;if(!element){element=document.createElement(elementName);for(attr in arguments[1])
+element[attr=='class'?'className':attr]=arguments[1][attr];}
+if(element.tagName.toUpperCase()!=elementName)
+element=parentElement.getElementsByTagName(elementName)[0];}}
+if(arguments[2])
+this._children(element,arguments[2]);return element;},_text:function(text){return document.createTextNode(text);},ATTR_MAP:{'className':'class','htmlFor':'for'},_attributes:function(attributes){var attrs=[];for(attribute in attributes)
+attrs.push((attribute in this.ATTR_MAP?this.ATTR_MAP[attribute]:attribute)+'="'+attributes[attribute].toString().escapeHTML().gsub(/"/,'&quot;')+'"');return attrs.join(" ");},_children:function(element,children){if(children.tagName){element.appendChild(children);return;}
+if(typeof children=='object'){children.flatten().each(function(e){if(typeof e=='object')
+element.appendChild(e)
+else
+if(Builder._isStringOrNumber(e))
+element.appendChild(Builder._text(e));});}else
+if(Builder._isStringOrNumber(children))
+element.appendChild(Builder._text(children));},_isStringOrNumber:function(param){return(typeof param=='string'||typeof param=='number');},build:function(html){var element=this.node('div');$(element).update(html.strip());return element.down();},dump:function(scope){if(typeof scope!='object'&&typeof scope!='function')scope=window;var tags=("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY "+"BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET "+"FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+"KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+"PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+"TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/);tags.each(function(tag){scope[tag]=function(){return Builder.node.apply(Builder,[tag].concat($A(arguments)));}});}}
\ No newline at end of file
diff --git a/ansel/js/src/carousel.js b/ansel/js/src/carousel.js
new file mode 100644 (file)
index 0000000..b966dbb
--- /dev/null
@@ -0,0 +1,1076 @@
+/*  Prototype-UI, version trunk
+ *
+ *  Prototype-UI is freely distributable under the terms of an MIT-style license.
+ *  For details, see the PrototypeUI web site: http://www.prototype-ui.com/
+ *
+ *--------------------------------------------------------------------------*/
+
+if(typeof Prototype == 'undefined' || !Prototype.Version.match("1.6"))
+  throw("Prototype-UI library require Prototype library >= 1.6.0");
+
+if (Prototype.Browser.WebKit) {
+  Prototype.Browser.WebKitVersion = parseFloat(navigator.userAgent.match(/AppleWebKit\/([\d\.\+]*)/)[1]);
+  Prototype.Browser.Safari2 = (Prototype.Browser.WebKitVersion < 420);
+}
+
+if (Prototype.Browser.IE) {
+  Prototype.Browser.IEVersion = parseFloat(navigator.appVersion.split(';')[1].strip().split(' ')[1]);
+  Prototype.Browser.IE6 =  Prototype.Browser.IEVersion == 6;
+  Prototype.Browser.IE7 =  Prototype.Browser.IEVersion == 7;
+}
+
+Prototype.falseFunction = function() { return false };
+Prototype.trueFunction  = function() { return true  };
+
+/*
+Namespace: UI
+
+  Introduction:
+    Prototype-UI is a library of user interface components based on the Prototype framework.
+    Its aim is to easilly improve user experience in web applications.
+
+    It also provides utilities to help developers.
+
+  Guideline:
+    - Prototype conventions are followed
+    - Everything should be unobstrusive
+    - All components are themable with CSS stylesheets, various themes are provided
+
+  Warning:
+    Prototype-UI is still under deep development, this release is targeted to developers only.
+    All interfaces are subjects to changes, suggestions are welcome.
+
+    DO NOT use it in production for now.
+
+  Authors:
+    - Sébastien Gruhier, <http://www.xilinus.com>
+    - Samuel Lebeau, <http://gotfresh.info>
+*/
+
+var UI = {
+  Abstract: { },
+  Ajax: { }
+};
+Object.extend(Class.Methods, {
+  extend: Object.extend.methodize(),
+
+  addMethods: Class.Methods.addMethods.wrap(function(proceed, source) {
+    // ensure we are not trying to add null or undefined
+    if (!source) return this;
+
+    // no callback, vanilla way
+    if (!source.hasOwnProperty('methodsAdded'))
+      return proceed(source);
+
+    var callback = source.methodsAdded;
+    delete source.methodsAdded;
+    proceed(source);
+    callback.call(source, this);
+    source.methodsAdded = callback;
+
+    return this;
+  }),
+
+  addMethod: function(name, lambda) {
+    var methods = {};
+    methods[name] = lambda;
+    return this.addMethods(methods);
+  },
+
+  method: function(name) {
+    return this.prototype[name].valueOf();
+  },
+
+  classMethod: function() {
+    $A(arguments).flatten().each(function(method) {
+      this[method] = (function() {
+        return this[method].apply(this, arguments);
+      }).bind(this.prototype);
+    }, this);
+    return this;
+  },
+
+  // prevent any call to this method
+  undefMethod: function(name) {
+    this.prototype[name] = undefined;
+    return this;
+  },
+
+  // remove the class' own implementation of this method
+  removeMethod: function(name) {
+    delete this.prototype[name];
+    return this;
+  },
+
+  aliasMethod: function(newName, name) {
+    this.prototype[newName] = this.prototype[name];
+    return this;
+  },
+
+  aliasMethodChain: function(target, feature) {
+    feature = feature.camelcase();
+
+    this.aliasMethod(target+"Without"+feature, target);
+    this.aliasMethod(target, target+"With"+feature);
+
+    return this;
+  }
+});
+Object.extend(Number.prototype, {
+  // Snap a number to a grid
+  snap: function(round) {
+    return parseInt(round == 1 ? this : (this / round).floor() * round);
+  }
+});
+/*
+Interface: String
+
+*/
+
+Object.extend(String.prototype, {
+  camelcase: function() {
+    var string = this.dasherize().camelize();
+    return string.charAt(0).toUpperCase() + string.slice(1);
+  },
+
+  /*
+    Method: makeElement
+      toElement is unfortunately already taken :/
+
+      Transforms html string into an extended element or null (when failed)
+
+      > '<li><a href="#">some text</a></li>'.makeElement(); // => LI href#
+      > '<img src="foo" id="bar" /><img src="bar" id="bar" />'.makeElement(); // => IMG#foo (first one)
+
+    Returns:
+      Extended element
+
+  */
+  makeElement: function() {
+    var wrapper = new Element('div'); wrapper.innerHTML = this;
+    return wrapper.down();
+  }
+});
+Object.extend(Array.prototype, {
+  empty: function() {
+    return !this.length;
+  },
+
+  extractOptions: function() {
+    return this.last().constructor === Object ? this.pop() : { };
+  },
+
+  removeAt: function(index) {
+    var object = this[index];
+    this.splice(index, 1);
+    return object;
+  },
+
+  remove: function(object) {
+    var index;
+    while ((index = this.indexOf(object)) != -1)
+      this.removeAt(index);
+    return object;
+  },
+
+  insert: function(index) {
+    var args = $A(arguments);
+    args.shift();
+    this.splice.apply(this, [ index, 0 ].concat(args));
+    return this;
+  }
+});
+Element.addMethods({
+  getScrollDimensions: function(element) {
+    return {
+      width:  element.scrollWidth,
+      height: element.scrollHeight
+    }
+  },
+
+  getScrollOffset: function(element) {
+    return Element._returnOffset(element.scrollLeft, element.scrollTop);
+  },
+
+  setScrollOffset: function(element, offset) {
+    element = $(element);
+    if (arguments.length == 3)
+      offset = { left: offset, top: arguments[2] };
+    element.scrollLeft = offset.left;
+    element.scrollTop  = offset.top;
+    return element;
+  },
+
+  // returns "clean" numerical style (without "px") or null if style can not be resolved
+  // or is not numeric
+  getNumStyle: function(element, style) {
+    var value = parseFloat($(element).getStyle(style));
+    return isNaN(value) ? null : value;
+  },
+
+  // by Tobie Langel (http://tobielangel.com/2007/5/22/prototype-quick-tip)
+  appendText: function(element, text) {
+    element = $(element);
+    text = String.interpret(text);
+    element.appendChild(document.createTextNode(text));
+    return element;
+  }
+});
+
+document.whenReady = function(callback) {
+  if (document.loaded)
+    callback.call(document);
+  else
+    document.observe('dom:loaded', callback);
+};
+
+Object.extend(document.viewport, {
+  // Alias this method for consistency
+  getScrollOffset: document.viewport.getScrollOffsets,
+
+  setScrollOffset: function(offset) {
+    Element.setScrollOffset(Prototype.Browser.WebKit ? document.body : document.documentElement, offset);
+  },
+
+  getScrollDimensions: function() {
+    return Element.getScrollDimensions(Prototype.Browser.WebKit ? document.body : document.documentElement);
+  }
+});
+/*
+Interface: UI.Options
+  Mixin to handle *options* argument in initializer pattern.
+
+  TODO: find a better example than Circle that use an imaginary Point function,
+        this example should be used in tests too.
+
+  It assumes class defines a property called *options*, containing
+  default options values.
+
+  Instances hold their own *options* property after a first call to <setOptions>.
+
+  Example:
+    > var Circle = Class.create(UI.Options, {
+    >
+    >   // default options
+    >   options: {
+    >     radius: 1,
+    >     origin: Point(0, 0)
+    >   },
+    >
+    >   // common usage is to call setOptions in initializer
+    >   initialize: function(options) {
+    >     this.setOptions(options);
+    >   }
+    > });
+    >
+    > var circle = new Circle({ origin: Point(1, 4) });
+    >
+    > circle.options
+    > // => { radius: 1, origin: Point(1,4) }
+
+  Accessors:
+    There are builtin methods to automatically write options accessors. All those
+    methods can take either an array of option names nor option names as arguments.
+    Notice that those methods won't override an accessor method if already present.
+
+     * <optionsGetter> creates getters
+     * <optionsSetter> creates setters
+     * <optionsAccessor> creates both getters and setters
+
+    Common usage is to invoke them on a class to create accessors for all instances
+    of this class.
+    Invoking those methods on a class has the same effect as invoking them on the class prototype.
+    See <classMethod> for more details.
+
+    Example:
+    > // Creates getter and setter for the "radius" options of circles
+    > Circle.optionsAccessor('radius');
+    >
+    > circle.setRadius(4);
+    > // 4
+    >
+    > circle.getRadius();
+    > // => 4 (circle.options.radius)
+
+  Inheritance support:
+    Subclasses can refine default *options* values, after a first instance call on setOptions,
+    *options* attribute will hold all default options values coming from the inheritance hierarchy.
+*/
+
+(function() {
+  UI.Options = {
+    methodsAdded: function(klass) {
+      klass.classMethod($w(' setOptions allOptions optionsGetter optionsSetter optionsAccessor '));
+    },
+
+    // Group: Methods
+
+    /*
+      Method: setOptions
+        Extends object's *options* property with the given object
+    */
+    setOptions: function(options) {
+      if (!this.hasOwnProperty('options'))
+        this.options = this.allOptions();
+
+      this.options = Object.extend(this.options, options || {});
+    },
+
+    /*
+      Method: allOptions
+        Computes the complete default options hash made by reverse extending all superclasses
+        default options.
+
+        > Widget.prototype.allOptions();
+    */
+    allOptions: function() {
+      var superclass = this.constructor.superclass, ancestor = superclass && superclass.prototype;
+      return (ancestor && ancestor.allOptions) ?
+          Object.extend(ancestor.allOptions(), this.options) :
+          Object.clone(this.options);
+    },
+
+    /*
+      Method: optionsGetter
+        Creates default getters for option names given as arguments.
+        With no argument, creates getters for all option names.
+    */
+    optionsGetter: function() {
+      addOptionsAccessors(this, arguments, false);
+    },
+
+    /*
+      Method: optionsSetter
+        Creates default setters for option names given as arguments.
+        With no argument, creates setters for all option names.
+    */
+    optionsSetter: function() {
+      addOptionsAccessors(this, arguments, true);
+    },
+
+    /*
+      Method: optionsAccessor
+        Creates default getters/setters for option names given as arguments.
+        With no argument, creates accessors for all option names.
+    */
+    optionsAccessor: function() {
+      this.optionsGetter.apply(this, arguments);
+      this.optionsSetter.apply(this, arguments);
+    }
+  };
+
+  // Internal
+  function addOptionsAccessors(receiver, names, areSetters) {
+    names = $A(names).flatten();
+
+    if (names.empty())
+      names = Object.keys(receiver.allOptions());
+
+    names.each(function(name) {
+      var accessorName = (areSetters ? 'set' : 'get') + name.camelcase();
+
+      receiver[accessorName] = receiver[accessorName] || (areSetters ?
+        // Setter
+        function(value) { return this.options[name] = value } :
+        // Getter
+        function()      { return this.options[name]         });
+    });
+  }
+})();
+/*
+  Class: UI.Carousel
+
+  Main class to handle a carousel of elements in a page. A carousel :
+    * could be vertical or horizontal
+    * works with liquid layout
+    * is designed by CSS
+
+  Assumptions:
+    * Elements should be from the same size
+
+  Example:
+    > ...
+    > <div id="horizontal_carousel">
+    >   <div class="previous_button"></div>
+    >   <div class="container">
+    >     <ul>
+    >       <li> What ever you like</li>
+    >     </ul>
+    >   </div>
+    >   <div class="next_button"></div>
+    > </div>
+    > <script>
+    > new UI.Carousel("horizontal_carousel");
+    > </script>
+    > ...
+*/
+UI.Carousel = Class.create(UI.Options, {
+  // Group: Options
+  options: {
+    // Property: direction
+    //   Can be horizontal or vertical, horizontal by default
+    direction               : "horizontal",
+
+    // Property: previousButton
+    //   Selector of previous button inside carousel element, ".previous_button" by default,
+    //   set it to false to ignore previous button
+    previousButton          : ".previous_button",
+
+    // Property: nextButton
+    //   Selector of next button inside carousel element, ".next_button" by default,
+    //   set it to false to ignore next button
+    nextButton              : ".next_button",
+
+    // Property: container
+    //   Selector of carousel container inside carousel element, ".container" by default,
+    container               : ".container",
+
+    // Property: scrollInc
+    //   Define the maximum number of elements that gonna scroll each time, auto by default
+    scrollInc               : "auto",
+
+    // Property: disabledButtonSuffix
+    //   Define the suffix classanme used when a button get disabled, to '_disabled' by default
+    //   Previous button classname will be previous_button_disabled
+    disabledButtonSuffix : '_disabled',
+
+    // Property: overButtonSuffix
+    //   Define the suffix classanme used when a button has a rollover status, '_over' by default
+    //   Previous button classname will be previous_button_over
+    overButtonSuffix : '_over'
+  },
+
+  /*
+    Group: Attributes
+
+      Property: element
+        DOM element containing the carousel
+
+      Property: id
+        DOM id of the carousel's element
+
+      Property: container
+        DOM element containing the carousel's elements
+
+      Property: elements
+        Array containing the carousel's elements as DOM elements
+
+      Property: previousButton
+        DOM id of the previous button
+
+      Property: nextButton
+        DOM id of the next button
+
+      Property: posAttribute
+        Define if the positions are from left or top
+
+      Property: dimAttribute
+        Define if the dimensions are horizontal or vertical
+
+      Property: elementSize
+        Size of each element, it's an integer
+
+      Property: nbVisible
+        Number of visible elements, it's a float
+
+      Property: animating
+        Define whether the carousel is in animation or not
+  */
+
+  /*
+    Group: Events
+      List of events fired by a carousel
+
+      Notice: Carousel custom events are automatically namespaced in "carousel:" (see Prototype custom events).
+
+      Examples:
+        This example will observe all carousels
+        > document.observe('carousel:scroll:ended', function(event) {
+        >   alert("Carousel with id " + event.memo.carousel.id + " has just been scrolled");
+        > });
+
+        This example will observe only this carousel
+        > new UI.Carousel('horizontal_carousel').observe('scroll:ended', function(event) {
+        >   alert("Carousel with id " + event.memo.carousel.id + " has just been scrolled");
+        > });
+
+      Property: previousButton:enabled
+        Fired when the previous button has just been enabled
+
+      Property: previousButton:disabled
+        Fired when the previous button has just been disabled
+
+      Property: nextButton:enabled
+        Fired when the next button has just been enabled
+
+      Property: nextButton:disabled
+        Fired when the next button has just been disabled
+
+      Property: scroll:started
+        Fired when a scroll has just started
+
+      Property: scroll:ended
+        Fired when a scroll has been done,
+        memo.shift = number of elements scrolled, it's a float
+
+      Property: sizeUpdated
+        Fired when the carousel size has just been updated.
+        Tips: memo.carousel.currentSize() = the new carousel size
+  */
+
+  // Group: Constructor
+
+  /*
+    Method: initialize
+      Constructor function, should not be called directly
+
+    Parameters:
+      element - DOM element
+      options - (Hash) list of optional parameters
+
+    Returns:
+      this
+  */
+  initialize: function(element, options) {
+    this.setOptions(options);
+    this.element = $(element);
+    this.id = this.element.id;
+    this.container   = this.element.down(this.options.container).firstDescendant();
+    this.elements    = this.container.childElements();
+    this.previousButton = this.options.previousButton == false ? null : this.element.down(this.options.previousButton);
+    this.nextButton = this.options.nextButton == false ? null : this.element.down(this.options.nextButton);
+
+    this.posAttribute = (this.options.direction == "horizontal" ? "left" : "top");
+    this.dimAttribute = (this.options.direction == "horizontal" ? "width" : "height");
+
+    this.elementSize = this.computeElementSize();
+    this.nbVisible = this.currentSize() / this.elementSize;
+
+    var scrollInc = this.options.scrollInc;
+    if (scrollInc == "auto")
+      scrollInc = Math.floor(this.nbVisible);
+    [ this.previousButton, this.nextButton ].each(function(button) {
+      if (!button) return;
+      var className = (button == this.nextButton ? "next_button" : "previous_button") + this.options.overButtonSuffix;
+      button.clickHandler = this.scroll.bind(this, (button == this.nextButton ? -1 : 1) * scrollInc * this.elementSize);
+      button.observe("click", button.clickHandler)
+            .observe("mouseover", function() {button.addClassName(className)}.bind(this))
+            .observe("mouseout",  function() {button.removeClassName(className)}.bind(this));
+    }, this);
+    this.updateButtons();
+  },
+
+  // Group: Destructor
+
+  /*
+    Method: destroy
+      Cleans up DOM and memory
+  */
+  destroy: function($super) {
+    [ this.previousButton, this.nextButton ].each(function(button) {
+      if (!button) return;
+        button.stopObserving("click", button.clickHandler);
+    }, this);
+      this.element.remove();
+      this.fire('destroyed');
+  },
+
+  // Group: Event handling
+
+  /*
+    Method: fire
+      Fires a carousel custom event automatically namespaced in "carousel:" (see Prototype custom events).
+      The memo object contains a "carousel" property referring to the carousel.
+
+    Example:
+      > document.observe('carousel:scroll:ended', function(event) {
+      >   alert("Carousel with id " + event.memo.carousel.id + " has just been scrolled");
+      > });
+
+    Parameters:
+      eventName - an event name
+      memo      - a memo object
+
+    Returns:
+      fired event
+  */
+  fire: function(eventName, memo) {
+    memo = memo || { };
+    memo.carousel = this;
+    return this.element.fire('carousel:' + eventName, memo);
+  },
+
+  /*
+    Method: observe
+      Observe a carousel event with a handler function automatically bound to the carousel
+
+    Parameters:
+      eventName - an event name
+      handler   - a handler function
+
+    Returns:
+      this
+  */
+  observe: function(eventName, handler) {
+    this.element.observe('carousel:' + eventName, handler.bind(this));
+    return this;
+  },
+
+  /*
+    Method: stopObserving
+      Unregisters a carousel event, it must take the same parameters as this.observe (see Prototype stopObserving).
+
+    Parameters:
+      eventName - an event name
+      handler   - a handler function
+
+    Returns:
+      this
+  */
+  stopObserving: function(eventName, handler) {
+      this.element.stopObserving('carousel:' + eventName, handler);
+      return this;
+  },
+
+  // Group: Actions
+
+  /*
+    Method: checkScroll
+      Check scroll position to avoid unused space at right or bottom
+
+    Parameters:
+      position       - position to check
+      updatePosition - should the container position be updated ? true/false
+
+    Returns:
+      position
+  */
+  checkScroll: function(position, updatePosition) {
+    if (position > 0)
+      position = 0;
+    else {
+      var limit = this.elements.last().positionedOffset()[this.posAttribute] + this.elementSize;
+      var carouselSize = this.currentSize();
+
+      if (position + limit < carouselSize)
+        position += carouselSize - (position + limit);
+      position = Math.min(position, 0);
+    }
+    if (updatePosition)
+      this.container.style[this.posAttribute] = position + "px";
+
+    return position;
+  },
+
+  /*
+    Method: scroll
+      Scrolls carousel from maximum deltaPixel
+
+    Parameters:
+      deltaPixel - a float
+
+    Returns:
+      this
+  */
+  scroll: function(deltaPixel) {
+    if (this.animating)
+      return this;
+
+    // Compute new position
+    var position =  this.currentPosition() + deltaPixel;
+
+    // Check bounds
+    position = this.checkScroll(position, false);
+
+    // Compute shift to apply
+    deltaPixel = position - this.currentPosition();
+    if (deltaPixel != 0) {
+      this.animating = true;
+      this.fire("scroll:started");
+
+      var that = this;
+      // Move effects
+      this.container.morph("opacity:0.5", {duration: 0.2, afterFinish: function() {
+        that.container.morph(that.posAttribute + ": " + position + "px", {
+          duration: 0.4,
+          delay: 0.2,
+          afterFinish: function() {
+            that.container.morph("opacity:1", {
+              duration: 0.2,
+              afterFinish: function() {
+                that.animating = false;
+                that.updateButtons()
+                  .fire("scroll:ended", { shift: deltaPixel / that.currentSize() });
+              }
+            });
+          }
+        });
+      }});
+    }
+    return this;
+  },
+
+  /*
+    Method: scrollTo
+      Scrolls carousel, so that element with specified index is the left-most.
+      This method is convenient when using carousel in a tabbed navigation.
+      Clicking on first tab should scroll first container into view, clicking on a fifth - fifth one, etc.
+      Indexing starts with 0.
+
+    Parameters:
+      Index of an element which will be a left-most visible in the carousel
+
+    Returns:
+      this
+  */
+  scrollTo: function(index) {
+    if (this.animating || index < 0 || index > this.elements.length || index == this.currentIndex() || isNaN(parseInt(index)))
+      return this;
+    return this.scroll((this.currentIndex() - index) * this.elementSize);
+  },
+
+  /*
+    Method: updateButtons
+      Update buttons status to enabled or disabled
+      Them status is defined by classNames and fired as carousel's custom events
+
+    Returns:
+      this
+  */
+  updateButtons: function() {
+      this.updatePreviousButton();
+    this.updateNextButton();
+    return this;
+  },
+
+  updatePreviousButton: function() {
+    var position = this.currentPosition();
+    var previousClassName = "previous_button" + this.options.disabledButtonSuffix;
+
+    if (this.previousButton.hasClassName(previousClassName) && position != 0) {
+      this.previousButton.removeClassName(previousClassName);
+      this.fire('previousButton:enabled');
+    }
+    if (!this.previousButton.hasClassName(previousClassName) && position == 0) {
+        this.previousButton.addClassName(previousClassName);
+      this.fire('previousButton:disabled');
+    }
+  },
+
+  updateNextButton: function() {
+    var lastPosition = this.currentLastPosition();
+    var size = this.currentSize();
+    var nextClassName = "next_button" + this.options.disabledButtonSuffix;
+
+    if (this.nextButton.hasClassName(nextClassName) && lastPosition != size) {
+      this.nextButton.removeClassName(nextClassName);
+      this.fire('nextButton:enabled');
+    }
+    if (!this.nextButton.hasClassName(nextClassName) && lastPosition == size) {
+        this.nextButton.addClassName(nextClassName);
+      this.fire('nextButton:disabled');
+    }
+  },
+
+  // Group: Size and Position
+
+  /*
+    Method: computeElementSize
+      Return elements size in pixel, height or width depends on carousel orientation.
+
+    Returns:
+      an integer value
+  */
+  computeElementSize: function() {
+    return this.elements.first().getDimensions()[this.dimAttribute];
+  },
+
+  /*
+    Method: currentIndex
+      Returns current visible index of a carousel.
+      For example, a horizontal carousel with image #3 on left will return 3 and with half of image #3 will return 3.5
+      Don't forget that the first image have an index 0
+
+    Returns:
+      a float value
+  */
+  currentIndex: function() {
+    return - this.currentPosition() / this.elementSize;
+  },
+
+  /*
+    Method: currentLastPosition
+      Returns the current position from the end of the last element. This value is in pixel.
+
+    Returns:
+      an integer value, if no images a present it will return 0
+  */
+  currentLastPosition: function() {
+    if (this.container.childElements().empty())
+      return 0;
+    return this.currentPosition() +
+           this.elements.last().positionedOffset()[this.posAttribute] +
+           this.elementSize;
+  },
+
+  /*
+    Method: currentPosition
+      Returns the current position in pixel.
+      Tips: To get the position in elements use currentIndex()
+
+    Returns:
+      an integer value
+  */
+  currentPosition: function() {
+    return this.container.getNumStyle(this.posAttribute);
+  },
+
+  /*
+    Method: currentSize
+      Returns the current size of the carousel in pixel
+
+    Returns:
+      Carousel's size in pixel
+  */
+  currentSize: function() {
+    return this.container.parentNode.getDimensions()[this.dimAttribute];
+  },
+
+  /*
+    Method: updateSize
+      Should be called if carousel size has been changed (usually called with a liquid layout)
+
+    Returns:
+      this
+  */
+  updateSize: function() {
+    this.nbVisible = this.currentSize() / this.elementSize;
+    var scrollInc = this.options.scrollInc;
+    if (scrollInc == "auto")
+      scrollInc = Math.floor(this.nbVisible);
+
+    [ this.previousButton, this.nextButton ].each(function(button) {
+      if (!button) return;
+      button.stopObserving("click", button.clickHandler);
+      button.clickHandler = this.scroll.bind(this, (button == this.nextButton ? -1 : 1) * scrollInc * this.elementSize);
+      button.observe("click", button.clickHandler);
+    }, this);
+
+    this.checkScroll(this.currentPosition(), true);
+    this.updateButtons().fire('sizeUpdated');
+    return this;
+  }
+});
+/*
+  Class: UI.Ajax.Carousel
+
+  Gives the AJAX power to carousels. An AJAX carousel :
+    * Use AJAX to add new elements on the fly
+
+  Example:
+    > new UI.Ajax.Carousel("horizontal_carousel",
+    >   {url: "get-more-elements", elementSize: 250});
+*/
+UI.Ajax.Carousel = Class.create(UI.Carousel, {
+  // Group: Options
+  //
+  //   Notice:
+  //     It also include of all carousel's options
+  options: {
+    // Property: elementSize
+    //   Required, it define the size of all elements
+    elementSize : -1,
+
+    // Property: url
+    //   Required, it define the URL used by AJAX carousel to request new elements details
+    url         : null
+  },
+
+  /*
+    Group: Attributes
+
+      Notice:
+        It also include of all carousel's attributes
+
+      Property: elementSize
+        Size of each elements, it's an integer
+
+      Property: endIndex
+        Index of the last loaded element
+
+      Property: hasMore
+        Flag to define if there's still more elements to load
+
+      Property: requestRunning
+        Define whether a request is processing or not
+
+      Property: updateHandler
+        Callback to update carousel, usually used after request success
+
+      Property: url
+        URL used to request additional elements
+  */
+
+  /*
+    Group: Events
+      List of events fired by an AJAX carousel, it also include of all carousel's custom events
+
+      Property: request:started
+        Fired when the request has just started
+
+      Property: request:ended
+        Fired when the request has succeed
+  */
+
+  // Group: Constructor
+
+  /*
+    Method: initialize
+      Constructor function, should not be called directly
+
+    Parameters:
+      element - DOM element
+      options - (Hash) list of optional parameters
+
+    Returns:
+      this
+  */
+  initialize: function($super, element, options) {
+    if (!options.url)
+      throw("url option is required for UI.Ajax.Carousel");
+    if (!options.elementSize)
+      throw("elementSize option is required for UI.Ajax.Carousel");
+
+    $super(element, options);
+
+    this.endIndex = 0;
+    this.hasMore  = true;
+
+    // Cache handlers
+    this.updateHandler = this.update.bind(this);
+    this.updateAndScrollHandler = function(nbElements, transport, json) {
+        this.update(transport, json);
+        this.scroll(nbElements);
+      }.bind(this);
+
+    // Run first ajax request to fill the carousel
+    this.runRequest.bind(this).defer({parameters: {from: 0, to: Math.floor(this.nbVisible)}, onSuccess: this.updateHandler});
+  },
+
+  // Group: Actions
+
+  /*
+    Method: runRequest
+      Request the new elements details
+
+    Parameters:
+      options - (Hash) list of optional parameters
+
+    Returns:
+      this
+  */
+  runRequest: function(options) {
+    this.requestRunning = true;
+    //alert("Asking for: " + options.parameters.from + " - " + options.parameters.to);
+    new Ajax.Request(this.options.url, Object.extend({method: "GET"}, options));
+    this.fire("request:started");
+    return this;
+  },
+
+  /*
+    Method: scroll
+      Scrolls carousel from maximum deltaPixel
+
+    Parameters:
+      deltaPixel - a float
+
+    Returns:
+      this
+  */
+  scroll: function($super, deltaPixel) {
+    if (this.animating || this.requestRunning)
+      return this;
+
+    var nbElements = (-deltaPixel) / this.elementSize;
+    // Check if there is not enough
+    if (this.hasMore && nbElements > 0 && this.currentIndex() + this.nbVisible + nbElements - 1 > this.endIndex) {
+      var from = this.endIndex + 1;
+      var to   = Math.floor(from + this.nbVisible - 1);
+      this.runRequest({parameters: {from: from, to: to}, onSuccess: this.updateAndScrollHandler.curry(deltaPixel).bind(this)});
+      return this;
+    }
+    else
+      $super(deltaPixel);
+  },
+
+  /*
+    Method: update
+      Update the carousel
+
+    Parameters:
+      transport - XMLHttpRequest object
+      json      - JSON object
+
+    Returns:
+      this
+  */
+  update: function(transport, json) {
+    this.requestRunning = false;
+    this.fire("request:ended");
+    if (!json)
+      json = transport.responseJSON;
+    this.hasMore = json.more;
+
+    this.endIndex = Math.max(this.endIndex, json.to);
+    this.elements = this.container.insert({bottom: json.html}).childElements();
+    return this.updateButtons();
+  },
+
+  // Group: Size and Position
+
+  /*
+    Method: computeElementSize
+      Return elements size in pixel
+
+    Returns:
+      an integer value
+  */
+  computeElementSize: function() {
+    return this.options.elementSize;
+  },
+
+  /*
+    Method: updateSize
+      Should be called if carousel size has been changed (usually called with a liquid layout)
+
+    Returns:
+      this
+  */
+  updateSize: function($super) {
+    var nbVisible = this.nbVisible;
+    $super();
+    // If we have enough space for at least a new element
+    if (Math.floor(this.nbVisible) - Math.floor(nbVisible) >= 1 && this.hasMore) {
+      if (this.currentIndex() + Math.floor(this.nbVisible) >= this.endIndex) {
+        var nbNew = Math.floor(this.currentIndex() + Math.floor(this.nbVisible) - this.endIndex);
+        this.runRequest({parameters: {from: this.endIndex + 1, to: this.endIndex + nbNew}, onSuccess: this.updateHandler});
+      }
+    }
+    return this;
+  },
+
+  updateNextButton: function($super) {
+    var lastPosition = this.currentLastPosition();
+    var size = this.currentSize();
+    var nextClassName = "next_button" + this.options.disabledButtonSuffix;
+
+    if (this.nextButton.hasClassName(nextClassName) && lastPosition != size) {
+      this.nextButton.removeClassName(nextClassName);
+      this.fire('nextButton:enabled');
+    }
+    if (!this.nextButton.hasClassName(nextClassName) && lastPosition == size && !this.hasMore) {
+        this.nextButton.addClassName(nextClassName);
+      this.fire('nextButton:disabled');
+    }
+  }
+});
diff --git a/ansel/js/src/cropper.js b/ansel/js/src/cropper.js
new file mode 100644 (file)
index 0000000..08519bb
--- /dev/null
@@ -0,0 +1,555 @@
+/**
+ * Copyright 2006, David Spurr (http://www.defusion.org.uk/)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the David Spurr nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * See scriptaculous.js for full scriptaculous licence
+ */
+
+var CropDraggable=Class.create();
+Object.extend(Object.extend(CropDraggable.prototype,Draggable.prototype),{initialize:function(_1){
+this.options=Object.extend({drawMethod:function(){
+}},arguments[1]||{});
+this.element=$(_1);
+this.handle=this.element;
+this.delta=this.currentDelta();
+this.dragging=false;
+this.eventMouseDown=this.initDrag.bindAsEventListener(this);
+Event.observe(this.handle,"mousedown",this.eventMouseDown);
+Draggables.register(this);
+},draw:function(_2){
+var _3=Position.cumulativeOffset(this.element);
+var d=this.currentDelta();
+_3[0]-=d[0];
+_3[1]-=d[1];
+var p=[0,1].map(function(i){
+return (_2[i]-_3[i]-this.offset[i]);
+}.bind(this));
+this.options.drawMethod(p);
+}});
+var Cropper={};
+Cropper.Img=Class.create();
+Cropper.Img.prototype={initialize:function(_7,_8){
+this.options=Object.extend({ratioDim:{x:0,y:0},minWidth:0,minHeight:0,displayOnInit:false,onEndCrop:Prototype.emptyFunction,captureKeys:true,onloadCoords:null,maxWidth:0,maxHeight:0},_8||{});
+this.img=$(_7);
+this.clickCoords={x:0,y:0};
+this.dragging=false;
+this.resizing=false;
+this.isWebKit=/Konqueror|Safari|KHTML/.test(navigator.userAgent);
+this.isIE=/MSIE/.test(navigator.userAgent);
+this.isOpera8=/Opera\s[1-8]/.test(navigator.userAgent);
+this.ratioX=0;
+this.ratioY=0;
+this.attached=false;
+this.fixedWidth=(this.options.maxWidth>0&&(this.options.minWidth>=this.options.maxWidth));
+this.fixedHeight=(this.options.maxHeight>0&&(this.options.minHeight>=this.options.maxHeight));
+if(typeof this.img=="undefined"){
+return;
+}
+if(this.options.ratioDim.x>0&&this.options.ratioDim.y>0){
+var _c=this.getGCD(this.options.ratioDim.x,this.options.ratioDim.y);
+this.ratioX=this.options.ratioDim.x/_c;
+this.ratioY=this.options.ratioDim.y/_c;
+}
+this.subInitialize();
+if(this.img.complete||this.isWebKit){
+this.onLoad();
+}else{
+Event.observe(this.img,"load",this.onLoad.bindAsEventListener(this));
+}
+},getGCD:function(a,b){
+if(b==0){
+return a;
+}
+return this.getGCD(b,a%b);
+},onLoad:function(){
+var _f="imgCrop_";
+var _10=this.img.parentNode;
+var _11="";
+if(this.isOpera8){
+_11=" opera8";
+}
+this.imgWrap=Builder.node("div",{"class":_f+"wrap"+_11});
+this.north=Builder.node("div",{"class":_f+"overlay "+_f+"north"},[Builder.node("span")]);
+this.east=Builder.node("div",{"class":_f+"overlay "+_f+"east"},[Builder.node("span")]);
+this.south=Builder.node("div",{"class":_f+"overlay "+_f+"south"},[Builder.node("span")]);
+this.west=Builder.node("div",{"class":_f+"overlay "+_f+"west"},[Builder.node("span")]);
+var _12=[this.north,this.east,this.south,this.west];
+this.dragArea=Builder.node("div",{"class":_f+"dragArea"},_12);
+this.handleN=Builder.node("div",{"class":_f+"handle "+_f+"handleN"});
+this.handleNE=Builder.node("div",{"class":_f+"handle "+_f+"handleNE"});
+this.handleE=Builder.node("div",{"class":_f+"handle "+_f+"handleE"});
+this.handleSE=Builder.node("div",{"class":_f+"handle "+_f+"handleSE"});
+this.handleS=Builder.node("div",{"class":_f+"handle "+_f+"handleS"});
+this.handleSW=Builder.node("div",{"class":_f+"handle "+_f+"handleSW"});
+this.handleW=Builder.node("div",{"class":_f+"handle "+_f+"handleW"});
+this.handleNW=Builder.node("div",{"class":_f+"handle "+_f+"handleNW"});
+this.selArea=Builder.node("div",{"class":_f+"selArea"},[Builder.node("div",{"class":_f+"marqueeHoriz "+_f+"marqueeNorth"},[Builder.node("span")]),Builder.node("div",{"class":_f+"marqueeVert "+_f+"marqueeEast"},[Builder.node("span")]),Builder.node("div",{"class":_f+"marqueeHoriz "+_f+"marqueeSouth"},[Builder.node("span")]),Builder.node("div",{"class":_f+"marqueeVert "+_f+"marqueeWest"},[Builder.node("span")]),this.handleN,this.handleNE,this.handleE,this.handleSE,this.handleS,this.handleSW,this.handleW,this.handleNW,Builder.node("div",{"class":_f+"clickArea"})]);
+this.imgWrap.appendChild(this.img);
+this.imgWrap.appendChild(this.dragArea);
+this.dragArea.appendChild(this.selArea);
+this.dragArea.appendChild(Builder.node("div",{"class":_f+"clickArea"}));
+_10.appendChild(this.imgWrap);
+this.startDragBind=this.startDrag.bindAsEventListener(this);
+Event.observe(this.dragArea,"mousedown",this.startDragBind);
+this.onDragBind=this.onDrag.bindAsEventListener(this);
+Event.observe(document,"mousemove",this.onDragBind);
+this.endCropBind=this.endCrop.bindAsEventListener(this);
+Event.observe(document,"mouseup",this.endCropBind);
+this.resizeBind=this.startResize.bindAsEventListener(this);
+this.handles=[this.handleN,this.handleNE,this.handleE,this.handleSE,this.handleS,this.handleSW,this.handleW,this.handleNW];
+this.registerHandles(true);
+if(this.options.captureKeys){
+this.keysBind=this.handleKeys.bindAsEventListener(this);
+Event.observe(document,"keypress",this.keysBind);
+}
+new CropDraggable(this.selArea,{drawMethod:this.moveArea.bindAsEventListener(this)});
+this.setParams();
+},registerHandles:function(_13){
+for(var i=0;i<this.handles.length;i++){
+var _15=$(this.handles[i]);
+if(_13){
+var _16=false;
+if(this.fixedWidth&&this.fixedHeight){
+_16=true;
+}else{
+if(this.fixedWidth||this.fixedHeight){
+var _17=_15.className.match(/([S|N][E|W])$/);
+var _18=_15.className.match(/(E|W)$/);
+var _19=_15.className.match(/(N|S)$/);
+if(_17){
+_16=true;
+}else{
+if(this.fixedWidth&&_18){
+_16=true;
+}else{
+if(this.fixedHeight&&_19){
+_16=true;
+}
+}
+}
+}
+}
+if(_16){
+_15.hide();
+}else{
+Event.observe(_15,"mousedown",this.resizeBind);
+}
+}else{
+_15.show();
+Event.stopObserving(_15,"mousedown",this.resizeBind);
+}
+}
+},setParams:function(){
+this.imgW=this.img.width;
+this.imgH=this.img.height;
+$(this.north).setStyle({height:0});
+$(this.east).setStyle({width:0,height:0});
+$(this.south).setStyle({height:0});
+$(this.west).setStyle({width:0,height:0});
+$(this.imgWrap).setStyle({"width":this.imgW+"px","height":this.imgH+"px"});
+$(this.selArea).hide();
+var _1a={x1:0,y1:0,x2:0,y2:0};
+var _1b=false;
+if(this.options.onloadCoords!=null){
+_1a=this.cloneCoords(this.options.onloadCoords);
+_1b=true;
+}else{
+if(this.options.ratioDim.x>0&&this.options.ratioDim.y>0){
+_1a.x1=Math.ceil((this.imgW-this.options.ratioDim.x)/2);
+_1a.y1=Math.ceil((this.imgH-this.options.ratioDim.y)/2);
+_1a.x2=_1a.x1+this.options.ratioDim.x;
+_1a.y2=_1a.y1+this.options.ratioDim.y;
+_1b=true;
+}
+}
+this.setAreaCoords(_1a,false,false,1);
+if(this.options.displayOnInit&&_1b){
+this.selArea.show();
+this.drawArea();
+this.endCrop();
+}
+this.attached=true;
+},remove:function(){
+if(this.attached){
+this.attached=false;
+this.imgWrap.parentNode.insertBefore(this.img,this.imgWrap);
+this.imgWrap.parentNode.removeChild(this.imgWrap);
+Event.stopObserving(this.dragArea,"mousedown",this.startDragBind);
+Event.stopObserving(document,"mousemove",this.onDragBind);
+Event.stopObserving(document,"mouseup",this.endCropBind);
+this.registerHandles(false);
+if(this.options.captureKeys){
+Event.stopObserving(document,"keypress",this.keysBind);
+}
+}
+},reset:function(){
+if(!this.attached){
+this.onLoad();
+}else{
+this.setParams();
+}
+this.endCrop();
+},handleKeys:function(e){
+var dir={x:0,y:0};
+if(!this.dragging){
+switch(e.keyCode){
+case (37):
+dir.x=-1;
+break;
+case (38):
+dir.y=-1;
+break;
+case (39):
+dir.x=1;
+break;
+case (40):
+dir.y=1;
+break;
+}
+if(dir.x!=0||dir.y!=0){
+if(e.shiftKey){
+dir.x*=10;
+dir.y*=10;
+}
+this.moveArea([this.areaCoords.x1+dir.x,this.areaCoords.y1+dir.y]);
+Event.stop(e);
+}
+}
+},calcW:function(){
+return (this.areaCoords.x2-this.areaCoords.x1);
+},calcH:function(){
+return (this.areaCoords.y2-this.areaCoords.y1);
+},moveArea:function(_1e){
+this.setAreaCoords({x1:_1e[0],y1:_1e[1],x2:_1e[0]+this.calcW(),y2:_1e[1]+this.calcH()},true,false);
+this.drawArea();
+},cloneCoords:function(_1f){
+return {x1:_1f.x1,y1:_1f.y1,x2:_1f.x2,y2:_1f.y2};
+},setAreaCoords:function(_20,_21,_22,_23,_24){
+if(_21){
+var _25=_20.x2-_20.x1;
+var _26=_20.y2-_20.y1;
+if(_20.x1<0){
+_20.x1=0;
+_20.x2=_25;
+}
+if(_20.y1<0){
+_20.y1=0;
+_20.y2=_26;
+}
+if(_20.x2>this.imgW){
+_20.x2=this.imgW;
+_20.x1=this.imgW-_25;
+}
+if(_20.y2>this.imgH){
+_20.y2=this.imgH;
+_20.y1=this.imgH-_26;
+}
+}else{
+if(_20.x1<0){
+_20.x1=0;
+}
+if(_20.y1<0){
+_20.y1=0;
+}
+if(_20.x2>this.imgW){
+_20.x2=this.imgW;
+}
+if(_20.y2>this.imgH){
+_20.y2=this.imgH;
+}
+if(_23!=null){
+if(this.ratioX>0){
+this.applyRatio(_20,{x:this.ratioX,y:this.ratioY},_23,_24);
+}else{
+if(_22){
+this.applyRatio(_20,{x:1,y:1},_23,_24);
+}
+}
+var _27=[this.options.minWidth,this.options.minHeight];
+var _28=[this.options.maxWidth,this.options.maxHeight];
+if(_27[0]>0||_27[1]>0||_28[0]>0||_28[1]>0){
+var _29={a1:_20.x1,a2:_20.x2};
+var _2a={a1:_20.y1,a2:_20.y2};
+var _2b={min:0,max:this.imgW};
+var _2c={min:0,max:this.imgH};
+if((_27[0]!=0||_27[1]!=0)&&_22){
+if(_27[0]>0){
+_27[1]=_27[0];
+}else{
+if(_27[1]>0){
+_27[0]=_27[1];
+}
+}
+}
+if((_28[0]!=0||_28[0]!=0)&&_22){
+if(_28[0]>0&&_28[0]<=_28[1]){
+_28[1]=_28[0];
+}else{
+if(_28[1]>0&&_28[1]<=_28[0]){
+_28[0]=_28[1];
+}
+}
+}
+if(_27[0]>0){
+this.applyDimRestriction(_29,_27[0],_23.x,_2b,"min");
+}
+if(_27[1]>1){
+this.applyDimRestriction(_2a,_27[1],_23.y,_2c,"min");
+}
+if(_28[0]>0){
+this.applyDimRestriction(_29,_28[0],_23.x,_2b,"max");
+}
+if(_28[1]>1){
+this.applyDimRestriction(_2a,_28[1],_23.y,_2c,"max");
+}
+_20={x1:_29.a1,y1:_2a.a1,x2:_29.a2,y2:_2a.a2};
+}
+}
+}
+this.areaCoords=_20;
+},applyDimRestriction:function(_2d,val,_2f,_30,_31){
+var _32;
+if(_31=="min"){
+_32=((_2d.a2-_2d.a1)<val);
+}else{
+_32=((_2d.a2-_2d.a1)>val);
+}
+if(_32){
+if(_2f==1){
+_2d.a2=_2d.a1+val;
+}else{
+_2d.a1=_2d.a2-val;
+}
+if(_2d.a1<_30.min){
+_2d.a1=_30.min;
+_2d.a2=val;
+}else{
+if(_2d.a2>_30.max){
+_2d.a1=_30.max-val;
+_2d.a2=_30.max;
+}
+}
+}
+},applyRatio:function(_33,_34,_35,_36){
+var _37;
+if(_36=="N"||_36=="S"){
+_37=this.applyRatioToAxis({a1:_33.y1,b1:_33.x1,a2:_33.y2,b2:_33.x2},{a:_34.y,b:_34.x},{a:_35.y,b:_35.x},{min:0,max:this.imgW});
+_33.x1=_37.b1;
+_33.y1=_37.a1;
+_33.x2=_37.b2;
+_33.y2=_37.a2;
+}else{
+_37=this.applyRatioToAxis({a1:_33.x1,b1:_33.y1,a2:_33.x2,b2:_33.y2},{a:_34.x,b:_34.y},{a:_35.x,b:_35.y},{min:0,max:this.imgH});
+_33.x1=_37.a1;
+_33.y1=_37.b1;
+_33.x2=_37.a2;
+_33.y2=_37.b2;
+}
+},applyRatioToAxis:function(_38,_39,_3a,_3b){
+var _3c=Object.extend(_38,{});
+var _3d=_3c.a2-_3c.a1;
+var _3e=Math.floor(_3d*_39.b/_39.a);
+var _3f;
+var _40;
+var _41=null;
+if(_3a.b==1){
+_3f=_3c.b1+_3e;
+if(_3f>_3b.max){
+_3f=_3b.max;
+_41=_3f-_3c.b1;
+}
+_3c.b2=_3f;
+}else{
+_3f=_3c.b2-_3e;
+if(_3f<_3b.min){
+_3f=_3b.min;
+_41=_3f+_3c.b2;
+}
+_3c.b1=_3f;
+}
+if(_41!=null){
+_40=Math.floor(_41*_39.a/_39.b);
+if(_3a.a==1){
+_3c.a2=_3c.a1+_40;
+}else{
+_3c.a1=_3c.a1=_3c.a2-_40;
+}
+}
+return _3c;
+},drawArea:function(){
+var _42=this.calcW();
+var _43=this.calcH();
+var px="px";
+var _45=[this.areaCoords.x1+px,this.areaCoords.y1+px,_42+px,_43+px,this.areaCoords.x2+px,this.areaCoords.y2+px,(this.img.width-this.areaCoords.x2)+px,(this.img.height-this.areaCoords.y2)+px];
+var _46=this.selArea.style;
+_46.left=_45[0];
+_46.top=_45[1];
+_46.width=_45[2];
+_46.height=_45[3];
+var _47=Math.ceil((_42-6)/2)+px;
+var _48=Math.ceil((_43-6)/2)+px;
+this.handleN.style.left=_47;
+this.handleE.style.top=_48;
+this.handleS.style.left=_47;
+this.handleW.style.top=_48;
+this.north.style.height=_45[1];
+var _49=this.east.style;
+_49.top=_45[1];
+_49.height=_45[3];
+_49.left=_45[4];
+_49.width=_45[6];
+var _4a=this.south.style;
+_4a.top=_45[5];
+_4a.height=_45[7];
+var _4b=this.west.style;
+_4b.top=_45[1];
+_4b.height=_45[3];
+_4b.width=_45[0];
+this.subDrawArea();
+this.forceReRender();
+},forceReRender:function(){
+if(this.isIE||this.isWebKit){
+var n=document.createTextNode(" ");
+var d,el,fixEL,i;
+if(this.isIE){
+fixEl=this.selArea;
+}else{
+if(this.isWebKit){
+fixEl=document.getElementsByClassName("imgCrop_marqueeSouth",this.imgWrap)[0];
+d=Builder.node("div","");
+d.style.visibility="hidden";
+var _4e=["SE","S","SW"];
+for(i=0;i<_4e.length;i++){
+el=document.getElementsByClassName("imgCrop_handle"+_4e[i],this.selArea)[0];
+if(el.childNodes.length){
+el.removeChild(el.childNodes[0]);
+}
+el.appendChild(d);
+}
+}
+}
+fixEl.appendChild(n);
+fixEl.removeChild(n);
+}
+},startResize:function(e){
+this.startCoords=this.cloneCoords(this.areaCoords);
+this.resizing=true;
+this.resizeHandle=Event.element(e).classNames().toString().replace(/([^N|NE|E|SE|S|SW|W|NW])+/,"");
+Event.stop(e);
+},startDrag:function(e){
+this.selArea.show();
+this.clickCoords=this.getCurPos(e);
+this.setAreaCoords({x1:this.clickCoords.x,y1:this.clickCoords.y,x2:this.clickCoords.x,y2:this.clickCoords.y},false,false,null);
+this.dragging=true;
+this.onDrag(e);
+Event.stop(e);
+},getCurPos:function(e){
+var el=this.imgWrap,wrapOffsets=Position.cumulativeOffset(el);
+while(el.nodeName!="BODY"){
+wrapOffsets[1]-=el.scrollTop||0;
+wrapOffsets[0]-=el.scrollLeft||0;
+el=el.parentNode;
+}
+return curPos={x:Event.pointerX(e)-wrapOffsets[0],y:Event.pointerY(e)-wrapOffsets[1]};
+},onDrag:function(e){
+if(this.dragging||this.resizing){
+var _54=null;
+var _55=this.getCurPos(e);
+var _56=this.cloneCoords(this.areaCoords);
+var _57={x:1,y:1};
+if(this.dragging){
+if(_55.x<this.clickCoords.x){
+_57.x=-1;
+}
+if(_55.y<this.clickCoords.y){
+_57.y=-1;
+}
+this.transformCoords(_55.x,this.clickCoords.x,_56,"x");
+this.transformCoords(_55.y,this.clickCoords.y,_56,"y");
+}else{
+if(this.resizing){
+_54=this.resizeHandle;
+if(_54.match(/E/)){
+this.transformCoords(_55.x,this.startCoords.x1,_56,"x");
+if(_55.x<this.startCoords.x1){
+_57.x=-1;
+}
+}else{
+if(_54.match(/W/)){
+this.transformCoords(_55.x,this.startCoords.x2,_56,"x");
+if(_55.x<this.startCoords.x2){
+_57.x=-1;
+}
+}
+}
+if(_54.match(/N/)){
+this.transformCoords(_55.y,this.startCoords.y2,_56,"y");
+if(_55.y<this.startCoords.y2){
+_57.y=-1;
+}
+}else{
+if(_54.match(/S/)){
+this.transformCoords(_55.y,this.startCoords.y1,_56,"y");
+if(_55.y<this.startCoords.y1){
+_57.y=-1;
+}
+}
+}
+}
+}
+this.setAreaCoords(_56,false,e.shiftKey,_57,_54);
+this.drawArea();
+Event.stop(e);
+}
+},transformCoords:function(_58,_59,_5a,_5b){
+var _5c=[_58,_59];
+if(_58>_59){
+_5c.reverse();
+}
+_5a[_5b+"1"]=_5c[0];
+_5a[_5b+"2"]=_5c[1];
+},endCrop:function(){
+this.dragging=false;
+this.resizing=false;
+this.options.onEndCrop(this.areaCoords,{width:this.calcW(),height:this.calcH()});
+},subInitialize:function(){
+},subDrawArea:function(){
+}};
+Cropper.ImgWithPreview=Class.create();
+Object.extend(Object.extend(Cropper.ImgWithPreview.prototype,Cropper.Img.prototype),{subInitialize:function(){
+this.hasPreviewImg=false;
+if(typeof (this.options.previewWrap)!="undefined"&&this.options.minWidth>0&&this.options.minHeight>0){
+this.previewWrap=$(this.options.previewWrap);
+this.previewImg=this.img.cloneNode(false);
+this.previewImg.id="imgCrop_"+this.previewImg.id;
+this.options.displayOnInit=true;
+this.hasPreviewImg=true;
+this.previewWrap.addClassName("imgCrop_previewWrap");
+this.previewWrap.setStyle({width:this.options.minWidth+"px",height:this.options.minHeight+"px"});
+this.previewWrap.appendChild(this.previewImg);
+}
+},subDrawArea:function(){
+if(this.hasPreviewImg){
+var _5d=this.calcW();
+var _5e=this.calcH();
+var _5f={x:this.imgW/_5d,y:this.imgH/_5e};
+var _60={x:_5d/this.options.minWidth,y:_5e/this.options.minHeight};
+var _61={w:Math.ceil(this.options.minWidth*_5f.x)+"px",h:Math.ceil(this.options.minHeight*_5f.y)+"px",x:"-"+Math.ceil(this.areaCoords.x1/_60.x)+"px",y:"-"+Math.ceil(this.areaCoords.y1/_60.y)+"px"};
+var _62=this.previewImg.style;
+_62.width=_61.w;
+_62.height=_61.h;
+_62.left=_61.x;
+_62.top=_61.y;
+}
+}});
+
diff --git a/ansel/js/src/editcaption.js b/ansel/js/src/editcaption.js
new file mode 100644 (file)
index 0000000..3b49ac2
--- /dev/null
@@ -0,0 +1,38 @@
+// InPlaceEditor extension based somewhat on an example given in the
+// scriptaculous wiki
+Ajax.InPlaceEditor.prototype.__initialize = Ajax.InPlaceEditor.prototype.initialize;
+Ajax.InPlaceEditor.prototype.__getText = Ajax.InPlaceEditor.prototype.getText;
+Object.extend(Ajax.InPlaceEditor.prototype, {
+    initialize: function(element, url, options) {
+        this.__initialize(element, url, options);
+        this.setOptions(options);
+        // Remove this line to stop from auto-showing the
+        // empty caption text on page load.
+        this.checkEmpty();
+    },
+
+    setOptions: function(options) {
+        this.options = Object.extend(Object.extend(this.options, {
+            emptyClassName: 'inplaceeditor-empty'
+        }),options||{});
+    },
+
+    checkEmpty: function() {
+        if (this.element.innerHTML.length == 0) {
+            emptyNode = new Element('span', {className: this.options.emptyClassName}).update(this.options.emptyText);
+            this.element.appendChild(emptyNode);
+        }
+    },
+
+    getText: function() {
+        $(this.element).select('.' + this.options.emptyClassName).each(function(child) {
+            this.element.removeChild(child);
+        }.bind(this));
+        return this.__getText();
+    }
+});
+
+function tileExit(ipe, e)
+{
+    ipe.checkEmpty();
+}
diff --git a/ansel/js/src/editfaces.js b/ansel/js/src/editfaces.js
new file mode 100644 (file)
index 0000000..4657ac1
--- /dev/null
@@ -0,0 +1,56 @@
+document.observe('dom:loaded', function() {
+    Ansel.deleteFace = function(image_id, face_id)
+    {
+        new Ajax.Request(Ansel.ajax.editFaces.url, 
+            {
+                method: 'post',
+                parameters: {
+                                 action: 'delete',
+                                 image: image_id,
+                                 face: face_id
+                            }
+            });
+        $('face' + face_id).remove();
+    };
+    
+    Ansel.setFaceName = function(image_id, face_id)
+    {
+        new Ajax.Request(Ansel.ajax.editFaces.url,
+                         {    
+                              method: 'post',
+                              parameters: 
+                              {
+                                  action: 'setname',
+                                  face: face_id,
+                                  image: image_id,
+                                  facename: encodeURIComponent($F('facename' + face_id))
+                              },
+                              onComplete: function(r) {
+                                  if (r.responseJSON.response == 1) {
+                                      $('faces_widget_content').update(r.responseJSON.message);
+                                  }
+                              }
+                         }
+        );
+    };
+    
+    Ansel.doFaceEdit = function(image_id)
+    {
+        $('faces_widget_content').update(Ansel.ajax.editFaces.text.loading);
+        new Ajax.Request(Ansel.ajax.editFaces.url,
+                         {
+                             method: 'post',
+                             parameters:
+                             {
+                                 action: 'process',
+                                 image: image_id
+                             },
+                             onComplete: function(r) {
+                                 if (r.responseJSON.response == 1) {
+                                     $('faces_widget_content').update(r.responseJSON.message);
+                                 }
+                             }
+                         }
+        );
+    };
+});
\ No newline at end of file
diff --git a/ansel/js/src/embed.js b/ansel/js/src/embed.js
new file mode 100755 (executable)
index 0000000..f7f49e6
--- /dev/null
@@ -0,0 +1,158 @@
+//<![CDATA[
+Event.observe(window, 'load', function() {
+    // The number of unique, embedded instances
+    var nodeCount = anselnodes.length;
+
+    // Holds any lightbox json
+    var lightboxData = new Array();
+
+    // Iterate over each embedded instance and create the DOM elements.
+    for (var n = 0; n < nodeCount; n++) {
+
+        // j is the textual name of the container, used as a key
+        var j = anselnodes[n];
+
+        // Do we have any lightbox data?
+        if (typeof anseljson[j]['lightbox'] != 'undefined') {
+            lightboxData = lightboxData.concat(anseljson[j]['lightbox']);
+        }
+
+        // Top level DOM node for this embedded instannce
+        var mainNode = $(j);
+
+        // Used if we have requested the optional paging feature
+        if (anseljson[j]['perpage']) {
+            var pagecount = anseljson[j]['perpage'];
+        } else {
+            var pagecount = anseljson[j]['data'].size();
+        }
+
+        // For each image in this instance, create the DOM structure
+        for (var i = 0; i < pagecount; i++) {
+            // Need a nested function and closures to force new scope
+            (function() {
+                var jx = j;
+                var ix = i;
+                var imgContainer = new Element('span', {className: 'anselGalleryWidget'});
+                if (!anseljson[jx]['hideLinks']) {
+                    if (anseljson[jx]['linkToGallery']) {
+                        var idx = 6;
+                    } else {
+                        var idx = 5;
+                    }
+                    var imgLink = imgContainer.appendChild(new Element('a',
+                        {
+                            href: anseljson[jx]['data'][ix][idx],
+                            title: anseljson[jx]['data'][ix][2]
+                         }));
+                   var lb_data = {image: anseljson[jx]['data'][ix][3]};
+                   imgLink.appendChild(new Element('img', {src: anseljson[jx]['data'][ix][0]}));
+                   // Attach the lightbox action if we have lightbox data
+                   if (typeof anseljson[j]['lightbox'] != 'undefined') {
+                       imgLink.observe('click', function(e) {ansel_lb.start(lb_data.image); e.stop();});
+                   }
+                } else {
+                    imgContainer.appendChild(new Element('img', {src: anseljson[jx]['data'][ix][0]}));
+                    // Attach the lightbox action if we have lightbox data
+                    if (typeof anseljson[j]['lightbox'] != 'undefined') {
+                        imgLink.observe('click', function(e) {ansel_lb.start(lb_data.image); e.stop();});
+                    }
+                }
+
+                mainNode.appendChild(imgContainer);
+            })();
+        }
+
+        if (anseljson[j]['perpage'] > 0) {
+            (function() {
+                var jx = j;
+
+                var nextLink = new Element('a',{href: '#', title: 'Next Image', className: 'anselNext', style: 'text-decoration:none;width:40%;float:right;'});
+                nextLink.update('>>');
+                var arg1 = {node: jx, page: 1};
+                nextLink.observe('click', function(e) {displayPage(e, arg1)});
+
+                var prevLink = new Element('a',{href: '#', title: 'Previous Image', className: 'anselPrev', style: 'text-decoration:none;width:40%;float:right;'});
+                prevLink.update('<<');
+                var arg2 = {node: jx, page: -1};
+                prevLink.observe('click', function(e) {displayPage(e, arg2)});
+                $(jx).appendChild(nextLink);
+                $(jx).appendChild(prevLink);
+                Horde_ToolTips.attachBehavior(jx);
+                Event.observe(window, 'unload', Horde_ToolTips.out.bind(Horde_ToolTips));
+
+            })();
+        } else {
+            (function () {
+                var jx = j;
+                Horde_ToolTips.attachBehavior(jx);
+            })();
+        }
+    }
+    if (lightboxData.length) {
+        lbOptions['gallery_json'] = lightboxData;
+        ansel_lb = new Lightbox(lbOptions);
+    }
+
+    Event.observe(window, 'unload', Horde_ToolTips.out.bind(Horde_ToolTips));
+ });
+
+/**
+ * Display the images from the requested page for the requested node.
+ *
+ * @param string $node   The DOM id of the embedded widget.
+ * @param integer $page  The requested page number.
+ */
+function displayPage(event, args) {
+    var node = args.node;
+    var page = args.page;
+    var perpage = anseljson[node]['perpage'];
+    var imgcount = anseljson[node]['data'].size();
+    var pages = Math.ceil(imgcount / perpage) - 1;
+    var oldPage = anseljson[node]['page'];
+
+    page = oldPage + page;
+
+    /* Rollover? */
+    if (page > pages) {
+        page = 0;
+    }
+    if (page < 0) {
+        page = pages;
+    }
+
+    var mainNode = $(node);
+    mainNode.update();
+    var start = page * perpage;
+    var end = Math.min(imgcount - 1, start + perpage - 1);
+    for (var i = start; i <= end; i++) {
+        var imgContainer = mainNode.appendChild(new Element('span', {className: 'anselGalleryWidget'}));
+        var imgLink = imgContainer.appendChild(new Element('a',
+            {
+                href: anseljson[node]['data'][i][5],
+                alt: anseljson[node]['data'][i][2],
+                title: anseljson[node]['data'][i][2]
+             }));
+        imgLink.appendChild(new Element('img', {src: anseljson[node]['data'][i][0]}));
+    }
+
+     var nextLink = new Element('a',{href: '', title: 'Next Image', style: 'text-decoration:none;width:40%;float:right;'});
+     nextLink.update('>>');
+
+     var args = {node: node, page: ++oldPage};
+     nextLink.observe('click', function(e) {displayPage(e, args);}.bind());
+
+     var prevLink = new Element('a',{href: '', title: 'Previous Image', style: 'text-decoration:none;width:40%;float:right;'});
+     prevLink.update('<<');
+
+     var args = {node: node, page: --oldPage};
+     prevLink.observe('click', function(e) {displayPage(e, args);}.bind());
+
+     mainNode.appendChild(nextLink);
+     mainNode.appendChild(prevLink);
+
+     Horde_ToolTips.attachBehavior(node);
+     anseljson[node]['page'] = page;
+     event.stop();
+}
+//]
diff --git a/ansel/js/src/googlemap.js b/ansel/js/src/googlemap.js
new file mode 100644 (file)
index 0000000..fb7689d
--- /dev/null
@@ -0,0 +1,504 @@
+/**
+ * Google maps implementation for Ansel
+ *
+ * Copyright 2009 The Horde Project (http://www.horde.org/)
+ *
+ * $Horde: ansel/js/src/googlemap.js,v 1.36 2009/07/30 18:02:13 mrubinsk Exp $
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ */
+var Ansel_GMap = Class.create();
+
+Ansel_GMap.prototype = {
+    // Main google map handle
+    mainMap: undefined,
+
+    // Smaller overview map handle
+    smallMap: undefined,
+
+    // Tinymarker icons...
+    tI: undefined,
+    tIO: undefined,
+
+    // GLatLngBounds obejct for calculating proper center and zoom
+    bounds: undefined,
+
+    // Geocoder
+    geocoder: undefined,
+
+    // MarkerManager, if we are browsing the map.
+    // Note we need <script src="http://gmaps-utility-library.googlecode.com/svn/trunk/markermanager/release/src/markermanager.js">
+    manager: undefined,
+
+    // Can override via options array
+    // Pass these in options array as empty string if you do not have them
+    // on your view
+    tilePrefix: 'imagetile_',
+    locationId: 'ansel_locationtext',
+    coordId: 'ansel_latlng',
+    relocateId: 'ansel_relocate',
+    deleteId: 'ansel_deleteGeotag',
+    maxZoom: 15,
+    defaultZoom: 15,
+    options: {},
+
+    // Array of GOverlays (GMarker or Ansel_GOverlay objects)
+    points: [],
+
+    // const'r
+    // options.smallMap = [id: 80px];
+    //        .mainMap [id: xx]
+    //        .viewType (Gallery, Image, Edit)
+    //        .relocateUrl base url to the edit/relocate page
+    //        .relocateText localized text for the relocate link
+    //        .deleteGeotagText localized text for delete geotag link
+    //        .deleteGeotagCallback js callback function to be called after
+    //                              deletion is successful
+
+    //        .clickHandler - optional callback to handle click events on the mainMap
+    //        .hasEdit - Has PERMS_EDIT on the image
+    //        .calculateMaxZoom - call Google's getMaxZoomAtLatLng() method to
+    //                            avoid autozooming in to a level with no detail.
+    //                            Performance penalty because we make another
+    //                            round trip to google's service and lately it
+    //                            appears rather slow.
+    //        .updateEndpoint     URL to imple.php
+    initialize: function(options) {
+        // Use the manager by default.
+        if (typeof options.useManager == 'undefined') {
+            options.useManager = true;
+        }
+        this.mainMap = new GMap2($(options.mainMap));
+        this.mainMap.setMapType(G_HYBRID_MAP);
+        this.mainMap.setUIToDefault();
+        this.bounds = new GLatLngBounds();
+        this.geocoder = new GClientGeocoder();
+        this.options = options;
+        if (options.tilePrefix) {
+            this.tilePrefix = options.tilePrefix;
+        }
+
+        if (typeof options.calculateMaxZoom == 'undefined') {
+            options.calculateMaxZoom = true;
+        }
+
+        if (options.smallMap) {
+            this.smallMap = new GMap2($(options.smallMap));
+            var cUI = this.smallMap.getDefaultUI();
+            cUI.controls.menumaptypecontrol = false;
+            this.smallMap.setUI(cUI);
+
+            // Create our "tiny" marker icon
+            // We should copy these locally once this is fleshed out...
+            this.tI = new GIcon();
+            this.tI.image = "http://labs.google.com/ridefinder/images/mm_20_red.png";
+            this.tI.shadow = "http://labs.google.com/ridefinder/images/mm_20_shadow.png";
+            this.tI.iconSize = new GSize(12, 20);
+            this.tI.shadowSize = new GSize(22, 20);
+            this.tI.iconAnchor = new GPoint(6, 20);
+            this.tI.infoWindowAnchor = new GPoint(5, 1);
+            this.tIO = { icon:this.tI };
+        }
+
+        // Clean up
+        document.observe('unload', function() {GUnload();});
+    },
+
+    /**
+     * Adds a set of points to the map. Each entry in the points array should
+     * contain:
+     *   image_latitude,image_longitude
+     *   (optional)markerOnly - Don't add thumbnails or event handlers
+     *
+     * @param array
+     * @param minZoom  at what minimum zoom level should this set of points be
+     *                 displayed? Ignored if not using the MarkerManager
+     */
+    addPoints: function(points, minZoom) {
+        var l = points.length;
+        for (var i = 0; i < l; i++) {
+            var ll = new GLatLng(parseFloat(points[i].image_latitude), parseFloat(points[i].image_longitude));
+            this.bounds.extend(ll);
+            if (points[i].markerOnly == true) {
+                // We only support draggable GMarkers, not custom overlays.
+                if (points[i].draggable) {
+                    var mO = {draggable: true};
+                    var marker = new GMarker(ll, mO);
+                    GEvent.addListener(marker, "drag", function(ll) {
+                        $(this.coordId).update(this._point2Deg(ll));
+                    }.bind(this));
+                    GEvent.addListener(marker, "dragend", function(ll) {
+                        this.geocoder.getLocations(ll, function(address) {
+                                this.getLocationCallback(address, new GMarker(ll));
+                                }.bind(this));
+                        }.bind(this));
+                } else {
+                    // This is the single marker for the current image in the image view.
+                    // Make sure we have a high enough zIndex value for it.
+                    var marker = new GMarker(ll, {zIndexProcess: function(marker) {return GOverlay.getZIndex(-90);}});
+                }
+            } else {
+                var marker = new anselGOverlay(ll, points[i]);
+            }
+            // Click handlers only apply to our custom GOverlay.
+            if (!points[i].markerOnly && !options.viewType == 'Block') {
+                (function() {
+                    var p = points[i];
+                    GEvent.addDomListener(marker.div_, 'click', function() {
+                        a = $$('#' + this.tilePrefix + p.image_id + ' a')[0];
+                        if (!a.onclick || a.onclick() != false) {
+                            location.href = a.href;
+                        }
+                    }.bind(this));}.bind(this))();
+            }
+            // extend the GOverlay with our image data too.
+            marker.image_data = points[i];
+            this.points.push(marker);
+
+            // Only put the current image on the small map if we are in the
+            // Image view.
+            if (this.options.smallMap &&
+                (options.viewType != 'Image' || points[i].markerOnly)) {
+                var marker2 = new GMarker(ll, this.tIO);
+                this.smallMap.addOverlay(marker2);
+            }
+        }
+
+        if (this.options.viewType == 'Gallery' || this.options.viewType == 'Block') {
+            if (this.options.calculateMaxZoom) {
+                this.mainMap.getCurrentMapType().getMaxZoomAtLatLng(this.bounds.getCenter(), function(response) {
+                    if (response.status != 200) {
+                        var zl = Math.min(this.mainMap.getBoundsZoomLevel(this.bounds) - 1, this.maxZoom);
+                    } else {
+                        var zl = Math.min(this.mainMap.getBoundsZoomLevel(this.bounds) - 1, Math.min(this.maxZoom, response.zoom - 1));
+                    }
+                    this._mapSetCenter(zl);
+                    this._managerSetup(minZoom);
+                }.bind(this));
+            } else {
+                this._mapSetCenter(Math.min(this.mainMap.getBoundsZoomLevel(this.bounds) - 1, this.maxZoom));
+                this._managerSetup(minZoom);
+            }
+        } else {
+            // Not a Gallery View...
+            this.mainMap.setCenter(this.points[0].getLatLng(), Math.min(this.mainMap.getBoundsZoomLevel(this.bounds) - 1, this.maxZoom));
+            // Can't instantiate a manager until after the GMap2 has had
+            // setCenter() called, so we can't do this in the const'r
+            if (this.options.useManager && this.manager == null) {
+                this.manager = new MarkerManager(this.mainMap);
+            }
+            if (this.options.useManager) {
+                if (minZoom == null) {
+                    minZoom = 0;
+                }
+                this.manager.addMarkers(this.points, minZoom);
+                this.manager.refresh();
+            }
+            if (this.options.smallMap) {
+                this.smallMap.setCenter(this.mainMap.getCenter(), 1);
+            }
+        }
+    },
+
+    /**
+     * Helper method to set the map center and refresh the MarkerManager
+     * if we are using one.
+     *
+     * @param integer zl  The zoom level to set the map at once it's centered.
+     * @param integer mz  The minimum zoom level needed to display the currently
+     *                    added points if using the MarkerManager.
+     */
+    _mapSetCenter: function(zl, mz) {
+        this.mainMap.setCenter(this.bounds.getCenter(), zl);
+        if (this.options.smallMap) {
+            this.smallMap.setCenter(this.mainMap.getCenter(), 1);
+        }
+    },
+
+    _managerSetup: function(mz) {
+        // Can't instantiate a manager until after the GMap2 has had
+        // setCenter() called, so we *must* do this here.
+        if (this.options.useManager && this.manager == null) {
+            this.manager = new MarkerManager(this.mainMap);
+        }
+        if (this.options.useManager) {
+            if (mz == null) {
+                mz = 0;
+            }
+            this.manager.addMarkers(this.points, mz);
+            this.manager.refresh();
+        }
+    },
+
+    /**
+     * Display all points on the map. If we are using the MarkerManager, then
+     * this function only obtains the reverse geocode data and (via the callback)
+     * adds the event handlers to display the geocode data.  If we aren't using
+     * the manager, this also adds the overlay to the map.
+     */
+    display: function() {
+        var l = this.points.length;
+        for (var i = 0; i < l; i++) {
+            // Force closure on p
+            (function() {
+                var p = this.points[i];
+                if (!this.options.useManager) {
+                    this.mainMap.addOverlay(p);
+                }
+                // For now, only do this on the current Image in the image view
+                // or for all images in Gallery view.
+                if ((this.options.viewType != 'Block' && this.options.viewType != 'Image') || p.image_data.markerOnly) {
+                    this.getLocations(p);
+                }
+            }.bind(this))();
+        }
+
+        if (this.options.clickHandler) {
+            GEvent.addListener(this.mainMap, "click", this.options.clickHandler);
+        }
+    },
+
+    /**
+     * Custom getLocations method so we can check for our own locally cached
+     * geodata first. We can't implement our own GGeocodeCache becuase we want
+     * to allow a per-user override of an individual image, not a general cache
+     * of lat/lng => location text.
+     */
+    getLocations: function(p) {
+        if (p.image_data.image_location.length > 0) {
+            r = {Status: {code: 200}, Placemark: [{AddressDetails: {Accuracy: 4}, address:p.image_data.image_location}], NoUpdate: true};
+            this.getLocationCallback(r, p, false);
+        } else {
+            this.geocoder.getLocations(p.getLatLng(), function(address) {this.getLocationCallback(address, p, true)}.bind(this));
+        }
+    },
+
+    /**
+     *  Callback to parse and attach location data the the points on the map.
+     *  Adds event handlers to display the location data on mouseover. Also
+     *  highlights the image tile (since these would only be called in gallery
+     *  view) - need to use a seperate handler for that once we start storing
+     *  reverse geocode data locally.
+     *
+     * @TODO: Is it worth the effort to define the callback in the page that
+     *        is calling this to make this more OO-like? Maybe for H4 when I
+     *        try to make this a more generic Google library??
+     *
+     */
+    getLocationCallback: function(points, marker, update) {
+        if (typeof update == 'undefined') { update = false;}
+        if (points.Status.code != 200) {
+            // Fake the data so we can at least update what we have
+            points.Placemark = [{AddressDetails: {Accuracy: 0}, address: ''}];
+            update = false;
+        }
+
+        if (marker.image_data) {
+            var image_data = marker.image_data;
+        } else {
+            image_data = {};
+        }
+
+        for (var i = 0; i < points.Placemark.length; i++) {
+            var place = points.Placemark[i];
+            if (place.AddressDetails.Accuracy <= 4) {
+                // These events handlers should only be fired on the Gallery
+                // view for our special GOverlay objects (which already have
+                // a mouseover/out handler to focus them).
+                if (!image_data.markerOnly && this.options.viewType == 'Gallery' && this.locationId) {
+                    GEvent.addDomListener(marker.div_, 'mouseover', function() {
+                        $(this.locationId).update(place.address);
+                        $$('#' + this.tilePrefix + image_data.image_id + ' img')[0].toggleClassName('image-tile-highlight');
+                    }.bind(this));
+                    GEvent.addDomListener(marker.div_, 'mouseout', function() {
+                        $(this.locationId).update('<br />');
+                        $$('#' + this.tilePrefix + image_data.image_id + ' img')[0].toggleClassName('image-tile-highlight');
+                    }.bind(this));
+                }
+
+                // Cache the location locally?
+                if (update) {
+                    new Ajax.Request(this.options['updateEndpoint'], 
+                                    {
+                                        method: 'post',
+                                        parameters: {
+                                            type: 'location',
+                                            location: encodeURIComponent(place.address),
+                                            img: image_data.image_id
+                                        }
+                                    }
+                    );
+                }
+                // These handlers are for the image tiles themselves in the
+                // Gallery view - to highlight our GOverlays on the map.
+                if (this.options.viewType == 'Gallery') {
+                    if (this.locationId) {
+                        $$('#' + this.tilePrefix + image_data.image_id + ' img')[0].observe('mouseover', function() {
+                            $(this.locationId).update(place.address);
+                            $$('#' + this.tilePrefix + image_data.image_id + ' img')[0].toggleClassName('image-tile-highlight');
+                            marker.focus();
+                        }.bind(this));
+                        $$('#' + this.tilePrefix + image_data.image_id + ' img')[0].observe('mouseout', function() {
+                            $(this.locationId).update('<br />');
+                            $$('#' + this.tilePrefix + image_data.image_id + ' img')[0].toggleClassName('image-tile-highlight');
+                            marker.div_.style.border = '1px solid white';
+                            marker.focus();
+                        }.bind(this));
+                    }
+
+                    return;
+                } else if (this.options.viewType == 'Image') {
+                    // If Image view and this is the markerOnly point.
+                    if (image_data.markerOnly) {
+                        if (this.locationId) {
+                            $(this.locationId).update(place.address);
+                        }
+                        if (this.coordId) {
+                            $(this.coordId).update(this._point2Deg(marker.getLatLng()));
+                        }
+                        if (this.relocateId) {
+                            $(this.relocateId).update(this._getRelocateLink(image_data.image_id));
+                        }
+
+                        if (this.deleteId) {
+                            $(this.deleteId).update(this._getDeleteLink(image_data.image_id));
+                        }
+                    }
+
+                    return;
+                } else {
+                    // Edit view
+                    $(this.locationId).update(place.address);
+                    $(this.coordId).update(this._point2Deg(marker.getLatLng()));
+
+                    return;
+                }
+
+            } else {
+                // Parse less detail, or just move on to the next hit??
+            }
+        }
+    },
+
+    _point2Deg: function(ll) {
+         function dec2deg(dec, lat)
+         {
+             var letter = lat ? (dec > 0 ? "N" : "S") : (dec > 0 ? "E" : "W");
+             dec = Math.abs(dec);
+             var deg = Math.floor(dec);
+             var min = Math.floor((dec - deg) * 60);
+             var sec = (dec - deg - min / 60) * 3600;
+             return deg + "&deg; " + min + "' " + sec.toFixed(2) + "\" " + letter;
+         }
+
+         return dec2deg(ll.lat(), true) + " " + dec2deg(ll.lng());
+    },
+
+    _getRelocateLink: function(iid) {
+        if (options.hasEdit) {
+            var a = new Element('a', {href: this.options.relocateUrl + '?image=' + iid}).update(this.options.relocateText);
+            a.observe('click', function(e) {popup(this.options.relocateUrl + '?image=' + iid, 750, 600); e.stop();}.bind(this));
+            return a;
+        } else {
+            return '';
+        }
+    },
+
+    _getDeleteLink: function(iid) {
+        var x = new Element('a', {href: this.options.relocateUrl + '?image=' + iid}).update(this.options.deleteGeotagText);
+        x.observe('click', function(e) {this.options.deleteGeotagCallback(); e.stop();}.bindAsEventListener(this));
+        return x;
+    }
+
+}
+
+/**
+ * Define our custom GOverlay to display thumbnails of images on the map.
+ * Use an Image object to get the exact dimensions of the image. Need this
+ * wrapped in an onload handler to be sure GOverlay() is defined.
+ */
+anselGOverlay = function(latlng, image_data) {
+    this.src_ = image_data.icon;
+    this.latlng_ = latlng;
+    var img = new Image();
+    img.src = image_data.icon;
+    this.width_ = img.width;
+    this.height_ = img.height;
+    var z = GOverlay.getZIndex(this.latlng_.lat());
+    this.div_ = new Element('div', {style: 'position:absolute;border:1px solid white;width:' + (this.width_ - 2) + 'px; height:' + (this.height_ - 2) + 'px;zIndex:' + z});
+    this.img_ = new Element('img', {src: this.src_, style: 'width:' + (this.width_ - 2) + 'px;height:' + (this.height_ - 2) + 'px'});
+    this.div_.appendChild(this.img_);
+    this.selected_ = false;
+    this.link = image_data.link;
+
+    // Handlers to hightlight the node for this overlay on mouseover/out
+    GEvent.addDomListener(this.div_, 'mouseover', function() {
+        this.focus();
+    }.bind(this));
+    GEvent.addDomListener(this.div_, 'mouseout', function() {
+        this.focus();
+    }.bind(this));
+
+    // Add a click handler to navigate to the image view for this image.
+    if (this.link) {
+        GEvent.addDomListener(this.div_, 'click', function() {
+                var a = this.link;
+                location.href = a;
+            }.bind(this));
+        }
+    };
+
+    anselGOverlay.prototype = new GOverlay();
+    anselGOverlay.prototype.initialize =  function(map) {
+        map.getPane(G_MAP_MARKER_PANE).appendChild(this.div_);
+        this.map_ = map;
+    };
+
+    //Remove the main DIV from the map pane
+    // TODO: We should unregister the event handlers adding in initialize()
+    anselGOverlay.prototype.remove = function() {
+      this.div_.parentNode.removeChild(this.div_);
+    };
+
+    // Copy our data to a new GOverlay
+    anselGOverlay.prototype.copy = function() {
+      return new Ansel_GOverlay(this.latlng_, this.src_);
+    };
+
+    anselGOverlay.prototype.redraw = function(force) {
+        // We only need to redraw if the coordinate system has changed
+    if (!force) return;
+    var coords = this.map_.fromLatLngToDivPixel(this.latlng_);
+    this.div_.style.left = coords.x + "px";
+    this.div_.style.top  = coords.y + "px";
+};
+
+anselGOverlay.prototype.focus = function()
+{
+    if (this.selected_ == false) {
+        this.div_.style.border = '1px solid red';
+        this.div_.style.left = (parseInt(this.div_.style.left) - 1) + "px";
+        this.div_.style.top = (parseInt(this.div_.style.top) - 1) + "px";
+        this.div_.style.zIndex = GOverlay.getZIndex(-90.0);
+        this.selected_ = true;
+    } else {
+        this.div_.style.border = '1px solid white';
+        this.div_.style.left = (parseInt(this.div_.style.left) + 1) + "px";
+        this.div_.style.top = (parseInt(this.div_.style.top) + 1) + "px";
+        this.div_.style.zIndex = GOverlay.getZIndex(this.latlng_.lat());
+        this.selected_ = false;
+    }
+};
+
+// MarkerManager seems to be incosistent with the methods it calls to get
+// the GLatLng for each overlay. addMarkers() seems to need the deprecated
+// getPoint() while addMarker() uses the newer getLatLng() mehtod.
+anselGOverlay.prototype.getPoint = function() {
+    return this.latlng_;
+}
+anselGOverlay.prototype.getLatLng = function() {
+    return this.latlng_;
+}
diff --git a/ansel/js/src/googlemap_edit.js b/ansel/js/src/googlemap_edit.js
new file mode 100644 (file)
index 0000000..f6e0321
--- /dev/null
@@ -0,0 +1,196 @@
+/**
+ * Javascript specific for the adding/moving a geotag via the map_edit.php page
+ *
+ * Copyright 2009 The Horde Project (http://www.horde.org)
+ *
+ * @author Michael J. Rubinsky
+ * @package Ansel
+ */
+
+/**
+ * Ansel_MapEdit class encapsulates various functions for searching/setting
+ * locations.
+ *
+ * mapEdit = new Ansel_MapEdit(options)
+ *
+ * options is an object with the following required properties:
+ *
+ *    mainMap  - DOM id for the google map
+ *    image_id - The image_id for the Ansel_Image we are tagging.
+ *    gettext  - Various gettext strings (fetching, errortext)
+ *    xurl     - The URL for imple.php
+ *
+ * and some optional properties:
+ *
+ *    statusId       - DOM id for a status message
+ *    locationInput  - DOM id for the location search input field
+ *    locationAction - DOM id for the Find button
+ *    isNew          - Set to 1 if image is a newly geotagged image, 0 if we are
+ *                     moving an existing geotag.
+ *
+ *
+ */
+Ansel_MapEdit = Class.create();
+Ansel_MapEdit.prototype = {
+
+    _options: null,
+    _map: null,
+
+    // {image_latitude: image_longitude:}
+    ll: null,
+
+    initialize: function(options)
+    {
+        this._options = Object.extend({
+            statusId: 'status',
+            locationInput: 'locationInput',
+            locationAction: 'locationAction',
+            isNew: '0'
+            }, options);
+
+        this._map = new Ansel_GMap({mainMap: this._options.mainMap,
+                                    viewType: 'Edit',
+                                    useManager: false,
+                                    clickHandler: function(ol, ll, olll) {
+                                        this._map.points[0].setLatLng(ll);
+                                        this._map.points[0].image_data = {image_location: '',
+                                                                          image_latitude: ll.lat(),
+                                                                          image_longitude: ll.lng()};
+                                        this._map.getLocations(this._map.points[0]);
+                                    }.bind(this)});
+        if (this._options.isNew) {
+            this._map.maxZoom = 1;
+        }
+
+        this._map._getLocationCallback = this._map.getLocationCallback;
+        this._map.getLocationCallback = function(points, marker) {
+            this._map._getLocationCallback(points, marker);
+        }.bind(this);
+
+        this._map.addPoints(this._options.points);
+        this._map.display();
+        $(this._options.locationAction).observe('click', function(e) {this.getLocation();e.stop();}.bindAsEventListener(this));
+        $(this._options.saveId).observe('click', function() {this.handleSave(this._options.image_id);}.bind(this));
+    },
+
+    handleSave: function(id)
+    {
+        var o = this._options;
+        params = {
+            img: id,
+            lat: this._map.points[0].getLatLng().lat(),
+            lng: this._map.points[0].getLatLng().lng(),
+            type: 'geotag'
+        };
+        //url = this._options.url;
+        new Ajax.Request(o.xurl, {
+            method: 'post',
+            parameters: params,
+            onComplete: function(transport) {
+                if (transport.responseJSON.response > 0) {
+                    window.opener.location.href = window.opener.location.href;
+                    window.close();
+                } // what to do if failure?
+            }
+        });
+    },
+
+    getLocation: function()
+    {
+        var o = this._options;
+
+        $(this._options.statusId).update(this._options.gettext.fetching);
+        if (this.ll) {
+            //already have lat/lng from the autocompleter
+            var gll = new GLatLng(this.ll.image_latitude, this.ll.image_longitude);
+            this._map.points[0].setLatLng(gll);
+            this._map.points[0].image_data = {image_location: $F(o.locationInput),
+                                              image_latitude: gll.lat(),
+                                              image_longitude: gll.lng()};
+
+            this._map.getLocations(this._map.points[0]);
+            this._map.mainMap.setCenter(gll, this._map.defaultZoom);
+            $(o.statusId).update('');
+        } else {
+            this._map.geocoder.getLocations($(o.locationInput).value, function(address) {
+                if (address.Status.code == '200') {
+                    // For now, just try the first returned spot - not sure how else to handle this
+                    var lat = address.Placemark[0].Point.coordinates[1];
+                    var lng = address.Placemark[0].Point.coordinates[0];
+                    var gll = new GLatLng(lat, lng);
+                    this._map.points[0].setLatLng(gll);
+                    this._map.points[0].image_data = {image_location: '',
+                                                      image_latitude: lat,
+                                                      image_longitude: lng};
+
+                    this._map.getLocations(this._map.points[0]);
+                    this._map.mainMap.setCenter(gll, this._map.defaultZoom);
+                    $(o.statusId).update('');
+                } else {
+                    $(o.statusId).update(o.gettext.errortext + address.Status.code);
+                }
+           }.bind(this));
+       }
+    },
+
+    setLocation: function(lat, lng, loc)
+    {
+        var gll = new GLatLng(lat, lng);
+        this._map.points[0].setLatLng(gll);
+        this._map.points[0].image_data = {image_location: loc,
+                                          image_latitude: lat,
+                                          image_longitude: lng};
+
+        this._map.getLocations(this._map.points[0]);
+        this._map.mainMap.setCenter(gll, this._map.defaultZoom);
+    }
+
+}
+
+/**
+ * Override the Ajax.Autocompleter#updateChoices method so we can handle
+ * receiving lat/lng points bound to the location.
+ */
+Ajax.Autocompleter.prototype.updateChoices = function(choices) {
+    var li, re, ul,
+    i = 0;
+    this.geocache = choices;
+    var hc = $H(choices);
+    if (!this.changed && this.hasFocus) {
+        li = new Element('LI');
+        ul = new Element('UL');
+        re = new RegExp("(" + this.getToken() + ")", "i");
+        var k = hc.keys();
+        k.each(function(n) {
+            ul.insert(li.cloneNode(false).writeAttribute('acIndex', i++).update(n.gsub(re, '<strong>#{1}</strong>')));
+        });
+
+        this.update.update(ul);
+
+        this.entryCount = k.size();
+        ul.childElements().each(this.addObservers.bind(this));
+
+        this.stopIndicator();
+        this.index = 0;
+
+        if (this.entryCount == 1 && this.options.autoSelect) {
+            this.selectEntry();
+        } else {
+            this.render();
+        }
+    }
+
+    if (this.options.afterUpdateChoices) {
+        this.options.afterUpdateChoices(hc, ul);
+    }
+}
+
+/**
+ * Override the Autocompler.Local#initialize method to take an Object instead
+ * of an Array, and set the appropriate properties.
+ */
+Autocompleter.Local.prototype.initialize = function(element, update, obj, options) {
+    this.baseInitialize(element, update, options);
+    this.geocache = obj;
+    this.options.arr = $H(obj).keys();
+}
diff --git a/ansel/js/src/imagefaces.js b/ansel/js/src/imagefaces.js
new file mode 100644 (file)
index 0000000..a32eebb
--- /dev/null
@@ -0,0 +1,19 @@
+function showFace(id)
+{
+    $('facediv' + id).addClassName('shown');
+    $('facethumb' + id).style.border = '1px solid red';
+    $('facedivname' + id).style.display = 'inline';
+}
+function hideFace(id)
+{
+    $('facediv' + id).removeClassName('shown');
+    $('facethumb' + id).style.border = '1px solid black';
+    $('facedivname' + id).style.display = 'none';
+}
+document.observe('dom:loaded', function() {
+    Event.observe($('photodiv'), 'load', function() {
+        $('faces-on-image').immediateDescendants().collect(function(element) {
+            element.clonePosition($('photodiv'), {setWidth: false, setHeight: false});
+        });
+    });
+});
diff --git a/ansel/js/src/lightbox.js b/ansel/js/src/lightbox.js
new file mode 100755 (executable)
index 0000000..ef49576
--- /dev/null
@@ -0,0 +1,460 @@
+// This file modified in various ways for use in Ansel. Mostly to allow
+// the lightbox images to span multiple gallery pages...and thus is no longer
+// really "Lightbox", but a good bit of the original code is still intact.
+// The original credits/copyright appears below.
+
+// -----------------------------------------------------------------------------------
+//
+//    Lightbox v2.04
+//    by Lokesh Dhakar - http://www.lokeshdhakar.com
+//    Last Modification: 2/9/08
+//
+//    For more information, visit:
+//    http://lokeshdhakar.com/projects/lightbox2/
+//
+//    Licensed under the Creative Commons Attribution 2.5 License - http://creativecommons.org/licenses/by/2.5/
+//      - Free for use in both personal and commercial projects
+//        - Attribution requires leaving author name, author link, and the license info intact.
+//
+//  Thanks: Scott Upton(uptonic.com), Peter-Paul Koch(quirksmode.com), and Thomas Fuchs(mir.aculo.us) for ideas, libs, and snippets.
+//          Artemy Tregubenko (arty.name) for cleanup and help in updating to latest ver of proto-aculous.
+//
+// -----------------------------------------------------------------------------------
+/*
+
+    Table of Contents
+    -----------------
+    Configuration
+
+    Lightbox Class Declaration
+    - initialize()
+    - updateImageList()
+    - start()
+    - changeImage()
+    - resizeImageContainer()
+    - showImage()
+    - updateDetails()
+    - updateNav()
+    - enableKeyboardNav()
+    - disableKeyboardNav()
+    - keyboardAction()
+    - preloadNeighborImages()
+    - end()
+
+    Function Calls
+    - document.observe()
+
+*/
+// -----------------------------------------------------------------------------------
+
+var Lightbox = Class.create();
+
+Lightbox.prototype = {
+    imageArray: [],
+    activeImage: undefined,
+    options: undefined,
+
+    // initialize()
+    // Constructor runs on completion of the DOM loading. Calls updateImageList and then
+    // the function inserts html at the bottom of the page which is used to display the shadow
+    // overlay and the image container.
+    //
+    // Modified 3/25/2008 Michael J. Rubinsky <mrubinsk@horde.org> to remove
+    // dependency on scriptaculous' Builder object since the new Element
+    // constructor in Prototype does this more efficently.
+    initialize: function(options) {
+
+        this.options = options;
+        this.imageArray = this.options.gallery_json;
+        this.keyboardAction = this.keyboardAction.bindAsEventListener(this);
+        if (this.options.resizeSpeed > 10) this.options.resizeSpeed = 10;
+        if (this.options.resizeSpeed < 1)  this.options.resizeSpeed = 1;
+        this.resizeDuration = this.options.animate ? ((11 - this.options.resizeSpeed) * 0.15) : 0;
+        this.overlayDuration = this.options.animate ? 0.2 : 0;  // shadow fade in/out duration
+
+        // When Lightbox starts it will resize itself from 250 by 250 to the current image dimension.
+        // If animations are turned off, it will be hidden as to prevent a flicker of a
+        // white 250 by 250 box.
+
+        var size = (this.options.animate ? 250 : 1) + 'px';
+
+        // Code inserts html at the bottom of the page that looks similar to this:
+        //
+        //  <div id="overlay"></div>
+        //  <div id="lightbox">
+        //      <div id="outerImageContainer">
+        //          <div id="imageContainer">
+        //              <img id="lightboxImage">
+        //              <div style="" id="hoverNav">
+        //                  <a href="#" id="prevLink"></a>
+        //                  <a href="#" id="nextLink"></a>
+        //              </div>
+        //              <div id="loading">
+        //                  <a href="#" id="loadingLink">
+        //                      <img src="images/loading.gif">
+        //                  </a>
+        //              </div>
+        //          </div>
+        //      </div>
+        //      <div id="imageDataContainer">
+        //          <div id="imageData">
+        //              <div id="imageDetails">
+        //                  <span id="caption"></span>
+        //                  <span id="numberDisplay"></span>
+        //              </div>
+        //              <div id="bottomNav">
+        //                  <a href="#" id="bottomNavClose">
+        //                      <img src="images/close.gif">
+        //                  </a>
+        //              </div>
+        //          </div>
+        //      </div>
+        //  </div>
+
+
+        var objBody = $$('body')[0];
+
+        objBody.appendChild(new Element('div', {id: 'overlay'}));
+
+        // Build innermost children
+        var hoverNav = new Element('div', {id: 'hoverNav'});
+          hoverNav.appendChild(new Element('a', {id:'prevLink', href: '#'}));
+          hoverNav.appendChild(new Element('a', {id: 'nextLink', href: '#'}));
+
+        var loadingLink = new Element('a', {id: 'loadingLink', href: '#'});
+          loadingLink.appendChild(new Element('img', {src: this.options.fileLoadingImage}));
+
+        var loading = new Element('div', {id: 'loading'});
+          loading.appendChild(loadingLink);
+
+        var container = new Element('div', {id: 'imageContainer'});
+          container.appendChild(new Element('img', {id: 'lightboxImage'}));
+          container.appendChild(hoverNav);
+          container.appendChild(loading);
+
+        var outerContainer = new Element('div', {id: 'outerImageContainer'});
+          outerContainer.appendChild(container);
+
+        var imageDetails = new Element('div', {id: 'imageDetails'});
+          imageDetails.appendChild(new Element('span', {id: 'caption'}));
+          imageDetails.appendChild(new Element('span', {id: 'numberDisplay'}));
+
+        var bottomClose = new Element('a', {id: 'bottomNavClose', href: '#'});
+           bottomClose.appendChild(new Element('img', {src: this.options.fileBottomNavCloseImage}));
+
+        var bottomNav = new Element('div', {id: 'bottomNav'});
+           bottomNav.appendChild(bottomClose);
+
+        var imageData = new Element('div', {id: 'imageData'});
+          imageData.appendChild(imageDetails);
+          imageData.appendChild(bottomNav);
+
+        var imageDataContainer = new Element('div', {id: 'imageDataContainer'});
+           imageDataContainer.appendChild(imageData);
+
+        // The outermost node
+        var lightbox = new Element('div', {id: 'lightbox'});
+          lightbox.appendChild(outerContainer);
+          lightbox.appendChild(imageDataContainer);
+
+        objBody.appendChild(lightbox);
+
+        $('overlay').hide().observe('click', (function() { this.end(); }).bind(this));
+        $('lightbox').hide().observe('click', (function(event) { if (event.element().id == 'lightbox') this.end(); }).bind(this));
+        $('outerImageContainer').setStyle({ width: size, height: size });
+
+        // Need to cache the event listener function so we can call stopObserving() later
+        this.prevEventListener = (function(event) { event.stop(); this.changeImage(this.activeImage - 1); }).bindAsEventListener(this);
+        $('prevLink').observe('click', this.prevEventListener);
+
+        this.nextEventListener = (function(event) { event.stop(); this.changeImage(this.activeImage + 1); }).bindAsEventListener(this);
+        $('nextLink').observe('click', this.nextEventListener);
+        $('loadingLink').observe('click', (function(event) { event.stop(); this.end(); }).bind(this));
+        $('bottomNavClose').observe('click', (function(event) { event.stop(); this.end(); }).bind(this));
+    },
+
+    //
+    //  start()
+    //  Display overlay and lightbox.
+    //
+    start: function(imageId) {
+
+        $$('select', 'object', 'embed').invoke('setStyle', 'visibility:hidden');
+
+        // stretch overlay to fill page and fade in
+        var arrayPageSize = this.getPageSize();
+        $('overlay').setStyle({ height: arrayPageSize[1] + 'px' });
+
+        new Effect.Appear($('overlay'), { duration: this.overlayDuration, from: 0.0, to: this.options.overlayOpacity });
+
+        // calculate top and left offset for the lightbox
+        var arrayPageScroll = document.viewport.getScrollOffsets();
+        var lightboxTop = arrayPageScroll[1] + (document.viewport.getHeight() / 15);
+        var lightboxLeft = arrayPageScroll[0];
+        $('lightbox').setStyle({ top: lightboxTop + 'px', left: lightboxLeft + 'px' }).show();
+
+        // Need to find the index for this image.
+        var imageNum = 0;
+        while (this.imageArray[imageNum][3] != imageId || imageNum > this.imageArray.length - 1) {
+            imageNum++;
+        }
+
+        this.changeImage(imageNum);
+        return false;
+    },
+
+    //
+    //  changeImage()
+    //  Hide most elements and preload image in preparation for resizing image container.
+    //
+    changeImage: function(imageNum) {
+        this.activeImage = imageNum; // update global var
+
+        // hide elements during transition
+        if (this.options.animate) $('loading').show();
+        $('lightboxImage', 'hoverNav', 'prevLink', 'nextLink', 'numberDisplay').invoke('hide');
+        // HACK: Opera9 does not currently support scriptaculous opacity and appear fx
+        $('imageDataContainer').setStyle({opacity: .0001});
+
+        var imgPreloader = new Image();
+
+        // once image is preloaded, resize image container
+        imgPreloader.onload = (function(){
+            $('lightboxImage').src = this.imageArray[this.activeImage][0];
+            this.resizeImageContainer(imgPreloader.width, imgPreloader.height);
+        }).bind(this);
+        imgPreloader.src = this.imageArray[this.activeImage][0];
+    },
+
+    //
+    //  resizeImageContainer()
+    //
+    resizeImageContainer: function(imgWidth, imgHeight) {
+
+        // get current width and height
+        var widthCurrent  = $('outerImageContainer').getWidth();
+        var heightCurrent = $('outerImageContainer').getHeight();
+
+        // get new width and height
+        var widthNew  = (imgWidth  + this.options.borderSize * 2);
+        var heightNew = (imgHeight + this.options.borderSize * 2);
+
+        // scalars based on change from old to new
+        var xScale = (widthNew  / widthCurrent)  * 100;
+        var yScale = (heightNew / heightCurrent) * 100;
+
+        // calculate size difference between new and old image, and resize if necessary
+        var wDiff = widthCurrent - widthNew;
+        var hDiff = heightCurrent - heightNew;
+
+        if (hDiff != 0) new Effect.Scale($('outerImageContainer'), yScale, {scaleX: false, duration: this.resizeDuration, queue: 'front'});
+        if (wDiff != 0) new Effect.Scale($('outerImageContainer'), xScale, {scaleY: false, duration: this.resizeDuration, delay: this.resizeDuration});
+
+        // if new and old image are same size and no scaling transition is necessary,
+        // do a quick pause to prevent image flicker.
+        var timeout = 0;
+        if ((hDiff == 0) && (wDiff == 0)){
+            timeout = 100;
+            if (Prototype.Browser.IE) timeout = 250;
+        }
+
+        (function(){
+            $('prevLink', 'nextLink').invoke('setStyle', 'height:' + imgHeight + 'px');
+            $('imageDataContainer').setStyle({ width: widthNew + 'px' });
+            this.showImage();
+        }).bind(this).delay(timeout / 1000);
+    },
+
+    //
+    //  showImage()
+    //  Display image and begin preloading neighbors.
+    //
+    showImage: function(){
+        $('loading').hide();
+        new Effect.Appear($('lightboxImage'), {
+            duration: this.resizeDuration,
+            queue: 'end',
+            afterFinish: (function(){ this.updateDetails(); }).bind(this)
+        });
+        this.preloadNeighborImages();
+    },
+
+    //
+    //  updateDetails()
+    //  Display caption, image number, and bottom nav.
+    //
+    updateDetails: function() {
+
+        // use caption, or fall back to the file name if it's empty.
+        if (this.imageArray[this.activeImage][2] != ""){
+            $('caption').update(this.imageArray[this.activeImage][2]).show();
+        } else {
+            $('caption').update(this.imageArray[this.activeImage][1]).show();
+        }
+
+        // if image is part of set display 'Image x of x'
+        if (this.imageArray.length > 1){
+            $('numberDisplay').update( this.options.labelImage + ' ' + (this.activeImage + 1) + ' ' + this.options.labelOf + '  ' + this.imageArray.length).show();
+        }
+
+        new Effect.Parallel(
+            [
+                new Effect.SlideDown($('imageDataContainer'), { sync: true, duration: this.resizeDuration, from: 0.0, to: 1.0 }),
+                new Effect.Appear($('imageDataContainer'), { sync: true, duration: this.resizeDuration })
+            ],
+            {
+                duration: this.resizeDuration,
+                afterFinish: (function() {
+                    // update overlay size and update nav
+                    var arrayPageSize = this.getPageSize();
+                    $('overlay').setStyle({ height: arrayPageSize[1] + 'px' });
+                    this.updateNav();
+                }).bind(this)
+            }
+        );
+    },
+
+    //
+    //  updateNav()
+    //  Display appropriate previous and next hover navigation.
+    //
+    updateNav: function() {
+
+        $('hoverNav').show();
+        // if not first image in set, display prev image button
+        if (this.activeImage > 0) $('prevLink').show();
+
+        // if not last image in set, display next image button
+        if (this.activeImage < (this.imageArray.length - 1)) $('nextLink').show();
+
+        this.enableKeyboardNav();
+    },
+
+    //
+    //  enableKeyboardNav()
+    //
+    enableKeyboardNav: function() {
+        document.observe('keydown', this.keyboardAction);
+    },
+
+    //
+    //  disableKeyboardNav()
+    //
+    disableKeyboardNav: function() {
+        document.stopObserving('keydown', this.keyboardAction);
+    },
+
+    //
+    //  keyboardAction()
+    //
+    keyboardAction: function(event) {
+        var keycode = event.keyCode;
+
+        var escapeKey;
+        if (event.DOM_VK_ESCAPE) {  // mozilla
+            escapeKey = event.DOM_VK_ESCAPE;
+        } else { // ie
+            escapeKey = 27;
+        }
+
+        var key = String.fromCharCode(keycode).toLowerCase();
+
+        if (key.match(/x|o|c/) || (keycode == escapeKey)){ // close lightbox
+            this.end();
+        } else if ((key == 'p') || (keycode == 37)){ // display previous image
+            if (this.activeImage != 0){
+                this.disableKeyboardNav();
+                this.changeImage(this.activeImage - 1);
+            }
+        } else if ((key == 'n') || (keycode == 39)){ // display next image
+            if (this.activeImage != (this.imageArray.length - 1)){
+                this.disableKeyboardNav();
+                this.changeImage(this.activeImage + 1);
+            }
+        }
+    },
+
+    //
+    //  preloadNeighborImages()
+    //  Preload previous and next images.
+    //
+    preloadNeighborImages: function(){
+        var preloadNextImage, preloadPrevImage;
+        if (this.imageArray.length > this.activeImage + 1){
+            preloadNextImage = new Image();
+            preloadNextImage.src = this.imageArray[this.activeImage + 1][0];
+        }
+        if (this.activeImage > 0){
+            preloadPrevImage = new Image();
+            preloadPrevImage.src = this.imageArray[this.activeImage - 1][0];
+        }
+
+    },
+
+    //
+    //  end()
+    //
+    end: function() {
+        this.disableKeyboardNav();
+        $('lightbox').hide();
+        new Effect.Fade($('overlay'), { duration: this.overlayDuration });
+        $$('select', 'object', 'embed').invoke('setStyle', 'visibility:visible');
+
+        //redirect here//
+        if (this.options.startPage != this.imageArray[this.activeImage][4]) {
+            location.href = this.options.returnURL + "page=" + this.imageArray[this.activeImage][4];
+        }
+    },
+
+    //
+    //  getPageSize()
+    //
+    getPageSize: function() {
+
+         var xScroll, yScroll;
+
+        if (window.innerHeight && window.scrollMaxY) {
+            xScroll = window.innerWidth + window.scrollMaxX;
+            yScroll = window.innerHeight + window.scrollMaxY;
+        } else if (document.body.scrollHeight > document.body.offsetHeight){ // all but Explorer Mac
+            xScroll = document.body.scrollWidth;
+            yScroll = document.body.scrollHeight;
+        } else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
+            xScroll = document.body.offsetWidth;
+            yScroll = document.body.offsetHeight;
+        }
+
+        var windowWidth, windowHeight;
+
+        if (self.innerHeight) {    // all except Explorer
+            if(document.documentElement.clientWidth){
+                windowWidth = document.documentElement.clientWidth;
+            } else {
+                windowWidth = self.innerWidth;
+            }
+            windowHeight = self.innerHeight;
+        } else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode
+            windowWidth = document.documentElement.clientWidth;
+            windowHeight = document.documentElement.clientHeight;
+        } else if (document.body) { // other Explorers
+            windowWidth = document.body.clientWidth;
+            windowHeight = document.body.clientHeight;
+        }
+
+        // for small pages with total height less then height of the viewport
+        if(yScroll < windowHeight){
+            pageHeight = windowHeight;
+        } else {
+            pageHeight = yScroll;
+        }
+
+        // for small pages with total width less then width of the viewport
+        if(xScroll < windowWidth){
+            pageWidth = xScroll;
+        } else {
+            pageWidth = windowWidth;
+        }
+
+        return [pageWidth,pageHeight];
+    }
+}
\ No newline at end of file
diff --git a/ansel/js/src/slideshow.js b/ansel/js/src/slideshow.js
new file mode 100755 (executable)
index 0000000..0c6a474
--- /dev/null
@@ -0,0 +1,241 @@
+/**
+ * You can only have one SlideController on a page.
+ *
+ * $Horde: ansel/js/src/slideshow.js,v 1.30 2008/06/29 15:31:03 mrubinsk Exp $
+ */
+var SlideController = {
+
+    photos: null,
+    photoId: 0,
+
+    slide: null,
+
+    interval: null,
+    intervalSeconds: 5,
+
+    /**
+     * CSS border size x 2
+     */
+    borderSize: 0,
+
+    /**
+     * So we can update the links
+     */
+    baseUrl: null,
+    galleryId: 0,
+    galleryShare: 0,
+    playing: false,
+    interval: null,
+    tempImage: new Image(),
+
+    /**
+     * Initialization.
+     */
+    initialize: function(photos, start, baseUrl, galleryId, galleryShare) {
+        SlideController.photoId = start || 0;
+        SlideController.baseUrl = baseUrl;
+        SlideController.galleryId = galleryId;
+        SlideController.galleryShare = galleryShare;
+
+        Event.observe(window, 'load', function() {
+            Event.observe(SlideController.tempImage, 'load', function() {
+                SlideController.photo.initSwap();
+            });
+            Event.observe($('Photo'), 'load', function() {
+                SlideController.photo.showPhoto();
+            });
+
+            SlideController.photos = photos;
+            SlideController.photo = new Slide(SlideController.photoId);
+            SlideController.photo.initSwap();
+            SlideController.play();
+
+        });
+    },
+
+    /**
+     */
+    play: function() {
+        Element.hide('ssPlay');
+        Element.show('ssPause');
+        // This sets the first interval for the currently displayed image.
+        if (SlideController.interval) {
+            clearTimeout(SlideController.interval);
+        }
+        SlideController.interval = setTimeout(SlideController.next, SlideController.intervalSeconds * 1000);
+        SlideController.playing = true;
+    },
+
+    /**
+     */
+    pause: function() {
+        Element.hide('ssPause');
+        Element.show('ssPlay');
+        if (SlideController.interval) {
+            clearTimeout(SlideController.interval);
+        }
+        SlideController.playing = false;
+    },
+
+    /**
+     */
+    prev: function() {
+        SlideController.photo.prevPhoto();
+    },
+
+    /**
+     */
+    next: function() {
+        SlideController.photo.nextPhoto();
+    }
+
+}
+
+// -----------------------------------------------------------------------------------
+//
+// This page coded by Scott Upton
+// http://www.uptonic.com | http://www.couloir.org
+//
+// This work is licensed under a Creative Commons License
+// Attribution-ShareAlike 2.0
+// http://creativecommons.org/licenses/by-sa/2.0/
+//
+// Associated APIs copyright their respective owners
+//
+// -----------------------------------------------------------------------------------
+// --- version date: 11/28/05 --------------------------------------------------------
+//
+// Various changes for properly updating image links, image comments etc...
+// added 4/07 by Michael Rubinsky <mrubinsk@horde.org>
+
+/**
+ * Additional methods for Element added by SU, Couloir.
+ */
+Object.extend(Element, {
+    getWidth: function(element) {
+        element = $(element);
+        return element.offsetWidth;
+    },
+    setWidth: function(element,w) {
+        element = $(element);
+        element.style.width = w + 'px';
+    },
+    setSrc: function(element,src) {
+        element = $(element);
+        element.src = src;
+    },
+    setHref: function(element,href) {
+        element = $(element);
+        element.href = href;
+    },
+    setInnerHTML: function(element,content) {
+        element = $(element);
+        element.innerHTML = content;
+    },
+    setOnClick: function(element,action) {
+        element = $(element);
+        element.onclick = action;
+    }
+});
+
+var Slide = Class.create();
+Slide.prototype = {
+    initialize: function(photoId) {
+        this.photoId = photoId;
+        this.photo = 'Photo';
+        this.captionBox = 'CaptionContainer';
+        this.caption = 'Caption';
+    },
+    setNewPhotoParams: function() {
+        // Set source of new image.
+        Element.setSrc(this.photo, SlideController.photos[SlideController.photoId][0]);
+
+        // Add caption from gallery array.
+        Element.setInnerHTML(this.caption, SlideController.photos[SlideController.photoId][2]);
+
+        try {
+            document.title = document.title.replace(SlideController.photos[this.photoId][1],
+                                                    SlideController.photos[SlideController.photoId][1]);
+            if (parent.frames.horde_main) {
+                parent.document.title = document.title;
+            }
+        } catch (e) {}
+    },
+    updateLinks: function() {
+
+        var params = '?gallery=' + SlideController.galleryId + '&image=' + SlideController.photos[SlideController.photoId][3] + '&page=' + SlideController.photos[SlideController.photoId][4];
+
+        Element.setInnerHTML('PhotoName', SlideController.photos[SlideController.photoId][1]);
+        Element.setInnerHTML('breadcrumb_image', SlideController.photos[SlideController.photoId][1]);
+        Element.setHref($('breadcrumb_image'), SlideController.baseUrl + '/view.php' + params + '&view=Image');
+        Element.setHref($('breadcrumb_gallery'), SlideController.baseUrl + '/view.php' + params + '&view=Gallery');
+        if ($('image_properties_link')) {
+            Element.setHref('image_properties_link', SlideController.baseUrl + '/image.php' + params + '&actionID=modify&share=' + SlideController.galleryShare);
+            Element.setOnClick('image_properties_link', function(){SlideController.pause();popup(this.href);return false;});
+        }
+        if ($('image_edit_link')) {
+            Element.setHref('image_edit_link', SlideController.baseUrl + '/image.php' + params + '&actionID=editimage');
+        }
+        if ($('image_ecard_link')) {
+            Element.setHref('image_ecard_link', SlideController.baseUrl + '/img/ecard.php?image=' + SlideController.photos[SlideController.photoId][3] + '&gallery=' + SlideController.galleryId);
+            Element.setOnClick('image_ecard_link', function(){SlideController.pause();popup(this.href);return false;});
+        }
+        if ($('image_delete_link')) {
+            //TODO : Guess we should have PHP save the localized text for this...
+            var deleteAction = function() {return window.confirm("Do you want to permanently delete " +  SlideController.photos[SlideController.photoId][1])};
+            Element.setHref($("image_delete_link"), SlideController.baseUrl + '/image.php' + params + '&actionID=delete');
+            Element.setOnClick('image_delete_link', deleteAction);
+        }
+        Element.setHref('image_download_link', SlideController.baseUrl + '/img/download.php?image=' + SlideController.photos[SlideController.photoId][3]);
+        Element.setOnClick('image_download_link', function(){SlideController.pause();});
+    },
+
+    showPhoto: function() {
+        new Effect.Appear(this.photo, { duration: 1.0, queue: 'end', afterFinish: (function() { Element.show(this.captionBox); this.updateLinks();}).bind(this) });
+
+        if (SlideController.playing) {
+            if (SlideController.interval) {
+                clearTimeout(SlideController.interval);
+            }
+            SlideController.interval = setTimeout(SlideController.next, SlideController.intervalSeconds * 1000);
+        }
+    },
+    nextPhoto: function() {
+        // Figure out which photo is next.
+        (SlideController.photoId == (SlideController.photos.length - 1)) ? SlideController.photoId = 0 : ++SlideController.photoId;
+        // Make sure the photo is loaded locally before we fade the current image.
+        SlideController.tempImage.src = SlideController.photos[SlideController.photoId][0];
+
+    },
+    prevPhoto: function() {
+        // Figure out which photo is previous.
+        (SlideController.photoId == 0) ? SlideController.photoId = SlideController.photos.length - 1 : --SlideController.photoId;
+        SlideController.tempImage.src = SlideController.photos[SlideController.photoId][0];
+    },
+    initSwap: function() {
+        // Begin by hiding main elements.
+        new Effect.Fade(this.captionBox, {duration: 0.5 });
+        new Effect.Fade(this.photo, { duration: 1.0, afterFinish: (function() { SlideController.photo.setNewPhotoParams();})});
+
+        // Update the current photo id.
+        this.photoId = SlideController.photoId;
+    }
+}
+ // Arrow keys for navigation
+ document.observe('keydown', arrowHandler);
+function arrowHandler(e)
+{
+    if (e.altKey || e.shiftKey || e.ctrlKey) {
+        return;
+    }
+
+    switch (e.keyCode || e.charCode) {
+    case Event.KEY_LEFT:
+        SlideController.prev();
+        break;
+
+    case Event.KEY_RIGHT:
+        SlideController.next();
+        break;
+    }
+}
diff --git a/ansel/js/src/slugcheck.js b/ansel/js/src/slugcheck.js
new file mode 100755 (executable)
index 0000000..f7bf5db
--- /dev/null
@@ -0,0 +1,43 @@
+function checkSlug()
+{
+    slug = document.gallery.gallery_slug.value;
+    // Empty slugs are always allowed.
+    if (!slug.length) {
+        return true;
+    }
+    
+    if (slug != Ansel.ajax.gallerySlugCheck.slugText) {
+        var url = Ansel.ajax.gallerySlugCheck.url;
+        var params = new Object();
+        params.slug = slug;
+        new Ajax.Request(url, {
+            method: 'post',
+            parameters: params,
+            onComplete: function(transport) {
+                var slugFlag = $('slug_flag');
+                response = transport.responseJSON.response;
+                if (response == 1) {
+                    if (slugFlag.hasClassName('problem')) {
+                        slugFlag.removeClassName('problem');
+                    }
+                    slugFlag.addClassName('success');
+                    $('gallery_submit').enable();
+                    // In case we try various slugs
+                    Ansel.ajax.gallerySlugCheck.slugText = slug;
+                } else {
+                    if (slugFlag.hasClassName('success')) {
+                        slugFlag.removeClassName('success');
+                    }
+                    slugFlag.addClassName('problem');
+                    $('gallery_submit').disable();
+                }
+            }
+        });
+    } else {
+           if (slugFlag.hasClassName('problem')) {
+               slugFlag.removeClassName('problem');
+           }
+           slugFlag.addClassName('success');
+           $('gallery_submit').enable();
+    }
+}
diff --git a/ansel/js/src/tagactions.js b/ansel/js/src/tagactions.js
new file mode 100755 (executable)
index 0000000..985044d
--- /dev/null
@@ -0,0 +1,47 @@
+function addTag()
+{
+    if (!$('addtag').value.blank()) {
+        var params = new Object();
+        params.action = 'add';
+        params[Ansel.ajax.tagActions.input] = $('addtag').value;
+        new Ajax.Request(Ansel.ajax.tagActions.url,
+                         {
+                            method: 'post',
+                            parameters: params,
+                            onComplete: function(r) {
+                                $('addtag').value = "";
+                                if (r.responseJSON.response == 1) {
+                                    $('tags').update(r.responseJSON.message);
+                                }
+                            }      
+                         });
+    }
+
+    return true;
+}
+
+function removeTag(tagid)
+{
+    var params = new Object();
+    params.action = 'remove';
+    params[Ansel.ajax.tagActions.input] = tagid;
+    new Ajax.Request(Ansel.ajax.tagActions.url,
+                    {
+                        method: 'post',
+                        parameters: params,
+                        onComplete: function(r) {
+                            if (r.responseJSON.response == 1) {
+                                $('tags').update(r.responseJSON.message);
+                            }
+                        }
+                    });
+
+    return true;
+}
+
+// Since onsubmit is never called when submitting programatically we
+// can use this function to add tags when we press enter on the tag form.
+function submitcheck()
+{
+    return !addTag();
+}
\ No newline at end of file
diff --git a/ansel/js/src/togglewidget.js b/ansel/js/src/togglewidget.js
new file mode 100755 (executable)
index 0000000..c299065
--- /dev/null
@@ -0,0 +1,38 @@
+/**
+ * @param string node  The DOM id of the node to show or hide.
+ *                     The node that contains the toggle link should be named
+ *                     {node}-toggle
+ *
+ * @param string requestType  The class name of the Ajax_Imple type for this
+ *                            widget.
+ *
+ */
+function doActionToggle(node, requestType)
+{
+    $(node).toggle();
+    togglePlusMinus(node, requestType);
+    return false;
+}
+
+function togglePlusMinus(node, requestType)
+{
+    var pref_value;
+
+    if ($(node + '-toggle').hasClassName('show')) {
+        $(node + '-toggle').removeClassName('show');
+        $(node + '-toggle').addClassName('hide');
+        var pref_value = 1;
+    } else if ($(node + '-toggle').hasClassName('hide')) {
+        $(node + '-toggle').removeClassName('hide');
+        $(node + '-toggle').addClassName('show');
+        var pref_value = 0;
+    }
+
+    var url = Ansel.widgets[requestType].url;
+    var params = new Object();
+    params.pref_value = pref_value;
+    new Ajax.Request(url, {
+        parameters: params
+    });
+}
\ No newline at end of file
diff --git a/ansel/lib/Ajax/Imple/EditCaption.php b/ansel/lib/Ajax/Imple/EditCaption.php
new file mode 100644 (file)
index 0000000..4da0207
--- /dev/null
@@ -0,0 +1,89 @@
+<?php
+/**
+ * Ansel_Ajax_Imple_EditCaption:: class for performing Ajax setting of image
+ * captions
+ *
+ * $Horde: ansel/lib/Ajax/Imple/EditCaption.php,v 1.2 2009/07/30 18:02:14 mrubinsk Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_Ajax_Imple_EditCaption extends Horde_Ajax_Imple_Base
+{
+    public function __construct($params)
+    {
+        /* Set up some defaults */
+        if (empty($params['rows'])) {
+            $params['rows'] = 2;
+        }
+        if (empty($params['cols'])) {
+            $params['cols'] = 20;
+        }
+        parent::__construct($params);
+    }
+
+    public function attach()
+    {
+        Horde::addScriptFile('effects.js', 'horde', true);
+        Horde::addScriptFile('controls.js', 'horde', true);
+        Horde::addScriptFile('editcaption.js', 'ansel', true);
+
+        $params = array('input' => 'value',
+                        'id' => $this->_params['id']);
+
+        $url = $this->_getUrl('EditCaption', 'ansel', $params);
+        $loadTextUrl = $this->_getUrl('EditCaption', 'ansel', array_merge($params, array('action' => 'load')));
+        $js = array();
+
+        $js[] = "new Ajax.InPlaceEditor('" . $this->_params['domid'] . "', '" . $url . "', {"
+                . "    callback: function(form, value) {"
+                . "      return 'value=' + encodeURIComponent(value);},"
+                . "   loadTextURL: '". $loadTextUrl . "',"
+                . "   rows:" . $this->_params['rows'] . ","
+                . "   cols:" . $this->_params['cols'] . ","
+                . "   emptyText: '" . _("Click to add caption...") . "',"
+                . "   onComplete: function(transport, element) {tileExit(this);}"
+                . "  });";
+
+        Horde::addInlineScript($js, 'dom');
+    }
+
+    public function handle($args)
+    {
+        include_once dirname(__FILE__) . '/../../base.php';
+
+        if (Horde_Auth::getAuth()) {
+            /* Are we requesting the unformatted text? */
+            if (!empty($args['action']) && $args['action'] == 'load') {
+                $id = $args['id'];
+                $image = $GLOBALS['ansel_storage']->getImage($id);
+                $caption = $image->caption;
+
+                return $caption;
+            }
+            if (empty($args['input']) ||
+                is_null($pref_value = Horde_Util::getPost($args['input'], null)) ||
+                empty($args['id']) || !is_numeric($args['id'])) {
+
+                    return '';
+            }
+            $id = $args['id'];
+            $image = $GLOBALS['ansel_storage']->getImage($id);
+            $g = $GLOBALS['ansel_storage']->getGallery($image->gallery);
+            if ($g->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+                $image->caption = $pref_value;
+                $result = $image->save();
+                if (is_a($result, 'PEAR_Error')) {
+                    return '';
+                }
+            }
+            $imageCaption = Horde_Text_Filter::filter(
+                $image->caption, 'text2html',
+                array('parselevel' => Horde_Text_Filter_Text2html::MICRO));
+            return $imageCaption;
+        }
+    }
+
+}
diff --git a/ansel/lib/Ajax/Imple/EditFaces.php b/ansel/lib/Ajax/Imple/EditFaces.php
new file mode 100644 (file)
index 0000000..f541170
--- /dev/null
@@ -0,0 +1,148 @@
+<?php
+/**
+ * Ansel_Ajax_Imple_EditFaces:: class for performing Ajax discovery and editing
+ * of image faces
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * @author Duck <duck@obala.net>
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ *
+ * @package Ansel
+ */
+class Ansel_Ajax_Imple_EditFaces extends Horde_Ajax_Imple_Base
+{
+    /**
+     * Attach these actions to the view
+     *
+     */
+    public function attach()
+    {
+        Horde::addScriptFile('editfaces.js');
+
+        $url = $this->_getUrl('EditFaces', 'ansel', array('url' => rawurlencode($this->_params['selfUrl'])));
+        $js = array();
+        $js[] = "Ansel.ajax['editFaces'] = {'url':'" . $url . "', text: {loading:'" . _("Loading...") . "'}};";
+        $js[] = "Event.observe('" . $this->_params['domid'] . "', 'click', function(event) {Ansel.doFaceEdit(" . $this->_params['image_id'] . ");Event.stop(event)});";
+
+        Horde::addInlineScript($js, 'dom');
+    }
+
+    function handle($args)
+    {
+        include_once dirname(__FILE__) . '/../../base.php';
+
+        if (Horde_Auth::getAuth()) {
+            /* Require POST for these actions */
+            $action = Horde_Util::getPost('action');
+            $image_id = (int)Horde_Util::getPost('image');
+            $reload = Horde_Util::getPost('reload', 0);
+
+            if (empty($action)) {
+                return array('response' => 0);
+            }
+
+            $faces = Ansel_Faces::factory();
+            if (is_a($faces, 'PEAR_Error')) {
+                die($faces->getMessage());
+            }
+
+            switch($action) {
+            case 'process':
+                // process - detects all faces in the image.
+                $name = '';
+                $autocreate = true;
+                $result = $faces->getImageFacesData($image_id);
+                // Attempt to get faces from the picture if we don't already have results,
+                // or if we were asked to explicitly try again.
+                if (($reload || empty($result))) {
+                    $image = &$GLOBALS['ansel_storage']->getImage($image_id);
+                    if (is_a($image, 'PEAR_Error')) {
+                        exit;
+                    }
+
+                    $result = $image->createView('screen');
+                    if (is_a($result, 'PEAR_Error')) {
+                        exit;
+                    }
+
+                    $result = $faces->getFromPicture($image_id, $autocreate);
+                    if (is_a($result, 'PEAR_Error')) {
+                        exit;
+                    }
+                }
+                if (!empty($result)) {
+                    $imgdir = $GLOBALS['registry']->getImageDir('horde');
+                    $customurl = Horde::applicationUrl('faces/custom.php');
+                    $url = (!empty($args['url']) ? urldecode($args['url']) : '');
+                    ob_start();
+                    require_once ANSEL_TEMPLATES . '/faces/image.inc';
+                    $html = ob_get_clean();
+                    return array('response' => 1,
+                                 'message' => $html);
+                } else {
+                    return array('response' => 1,
+                                 'message' => _("No faces found"));
+                }
+                break;
+
+            case 'delete':
+                // delete - deletes a single face from an image.
+                $face_id = (int)Horde_Util::getPost('face');
+                $image = &$GLOBALS['ansel_storage']->getImage($image_id);
+                if (is_a($image, 'PEAR_Error')) {
+                    die($image->getMessage());
+                }
+
+                $gallery = &$GLOBALS['ansel_storage']->getGallery($image->gallery);
+                if (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+                    die(_("Access denied editing the photo."));
+                }
+
+                $faces = Ansel_Faces::factory();
+                if (is_a($faces, 'PEAR_Error')) {
+                    die($faces->getMessage());
+                }
+
+                $result = $faces->delete($image, $face_id);
+                if (is_a($result, 'PEAR_Error')) {
+                    die($result->getMessage());
+                }
+                break;
+
+            case 'setname':
+                // setname - sets the name of a single image.
+                $face_id = (int)Horde_Util::getPost('face');
+                if (!$face_id) {
+                    return array('response' => 0);
+                }
+
+                $name = Horde_Util::getPost('facename');
+                $image = &$GLOBALS['ansel_storage']->getImage($image_id);
+                if (is_a($image, 'PEAR_Error')) {
+                    die($image->getMessage());
+                }
+
+                $gallery = &$GLOBALS['ansel_storage']->getGallery($image->gallery);
+                if (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+                    die(_("You are not allowed to edit this photo."));
+                }
+
+                $faces = Ansel_Faces::factory();
+                if (is_a($faces, 'PEAR_Error')) {
+                    die($faces->getMessage());
+                }
+
+                $result = $faces->setName($face_id, $name);
+                if (is_a($result, 'PEAR_Error')) {
+                    die($result->getDebugInfo());
+                }
+
+                return array('response' => 1,
+                             'message' => Ansel_Faces::getFaceTile($face_id));
+                break;
+            }
+        }
+    }
+
+}
diff --git a/ansel/lib/Ajax/Imple/Embed.php b/ansel/lib/Ajax/Imple/Embed.php
new file mode 100644 (file)
index 0000000..1417916
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+/**
+ * Ansel_Ajax_Imple_Embed:: Class for embedding a small gallery widget in external
+ * websites. Meant to be called via a single script tag, therefore this will
+ * always return nothing but valid javascript.
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ *
+ * @package Ansel
+ */
+class Ansel_Ajax_Imple_Embed extends Horde_Ajax_Imple_Base
+{
+    // Noop since we don't attach this to any UI element.
+    public function attach(){}
+
+    public function getUrl()
+    {
+        return $this->_getUrl('Embed', 'ansel', $this->_params, true);
+    }
+
+    /**
+     * Handles the output of the embedded widget. This must always be valid
+     * javascript.
+     *
+     * @see Ansel_View_Embedded for parameters.
+     *
+     * @param array $args  Arguments for this view.
+     */
+    public function handle($args)
+    {
+        include_once dirname(__FILE__) . '/../../base.php';
+
+        /* First, determine the type of view we are asking for */
+        $view = empty($args['gallery_view']) ? 'Mini' : $args['gallery_view'];
+
+        require_once ANSEL_BASE . '/lib/Views/EmbeddedRenderers/' . basename($view) . '.php';
+        $class = 'Ansel_View_EmbeddedRenderer_' . basename($view);
+        if (!class_exists($class)) {
+            return '';
+        }
+
+        $view = call_user_func(array($class, 'makeView'), $args);
+
+        header('Content-Type: script/javascript');
+        return $view->html();
+    }
+
+}
diff --git a/ansel/lib/Ajax/Imple/GallerySlugCheck.php b/ansel/lib/Ajax/Imple/GallerySlugCheck.php
new file mode 100644 (file)
index 0000000..b4717c1
--- /dev/null
@@ -0,0 +1,47 @@
+<?php
+/**
+ * Ansel_Ajax_Imple_GallerySlugCheck:: class for performing Ajax validation of
+ * gallery slugs.
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_Ajax_Imple_GallerySlugCheck extends Horde_Ajax_Imple_Base
+{
+    public function attach()
+    {
+        // Include the js
+        Horde::addScriptFile('prototype.js', 'horde');
+        Horde::addScriptFile('slugcheck.js');
+
+        $url = $this->_getUrl('GallerySlugCheck', 'ansel', array('input' => 'slug'));
+
+        $js = array();
+        $js[] = "Ansel.ajax['gallerySlugCheck'] = {slugText:'" . $this->_params['slug'] . "', 'url': '" . $url . "', bindTo: '" . $this->_params['bindTo'] . "'};";
+        $js[] = "Event.observe(Ansel.ajax.gallerySlugCheck.bindTo, 'change', checkSlug);";
+
+        Horde::addInlineScript($js, 'dom');
+    }
+
+    public function handle($args)
+    {
+        include_once dirname(__FILE__) . '/../../base.php';
+        if (empty($args['input'])) {
+            return array('response' => '1');
+         }
+        $slug = Horde_Util::getPost($args['input']);
+        if (empty($slug)) {
+            return array('response' => '1');
+        }
+        $valid = preg_match('/^[a-zA-Z0-9_-]*$/', $slug);
+        if (!$valid) {
+            return array('response' => '0');
+        }
+
+        $exists = $GLOBALS['ansel_storage']->slugExists($slug) ? 0 : 1;
+        return array('response' => $exists);
+    }
+
+}
diff --git a/ansel/lib/Ajax/Imple/ImageSaveGeotag.php b/ansel/lib/Ajax/Imple/ImageSaveGeotag.php
new file mode 100644 (file)
index 0000000..49fcadc
--- /dev/null
@@ -0,0 +1,92 @@
+<?php
+/**
+ * Ansel_Ajax_Imple_ImageSaveGeotag:: class for saving/updating image geotag
+ * data.
+ *
+ * Copyright 2009 The Horde Project (http://www.horde.org)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_Ajax_Imple_ImageSaveGeotag extends Horde_Ajax_Imple_Base
+{
+    // Noop since this isn't attached to any UI Element
+    public function attach() {}
+
+    public function getUrl()
+    {
+        return $this->_getUrl('ImageSaveGeotag', 'ansel');
+    }
+
+    public function handle($args)
+    {
+        include_once dirname(__FILE__) . '/../../base.php';
+
+        /* Require type, location, img to be from POST */
+        $type = Horde_Util::getPost('type');
+        $location = Horde_Util::getPost('location');
+        $img = Horde_Util::getPost('img');
+        $lat = Horde_Util::getPost('lat');
+        $lng = Horde_Util::getPost('lng');
+
+        if (empty($img) ||
+            ($type == 'location' && empty($location)) ||
+            ((empty($type) || $type == 'all') &&
+             ($type == 'all' && empty($lat)))) {
+
+            return array('response' => 0);
+        }
+
+        // Get the image and gallery to check perms
+        $image = $GLOBALS['ansel_storage']->getImage((int)$img);
+        if (is_a($image, 'PEAR_Error')) {
+            return array('response' => 0);
+        }
+        $gallery = $GLOBALS['ansel_storage']->getGallery($image->gallery);
+        if (is_a($gallery, 'PEAR_Error')) {
+            return array('response' => 0);
+        }
+        // Bail out if no perms on the image.
+        if (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+            return array('response' => 0);
+        }
+        switch ($type) {
+        case 'geotag':
+            $image->geotag($lat, $lng, !empty($location) ? $location : '');
+            return array('response' => 1);
+
+        case 'location':
+            $image->location = !empty($location) ? urldecode($location) : '';
+            $image->save();
+            return array('response' => 1, 'message' => htmlentities($image->location));
+
+        case 'untag':
+            $image->geotag('', '', '');
+            // Now get the "add geotag" stuff
+            $addurl = Horde_Util::addParameter(Horde::applicationUrl('map_edit.php'), 'image', $img);
+            $addLink = Horde::link($addurl, '', '', '', 'popup(\'' . Horde_Util::addParameter(Horde::applicationUrl('map_edit.php'), 'image', $img) . '\'); return false;');
+            $imgs = $GLOBALS['ansel_storage']->getRecentImagesGeodata(Horde_Auth::getAuth());
+            if (count($imgs) > 0) {
+                $imgsrc = '<div class="ansel_location_sameas">';
+                foreach ($imgs as $id => $data) {
+                    if (!empty($data['image_location'])) {
+                        $title = $data['image_location'];
+                    } else {
+                        $title = $this->_point2Deg($data['image_latitude'], true) . ' ' . $this->_point2Deg($data['image_longitude']);
+                    }
+                    $imgsrc .= Horde::link($addurl, $title, '', '', "setLocation('" . $data['image_latitude'] . "', '" . $data['image_longitude'] . "');return false") . '<img src="' . Ansel::getImageUrl($id, 'mini', true) . '" alt="[image]" /></a>';
+                }
+                $imgsrc .= '</div>';
+                $content = sprintf(_("No location data present. Place using %s map %s or click on image to place at the same location."), $addLink, '</a>') . $imgsrc;
+            } else {
+                $content = _("No location data present. You may add some ") . $addLink . _("here") . '</a>';
+            }
+
+            return array('response' => 1, 'message' => $content);
+        }
+    }
+
+}
diff --git a/ansel/lib/Ajax/Imple/LocationAutoCompleter.php b/ansel/lib/Ajax/Imple/LocationAutoCompleter.php
new file mode 100644 (file)
index 0000000..bb46145
--- /dev/null
@@ -0,0 +1,104 @@
+<?php
+/**
+ * Autocompleter for textual location data.
+ *
+ * Copyright 2009 The Horde Project (http://www.horde.org)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_Ajax_Imple_LocationAutoCompleter extends Horde_Ajax_Imple_Base
+{
+    public function __construct($params)
+    {
+        if (!empty($params['triggerId'])) {
+            if (empty($params['resultsId'])) {
+                $params['resultsId'] = $params['triggerId'] . '_results';
+            }
+        }
+
+        parent::__construct($params);
+    }
+
+    public function attach()
+    {
+        Horde::addScriptFile('prototype.js', 'horde', true);
+        Horde::addScriptFile('autocomplete.js', 'horde', true);
+        Horde::addScriptFile('effects.js', 'horde', true);
+
+        $url = $this->_getUrl('LocationAutoCompleter', 'ansel', array('input' => $this->_params['triggerId']));
+
+        /* Use ajax? */
+        if (!isset($_SESSION['ansel']['ajax_locationac'])) {
+            $results = $GLOBALS['ansel_storage']->searchLocations();
+            if (is_a($results, 'PEAR_Error')) {
+                Horde::logMessage($results, __FILE__, __LINE__, PEAR_LOG_ERR);
+            } else {
+                // @TODO: This should be a config param?
+                if (count($results) > 50) {
+                    $_SESSION['ansel']['ajax_locationac'] = true;
+                } else {
+                    $_SESSION['ansel']['ajax_locationac'] = false;
+                }
+            }
+        }
+
+        $params = array(
+            '"' . $this->_params['triggerId'] . '"',
+            '"' . $this->_params['resultsId'] . '"'
+        );
+
+        $js_params = array(
+            'tokens: []',
+            'indicator: "' . $this->_params['triggerId'] . '_loading_img"',
+            'afterUpdateElement: function(e, v) {' . $this->_params['map'] . '.ll = Ansel.ajax.locationAutoCompleter.geocache[v.collectTextNodesIgnoreClass(\'informal\')];}',
+            'afterUpdateChoices: function(c, l) {if (!c.size()) {' . $this->_params['map'] . '.ll = null;}}'
+        );
+        $js = array();
+        if ($_SESSION['ansel']['ajax_locationac']) {
+            $params[] = '"' . $url . '"';
+            $params[] = '{' . implode(',', $js_params) . '}';
+            $js[] = 'Ansel.ajax[\'locationAutoCompleter\'] = new Ajax.Autocompleter(' . implode(',', $params) . ');';
+        } else {
+            if (empty($results)) {
+                $results = $GLOBALS['ansel_storage']->searchLocations();
+            }
+            $jsparams[] = 'ignoreCase: true';
+            $params[] = Horde_Serialize::serialize($results, Horde_Serialize::JSON, Horde_Nls::getCharset());
+            $params[] = '{' . implode(',', $js_params) . '}';
+            $js[] = 'Ansel.ajax[\'locationAutoCompleter\'] = new Autocompleter.Local(' . implode(',', $params) . ');';
+        }
+
+        Horde::addInlineScript($js, 'dom');
+    }
+
+    public function handle($args)
+    {
+        include_once dirname(__FILE__) . '/../../base.php';
+
+        // Avoid errors if 'input' isn't set and short-circuit empty searches.
+        if (empty($args['input']) ||
+            !($input = Horde_Util::getFormData($args['input']))) {
+            return array();
+        }
+        $locs = $GLOBALS['ansel_storage']->searchLocations($input);
+        if (is_a($locs, 'PEAR_Error')) {
+            return array('response' => 0);
+        }
+
+        $results = $locs;
+
+        if (count($results) == 0) {
+            $results = array('response' => 0, 'message' => array());
+        } else {
+            $results = array('response' => count($results),
+                             'message' => Horde_Serialize::serialize($results, Horde_Serialize::JSON, Horde_Nls::getCharset()));
+        }
+
+        return $results;
+    }
+
+}
diff --git a/ansel/lib/Ajax/Imple/TagActions.php b/ansel/lib/Ajax/Imple/TagActions.php
new file mode 100644 (file)
index 0000000..34bb4a5
--- /dev/null
@@ -0,0 +1,122 @@
+<?php
+/**
+ * Ansel_Ajax_Imple_TagActions:: class for handling adding/deleting tags via
+ * Ajax calls.
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_Ajax_Imple_TagActions extends Horde_Ajax_Imple_Base
+{
+    public function attach()
+    {
+        // Include the js
+        Horde::addScriptFile('prototype.js', 'horde');
+        Horde::addScriptFile('tagactions.js');
+
+        $url = $this->_getUrl('TagActions', 'ansel', array('input' => 'tags',
+                                                           'gallery' => $this->_params['gallery'],
+                                                           'image' =>  (isset($this->_params['image']) ? $this->_params['image'] : 0)));
+        $params = array('url' => $url,
+                        'gallery' => $this->_params['gallery'],
+                        'image' => (isset($this->_params['image']) ? $this->_params['image'] : 0),
+                        'bindTo' => $this->_params['bindTo'],
+                        'input' => 'tags');
+        $js = array();
+        $js[] = "Ansel.ajax['tagActions'] = " . Horde_Serialize::serialize($params, Horde_Serialize::JSON) . ";";
+        // TODO: Attach the Add & Delete actions too
+        $js[] = "Event.observe(Ansel.ajax.tagActions.bindTo.add, 'click', function(event) {addTag(); Event.stop(event)});";
+
+        Horde::addInlineScript($js, 'dom');
+    }
+
+    public function handle($args)
+    {
+        include_once dirname(__FILE__) . '/../../base.php';
+
+        /* Require a POST for the action parameter and the tags parameter */
+        $action = Horde_Util::getPost('action');
+        if (empty($action) || empty($args['input'])) {
+            return array('response' => '0');
+        }
+
+        $gallery = $args['gallery'];
+        $image = isset($args['image']) ? $args['image'] : null;
+
+        if ($image) {
+            $id = $image;
+            $type = 'image';
+        } else {
+            $id = $gallery;
+            $type = 'gallery';
+        }
+        $tags = Horde_Util::getPost($args['input']);
+        if (!is_numeric($id)) {
+            return array('response' => 0,
+                         'message' => sprintf(_("Invalid input %s"), htmlspecialchars($id)));
+        }
+
+        /* Get the resource owner */
+        if ($type == 'gallery') {
+            $resource = $GLOBALS['ansel_storage']->getGallery($id);
+            $parent = $resource;
+        } else {
+            $resource = $GLOBALS['ansel_storage']->getImage($id);
+            $parent = $GLOBALS['ansel_storage']->getGallery($resource->gallery);
+        }
+        $owner = $parent->get('owner');
+
+        switch ($action) {
+        case 'add':
+            if (!empty($tags)) {
+                $tags = explode(',', $tags);
+                /* Get current tags so we don't overwrite them */
+                $etags = Ansel_Tags::readTags($id, $type);
+                $tags = array_keys(array_flip(array_merge($tags, array_values($etags))));
+                $resource->setTags($tags);
+
+                /* Get the tags again since we need the newly added tag_ids */
+                $newTags = $resource->getTags();
+                if (count($newTags)) {
+                    $newTags = Ansel_Tags::listTagInfo(array_keys($newTags));
+                }
+
+                return array('response' => 1,
+                             'message' => $this->_getTagHtml($newTags, $owner,
+                                                             $parent->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)));
+            }
+            break;
+
+        case 'remove':
+            $existingTags = $resource->getTags();
+            unset($existingTags[$tags]);
+            $resource->setTags($existingTags);
+            if (count($existingTags)) {
+                $newTags = Ansel_Tags::listTagInfo(array_keys($existingTags));
+            } else {
+                $newTags = array();
+            }
+
+            return array('response' => 1,
+                         'message' => $this->_getTagHtml($newTags, $owner,
+                                                         $parent->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)));
+            break;
+        }
+
+    }
+
+    private function _getTagHtml($tags, $owner, $hasEdit)
+    {
+        global $registry;
+        $links = Ansel_Tags::getTagLinks($tags, 'add');
+        $html = '<ul>';
+        foreach ($tags as $tag_id => $taginfo) {
+            $html .= '<li>' . Horde::link($links[$tag_id], sprintf(ngettext("%d photo", "%d photos", $taginfo['total']), $taginfo['total'])) . $taginfo['tag_name'] . '</a>' . ($hasEdit ? '<a href="#" onclick="removeTag(' . $tag_id . ');">' . Horde::img('delete-small.png', _("Remove Tag"), '', $registry->getImageDir('horde')) . '</a>' : '') . '</li>';
+        }
+        $html .= '</ul>';
+        return $html;
+    }
+
+}
diff --git a/ansel/lib/Ajax/Imple/ToggleGalleryActions.php b/ansel/lib/Ajax/Imple/ToggleGalleryActions.php
new file mode 100644 (file)
index 0000000..9f019cd
--- /dev/null
@@ -0,0 +1,39 @@
+<?php
+/**
+ * Ansel_Ajax_Imple_ToggleGalleryActions:: class for performing Ajax setting of
+ * the gallery show_galleryactions user pref.
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_Ajax_Imple_ToggleGalleryActions extends Horde_Ajax_Imple_Base
+{
+    public function attach()
+    {
+        // Include the js
+        Horde::addScriptFile('prototype.js', 'horde');
+        Horde::addScriptFile('togglewidget.js');
+
+        $url = $this->_getUrl('ToggleGalleryActions', 'ansel', array('input' => 'pref_value', 'sessionWrite' => true));
+
+        $js = array();
+        $js[] = "Ansel.widgets['galleryActions'] = {'bindTo': '" . $this->_params['bindTo'] . "', 'url': '" . $url . "'}";
+        $js[] = "Event.observe(Ansel.widgets.galleryActions.bindTo + '-toggle', 'click', function(event) {doActionToggle('" . $this->_params['bindTo'] . "', 'galleryActions'); Event.stop(event)});";
+
+        Horde::addInlineScript($js, 'dom');
+    }
+
+    public function handle($args)
+    {
+         if (empty($args['input'])) {
+            return 0;
+         }
+         $input = Horde_Util::getPost($args['input']);
+         $GLOBALS['prefs']->setValue('show_actions', $input);
+
+        return 1;
+    }
+
+}
diff --git a/ansel/lib/Ajax/Imple/ToggleOtherGalleries.php b/ansel/lib/Ajax/Imple/ToggleOtherGalleries.php
new file mode 100644 (file)
index 0000000..885c8d6
--- /dev/null
@@ -0,0 +1,39 @@
+<?php
+/**
+ * Ansel_Ajax_Imple_ToggleOtherGalleries:: class for performing Ajax setting of
+ * the gallery show_actions user pref.
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_Ajax_Imple_ToggleOtherGalleries extends Horde_Ajax_Imple_Base
+{
+    public function attach()
+    {
+        // Include the js
+        Horde::addScriptFile('prototype.js', 'horde');
+        Horde::addScriptFile('togglewidget.js');
+
+        $url = $this->_getUrl('ToggleOtherGalleries', 'ansel', array('input' => 'pref_value', 'sessionWrite' => true));
+        $js = array();
+        $js[] = "Ansel.widgets['otherGalleries'] = {'bindTo': '" . $this->_params['bindTo'] . "', 'url': '" . $url . "'}";
+        $js[] = "Event.observe(Ansel.widgets.otherGalleries.bindTo + '-toggle', 'click', function(event) {doActionToggle('" . $this->_params['bindTo'] . "', 'otherGalleries'); Event.stop(event)});";
+
+        Horde::addInlineScript($js, 'dom');
+    }
+
+    public function handle($args)
+    {
+         if (empty($args['input'])) {
+            return 0;
+         }
+        $input = Horde_Util::getPost($args['input']);
+        $GLOBALS['prefs']->setValue('show_othergalleries', $input);
+        echo $GLOBALS['prefs']->getValue('show_othergalleries');
+
+        return 1;
+    }
+
+}
diff --git a/ansel/lib/Ansel.php b/ansel/lib/Ansel.php
new file mode 100644 (file)
index 0000000..5157a9b
--- /dev/null
@@ -0,0 +1,3996 @@
+<?php
+/**
+ * $Horde: ansel/lib/Ansel.php,v 1.628 2009/07/30 18:02:14 mrubinsk Exp $
+ *
+ * Copyright 2001-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ */
+
+/** Horde_Share */
+require_once 'Horde/Share.php';
+
+/** Need to bring this in explicitly since we extend the object class */
+require_once 'Horde/Share/sql_hierarchical.php';
+
+/**
+ * Ansel Base Class.
+ *
+ * @author  Chuck Hagenbuch <chuck@horde.org>
+ * @author  Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel {
+
+    /**
+     * Build initial Ansel javascript object.
+     *
+     * @return string
+     */
+    function initJSVars()
+    {
+        $code = array('Ansel = {ajax: {}, widgets: {}}');
+        return $code;
+    }
+
+    /**
+     * Create and initialize the database object.
+     *
+     * @return mixed MDB2 object || PEAR_Error
+     */
+    function &getDb()
+    {
+        require_once 'MDB2.php';
+        $config = $GLOBALS['conf']['sql'];
+        unset($config['charset']);
+        $mdb = &MDB2::singleton($config);
+        if (is_a($mdb, 'PEAR_Error')) {
+            return $mdb;
+        }
+        $mdb->setOption('seqcol_name', 'id');
+
+        /* Set DB portability options. */
+        switch ($mdb->phptype) {
+        case 'mssql':
+            $mdb->setOption('field_case', CASE_LOWER);
+            $mdb->setOption('portability', MDB2_PORTABILITY_FIX_CASE | MDB2_PORTABILITY_ERRORS | MDB2_PORTABILITY_RTRIM | MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES);
+            break;
+        default:
+            $mdb->setOption('field_case', CASE_LOWER);
+            $mdb->setOption('portability', MDB2_PORTABILITY_FIX_CASE | MDB2_PORTABILITY_ERRORS | MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES);
+        }
+
+        return $mdb;
+    }
+
+    /**
+     * Create and initialize the VFS object
+     *
+     * @return VFS object or fatals on error.
+     */
+    function &getVFS()
+    {
+        $v_params = Horde::getVFSConfig('images');
+        if (is_a($v_params, 'PEAR_Error')) {
+            Horde::fatal(_("You must configure a VFS backend to use Ansel."),
+                         __FILE__, __LINE__);
+        }
+        if ($v_params['type'] != 'none') {
+            require_once 'VFS.php';
+            $vfs = &VFS::singleton($v_params['type'], $v_params['params']);
+        }
+        if (empty($vfs) || is_a($vfs, 'PEAR_ERROR')) {
+            Horde::fatal(_("You must configure a VFS backend to use Ansel."),
+                         __FILE__, __LINE__);
+        }
+        return $vfs;
+    }
+
+    /**
+     * Return a string containing an <option> listing of the given
+     * gallery array.
+     *
+     * @param array $selected     The gallery_id of the  gallery that is
+     *                            selected by default in the returned option
+     *                            list.
+     * @param integer $perm       The permissions filter to use.
+     * @param mixed $attributes   Restrict the galleries returned to those
+     *                            matching $attributes. An array of
+     *                            attribute/values pairs or a gallery owner
+     *                            username.
+     * @param string $parent      The parent share to start listing at.
+     * @param integer $from       The gallery to start listing at.
+     * @param integer $count      The number of galleries to return.
+     * @param integer $ignore     An Ansel_Gallery id to ignore when building
+     *                            the tree.
+     *
+     * @return string  The <option> list.
+     */
+    function selectGalleries($selected = null, $perm = PERMS_SHOW,
+                             $attributes = null, $parent = null,
+                             $allLevels = true, $from = 0, $count = 0,
+                             $ignore = null)
+    {
+        global $ansel_storage;
+        require_once 'Horde/Tree.php';
+        $galleries = $ansel_storage->listGalleries($perm, $attributes, $parent,
+                                                   $allLevels, $from, $count);
+        $tree = Horde_Tree::factory('gallery_tree', 'select');
+
+        if (!empty($ignore)) {
+           unset($galleries[$ignore]);
+           if ($selected == $ignore) {
+               $selected = null;
+           }
+        }
+        foreach ($galleries as $gallery_id => $gallery) {
+            // We don't use $gallery->getParents() on purpose since we
+            // only need the count of parents. This potentially saves a number
+            // of DB queries.
+            $parents = $gallery->get('parents');
+            if (empty($parents)) {
+                $indents = 0;
+            } else {
+                $indents = substr_count($parents, ':') + 1;
+            }
+
+            $gallery_name = $gallery->get('name');
+            $len = Horde_String::length($gallery_name);
+            if ($len > 30) {
+                $label = Horde_String::substr($gallery_name, 0, 30) . '...';
+            } else {
+                $label = $gallery_name;
+            }
+
+            $params['selected'] = ($gallery_id == $selected);
+            $parent = $gallery->getParent();
+            $parent = (is_null($parent)) ? $parent : $parent->id;
+            if ((!empty($parent) && !empty($galleries[$parent])) ||
+                (empty($parent))) {
+                $tree->addNode($gallery->id, $parent, $label, $indents, true,
+                               $params);
+            }
+        }
+
+        return $tree->getTree();
+    }
+
+    /**
+     * Return a link to a photo placeholder, suitable for use in an <img/>
+     * tag (or a Horde::img() call, with the path parameter set to * '').
+     * This photo should be used as a placeholder if the correct photo can't
+     * be retrieved
+     *
+     * @param string $view  The view ('screen', 'thumb', or 'full') to show.
+     *                      Defaults to 'screen'.
+     *
+     * @return string  The image path.
+     */
+    function getErrorImage($view = 'screen')
+    {
+        return $GLOBALS['registry']->getImageDir() . '/' . $view . '-error.png';
+    }
+
+    /**
+     * Return a properly formatted link depending on the global pretty url
+     * configuration
+     *
+     * @param string $controller       The controller to generate a URL for.
+     * @param array $data              The data needed to generate the URL.
+     * @param boolean $full            Generate a full URL.
+     * @param integer $append_session  0 = only if needed, 1 = always,
+     *                                 -1 = never.
+     *
+     * @param string  The generated URL
+     */
+    function getUrlFor($controller, $data, $full = false, $append_session = 0)
+    {
+        global $prefs;
+
+        $rewrite = isset($GLOBALS['conf']['urls']['pretty']) &&
+            $GLOBALS['conf']['urls']['pretty'] == 'rewrite';
+
+        switch ($controller ) {
+        case 'view':
+            if ($rewrite && (empty($data['special']))) {
+                $url = '';
+
+                /* Viewing a List */
+                if ($data['view'] == 'List') {
+                    if (!empty($data['groupby']) &&
+                        $data['groupby'] == 'category' &&
+                        empty($data['category']) &&
+                        empty($data['special'])) {
+
+                        $data['groupby'] = 'owner';
+                    }
+
+                    $groupby = isset($data['groupby'])
+                        ? $data['groupby']
+                        : $prefs->getValue('groupby');
+                    if ($groupby == 'owner' && !empty($data['owner'])) {
+                        $url = 'user/' . urlencode($data['owner']) . '/';
+                    } elseif ($groupby == 'owner') {
+                        $url = 'user/';
+                    } elseif ($groupby == 'category' &&
+                              !empty($data['category'])) {
+                            $url = 'category/' . urlencode($data['category']) . '/';
+
+                    } elseif ($groupby == 'category') {
+                        $url = 'category/';
+                    } elseif ($groupby == 'none') {
+                       $url = 'all/';
+                    }
+
+                    // Keep the URL as clean as possible - don't append the page
+                    // number if it's zero, which would be the default.
+                    if (!empty($data['page'])) {
+                        $url = Horde_Util::addParameter($url, 'page', $data['page']);
+                    }
+                    return Horde::applicationUrl($url, $full, $append_session);
+                }
+
+                /* Viewing a Gallery or Image */
+                if ($data['view'] == 'Gallery' || $data['view'] == 'Image') {
+
+                    /**
+                     * This is needed to correctly generate URLs for images in
+                     * places that are not specifically requested by the user,
+                     * for instance, in a gallery block. Otherwise, the proper
+                     * date variables would not be attached to the url, since we
+                     * don't know them ahead of time.  This is a slight hack and
+                     * needs to be corrected, probably by delegating at least
+                     * some of the URL generation to the gallery/image/view
+                     * object...most likely when we move to PHP5.
+                     */
+                    if (empty($data['year']) && $data['view'] == 'Image') {
+                        // Getting these objects is not ideal, but at this point
+                        // they should already be locally cached so the cost
+                        // is minimized.
+                        $i = &$GLOBALS['ansel_storage']->getImage($data['image']);
+                        $g = &$GLOBALS['ansel_storage']->getGallery($data['gallery']);
+                        if (!is_a($g, 'PEAR_Error') &&
+                            !is_a($i, 'PEAR_Error') &&
+                            $g->get('view_mode') == 'Date') {
+
+                            require_once 'Horde/Date.php';
+                            $imgDate = new Horde_Date($i->originalDate);
+                            $data['year'] = $imgDate->year;
+                            $data['month'] = $imgDate->month;
+                            $data['day'] = $imgDate->mday;
+                        }
+                    }
+
+                    $url = 'gallery/'
+                        . (!empty($data['slug'])
+                           ? $data['slug']
+                           : 'id/' . (int)$data['gallery'])
+                        . '/';
+
+                    // See comments below about lightbox
+                    if ($data['view'] == 'Image' &&
+                        (empty($data['gallery_view']) ||
+                         (!empty($data['gallery_view']) &&
+                         $data['gallery_view'] != 'GalleryLightbox'))) {
+
+                        $url .= (int)$data['image'] . '/';
+                    }
+
+                    $extras = array();
+                    // We may have a value of zero here, but it's the default,
+                    // so ignore it if it's empty.
+                    if (!empty($data['havesearch'])) {
+                        $extras['havesearch'] = $data['havesearch'];
+                    }
+
+                    // Block any auto navigation (for date views)
+                    if (!empty($data['force_grouping'])) {
+                        $extras['force_grouping'] = $data['force_grouping'];
+                    }
+
+                    if (count($extras)) {
+                        $url = Horde_Util::addParameter($url, $extras);
+                    }
+
+                }
+
+                if ($data['view'] == 'Results')  {
+                    $url = 'tag/' . (!empty($data['tag'])
+                                     ? urlencode($data['tag']) . '/'
+                                     : '');
+
+                    if (!empty($data['actionID'])) {
+                        $url = Horde_Util::addParameter($url, 'actionID',
+                                                  $data['actionID']);
+                    }
+
+                    if (!empty($data['owner'])) {
+                        $url = Horde_Util::addParameter($url, 'owner',
+                                                  $data['owner']);
+                    }
+                }
+
+                // Keep the URL as clean as possible - don't append the page
+                // number if it's zero, which would be the default.
+                if (!empty($data['page'])) {
+                    $url = Horde_Util::addParameter($url, 'page', $data['page']);
+                }
+
+                if (!empty($data['year'])) {
+                    $url = Horde_Util::addParameter($url, array('year' => $data['year'],
+                                                          'month' => (empty($data['month']) ? 0 : $data['month']),
+                                                          'day' => (empty($data['day']) ? 0 : $data['day'])));
+                }
+
+                // If we are using GalleryLightbox, AND we are linking to an
+                // image view, append the imageId here to be sure it's at the
+                // end of the URL. This is a complete hack, but saves us from
+                // having to delegate the URL generation to the view object for
+                // now.
+                if ($data['view'] == 'Image' &&
+                    !empty($data['gallery_view']) &&
+                    $data['gallery_view'] == 'GalleryLightbox') {
+
+                    $url .= '#' . $data['image'];
+                }
+
+                return Horde::applicationUrl($url, $full, $append_session);
+            } else {
+                $url = Horde::applicationUrl(
+                         Horde_Util::addParameter('view.php', $data),
+                         $full,
+                         $append_session);
+
+                if ($data['view'] == 'Image' &&
+                    !empty($data['gallery_view']) &&
+                    $data['gallery_view'] == 'GalleryLightbox') {
+
+                    $url .= '#' . $data['image'];
+                }
+
+                return $url;
+
+            }
+            break;
+
+        case 'group':
+            if ($rewrite) {
+                if (empty($data['groupby'])) {
+                    $data['groupby'] = $prefs->getValue('groupby');
+                }
+
+                if ($data['groupby'] == 'owner') {
+                    $url = 'user/';
+                }
+                if ($data['groupby'] == 'category') {
+                    $url = 'category/';
+                }
+                if ($data['groupby'] == 'none') {
+                    $url = 'all/';
+                }
+                unset($data['groupby']);
+                if (count($data)) {
+                    $url = Horde_Util::addParameter($url,$data);
+                }
+                return Horde::applicationUrl($url, $full, $append_session);
+            } else {
+                return Horde::applicationUrl(
+                    Horde_Util::addParameter('group.php', $data),
+                    $full,
+                    $append_session);
+            }
+            break;
+
+        case 'rss_user':
+            if ($rewrite) {
+                $url = 'user/' . urlencode($data['owner']) . '/rss';
+                return Horde::applicationUrl($url, $full, $append_session);
+            } else {
+                return Horde::applicationUrl(
+                    Horde_Util::addParameter('rss.php',
+                                       array('stream_type' => 'user',
+                                             'id' => $data['owner'])),
+                    $full, $append_session);
+            }
+            break;
+
+        case 'rss_gallery':
+            if ($rewrite) {
+                $id = (!empty($data['slug'])) ? $data['slug'] : 'id/' . (int)$data['gallery'];
+                $url = 'gallery/' . $id . '/rss';
+                return Horde::applicationUrl($url, $full, $append_session);
+            } else {
+                return Horde::applicationUrl(
+                    Horde_Util::addParameter('rss.php',
+                                       array('stream_type' => 'gallery',
+                                             'id' => (int)$data['gallery'])),
+                    $full, $append_session);
+            }
+            break;
+
+        case 'default_view':
+            switch ($prefs->getValue('defaultview')) {
+            case 'browse':
+                $url = 'browse.php';
+                return Horde::applicationUrl($url, $full, $append_session);
+                break;
+
+            case 'galleries':
+                $url = Ansel::getUrlFor('view', array('view' => 'List'), true);
+                break;
+
+            case 'mygalleries':
+            default:
+               $url = Ansel::getUrlFor('view',
+                                       array('view' => 'List',
+                                             'owner' => Horde_Auth::getAuth(),
+                                             'groupby' => 'owner'),
+                                       true);
+               break;
+            }
+            return $url;
+        }
+    }
+
+    /**
+     * Return a link to an image, suitable for use in an <img/> tag
+     * Takes into account $conf['vfs']['direct'] and other
+     * factors.
+     *
+     * @param string $imageId  The id of the image.
+     * @param string $view     The view ('screen', 'thumb', 'prettythumb' or
+     *                         'full') to show.
+     * @param boolean $full    Return a path that includes the server name?
+     * @param string $style    Use this gallery style
+     *
+     * @return string  The image path.
+     */
+    function getImageUrl($imageId, $view = 'screen', $full = false,
+                         $style = null)
+    {
+        global $conf, $ansel_storage;
+
+        // To avoid having to add a new img/* file everytime we add a new
+        // thumbstyle, we check for the 'non-prettythumb' views, then route the
+        // rest through prettythumb, passing it the style.
+        switch ($view) {
+        case 'screen':
+        case 'full':
+        case 'thumb':
+        case 'mini':
+            // Do nothing.
+            break;
+        default:
+            $view = 'prettythumb';
+        }
+
+        if (empty($imageId)) {
+            return Ansel::getErrorImage($view);
+        }
+
+        // Default to ansel_default since we really only need to know the style
+        // if we are requesting a 'prettythumb'
+        if (is_null($style)) {
+            $style = 'ansel_default';
+        }
+
+        // Don't load the image if the view exists
+        if ($conf['vfs']['src'] != 'php' &&
+            ($viewHash = Ansel_Image::viewExists($imageId, $view, $style)) === false) {
+            // We have to make sure the image exists first, since we won't
+            // be going through img/*.php to auto-create it.
+            if (is_a($image = $ansel_storage->getImage($imageId), 'PEAR_Error')) {
+                return Ansel::getErrorImage($view);
+            }
+            if (is_a($result = $image->createView($view, $style, false), 'PEAR_Error')) {
+                return Ansel::getErrorImage($view);
+            }
+            $viewHash = $image->_getViewHash($view, $style) . '/'
+                . $image->getVFSName($view);
+        }
+
+        // First check for vfs-direct. If we are not using it, pass this off to
+        // the img/*.php files, and check for sendfile support there.
+        if ($conf['vfs']['src'] != 'direct') {
+            $params = array('image' => $imageId);
+            if (!is_null($style)) {
+                $params['style'] = $style;
+            }
+            $url = Horde_Util::addParameter('img/' . $view . '.php', $params);
+            return Horde::applicationUrl($url, $full);
+        }
+
+        // Using vfs-direct
+        $path = substr(str_pad($imageId, 2, 0, STR_PAD_LEFT), -2) . '/'
+            . $viewHash;
+        if ($full && substr($conf['vfs']['path'], 0, 7) != 'http://') {
+            return Horde::url($conf['vfs']['path'] . $path, true, -1);
+        } else {
+            return $conf['vfs']['path'] . htmlspecialchars($path);
+        }
+    }
+
+    /**
+     * Obtain a Horde_Image object
+     *
+     * @param array $params  Any additional parameters
+     *
+     * @return Horde_Image object | PEAR_Error
+     */
+    function getImageObject($params = array())
+    {
+        global $conf;
+        require_once 'Horde/Image.php';
+        $context = array('tmpdir' => Horde::getTempDir());
+        if (!empty($conf['image']['convert'])) {
+            $context['convert'] = $conf['image']['convert'];
+        }
+        $params = array_merge(array('type' => $conf['image']['type'],
+                                    'context' => $context),
+                              $params);
+        $driver = empty($conf['image']['convert']) ? 'Gd' : 'Im';
+        return Horde_Image::factory($driver, $params);
+    }
+
+    /**
+     * Read an image from the filesystem.
+     *
+     * @param string $file     The filename of the image.
+     * @param array $override  Overwrite the file array with these values.
+     *
+     * @return array  The image data of the file as an array or PEAR_Error
+     */
+    function getImageFromFile($file, $override = array())
+    {
+        if (!file_exists($file)) {
+            return PEAR::raiseError(sprintf(_("The file \"%s\" doesn't exist."),
+                                    $file));
+        }
+
+        global $conf;
+
+        // Get the mime type of the file (and make sure it's an image).
+        require_once 'Horde/Mime/Magic.php';
+        $mime_type = Horde_Mime_Magic::analyzeFile($file, isset($conf['mime']['magic_db']) ? $conf['mime']['magic_db'] : null);
+        if (strpos($mime_type, 'image') === false) {
+            return PEAR::raiseError(sprintf(_("Can't get unknown file type \"%s\"."), $file));
+        }
+
+        $image = array('image_filename' => basename($file),
+                       'image_caption' => '',
+                       'image_type' => $mime_type,
+                       'data' => file_get_contents($file),
+                       );
+
+        // Override the array, for example if we're setting the filename to
+        // something else.
+        if (count($override)) {
+            $image = array_merge($image, $override);
+        }
+
+        return $image;
+    }
+
+    /**
+     * Check to see if a particular image manipulation function is
+     * available.
+     *
+     * @param string $feature  The name of the function.
+     *
+     * @return boolean  True if the function is available.
+     */
+    function isAvailable($feature)
+    {
+        static $capabilities;
+
+        // If the administrator locked auto watermark on, disable user
+        // intervention
+        if ($feature == 'text_watermark' &&
+            $GLOBALS['prefs']->getValue('watermark_auto') &&
+            $GLOBALS['prefs']->isLocked('watermark_auto')) {
+
+            return false;
+        }
+
+        if (!isset($capabilities)) {
+            $im = Ansel::getImageObject();
+            $capabilities = array_merge($im->getCapabilities(),
+                                        $im->getLoadedEffects());
+        }
+
+        return in_array($feature, $capabilities);
+    }
+
+    /**
+     * Build Ansel's list of menu items.
+     */
+    function getMenu($returnType = 'object')
+    {
+        global $conf, $registry;
+
+        $menu = new Horde_Menu();
+
+        /* Browse/Search */
+        $menu->add(Horde::applicationUrl('browse.php'), _("_Browse"),
+                   'browse.png', null, null, null,
+                   (($GLOBALS['prefs']->getValue('defaultview') == 'browse' &&
+                     basename($_SERVER['PHP_SELF']) == 'index.php') ||
+                    (basename($_SERVER['PHP_SELF']) == 'browse.php'))
+                   ? 'current'
+                   : '__noselection');
+
+        $menu->add(Ansel::getUrlFor('view', array('view' => 'List')), _("_Galleries"),
+                   'galleries.png', null, null, null,
+                   (($GLOBALS['prefs']->getValue('defaultview') == 'galleries' &&
+                     basename($_SERVER['PHP_SELF']) == 'index.php') ||
+                    ((basename($_SERVER['PHP_SELF']) == 'group.php') &&
+                     Horde_Util::getFormData('owner') !== Horde_Auth::getAuth())
+                    ? 'current'
+                    : '__noselection'));
+        if (Horde_Auth::getAuth()) {
+            $url = Ansel::getUrlFor('view', array('owner' => Horde_Auth::getAuth(),
+                                                  'groupby' => 'owner',
+                                                  'view' => 'List'));
+            $menu->add($url, _("_My Galleries"), 'mygalleries.png', null, null,
+                       null,
+                       (Horde_Util::getFormData('owner', false) == Horde_Auth::getAuth())
+                       ? 'current' :
+                       '__noselection');
+        }
+
+        /* Let authenticated users create new galleries. */
+        if (Horde_Auth::isAdmin() ||
+            (!$GLOBALS['perms']->exists('ansel') && Horde_Auth::getAuth()) ||
+            $GLOBALS['perms']->hasPermission('ansel', Horde_Auth::getAuth(), PERMS_EDIT)) {
+            $menu->add(Horde::applicationUrl(Horde_Util::addParameter('gallery.php', 'actionID', 'add')),
+                       _("_New Gallery"), 'add.png', null, null, null,
+                       (basename($_SERVER['PHP_SELF']) == 'gallery.php' &&
+                        Horde_Util::getFormData('actionID') == 'add')
+                       ? 'current'
+                       : '__noselection');
+        }
+
+        if ($conf['faces']['driver'] && Horde_Auth::isAuthenticated()) {
+            $menu->add(Horde::applicationUrl('faces/search/all.php'), _("_Faces"), 'user.png', $registry->getImageDir('horde'));
+        }
+
+        /* Print. */
+        if ($conf['menu']['print'] && ($pl = Horde_Util::nonInputVar('print_link'))) {
+            $menu->add($pl, _("_Print"), 'print.png',
+                       $registry->getImageDir('horde'), '_blank',
+                       'popup(this.href); return false;');
+        }
+
+        if ($returnType == 'object') {
+            return $menu;
+        } else {
+            return $menu->render();
+        }
+    }
+
+    /**
+     * Generate a list of breadcrumbs showing where we are in the gallery
+     * tree.
+     */
+    function getBreadCrumbs($separator = ' &raquo; ', $gallery = null)
+    {
+        global $prefs, $ansel_storage;
+
+        $groupby = Horde_Util::getFormData('groupby', $prefs->getValue('groupby'));
+        $owner = Horde_Util::getFormData('owner');
+        $image_id = (int)Horde_Util::getFormData('image');
+        $actionID = Horde_Util::getFormData('actionID');
+        $page = Horde_Util::getFormData('page', 0);
+        $haveSearch = Horde_Util::getFormData('havesearch', 0);
+
+        if (is_null($gallery)) {
+            $gallery_id = (int)Horde_Util::getFormData('gallery');
+            $gallery_slug = Horde_Util::getFormData('slug');
+            if (!empty($gallery_slug)) {
+                $gallery = $ansel_storage->getGalleryBySlug($gallery_slug);
+            } elseif (!empty($gallery_id)) {
+                $gallery = $ansel_storage->getGallery($gallery_id);
+            }
+        }
+
+        if (is_a($gallery, 'PEAR_Error')) {
+            $gallery = null;
+        }
+
+        if ($gallery) {
+            $owner = $gallery->get('owner');
+        }
+
+        if (!empty($image_id)) {
+            $image = &$ansel_storage->getImage($image_id);
+            if (empty($gallery) && !is_a($image, 'PEAR_Error')) {
+                $gallery = $ansel_storage->getGallery($image->gallery);
+            }
+        }
+        if (isset($gallery) && !is_a($gallery, 'PEAR_Error')) {
+            $owner = $gallery->get('owner');
+        }
+        if (!empty($owner)) {
+            if ($owner == Horde_Auth::getAuth()) {
+                $owner_title = _("My Galleries");
+            } elseif (!empty($GLOBALS['conf']['gallery']['customlabel'])) {
+                $uprefs = &Prefs::singleton($GLOBALS['conf']['prefs']['driver'],
+                                            'ansel',
+                                            $owner, '', null, false);
+                $fullname = $uprefs->getValue('grouptitle');
+                if (!$fullname) {
+                    require_once 'Horde/Identity.php';
+                    $identity = &Identity::singleton('none', $owner);
+                    $fullname = $identity->getValue('fullname');
+                    if (!$fullname) {
+                        $fullname = $owner;
+                    }
+                    $owner_title = sprintf(_("%s's Galleries"), $fullname);
+                } else {
+                    $owner_title = $fullname;
+                }
+            } else {
+                $owner_title = sprintf(_("%s's Galleries"), $owner);
+            }
+        }
+
+        // Construct the breadcrumbs backward, from where we are now up through
+        // the path back to the top.  By constructing it backward we can treat
+        // the last element (the current page) specially.
+        $levels = 0;
+        $nav = '</span>';
+        $urlFlags = array('havesearch' => $haveSearch,
+                          'force_grouping' => true);
+
+        // Check for an active image
+        if (!empty($image_id) && !is_a($image, 'PEAR_Error')) {
+            $text = '<span class="thiscrumb" id="PhotoName">' . htmlspecialchars($image->filename, ENT_COMPAT, Horde_Nls::getCharset()) . '</span>';
+            $nav = $separator . $text . $nav;
+            $levels++;
+        }
+
+        if ($gallery) {
+            $trails = $gallery->getGalleryCrumbData();
+            foreach ($trails as $trail) {
+                $title = $trail['title'];
+                $navdata = $trail['navdata'];
+                if ($levels++ > 0) {
+                    if ((empty($image_id) && $levels == 1) ||
+                        (!empty($image_id) && $levels == 2)) {
+                        $urlParameters = array_merge($urlFlags, array('page' => $page));
+                    } else {
+                        $urlParameters = $urlFlags;
+                    }
+                    $nav = $separator . Horde::link(Ansel::getUrlFor('view', array_merge($navdata, $urlParameters))) . $title . '</a>' . $nav;
+                } else {
+                    $nav = $separator . '<span class="thiscrumb">' . $title . '</span>' . $nav;
+                }
+            }
+        }
+
+        if (!empty($owner_title)) {
+            $owner_title = htmlspecialchars($owner_title, ENT_COMPAT, Horde_Nls::getCharset());
+            $levels++;
+            if ($gallery) {
+                $nav = $separator . Horde::link(Ansel::getUrlFor('view', array('view' => 'List', 'groupby' => 'owner', 'owner' => $owner, 'havesearch' => $haveSearch))) . $owner_title . '</a>' . $nav;
+            } else {
+                $nav = $separator . $owner_title . $nav;
+            }
+        }
+
+        if ($haveSearch == 0) {
+            $text = _("Galleries");
+            $link = Horde::link(Ansel::getUrlFor('view', array('view' => 'List')));
+        } else {
+            $text = _("Browse Tags");
+            $link = Horde::link(Ansel::getUrlFor('view', array('view' => 'Results'), true));
+        }
+        if ($levels > 0) {
+            $nav = $link . $text . '</a>' . $nav;
+        } else {
+            $nav = $text . $nav;
+        }
+
+        $nav = '<span class="breadcrumbs">' . $nav;
+
+        return $nav;
+    }
+
+    /**
+     * Build a HTML <select> element containing all the available
+     * gallery styles.
+     *
+     * @param string $element_name  The element's id/name attribute.
+     * @param string $selected      Mark this element as currently selected.
+     *
+     * @return string  The HTML for the <select> element.
+     */
+    function getStyleSelect($element_name, $selected = '')
+    {
+        $styles = Horde::loadConfiguration('styles.php', 'styles', 'ansel');
+
+        /* No prettythumbs allowed at all by admin choice */
+        if (empty($GLOBALS['conf']['image']['prettythumbs'])) {
+            $test = $styles;
+            foreach ($test as $key => $style) {
+                if ($style['thumbstyle'] != 'thumb') {
+                    unset($styles[$key]);
+                }
+            }
+        }
+
+        /* Build the available styles, but don't show hidden styles */
+        foreach ($styles as $key => $style) {
+            if (empty($style['hide'])) {
+                $options[$key] = $style['title'];
+            }
+        }
+
+        /* Nothing explicitly selected, use the global pref */
+        if ($selected == '') {
+            $selected = $GLOBALS['prefs']->getValue('default_gallerystyle');
+        }
+
+        $html = '<select id="' . $element_name . '" name="' . $element_name . '">';
+        foreach ($options as $key => $option) {
+            $html .= '  <option value="' . $key . '"' . (($selected == $key) ? 'selected="selected"' : '') . '>' . $option . '</option>';
+        }
+        $html .= '</select>';
+        return $html;
+    }
+
+    /**
+     * Get an array of all currently viewable styles.
+     */
+    function getAvailableStyles()
+    {
+        /* Brings in the $styles array in this scope only */
+        $styles = Horde::loadConfiguration('styles.php', 'styles', 'ansel');
+
+        /* No prettythumbs allowed at all by admin choice */
+        if (empty($GLOBALS['conf']['image']['prettythumbs'])) {
+            $test = $styles;
+            foreach ($test as $key => $style) {
+                if ($style['thumbstyle'] != 'thumb') {
+                    unset($styles[$key]);
+                }
+            }
+        }
+
+        /* Check if the browser / server has png support */
+        if ($GLOBALS['browser']->hasQuirk('png_transparency') ||
+            $GLOBALS['conf']['image']['type'] != 'png') {
+
+            $test = $styles;
+            foreach ($test as $key => $style) {
+                if (!empty($style['requires_png'])) {
+                    if (!empty($style['fallback'])) {
+                        $styles[$key] = $styles[$style['fallback']];
+                    } else {
+                        unset($styles[$key]);
+                    }
+                }
+            }
+        }
+        return $styles;
+    }
+
+    /**
+     * Get a style definition for the requested named style
+     *
+     * @param string $style  The name of the style to fetch
+     *
+     * @return array  The definition of the requested style if it's available
+     *                otherwise, the ansel_default style is returned.
+     */
+    function getStyleDefinition($style)
+    {
+        if (isset($GLOBALS['ansel_styles'][$style])) {
+            $style_def = $GLOBALS['ansel_styles'][$style];
+        } else {
+            $style_def = $GLOBALS['ansel_styles']['ansel_default'];
+        }
+
+        /* Fill in defaults */
+        if (empty($style_def['gallery_view'])) {
+            $style_def['gallery_view'] = 'Gallery';
+        }
+        if (empty($style_def['default_galleryimage_type'])) {
+            $style_def['default_galleryimage_type'] = 'plain';
+        }
+        if (empty($style_def['requires_png'])) {
+            $style_def['requires_png'] = false;
+        }
+
+        return $style_def;
+    }
+
+    /**
+     * Add a custom stylesheet to the current page. Need our own implementation
+     * since we want to be able to ouput specific CSS files at specific times
+     * (like when rendering embedded content, or calling via the api etc...).
+     *
+     * @param string $stylesheet  The stylesheet to add. A path relative
+     *                            to $themesfs
+     * @param boolean $link       Immediately output the CSS link
+     */
+    function attachStylesheet($stylesheet, $link = false)
+    {
+       $GLOBALS['ansel_stylesheets'][] = $stylesheet;
+       if ($link) {
+           Ansel::stylesheetLinks(true);
+       }
+    }
+
+    /**
+     * Output the stylesheet links
+     *
+     * @param boolean $custom_only  Don't include ansel's base CSS file
+     */
+    function stylesheetLinks($custom_only = false)
+    {
+        /* Custom CSS */
+        $themesuri = $GLOBALS['registry']->get('themesuri', 'ansel');
+        $themesfs = $GLOBALS['registry']->get('themesfs', 'ansel');
+        $css = array();
+        if (!empty($GLOBALS['ansel_stylesheets'])) {
+            foreach ($GLOBALS['ansel_stylesheets'] as $css_file) {
+                $css[] = array('u' => Horde::applicationUrl($themesuri . '/' . $css_file, true),
+                               'f' => $themesfs . '/' . $css_file);
+            }
+        }
+
+        /* Use Horde's stylesheet code if we aren't ouputting css directly */
+        if (!$custom_only) {
+            Horde::includeStylesheetFiles(array('additional' => $css));
+        } else {
+            foreach ($css as $file) {
+                echo '<link href="' . $file['u']
+                     . '" rel="stylesheet" type="text/css"'
+                     . (isset($file['m']) ? ' media="' . $file['m'] . '"' : '')
+                     . ' />' . "\n";
+            }
+        }
+    }
+
+    /**
+     * Get a date parts array containing only enough date parts for the depth
+     * we are at. If an empty array is passed, attempt to get the parts from
+     * url parametrs. Any missing date parts must be set to 0.
+     *
+     * @param array $date  A full date parts array or an empty array.
+     *
+     * @return A trimmed down (if necessary) date parts array.
+     */
+    function getDateParameter($date = array())
+    {
+        if (!count($date)) {
+            $date = array(
+                'year' => Horde_Util::getFormData('year', 0),
+                'month' => Horde_Util::getFormData('month', 0),
+                'day' => Horde_Util::getFormData('day', 0));
+        }
+        $return = array();
+        $return['year'] = !empty($date['year']) ? $date['year'] : 0;
+        $return['month'] = !empty($date['month']) ? $date['month'] : 0;
+        $return['day'] = !empty($date['day']) ? $date['day'] : 0;
+
+        return $return;
+    }
+
+    /**
+     * Downloads all requested images as a zip file.  Assumes all permissions
+     * have been checked on the requested resource.
+     * @param unknown_type $images
+     */
+    function downloadImagesAsZip($gallery = null, $images = array())
+    {
+
+        if (empty($GLOBALS['conf']['gallery']['downloadzip'])) {
+            $GLOBALS['notification']->push(_("Downloading zip files is not enabled. Talk to your server administrator."));
+            header('Location: ' . Horde::applicationUrl('view.php?view=List', true));
+            exit;
+        }
+
+        /* Requested a gallery */
+        if (!is_null($gallery)) {
+            /* We can name the zip file with the slug if we have it */
+            $slug = $gallery->get('slug');
+
+            /* Set the date in case we are viewing in date mode */
+            $gallery->setDate(Ansel::getDateParameter());
+
+            /*
+             * More efficeint to get the images and then see how many instead of calling
+             * countImages() and then getting the images.
+             */
+            $images = $gallery->listImages();
+        }
+
+        /* At this point, we should always have a list of images */
+        if (!count($images)) {
+            $notification->push(sprintf(_("There are no photos in %s to download."),
+                                $gallery->get('name')), 'horde.message');
+            header('Location: ' . Horde::applicationUrl('view.php?view=List', true));
+            exit;
+        }
+
+        // Try to close off the current session to avoid locking it while the
+        // gallery is downloading.
+        @session_write_close();
+
+        if (!is_null($gallery)) {
+            // Check full photo permissions
+            if ($gallery->canDownload()) {
+                $view = 'full';
+            } else {
+                $view = 'screen';
+            }
+        }
+
+        $zipfiles = array();
+        foreach ($images as $id) {
+            $image = &$GLOBALS['ansel_storage']->getImage($id);
+            if (!is_a($image, 'PEAR_Error')) {
+                // If we didn't select an entire gallery, check the download
+                // size for each image.
+                if (!isset($view)) {
+                    $g = $GLOBALS['ansel_storage']->getGallery($image->gallery);
+                    $v = $g->canDownload() ? 'full' : 'screen';
+                } else {
+                    $v = $view;
+                }
+
+                $zipfiles[] = array('data' => $image->raw($v),
+                                    'name' => $image->filename);
+            }
+        }
+
+        $zip = Horde_Compress::factory('zip');
+        $body = $zip->compress($zipfiles);
+        if (!empty($gallery)) {
+            $filename = (!empty($slug) ? $slug : $gallery->id) . '.zip';
+        } else {
+            $filename = 'Ansel.zip';
+        }
+        $GLOBALS['browser']->downloadHeaders($filename, 'application/zip', false,
+                                  strlen($body));
+        echo $body;
+        exit;
+    }
+
+    function embedCode($options)
+    {
+        if (empty($options['container'])) {
+            $domid = md5(uniqid());
+            $options['container'] = $domid;
+        } else {
+            $domid = $options['container'];
+        }
+
+        $imple = Horde_Ajax_Imple::factory(array('ansel', 'Embed'), $options);
+        $src = $imple->getUrl();
+
+       return '<script type="text/javascript" src="' . $src . '"></script><div id="' . $domid . '"></div>';
+    }
+
+}
+
+/**
+ * Class to encapsulate a single gallery. Implemented as an extension of
+ * the Horde_Share_Object class.
+ *
+ * @author  Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_Gallery extends Horde_Share_Object_sql_hierarchical {
+
+    /**
+     * Cache the Gallery Id - to match the Ansel_Image interface
+     */
+    var $id;
+
+    /**
+     * The gallery mode helper
+     *
+     * @var Ansel_Gallery_Mode object
+     */
+    var $_modeHelper;
+
+    /**
+     *
+     */
+    function __sleep()
+    {
+        $properties = get_object_vars($this);
+        unset($properties['_shareOb']);
+        unset($properties['_modeHelper']);
+        $properties = array_keys($properties);
+        return $properties;
+    }
+
+    function __wakeup()
+    {
+        $this->setShareOb($GLOBALS['ansel_storage']->shares);
+        $mode = $this->get('view_mode');
+        $this->_setModeHelper($mode);
+    }
+
+    /**
+     * The Ansel_Gallery constructor.
+     *
+     * @param string $name  The name of the gallery
+     */
+    function Ansel_Gallery($attributes = array())
+    {
+        /* Existing gallery? */
+        if (!empty($attributes['share_id'])) {
+            $this->id = (int)$attributes['share_id'];
+        }
+
+        /* Pass on up the chain */
+        parent::Horde_Share_Object_sql_hierarchical($attributes);
+        $this->setShareOb($GLOBALS['ansel_storage']->shares);
+        $mode = isset($attributes['attribute_view_mode']) ? $attributes['attribute_view_mode'] : 'Normal';
+        $this->_setModeHelper($mode);
+    }
+
+    /**
+     * Check for special capabilities of this gallery.
+     *
+     */
+    function hasFeature($feature)
+    {
+
+        // First check for purely Ansel_Gallery features
+        // Currently we have none of these.
+
+        // Delegate to the modeHelper
+        return $this->_modeHelper->hasFeature($feature);
+
+    }
+
+    /**
+     * Simple factory to retrieve the proper mode object.
+     *
+     * @param string $type  The mode to use
+     *
+     * @return Ansel_Gallery_Mode object
+     */
+    function _setModeHelper($type = 'Normal')
+    {
+        $type = basename($type);
+        $class = 'Ansel_GalleryMode_' . $type;
+        require_once dirname(__FILE__) . '/GalleryMode/' . $type . '.php';
+        $this->_modeHelper = new $class($this);
+        $this->_modeHelper->init();
+    }
+
+    /**
+     * Checks if the user can download the full photo
+     *
+     * @return boolean  Whether or not user can download full photos
+     */
+    function canDownload()
+    {
+        if (Horde_Auth::getAuth() == $this->data['share_owner'] || Horde_Auth::isAdmin('ansel:admin')) {
+            return true;
+        }
+
+        switch ($this->data['attribute_download']) {
+        case 'all':
+            return true;
+
+        case 'authenticated':
+            return Horde_Auth::isAuthenticated();
+
+        case 'edit':
+            return $this->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT);
+
+        case 'hook':
+            return Horde::callHook('_ansel_hook_can_download', array($this->id));
+
+        default:
+            return false;
+        }
+    }
+
+    /**
+     * Saves any changes to this object to the backend permanently.
+     *
+     * @return mixed true || PEAR_Error on failure.
+     */
+    function _save()
+    {
+        // Check for invalid characters in the slug.
+        if (!empty($this->data['attribute_slug']) &&
+            preg_match('/[^a-zA-Z0-9_@]/', $this->data['attribute_slug'])) {
+
+            return PEAR::raiseError(
+                sprintf(_("Could not save gallery, the slug, \"%s\", contains invalid characters."),
+                        $this->data['attribute_slug']));
+        }
+
+        // Check for slug uniqueness
+        $slugGalleryId = $GLOBALS['ansel_storage']->slugExists($this->data['attribute_slug']);
+        if ($slugGalleryId > 0 && $slugGalleryId <> $this->id) {
+            return PEAR::raiseError(sprintf(_("Could not save gallery, the slug, \"%s\", already exists."),
+                                            $this->data['attribute_slug']));
+        }
+
+        if ($GLOBALS['conf']['ansel_cache']['usecache']) {
+            $GLOBALS['cache']->expire('Ansel_Gallery' . $this->id);
+        }
+        return parent::_save();
+    }
+
+    /**
+     * Update the gallery image count.
+     *
+     * @param integer $images      Number of images in action
+     * @param boolean $add         Action to take (add or remove)
+     * @param integer $gallery_id  Gallery id to update images for
+     */
+    function _updateImageCount($images, $add = true, $gallery_id = null)
+    {
+        // We do the query directly here to avoid having to instantiate a
+        // gallery object just to increment/decrement one value in the table.
+        $sql = 'UPDATE ' . $this->_shareOb->_table
+            . ' SET attribute_images = attribute_images '
+            . ($add ? ' + ' : ' - ') . $images . ' WHERE share_id = '
+            . ($gallery_id ? $gallery_id : $this->id);
+
+        // Make sure to update the local value as well, so it doesn't get
+        // overwritten by any other updates from ->set() calls.
+        if (is_null($gallery_id) || $gallery_id === $this->id) {
+            if ($add) {
+                $this->data['attribute_images'] += $images;
+            } else {
+                $this->data['attribute_images'] -= $images;
+            }
+        }
+
+        /* Need to expire the cache for the gallery that was changed */
+        if ($GLOBALS['conf']['ansel_cache']['usecache']) {
+            $id = (is_null($gallery_id) ? $this->id : $gallery_id);
+            $GLOBALS['cache']->expire('Ansel_Gallery' . $id);
+        }
+
+        return $this->_shareOb->_write_db->exec($sql);
+
+    }
+
+    /**
+     * Add an image to this gallery.
+     *
+     * @param array $image_data  The image to add. Required keys include
+     *                           'image_caption', and 'data'. Optional keys
+     *                           include 'image_filename' and 'image_type'
+     *
+     * @param boolean $default   Make this image the new default tile image.
+     *
+     * @return integer  The id of the new image.
+     */
+    function addImage($image_data, $default = false)
+    {
+        global $conf;
+
+        /* Normal is the only view mode that can accurately update gallery counts */
+        $vMode = $this->get('view_mode');
+        if ($vMode != 'Normal') {
+            $this->_setModeHelper('Normal');
+        }
+
+        $resetStack = false;
+        if (!isset($image_data['image_filename'])) {
+            $image_data['image_filename'] = 'Untitled';
+        }
+        $image_data['gallery_id'] = $this->id;
+        $image_data['image_sort'] = $this->countImages();
+
+        /* Create the image object */
+        $image = new Ansel_Image($image_data);
+        $result = $image->save();
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        }
+
+        if (empty($image_data['image_id'])) {
+            $this->_updateImageCount(1);
+            if ($this->countImages() < 5) {
+                $resetStack = true;
+            }
+        }
+
+        /* Should this be the default image? */
+        if (!$default && $this->data['attribute_default_type'] == 'auto') {
+            $this->data['attribute_default'] = $image->id;
+            $resetStack = true;
+        } elseif ($default) {
+            $this->data['attribute_default'] = $image->id;
+            $this->data['default_type'] = 'manual';
+        }
+
+        /* Reset the gallery default image stacks if needed. */
+        if ($resetStack) {
+            $this->clearStacks();
+        }
+
+        /* Update the modified flag and save gallery changes */
+        $this->data['attribute_last_modified'] = time();
+
+        /* Save all changes to the gallery */
+        $this->save();
+
+        /* Return to the proper view mode */
+        if ($vMode != 'Normal') {
+            $this->_setModeHelper($vMode);
+        }
+
+        /* Return the ID of the new image. */
+        return $image->id;
+    }
+
+    /**
+     * Clear all of this gallery's default image stacks from the VFS and the
+     * gallery's data store.
+     *
+     */
+    function clearStacks()
+    {
+        $ids = @unserialize($this->data['attribute_default_prettythumb']);
+        if (is_array($ids)) {
+            foreach ($ids as $imageId) {
+                $this->removeImage($imageId, true);
+            }
+        }
+
+        // Using the set function here so we can efficently update the db
+        $this->set('default_prettythumb', '', true);
+    }
+
+    /**
+     * Removes all generated and cached 'prettythumb' thumbnails for this
+     * gallery
+     *
+     */
+    function clearThumbs()
+    {
+        $images = $this->listImages();
+        foreach ($images as $id) {
+            $image = $this->getImage($id);
+            $image->deleteCache('prettythumb');
+        }
+    }
+
+    /**
+     * Removes all generated and cached views for this gallery
+     *
+     */
+    function clearViews()
+    {
+        $images = $this->listImages();
+        foreach ($images as $id) {
+            $image = $this->getImage($id);
+            $image->deleteCache('all');
+        }
+    }
+
+    /**
+     * Move images from this gallery to a new gallery.
+     *
+     * @param array $images          An array of image ids.
+     * @param Ansel_Gallery $gallery The gallery to move the images to.
+     *
+     * @return integer | PEAR_Error The number of images moved, or an error message.
+     */
+    function moveImagesTo($images, $gallery)
+    {
+        return $this->_modeHelper->moveImagesTo($images, $gallery);
+    }
+
+    /**
+     * Copy image and related data to specified gallery.
+     *
+     * @param array $images           An array of image ids.
+     * @param Ansel_Gallery $gallery  The gallery to copy images to.
+     *
+     * @return integer | PEAR_Error The number of images copied or error message
+     */
+    function copyImagesTo($images, $gallery)
+    {
+        if (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+            return PEAR::raiseError(
+                sprintf(_("Access denied copying photos to \"%s\"."),
+                          $gallery->get('name')));
+        }
+
+        $db = $this->_shareOb->_write_db;
+        $imgCnt = 0;
+        foreach ($images as $imageId) {
+            $img = &$this->getImage($imageId);
+            // Note that we don't pass the tags when adding the image..see below
+            $newId = $gallery->addImage(array(
+                               'image_caption' => $img->caption,
+                               'data' => $img->raw(),
+                               'image_filename' => $img->filename,
+                               'image_type' => $img->getType(),
+                               'image_uploaded_date' => $img->uploaded));
+            if (is_a($newId, 'PEAR_Error')) {
+                return $newId;
+            }
+            /* Copy any tags */
+            // Since we know that the tags already exist, no need to
+            // go through Ansel_Tags::writeTags() - this saves us a SELECT query
+            // for each tag - just write the data into the DB ourselves.
+            $tags = $img->getTags();
+            $query = $this->_shareOb->_write_db->prepare('INSERT INTO ansel_images_tags (image_id, tag_id) VALUES(' . $newId . ',?);');
+            if (is_a($query, 'PEAR_Error')) {
+                return $query;
+            }
+            foreach ($tags as $tag_id => $tag_name) {
+                $result = $query->execute($tag_id);
+                if (is_a($result, 'PEAR_Error')) {
+                    return $result;
+                }
+            }
+            $query->free();
+
+            /* exif data */
+            // First check to see if the exif data was present in the raw data.
+            $count = $db->queryOne('SELECT COUNT(image_id) FROM ansel_image_attributes WHERE image_id = ' . (int) $newId . ';');
+            if ($count == 0) {
+                $exif = $db->queryAll('SELECT attr_name, attr_value FROM ansel_image_attributes WHERE image_id = ' . (int) $imageId . ';',null, MDB2_FETCHMODE_ASSOC);
+                if (is_array($exif) && count($exif) > 0) {
+                    $insert = $db->prepare('INSERT INTO ansel_image_attributes (image_id, attr_name, attr_value) VALUES (?, ?, ?)');
+                    if (is_a($insert, 'PEAR_Error')) {
+                        return $insert;
+                    }
+                    foreach ($exif as $attr){
+                        $result = $insert->execute(array($newId, $attr['attr_name'], $attr['attr_value']));
+                        if (is_a($result, 'PEAR_Error')) {
+                            return $result;
+                        }
+                    }
+                    $insert->free();
+                }
+            }
+            ++$imgCnt;
+        }
+
+        return $imgCnt;
+    }
+
+    /**
+     * Set the order of an image in this gallery.
+     *
+     * @param integer $imageId The image to sort.
+     * @param integer $pos     The sort position of the image.
+     */
+    function setImageOrder($imageId, $pos)
+    {
+        return $this->_shareOb->_write_db->exec('UPDATE ansel_images SET image_sort = ' . (int)$pos . ' WHERE image_id = ' . (int)$imageId);
+    }
+
+    /**
+     * Remove the given image from this gallery.
+     *
+     * @param mixed   $image   Image to delete. Can be an Ansel_Image
+     *                         or an image ID.
+     *
+     * @return boolean  True on success, false on failure.
+     */
+    function removeImage($image, $isStack = false)
+    {
+        return $this->_modeHelper->removeImage($image, $isStack);
+    }
+
+    /**
+     * Returns this share's owner's Identity object.
+     *
+     * @return Identity object for the owner of this gallery.
+     */
+    function getOwner()
+    {
+        require_once 'Horde/Identity.php';
+        $identity = &Identity::singleton('none', $this->data['share_owner']);
+        return $identity;
+    }
+
+    /**
+     * Output the HTML for this gallery's tile.
+     *
+     * @param Ansel_Gallery $parent  The parent Ansel_Gallery object
+     * @param string $style          A named gallery style to use.
+     * @param boolean $mini          Force the use of a mini thumbnail?
+     * @param array $params          Any additional parameters the Ansel_Tile
+     *                               object may need.
+     */
+    function getTile($parent = null, $style = null, $mini = false,
+                     $params = array())
+    {
+        require_once ANSEL_BASE . '/lib/Tile/Gallery.php';
+
+        if (!is_null($parent) && is_null($style)) {
+            $style = $parent->getStyle();
+        } else {
+            $style = Ansel::getStyleDefinition($style);
+        }
+
+        if (!empty($view_url)) {
+            $view_url = str_replace('%g', $this->id, $view_url);
+        }
+
+        return Ansel_Tile_Gallery::getTile($this, $style, $mini, $params);
+    }
+
+    /**
+     * Get the children of this gallery.
+     *
+     * @param integer $perm    The permissions to limit to.
+     * @param integer $from    The child to start at.
+     * @param integer $to      The child to end with.
+     * @param boolean $noauto  Prevent auto
+     *
+     * @return A mixed array of Ansel_Gallery and Ansel_Image objects that are
+     *         children of this gallery.
+     */
+    function getGalleryChildren($perm = PERMS_SHOW, $from = 0, $to = 0, $noauto = true)
+    {
+        return $this->_modeHelper->getGalleryChildren($perm, $from, $to, $noauto);
+    }
+
+
+    /**
+     * Return the count of this gallery's children
+     *
+     * @param integer $perm            The permissions to require.
+     * @param boolean $galleries_only  Only include galleries, no images.
+     *
+     * @return integer The count of this gallery's children.
+     */
+    function countGalleryChildren($perm = PERMS_SHOW, $galleries_only = false, $noauto = true)
+    {
+        return $this->_modeHelper->countGalleryChildren($perm, $galleries_only, $noauto);
+    }
+
+    /**
+     * Lists a slice of the image ids in this gallery.
+     *
+     * @param integer $from  The image to start listing.
+     * @param integer $count The numer of images to list.
+     *
+     * @return mixed  An array of image_ids | PEAR_Error
+     */
+    function listImages($from = 0, $count = 0)
+    {
+        return $this->_modeHelper->listImages($from, $count);
+    }
+
+    /**
+     * Gets a slice of the images in this gallery.
+     *
+     * @param integer $from  The image to start fetching.
+     * @param integer $count The numer of images to return.
+     *
+     * @param mixed An array of Ansel_Image objects | PEAR_Error
+     */
+    function getImages($from = 0, $count = 0)
+    {
+        return $this->_modeHelper->getImages($from, $count);
+    }
+
+    /**
+     * Return the most recently added images in this gallery.
+     *
+     * @param integer $limit  The maximum number of images to return.
+     *
+     * @return mixed  An array of Ansel_Image objects | PEAR_Error
+     */
+    function getRecentImages($limit = 10)
+    {
+        return $GLOBALS['ansel_storage']->getRecentImages(array($this->id),
+                                                          $limit);
+    }
+
+    /**
+     * Returns the image in this gallery corresponding to the given id.
+     *
+     * @param integer $id  The ID of the image to retrieve.
+     *
+     * @return Ansel_Image  The image object corresponding to the given id.
+     */
+    function &getImage($id)
+    {
+        return $GLOBALS['ansel_storage']->getImage($id);
+    }
+
+    /**
+     * Checks if the gallery has any subgallery
+     */
+    function hasSubGalleries()
+    {
+        return $this->_modeHelper->hasSubGalleries();
+    }
+
+    /**
+     * Returns the number of images in this gallery and, optionally, all
+     * sub-galleries.
+     *
+     * @param boolean $subgalleries  Determines whether subgalleries should
+     *                               be counted or not.
+     *
+     * @return integer number of images in this gallery
+     */
+    function countImages($subgalleries = false)
+    {
+        return $this->_modeHelper->countImages($subgalleries);
+    }
+
+    /**
+     * Returns the default image for this gallery.
+     *
+     * @param string $style  Force the use of this style, if it's available
+     *                       otherwise use whatever style is choosen for this
+     *                       gallery. If prettythumbs are not available then
+     *                       we always use ansel_default style.
+     *
+     * @return mixed  The image_id of the default image or false.
+     */
+    function getDefaultImage($style = null)
+    {
+       // Check for explicitly requested style
+        if (!is_null($style)) {
+            $gal_style = Ansel::getStyleDefinition($style);
+        } else {
+            // Use gallery's default.
+            $gal_style = $this->getStyle();
+            if (!isset($GLOBALS['ansel_styles'][$gal_style['name']])) {
+                $gal_style = $GLOBALS['ansel_styles']['ansel_default'];
+            }
+        }
+        Horde::logMessage(sprintf("using gallery style: %s in Ansel::getDefaultImage()", $gal_style['name']), __FILE__, __LINE__, PEAR_LOG_DEBUG);
+        if (!empty($gal_style['default_galleryimage_type']) &&
+            $gal_style['default_galleryimage_type'] != 'plain') {
+
+            $thumbstyle = $gal_style['default_galleryimage_type'];
+            $styleHash = $this->_getViewHash($thumbstyle, $style);
+
+            // First check for the existence of a default image in the style
+            // we are looking for.
+            if (!empty($this->data['attribute_default_prettythumb'])) {
+                $thumbs = @unserialize($this->data['attribute_default_prettythumb']);
+            }
+            if (!isset($thumbs) || !is_array($thumbs)) {
+                $thumbs = array();
+            }
+
+            if (!empty($thumbs[$styleHash])) {
+                return $thumbs[$styleHash];
+            }
+
+            // Don't already have one, must generate it.
+            require_once dirname(__FILE__) . '/ImageView.php';
+            $params = array('gallery' => $this, 'style' => $gal_style);
+            $iview = Ansel_ImageView::factory(
+                $gal_style['default_galleryimage_type'], $params);
+
+            if (!is_a($iview, 'PEAR_Error')) {
+                $img = $iview->create();
+                if (!is_a($img, 'PEAR_Error')) {
+                     // Note the gallery_id is negative for generated stacks
+                     $iparams = array('image_filename' => $this->get('name'),
+                                      'image_caption' => $this->get('name'),
+                                      'data' => $img->raw(),
+                                      'image_sort' => 0,
+                                      'gallery_id' => -$this->id);
+                     $newImg = new Ansel_Image($iparams);
+                     $newImg->save();
+                     $prettyData = serialize(
+                         array_merge($thumbs,
+                                     array($styleHash => $newImg->id)));
+
+                     $this->set('default_prettythumb', $prettyData, true);
+                     return $newImg->id;
+                } else {
+                    Horde::logMessage($img, __FILE__, __LINE__, PEAR_LOG_ERR);
+                }
+            } else {
+                // Might not support the requested style...try ansel_default
+                // but protect against infinite recursion.
+                Horde::logMessage($iview, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+                if ($style != 'ansel_default') {
+                    return $this->getDefaultImage('ansel_default');
+                }
+                Horde::logMessage($iview, __FILE__, __LINE__, PEAR_LOG_ERR);
+            }
+        } else {
+            // We are just using an image thumbnail for the gallery default.
+            if ($this->countImages()) {
+                if (!empty($this->data['attribute_default']) &&
+                    $this->data['attribute_default'] > 0) {
+
+                    return $this->data['attribute_default'];
+                }
+                $keys = $this->listImages();
+                if (is_a($keys, 'PEAR_Error')) {
+                    return $keys;
+                }
+                $this->data['attribute_default'] = $keys[count($keys) - 1];
+                $this->data['attribute_default_type'] = 'auto';
+                $this->save();
+                return $keys[count($keys) - 1];
+            }
+
+            if ($this->hasSubGalleries()) {
+                // Fall through to a default image of a sub gallery.
+                $galleries = $GLOBALS['ansel_storage']->listGalleries(
+                    PERMS_SHOW, null, $this, false);
+                if ($galleries && !is_a($galleries, 'PEAR_Error')) {
+                    foreach ($galleries as $galleryId => $gallery) {
+                        if ($default_img = $gallery->getDefaultImage($style)) {
+                            return $default_img;
+                        }
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns this gallery's tags.
+     */
+    function getTags() {
+        require_once ANSEL_BASE . '/lib/Tags.php';
+        if ($this->hasPermission(Horde_Auth::getAuth(), PERMS_READ)) {
+            return Ansel_Tags::readTags($this->id, 'gallery');
+        } else {
+            return PEAR::raiseError(_("Access denied viewing this gallery."));
+        }
+    }
+
+    /**
+     * Set/replace this gallery's tags.
+     *
+     * @param array $tags  AN array of tag names to associate with this image.
+     */
+    function setTags($tags)
+    {
+        require_once ANSEL_BASE . '/lib/Tags.php';
+        if ($this->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+            return Ansel_Tags::writeTags($this->id, $tags, 'gallery');
+        } else {
+            return PEAR::raiseError(_("Access denied adding tags to this gallery."));
+        }
+    }
+
+    /**
+     * Return the style definition for this gallery. Returns the first available
+     * style in this order: Explicitly configured style if available, if
+     * configured style is not available, use ansel_default.  If nothing has
+     * been configured, the user's selected default is attempted.
+     *
+     * @return array  The style definition array.
+     */
+    function getStyle()
+    {
+        if (empty($this->data['attribute_style'])) {
+            $style = $GLOBALS['prefs']->getValue('default_gallerystyle');
+        } else {
+            $style = $this->data['attribute_style'];
+        }
+        return Ansel::getStyleDefinition($style);
+
+    }
+
+    /**
+     * Return a hash key for the given view and style.
+     *
+     * @param string $view   The view (thumb, prettythumb etc...)
+     * @param string $style  The named style.
+     *
+     * @return string  A md5 hash suitable for use as a key.
+     */
+    function _getViewHash($view, $style = null)
+    {
+        if (is_null($style)) {
+            $style = $this->getStyle();
+        } else {
+            $style = Ansel::getStyleDefinition($style);
+        }
+        if ($view != 'screen' && $view != 'thumb' && $view != 'mini' &&
+            $view != 'full') {
+
+            $view = md5($style['thumbstyle'] . '.' . $style['background']);
+        }
+        return $view;
+    }
+    /**
+     * Checks to see if a user has a given permission.
+     *
+     * @param string $userid       The userid of the user.
+     * @param integer $permission  A PERMS_* constant to test for.
+     * @param string $creator      The creator of the event.
+     *
+     * @return boolean  Whether or not $userid has $permission.
+     */
+    function hasPermission($userid, $permission, $creator = null)
+    {
+        if ($userid == $this->data['share_owner'] ||
+            Horde_Auth::isAdmin('ansel:admin')) {
+
+            return true;
+        }
+
+
+        return $GLOBALS['perms']->hasPermission($this->getPermission(),
+                                                $userid, $permission, $creator);
+    }
+
+    /**
+     * Check user age limtation
+     *
+     * @return boolean
+     */
+    function isOldEnough()
+    {
+        if ($this->data['share_owner'] == Horde_Auth::getAuth() ||
+            empty($GLOBALS['conf']['ages']['limits']) ||
+            empty($this->data['attribute_age'])) {
+
+            return true;
+        }
+
+        // Do we have the user age already cheked?
+        if (!isset($_SESSION['ansel']['user_age'])) {
+            $_SESSION['ansel']['user_age'] = 0;
+        } elseif ($_SESSION['ansel']['user_age'] >= $this->data['attribute_age']) {
+            return true;
+        }
+
+        // Can we hook user's age?
+        if ($GLOBALS['conf']['ages']['hook'] && Horde_Auth::isAuthenticated()) {
+            $result = Horde::callHook('_ansel_hook_user_age');
+            if (is_int($result)) {
+                $_SESSION['ansel']['user_age'] = $result;
+            }
+        }
+
+        return ($_SESSION['ansel']['user_age'] >= $this->data['attribute_age']);
+    }
+
+    /**
+     * Determine if we need to unlock a password protected gallery
+     *
+     * @return boolean
+     */
+    function hasPasswd()
+    {
+        if (Horde_Auth::getAuth() == $this->get('owner') || Horde_Auth::isAdmin('ansel:admin')) {
+            return false;
+        }
+
+        $passwd = $this->get('passwd');
+        if (empty($passwd) ||
+            (!empty($_SESSION['ansel']['passwd'][$this->id])
+                && $_SESSION['ansel']['passwd'][$this->id] = md5($this->get('passwd')))) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Sets this gallery's parent gallery.
+     *
+     * @TODO: Check how this interacts with date galleries - shouldn't be able
+     *        to remove a subgallery from a date gallery anyway, but just incase
+     * @param mixed $parent    An Ansel_Gallery or a gallery_id.
+     *
+     * @return mixed  Ture || PEAR_Error
+     */
+    function setParent($parent)
+    {
+        /* Make sure we have a gallery object */
+        if (!is_null($parent) && !is_a($parent, 'Ansel_Gallery')) {
+            $parent = $GLOBALS['ansel_storage']->getGallery($parent);
+            if (is_a($parent, 'PEAR_Error')) {
+                return $parent;
+            }
+        }
+
+        /* Check this now since we don't know if we are updating the DB or not */
+        $old = $this->getParent();
+        $reset_has_subgalleries = false;
+        if (!is_null($old)) {
+            $cnt = $old->countGalleryChildren(PERMS_READ, true);
+            if ($cnt == 1) {
+                /* Count is 1, and we are about to delete it */
+                $reset_has_subgalleries = true;
+            }
+        }
+
+        /* Call the parent class method */
+        $result = parent::setParent($parent);
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        }
+
+        /* Tell the parent the good news */
+        if (!is_null($parent) && !$parent->get('has_subgalleries')) {
+            return $parent->set('has_subgalleries', '1', true);
+        }
+        Horde::logMessage('Ansel_Gallery parent successfully set', __FILE__,
+                          __LINE__, PEAR_LOG_DEBUG);
+
+       /* Gallery parent changed, safe to change the parent's attributes */
+       if ($reset_has_subgalleries) {
+           $old->set('has_subgalleries', 0, true);
+       }
+
+        return true;
+    }
+
+    /**
+     * Sets an attribute value in this object.
+     *
+     * @param string $attribute  The attribute to set.
+     * @param mixed $value       The value for $attribute.
+     * @param boolean $update    Commit only this change to storage.
+     *
+     * @return mixed  True if setting the attribute did succeed, a PEAR_Error
+     *                otherwise.
+     */
+    function set($attribute, $value, $update = false)
+    {
+        /* Translate the keys */
+        if ($attribute == 'owner') {
+            $driver_key = 'share_owner';
+        } else {
+            $driver_key = 'attribute_' . $attribute;
+        }
+
+        if ($driver_key == 'attribute_view_mode' &&
+            !empty($this->data[$driver_key]) &&
+            $value != $this->data[$driver_key]) {
+
+            $mode = isset($attributes['attribute_view_mode']) ? $attributes['attribute_view_mode'] : 'Normal';
+            $this->_setModeHelper($mode);
+        }
+
+        $this->data[$driver_key] = $value;
+
+        /* Update the backend, but only this current change */
+        if ($update) {
+            $db = $this->_shareOb->_write_db;
+            // Manually convert the charset since we're not going through save()
+            $data = $this->_shareOb->_toDriverCharset(array($driver_key => $value));
+            $query = $db->prepare('UPDATE ' . $this->_shareOb->_table . ' SET ' . $driver_key . ' = ? WHERE share_id = ?', null, MDB2_PREPARE_MANIP);
+            if ($GLOBALS['conf']['ansel_cache']['usecache']) {
+                $GLOBALS['cache']->expire('Ansel_Gallery' . $this->id);
+            }
+            $result = $query->execute(array($data[$driver_key], $this->id));
+            $query->free();
+
+            return $result;
+        }
+
+        return true;
+    }
+
+    function setDate($date)
+    {
+        $this->_modeHelper->setDate($date);
+    }
+
+    function getDate()
+    {
+        return $this->_modeHelper->getDate();
+    }
+
+    /**
+     * Get an array describing where this gallery is in a breadcrumb trail.
+     *
+     * @return  An array of 'title' and 'navdata' hashes with the [0] element
+     *          being the deepest part.
+     */
+    function getGalleryCrumbData()
+    {
+        return $this->_modeHelper->getGalleryCrumbData();
+    }
+
+}
+
+/**
+ * Class to describe a single Ansel image.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_Image {
+
+    /**
+     * @var integer  The gallery id of this image's parent gallery
+     */
+    var $gallery;
+
+    /**
+     * @var Horde_Image  Horde_Image object for this image.
+     */
+    var $_image;
+
+    var $id = null;
+    var $filename = 'Untitled';
+    var $caption = '';
+    var $type = 'image/jpeg';
+
+    /**
+     * timestamp of uploaded date
+     *
+     * @var integer
+     */
+    var $uploaded;
+
+    var $sort;
+    var $commentCount;
+    var $facesCount;
+    var $lat;
+    var $lng;
+    var $location;
+    var $geotag_timestamp;
+
+    var $_dirty;
+
+
+    /**
+     * Timestamp of original date.
+     *
+     * @var integer
+     */
+    var $originalDate;
+
+    /**
+     * Holds an array of tags for this image
+     * @var array
+     */
+    var $_tags = array();
+
+    var $_loaded = array();
+    var $_data = array();
+
+    /**
+     * Cache the raw EXIF data locally
+     *
+     * @var array
+     */
+    var $_exif = array();
+
+    /**
+     * TODO: refactor Ansel_Image to use a ::get() method like Ansel_Gallery
+     * instead of direct instance variable access and all the nonsense below.
+     *
+     * @param unknown_type $image
+     * @return Ansel_Image
+     */
+    function Ansel_Image($image = array())
+    {
+        if ($image) {
+            $this->filename = $image['image_filename'];
+            $this->caption = $image['image_caption'];
+            $this->sort = $image['image_sort'];
+            $this->gallery = $image['gallery_id'];
+
+            // New image?
+            if (!empty($image['image_id'])) {
+                $this->id = $image['image_id'];
+            }
+
+            if (!empty($image['data'])) {
+                $this->_data['full'] = $image['data'];
+            }
+
+            if (!empty($image['image_uploaded_date'])) {
+                $this->uploaded = $image['image_uploaded_date'];
+            } else {
+                $this->uploaded = time();
+            }
+
+            if (!empty($image['image_type'])) {
+                $this->type = $image['image_type'];
+            }
+
+            if (!empty($image['tags'])) {
+                $this->_tags = $image['tags'];
+            }
+
+            if (!empty($image['image_faces'])) {
+               $this->facesCount = $image['image_faces'];
+            }
+
+            $this->location = !empty($image['image_location']) ? $image['image_location'] : '';
+
+            // The following may have to be rewritten by EXIF.
+            // EXIF requires both an image id and a stream, so we can't
+            // get EXIF data before we save the image to the VFS.
+            if (!empty($image['image_original_date'])) {
+                $this->originalDate = $image['image_original_date'];
+            } else {
+                $this->originalDate = $this->uploaded;
+            }
+            $this->lat = !empty($image['image_latitude']) ? $image['image_latitude'] : '';
+            $this->lng = !empty($image['image_longitude']) ? $image['image_longitude'] : '';
+            $this->geotag_timestamp = !empty($image['image_geotag_date']) ? $image['image_geotag_date'] : '0';
+        }
+
+        $this->_image = Ansel::getImageObject();
+        $this->_image->reset();
+    }
+
+    /**
+     * Return the vfs path for this image.
+     *
+     * @param string $view   The view we want.
+     * @param string $style  A named gallery style.
+     *
+     * @return string  The vfs path for this image.
+     */
+    function getVFSPath($view = 'full', $style = null)
+    {
+        $view = $this->_getViewHash($view, $style);
+        return '.horde/ansel/'
+               . substr(str_pad($this->id, 2, 0, STR_PAD_LEFT), -2)
+               . '/' . $view;
+    }
+
+    /**
+     * Returns the file name of this image as used in the VFS backend.
+     *
+     * @return string  This image's VFS file name.
+     */
+    function getVFSName($view)
+    {
+        $vfsname = $this->id;
+
+        if ($view == 'full' && $this->type) {
+            $type = strpos($this->type, '/') === false ? 'image/' . $this->type : $this->type;
+            require_once 'Horde/Mime/Magic.php';
+            if ($ext = Horde_Mime_Magic::mimeToExt($type)) {
+                $vfsname .= '.' . $ext;
+            }
+        } elseif (($GLOBALS['conf']['image']['type'] == 'jpeg') || $view == 'screen') {
+            $vfsname .= '.jpg';
+        } else {
+            $vfsname .= '.png';
+        }
+
+        return $vfsname;
+    }
+
+    /**
+     * Loads the given view into memory.
+     *
+     * @param string $view   Which view to load.
+     * @param string $style  The named gallery style.
+     *
+     * @return mixed  True || PEAR_Error
+     */
+    function load($view = 'full', $style = null)
+    {
+        // If this is a new image that hasn't been saved yet, we will
+        // already have the full data loaded. If we auto-rotate the image
+        // then there is no need to save it just to load it again.
+        if ($view == 'full' && !empty($this->_data['full'])) {
+            $this->_image->loadString('original', $this->_data['full']);
+            $this->_loaded['full'] = true;
+            return true;
+        }
+
+        $viewHash = $this->_getViewHash($view, $style);
+        /* If we've already loaded the data, just return now. */
+        if (!empty($this->_loaded[$viewHash])) {
+            return true;
+        }
+
+        $result = $this->createView($view, $style);
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        }
+
+        /* If createView() had to resize the full image, we've already
+         * loaded the data, so return now. */
+        if (!empty($this->_loaded[$viewHash])) {
+            return;
+        }
+
+        /* We've definitely successfully loaded the image now. */
+        $this->_loaded[$viewHash] = true;
+
+        /* Get the VFS info. */
+        $vfspath = $this->getVFSPath($view, $style);
+        if (is_a($vfspath, 'PEAR_Error')) {
+            return $vfspath;
+        }
+
+        /* Read in the requested view. */
+        $data = $GLOBALS['ansel_vfs']->read($vfspath, $this->getVFSName($view));
+        if (is_a($data, 'PEAR_Error')) {
+            Horde::logMessage($date, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return $data;
+        }
+
+        $this->_data[$viewHash] = $data;
+        $this->_image->loadString($vfspath . '/' . $this->id, $data);
+        return true;
+    }
+
+    /**
+     * Check if an image view exists and returns the vfs name complete with
+     * the hash directory name prepended if appropriate.
+     *
+     * @param integer $id    Image id to check
+     * @param string $view   Which view to check for
+     * @param string $style  A named gallery style
+     *
+     * @return mixed  False if image does not exists | string vfs name
+     *
+     * @static
+     */
+    function viewExists($id, $view, $style)
+    {
+        /* We cannot check empty styles since we cannot get the hash */
+        if (empty($style)) {
+            return false;
+        }
+
+        /* Get the VFS path. */
+        $view = Ansel_Gallery::_getViewHash($view, $style);
+
+        /* Can't call the various vfs methods here, since this method needs
+        to be called statically */
+        $vfspath = '.horde/ansel/' . substr(str_pad($id, 2, 0, STR_PAD_LEFT), -2) . '/' . $view;
+
+        /* Get VFS name */
+        $vfsname = $id . '.';
+        if ($GLOBALS['conf']['image']['type'] == 'jpeg' || $view == 'screen') {
+            $vfsname .= 'jpg';
+        } else {
+            $vfsname .= 'png';
+        }
+
+        if ($GLOBALS['ansel_vfs']->exists($vfspath, $vfsname)) {
+            return $view . '/' . $vfsname;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Creates and caches the given view.
+     *
+     * @param string $view  Which view to create.
+     * @param string $style  A named gallery style
+     */
+    function createView($view, $style = null)
+    {
+        // HACK: Need to replace the image object with a JPG typed image if
+        //       we are generating a screen image. Need to do the replacement
+        //       and do it *here* for BC reasons with Horde_Image...and this
+        //       needs to be done FIRST, since the view might already be cached
+        //       in the VFS.
+        if ($view == 'screen' && $GLOBALS['conf']['image']['type'] != 'jpeg') {
+            $this->_image = Ansel::getImageObject(array('type' => 'jpeg'));
+            $this->_image->reset();
+        }
+
+        /* Get the VFS info. */
+        $vfspath = $this->getVFSPath($view, $style);
+        if ($GLOBALS['ansel_vfs']->exists($vfspath, $this->getVFSName($view))) {
+            return true;
+        }
+
+        $data = $GLOBALS['ansel_vfs']->read($this->getVFSPath('full'),
+                                            $this->getVFSName('full'));
+        if (is_a($data, 'PEAR_Error')) {
+            Horde::logMessage($data, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return $data;
+        }
+
+        if (is_a($result = $this->_image->loadString($this->getVFSPath('full') . '/' . $this->id, $data), 'PEAR_Error')) {
+            return $result;
+        }
+        $styleDef = Ansel::getStyleDefinition($style);
+
+        require_once dirname(__FILE__) . '/ImageView.php';
+        if ($view == 'prettythumb') {
+            $viewType = $styleDef['thumbstyle'];
+        } else {
+            $viewType = $view;
+        }
+        $iview = Ansel_ImageView::factory($viewType, array('image' => $this,
+                                                           'style' => $style));
+
+        if (is_a($iview, 'PEAR_Error')) {
+            // It could be we don't support the requested effect, try
+            // ansel_default before giving up.
+            if ($view == 'prettythumb') {
+                $iview = Ansel_ImageView::factory(
+                    'thumb', array('image' => $this,
+                                   'style' => 'ansel_default'));
+
+                if (is_a($iview, 'PEAR_Error')) {
+                    return $iview;
+                }
+            }
+        }
+
+        $res = $iview->create();
+        if (is_a($res, 'PEAR_Error')) {
+            return $res;
+        }
+
+        $view = $this->_getViewHash($view, $style);
+
+        $this->_data[$view] = $this->_image->raw();
+        $this->_image->loadString($vfspath . '/' . $this->id,
+                                  $this->_data[$view]);
+        $this->_loaded[$view] = true;
+        $GLOBALS['ansel_vfs']->writeData($vfspath, $this->getVFSName($view),
+                                         $this->_data[$view], true);
+
+        // Autowatermark the screen view
+        if ($view == 'screen' &&
+            $GLOBALS['prefs']->getValue('watermark_auto') &&
+            $GLOBALS['prefs']->getValue('watermark_text') != '') {
+
+            $this->watermark('screen');
+            $GLOBALS['ansel_vfs']->writeData($vfspath, $this->getVFSName($view),
+                                             $this->_image->_data);
+        }
+
+        return true;
+    }
+
+    /**
+     * Writes the current data to vfs, used when creating a new image
+     */
+    function _writeData()
+    {
+        $this->_dirty = false;
+        return $GLOBALS['ansel_vfs']->writeData($this->getVFSPath('full'),
+                                                $this->getVFSName('full'),
+                                                $this->_data['full'], true);
+    }
+
+    /**
+     * Change the image data. Deletes old cache and writes the new
+     * data to the VFS. Used when updating an image
+     *
+     * @param string $data  The new data for this image.
+     * @param string $view  If specified, the $data represents only this
+     *                      particular view. Cache will not be deleted.
+     */
+    function updateData($data, $view = 'full')
+    {
+        if (is_a($data, 'PEAR_Error')) {
+            return $data;
+        }
+
+        /* Delete old cached data if we are replacing the full image */
+        if ($view == 'full') {
+            $this->deleteCache();
+        }
+
+        return $GLOBALS['ansel_vfs']->writeData($this->getVFSPath($view),
+                                                $this->getVFSName($view),
+                                                $data, true);
+    }
+
+    /**
+     * Update the geotag data
+     */
+    function geotag($lat, $lng, $location = '')
+    {
+        $this->lat = $lat;
+        $this->lng = $lng;
+        $this->location = $location;
+        $this->geotag_timestamp = time();
+        $this->save();
+    }
+
+    /**
+     * Save basic image details
+     *
+     * @TODO: Move all SQL queries to Ansel_Storage::?
+     */
+    function save()
+    {
+        /* If we have an id, then it's an existing image.*/
+        if ($this->id) {
+            $update = $GLOBALS['ansel_db']->prepare('UPDATE ansel_images SET image_filename = ?, image_type = ?, image_caption = ?, image_sort = ?, image_original_date = ?, image_latitude = ?, image_longitude = ?, image_location = ?, image_geotag_date = ? WHERE image_id = ?');
+            if (is_a($update, 'PEAR_Error')) {
+                Horde::logMessage($update, __FILE__, __LINE__, PEAR_LOG_ERR);
+                return $update;
+            }
+            $result = $update->execute(array(Horde_String::convertCharset($this->filename, Horde_Nls::getCharset(), $GLOBALS['conf']['sql']['charset']),
+                                             $this->type,
+                                             Horde_String::convertCharset($this->caption, Horde_Nls::getCharset(), $GLOBALS['conf']['sql']['charset']),
+                                             $this->sort,
+                                             $this->originalDate,
+                                             $this->lat,
+                                             $this->lng,
+                                             $this->location,
+                                             $this->geotag_timestamp,
+                                             $this->id));
+            if (is_a($result, 'PEAR_Error')) {
+                Horde::logMessage($update, __FILE__, __LINE__, PEAR_LOG_ERR);
+            } else {
+                $update->free();
+            }
+            return $result;
+        }
+
+        /* Saving a new Image */
+        if (!$this->gallery || !strlen($this->filename) || !$this->type) {
+            $error = PEAR::raiseError(_("Incomplete photo"));
+            Horde::logMessage($error, __FILE__, __LINE__, PEAR_LOG_ERR);
+        }
+
+        /* Get the next image_id */
+        $image_id = $GLOBALS['ansel_db']->nextId('ansel_images');
+        if (is_a($image_id, 'PEAR_Error')) {
+            return $image_id;
+        }
+
+        /* Prepare the SQL statement */
+        $insert = $GLOBALS['ansel_db']->prepare('INSERT INTO ansel_images (image_id, gallery_id, image_filename, image_type, image_caption, image_uploaded_date, image_sort, image_original_date, image_latitude, image_longitude, image_location, image_geotag_date) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
+        if (is_a($insert, 'PEAR_Error')) {
+            Horde::logMessage($insert, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return $insert;
+        }
+
+        /* Perform the INSERT */
+        $result = $insert->execute(array($image_id,
+                                         $this->gallery,
+                                         Horde_String::convertCharset($this->filename, Horde_Nls::getCharset(), $GLOBALS['conf']['sql']['charset']),
+                                         $this->type,
+                                         Horde_String::convertCharset($this->caption, Horde_Nls::getCharset(), $GLOBALS['conf']['sql']['charset']),
+                                         $this->uploaded,
+                                         $this->sort,
+                                         $this->originalDate,
+                                         $this->lat,
+                                         $this->lng,
+                                         $this->location,
+                                         (empty($this->lat) ? 0 : $this->uploaded)));
+        $insert->free();
+        if (is_a($result, 'PEAR_Error')) {
+            Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return $result;
+        }
+
+        /* Keep the image_id */
+        $this->id = $image_id;
+
+        /* The EXIF functions require a stream, so we need to save before we read */
+        $this->_writeData();
+
+        /* Get the EXIF data now that we have an id and a file in the VFS */
+        $needUpdate = $this->_getEXIF();
+
+        /* Create tags from exif data if desired */
+        $fields = @unserialize($GLOBALS['prefs']->getValue('exif_tags'));
+        if ($fields) {
+            $this->_exifToTags($fields);
+        }
+
+        /* Save the tags */
+        if (count($this->_tags)) {
+            $result = $this->setTags($this->_tags);
+            if (is_a($result, 'PEAR_Error')) {
+                // Since we got this far, the image has been added, so
+                // just log the tag failure.
+                Horde::logMessage($result, __LINE__, __FILE__, PEAR_LOG_ERR);
+            }
+        }
+
+        /* Save again if EXIF changed any values */
+        if ($needUpdate) {
+            $this->save();
+        }
+
+        return $this->id;
+    }
+
+   /**
+    * Replace this image's image data.
+    *
+    */
+    function replace($imageData)
+    {
+        /* Reset the data array and remove all cached images */
+        $this->_data = array();
+        $this->reset();
+
+        /* Remove attributes */
+        $result = $GLOBALS['ansel_db']->exec('DELETE FROM ansel_image_attributes WHERE image_id = ' . (int)$this->id);
+        if (is_a($result, 'PEAR_Error')) {
+            Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERROR);
+            return $result;
+        }
+        /* Load the new image data */
+        $this->_getEXIF();
+        $this->updateData($imageData);
+
+        return true;
+    }
+
+    /**
+     * Adds specified EXIF fields to this image's tags. Called during image
+     * upload/creation.
+     *
+     * @param array $fields  An array of EXIF fields to import as a tag.
+     *
+     */
+    function _exifToTags($fields = array())
+    {
+        require_once 'Horde/Date.php';
+
+        $tags = array();
+        foreach ($fields as $field) {
+            if (!empty($this->_exif[$field])) {
+                if (substr($field, 0, 8) == 'DateTime') {
+                    $d = new Horde_Date(strtotime($this->_exif[$field]));
+                    $tags[] = $d->format("Y-m-d");
+                } else {
+                    $tags[] = $this->_exif[$field];
+                }
+            }
+        }
+
+        $this->_tags = array_merge($this->_tags, $tags);
+    }
+
+    /**
+     * Reads the EXIF data from the image and stores in _exif array() as well
+     * also populates any local properties that come from the EXIF data.
+     *
+     * @return mixed  true if any local properties were modified, false otherwise, PEAR_Error on failure
+     */
+    function _getEXIF()
+    {
+        require_once ANSEL_BASE . '/lib/Exif.php';
+
+        /* Clear the local copy */
+        $this->_exif = array();
+
+        /* Get the data */
+        $exif_fields = Ansel_ImageData::getExifData($this);
+
+        /* Flag to determine if we need to resave the image data */
+        $needUpdate = false;
+
+        /* Populate any local properties that come from EXIF */
+        if (!is_a($exif_fields, 'PEAR_Error')) {
+            /* Save any geo data to a seperate table as well */
+            if (!empty($exif_fields['GPSLatitude'])) {
+                $this->lat = $exif_fields['GPSLatitude'];
+                $this->lng = $exif_fields['GPSLongitude'];
+                $this->geotag_timestamp = time();
+                $needUpdate = true;
+            }
+
+            if (!empty($exif_fields['DateTimeOriginal'])) {
+                $this->originalDate = $exif_fields['DateTimeOriginal'];
+                $needUpdate = true;
+            }
+
+            /* Attempt to autorotate based on Orientation field */
+            $this->_autoRotate();
+
+            /* Save attributes. */
+            $insert = $GLOBALS['ansel_db']->prepare('INSERT INTO ansel_image_attributes (image_id, attr_name, attr_value) VALUES (?, ?, ?)');
+            foreach ($exif_fields as $name => $value) {
+                $result = $insert->execute(array($this->id, $name, Horde_String::convertCharset($value, Horde_Nls::getCharset(), $GLOBALS['conf']['sql']['charset'])));
+                if (is_a($result, 'PEAR_Error')) {
+                    return $result;
+                }
+                /* Cache it locally */
+                $this->_exif[$name] = Ansel_ImageData::getHumanReadable($name, $value);
+            }
+            $insert->free();
+        }
+
+        return $needUpdate;
+    }
+
+    /**
+     * Autorotate based on EXIF orientation field. Updates the data in memory
+     * only.
+     *
+     */
+    function _autoRotate()
+    {
+        if (isset($this->_exif['Orientation']) && $this->_exif['Orientation'] != 1) {
+            switch ($this->_exif['Orientation']) {
+            case 2:
+                 $this->mirror();
+                break;
+
+            case 3:
+                $this->rotate('full', 180);
+                break;
+
+            case 4:
+                $this->mirror();
+                $this->rotate('full', 180);
+                break;
+
+            case 5:
+                $this->flip();
+                $this->rotate('full', 90);
+                break;
+
+            case 6:
+                $this->rotate('full', 90);
+                break;
+
+            case 7:
+                $this->mirror();
+                $this->rotate('full', 90);
+                break;
+
+            case 8:
+                $this->rotate('full', 270);
+                break;
+            }
+
+            if ($this->_dirty) {
+                $this->_exif['Orientation'] = 1;
+                $this->data['full'] = $this->raw();
+                $this->_writeData();
+            }
+        }
+    }
+
+    /**
+     * Reset the image, removing all loaded views.
+     */
+    function reset()
+    {
+        $this->_image->reset();
+        $this->_loaded = array();
+    }
+
+    /**
+     * Deletes the specified cache file.
+     *
+     * If none is specified, deletes all of the cache files.
+     *
+     * @param string $view  Which cache file to delete.
+     */
+    function deleteCache($view = 'all')
+    {
+        /* Delete cached screen image. */
+        if ($view == 'all' || $view == 'screen') {
+            $GLOBALS['ansel_vfs']->deleteFile($this->getVFSPath('screen'),
+                                              $this->getVFSName('screen'));
+        }
+
+        /* Delete cached thumbnail. */
+        if ($view == 'all' || $view == 'thumb') {
+            $GLOBALS['ansel_vfs']->deleteFile($this->getVFSPath('thumb'),
+                                              $this->getVFSName('thumb'));
+        }
+
+        /* Delete cached mini image. */
+        if ($view == 'all' || $view == 'mini') {
+            $GLOBALS['ansel_vfs']->deleteFile($this->getVFSPath('mini'),
+                                              $this->getVFSName('mini'));
+        }
+
+        if ($view == 'all' || $view == 'prettythumb') {
+
+            /* No need to try to delete a hash we already removed */
+            $deleted = array();
+
+            /* Need to generate hashes for each possible style */
+            $styles = Horde::loadConfiguration('styles.php', 'styles', 'ansel');
+            foreach ($styles as $style) {
+                $hash =  md5($style['thumbstyle'] . '.' . $style['background']);
+                if (empty($deleted[$hash])) {
+                    $GLOBALS['ansel_vfs']->deleteFile($this->getVFSPath($hash),
+                                                      $this->getVFSName($hash));
+                    $deleted[$hash] = true;
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns the raw data for the given view.
+     *
+     * @param string $view  Which view to return.
+     */
+    function raw($view = 'full')
+    {
+        if ($this->_dirty) {
+          return $this->_image->raw();
+        } else {
+            $this->load($view);
+            return $this->_data[$view];
+        }
+    }
+
+    /**
+     * Sends the correct HTTP headers to the browser to download this image.
+     *
+     * @param string $view  The view to download.
+     */
+    function downloadHeaders($view = 'full')
+    {
+        global $browser, $conf;
+
+        $filename = $this->filename;
+        if ($view != 'full') {
+            require_once 'Horde/Mime/Magic.php';
+            if ($ext = Horde_Mime_Magic::mimeToExt('image/' . $conf['image']['type'])) {
+                $filename .= '.' . $ext;
+            }
+        }
+
+        $browser->downloadHeaders($filename);
+    }
+
+    /**
+     * Display the requested view.
+     *
+     * @param string $view   Which view to display.
+     * @param string $style  Force use of this gallery style.
+     */
+    function display($view = 'full', $style = null)
+    {
+        if ($view == 'full' && !$this->_dirty) {
+
+            // Check full photo permissions
+            $gallery = $GLOBALS['ansel_storage']->getGallery($this->gallery);
+            if (is_a($gallery, 'PEAR_Error')) {
+                return $gallery;
+            }
+            if (!$gallery->canDownload()) {
+                return PEAR::RaiseError(sprintf(_("Access denied downloading photos from \"%s\"."), $gallery->get('name')));
+            }
+
+            $data = $GLOBALS['ansel_vfs']->read($this->getVFSPath('full'),
+                                                $this->getVFSName('full'));
+
+            if (is_a($data, 'PEAR_Error')) {
+                return $data;
+            }
+            echo $data;
+            return;
+        }
+
+        if (is_a($result = $this->load($view, $style), 'PEAR_Error')) {
+            Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return $result;
+        }
+
+        return $this->_image->display();
+    }
+
+    /**
+     * Wraps the given view into a file.
+     *
+     * @param string $view  Which view to wrap up.
+     */
+    function toFile($view = 'full')
+    {
+        if (is_a(($result = $this->load($view)), 'PEAR_Error')) {
+            return $result;
+        }
+        return $this->_image->toFile($this->_dirty ? false : $this->_data[$view]);
+    }
+
+    /**
+     * Returns the dimensions of the given view.
+     *
+     * @param string $view  The view (size) to check dimensions for.
+     */
+    function getDimensions($view = 'full')
+    {
+        if (is_a(($result = $this->load($view)), 'PEAR_Error')) {
+            return $result;
+        }
+        return $this->_image->getDimensions();
+    }
+
+    /**
+     * Rotates the image.
+     *
+     * @param string $view The view (size) to work with.
+     * @param integer $angle  What angle to rotate the image by.
+     */
+    function rotate($view = 'full', $angle)
+    {
+        $this->load($view);
+        $this->_dirty = true;
+        return $this->_image->rotate($angle);
+    }
+
+    function crop($x1, $y1, $x2, $y2)
+    {
+        $this->_dirty = true;
+        return $this->_image->crop($x1, $y1, $x2, $y2);
+    }
+
+    /**
+     * Converts the image to grayscale.
+     *
+     * @param string $view The view (size) to work with.
+     */
+    function grayscale($view = 'full')
+    {
+        $this->load($view);
+        $this->_dirty = true;
+        return $this->_image->grayscale();
+    }
+
+    /**
+     * Watermarks the image.
+     *
+     * @param string $view The view (size) to work with.
+     * @param string $watermark  String to use as the watermark.
+     */
+    function watermark($view = 'full', $watermark = null, $halign = null,
+                       $valign = null, $font = null)
+    {
+        if (empty($watermark)) {
+            $watermark = $GLOBALS['prefs']->getValue('watermark_text');
+        }
+
+        if (empty($halign)) {
+            $halign = $GLOBALS['prefs']->getValue('watermark_horizontal');
+        }
+
+        if (empty($valign)) {
+            $valign = $GLOBALS['prefs']->getValue('watermark_vertical');
+        }
+
+        if (empty($font)) {
+            $font = $GLOBALS['prefs']->getValue('watermark_font');
+        }
+
+        if (empty($watermark)) {
+            require_once 'Horde/Identity.php';
+            $identity = &Identity::singleton();
+            $name = $identity->getValue('fullname');
+            if (empty($name)) {
+                $name = Horde_Auth::getAuth();
+            }
+            $watermark = sprintf(_("(c) %s %s"), date('Y'), $name);
+        }
+
+        $this->load($view);
+        $this->_dirty = true;
+        $params = array('text' => $watermark,
+                        'halign' => $halign,
+                        'valign' => $valign,
+                        'fontsize' => $font);
+        if (!empty($GLOBALS['conf']['image']['font'])) {
+            $params['font'] = $GLOBALS['conf']['image']['font'];
+        }
+        $this->_image->addEffect('TextWatermark', $params);
+
+        return true;
+    }
+
+    /**
+     * Flips the image.
+     *
+     * @param string $view The view (size) to work with.
+     */
+    function flip($view = 'full')
+    {
+        $this->load($view);
+        $this->_dirty = true;
+        return $this->_image->flip();
+    }
+
+    /**
+     * Mirrors the image.
+     *
+     * @param string $view The view (size) to work with.
+     */
+    function mirror($view = 'full')
+    {
+        $this->load($view);
+        $this->_dirty = true;
+        return $this->_image->mirror();
+    }
+
+    /**
+     * Returns this image's tags.
+     *
+     * @return mixed  An array of tags | PEAR_Error
+     * @see Ansel_Tags::readTags()
+     */
+    function getTags()
+    {
+        global $ansel_storage;
+
+        if (count($this->_tags)) {
+            return $this->_tags;
+        }
+        require_once ANSEL_BASE . '/lib/Tags.php';
+        $gallery = $ansel_storage->getGallery($this->gallery);
+        if (is_a($gallery, 'PEAR_Error')) {
+            return $gallery;
+        }
+        if ($gallery->hasPermission(Horde_Auth::getAuth(), PERMS_READ)) {
+            $res = Ansel_Tags::readTags($this->id);
+            if (!is_a($res, 'PEAR_Error')) {
+                $this->_tags = $res;
+                return $this->_tags;
+            } else {
+                return $res;
+            }
+        } else {
+            return PEAR::raiseError(_("Access denied viewing this photo."));
+        }
+    }
+
+    /**
+     * Set/replace this image's tags.
+     *
+     * @param array $tags  An array of tag names to associate with this image.
+     */
+    function setTags($tags)
+    {
+        global $ansel_storage;
+
+        require_once ANSEL_BASE . '/lib/Tags.php';
+
+        $gallery = $ansel_storage->getGallery(abs($this->gallery));
+        if ($gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+            // Clear the local cache.
+            $this->_tags = array();
+            return Ansel_Tags::writeTags($this->id, $tags);
+        } else {
+            return PEAR::raiseError(_("Access denied adding tags to this photo."));
+        }
+    }
+
+    /**
+     * Get the Ansel_View_Image_Thumb object
+     *
+     * @param Ansel_Gallery $parent  The parent Ansel_Gallery object.
+     * @param string $style          A named gallery style to use.
+     * @param boolean $mini          Force the use of a mini thumbnail?
+     * @param array $params          Any additional parameters the Ansel_Tile
+     *                               object may need.
+     *
+     */
+    function getTile($parent = null, $style = null, $mini = false,
+                     $params = array())
+    {
+        require_once ANSEL_BASE . '/lib/Tile/Image.php';
+
+        if (!is_null($parent) && is_null($style)) {
+            $style = $parent->getStyle();
+        } else {
+            $style = Ansel::getStyleDefinition($style);
+        }
+
+        return Ansel_Tile_Image::getTile($this, $style, $mini, $params);
+    }
+
+    /**
+     * Get the image type for the requested view.
+     */
+    function getType($view = 'full')
+    {
+        if ($view == 'full') {
+           return $this->type;
+        } elseif ($view == 'screen') {
+            return 'image/jpg';
+        } else {
+            return 'image/' . $GLOBALS['conf']['image']['type'];
+        }
+    }
+
+    /**
+     * Return a hash key for the given view and style.
+     *
+     * @param string $view   The view (thumb, prettythumb etc...)
+     * @param string $style  The named style.
+     *
+     * @return string  A md5 hash suitable for use as a key.
+     */
+    function _getViewHash($view, $style = null)
+    {
+        global $ansel_storage;
+
+        // These views do not care about style...just return the $view value.
+        if ($view == 'screen' || $view == 'thumb' || $view == 'mini' ||
+            $view == 'full') {
+
+            return $view;
+        }
+        if (is_null($style)) {
+            $gallery = $ansel_storage->getGallery(abs($this->gallery));
+            if (is_a($gallery, 'PEAR_Error')) {
+                return $gallery;
+            }
+            $style = $gallery->getStyle();
+        } else {
+            $style = Ansel::getStyleDefinition($style);
+        }
+
+       $view = md5($style['thumbstyle'] . '.' . $style['background']);
+       return $view;
+    }
+
+}
+
+/**
+ * Class for interfacing with back end data storage.
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ *
+ * @package Ansel
+ */
+class Ansel_Storage {
+
+    var $_scope = 'ansel';
+    var $_db = null;
+    var $galleries = array();
+
+    /**
+     * The Horde_Shares object to use for this scope.
+     *
+     * @var Horde_Share
+     */
+    var $shares = null;
+
+    /* Local cache of retrieved images */
+    var $images = array();
+
+    function Ansel_Storage($scope = null)
+    {
+        /* Check for a scope other than the default Ansel scope.*/
+        if (!is_null($scope)) {
+            $this->_scope = $scope;
+        }
+
+        /* This is the only supported share backend for Ansel */
+        $this->shares = Horde_Share::singleton($this->_scope,
+                                               'sql_hierarchical');
+
+        /* Ansel_Gallery is just a subclass of Horde_Share_Object */
+        $this->shares->_shareObject = 'Ansel_Gallery';
+
+        /* Database handle */
+        $this->_db = $GLOBALS['ansel_db'];
+    }
+
+   /**
+    * Create and initialise a new gallery object.
+    *
+    * @param array $attributes     The gallery attributes
+    * @param object Perms $perm    The permissions for the gallery if the
+    *                              defaults are not desirable.
+    * @param mixed  $parent       The gallery id of the parent (if any)
+    *
+    * @return Ansel_Gallery  A new gallery object or PEAR_Error.
+    */
+    function createGallery($attributes = array(), $perm = null, $parent = null)
+    {
+        /* Required values. */
+        if (empty($attributes['owner'])) {
+            $attributes['owner'] = Horde_Auth::getAuth();
+        }
+        if (empty($attributes['name'])) {
+            $attributes['name'] = _("Unnamed");
+        }
+        if (empty($attributes['desc'])) {
+            $attributes['desc'] = '';
+        }
+
+        /* Default values */
+        $attributes['default_type'] = isset($attributes['default_type']) ? $attributes['default_type'] : 'auto';
+        $attributes['default'] = isset($attributes['default']) ? (int)$attributes['default'] : 0;
+        $attributes['default_prettythumb'] = isset($attributes['default_prettythumb']) ? $attributes['default_prettythumb'] : '';
+        $attributes['style'] = isset($attributes['style']) ? $attributes['style'] : $GLOBALS['prefs']->getValue('default_gallerystyle');
+        $attributes['category'] = isset($attributes['category']) ? $attributes['category'] : $GLOBALS['prefs']->getValue('default_category');
+        $attributes['date_created'] = time();
+        $attributes['last_modified'] = $attributes['date_created'];
+        $attributes['images'] = isset($attributes['images']) ? (int)$attributes['images'] : 0;
+        $attributes['slug'] = isset($attributes['slug']) ? $attributes['slug'] : '';
+        $attributes['age'] = isset($attributes['age']) ? (int)$attributes['age'] : 0;
+        $attributes['download'] = isset($attributes['download']) ? $attributes['download'] : $GLOBALS['prefs']->getValue('default_download');
+        $attributes['view_mode'] = isset($attributes['view_mode']) ? $attributes['view_mode'] : 'Normal';
+        $attributes['passwd'] = isset($attributes['passwd']) ? $attributes['passwd'] : '';
+
+        /* Don't pass tags to the share creation method */
+        if (isset($attributes['tags'])) {
+            $tags = $attributes['tags'];
+            unset($attributes['tags']);
+        } else {
+            $tags = array();
+        }
+
+        /* Check for slug uniqueness */
+        if (!empty($attributes['slug']) &&
+            $this->slugExists($attributes['slug'])) {
+            return PEAR::raiseError(sprintf(_("The slug \"%s\" already exists."),
+                                            $attributes['slug']));
+        }
+
+        /* Create the gallery */
+        $gallery = $this->shares->newShare('');
+        if (is_a($gallery, 'PEAR_Error')) {
+            Horde::logMessage($gallery, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return $gallery;
+        }
+        Horde::logMessage('New Ansel_Gallery object instantiated', __FILE__, __LINE__, PEAR_LOG_DEBUG);
+
+        /* Set the gallery's parent if needed */
+        if (!is_null($parent)) {
+            $result = $gallery->setParent($parent);
+
+            /* Clear the parent from the cache */
+            if ($GLOBALS['conf']['ansel_cache']['usecache']) {
+                $GLOBALS['cache']->expire('Ansel_Gallery' . $parent);
+            }
+            if (is_a($result, 'PEAR_Error')) {
+                Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+                return $result;
+            }
+        }
+
+        /* Fill up the new gallery */
+        // TODO: New private method to bulk load these (it's done this way
+        // since the data is stored in the Share_Object class keyed by the
+        // DB specific fields and set() translates them.
+        foreach ($attributes as $key => $value) {
+            $gallery->set($key, $value);
+        }
+
+        /* Save it to storage */
+        $result = $this->shares->addShare($gallery);
+        if (is_a($result, 'PEAR_Error')) {
+            $error = sprintf(_("The gallery \"%s\" could not be created: %s"),
+                             $attributes['name'], $result->getMessage());
+            Horde::logMessage($error, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return PEAR::raiseError($error);
+        }
+
+        /* Convenience */
+        $gallery->id = $gallery->getId();
+
+        /* Add default permissions. */
+        if (empty($perm)) {
+            $perm = $gallery->getPermission();
+
+            /* Default permissions for logged in users */
+            switch ($GLOBALS['prefs']->getValue('default_permissions')) {
+            case 'read':
+                $perms = PERMS_SHOW | PERMS_READ;
+                break;
+            case 'edit':
+                $perms = PERMS_SHOW | PERMS_READ | PERMS_EDIT;
+                break;
+            case 'none':
+                $perms = 0;
+                break;
+            }
+            $perm->addDefaultPermission($perms, false);
+
+            /* Default guest permissions */
+            switch ($GLOBALS['prefs']->getValue('guest_permissions')) {
+            case 'read':
+                $perms = PERMS_SHOW | PERMS_READ;
+                break;
+            case 'none':
+            default:
+                $perms = 0;
+                break;
+            }
+            $perm->addGuestPermission($perms, false);
+
+            /* Default user groups permissions */
+            switch ($GLOBALS['prefs']->getValue('group_permissions')) {
+            case 'read':
+                $perms = PERMS_SHOW | PERMS_READ;
+                break;
+            case 'edit':
+                $perms = PERMS_SHOW | PERMS_READ | PERMS_EDIT;
+                break;
+            case 'delete':
+                $perms = PERMS_SHOW | PERMS_READ | PERMS_EDIT | PERMS_DELETE;
+                break;
+            case 'none':
+            default:
+                $perms = 0;
+                break;
+            }
+
+            if ($perms) {
+                require_once 'Horde/Group.php';
+                $groups = &Group::singleton();
+                $group_list = $groups->getGroupMemberships(Horde_Auth::getAuth());
+                if (!is_a($group_list, 'PEAR_Error') && count($group_list)) {
+                    foreach ($group_list as $group_id => $group_name) {
+                        $perm->addGroupPermission($group_id, $perms, false);
+                    }
+                }
+            }
+        }
+        $gallery->setPermission($perm, true);
+
+        /* Initial tags */
+        if (count($tags)) {
+            $gallery->setTags($tags);
+        }
+
+        return $gallery;
+    }
+
+    /**
+     * Check that a slug exists.
+     *
+     * @param string $slug  The slug name
+     *
+     * @return integer  The share_id the slug represents, or 0 if not found.
+     */
+    function slugExists($slug)
+    {
+        // An empty slug should never match.
+        if (!strlen($slug)) {
+            return 0;
+        }
+
+        $stmt = $this->_db->prepare('SELECT share_id FROM '
+            . $this->shares->_table . ' WHERE attribute_slug = ?');
+
+        if (is_a($stmt, 'PEAR_Error')) {
+            Horde::logMessage($stmt, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return 0;
+        }
+
+        $result = $stmt->execute($slug);
+        if (is_a($result, 'PEAR_Error')) {
+            Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+        }
+        if (!$result->numRows()) {
+            return 0;
+        }
+
+        $slug = $result->fetchRow();
+
+        $result->free();
+        $stmt->free();
+
+        return $slug[0];
+    }
+
+    /**
+     * Retrieve an Ansel_Gallery given the gallery's slug
+     *
+     * @param string $slug  The gallery slug
+     * @param array $overrides  An array of attributes that should be overridden
+     *                          when the gallery is returned.
+     *
+     * @return mixed  Ansel_Gallery object | PEAR_Error
+     */
+    function &getGalleryBySlug($slug, $overrides = array())
+    {
+        $id = $this->slugExists($slug);
+        if ($id) {
+            return $this->getGallery($id, $overrides);
+        } else {
+            return PEAR::raiseError(sprintf(_("Gallery %s not found."), $slug));
+        }
+     }
+
+    /**
+     * Retrieve an Ansel_Gallery given the share id
+     *
+     * @param integer $gallery_id  The share_id to fetch
+     * @param array $overrides     An array of attributes that should be
+     *                             overridden when the gallery is returned.
+     *
+     * @return mixed  Ansel_Gallery | PEAR_Error
+     */
+    function &getGallery($gallery_id, $overrides = array())
+    {
+        // avoid cache server hits
+        if (isset($this->galleries[$gallery_id]) && !count($overrides)) {
+            return $this->galleries[$gallery_id];
+        }
+
+       if (!count($overrides) && $GLOBALS['conf']['ansel_cache']['usecache'] &&
+           ($gallery = $GLOBALS['cache']->get('Ansel_Gallery' . $gallery_id, $GLOBALS['conf']['cache']['default_lifetime'])) !== false) {
+
+               $this->galleries[$gallery_id] = unserialize($gallery);
+
+               return $this->galleries[$gallery_id];
+       }
+
+       $result = &$this->shares->getShareById($gallery_id);
+       if (is_a($result, 'PEAR_Error')) {
+           return $result;
+       }
+       $this->galleries[$gallery_id] = &$result;
+
+       // Don't cache if we have overridden anything
+       if (!count($overrides)) {
+           if ($GLOBALS['conf']['ansel_cache']['usecache']) {
+               $GLOBALS['cache']->set('Ansel_Gallery' . $gallery_id, serialize($result));
+           }
+       } else {
+           foreach ($overrides as $key => $value) {
+               $this->galleries[$gallery_id]->set($key, $value, false);
+           }
+       }
+        return $this->galleries[$gallery_id];
+    }
+
+    /**
+     * Retrieve an array of Ansel_Gallery objects for the given slugs.
+     *
+     * @param array $slugs  The gallery slugs
+     *
+     * @return mixed  Array of Ansel_Gallery objects | PEAR_Error
+     */
+    function getGalleriesBySlugs($slugs)
+    {
+        $sql = 'SELECT share_id FROM ' . $this->shares->_table
+            . ' WHERE attribute_slug IN (' . str_repeat('?, ', count($slugs) - 1) . '?)';
+
+        $stmt = $this->shares->_db->prepare($sql);
+        if (is_a($stmt, 'PEAR_Error')) {
+            return $stmt;
+        }
+        $result = $stmt->execute($slugs);
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        }
+        $ids = array_values($result->fetchCol());
+        $shares = $this->shares->getShares($ids);
+
+        $stmt->free();
+        $result->free();
+
+        return $shares;
+    }
+
+    /**
+     * Retrieve an array of Ansel_Gallery objects for the requested ids
+     */
+    function getGalleries($ids)
+    {
+        return $this->shares->getShares($ids);
+    }
+
+    /**
+     * Empties a gallery of all images.
+     *
+     * @param Ansel_Gallery $gallery  The ansel gallery to empty.
+     */
+    function emptyGallery($gallery)
+    {
+        $images = $gallery->listImages();
+        foreach ($images as $image) {
+            // Pretend we are a stack so we don't update the images count
+            // for every image deletion, since we know the end result will
+            // be zero.
+            $gallery->removeImage($image, true);
+        }
+        $gallery->set('images', 0, true);
+
+        // Clear the OtherGalleries widget cache
+        if ($GLOBALS['conf']['ansel_cache']['usecache']) {
+            $GLOBALS['cache']->expire('Ansel_OtherGalleries' . $gallery->get('owner'));
+        }
+
+    }
+
+    /**
+     * Removes an Ansel_Gallery.
+     *
+     * @param Ansel_Gallery $gallery  The gallery to delete
+     *
+     * @return mixed  True || PEAR_Error
+     */
+    function removeGallery($gallery)
+    {
+        /* Get any children and empty them */
+        $children = $gallery->getChildren(null, true);
+        if (is_a($children, 'PEAR_Error')) {
+            return $children;
+        }
+        foreach ($children as $child) {
+            $this->emptyGallery($child);
+            $child->setTags(array());
+        }
+
+        /* Now empty the selected gallery of images */
+        $this->emptyGallery($gallery);
+
+        /* Clear all the tags. */
+        $gallery->setTags(array());
+
+        /* Get the parent, if it exists, before we delete the gallery. */
+        $parent = $gallery->getParent();
+        $id = $gallery->id;
+
+        /* Delete the gallery from storage */
+        $result = $this->shares->removeShare($gallery);
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        }
+
+        /* Expire the cache */
+        if ($GLOBALS['conf']['ansel_cache']['usecache']) {
+            $GLOBALS['cache']->expire('Ansel_Gallery' . $id);
+        }
+        unset($this->galleries[$id]);
+
+        /* See if we need to clear the has_subgalleries field */
+        if (is_a($parent, 'Ansel_Gallery')) {
+            if (!$parent->countChildren(PERMS_SHOW, false)) {
+                $parent->set('has_subgalleries', 0, true);
+
+                if ($GLOBALS['conf']['ansel_cache']['usecache']) {
+                    $GLOBALS['cache']->expire('Ansel_Gallery' . $parent->id);
+                }
+                unset($this->galleries[$id]);
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns the image corresponding to the given id.
+     *
+     * @param integer $id  The ID of the image to retrieve.
+     *
+     * @return Ansel_Image  The image object corresponding to the given name.
+     */
+    function &getImage($id)
+    {
+        if (isset($this->images[$id])) {
+            return $this->images[$id];
+        }
+
+        $q = $this->_db->prepare('SELECT ' . $this->_getImageFields() . ' FROM ansel_images WHERE image_id = ?');
+        if (is_a($q, 'PEAR_Error')) {
+            Horde::logMessage($q, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return $q;
+        }
+        $result = $q->execute((int)$id);
+        if (is_a($result, 'PEAR_Error')) {
+            Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return $result;
+        }
+        $image = $result->fetchRow(MDB2_FETCHMODE_ASSOC);
+        $q->free();
+        $result->free();
+        if (is_null($image)) {
+            return PEAR::raiseError(_("Photo not found"));
+        } elseif (is_a($image, 'PEAR_Error')) {
+            Horde::logMessage($image, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return $image;
+        } else {
+            $image['image_filename'] = Horde_String::convertCharset($image['image_filename'], $GLOBALS['conf']['sql']['charset']);
+            $image['image_caption'] = Horde_String::convertCharset($image['image_caption'], $GLOBALS['conf']['sql']['charset']);
+            $this->images[$id] = new Ansel_Image($image);
+
+            return $this->images[$id];
+        }
+    }
+
+    /**
+     * Returns the images corresponding to the given ids.
+     *
+     * @param array $ids  An array of image ids.
+     *
+     * @return array of Ansel_Image objects.
+     */
+    function getImages($ids, $preserve_order = false)
+    {
+        if (is_array($ids) && count($ids) > 0) {
+            $sql = 'SELECT ' . $this->_getImageFields() . ' FROM ansel_images WHERE image_id IN (';
+            $i = 1;
+            $cnt = count($ids);
+            foreach ($ids as $id) {
+                $sql .= (int)$id . (($i++ < $cnt) ? ',' : ');');
+            }
+
+            $images = $this->_db->query($sql);
+            if (is_a($images, 'PEAR_Error')) {
+                return $images;
+            } elseif ($images->numRows() == 0) {
+                $images->free();
+                return PEAR::raiseError(_("Photos not found"));
+            }
+
+            $return = array();
+            while ($image = $images->fetchRow(MDB2_FETCHMODE_ASSOC)) {
+                $image['image_filename'] = Horde_String::convertCharset($image['image_filename'], $GLOBALS['conf']['sql']['charset']);
+                $image['image_caption'] = Horde_String::convertCharset($image['image_caption'], $GLOBALS['conf']['sql']['charset']);
+                $return[$image['image_id']] = new Ansel_Image($image);
+                $this->images[(int)$image['image_id']] = &$return[$image['image_id']];
+            }
+            $images->free();
+
+            /* Need to get comment counts if comments are enabled */
+            $ccounts = $this->_getImageCommentCounts(array_keys($return));
+            if (!is_a($ccounts, 'PEAR_Error') && count($ccounts)) {
+                foreach ($return as $key => $image) {
+                    $return[$key]->commentCount = (!empty($ccounts[$key]) ? $ccounts[$key] : 0);
+                }
+            }
+
+            /* Preserve the order the ids were passed in) */
+            if ($preserve_order) {
+                foreach ($ids as $id) {
+                    $ordered[$id] = $return[$id];
+                }
+                return $ordered;
+            }
+            return $return;
+        } else {
+            return array();
+        }
+    }
+
+    function _getImageCommentCounts($ids)
+    {
+        global $conf, $registry;
+
+        /* Need to get comment counts if comments are enabled */
+        if (($conf['comments']['allow'] == 'all' || ($conf['comments']['allow'] == 'authenticated' && Horde_Auth::getAuth())) &&
+            $registry->hasMethod('forums/numMessagesBatch')) {
+
+            return $registry->call('forums/numMessagesBatch',
+                                   array($ids, 'ansel'));
+        }
+
+        return array();
+    }
+
+    /**
+     * Return a list of image ids of the most recently added images.
+     *
+     * @param array $galleries  An array of gallery ids to search in. If
+     *                          left empty, will search all galleries
+     *                          with PERMS_SHOW.
+     * @param integer $limit    The maximum number of images to return
+     * @param string $slugs     An array of gallery slugs.
+     * @param string $where     Additional where clause
+     *
+     * @return array An array of Ansel_Image objects
+     */
+    function getRecentImages($galleries = array(), $limit = 10, $slugs = array())
+    {
+        $results = array();
+
+        if (!count($galleries) && !count($slugs)) {
+            $sql = 'SELECT DISTINCT ' . $this->_getImageFields('i') . ' FROM ansel_images i, '
+            . str_replace('WHERE' , ' WHERE i.gallery_id = s.share_id AND (', substr($this->shares->_getShareCriteria(Horde_Auth::getAuth()), 5)) . ')';
+        } elseif (!count($slugs) && count($galleries)) {
+            // Searching by gallery_id
+            $sql = 'SELECT ' . $this->_getImageFields() . ' FROM ansel_images '
+                   . 'WHERE gallery_id IN ('
+                   . str_repeat('?, ', count($galleries) - 1) . '?) ';
+        } elseif (count($slugs)) {
+            // Searching by gallery_slug so we need to join the share table
+            $sql = 'SELECT ' . $this->_getImageFields() . ' FROM ansel_images LEFT JOIN '
+                . $this->shares->_table . ' ON ansel_images.gallery_id = '
+                . $this->shares->_table . '.share_id ' . 'WHERE attribute_slug IN ('
+                . str_repeat('?, ', count($slugs) - 1) . '?) ';
+        } else {
+            return array();
+        }
+
+        $sql .= ' ORDER BY image_uploaded_date DESC LIMIT ' . (int)$limit;
+        $query = $this->_db->prepare($sql);
+        if (is_a($query, 'PEAR_Error')) {
+            return $query;
+        }
+
+        if (count($slugs)) {
+            $images = $query->execute($slugs);
+        } else {
+            $images = $query->execute($galleries);
+        }
+        $query->free();
+        if (is_a($images, 'PEAR_Error')) {
+            return $images;
+        } elseif ($images->numRows() == 0) {
+            return array();
+        }
+
+        while ($image = $images->fetchRow(MDB2_FETCHMODE_ASSOC)) {
+            $image['image_filename'] = Horde_String::convertCharset($image['image_filename'], $GLOBALS['conf']['sql']['charset']);
+            $image['image_caption'] = Horde_String::convertCharset($image['image_caption'], $GLOBALS['conf']['sql']['charset']);
+            $results[] = new Ansel_Image($image);
+        }
+
+        $images->free();
+        return $results;
+    }
+
+    /**
+     * Check if a gallery exists. Need to do this here instead of Horde_Share
+     * since Horde_Share::exists() takes a share_name, not a share_id plus we
+     * might also be checking by gallery_slug and this is more efficient than
+     * a listShares() call for one gallery.
+     *
+     * @param integer $gallery_id  The gallery id
+     * @param string  $slug        The gallery slug
+     *
+     * @return mixed  true | false | PEAR_Error
+     */
+    function galleryExists($gallery_id, $slug = null)
+    {
+        if (empty($slug)) {
+            return (bool)$this->_db->queryOne(
+                'SELECT COUNT(share_id) FROM ' . $this->shares->_table
+                . ' WHERE share_id = ' . (int)$gallery_id);
+        } else {
+            return (bool)$this->slugExists($slug);
+        }
+    }
+
+   /**
+    * Return a list of categories containing galleries with the given
+    * permissions for the current user.
+    *
+    * @param integer $perm   The level of permissions required.
+    * @param integer $from   The gallery to start listing at.
+    * @param integer $count  The number of galleries to return.
+    *
+    * @return mixed  List of categories | PEAR_Error
+    */
+    function listCategories($perm = PERMS_SHOW, $from = 0, $count = 0)
+    {
+        require_once 'Horde/Array.php';
+
+        $sql = 'SELECT DISTINCT attribute_category FROM '
+               . $this->shares->_table;
+        $results = $this->shares->_db->query($sql);
+        if (is_a($results, 'PEAR_Error')) {
+            return $results;
+        }
+        $all_categories = $results->fetchCol('attribute_category');
+        $results->free();
+        if (count($all_categories) < $from) {
+            return array();
+        } else {
+            $categories = array();
+            foreach ($all_categories as $category) {
+                $categories[] = Horde_String::convertCharset(
+                    $category, $GLOBALS['conf']['sql']['charset']);
+            }
+            if ($count > 0) {
+                return array_slice($categories, $from, $count);
+            } else {
+                return array_slice($categories, $from);
+            }
+        }
+    }
+
+    function countCategories($perms = PERMS_SHOW)
+    {
+        return count($this->listCategories($perms));
+    }
+
+   /**
+    * Return the count of galleries that the user has specified permissions to
+    * and that match any of the requested attributes.
+    *
+    * @param string  $userid       The user to check access for.
+    * @param integer $perm         The level of permissions to require for a
+    *                              gallery to return it.
+    * @param mixed   $attributes   Restrict the galleries counted to those
+    *                              matching $attributes. An array of
+    *                              attribute/values pairs or a gallery owner
+    *                              username.
+    * @param string  $parent       The parent share to start counting at.
+    * @param boolean $allLevels    Return all levels, or just the direct
+    *                              children of $parent? Defaults to all levels.
+    */
+    function countGalleries($userid, $perm = PERMS_SHOW, $attributes = null,
+                            $parent = null, $allLevels = true)
+    {
+        static $counts;
+
+        if (is_a($parent, 'Ansel_Gallery')) {
+            $parent_id = $parent->getId();
+        } else {
+            $parent_id = $parent;
+        }
+
+        $key = "$userid,$perm,$parent_id,$allLevels"
+               . serialize($attributes);
+        if (isset($counts[$key])) {
+            return $counts[$key];
+        }
+
+        $count = $this->shares->countShares($userid, $perm, $attributes,
+                                            $parent, $allLevels);
+
+        $counts[$key] = $count;
+
+        return $count;
+    }
+
+   /**
+    * Retrieves the current user's gallery list from storage.
+    *
+    * @param integer $perm         The level of permissions to require for a
+    *                              gallery to return it.
+    * @param mixed   $attributes   Restrict the galleries counted to those
+    *                              matching $attributes. An array of
+    *                              attribute/values pairs or a gallery owner
+    *                              username.
+    * @param mixed   $parent       The parent gallery to start listing at.
+    *                              (Ansel_Gallery, gallery id or null)
+    * @param boolean $allLevels    Return all levels, or just the direct
+    *                              children of $parent?
+    * @param integer $from         The gallery to start listing at.
+    * @param integer $count        The number of galleries to return.
+    * @param string  $sort_by      The field to order the results by.
+    * @param integer $direction    Sort direction:
+    *                               0 - ascending
+    *                               1 - descending
+    *
+    * @return mixed An array of Ansel_Gallery objects | PEAR_Error
+    */
+    function listGalleries($perm = PERMS_SHOW,
+                           $attributes = null,
+                           $parent = null,
+                           $allLevels = true,
+                           $from = 0,
+                           $count = 0,
+                           $sort_by = null,
+                           $direction = 0)
+    {
+        return $this->shares->listShares(Horde_Auth::getAuth(), $perm, $attributes,
+                                         $from, $count, $sort_by, $direction,
+                                         $parent, $allLevels);
+    }
+
+    /**
+     * Retrieve json data for an arbitrary list of image ids, not necessarily
+     * from the same gallery.
+     *
+     * @param array $images        An array of image ids
+     * @param string $style        A named gallery style to force if requesting
+     *                             pretty thumbs.
+     * @param boolean $full        Generate full urls
+     * @param string $image_view   Which image view to use? screen, thumb etc..
+     * @param boolean $view_links  Include links to the image view
+     *
+     * @return string  The json data || PEAR_Error
+     */
+    function getImageJson($images, $style = null, $full = false,
+                          $image_view = 'mini', $view_links = false)
+    {
+        $galleries = array();
+        if (is_null($style)) {
+            $style = 'ansel_default';
+        }
+
+        $json = array();
+
+        foreach ($images as $id) {
+            $image = $this->getImage($id);
+            if (!is_a($image, 'PEAR_Error')) {
+                $gallery_id = abs($image->gallery);
+
+                if (empty($galleries[$gallery_id])) {
+                    $galleries[$gallery_id]['gallery'] = $GLOBALS['ansel_storage']->getGallery($gallery_id);
+                    if (is_a($galleries[$gallery_id]['gallery'], 'PEAR_Error')) {
+                        return $galleries[$gallery_id];
+                    }
+                }
+
+                // Any authentication that needs to take place for any of the
+                // images included here MUST have already taken place or the
+                // image will not be incldued in the output.
+                if (!isset($galleries[$gallery_id]['perm'])) {
+                    $galleries[$gallery_id]['perm'] =
+                        ($galleries[$gallery_id]['gallery']->hasPermission(Horde_Auth::getAuth(), PERMS_READ) &&
+                         $galleries[$gallery_id]['gallery']->isOldEnough() &&
+                         !$galleries[$gallery_id]['gallery']->hasPasswd());
+                }
+
+                if ($galleries[$gallery_id]['perm']) {
+                    $data = array(Ansel::getImageUrl($image->id, $image_view, $full, $style),
+                        htmlspecialchars($image->filename, ENT_COMPAT, Horde_Nls::getCharset()),
+                        Horde_Text_Filter::filter($image->caption, 'text2html', array('parselevel' => Horde_Text_Filter_Text2html::MICRO_LINKURL)),
+                        $image->id,
+                        0);
+
+                    if ($view_links) {
+                        $data[] = Ansel::getUrlFor('view',
+                            array('gallery' => $image->gallery,
+                                  'image' => $image->id,
+                                  'view' => 'Image',
+                                  'slug' => $galleries[$gallery_id]['gallery']->get('slug')),
+                            $full);
+
+                        $data[] = Ansel::getUrlFor('view',
+                            array('gallery' => $image->gallery,
+                                  'slug' => $galleries[$gallery_id]['gallery']->get('slug'),
+                                  'view' => 'Gallery'),
+                            $full);
+                    }
+
+                    $json[] = $data;
+                }
+            }
+        }
+
+        if (count($json)) {
+            require_once 'Horde/Serialize.php';
+            return Horde_Serialize::serialize($json, Horde_Serialize::JSON, Horde_Nls::getCharset());
+        } else {
+            return '';
+        }
+    }
+
+    /**
+     * Returns a random Ansel_Gallery from a list fitting the search criteria.
+     *
+     * @see Ansel_Storage::listGalleries()
+     */
+    function getRandomGallery($perm = PERMS_SHOW, $attributes = null,
+                              $parent = null, $allLevels = true)
+    {
+        $num_galleries = $this->countGalleries(Horde_Auth::getAuth(), $perm,
+                                               $attributes, $parent,
+                                               $allLevels);
+        if (!$num_galleries) {
+            return $num_galleries;
+        }
+
+        $galleries = $this->listGalleries($perm, $attributes, $parent,
+                                          $allLevels,
+                                          rand(0, $num_galleries - 1),
+                                          1);
+        $gallery = array_pop($galleries);
+        return $gallery;
+    }
+
+    /**
+     * Lists a slice of the image ids in the given gallery.
+     *
+     * @param integer $gallery_id  The gallery to list from.
+     * @param integer $from        The image to start listing.
+     * @param integer $count       The numer of images to list.
+     * @param mixed $fields        The fields to return (either an array of
+     *                             fileds or a single string).
+     * @param string $where        A SQL where clause ($gallery_id will be
+     *                             ignored if this is non-empty).
+     * @param mixed $sort          The field(s) to sort by.
+     *
+     * @return mixed  An array of image_ids | PEAR_Error
+     */
+    function listImages($gallery_id, $from = 0, $count = 0,
+                        $fields = 'image_id', $where = '', $sort = 'image_sort')
+    {
+        if (is_array($fields)) {
+            $field_count = count($fields);
+            $fields = implode(', ', $fields);
+        } elseif ($fields == '*') {
+            // The count is not important, as long as it's > 1
+            $field_count = 2;
+        } else {
+            $field_count = substr_count($fields, ',') + 1;
+        }
+
+        if (is_array($sort)) {
+            $sort = implode(', ', $sort);
+        }
+
+        if (!empty($where)) {
+            $query_where = 'WHERE ' . $where;
+        } else {
+            $query_where = 'WHERE gallery_id = ' . $gallery_id;
+        }
+        $this->_db->setLimit($count, $from);
+        $sql = 'SELECT ' . $fields . ' FROM ansel_images ' . $query_where . ' ORDER BY ' . $sort;
+        Horde::logMessage('Query by Ansel_Storage::listImages: ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+        $results = $this->_db->query('SELECT ' . $fields . ' FROM ansel_images '
+            . $query_where . ' ORDER BY ' . $sort);
+        if (is_a($results, 'PEAR_Error')) {
+            return $results;
+        }
+        if ($field_count > 1) {
+            return $results->fetchAll(MDB2_FETCHMODE_ASSOC, true, true, false);
+        } else {
+            return $results->fetchCol();
+        }
+    }
+
+    /**
+     * Return images' geolocation data.
+     *
+     * @param array $image_ids  An array of image_ids to look up.
+     * @param integer $gallery  A gallery id. If this is provided, will return
+     *                          all images in the gallery that have geolocation
+     *                          data ($image_ids would be ignored).
+     *
+     * @return mixed An array of geodata || PEAR_Error
+     */
+    function getImagesGeodata($image_ids = array(), $gallery = null)
+    {
+        if ((!is_array($image_ids) || count($image_ids) == 0) && empty($gallery)) {
+            return array();
+        }
+
+        if (!empty($gallery)) {
+            $where = 'gallery_id = ' . (int)$gallery . ' AND LENGTH(image_latitude) > 0';
+        } elseif (count($image_ids) > 0) {
+            $where = 'image_id IN(' . implode(',', $image_ids) . ') AND LENGTH(image_latitude) > 0';
+        } else {
+            return array();
+        }
+
+        return $this->listImages(0, 0, 0, array('image_id as id', 'image_id', 'image_latitude', 'image_longitude', 'image_location'), $where);
+    }
+
+    /**
+     * Like getRecentImages, but returns geotag data for the most recently added
+     * images from the current user. Useful for providing images to help locate
+     * images at the same place.
+     */
+    function getRecentImagesGeodata($user = null, $start = 0, $count = 8)
+    {
+        $galleries = $this->listGalleries('PERMS_EDIT', $user);
+        $where = 'gallery_id IN(' . implode(',', array_keys($galleries)) . ') AND LENGTH(image_latitude) > 0 GROUP BY image_latitude, image_longitude';
+        return $this->listImages(0, $start, $count, array('image_id as id', 'image_id', 'gallery_id', 'image_latitude', 'image_longitude', 'image_location'), $where, 'image_geotag_date DESC');
+    }
+
+    function searchLocations($search = '')
+    {
+        $sql = 'SELECT DISTINCT image_location, image_latitude, image_longitude'
+            . ' FROM ansel_images WHERE image_location LIKE "' . $search . '%"';
+        $results = $this->_db->query($sql);
+        if (is_a($results, 'PEAR_Error')) {
+            return $results;
+        }
+
+        return $results->fetchAll(MDB2_FETCHMODE_ASSOC, true, true, false);
+    }
+
+    /**
+     * Helper function to get a string of field names
+     *
+     * @return string
+     */
+    function _getImageFields($alias = '')
+    {
+        $fields = array('image_id', 'gallery_id', 'image_filename', 'image_type',
+                        'image_caption', 'image_uploaded_date', 'image_sort',
+                        'image_faces', 'image_original_date', 'image_latitude',
+                        'image_longitude', 'image_location', 'image_geotag_date');
+        if (!empty($alias)) {
+            foreach ($fields as $field) {
+                $new[] = $alias . '.' . $field;
+            }
+            return implode(', ', $new);
+        }
+
+        return implode(', ', $fields);
+    }
+
+}
diff --git a/ansel/lib/Block/cloud.php b/ansel/lib/Block/cloud.php
new file mode 100644 (file)
index 0000000..afa4b65
--- /dev/null
@@ -0,0 +1,60 @@
+<?php
+
+$block_name = _("Tag Cloud");
+
+/**
+ * Display Tag Cloud
+ *
+ * $Horde: ansel/lib/Block/cloud.php,v 1.15 2009/01/06 17:48:52 jan Exp $
+ *
+ * Copyright 2007-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author  Michael Rubinsky <mrubinsk@horde.org>
+ * @package Horde_Block
+ */
+class Horde_Block_ansel_cloud extends Horde_Block {
+
+    var $_app = 'ansel';
+
+    function _params()
+    {
+        $params = array('count' => array(
+                            'name' => _("Number of tags to display"),
+                            'type' => 'int',
+                            'default' => 20));
+        return $params;
+    }
+
+    function _title()
+    {
+        return _("Tag Cloud");
+    }
+
+    function _content()
+    {
+        require_once 'Horde/UI/TagCloud.php';
+        require_once dirname(__FILE__) . '/../base.php';
+        require_once ANSEL_BASE . '/lib/Tags.php';
+
+        global $registry;
+
+        /* Get the tags */
+        $tags = Ansel_Tags::listTagInfo(null, $this->_params['count']);
+        if (count($tags)) {
+            $cloud = new Horde_UI_TagCloud();
+            foreach ($tags as $id => $tag) {
+                $link = Ansel::getUrlFor('view', array('view' => 'Results',
+                                                       'tag' => $tag['tag_name']));
+                $cloud->addElement($tag['tag_name'], $link, $tag['total']);
+            }
+            $html = $cloud->buildHTML();
+        } else {
+            $html = '';
+        }
+        return $html;
+    }
+
+}
diff --git a/ansel/lib/Block/gallery.php b/ansel/lib/Block/gallery.php
new file mode 100644 (file)
index 0000000..c59d682
--- /dev/null
@@ -0,0 +1,155 @@
+<?php
+
+$block_name = _("Gallery");
+
+/**
+ * This file provides a selected Ansel gallery through the Horde_Blocks, by
+ * extending the Horde_Blocks class.
+ *
+ * $Horde: ansel/lib/Block/gallery.php,v 1.56 2009/07/30 18:02:14 mrubinsk Exp $
+ *
+ * Copyright 2007-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author  Duck <Duck@obla.net>
+ * @author  Marcus Ryan <marcus@horde.org>
+ * @package Horde_Block
+ */
+class Horde_Block_ansel_gallery extends Horde_Block {
+
+    var $_app = 'ansel';
+    var $_gallery = null;
+
+    function _params()
+    {
+        require_once dirname(__FILE__) . '/../base.php';
+
+        $params = array('gallery' => array(
+                            'name' => _("Gallery"),
+                            'type' => 'enum',
+                            'default' => '__random',
+                            'values' => array('__random' => _("Random gallery"))),
+                        'perpage' => array(
+                            'name' => _("Maximum number of photos to display (0 means unlimited)"),
+                            'type' => 'int',
+                            'default' => 20),
+                        'use_lightbox' => array(
+                            'name' => _("Use a lightbox to view photos"),
+                            'type' => 'checkbox',
+                            'default' => true));
+
+        if ($GLOBALS['ansel_storage']->countGalleries(Horde_Auth::getAuth(), PERMS_READ) < $GLOBALS['conf']['gallery']['listlimit']) {
+            foreach ($GLOBALS['ansel_storage']->listGalleries() as $gal) {
+                $params['gallery']['values'][$gal->id] = $gal->get('name');
+            }
+        }
+
+        return $params;
+    }
+
+    function _title()
+    {
+        $gallery = $this->_getGallery();
+        if (is_a($gallery, 'PEAR_Error')) {
+            return Horde::link(Ansel::getUrlFor('view', array('view' => 'List'),
+                                                true)) . _("Gallery") . '</a>';
+        }
+
+        // Build the gallery name.
+        if (isset($this->_params['gallery']) &&
+            $this->_params['gallery'] == '__random') {
+            $name = _("Random Gallery") . ': ' . $gallery->get('name');
+        } else {
+            $name = $gallery->get('name');
+        }
+
+        $style = $gallery->getStyle();
+        $viewurl = Ansel::getUrlFor('view',
+            array('view' => 'Gallery',
+                  'gallery' => $gallery->id,
+                  'slug' => $gallery->get('slug')),
+            true);
+        return Horde::link($viewurl)
+               . @htmlspecialchars($name, ENT_COMPAT, Horde_Nls::getCharset())
+               . '</a>';
+
+    }
+    function _content()
+    {
+        $gallery = $this->_getGallery();
+        if (is_a($gallery, 'PEAR_Error')) {
+            return $gallery->getMessage();
+        }
+
+        $params = array('gallery_id' => $gallery->id,
+                        'count' => $this->_params['perpage']);
+        if (!empty($this->_params['use_lightbox'])) {
+            $params['lightbox'] = true;
+        }
+
+        $html = Ansel::embedCode($params);
+
+        // Be nice to people with <noscript>
+        $style = $gallery->getStyle();
+        $viewurl = Ansel::getUrlFor('view', array('view' => 'Gallery',
+                                                  'gallery' => $gallery->id,
+                                                  'slug' => $gallery->get('slug')),
+                                    true);
+        $html .= '<noscript>';
+        $html .= Horde::link($viewurl, sprintf(_("View %s"),
+                            $gallery->get('name')));
+
+        if ($iid = $gallery->getDefaultImage('ansel_default') &&
+            $gallery->hasPermission(Horde_Auth::getAuth(), PERMS_READ)) {
+
+            $html .= '<img src="' . Ansel::getImageUrl($gallery->getDefaultImage('ansel_default'), 'thumb', true) . '" alt="' . htmlspecialchars($gallery->get('name')) . '" />';
+        } else {
+            $html .= Horde::img('thumb-error.png');
+        }
+
+        return $html . '</a></noscript>';
+    }
+
+    function _getGallery($retry = false)
+    {
+        require_once dirname(__FILE__) . '/../base.php';
+
+        // Make sure we haven't already selected a gallery.
+        if (is_a($this->_gallery, 'Ansel_Gallery')) {
+            return $this->_gallery;
+        }
+
+        // Get the gallery object and cache it.
+        if (isset($this->_params['gallery']) && $this->_params['gallery'] != '__random') {
+            $this->_gallery = $GLOBALS['ansel_storage']->getGallery($this->_params['gallery']);
+        } else {
+            $this->_gallery = $GLOBALS['ansel_storage']->getRandomGallery();
+        }
+
+        // Protect at least a little bit against getting an empty gallery. We
+        // can't just loop until we get one with images since it's possible we
+        // actually don't *have* any with images yet.
+        if ($this->_params['gallery'] == '__random' &&
+            !empty($this->_gallery) &&
+            !$this->_gallery->countImages() &&
+            $this->_gallery->hasSubGalleries() && !$retry) {
+
+            $this->_gallery = null;
+            $this->_gallery = $this->_getGallery(true);
+        }
+
+        if (empty($this->_gallery)) {
+            return PEAR::raiseError(_("Gallery does not exist."));
+        } elseif (is_a($this->_gallery, 'PEAR_Error') ||
+                  !$this->_gallery->hasPermission(Horde_Auth::getAuth(), PERMS_SHOW) ||
+                  !$this->_gallery->isOldEnough() || $this->_gallery->hasPasswd()) {
+            return PEAR::raiseError(_("Access denied viewing this gallery."));
+        }
+
+        // Return the gallery.
+        return $this->_gallery;
+    }
+
+}
diff --git a/ansel/lib/Block/my_galleries.php b/ansel/lib/Block/my_galleries.php
new file mode 100644 (file)
index 0000000..b45a582
--- /dev/null
@@ -0,0 +1,90 @@
+<?php
+
+$block_name = _("My Galleries");
+
+/**
+ * Display summary information on top level galleries.
+ *
+ * $Horde: ansel/lib/Block/my_galleries.php,v 1.28 2009/07/09 08:17:49 slusarz Exp $
+ *
+ * Copyright 2007-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author  Michael Rubinsky <mrubinsk@horde.org>
+ * @package Horde_Block
+ */
+class Horde_Block_ansel_my_galleries extends Horde_Block {
+
+    var $_app = 'ansel';
+
+    function _params()
+    {
+        $params = array('limit' => array(
+                            'name' => _("Maximum number of galleries"),
+                            'type' => 'int',
+                            'default' => 0));
+        return $params;
+    }
+
+    function _title()
+    {
+        return Horde::link(
+            Ansel::getUrlFor('view',array('groupby' => 'owner',
+                                          'owner' => Horde_Auth::getAuth(),
+                                          'view' => 'List')))
+            . _("My Galleries") . '</a>';
+    }
+
+    function _content()
+    {
+        /* Get the top level galleries */
+        $galleries = $GLOBALS['ansel_storage']->listGalleries(
+            PERMS_EDIT, Horde_Auth::getAuth(), null, false, 0,
+            empty($this->_params['limit']) ? 0 : $this->_params['limit'],
+            'last_modified', 1);
+
+        if (is_a($galleries, 'PEAR_Error')) {
+            return $galleries->getMessage();
+        }
+
+        $preview_url = Horde::applicationUrl('preview.php');
+        $header = array(_("Gallery Name"), _("Last Modified"), _("Photo Count"));
+        $html = <<<HEADER
+<div id="ansel_preview"></div>
+<script type="text/javascript">
+function previewImageMg(e, image_id)
+{
+    $('ansel_preview').style.left = Event.pointerX(e) + 'px';
+    $('ansel_preview').style.top = Event.pointerY(e) + 'px';
+    new Ajax.Updater({success: 'ansel_preview'}, '$preview_url', {method: 'post', parameters: '?image=' + image_id, onsuccess: $('ansel_preview').show()});
+}
+</script>
+<table class="linedRow" cellspacing="0" style="width:100%">
+ <thead><tr class="item nowrap">
+  <th class="item leftAlign">$header[0]</th>
+  <th class="item leftAlign">$header[1]</th>
+  <th class="item leftAlign">$header[2]</th>
+ </tr></thead>
+ <tbody>
+HEADER;
+
+        foreach ($galleries as $gallery) {
+            $style = $gallery->getStyle();
+            $url = Ansel::getUrlFor('view', array('view' => 'Gallery',
+                                                  'slug' => $gallery->get('slug'),
+                                                  'gallery' => $gallery->id),
+                                    true);
+            $html .= '<tr><td>'
+                . Horde::link($url, '', '', '', '', '', '', array('onmouseout' => '$("ansel_preview").hide();$("ansel_preview").update("");', 'onmouseover' => 'previewImageMg(event, ' . $gallery->getDefaultImage('ansel_default') . ');'))
+                . @htmlspecialchars($gallery->get('name'), ENT_COMPAT, Horde_Nls::getCharset()) . '</a></td><td>'
+                . strftime($GLOBALS['prefs']->getValue('date_format'), $gallery->get('last_modified'))
+                . '</td><td>' . (int)$gallery->countImages(true) . '</td></tr>';
+        }
+        $html .= '</tbody></table>';
+
+        return $html;
+    }
+
+}
diff --git a/ansel/lib/Block/random_photo.php b/ansel/lib/Block/random_photo.php
new file mode 100644 (file)
index 0000000..306f86a
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+
+$block_name = _("Random photo");
+
+/**
+ * This file provides a random photo through the Horde_Blocks, by extending
+ * the Horde_Blocks class.
+ *
+ * $Horde: ansel/lib/Block/random_photo.php,v 1.39 2009/07/22 21:08:09 mrubinsk Exp $
+ *
+ * Copyright 2003-2007 Duck <duck@obla.net>
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author  Duck <Duck@obla.net>
+ * @author  Ben Chavet <ben@horde.org>
+ * @package Horde_Block
+ */
+class Horde_Block_ansel_random_photo extends Horde_Block {
+
+    var $_app = 'ansel';
+
+    var $updateable = true;
+
+    function _title()
+    {
+        return _("Random photo");
+    }
+
+    function _content()
+    {
+        require_once dirname(__FILE__) . '/../base.php';
+
+        $gallery = $GLOBALS['ansel_storage']->getRandomGallery();
+        if (!$gallery) {
+            return _("There are no photo galleries available.");
+        }
+        $imagelist = $gallery->listImages(rand(0, $gallery->countImages() - 1), 1);
+        if (empty($imagelist)) {
+            return '';
+        }
+        $imageId = $imagelist[0];
+        $viewurl = Ansel::getUrlFor('view', array('gallery' => $gallery->id,
+                                                  'slug' => $gallery->get('slug'),
+                                                  'image' => $imageId,
+                                                  'view' => 'Image'), true);
+
+        if ($gallery->isOldEnough() && !$gallery->hasPasswd()) {
+            $img = '<img src="' . Ansel::getImageUrl($imageId, 'thumb', true) . '" alt="[random photo]" />';
+        } else {
+            $img = Horde::img(
+                $GLOBALS['registry']->getImageDir() . '/thumb-error.png', '',
+                '', '');
+        }
+        return Horde::link($viewurl, _("View Photo")) . $img . '</a>';
+    }
+
+}
diff --git a/ansel/lib/Block/recent_comments.php b/ansel/lib/Block/recent_comments.php
new file mode 100644 (file)
index 0000000..b57b79b
--- /dev/null
@@ -0,0 +1,188 @@
+<?php
+
+if ($GLOBALS['registry']->call('images/hasComments') &&
+    $GLOBALS['registry']->hasMethod('forums/getThreadsBatch')) {
+    $block_name = _("Recent Photo Comments");
+}
+
+/**
+ * Display most recent image comments for galleries.
+ *
+ * $Horde: ansel/lib/Block/recent_comments.php,v 1.34 2009/07/09 08:17:49 slusarz Exp $
+ *
+ * Copyright 2007-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author  Michael Rubinsky <mrubinsk@horde.org>
+ * @package Horde_Block
+ */
+class Horde_Block_ansel_recent_comments extends Horde_Block {
+
+    var $_app = 'ansel';
+    var $_gallery = null;
+
+    function _params()
+    {
+        require_once dirname(__FILE__) . '/../base.php';
+
+        $params = array('gallery' => array(
+                            'name' => _("Gallery"),
+                            'type' => 'enum',
+                            'default' => '__random',
+                            'values' => array('all' => 'All')));
+
+        if ($GLOBALS['ansel_storage']->countGalleries(Horde_Auth::getAuth(), PERMS_READ) < $GLOBALS['conf']['gallery']['listlimit']) {
+            foreach ($GLOBALS['ansel_storage']->listGalleries(PERMS_READ) as $id => $gal) {
+                $params['gallery']['values'][$id] = $gal->get('name');
+            }
+        }
+
+        return $params;
+    }
+
+    function _title()
+    {
+        require_once dirname(__FILE__) . '/../base.php';
+
+        if ($this->_params['gallery'] != 'all') {
+            $gallery = $this->_getGallery();
+            if (is_a($gallery, 'PEAR_Error')) {
+                return Horde::link(
+                    Ansel::getUrlFor('view', array('view' => 'List'), true))
+                    . _("Gallery") . '</a>';
+            }
+            // Build the gallery name.
+            if (isset($this->_params['gallery'])) {
+                $name = @htmlspecialchars($gallery->get('name'), ENT_COMPAT, Horde_Nls::getCharset());
+            }
+            $style = $gallery->getStyle();
+            $viewurl = Ansel::getUrlFor('view',
+                                        array('gallery' => $gallery->id,
+                                              'view' => 'Gallery',
+                                              'slug' => $gallery->get('slug')),
+                                        true);
+        } else {
+            $viewurl = Ansel::getUrlFor('view', array('view' => 'List'), true);
+            $name = _("All Galleries");
+        }
+
+        return sprintf(_("Recent Comments In %s"), Horde::link($viewurl) . $name . '</a>');
+    }
+
+    function _content()
+    {
+        global $registry, $ansel_storage;
+
+        if ($this->_params['gallery'] == 'all') {
+            $threads = $registry->call('forums/list', array(0, 'ansel'));
+            $image_ids = array();
+            foreach ($threads as $thread) {
+                $image_ids[] = $thread['forum_name'];
+            }
+        } else {
+            $gallery = $this->_getGallery();
+            if (is_a($gallery, 'PEAR_Error')) {
+                return $gallery->getMessage();
+            }
+            $results = array();
+            $image_ids = $gallery->listImages();
+        }
+        $results = array();
+        $threads = $registry->call('forums/getThreadsBatch', array($image_ids, 'message_timestamp', 1, false, 'ansel', null, 0, 10));
+        foreach ($threads as $image_id => $messages) {
+            foreach ($messages as $message) {
+                $message['image_id'] = $image_id;
+                $results[] = $message;
+            }
+        }
+
+        $results = $this->_asortbyindex($results, 'message_timestamp');
+        $html = '<div id="ansel_preview"></div>'
+            . '<script type="text/javascript">'
+            . 'function previewImage(e, image_id) {$(\'ansel_preview\').style.left = Event.pointerX(e) + \'px\'; $(\'ansel_preview\').style.top = Event.pointerY(e) + \'px\';new Ajax.Updater({success:\'ansel_preview\'}, \'' . Horde::applicationUrl('preview.php') . '\', {method: \'post\', parameters:\'?image=\' + image_id, onsuccess:$(\'ansel_preview\').show()});}'
+            . '</script>'
+            . '<table class="linedRow" cellspacing="0" style="width:100%"><thead><tr class="item nowrap"><th class="item leftAlign">' . _("Date") . '</th><th class="item leftAlign">' . _("Image") . '</th><th class="item leftAlign">' . _("Subject") . '</th><th class="item leftAlign">' . _("By") . '</th></tr></thead><tbody>';
+
+        foreach ($results as $comment) {
+            $image = &$ansel_storage->getImage($comment['image_id']);
+            if (!is_a($image, 'PEAR_Error')) {
+                $url = Ansel::getUrlFor('view',
+                                        array('view' => 'Image',
+                                              'gallery' => abs($image->gallery),
+                                              'image' => $comment['image_id']),
+                                        true);
+                $caption = substr($image->caption, 0, 30);
+                if (strlen($image->caption) > 30) {
+                    $caption .= '...';
+                }
+                $html .= '<tr><td>'
+                    . strftime('%x', $comment['message_timestamp'])
+                    . '</td><td class="nowrap">'
+                    . Horde::link($url, '', '', '', '', '', '', array('onmouseout' => '$("ansel_preview").hide();$("ansel_preview").update("");', 'onmouseover' => 'previewImage(event, ' . $comment['image_id'] . ');'))
+                    . ($image->caption == '' ? $image->filename : $caption)
+                    . '</a></td><td class="nowrap">'
+                    . $comment['message_subject'] . '</td><td class="nowrap">'
+                    . $comment['message_author'] . '</td></tr>';
+            }
+        }
+        $html .= '</tbody></table>';
+
+        return $html;
+    }
+
+    function _getGallery()
+    {
+        // Make sure we haven't already selected a gallery.
+        if (is_a($this->_gallery, 'Ansel_Gallery')) {
+            return $this->_gallery;
+        }
+
+        // Get the gallery object and cache it.
+        if (isset($this->_params['gallery']) &&
+            $this->_params['gallery'] != '__random') {
+            $this->_gallery = $GLOBALS['ansel_storage']->getGallery($this->_params['gallery']);
+        } else {
+            $this->_gallery =$GLOBALS['ansel_storage']->getRandomGallery();
+        }
+
+        if (empty($this->_gallery)) {
+            return PEAR::raiseError(_("Gallery does not exist."));
+        } elseif (is_a($this->_gallery, 'PEAR_Error') ||
+                  !$this->_gallery->hasPermission(Horde_Auth::getAuth(), PERMS_READ)) {
+            return PEAR::raiseError(_("Access denied viewing this gallery."));
+        }
+
+        // Return a reference to the gallery.
+        return $this->_gallery;
+    }
+
+    /**
+     * Numerically sorts an associative array by a specific index.
+     *
+     * Designed to ease sorting stories by a timestamp when combining seperate
+     * channels into one array.
+     *
+     * @param array  $sortarray  The array to sort.
+     * @param string $index      The index that contains the numerical value
+     *                           to sort by.
+     */
+    function _asortbyindex ($sortarray, $index) {
+        $lastindex = count ($sortarray) - 1;
+        for ($subindex = 0; $subindex < $lastindex; $subindex++) {
+            $lastiteration = $lastindex - $subindex;
+            for ($iteration = 0; $iteration < $lastiteration; $iteration++) {
+                $nextchar = 0;
+                if ($sortarray[$iteration][$index] < $sortarray[$iteration + 1][$index]) {
+                    $temp = $sortarray[$iteration];
+                    $sortarray[$iteration] = $sortarray[$iteration + 1];
+                    $sortarray[$iteration + 1] = $temp;
+                }
+            }
+        }
+
+        return ($sortarray);
+    }
+
+}
diff --git a/ansel/lib/Block/recent_faces.php b/ansel/lib/Block/recent_faces.php
new file mode 100644 (file)
index 0000000..a7f3019
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+if (!empty($GLOBALS['conf']['faces']['driver'])) {
+    $block_name = _("Recent faces");
+}
+
+/**
+ * This file provides a recent faces through the Horde_Blocks, by extending
+ * the Horde_Blocks class.
+ *
+ * $Horde: ansel/lib/Block/recent_faces.php,v 1.3 2009/07/13 17:18:39 mrubinsk Exp $
+ *
+ * Copyright 2007-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author  Duck <Duck@obala.net>
+ * @package Horde_Block
+ */
+class Horde_Block_ansel_recent_faces extends Horde_Block {
+
+    var $_app = 'ansel';
+
+    function _params()
+    {
+        return array('limit' => array(
+                        'name' => _("Maximum number of faces"),
+                        'type' => 'int',
+                        'default' => 10));
+    }
+
+    function _title()
+    {
+        return _("Recent faces");
+    }
+
+    function _content()
+    {
+        require_once dirname(__FILE__) . '/../base.php';
+        require_once ANSEL_BASE . '/lib/Faces.php';
+        $faces = Ansel_Faces::singleton();
+
+        $results = $faces->allFaces(0, $this->_params['limit']);
+        if (is_a($results, 'PEAR_Error')) {
+            return $results;
+        }
+
+        $html = '';
+        foreach ($results as $face_id => $face) {
+            $facename = htmlspecialchars($face['face_name'], ENT_COMPAT, Horde_Nls::getCharset());
+            $html .= '<a href="' . $faces->getLink($face) . '" title="' . $facename . '">'
+                    . '<img src="' . $faces->getFaceUrl($face['image_id'], $face_id)
+                    . '" style="padding-bottom: 5px; padding-left: 5px" alt="' . $facenane  . '" /></a>';
+        }
+
+        return $html;
+    }
+
+}
diff --git a/ansel/lib/Block/recently_added.php b/ansel/lib/Block/recently_added.php
new file mode 100644 (file)
index 0000000..4c8d6a3
--- /dev/null
@@ -0,0 +1,204 @@
+<?php
+
+$block_name = _("Recently Added Photos");
+
+/**
+ * Display most recently added images.
+ *
+ * $Horde: ansel/lib/Block/recently_added.php,v 1.43 2009/07/09 08:17:49 slusarz Exp $
+ *
+ * Copyright 2007-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author  Michael Rubinsky <mrubinsk@horde.org>
+ * @package Horde_Block
+ */
+class Horde_Block_ansel_recently_added extends Horde_Block {
+
+    var $_app = 'ansel';
+    var $_gallery = null;
+
+    function _params()
+    {
+        require_once dirname(__FILE__) . '/../base.php';
+
+        $params = array('gallery' => array(
+                            'name' => _("Gallery"),
+                            'type' => 'enum',
+                            'default' => '__random',
+                            'values' => array('all' => 'All')),
+                        'limit' => array(
+                             'name' => _("Maximum number of photos"),
+                             'type' => 'int',
+                             'default' => 10),
+        );
+
+        if ($GLOBALS['ansel_storage']->countGalleries(Horde_Auth::getAuth(), PERMS_READ) < $GLOBALS['conf']['gallery']['listlimit']) {
+            foreach ($GLOBALS['ansel_storage']->listGalleries(PERMS_READ) as $id => $gal) {
+                if (!$gal->hasPasswd() && $gal->isOldEnough()) {
+                    $params['gallery']['values'][$id] = $gal->get('name');
+                }
+            }
+        }
+
+        return $params;
+    }
+
+    function _title()
+    {
+        require_once dirname(__FILE__) . '/../base.php';
+
+        if ($this->_params['gallery'] != 'all') {
+            $gallery = $this->_getGallery();
+            if (is_a($gallery, 'PEAR_Error')) {
+                return Horde::link(
+                    Ansel::getUrlFor('view', array('view' => 'List'), true))
+                    . _("Gallery") . '</a>';
+            }
+
+            // Build the gallery name.
+            if (isset($this->_params['gallery'])) {
+                $name = @htmlspecialchars($gallery->get('name'), ENT_COMPAT,
+                                          Horde_Nls::getCharset());
+            }
+
+            $style = $gallery->getStyle();
+            $viewurl = Ansel::getUrlFor('view',
+                                        array('slug' => $gallery->get('slug'),
+                                              'gallery' => $gallery->id,
+                                              'view' => 'Gallery'),
+                                        true);
+        } else {
+            $viewurl = Ansel::getUrlFor('view', array('view' => 'List'), true);
+            $name = _("All Galleries");
+        }
+        return sprintf(_("Recently Added Photos From %s"),
+                       Horde::link($viewurl) . $name . '</a>');
+    }
+
+    function _content()
+    {
+        require_once dirname(__FILE__) . '/../base.php';
+
+        if ($this->_params['gallery'] == 'all') {
+            $galleries = array();
+        } elseif (!is_array($this->_params['gallery'])) {
+            $galleries = array($this->_params['gallery']);
+        } else {
+            $galleries = $this->_params['gallery'];
+        }
+
+        // Retrieve the images, but protect against very large values for
+        // limit.
+        $results = $GLOBALS['ansel_storage']->getRecentImages(
+            $galleries, min($this->_params['limit'], 100));
+        if (is_a($results, 'PEAR_Error')) {
+            return $results->getMessage();
+        }
+        $preview_url = Horde::applicationUrl('preview.php', true);
+        $header = array(_("Date"), _("Photo"), _("Gallery"));
+
+        $html = <<<HEADER
+
+<div id="ansel_preview"></div>
+<script type="text/javascript">
+function previewImage(e, image_id) {
+    $('ansel_preview').style.left = Event.pointerX(e) + 'px';
+    $('ansel_preview').style.top = Event.pointerY(e) + 'px';
+    new Ajax.Updater({success:'ansel_preview'},
+                     '$preview_url',
+                     {method: 'post',
+                      parameters:'?image=' + image_id,
+                      onsuccess:$('ansel_preview').show()});
+}
+</script>
+<table class="linedRow" cellspacing="0" style="width:100%">
+ <thead><tr class="item nowrap">
+  <th class="item leftAlign">$header[0]</th>
+  <th class="item leftAlign">$header[1]</th>
+  <th class="item leftAlign">$header[2]</th>
+</tr></thead>
+<tbody>
+HEADER;
+
+        foreach ($results as $image) {
+            if (is_a($image, 'PEAR_Error')) {
+                continue;
+            }
+            $gallery = $GLOBALS['ansel_storage']->getGallery($image->gallery);
+
+            // Don't show locked galleries in the block.
+            if (!$gallery->isOldEnough() || $gallery->hasPasswd()) {
+                continue;
+            }
+            $style = $gallery->getStyle();
+
+            $galleryLink = Ansel::getUrlFor(
+                'view', array('slug' => $gallery->get('slug'),
+                              'gallery' => $gallery->id,
+                              'view' => 'Gallery'),
+                true);
+            $galleryLink = Horde::link($galleryLink)
+                . @htmlspecialchars($gallery->get('name'), ENT_COMPAT,
+                                    Horde_Nls::getCharset())
+                . '</a>';
+
+            $caption = substr($image->caption, 0, 30);
+            if (strlen($image->caption) > 30) {
+                $caption .= '...';
+            }
+
+            /* Generate the image view url */
+            $url = Ansel::getUrlFor(
+                'view',
+                array('view' => 'Image',
+                      'slug' => $gallery->get('slug'),
+                      'gallery' => $gallery->id,
+                      'image' => $image->id,
+                      'gallery_view' => $style['gallery_view']));
+            $html .= '<tr><td>' . strftime('%x', $image->uploaded)
+                . '</td><td class="nowrap">'
+                . Horde::link(
+                    $url, '', '', '', '', '', '',
+                    array('onmouseout' => '$("ansel_preview").hide();$("ansel_preview").update("");',
+                          'onmouseover' => 'previewImage(event, ' . $image->id . ');'))
+                . @htmlspecialchars(
+                    strlen($caption) ? $caption : $image->filename,
+                    ENT_COMPAT, Horde_Nls::getCharset())
+                . '</a></td><td class="nowrap">' . $galleryLink . '</td></tr>';
+        }
+
+        $html .= '</tbody></table>';
+        return $html;
+    }
+
+    function _getGallery()
+    {
+        // Make sure we haven't already selected a gallery.
+        if (is_a($this->_gallery, 'Ansel_Gallery')) {
+            return $this->_gallery;
+        }
+
+        // Get the gallery object and cache it.
+        if (isset($this->_params['gallery']) &&
+            $this->_params['gallery'] != '__random') {
+            $this->_gallery = $GLOBALS['ansel_storage']->getGallery(
+                $this->_params['gallery']);
+        } else {
+            $this->_gallery = $GLOBALS['ansel_storage']->getRandomGallery();
+        }
+
+        if (empty($this->_gallery)) {
+            return PEAR::raiseError(_("Gallery does not exist."));
+        } elseif (is_a($this->_gallery, 'PEAR_Error') ||
+                  !$this->_gallery->hasPermission(Horde_Auth::getAuth(), PERMS_READ)) {
+            return PEAR::raiseError(_("Access denied viewing this gallery."));
+        }
+
+        // Return a reference to the gallery.
+        return $this->_gallery;
+    }
+
+}
diff --git a/ansel/lib/Block/recently_added_geodata.php b/ansel/lib/Block/recently_added_geodata.php
new file mode 100644 (file)
index 0000000..d47aa6d
--- /dev/null
@@ -0,0 +1,180 @@
+<?php
+
+$block_name = _("Recently Geotagged");
+
+/**
+ * Display most recently geotagged images.
+ *
+ * $Horde: ansel/lib/Block/recently_added_geodata.php,v 1.4 2009/07/30 16:19:00 mrubinsk Exp $
+ *
+ * Copyright 2007-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author  Michael Rubinsky <mrubinsk@horde.org>
+ * @package Horde_Block
+ */
+class Horde_Block_ansel_recently_added_geodata extends Horde_Block {
+
+    var $_app = 'ansel';
+    var $_gallery = null;
+
+    function _params()
+    {
+        require_once dirname(__FILE__) . '/../base.php';
+
+        $params = array('gallery' => array(
+                            'name' => _("Gallery"),
+                            'type' => 'enum',
+                            'default' => '__random',
+                            'values' => array('all' => 'All')),
+                        'limit' => array(
+                             'name' => _("Maximum number of photos"),
+                             'type' => 'int',
+                             'default' => 10),
+                        'height' => array(
+                             'name' => _("Height of map (width automatically adjusts to block)"),
+                             'type' => 'int',
+                             'default' => 250),
+        );
+
+        if ($GLOBALS['ansel_storage']->countGalleries(Horde_Auth::getAuth(), PERMS_READ) < $GLOBALS['conf']['gallery']['listlimit']) {
+            foreach ($GLOBALS['ansel_storage']->listGalleries(PERMS_READ) as $id => $gal) {
+                if (!$gal->hasPasswd() && $gal->isOldEnough()) {
+                    $params['gallery']['values'][$id] = $gal->get('name');
+                }
+            }
+        }
+
+        return $params;
+    }
+
+    function _title()
+    {
+        require_once dirname(__FILE__) . '/../base.php';
+
+        Horde::addScriptFile('prototype.js', 'horde');
+        Horde::addExternalScriptFile('http://maps.google.com/maps?file=api&v=2&sensor=false&key=' . $GLOBALS['conf']['api']['googlemaps'], 'ansel');
+        Horde::addExternalScriptFile('http://gmaps-utility-library.googlecode.com/svn/trunk/markermanager/1.1/src/markermanager.js', 'ansel');
+        Horde::addScriptFile('googlemap.js');
+        if ($this->_params['gallery'] != 'all') {
+            $gallery = $this->_getGallery();
+            if (is_a($gallery, 'PEAR_Error')) {
+                return Horde::link(
+                    Ansel::getUrlFor('view', array('view' => 'List'), true))
+                    . _("Gallery") . '</a>';
+            }
+
+            // Build the gallery name.
+            if (isset($this->_params['gallery'])) {
+                $name = @htmlspecialchars($gallery->get('name'), ENT_COMPAT,
+                                          Horde_Nls::getCharset());
+            }
+
+            $style = $gallery->getStyle();
+            $viewurl = Ansel::getUrlFor('view',
+                                        array('slug' => $gallery->get('slug'),
+                                              'gallery' => $gallery->id,
+                                              'view' => 'Gallery'),
+                                        true);
+        } else {
+            $viewurl = Ansel::getUrlFor('view', array('view' => 'List'), true);
+            $name = _("All Galleries");
+        }
+        return sprintf(_("Recently Geotagged Photos From %s"),
+                       Horde::link($viewurl) . $name . '</a>');
+    }
+
+    function _content()
+    {
+        require_once dirname(__FILE__) . '/../base.php';
+
+        if ($this->_params['gallery'] == 'all') {
+            $galleries = array();
+        } elseif (!is_array($this->_params['gallery'])) {
+            $galleries = array($this->_params['gallery']);
+        } else {
+            $galleries = $this->_params['gallery'];
+        }
+
+        $images = $GLOBALS['ansel_storage']->getRecentImagesGeodata(null, 0, min($this->_params['limit'], 100));
+        if (is_a($images, 'PEAR_Error')) {
+            return $images->getMessage();
+        }
+
+        foreach ($images as $id => $image) {
+            if (is_a($image, 'PEAR_Error')) {
+                continue;
+            }
+            $gallery = $GLOBALS['ansel_storage']->getGallery($image['gallery_id']);
+
+            /* Don't show locked galleries in the block. */
+            if (!$gallery->isOldEnough() || $gallery->hasPasswd()) {
+                continue;
+            }
+            $style = $gallery->getStyle();
+
+            /* Generate the image view url */
+            $url = Ansel::getUrlFor(
+                'view',
+                array('view' => 'Image',
+                      'slug' => $gallery->get('slug'),
+                      'gallery' => $gallery->id,
+                      'image' => $id,
+                      'gallery_view' => $style['gallery_view']), true);
+            $images[$id]['icon'] = Ansel::getImageUrl($images[$id]['image_id'], 'mini', true);
+            $images[$id]['link'] = $url;
+        }
+
+        $json = Horde_Serialize::serialize(array_values($images), Horde_Serialize::JSON);
+        $html = '<div id="ansel_map" style="height:' . $this->_params['height'] . 'px;"></div>';
+        $html .= <<<EOT
+        <script type="text/javascript">
+        var map = {};
+        var pageImages = {$json};
+        options = {
+            mainMap:  'ansel_map',
+            viewType: 'Block',
+            calculateMaxZoom: false
+        };
+        function doMap(points) {
+            map = new Ansel_GMap(options);
+            map.addPoints(points);
+            map.display();
+        }
+
+        Event.observe(window, "load", function() {doMap(pageImages);});
+        </script>
+EOT;
+        return $html;
+    }
+
+    function _getGallery()
+    {
+        // Make sure we haven't already selected a gallery.
+        if (is_a($this->_gallery, 'Ansel_Gallery')) {
+            return $this->_gallery;
+        }
+
+        // Get the gallery object and cache it.
+        if (isset($this->_params['gallery']) &&
+            $this->_params['gallery'] != '__random') {
+            $this->_gallery = $GLOBALS['ansel_storage']->getGallery(
+                $this->_params['gallery']);
+        } else {
+            $this->_gallery = $GLOBALS['ansel_storage']->getRandomGallery();
+        }
+
+        if (empty($this->_gallery)) {
+            return PEAR::raiseError(_("Gallery does not exist."));
+        } elseif (is_a($this->_gallery, 'PEAR_Error') ||
+                  !$this->_gallery->hasPermission(Horde_Auth::getAuth(), PERMS_READ)) {
+            return PEAR::raiseError(_("Access denied viewing this gallery."));
+        }
+
+        // Return a reference to the gallery.
+        return $this->_gallery;
+    }
+
+}
diff --git a/ansel/lib/Exif.php b/ansel/lib/Exif.php
new file mode 100644 (file)
index 0000000..27fb8ff
--- /dev/null
@@ -0,0 +1,1364 @@
+<?php
+/**
+ * General class for fetching and parsing EXIF information from images.
+ *
+ * Works equally well with either the built in php exif functions (if PHP
+ * compiled with exif support) or the (slower) bundled exif library.
+ *
+ * $Horde: ansel/lib/Exif.php,v 1.58 2009/07/13 14:29:04 mrubinsk Exp $
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ */
+class Ansel_ImageData {
+
+    /**
+     * Get the image attributes from the backend.
+     *
+     * @param Ansel_Image $image  The image to retrieve attributes for.
+     *                            attributes for.
+     * @param boolean $format     Format the EXIF data. If false, the raw data
+     *                            is returned.
+     *
+     * @return array  The EXIF data.
+     * @static
+     */
+    function getAttributes($image, $format = false)
+    {
+        $attributes = $GLOBALS['ansel_db']->queryAll('SELECT attr_name, attr_value FROM ansel_image_attributes WHERE image_id = ' . (int)$image->id, null, MDB2_FETCHMODE_ASSOC, true);
+        $fields = Ansel_ImageData::getFields();
+        $output = array();
+
+        foreach ($fields as $field => $data) {
+            if (!isset($attributes[$field])) {
+                continue;
+            }
+            $value = Ansel_ImageData::getHumanReadable($field, Horde_String::convertCharset($attributes[$field], $GLOBALS['conf']['sql']['charset']));
+            if (!$format) {
+                $output[$field] = $value;
+            } else {
+                $description = isset($data['description']) ? $data['description'] : $field;
+                $output[] = '<td><strong>' . $description . '</strong></td><td>' . htmlspecialchars($value, ENT_COMPAT, Horde_Nls::getCharset()) . '</td>';
+            }
+        }
+
+        return $output;
+    }
+
+    /**
+     * Get the EXIF data from an image, process it, and return it.
+     *
+     * @see getFields()
+     *
+     * @param Ansel_Image $image  The image to read exif data from.
+     * @return array Array of EXIF attributes.
+     */
+    function getExifData($image)
+    {
+        // Unfortunately, the PHP function requires a file, not a stream.
+        $imageFile = $GLOBALS['ansel_vfs']->readFile($image->getVFSPath('full'),
+                                                     $image->getVFSName('full'));
+        if (is_a($imageFile, 'PEAR_Error')) {
+            return $imageFile;
+        }
+
+        if (function_exists('exif_read_data')) {
+            $exif = @exif_read_data($imageFile, 0, false);
+        } else {
+            $raw = read_exif_data_raw($imageFile);
+            $exif = array();
+            foreach ($raw as $key => $value) {
+                if (($key == 'IFD0') || ($key == 'SubIFD')) {
+                    foreach ($value as $subkey => $subvalue) {
+                        $exif[$subkey] = $subvalue;
+                    }
+                } else {
+                    $exif[$key] = $value;
+                }
+            }
+            // Not really an EXIF property, but an attribute nonetheless...
+            // PHP's exif functions return it, so add it here to be consistent.
+            $exif['FileSize'] = @filesize($imageFile);
+        }
+
+        // See if we got any attributes back.
+        $results = array();
+        if ($exif) {
+            $fields = Ansel_ImageData::getFields();
+            foreach ($fields as $field => $data) {
+                $value = isset($exif[$field]) ? $exif[$field] : '';
+
+                // Don't store empty fields.
+                if ($value === '') {
+                    continue;
+                }
+
+                if ($data['type'] == 'gps') {
+                    $value = Ansel_ImageData::_parseGPSData($exif[$field]); // . (!empty($exif[$field . 'Ref']) ? $exif[$field . 'Ref'] : '');
+                    if (!empty($exif[$field . 'Ref']) && ($exif[$field . 'Ref'] == 'S' || $exif[$field . 'Ref'] == 'W')) {
+                        $value = '-' . $value;
+                    }
+                }
+                // If the field is a date field, convert the value to a
+                // timestamp.
+                if ($data['type'] == 'date') {
+                    @list($ymd, $hms) = explode(' ', $value, 2);
+                    @list($year, $month, $day) = explode(':', $ymd, 3);
+                    if (!empty($hms) && !empty($year) && !empty($month) && !empty($day)) {
+                        $time = "$month/$day/$year $hms";
+                        $value = strtotime($time);
+                    }
+                }
+
+                $results[$field] = $value;
+            }
+        }
+
+        return $results;
+    }
+
+    /**
+     * Parse the Longitude and Latitude values into a standardized format
+     * regardless of the source format.
+     *
+     * @param mixed $data  An array containing degrees, minutes, seconds
+     *                     in index 0, 1, 2 respectifully.
+     *
+     * @return double  The location data in a decimal format.
+     */
+    function _parseGPSData($data)
+    {
+        // According to EXIF standard, GPS data can be in the form of
+        // dd/1 mm/1 ss/1 or as a decimal reprentation.
+        if ($data[0] == 0) {
+            return 0;
+        }
+        $min = explode('/', $data[1]);
+        if (count($min) > 1) {
+            $min = $min[0] / $min[1];
+        } else {
+            $min = $min[0];
+        }
+
+        $sec = explode('/', $data[2]);
+        if (count($sec) > 1) {
+            $sec = $sec[0] / $sec[1];
+        } else {
+            $sec = $sec[0];
+        }
+
+        return Ansel_ImageData::_degToDecimal($data[0], $min, $sec);
+    }
+
+    function _degToDecimal($degrees, $minutes, $seconds)
+    {
+        $degs = (double)($degrees + ($minutes / 60) + ($seconds/3600));
+        return round($degs, 6);
+    }
+
+    /**
+     * More human friendly exposure formatting.
+     */
+    function _formatExposure($data) {
+        if ($data > 0) {
+            if ($data > 1) {
+                return sprintf(_("%d sec"), round($data, 2));
+            } else {
+                $n = $d = 0;
+                Ansel_ImageData::_convertToFraction($data, $n, $d);
+                if ($n <> 1) {
+                    return sprintf(_("%4f sec"), $n / $d);
+                }
+                return sprintf(_("%s / %s sec"), $n, $d);
+            }
+        } else {
+            return _("Bulb");
+        }
+    }
+
+    /**
+     * Converts a floating point number into a fraction.
+     * Many thanks to Matthieu Froment for this code.
+     *
+     * (Ported from the Exifer library).
+     */
+    function _convertToFraction($v, &$n, &$d)
+    {
+        $MaxTerms = 15;         // Limit to prevent infinite loop
+        $MinDivisor = 0.000001; // Limit to prevent divide by zero
+        $MaxError = 0.00000001; // How close is enough
+
+        // Initialize fraction being converted
+        $f = $v;
+
+        // Initialize fractions with 1/0, 0/1
+        $n_un = 1;
+        $d_un = 0;
+        $n_deux = 0;
+        $d_deux = 1;
+
+        for ($i = 0; $i < $MaxTerms; $i++) {
+            $a = floor($f); // Get next term
+            $f = $f - $a; // Get new divisor
+            $n = $n_un * $a + $n_deux; // Calculate new fraction
+            $d = $d_un * $a + $d_deux;
+            $n_deux = $n_un; // Save last two fractions
+            $d_deux = $d_un;
+            $n_un = $n;
+            $d_un = $d;
+
+            // Quit if dividing by zero
+            if ($f < $MinDivisor) {
+                break;
+            }
+            if (abs($v - $n / $d) < $MaxError) {
+                break;
+            }
+
+            // reciprocal
+            $f = 1 / $f;
+        }
+    }
+
+    /**
+     * Convert an exif field into human-readable form.
+     * Some of these cases are ported from the Exifer library, others were
+     * changed from their implementation where the EXIF standard dictated
+     * different behaviour.
+     *
+     * @param string $field  The name of the field to translate.
+     * @param string $data   The data value to translate.
+     *
+     * @return string  The converted data.
+     */
+    function getHumanReadable($field, $data)
+    {
+        switch ($field) {
+        case 'ExposureMode':
+            switch ($data) {
+            case 0: return _("Auto exposure");
+            case 1: return _("Manual exposure");
+            case 2: return _("Auto bracket");
+            default: return _("Unknown");
+            }
+
+        case 'ExposureProgram':
+            switch ($data) {
+            case 1: return _("Manual");
+            case 2: return _("Normal Program");
+            case 3: return _("Aperture Priority");
+            case 4: return _("Shutter Priority");
+            case 5: return _("Creative");
+            case 6: return _("Action");
+            case 7: return _("Portrait");
+            case 8: return _("Landscape");
+            default: return _("Unknown");
+            }
+
+        case 'XResolution':
+        case 'YResolution':
+            if (strpos($data, '/') !== false) {
+                list($n, $d) = explode('/', $data, 2);
+                return sprintf(_("%d dots per unit"), $n);
+            }
+            return sprintf(_("%d per unit"), $data);
+
+        case 'ResolutionUnit':
+            switch ($data) {
+            case 1: return _("Pixels");
+            case 2: return _("Inch");
+            case 3: return _("Centimeter");
+            default: return _("Unknown");
+            }
+
+        case 'ExifImageWidth':
+        case 'ExifImageLength':
+            return sprintf(_("%d pixels"), $data);
+
+        case 'Orientation':
+            switch ($data) {
+            case 1:
+                return sprintf(_("Normal (O deg)"));
+            case 2:
+                return sprintf(_("Mirrored"));
+            case 3:
+                return sprintf(_("Upsidedown"));
+            case 4:
+                return sprintf(_("Upsidedown Mirrored"));
+            case 5:
+                return sprintf(_("90 deg CW Mirrored"));
+            case 6:
+                return sprintf(_("90 deg CCW"));
+            case 7:
+                return sprintf(_("90 deg CCW Mirrored"));
+            case 8:
+                return sprintf(_("90 deg CW"));
+            }
+            break;
+
+        //@TODO: normalize these values...
+        case 'ExposureTime':
+            if (strpos($data, '/') !== false) {
+                list($n, $d) = explode('/', $data, 2);
+                $data = $n / $d;
+            }
+            return Ansel_ImageData::_formatExposure($data);
+
+        case 'ShutterSpeedValue':
+            if (strpos($data, '/') !== false) {
+                list($n, $d) = explode('/', $data, 2);
+                $data = $n / $d;
+            }
+            $data = exp($data * log(2));
+            if ($data > 0) {
+                $data = 1 / $data;
+            }
+            return Ansel_ImageData::_formatExposure($data);
+
+        case 'ApertureValue':
+        case 'MaxApertureValue':
+            if (strpos($data, '/') !== false) {
+                list($n, $d) = explode('/', $data, 2);
+                $data = $n / $d;
+                $data = exp(($data * log(2)) / 2);
+
+                // Precision is 1 digit.
+                $data = round($data, 1);
+            }
+            return 'f/' . $data;
+
+        case 'FocalLength':
+            if (strpos($data, '/') !== false) {
+                list($n, $d) = explode('/', $data, 2);
+                return sprintf(_("%d mm"), round($n / $d));
+            }
+            return sprintf(_("%d mm"), $data);
+
+        case 'FNumber':
+            if (strpos($data, '/') !== false) {
+                list($n, $d) = explode('/', $data, 2);
+                if ($d != 0) {
+                    return 'f/' . round($n / $d, 1);
+                }
+            }
+            return 'f/' . $data;
+
+        case 'ExposureBiasValue':
+            if (strpos($data, '/') !== false) {
+                list($n, $d) = explode('/', $data, 2);
+                if ($n == 0) {
+                    return '0 EV';
+                }
+            }
+            return $data . ' EV';
+
+        case 'MeteringMode':
+            switch ($data) {
+            case 0: return _("Unknown");
+            case 1: return _("Average");
+            case 2: return _("Center Weighted Average");
+            case 3: return _("Spot");
+            case 4: return _("Multi-Spot");
+            case 5: return _("Multi-Segment");
+            case 6: return _("Partial");
+            case 255: return _("Other");
+            default: return sprintf(_("Unknown: %s"), $data);
+            }
+            break;
+
+        case 'LightSource':
+            switch ($data) {;
+            case 1: return _("Daylight");
+            case 2: return _("Fluorescent");
+            case 3: return _("Tungsten");
+            case 4: return _("Flash");
+            case 9: return _("Fine weather");
+            case 10: return _("Cloudy weather");
+            case 11: return _("Shade");
+            case 12: return _("Daylight fluorescent");
+            case 13: return _("Day white fluorescent");
+            case 14: return _("Cool white fluorescent");
+            case 15: return _("White fluorescent");
+            case 17: return _("Standard light A");
+            case 18: return _("Standard light B");
+            case 19: return _("Standard light C");
+            case 20: return 'D55';
+            case 21: return 'D65';
+            case 22: return 'D75';
+            case 23: return 'D50';
+            case 24: return _("ISO studio tungsten");
+            case 255: return _("other light source");
+            default: return _("Unknown");
+            }
+
+        case 'WhiteBalance':
+            switch ($data) {
+            case 0: return _("Auto");
+            case 1: return _("Manual");
+            default: _("Unknown");
+            }
+            break;
+
+        case 'FocalLengthIn35mmFilm':
+            return $data . ' mm';
+
+        case 'Flash':
+            switch ($data) {
+            case 0: return _("No Flash");
+            case 1: return _("Flash");
+            case 5: return _("Flash, strobe return light not detected");
+            case 7: return _("Flash, strobe return light detected");
+            case 9: return _("Compulsory Flash");
+            case 13: return _("Compulsory Flash, Return light not detected");
+            case 15: return _("Compulsory Flash, Return light detected");
+            case 16: return _("No Flash");
+            case 24: return _("No Flash");
+            case 25: return _("Flash, Auto-Mode");
+            case 29: return _("Flash, Auto-Mode, Return light not detected");
+            case 31: return _("Flash, Auto-Mode, Return light detected");
+            case 32: return _("No Flash");
+            case 65: return _("Red Eye");
+            case 69: return _("Red Eye, Return light not detected");
+            case 71: return _("Red Eye, Return light detected");
+            case 73: return _("Red Eye, Compulsory Flash");
+            case 77: return _("Red Eye, Compulsory Flash, Return light not detected");
+            case 79: return _("Red Eye, Compulsory Flash, Return light detected");
+            case 89: return _("Red Eye, Auto-Mode");
+            case 93: return _("Red Eye, Auto-Mode, Return light not detected");
+            case 95: return _("Red Eye, Auto-Mode, Return light detected");
+            }
+            break;
+
+        case 'FileSize':
+           if ($data <= 0) {
+              return '0 Bytes';
+           }
+           $s = array('B', 'kB', 'MB', 'GB');
+           $e = floor(log($data, 1024));
+           return round($data/pow(1024, $e), 2) . ' ' . $s[$e];
+
+        case 'FileSource':
+            $data = bin2hex($data);
+            $data = str_replace('00', '', $data);
+            $data = str_replace('03', _("Digital Still Camera"), $data);
+            return $data;
+
+        case 'SensingMethod':
+            switch ($data) {
+            case 1: return _("Not defined");
+            case 2: return _("One Chip Color Area Sensor");
+            case 3: return _("Two Chip Color Area Sensor");
+            case 4: return _("Three Chip Color Area Sensor");
+            case 5: return _("Color Sequential Area Sensor");
+            case 7: return _("Trilinear Sensor");
+            case 8: return _("Color Sequential Linear Sensor");
+            default: return _("Unknown");
+            }
+
+        case 'ColorSpace':
+            switch ($data) {
+            case 1: return _("sRGB");
+            default: return _("Uncalibrated");
+            }
+
+        case 'DateTime':
+        case 'DateTimeOriginal':
+        case 'DateTimeDigitized':
+            return date('m/d/Y H:i:s O', $data);
+
+        default:
+            return $data;
+        }
+    }
+
+    /**
+     * Get the list of Exif fields we support, and their descriptions.
+     *
+     * @static
+     *
+     * @return array  Hash of fieldname => description.
+     */
+    function getFields()
+    {
+        return array('Make' => array('description' => _("Camera Make"), 'type' => 'text'),
+                     'Model' => array('description' => _("Camera Model"), 'type' => 'text'),
+                     'ImageType' => array('description' => _("Photo Type"), 'type' => 'text'),
+                     'ImageDescription' => array('description' => _("Photo Description"), 'type' => 'text'),
+                     'FileSize' => array('description' => _("File Size"), 'type' => 'number'),
+                     'DateTime' => array('description' => _("Date Photo Modified"), 'type' => 'date'),
+                     'DateTimeOriginal' => array('description' => _("Date Photo Taken"), 'type' => 'date'),
+                     'DateTimeDigitized' => array('description' => _("Date Photo Digitized"), 'type' => 'date'),
+                     'ExifImageWidth' => array('description' => _("Width"), 'type' => 'number'),
+                     'ExifImageLength' => array('description' => _("Height"), 'type' => 'number'),
+                     'XResolution' => array('description' => _("X Resolution"), 'type' => 'number'),
+                     'YResolution' => array('description' => _("Y Resolution"), 'type' => 'number'),
+                     'ResolutionUnit' => array('description' => _("Resolution Unit"), 'type' => 'text'),
+                     'ShutterSpeedValue' => array('description' => _("Shutter Speed"), 'type' => 'number'),
+                     'ExposureTime' => array('description' => _("Exposure"), 'type' => 'number'),
+                     'FocalLength' => array('description' => _("Focal Length"), 'type' => 'number'),
+                     'FocalLengthIn35mmFilm' => array('description' => _("Focal Length (35mm equiv)"), 'type' => 'number'),
+                     'ApertureValue' => array('description' => _("Aperture"), 'type' => 'number'),
+                     'FNumber' => array('description' => _("F-Number"), 'type' => 'number'),
+                     'ISOSpeedRatings' => array('description' => _("ISO Setting"), 'type' => 'number'),
+                     'ExposureBiasValue' => array('description' => _("Exposure Bias"), 'type' => 'number'),
+                     'ExposureMode' => array('description' => _("Exposure Mode"), 'type' => 'number'),
+                     'ExposureProgram' => array('description' => _("Exposure Program"), 'type' => 'number'),
+                     'MeteringMode' => array('description' => _("Metering Mode"), 'type' => 'number'),
+                     'Flash' => array('description' => _("Flash Setting"), 'type' => 'number'),
+                     'UserComment' => array('description' => _("User Comment"), 'type' => 'text'),
+                     'ColorSpace' => array('description' => _("Color Space"), 'type' => 'number'),
+                     'SensingMethod' => array('description' => _("Sensing Method"), 'type' => 'number'),
+                     'WhiteBalance' => array('description' => _("White Balance"), 'type' => 'number'),
+                     'Orientation' => array('description' => _("Camera Orientation"), 'type' => 'number'),
+                     'Copyright' => array('description' => _("Copyright"), 'type' => 'text'),
+                     'Artist' => array('description' => _("Artist"), 'type' => 'text'),
+                     'GPSLatitude' => array('description' => _("Latitude"), 'type' => 'gps'),
+                     'GPSLongitude' => array('description' => _("Longitude"), 'type' => 'gps'),
+                     'LightSource' => array('description' => _("Light source"), 'type' => 'number'),
+                     'FileSource' => array('description' => _("File source"), 'type' => 'number'),
+        );
+    }
+
+}
+
+/**
+ * Greatly modified Exifer library based on Exifer 1.6 -
+ * Modified to make it more of a drop-in replacement for php's
+ * exif functions to allow either to function with our exif code.
+ *
+ * Also added a number of exif properties, including a proper 35mm
+ * focal length tag as well as tweaks to the calculations of some of
+ * the values.
+ *
+ * Modifications Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ * and done by Michael J. Rubinsky <mrubinsk@horde.org>
+ */
+
+/*
+    Exifer 1.6
+    Extracts EXIF information from digital photos.
+
+    Originally created by:
+    Copyright © 2005 Jake Olefsky
+    http:// www.offsky.com/software/exif/index.php
+    jake@olefsky.com
+
+    This program is free software; you can redistribute it and/or modify it under the terms of
+    the GNU General Public License as published by the Free Software Foundation; either version 2
+    of the License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+    without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+    See the GNU General Public License for more details. http:// www.gnu.org/copyleft/gpl.html
+
+    SUMMARY:
+                This script will correctly parse all of the EXIF data included in images taken
+                with digital cameras.  It will read the IDF0, IDF1, SubIDF and InteroperabilityIFD
+                fields as well as parsing some of the MakerNote fields that vary depending on
+                camera make and model.  This script parses more tags than the internal PHP exif
+                implementation and it will correctly identify and decode what all the values mean.
+
+                This version will correctly parse the MakerNote field for Nikon, Olympus, and Canon
+                digital cameras.  Others will follow.
+
+    TESTED WITH:
+                Nikon CoolPix 700
+                Nikon CoolPix E3200
+                Nikon CoolPix 4500
+                Nikon CoolPix 950
+                Nikon Coolpix 5700
+                Canon PowerShot S200
+                Canon PowerShot S110
+                Olympus C2040Z
+                Olympus C960
+                Olumpus E-300
+                Olympus E-410
+                Olympus E-500
+                Olympus E-510
+                Olympus E-3
+                Canon Ixus
+                Canon EOS 300D
+                Canon Digital Rebel
+                Canon EOS 10D
+                Canon PowerShot G2
+                FujiFilm DX 10
+                FujiFilm MX-1200
+                FujiFilm FinePix2400
+                FujiFilm FinePix2600
+                FujiFilm FinePix S602
+                FujiFilm FinePix40i
+                Sony D700
+                Sony Cybershot
+                Kodak DC210
+                Kodak DC240
+                Kodak DC4800
+                Kodak DX3215
+                Ricoh RDC-5300
+                Sanyo VPC-G250
+                Sanyo VPC-SX550
+                Epson 3100z
+
+
+    VERSION HISTORY:
+
+    1.0   September 23, 2002
+
+        + First Public Release
+
+    1.1    January 25, 2003
+
+        + Gracefully handled the error case where you pass an empty string to this library
+        + Fixed an inconsistency in the Olympus Camera parsing module
+        + Added support for parsing the MakerNote of Canon images.
+        + Modified how the imagefile is opened so it works for windows machines.
+        + Correctly parses the FocalPlaneResolutionUnit and PhotometricInterpretation fields
+        + Negative rational numbers are properly displayed
+        + Strange old cameras that use Motorola endineness are now properly supported
+        + Tested with several more cameras
+
+        Potential Problem: Negative Shorts and Negative Longs may not be correctly displayed, but I
+            have not yet found an example of negative shorts or longs being used.
+
+    1.2    March 30, 2003
+
+        + Fixed an error that was displayed if you edited your image with WinXP's image viewer
+        + Fixed a bug that caused some images saved from 3rd party software to not parse correctly
+        + Changed the ExposureTime tag to display in fractional seconds rather than decimal
+        + Updated the ShutterSpeedValue tag to have the units of 'sec'
+        + Added support for parsing the MakeNote of FujiFilm images
+        + Added support for parsing the MakeNote of Sanyo images
+        + Fixed a bug with parsing some Olympus MakerNote tags
+        + Tested with several more cameras
+
+    1.3    June 15, 2003
+
+        + Fixed Canon MakerNote support for some models
+             (Canon has very difficult and inconsistent MakerNote syntax)
+        + Negative signed shorts and negative signed longs are properly displayed
+        + Several more tags are defined
+        + More information in my comments about what each tag is
+        + Parses and Displays GPS information if available
+        + Tested with several more cameras
+
+    1.4    September 14, 2003
+
+        + This software is now licensed under the GNU General Public License
+        + Exposure time is now correctly displayed when the numerator is 10
+        + Fixed the calculation and display of ShutterSpeedValue, ApertureValue and MaxApertureValue
+        + Fixed a bug with the GPS code
+        + Tested with several more cameras
+
+    1.5    February 18, 2005
+
+        + It now gracefully deals with a passed in file that cannot be found.
+        + Fixed a GPS bug for the parsing of Altitude and other signed rational numbers
+        + Defined more values for Canon cameras.
+        + Added 'bulb' detection for ShutterSpeed
+        + Made script loading a little faster and less memory intensive.
+        + Bug fixes
+        + Better error reporting
+        + Graceful failure for files with corrupt exif info.
+        + QuickTime (including iPhoto) messes up the Makernote tag for certain photos (no workaround yet)
+        + Now reads exif information when the jpeg markers are out of order
+        + Gives raw data output for IPTC, COM and APP2 fields which are sometimes set by other applications
+        + Improvements to Nikon Makernote parsing
+
+    1.6    March 25th, 2007 [Zenphoto]
+
+        + Adopted into the Zenphoto gallery project, at http://www.zenphoto.org
+        + Fixed a bug where strings had trailing null bytes.
+        + Formatted selected strings better.
+        + Added calculation of 35mm-equivalent focal length when possible.
+        + Cleaned up code for readability and efficiency.
+
+    1.7    April 11th, 2008 [Zenphoto]
+
+      + Fixed bug with newer Olympus cameras where number of fields was miscalculated leading to bad performance.
+        + More logical fraction calculation for shutter speed.
+
+*/
+
+
+
+
+
+
+//================================================================================================
+// Converts from Intel to Motorola endien.  Just reverses the bytes (assumes hex is passed in)
+//================================================================================================
+
+function intel2Moto($intel) {
+    $len  = strlen($intel);
+    $moto = '';
+    for($i = 0; $i <= $len; $i += 2) {
+        $moto .= substr($intel, $len-$i, 2);
+    }
+    return $moto;
+}
+
+
+//================================================================================================
+// Looks up the name of the tag
+//================================================================================================
+function lookup_tag($tag) {
+    switch($tag) {
+        // used by IFD0 'Camera Tags'
+        case '000b': $tag = 'ACDComment'; break;               // text string up to 999 bytes long
+        case '00fe': $tag = 'ImageType'; break;                // integer -2147483648 to 2147483647
+        case '0106': $tag = 'PhotometricInterpret'; break;     // ?? Please send sample image with this tag
+        case '010e': $tag = 'ImageDescription'; break;         // text string up to 999 bytes long
+        case '010f': $tag = 'Make'; break;                     // text string up to 999 bytes long
+        case '0110': $tag = 'Model'; break;                    // text string up to 999 bytes long
+        case '0112': $tag = 'Orientation'; break;              // integer values 1-9
+        case '0115': $tag = 'SamplePerPixel'; break;           // integer 0-65535
+        case '011a': $tag = 'xResolution'; break;              // positive rational number
+        case '011b': $tag = 'yResolution'; break;              // positive rational number
+        case '011c': $tag = 'PlanarConfig'; break;             // integer values 1-2
+        case '0128': $tag = 'ResolutionUnit'; break;           // integer values 1-3
+        case '0131': $tag = 'Software'; break;                 // text string up to 999 bytes long
+        case '0132': $tag = 'DateTime'; break;                 // YYYY:MM:DD HH:MM:SS
+        case '013b': $tag = 'Artist'; break;                   // text string up to 999 bytes long
+        case '013c': $tag = 'HostComputer'; break;             // text string
+        case '013e': $tag = 'WhitePoint'; break;               // two positive rational numbers
+        case '013f': $tag = 'PrimaryChromaticities'; break;    // six positive rational numbers
+        case '0211': $tag = 'YCbCrCoefficients'; break;        // three positive rational numbers
+        case '0213': $tag = 'YCbCrPositioning'; break;         // integer values 1-2
+        case '0214': $tag = 'ReferenceBlackWhite'; break;      // six positive rational numbers
+        case '8298': $tag = 'Copyright'; break;                // text string up to 999 bytes long
+        case '8649': $tag = 'PhotoshopSettings'; break;        // ??
+        case '8825': $tag = 'GPSInfoOffset'; break;
+        case '9286': $tag = 'UserCommentOld'; break;           // ??
+        case '8769': $tag = 'ExifOffset'; break;               // positive integer
+        // used by Exif SubIFD 'Image Tags'
+        case '829a': $tag = 'ExposureTime'; break;             // seconds or fraction of seconds 1/x
+        case '829d': $tag = 'FNumber'; break;                  // positive rational number
+        case '8822': $tag = 'ExposureProgram'; break;          // integer value 1-9
+        case '8824': $tag = 'SpectralSensitivity'; break;      // ??
+        case '8827': $tag = 'ISOSpeedRatings'; break;          // integer 0-65535
+        case '9000': $tag = 'ExifVersion'; break;              // ??
+        case '9003': $tag = 'DateTimeOriginal'; break;         // YYYY:MM:DD HH:MM:SS
+        case '9004': $tag = 'DateTimedigitized'; break;        // YYYY:MM:DD HH:MM:SS
+        case '9101': $tag = 'ComponentsConfiguration'; break;  // ??
+        case '9102': $tag = 'CompressedBitsPerPixel'; break;   // positive rational number
+        case '9201': $tag = 'ShutterSpeedValue'; break;        // seconds or fraction of seconds 1/x
+        case '9202': $tag = 'ApertureValue'; break;            // positive rational number
+        case '9203': $tag = 'BrightnessValue'; break;          // positive rational number
+        case '9204': $tag = 'ExposureBiasValue'; break;        // positive rational number (EV)
+        case '9205': $tag = 'MaxApertureValue'; break;         // positive rational number
+        case '9206': $tag = 'SubjectDistance'; break;          // positive rational number (meters)
+        case '9207': $tag = 'MeteringMode'; break;             // integer 1-6 and 255
+        case '9208': $tag = 'LightSource'; break;              // integer 1-255
+        case '9209': $tag = 'Flash'; break;                    // integer 1-255
+        case '920a': $tag = 'FocalLength'; break;              // positive rational number (mm)
+        case '9213': $tag = 'ImageHistory'; break;             // text string up to 999 bytes long
+        case '927c': $tag = 'MakerNote'; break;                // a bunch of data
+        case '9286': $tag = 'UserComment'; break;              // text string
+        case '9290': $tag = 'SubsecTime'; break;               // text string up to 999 bytes long
+        case '9291': $tag = 'SubsecTimeOriginal'; break;       // text string up to 999 bytes long
+        case '9292': $tag = 'SubsecTimeDigitized'; break;      // text string up to 999 bytes long
+        case 'a000': $tag = 'FlashPixVersion'; break;          // ??
+        case 'a001': $tag = 'ColorSpace'; break;               // values 1 or 65535
+        case 'a002': $tag = 'ExifImageWidth'; break;           // ingeter 1-65535
+        case 'a003': $tag = 'ExifImageHeight'; break;          // ingeter 1-65535
+        case 'a004': $tag = 'RelatedSoundFile'; break;         // text string 12 bytes long
+        case 'a005': $tag = 'ExifInteroperabilityOffset'; break;    // positive integer
+        case 'a20c': $tag = 'SpacialFreqResponse'; break;      // ??
+        case 'a20b': $tag = 'FlashEnergy'; break;              // positive rational number
+        case 'a20e': $tag = 'FocalPlaneXResolution'; break;    // positive rational number
+        case 'a20f': $tag = 'FocalPlaneYResolution'; break;    // positive rational number
+        case 'a210': $tag = 'FocalPlaneResolutionUnit'; break; // values 1-3
+        case 'a214': $tag = 'SubjectLocation'; break;          // two integers 0-65535
+        case 'a215': $tag = 'ExposureIndex'; break;            // positive rational number
+        case 'a217': $tag = 'SensingMethod'; break;            // values 1-8
+        case 'a300': $tag = 'FileSource'; break;               // integer
+        case 'a301': $tag = 'SceneType'; break;                // integer
+        case 'a302': $tag = 'CFAPattern'; break;               // undefined data type
+        case 'a401': $tag = 'CustomerRender'; break;           // values 0 or 1
+        case 'a402': $tag = 'ExposureMode'; break;             // values 0-2
+        case 'a403': $tag = 'WhiteBalance'; break;             // values 0 or 1
+        case 'a404': $tag = 'DigitalZoomRatio'; break;         // positive rational number
+        case 'a405': $tag = 'FocalLengthIn35mmFilm';break;
+        case 'a406': $tag = 'SceneCaptureMode'; break;         // values 0-3
+        case 'a407': $tag = 'GainControl'; break;              // values 0-4
+        case 'a408': $tag = 'Contrast'; break;                 // values 0-2
+        case 'a409': $tag = 'Saturation'; break;               // values 0-2
+        case 'a40a': $tag = 'Sharpness'; break;                // values 0-2
+
+        // used by Interoperability IFD
+        case '0001': $tag = 'InteroperabilityIndex'; break;    // text string 3 bytes long
+        case '0002': $tag = 'InteroperabilityVersion'; break;  // datatype undefined
+        case '1000': $tag = 'RelatedImageFileFormat'; break;   // text string up to 999 bytes long
+        case '1001': $tag = 'RelatedImageWidth'; break;        // integer in range 0-65535
+        case '1002': $tag = 'RelatedImageLength'; break;       // integer in range 0-65535
+
+        // used by IFD1 'Thumbnail'
+        case '0100': $tag = 'ImageWidth'; break;               // integer in range 0-65535
+        case '0101': $tag = 'ImageLength'; break;              // integer in range 0-65535
+        case '0102': $tag = 'BitsPerSample'; break;            // integers in range 0-65535
+        case '0103': $tag = 'Compression'; break;              // values 1 or 6
+        case '0106': $tag = 'PhotometricInterpretation'; break;// values 0-4
+        case '010e': $tag = 'ThumbnailDescription'; break;     // text string up to 999 bytes long
+        case '010f': $tag = 'ThumbnailMake'; break;            // text string up to 999 bytes long
+        case '0110': $tag = 'ThumbnailModel'; break;           // text string up to 999 bytes long
+        case '0111': $tag = 'StripOffsets'; break;             // ??
+        case '0112': $tag = 'ThumbnailOrientation'; break;     // integer 1-9
+        case '0115': $tag = 'SamplesPerPixel'; break;          // ??
+        case '0116': $tag = 'RowsPerStrip'; break;             // ??
+        case '0117': $tag = 'StripByteCounts'; break;          // ??
+        case '011a': $tag = 'ThumbnailXResolution'; break;     // positive rational number
+        case '011b': $tag = 'ThumbnailYResolution'; break;     // positive rational number
+        case '011c': $tag = 'PlanarConfiguration'; break;      // values 1 or 2
+        case '0128': $tag = 'ThumbnailResolutionUnit'; break;  // values 1-3
+        case '0201': $tag = 'JpegIFOffset'; break;
+        case '0202': $tag = 'JpegIFByteCount'; break;
+        case '0212': $tag = 'YCbCrSubSampling'; break;
+
+        // misc
+        case '00ff': $tag = 'SubfileType'; break;
+        case '012d': $tag = 'TransferFunction'; break;
+        case '013d': $tag = 'Predictor'; break;
+        case '0142': $tag = 'TileWidth'; break;
+        case '0143': $tag = 'TileLength'; break;
+        case '0144': $tag = 'TileOffsets'; break;
+        case '0145': $tag = 'TileByteCounts'; break;
+        case '014a': $tag = 'SubIFDs'; break;
+        case '015b': $tag = 'JPEGTables'; break;
+        case '828d': $tag = 'CFARepeatPatternDim'; break;
+        case '828e': $tag = 'CFAPattern'; break;
+        case '828f': $tag = 'BatteryLevel'; break;
+        case '83bb': $tag = 'IPTC/NAA'; break;
+        case '8773': $tag = 'InterColorProfile'; break;
+
+        case '8828': $tag = 'OECF'; break;
+        case '8829': $tag = 'Interlace'; break;
+        case '882a': $tag = 'TimeZoneOffset'; break;
+        case '882b': $tag = 'SelfTimerMode'; break;
+        case '920b': $tag = 'FlashEnergy'; break;
+        case '920c': $tag = 'SpatialFrequencyResponse'; break;
+        case '920d': $tag = 'Noise'; break;
+        case '9211': $tag = 'ImageNumber'; break;
+        case '9212': $tag = 'SecurityClassification'; break;
+        case '9214': $tag = 'SubjectLocation'; break;
+        case '9215': $tag = 'ExposureIndex'; break;
+        case '9216': $tag = 'TIFF/EPStandardID'; break;
+        case 'a20b': $tag = 'FlashEnergy'; break;
+
+        default: $tag = 'unknown:'.$tag; break;
+    }
+    return $tag;
+
+}
+
+
+//==============================================================================
+// Looks up the datatype
+//==============================================================================
+function lookup_type(&$type,&$size) {
+    switch($type) {
+        case '0001': $type = 'UBYTE'; $size=1; break;
+        case '0002': $type = 'ASCII'; $size=1; break;
+        case '0003': $type = 'USHORT'; $size=2; break;
+        case '0004': $type = 'ULONG'; $size=4; break;
+        case '0005': $type = 'URATIONAL'; $size=8; break;
+        case '0006': $type = 'SBYTE'; $size=1; break;
+        case '0007': $type = 'UNDEFINED'; $size=1; break;
+        case '0008': $type = 'SSHORT'; $size=2; break;
+        case '0009': $type = 'SLONG'; $size=4; break;
+        case '000a': $type = 'SRATIONAL'; $size=8; break;
+        case '000b': $type = 'FLOAT'; $size=4; break;
+        case '000c': $type = 'DOUBLE'; $size=8; break;
+        default: $type = 'error:'.$type; $size=0; break;
+    }
+    return $type;
+}
+
+//==============================================================================
+// Formats Data for the data type
+//==============================================================================
+function formatData($type, $tag, $intel, $data)
+{
+    if ($type == 'ASCII') {
+        // Search for a null byte and stop there.
+        if (($pos = strpos($data, chr(0))) !== false) {
+            $data = substr($data, 0, $pos);
+        }
+        // Format certain kinds of strings nicely (Camera make etc.)
+        if ($tag == '010f') {
+            $data = ucwords(strtolower(trim($data)));
+        }
+
+    } elseif ($type == 'URATIONAL' || $type == 'SRATIONAL') {
+        $data = bin2hex($data);
+        if ($intel == 1) {
+            $data = intel2Moto($data);
+        }
+
+        if ($intel == 1) {
+            $top = hexdec(substr($data,8,8)); // intel stores them bottom-top
+        } else {
+            $top = hexdec(substr($data,0,8)); // motorola stores them top-bottom
+        }
+
+        if ($intel == 1) {
+            $bottom = hexdec(substr($data,0,8));  // intel stores them bottom-top
+        } else {
+            $bottom = hexdec(substr($data,8,8));  // motorola stores them top-bottom
+        }
+
+        if ($type == 'SRATIONAL' && $top > 2147483647) {
+            // this makes the number signed instead of unsigned
+            $top = $top - 4294967296;
+        }
+        if ($bottom != 0) {
+            $data = $top / $bottom;
+        } elseif ($top == 0) {
+            $data = 0;
+        } else {
+            $data = $top . '/' . $bottom;
+        }
+
+        // Exposure Time
+        if ($tag == '829a') {
+            if ($bottom != 0) {
+                $data = $top . '/' . $bottom;
+            } else {
+                $data = 0;
+            }
+        }
+
+    } elseif ($type == 'USHORT' || $type == 'SSHORT' || $type == 'ULONG' ||
+              $type == 'SLONG' || $type == 'FLOAT' || $type == 'DOUBLE') {
+
+        $data = bin2hex($data);
+        if ($intel == 1) {
+            $data = intel2Moto($data);
+        }
+        if ($intel == 0 && ($type == 'USHORT' || $type == 'SSHORT')) {
+            $data = substr($data, 0, 4);
+        }
+        $data = hexdec($data);
+        if ($type == 'SSHORT' && $data > 32767) {
+            // this makes the number signed instead of unsigned
+            $data = $data - 65536;
+        }
+        if ($type == 'SLONG' && $data > 2147483647) {
+            // this makes the number signed instead of unsigned
+            $data = $data - 4294967296;
+        }
+    } elseif ($type == 'UNDEFINED') {
+        // ExifVersion,FlashPixVersion,InteroperabilityVersion
+        if ($tag == '9000' || $tag == 'a000' || $tag == '0002') {
+            $data = sprintf(_("version %d"), $data / 100);
+        }
+    } else {
+        $data = bin2hex($data);
+        if ($intel == 1) $data = intel2Moto($data);
+    }
+
+    return $data;
+}
+
+//==============================================================================
+// Reads one standard IFD entry
+//==============================================================================
+function read_entry(&$result, $in, $seek, $intel, $ifd_name, $globalOffset) {
+
+    // Still ok to read?
+    if (feof($in)) {
+        $result['Errors'] = $result['Errors'] + 1;
+        return;
+    }
+
+    // 2 byte tag
+    $tag = bin2hex(fread($in, 2));
+    if ($intel == 1) $tag = intel2Moto($tag);
+    $tag_name = lookup_tag($tag);
+
+    // 2 byte datatype
+    $type = bin2hex(fread($in, 2));
+    if ($intel == 1) $type = intel2Moto($type);
+    lookup_type($type, $size);
+
+    // 4 byte number of elements
+    $count = bin2hex(fread($in, 4));
+    if ($intel == 1) $count = intel2Moto($count);
+    $bytesofdata = $size * hexdec($count);
+
+    // 4 byte value or pointer to value if larger than 4 bytes
+    $value = fread($in, 4 );
+
+    // if datatype is 4 bytes or less, its the value
+    if ($bytesofdata <= 4) {
+        $data = $value;
+    } elseif ($bytesofdata < 100000) {
+        // otherwise its a pointer to the value, so lets go get it
+        $value = bin2hex($value);
+        if ($intel == 1) {
+            $value = intel2Moto($value);
+        }
+        // offsets are from TIFF header which is 12 bytes from the start of file
+        $v = fseek($seek, $globalOffset+hexdec($value));
+        if ($v == 0) {
+            $data = fread($seek, $bytesofdata);
+        } elseif ($v == -1) {
+            $result['Errors'] = $result['Errors'] + 1;
+        }
+    } else {
+        // bytesofdata was too big, so the exif had an error
+        $result['Errors'] = $result['Errors'] + 1;
+        return;
+    }
+
+    // if its a maker tag, we need to parse this specially
+    if ($tag_name == 'MakerNote') {
+        $make = $result['IFD0']['Make'];
+        if (strpos(strtolower($make), 'nikon') !== false) {
+            include_once ANSEL_BASE . '/lib/Exif/nikon.php';
+            parseNikon($data, $result);
+            $result[$ifd_name]['KnownMaker'] = 1;
+        } elseif (strpos(strtolower($make), 'olympus') !== false) {
+            include_once ANSEL_BASE . '/lib/Exif/olympus.php';
+            parseOlympus($data, $result, $seek, $globalOffset);
+            $result[$ifd_name]['KnownMaker'] = 1;
+        } elseif (strpos(strtolower($make), 'canon') !== false) {
+            include_once ANSEL_BASE . '/lib/Exif/canon.php';
+            parseCanon($data, $result, $seek, $globalOffset);
+            $result[$ifd_name]['KnownMaker'] = 1;
+        } elseif (strpos(strtolower($make), 'fujifilm') !== false) {
+            include_once ANSEL_BASE . '/lib/Exif/fujifilm.php';
+            parseFujifilm($data, $result);
+            $result[$ifd_name]['KnownMaker'] = 1;
+        } elseif (strpos(strtolower($make), 'sanyo') !== false) {
+            include_once ANSEL_BASE . '/lib/Exif/sanyo.php';
+            parseSanyo($data, $result, $seek, $globalOffset);
+            $result[$ifd_name]['KnownMaker'] = 1;
+        } elseif (strpos(strtolower($make), 'panasonic') !== false) {
+            include_once ANSEL_BASE . '/lib/Exif/panasonic.php';
+            parsePanasonic($data, $result, $seek, $globalOffset);
+            $result[$ifd_name]['KnownMaker'] = 1;
+        } else {
+            $result[$ifd_name]['KnownMaker'] = 0;
+        }
+    } elseif ($tag_name == 'GPSInfoOffset') {
+        include_once ANSEL_BASE . '/lib/Exif/gps.php';
+        $formated_data = formatData($type, $tag, $intel, $data);
+        $result[$ifd_name]['GPSInfo'] = $formated_data;
+        parseGPS($data, $result, $formated_data, $seek, $globalOffset);
+    } else {
+        // Format the data depending on the type and tag
+        $formated_data = formatData($type, $tag, $intel, $data);
+        $result[$ifd_name][$tag_name] = $formated_data;
+    }
+}
+
+
+//================================================================================================
+// Pass in a file and this reads the EXIF data
+//
+// Usefull resources
+// http:// www.ba.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html
+// http:// www.w3.org/Graphics/JPEG/jfif.txt
+// http:// exif.org/
+// http:// www.ozhiker.com/electronics/pjmt/library/list_contents.php4
+// http:// www.ozhiker.com/electronics/pjmt/jpeg_info/makernotes.html
+// http:// pel.sourceforge.net/
+// http:// us2.php.net/manual/en/function.exif-read-data.php
+//================================================================================================
+function read_exif_data_raw($path)
+{
+
+    if ($path == '' || $path == 'none') {
+        return;
+    }
+
+    // the b is for windows machines to open in binary mode
+    $in = @fopen($path, 'rb');
+
+    // There may be an elegant way to do this with one file handle.
+    $seek = @fopen($path, 'rb');
+    $globalOffset = 0;
+    $result['Errors'] = 0;
+
+    // if the path was invalid, this error will catch it
+    if (!$in || !$seek) {
+        $result['Errors'] = 1;
+        $result['Error'][$result['Errors']] = _("The file could not be found.");
+        return $result;
+    }
+
+    // First 2 bytes of JPEG are 0xFFD8
+    $data = bin2hex(fread($in, 2));
+    if ($data == 'ffd8') {
+        $result['ValidJpeg'] = 1;
+    } else {
+        $result['ValidJpeg'] = 0;
+        fclose($in);
+        fclose($seek);
+        return $result;
+    }
+
+    $result['ValidIPTCData'] = 0;
+    $result['ValidJFIFData'] = 0;
+    $result['ValidEXIFData'] = 0;
+    $result['ValidAPP2Data'] = 0;
+    $result['ValidCOMData'] = 0;
+
+    // Next 2 bytes are MARKER tag (0xFFE#)
+    $data = bin2hex(fread($in, 2));
+    $size = bin2hex(fread($in, 2));
+
+    // LOOP THROUGH MARKERS TILL YOU GET TO FFE1  (exif marker)
+    while(!feof($in) && $data != 'ffe1' && $data != 'ffc0' && $data != 'ffd9') {
+        if ($data == 'ffe0') { // JFIF Marker
+            $result['ValidJFIFData'] = 1;
+            $result['JFIF']['Size'] = hexdec($size);
+
+            if (hexdec($size) - 2 > 0) {
+                $data = fread($in, hexdec($size) - 2);
+                $result['JFIF']['Data'] = $data;
+            }
+
+            $result['JFIF']['Identifier'] = substr($data, 0, 5);;
+            $result['JFIF']['ExtensionCode'] =  bin2hex(substr($data, 6, 1));
+
+            $globalOffset+=hexdec($size) + 2;
+
+        } elseif ($data == 'ffed') {  // IPTC Marker
+            $result['ValidIPTCData'] = 1;
+            $result['IPTC']['Size'] = hexdec($size);
+
+            if (hexdec($size) - 2 > 0) {
+                $data = fread($in, hexdec($size)-2);
+                $result['IPTC']['Data'] = $data ;
+            }
+            $globalOffset += hexdec($size) + 2;
+
+        } elseif ($data == 'ffe2') {  // EXIF extension Marker
+            $result['ValidAPP2Data'] = 1;
+            $result['APP2']['Size'] = hexdec($size);
+
+            if (hexdec($size)-2 > 0) {
+                $data = fread($in, hexdec($size) - 2);
+                $result['APP2']['Data'] = $data ;
+            }
+            $globalOffset+=hexdec($size) + 2;
+
+        } elseif ($data == 'fffe') {  // COM extension Marker
+            $result['ValidCOMData'] = 1;
+            $result['COM']['Size'] = hexdec($size);
+
+            if (hexdec($size)-2 > 0) {
+                $data = fread($in, hexdec($size) - 2);
+                $result['COM']['Data'] = $data ;
+            }
+            $globalOffset += hexdec($size) + 2;
+
+        } else if ($data == 'ffe1') {
+            $result['ValidEXIFData'] = 1;
+        }
+
+        $data = bin2hex(fread($in, 2));
+        $size = bin2hex(fread($in, 2));
+    }
+    // END MARKER LOOP
+
+    if ($data == 'ffe1') {
+        $result['ValidEXIFData'] = 1;
+    } else {
+        fclose($in);
+        fclose($seek);
+        return $result;
+    }
+
+    // Size of APP1
+    $result['APP1Size'] = hexdec($size);
+
+    // Start of APP1 block starts with 'Exif' header (6 bytes)
+    $header = fread($in, 6);
+
+    // Then theres a TIFF header with 2 bytes of endieness (II or MM)
+    $header = fread($in, 2);
+    if ($header==='II') {
+        $intel = 1;
+        $result['Endien'] = 'Intel';
+    } elseif ($header==='MM') {
+        $intel = 0;
+        $result['Endien'] = 'Motorola';
+    } else {
+        $intel = 1; // not sure what the default should be, but this seems reasonable
+        $result['Endien'] = 'Unknown';
+    }
+
+    // 2 bytes of 0x002a
+    $tag = bin2hex(fread( $in, 2 ));
+
+    // Then 4 bytes of offset to IFD0 (usually 8 which includes all 8 bytes of TIFF header)
+    $offset = bin2hex(fread($in, 4));
+    if ($intel == 1) {
+        $offset = intel2Moto($offset);
+    }
+
+    // Check for extremely large values here
+    if (hexdec($offset) > 100000) {
+        $result['ValidEXIFData'] = 0;
+        fclose($in);
+        fclose($seek);
+        return $result;
+    }
+
+    if (hexdec($offset) > 8) {
+        $unknown = fread($in, hexdec($offset) - 8);
+    }
+
+    // add 12 to the offset to account for TIFF header
+    $globalOffset += 12;
+
+    //===========================================================
+    // Start of IFD0
+    $num = bin2hex(fread($in, 2));
+    if ($intel == 1) {
+        $num = intel2Moto($num);
+    }
+    $num = hexdec($num);
+    $result['IFD0NumTags'] = $num;
+
+    // 1000 entries is too much and is probably an error.
+    if ($num < 1000) {
+        for($i = 0; $i < $num; $i++) {
+            read_entry($result, $in, $seek, $intel, 'IFD0', $globalOffset);
+        }
+    } else {
+        $result['Errors'] = $result['Errors'] + 1;
+        $result['Error'][$result['Errors']] = 'Illegal size for IFD0';
+    }
+
+    // store offset to IFD1
+    $offset = bin2hex(fread($in, 4));
+    if ($intel == 1) {
+        $offset = intel2Moto($offset);
+    }
+    $result['IFD1Offset'] = hexdec($offset);
+
+    // Check for SubIFD
+    if (!isset($result['IFD0']['ExifOffset']) || $result['IFD0']['ExifOffset'] == 0) {
+        fclose($in);
+        fclose($seek);
+        return $result;
+    }
+
+    // seek to SubIFD (Value of ExifOffset tag) above.
+    $ExitOffset = $result['IFD0']['ExifOffset'];
+    $v = fseek($in, $globalOffset + $ExitOffset);
+    if ($v == -1) {
+        $result['Errors'] = $result['Errors'] + 1;
+        $result['Error'][$result['Errors']] = _("Couldnt Find SubIFD");
+    }
+
+    //===========================================================
+    // Start of SubIFD
+    $num = bin2hex(fread($in, 2));
+    if ($intel == 1) {
+        $num = intel2Moto($num);
+    }
+    $num = hexdec($num);
+    $result['SubIFDNumTags'] = $num;
+
+    // 1000 entries is too much and is probably an error.
+    if ($num < 1000) {
+        for($i = 0; $i < $num; $i++) {
+            read_entry($result, $in, $seek, $intel, 'SubIFD', $globalOffset);
+        }
+    } else {
+        $result['Errors'] = $result['Errors'] + 1;
+        $result['Error'][$result['Errors']] = _("Illegal size for SubIFD");
+    }
+
+    // Add the 35mm equivalent focal length:
+    // Now properly get this using the FocalLength35mmFilm tag
+    //$result['SubIFD']['FocalLength35mmEquiv'] = get35mmEquivFocalLength($result);
+
+    // Check for IFD1
+    if (!isset($result['IFD1Offset']) || $result['IFD1Offset'] == 0) {
+        fclose($in);
+        fclose($seek);
+        return $result;
+    }
+
+    // seek to IFD1
+    $v = fseek($in, $globalOffset + $result['IFD1Offset']);
+    if ($v == -1) {
+        $result['Errors'] = $result['Errors'] + 1;
+        $result['Error'][$result['Errors']] = _("Couldnt Find IFD1");
+    }
+
+    //===========================================================
+    // Start of IFD1
+    $num = bin2hex(fread($in, 2));
+    if ($intel == 1) {
+        $num = intel2Moto($num);
+    }
+    $num = hexdec($num);
+    $result['IFD1NumTags'] = $num;
+
+    // 1000 entries is too much and is probably an error.
+    if ($num < 1000) {
+        for($i = 0; $i < $num; $i++) {
+            read_entry($result, $in, $seek, $intel, 'IFD1', $globalOffset);
+        }
+    } else {
+        $result['Errors'] = $result['Errors'] + 1;
+        $result['Error'][$result['Errors']] = _("Illegal size for IFD1");
+    }
+    // include the thumbnail raw data...
+    if ($result['IFD1']['JpegIFOffset'] > 0 &&
+        $result['IFD1']['JpegIFByteCount'] > 0) {
+
+        $v = fseek($seek, $globalOffset + $result['IFD1']['JpegIFOffset']);
+        if ($v == 0) {
+            $data = fread($seek, $result['IFD1']['JpegIFByteCount']);
+        } else if ($v == -1) {
+            $result['Errors'] = $result['Errors'] + 1;
+        }
+        $result['IFD1']['ThumbnailData'] = $data;
+    }
+
+    // Check for Interoperability IFD
+    if (!isset($result['SubIFD']['ExifInteroperabilityOffset']) ||
+        $result['SubIFD']['ExifInteroperabilityOffset'] == 0) {
+
+        fclose($in);
+        fclose($seek);
+        return $result;
+    }
+
+    // Seek to InteroperabilityIFD
+    $v = fseek($in, $globalOffset + $result['SubIFD']['ExifInteroperabilityOffset']);
+    if ($v == -1) {
+        $result['Errors'] = $result['Errors'] + 1;
+        $result['Error'][$result['Errors']] = _("Couldnt Find InteroperabilityIFD");
+    }
+
+    //===========================================================
+    // Start of InteroperabilityIFD
+    $num = bin2hex(fread($in, 2));
+    if ($intel == 1) {
+        $num = intel2Moto($num);
+    }
+    $num = hexdec($num);
+    $result['InteroperabilityIFDNumTags'] = $num;
+
+    // 1000 entries is too much and is probably an error.
+    if ($num < 1000) {
+        for($i = 0; $i < $num; $i++) {
+            read_entry($result, $in, $seek, $intel, 'InteroperabilityIFD', $globalOffset);
+        }
+    } else {
+        $result['Errors'] = $result['Errors'] + 1;
+        $result['Error'][$result['Errors']] = _("Illegal size for InteroperabilityIFD");
+    }
+    fclose($in);
+    fclose($seek);
+    return $result;
+}
diff --git a/ansel/lib/Exif/canon.php b/ansel/lib/Exif/canon.php
new file mode 100644 (file)
index 0000000..f2ec75c
--- /dev/null
@@ -0,0 +1,532 @@
+<?php
+/**
+ *   Exifer
+ *   Extracts EXIF information from digital photos.
+ *
+ *   $Horde: ansel/lib/Exif/canon.php,v 1.11 2008/12/30 22:51:17 mrubinsk Exp $
+ *
+ *   Copyright 2003 Jake Olefsky
+ *   http://www.offsky.com/software/exif/index.php
+ *   jake@olefsky.com
+ *
+ *   Please see exif.php for the complete information about this software.
+ *
+ *   ------------
+ *
+ *   This program is free software; you can redistribute it and/or modify it under the terms of
+ *   the GNU General Public License as published by the Free Software Foundation; either version 2
+ *   of the License, or (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ *   without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *   See the GNU General Public License for more details. http://www.gnu.org/copyleft/gpl.html
+ */
+
+/**
+ *Looks up the name of the tag for the MakerNote (Depends on Manufacturer)
+ */
+function lookup_Canon_tag($tag)
+{
+    switch($tag) {
+    case '0001': $tag = 'Settings 1'; break;
+    case '0004': $tag = 'Settings 4'; break;
+    case '0006': $tag = 'ImageType'; break;
+    case '0007': $tag = 'FirmwareVersion'; break;
+    case '0008': $tag = 'ImageNumber'; break;
+    case '0009': $tag = 'OwnerName'; break;
+    case '000c': $tag = 'CameraSerialNumber'; break;
+    case '000f': $tag = 'CustomFunctions'; break;
+    default: $tag = sprintf(_("Unknown: (%s)"), $tag); break;
+    }
+
+    return $tag;
+}
+
+/**
+ * Formats Data for the data type
+ */
+function formatCanonData($type, $tag, $intel, $data, $exif, &$result)
+{
+    $place = 0;
+
+    if ($type == 'ASCII') {
+        $result = $data = str_replace('\0', '', $data);
+    } elseif ($type == 'URATIONAL' || $type == 'SRATIONAL') {
+        $data = bin2hex($data);
+        if ($intel == 1) {
+            $data = intel2Moto($data);
+        }
+        $top = hexdec(substr($data, 8, 8));
+        $bottom = hexdec(substr($data, 0, 8));
+        if ($bottom != 0) {
+            $data = $top / $bottom;
+        } elseif ($top == 0) {
+            $data = 0;
+        } else {
+            $data = $top . '/' . $bottom;
+        }
+
+        if ($tag == '0204') { //DigitalZoom
+            $data = $data . 'x';
+        }
+    } elseif ($type == 'USHORT' || $type == 'SSHORT' || $type == 'ULONG' ||
+              $type == 'SLONG' || $type == 'FLOAT' || $type == 'DOUBLE') {
+
+        $data = bin2hex($data);
+        $result['RAWDATA'] = $data;
+
+        if ($tag == '0001') { //first chunk
+            $result['Bytes'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;
+            if ($result['Bytes'] != strlen($data) / 2) {
+                return $result; //Bad chunk
+            }
+            $result['Macro'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//1
+            switch($result['Macro']) {
+            case 1: $result['Macro'] = _("Macro"); break;
+            case 2: $result['Macro'] = _("Normal"); break;
+            default: $result['Macro'] = _("Unknown");
+            }
+            $result['SelfTimer'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//2
+            switch($result['SelfTimer']) {
+            case 0: $result['SelfTimer'] = _("Off"); break;
+            default: $result['SelfTimer'] .= _("/10s");
+            }
+            $result['Quality'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//3
+            switch($result['Quality']) {
+            case 2: $result['Quality'] = _("Normal"); break;
+            case 3: $result['Quality'] = _("Fine"); break;
+            case 5: $result['Quality'] = _("Superfine"); break;
+            default: $result['Quality'] = _("Unknown");
+            }
+            $result['Flash'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//4
+            switch($result['Flash']) {
+            case 0: $result['Flash'] = _("Off"); break;
+            case 1: $result['Flash'] = _("Auto"); break;
+            case 2: $result['Flash'] = _("On"); break;
+            case 3: $result['Flash'] = _("Red Eye Reduction"); break;
+            case 4: $result['Flash'] = _("Slow Synchro"); break;
+            case 5: $result['Flash'] = _("Auto + Red Eye Reduction"); break;
+            case 6: $result['Flash'] = _("On + Red Eye Reduction"); break;
+            case 16: $result['Flash'] = _("External Flash"); break;
+            default: $result['Flash'] = _("Unknown");
+            }
+            $result['DriveMode'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//5
+            switch($result['DriveMode']) {
+            case 0: $result['DriveMode'] = _("Single/Timer"); break;
+            case 1: $result['DriveMode'] = _("Continuous"); break;
+            default: $result['DriveMode'] = _("Unknown");
+            }
+            $result['Unknown'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//6
+            $result['FocusMode'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//7
+            switch($result['FocusMode']) {
+            case 0: $result['FocusMode'] = _("One-Shot"); break;
+            case 1: $result['FocusMode'] = _("AI Servo"); break;
+            case 2: $result['FocusMode'] = _("AI Focus"); break;
+            case 3: $result['FocusMode'] = _("Manual Focus"); break;
+            case 4: $result['FocusMode'] = _("Single"); break;
+            case 5: $result['FocusMode'] = _("Continuous"); break;
+            case 6: $result['FocusMode'] = _("Manual Focus"); break;
+            default: $result['FocusMode'] = _("Unknown");
+            }
+            $result['Unknown'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//8
+            $result['Unknown'] = hexdec(intel2Moto(substr($data, $place,4 )));
+            $place+=4;//9
+            $result['ImageSize'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//10
+            switch($result['ImageSize']) {
+            case 0: $result['ImageSize'] = _("Large"); break;
+            case 1: $result['ImageSize'] = _("Medium"); break;
+            case 2: $result['ImageSize'] = _("Small"); break;
+            default: $result['ImageSize'] = _("Unknown");
+            }
+            $result['EasyShooting'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//11
+            switch($result['EasyShooting']) {
+            case 0: $result['EasyShooting'] = _("Full Auto"); break;
+            case 1: $result['EasyShooting'] = _("Manual"); break;
+            case 2: $result['EasyShooting'] = _("Landscape"); break;
+            case 3: $result['EasyShooting'] = _("Fast Shutter"); break;
+            case 4: $result['EasyShooting'] = _("Slow Shutter"); break;
+            case 5: $result['EasyShooting'] = _("Night"); break;
+            case 6: $result['EasyShooting'] = _("Black & White"); break;
+            case 7: $result['EasyShooting'] = _("Sepia"); break;
+            case 8: $result['EasyShooting'] = _("Portrait"); break;
+            case 9: $result['EasyShooting'] = _("Sport"); break;
+            case 10: $result['EasyShooting'] = _("Macro/Close-Up"); break;
+            case 11: $result['EasyShooting'] = _("Pan Focus"); break;
+            default: $result['EasyShooting'] = _("Unknown");
+            }
+            $result['DigitalZoom'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//12
+            switch($result['DigitalZoom']) {
+            case 0:
+            case 65535: $result['DigitalZoom'] = _("None"); break;
+            case 1: $result['DigitalZoom'] = _("2x"); break;
+            case 2: $result['DigitalZoom'] = _("4x"); break;
+            default: $result['DigitalZoom'] = _("Unknown");
+            }
+            $result['Contrast'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//13
+            switch($result['Contrast']) {
+            case 0: $result['Contrast'] = _("Normal"); break;
+            case 1: $result['Contrast'] = _("High"); break;
+            case 65535: $result['Contrast'] = _("Low"); break;
+            default: $result['Contrast'] = _("Unknown");
+            }
+            $result['Saturation'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//14
+            switch($result['Saturation']) {
+               case 0: $result['Saturation'] = _("Normal"); break;
+                case 1: $result['Saturation'] = _("High"); break;
+                case 65535: $result['Saturation'] = _("Low"); break;
+                default: $result['Saturation'] = _("Unknown");
+            }
+            $result['Sharpness'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//15
+            switch($result['Sharpness']) {
+            case 0: $result['Sharpness'] = _("Normal"); break;
+            case 1: $result['Sharpness'] = _("High"); break;
+            case 65535: $result['Sharpness'] = _("Low"); break;
+            default: $result['Sharpness'] = _("Unknown");
+            }
+            $result['ISO'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//16
+            switch($result['ISO']) {
+            case 32767:
+            case 0:
+                $result['ISO'] = isset($exif['SubIFD']['ISOSpeedRatings']) ?
+                    $exif['SubIFD']['ISOSpeedRatings'] :
+                    'Unknown';
+                 break;
+            case 15:
+                $result['ISO'] = _("Auto");
+                break;
+            case 16:
+                $result['ISO'] = 50;
+                 break;
+            case 17:
+                $result['ISO'] = 100;
+                 break;
+            case 18:
+                $result['ISO'] = 200;
+                 break;
+            case 19:
+                $result['ISO'] = 400;
+                 break;
+            default:
+                $result['ISO'] = _("Unknown");
+            }
+            $result['MeteringMode'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//17
+            switch($result['MeteringMode']) {
+            case 3: $result['MeteringMode'] = _("Evaluative"); break;
+            case 4: $result['MeteringMode'] = _("Partial"); break;
+            case 5: $result['MeteringMode'] = _("Center-weighted"); break;
+            default: $result['MeteringMode'] = _("Unknown");
+            }
+            $result['FocusType'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//18
+            switch($result['FocusType']) {
+            case 0: $result['FocusType'] = _("Manual"); break;
+            case 1: $result['FocusType'] = _("Auto"); break;
+            case 3: $result['FocusType'] = _("Close-up (Macro)"); break;
+            case 8: $result['FocusType'] = _("Locked (Pan Mode)"); break;
+            default: $result['FocusType'] = _("Unknown");
+            }
+            $result['AFPointSelected'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//19
+            switch($result['AFPointSelected']) {
+            case 12288: $result['AFPointSelected'] = _("Manual Focus"); break;
+            case 12289: $result['AFPointSelected'] = _("Auto Selected"); break;
+            case 12290: $result['AFPointSelected'] = _("Right"); break;
+            case 12291: $result['AFPointSelected'] = _("Center"); break;
+            case 12292: $result['AFPointSelected'] = _("Left"); break;
+            default: $result['AFPointSelected'] = _("Unknown");
+            }
+            $result['ExposureMode'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//20
+            switch($result['ExposureMode']) {
+            case 0: $result['ExposureMode'] = _("EasyShoot"); break;
+            case 1: $result['ExposureMode'] = _("Program"); break;
+            case 2: $result['ExposureMode'] = _("Tv"); break;
+            case 3: $result['ExposureMode'] = _("Av"); break;
+            case 4: $result['ExposureMode'] = _("Manual"); break;
+            case 5: $result['ExposureMode'] = _("Auto-DEP"); break;
+            default: $result['ExposureMode'] = _("Unknown");
+            }
+            $result['Unknown'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//21
+            $result['Unknown'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//22
+            $result['LongFocalLength'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//23
+            $result['LongFocalLength'] .=  'focal units';
+            $result['ShortFocalLength'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//24
+            $result['ShortFocalLength'] .= ' focal units';
+            $result['FocalUnits'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//25
+             $result['FocalUnits'] .= ' per mm';
+            $result['Unknown'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//26
+            $result['Unknown'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//27
+            $result['FlashActivity'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//28
+            switch($result['FlashActivity']) {
+            case 0: $result['FlashActivity'] = _("Flash Did Not Fire"); break;
+            case 1: $result['FlashActivity'] = _("Flash Fired"); break;
+            default: $result['FlashActivity'] = _("Unknown");
+            }
+            $result['FlashDetails'] = str_pad(base_convert(intel2Moto(substr($data, $place, 4)), 16, 2), 16, '0', STR_PAD_LEFT);
+            $place += 4;//29
+            $flashDetails = array();
+            if (substr($result['FlashDetails'], 1, 1) == 1) {
+                $flashDetails[] = _("External E-TTL");
+            }
+            if (substr($result['FlashDetails'], 2, 1) == 1) {
+                $flashDetails[] = _("Internal Flash");
+            }
+            if (substr($result['FlashDetails'], 4, 1) == 1) {
+                $flashDetails[] = _("FP sync used");
+            }
+            if (substr($result['FlashDetails'], 8, 1) == 1) {
+                $flashDetails[] = _("2nd(rear)-curtain sync used");
+             }
+            if (substr($result['FlashDetails'], 12, 1) == 1) {
+                $flashDetails[] = _("1st curtain sync");
+            }
+            $result['FlashDetails'] = implode(',', $flashDetails);
+            $result['Unknown'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//30
+            $result['Unknown'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//31
+            $anotherFocusMode = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//32
+            if (strpos(strtoupper($exif['IFD0']['Model']), 'G1') !== false) {
+                switch($anotherFocusMode) {
+                case 0: $result['FocusMode'] = _("Single"); break;
+                case 1: $result['FocusMode'] = _("Continuous"); break;
+                default: $result['FocusMode'] = _("Unknown");
+                }
+            }
+
+        } elseif ($tag=='0004') { //second chunk
+            $result['Bytes']=hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//0
+            if ($result['Bytes'] != strlen($data) / 2) {
+                return $result; //Bad chunk
+            }
+            $result['Unknown'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//1
+            $result['Unknown'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//2
+            $result['Unknown'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//3
+            $result['Unknown'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//4
+            $result['Unknown'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//5
+            $result['Unknown'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//6
+            $result['WhiteBalance'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//7
+            switch($result['WhiteBalance']) {
+            case 0: $result['WhiteBalance'] = _("Auto"); break;
+            case 1: $result['WhiteBalance'] = _("Sunny"); break;
+            case 2: $result['WhiteBalance'] = _("Cloudy"); break;
+            case 3: $result['WhiteBalance'] = _("Tungsten"); break;
+            case 4: $result['WhiteBalance'] = _("Fluorescent"); break;
+            case 5: $result['WhiteBalance'] = _("Flash"); break;
+            case 6: $result['WhiteBalance'] = _("Custom"); break;
+            default: $result['WhiteBalance'] = _("Unknown");
+            }
+            $result['Unknown'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//8
+            $result['SequenceNumber'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//9
+            $result['Unknown'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//10
+            $result['Unknown'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//11
+            $result['Unknown'] = hexdec(intel2Moto(substr($data ,$place, 4)));
+            $place += 4;//12
+            $result['Unknown'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//13
+            $result['AFPointUsed']=hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//14
+            $afPointUsed = array();
+            if ($result['AFPointUsed'] & 0x0001) {
+                $afPointUsed[] = _("Right"); //bit 0
+            }
+            if ($result['AFPointUsed'] & 0x0002) {
+                $afPointUsed[] = _("Center"); //bit 1
+            }
+            if ($result['AFPointUsed'] & 0x0004) {
+                $afPointUsed[] = _("Left"); //bit 2
+            }
+            if ($result['AFPointUsed'] & 0x0800) {
+                $afPointUsed[] = 12; //bit 12
+            }
+            if ($result['AFPointUsed'] & 0x1000) {
+                $afPointUsed[] = 13; //bit 13
+            }
+            if ($result['AFPointUsed'] & 0x2000) {
+                $afPointUsed[] = 14; //bit 14
+            }
+            if ($result['AFPointUsed'] & 0x4000) {
+                $afPointUsed[] = 15; //bit 15
+            }
+            $result['AFPointUsed'] = implode(',', $afPointUsed);
+            $result['FlashBias'] = intel2Moto(substr($data, $place, 4));
+            $place += 4;//15
+            switch($result['FlashBias']) {
+            case 'ffc0': $result['FlashBias'] = '-2 EV'; break;
+            case 'ffcc': $result['FlashBias'] = '-1.67 EV'; break;
+            case 'ffd0': $result['FlashBias'] = '-1.5 EV'; break;
+            case 'ffd4': $result['FlashBias'] = '-1.33 EV'; break;
+            case 'ffe0': $result['FlashBias'] = '-1 EV'; break;
+            case 'ffec': $result['FlashBias'] = '-0.67 EV'; break;
+            case 'fff0': $result['FlashBias'] = '-0.5 EV'; break;
+            case 'fff4': $result['FlashBias'] = '-0.33 EV'; break;
+            case '0000': $result['FlashBias'] = '0 EV'; break;
+            case '000c': $result['FlashBias'] = '0.33 EV'; break;
+            case '0010': $result['FlashBias'] = '0.5 EV'; break;
+            case '0014': $result['FlashBias'] = '0.67 EV'; break;
+            case '0020': $result['FlashBias'] = '1 EV'; break;
+            case '002c': $result['FlashBias'] = '1.33 EV'; break;
+            case '0030': $result['FlashBias'] = '1.5 EV'; break;
+            case '0034': $result['FlashBias'] = '1.67 EV'; break;
+            case '0040': $result['FlashBias'] = '2 EV'; break;
+            default: $result['FlashBias'] = _("Unknown");
+            }
+            $result['Unknown'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//16
+            $result['Unknown'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//17
+            $result['Unknown'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//18
+            $result['SubjectDistance'] = hexdec(intel2Moto(substr($data, $place, 4)));
+            $place += 4;//19
+            $result['SubjectDistance'] .= '/100 m';
+
+        } elseif ($tag=='0008') { //image number
+            if ($intel == 1) {
+                $data = intel2Moto($data);
+            }
+            $data = hexdec($data);
+            $result = round($data / 10000) . '-' . $data % 10000;
+        } elseif ($tag == '000c') { //camera serial number
+            if ($intel == 1) {
+                $data = intel2Moto($data);
+            }
+            $data = hexdec($data);
+            $result = '#' . bin2hex(substr($data, 0, 16)) . substr($data, 16, 16);
+        }
+
+    } elseif ($type != 'UNDEFINED') {
+        $data = bin2hex($data);
+        if ($intel == 1) {
+            $data = intel2Moto($data);
+        }
+    }
+
+    return $data;
+}
+
+/**
+ * Cannon Special data section
+ * Useful:  http://www.burren.cx/david/canon.html
+ * http://www.burren.cx/david/canon.html
+ * http://www.ozhiker.com/electronics/pjmt/jpeg_info/canon_mn.html
+ */
+function parseCanon($block, &$result, $seek, $globalOffset)
+{
+    $place = 0; //current place
+    if ($result['Endien'] == 'Intel') {
+        $intel = 1;
+    } else {
+        $intel = 0;
+    }
+
+    $model = $result['IFD0']['Model'];
+
+    //Get number of tags (2 bytes)
+    $num = bin2hex(substr($block, $place, 2));
+    $place += 2;
+    if ($intel == 1) {
+        $num = intel2Moto($num);
+    }
+    $result['SubIFD']['MakerNote']['MakerNoteNumTags'] = hexdec($num);
+
+    //loop thru all tags  Each field is 12 bytes
+    for ($i = 0; $i < hexdec($num); $i++) {
+         //2 byte tag
+        $tag = bin2hex(substr($block, $place, 2));
+        $place += 2;
+        if ($intel == 1) {
+            $tag = intel2Moto($tag);
+        }
+        $tag_name = lookup_Canon_tag($tag);
+
+        //2 byte type
+        $type = bin2hex(substr($block, $place, 2));
+        $place += 2;
+        if ($intel == 1) {
+            $type = intel2Moto($type);
+        }
+        lookup_type($type, $size);
+
+        //4 byte count of number of data units
+        $count = bin2hex(substr($block, $place, 4));
+        $place += 4;
+        if ($intel == 1) {
+            $count = intel2Moto($count);
+        }
+        $bytesofdata = $size * hexdec($count);
+
+        if ($bytesofdata <= 0) {
+            return; //if this value is 0 or less then we have read all the tags we can
+        }
+
+            //4 byte value of data or pointer to data
+        $value = substr($block, $place, 4);
+        $place += 4;
+
+        if ($bytesofdata <= 4) {
+            $data = $value;
+        } else {
+            $value = bin2hex($value);
+            if ($intel == 1) {
+                $value = intel2Moto($value);
+            }
+            $v = fseek($seek, $globalOffset+hexdec($value));  //offsets are from TIFF header which is 12 bytes from the start of the file
+            if (isset($GLOBALS['exiferFileSize'])) {
+                $exiferFileSize = $GLOBALS['exiferFileSize'];
+            } else {
+                $exiferFileSize = 0;
+            }
+            if ($v == 0 && $bytesofdata < $exiferFileSize) {
+                $data = fread($seek, $bytesofdata);
+            } elseif ($v == -1) {
+                $result['Errors'] = $result['Errors']++;
+                $data = '';
+            } else {
+                $data = '';
+            }
+        }
+        $result['SubIFD']['MakerNote'][$tag_name] = ''; // insure the index exists
+        $formated_data = formatCanonData($type, $tag, $intel, $data, $result, $result['SubIFD']['MakerNote'][$tag_name]);
+        $result['SubIFD']['MakerNote'][$tag_name] = $formated_data;
+    }
+
+}
\ No newline at end of file
diff --git a/ansel/lib/Exif/fujifilm.php b/ansel/lib/Exif/fujifilm.php
new file mode 100644 (file)
index 0000000..aace44f
--- /dev/null
@@ -0,0 +1,236 @@
+<?php
+/**
+ *   Exifer
+ *   Extracts EXIF information from digital photos.
+ *
+ *   Copyright � 2003 Jake Olefsky
+ *   http://www.offsky.com/software/exif/index.php
+ *   jake@olefsky.com
+ *
+ *   Please see exif.php for the complete information about this software.
+ *
+ *   ------------
+ *
+ *  This program is free software; you can redistribute it and/or modify it under the terms of
+ *    the GNU General Public License as published by the Free Software Foundation; either version 2
+ *    of the License, or (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ *   without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *   See the GNU General Public License for more details. http://www.gnu.org/copyleft/gpl.html
+ */
+
+/**
+ * Looks up the name of the tag for the MakerNote (Depends on Manufacturer)
+ */
+function lookup_Fujifilm_tag($tag) {
+
+    switch($tag) {
+        case "0000": $tag = "Version";break;
+        case "1000": $tag = "Quality";break;
+        case "1001": $tag = "Sharpness";break;
+        case "1002": $tag = "WhiteBalance";break;
+        case "1003": $tag = "Color";break;
+        case "1004": $tag = "Tone";break;
+        case "1010": $tag = "FlashMode";break;
+        case "1011": $tag = "FlashStrength";break;
+        case "1020": $tag = "Macro";break;
+        case "1021": $tag = "FocusMode";break;
+        case "1030": $tag = "SlowSync";break;
+        case "1031": $tag = "PictureMode";break;
+        case "1032": $tag = "Unknown";break;
+        case "1100": $tag = "ContinuousTakingBracket";break;
+        case "1200": $tag = "Unknown";break;
+        case "1300": $tag = "BlurWarning";break;
+        case "1301": $tag = "FocusWarning";break;
+        case "1302": $tag = "AEWarning";break;
+
+        default: $tag = "unknown:".$tag;break;
+    }
+
+    return $tag;
+}
+
+//=================
+// Formats Data for the data type
+//====================================================================
+function formatFujifilmData($type,$tag,$intel,$data) {
+
+    if($type=="ASCII") {
+
+
+    } else if($type=="URATIONAL" || $type=="SRATIONAL") {
+        $data = bin2hex($data);
+        if($intel==1) $data = intel2Moto($data);
+        $top = hexdec(substr($data,8,8));
+        $bottom = hexdec(substr($data,0,8));
+        if($bottom!=0) $data=$top/$bottom;
+        else if($top==0) $data = 0;
+        else $data=$top."/".$bottom;
+
+        if($tag=="1011") { //FlashStrength
+            $data=$data." EV";
+        }
+
+    } else if($type=="USHORT" || $type=="SSHORT" || $type=="ULONG" || $type=="SLONG" || $type=="FLOAT" || $type=="DOUBLE") {
+        $data = bin2hex($data);
+        if($intel==1) $data = intel2Moto($data);
+        $data=hexdec($data);
+
+        if($tag=="1001") { //Sharpness
+            if($data == 1) $data = _("Soft");
+            else if($data == 2) $data = _("Soft");
+            else if($data == 3) $data = _("Normal");
+            else if($data == 4) $data = _("Hard");
+            else if($data == 5) $data = _("Hard");
+            else $data = _("Unknown").": ".$data;
+        }
+        if($tag=="1002") { //WhiteBalance
+            if($data == 0) $data = _("Auto");
+            else if($data == 256) $data = _("Daylight");
+            else if($data == 512) $data = _("Cloudy");
+            else if($data == 768) $data = _("DaylightColor-fluorescence");
+            else if($data == 769) $data = _("DaywhiteColor-fluorescence");
+            else if($data == 770) $data = _("White-fluorescence");
+            else if($data == 1024) $data = _("Incandescense");
+            else if($data == 3840) $data = _("Custom");
+            else $data = _("Unknown").": ".$data;
+        }
+        if($tag=="1003") { //Color
+            if($data == 0) $data = _("Chroma Saturation Normal(STD)");
+            else if($data == 256) $data = _("Chroma Saturation High");
+            else if($data == 512) $data = _("Chroma Saturation Low(ORG)");
+            else $data = _("Unknown: ").$data;
+        }
+        if($tag=="1004") { //Tone
+            if($data == 0) $data = _("Contrast Normal(STD)");
+            else if($data == 256) $data = _("Contrast High(HARD)");
+            else if($data == 512) $data = _("Contrast Low(ORG)");
+            else $data = _("Unknown: ").$data;
+        }
+        if($tag=="1010") { //FlashMode
+            if($data == 0) $data = _("Auto");
+            else if($data == 1) $data = _("On");
+            else if($data == 2) $data = _("Off");
+            else if($data == 3) $data = _("Red-Eye Reduction");
+            else $data = _("Unknown: ").$data;
+        }
+        if($tag=="1020") { //Macro
+            if($data == 0) $data = _("Off");
+            else if($data == 1) $data = _("On");
+            else $data = _("Unknown: ").$data;
+        }
+        if($tag=="1021") { //FocusMode
+            if($data == 0) $data = _("Auto");
+            else if($data == 1) $data = _("Manual");
+            else $data = _("Unknown: ").$data;
+        }
+        if($tag=="1030") { //SlowSync
+            if($data == 0) $data = _("Off");
+            else if($data == 1) $data = _("On");
+            else $data = _("Unknown: ").$data;
+        }
+        if($tag=="1031") { //PictureMode
+            if($data == 0) $data = _("Auto");
+            else if($data == 1) $data = _("Portrait");
+            else if($data == 2) $data = _("Landscape");
+            else if($data == 4) $data = _("Sports");
+            else if($data == 5) $data = _("Night");
+            else if($data == 6) $data = _("Program AE");
+            else if($data == 256) $data = _("Aperture Prority AE");
+            else if($data == 512) $data = _("Shutter Priority");
+            else if($data == 768) $data = _("Manual Exposure");
+            else $data = _("Unknown: ").$data;
+        }
+        if($tag=="1100") { //ContinuousTakingBracket
+            if($data == 0) $data = _("Off");
+            else if($data == 1) $data = _("On");
+            else $data = _("Unknown: ").$data;
+        }
+        if($tag=="1300") { //BlurWarning
+            if($data == 0) $data = _("No Warning");
+            else if($data == 1) $data = _("Warning");
+            else $data = _("Unknown: ").$data;
+        }
+        if($tag=="1301") { //FocusWarning
+            if($data == 0) $data = _("Auto Focus Good");
+            else if($data == 1) $data = _("Out of Focus");
+            else $data = _("Unknown: ").$data;
+        }
+        if($tag=="1302") { //AEWarning
+            if($data == 0) $data = _("AE Good");
+            else if($data == 1) $data = _("Over Exposure");
+            else $data = _("Unknown: ").$data;
+        }
+    } else if($type=="UNDEFINED") {
+
+
+
+    } else {
+        $data = bin2hex($data);
+        if($intel==1) $data = intel2Moto($data);
+    }
+
+    return $data;
+}
+
+
+
+//=================
+// Fujifilm Special data section
+//====================================================================
+function parseFujifilm($block,&$result) {
+
+    //if($result['Endien']=="Intel") $intel=1;
+    //else $intel=0;
+    $intel=1;
+
+    $model = $result['IFD0']['Model'];
+
+    $place=8; //current place
+    $offset=8;
+
+
+    $num = bin2hex(substr($block,$place,4));$place+=4;
+    if($intel==1) $num = intel2Moto($num);
+    $result['SubIFD']['MakerNote']['Offset'] = hexdec($num);
+
+        //Get number of tags (2 bytes)
+    $num = bin2hex(substr($block,$place,2));$place+=2;
+    if($intel==1) $num = intel2Moto($num);
+    $result['SubIFD']['MakerNote']['MakerNoteNumTags'] = hexdec($num);
+
+    //loop thru all tags  Each field is 12 bytes
+    for($i=0;$i<hexdec($num);$i++) {
+
+            //2 byte tag
+        $tag = bin2hex(substr($block,$place,2));$place+=2;
+        if($intel==1) $tag = intel2Moto($tag);
+        $tag_name = lookup_Fujifilm_tag($tag);
+
+            //2 byte type
+        $type = bin2hex(substr($block,$place,2));$place+=2;
+        if($intel==1) $type = intel2Moto($type);
+        lookup_type($type,$size);
+
+            //4 byte count of number of data units
+        $count = bin2hex(substr($block,$place,4));$place+=4;
+        if($intel==1) $count = intel2Moto($count);
+        $bytesofdata = $size*hexdec($count);
+
+            //4 byte value of data or pointer to data
+        $value = substr($block,$place,4);$place+=4;
+
+
+        if($bytesofdata<=4) {
+            $data = $value;
+        } else {
+            $value = bin2hex($value);
+            if($intel==1) $value = intel2Moto($value);
+            $data = substr($block,hexdec($value)-$offset,$bytesofdata*2);
+        }
+        $formated_data = formatFujifilmData($type,$tag,$intel,$data);
+        $result['SubIFD']['MakerNote'][$tag_name] = $formated_data;
+    }
+
+}
\ No newline at end of file
diff --git a/ansel/lib/Exif/gps.php b/ansel/lib/Exif/gps.php
new file mode 100644 (file)
index 0000000..698560b
--- /dev/null
@@ -0,0 +1,271 @@
+<?php
+/**
+    Exifer
+    Extracts EXIF information from digital photos.
+
+    Copyright © 2003 Jake Olefsky
+    http://www.offsky.com/software/exif/index.php
+    jake@olefsky.com
+
+    Please see exif.php for the complete information about this software.
+
+    ------------
+
+    This program is free software; you can redistribute it and/or modify it under the terms of
+    the GNU General Public License as published by the Free Software Foundation; either version 2
+    of the License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+    without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+    See the GNU General Public License for more details. http://www.gnu.org/copyleft/gpl.html
+*/
+
+/**
+ * Looks up the name of the tag
+ *
+ * @param unknown_type $tag
+ * @return string
+ */
+function lookup_GPS_tag($tag)
+{
+
+    switch($tag) {
+        case "0000": return "Version";
+        case "0001": return "LatitudeRef";              //north or south
+        case "0002": return "Latitude";                                    //dd mm.mm or dd mm ss
+        case "0003": return "LongitudeRef";                    //east or west
+        case "0004": return "Longitude";                               //dd mm.mm or dd mm ss
+        case "0005": return "AltitudeRef";                         //sea level or below sea level
+        case "0006": return "Altitude";                                        //positive rational number
+        case "0007": return "Time";                                            //three positive rational numbers
+        case "0008": return "Satellite";                               //text string up to 999 bytes long
+        case "0009": return "ReceiveStatus";                   //in progress or interop
+        case "000a": return "MeasurementMode";             //2D or 3D
+        case "000b": return "MeasurementPrecision";        //positive rational number
+        case "000c": return "SpeedUnit";                               //KPH, MPH, knots
+        case "000d": return "ReceiverSpeed";                   //positive rational number
+        case "000e": return "MovementDirectionRef";        //true or magnetic north
+        case "000f": return "MovementDirection";               //positive rational number
+        case "0010": return "ImageDirectionRef";               //true or magnetic north
+        case "0011": return "ImageDirection";                  //positive rational number
+        case "0012": return "GeodeticSurveyData";              //text string up to 999 bytes long
+        case "0013": return "DestLatitudeRef";             //north or south
+        case "0014": return "DestinationLatitude";             //three positive rational numbers
+        case "0015": return "DestLongitudeRef";                    //east or west
+        case "0016": return "DestinationLongitude";            //three positive rational numbers
+        case "0017": return "DestBearingRef";                  //true or magnetic north
+        case "0018": return "DestinationBearing";              //positive rational number
+        case "0019": return "DestDistanceRef";             //km, miles, knots
+        case "001a": return "DestinationDistance";         //positive rational number
+        case "001b": return "ProcessingMethod";
+        case "001c": return "AreaInformation";
+        case "001d": return "Datestamp";                           //text string 10 bytes long
+        case "001e": return "DifferentialCorrection";  //integer in range 0-65535
+        default: return "unknown:".$tag;
+    }
+
+}
+
+/**
+ * Formats a rational number
+ */
+function GPSRational($data, $intel)
+{
+
+    if ($intel == 1) {
+        $top = hexdec(substr($data, 8, 8));    //intel stores them bottom-top
+    } else {
+        $top = hexdec(substr($data, 0, 8));            //motorola stores them top-bottom
+    }
+
+    if ($intel == 1) {
+        $bottom = hexdec(substr($data, 0, 8));
+    } else {
+        $bottom = hexdec(substr($data, 8, 8));
+    }
+
+    if ($bottom!=0) {
+        $data = $top / $bottom;
+    } elseif ($top == 0) {
+        $data = 0;
+    } else {
+        $data = $top . "/" . $bottom;
+    }
+
+    return $data;
+}
+
+/**
+ * Formats Data for the data type
+ */
+function formatGPSData($type, $tag, $intel, $data)
+{
+
+    if($type == "ASCII") {
+        // Latitude Reference, Longitude Reference
+        if ($tag == "0001" || $tag == "0003") {
+            $data = ($data{1} == $data{2} && $data{1} == $data{3}) ? $data{0} : $data;
+        }
+    } elseif ($type == "URATIONAL" || $type == "SRATIONAL") {
+        $data = bin2hex($data);
+        if ($intel ==1 ) {
+            $data = intel2Moto($data);
+        }
+        if ($intel == 1) {
+            $top = hexdec(substr($data, 8, 8)); //intel stores them bottom-top
+        } else {
+            $top = hexdec(substr($data, 0, 8));        //motorola stores them top-bottom
+        }
+
+        if ($intel == 1) {
+            $bottom = hexdec(substr($data, 0, 8));
+        } else {
+            $bottom = hexdec(substr($data, 8, 8));
+        }
+
+        if ($type == "SRATIONAL" && $top > 2147483647) {
+             // make the number signed instead of unsigned
+            $top = $top - 4294967296;
+        }
+
+        //Latitude, Longitude
+        if ($tag=="0002" || $tag=="0004") {
+            if ($intel == 1) {
+                $seconds = GPSRational(substr($data, 0, 16), $intel);
+                $hour = GPSRational(substr($data, 32, 16), $intel);
+            } else {
+                $hour = GPSRational(substr($data, 0, 16), $intel);
+                $seconds = GPSRational(substr($data, 32, 16), $intel);
+            }
+            $minutes = GPSRational(substr($data, 16, 16), $intel);
+            $data = array($hour, $minutes, $seconds);
+        } elseif ($tag == "0007") { //Time
+            $seconds = GPSRational(substr($data, 0, 16), $intel);
+            $minutes = GPSRational(substr($data, 16, 16), $intel);
+            $hour = GPSRational(substr($data, 32, 16), $intel);
+            $data = $hour . ":" . $minutes . ":" . $seconds;
+        } else {
+            if ($bottom != 0) {
+                $data = $top / $bottom;
+            } elseif ($top == 0) {
+                $data = 0;
+            } else {
+                $data = $top . "/" . $bottom;
+            }
+            if ($tag == "0006") {
+                $data .= 'm';
+            }
+        }
+    } elseif ($type == "USHORT" || $type == "SSHORT" || $type == "ULONG" ||
+              $type == "SLONG" || $type == "FLOAT" || $type == "DOUBLE") {
+
+        $data = bin2hex($data);
+        if ($intel == 1) {
+            $data = intel2Moto($data);
+        }
+        $data = hexdec($data);
+    } elseif ($type == "UNDEFINED") {
+    } elseif ($type == "UBYTE") {
+        $data = bin2hex($data);
+        if ($intel == 1) {
+            $num = intel2Moto($data);
+        }
+        if ($tag == "0000") { // VersionID
+            $data =  hexdec(substr($data, 0, 2))
+                     . '.' . hexdec(substr($data, 2, 2))
+                     . '.' . hexdec(substr($data, 4, 2))
+                     . '.'. hexdec(substr($data, 6, 2));
+        } elseif ($tag == "0005") { // Altitude Reference
+            if ($data == "00000000") {
+                $data = 'Above Sea Level';
+            } elseif ($data == "01000000") {
+                $data = 'Below Sea Level';
+            }
+        }
+    } else {
+        $data = bin2hex($data);
+        if ($intel == 1) {
+            $data = intel2Moto($data);
+        }
+    }
+
+    return $data;
+}
+/**
+ * GPS Special data section
+ * Useful websites
+ * http://drewnoakes.com/code/exif/sampleOutput.html
+ * http://www.geosnapper.com
+ */
+function parseGPS($block,&$result,$offset,$seek, $globalOffset)
+{
+    if ($result['Endien'] == "Intel") {
+        $intel = 1;
+    } else {
+        $intel = 0;
+    }
+
+    //offsets are from TIFF header which is 12 bytes from the start of the file
+    $v = fseek($seek, $globalOffset + $offset);
+    if ($v == -1) {
+        $result['Errors'] = $result['Errors']++;
+    }
+
+    $num = bin2hex(fread($seek, 2));
+    if ($intel == 1) {
+        $num = intel2Moto($num);
+    }
+    $num = hexdec($num);
+    $result['GPS']['NumTags'] = $num;
+    $block = fread($seek, $num * 12);
+    $place = 0;
+
+    //loop thru all tags  Each field is 12 bytes
+    for ($i = 0; $i < $num; $i++) {
+        //2 byte tag
+        $tag = bin2hex(substr($block, $place, 2));
+        $place += 2;
+        if ($intel == 1) {
+            $tag = intel2Moto($tag);
+        }
+        $tag_name = lookup_GPS_tag($tag);
+
+        //2 byte datatype
+        $type = bin2hex(substr($block, $place, 2));
+        $place += 2;
+        if ($intel == 1) {
+            $type = intel2Moto($type);
+        }
+        lookup_type($type, $size);
+
+        //4 byte number of elements
+        $count = bin2hex(substr($block, $place, 4));
+        $place += 4;
+        if ($intel==1) {
+            $count = intel2Moto($count);
+        }
+        $bytesofdata = $size * hexdec($count);
+
+        //4 byte value or pointer to value if larger than 4 bytes
+        $value = substr($block, $place, 4);
+        $place += 4;
+
+        if ($bytesofdata <= 4) {
+            $data = $value;
+        } else {
+            $value = bin2hex($value);
+            if ($intel == 1) {
+                $value = intel2Moto($value);
+            }
+            //offsets are from TIFF header which is 12 bytes from the start of the file
+            $v = fseek($seek, $globalOffset + hexdec($value));
+            if ($v == 0) {
+                $data = fread($seek, $bytesofdata);
+            } elseif ($v == -1) {
+                $result['Errors'] = $result['Errors']++;
+            }
+        }
+        $result['GPS' . $tag_name] = formatGPSData($type, $tag, $intel, $data);
+    }
+
+}
\ No newline at end of file
diff --git a/ansel/lib/Exif/nikon.php b/ansel/lib/Exif/nikon.php
new file mode 100644 (file)
index 0000000..a344ad9
--- /dev/null
@@ -0,0 +1,307 @@
+<?php //================================================================================================
+//================================================================================================
+//================================================================================================
+/*
+    Exifer
+    Extracts EXIF information from digital photos.
+
+    Copyright � 2003 Jake Olefsky
+    http://www.offsky.com/software/exif/index.php
+    jake@olefsky.com
+
+    Please see exif.php for the complete information about this software.
+
+    ------------
+
+    This program is free software; you can redistribute it and/or modify it under the terms of
+    the GNU General Public License as published by the Free Software Foundation; either version 2
+    of the License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+    without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+    See the GNU General Public License for more details. http://www.gnu.org/copyleft/gpl.html
+*/
+//================================================================================================
+//================================================================================================
+//================================================================================================
+
+
+
+//=================
+// Looks up the name of the tag for the MakerNote (Depends on Manufacturer)
+//====================================================================
+function lookup_Nikon_tag($tag,$model) {
+
+    if($model==0) {
+        switch($tag) {
+            case "0003": $tag = "Quality";break;
+            case "0004": $tag = "ColorMode";break;
+            case "0005": $tag = "ImageAdjustment";break;
+            case "0006": $tag = "CCDSensitivity";break;
+            case "0007": $tag = "WhiteBalance";break;
+            case "0008": $tag = "Focus";break;
+            case "0009": $tag = "Unknown2";break;
+            case "000a": $tag = "DigitalZoom";break;
+            case "000b": $tag = _("Converter");break;
+
+            default: $tag = "unknown:".$tag;break;
+        }
+    } else if($model==1) {
+        switch($tag) {
+            case "0002": $tag = "ISOSetting";break;
+            case "0003": $tag = "ColorMode";break;
+            case "0004": $tag = "Quality";break;
+            case "0005": $tag = "Whitebalance";break;
+            case "0006": $tag = "ImageSharpening";break;
+            case "0007": $tag = "FocusMode";break;
+            case "0008": $tag = "FlashSetting";break;
+            case "0009": $tag = "FlashMode";break;
+            case "000b": $tag = "WhiteBalanceFine";break;
+            case "000f": $tag = "ISOSelection";break;
+            case "0013": $tag = "ISOSelection2";break;
+            case "0080": $tag = "ImageAdjustment";break;
+            case "0081": $tag = "ToneCompensation";break;
+            case "0082": $tag = "Adapter";break;
+            case "0083": $tag = "LensType";break;
+            case "0084": $tag = "LensInfo";break;
+            case "0085": $tag = "ManualFocusDistance";break;
+            case "0086": $tag = "DigitalZoom";break;
+            case "0087": $tag = "FlashUsed";break;
+            case "0088": $tag = "AFFocusPosition";break;
+            case "008d": $tag = "ColorMode";break;
+            case "0090": $tag = "LightType";break;
+            case "0094": $tag = "Saturation";break;
+            case "0095": $tag = "NoiseReduction";break;
+            case "0010": $tag = "DataDump";break;
+
+            default: $tag = "unknown:".$tag;break;
+        }
+    }
+
+    return $tag;
+}
+
+//=================
+// Formats Data for the data type
+//====================================================================
+function formatNikonData($type,$tag,$intel,$model,$data) {
+
+    if($type=="ASCII") {
+
+
+    } else if($type=="URATIONAL" || $type=="SRATIONAL") {
+        $data = bin2hex($data);
+        if($intel==1) $data = intel2Moto($data);
+        $top = hexdec(substr($data,8,8));
+        $bottom = hexdec(substr($data,0,8));
+        if($bottom!=0) $data=$top/$bottom;
+        else if($top==0) $data = 0;
+        else $data=$top."/".$bottom;
+
+                 if($tag=="0085" && $model==1) { //ManualFocusDistance
+            $data=$data." m";
+        }
+        if($tag=="0086" && $model==1) { //DigitalZoom
+            $data=$data."x";
+        }
+        if($tag=="000a" && $model==0) { //DigitalZoom
+            $data=$data."x";
+        }
+    } else if($type=="USHORT" || $type=="SSHORT" || $type=="ULONG" || $type=="SLONG" || $type=="FLOAT" || $type=="DOUBLE") {
+        $data = bin2hex($data);
+        if($intel==1) $data = intel2Moto($data);
+        $data=hexdec($data);
+
+        if($tag=="0003" && $model==0) { //Quality
+            if($data == 1) $data = _("VGA Basic");
+            else if($data == 2) $data = _("VGA Normal");
+            else if($data == 3) $data = _("VGA Fine");
+            else if($data == 4) $data = _("SXGA Basic");
+            else if($data == 5) $data = _("SXGA Normal");
+            else if($data == 6) $data = _("SXGA Fine");
+            else $data = _("Unknown").": ".$data;
+        }
+        if($tag=="0004" && $model==0) { //Color
+            if($data == 1) $data = _("Color");
+            else if($data == 2) $data = _("Monochrome");
+            else $data = _("Unknown").": ".$data;
+        }
+        if($tag=="0005" && $model==0) { //Image Adjustment
+            if($data == 0) $data = _("Normal");
+            else if($data == 1) $data = _("Bright+");
+            else if($data == 2) $data = _("Bright-");
+            else if($data == 3) $data = _("Contrast+");
+            else if($data == 4) $data = _("Contrast-");
+            else $data = _("Unknown").": ".$data;
+        }
+        if($tag=="0006" && $model==0) { //CCD Sensitivity
+            if($data == 0) $data = "ISO-80";
+            else if($data == 2) $data = "ISO-160";
+            else if($data == 4) $data = "ISO-320";
+            else if($data == 5) $data = "ISO-100";
+            else $data = _("Unknown").": ".$data;
+        }
+        if($tag=="0007" && $model==0) { //White Balance
+            if($data == 0) $data = _("Auto");
+            else if($data == 1) $data = _("Preset");
+            else if($data == 2) $data = _("Daylight");
+            else if($data == 3) $data = _("Incandescense");
+            else if($data == 4) $data = _("Flourescence");
+            else if($data == 5) $data = _("Cloudy");
+            else if($data == 6) $data = _("SpeedLight");
+            else $data = _("Unknown").": ".$data;
+        }
+        if($tag=="000b" && $model==0) { //Converter
+            if($data == 0) $data = _("None");
+            else if($data == 1) $data = _("Fisheye");
+            else $data = _("Unknown").": ".$data;
+        }
+    } else if($type=="UNDEFINED") {
+
+        if($tag=="0001" && $model==1) { //Unknown (Version?)
+            $data=$data/100;
+        }
+        if($tag=="0088" && $model==1) { //AF Focus Position
+            $temp = _("Center");
+            $data = bin2hex($data);
+            $data = str_replace("01","Top",$data);
+            $data = str_replace("02","Bottom",$data);
+            $data = str_replace("03","Left",$data);
+            $data = str_replace("04","Right",$data);
+            $data = str_replace("00","",$data);
+            if(strlen($data)==0) $data = $temp;
+        }
+
+    } else {
+        $data = bin2hex($data);
+        if($intel==1) $data = intel2Moto($data);
+
+        if($tag=="0083" && $model==1) { //Lens Type
+                $data = hexdec(substr($data,0,2));
+            if($data == 0) $data = _("AF non D");
+            else if($data == 1) $data = _("Manual");
+            else if($data == 2) $data = "AF-D or AF-S";
+            else if($data == 6) $data = "AF-D G";
+            else if($data == 10) $data = "AF-D VR";
+            else $data = _("Unknown").": ".$data;
+        }
+        if($tag=="0087" && $model==1) { //Flash type
+                $data = hexdec(substr($data,0,2));
+            if($data == 0) $data = _("Did Not Fire");
+            else if($data == 4) $data = _("Unknown");
+            else if($data == 7) $data = _("External");
+            else if($data == 9) $data = _("On Camera");
+            else $data = _("Unknown").": ".$data;
+        }
+    }
+
+    return $data;
+}
+
+
+//=================
+// Nikon Special data section
+//====================================================================
+function parseNikon($block,&$result) {
+
+    if($result['Endien']=="Intel") $intel=1;
+    else $intel=0;
+
+    $model = $result['IFD0']['Model'];
+
+    //these 6 models start with "Nikon".  Other models dont.
+    if($model=="E700\0" || $model=="E800\0" || $model=="E900\0" || $model=="E900S\0" || $model=="E910\0" || $model=="E950\0") {
+        $place=8; //current place
+        $model = 0;
+
+        //Get number of tags (2 bytes)
+        $num = bin2hex(substr($block,$place,2));$place+=2;
+        if($intel==1) $num = intel2Moto($num);
+        $result['SubIFD']['MakerNote']['MakerNoteNumTags'] = hexdec($num);
+
+        //loop thru all tags  Each field is 12 bytes
+        for($i=0;$i<hexdec($num);$i++) {
+            //2 byte tag
+            $tag = bin2hex(substr($block,$place,2));$place+=2;
+            if($intel==1) $tag = intel2Moto($tag);
+            $tag_name = lookup_Nikon_tag($tag, $model);
+
+            //2 byte type
+            $type = bin2hex(substr($block,$place,2));$place+=2;
+            if($intel==1) $type = intel2Moto($type);
+            lookup_type($type,$size);
+
+            //4 byte count of number of data units
+            $count = bin2hex(substr($block,$place,4));$place+=4;
+            if($intel==1) $count = intel2Moto($count);
+            $bytesofdata = $size*hexdec($count);
+
+            //4 byte value of data or pointer to data
+            $value = substr($block,$place,4);$place+=4;
+
+            //if tag is 0002 then its the ASCII value which we know is at 140 so calc offset
+            //THIS HACK ONLY WORKS WITH EARLY NIKON MODELS
+            if($tag=="0002") $offset = hexdec($value)-140;
+            if($bytesofdata<=4) {
+                $data = $value;
+            } else {
+                $value = bin2hex($value);
+                if($intel==1) $value = intel2Moto($value);
+                $data = substr($block,hexdec($value)-$offset,$bytesofdata*2);
+            }
+            $formated_data = formatNikonData($type,$tag,$intel,$model,$data);
+            $result['SubIFD']['MakerNote'][$tag_name] = $formated_data;
+        }
+
+    } else {
+        $place=0;//current place
+        $model = 1;
+
+        $nikon = substr($block,$place,8);$place+=8;
+        $endien = substr($block,$place,4);$place+=4;
+
+        //2 bytes of 0x002a
+        $tag = bin2hex(substr($block,$place,2));$place+=2;
+
+        //Then 4 bytes of offset to IFD0 (usually 8 which includes all 8 bytes of TIFF header)
+        $offset = bin2hex(substr($block,$place,4));$place+=4;
+        if($intel==1) $offset = intel2Moto($offset);
+        if(hexdec($offset)>8) $place+=$offset-8;
+
+        //Get number of tags (2 bytes)
+        $num = bin2hex(substr($block,$place,2));$place+=2;
+        if($intel==1) $num = intel2Moto($num);
+
+        //loop thru all tags  Each field is 12 bytes
+        for($i=0;$i<hexdec($num);$i++) {
+            //2 byte tag
+            $tag = bin2hex(substr($block,$place,2));$place+=2;
+            if($intel==1) $tag = intel2Moto($tag);
+            $tag_name = lookup_Nikon_tag($tag, $model);
+
+            //2 byte type
+            $type = bin2hex(substr($block,$place,2));$place+=2;
+            if($intel==1) $type = intel2Moto($type);
+            lookup_type($type,$size);
+
+            //4 byte count of number of data units
+            $count = bin2hex(substr($block,$place,4));$place+=4;
+            if($intel==1) $count = intel2Moto($count);
+            $bytesofdata = $size*hexdec($count);
+
+            //4 byte value of data or pointer to data
+            $value = substr($block,$place,4);$place+=4;
+
+            if($bytesofdata<=4) {
+                $data = $value;
+            } else {
+                $value = bin2hex($value);
+                if($intel==1) $value = intel2Moto($value);
+                $data = substr($block,hexdec($value)+hexdec($offset)+2,$bytesofdata);
+            }
+            $formated_data = formatNikonData($type,$tag,$intel,$model,$data);
+            $result['SubIFD']['MakerNote'][$tag_name] = $formated_data;
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/ansel/lib/Exif/olympus.php b/ansel/lib/Exif/olympus.php
new file mode 100644 (file)
index 0000000..4926669
--- /dev/null
@@ -0,0 +1,182 @@
+<?php //================================================================================================
+//================================================================================================
+//================================================================================================
+/*
+    Exifer
+    Extracts EXIF information from digital photos.
+
+    Copyright � 2003 Jake Olefsky
+    http://www.offsky.com/software/exif/index.php
+    jake@olefsky.com
+
+    Please see exif.php for the complete information about this software.
+
+    ------------
+
+    This program is free software; you can redistribute it and/or modify it under the terms of
+    the GNU General Public License as published by the Free Software Foundation; either version 2
+    of the License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+    without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+    See the GNU General Public License for more details. http://www.gnu.org/copyleft/gpl.html
+*/
+//================================================================================================
+//================================================================================================
+//================================================================================================
+
+
+
+//=================
+// Looks up the name of the tag for the MakerNote (Depends on Manufacturer)
+//====================================================================
+function lookup_Olympus_tag($tag) {
+    switch($tag) {
+        case "0200": $tag = "SpecialMode";break;
+        case "0201": $tag = "JpegQual";break;
+        case "0202": $tag = "Macro";break;
+        case "0203": $tag = "Unknown1";break;
+        case "0204": $tag = "DigiZoom";break;
+        case "0205": $tag = "Unknown2";break;
+        case "0206": $tag = "Unknown3";break;
+        case "0207": $tag = "SoftwareRelease";break;
+        case "0208": $tag = "PictInfo";break;
+        case "0209": $tag = "CameraID";break;
+        case "0f00": $tag = "DataDump";break;
+
+        default: $tag = "unknown:".$tag;break;
+    }
+
+    return $tag;
+}
+
+//=================
+// Formats Data for the data type
+//====================================================================
+function formatOlympusData($type,$tag,$intel,$data) {
+    if($type=="ASCII") {
+
+    } else if($type=="URATIONAL" || $type=="SRATIONAL") {
+        $data = bin2hex($data);
+        if($intel==1) $data = intel2Moto($data);
+        $top = hexdec(substr($data,8,8));
+        $bottom = hexdec(substr($data,0,8));
+        if($bottom!=0) $data=$top/$bottom;
+        else if($top==0) $data = 0;
+        else $data=$top."/".$bottom;
+
+        if($tag=="0204") { //DigitalZoom
+            $data=$data."x";
+        }
+        if($tag=="0205") { //Unknown2
+            $data=$top."/".$bottom;
+        }
+    } else if($type=="USHORT" || $type=="SSHORT" || $type=="ULONG" || $type=="SLONG" || $type=="FLOAT" || $type=="DOUBLE") {
+        $data = bin2hex($data);
+        if($intel==1) $data = intel2Moto($data);
+        $data=hexdec($data);
+
+        if($tag=="0201") { //JPEGQuality
+            if($data == 1) $data = "SQ";
+            else if($data == 2) $data = "HQ";
+            else if($data == 3) $data = "SHQ";
+            else $data = _("Unknown").": ".$data;
+        }
+        if($tag=="0202") { //Macro
+            if($data == 0) $data = "Normal";
+            else if($data == 1) $data = "Macro";
+            else $data = _("Unknown").": ".$data;
+        }
+    } else if($type=="UNDEFINED") {
+
+    } else {
+        $data = bin2hex($data);
+        if($intel==1) $data = intel2Moto($data);
+    }
+
+    return $data;
+}
+
+
+
+//==============================================================================
+// Olympus Special data section
+// - Updated by Zenphoto for new header tag in E-410/E-510/E-3 cameras. 2/24/2008
+//==============================================================================
+function parseOlympus($block, &$result, $seek, $globalOffset) {
+
+    if($result['Endien']=="Intel") $intel = 1;
+    else $intel = 0;
+
+    $model = $result['IFD0']['Model'];
+
+    // New header for new DSLRs - Check for it because the
+    // number of bytes that count the IFD fields differ in each case.
+    // Fixed by Zenphoto 2/24/08
+    $new = false;
+    if (substr($block, 0, 8) == "OLYMPUS\x00") {
+        $new = true;
+    } else if (substr($block, 0, 7) == "OLYMP\x00\x01"
+        || substr($block, 0, 7) == "OLYMP\x00\x02") {
+        $new = false;
+    } else {
+        // Header does not match known Olympus headers.
+        // This is not a valid OLYMPUS Makernote.
+        return false;
+    }
+
+    // Offset of IFD entry after Olympus header.
+    $place = 8;
+    $offset = 8;
+
+    // Get number of tags (1 or 2 bytes, depending on New or Old makernote)
+    $countfieldbits = $new ? 1 : 2;
+    // New makernote repeats 1-byte value twice, so increment $place by 2 in either case.
+    $num = bin2hex(substr($block, $place, $countfieldbits)); $place += 2;
+    if ($intel == 1) $num = intel2Moto($num);
+    $ntags = hexdec($num);
+    $result['SubIFD']['MakerNote']['MakerNoteNumTags'] = $ntags;
+
+    //loop thru all tags  Each field is 12 bytes
+    for($i=0; $i < $ntags; $i++) {
+        //2 byte tag
+        $tag = bin2hex(substr($block, $place,2));
+        $place += 2;
+        if ($intel == 1) $tag = intel2Moto($tag);
+        $tag_name = lookup_Olympus_tag($tag);
+
+        //2 byte type
+        $type = bin2hex(substr($block, $place,2));
+        $place += 2;
+        if ($intel == 1) $type = intel2Moto($type);
+        lookup_type($type,$size);
+
+        //4 byte count of number of data units
+        $count = bin2hex(substr($block, $place,4));
+        $place+=4;
+        if ($intel == 1) $count = intel2Moto($count);
+        $bytesofdata = $size * hexdec($count);
+
+        //4 byte value of data or pointer to data
+        $value = substr($block, $place,4);
+        $place += 4;
+
+
+        if ($bytesofdata <= 4) {
+            $data = $value;
+        } else {
+            $value = bin2hex($value);
+            if($intel==1) $value = intel2Moto($value);
+            $v = fseek($seek,$globalOffset+hexdec($value));  //offsets are from TIFF header which is 12 bytes from the start of the file
+            if(isset($GLOBALS['exiferFileSize']) && $v == 0 && $bytesofdata < $GLOBALS['exiferFileSize']) {
+                $data = fread($seek, $bytesofdata);
+            } else {
+                $result['Errors'] = $result['Errors']++;
+                $data = '';
+            }
+        }
+        $formated_data = formatOlympusData($type,$tag,$intel,$data);
+        $result['SubIFD']['MakerNote'][$tag_name] = $formated_data;
+    }
+
+}
\ No newline at end of file
diff --git a/ansel/lib/Exif/panasonic.php b/ansel/lib/Exif/panasonic.php
new file mode 100644 (file)
index 0000000..e846224
--- /dev/null
@@ -0,0 +1,403 @@
+<?php
+/**
+ *   Exifer
+ *   Extracts EXIF information from digital photos.
+ *
+ *   $Horde: ansel/lib/Exif/panasonic.php,v 1.4 2008/12/30 18:30:25 mrubinsk Exp $
+ *
+ *   Copyright 2003 Jake Olefsky
+ *   http://www.offsky.com/software/exif/index.php
+ *   jake@olefsky.com
+ *
+ *   Please see exif.php for the complete information about this software.
+ *
+ *   ------------
+ *
+ *   This program is free software; you can redistribute it and/or modify it under the terms of
+ *   the GNU General Public License as published by the Free Software Foundation; either version 2
+ *   of the License, or (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ *   without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *   See the GNU General Public License for more details. http://www.gnu.org/copyleft/gpl.html
+ */
+
+/**
+ * Looks up the name of the tag for the MakerNote (Depends on Manufacturer)
+ */
+function lookup_Panasonic_tag($tag)
+{
+    switch ($tag) {
+    case '0001': $tag = 'Quality'; break;
+    case '0002': $tag = 'FirmwareVersion'; break;
+    case '0003': $tag = 'WhiteBalance'; break;
+    case '0007': $tag = 'FocusMode'; break;
+    case '000f': $tag = 'AFMode'; break;
+    case '001a': $tag = 'ImageStabilizer'; break;
+    case '001c': $tag = 'MacroMode'; break;
+    case '001f': $tag = 'ShootingMode'; break;
+    case '0020': $tag = 'Audio'; break;
+    case '0021': $tag = 'DataDump'; break;
+    case '0023': $tag = 'WhiteBalanceBias'; break;
+    case '0024': $tag = 'FlashBias'; break;
+    case '0025': $tag = 'SerialNumber'; break;
+    case '0028': $tag = 'ColourEffect'; break;
+    case '002a': $tag = 'BurstMode'; break;
+    case '002b': $tag = 'SequenceNumber'; break;
+    case '002c': $tag = 'Contrast'; break;
+    case '002d': $tag = 'NoiseReduction'; break;
+    case '002e': $tag = 'SelfTimer'; break;
+    case '0030': $tag = 'Rotation'; break;
+    case '0032': $tag = 'ColorMode'; break;
+    case '0036': $tag = 'TravelDay'; break;
+    default: $tag = 'unknown:' . $tag; break;
+    }
+
+    return $tag;
+}
+
+/**
+ * Formats Data for the data type
+ */
+function formatPanasonicData($type,$tag,$intel,$data)
+{
+    if ($type == 'UBYTE' || $type == 'SBYTE') {
+        $data = bin2hex($data);
+        if ($intel == 1) {
+            $data = intel2Moto($data);
+        }
+        $data = hexdec($data);
+        if ($tag == '000f') { //AFMode
+            if ($data == 256) {
+                $data = _("9-area-focusing");
+            } elseif ($data == 16) {
+                $data = _("1-area-focusing");
+            } elseif ($data == 4096) {
+                $data = _("3-area-focusing (High speed)");
+            } elseif ($data == 4112) {
+                $data = _("1-area-focusing (High speed)");
+            } elseif ($data == 16) {
+                $data = _("1-area-focusing");
+            } elseif ($data == 1) {
+                $data = _("Spot-focusing");
+            } else {
+                $data = sprintf(_("Unknown (%s)"), $data);
+            }
+        }
+
+    } elseif ($type == 'URATIONAL' || $type == 'SRATIONAL') {
+        $data = bin2hex($data);
+        if ($intel == 1) {
+            $data = intel2Moto($data);
+        }
+        $top = hexdec(substr($data, 8, 8));
+        $bottom = hexdec(substr($data, 0, 8));
+        if ($bottom!=0) {
+            $data = $top / $bottom;
+        } elseif ($top == 0) {
+            $data = 0;
+        } else {
+            $data = $top . '/' . $bottom;
+        }
+
+    } elseif ($type == 'USHORT' || $type == 'SSHORT' || $type == 'ULONG' ||
+              $type == 'SLONG' || $type == 'FLOAT' || $type == 'DOUBLE') {
+
+        $data = bin2hex($data);
+        if ($intel == 1) {
+            $data = intel2Moto($data);
+        }
+
+        $data = hexdec($data);
+        if ($tag == '0001') { //Image Quality
+            if ($data == 2) {
+                $data = _("High");
+            } elseif ($data == 3) {
+                $data = _("Standard");
+            } elseif ($data == 6) {
+                $data = _("Very High");
+            } elseif ($data == 7) {
+                $data = _("RAW");
+            } else {
+                $data = sprintf(_("Unknown (%s)"), $data);
+            }
+        }
+        if ($tag == '0003') { //White Balance
+            if ($data == 1) {
+                $data = _("Auto");
+            } elseif ($data == 2) {
+                $data = _("Daylight");
+            } elseif ($data == 3) {
+                $data = _("Cloudy");
+            } elseif ($data == 4) {
+                $data = _("Halogen");
+            } elseif ($data == 5) {
+                $data = _("Manual");
+            } elseif ($data == 8) {
+                $data = _("Flash");
+            } elseif ($data == 10) {
+                $data = _("Black and White");
+            } elseif ($data == 11) {
+                $data = _("Manual");
+            } else {
+                $data = sprintf(_("Unknown(%s)"), $data);
+            }
+        }
+        if ($tag=='0007') { //Focus Mode
+            if ($data == 1) {
+                $data = _("Auto");
+            } elseif ($data == 2) {
+                $data = _("Manual");
+            } elseif ($data == 4) {
+                $data = _("Auto, Focus button");
+            } elseif ($data == 5) {
+                $data = _("Auto, Continuous");
+            } else {
+                $data = sprintf(_("Unknown(%s)"), $data);
+            }
+        }
+        if ($tag == '001a') { //Image Stabilizer
+            if ($data == 2) {
+                $data = _("Mode 1");
+            } elseif ($data == 3) {
+                $data = _("Off");
+            } elseif ($data == 4) {
+                $data = _("Mode 2");
+            } else {
+                $data = sprintf(_("Unknown(%s)"), $data);
+            }
+        }
+        if ($tag == '001c') { //Macro mode
+            if ($data == 1) {
+                $data = _("On");
+            } elseif ($data == 2) {
+                $data = _("Off");
+            } else {
+                $data = sprintf(_("Unknown(%s)"), $data);
+            }
+        }
+        if ($tag == '001f') { //Shooting Mode
+            if ($data == 1) {
+                $data = _("Normal");
+            } elseif ($data == 2) {
+                $data = _("Portrait");
+            } elseif ($data == 3) {
+                $data = _("Scenery");
+            } elseif ($data == 4) {
+                $data = _("Sports");
+            } elseif ($data == 5) {
+                $data = _("Night Portrait");
+            } elseif ($data == 6) {
+                $data = _("Program");
+            } elseif ($data == 7) {
+                $data = _("Aperture Priority");
+            } elseif ($data == 8) {
+                $data = _("Shutter Priority");
+            } elseif ($data == 9) {
+                $data = _("Macro");
+            } elseif ($data == 11) {
+                $data = _("Manual");
+            } elseif ($data == 13) {
+                $data = _("Panning");
+            } elseif ($data == 14) {
+                $data = _("Simple");
+            } elseif ($data == 18) {
+                $data = _("Fireworks");
+            } elseif ($data == 19) {
+                $data = _("Party");
+            } elseif ($data == 20) {
+                $data = _("Snow");
+            } elseif ($data == 21) {
+                $data = _("Night Scenery");
+            } elseif ($data == 22) {
+                $data = _("Food");
+            } elseif ($data == 23) {
+                $data = _("Baby");
+            } elseif ($data == 27) {
+                $data = _("High Sensitivity");
+            } elseif ($data == 29) {
+                $data = _("Underwater");
+            } elseif ($data == 33) {
+                $data = _("Pet");
+            } else {
+                $data = sprintf(_("Unknown(%s)"), $data);
+            }
+        }
+        if ($tag == '0020') { //Audio
+            if ($data == 1) {
+                $data = _("Yes");
+            } elseif ($data == 2) {
+                $data = _("No");
+            } else {
+                $data = sprintf(_("Unknown (%s)"), $data);
+            }
+        }
+        if ($tag == '0023') { //White Balance Bias
+            $data = $data . ' EV';
+        }
+        if ($tag == '0024') { //Flash Bias
+            $data = $data;
+        }
+        if ($tag == '0028') { //Colour Effect
+            if ($data == 1) {
+                $data = _("Off");
+            } elseif ($data == 2) {
+                $data = _("Warm");
+            } elseif ($data == 3) {
+                $data = _("Cool");
+            } elseif ($data == 4) {
+                $data = _("Black and White");
+            } elseif ($data == 5) {
+                $data = _("Sepia");
+            } else {
+                $data = sprintf(_("Unknown (%s)"), $data);
+            }
+        }
+        if ($tag == '002a') { //Burst Mode
+            if ($data == 0) {
+                $data = _("Off");
+            } elseif ($data == 1) {
+                $data = _("Low/High Quality");
+            } elseif ($data == 2) {
+                $data = _("Infinite");
+            } else {
+                $data = sprintf(_("Unknown (%s)"), $data);
+            }
+        }
+        if ($tag == '002c') { //Contrast
+            if ($data == 0) {
+                $data = _("Standard");
+            } elseif ($data == 1) {
+                $data = _("Low");
+            } elseif ($data == 2) {
+                $data = _("High");
+            } else {
+                $data = sprintf(_("Unknown (%s)"), $data);
+            }
+        }
+        if ($tag == '002d') { //Noise Reduction
+            if ($data == 0) {
+                $data = _("Standard");
+            } elseif ($data == 1) {
+                $data = _("Low");
+            } elseif ($data == 2) {
+                $data = _("High");
+            } else {
+                $data = sprintf(_("Unknown (%s)"), $data);
+            }
+        }
+        if ($tag == '002e') { //Self Timer
+            if ($data == 1) {
+                $data = _("Off");
+            } elseif ($data == 2) {
+                $data = _("10s");
+            } elseif ($data == 3) {
+                $data = _("2s");
+            } else {
+                $data = sprintf(_("Unknown (%s)"), $data);
+            }
+        }
+        if ($tag == '0030') { //Rotation
+            if ($data == 1) {
+                $data = _("Horizontal (normal)");
+            } elseif ($data == 6) {
+                $data = _("Rotate 90 CW");
+            } elseif ($data == 8) {
+                $data = _("Rotate 270 CW");
+            } else {
+                $data = sprintf(_("Unknown (%s)"), $data);
+            }
+        }
+        if ($tag == '0032') { //Color Mode
+            if ($data == 0) {
+                $data = _("Normal");
+            } elseif ($data == 1) {
+                $data = _("Natural");
+            } else {
+                $data = sprintf(_("Unknown (%s)"), $data);
+            }
+        }
+        if ($tag == '0036') { //Travel Day
+            $data = $data;
+        }
+    } elseif ($type != "UNDEFINED") {
+        $data = bin2hex($data);
+        if ($intel == 1) {
+            $data = intel2Moto($data);
+        }
+    }
+
+    return $data;
+}
+
+/**
+ * Panasonic Special data section
+ */
+function parsePanasonic($block, &$result)
+ {
+    $intel = 1;
+    $model = $result['IFD0']['Model'];
+    $place = 8; //current place
+    $offset = 8;
+
+    $num = bin2hex(substr($block, $place, 4));
+    $place += 4;
+
+    if ($intel == 1) {
+        $num = intel2Moto($num);
+    }
+    $result['SubIFD']['MakerNote']['Offset'] = hexdec($num);
+
+    //Get number of tags (2 bytes)
+    $num = bin2hex(substr($block, $place, 2));
+    $place+=2;
+
+    if ($intel == 1) {
+        $num = intel2Moto($num);
+    }
+    $result['SubIFD']['MakerNote']['MakerNoteNumTags'] = hexdec($num);
+
+    //loop thru all tags  Each field is 12 bytes
+    for($i = 0; $i < hexdec($num); $i++) {
+        //2 byte tag
+        $tag = bin2hex(substr($block, $place, 2));
+        $place += 2;
+        if ($intel == 1) {
+            $tag = intel2Moto($tag);
+        }
+        $tag_name = lookup_Panasonic_tag($tag);
+
+        //2 byte type
+        $type = bin2hex(substr($block, $place, 2));
+        $place += 2;
+        if ($intel == 1) {
+            $type = intel2Moto($type);
+        }
+        lookup_type($type, $size);
+
+        //4 byte count of number of data units
+        $count = bin2hex(substr($block, $place, 4));
+        $place += 4;
+        if ($intel == 1) {
+            $count = intel2Moto($count);
+        }
+        $bytesofdata = $size * hexdec($count);
+
+        //4 byte value of data or pointer to data
+        $value = substr($block, $place, 4);
+        $place += 4;
+
+        if ($bytesofdata <= 4) {
+            $data = $value;
+        } else {
+            $value = bin2hex($value);
+            if ($intel == 1) {
+                $value = intel2Moto($value);
+            }
+            $data = substr($block, hexdec($value) - $offset, $bytesofdata * 2);
+        }
+        $formated_data = formatPanasonicData($type, $tag, $intel, $data);
+        $result['SubIFD']['MakerNote'][$tag_name] = $formated_data;
+    }
+
+}
\ No newline at end of file
diff --git a/ansel/lib/Exif/sanyo.php b/ansel/lib/Exif/sanyo.php
new file mode 100644 (file)
index 0000000..7289b35
--- /dev/null
@@ -0,0 +1,153 @@
+<?php //================================================================================================
+//================================================================================================
+//================================================================================================
+/*
+    Exifer
+    Extracts EXIF information from digital photos.
+
+    Copyright � 2003 Jake Olefsky
+    http://www.offsky.com/software/exif/index.php
+    jake@olefsky.com
+
+    Please see exif.php for the complete information about this software.
+
+    ------------
+
+    This program is free software; you can redistribute it and/or modify it under the terms of
+    the GNU General Public License as published by the Free Software Foundation; either version 2
+    of the License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+    without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+    See the GNU General Public License for more details. http://www.gnu.org/copyleft/gpl.html
+*/
+//================================================================================================
+//================================================================================================
+//================================================================================================
+
+
+
+//=================
+// Looks up the name of the tag for the MakerNote (Depends on Manufacturer)
+//====================================================================
+function lookup_Sanyo_tag($tag) {
+
+    switch($tag) {
+        case "0200": $tag = "SpecialMode";break;
+        case "0201": $tag = "Quality";break;
+        case "0202": $tag = "Macro";break;
+        case "0203": $tag = "Unknown";break;
+        case "0204": $tag = "DigiZoom";break;
+        case "0f00": $tag = "DataDump";break;
+        default: $tag = "unknown:".$tag;break;
+    }
+
+    return $tag;
+}
+
+//=================
+// Formats Data for the data type
+//====================================================================
+function formatSanyoData($type,$tag,$intel,$data) {
+
+    if($type=="ASCII") {
+
+
+    } else if($type=="URATIONAL" || $type=="SRATIONAL") {
+        $data = bin2hex($data);
+        if($intel==1) $data = intel2Moto($data);
+        $top = hexdec(substr($data,8,8));
+        $bottom = hexdec(substr($data,0,8));
+        if($bottom!=0) $data=$top/$bottom;
+        else if($top==0) $data = 0;
+        else $data=$top."/".$bottom;
+
+
+    } else if($type=="USHORT" || $type=="SSHORT" || $type=="ULONG" || $type=="SLONG" || $type=="FLOAT" || $type=="DOUBLE") {
+        $data = bin2hex($data);
+        if($intel==1) $data = intel2Moto($data);
+        $data=hexdec($data);
+
+        if($tag=="0200") { //SpecialMode
+            if($data == 0) $data = _("Normal");
+            else $data = _("Unknown").": ".$data;
+        }
+        if($tag=="0201") { //Quality
+            if($data == 2) $data = _("High");
+            else $data = _("Unknown").": ".$data;
+        }
+        if($tag=="0202") { //Macro
+            if($data == 0) $data = _("Normal");
+            else $data = _("Unknown").": ".$data;
+        }
+    } else if($type=="UNDEFINED") {
+
+
+
+    } else {
+        $data = bin2hex($data);
+        if($intel==1) $data = intel2Moto($data);
+    }
+
+    return $data;
+}
+
+
+
+//=================
+// Sanyo Special data section
+//====================================================================
+function parseSanyo($block,&$result,$seek, $globalOffset) {
+
+    if($result['Endien']=="Intel") $intel=1;
+    else $intel=0;
+
+    $model = $result['IFD0']['Model'];
+
+    $place=8; //current place
+    $offset=8;
+
+        //Get number of tags (2 bytes)
+    $num = bin2hex(substr($block,$place,2));$place+=2;
+    if($intel==1) $num = intel2Moto($num);
+    $result['SubIFD']['MakerNote']['MakerNoteNumTags'] = hexdec($num);
+
+    //loop thru all tags  Each field is 12 bytes
+    for($i=0;$i<hexdec($num);$i++) {
+
+            //2 byte tag
+        $tag = bin2hex(substr($block,$place,2));$place+=2;
+        if($intel==1) $tag = intel2Moto($tag);
+        $tag_name = lookup_Sanyo_tag($tag);
+
+            //2 byte type
+        $type = bin2hex(substr($block,$place,2));$place+=2;
+        if($intel==1) $type = intel2Moto($type);
+        lookup_type($type,$size);
+
+            //4 byte count of number of data units
+        $count = bin2hex(substr($block,$place,4));$place+=4;
+        if($intel==1) $count = intel2Moto($count);
+        $bytesofdata = $size*hexdec($count);
+
+            //4 byte value of data or pointer to data
+        $value = substr($block,$place,4);$place+=4;
+
+
+        if($bytesofdata<=4) {
+            $data = $value;
+        } else {
+            $value = bin2hex($value);
+            if($intel==1) $value = intel2Moto($value);
+            $v = fseek($seek,$globalOffset+hexdec($value));  //offsets are from TIFF header which is 12 bytes from the start of the file
+            if($v==0) {
+                $data = fread($seek, $bytesofdata);
+            } else if($v==-1) {
+                $result['Errors'] = $result['Errors']++;
+            }
+        }
+        $formated_data = formatSanyoData($type,$tag,$intel,$data);
+        $result['SubIFD']['MakerNote'][$tag_name] = $formated_data;
+    }
+
+}
\ No newline at end of file
diff --git a/ansel/lib/Faces.php b/ansel/lib/Faces.php
new file mode 100755 (executable)
index 0000000..5c198bb
--- /dev/null
@@ -0,0 +1,1138 @@
+<?php
+/**
+ * Face recognition class
+ *
+ * $Horde: ansel/lib/Faces.php,v 1.29 2009/07/14 00:25:28 mrubinsk Exp $
+ *
+ * @author  Duck <duck@obala.net>
+ * @package Ansel
+ */
+class Ansel_Faces {
+
+    /**
+     * Attempts to return a reference to a concrete Ansel_Faces instance.
+     */
+    function &singleton()
+    {
+        static $face;
+
+        if (!isset($face)) {
+            $face = Ansel_Faces::factory();
+        }
+
+        return $face;
+    }
+
+    /**
+     * Create instance
+     */
+    function factory($driver = null, $params = array())
+    {
+        if ($driver === null) {
+            $driver = $GLOBALS['conf']['faces']['driver'];
+        }
+
+        if (empty($params)) {
+            $params = $GLOBALS['conf']['faces'];
+        }
+
+        $class_name = 'Ansel_Faces';
+
+        // Load system helpers if possible
+        if (Ansel_Faces::autogenerate($driver)) {
+            require_once ANSEL_BASE . '/lib/Faces/' . basename($driver)  . '.php';
+            $class_name .= '_' . $driver;
+            if (!class_exists($class_name)) {
+                $err = PEAR::raiseError(_("Face driver does not exist."));
+                Horde::logMessage($err, __FILE__, __LINE__, PEAR_LOG_ERR);
+                return $err;
+            }
+        }
+
+        $parser = new $class_name($params);
+
+        return $parser;
+    }
+
+    /**
+     * Tell if the driver can auto generate faces
+     *
+     * @param string $driver Driver name
+     */
+    function autogenerate($driver = null)
+    {
+        if ($driver === null) {
+            $driver = $GLOBALS['conf']['faces']['driver'];
+        }
+
+        return $driver == 'opencv' ||
+            ($driver == 'facedetect' &&
+             version_compare(PHP_VERSION, '5.0.0', '>'));
+    }
+
+    /**
+     * Get faces
+     *
+     * @param string $file Picture filename
+     * @abstract
+     */
+    function _getFaces($file)
+    {
+        return array();
+    }
+
+    /**
+     * Get all the coordinates for faces in an image.
+     *
+     * @param mixed $image  The Ansel_Image or a path to the image to check.
+     *
+     * @return mixed  Array of face data || PEAR_Error
+     */
+    function getFaces(&$image)
+    {
+        if (is_a($image, 'Ansel_Image')) {
+            // First check if screen view exists
+            if (is_a($result = $image->load('screen'), 'PEAR_Error')) {
+                return $result;
+            }
+
+            // Make sure we have an on-disk copy of the file.
+            $file = $GLOBALS['ansel_vfs']->readFile($image->getVFSPath('screen'),
+                                                    $image->getVFSName('screen'));
+        } elseif (empty($file) || !is_string($image)) {
+              return array();
+        }
+
+        // Get faces from driver
+        $faces = $this->_getFaces($file);
+        if (is_a($faces, 'PEAR_Error')) {
+            return $faces;
+        }
+        if (empty($faces)) {
+            return array();
+        }
+
+        // Remove faces containg faces
+        // for example when 2 are together we can have 3 faces
+        foreach ($faces as $face) {
+            $id = $this->_isInFace($face, $faces);
+            if ($id !== false) {
+                unset($faces[$id]);
+            }
+        }
+
+        return $faces;
+    }
+
+    /**
+     * Get existing faces data from storage for the given image.
+     *
+     * Used if we need to build the face image at some point after it is
+     * detected.
+     *
+     * @param integer $image_id  The image_id of the Ansel_Image these faces are
+     *                           for.
+     * @param boolean $full      Get full face data or just face_id and
+     *                           face_name.
+     *
+     * @return mixed  Array of faces data || PEAR_Error
+     */
+    function getImageFacesData($image_id, $full = false)
+    {
+        $sql = 'SELECT face_id, face_name ';
+        if ($full) {
+            $sql .= ', gallery_id, face_x1, face_y1, face_x2, face_y2';
+        }
+        $sql .= ' FROM ansel_faces WHERE image_id = ' . (int)$image_id
+                . ' ORDER BY face_id DESC';
+
+       Horde::logMessage('SQL Query by Ansel_Faces::getImageFacesData: ' . $sql,
+                         __FILE__, __LINE__, PEAR_LOG_DEBUG);
+       $result = $GLOBALS['ansel_db']->query($sql);
+        if (is_a($result, 'PEAR_Error')) {
+            Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return $result;
+        } elseif ($result->numRows() == 0) {
+            return array();
+        }
+
+        $faces = array();
+        while ($face = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
+            if ($full) {
+                $faces[$face['face_id']] = array(
+                    'face_name' => $face['face_name'],
+                    'face_id' => $face['face_id'],
+                    'gallery_id' => $face['gallery_id'],
+                    'face_x1' => $face['face_x1'],
+                    'face_y1' => $face['face_y1'],
+                    'face_x2' => $face['face_x2'],
+                    'face_y2' => $face['face_y2'],
+                    'image_id' => $image_id);
+            } else {
+                $faces[$face['face_id']] = $face['face_name'];
+            }
+        }
+
+        return $faces;
+    }
+
+    /**
+     * Get existing faces data for an entire gallery.
+     *
+     * @param integer $gallery  gallery_id to get data for.\
+     *
+     * @return mixed  array of faces data || PEAR_Error
+     */
+    function getGalleryFaces($gallery)
+    {
+        $sql = 'SELECT face_id, image_id, gallery_id, face_name FROM ansel_faces '
+               . ' WHERE gallery_id = ' . (int)$gallery . ' ORDER BY face_id DESC';
+
+        $result = $GLOBALS['ansel_db']->query($sql);
+        if (is_a($result, 'PEAR_Error')) {
+            Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return $result;
+        } elseif ($result->numRows() == 0) {
+            return array();
+        }
+
+        $faces = array();
+        while ($face = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
+            $faces[$face['face_id']] = array('face_name' => $face['face_name'],
+                                      'face_id' => $face['face_id'],
+                                      'gallery_id' => $face['gallery_id'],
+                                      'image_id' => $face['image_id']);
+        }
+
+        return $faces;
+    }
+
+    /**
+     * Fetchs all faces from all galleries the current user has READ access to?
+     *
+     * @param array $info     Array of select criteria
+     * @param integer $from   Offset
+     * @param integer $count  Limit
+     *
+     * @return mixed  An array of faces data || PEAR_Error
+     */
+    function _fetchFaces($info, $from = 0, $count = 0)
+    {
+        // add gallery permission
+        // FIXME: This is a REALLY ugly hack, permissions checking like this
+        // should be encapsulated by the shares driver and not parsed from
+        // an internally generated query string fragment. Will need to split
+        // this out into two seperate operations somehow.
+        $share = substr($GLOBALS['ansel_storage']->shares->_getShareCriteria(
+            Horde_Auth::getAuth(), PERMS_READ), 5);
+
+        $sql = 'SELECT f.face_id, f.gallery_id, f.image_id, f.face_name FROM ansel_faces f, '
+                . str_replace('WHERE', 'WHERE (', $share)
+                . ' ) AND f.gallery_id = s.share_id'
+                . (isset($info['filter']) ? ' AND ' . $info['filter'] : '')
+                . ' ORDER BY ' . (isset($info['order']) ? $info['order'] : ' f.face_id DESC');
+
+        $GLOBALS['ansel_db']->setLimit($count, $from);
+        $result = $GLOBALS['ansel_db']->query($sql);
+        if (is_a($result, 'PEAR_Error')) {
+            Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return $result;
+        } elseif ($result->numRows() == 0) {
+            return array();
+        }
+
+        $faces = array();
+        while ($face = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
+            $faces[$face['face_id']] = array('face_name' => $face['face_name'],
+                                            'face_id' => $face['face_id'],
+                                            'gallery_id' => $face['gallery_id'],
+                                            'image_id' => $face['image_id']);
+        }
+
+        return $faces;
+    }
+
+    /**
+     * Count faces
+     *
+     * @param array $info Array of select criteria
+     */
+    function _countFaces($info)
+    {
+        // add gallery permission
+        // FIXME: Ditto on the REALLY ugly hack comment from above!
+        $share = substr($GLOBALS['ansel_storage']->shares->_getShareCriteria(
+            Horde_Auth::getAuth(), PERMS_READ), 5);
+
+        $sql = 'SELECT COUNT(*) FROM ansel_faces f, '
+                . str_replace('WHERE', 'WHERE (', $share)
+                . ' ) AND f.gallery_id = s.share_id'
+                . (isset($info['filter']) ? ' AND ' . $info['filter'] : '');
+
+        return $GLOBALS['ansel_db']->queryOne($sql);
+    }
+
+    /**
+     * Get all faces
+     *
+     * Note: I removed the 'random' parameter since it won't work across
+     *       different RDBMS and it's incredibly resource intensive as it
+     *       causes the RDBMS to generate a rand() number for each row and THEN
+     *       sort the table by those numbers.
+     * @param integer $from Offset
+     * @param integer $count Limit
+     */
+    function allFaces($from = 0, $count = 0)
+    {
+        $info = array('order' => 'f.face_id DESC');
+        return $this->_fetchFaces($info, $from, $count);
+    }
+
+    /**
+     * Get named faces
+     *
+     * @param integer $from Offset
+     * @param integer $count Limit
+     */
+    function namedFaces($from = 0, $count = 0)
+    {
+        $info = array('filter' => 'f.face_name IS NOT NULL AND f.face_name <> \'\'');
+        return $this->_fetchFaces($info, $from, $count);
+    }
+
+    /**
+     * Get faces owned by user
+     *
+     * @param string  $owner User
+     * @param integer $from Offset
+     * @param integer $count Limit
+     */
+    function ownerFaces($owner, $from = 0, $count = 0)
+    {
+        $info = array(
+            'filter' => 's.share_owner = ' . $GLOBALS['ansel_db']->quote($owner),
+            'order' => 'f.face_id DESC');
+
+        if ($owner != Horde_Auth::getAuth()) {
+            $info['filter'] .= ' AND s.gallery_passwd IS NULL';
+        }
+
+        return $this->_fetchFaces($info, $from, $count);
+    }
+
+    /**
+     * Seach faces for a name
+     *
+     * @param string  $name   Search string
+     * @param integer $from   Offset
+     * @param integer $count  Limit
+     */
+    function searchFaces($name, $from = 0, $count = 0)
+    {
+        $info = array('filter' => 'f.face_name LIKE ' . $GLOBALS['ansel_db']->quote("%$name%"));
+        return $this->_fetchFaces($info, $from, $count);
+    }
+
+    /**
+     * Get faces owned by owner
+     *
+     * @param string  $owner User
+     */
+    function countOwnerFaces($owner)
+    {
+        $info = array('filter' => 's.share_owner = ' . $GLOBALS['ansel_db']->quote($owner));
+        if ($owner != Horde_Auth::getAuth()) {
+            $info['filter'] .= ' AND s.gallery_passwd IS NULL';
+        }
+
+        return $this->_countFaces($info);
+    }
+
+    /**
+     * Count all faces
+     */
+    function countAllFaces()
+    {
+        return $this->_countFaces(array());
+    }
+
+    /**
+     * Get named faces
+     */
+    function countNamedFaces()
+    {
+        $sql = 'SELECT COUNT(*) FROM ansel_faces WHERE face_name IS NOT NULL AND face_name <> \'\'';
+        return $GLOBALS['ansel_db']->queryOne($sql);
+    }
+
+    /**
+     * Seach faces for a name
+     *
+     * @param string  $name Search string
+     */
+    function countSearchFaces($name)
+    {
+        $info = array('filter' => 'f.face_name LIKE ' . $GLOBALS['ansel_db']->quote("%$name%"));
+        return $this->_countFaces($info);
+    }
+
+
+    /**
+     * Checks to see that a given face image exists in the VFS.
+     *
+     * If $create is true, the image is created if it does not
+     * exist. Otherwise false is returned if the image does not exist. True is
+     * returned both if the image already existed OR if it did not exist, but
+     * was successfully created.
+     *
+     * @param integer $image_id  The image_id the face belongs to.
+     * @param integer $face_id   The face_id we are checking for.
+     * @param boolean $create    Automatically create the image if it is not
+     *                           found.
+     *
+     * @return boolean  True if image exists at end of function call, false
+     *                  otherwise.
+     */
+    function viewExists($image_id, $face_id, $create = true)
+    {
+        $vfspath = $this->getVFSPath($image_id) . 'faces';
+        $vfsname = $face_id . $this->getExtension();
+        if (!$GLOBALS['ansel_vfs']->exists($vfspath, $vfsname)) {
+            if (!$create) {
+                return false;
+            }
+            $data = $this->getFaceById($face_id, true);
+            if (is_a($data, 'PEAR_Error')) {
+                return $data;
+            }
+            $image = &$GLOBALS['ansel_storage']->getImage($image_id);
+            if (is_a($image, 'PEAR_Error')) {
+                return $image;
+            }
+
+            // Actually create the image.
+            $result = $this->createView(
+                $face_id,
+                $image,
+                $data['face_x1'],
+                $data['face_y1'],
+                $data['face_x2'],
+                $data['face_y2']);
+            if (is_a($result, 'PEAR_Error')) {
+                return $result;
+            }
+            $this->saveSignature($image_id, $face_id);
+        }
+        return true;
+    }
+
+    /**
+     * Get a Horde_Image object representing the requested face.
+     *
+     * @param integer $face_id  The requested face_id
+     *
+     * @return mixed  The requeste Horde_Image object || PEAR_Error
+     */
+    function getFaceImageObject($face_id)
+    {
+        $face = $this->getFaceById($face_id, true);
+        if (is_a($face, 'PEAR_Error')) {
+            Horde::logMessage($face, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return $face;
+        }
+
+        // Load the image for this face
+        if (!$this->viewExists($face['image_id'], $face_id, true)) {
+            $err = PEAR::raiseError(sprintf("Unable to create or locate face_id %u", $face_id));
+            Horde::logMessage($err, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return $err;
+        }
+        $vfspath = $this->getVFSPath($face['image_id']) . 'faces';
+        $vfsname = $face_id . $this->getExtension();
+        $img = Ansel::getImageObject();
+        $data = $GLOBALS['ansel_vfs']->read($vfspath, $vfsname);
+        if (is_a($data, 'PEAR_Error')) {
+            Horde::logMessage($data, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return $data;
+        }
+        $img->loadString($face_id, $data);
+        return $img;
+    }
+
+    /**
+     * Get a URL for a face image suitable for using as the src attribute in an
+     * image tag.
+     *
+     * @param integer $image_id  Image ID to get url for
+     * @param integer $face_id   Face ID to get url for
+     * @param boolean $full      Should we generate a full URL?
+     *
+     * @return string  The URL for the face image suitable for use as the src
+     *                 attribute in an <img> tag.
+     */
+    function getFaceUrl($image_id, $face_id, $full = false)
+    {
+        global $conf;
+
+        // If we won't be using img.php to generate it, make sure the image
+        // is generated before returning a url to access it.
+        if ($conf['vfs']['src'] != 'php') {
+            $this->viewExists($image_id, $face_id, true);
+        }
+
+        // If not viewing directly out of the VFS, hand off to img.php
+        if ($conf['vfs']['src'] != 'direct') {
+            return Horde::applicationUrl(
+                Horde_Util::addParameter('faces/img.php', 'face', $face_id), $full);
+        } else {
+            $path = substr(str_pad($image_id, 2, 0, STR_PAD_LEFT), -2) . '/faces';
+            return $GLOBALS['conf']['vfs']['path'] . htmlspecialchars($path . '/' . $face_id . $this->getExtension());
+        }
+    }
+
+    /**
+     * Get image path
+     *
+     * @param integer $image Image ID to get
+     * @static
+     */
+    function getVFSPath($image)
+    {
+        return '.horde/ansel/' . substr(str_pad($image, 2, 0, STR_PAD_LEFT), -2) . '/';
+    }
+
+    /**
+     * Get filename extension
+     *
+     * @static
+     */
+    function getExtension()
+    {
+        if ($GLOBALS['conf']['image']['type'] == 'jpeg') {
+            return '.jpg';
+        } else {
+            return '.png';
+        }
+    }
+
+    /**
+     * Associates a given rectangle with the given image and creates the face
+     * image. Used for setting a face range explicitly.
+     *
+     * @param integer $face_id   Face id to save
+     * @param integer $image     Image face belongs to
+     * @param integer $x1        The top left corner of the cropped image.
+     * @param integer $y1        The top right corner of the cropped image.
+     * @param integer $x2        The bottom left corner of the cropped image.
+     * @param integer $y2        The bottom right corner of the cropped image.
+     * @param string  $name      Face name
+     *
+     * @return array Faces found
+     */
+    function saveCustomFace($face_id, $image, $x1, $y1, $x2, $y2, $name = '')
+    {
+        $image = &$GLOBALS['ansel_storage']->getImage($image);
+        if (is_a($image, 'PEAR_Error')) {
+            return $image;
+        }
+        $gallery = $GLOBALS['ansel_storage']->getGallery($image->gallery);
+        if (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+            return PEAR::raiseError(_("Access denied editing the photo."));
+        }
+
+        if (empty($face_id)) {
+            $new = true;
+            $face_id = $GLOBALS['ansel_db']->nextId('ansel_faces');
+            if (is_a($face_id, 'PEAR_Error')) {
+                return $face_id;
+            }
+        }
+
+        // The user edits the screen image not the full image
+        $image->load('screen');
+
+        // Process the image
+        $result = $this->createView($face_id,
+                                    $image,
+                                    $x1,
+                                    $y1,
+                                    $x2,
+                                    $y2);
+
+        // Clean up as images are static and all gallery images data will remain in memory
+        $image->reset();
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        }
+
+        // Store face id db
+        if (empty($new)) {
+            $sql = 'UPDATE ansel_faces SET face_name = ?, face_x1 = ?, face_y1 = ?, face_x2 = ?, face_y2 = ?'
+                    . ' WHERE face_id = ?';
+            $params = array($name,
+                            $x1,
+                            $y1,
+                            $x2,
+                            $y2,
+                            $face_id);
+        } else {
+
+            $sql = 'INSERT INTO ansel_faces (face_id, image_id, gallery_id, face_name, '
+                    . ' face_x1, face_y1, face_x2, face_y2)'
+                    . ' VALUES (?, ?, ?, ?, ?, ?, ?, ?)';
+            $params = array($face_id,
+                            $image->id,
+                            $image->gallery,
+                            $name,
+                            $x1,
+                            $y1,
+                            $x2,
+                            $y2);
+        }
+
+        $q = $GLOBALS['ansel_db']->prepare($sql, null, MDB2_PREPARE_MANIP);
+        if (is_a($q, 'PEAR_Error')) {
+            Horde::logMessage($q, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return $q;
+        }
+        $result = $q->execute($params);
+        $q->free();
+        if (is_a($result, 'PEAR_Error')) {
+            Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return $result;
+        }
+
+        // Update gallery and image counts
+        $GLOBALS['ansel_db']->exec('UPDATE ansel_images SET image_faces = image_faces + 1 WHERE image_id = ' . $image->id);
+        $GLOBALS['ansel_db']->exec('UPDATE ansel_shares SET attribute_faces = attribute_faces + 1 WHERE gallery_id = ' . $image->gallery);
+
+        // Save signature
+        $this->saveSignature($image->id, $face_id);
+
+        return $face_id;
+    }
+
+
+    /**
+     * Look for and save faces in a picture, and optionally create the face
+     * image.
+     *
+     * @param mixed $image Image Object/ID to check
+     * @param boolen $create Create images or store data?
+     *
+     * @return array Faces found
+     */
+    function getFromPicture(&$image, $create = false)
+    {
+        // get image if ID is passed
+        if (!is_a($image, 'Ansel_Image')) {
+            $image = &$GLOBALS['ansel_storage']->getImage($image);
+            if (is_a($image, 'PEAR_Error')) {
+                return $image;
+            }
+            $gallery = $GLOBALS['ansel_storage']->getGallery($image->gallery);
+            if (is_a($gallery, 'PEAR_Error')) {
+                return $gallery;
+            }
+            if (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+                return PEAR::raiseError(_("Access denied editing the photo."));
+            }
+        }
+
+        // Get the rectangles for any faces in this image.
+        $faces = $this->getFaces($image);
+        if (is_a($faces, 'PEAR_Error')) {
+            return $faces;
+        } elseif (empty($faces)) {
+            return array();
+        }
+
+        // Clean up any existing faces we may have had in this image.
+        $result = $this->delete($image);
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        }
+
+        // Process faces
+        $fids = array();
+        foreach ($faces as $i => $rect) {
+            // Create Face id
+            $face_id = $GLOBALS['ansel_db']->nextId('ansel_faces');
+            if (is_a($face_id, 'PEAR_Error')) {
+                Horde::logMessage($face_id, __FILE__, __LINE__, PEAR_LOG_ERR);
+                return $face_id;
+            }
+
+            // Store face id db
+            $sql = 'INSERT INTO ansel_faces (face_id, image_id, gallery_id, face_x1, '
+                    . ' face_y1, face_x2, face_y2)'
+                    . ' VALUES (?, ?, ?, ?, ?, ?, ?)';
+
+            $params = $this->_getParamsArray($face_id, $image, $rect);
+
+            $q = $GLOBALS['ansel_db']->prepare($sql, null, MDB2_PREPARE_MANIP);
+            if (is_a($q, 'PEAR_Error')) {
+                Horde::logMessage($q, __FILE__, __LINE__, PEAR_LOG_ERR);
+                return $q;
+            }
+            $result = $q->execute($params);
+            $q->free();
+            if (is_a($result, 'PEAR_Error')) {
+                Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+                return $result;
+            }
+            if ($create) {
+                // Process image
+                $result = $this->_createView($face_id, $image, $rect);
+
+                // Clear any loaded views to save on memory usage.
+                // TODO: Not sure if this is really necessary or not.
+                $image->reset();
+                if (is_a($result, 'PEAR_Error')) {
+                    return $result;
+                }
+                $this->saveSignature($image->id, $face_id);
+            }
+            $fids[$face_id] = '';
+
+        }
+
+        // Update gallery and image counts
+        $GLOBALS['ansel_db']->exec('UPDATE ansel_images SET image_faces = ' . count($fids) . ' WHERE image_id = ' . $image->id);
+        $GLOBALS['ansel_db']->exec('UPDATE ansel_shares SET attribute_faces = attribute_faces + ' . count($fids) . ' WHERE gallery_id = ' . $image->gallery);
+
+        // Expire gallery cache
+        if ($GLOBALS['conf']['ansel_cache']['usecache']) {
+            $GLOBALS['cache']->expire('Ansel_Gallery' . $gallery->id);
+        }
+
+        return $fids;
+    }
+
+    /**
+     * Create a face image from the given data.
+     *
+     * @param integer $face_id   Face id to generate
+     * @param integer $image     Image face belongs to
+     * @param integer $x1        The top left corner of the cropped image.
+     * @param integer $y1        The top right corner of the cropped image.
+     * @param integer $x2        The bottom left corner of the cropped image.
+     * @param integer $y2        The bottom right corner of the cropped image.
+     *
+     * @return mixed  the face id or PEAR_Error on failure.
+     */
+    function createView($face_id, &$image, $x1, $y1, $x2, $y2)
+    {
+        // Make sure screen view is created and loaded
+        $image->load('screen');
+
+        // Crop to the face
+        $result = $image->_image->crop($x1, $y1, $x2, $y2);
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        }
+
+        // Resize and save
+        $ext = $this->getExtension();
+        $path = $this->getVFSPath($image->id);
+        $image->_image->resize(50, 50, false);
+        $result = $GLOBALS['ansel_vfs']->writeData($path . 'faces', $face_id . $ext,
+                                                   $image->_image->raw(), true);
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        }
+
+        return $face_id;
+    }
+
+    /**
+     * Get get face signature from an existing face image.
+     *
+     * @param integer $image_id Image ID face belongs to
+     * @param integer $face_id Face ID to check
+     *
+     * @return mixed  True || PEAR_Error
+     */
+    function saveSignature($image_id, $face_id)
+    {
+        // can we get it?
+        if (empty($GLOBALS['conf']['faces']['search']) ||
+            Horde_Util::loadExtension('libpuzzle') === false) {
+
+            return '';
+        }
+
+        // Ensure we have an on-disk file to read the signature from.
+        $path  = $GLOBALS['ansel_vfs']->readFile($this->getVFSPath($image_id) . '/faces',
+                                                 $face_id . $this->getExtension());
+
+        $signature = puzzle_fill_cvec_from_file($path);
+        if (empty($signature)) {
+            return '';
+        }
+        // save compressed signature
+        $sql = 'UPDATE ansel_faces SET face_signature = ? WHERE face_id = ?';
+        $params = array(puzzle_compress_cvec($signature), $face_id);
+        $q = $GLOBALS['ansel_db']->prepare($sql, null, MDB2_PREPARE_MANIP);
+        if (is_a($q, 'PEAR_Error')) {
+            Horde::logMessage($q, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return $q;
+        }
+        $result = $q->execute($params);
+        $q->free();
+        if (is_a($result, 'PEAR_Error')) {
+            Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return $result;
+        }
+
+        // create index
+        $word_len = $GLOBALS['conf']['faces']['search'];
+        $str_len = strlen($signature);
+        $times = $str_len / $word_len;
+        $data = array();
+        for ($i = 0; $i < $times; $i++) {
+            $data[] = array($face_id,
+                            $i,
+                            substr($signature, $i * $word_len, $word_len));
+        }
+
+        $GLOBALS['ansel_db']->exec('DELETE FROM ansel_faces_index WHERE face_id = ' . $face_id);
+        $q = &$GLOBALS['ansel_db']->prepare('INSERT INTO ansel_faces_index (face_id, index_position, index_part) VALUES (?, ?, ?)');
+        if (is_a($q, 'PEAR_Error')) {
+            Horde::logMessage($q, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return $q;
+        }
+
+        $GLOBALS['ansel_db']->loadModule('Extended');
+        $GLOBALS['ansel_db']->executeMultiple($q, $data);
+        $q->free();
+
+        return true;
+    }
+
+    /**
+     * Get an image signature from an arbitrary file. Currently used when
+     * searching for faces that appear in a user-supplied image.
+     *
+     * @param integer $filename Image filename to check
+     *
+     * @return binary vector signature
+     */
+    function getSignatureFromFile($filename)
+    {
+        if ($GLOBALS['conf']['faces']['search'] == 0 ||
+            Horde_Util::loadExtension('libpuzzle') === false) {
+
+            return '';
+        }
+
+        return puzzle_fill_cvec_from_file($filename);
+    }
+
+    /**
+     * Get faces for all images in a gallery
+     *
+     * @param integer $gallery_id  The share_id/gallery_id of the gallery to
+     *                             check.
+     * @param boolen $create       Create faces and signatures or just store coordniates?
+     * @param boolen $force Force recreation even if image has faces
+     *
+     * @return array Faces found
+     */
+    function getFromGallery($gallery_id, $create = false, $force = false)
+    {
+        $gallery = $GLOBALS['ansel_storage']->getGallery($gallery_id);
+        if (is_a($gallery, 'PEAR_Error')) {
+            return $gallery;
+        } elseif (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+            return PEAR::raiseError(sprintf(_("Access denied editing gallery \"%s\"."), $gallery->get('name')));
+        }
+
+        $images = $gallery->getImages();
+        if (is_a($images, 'PEAR_Error')) {
+            return $images;
+        }
+
+        $faces = array();
+        foreach ($images as $image) {
+            if ($image->facesCount && $force == false) {
+                continue;
+            }
+            $result = $this->getFromPicture($image, $create);
+            if (is_a($result, 'PEAR_Error')) {
+                return $result;
+            } elseif (!empty($result)) {
+                $faces[$image->id] = $result;
+            }
+            unset($image);
+        }
+
+        return $faces;
+    }
+
+    /**
+     * Delete faces from VFS and DB storage.
+     *
+     * @param Ansel_Image $image Image object to delete faces for
+     * @param integer $face  Face id
+     * @static
+     */
+    function delete(&$image, $face = null)
+    {
+        if ($image->facesCount == 0) {
+            return true;
+        }
+
+        $path = Ansel_Faces::getVFSPath($image->id) . '/faces';
+        $ext = Ansel_Faces::getExtension();
+
+        if ($face === null) {
+            $sql = 'SELECT face_id FROM ansel_faces WHERE image_id = ' . $image->id;
+            $face = $GLOBALS['ansel_db']->queryCol($sql);
+            if (is_a($face, 'PEAR_Error')) {
+                Horde::logMessage($face, __FILE__, __LINE__, PEAR_LOG_ERR);
+                return $face;
+            }
+
+            foreach ($face as $id) {
+                $GLOBALS['ansel_vfs']->deleteFile($path, $id . $ext);
+            }
+
+            $GLOBALS['ansel_db']->exec('DELETE FROM ansel_faces WHERE image_id = ' . $image->id);
+            $GLOBALS['ansel_db']->exec('UPDATE ansel_images SET image_faces = 0 WHERE image_id = ' . $image->id . ' AND image_faces > 0 ');
+            $GLOBALS['ansel_db']->exec('UPDATE ansel_shares SET attribute_faces = attribute_faces - ' . count($face) . ' WHERE gallery_id = ' . $image->gallery . ' AND attribute_faces > 0 ');
+        } else {
+            $GLOBALS['ansel_vfs']->deleteFile($path, (int)$face . $ext);
+            $GLOBALS['ansel_db']->exec('DELETE FROM ansel_faces WHERE face_id = ' . (int)$face);
+            $GLOBALS['ansel_db']->exec('UPDATE ansel_images SET image_faces = image_faces - 1 WHERE image_id = ' . $image->id . ' AND image_faces > 0 ');
+            $GLOBALS['ansel_db']->exec('UPDATE ansel_shares SET attribute_faces = attribute_faces - 1 WHERE gallery_id = ' . $image->gallery . ' AND attribute_faces > 0 ');
+        }
+
+        return true;
+    }
+
+    /**
+     * Set face name
+     *
+     * @param integer $face  Face id
+     * @param string $name  Face name
+     */
+    function setName($face, $name)
+    {
+        $sql = 'UPDATE ansel_faces SET face_name = ? WHERE face_id = ?';
+        $params = array($name, $face);
+
+        $q = $GLOBALS['ansel_db']->prepare($sql, null, MDB2_PREPARE_MANIP);
+        if (is_a($q, 'PEAR_Error')) {
+            Horde::logMessage($q, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return $q;
+        }
+
+        return $q->execute($params);
+    }
+
+    /**
+     * Get face link. Points to the image that this face is from.
+     *
+     * @param array $face  Face data
+     *
+     * @static
+     * @return string  The url for the image this face belongs to.
+     */
+    function getLink($face)
+    {
+        return Ansel::getUrlFor('view',
+                                array('view' => 'Image',
+                                      'gallery' => $face['gallery_id'],
+                                      'image' => $face['image_id']));
+    }
+
+    /**
+     * Get face data
+     *
+     * @param integer $face_id  Face id
+     * @param boolean $full     Retreive full face data?
+     */
+    function getFaceById($face_id, $full = false)
+    {
+        $sql = 'SELECT image_id, gallery_id, face_name';
+        if ($full) {
+            $sql .= ', face_x1, face_y1, face_x2, face_y2, face_signature';
+        }
+        $sql .= ' FROM ansel_faces WHERE face_id = ?';
+        $q = $GLOBALS['ansel_db']->prepare($sql);
+        if (is_a($q, 'PEAR_Error')) {
+            Horde::logMessage($q, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return $q;
+        }
+
+        $result = $q->execute((int)$face_id);
+        if (is_a($result, 'PEAR_Error')) {
+            Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return $result;
+        } elseif ($result->numRows() == 0) {
+            return PEAR::raiseError(_("Face does not exist"));
+        }
+
+        $face = $result->fetchRow(MDB2_FETCHMODE_ASSOC);
+        if (is_a($face, 'PEAR_Error')) {
+            Horde::logMessage($face, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return $face;
+        }
+
+        // Always return the face_id
+        $face['face_id'] = $face_id;
+
+        if ($full && $GLOBALS['conf']['faces']['search'] &&
+            function_exists('puzzle_uncompress_cvec')) {
+            $face['face_signature'] = puzzle_uncompress_cvec($face['face_signature']);
+        }
+
+        if (empty($face['face_name'])) {
+            $face['galleries'][$face['gallery_id']][] = $face['image_id'];
+            return $face;
+        }
+
+        $sql = 'SELECT gallery_id, image_id FROM ansel_faces WHERE face_name = ' . $GLOBALS['ansel_db']->quote($face['face_name']);
+        $result = $GLOBALS['ansel_db']->query($sql);
+
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        } elseif ($result->numRows() == 0) {
+            return PEAR::RaiseError(_("Face does not exist"));
+        }
+
+        while ($gallery = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
+            $face['galleries'][$gallery['gallery_id']][] = $gallery['image_id'];
+        }
+
+        return $face;
+    }
+
+    /**
+     * Get possible matches from sql index
+     *
+     * @param binary $signature Image signature
+     * @param integer $from Offset
+     * @param integer $count Limit
+     *
+     * @return binary vector signature
+     */
+    function getSignatureMatches($signature, $face_id = 0, $from = 0, $count = 0)
+    {
+        $word_len = $GLOBALS['conf']['faces']['search'];
+        $str_len = strlen($signature);
+        $times = $str_len / $word_len;
+
+        $indexes = array();
+        for ($i = 0; $i < $times; $i++) {
+            $indexes[] = '(index_position = '
+                . $GLOBALS['ansel_db']->quote($i, 'integer')
+                . ' AND index_part = '
+                . $GLOBALS['ansel_db']->quote(
+                    substr($signature, $i * $word_len, $word_len))
+                . ')';
+        }
+
+        $sql = 'SELECT COUNT(*) as face_matches, i.face_id, f.face_name, '
+            . 'f.image_id, f.gallery_id, f.face_signature '
+            . 'FROM ansel_faces_index i, ansel_faces f '
+            . 'WHERE f.face_id = i.face_id';
+        if ($face_id) {
+            $sql .= ' AND i.face_id <> '
+                . $GLOBALS['ansel_db']->quote($face_id, 'integer');
+        }
+        if ($indexes) {
+            $sql .= ' AND (' . implode(' OR ', $indexes) . ')';
+        }
+        $sql .= ' GROUP BY i.face_id HAVING face_matches > 0 '
+            . 'ORDER BY face_matches DESC';
+        $GLOBALS['ansel_db']->setLimit($count, $from);
+
+        $result = $GLOBALS['ansel_db']->query($sql);
+        if (is_a($result, 'PEAR_Error')) {
+            Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return $result;
+        } elseif ($result->numRows() == 0) {
+            return array();
+        }
+
+        $faces = array();
+        while ($face = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
+            $faces[$face['face_id']] = array(
+                'face_name' => $face['face_name'],
+                'face_id' => $face['face_id'],
+                'gallery_id' => $face['gallery_id'],
+                'image_id' => $face['image_id'],
+                'similarity' => puzzle_vector_normalized_distance(
+                    $signature,
+                    puzzle_uncompress_cvec($face['face_signature'])));
+        }
+        uasort($faces, array($this, '_getSignatureMatches'));
+
+        return $faces;
+    }
+
+    /**
+     * Compare faces by similarity.
+     *
+     * @param array $a
+     * @param array $b
+     */
+    function _getSignatureMatches($a, $b)
+    {
+        return $a['similarity'] > $b['similarity'];
+    }
+
+    /**
+     * Output HTML for this face's tile
+     * @static
+     */
+    function getFaceTile($face)
+    {
+        $faces = Ansel_Faces::singleton();
+
+        if (!is_array($face)) {
+            $face = $faces->getFaceById($face, true);
+        }
+
+        $face_id = $face['face_id'];
+        $claim_url = Horde::applicationUrl('faces/claim.php');
+        $search_url = Horde::applicationUrl('faces/search/image_search.php');
+
+        // The HTML to display the face image.
+        $imghtml = sprintf("<img src=\"%s\" class=\"bordered-facethumb\" id=\"%s\" alt=\"%s\" />",
+             $faces->getFaceUrl($face['image_id'], $face_id),
+             'facethumb' . $face_id,
+             htmlspecialchars($face['face_name']));
+
+        $img_view_url = Ansel::getUrlFor('view',
+            array('gallery' => $face['gallery_id'],
+                  'view' => 'Image',
+                  'image'=> $face['image_id'],
+                  'havesearch' => false));
+
+        // Build the actual html
+        $html = '<div id="face' . $face_id . '"><table><tr><td>'
+                . ' <a href="' . $img_view_url . '">' . $imghtml . '</a></td><td>';
+        if (!empty($face['face_name'])) {
+            $html .= Horde::link(Horde_Util::addParameter(Horde::applicationUrl('faces/face.php'), 'face', $face['face_id'], false)) . $face['face_name'] . '</a><br />';
+        }
+
+        // Display the face name or a link to claim the face.
+        if (empty($face['face_name']) && $GLOBALS['conf']['report_content']['driver']) {
+            $html .= ' <a href="' . Horde_Util::addParameter($claim_url, 'face', $face_id)
+                . '" title="' . _("Do you know someone in this photo?") . '">'
+                . _("Claim") . '</a>';
+        }
+
+        // Link for searching for similar faces.
+        $html .= ' <a href="' . Horde_Util::addParameter($search_url, 'face_id', $face_id)
+            . '">' . _("Find similar") . '</a>';
+        $html .= '</div></td></tr></table>';
+
+        return $html;
+    }
+
+
+}
diff --git a/ansel/lib/Faces/facedetect.php b/ansel/lib/Faces/facedetect.php
new file mode 100644 (file)
index 0000000..80b6616
--- /dev/null
@@ -0,0 +1,81 @@
+<?php
+/**
+ * Face_detect implementation
+ *
+ * @author  Duck <duck@obala.net>
+ * @package Ansel
+ */
+class Ansel_Faces_facedetect extends Ansel_Faces
+{
+    /**
+     * Where the face defintions are stored
+     */
+    private $_defs = '';
+
+    /**
+     * Create instance
+     */
+    public function __construct($params)
+    {
+        $this->_defs = $params['defs'];
+    }
+
+    /**
+     * Get faces
+     *
+     * @param string $file Picture filename
+     * @throws Horde_Exception
+     */
+    protected function _getFaces($file)
+    {
+        $result = Horde_Util::loadExtension('facedetect');
+        if (!$result) {
+            throw new Horde_Exception('You do not have the facedetect extension enabled in PHP');
+        }
+
+        return face_detect($file, $this->_defs);
+    }
+
+    /**
+     * Check if a face in is inside anoter face
+     *
+     * @param array $face  Face we are cheking
+     * @param array $faces Existing faces
+     *
+     * @param int Face ID containg passed face
+     */
+    protected function _isInFace($face, $faces)
+    {
+        foreach ($faces as $id => $rect) {
+            if ($face['x'] > $rect['x'] && $face['x'] + $face['w'] < $face['x'] + $rect['w']
+                && $face['y'] > $rect['y'] && $face['y'] + $face['h'] < $face['y'] + $rect['h']) {
+                return $id;
+            }
+        }
+
+        return false;
+    }
+
+    protected function _getParamsArray($face_id, $image, $rect)
+    {
+        $params = array($face_id,
+                $image->id,
+                $image->gallery,
+                $rect['x'],
+                $rect['y'],
+                $rect['x'] + $rect['w'],
+                $rect['y'] + $rect['h']);
+       return $params;
+    }
+
+    protected function _createView($face_id, $image, $rect)
+    {
+        return $this->createView($face_id,
+                                $image,
+                                $rect['x'],
+                                $rect['y'],
+                                $rect['x'] + $rect['w'],
+                                $rect['y'] + $rect['h']);
+    }
+
+}
diff --git a/ansel/lib/Faces/opencv.php b/ansel/lib/Faces/opencv.php
new file mode 100644 (file)
index 0000000..77a87ce
--- /dev/null
@@ -0,0 +1,95 @@
+<?php
+/**
+ * OpenCV implementation
+ *
+ * @author  Duck <duck@obala.net>
+ * @package Ansel
+ */
+class Ansel_Faces_opencv extends Ansel_Faces
+{
+    /**
+     * Where the face defintions are stored
+     */
+    private $_defs = '';
+
+    /**
+     * Create instance
+     */
+    function Ansel_Faces_opencv($params)
+    {
+        $this->_defs = $params['defs'];
+    }
+
+    /**
+     * Get faces
+     *
+     * @param string $file Picture filename
+     */
+    function _getFaces($file)
+    {
+        $result = Horde_Util::loadExtension('opencv');
+        if (!$result) {
+            $err = PEAR::raiseError(_("You do not have the opencv extension enabled in PHP"));
+            Horde::logMessage($err, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return $err;
+        }
+        $im = cv_image_load($file);
+        $haar = cv_object_load($this->_defs);
+        $seq = cv_haar_classifier_cascade_detect_objects($haar, $im);
+        $l = cv_seq_count($seq);
+        Horde::logMessage(sprintf("opencv extension detected %u faces.", $l),
+                          __FILE__, __LINE__, PEAR_LOG_DEBUG);
+        $rects = array();
+        for ($i = 0; $i < $l; $i++) {
+            $r = cv_seq_get($seq, $i);
+            $rects[] = array('x' => $r['x'],
+                             'y' => $r['y'],
+                             'width' => $r['w'],
+                             'height' => $r['h']);
+        }
+
+        return $rects;
+    }
+
+    /**
+     * Check if a face in is inside anoter face
+     *
+     * @param array $face  Face we are cheking
+     * @param array $faces Existing faces
+     *
+     * @param int Face ID containg passed face
+     */
+    function _isInFace($face, $faces)
+    {
+        foreach ($faces as $id => $rect) {
+            if ($face['x'] > $rect['x'] && $face['x'] + $face['width'] < $face['x'] + $rect['width']
+                && $face['y'] > $rect['y'] && $face['y'] + $face['height'] < $face['y'] + $rect['height']) {
+                return $id;
+            }
+        }
+
+        return false;
+    }
+
+    function _getParamsArray($face_id, $image, $rect)
+    {
+        $params = array($face_id,
+                $image->id,
+                $image->gallery,
+                $rect['x'],
+                $rect['y'],
+                $rect['x'] + $rect['width'],
+                $rect['y'] + $rect['height']);
+       return $params;
+    }
+
+    function _createView($face_id, $image, $rect)
+    {
+        return $this->createView($face_id,
+                                $image,
+                                $rect['x'],
+                                $rect['y'],
+                                $rect['x'] + $rect['width'],
+                                $rect['y'] + $rect['height']);
+    }
+}
diff --git a/ansel/lib/Forms/Ecard.php b/ansel/lib/Forms/Ecard.php
new file mode 100644 (file)
index 0000000..155e96d
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+/**
+ * $Horde: ansel/lib/Forms/Ecard.php,v 1.3 2009/07/28 20:16:18 jan Exp $
+ *
+ * @package Ansel
+ */
+
+/** Horde_Form */
+require_once 'Horde/Form.php';
+
+/**
+ * Ecard generator.
+ *
+ * @package Ansel
+ */
+
+class EcardForm extends Horde_Form {
+
+    var $_useFormToken = false;
+
+    function EcardForm(&$vars, $title)
+    {
+        parent::Horde_Form($vars, $title);
+
+        $this->setButtons(_("Send"));
+        $this->addHidden('', 'actionID', 'text', false);
+        $this->addHidden('', 'gallery', 'text', false);
+        $this->addHidden('', 'image', 'text', false);
+        $this->addHidden('', 'image_desc', 'text', false);
+
+        $user = Horde_Auth::getAuth();
+        if (empty($user)) {
+            $this->addVariable(_("Use the following return address:"), 'ecard_retaddr', 'text', true);
+        } else {
+            require_once 'Horde/Identity.php';
+            $identity = &Identity::singleton();
+            $from_addr = $identity->getDefaultFromAddress();
+            $vars->set('ecard_retaddr', $from_addr);
+            $this->addHidden('', 'ecard_retaddr', 'text', true);
+        }
+
+        $this->addVariable(_("Send ecard to the following address:"), 'ecard_addr', 'text', true);
+        $this->addVariable(_("Comments:"), 'ecard_comments', 'longtext', false, false, null, array('15', '60'));
+    }
+
+}
diff --git a/ansel/lib/Forms/Image.php b/ansel/lib/Forms/Image.php
new file mode 100644 (file)
index 0000000..e8cab6a
--- /dev/null
@@ -0,0 +1,73 @@
+<?php
+/**
+ * $Horde: ansel/lib/Forms/Image.php,v 1.8 2009/01/06 17:48:53 jan Exp $
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ */
+
+/** Horde_Form **/
+require_once 'Horde/Form.php';
+
+class ImageForm extends Horde_Form {
+
+    var $_useFormToken = false;
+
+    function ImageForm(&$vars, $title)
+    {
+        global $gallery;
+
+        parent::Horde_Form($vars, $title);
+
+        $this->setButtons(_("Save"));
+        $this->addHidden('', 'actionID', 'text', false);
+        $this->addHidden('', 'gallery', 'text', false);
+        $this->addHidden('', 'image', 'text', false);
+        $this->addHidden('', 'page', 'text', false);
+
+        $filesize = ini_get('upload_max_filesize');
+        if (substr($filesize, -1) == 'M') {
+            $filesize = $filesize * 1048576;
+        }
+        $filesize = $this->_get_size($filesize);
+        $this->addVariable(_("Make this the default photo for this gallery?"),
+                           'image_default', 'boolean', false);
+        $this->addVariable(_("Caption"), 'image_desc', 'longtext', false, false,
+                           null, array('4', '40'));
+
+        $this->addVariable(_("Original Date"), 'image_originalDate',
+                           'monthdayyear', true, false, null,
+                           array('start_year' => 1900));
+
+        $this->addVariable(_("Tags"), 'image_tags', 'text', false);
+
+        $this->addHidden('', 'image0', 'text', false);
+        $upload = &$this->addVariable(
+        _("Replace photo with this file"), 'file0', 'image', false, false,
+        _("Maximum photo size:") . ' '  . $filesize, array(false));
+        $upload->setHelp('upload');
+    }
+
+    /**
+     * Format file size
+     */
+    function _get_size($size)
+    {
+        $bytes = array('B', 'KB', 'MB', 'GB', 'TB');
+
+        foreach ($bytes as $val) {
+            if ($size > 1024) {
+                $size = $size / 1024;
+            } else {
+                break;
+            }
+        }
+
+        return round($size, 2) . ' '  . $val;
+    }
+
+}
\ No newline at end of file
diff --git a/ansel/lib/Forms/ImageDate.php b/ansel/lib/Forms/ImageDate.php
new file mode 100644 (file)
index 0000000..e3d99f2
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+/**
+ * $Horde: ansel/lib/Forms/ImageDate.php,v 1.3 2009/01/06 17:48:53 jan Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ */
+
+/** Horde_Form **/
+require_once 'Horde/Form.php';
+
+class ImageDateForm extends Horde_Form {
+
+    var $_useFormToken = false;
+
+    function ImageDateForm(&$vars, $title)
+    {
+        global $gallery;
+
+        parent::Horde_Form($vars, $title);
+
+        $this->setButtons(_("Save"));
+        $this->addHidden('', 'actionID', 'text', false);
+        $this->addHidden('', 'gallery', 'text', false);
+        $this->addHidden('', 'image', 'text', false);
+        $this->addHidden('', 'page', 'text', false);
+        $this->addVariable(_("Editing dates for the following photos"), 'image_list', 'html', false, true);
+        $this->addVariable(_("Original Date"), 'image_originalDate',
+                           'monthdayyear', true, false, null,
+                           array('start_year' => 1900));
+    }
+
+}
\ No newline at end of file
diff --git a/ansel/lib/Forms/Upload.php b/ansel/lib/Forms/Upload.php
new file mode 100644 (file)
index 0000000..20c8e74
--- /dev/null
@@ -0,0 +1,102 @@
+<?php
+/**
+ * $Horde: ansel/lib/Forms/Upload.php,v 1.12 2008/12/14 18:54:19 mrubinsk Exp $
+ *
+ * @package Ansel
+ */
+
+/** Horde_Form */
+require_once 'Horde/Form.php';
+
+/**
+ * Image uploader. Provides 3 different options - single images,
+ * multiple images, and zip file.
+ *
+ * @package Ansel
+ */
+class UploadForm extends Horde_Form {
+
+    var $_useFormToken = false;
+
+    function UploadForm(&$vars, $title)
+    {
+        global $gallery, $conf;
+
+        parent::Horde_Form($vars, $title);
+
+        $filesize = ini_get('upload_max_filesize');
+        if (substr($filesize, -1) == 'M') {
+            $filesize = $filesize * 1048576;
+        }
+        $filesize = $this->_get_size($filesize);
+
+        $postsize = ini_get('post_max_size');
+        if (substr($postsize, -1) == 'M') {
+            $postsize = $postsize * 1048576;
+        }
+        $postsize = $this->_get_size($postsize);
+
+        $this->setButtons(array(_("Upload"), _("Cancel")));
+        $this->addHidden('', 'gallery', 'text', false);
+        $this->addHidden('', 'page', 'text', false);
+
+        $this->setSection('single_file', _("Single Photo"));
+        $this->addHidden('', 'image0', 'text', false);
+        if (!strlen($vars->get('image0'))) {
+            $upload = &$this->addVariable(
+                _("File to upload"), 'file0', 'image', false, false,
+                _("Maximum photo size:") . ' '  . $filesize, array(false));
+            $upload->setHelp('upload');
+        }
+        $this->addVariable(_("Make this the default photo for this gallery?"), 'image0_default', 'boolean', false);
+        $this->addVariable(_("Caption"), 'image0_desc', 'longtext', false, false, null, array('4', '40'));
+        $this->addVariable(_("Tags"), 'image0_tags', 'text', false, false, _("Separate tags with commas."));
+
+        $this->setSection('multi_file', _("Multiple Photos"));
+
+        if (!strlen($vars->get('image0'))) {
+            $msg = sprintf(_("Maximum photo size: %s; with a total of: %s"),
+                           $filesize, $postsize);
+            $this->addVariable($msg, 'description', 'description', false);
+        }
+
+        // start at $i = 1 because 0 is used above.
+        for ($i = 1; $i <= $conf['image']['num_uploads']; $i++) {
+            $this->addHidden('', 'image' . $i, 'text', false);
+            if (!strlen($vars->get('image' . $i))) {
+                $upload = &$this->addVariable(sprintf(_("File %s"), $i), 'file' . $i, 'image', false, false, null, array(false));
+                $upload->setHelp('upload');
+            }
+        }
+
+        $this->setSection('zip_file', _("Zip File Upload"));
+        $this->addHidden('', 'image' . ($conf['image']['num_uploads'] + 1), 'text', false);
+        if (!strlen($vars->get('zip'))) {
+            $upload = &$this->addVariable(
+                _("File to upload"),
+                'file' . ($conf['image']['num_uploads'] + 1),
+                'file', false, false,
+                _("Maximum file size:") . ' ' . $filesize);
+            $upload->setHelp('upload');
+        }
+    }
+
+    /**
+     * Format file size
+     */
+    function _get_size($size)
+    {
+        $bytes = array('B', 'KB', 'MB', 'GB', 'TB');
+
+        foreach ($bytes as $val) {
+            if ($size > 1024) {
+                $size = $size / 1024;
+            } else {
+                break;
+            }
+        }
+
+        return round($size, 2) . ' '  . $val;
+    }
+
+}
diff --git a/ansel/lib/Forms/Watermark.php b/ansel/lib/Forms/Watermark.php
new file mode 100644 (file)
index 0000000..22ba50b
--- /dev/null
@@ -0,0 +1,60 @@
+<?php
+/**
+ * $Horde: ansel/lib/Forms/Watermark.php,v 1.2 2009/01/06 17:48:53 jan Exp $
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ */
+
+/** Horde_Form **/
+require_once 'Horde/Form.php';
+
+class WatermarkForm extends Horde_Form {
+
+    var $_useFormToken = false;
+
+    function WatermarkForm(&$vars, $title)
+    {
+        global $gallery, $prefs;
+
+        parent::Horde_Form($vars, $title);
+
+        $this->setButtons(_("Save"));
+        $this->addHidden('', 'actionID', 'text', false);
+        $this->addHidden('', 'gallery', 'text', false);
+        $this->addHidden('', 'image', 'text', false);
+        $this->addHidden('', 'page', 'text', false);
+
+        $w = &$this->addVariable(_("Custom Watermark"), 'watermark', 'text',
+                                 false, false, null);
+        $w->setDefault($prefs->getValue('watermark_text'));
+
+        $fonts = array('tiny' => _("Tiny"),
+                       'small' => _("Small"),
+                       'medium' => _("Medium"),
+                       'large' => _("Large"),
+                       'giant' => _("Giant"));
+        $f = &$this->addVariable(_("Watermark Font"), 'font', 'enum', false,
+                                 false, null, array($fonts));
+        $f->setDefault($prefs->getValue('watermark_font'));
+
+        $ha = array('left' => _("Left"),
+                    'center' => _("Center"),
+                    'right' => _("Right"));
+        $wha = &$this->addVariable(_("Horizontal Alignment"), 'whalign', 'enum',
+                                   false, false, null, array($ha));
+        $wha->setDefault($prefs->getValue('watermark_horizontal'));
+
+        $va = array('top' => _("Top"),
+                    'center' => _("Center"),
+                    'bottom' => _("Bottom"));
+        $wva = &$this->addVariable(_("Vertical Alignment"), 'wvalign', 'enum',
+                                   false, false, null, array($va));
+        $wva->setDefault($prefs->getValue('watermark_vertical'));
+    }
+
+}
\ No newline at end of file
diff --git a/ansel/lib/GalleryMode/Date.php b/ansel/lib/GalleryMode/Date.php
new file mode 100644 (file)
index 0000000..cd1a523
--- /dev/null
@@ -0,0 +1,1056 @@
+<?php
+/**
+ * $Horde: ansel/lib/GalleryMode/Date.php,v 1.29 2009/07/13 14:29:04 mrubinsk Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ */
+
+/** Horde_Date **/
+require_once 'Horde/Date.php';
+
+/**
+ * Ansel_GalleryMode_Date:: Class for encapsulating gallery methods that
+ * depend on the current display mode of the gallery being Date.
+ */
+class Ansel_GalleryMode_Date {
+
+    /**
+     * @var Ansel_Gallery
+     */
+    var $_gallery;
+
+    /**
+     * The date part array for the current grouping.
+     *
+     * @var array
+     */
+    var $_date = array();
+
+    var $_features = array('slideshow', 'zipdownload', 'upload');
+
+    var $_subGalleries = null;
+
+    /**
+     * Constructor
+     *
+     * @param Ansel_Gallery $gallery  The gallery to bind to.
+     *
+     * @return Ansel_Gallery_ModeDate
+     */
+    function Ansel_GalleryMode_Date($gallery)
+    {
+        $this->_gallery = $gallery;
+    }
+
+    function init() {
+        // noop
+        return true;
+    }
+
+    function hasFeature($feature)
+    {
+        /* First, some special cases */
+        switch ($feature) {
+        case 'sort_images':
+        case 'image_captions':
+        case 'faces':
+            /* Only allowed when we are on a specific day */
+            if (!empty($this->_date['day'])) {
+                return true;
+            }
+            break;
+        }
+        return in_array($feature, $this->_features);
+    }
+
+    /**
+     * Get an array describing where this gallery is in a breadcrumb trail.
+     *
+     * @return  An array of 'title' and 'navdata' hashes with the [0] element
+     *          being the deepest part.
+     */
+    function getGalleryCrumbData()
+    {
+        // Convienience
+        $year = !empty($this->_date['year']) ? $this->_date['year'] : 0;
+        $month = !empty($this->_date['month']) ? $this->_date['month'] : 0;
+        $day = !empty($this->_date['day']) ? $this->_date['day'] : 0;
+        $trail = array();
+
+
+        // Do we have any date parts?
+        if (!empty($year)) {
+            if (!empty($day)) {
+                $date = new Horde_Date($this->_date);
+                $text = $date->format('jS');
+
+                $navdata =  array('view' => 'Gallery',
+                                  'gallery' => $this->_gallery->id,
+                                  'slug' => $this->_gallery->get('slug'),
+                                  'year' => $year,
+                                  'month' => $month,
+                                  'day' => $day);
+
+                $trail[] = array('title' => $text, 'navdata' => $navdata);
+
+            }
+
+            if (!empty($month)) {
+                $date = new Horde_Date(array('year' => $year,
+                                             'month' => $month,
+                                             'day' => 1));
+                $text = $date->format('F');
+                $navdata = array('view' => 'Gallery',
+                                 'gallery' => $this->_gallery->id,
+                                 'slug' => $this->_gallery->get('slug'),
+                                 'year' => $year,
+                                 'month' => $month);
+                $trail[] = array('title' => $text, 'navdata' => $navdata);
+            }
+
+            $navdata = array('view' => 'Gallery',
+                             'gallery' => $this->_gallery->id,
+                             'slug' => $this->_gallery->get('slug'),
+                             'year' => $year);
+            $trail[] = array('title' => $year, 'navdata' => $navdata);
+
+        } else {
+
+            // This is the first level of a date mode gallery.
+            $navdata = array('view' => 'Gallery',
+                             'gallery' => $this->_gallery->id,
+                             'slug' => $this->_gallery->get('slug'));
+            $trail[] = array('title' => _("All dates"), 'navdata' => $navdata);
+        }
+
+        $text = htmlspecialchars($this->_gallery->get('name'), ENT_COMPAT, Horde_Nls::getCharset());
+        $navdata = array('view' => 'Gallery',
+                         'gallery' => $this->_gallery->id,
+                         'slug' => $this->_gallery->get('slug'));
+
+        $trail[] = array('title' => $text, 'navdata' => $navdata);
+
+        return $trail;
+    }
+
+    /**
+     * Getter for _date
+     *
+     * @return array  A date parts array.
+     */
+    function getDate()
+    {
+        return $this->_date;
+    }
+
+    /**
+     * Setter for _date
+     *
+     * @param array $date
+     */
+    function setDate($date = array())
+    {
+        $this->_date = $date;
+    }
+
+    function _getSubGalleries()
+    {
+        if (!is_array($this->_subGalleries)) {
+            /* Get a list of all the subgalleries */
+            $subs = $GLOBALS['ansel_storage']->listGalleries(PERMS_SHOW, null, $this->_gallery);
+            if (is_a($subs, 'PEAR_Error')) {
+                return $subs;
+            }
+            $this->_subGalleries = array_keys($subs);
+        }
+    }
+
+    /**
+     * Get the children of this gallery.
+     *
+     * @param integer $perm  The permissions to limit to.
+     * @param integer $from  The child to start at.
+     * @param integer $to    The child to end with.
+     *
+     * @return A mixed array of Ansel_Gallery_Date and Ansel_Image objects.
+     */
+    function getGalleryChildren($perm = PERMS_SHOW, $from = 0, $to = 0, $noauto = false)
+    {
+        global $ansel_db, $ansel_storage;
+
+        /* Cache the results */
+        static $children = array();
+
+        $cache_key = md5($this->_gallery->id . serialize($this->_date) . $from . $to);
+
+        if (!empty($children[$cache_key])) {
+            return $children[$cache_key];
+        }
+
+        /* Get a list of all the subgalleries */
+        $this->_getSubGalleries();
+        if (count($this->_subGalleries)) {
+            $gallery_where = 'gallery_id IN (' . implode(', ', $this->_subGalleries) . ', ' . $this->_gallery->id . ')';
+        } else {
+            $gallery_where = 'gallery_id = ' . $this->_gallery->id;
+        }
+
+        $sorted_dates = array();
+        /* First let's see how specific the date is */
+        if (!count($this->_date) || empty($this->_date['year'])) {
+            /* All available images - grouped by year */
+            $images = $ansel_storage->listImages($this->_gallery->id, 0, 0, array('image_id', 'image_original_date'), $gallery_where);
+            if (is_a($images, 'PEAR_Error')) {
+                return $images;
+            }
+            $dates = array();
+            foreach ($images as $key => $image) {
+                $dates[date('Y', $image['image_original_date'])][] = $key;
+            }
+            $keys = array_keys($dates);
+
+            /* Drill down further if we only have a single group */
+            if (!$noauto && count($keys) == 1) {
+                $this->_date['year'] = array_pop($keys);
+                return $this->getGalleryChildren($perm, $from, $to, $noauto);
+            }
+            sort($keys, SORT_NUMERIC);
+            foreach ($keys as $key) {
+                $sorted_dates[$key] = $dates[$key];
+            }
+            $display_unit = 'year';
+        } elseif (empty($this->_date['month'])) {
+            /* Specific year - grouped by month */
+            $start = new Horde_Date(
+                array('year' => $this->_date['year'],
+                      'month' => 1,
+                      'day' => 1));
+
+            /* Last second of the year */
+            $end = new Horde_Date($start);
+            $end->mday = 31;
+            $end->month = 12;
+            $end->hour = 23;
+            $end->min = 59;
+            $end->sec = 59;
+
+            /* Get the image ids and dates */
+            $where = 'image_original_date <= ' . (int)$end->timestamp() . ' AND image_original_date >= ' . (int)$start->timestamp();
+            if (!empty($gallery_where)) {
+                $where .= ' AND ' . $gallery_where;
+            }
+            $images= $ansel_storage->listImages($this->_gallery->id, 0, 0, array('image_id', 'image_original_date'), $where);
+            $dates = array();
+            foreach ($images as $key => $image) {
+                $dates[date('n', $image['image_original_date'])][] = $key;
+            }
+            $keys = array_keys($dates);
+
+            /* Only 1 date grouping here, automatically drill down */
+            if (!$noauto && count($keys) == 1) {
+                $this->_date['month'] = array_pop($keys);
+                return $this->getGalleryChildren($perm, $from, $to, $noauto);
+            }
+            sort($keys, SORT_NUMERIC);
+            foreach ($keys as $key) {
+                $sorted_dates[$key] = $dates[$key];
+            }
+            $display_unit = 'month';
+        } elseif (empty($this->_date['day'])) {
+            /* A single month - group by day */
+            $start = new Horde_Date(
+                array('year' => $this->_date['year'],
+                      'month' => $this->_date['month'],
+                      'day' => 1));
+
+            /* Last second of the month */
+            $end = new Horde_Date($start);
+            $end->mday = Horde_Date_Utils::daysInMonth($end->month, $end->year);
+            $end->hour = 23;
+            $end->min = 59;
+            $end->sec = 59;
+
+            $where = 'image_original_date <= ' . (int)$end->timestamp() . ' AND image_original_date >= ' . (int)$start->timestamp();
+            if (!empty($gallery_where)) {
+                $where .= ' AND ' . $gallery_where;
+            }
+            $images= $ansel_storage->listImages($this->_gallery->id, 0, 0, array('image_id', 'image_original_date'), $where);
+            $dates = array();
+            foreach ($images as $key => $image) {
+                $dates[date('d', $image['image_original_date'])][] = $key;
+            }
+            $keys = array_keys($dates);
+
+            /* Only a single grouping, go deeper */
+            if (!$noauto && count($keys) == 1) {
+                $this->_date['day'] = array_pop($keys);
+                return $this->getGalleryChildren($perm, $from, $to, $noauto);
+            }
+            sort($keys, SORT_NUMERIC);
+            foreach ($keys as $key) {
+                $sorted_dates[$key] = $dates[$key];
+            }
+            $dates = $sorted_dates;
+            $display_unit = 'day';
+        } else {
+            /* We are down to a specific day */
+            $start = new Horde_Date($this->_date);
+
+            /* Last second of this day */
+            $end = new Horde_Date($start->timestamp());
+            $end->hour = 23;
+            $end->min = 59;
+            $end->sec = 59;
+
+            $where = 'image_original_date <= ' . (int)$end->timestamp() . ' AND image_original_date >= ' . (int)$start->timestamp();
+            if (!empty($gallery_where)) {
+                $where .= ' AND ' . $gallery_where;
+            }
+            $images= $ansel_storage->listImages($this->_gallery->id, $from, $to, 'image_id', $where, 'image_sort');
+            $results = $ansel_storage->getImages($images, true);
+            if (!is_a($results, 'PEAR_Error')) {
+                if ($this->_gallery->get('has_subgalleries')) {
+                    $images = array();
+                    foreach ($results as $id => $image) {
+                        $image->gallery = $this->_gallery->id;
+                        $images[$id] = $image;
+                    }
+                    $children[$cache_key] = $images;
+                } else {
+                    $children[$cache_key] = $results;
+                }
+            }
+
+            return $children[$cache_key];
+        }
+
+        $results = array();
+        foreach ($sorted_dates as $key => $images) {
+            /* Get the new date parameter */
+            switch ($display_unit) {
+            case 'year':
+                $date = array('year' => $key);
+                break;
+            case 'month':
+                $date = array('year' => $this->_date['year'],
+                              'month' => (int)$key);
+                break;
+            case 'day':
+                $date = array('year' => (int)$this->_date['year'],
+                              'month' => (int)$this->_date['month'],
+                              'day' => (int)$key);
+            }
+
+            $obj = new Ansel_Gallery_Date($this->_gallery, $images);
+            $obj->setDate($date);
+            $results[$key] = $obj;
+        }
+        $children[$cache_key] = $results;
+        if ($from > 0 || $to > 0) {
+            return $this->_getArraySlice($results, $from, $to, true);
+        }
+        return $results;
+    }
+
+
+    /**
+     * Return the count of this gallery's children
+     *
+     * @param integer $perm            The permissions to require.
+     * @param boolean $galleries_only  Only include galleries, no images.
+     *                                 (Ignored since this makes no sense for a
+     *                                  gallery grouped by dates).
+     * @param boolean $noauto          Auto navigate down to the first populated
+     *                                 date grouping.
+     *
+     * @return integer The count of this gallery's children. The count is either
+     *                 a count of of the number of date groupings (months, days,
+     *                 etc..) that need to be displayed, or a count of all the
+     *                 images in the current date grouping (for a specific day).
+     */
+    function countGalleryChildren($perm = PERMS_SHOW, $galleries_only = false, $noauto = true)
+    {
+        $results = $this->getGalleryChildren($this->_date, 0, 0, $noauto);
+        if (is_a($results, 'PEAR_Error')) {
+            return $results;
+        }
+
+        return count($results);
+    }
+
+
+    /**
+     * Lists a slice of the image ids in this gallery.
+     * In Date mode, this only makes sense if we are currently viewing a
+     * specific day, otherwise we return 0.
+     *
+     * @param integer $from  The image to start listing.
+     * @param integer $count The numer of images to list.
+     *
+     * @return mixed  An array of image_ids | PEAR_Error
+     */
+    function listImages($from = 0, $count = 0)
+    {
+        // FIXME: Custom query to get only image_ids when we are at a specific
+        //        date.
+        /* Get all of this grouping's children. */
+        $children = $this->getGalleryChildren();
+
+        /* At day level, these are all Ansel_Images */
+        if (!empty($this->_date['day'])) {
+            $images = array_keys($children);
+        } else {
+            $images = array();
+            // typeof $child == Ansel_Gallery_Date
+            foreach ($children as $child) {
+                $images = array_merge($images, $child->_images);
+            }
+        }
+
+        return $this->_getArraySlice($images, $from, $count);
+    }
+
+    /**
+     * Moves images from one gallery to another. Since we're viewing by date
+     * some images might belong to a subgallery so we need to take care to
+     * udate the appropriate gallery data.
+     *
+     * @param array $images           An array of image_ids to move.
+     * @param Ansel_Gallery $gallery  The Ansel_Gallery to move them to.
+     *
+     * @return mixed  boolean || PEAR_Error
+     */
+    function moveImagesTo($images, $gallery)
+    {
+        if (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+            return PEAR::raiseError(sprintf(_("Access denied moving photos to \"%s\"."), $newGallery->get('name')));
+        } elseif (!$this->_gallery->hasPermission(Horde_Auth::getAuth(), PERMS_DELETE)) {
+            return PEAR::raiseError(sprintf(_("Access denied removing photos from \"%s\"."), $gallery->get('name')));
+        }
+
+        /* Sanitize image ids, and see if we're removing our default image. */
+        $ids = array();
+        foreach ($images as $imageId) {
+            $ids[] = (int)$imageId;
+            if ($imageId == $this->_gallery->data['attribute_default']) {
+                $this->_gallery->set('default', null, true);
+            }
+        }
+
+        /* If we have subgalleries, we need to go the more expensive route. Note
+         * we can't use $gallery->hasSubgalleries() since that would be
+         * overridden here since we are in date mode and thus would return false
+         */
+        if ($this->_gallery->get('has_subgalleries')) {
+            $gallery_ids = array();
+            $images = $GLOBALS['ansel_storage']->getImages($ids);
+            foreach ($images as $image) {
+                if (empty($gallery_ids[$image->gallery])) {
+                    $gallery_ids[$image->gallery] = 1;
+                } else {
+                    $gallery_ids[$image->gallery]++;
+                }
+            }
+        }
+
+        /* Bulk update the images to their new gallery_id */
+        $result = $this->_gallery->_shareOb->_write_db->exec('UPDATE ansel_images SET gallery_id = ' . $gallery->id . ' WHERE image_id IN (' . implode(',', $ids) . ')');
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        }
+
+        /* Update the gallery counts for each affected gallery */
+        if ($this->_gallery->get('has_subgalleries')) {
+            foreach ($gallery_ids as $id => $count) {
+                $this->_gallery->_updateImageCount($count, false, $id);
+            }
+        } else {
+            $this->_gallery->_updateImageCount(count($ids), false);
+        }
+        $this->_gallery->_updateImageCount(count($ids), true, $gallery->id);
+
+        /* Expire the cache since we have no reason to save() the gallery */
+        if ($GLOBALS['conf']['ansel_cache']['usecache']) {
+            $GLOBALS['cache']->expire('Ansel_Gallery' . $gallery->id);
+            $GLOBALS['cache']->expire('Ansel_Gallery' . $this->_gallery->id);
+        }
+
+        return true;
+    }
+
+    /**
+     * Remove an image from this gallery. Note that the image might actually
+     * belong to a subgallery of this gallery since we are viewing by date.
+     * Need to take care of updating correct subgallery's image count etc...
+     *
+     * @param mixed $image      An image_id or Ansel_Image object to delete.
+     * @param boolean $isStack  Image is a stack image (doesn't update count).
+     *
+     * @return mixed  boolean || PEAR_Error
+     */
+    function removeImage($image, $isStack)
+    {
+
+        /* Make sure $image is an Ansel_Image; if not, try loading it. */
+        if (!is_a($image, 'Ansel_Image')) {
+            $img = &$GLOBALS['ansel_storage']->getImage($image);
+            if (is_a($img, 'PEAR_Error')) {
+                return $img;
+            }
+            $image = $img;
+        }
+
+        /* Make sure the image is in this gallery. */
+        if ($image->gallery != $this->_gallery->id) {
+            $this->_getSubGalleries();
+            if (!in_array($image->gallery, $this->_subGalleries)) {
+                return false;
+            }
+        }
+
+        /* Save this for later */
+        $image_gallery = $image->gallery;
+
+        /* Change gallery info. */
+        if ($this->_gallery->data['attribute_default'] == $image->id) {
+            $this->_gallery->data['attribute_default'] = null;
+            $this->_gallery->data['attribute_default_type'] = 'auto';
+        }
+
+        /* Delete cached files from VFS. */
+        $image->deleteCache();
+
+        /* Delete original image from VFS. */
+        $GLOBALS['ansel_vfs']->deleteFile($image->getVFSPath('full'),
+                                          $image->getVFSName('full'));
+
+        /* Delete from SQL. */
+        $this->_gallery->_shareOb->_write_db->exec('DELETE FROM ansel_images WHERE image_id = ' . (int)$image->id);
+
+        /* Remove any attributes */
+        $this->_gallery->_shareOb->_write_db->exec('DELETE FROM ansel_image_attributes WHERE image_id = ' . (int)$image->id);
+
+        /* Remove any geolocation data */
+        $this->_gallery->_shareOb->_write_db->exec('DELETE FROM ansel_images_geolocation WHERE image_id = ' . (int)$image->id);
+
+        if (!$isStack) {
+            $this->_gallery->_updateImageCount(1, false, $image_gallery);
+        }
+
+        /* Update the modified flag if we are not a stack image */
+        if (!$isStack) {
+            $this->_gallery->data['attribute_last_modified'] = time();
+        }
+
+        /* Save all gallery changes */
+        $this->_gallery->save();
+
+        /* Clear the image's tags */
+        $image->setTags(array());
+
+        /* Clear the image's faces */
+        if ($image->facesCount) {
+            require_once ANSEL_BASE . '/lib/Faces.php';
+            Ansel_Faces::delete($image);
+        }
+
+        /* Clear any comments */
+        if (($GLOBALS['conf']['comments']['allow'] == 'all' || ($GLOBALS['conf']['comments']['allow'] == 'authenticated' && Horde_Auth::getAuth())) &&
+            $GLOBALS['registry']->hasMethod('forums/deleteForum')) {
+
+            $result = $GLOBALS['registry']->call('forums/deleteForum',
+                                                 array('ansel', $image->id));
+
+            if (is_a($result, 'PEAR_Error')) {
+                Horde::logMessage($result, __LINE__, __FILE__, PEAR_LOG_ERR);
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Helper function to get an array slice while preserving keys.
+     *
+     * @param unknown_type $array
+     * @param unknown_type $from
+     * @param unknown_type $count
+     * @return unknown
+     */
+    function _getArraySlice($array, $from, $count, $preserve = false)
+    {
+
+        if ($from == 0 && $count == 0) {
+            return $array;
+        }
+
+        if ($preserve && version_compare(PHP_VERSION, '5.0.2', '>=')) {
+            return array_slice($array, $from, $count, true);
+        } elseif ($preserve) {
+            // Can't use array_slice since it won't preserve keys in PHP4
+            $return = array();
+            $i = 0;
+            foreach ($array as $key => $child) {
+                if ($i >= $from && $i < ($from + $count)) {
+                    $return[$key] = $child;
+                }
+                $i++;
+            }
+            return $return;
+        } else {
+            return array_slice($array, $from, $count);
+        }
+    }
+
+    /**
+     * Gets a slice of the images in this gallery.
+     *
+     * @param integer $from  The image to start fetching.
+     * @param integer $count The numer of images to return.
+     *
+     * @param mixed An array of Ansel_Image objects | PEAR_Error
+     */
+    function getImages($from = 0, $count = 0)
+    {
+        /* Get all of this grouping's children. */
+        $children = $this->getGalleryChildren(PERMS_SHOW);
+
+        /* At day level, these are all Ansel_Images, otherwise they are
+         * Ansel_Gallery_Date objects.
+         */
+        if (!empty($this->_date['day'])) {
+            $images = $this->_getArraySlice($children, $from, $count, true);
+        } else {
+            // typeof $child == Ansel_Gallery_Date
+            $ids = array();
+            foreach ($children as $child) {
+                $ids = array_merge($ids, $child->_images);
+            }
+            $ids = $this->_getArraySlice($ids, $from, $count);
+            $images = $GLOBALS['ansel_storage']->getImages($ids);
+        }
+
+        return $images;
+    }
+
+   /**
+     * Checks if the gallery has any subgalleries. This will always be false
+     * for a gallery in date view.
+     */
+    function hasSubGalleries()
+    {
+        return false;
+    }
+
+    /**
+     * Returns the number of images in this gallery and, optionally, all
+     * sub-galleries.
+     *
+     * @param boolean $subgalleries  Determines whether subgalleries should
+     *                               be counted or not.
+     *
+     * @return integer number of images in this gallery
+     */
+    function countImages($subgalleries = false)
+    {
+        return count($this->listImages());
+    }
+
+}
+
+/**
+ * A wrapper/decorator around an Ansel_Gallery to allow multiple date groupings
+ * to access the same Ansel_Gallery instance. This is not a full Ansel_Gallery
+ * implementation.
+ *
+ * TODO: For PHP5, this should be rewritten to get rid of all these gosh-darn
+ * pass through functions.
+ */
+class Ansel_Gallery_Date {
+
+    /* Cache the Gallery Id */
+    var $id;
+
+    /**
+     * The gallery mode helper
+     *
+     * @var Ansel_Gallery_Mode object
+     */
+    var $_modeHelper;
+
+    /* The gallery we are decorating */
+    var $_gallery;
+
+    /* An array of image ids that this "gallery" contains */
+    var $_images;
+
+    /**
+     * The Ansel_Gallery_Date constructor.
+     *
+     * @param Ansel_Gallery $gallery  The gallery we are decorating.
+     * @param array $images           An array of image ids that this grouping
+     *                                contains.
+     */
+    function Ansel_Gallery_Date($gallery, $images = array())
+    {
+        $this->_gallery = $gallery;
+        $this->id = $gallery->id;
+        $this->_setModeHelper();
+        $this->data = $this->_gallery->data;
+        $this->_images = $images;
+    }
+
+    /**
+     * Sets a new GalleryMode helper for this decorated gallery. The client
+     * code (Ansel_GalleryMode_Date) needs to call the setDate() method on the
+     * new GalleryMode_Date object before it's used.
+     *
+     * @return Ansel_Gallery_Mode object
+     */
+    function _setModeHelper()
+    {
+        require_once ANSEL_BASE . '/lib/GalleryMode/Date.php';
+        $this->_modeHelper = new Ansel_GalleryMode_Date($this);
+    }
+
+    /**
+     * Checks if the user can download the full photo
+     *
+     * @return boolean  Whether or not user can download full photos
+     */
+    function canDownload()
+    {
+        return $this->_gallery->canDownload();
+    }
+
+    /**
+     * Copy image and related data to specified gallery.
+     *
+     * @param array $images           An array of image ids.
+     * @param Ansel_Gallery $gallery  The gallery to copy images to.
+     *
+     * @return integer | PEAR_Error The number of images copied or error message
+     */
+    function copyImagesTo($images, $gallery)
+    {
+        return $this->_gallery->copyImagesTo($images, $gallery);
+    }
+
+    /**
+     * Set the order of an image in this gallery.
+     *
+     * @param integer $imageId The image to sort.
+     * @param integer $pos     The sort position of the image.
+     */
+    function setImageOrder($imageId, $pos)
+    {
+        return $this->_gallery->setImageOrder($imageId, $pos);
+    }
+
+    /**
+     * Remove the given image from this gallery.
+     *
+     * @param mixed   $image   Image to delete. Can be an Ansel_Image
+     *                         or an image ID.
+     *
+     * @return boolean  True on success, false on failure.
+     */
+    function removeImage($image, $isStack = false)
+    {
+        return $this->_gallery->removeImage($image, $isStack = false);
+    }
+
+    /**
+     * Returns this share's owner's Identity object.
+     *
+     * @return Identity object for the owner of this gallery.
+     */
+    function getOwner()
+    {
+        return $this->_gallery->getOwner();
+    }
+
+    /**
+     * Output the HTML for this gallery's tile.
+     *
+     * @param Ansel_Gallery $parent  The parent Ansel_Gallery object
+     * @param string $style          A named gallery style to use.
+     * @param boolean $mini          Force the use of a mini thumbnail?
+     * @param array $params          Any additional parameters the Ansel_Tile
+     *                               object may need.
+     */
+    function getTile($parent = null, $style = null, $mini = false,
+                     $params = array())
+    {
+        require_once ANSEL_BASE . '/lib/Tile/DateGallery.php';
+        if (!is_null($parent) && is_null($style)) {
+            $style = $parent->getStyle();
+        } else {
+            $style = Ansel::getStyleDefinition($style);
+        }
+
+        return Ansel_Tile_DateGallery::getTile($this, $style, $mini, $params);
+    }
+
+    /**
+     * Get the children of this gallery.
+     *
+     * @param integer $perm  The permissions to limit to.
+     * @param integer $from  The child to start at.
+     * @param integer $to    The child to end with.
+     *
+     * @return A mixed array of Ansel_Gallery and Ansel_Image objects that are
+     *         children of this gallery.
+     */
+    function getGalleryChildren($perm = PERMS_SHOW, $from = 0, $to = 0, $noauto = false)
+    {
+        return $this->_modeHelper->getGalleryChildren($perm, $from, $to, $noauto);
+    }
+
+
+    /**
+     * Return the count this gallery's children
+     *
+     * @param integer $perm            The permissions to require.
+     * @param boolean $galleries_only  Only include galleries, no images.
+     *
+     * @return integer The count of this gallery's children.
+     */
+    function countGalleryChildren($perm = PERMS_SHOW, $galleries_only = false, $noauto = true)
+    {
+        // Need to force the date helper to not auto drill down when counting
+        // from this method, since we are only called here when we are not
+        // autonavigating.
+        return $this->_modeHelper->countGalleryChildren($perm, $galleries_only, $noauto);
+    }
+
+    /**
+     * Lists a slice of the image ids in this gallery.
+     *
+     * @param integer $from  The image to start listing.
+     * @param integer $count The numer of images to list.
+     *
+     * @return mixed  An array of image_ids | PEAR_Error
+     */
+    function listImages($from = 0, $count = 0)
+    {
+        return $this->_modeHelper->listImages(0, 0);
+    }
+
+    /**
+     * Gets a slice of the images in this gallery.
+     *
+     * @param integer $from  The image to start fetching.
+     * @param integer $count The numer of images to return.
+     *
+     * @param mixed An array of Ansel_Image objects | PEAR_Error
+     */
+    function getImages($from = 0, $count = 0)
+    {
+        return $this->_modeHelper->getImages($from, $count);
+    }
+
+    /**
+     * Return the most recently added images in this gallery.
+     *
+     * @param integer $limit  The maximum number of images to return.
+     *
+     * @return mixed  An array of Ansel_Image objects | PEAR_Error
+     */
+    function getRecentImages($limit = 10)
+    {
+        return $GLOBALS['ansel_storage']->getRecentImages(array($this->id),
+                                                          $limit);
+    }
+
+    /**
+     * Returns the image in this gallery corresponding to the given id.
+     *
+     * @param integer $id  The ID of the image to retrieve.
+     *
+     * @return Ansel_Image  The image object corresponding to the given id.
+     */
+    function &getImage($id)
+    {
+        return $GLOBALS['ansel_storage']->getImage($id);
+    }
+
+    /**
+     * Checks if the gallery has any subgallery
+     */
+    function hasSubGalleries()
+    {
+        return $this->_modeHelper->hasSubGalleries();
+    }
+
+    /**
+     * Returns the number of images in this gallery and, optionally, all
+     * sub-galleries.
+     *
+     * @param boolean $subgalleries  Determines whether subgalleries should
+     *                               be counted or not.
+     *
+     * @return integer number of images in this gallery
+     */
+    function countImages($subgalleries = false)
+    {
+        return count($this->_images);
+    }
+
+    /**
+     * Returns the default image for this gallery.
+     *
+     * @param string $style  Force the use of this style, if it's available
+     *                       otherwise use whatever style is choosen for this
+     *                       gallery. If prettythumbs are not available then
+     *                       we always use ansel_default style.
+     *
+     * @return mixed  The image_id of the default image or false.
+     */
+    function getDefaultImage($style = null)
+    {
+        if (count($this->_images)) {
+            return reset($this->_images);
+        } else {
+            return 0;
+        }
+    }
+
+    /**
+     * Returns this gallery's tags.
+     */
+    function getTags()
+    {
+        return $this->_gallery->getTags();
+    }
+
+    /**
+     * Set/replace this gallery's tags.
+     *
+     * @param array $tags  AN array of tag names to associate with this image.
+     */
+    function setTags($tags)
+    {
+        $this->_gallery->setTags($tags);
+    }
+
+    /**
+     * Return the style definition for this gallery. Returns the first available
+     * style in this order: Explicitly configured style if available, if
+     * configured style is not available, use ansel_default.  If nothing has
+     * been configured, the user's selected default is attempted.
+     *
+     * @return array  The style definition array.
+     */
+    function getStyle()
+    {
+        return $this->_gallery->getStyle();
+    }
+
+    /**
+     * Return a hash key for the given view and style.
+     *
+     * @param string $view   The view (thumb, prettythumb etc...)
+     * @param string $style  The named style.
+     *
+     * @return string  A md5 hash suitable for use as a key.
+     */
+    function _getViewHash($view, $style = null)
+    {
+        return $this->_gallery->_getViewHash($view, $style);
+    }
+
+    /**
+     * Checks to see if a user has a given permission.
+     *
+     * @param string $userid       The userid of the user.
+     * @param integer $permission  A PERMS_* constant to test for.
+     * @param string $creator      The creator of the event.
+     *
+     * @return boolean  Whether or not $userid has $permission.
+     */
+    function hasPermission($userid, $permission, $creator = null)
+    {
+        return $this->_gallery->hasPermission($userid, $permission, $creator);
+    }
+
+    /**
+     * Check user age limtation
+     *
+     * @return boolean
+     */
+    function isOldEnough()
+    {
+        return $this->_gallery->isOldEnough();
+    }
+
+    /**
+     * Return a count of the number of children this share has
+     *
+     * @param integer $perm  A PERMS_* constant
+     * @param boolean $allLevels  Count grandchildren or just children
+     *
+     * @return mixed  The number of child shares || PEAR_Error
+     */
+    function countChildren($perm = PERMS_SHOW, $allLevels = true)
+    {
+        return $this->_gallery->_shareOb->countShares(Horde_Auth::getAuth(), $perm, null, $this, $allLevels);
+    }
+
+    /**
+     * Get all children of this share.
+     *
+     * @param int $perm           PERMS_* constant. If NULL will return all
+     *                            shares regardless of permissions.
+     * @param boolean $allLevels  Return all levels.
+     *
+     * @return mixed  An array of Horde_Share_Object objects || PEAR_Error
+     */
+    function getChildren($perm = PERMS_SHOW, $allLevels = true)
+    {
+        return $this->_gallery->getChildren($perm, $allLevels);
+    }
+
+    /**
+     * Returns a child's direct parent
+     *
+     * @return mixed  The direct parent Horde_Share_Object or PEAR_Error
+     */
+    function getParent()
+    {
+        return $this->_gallery->_shareOb->getParent($this);
+    }
+
+    /**
+     * Get all of this share's parents.
+     *
+     * @return array()  An array of Horde_Share_Objects
+     */
+    function getParents()
+    {
+        return $this->_gallery->getParents();
+
+    }
+
+    function get($attribute)
+    {
+        return $this->_gallery->get($attribute);
+    }
+
+    function getDate()
+    {
+        return $this->_modeHelper->getDate();
+    }
+
+    function setDate($date)
+    {
+        $this->_modeHelper->setDate($date);
+    }
+
+}
diff --git a/ansel/lib/GalleryMode/Normal.php b/ansel/lib/GalleryMode/Normal.php
new file mode 100644 (file)
index 0000000..732aa95
--- /dev/null
@@ -0,0 +1,353 @@
+<?php
+/**
+ * Ansel_Gallery_Mode_Normal:: Class for encapsulating gallery methods that
+ * depend on the current display mode of the gallery.
+ *
+ * $Horde: ansel/lib/GalleryMode/Normal.php,v 1.17 2009/07/17 17:26:40 mrubinsk Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ */
+
+class Ansel_GalleryMode_Normal {
+
+    /**
+     * @var Ansel_Gallery
+     */
+    var $_gallery;
+
+    var $_features = array('subgalleries', 'stacks', 'sort_images',
+                           'image_captions', 'faces', 'slideshow',
+                           'zipdownload', 'upload');
+
+    /**
+     * Constructor
+     *
+     * @param Ansel_Gallery $gallery  The gallery to bind to.
+     *
+     * @return Ansel_Gallery_ModeNormal
+     */
+    function Ansel_GalleryMode_Normal($gallery)
+    {
+        $this->_gallery = $gallery;
+    }
+
+    function init()
+    {
+        // noop
+        return true;
+    }
+
+    function hasFeature($feature)
+    {
+        return in_array($feature, $this->_features);
+    }
+
+    /**
+     * Get the children of this gallery.
+     *
+     * @param integer $perm  The permissions to limit to.
+     * @param integer $from  The child to start at.
+     * @param integer $to    The child to end with.
+     *
+     * @return A mixed array of Ansel_Gallery and Ansel_Image objects that are
+     *         children of this gallery.
+     */
+    function getGalleryChildren($perm = PERMS_SHOW, $from = 0, $to = 0)
+    {
+        $galleries = array();
+        $num_galleries = 0;
+
+        if ($this->hasSubGalleries()) {
+
+            /* Get the number of images and galleries */
+            $numimages = $this->countImages();
+            $num_galleries = $GLOBALS['ansel_storage']->countGalleries(
+                Horde_Auth::getAuth(), PERMS_SHOW, null, $this->_gallery, false);
+
+            /* Now fetch the subgalleries, but only if we need to */
+            if ($num_galleries > $from) {
+                $galleries = $GLOBALS['ansel_storage']->listGalleries(
+                    PERMS_SHOW, null, $this->_gallery, false, $from, $to);
+            }
+        }
+
+        /* Now grab any images if we still have room */
+        if (($to - count($galleries) > 0) || ($from == 0 && $to == 0) &&
+             $this->_gallery->data['attribute_images']) {
+            $images = $this->getImages(max(0, $from - $num_galleries), $to - count($galleries));
+            if (is_a($images, 'PEAR_Error')) {
+                Horde::logMessage($images->message, __FILE__, __LINE__,
+                                  PEAR_LOG_ERR);
+                $images = array();
+            }
+        } else {
+            $images = array();
+        }
+
+        return array_merge($galleries, $images);
+    }
+
+    /**
+     * Get an array describing where this gallery is in a breadcrumb trail.
+     *
+     * @return  An array of 'title' and 'navdata' hashes with the [0] element
+     *          being the deepest part.
+     */
+    function getGalleryCrumbData()
+    {
+        $trail = array();
+        $text = htmlspecialchars($this->_gallery->get('name'));
+        $navdata = array('view' => 'Gallery',
+                         'gallery' => $this->_gallery->id,
+                         'slug' => $this->_gallery->get('slug'));
+        $trail[] = array('title' => $text, 'navdata' => $navdata);
+        $parent_list = array_reverse($this->_gallery->getParents());
+        foreach ($parent_list as $p) {
+            $text = htmlspecialchars($p->get('name'));
+            $navdata = array('view' => 'Gallery',
+                             'gallery' => $p->id,
+                             'slug' => $p->get('slug'));
+            $trail[] = array('title' => $text, 'navdata' => $navdata);
+        }
+
+        return $trail;
+    }
+
+    function setDate($date = array())
+    {
+        //noop
+    }
+
+    function getDate()
+    {
+        return array();
+    }
+
+    /**
+     * Return the count this gallery's children
+     *
+     * @param integer $perm            The permissions to require.
+     * @param boolean $galleries_only  Only include galleries, no images.
+     *
+     * @return integer The count of this gallery's children.
+     */
+    function countGalleryChildren($perm = PERMS_SHOW, $galleries_only = false)
+    {
+        if (!$galleries_only && !$this->hasSubGalleries()) {
+            return $this->_gallery->data['attribute_images'];
+        }
+
+        $gCnt = $GLOBALS['ansel_storage']->countGalleries(Horde_Auth::getAuth(),
+                                                          $perm, null,
+                                                          $this->_gallery, false);
+
+        if (!$galleries_only) {
+            $iCnt = $this->countImages(false);
+        } else {
+            $iCnt = 0;
+        }
+        return $gCnt + $iCnt;
+    }
+    /**
+     * Lists a slice of the image ids in this gallery.
+     *
+     * @param integer $from  The image to start listing.
+     * @param integer $count The numer of images to list.
+     *
+     * @return mixed  An array of image_ids | PEAR_Error
+     */
+    function listImages($from = 0, $count = 0)
+    {
+        return $GLOBALS['ansel_storage']->listImages($this->_gallery->id, $from,
+                                                     $count);
+    }
+
+
+    function moveImagesTo($images, $gallery)
+    {
+        if (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+            return PEAR::raiseError(sprintf(_("Access denied moving photos to \"%s\"."), $newGallery->get('name')));
+        } elseif (!$this->_gallery->hasPermission(Horde_Auth::getAuth(), PERMS_DELETE)) {
+            return PEAR::raiseError(sprintf(_("Access denied removing photos from \"%s\"."), $gallery->get('name')));
+        }
+
+        /* Sanitize image ids, and see if we're removing our default image. */
+        $ids = array();
+        foreach ($images as $imageId) {
+            $ids[] = (int)$imageId;
+            if ($imageId == $this->_gallery->data['attribute_default']) {
+                $this->_gallery->set('default', null, true);
+            }
+        }
+
+        $result = $this->_gallery->_shareOb->_write_db->exec('UPDATE ansel_images SET gallery_id = ' . $gallery->id . ' WHERE image_id IN (' . implode(',', $ids) . ')');
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        }
+
+        $this->_gallery->_updateImageCount(count($ids), false);
+        $this->_gallery->_updateImageCount(count($ids), true, $gallery->id);
+
+        /* Expire the cache since we have no reason to save() the gallery */
+        if ($GLOBALS['conf']['ansel_cache']['usecache']) {
+            $GLOBALS['cache']->expire('Ansel_Gallery' . $gallery->id);
+            $GLOBALS['cache']->expire('Ansel_Gallery' . $this->_gallery->id);
+        }
+
+        return true;
+    }
+
+    function removeImage($image, $isStack)
+    {
+        /* Make sure $image is an Ansel_Image; if not, try loading it. */
+        if (!is_a($image, 'Ansel_Image')) {
+            $img = &$this->_gallery->getImage($image);
+            if (is_a($img, 'PEAR_Error')) {
+                return $img;
+            }
+            $image = $img;
+        } else {
+            /* Make sure the image is in this gallery. */
+            if ($image->gallery != $this->_gallery->id) {
+                return false;
+            }
+        }
+
+        /* Change gallery info. */
+        if ($this->_gallery->data['attribute_default'] == $image->id) {
+            $this->_gallery->data['attribute_default'] = null;
+            $this->_gallery->data['attribute_default_type'] = 'auto';
+        }
+
+        /* Delete cached files from VFS. */
+        $image->deleteCache();
+
+        /* Delete original image from VFS. */
+        $GLOBALS['ansel_vfs']->deleteFile($image->getVFSPath('full'),
+                                          $image->getVFSName('full'));
+
+        /* Delete from SQL. */
+        $this->_gallery->_shareOb->_write_db->exec('DELETE FROM ansel_images WHERE image_id = ' . (int)$image->id);
+
+        /* Remove any attributes */
+        $this->_gallery->_shareOb->_write_db->exec('DELETE FROM ansel_image_attributes WHERE image_id = ' . (int)$image->id);
+
+        if (!$isStack) {
+            $this->_gallery->_updateImageCount(1, false);
+        }
+
+        /* Remove any geolocation data */
+        $this->_gallery->_shareOb->_write_db->exec('DELETE FROM ansel_images_geolocation WHERE image_id = ' . (int)$image->id);
+
+        /* Update the modified flag if we are not a stack image */
+        if (!$isStack) {
+            $this->_gallery->data['attribute_last_modified'] = time();
+        }
+
+        /* Save all gallery changes */
+        $this->_gallery->save();
+
+        /* Clear the image's tags */
+        $image->setTags(array());
+
+        /* Clear the image's faces */
+        if ($image->facesCount) {
+            require_once ANSEL_BASE . '/lib/Faces.php';
+            Ansel_Faces::delete($image);
+        }
+
+        /* Clear any comments */
+        if (($GLOBALS['conf']['comments']['allow'] == 'all' || ($GLOBALS['conf']['comments']['allow'] == 'authenticated' && Horde_Auth::getAuth())) &&
+            $GLOBALS['registry']->hasMethod('forums/deleteForum')) {
+
+            $result = $GLOBALS['registry']->call('forums/deleteForum',
+                                                 array('ansel', $image->id));
+
+            if (is_a($result, 'PEAR_Error')) {
+                Horde::logMessage($result, __LINE__, __FILE__, PEAR_LOG_ERR);
+                return false;
+            }
+        }
+
+        return true;
+    }
+    /**
+     * Gets a slice of the images in this gallery.
+     *
+     * @param integer $from  The image to start fetching.
+     * @param integer $count The numer of images to return.
+     *
+     * @param mixed An array of Ansel_Image objects | PEAR_Error
+     */
+    function getImages($from = 0, $count = 0)
+    {
+        $this->_gallery->_shareOb->_db->setLimit($count, $from);
+
+        $images = $this->_gallery->_shareOb->_db->query('SELECT image_id, gallery_id, image_filename, image_type, image_caption, image_uploaded_date, image_sort, image_latitude, image_longitude, image_location, image_geotag_date FROM ansel_images WHERE gallery_id = ' . $this->_gallery->id . ' ORDER BY image_sort');
+        if (is_a($images, 'PEAR_Error')) {
+            return $images;
+        }
+
+        $objects = array();
+        while ($image = $images->fetchRow(MDB2_FETCHMODE_ASSOC)) {
+            $image['image_filename'] = Horde_String::convertCharset($image['image_filename'], $GLOBALS['conf']['sql']['charset']);
+            $image['image_caption'] = Horde_String::convertCharset($image['image_caption'], $GLOBALS['conf']['sql']['charset']);
+            $objects[$image['image_id']] = new Ansel_Image($image);
+            $GLOBALS['ansel_storage']->images[(int)$image['image_id']] = &$objects[$image['image_id']];
+        }
+        $images->free();
+
+        $ccounts = $GLOBALS['ansel_storage']->_getImageCommentCounts(array_keys($objects));
+        if (!is_a($ccounts, 'PEAR_Error') && count($ccounts)) {
+            foreach ($objects as $key => $image) {
+                $objects[$key]->commentCount = (!empty($ccounts[$key]) ? $ccounts[$key] : 0);
+            }
+        }
+        return array_values($objects);
+    }
+
+    /**
+     * Checks if the gallery has any subgallery
+     *
+     * @return boolean
+     */
+    function hasSubGalleries()
+    {
+        return $this->_gallery->get('has_subgalleries') == 1;
+    }
+
+    /**
+     * Returns the number of images in this gallery and, optionally, all
+     * sub-galleries.
+     *
+     * @param boolean $subgalleries  Determines whether subgalleries should
+     *                               be counted or not.
+     *
+     * @return integer number of images in this gallery
+     */
+    function countImages($subgalleries = false)
+    {
+        if ($subgalleries && $this->hasSubGalleries()) {
+            $count = $this->countImages(false);
+            $galleries = $GLOBALS['ansel_storage']->listGalleries(PERMS_SHOW,
+                                                                  false,
+                                                                  $this->_gallery,
+                                                                  true);
+
+            foreach ($galleries as $galleryId => $gallery) {
+                $count += $gallery->countImages();
+            }
+
+            return $count;
+        }
+
+        return $this->_gallery->data['attribute_images'];
+    }
+
+
+}
diff --git a/ansel/lib/GalleryMode/RemoteVimeo.php b/ansel/lib/GalleryMode/RemoteVimeo.php
new file mode 100644 (file)
index 0000000..c4b25e3
--- /dev/null
@@ -0,0 +1,259 @@
+<?php
+/**
+ * Ansel_Gallery_Mode_Normal:: Class for encapsulating gallery methods that
+ * depend on the current display mode of the gallery.
+ *
+ * $Horde: ansel/lib/GalleryMode/Normal.php,v 1.9 2009/01/06 17:48:53 jan Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ */
+class Ansel_GalleryMode_RemoteVimeo {
+
+    var $_vimeo;
+    var $_thumbs;
+
+    /**
+     * @var Ansel_Gallery
+     */
+    var $_gallery;
+    var $_features = array();
+
+    /**
+     * Constructor
+     *
+     * @param Ansel_Gallery $gallery  The gallery to bind to.
+     *
+     * @return Ansel_Gallery_ModeNormal
+     */
+    function Ansel_GalleryMode_RemoteVimeo($gallery)
+    {
+        require_once 'Horde/Autoloader.php';
+
+        // Build a Horde_Service_Vimeo object
+        // It *requires* a http client object and can make use of a cache object,
+        $params = array('http_client' => new Horde_Http_Client(),
+                        'cache' => $GLOBALS['cache'],
+                        'cache_lifetime' => $GLOBALS['conf']['cache']['default_lifetime']);
+
+        $this->_vimeo = Horde_Service_Vimeo::factory('Simple', $params);
+        $vimeo_id = 'user1015172'; //TODO: Get this from prefs?
+        $this->_thumbs = unserialize($this->_vimeo->user($vimeo_id)->clips()->run());
+        $this->_gallery = $gallery;
+    }
+
+    function init()
+    {
+        $remote_ids = array();
+
+        // Get the remote video_ids
+        foreach ($this->_thumbs as $thumb) {
+            $remote_ids[$thumb['clip_id']] = false;
+        }
+
+        // Get localimage objects...
+        $images = $this->getImages();
+        if (!is_a($images, 'PEAR_Error')) {
+            foreach ($images as $image) {
+                $caption = $image->caption;
+                if (in_array($caption, array_keys($remote_ids))) {
+                    // We still have this video on Vimeo.
+                    // AND we know that we will be checking for this locally
+                    // later, so save the info now.
+                    $remote_ids[$caption] = true;
+                } else {
+                    // Remote no longer exists - delete the local thumbnail
+                    $this->removeImage($image, false);
+                }
+            }
+
+            // Now check the other direction
+            foreach($this->_thumbs as $thumb) {
+                if (!$remote_ids[$thumb['clip_id']]) {
+                    // We didn't find a match in any of our local images earlier
+                    // create one now.
+                    $hc = new Horde_Http_Client();
+                    $response = $hc->get($thumb['thumbnail_large']);
+
+                    $image_id = $this->_gallery->addImage(array(
+                        'image_filename' => $thumb['title'],
+                        'image_caption' => $thumb['clip_id'],
+                        'data' => $response->getBody(),
+                        'image_type' => $response->getHeader('Content-Type')
+                        ));
+                }
+            }
+        }
+    }
+
+    function hasFeature($feature)
+    {
+        return in_array($feature, $this->_features);
+    }
+
+    /**
+     * Get the children of this gallery.
+     *
+     * Should never be called with a RemoteVimeo gallery since we override
+     * fetchChildren() in Ansel_GalleryRenderer...but implement something
+     * sensible just in case that ever changes.
+     *
+     * @param integer $perm  The permissions to limit to.
+     * @param integer $from  The child to start at.
+     * @param integer $to    The child to end with.
+     *
+     * @return A mixed array of Ansel_Gallery and Ansel_Image objects that are
+     *         children of this gallery.
+     */
+    function getGalleryChildren($perm = PERMS_SHOW, $from = 0, $to = 0)
+    {
+        if ($this->_gallery->data['attribute_images']) {
+            $images = $this->getImages($from, $to);
+            if (is_a($images, 'PEAR_Error')) {
+                Horde::logMessage($images->message, __FILE__, __LINE__,
+                                  PEAR_LOG_ERR);
+                $images = array();
+            }
+        } else {
+            $images = array();
+        }
+
+        return $images;
+    }
+
+    /**
+     * Get an array describing where this gallery is in a breadcrumb trail.
+     *
+     * @return  An array of 'title' and 'navdata' hashes with the [0] element
+     *          being the deepest part.
+     */
+    function getGalleryCrumbData()
+    {
+        $trail = array();
+        $text = htmlspecialchars($this->_gallery->get('name'));
+        $navdata = array('view' => 'Gallery',
+                         'gallery' => $this->_gallery->id,
+                         'slug' => $this->_gallery->get('slug'));
+        $trail[] = array('title' => $text, 'navdata' => $navdata);
+        $parent_list = array_reverse($this->_gallery->getParents());
+        foreach ($parent_list as $p) {
+            $text = htmlspecialchars($p->get('name'));
+            $navdata = array('view' => 'Gallery',
+                             'gallery' => $p->id,
+                             'slug' => $p->get('slug'));
+            $trail[] = array('title' => $text, 'navdata' => $navdata);
+        }
+
+        return $trail;
+    }
+
+    function setDate($date = array())
+    {
+        //noop
+    }
+
+    function getDate()
+    {
+        return array();
+    }
+
+    /**
+     * Return the count this gallery's children
+     *
+     * @param integer $perm            The permissions to require.
+     * @param boolean $galleries_only  Only include galleries, no images.
+     *
+     * @return integer The count of this gallery's children.
+     */
+    function countGalleryChildren($perm = PERMS_SHOW, $galleries_only = false)
+    {
+        return count($this->_thumbs);
+    }
+
+    /**
+     * Lists a slice of the image ids in this gallery.
+     *
+     * @param integer $from  The image to start listing.
+     * @param integer $count The numer of images to list.
+     *
+     * @return mixed  An array of image_ids | PEAR_Error
+     */
+    function listImages($from = 0, $count = 0)
+    {
+        return $GLOBALS['ansel_storage']->listImages($this->_gallery->id, $from,
+                                                     $count);
+    }
+
+    function moveImagesTo($images, $gallery)
+    {
+        return false;
+    }
+
+    function removeImage($image, $isStack)
+    {
+        return false;
+    }
+
+    /**
+     * Gets a slice of the images in this gallery.
+     *
+     * @param integer $from  The image to start fetching.
+     * @param integer $count The numer of images to return.
+     *
+     * @param mixed An array of Ansel_Image objects | PEAR_Error
+     */
+    function getImages($from = 0, $count = 0)
+    {
+        $this->_gallery->_shareOb->_db->setLimit($count, $from);
+        $images = $this->_gallery->_shareOb->_db->query('SELECT image_id, gallery_id, image_filename, image_type, image_caption, image_uploaded_date, image_sort FROM ansel_images WHERE gallery_id = ' . $this->_gallery->id . ' ORDER BY image_sort');
+        if (is_a($images, 'PEAR_Error')) {
+            return $images;
+        }
+
+        $objects = array();
+        while ($image = $images->fetchRow(MDB2_FETCHMODE_ASSOC)) {
+            $image['image_filename'] = Horde_String::convertCharset($image['image_filename'], $GLOBALS['conf']['sql']['charset']);
+            $image['image_caption'] = Horde_String::convertCharset($image['image_caption'], $GLOBALS['conf']['sql']['charset']);
+            $objects[$image['image_id']] = new Ansel_Image($image);
+            $GLOBALS['ansel_storage']->images[(int)$image['image_id']] = &$objects[$image['image_id']];
+        }
+        $images->free();
+
+        $ccounts = $GLOBALS['ansel_storage']->_getImageCommentCounts(array_keys($objects));
+        if (!is_a($ccounts, 'PEAR_Error') && count($ccounts)) {
+            foreach ($objects as $key => $image) {
+                $objects[$key]->commentCount = (!empty($ccounts[$key]) ? $ccounts[$key] : 0);
+            }
+        }
+        return array_values($objects);
+    }
+
+    /**
+     * Checks if the gallery has any subgallery
+     *
+     * @return boolean
+     */
+    function hasSubGalleries()
+    {
+        return false;
+    }
+
+    /**
+     * Returns the number of images in this gallery and, optionally, all
+     * sub-galleries.
+     *
+     * @param boolean $subgalleries  Determines whether subgalleries should
+     *                               be counted or not.
+     *
+     * @return integer number of images in this gallery
+     */
+    function countImages($subgalleries = false)
+    {
+        return count($this->_thumbs);
+    }
+
+}
\ No newline at end of file
diff --git a/ansel/lib/ImageView.php b/ansel/lib/ImageView.php
new file mode 100644 (file)
index 0000000..7e9f67b
--- /dev/null
@@ -0,0 +1,170 @@
+<?php
+/**
+ * Class to abstract the creation of various image views.
+ *
+ * $Horde: ansel/lib/ImageView.php,v 1.8 2009/01/06 17:48:51 jan Exp $
+ *
+ * Copyright 2007-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * Copyright 2007-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ *
+ * @package Ansel
+ */
+
+class Ansel_ImageView {
+
+    /**
+     * Ansel_Image object that this view is created from.
+     *
+     * @var Ansel_Image
+     */
+    var $_image = null;
+
+    /**
+     * Parameters for this view
+     *
+     * @var array
+     */
+    var $_params = array();
+
+    /**
+     * Image dimensions
+     *
+     * @var array
+     */
+    var $_dimensions = array();
+
+    var $_style = array();
+
+    var $need = array();
+
+    /**
+     * Constructor
+     */
+    function Ansel_ImageView($params)
+    {
+        $this->_params = $params;
+        if (!empty($params['image'])) {
+            $this->_image = $params['image'];
+        }
+        $this->_style = $params['style'];
+    }
+
+    /**
+     * Function to actually create and cache the view.
+     */
+    function create()
+    {
+        if (!empty($this->_image)) {
+            $this->_dimensions = $this->_image->_image->getDimensions();
+            if (is_a($this->_dimensions, 'PEAR_Error')) {
+                Horde::logMessage($this->_dimensions, __FILE__, __LINE__, PEAR_LOG_ERR);
+                return $this->_dimensions;
+            }
+        }
+        return $this->_create();
+    }
+
+    function factory($type, $params = array())
+    {
+        $type = basename($type);
+        $class = 'Ansel_ImageView_' . $type;
+        if (!class_exists($class)) {
+            include dirname(__FILE__) . '/ImageView/' . $type . '.php';
+        }
+        if (class_exists($class)) {
+            $view = new $class($params);
+            // Check that the image object supports what we need for the
+            // requested effect.
+            foreach ($view->need as $need) {
+                if (!Ansel::isAvailable($need)) {
+                    $err = PEAR::raiseError(_("This install does not support the %s feature. Please contact your administrator."), $need);
+                    Horde::logMessage($err, __FILE__, __LINE__, PEAR_LOG_ERR);
+                    return $err;
+                }
+            }
+            return $view;
+        } else {
+            $err = PEAR::raiseError(sprintf(_("Unable to load the definition of %s."), $class));
+            Horde::logMessage($err, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return $err;
+        }
+
+    }
+
+    /**
+     * Utility function to make every effort to find a subgallery that
+     * contains images.
+     *
+     * @param Ansel_Gallery $parent  The gallery to start looking in
+     *
+     * @return An Ansel_Gallery object that has images, or the original $parent
+     */
+    function _getGalleryWithImages($parent)
+    {
+       $galleries = $GLOBALS['ansel_storage']->listGalleries(
+                                                    PERMS_SHOW,
+                                                    null,
+                                                    $parent,
+                                                    false);
+
+        foreach ($galleries as $gallery) {
+            if ($gallery->countImages()) {
+                return $gallery;
+            }
+            $result = $this->_getGalleryWithImages($gallery);
+            if ($result->countImages()) {
+                return $result;
+            }
+        }
+        return $parent;
+    }
+
+   /**
+     * Utility function to return an array of Ansel_Images to use
+     * in building a polaroid stack. Returns a random set of 5 images from
+     * the gallery, or the explicitly set default image plus 4 others.
+     */
+    function _getStackImages()
+    {
+        $images = array();
+        $gallery = $this->_params['gallery'];
+
+        // Make sure we have images.
+        if (!$gallery->countImages() && $gallery->hasSubGalleries()) {
+            $gallery = $this->_getGalleryWithImages($gallery);
+        }
+
+        $cnt = min(5, $gallery->countImages());
+        $default = $gallery->get('default');
+        if (!empty($default) && $default > 0) {
+            $img = &$gallery->getImage($default);
+            if (!is_a($img, 'PEAR_Error')) {
+                $images[] = &$gallery->getImage($default);
+                $cnt--;
+            }
+        }
+
+        for ($i = 0; $i < $cnt; $i++) {
+            $rnd = mt_rand(0, $cnt);
+            $temp = $gallery->getImages($rnd, 1);
+            if (!is_a($temp, 'PEAR_Error') && count($temp)) {
+                $images[] = array_shift($temp);
+            }
+        }
+
+        // Reverse the array to ensure the requested default image
+        // is the last in the array (so it will appear on the top of
+        // the stack.
+        return array_reverse($images);
+    }
+
+}
\ No newline at end of file
diff --git a/ansel/lib/ImageView/mini.php b/ansel/lib/ImageView/mini.php
new file mode 100755 (executable)
index 0000000..bc9b166
--- /dev/null
@@ -0,0 +1,19 @@
+<?php
+/**
+ * ImageView to create the mini view.
+ *
+ * $Horde: ansel/lib/ImageView/mini.php,v 1.2 2007/11/14 16:11:27 chuck Exp $
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_ImageView_mini extends Ansel_ImageView {
+
+    function _create()
+    {
+        return $this->_image->_image->resize(min(50, $this->_dimensions['width']),
+                                             min(50, $this->_dimensions['height']),
+                                             true);
+    }
+
+}
diff --git a/ansel/lib/ImageView/plainstack.php b/ansel/lib/ImageView/plainstack.php
new file mode 100644 (file)
index 0000000..d2c8edc
--- /dev/null
@@ -0,0 +1,52 @@
+<?php
+/**
+ * ImageView to create the gallery image stacks.
+ *
+ * $Horde: ansel/lib/ImageView/plainstack.php,v 1.10 2009/05/27 18:07:28 mrubinsk Exp $
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_ImageView_plainstack extends Ansel_ImageView {
+
+    var $need = array('PhotoStack');
+
+    function _create()
+    {
+        $imgobjs = array();
+        $images = $this->_getStackImages();
+        $style = $this->_params['style'];
+        foreach ($images as $image) {
+            $result = $image->load('screen');
+            if (is_a($result, 'PEAR_Error')) {
+                return $result;
+            }
+            $imgobjs[] = $image->_image;
+        }
+
+        $params = array('width' => 100,
+                        'height' => 100,
+                        'background' => $style['background']);
+
+        $baseImg = Ansel::getImageObject($params);
+        $result = $baseImg->addEffect(
+            'PhotoStack',
+            array('images' => $imgobjs,
+                  'resize_height' => $GLOBALS['conf']['thumbnail']['height'],
+                  'padding' => 0,
+                  'background' => $style['background'],
+                  'type' => 'plain'));
+
+        $baseImg->applyEffects();
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        }
+
+        $baseImg->resize($GLOBALS['conf']['thumbnail']['width'],
+                         $GLOBALS['conf']['thumbnail']['height']);
+
+        return $baseImg;
+
+    }
+
+}
diff --git a/ansel/lib/ImageView/polaroidstack.php b/ansel/lib/ImageView/polaroidstack.php
new file mode 100644 (file)
index 0000000..03db508
--- /dev/null
@@ -0,0 +1,52 @@
+<?php
+/**
+ * ImageView to create the gallery polaroid stacks.
+ *
+ * $Horde: ansel/lib/ImageView/polaroidstack.php,v 1.11 2009/05/27 18:07:28 mrubinsk Exp $
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_ImageView_polaroidstack extends Ansel_ImageView {
+
+    var $need = array('PhotoStack');
+
+    function _create()
+    {
+        $imgobjs = array();
+        $images = $this->_getStackImages();
+        $style = $this->_params['style'];
+        foreach ($images as $image) {
+            $result = $image->load('screen');
+            if (is_a($result, 'PEAR_Error')) {
+                return $result;
+            }
+            $imgobjs[] = $image->_image;
+        }
+
+        $params = array('width' => 100,
+                        'height' => 100,
+                        'background' => $style['background']);
+
+        $baseImg = Ansel::getImageObject($params);
+        $result = $baseImg->addEffect(
+            'PhotoStack',
+            array('images' => $imgobjs,
+                  'resize_height' => $GLOBALS['conf']['thumbnail']['height'],
+                  'padding' => 10,
+                  'background' => $style['background'],
+                  'type' => 'polaroid'));
+
+        $baseImg->applyEffects();
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        }
+
+        $baseImg->resize($GLOBALS['conf']['thumbnail']['width'],
+                         $GLOBALS['conf']['thumbnail']['height']);
+
+        return $baseImg;
+
+    }
+
+}
diff --git a/ansel/lib/ImageView/polaroidthumb.php b/ansel/lib/ImageView/polaroidthumb.php
new file mode 100644 (file)
index 0000000..1de49ff
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+/**
+ * ImageView to create the shadowsharpthumb view (sharp corners, shadowed)
+ *
+ * $Horde: ansel/lib/ImageView/polaroidthumb.php,v 1.4 2009/05/27 18:07:28 mrubinsk Exp $
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+
+class Ansel_ImageView_polaroidthumb extends Ansel_ImageView {
+
+    var $need = array('PolaroidImage');
+
+    function _create()
+    {
+        $this->_image->_image->resize(min($GLOBALS['conf']['thumbnail']['width'], $this->_dimensions['width']),
+                                      min($GLOBALS['conf']['thumbnail']['height'], $this->_dimensions['height']),
+                                      true);
+
+        /* Don't bother with these effects for a custom gallery default image
+           (which will have a negative gallery_id). */
+        if ($this->_image->gallery > 0) {
+            if (is_null($this->_style)) {
+                $gal = $GLOBALS['ansel_storage']->getGallery($this->_image->gallery);
+                $styleDef = $gal->getStyle();
+            } else {
+                $styleDef = Ansel::getStyleDefinition($this->_style);
+            }
+            $res = $this->_image->_image->addEffect('PolaroidImage',
+                                                    array('background' => $styleDef['background'],
+                                                          'padding' => 5));
+            if (is_a($res, 'PEAR_Error')) {
+                Horde::logMessage($res, __FILE__, __LINE__, PEAR_LOG_ERR);
+            }
+
+            return $this->_image->_image->applyEffects();
+        }
+    }
+
+}
diff --git a/ansel/lib/ImageView/prettythumb.php b/ansel/lib/ImageView/prettythumb.php
new file mode 100644 (file)
index 0000000..79a2f7c
--- /dev/null
@@ -0,0 +1,51 @@
+<?php
+/**
+ * ImageView to create the prettythumb view (rounded, shadowed thumbnails).
+ *
+ * $Horde: ansel/lib/ImageView/prettythumb.php,v 1.7 2009/05/27 18:07:28 mrubinsk Exp $
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_ImageView_prettythumb extends Ansel_ImageView {
+
+    var $need = array('RoundCorners', 'DropShadow');
+
+    function _create()
+    {
+        $this->_image->_image->resize(min($GLOBALS['conf']['thumbnail']['width'], $this->_dimensions['width']),
+                                      min($GLOBALS['conf']['thumbnail']['height'], $this->_dimensions['height']),
+                                      true);
+
+        /* Don't bother with these effects for a stack image
+         * (which will have a negative gallery_id). */
+        if ($this->_image->gallery > 0) {
+            if (is_null($this->_style)) {
+                $gal = $GLOBALS['ansel_storage']->getGallery($this->_image->gallery);
+                $styleDef = $gal->getStyle();
+            } else {
+                $styleDef = Ansel::getStyleDefinition($this->_style);
+            }
+
+            /* Apply the effects - continue on error, but be sure to log */
+            $res = $this->_image->_image->addEffect('RoundCorners',
+                                                    array('border' => 2,
+                                                          'bordercolor' => '#333'));
+            if (is_a($res, 'PEAR_Error')) {
+                Horde::logMessage($res, __FILE__, __LINE__, PEAR_LOG_ERR);
+            }
+
+            $res = $this->_image->_image->addEffect('DropShadow',
+                                                    array('background' => $styleDef['background'],
+                                                          'padding' => 5,
+                                                          'distance' => 5,
+                                                          'fade' => 3));
+            if (is_a($res, 'PEAR_Error')) {
+                Horde::logMessage($res, __FILE__, __LINE__, PEAR_LOG_ERR);
+            }
+
+            return $this->_image->_image->applyEffects();
+        }
+    }
+
+}
diff --git a/ansel/lib/ImageView/roundedstack.php b/ansel/lib/ImageView/roundedstack.php
new file mode 100644 (file)
index 0000000..361fefe
--- /dev/null
@@ -0,0 +1,53 @@
+ <?php
+/**
+ * ImageView to create the gallery image stacks.
+ *
+ * $Horde: ansel/lib/ImageView/roundedstack.php,v 1.11 2009/05/27 18:07:28 mrubinsk Exp $
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ *
+ */
+class Ansel_ImageView_roundedstack extends Ansel_ImageView {
+
+    var $need = array('PhotoStack');
+
+    function _create()
+    {
+        $imgobjs = array();
+        $images = $this->_getStackImages();
+        $style = $this->_params['style'];
+        foreach ($images as $image) {
+            $result = $image->load('screen');
+            if (is_a($result, 'PEAR_Error')) {
+                return $result;
+            }
+            $imgobjs[] = $image->_image;
+        }
+
+        $params = array('width' => 100,
+                        'height' => 100,
+                        'background' => $style['background']);
+
+        $baseImg = Ansel::getImageObject($params);
+        $result = $baseImg->addEffect(
+            'PhotoStack',
+            array('images' => $imgobjs,
+                  'resize_height' => $GLOBALS['conf']['thumbnail']['height'],
+                  'padding' => 0,
+                  'background' => $style['background'],
+                  'type' => 'rounded'));
+
+        $baseImg->applyEffects();
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        }
+
+        $baseImg->resize($GLOBALS['conf']['thumbnail']['width'],
+                         $GLOBALS['conf']['thumbnail']['height']);
+
+        return $baseImg;
+
+    }
+
+}
diff --git a/ansel/lib/ImageView/screen.php b/ansel/lib/ImageView/screen.php
new file mode 100755 (executable)
index 0000000..0612f4c
--- /dev/null
@@ -0,0 +1,19 @@
+<?php
+/**
+ * ImageView to create the screen view - image sized for slideshow view.
+ *
+ * $Horde: ansel/lib/ImageView/screen.php,v 1.2 2007/11/13 06:24:11 mrubinsk Exp $
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_ImageView_screen extends Ansel_ImageView {
+
+    function _create()
+    {
+        return $this->_image->_image->resize(min($GLOBALS['conf']['screen']['width'], $this->_dimensions['width']),
+                                             min($GLOBALS['conf']['screen']['height'], $this->_dimensions['height']),
+                                             true);
+    }
+
+}
diff --git a/ansel/lib/ImageView/shadowsharpthumb.php b/ansel/lib/ImageView/shadowsharpthumb.php
new file mode 100644 (file)
index 0000000..6e759c3
--- /dev/null
@@ -0,0 +1,47 @@
+<?php
+/**
+ * ImageView to create the shadowsharpthumb view (sharp corners, shadowed)
+ *
+ * $Horde: ansel/lib/ImageView/shadowsharpthumb.php,v 1.7 2009/05/27 18:07:28 mrubinsk Exp $
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_ImageView_shadowsharpthumb extends Ansel_ImageView {
+
+    var $need = array('DropShadow');
+
+    function _create()
+    {
+        $this->_image->_image->resize(min($GLOBALS['conf']['thumbnail']['width'], $this->_dimensions['width']),
+                                      min($GLOBALS['conf']['thumbnail']['height'], $this->_dimensions['height']),
+                                      true);
+
+        /* Don't bother with these effects for a stack image
+         * (which will have a negative gallery_id). */
+        if ($this->_image->gallery > 0) {
+            if (is_null($this->_style)) {
+                $gal = $GLOBALS['ansel_storage']->getGallery($this->_image->gallery);
+                $styleDef = $gal->getStyle();
+            } else {
+                $styleDef = Ansel::getStyleDefinition($this->_style);
+            }
+
+            $res = $this->_image->_image->addEffect(
+                'border', array('bordercolor' => '#333'));
+
+            $res = $this->_image->_image->addEffect('DropShadow',
+                                                    array('background' => $styleDef['background'],
+                                                          'padding' => 5,
+                                                          'distance' => '8',
+                                                          'fade' => 2));
+
+            if (is_a($res, 'PEAR_Error')) {
+                Horde::logMessage($res, __FILE__, __LINE__, PEAR_LOG_ERR);
+            }
+
+            return $this->_image->_image->applyEffects();
+        }
+    }
+
+}
diff --git a/ansel/lib/ImageView/thumb.php b/ansel/lib/ImageView/thumb.php
new file mode 100755 (executable)
index 0000000..098c539
--- /dev/null
@@ -0,0 +1,19 @@
+<?php
+/**
+ * ImageView to create the thumb view (plain, resized thumbnails).
+ *
+ * $Horde: ansel/lib/ImageView/thumb.php,v 1.2 2007/11/14 16:11:27 chuck Exp $
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_ImageView_thumb extends Ansel_ImageView {
+
+    function _create()
+    {
+        return $this->_image->_image->resize(min($GLOBALS['conf']['thumbnail']['width'], $this->_dimensions['width']),
+                                             min($GLOBALS['conf']['thumbnail']['height'], $this->_dimensions['height']),
+                                             true);
+    }
+
+}
diff --git a/ansel/lib/ImageView/vimeothumb.php b/ansel/lib/ImageView/vimeothumb.php
new file mode 100644 (file)
index 0000000..5c838f7
--- /dev/null
@@ -0,0 +1,47 @@
+<?php
+/**
+ * ImageView to create the shadowsharpthumb view (sharp corners, shadowed)
+ *
+ * $Horde: ansel/lib/ImageView/shadowsharpthumb.php,v 1.6 2008/01/16 01:30:58 mrubinsk Exp $
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_ImageView_vimeothumb extends Ansel_ImageView {
+
+    var $need = array('drop_shadow');
+
+    function _create()
+    {
+        $this->_image->_image->resize(min($GLOBALS['conf']['thumbnail']['width'], $this->_dimensions['width']),
+                                      min($GLOBALS['conf']['thumbnail']['height'], $this->_dimensions['height']),
+                                      true);
+
+        /* Don't bother with these effects for a stack image
+         * (which will have a negative gallery_id). */
+        if ($this->_image->gallery > 0) {
+            if (is_null($this->_style)) {
+                $gal = $GLOBALS['ansel_storage']->getGallery($this->_image->gallery);
+                $styleDef = $gal->getStyle();
+            } else {
+                $styleDef = Ansel::getStyleDefinition($this->_style);
+            }
+
+            $res = $this->_image->_image->addEffect(
+                'border', array('bordercolor' => '#333'));
+
+//            $res = $this->_image->_image->addEffect('drop_shadow',
+//                                                    array('background' => $styleDef['background'],
+//                                                          'padding' => 5,
+//                                                          'distance' => '8',
+//                                                          'fade' => 2));
+
+            if (is_a($res, 'PEAR_Error')) {
+                Horde::logMessage($res, __FILE__, __LINE__, PEAR_LOG_ERR);
+            }
+
+            return $this->_image->_image->applyEffects();
+        }
+    }
+
+}
diff --git a/ansel/lib/Report.php b/ansel/lib/Report.php
new file mode 100644 (file)
index 0000000..dc34f7f
--- /dev/null
@@ -0,0 +1,124 @@
+<?php
+/**
+ * Reporting abstraction class
+ *
+ * $Horde: ansel/lib/Report.php,v 1.12 2009/07/14 00:25:28 mrubinsk Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author  Duck <duck@obala.net>
+ * @package Ansel
+ */
+class Ansel_Report {
+
+    var $_title = '';
+
+    /**
+     * Create instance
+     */
+    function factory($driver = null, $params = array())
+    {
+        if ($driver === null) {
+            $driver = $GLOBALS['conf']['report_content']['driver'];
+        }
+
+        if (empty($params)) {
+            $params = $GLOBALS['conf']['report_content'];
+        }
+
+        require_once ANSEL_BASE . '/lib/Report/' . $driver  . '.php';
+        $class_name = 'Ansel_Report_' . $driver;
+        if (!class_exists($class_name)) {
+            return PEAR::RaiseError(_("Report driver does not exist."));
+        }
+
+        $report = new $class_name($params);
+
+        return $report;
+    }
+
+    /**
+     * Get reporting user email
+     */
+    function getUserEmail()
+    {
+        return $this->_getUserEmail();
+    }
+
+    /**
+     * Get user email
+     */
+    function _getUserEmail($user = null)
+    {
+        require_once 'Horde/Identity.php';
+
+        // Get user email
+        $identity = &Identity::singleton('none', $user);
+        return $identity->getValue('from_addr');
+    }
+
+    /**
+     * Get scope admins
+     */
+    function getAdmins()
+    {
+        $name = $GLOBALS['registry']->getApp() . ':admin';
+
+        if ($GLOBALS['perms']->exists($name)) {
+            return array();
+        }
+
+        $permission = $GLOBALS['perms']->getPermission($name);
+
+        return $permission->getUserPermissions(PERM_DELETE);
+    }
+
+    /**
+     * Set title
+     */
+    function setTitle($title)
+    {
+        $this->_title = $title;
+    }
+
+    /**
+     * Get report message title
+     */
+    function getTitle()
+    {
+        if (empty($this->_title)) {
+            return sprintf(_("Content abuse report in %s"), $GLOBALS['registry']->get('name'));
+        } else {
+            return $this->_title;
+        }
+    }
+
+    /**
+     * Get report message content
+     */
+    function getMessage($message)
+    {
+        $message .=  "\n\n" . _("Report by user") . ': ' . Horde_Auth::getAuth()
+                 . ' (' . $_SERVER['REMOTE_ADDR'] . ')';
+
+        return $message;
+    }
+
+    /**
+     * Report
+     *
+     * @param string $message to pass
+     */
+    function report($message, $users = array())
+    {
+        return PEAR::raiseError(_("Unsupported"));
+    }
+}
diff --git a/ansel/lib/Report/letter.php b/ansel/lib/Report/letter.php
new file mode 100755 (executable)
index 0000000..6a4f277
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+/**
+ * Report by letter api class
+ *
+ * $Horde: ansel/lib/Report/letter.php,v 1.5 2008/07/03 04:13:35 mrubinsk Exp $
+ *
+ * @author  Duck <duck@obala.net>
+ * @package Ansel
+ */
+class Ansel_Report_letter extends Ansel_Report {
+
+    /**
+     * Report
+     */
+    function report($message, $users = array())
+    {
+        if (!empty($users)) {
+            // We are sending a report to to the gallery owner
+            $admins = array($users);
+        } elseif (empty($GLOBALS['conf']['report_content']['users'])) {
+            $admins = $this->getAdmins();
+            if (empty($admins)) {
+                return true;
+            }
+        } else {
+            $admins = $GLOBALS['conf']['report_content']['users'];
+        }
+
+        $title = $this->getTitle();
+        $message = $this->getMessage($message);
+
+        return $GLOBALS['registry']->callByPackage(
+            'letter', 'sendMessage', array($admins,
+                                           array('title' => $title,
+                                                 'content' => $message)));
+    }
+}
diff --git a/ansel/lib/Report/mail.php b/ansel/lib/Report/mail.php
new file mode 100644 (file)
index 0000000..e24d94c
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+/**
+ * Report by email class
+ *
+ * $Horde: ansel/lib/Report/mail.php,v 1.10 2009/02/04 23:51:08 jan Exp $
+ *
+ * @author  Duck <duck@obala.net>
+ * @package Ansel
+ */
+class Ansel_Report_mail extends Ansel_Report {
+
+    /**
+     * Report
+     */
+    function report($message, $users = array())
+    {
+        global $conf;
+
+        if (empty($users)) {
+            $to = $conf['report_content']['email'];
+        } else {
+            // we are sending a report to to the gallery owner, but fall back
+            // to the admin in case the user has no email.
+            $to = $this->_getUserEmail($users);
+            if (empty($to)) {
+                $to = $conf['report_content']['email'];
+            }
+        }
+
+        /*
+         * Needed for the Horde 4 mime library - use autoload everywhere we can
+         * when this is *really* refactored for horde 4
+         */
+        $mail = new Horde_Mime_Mail($this->getTitle(),
+                                    $this->getMessage($message), $to,
+                                    $this->getUserEmail());
+
+        //FIXME: This address should be configurable
+        $mail->addHeader('Sender',
+                         'horde-problem@' . $conf['report_content']['maildomain']);
+        list($mail_driver, $mail_params) = Horde::getMailerConfig();
+        return $mail->send($mail_driver, $mail_params);
+    }
+}
diff --git a/ansel/lib/Report/tickets.php b/ansel/lib/Report/tickets.php
new file mode 100755 (executable)
index 0000000..70c93c6
--- /dev/null
@@ -0,0 +1,25 @@
+<?php
+/**
+ * Report using tickets
+ *
+ * $Horde: ansel/lib/Report/tickets.php,v 1.3 2008/05/07 01:45:56 chuck Exp $
+ *
+ * @author  Duck <duck@obala.net>
+ * @package Ansel
+ */
+class Ansel_Report_tickets extends Ansel_Report {
+
+    /**
+     * Report
+     */
+    function report($message)
+    {
+        $info = array_merge($GLOBALS['conf']['report_content']['ticket_params'],
+                            array('summary' => $this->getTitle(),
+                                    'comment' => $message,
+                                    'user_email' => $this->getUserEmail()));
+
+        return $registry->call('tickets/addTicket', array($info));
+    }
+
+}
diff --git a/ansel/lib/Search.php b/ansel/lib/Search.php
new file mode 100644 (file)
index 0000000..2423f2b
--- /dev/null
@@ -0,0 +1,105 @@
+<?php
+/**
+ * Ansel_Search:: Provides a generic interface for various types of image
+ * searches that are to be displayed in a paged results view.
+ *
+ * $Horde: ansel/lib/Search.php,v 1.2 2009/01/06 17:48:51 jan Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_Search {
+
+    /**
+     * The type of search we are performing.
+     *
+     * @var string
+     */
+    var $_type = '';
+
+    /**
+     * The field we are searching
+     *
+     * @var string
+     */
+    var $_field = '';
+
+    /**
+     * Parameters
+     *
+     * @var array
+     */
+    var $_params = array();
+
+    /**
+     * Create a concrete search instance.
+     *
+     * @param string $type   The type of search to perform.
+     * @param array $params  Parameters for the concrete class.
+     */
+    function factory($type, $params = array())
+    {
+        $type = basename($type);
+        $class = 'Ansel_Search_' . $type;
+        if (!class_exists($class)) {
+            include dirname(__FILE__) . '/Search/' . $type . '.php';
+        }
+        if (class_exists($class)) {
+            $search = new $class($params);
+            return $search;
+        }
+
+        return PEAR::raiseError(sprintf(_("Unable to load the definition of %s."), $class));
+    }
+
+    /**
+     * Save the current search terms to the session
+     *
+     */
+    function save()
+    {
+        $_SESSION['ansel_search'][$this->_type] = $this->_filter;
+    }
+
+    /**
+     * Load any search terms in the session
+     *
+     */
+    function load()
+    {
+        $this->_filter = (!empty($_SESSION['ansel_search'][$this->_type]) ?
+            $_SESSION['ansel_search'][$this->_type] :
+            array());
+    }
+    /**
+     * retrieve a slice of the current search
+     *
+     * @param unknown_type $page
+     * @param unknown_type $perpage
+     */
+    function getSlice($page, $perpage)
+    {
+    }
+
+    /**
+     * Add a search term
+     *
+     * @param array $filter value to filter.
+     */
+    function addFilter($filter)
+    {
+    }
+
+    /**
+     * Get the total number of resources that match
+     */
+    function count()
+    {
+    }
+
+}
\ No newline at end of file
diff --git a/ansel/lib/Search/exif.php b/ansel/lib/Search/exif.php
new file mode 100644 (file)
index 0000000..af9bda7
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+/**
+ * Ansel_Search_exif Provides an interface for searching image exif data.
+ *
+ * $Horde: ansel/lib/Search/exif.php,v 1.2 2009/01/06 17:48:53 jan Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_Search_exif {
+
+    /**
+     * Constructor
+     *
+     * @param array $params
+     * @return Ansel_Search_exif
+     */
+    function Ansel_Search_exif($params = array())
+    {
+        $this->_type = 'exif';
+    }
+
+
+    /**
+     * retrieve a slice of the current search
+     *
+     * @param unknown_type $page
+     * @param unknown_type $perpage
+     */
+    function getSlice($page, $perpage)
+    {
+    }
+
+    /**
+     * Get the total number of resources that match
+     */
+    function count()
+    {
+    }
+
+}
\ No newline at end of file
diff --git a/ansel/lib/Tags.php b/ansel/lib/Tags.php
new file mode 100644 (file)
index 0000000..be5ad1f
--- /dev/null
@@ -0,0 +1,669 @@
+<?php
+/**
+ * Classes for dealing with tags within Ansel
+ *
+ * $Horde: ansel/lib/Tags.php,v 1.96 2009/07/09 08:17:49 slusarz Exp $
+ *
+ * Copyright 2007-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * Copyright 2007-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author  Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+
+/**
+ * Static helper class for writing/reading tag values
+ *
+ * @static
+ */
+class Ansel_Tags {
+
+    /**
+     * Write out the tags for a specific resource.
+     *
+     * @param int    $resource_id    The resource we are tagging.
+     * @param array  $tags           An array of tags.
+     * @param string $resource_type  The type of resource (image or gallery)
+     *
+     * @return mixed True | PEAR_Error
+     */
+    function writeTags($resource_id, $tags, $resource_type = 'image')
+    {
+        // First, make sure all tag names exist in the DB.
+        $tagkeys = array();
+        $insert = $GLOBALS['ansel_db']->prepare('INSERT INTO ansel_tags (tag_id, tag_name) VALUES(?, ?)');
+        foreach ($tags as $tag) {
+            if (!empty($tag)) {
+                $tag = Horde_String::lower(trim($tag));
+                $sql = $GLOBALS['ansel_db']->prepare('SELECT tag_id FROM ansel_tags WHERE tag_name = ?');
+                $result = $sql->execute(Horde_String::convertCharset($tag, Horde_Nls::getCharset(), $GLOBALS['conf']['sql']['charset']));
+                $results = $result->fetchRow(MDB2_FETCHMODE_ASSOC);
+                $result->free();
+
+                if (empty($results)) {
+                    $id = $GLOBALS['ansel_db']->nextId('ansel_tags');
+                    $result = $insert->execute(array($id, Horde_String::convertCharset($tag, Horde_Nls::getCharset(), $GLOBALS['conf']['sql']['charset'])));
+                    $tagkeys[] = $id;
+                } elseif (is_a($results, 'PEAR_Error')) {
+                    return $results;
+                } else {
+                    $tagkeys[] = $results['tag_id'];
+                }
+            }
+        }
+        $insert->free();
+
+        if ($resource_type == 'image') {
+            $delete = $GLOBALS['ansel_db']->prepare('DELETE FROM ansel_images_tags WHERE image_id = ?');
+            $query = $GLOBALS['ansel_db']->prepare('INSERT INTO ansel_images_tags (image_id, tag_id) VALUES(?, ?)');
+        } else {
+            $delete =  $GLOBALS['ansel_db']->prepare('DELETE FROM ansel_galleries_tags WHERE gallery_id = ?');
+            $query = $GLOBALS['ansel_db']->prepare('INSERT INTO ansel_galleries_tags (gallery_id, tag_id) VALUES(?, ?)');
+        }
+        Horde::logMessage('SQL query by Ansel_Tags::writeTags: ' . $query->query,
+                          __FILE__, __LINE__, PEAR_LOG_DEBUG);
+        $delete->execute(array($resource_id));
+        foreach ($tagkeys as $key) {
+            $query->execute(array($resource_id, $key));
+        }
+
+        $delete->free();
+        $query->free();
+
+        /* We should clear at least any of our cached counts */
+        Ansel_Tags::clearCache();
+        return true;
+    }
+
+    /**
+     * Retrieve the tags for a specified resource.
+     *
+     * @param int     $resource_id    The resource to get tags for.
+     * @param string  $resource_type  The type of resource (gallery or image)
+     *
+     * @return mixed  An array of tags | PEAR_Error
+     */
+    function readTags($resource_id, $resource_type = 'image')
+    {
+        if ($resource_type == 'image') {
+            $stmt = $GLOBALS['ansel_db']->prepare('SELECT  ansel_tags.tag_id, tag_name FROM ansel_tags INNER JOIN ansel_images_tags ON ansel_images_tags.tag_id = ansel_tags.tag_id WHERE ansel_images_tags.image_id = ?');
+        } else {
+            $stmt = $GLOBALS['ansel_db']->prepare('SELECT  ansel_tags.tag_id, tag_name FROM ansel_tags INNER JOIN ansel_galleries_tags ON ansel_galleries_tags.tag_id = ansel_tags.tag_id WHERE ansel_galleries_tags.gallery_id = ?');
+        }
+        if (is_a($stmt, 'PEAR_Error')) {
+            return $stmt;
+        }
+        Horde::logMessage('SQL query by Ansel_Tags::readTags ' . $stmt->query, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+        $result = $stmt->execute((int)$resource_id);
+        $tags = $result->fetchAll(MDB2_FETCHMODE_ASSOC, true);
+        foreach ($tags as $id => $tag) {
+            $tags[$id] = Horde_String::convertCharset(
+                $tag, $GLOBALS['conf']['sql']['charset']);
+        }
+        $stmt->free();
+        $result->free();
+
+        return $tags;
+    }
+
+    /**
+     * Retrieve the list of used tag_names, tag_ids and the total number
+     * of resources that are linked to that tag.
+     *
+     * @param array $tags     An optional array of tag_ids. If omitted, all tags
+     *                        will be included.
+     * @param integer $limit  Limit the number of tags returned to this value.
+     *
+     * @return mixed  An array containing tag_name, and total | PEAR_Error
+     */
+    function listTagInfo($tags = null, $limit = 500)
+    {
+        global $conf;
+        // Only return the full list if $tags is omitted, not if
+        // an empty array is passed
+        if (is_array($tags) && count($tags) == 0) {
+            return array();
+        }
+        if (isset($GLOBALS['cache'])) {
+            $cache_key = 'ansel_taginfo_' . (!is_null($tags) ? md5(serialize($tags) . $limit) : $limit);
+            $cvalue = $GLOBALS['cache']->get($cache_key, $conf['cache']['default_lifetime']);
+            if ($cvalue) {
+                return unserialize($cvalue);
+            }
+        }
+
+        $sql = 'SELECT tn.tag_id, tag_name, COUNT(tag_name) as total FROM ansel_tags as tn INNER JOIN (SELECT tag_id FROM ansel_galleries_tags UNION ALL SELECT tag_id FROM ansel_images_tags) as t ON t.tag_id = tn.tag_id ';
+        if (!is_null($tags) && is_array($tags)) {
+            $sql .= 'WHERE tn.tag_id IN (' . implode(',', $tags) . ') ';
+        }
+        $sql .= 'GROUP BY tn.tag_id, tag_name ORDER BY total DESC';
+        if ($limit > 0) {
+            $sql .= ' LIMIT ' . (int)$limit;
+        }
+        $results = $GLOBALS['ansel_db']->queryAll($sql, null, MDB2_FETCHMODE_ASSOC, true);
+        foreach ($results as $id => $taginfo) {
+            $results[$id]['tag_name'] = Horde_String::convertCharset(
+                $taginfo['tag_name'], $GLOBALS['conf']['sql']['charset']);
+        }
+        if (isset($GLOBALS['cache'])) {
+            $GLOBALS['cache']->set($cache_key, serialize($results));
+        }
+
+        return $results;
+    }
+
+    /**
+     * Search for resources matching the specified criteria
+     *
+     * @param array  $ids            An array of tag_ids to search for.
+     * @param int    $max            The maximum number of resources to return.
+     * @param int    $from           The number to start from
+     * @param string $resource_type  Either 'images', 'galleries', or 'all'.
+     * @param string $user           Limit the result set to resources
+     *                               owned by this user.
+     *
+     * @return mixed An array of image_ids and galery_ids objects | PEAR_Error
+     */
+    function searchTagsById($ids, $max = 0, $from = 0,
+                         $resource_type = 'all', $user = null)
+    {
+        if (!is_array($ids) || !count($ids)) {
+            return array('galleries' => array(), 'images' => array());
+        }
+
+        $skey = md5(serialize($ids) . $from . $resource_type . $max . $user);
+
+        if (isset($GLOBALS['cache'])) {
+           $key = Horde_Auth::getAuth() . '__anseltagsearches';
+           $cvalue = $GLOBALS['cache']->get($key, 300);
+           $cvalue = @unserialize($cvalue);
+           if (!$cvalue) {
+               $cvalue = array();
+           }
+           if (!empty($cvalue[$skey])) {
+               return $cvalue[$skey];
+           }
+        }
+
+        $ids = array_values($ids);
+        $results = array();
+        /* Retrieve any images that match */
+        if ($resource_type != 'galleries') {
+            $sql = 'SELECT image_id, count(tag_id) FROM ansel_images_tags '
+                . 'WHERE tag_id IN (' . implode(',', $ids) . ') GROUP BY '
+                . 'image_id HAVING count(tag_id) = ' . count($ids);
+
+            Horde::logMessage('SQL query by Ansel_Tags::searchTags: ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+            $GLOBALS['ansel_db']->setLimit($max, $from);
+            $images = $GLOBALS['ansel_db']->queryCol($sql);
+            if (is_a($images, 'PEAR_Error')) {
+                Horde::logMessage($images, __FILE__, __LINE__, PEAR_LOG_ERR);
+                $results['images'] = array();
+            } else {
+                /* Check permissions and filter on $user if required */
+                $imgs = array();
+                foreach ($images as $id) {
+                    $img = &$GLOBALS['ansel_storage']->getImage($id);
+                    if (is_a($img, 'PEAR_Error')) {
+                        break;
+                    }
+                    $gal = $GLOBALS['ansel_storage']->getGallery($img->gallery);
+                    if (!is_a($gal, 'PEAR_Error')) {
+                        $owner = $gal->get('owner');
+                        if ($gal->hasPermission(Horde_Auth::getAuth(), PERMS_SHOW) &&
+                            (!isset($user) || (isset($user) && $owner == $user))) {
+                            $imgs[] = $id;
+                        }
+                    } else {
+                        Horde::logMessage($gal, __FILE__, __LINE__, PEAR_LOG_ERR);
+                    }
+                }
+                    $results['images'] = $imgs;
+            }
+        }
+
+        /* Now get the galleries that match */
+        if ($resource_type != 'images') {
+            $results['galleries'] = array();
+            $sql = 'SELECT gallery_id, count(tag_id) FROM ansel_galleries_tags '
+               . 'WHERE tag_id IN (' . implode(',', $ids) . ') GROUP BY '
+               . 'gallery_id HAVING count(tag_id) = ' . count($ids);
+
+            Horde::logMessage('SQL query by Ansel_Tags::searchTags: ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+            $GLOBALS['ansel_db']->setLimit($max, $from);
+
+            $galleries = $GLOBALS['ansel_db']->queryCol($sql);
+            if (is_a($galleries, 'PEAR_Error')) {
+                Horde::logMessage($galleries, __FILE__, __LINE__, PEAR_LOG_ERR);
+            } else {
+                /* Check perms */
+                foreach ($galleries as $id) {
+                    $gallery = $GLOBALS['ansel_storage']->getGallery($id);
+                    if (is_a($gallery, 'PEAR_Error')) {
+                        Horde::logMessage($gallery, __FILE__, __LINE__, PEAR_LOG_ERR);
+                        continue;
+                    }
+                    if ($gallery->hasPermission(Horde_Auth::getAuth(), PERMS_SHOW)  && (!isset($user) || (isset($user) && $gallery->get('owner') == $user))) {
+                        $results['galleries'][] = $id;
+                    }
+                }
+            }
+        }
+
+        if (isset($GLOBALS['cache'])) {
+            $cvalue[$skey] = $results;
+            $GLOBALS['cache']->set($key, serialize($cvalue));
+        }
+
+        return $results;
+    }
+
+    /**
+     * Search for resources matching a specified set of tags
+     * and optionally limit the result set to resources owned by
+      * a specific user.
+     *
+     * @param array  $names          An array of tag strings to search for.
+     * @param int    $max            The maximum number of resources to return.
+     * @param int    $from           The resource to start at.
+     * @param string $resource_type  Either 'images', 'galleries', or 'all'.
+     * @param string $user           Limit the result set to resources owned by
+     *                               specified user.
+     *
+     * @return mixed An array of image_ids and gallery_ids | PEAR_Error
+     */
+    function searchTags($names = array(), $max = 10, $from = 0,
+                        $resource_type = 'all', $user = null)
+    {
+        /* Get the tag_ids */
+        $ids = Ansel_Tags::getTagIds($names);
+        return Ansel_Tags::searchTagsbyId($ids, $max, $from, $resource_type,
+                                          $user);
+    }
+
+    /**
+     * Retrieve a set of tags with relationships to the specified set
+     * of tags.
+     *
+     * @param array $tags  An array of tag_ids
+     *
+     * @return mixed A hash of tag_id -> tag_name | PEAR_Error
+     */
+    function getRelatedTags($ids)
+    {
+        if (!count($ids)) {
+            return array();
+        }
+
+        /* Build the monster SQL statement.*/
+        $sql = 'SELECT DISTINCT t.tag_id, t.tag_name FROM ansel_images_tags as r, ansel_images as i, ansel_tags as t';
+        for ($i = 0; $i < count($ids); $i++) {
+            $sql .= ',ansel_images_tags as r' . $i;
+        }
+        $sql .= ' WHERE r.tag_id = t.tag_id AND r.image_id = i.image_id';
+        for ($i = 0; $i < count($ids); $i++) {
+            $sql .= ' AND r' . $i . '.image_id = r.image_id AND r.tag_id != ' . (int)$ids[$i] . ' AND r' . $i . '.tag_id = ' . (int)$ids[$i];
+        }
+
+        /* Note that we don't convertCharset here, it's done in listTagInfo */
+        $imgtags = $GLOBALS['ansel_db']->queryAll($sql, null, MDB2_FETCHMODE_ASSOC, true);
+
+        /* Now get the galleries. */
+        $table = 'ansel_shares';
+        $sql = 'SELECT DISTINCT t.tag_id, t.tag_name FROM ansel_galleries_tags as r, ' . $table . ' AS i, ansel_tags as t';
+        for ($i = 0; $i < count($ids); $i++) {
+            $sql .= ', ansel_galleries_tags as r' . $i;
+        }
+        $sql .= ' WHERE r.tag_id = t.tag_id AND r.gallery_id = i.share_id';
+        for ($i = 0; $i < count($ids); $i++) {
+            for ($i = 0; $i < count($ids); $i++) {
+                $sql .= ' AND r' . $i . '.gallery_id = r.gallery_id AND r.tag_id != ' . (int)$ids[$i] . ' AND r' . $i . '.tag_id = ' . (int)$ids[$i];
+            }
+        }
+        $galtags = $GLOBALS['ansel_db']->queryAll($sql, null, MDB2_FETCHMODE_ASSOC, true);
+
+        /* Can't use array_merge here since it would renumber the array keys */
+        foreach ($galtags as $id => $name) {
+            if (empty($imgtags[$id])) {
+                $imgtags[$id] = $name;
+            }
+        }
+
+        /* Get an array of tag info sorted by total */
+        $tagids = array_keys($imgtags);
+        if (count($tagids)) {
+            $imgtags = Ansel_Tags::listTagInfo($tagids);
+        }
+
+        return $imgtags;
+    }
+
+    /**
+     * Get the URL for a tag link
+     *
+     * @param array $tags      The tag ids to link to
+     * @param string $action   The action we want to perform with this tag.
+     * @param string $owner    The owner we want to filter the results by
+     *
+     * @return string  The URL for this tag and action
+     */
+    function getTagLinks($tags, $action = 'add', $owner = null)
+    {
+        $results = array();
+        foreach ($tags as $id => $taginfo) {
+            $params = array('view' => 'Results',
+                            'tag' => $taginfo['tag_name']);
+            if (!empty($owner)) {
+                $params['owner'] = $owner;
+            }
+            if ($action != 'add') {
+                $params['actionID'] = $action;
+            }
+            $link = Ansel::getUrlFor('view', $params, true);
+            $results[$id] = $link;
+        }
+
+        return $results;
+    }
+
+    /**
+      * Get a list of tag_ids from a list of tag_names
+      *
+      * @param array $tags An array of tag_names
+      *
+      * @return mixed  An array of tag_names => tag_ids | PEAR_Error
+      */
+    function getTagIds($tags)
+    {
+        if (!count($tags)) {
+            return array();
+        }
+        $stmt = $GLOBALS['ansel_db']->prepare('SELECT ansel_tags.tag_name, ansel_tags.tag_id FROM ansel_tags WHERE ansel_tags.tag_name IN (' . str_repeat('?, ', count($tags) - 1) . '?)');
+        $result = $stmt->execute(array_values($tags));
+        $ids = $result->fetchAll(MDB2_FETCHMODE_ASSOC, true);
+        $newIds = array();
+        foreach ($ids as $tag => $id) {
+            $newIds[Horde_String::convertCharset($tag, $GLOBALS['conf']['sql']['charset'])] = $id;
+        }
+        $result->free();
+        $stmt->free();
+
+        return $newIds;
+    }
+
+    /**
+     *
+     */
+    function getTagNames($ids)
+    {
+        if (!count($ids)) {
+            return array();
+        }
+        $stmt = $GLOBALS['ansel_db']->prepare('SELECT t.tag_id, t.tag_name FROM ansel_tags as t WHERE t.tag_id IN(' . str_repeat('?, ', count($ids) - 1) . '?)');
+        $result = $stmt->execute(array_values($ids));
+        $tags = $result->fetchAll(MDB2_FETCHMODE_ASSOC, true);
+        foreach ($tags as $id => $tag) {
+            $tags[$id] = Horde_String::convertCharset(
+                $tag, $GLOBALS['conf']['sql']['charset']);
+        }
+        $result->free();
+        $stmt->free();
+
+        return $tags;
+    }
+
+    /**
+     * Retrieve an Ansel_Tags_Search object
+     */
+    function getSearch($tags = null, $owner = null)
+    {
+        return new Ansel_Tags_Search($tags, $owner);
+    }
+
+    /**
+     * Clear the session cache
+     */
+    function clearSearch()
+    {
+        unset($_SESSION['ansel_tags_search']);
+    }
+
+    function clearCache()
+    {
+        if ($GLOBALS['conf']['ansel_cache']['usecache']) {
+            $GLOBALS['cache']->expire(Horde_Auth::getAuth() . '__anseltagsearches');
+        }
+    }
+}
+
+/**
+ * Class that represents a slice of a tag search
+ */
+class Ansel_Tags_Search {
+
+    var $_tags = array();
+    var $_totalCount = null;
+    var $_owner = null;
+    var $_dirty = false;
+
+    /**
+     * Constructor
+     *
+     * @param array $tags  An array of tag_ids to match. If null is passed then
+     *                     the tags will be loaded from the session.
+     */
+    function Ansel_Tags_Search($tags = null, $owner = null)
+    {
+        if (!empty($tags)) {
+            $this->_tags = $tags;
+        } else {
+            $this->_tags = (!empty($_SESSION['ansel_tags_search']) ? $_SESSION['ansel_tags_search'] : array());
+        }
+
+        $this->_owner = $owner;
+    }
+
+    /**
+     * Save the current search to the session
+     */
+    function save()
+    {
+        $_SESSION['ansel_tags_search'] = $this->_tags;
+        $this->_dirty = false;
+    }
+
+    /**
+     * Fetch the matching resources that should appear on the current page
+     *
+     * @return Array of Ansel_Images and Ansel_Galleries | PEAR_Error
+     */
+    function getSlice($page, $perpage)
+    {
+        global $conf, $registry;
+
+        $results = array();
+        $totals = $this->count();
+
+        /* First, the galleries */
+        $gstart = $page * $perpage;
+        $gresults = Ansel_Tags::searchTagsById($this->_tags,
+                                               $perpage,
+                                               $gstart,
+                                               'galleries',
+                                               $this->_owner);
+
+        if (is_a($gresults, 'PEAR_Error')) {
+            return $gresults;
+        }
+
+        $galleries = array();
+        foreach ($gresults['galleries'] as $gallery) {
+            $galleries[] = $GLOBALS['ansel_storage']->getGallery($gallery);
+        }
+
+        /* Do we need to get images? */
+        $istart = max(0, $page * $perpage - $totals['galleries']);
+        $count = $perpage - count($galleries);
+        if ($count > 0) {
+            $iresults = Ansel_Tags::searchTagsById($this->_tags,
+                                                   $count,
+                                                   $istart,
+                                                  'images',
+                                                   $this->_owner);
+            if (is_a($iresults, 'PEAR_Error')) {
+                return $iresults;
+            }
+
+            $images = array_values($GLOBALS['ansel_storage']->getImages($iresults['images']));
+            if (($conf['comments']['allow'] == 'all' || ($conf['comments']['allow'] == 'authenticated' && Horde_Auth::getAuth())) &&
+                $registry->hasMethod('forums/numMessagesBatch')) {
+
+                $ids = array_keys($images);
+                $ccounts = $GLOBALS['registry']->call('forums/numMessagesBatch', array($ids, 'ansel'));
+                if (!is_a($ccounts, 'PEAR_Error')) {
+                    foreach ($images as $image) {
+                        $image->commentCount = (!empty($ccounts[$image->id]) ? $ccounts[$image->id] : 0);
+                    }
+                }
+            }
+        } else {
+            $images = array();
+        }
+        return array_merge($galleries, $images);
+    }
+
+    /**
+     * Add a tag to the cumulative tag search
+     */
+    function addTag($tag_id)
+    {
+        if (array_search($tag_id, $this->_tags) === false) {
+            $this->_tags[] = $tag_id;
+            $this->_dirty = true;
+        }
+    }
+
+    /**
+     * Remove a tag from the cumulative search
+     */
+    function removeTag($tag_id)
+    {
+        $key = array_search($tag_id, $this->_tags);
+        if ($tag_id === false) {
+            return;
+        } else {
+            unset($this->_tags[$key]);
+            $this->_tags = array_values($this->_tags);
+            $this->_dirty = true;
+        }
+    }
+
+    /**
+     * Get the list of currently choosen tags
+     */
+    function getTags()
+    {
+        return $this->_tags;
+    }
+
+    /**
+     * Get breadcrumb style navigation html for choosen tags
+     *
+     */
+    function getTagTrail()
+    {
+        global $registry;
+
+        $tags = Ansel_Tags::getTagNames($this->_tags);
+        $html = '<ul class="tag-list">';
+
+        /* Use the local cache to preserve the order */
+        $count = 0;
+        foreach ($this->_tags as $tagid) {
+            $remove_url = Horde_Util::addParameter('view.php', array('view' => 'Results',
+                                                               'tag' => $tags[$tagid],
+                                                               'actionID' => 'remove'));
+
+            if (!empty($this->_owner)) {
+                $remove_url = Horde_Util::addParameter($remove_url, 'owner', $this->_owner);
+            }
+            $remove_url = Horde::applicationUrl($remove_url, true);
+            $delete_label = sprintf(_("Remove %s from search"), htmlspecialchars($tags[$tagid]));
+            $html .= '<li>' . htmlspecialchars($tags[$tagid]) . Horde::link($remove_url, $delete_label) . Horde::img('delete-small.png', $delete_label, '', $registry->getImageDir('horde')) . '</a></li>';
+        }
+
+        return $html . '</ul>';
+    }
+
+    /**
+     * Get the total number of tags included in this search.
+     */
+    function tagCount()
+    {
+        return count($this->_tags);
+    }
+
+    /**
+     * Get the total number of resources that match.
+     *
+     * @return array  Hash containing totals for both 'galleries' and 'images'.
+     */
+    function count()
+    {
+        if (!is_array($this->_tags) || !count($this->_tags)) {
+            return 0;
+        }
+
+        /* First see if we already calculated for the current page load */
+        if ($this->_totalCount && !$this->_dirty) {
+            return $this->_totalCount;
+        }
+
+        /* Can't perform a COUNT query since we have to check perms */
+        $results = Ansel_Tags::searchTagsById($this->_tags, 0, 0, 'all',
+                                              $this->_owner);
+        $count = array('galleries' => count($results['galleries']), 'images' => count($results['images']));
+        $this->_totalCount = $count;
+        return $count;
+    }
+
+    /**
+     * Get a list of tags related to this search
+     */
+    function getRelatedTags()
+    {
+        $tags = Ansel_Tags::getRelatedTags($this->getTags());
+        /* Make sure that we have actual results for each tag since
+         * some tags may exist on only images/galleries to which we
+         * have no perms */
+        $search = Ansel_Tags::getSearch(null, $this->_owner);
+        $results = array();
+        foreach ($tags as $id => $tag) {
+            $search->addTag($id);
+            $count = $search->count();
+            if ($count['images'] + $count['galleries'] > 0) {
+                $results[$id] = array('tag_name' => $tag['tag_name'], 'total' => $count['images'] + $count['galleries']);
+            }
+            $search->removeTag($id);
+        }
+
+        /* Get the results sorted by available totals for this user */
+        uasort($results, array($this, '_sortTagInfo'));
+        return $results;
+    }
+
+    /**
+     */
+    function _sortTagInfo($a, $b)
+    {
+        return $a['total']  <  $b['total'];
+    }
+
+}
diff --git a/ansel/lib/Tile/DateGallery.php b/ansel/lib/Tile/DateGallery.php
new file mode 100644 (file)
index 0000000..4d79191
--- /dev/null
@@ -0,0 +1,132 @@
+<?php
+/**
+ * Ansel_Tile_DateGallery:: class wraps display of thumbnail tile for the
+ * DateGallery psuedo gallery.
+ *
+ * $Horde: ansel/lib/Tile/DateGallery.php,v 1.15 2009/07/13 14:29:05 mrubinsk Exp $
+ *
+ * @author Michael Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+
+/** Horde_Date **/
+require_once 'Horde/Date.php';
+
+class Ansel_Tile_DateGallery {
+
+    /**
+     * Outputs the html for a DateGallery tile.
+     *
+     * @param Ansel_DateGallery $dgallery  The Ansel_Gallery_Date we are
+     *                                    displaying.
+     * @param array $style                A style definition array.
+     * @param boolean $mini               Force the use of a mini thumbail?
+     * @param array $params               An array containing additional
+     *                                    parameters. Currently,
+     *                                    gallery_view_url and
+     *                                    image_view_url are used to override
+     *                                    the respective urls. %g and %i are
+     *                                    replaced with image id and gallery id,
+     *                                    respectively
+     *
+     * @return  Outputs the HTML for the tile.
+     */
+    function getTile($dgallery, $style = null, $mini = false,
+                     $params = array())
+    {
+        /* User's preferred date format */
+        $date_format = $GLOBALS['prefs']->getValue('date_format');
+
+        /* Easier to work with a Horde_Date obejct */
+        $date_array = $dgallery->getDate();
+        if (empty($date_array['month'])) {
+            $date_array['month'] = 1;
+        }
+        if (empty($date_array['day'])) {
+            $date_array['day'] = 1;
+        }
+
+        $full_date = new Horde_Date($date_array);
+
+        /* Need the unaltered date part array */
+        $date_array = $dgallery->getDate();
+
+        /* Figure out the needed link for the next drill down level.
+         * We *must* have at least a year since we are in a date tile.
+         */
+        if (empty($date_array['month'])) {
+            // unit == year
+            $caption = $full_date->format('Y');
+            $next_date = array('year' => (int)$caption);
+        } elseif (empty($date_array['day'])) {
+            // unit == month
+            $caption = $full_date->format('F Y');
+            $next_date = array('year' => date('Y', $full_date->timestamp()),
+                               'month' => date('n', $full_date->timestamp()));
+        } else {
+            // unit == day ... hopefully ;)
+            $caption = $full_date->strftime($date_format);
+            $next_date = array('year' => date('Y', $full_date->timestamp()),
+                               'month' => date('n', $full_date->timestamp()),
+                               'day' => date('j', $full_date->timestamp()));
+        }
+
+        /* Get the currently displayed gallery view type */
+        // @TODO: $view would only be needed if we are displaying this tile in a
+        //        search result view as well. (Not implemented yet)
+        //$view = Horde_Util::getFormData('view', 'Gallery');
+
+        /* Check permissions on the gallery and get appropriate tile image */
+        if ($dgallery->hasPermission(Horde_Auth::getAuth(), PERMS_READ)) {
+            if (is_null($style)) {
+                $style = $dgallery->getStyle();
+            }
+
+            $thumbstyle = $mini ? 'mini' : $style['thumbstyle'];
+            $gallery_image = Ansel::getImageUrl(
+                $dgallery->getDefaultImage(),
+                $thumbstyle, true, $style['name']);
+
+            // No need to escape alt here since it's generated enitrely within
+            // horde from Horde_Date
+            $gallery_image = '<img src="' . $gallery_image . '" alt="' . $caption . '" />' ;
+        } else {
+            $gallery_image = Horde::img($GLOBALS['registry']->getImageDir() . '/thumb-error.png', '', '', '');
+        }
+
+        /* Check for being called via the api and generate correct view links */
+        if (!isset($params['gallery_view_url'])) {
+            if (empty($params['style'])) {
+                $gstyle = $dgallery->getStyle();
+            } else {
+                $gstyle = Ansel::getStyleDefinition($params['style']);
+            }
+            $params = array('gallery' => $dgallery->id,
+                            'view' => 'Gallery',
+                            'slug' => $dgallery->get('slug'));
+            $params = array_merge($params, $next_date);
+            $view_link = Ansel::getUrlFor('view', $params);
+            $view_link = Horde::link($view_link);
+        } else {
+            $url = str_replace(array('%g', '%s'),
+                array($dgallery->id, $dgallery->get('slug')),
+                urldecode($params['gallery_view_url']));
+
+            $url = Horde_Util::addParameter($url, $next_date);
+            $view_link = Horde::link($url);
+        }
+
+        /* Variables used in the template file */
+        $image_link = $view_link . $gallery_image . '</a>';
+        $text_link = $view_link . htmlspecialchars($caption, ENT_COMPAT, Horde_Nls::getCharset()) . '</a>';
+        $gallery_count = $dgallery->countImages(true);
+
+        /* Background color is needed if displaying a mini tile */
+        $background_color = $style['background'];
+
+        ob_start();
+        include ANSEL_TEMPLATES . '/tile/dategallery' . ($mini ? 'mini' : '') . '.inc';
+        return ob_get_clean();
+    }
+
+}
diff --git a/ansel/lib/Tile/Gallery.php b/ansel/lib/Tile/Gallery.php
new file mode 100644 (file)
index 0000000..fd9cb40
--- /dev/null
@@ -0,0 +1,132 @@
+<?php
+/**
+ * Ansel_Tile_Gallery:: class wraps display of thumbnail 'tiles' displayed
+ * for a gallery on the Ansel_View_Gallery view.
+ *
+ * $Horde: ansel/lib/Tile/Gallery.php,v 1.43 2009/07/13 17:18:40 mrubinsk Exp $
+ *
+ * @author Michael Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_Tile_Gallery {
+
+    /**
+     * Outputs the html for a gallery tile.
+     *
+     * @param Ansel_Gallery $gallery  The Ansel_Gallery we are displaying.
+     * @param array $style            A style definition array.
+     * @param boolean $mini           Force the use of a mini thumbail?
+     * @param array $params           An array containing additional parameters.
+     *                                Currently, gallery_view_url and
+     *                                image_view_url are used to override the
+     *                                respective urls. %g and %i are replaced
+     *                                with image id and gallery id, respectively
+     *
+     *
+     * @return  Outputs the HTML for the tile.
+     */
+    function getTile($gallery, $style = null, $mini = false,
+                     $params = array())
+    {
+        /*
+         * See what view we are being displayed in to see if we need to show
+         * the owner info or not.
+         */
+        $view = Horde_Util::getFormData('view', 'Gallery');
+        $haveSearch = ($view == 'Results') ? 1 : 0;
+        if (($view == 'Results' || $view == 'List') ||
+            (basename($_SERVER['PHP_SELF']) == 'index.php' &&
+             $GLOBALS['prefs']->getValue('defaultview') == 'galleries')) {
+            $showOwner = true;
+        } else {
+            $showOwner = false;
+        }
+
+        /* Check gallery permissions and get appropriate tile image */
+        if ($gallery->hasPermission(Horde_Auth::getAuth(), PERMS_READ)) {
+            if (is_null($style)) {
+                $style = $gallery->getStyle();
+            }
+            $thumbstyle = $mini ? 'mini' : $style['thumbstyle'];
+
+            if ($gallery->hasPasswd()) {
+                $gallery_image = Horde::img($GLOBALS['registry']->getImageDir() . '/gallery-locked.png', '', '', '');
+            } else {
+            $gallery_image = Ansel::getImageUrl(
+                $gallery->getDefaultImage($style['name']),
+                $thumbstyle, true, $style['name']);
+                $gallery_image = '<img src="' . $gallery_image . '" alt="' . htmlspecialchars($gallery->get('name')) . '" />';
+            }
+        } else {
+            $gallery_image = Horde::img(
+                $GLOBALS['registry']->getImageDir() . '/thumb-error.png', '',
+                '', '');
+        }
+
+        /* Check for being called via the api and generate correct view links */
+        if (!isset($params['gallery_view_url'])) {
+            if (empty($params['style'])) {
+                $gstyle = $gallery->getStyle();
+            } else {
+                $gstyle = Ansel::getStyleDefinition($params['style']);
+            }
+            $view_link = Ansel::getUrlFor('view',
+                                          array('gallery' => $gallery->id,
+                                                'view' => 'Gallery',
+                                                'havesearch' => $haveSearch,
+                                                'slug' => $gallery->get('slug')));
+
+            $view_link = Horde::link($view_link);
+        } else {
+            $view_link = Horde::link(
+                str_replace(array('%g', '%s'),
+                            array($gallery->id, $gallery->get('slug')),
+                            urldecode($params['gallery_view_url'])));
+        }
+
+        $image_link = $view_link . $gallery_image . '</a>';
+        $text_link = $view_link . htmlspecialchars($gallery->get('name'), ENT_COMPAT, Horde_Nls::getCharset())
+                     . '</a>';
+
+        if ($gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT) && !$mini) {
+            $properties_link = Horde_Util::addParameter(
+                    Horde::applicationUrl('gallery.php', true),
+                        array('gallery' => $gallery->id,
+                              'actionID' => 'modify',
+                              'havesearch' => $haveSearch,
+                              'url' => Horde::selfUrl(true, false, true)));
+            $properties_link = Horde::link($properties_link)
+                               . _("Gallery Properties") . '</a>';
+        }
+
+        if ($showOwner && !$mini &&
+            Horde_Auth::getAuth() != $gallery->get('owner')) {
+            $owner_link = Ansel::getUrlFor('view',
+                                            array('view' => 'List',
+                                                  'owner' => $gallery->get('owner'),
+                                                  'groupby' => 'owner'),
+                                            true);
+            $owner_link = Horde::link($owner_link);
+            $gallery_owner = $gallery->getOwner();
+            $owner_string = $gallery_owner->getValue('fullname');
+            if (empty($owner_string)) {
+                $owner_string = $gallery->get('owner');
+            }
+            $owner_link = $owner_link . htmlspecialchars($owner_string, ENT_COMPAT, Horde_Nls::getCharset()) . '</a>';
+        }
+
+        $gallery_count = $gallery->countImages(true);
+        $background_color = $style['background'];
+
+        $date_format = $GLOBALS['prefs']->getValue('date_format');
+        $created = _("Created:") . ' '
+                   . strftime($date_format, (int)$gallery->get('date_created'));
+        $modified = _("Modified") . ' '
+                   . strftime($date_format, (int)$gallery->get('last_modified'));
+
+        ob_start();
+        include ANSEL_TEMPLATES . '/tile/gallery' . ($mini ? 'mini' : '') . '.inc';
+        return ob_get_clean();
+    }
+
+}
diff --git a/ansel/lib/Tile/Image.php b/ansel/lib/Tile/Image.php
new file mode 100644 (file)
index 0000000..ae99724
--- /dev/null
@@ -0,0 +1,129 @@
+<?php
+/**
+ * Ansel_Tile_Image:: class wraps display of thumbnails displayed
+ * for a image on the Ansel_View_Gallery view.
+ *
+ * $Horde: ansel/lib/Tile/Image.php,v 1.55 2009/07/29 03:08:44 mrubinsk Exp $
+ *
+ * @author Michael Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_Tile_Image {
+
+    /**
+     * Outputs the HTML for an image thumbnail 'tile'.
+     *
+     * @param Ansel_Image $image     The Ansel_Image we are displaying.
+     * @param Ansel_Gallery $parent  The parent Ansel_Gallery for this image.
+     *                               If null, will create a new instance of
+     *                               the Ansel_Gallery
+     * @param array $style           A sytle definiition array.
+     * @param boolean $mini          Force the use of a mini thumbnail?
+     * @param string $params         Any other paramaters needed by this tile
+     *
+     * @return  Outputs the HTML for the image tile.
+     */
+    function getTile($image, $style = null, $mini = false,
+                     $params = array())
+    {
+        global $conf, $registry;
+
+        $parent = $GLOBALS['ansel_storage']->getGallery($image->gallery);
+        if (is_null($style)) {
+            $style = $parent->getStyle();
+        }
+
+        $page = isset($params['page']) ? $params['page'] : 0;
+        $view = isset($params['view']) ? $params['view'] : 'Gallery';
+        $date = $parent->getDate();
+
+        if ($view == 'Results') {
+            $haveSearch = 1;
+        } else {
+            $haveSearch = 0;
+        }
+
+        /* Override the thumbnail to mini or use style default? */
+        $thumbstyle = $mini ? 'mini' : $style['thumbstyle'];
+
+        /* URL for image properties/actions etc... */
+        $image_url = Horde_Util::addParameter('image.php', array_merge(
+             array('gallery' => $image->gallery,
+                   'page' => $page,
+                   'image' => $image->id,
+                   'havesearch' => $haveSearch),
+             $date));
+
+        /* URL to view the image. This is the link for the Tile.
+         * $view_url is the link for the thumbnail and since this might not
+         * always point to the image view page, we set $img_view_url to link to
+         * the image view
+         */
+        $img_view_url = Ansel::getUrlFor('view', array_merge(
+            array('gallery' => $image->gallery,
+                  'slug' => $parent->get('slug'),
+                  'page' => $page,
+                  'view' => 'Image',
+                  'image'=> $image->id,
+                  'havesearch' => $haveSearch),
+            $date));
+
+        if (!empty($params['image_view_src'])) {
+            $view_url = Ansel::getImageUrl($image->id, 'screen', true);
+        } elseif (empty($params['image_view_url'])) {
+            $view_url = $img_view_url;
+        } else {
+            $view_url = str_replace(array('%i', '%g', '%s'),
+                                    array($image->id, $image->gallery, $parent->get('slug')),
+                                    urldecode($params['image_view_url']));
+
+            // If we override the view_url, assume we want to override this
+            // as well.
+            $img_view_url = $view_url;
+        }
+
+        // Need the gallery URL to display the "From" link when showing
+        // the image tile from somewhere other than the gallery view.
+        if (!empty($view) || basename($_SERVER['PHP_SELF']) == 'view.php') {
+            $gallery_url = Ansel::getUrlFor('view', array_merge(
+                array('gallery' => $parent->id,
+                      'slug' => $parent->get('slug'),
+                      'view' => 'Gallery',
+                      'havesearch' => $haveSearch),
+                $date));
+        }
+
+        $thumb_url = Ansel::getImageUrl($image->id, $thumbstyle, true, $style['name']);
+        $option_select = $parent->hasPermission(Horde_Auth::getAuth(), PERMS_DELETE);
+        $option_edit = $parent->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT);
+        $imgAttributes = (!empty($params['image_view_attributes'])
+                         ? $params['image_view_attributes'] : array());
+
+        $imgOnClick = (!empty($params['image_onclick'])
+                      ? str_replace('%i', $image->id, $params['image_onclick']) : '');
+
+        $imageCaption = Horde_Text_Filter::filter(
+            $image->caption, 'text2html',
+            array('parselevel' => Horde_Text_Filter_Text2html::MICRO));
+
+        if (!empty($params['image_view_title']) &&
+            !empty($image->_data[$params['image_view_title']])) {
+            $title = $image->_data[$params['image_view_title']];
+        } else {
+            $title = $image->filename;
+        }
+
+        ob_start();
+        // In-line caption editing if we have PERMS_EDIT
+        if ($option_edit) {
+            $imple = Horde_Ajax_Imple::factory(array('ansel', 'EditCaption'),
+                                               array('id' => $image->id,
+                                                     'domid' => $image->id . 'caption'));
+            $imple->attach();
+        }
+
+        include ANSEL_BASE . '/templates/tile/image.inc';
+        return ob_get_clean();
+    }
+
+}
diff --git a/ansel/lib/Views/Abstract.php b/ansel/lib/Views/Abstract.php
new file mode 100644 (file)
index 0000000..bcf79b6
--- /dev/null
@@ -0,0 +1,191 @@
+<?php
+/**
+ * The Ansel_View_Abstract:: Parent class for the various Ansel_View classes
+ *
+ * $Horde: ansel/lib/Views/Abstract.php,v 1.46 2009/07/13 14:29:05 mrubinsk Exp $
+ *
+ * @author  Chuck Hagenbuch <chuck@horde.org>
+ * @author  Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_View_Abstract {
+
+    var $_params = array();
+
+    /**
+     * The ansel resource this view is for.
+     *
+     * @var mixed  Either an Ansel_Gallery or Ansel_Image
+     */
+    var $resource;
+
+    /**
+     * The gallery object (will be eq to $resource in a gallery view
+     *
+     * @var Ansel_Gallery
+     */
+    var $gallery;
+
+    /**
+     * Collection of Ansel_Widgets to display in this view.
+     *
+     * @var array
+     */
+    var $_widgets = array();
+
+    function &getGallery($galleryId = null, $slug = '')
+    {
+        if (is_null($galleryId) && empty($slug)) {
+            $galleryId = !empty($this->_params['gallery_id']) ? $this->_params['gallery_id'] : null;
+            $slug = !empty($this->_params['gallery_slug']) ? $this->_params['gallery_slug'] : null;
+        }
+
+        if (empty($galleryId) && empty($slug)) {
+            return PEAR::raiseError(_("No gallery specified"));
+        }
+
+        // If we have a slug, use it.
+        if (!empty($slug)) {
+            $gallery = &$GLOBALS['ansel_storage']->getGalleryBySlug($slug);
+        } else {
+            $gallery = &$GLOBALS['ansel_storage']->getGallery($galleryId);
+        }
+        if (is_a($gallery, 'PEAR_Error')) {
+            return $gallery;
+        } elseif (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_READ)) {
+            return PEAR::raiseError(sprintf(_("Access denied to gallery \"%s\"."), $gallery->get('name')));
+        }
+
+        /* Set any date info we might have */
+        if (!empty($this->_params['year'])) {
+            $date = Ansel::getDateParameter(
+                array('year' => $this->_params['year'],
+                      'month' => $this->_params['month'],
+                      'day' => $this->_params['day']));
+        } else {
+            $date = array();
+        }
+        $gallery->setDate($date);
+
+        return $gallery;
+    }
+
+    /**
+     * Add an Ansel_Widget to be displayed in this view.
+     *
+     * @param Ansel_Widget $widget  The Ansel_Widget to display
+     */
+    function addWidget($widget)
+    {
+        $result = $widget->attach($this);
+        if (!empty($result)) {
+            $this->_widgets[] = $widget;
+        }
+    }
+
+
+    /**
+     * Output any widgets associated with this view.
+     *
+     */
+    function renderWidgets()
+    {
+        $this->_renderWidgets();
+    }
+
+    /**
+     * Count the number of widgets we have attached.
+     *
+     * @return integer  The number of widgets attached to this view.
+     */
+    function countWidgets()
+    {
+        return count($this->_widgets);
+    }
+
+    /**
+     * Default widget rendering, can be overridden by any subclass.
+     *
+     */
+    function _renderWidgets()
+    {
+        echo '<div class="anselWidgets">';
+        foreach ($this->_widgets as $widget) {
+            if ($widget->_render == 'auto') {
+                echo $widget->html();
+                echo '<br />';
+            }
+        }
+        echo '</div>';
+    }
+
+   /**
+     * JSON representation of this gallery's images.
+     *
+     * @param array $images   An array of Ansel_Image objects. If this is null
+     *                        the images are fetched based on $from and $count.
+     *
+     * @param integer $from   Image to start at.
+     * @param integer $count  Number of images to get.
+     *
+     * @return string  A serialized JSON array.
+     */
+    function json($images = null, $full = false, $from = 0, $count = 0,
+                  $image_view = 'screen', $view_links = false)
+    {
+        global $conf, $prefs;
+
+        $json = array();
+        $perpage = $prefs->getValue('tilesperpage', $conf['thumbnail']['perpage']);
+        $curimage = 0;
+        $curpage =  0;
+
+        if (is_null($images)) {
+            $images = $this->gallery->getImages($from, $count);
+        }
+
+        $style = $this->gallery->getStyle();
+
+        foreach ($images as $image) {
+            // Calculate the page this image will appear on in the
+            // gallery view.
+            if (++$curimage > $perpage) {
+                ++$curpage;
+                $curimage = 0;
+            }
+
+            $data = array(Ansel::getImageUrl($image->id, $image_view, $full, $style['name']),
+                          htmlspecialchars($image->filename, ENT_COMPAT, Horde_Nls::getCharset()),
+                          Horde_Text_Filter::filter($image->caption, 'text2html', array('parselevel' => Horde_Text_Filter_Text2html::MICRO_LINKURL)),
+                          $image->id,
+                          $curpage);
+            if ($view_links) {
+                $data[] = Ansel::getUrlFor('view',
+                    array('gallery' => $this->gallery->id,
+                          'slug' => $this->gallery->get('slug'),
+                          'image' => $image->id,
+                          'view' => 'Image'),
+                    true);
+                $data[] = Ansel::getUrlFor('view',
+                    array('gallery' => $image->gallery,
+                          'slug' => $this->gallery->get('slug'),
+                          'view' => 'Gallery'),
+                    true);
+            }
+            // Source, Width, Height, Name, Caption, Image Id, Gallery Page
+            $json[] = $data;
+        }
+
+        require_once 'Horde/Serialize.php';
+        return Horde_Serialize::serialize($json, Horde_Serialize::JSON, Horde_Nls::getCharset());
+    }
+
+    /**
+     * @abstract
+     * @return unknown_type
+     */
+    function viewType()
+    {
+    }
+
+}
diff --git a/ansel/lib/Views/Embedded.php b/ansel/lib/Views/Embedded.php
new file mode 100644 (file)
index 0000000..1b6bfbd
--- /dev/null
@@ -0,0 +1,89 @@
+<?php
+/**
+ * Ansel_View_Embedded Class wraps output of a javascript embedded gallery/image
+ * widget. This view is responsible only for outputting the <script> tags that
+ * will embed the view.  Provided as a way to output these views via the
+ * renderViews api call. The actual rendering is done via the
+ * EmbeddedRenderers/*.php files which are called from the Ajax_Imple_Embed
+ * class when it handles the request.
+ *
+ * $Horde: ansel/lib/Views/Embedded.php,v 1.16 2009/07/30 18:02:14 mrubinsk Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author  Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_View_Embedded {
+
+    /**
+     * Initialize the view
+     *
+     * This view can take the following parameters:
+     *
+     *  container    The DOM id of the element to contain the embedded view.
+     *               This parameter is required.
+     *
+     *  gallery_id   The gallery id
+     *
+     *  gallery_slug The gallery slug
+     *
+     *  gallery_view The specific view to embed this must match an existing
+     *               EmbeddedRenderer (Mini, Slideshow, Carousel etc..)
+     *               Defaults to 'Mini'
+     *
+     *  start        Start displaying at this image offset. Defaults to the start
+     *               of the gallery.
+     *
+     *  count        Only return this many images. Defaults to returning the
+     *               entire gallery (minus any subgalleries).
+     *
+     *  perpage      Some embedded views support paging. This sets the number of
+     *               images per page. Note that all images are still returned.
+     *               The paging is done via javascript only.
+     *
+     *  images       An array of image ids, not necessarily from the same
+     *               gallery, to be displayed in this view. The gallery parameter
+     *               will be ignored if present.
+     *
+     *  thumbsize    Which type of thumbnail images to display in the view.
+     *               (mini, thumb, prettythumb etc...) Defaults to mini.
+     * @static
+     * @param array $params  Parameters for this view
+     */
+    function makeView($params)
+    {
+        global $ansel_storage;
+
+        $view = new Ansel_View_Embedded();
+        $view->_params = $params;
+        return $view;
+    }
+
+    /**
+     * Return the HTML representing this view.
+     *
+     * @return string  The HTML.
+     *
+     */
+    function html()
+    {
+        /* Are we displaying a gallery or a group of images? */
+        if (!empty($this->_params['images']) && count($this->_params['images'])) {
+            $this->_params['images'] = implode(':', $this->_params['images']);
+        }
+
+        $html = Ansel::embedCode($this->_params);
+
+        return $html;
+    }
+
+    function viewType()
+    {
+        return 'Embedded';
+    }
+
+}
diff --git a/ansel/lib/Views/EmbeddedRenderers/Carousel.php b/ansel/lib/Views/EmbeddedRenderers/Carousel.php
new file mode 100644 (file)
index 0000000..ea55826
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+
+/** Ansel_View_Gallery **/
+require_once ANSEL_BASE . '/lib/Views/Gallery.php';
+
+/**
+ * Ansel_View_EmbeddedRenderer_Carousel
+ *
+ * $Horde: ansel/lib/Views/EmbeddedRenderers/Carousel.php,v 1.2 2009/01/06 17:48:54 jan Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author  Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_View_EmbeddedRenderer_Carousel extends Ansel_View_Gallery {
+
+    /**
+     * Create a new renderer.
+     *
+     * @see Ansel_View_Embedded
+     *
+     * @param array $params
+     *
+     * @return Ansel_View_EmbeddedRenderer  The renderer object.
+     */
+    function makeView($params = array())
+    {
+        $view = new Ansel_View_EmbeddedRenderer_Carousel();
+        $view->_params = $params;
+
+        return $view;
+    }
+
+    /**
+     * Build the javascript that will render the view.
+     *
+     * @return string  A string containing valid javascript.
+     */
+    function html()
+    {
+
+    }
+
+}
\ No newline at end of file
diff --git a/ansel/lib/Views/EmbeddedRenderers/GalleryLink.php b/ansel/lib/Views/EmbeddedRenderers/GalleryLink.php
new file mode 100644 (file)
index 0000000..998e623
--- /dev/null
@@ -0,0 +1,152 @@
+<?php
+
+/** Ansel_View_Gallery */
+require_once ANSEL_BASE . '/lib/Views/Gallery.php';
+
+/**
+ * Ansel_View_EmbeddedRenderer_GalleryLink
+ *
+ * $Horde: ansel/lib/Views/EmbeddedRenderers/GalleryLink.php,v 1.10 2009/07/30 18:02:15 mrubinsk Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * Example usage:
+ * <pre>
+ *
+ *   <script type="text/javascript" src="http://example.com/horde/services/
+ *   imple.php?imple=Embed/impleApp=ansel/gallery_view=GalleryLink/
+ *   gallery_slug=slug1:slug2:slug3/container=divId/
+ *   thumbsize=prettythumb/style=ansel_polaroid"></script>
+ *   <div id="divId"></div>
+ *   <style type="text/css">#divId .anselGalleryWidget img {border:none;}</style>
+ *
+ *
+ * @author  Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_View_EmbeddedRenderer_GalleryLink extends Ansel_View_Gallery {
+
+    /**
+     * Create a new renderer.
+     *
+     * @see Ansel_View_Embedded
+     *
+     * @param array $params
+     *
+     * @return Ansel_View_EmbeddedRenderer  The renderer object.
+     */
+    function makeView($params = array())
+    {
+        $view = new Ansel_View_EmbeddedRenderer_GalleryLink();
+        $view->_params = $params;
+
+        return $view;
+    }
+
+    /**
+     * Build the javascript that will render the view.
+     *
+     * @return string  A string containing valid javascript.
+     */
+    function html()
+    {
+        /* Read in parameters and set defaults */
+
+        /* Required */
+        $node = $this->_params['container'];
+        if (empty($node)) {
+            return '';
+        }
+
+        /* Need at least one of these */
+        $galleries = !empty($this->_params['gallery_slug']) ? explode(':', $this->_params['gallery_slug']) : '';
+        $haveSlugs = true;
+        if (empty($galleries)) {
+            $galleries = !empty($this->_params['gallery_id']) ? explode(':', $this->_params['gallery_id']) : null;
+            $haveSlugs = false;
+        }
+
+        /* Determine the style/thumnailsize etc... */
+        $thumbsize = empty($this->_params['thumbsize']) ?
+            'thumb' :
+            $this->_params['thumbsize'];
+
+        foreach ($galleries as $identifier) {
+            if ($haveSlugs) {
+                $gallery = $this->getGallery(null, $identifier);
+            } else {
+                $gallery = $this->getGallery($identifier);
+            }
+            if (is_a($gallery, 'PEAR_Error')) {
+                Horde::logMessage($gallery, __FILE__, __LINE__, PEAR_LOG_ERR);
+                exit;
+            }
+            if (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_READ)) {
+                return '';
+            }
+
+            /*If a gallery_style is not specified, default to the gallery's
+             * defined style. Note that this only matters if the $thumbsize
+             * parameter is set to 'prettythumb' anyway.
+             */
+            $gallery_style = empty($this->_params['style']) ?
+                $gallery->get('style') :
+                $this->_params['style'];
+
+            /* Ideally, since gallery default images are unique in that each style
+             * needs it's own unique image_id, the thumbsize and style parameters
+             * are mutually exclusive - specifying a specific gallery style is only
+             * needed if requesting the prettythumb thumbsize value. Make sure that
+             * both were not passed in.
+             */
+            if ($thumbsize == 'thumb') {
+                $images[] = $gallery->getDefaultImage('ansel_default');
+            } else {
+                $images[] = $gallery->getDefaultImage($gallery_style);
+            }
+        }
+        $json = $GLOBALS['ansel_storage']->getImageJson($images, null, true, $thumbsize, true);
+
+        /* Some paths */
+        $cssurl = Horde::url($GLOBALS['registry']->get('themesuri', 'ansel') . '/jsembed.css', true);
+        $js_path = $GLOBALS['registry']->get('jsuri', 'horde');
+        $pturl = Horde::url($js_path . '/prototype.js', true);
+        $ansel_js_path = $GLOBALS['registry']->get('jsuri', 'ansel');
+        $jsurl = Horde::url($ansel_js_path . '/embed.js', true);
+
+        /* Start building the javascript - we use the same parameters as with
+         * the mini gallery view so we can use the same javascript to display it
+         */
+        $html = <<<EOT
+            //<![CDATA[
+            // Old fashioned way to play nice with Safari 2 (Adding script inline with the
+            // DOM won't work).  Need two seperate files output here since the incldued
+            // files don't seem to be parsed until after the entire page is loaded, so we
+            // can't include prototype on the same page it's needed.
+
+            if (typeof anseljson == 'undefined') {
+                if (typeof Prototype == 'undefined') {
+                    document.write('<script type="text/javascript" src="$pturl"></script>');
+                }
+                anselnodes = new Array();
+                anseljson = new Object();
+                document.write('<link type="text/css" rel="stylesheet" href="$cssurl" />');
+                document.write('<script type="text/javascript" src="$jsurl"></script>');
+            }
+            anselnodes[anselnodes.length] = '$node';
+            anseljson['$node'] = new Object();
+            anseljson['$node']['data'] = $json;
+            anseljson['$node']['perpage'] = 0;
+            anseljson['$node']['page'] = 0;
+            anseljson['$node']['hideLinks'] = false;
+            anseljson['$node']['linkToGallery'] = true;
+            //]]>
+EOT;
+
+        return $html;
+    }
+
+}
\ No newline at end of file
diff --git a/ansel/lib/Views/EmbeddedRenderers/Mini.php b/ansel/lib/Views/EmbeddedRenderers/Mini.php
new file mode 100644 (file)
index 0000000..e2227e6
--- /dev/null
@@ -0,0 +1,183 @@
+<?php
+
+/** Ansel_View_Gallery */
+require_once ANSEL_BASE . '/lib/Views/Gallery.php';
+
+/**
+ * Ansel_View_EmbeddedRenderer_Mini
+ *
+ * $Horde: ansel/lib/Views/EmbeddedRenderers/Mini.php,v 1.18 2009/07/08 18:28:45 slusarz Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author  Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_View_EmbeddedRenderer_Mini extends Ansel_View_Gallery {
+
+    /**
+     * Create a new renderer.
+     *
+     * @see Ansel_View_Embedded
+     *
+     * @param array $params
+     *
+     * @return Ansel_View_EmbeddedRenderer  The renderer object.
+     */
+    function makeView($params = array())
+    {
+        $view = new Ansel_View_EmbeddedRenderer_Mini();
+        $view->_params = $params;
+
+        return $view;
+    }
+
+    /**
+     * Build the javascript that will render the view.
+     *
+     * @return string  A string containing valid javascript.
+     */
+    function html()
+    {
+        /* Read in parameters and set defaults */
+
+        /* Required */
+        $node = $this->_params['container'];
+        if (empty($node)) {
+            return '';
+        }
+
+        /* Optional */
+        $gallery_slug = !empty($this->_params['gallery_slug']) ? $this->_params['gallery_slug'] : '';
+        $gallery_id = !empty($this->_params['gallery_id']) ? $this->_params['gallery_id']: null;
+
+        $start = (isset($this->_params['start'])) ? $this->_params['start'] : 0;
+        $count = (isset($this->_params['count'])) ? $this->_params['count'] : 0;
+        $perpage = (isset($this->_params['perpage'])) ? $this->_params['perpage'] : 0;
+        $thumbsize = (!empty($this->_params['thumbsize'])) ? $this->_params['thumbsize'] : 'mini';
+        // Limit to these image views only.
+        if ($thumbsize != 'mini' && $thumbsize != 'thumb' &&
+            $thumbsize != 'prettythumb' && $thumbsize != 'screen') {
+
+             $thumbsize = 'mini';
+        }
+
+        /* An image list instead of a gallery? */
+        $images = (!empty($this->_params['images'])) ? $this->_params['images'] : array();
+        if (!empty($images)) {
+            // Images are filtered for age and password protected galleries
+            // in the ::getImageJson() call since they could all be from different
+            // galleries.
+            $images = explode(':', $images);
+        } else {
+            $this->gallery = $this->getGallery($gallery_id, $gallery_slug);
+            if (is_a($this->gallery, 'PEAR_Error')) {
+                Horde::logMessage($this->gallery, __FILE__, __LINE__, PEAR_LOG_ERR);
+                exit;
+            }
+
+            // We don't allow age restricted or password locked galleries to be
+            // viewed via the mini embedded view since it shows *all* the images
+            if (!$this->gallery->hasPermission(Horde_Auth::getAuth(), PERMS_READ) ||
+                !$this->gallery->isOldEnough() ||
+                $this->gallery->hasPasswd()) {
+
+                return '';
+            }
+        }
+
+        if (empty($images)) {
+            $json = $this->json(null, true, $start, $count, $thumbsize, true);
+            $json_full = $this->json(null, true, $start, $count, 'screen', true);
+        } else {
+            $json = $GLOBALS['ansel_storage']->getImageJson($images, null, true, $thumbsize, true);
+            $json_full = $GLOBALS['ansel_storage']->getImageJson($images, null, true, 'screen', true);
+        }
+
+        /* Some paths */
+        $cssurl = Horde::url($GLOBALS['registry']->get('themesuri', 'ansel') . '/embed.css', true);
+        $hcssurl = Horde::url($GLOBALS['registry']->get('themesuri', 'horde') . '/embed.css', true);
+        $js_path = $GLOBALS['registry']->get('jsuri', 'horde');
+        $pturl = Horde::url($js_path . '/prototype.js', true);
+        $hjsurl = Horde::url($js_path . '/tooltips.js', true);
+        $ansel_js_path = $GLOBALS['registry']->get('jsuri', 'ansel');
+        $jsurl = Horde::url($ansel_js_path . '/embed.js', true);
+        $hideLinks = (bool)!empty($this->_params['hidelinks']);
+
+        /* Lightbox specific URLs */
+        if (!empty($this->_params['lightbox'])) {
+            $effectsurl = Horde::url($js_path . '/effects.js', true);
+            $lbjsurl = Horde::url($ansel_js_path . '/lightbox.js', true);
+            $lbcssurl = Horde::url($GLOBALS['registry']->get('themesuri', 'ansel') . '/lightbox.css', true);
+        }
+
+        /* Start building the javascript */
+        $html = <<<EOT
+            //<![CDATA[
+            // Old fashioned way to play nice with Safari 2 (Adding script inline with the
+            // DOM won't work).  Need two seperate files output here since the incldued
+            // files don't seem to be parsed until after the entire page is loaded, so we
+            // can't include prototype on the same page it's needed.
+
+            if (typeof anseljson == 'undefined') {
+                if (typeof Prototype == 'undefined') {
+                    document.write('<script type="text/javascript" src="$pturl"></script>');
+                }
+                if (typeof Horde_ToolTips == 'undefined') {
+                    document.write('<script type="text/javascript" src="$hjsurl"></script>');
+                    document.write('<link type="text/css" rel="stylesheet" href="$hcssurl" />');
+                }
+
+                anselnodes = new Array();
+                anseljson = new Object();
+                document.write('<link type="text/css" rel="stylesheet" href="$cssurl" />');
+                document.write('<script type="text/javascript" src="$jsurl"></script>');
+            }
+            anselnodes[anselnodes.length] = '$node';
+            anseljson['$node'] = new Object();
+            anseljson['$node']['data'] = $json;
+            anseljson['$node']['perpage'] = $perpage;
+            anseljson['$node']['page'] = 0;
+            anseljson['$node']['hideLinks'] = '$hideLinks';
+            //]]>
+
+EOT;
+            /* Special requirements for lightbox */
+            if (!empty($lbjsurl)) {
+                $graphic_dir = Horde::applicationUrl($GLOBALS['registry']->getImageDir(), true, -1);
+                $imageText = _("Photo");
+                $labelOf = _("of");
+                $html .= <<<EOT
+                if (typeof Effect == 'undefined') {
+                    document.write('<script type="text/javascript" src="$effectsurl"></script>');
+                }
+
+                /* Make sure we only include this stuff once */
+                if (typeof lbOptions == 'undefined') {
+
+                    document.write('<script type="text/javascript" src="$lbjsurl"></script>');
+                    document.write('<link type="text/css" rel="stylesheet" href="$lbcssurl" />');
+
+                    lbOptions = {
+                        fileLoadingImage: '$graphic_dir/lightbox/loading.gif',
+                        fileBottomNavCloseImage: '$graphic_dir/lightbox/closelabel.gif',
+                        overlayOpacity: 0.8,
+                        animate: true,
+                        resizeSpeed: 7,
+                        borderSize: 10,
+                        labelImage: '$imageText',
+                        labelOf: '$labelOf',
+                        returnURL: '#',
+                        startPage: 0
+                    }
+                }
+                anseljson['$node']['lightbox'] = $json_full;
+EOT;
+        }
+        return $html;
+    }
+
+}
diff --git a/ansel/lib/Views/EmbeddedRenderers/Slideshow.php b/ansel/lib/Views/EmbeddedRenderers/Slideshow.php
new file mode 100644 (file)
index 0000000..e2fa5cb
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+
+/** Ansel_View_Gallery **/
+require_once ANSEL_BASE . '/lib/Views/Gallery.php';
+
+/**
+ * Ansel_View_EmbeddedRenderer_Slideshow
+ *
+ * $Horde: ansel/lib/Views/EmbeddedRenderers/Slideshow.php,v 1.2 2009/01/06 17:48:54 jan Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author  Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_View_EmbeddedRenderer_Slideshow extends Ansel_View_Gallery {
+
+    /**
+     * Create a new renderer.
+     *
+     * @see Ansel_View_Embedded
+     *
+     * @param array $params
+     *
+     * @return Ansel_View_EmbeddedRenderer  The renderer object.
+     */
+    function makeView($params = array())
+    {
+        $view = new Ansel_View_EmbeddedRenderer_Carousel();
+        $view->_params = $params;
+
+        return $view;
+    }
+
+    /**
+     * Build the javascript that will render the view.
+     *
+     * @return string  A string containing valid javascript.
+     */
+    function html()
+    {
+
+    }
+
+}
\ No newline at end of file
diff --git a/ansel/lib/Views/Gallery.php b/ansel/lib/Views/Gallery.php
new file mode 100644 (file)
index 0000000..f3e57f1
--- /dev/null
@@ -0,0 +1,211 @@
+<?php
+/**
+ * @package Ansel
+ *
+ * $Horde: ansel/lib/Views/Gallery.php,v 1.130 2009/07/08 18:28:45 slusarz Exp $
+ */
+
+/** Ansel_View_Abstract */
+require_once ANSEL_BASE . '/lib/Views/Abstract.php';
+
+/** Tags **/
+require_once ANSEL_BASE . '/lib/Tags.php';
+
+/** Horde_UI_Pager */
+require_once 'Horde/UI/Pager.php';
+
+/**
+ * The Ansel_View_Gallery:: class wraps display of individual images.
+ *
+ * @author  Chuck Hagenbuch <chuck@horde.org>
+ * @package Ansel
+ */
+class Ansel_View_Gallery extends Ansel_View_Abstract {
+
+    /** Holds the object that does the actual rendering **/
+    var $_renderer;
+
+    /**
+     * @static
+     *
+     * @param array $params  Any parameters that the view might need.
+     * <pre>
+     * gallery_id              The gallery id this view is for. If omitted, it
+     *                         looks for a query parameter called 'gallery'
+     *
+     * gallery_slug            Same as above, but a slug
+     *
+     * gallery_view_url        If set, this is used as the link to a gallery
+     *                         view. %g is replaced by the gallery_id and %s is
+     *                         replaced by the gallery_slug.
+     *
+     * gallery_view            The specific Renderer to use, if needed.
+     *                         (GalleryLightbox, Gallery etc...).
+     *
+     * image_view_url          If this is set, the image tiles will use this url
+     *                         for the image view link. %i and %g will be
+     *                         replaced by image_id and gallery_id respectively.
+     *                         %s will be replaced by the gallery_slug
+     *
+     * image_view_src          If this is set to true, the image view link will go
+     *                         directly to the actual image. This overrides any
+     *                         setting of image_view_url.
+     *
+     * image_view_attributes   An optional array of attribute => value pairs
+     *                         that are used as attributes of the image view
+     *                         link.
+     *
+     * image_view_title        Specifies which property of the image object
+     *                         to use as the image caption.
+     *
+     * image_onclick           Specifies a onclick handler for the image tile
+     *                         links.
+     *
+     * style                   Force the use of this named style.
+     *
+     * api                     If set, we are being called from the external api
+     *
+     * page                    The gallery page number to display if not the
+     *                         default value of the first page (page = 0)
+     *
+     * day, month, year        Numeric date part values to describe the gallery
+     *                         date grouping to view in date mode.
+     *
+     * force_date_grouping     Do not auto navigate to the first date grouping
+     *                         with more then one resource. Used from the api
+     *                         when clicking on breadcrumb links, for example.
+     * </pre>
+     *
+     * @TODO use exceptions from the constructor instead of static
+     * instance-getting.
+     */
+    function makeView($params = array())
+    {
+        $view = new Ansel_View_Gallery();
+
+        if (count($params)) {
+            $view->_params = $params;
+        }
+
+        if (!empty($params['gallery_slug'])) {
+            $view->gallery = $view->getGallery(null, $params['gallery_slug']);
+        } elseif (!empty($params['gallery_id'])) {
+            $view->gallery = $view->getGallery($params['gallery_id']);
+        } else {
+            $view->gallery = $view->getGallery();
+        }
+
+        if (is_a($view->gallery, 'PEAR_Error')) {
+            return $view->gallery;
+        }
+
+        // Check user age
+        if (!$view->gallery->isOldEnough()) {
+            if (!empty($params['api'])) {
+                return PEAR::raiseError(_("Locked galleries are not viewable via the api."));
+            }
+            $date = Ansel::getDateParameter(
+                array('year' => isset($view->_params['year']) ? $view->_params['year'] : 0,
+                      'month' => isset($view->_params['month']) ? $view->_params['month'] : 0,
+                      'day' => isset($view->_params['day']) ? $view->_params['day'] : 0));
+
+                $galleryurl = Ansel::getUrlFor('view', array_merge(
+                                   array('gallery' => $view->gallery->id,
+                                         'slug' => empty($params['slug']) ? '' : $params['slug'],
+                                         'page' => empty($params['page']) ? 0 : $params['page'],
+                                         'view' => 'Gallery'),
+                                   $date),
+                                   true);
+
+            $params = array('gallery' => $view->gallery->id, 'url' => $galleryurl);
+            header('Location: ' . Horde_Util::addParameter(Horde::applicationUrl('disclamer.php'), $params, null, false));
+            exit;
+        }
+
+        if ($view->gallery->hasPasswd()) {
+            if (!empty($params['api'])) {
+                return PEAR::raiseError(_("Locked galleries are not viewable via the api."));
+            }
+            $date = Ansel::getDateParameter(
+                array('year' => isset($view->_params['year']) ? $view->_params['year'] : 0,
+                      'month' => isset($view->_params['month']) ? $view->_params['month'] : 0,
+                      'day' => isset($view->_params['day']) ? $view->_params['day'] : 0));
+
+                $galleryurl = Ansel::getUrlFor('view', array_merge(
+                                   array('gallery' => $view->gallery->id,
+                                         'slug' => empty($params['slug']) ? '' : $params['slug'],
+                                         'page' => empty($params['page']) ? 0 : $params['page'],
+                                         'view' => 'Gallery'),
+                                   $date),
+                                   true);
+
+            $params = array('gallery' => $view->gallery->id, 'url' => $galleryurl);
+            header('Location: ' . Horde_Util::addParameter(Horde::applicationUrl('protect.php'), $params, null, false));
+            exit;
+        }
+
+        if (!$view->gallery->hasPermission(Horde_Auth::getAuth(), PERMS_READ)) {
+            return PEAR::raiseError(_("Access denied viewing this gallery."));
+        }
+
+        // Since this is a gallery view, the resource is just a reference to the
+        // gallery. We keep both instance variables becuase both gallery and
+        // image views are assumed to have a gallery object.
+        $view->resource = &$view->gallery;
+
+        /* Do we have an explicit style set? If not, use the gallery's */
+        if (!empty($view->_params['style'])) {
+            $style = Ansel::getStyleDefinition($view->_params['style']);
+        } else {
+            $style = $view->gallery->getStyle();
+        }
+
+        if (!empty($view->_params['gallery_view'])) {
+            $renderer = $view->_params['gallery_view'];
+        } else {
+            $renderer = (!empty($style['gallery_view'])) ? $style['gallery_view'] : 'Gallery';
+        }
+        /* Load the helper */
+        $classname = 'Ansel_View_GalleryRenderer_' . basename($renderer);
+        require_once dirname(__FILE__) . '/GalleryRenderers/' . basename($renderer) . '.php';
+        $view->_renderer = new $classname($view);
+        $view->_renderer->init();
+
+        return $view;
+    }
+
+    function getGalleryCrumbData()
+    {
+        return $this->gallery->getGalleryCrumbData();
+    }
+
+    /**
+     * Get this gallery's title.
+     *
+     * @return string  The gallery's title.
+     */
+    function getTitle()
+    {
+        if (is_a($this->gallery, 'PEAR_Error')) {
+            return $this->gallery->getMessage();
+        }
+        return $this->gallery->get('name');
+    }
+
+    /**
+     * Return the HTML representing this view.
+     *
+     * @return string  The HTML.
+     *
+     */
+    function html()
+    {
+        return $this->_renderer->html();
+    }
+
+    function viewType()
+    {
+        return 'Gallery';
+    }
+
+}
diff --git a/ansel/lib/Views/GalleryRenderer.php b/ansel/lib/Views/GalleryRenderer.php
new file mode 100644 (file)
index 0000000..a290309
--- /dev/null
@@ -0,0 +1,223 @@
+<?php
+/**
+ * Ansel_View_GalleryRenderer::  Base class for all gallery renderers.
+ *
+ * $Horde: ansel/lib/Views/GalleryRenderer.php,v 1.14 2009/07/13 14:29:05 mrubinsk Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author  Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_View_GalleryRenderer {
+
+    /**
+     * The Ansel_View_Gallery object that this Renderer belongs to.
+     *
+     * @var Ansel_View_Gallery
+     */
+    var $view;
+
+    /**
+     * The gallery id for this view's gallery
+     *
+     * @var integer
+     */
+    var $galleryId;
+
+    /**
+     * Gallery slug for current gallery.
+     *
+     * @var string
+     */
+    var $gallerySlug;
+
+    /**
+     * The current page we are viewing
+     *
+     * @var integer
+     */
+    var $page = 0;
+
+    /**
+     * The display mode of the current gallery.
+     * 0 == Normal
+     * 1 == Group by date
+     *
+     * @var integer
+     */
+    var $mode;
+
+    /**
+     * The style definition array for this gallery.
+     *
+     * @var array
+     */
+    var $style;
+
+    /**
+     * Holds number of tiles to display per page
+     *
+     * @var integer
+     */
+    var $perpage;
+
+    /**
+     * The tile number we are starting with on the current page.
+     *
+     * @var integer
+     */
+    var $pagestart;
+
+    /**
+     * The last tile number on the current page.
+     *
+     * @var integer
+     */
+    var $pageend;
+
+    /**
+     * The total number of tiles that this view contains
+     *
+     * @var integer
+     */
+    var $numTiles;
+
+    /**
+     * The Ansel_Image or Ansel_DateGallery objects that appear on the current
+     * page in the current view.
+     *
+     * @var array of Ansel_Image or Ansel_DateGallery objects.
+     */
+    var $children;
+
+    /**
+     * If we are grouping by date, this holds the currently selected date parts.
+     *
+     * @var array containing sufficient date parts for the current depth.
+     */
+    var $date = array();
+
+    /**
+     * Constructor
+     *
+     * @param Ansel_View_Gallery  The view object for this renderer.
+     *
+     * @return Ansel_View_Renderer_Gallery
+     */
+    function Ansel_View_GalleryRenderer($view)
+    {
+        $this->view = $view;
+    }
+
+    /**
+     * Initialize the renderer. This *must* be called before any attempt is made
+     * to display or otherwise interact with the renderer.
+     *
+     */
+    function init()
+    {
+        global $prefs, $conf;
+
+        $this->galleryId = $this->view->gallery->id;
+        $this->gallerySlug = $this->view->gallery->get('slug');
+        if (isset($this->view->_params['page'])) {
+            $this->page = $this->view->_params['page'];
+        }
+
+        /* Number perpage from prefs or config */
+        $this->perpage = min($prefs->getValue('tilesperpage'),
+                             $conf['thumbnail']['perpage']);
+
+        /* Calculate the starting and ending images on this page */
+        $this->pagestart = ($this->page * $this->perpage) + 1;
+
+        /* Fetch the children */
+        $this->fetchChildren($this->view->_params['force_grouping']);
+
+        /* Do we have an explicit style set? If not, use the gallery's */
+        if (!empty($this->view->_params['style'])) {
+            $this->style = Ansel::getStyleDefinition($this->view->_params['style']);
+        } else {
+            $this->style = $this->view->gallery->getStyle();
+        }
+
+        /* Include any widgets */
+        if (!empty($this->style['widgets'])) {
+            require_once ANSEL_BASE . '/lib/Widget.php';
+
+            /* Special case widgets - these are built in */
+            if (array_key_exists('Actions', $this->style['widgets'])) {
+                /* Don't show action widget if no actions */
+                if (Horde_Auth::getAuth() ||
+                    !empty($conf['report_content']['driver']) &&
+                    (($conf['report_content']['allow'] == 'authenticated' && Horde_Auth::isAuthenticated()) ||
+                     $conf['report_content']['allow'] == 'all')) {
+
+                    $this->view->addWidget(Ansel_Widget::factory('Actions'));
+                }
+                unset($this->style['widgets']['Actions']);
+            }
+
+            // I *think* this is more efficient, iterate over the children
+            // since we already have them instead of calling listImages.
+            //$image_ids = $this->view->gallery->listImages($this->pagestart, $this->pagestart + $this->perpage);
+            $ids = array();
+            foreach ($this->children as $child) {
+                if (is_a($child, 'Ansel_Image')) {
+                    $ids[] = $child->id;
+                }
+            }
+            // Gallery widgets always receive an array of image ids for
+            // the current page.
+            foreach ($this->style['widgets'] as $wname => $wparams) {
+                $wparams = array_merge($wparams, array('images' => $ids));
+                $this->view->addWidget(Ansel_Widget::factory($wname, $wparams));
+            }
+        }
+
+        /* See if any renderer specific tasks need to be done as well */
+        $this->_init();
+    }
+
+    /**
+     * Default implementation for fetching children/images for this view.
+     * Other view classes can override this if they need anything special.
+     *
+     */
+    function fetchChildren($noauto)
+    {
+        /* Total number of tiles for this gallery view */
+        $this->numTiles = $this->view->gallery->countGalleryChildren(PERMS_SHOW, false, $noauto);
+
+        /* Children to display on this page */
+        $this->children = $this->view->gallery->getGalleryChildren(
+            PERMS_SHOW,
+            $this->page * $this->perpage,
+            $this->perpage,
+            !empty($this->view->_params['force_grouping']));
+
+        /* The last tile number to display on the current page */
+        $this->pageend = min($this->numTiles, $this->pagestart + $this->perpage - 1);
+    }
+
+    /**
+     * Return the HTML for this view. Done this way so we can override this in
+     * subclasses if desired.
+     *
+     * @return string
+     */
+    function html()
+    {
+        if (is_a($this->view->gallery, 'PEAR_Error')) {
+            echo htmlspecialchars($this->view->gallery->getMessage(), ENT_COMPAT, Horde_Nls::getCharset());
+            return;
+        }
+
+        return $this->_html();
+    }
+
+}
diff --git a/ansel/lib/Views/GalleryRenderers/Gallery.php b/ansel/lib/Views/GalleryRenderers/Gallery.php
new file mode 100644 (file)
index 0000000..54fe20a
--- /dev/null
@@ -0,0 +1,133 @@
+<?php
+/**
+ * Ansel_View_GalleryRenderer_Gallery:: Class wraps display of the traditional
+ * Gallery View.
+ *
+ * $Horde: ansel/lib/Views/GalleryRenderers/Gallery.php,v 1.29 2009/07/08 18:28:45 slusarz Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author  Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+require_once ANSEL_BASE . '/lib/Views/GalleryRenderer.php';
+
+class Ansel_View_GalleryRenderer_Gallery extends Ansel_View_GalleryRenderer {
+
+    /**
+     * Perform any tasks that should be performed before the view is rendered.
+     *
+     */
+    function _init()
+    {
+    }
+
+    /**
+     * Return the HTML representing this view.
+     *
+     * @return string  The HTML.
+     *
+     */
+    function _html()
+    {
+        global $conf, $prefs, $registry;
+
+        $galleryOwner = $this->view->gallery->get('owner');
+        $id = $this->view->gallery->getOwner();
+        $owner = $id->getValue('fullname');
+        if (!$owner) {
+            $owner = $galleryOwner;
+        }
+
+        /* Only need these if not being called via the api */
+        if (empty($this->view->_params['api'])) {
+            $option_edit = $this->view->gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT);
+            $option_select = $option_delete = $this->view->gallery->hasPermission(Horde_Auth::getAuth(), PERMS_DELETE);
+            $option_move = ($option_delete && $GLOBALS['ansel_storage']->countGalleries(PERMS_EDIT));
+            $option_copy = ($option_edit && $GLOBALS['ansel_storage']->countGalleries(PERMS_EDIT));
+            /* See if we requested a show_actions change */
+            if (Horde_Util::getFormData('actionID', '') == 'show_actions') {
+                $prefs->setValue('show_actions', (int)!$prefs->getValue('show_actions'));
+            }
+        }
+
+        /* Set up the pager */
+        $date_params = Ansel::getDateParameter(
+            array('year' => isset($this->view->_params['year']) ? $this->view->_params['year'] : 0,
+                  'month' => isset($this->view->_params['month']) ? $this->view->_params['month'] : 0,
+                  'day' => isset($this->view->_params['day']) ? $this->view->_params['day'] : 0));
+
+        $vars = Horde_Variables::getDefaultVariables();
+        if (!empty($this->view->_params['page'])) {
+            $vars->add('page', $this->view->_params['page']);
+        }
+        if (!empty($this->view->_params['gallery_view_url'])) {
+            $pagerurl = str_replace(array('%g', '%s'), array($this->galleryId, $this->gallerySlug), urldecode($this->view->_params['gallery_view_url']));
+            $pagerurl = Horde_Util::addParameter($pagerurl, $date_params);
+        } else {
+            /*
+             * Build the pager url. Add the needed variables directly to the
+             * url instead of passing it as a preserved variable to the pager
+             * since the logic to build the URL is already in getUrlFor()
+             */
+            $pager_params =  array_merge(
+                array('gallery' => $this->galleryId,
+                      'view' => 'Gallery',
+                      'slug' => $this->view->gallery->get('slug')),
+                $date_params);
+            $pagerurl = Ansel::getUrlfor('view', $pager_params, true);
+        }
+
+        /* See what callback to use to tweak the pager urls */
+        if (!empty($this->view->_params['urlCallback'])) {
+            $callback = $this->view->_params['urlCallback'];
+        } else {
+            $callback = null;
+        }
+        $params = array('num' => $this->numTiles,
+                        'url' => $pagerurl,
+                        'perpage' => $this->perpage,
+                        'url_callback' => $callback);
+
+        $pager = new Horde_UI_Pager('page', $vars, $params);
+
+        // Note that we can't use Horde_Util::bufferOutput() here since the include
+        // file would be included inside that method's scope, and not this one.
+        ob_start();
+        if (!empty($this->view->_params['api'])) {
+            $includes = new Horde_Script_Files();
+            $includes->disableAutoloadHordeJS();
+            $includes->_add('prototype.js', 'horde', true, true);
+            $includes->includeFiles();
+        }
+
+        /* Needed in the template files */
+        $tilesperrow = $prefs->getValue('tilesperrow');
+        $cellwidth = round(100 / $tilesperrow);
+        $count = 0;
+        $action_links = array();
+        if ($GLOBALS['conf']['gallery']['downloadzip']) {
+            $action_links[] = Horde::link('#', '', 'widget', '', 'downloadSelected(); return false;') . _("Download selected images") . '</a>';
+
+        }
+        if (!empty($option_edit)) {
+            $action_links[] = Horde::link('#', '', 'widget', '', 'editDates(); return false;') . _("Edit Dates") . '</a>';
+        }
+        if (!empty($option_delete)) {
+            $action_links[] = Horde::link('#', '', 'widget', '', 'deleteSelected(); return false;') . _("Delete") . '</a>';
+        }
+        if (!empty($option_move)) {
+            $action_links[] = Horde::link('#', '', 'widget', '', 'moveSelected(); return false;') . _("Move") . '</a>';
+        }
+        if (!empty($option_copy)) {
+            $action_links[] = Horde::link('#', '', 'widget', '', 'copySelected(); return false;') . _("Copy") . '</a>';
+        }
+
+        include ANSEL_TEMPLATES . '/view/gallery.inc';
+        return ob_get_clean();
+    }
+
+}
diff --git a/ansel/lib/Views/GalleryRenderers/GalleryLightbox.php b/ansel/lib/Views/GalleryRenderers/GalleryLightbox.php
new file mode 100644 (file)
index 0000000..330c3e2
--- /dev/null
@@ -0,0 +1,160 @@
+<?php
+/**
+ * Ansel_View_GalleryRenderer_GalleryLightbox:: Class wraps display of the lightbox
+ * style gallery views.
+ *
+ * $Horde: ansel/lib/Views/GalleryRenderers/GalleryLightbox.php,v 1.33 2009/07/08 18:28:45 slusarz Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author  Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+
+require_once ANSEL_BASE . '/lib/Views/GalleryRenderer.php';
+
+class Ansel_View_GalleryRenderer_GalleryLightbox extends Ansel_View_GalleryRenderer {
+
+    /**
+     * Perform any tasks that should be performed before the view is rendered.
+     *
+     */
+    function _init()
+    {
+        if (empty($this->view->_params['image_onclick'])) {
+            $this->view->_params['image_onclick'] = 'return lb.start(%i);';
+        }
+
+        // Attach the script and CSS files here if we aren't being called via the api
+        if (empty($this->view->_params['api'])) {
+            Ansel::attachStylesheet('lightbox.css');
+            Horde::addScriptFile('prototype.js', 'horde', true);
+            Horde::addScriptFile('effects.js', 'horde', true);
+            Horde::addScriptFile('lightbox.js', 'ansel', true);
+        }
+    }
+
+    /**
+     * Get the HTML representing this view.
+     *
+     * @return string The HTML
+     */
+    function _html()
+    {
+        global $conf, $prefs, $registry;
+
+        $galleryOwner = $this->view->gallery->get('owner');
+        $id = $this->view->gallery->getOwner();
+        $owner = $id->getValue('fullname');
+        if (!$owner) {
+            $owner = $galleryOwner;
+        }
+
+        /* Get JSON data for view */
+        // 0 == normal, 1 == by date
+        if ($this->mode == 0) {
+            $json = $this->view->json(null, !empty($this->view->_params['api']));
+        } else {
+            if (!empty($this->date['day']) && $this->numTiles) {
+                $json = $this->view->json(null, !empty($this->view->_params['api']));
+            } else {
+                $json = '[]';
+            }
+        }
+
+        /* Don't bother if we are being called from the api */
+        if (empty($this->view->_params['api'])) {
+            $option_edit = $this->view->gallery->hasPermission(Horde_Auth::getAuth(),
+                                                         PERMS_EDIT);
+            $option_select = $option_delete = $this->view->gallery->hasPermission(
+                Horde_Auth::getAuth(), PERMS_DELETE);
+            $option_move = ($option_delete && $GLOBALS['ansel_storage']->countGalleries(PERMS_EDIT));
+            $option_copy = ($option_edit && $GLOBALS['ansel_storage']->countGalleries(PERMS_EDIT));
+            /* See if we requested a show_actions change (fallback for non-js) */
+            if (Horde_Util::getFormData('actionID', '') == 'show_actions') {
+                $prefs->setValue('show_actions', (int)!$prefs->getValue('show_actions'));
+            }
+        }
+
+        /* Set up the pager */
+        $date_params = Ansel::getDateParameter(
+            array('year' => isset($this->view->_params['year']) ? $this->view->_params['year'] : 0,
+                  'month' => isset($this->view->_params['month']) ? $this->view->_params['month'] : 0,
+                  'day' => isset($this->view->_params['day']) ? $this->view->_params['day'] : 0));
+
+        $vars = Horde_Variables::getDefaultVariables();
+        if (!empty($this->view->_params['page'])) {
+            $vars->add('page', $this->view->_params['page']);
+            $page = $this->view->_params['page'];
+        } else {
+            $page = 0;
+        }
+        if (!empty($this->view->_params['gallery_view_url'])) {
+            $pagerurl = str_replace(array('%g', '%s'), array($this->galleryId, $this->gallerySlug), urldecode($this->view->_params['gallery_view_url']));
+            $pagerurl = Horde_Util::addParameter($pagerurl, $date_params, null, false);
+        } else {
+            /*
+             * Build the pager url. Add the needed variables directly to the
+             * url instead of passing it as a preserved variable to the pager
+             * since the logic to build the URL is already in getUrlFor()
+             */
+            $pager_params =  array_merge(
+                array('gallery' => $this->galleryId,
+                      'view' => 'Gallery',
+                      'slug' => $this->view->gallery->get('slug')),
+                $date_params);
+            $pagerurl = Ansel::getUrlfor('view', $pager_params, true);
+        }
+
+        if (!empty($this->view->_params['urlCallback'])) {
+            $callback = $this->view->_params['urlCallback'];
+        } else {
+            $callback = null;
+        }
+        $params = array('num' => $this->numTiles,
+                        'url' => $pagerurl,
+                        'perpage' => $this->perpage,
+                        'url_callback' => $callback);
+
+        $pager = new Horde_UI_Pager('page', $vars, $params);
+
+        /* Start buffering */
+        ob_start();
+
+        /* Create the js variables to pass to the lightbox script */
+        $jsvars = array('graphics_dir' => Horde::applicationUrl($registry->getImageDir(), true, -1),
+                        'image_text' => _("Photo"),
+                        'of_text' => _("of"),
+                        'start_page' => $page);
+
+        $flipped = array_flip($date_params);
+        if (count($flipped) == 1 && !empty($flipped[0])) {
+            $jsvars['gallery_url'] = $pagerurl . '?';
+        } else {
+            $jsvars['gallery_url'] = $pagerurl . '&';
+        }
+        /* Output js/css here if we are calling via the api */
+        if (!empty($this->view->_params['api'])) {
+            Ansel::attachStylesheet('lightbox.css', true);
+            $includes = new Horde_Script_Files();
+            $includes->disableAutoloadHordeJS();
+            $includes->_add('prototype.js', 'horde', true, true);
+            $includes->_add('accesskeys.js', 'horde', true, true);
+            $includes->_add('effects.js', 'horde', true, true);
+            $includes->_add('lightbox.js', 'ansel', true, true);
+            $includes->includeFiles();
+        }
+
+        /* Needed in the template files */
+        $tilesperrow = $prefs->getValue('tilesperrow');
+        $cellwidth = round(100 / $tilesperrow);
+        $count = 0;
+
+        include ANSEL_TEMPLATES . '/view/gallerylightbox.inc';
+        return ob_get_clean();
+    }
+
+}
diff --git a/ansel/lib/Views/GalleryRenderers/GalleryVimeo.php b/ansel/lib/Views/GalleryRenderers/GalleryVimeo.php
new file mode 100644 (file)
index 0000000..87d9520
--- /dev/null
@@ -0,0 +1,196 @@
+<?php
+/**
+ * Ansel_View_GalleryRenderer_GalleryVimeo:: An example of extending Ansel by
+ * adding a new gallery style. This fetches a list of videos from the Vimeo
+ * video service, and displays them as a gallery. The videos are viewed in a
+ * redbox overlay when the thumbnails are clicked.
+ *
+ * $Horde: $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author  Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+require_once ANSEL_BASE . '/lib/Views/GalleryRenderer.php';
+
+class Ansel_View_GalleryRenderer_GalleryVimeo extends Ansel_View_GalleryRenderer {
+    /**
+     *
+     * @var Horde_Service_Vimeo object
+     */
+    var $_vimeo;
+    var $_thumbs;
+
+    /**
+     * Perform any tasks that should be performed before the view is rendered.
+     *
+     */
+    function _init()
+    {
+        // Attach the script and CSS files here if we aren't being called via the api
+        if (empty($this->view->_params['api'])) {
+            Horde::addScriptFile('prototype.js', 'horde', true);
+            Horde::addScriptFile('effects.js', 'horde', true);
+            Horde::addScriptFile('redbox.js', 'horde', true);
+        }
+    }
+
+    /**
+     * Override the parent class' fetchChildren method so we can grab the video
+     * thumbnail information from Vimeo instead of from our local image storage.
+     *
+     * @param boolean $noauto  Ignored in this class since we won't be doing any
+     *                         date browsing.  Maybe another experiment? ;)
+     */
+    function fetchChildren($noauto = true)
+    {
+        // Build a Horde_Service_Vimeo object
+        // It *requires* a http client object and can make use of a cache object,
+        // so let's take advantage of it.
+        $params = array('http_client' => new Horde_Http_Client(),
+                        'cache' => $GLOBALS['cache'],
+                        'cache_lifetime' => $GLOBALS['conf']['cache']['default_lifetime']);
+
+        $this->_vimeo = Horde_Service_Vimeo::factory('Simple', $params);
+
+        // The identifier for what we are requesting.
+        // If we are requesting a user's videos, this is the user's vimeo_id
+        // if we want to request a particular group, this would be the group_id
+        // etc...
+        //
+        // For this example, the id is hard coded here, but if I were to implement
+        // this on a live site I would add a new user pref to ansel for the
+        // user to enter his/her own vimeo_id and then grab the value from
+        // pref storage here.
+        $vimeo_id = 'user1015172'; //TODO: Get this from prefs?
+
+        // This gets the data representing the videos. See the API docs for
+        // exactly what is returned, but for our purposes, we will be using:
+        // clip_id, url, caption, thumbnail_large etc...
+        $thumbs = unserialize($this->_vimeo->user($vimeo_id)->clips()->run());
+
+        // We fetch the information needed to embed each video now to make things
+        // easier for this example...the cache helps tremendously with load times
+        // after the first page is requested.
+        foreach ($thumbs as $thumb) {
+            $this->_json[$thumb['clip_id']]  = $this->_vimeo->getEmbedJSON(array('url' => $thumb['url'], 'byline' => 'false', 'portrait' => 'false'));
+            $this->_thumbs[$thumb['clip_id']] = $thumb;
+        }
+
+        // Vimeo's Simple API doesn't provide for paging - so we emulate it
+        // by only returning the video thumbnails that should appear on this
+        // current gallery page.  Like stated above, the first load will take
+        // a bit of time depending on the number of videos the user has - but
+        // each subsequent page will load *much* faster as we don't have to
+        // contact Vimeo at all.
+
+        // Total number of thumbnails in the gallery
+        $this->numTiles = count($thumbs);
+
+        // The last one to display on this page
+        $this->pageend = min($this->numTiles, $this->pagestart + $this->perpage - 1);
+
+
+       $this->children = $this->view->gallery->getGalleryChildren(
+            PERMS_SHOW,
+            $this->page * $this->perpage,
+            $this->perpage,
+            !empty($this->view->_params['force_grouping']));
+    }
+
+    /**
+     * Get the HTML representing this view.
+     *
+     * Responsible for building the HTML for the view. It's stripped down
+     * somewhat from the other styles...sets up the variables needed for the
+     * template we put in ansel/templates/view - though there is really no
+     * reason we *have* to have a template file there if we can generate the
+     * entire HTML here, or load a template from this directory or....?
+     *
+     * @return string The HTML
+     */
+    function _html()
+    {
+        global $conf, $prefs, $registry;
+
+        // Deal with getting the correct gallery owner string, get any
+        // parameters we are interested in from the view
+        $galleryOwner = $this->view->gallery->get('owner');
+        $id = $this->view->gallery->getOwner();
+        $owner = $id->getValue('fullname');
+        if (!$owner) {
+            $owner = $galleryOwner;
+        }
+        $vars = Horde_Variables::getDefaultVariables();
+        if (!empty($this->view->_params['page'])) {
+            $vars->add('page', $this->view->_params['page']);
+            $page = $this->view->_params['page'];
+        } else {
+            $page = 0;
+        }
+
+        // Build the proper pager urls
+        if (!empty($this->view->_params['gallery_view_url'])) {
+            $pagerurl = str_replace(array('%g', '%s'), array($this->galleryId, $this->gallerySlug), urldecode($this->view->_params['gallery_view_url']));
+        } else {
+            /*
+             * Build the pager url. Add the needed variables directly to the
+             * url instead of passing it as a preserved variable to the pager
+             * since the logic to build the URL is already in getUrlFor()
+             */
+            $pager_params =
+                array('gallery' => $this->galleryId,
+                      'view' => 'Gallery',
+                      'slug' => $this->view->gallery->get('slug'));
+            $pagerurl = Ansel::getUrlfor('view', $pager_params, true);
+        }
+        if (!empty($this->view->_params['urlCallback'])) {
+            $callback = $this->view->_params['urlCallback'];
+        } else {
+            $callback = null;
+        }
+        $params = array('num' => $this->numTiles,
+                        'url' => $pagerurl,
+                        'perpage' => $this->perpage,
+                        'url_callback' => $callback);
+
+        $pager = new Horde_UI_Pager('page', $vars, $params);
+
+        /* Start buffering */
+        ob_start();
+
+        /* Output js/css here if we are calling via the api */
+        if (!empty($this->view->_params['api'])) {
+            $includes = new Horde_Script_Files();
+            $includes->disableAutoloadHordeJS();
+            $includes->_add('prototype.js', 'horde', true, true);
+            $includes->_add('redbox.js', 'horde', true, true);
+            $includes->includeFiles();
+        }
+
+        /* Needed in the template files */
+        $tilesperrow = $prefs->getValue('tilesperrow');
+        $cellwidth = round(100 / $tilesperrow);
+        $count = 0;
+
+        include ANSEL_TEMPLATES . '/view/galleryvimeo.inc';
+        return ob_get_clean();
+    }
+
+    function getTile($image, $video, $cnt)
+    {
+        $imgOnClick = 'return showVideo(' . $cnt . ');';
+        $tile = '<div class="image-tile" id="imagetile_' . (int)$video->clip_id . '">'
+            . Horde::link($video->url, $video->title, '', '', $imgOnClick, $video->title)
+            . '<img src="' Ansel::getImageUrl($image->id, 'prettythumb', true, $this->view->gallery->get('style')) . '" />' . '</a>';
+        $tile .= '<div style="valign: bottom;">';
+        $tile .= ' <div class="image-tile-caption" id="' . (int)$video->clip_id . 'caption">' . $video->caption  . '</div></div></div>';
+
+        return $tile;
+    }
+
+}
diff --git a/ansel/lib/Views/Image.php b/ansel/lib/Views/Image.php
new file mode 100644 (file)
index 0000000..523c35f
--- /dev/null
@@ -0,0 +1,415 @@
+<?php
+/**
+ * The Ansel_View_Image:: class wraps display of individual images.
+ *
+ * $Horde: ansel/lib/Views/Image.php,v 1.86 2009/07/30 13:15:10 mrubinsk Exp $
+ *
+ * @author  Chuck Hagenbuch <chuck@horde.org>
+ * @package Ansel
+ */
+
+/** Ansel_View_Abstract */
+require_once ANSEL_BASE . '/lib/Views/Abstract.php';
+
+/** Tags **/
+require_once ANSEL_BASE . '/lib/Tags.php';
+
+class Ansel_View_Image extends Ansel_View_Abstract {
+
+    /**
+     *  The image selected for this view.
+     *
+     * @var Ansel_Image
+     */
+    var $image;
+
+    /**
+     * @static
+     *
+     * @TODO use exceptions from the constructor instead of static
+     * instance-getting.
+     */
+    function makeView($params = array())
+    {
+        /* Get the image */
+        $image_id = $params['image_id'];
+
+        $image = &$GLOBALS['ansel_storage']->getImage($image_id);
+        if (is_a($image, 'PEAR_Error')) {
+            return $image;
+        }
+
+        /* Create the Ansel_View object */
+        $view = new Ansel_View_Image;
+
+        /* Save params */
+        if (count($params)) {
+            $view->_params = $params;
+        }
+
+        $view->gallery = $view->getGallery();
+        if (is_a($view->gallery, 'PEAR_Error')) {
+            return $view->gallery;
+        }
+
+        /* Save the image reference */
+        $view->resource = $image;
+
+        // Check user age
+        if (!$view->gallery->isOldEnough()) {
+            if (!empty($params['api'])) {
+                return PEAR::raiseError(_("Locked galleries are not viewable via the api."));
+            }
+            $date = Ansel::getDateParameter(
+                array('year' => isset($view->_params['year']) ? $view->_params['year'] : 0,
+                      'month' => isset($view->_params['month']) ? $view->_params['month'] : 0,
+                      'day' => isset($view->_params['day']) ? $view->_params['day'] : 0));
+
+                $url = Ansel::getUrlFor('view', array_merge(
+                    array('gallery' => $view->gallery->id,
+                          'slug' => empty($params['slug']) ? '' : $params['slug'],
+                          'page' => empty($params['page']) ? 0 : $params['page'],
+                          'view' => 'Image',
+                          'image' => $image->id),
+                    $date),
+                    true);
+
+            $params = array('gallery' => $view->gallery->id, 'url' => $url);
+
+            header('Location: ' . Horde_Util::addParameter(Horde::applicationUrl('disclamer.php'), $params, null, false));
+            exit;
+        }
+
+        // Check password
+        if ($view->gallery->hasPasswd()) {
+            if (!empty($params['api'])) {
+                return PEAR::raiseError(_("Locked galleries are not viewable via the api."));
+            }
+            $date = Ansel::getDateParameter(
+                array('year' => isset($view->_params['year']) ? $view->_params['year'] : 0,
+                      'month' => isset($view->_params['month']) ? $view->_params['month'] : 0,
+                      'day' => isset($view->_params['day']) ? $view->_params['day'] : 0));
+
+                $url = Ansel::getUrlFor('view', array_merge(
+                    array('gallery' => $view->gallery->id,
+                          'slug' => empty($params['slug']) ? '' : $params['slug'],
+                          'page' => empty($params['page']) ? 0 : $params['page'],
+                          'view' => 'Image',
+                          'image' => $image->id),
+                    $date),
+                    true);
+
+            $params = array('gallery' => $view->gallery->id, 'url' => $url);
+
+            header('Location: ' . Horde_Util::addParameter(Horde::applicationUrl('protect.php'), $params, null, false));
+            exit;
+        }
+
+
+        /* Any script files we may need if not calling via the api */
+        if (empty($view->_params['api'])) {
+            Horde::addScriptFile('prototype.js', 'horde', true);
+            Horde::addScriptFile('effects.js', 'horde', true);
+            Horde::addScriptFile('stripe.js', 'horde', true);
+        }
+
+        return $view;
+    }
+
+    function getGalleryCrumbData()
+    {
+        return $this->gallery->getGalleryCrumbData();
+    }
+
+    /**
+     * Get the title for this view.
+     *
+     * @return string  The title.
+     */
+    function getTitle()
+    {
+        return $this->resource->filename;
+    }
+
+    /**
+     * Get the HTML representing this view.
+     *
+     * @return string  The HTML.
+     */
+    function html()
+    {
+        global $browser, $conf, $prefs, $registry;
+
+        if (is_a($this->gallery, 'PEAR_Error')) {
+            echo htmlspecialchars($this->gallery->getMessage());
+            return;
+        }
+
+        $page = isset($this->_params['page']) ? $this->_params['page'] : 0;
+        $galleryId = $this->gallery->id;
+        $gallerySlug = $this->gallery->get('slug');
+        $imageId = $this->resource->id;
+        $galleryOwner = $this->gallery->get('owner');
+        $date = $this->gallery->getDate();
+
+        /* Allow overriding the configured view_mode */
+        if (isset($this->_params['mode'])) {
+            $mode = $this->_params['mode'];
+        } else {
+            $mode = $this->_params['mode'] = $this->gallery->get('view_mode');
+        }
+
+        /* Get any date infor the gallery has */
+        $date = $this->gallery->getDate();
+
+        $style = (empty($this->_params['style']) ?
+             $this->gallery->getStyle() :
+             Ansel::getStyleDefinition($this->_params['style']));
+
+        /* Make sure the screen view is loaded and get the geometry */
+        $geometry = $this->resource->getDimensions('screen');
+        if (is_a($geometry, 'PEAR_Error')) {
+            Horde::logMessage($geometry->getMessage(), __FILE__, __LINE__, PEAR_LOG_ERR);
+            $geometry = $conf['screen'];
+        }
+
+        /* Get comments before any output in sent. */
+        if (($conf['comments']['allow'] == 'all' || ($conf['comments']['allow'] == 'authenticated' && Horde_Auth::getAuth())) &&
+            $registry->hasMethod('forums/doComments')) {
+            $hasComments = true;
+            $url = empty($this->_params['comment_url']) ? null : $this->_params['comment_url'];
+            $comments = $registry->call('forums/doComments',
+                                        array('ansel', $imageId,
+                                              'commentCallback', true, null,
+                                              $url));
+            if (is_a($comments, 'PEAR_Error')) {
+                Horde::logMessage($comments, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+                $comments = array();
+            }
+        } else {
+            $comments = array();
+            $hasComments = false;
+        }
+
+        /* Get the index of the starting image */
+        $imageList = $this->gallery->listImages();
+        $revList = array_flip($imageList);
+        $imageIndex = $revList[$imageId];
+
+        /* Not needed when being called via api */
+        if (empty($this->_params['api'])) {
+            $ecardurl = Horde::applicationUrl(
+                Horde_Util::addParameter('img/ecard.php', array_merge(
+                                   array('gallery' => $galleryId,
+                                         'image' => $imageId),
+                                   $date)),
+                true);
+
+            $imageActionUrl = Horde_Util::addParameter(
+                'image.php', array_merge(
+                array('gallery' => $galleryId,
+                      'image' => $imageId,
+                      'page' => $page),
+                $date));
+        }
+
+        /* Check for an explicit gallery view url to use */
+        if (!empty($this->_params['gallery_view_url'])) {
+            $galleryurl = str_replace(
+                array('%g', '%s'),
+                array($galleryId, $gallerySlug),
+                urldecode($this->_params['gallery_view_url']));
+                Horde_Util::addParameter($galleryurl, $date);
+        } else {
+            $galleryurl = Ansel::getUrlFor('view', array_merge(
+                                           array('gallery' => $galleryId,
+                                                 'slug' => $gallerySlug,
+                                                 'page' => $page,
+                                                 'view' => 'Gallery'),
+                                           $date),
+                                           true);
+        }
+
+        /* Get the image src url */
+        $imageUrl = Ansel::getImageUrl($imageId, 'screen', true, $style['name']);
+
+        /* And a self url. Can't use Horde::selfUrl() since that would ignore
+         * pretty urls. */
+        $selfUrl = Ansel::getUrlFor('view', array_merge(
+                                    array('gallery' => $galleryId,
+                                          'slug' => $gallerySlug,
+                                          'image' => $imageId,
+                                          'view' => 'Image',
+                                          'page' => $page),
+                                    $date));
+
+        /* Get the next and previous image ids */
+        if (isset($imageList[$imageIndex + 1])) {
+            $next = $imageList[$imageIndex + 1];
+        } else {
+            $next = $imageList[0];
+        }
+        if (isset($imageList[$imageIndex - 1])) {
+            $prev = $imageList[$imageIndex - 1];
+        } else {
+            $prev = $imageList[count($imageList) - 1];
+        }
+
+        /** Calculate the page number of the next/prev images */
+        $perpage = $prefs->getValue('tilesperpage');
+        $pagestart = $page * $perpage;
+        $pageend = min(count($imageList), $pagestart + $perpage - 1);
+        $page_next = $page;
+
+        if ($revList[$imageId] + 1 > $pageend) {
+            $page_next++;
+        }
+        $page_prev = $page;
+        if ($revList[$imageId] - 1 < $pagestart) {
+            $page_prev--;
+        }
+
+        /* Previous image link */
+        if (!empty($this->_params['image_view_url'])) {
+            $prev_url = str_replace(
+                array('%i', '%g', '%s'),
+                array($prev, $galleryId, $gallerySlug),
+                urldecode($this->_params['image_view_url']));
+        } else {
+            $prev_url = Ansel::getUrlFor('view', array_merge(
+                array('gallery' => $galleryId,
+                      'slug' => $gallerySlug,
+                      'image' => $prev,
+                      'view' => 'Image',
+                      'page' => $page_prev),
+                $date));
+        }
+        $prvImgUrl = Ansel::getImageUrl($prev, 'screen', false, $style['name']);
+
+        /* Next image link */
+        if (!empty($this->_params['image_view_url'])) {
+            $next_url = str_replace(
+                array('%i', '%g', '%s'),
+                array($prev, $galleryId, $gallerySlug),
+                urldecode($this->_params['image_view_url']));
+        } else {
+            $next_url = Ansel::getUrlFor('view', array_merge(
+                array('gallery' => $galleryId,
+                      'slug' => $gallerySlug,
+                      'image' => $next,
+                      'view' => 'Image',
+                      'page' => $page_next),
+                $date));
+        }
+        $nextImgUrl = Ansel::getImageUrl($next, 'screen', false, $style['name']);
+
+        /* Slideshow link */
+        if (!empty($this->_params['slideshow_link'])) {
+            $slideshow_url = str_replace(array('%i', '%g'),
+                                         array($imageId, $galleryId),
+                                         urldecode($this->_params['slideshow_link']));
+        } else {
+            $slideshow_url = Horde::applicationUrl(
+                Horde_Util::addParameter('view.php', array_merge(
+                                   array('gallery' => $galleryId,
+                                         'image' => $imageId,
+                                         'view' => 'Slideshow'),
+                                   $date)));
+        }
+
+        $commentHtml = '';
+        if (isset($hasComments)) {
+            if (!empty($comments['threads'])) {
+                $commentHtml .= '<br />' . $comments['threads'];
+            }
+            if (!empty($comments['comments'])) {
+                $commentHtml .= '<br />' . $comments['comments'];
+            }
+        }
+
+        if ($prefs->getValue('showexif')) {
+            require_once ANSEL_BASE . '/lib/Exif.php';
+            $exifHtml = $this->_getExifHtml();
+        } else {
+            $exifHtml = '';
+        }
+
+        /* Buffer the template file and return the html */
+        ob_start();
+
+        //@TODO: Refactor styles to allow dynamic inclusion/exclusion of widgets.
+        /* These items currently don't work when viewing through the api */
+        if (empty($this->_params['api'])) {
+            /* Add the widgets */
+            require_once ANSEL_BASE . '/lib/Widget.php';
+
+            // Tag widget
+            $this->addWidget(Ansel_Widget::factory('Tags', array('view' => 'image')));
+
+            // Similar photos
+            $this->addWidget(Ansel_Widget::factory('SimilarPhotos'));
+
+           // Geolocation
+           $this->addWidget(Ansel_Widget::factory('Geodata', array('images' => array($this->resource->id))));
+
+            // Faces
+            if ($conf['faces']['driver']) {
+                $this->addWidget(Ansel_Widget::factory('ImageFaces', array('selfUrl' => $selfUrl)));
+            }
+
+            // Links
+            $this->addWidget(Ansel_Widget::factory('Links', array()));
+
+            /* In line caption editing */
+            if ($this->gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+                $imple = Horde_Ajax_Imple::factory(array('ansel', 'EditCaption'),
+                                                array('id' => $imageId,
+                                                      'domid' => "Caption",
+                                                      'cols' => 120));
+                $imple->attach();
+            }
+        }
+
+        /* Output the js if we are calling via the api */
+        if (!empty($this->_params['api'])) {
+            $includes = new Horde_Script_Files();
+            $includes->disableAutoloadHordeJS();
+            $includes->_add('prototype.js', 'horde', true, true);
+            $includes->_add('effects.js', 'horde',true, true);
+            $includes->_add('stripe.js', 'horde', true, true);
+            $includes->includeFiles();
+        }
+
+        require ANSEL_TEMPLATES . '/view/image.inc';
+        return ob_get_clean();
+    }
+
+    /**
+     * Helper function for generating the HTML for EXIF data.
+     *
+     * @return string  The HTML
+     */
+    function _getExifHtml()
+    {
+        $data = Ansel_ImageData::getAttributes($this->resource, true);
+
+        $html = '';
+        if (count($data)) {
+            $data = array_chunk($data, 3);
+            $html .= '<table class="box striped" cellspacing="0" style="width:100%; padding:4px">';
+            $i = 0;
+            foreach ($data as $elem) {
+                $html .= '<tr class="' . (($i++ % 2 == 0) ? 'rowEven' : 'rowOdd')
+                         . '">' . implode('', $elem) . '</tr>';
+            }
+            $html .= '</table>';
+        }
+        return $html;
+    }
+
+    function viewType()
+    {
+        return 'Image';
+    }
+
+}
diff --git a/ansel/lib/Views/List.php b/ansel/lib/Views/List.php
new file mode 100644 (file)
index 0000000..1a2402e
--- /dev/null
@@ -0,0 +1,281 @@
+<?php
+/**
+ * @package Ansel
+ *
+ * $Horde: ansel/lib/Views/List.php,v 1.41 2009/07/08 18:28:45 slusarz Exp $
+ */
+
+/** Ansel_View_Abstract */
+require_once ANSEL_BASE . '/lib/Views/Abstract.php';
+
+/** Tags **/
+require_once ANSEL_BASE . '/lib/Tags.php';
+
+/** Gallery Tiles **/
+require_once ANSEL_BASE . '/lib/Tile/Gallery.php';
+
+/** Horde_UI_Pager */
+require_once 'Horde/UI/Pager.php';
+
+/**
+ * The Ansel_View_Gallery:: class wraps display of individual images.
+ *
+ * @author  Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_View_List extends Ansel_View_Abstract {
+
+    /**
+     * @static
+     *
+     * @param array $params  Any parameters that the view might need.
+     * <pre>
+     *  In addition to the params taken by Ansel_View_Gallery, this view
+     *  can also take:
+     *
+     *  groupby      -  Group the results (owner, category etc...)
+     *
+     *  owner        -  The owner to group by
+     *
+     *  category     -  The category to group by
+     *
+     *  gallery_ids  -  No fitering, just show these galleries
+     *
+     *  pager_url    -  The url for the pager to use see Ansel_Gallery for
+     *                  more information on the url parameters.
+     *
+     * @TODO use exceptions from the constructor instead of static
+     * instance-getting.
+     */
+    function makeView($params = array())
+    {
+        $view = new Ansel_View_List;
+        $view->_params = $params;
+        return $view;
+    }
+
+    /**
+     * Get this view's title.
+     *
+     * @return string  The gallery's title.
+     */
+    function getTitle()
+    {
+        return _("Gallery List");
+    }
+
+    /**
+     * Return the HTML representing this view.
+     *
+     * @return string  The HTML.
+     *
+     */
+    function html()
+    {
+        global $conf, $prefs, $registry, $ansel_storage, $notification;
+
+        // If we aren't supplied with a page number, default to page 0.
+        if (isset($this->_params['page'])) {
+            $page = $this->_params['page'];
+        } else {
+            $page = Horde_Util::getFormData('page', 0);
+        }
+        $galleries_perpage = $prefs->getValue('tilesperpage');
+
+        // Check for grouping.
+        if (empty($this->_params['groupby'])) {
+            $groupby = Horde_Util::getFormData('groupby', $prefs->getValue('groupby'));
+        } else {
+            $groupby = $this->_params['groupby'];
+        }
+
+        if (empty($this->_params['owner'])) {
+            $owner = Horde_Util::getFormData('owner');
+            $owner = empty($owner) ? null : $owner;
+        } else {
+            $owner = $this->_params['owner'];
+        }
+
+        $special = Horde_Util::getFormData('special');
+
+        if (empty($this->_params['category'])) {
+            $category = Horde_Util::getFormData('category');
+            $category = empty($category) ? null : $category;
+        } else {
+            $category = $this->_params['category'];
+        }
+        if (!$owner && !$category && !$special && $groupby != 'none' ) {
+            header('Location: ' . Ansel::getUrlFor('group', array('groupby' => $groupby), true));
+            exit;
+        }
+
+        // We'll need this in the template.
+        $sortby = !empty($this->_params['sort']) ? $this->_params['sort'] : 'name';
+        $sortdir = isset($this->_params['sort_dir']) ? $this->_params['sort_dir'] : 0;
+
+        // If we are calling from the api, we can just pass a list of gallery
+        // ids instead of doing grouping stuff.
+        if (!empty($this->_params['api']) &&
+            !empty($this->_params['gallery_ids']) &&
+            count($this->_params['gallery_ids'])) {
+
+            $start = $page * $galleries_perpage;
+            $num_galleries = count($this->_params['gallery_ids']);
+            if ($num_galleries > $start) {
+                $getThese = array_slice($this->_params['gallery_ids'], $start, $galleries_perpage);
+                $try = $ansel_storage->getGalleries($getThese);
+                $gallerylist = array();
+                foreach ($try as $id => $gallery) {
+                    if ($gallery->hasPermission(Horde_Auth::getAuth(), PERMS_SHOW)) {
+                        $gallerylist[$id] = $gallery;
+                    }
+                }
+            } else {
+                $gallerylist = array();
+            }
+        } else {
+            // Set list filter/title
+            $filter = array();
+            if (!is_null($owner)) {
+                $filter['owner'] = $owner;
+            }
+
+            if (!is_null($category)) {
+                $filter['category'] = $category;
+            }
+
+            if ($owner) {
+                if ($owner == Horde_Auth::getAuth() && empty($this->_params['api'])) {
+                    $list_title = _("My Galleries");
+                } elseif (!empty($GLOBALS['conf']['gallery']['customlabel'])) {
+                    $uprefs = &Prefs::singleton($GLOBALS['conf']['prefs']['driver'],
+                                                'ansel', $owner, '', null, false);
+                    $fullname = $uprefs->getValue('grouptitle');
+                    if (!$fullname) {
+                        require_once 'Horde/Identity.php';
+                        $identity = &Identity::singleton('none', $owner);
+                        $fullname = $identity->getValue('fullname');
+                        if (!$fullname) {
+                            $fullname = $owner;
+                        }
+                        $list_title = sprintf(_("%s's Galleries"), $fullname);
+                    } else {
+                        $list_title = $fullname;
+                    }
+                } else {
+                    $list_title = sprintf(_("%s's Galleries"), $owner);
+                }
+            } elseif ($category || ($groupby == 'category' && $special)) {
+                if ($special == 'unfiled') {
+                    $list_title = sprintf(_("Galleries in category \"%s\""),
+                                          _("Unfiled"));
+                    $filter['category'] = '';
+                } else {
+                    $list_title = sprintf(_("Galleries in category \"%s\""), $category);
+                }
+            } else {
+                $list_title = _("Gallery List");
+            }
+
+            $num_galleries = $ansel_storage->countGalleries(
+                Horde_Auth::getAuth(), PERMS_SHOW, $filter, null, false);
+            if (is_a($num_galleries, 'PEAR_Error')) {
+                return $num_galleries->getMessage();
+            }
+
+            if ($num_galleries == 0 && empty($this->_params['api'])) {
+                if ($filter == $owner && $owner == Horde_Auth::getAuth()) {
+                    $notification->push(_("You have no photo galleries, add one!"),
+                                        'horde.message');
+                    header('Location: ' . Horde_Util::addParameter(Horde::applicationUrl('gallery.php'), 'actionID', 'add'));
+                    exit;
+                }
+                $notification->push(_("There are no photo galleries available."), 'horde.message');
+                $gallerylist = array();
+            } else {
+                $gallerylist = $ansel_storage->listGalleries(
+                    PERMS_SHOW, $filter, null, false, $page * $galleries_perpage,
+                    $galleries_perpage, $sortby, $sortdir);
+            }
+
+        }
+
+        $vars = Horde_Variables::getDefaultVariables();
+        if (!empty($this->_params['page'])) {
+            $vars->add('page', $this->_params['page']);
+        }
+
+        if (!empty($this->_params['pager_url'])) {
+            $pagerurl = $this->_params['pager_url'];
+            $override = true;
+        } else {
+            $override = false;
+            $pagerurl = Ansel::getUrlFor('view',
+                                    array('owner' => $owner,
+                                          'category' => $category,
+                                          'special' => $special,
+                                          'groupby' => $groupby,
+                                          'view' => 'List'));
+        }
+        $p_params = array('num' => $num_galleries,
+                          'url' => $pagerurl,
+                          'perpage' => $galleries_perpage);
+
+        if ($override) {
+            $p_params['url_callback'] = null;
+        }
+        $pager = new Horde_UI_Pager('page', $vars, $p_params);
+        $preserve = array('sort_dir' => $sortdir);
+        if (!empty($sortby)) {
+            $preserve['sort'] = $sortby;
+        }
+        $pager->preserve($preserve);
+
+        if ($num_galleries) {
+            $min = $page * $galleries_perpage;
+            $max = $min + $galleries_perpage;
+            if ($max > $num_galleries) {
+                $max = $num_galleries - $min;
+            }
+            $start = $min + 1;
+            $end = min($num_galleries, $min + $galleries_perpage);
+
+            if ($owner) {
+                $refresh_link = Ansel::getUrlFor('view',
+                                                 array('groupby' => $groupby,
+                                                       'owner' => $owner,
+                                                       'page' => $page,
+                                                       'view' => 'List'));
+
+            } else {
+                $refresh_link = Ansel::getUrlFor('view',
+                                                 array('view' => 'List',
+                                                       'groupby' => $groupby,
+                                                       'page' => $page,
+                                                       'category' => $category));
+            }
+
+            // Get top-level / default gallery style.
+            if (empty($this->_params['style'])) {
+                $style = Ansel::getStyleDefinition(
+                    $prefs->getValue('default_gallerystyle'));
+            } else {
+                $style = Ansel::getStyleDefinition($this->_params['style']);
+            }
+            $count = 0;
+            $width = round(100 / $prefs->getValue('tilesperrow'));
+
+            ob_start();
+            include ANSEL_TEMPLATES . '/view/list.inc';
+            $html = ob_get_clean();
+            return $html;
+        }
+        return '';
+    }
+
+    function viewType()
+    {
+        return 'List';
+    }
+
+}
diff --git a/ansel/lib/Views/Results.php b/ansel/lib/Views/Results.php
new file mode 100644 (file)
index 0000000..f60a009
--- /dev/null
@@ -0,0 +1,317 @@
+<?php
+/**
+ * Ansel_View for displaying search / tag browsing results.
+ *
+ * Copyright 2006-2009 The Horde Project (http://www.horde.org/)
+ *
+ * @author Michael J. Rubinsky (mrubinsk@horde.org)
+ * @package Ansel
+ *
+ * $Horde: ansel/lib/Views/Results.php,v 1.40 2009/07/08 18:28:45 slusarz Exp $
+ */
+
+/** Ansel_View_Abstract */
+require_once ANSEL_BASE . '/lib/Views/Abstract.php';
+
+/** Horde_UI_Pager */
+require_once 'Horde/UI/Pager.php';
+
+/**
+ * The Ansel_View_Results:: class wraps display of images/galleries from
+ * multiple parent sources..
+ *
+ * @author  Michael Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_View_Results extends Ansel_View_Abstract {
+
+    /**
+     * Instance of our tag search
+     *
+     * @var Ansel_Tag_Search
+     */
+    var $_search;
+
+    /**
+     * Gallery owner id
+     *
+     * @var string
+     */
+    var $_owner;
+
+    /**
+     * Contructor - just set some instance variables.
+     *
+     * @return Ansel_View_Results
+     */
+    function Ansel_View_Results()
+    {
+        require_once ANSEL_BASE . '/lib/Tags.php';
+
+        $this->_owner = Horde_Util::getFormData('owner', null);
+        $this->_search = Ansel_Tags::getSearch(null, $this->_owner);
+    }
+
+    /**
+     * @static
+     *
+     * @return Ansel_View_Results  The view object.
+     *
+     * @TODO use exceptions from the constructor instead of static
+     * instance-getting.
+     */
+    function makeView($params = array())
+    {
+        $view = new Ansel_View_Results();
+        if (count($params)) {
+            $view->_params = $params;
+        }
+        return $view;
+    }
+
+    /**
+     * Return the title for this view.
+     *
+     * @return string The title for this view.
+     */
+    function getTitle()
+    {
+        return (!empty($this->_owner))
+                ? sprintf(_("Searching %s's photos tagged: "), $this->_owner)
+                : _("Searching all photos tagged: ");
+    }
+
+    /**
+     * Get the HTML representing this view.
+     *
+     * @return string  The HTML
+     */
+    function html()
+    {
+        global $conf, $prefs, $registry, $ansel_storage;
+
+        $page = Horde_Util::getFormData('page', 0);
+        $action = Horde_Util::getFormData('actionID', '');
+        $image_id = Horde_Util::getFormData('image');
+
+        $vars = Horde_Variables::getDefaultVariables();
+
+        // Number perpage from prefs or config.
+        $perpage = min($prefs->getValue('tilesperpage'),
+                       $conf['thumbnail']['perpage']);
+
+        switch ($action) {
+        // Image related actions
+        case 'delete':
+             if (is_array($image_id)) {
+                 $images = array_keys($image_id);
+             } else {
+                 $images = array($image_id);
+             }
+
+             foreach ($images as $image) {
+                 // Need a gallery object to delete the image, but need
+                 // the image object to get the gallery.
+                 $img = $ansel_storage->getImage($image);
+                 $gallery = $ansel_storage->getgallery($img->gallery);
+                 if (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_DELETE)) {
+                     $GLOBALS['notification']->push(
+                        sprintf(_("Access denied deleting photos from \"%s\"."), $image),
+                                'horde.error');
+                 } else {
+                     $result = $gallery->removeImage($image);
+                    if (is_a($result, 'PEAR_Error')) {
+                        $GLOBALS['notification']->push(
+                            sprintf(_("There was a problem deleting photos: %s"),
+                                    $result->getMessage()), 'horde.error');
+                    } else {
+                        $GLOBALS['notification']->push(_("Deleted the photo."),
+                                                       'horde.success');
+                        Ansel_Tags::clearCache();
+                    }
+                 }
+             }
+
+             // Reload the browse view again to get notifications.
+             header('Location: ' . Ansel::getUrlFor('view',
+                                                    array('view' => 'Results'),
+                                                    true));
+             exit;
+
+        case 'move':
+            if (is_array($image_id)) {
+                $images = array_keys($image_id);
+            } else {
+                $images = array($image_id);
+            }
+
+            // Move the images if we're provided with at least one
+            // valid image ID.
+            $newGallery = Horde_Util::getFormData('new_gallery');
+            if ($images && $newGallery) {
+                $newGallery = $ansel_storage->getGallery($newGallery);
+                if (is_a($newGallery, 'PEAR_Error')) {
+                    $GLOBALS['notification']->push(_("Bad input."),
+                                                   'horde.error');
+                } else {
+                    // Group by gallery first, then process in bulk by gallery.
+                    $galleries = array();
+                    foreach ($images as $image) {
+                        $img = $ansel_storage->getImage($image);
+                        $galleries[$img->gallery][] = $image;
+                    }
+                    foreach ($galleries as $gallery_id => $images) {
+                        $gallery = $ansel_storage->getGallery($gallery_id);
+                        $result = $gallery->moveImagesTo($images, $newGallery);
+                        if (is_a($result, 'PEAR_Error')) {
+                            $GLOBALS['notification']->push($result, 'horde.error');
+                        } else {
+                            $GLOBALS['notification']->push(
+                                sprintf(ngettext("Moved %d photo from \"%s\" to \"%s\"",
+                                                 "Moved %d photos from \"%s\" to \"%s\"",
+                                                 count($images)),
+                                        count($images), $gallery->get('name'),
+                                        $newGallery->get('name')),
+                                'horde.success');
+                        }
+                    }
+                }
+            }
+
+            // Return to the image list.
+            $imageurl = Horde_Util::addParameter('view.php',
+                                           array('view' => 'Results'));
+            header('Location: ' . Ansel::getUrlFor('view',
+                                                   array('view' => 'Results'),
+                                                   true));
+            exit;
+
+        case 'copy':
+            if (is_array($image_id)) {
+                $images = array_keys($image_id);
+            } else {
+                $images = array($image_id);
+            }
+
+            // Move the images if we're provided with at least one
+            // valid image ID.
+            $newGallery = Horde_Util::getFormData('new_gallery');
+            if ($images && $newGallery) {
+                $newGallery = $ansel_storage->getGallery($newGallery);
+                if (is_a($newGallery, 'PEAR_Error')) {
+                    $GLOBALS['notification']->push(_("Bad input."),
+                                                   'horde.error');
+                } else {
+                    // Group by gallery first, then process in bulk by gallery.
+                    $galleries = array();
+                    foreach ($images as $image) {
+                        $img = $ansel_storage->getImage($image);
+                        $galleries[$img->gallery][] = $image;
+                    }
+                    foreach ($galleries as $gallery_id => $images) {
+                        $gallery = $ansel_storage->getGallery($gallery_id);
+                        $result = $gallery->copyImagesTo($images, $newGallery);
+                        if (is_a($result, 'PEAR_Error')) {
+                            $GLOBALS['notification']->push($result,
+                                                           'horde.error');
+                        } else {
+                            $GLOBALS['notification']->push(
+                                sprintf(ngettext("Copied %d photo from %s to %s",
+                                                 "Copied %d photos from %s to %s",
+                                                 count($images)),
+                                        count($images), $gallery->get('name'),
+                                        $newGallery->get('name')),
+                                'horde.success');
+                        }
+                    }
+                }
+            }
+
+            // Return to the image list.
+            $imageurl = Horde_Util::addParameter('view.php',
+                                           array('view' => 'Results'));
+            header('Location: ' . Horde::applicationUrl($imageurl, true));
+            exit;
+
+        // Tag related actions
+        case 'remove':
+            $tag = Horde_Util::getFormData('tag');
+            if (isset($tag)) {
+                $tag = Ansel_Tags::getTagIds(array($tag));
+                $tag = array_pop($tag);
+                $this->_search->removeTag($tag);
+                $this->_search->save();
+            }
+            break;
+
+        case 'add':
+        default:
+            $tag = Horde_Util::getFormData('tag');
+            if (isset($tag)) {
+                $tag = Ansel_Tags::getTagIds(array($tag));
+                $tag = array_pop($tag);
+                $this->_search->addTag($tag);
+                $this->_search->save();
+            }
+            break;
+        }
+
+        // Check for empty tag search and redirect if empty
+        if ($this->_search->tagCount() < 1) {
+            header('Location: ' . Horde::applicationUrl('browse.php', true));
+            exit;
+        }
+
+        // Get the slice of galleries/images to view on this page.
+        $results = $this->_search->getSlice($page, $perpage);
+        $total = $this->_search->count();
+        $total = $total['galleries'] + $total['images'];
+
+        // The number of resources to display on this page.
+        $numimages = count($results);
+
+        // Get any related tags to display.
+        if ($conf['tags']['relatedtags']) {
+            $rtags = $this->_search->getRelatedTags();
+            $rtaghtml = '<ul>';
+            $links = Ansel_Tags::getTagLinks($rtags, 'add');
+            foreach ($rtags as $id => $taginfo) {
+                if (!empty($this->_owner)) {
+                    $links[$id] = Horde_Util::addParameter($links[$id], 'owner',
+                                                     $this->_owner);
+                }
+                $rtaghtml .= '<li>' . Horde::link($links[$id],
+                                                  sprintf(ngettext(
+                                                    "%d photo", "%d photos",
+                                                    $taginfo['total']),
+                                                  $taginfo['total']))
+                             . $taginfo['tag_name'] . '</a></li>';
+            }
+            $rtaghtml .= '</ul>';
+        }
+        $styleDef = Ansel::getStyleDefinition(
+            $GLOBALS['prefs']->getValue('default_gallerystyle'));
+        $style = $styleDef['name'];
+        $viewurl = Horde_Util::addParameter('view.php', array('view' => 'Results',
+                                                        'actionID' => 'add'));
+
+        $vars = Horde_Variables::getDefaultVariables();
+        $option_move = $option_copy = $ansel_storage->countGalleries(PERMS_EDIT);
+
+
+        $pagestart = ($page * $perpage) + 1;
+        $pageend = min($pagestart + $numimages - 1, $pagestart + $perpage - 1);
+        $pager = new Horde_UI_Pager('page', $vars, array('num' => $total,
+                                                         'url' => $viewurl,
+                                                         'perpage' => $perpage));
+        ob_start();
+        include ANSEL_TEMPLATES . '/view/results.inc';
+        return ob_get_clean();
+    }
+
+    function viewType()
+    {
+        return 'Results';
+    }
+
+}
diff --git a/ansel/lib/Views/Slideshow.php b/ansel/lib/Views/Slideshow.php
new file mode 100644 (file)
index 0000000..f0f8019
--- /dev/null
@@ -0,0 +1,183 @@
+<?php
+/**
+ * The Ansel_View_Slideshow:: class wraps display of the gallery slideshow.
+ *
+ * $Horde: ansel/lib/Views/Slideshow.php,v 1.16 2009/06/16 00:34:39 mrubinsk Exp $
+ *
+ * @author  Chuck Hagenbuch <chuck@horde.org>
+ * @author  Michael J. Rubinsky <mrubinsk@horde.org>
+ *
+ * @package Ansel
+ */
+
+/** Ansel_View_Abstract */
+require_once ANSEL_BASE . '/lib/Views/Abstract.php';
+
+class Ansel_View_Slideshow extends Ansel_View_Abstract {
+
+    /**
+     * The Ansel_Image object representing the first image selected for view.
+     *
+     * @var Ansel_Image
+     */
+    var $image;
+
+    /**
+     * @static
+     *
+     * @return Ansel_View_Slidshow  The view object.
+     *
+     * @TODO use exceptions from the constructor instead of static
+     * instance-getting.
+     */
+    function makeView($params = array())
+    {
+        if (empty($params['image_id'])) {
+            $image_id = Horde_Util::getFormData('image');
+        } else {
+            $image_id = $params['image_id'];
+        }
+        $image = $GLOBALS['ansel_storage']->getImage($image_id);
+        if (is_a($image, 'PEAR_Error')) {
+            return $image;
+        }
+
+        $view = new Ansel_View_Slideshow();
+        if (count($params)) {
+            $view->_params = $params;
+        }
+        $view->gallery = $view->getGallery($image->gallery);
+        if (is_a($view->gallery, 'PEAR_Error')) {
+            return $view->gallery;
+        }
+        $view->image = $image;
+
+        // Check user age
+        if (!$view->gallery->isOldEnough()) {
+           $date = Ansel::getDateParameter(
+                array('year' => isset($view->_params['year']) ? $view->_params['year'] : 0,
+                      'month' => isset($view->_params['month']) ? $view->_params['month'] : 0,
+                      'day' => isset($view->_params['day']) ? $view->_params['day'] : 0));
+
+                $url = Ansel::getUrlFor('view', array_merge(
+                    array('gallery' => $view->gallery->id,
+                          'slug' => empty($params['slug']) ? '' : $params['slug'],
+                          'page' => empty($params['page']) ? 0 : $params['page'],
+                          'view' => 'Slideshow',
+                          'image' => $image->id),
+                    $date),
+                    true);
+
+            $params = array('gallery' => $view->gallery->id, 'url' => $url);
+
+            header('Location: ' . Horde_Util::addParameter(Horde::applicationUrl('disclamer.php'), $params, null, false));
+            exit;
+        }
+
+       // Check password
+        if ($view->gallery->hasPasswd()) {
+           $date = Ansel::getDateParameter(
+                array('year' => isset($view->_params['year']) ? $view->_params['year'] : 0,
+                      'month' => isset($view->_params['month']) ? $view->_params['month'] : 0,
+                      'day' => isset($view->_params['day']) ? $view->_params['day'] : 0));
+
+                $url = Ansel::getUrlFor('view', array_merge(
+                    array('gallery' => $view->gallery->id,
+                          'slug' => empty($params['slug']) ? '' : $params['slug'],
+                          'page' => empty($params['page']) ? 0 : $params['page'],
+                          'view' => 'Slideshow',
+                          'image' => $image->id),
+                    $date),
+                    true);
+
+            $params = array('gallery' => $view->gallery->id, 'url' => $url);
+
+            header('Location: ' . Horde_Util::addParameter(Horde::applicationUrl('protect.php'), $params, null, false));
+            exit;
+        }
+
+
+        Horde::addScriptFile('prototype.js', 'horde', true);
+        Horde::addScriptFile('effects.js', 'horde', true);
+        Horde::addScriptFile('stripe.js', 'horde', true);
+        Horde::addScriptFile('slideshow.js', 'ansel', true);
+
+        return $view;
+    }
+
+    /**
+     * Get the title for this view.
+     *
+     * @return string  The title.
+     */
+    function getTitle()
+    {
+        return $this->image->filename;
+    }
+
+    /**
+     * Get the HTML representing this view.
+     *
+     * @return string  The HTML.
+     */
+    function html()
+    {
+        global $browser, $conf, $prefs, $registry;
+
+        if (is_a($this->gallery, 'PEAR_Error')) {
+            echo htmlspecialchars($this->gallery->getMessage());
+            return;
+        }
+        $page = Horde_Util::getFormData('page', 0);
+        $galleryId = $this->gallery->id;
+        $imageId = $this->image->id;
+        $galleryOwner = $this->gallery->get('owner');
+        $style = $this->gallery->getStyle();
+
+        /* Get date info to pass along the links */
+        if (!empty($this->_params['year'])) {
+            $date = Ansel::getDateParameter(
+                array('year' => $this->_params['year'],
+                      'month' => $this->_params['month'],
+                      'day' => $this->_params['day']));
+        } else {
+            $date = array();
+        }
+
+        /* Get the index of the starting image */
+        $imageList = $this->gallery->listImages();
+
+        $style = $this->gallery->getStyle();
+        $revList = array_flip($imageList);
+        $imageIndex = $revList[$imageId];
+        if (isset($imageList[$imageIndex - 1])) {
+            $prev = $imageList[$imageIndex - 1];
+        } else {
+            $prev = $imageList[count($imageList) - 1];
+        }
+
+        $ecardurl = Horde_Util::addParameter('img/ecard.php',
+                                       array('gallery' => $galleryId,
+                                             'image' => $imageId));
+        $galleryurl = Horde_Util::addParameter('view.php', array_merge(
+            array('gallery' => $galleryId,
+                  'page' => $page),
+            $date));
+        $imageActionUrl = Horde_Util::addParameter('image.php', array_merge(
+            array('gallery' => $galleryId,
+                  'image' => $imageId,
+                  'page' => $page),
+            $date));
+        $imageUrl = Ansel::getImageUrl($imageId, 'screen', false, $style['name']);
+
+        ob_start();
+        require ANSEL_TEMPLATES . '/view/slideshow.inc';
+        return ob_get_clean();
+    }
+
+    function viewType()
+    {
+        return 'Slideshow';
+    }
+
+}
diff --git a/ansel/lib/Widget.php b/ansel/lib/Widget.php
new file mode 100644 (file)
index 0000000..61dedc8
--- /dev/null
@@ -0,0 +1,157 @@
+<?php
+/**
+ * Ansel_Widget:: class wraps the display of widgets to be displayed in various
+ * Ansel_Views.
+ *
+ * $Horde: ansel/lib/Widget.php,v 1.10 2009/06/19 22:32:18 mrubinsk Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_Widget {
+
+    /**
+     * Any parameters this widget will need..
+     *
+     * @var array
+     */
+    var $_params = array();
+
+    /**
+     * Reference to the Ansel_View we are attaching to
+     *
+     * @var Ansel_View
+     */
+    var $_view;
+
+    /**
+     * Holds the style definition for the gallery this view is for
+     * (or the image's parent gallery if this is for an image view).
+     *
+     * @var array
+     */
+    var $_style;
+
+    /**
+     * Title for this widget.
+     *
+     * @var string
+     */
+    var $_title;
+
+    /**
+     * Determine if this widget will be automatically rendered, or if it is
+     * the calling code's responsibility to render it.
+     *
+     * @var string
+     */
+    var $_render = 'auto';
+
+    /**
+     * Factory method for creating Ansel_Widgets
+     *
+     * @param string $type   The type of widget to create.
+     * @param array $params  Any parameters the widget needs.
+     *
+     * @return mixed Ansel_Widget object | PEAR_Error
+     */
+    function factory($type, $params = array())
+    {
+        $type = basename($type);
+        $class = 'Ansel_Widget_' . $type;
+        if (!class_exists($class)) {
+            include dirname(__FILE__) . '/Widget/' . $type . '.php';
+        }
+        if (class_exists($class)) {
+            $widget = new $class($params);
+            return $widget;
+        }
+
+        return PEAR::raiseError(sprintf(_("Unable to load the definition of %s."), $class));
+    }
+
+    /**
+     * Constructor
+     *
+     * @param array $params
+     * @return Ansel_Widget
+     */
+    function Ansel_Widget($params)
+    {
+        $this->_params = array_merge($params, $this->_params);
+        if (!empty($params['render'])) {
+            $this->_render = $params['render'];
+        }
+    }
+
+    /**
+     * Attach this widget to the passed in view. Normally called
+     * by the Ansel_View once this widget is added.
+     *
+     * @param Ansel_View $view  The view to attach to
+     */
+    function attach($view)
+    {
+        $this->_view = $view;
+
+        if (!empty($this->_params['style'])) {
+            $this->_style = Ansel::getStyleDefinition($this->_params['style']);
+        } else {
+            $this->_style = $view->gallery->getStyle();
+        }
+
+        return true;
+    }
+
+    /**
+     * Get the HTML for this widget
+     *
+     * @abstract
+     */
+    function html()
+    {
+    }
+
+    /**
+     * Default HTML for the beginning of the widget.
+     *
+     * @return string
+     */
+    function _htmlBegin()
+    {
+        $html = '<div class="anselWidget" style="background-color:' . $this->_style['background'] .   ';">';
+        $html .= '<h2 class="header tagTitle">' . $this->_title . '</h2>';
+        return $html;
+    }
+
+    /**
+     * Default HTML for the end of the widget.
+     *
+     * @return string
+     */
+    function _htmlEnd()
+    {
+        return '</div>';
+    }
+
+
+    /**
+     * Determine if a particular view (Image, Gallery etc..) is supported
+     * by this widget.
+     *
+     * @TODO
+     * @param string $view  The view to check
+     *
+     * @return boolean
+     */
+    function isSupported($view)
+    {
+        return true;
+    }
+
+}
diff --git a/ansel/lib/Widget/Actions.php b/ansel/lib/Widget/Actions.php
new file mode 100644 (file)
index 0000000..a83b245
--- /dev/null
@@ -0,0 +1,193 @@
+<?php
+/**
+ * Ansel_Widget_Actions:: class to wrap the display of gallery actions
+ *
+ * $Horde: ansel/lib/Widget/Actions.php,v 1.54 2009/07/27 17:07:34 mrubinsk Exp $
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_Widget_Actions extends Ansel_Widget {
+
+    var $_supported_views = array('Gallery');
+
+    function Ansel_Widget_Actions($params)
+    {
+        parent::Ansel_Widget($params);
+        $this->_title = _("Gallery Actions");
+    }
+
+    function html()
+    {
+        global $registry;
+        $html = $this->_htmlBegin();
+        $id = $this->_view->gallery->id;
+        $galleryurl = Horde_Util::addParameter(Horde::applicationUrl('gallery.php'),
+                                                               'gallery', $id);
+
+        if ($this->_view->gallery->hasFeature('upload')) {
+            $uploadurl = Horde_Util::addParameter(Horde::applicationUrl('img/upload.php'),
+                                            array('gallery' => $id,
+                                                  'page' => !empty($this->_view->_params['page']) ? $this->_view->_params['page'] : 0));
+        }
+
+        $html .= '<ul style="list-style-type:none;">';
+        if (empty($this->_params['hide_slideshow']) &&
+            $this->_view->gallery->hasFeature('slideshow') &&
+            $this->_view->gallery->countImages()) {
+            /* Slideshow link */
+            if (!empty($this->_params['slideshow_link'])) {
+                $slideshow_url = str_replace(array('%i', '%g'),
+                                             array(array_pop($this->_view->gallery->listImages(0, 1)), $id),
+                                             urldecode($this->_params['slideshow_link']));
+            } else {
+                /* Get any date info the gallery has */
+                $date = $this->_view->gallery->getDate();
+                $slideshow_url = Horde::applicationUrl(
+                    Horde_Util::addParameter('view.php', array_merge(
+                                       array('gallery' => $id,
+                                             'image' => array_pop($this->_view->gallery->listImages(0, 1)),
+                                             'view' => 'Slideshow'),
+                                       $date)));
+            }
+            $html .= '<li>' . Horde::link($slideshow_url, '', 'widget') . Horde::img('slideshow_play.png', _("Start Slideshow")) . ' ' . _("Start Slideshow") . '</a></li>';
+        }
+        if (!empty($uploadurl) && $this->_view->gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+            $html .= '<li>' . Horde::link($uploadurl, '', 'widget') . Horde::img('image_add.png') . ' ' . _("Upload photos") . '</a></li>';
+
+            /* Subgalleries */
+            if ($this->_view->gallery->hasFeature('subgalleries')) {
+                $html .= '<li>' . Horde::link(Horde_Util::addParameter($galleryurl, 'actionID', 'addchild'), '', 'widget') . Horde::img('add.png') . ' ' . _("Create a subgallery") . '</a></li>';
+            }
+        }
+        $html .= '</ul>';
+        $html .= $this->_getGalleryActions();
+
+        $selfurl = Horde::selfUrl(true, true);
+        $html .=  '<div class="control"><a href="'
+                 . Horde_Util::addParameter($selfurl, 'actionID',
+                                     'show_actions')
+                 . '" id="gallery-actions-toggle" class="'
+                 . (($GLOBALS['prefs']->getValue('show_actions'))
+                 ? 'hide'
+                 : 'show') . '">&nbsp;</a></div>' . "\n";
+
+        $html .= $this->_htmlEnd();
+        return $html;
+    }
+
+   /**
+    * Helper function for generating the gallery actions selection widget.
+    *
+    * @return string  The HTML
+    */
+    function _getGalleryActions()
+    {
+        global $registry, $prefs, $conf;
+
+        $id = $this->_view->gallery->id;
+        $galleryurl = Horde_Util::addParameter(Horde::applicationUrl('gallery.php'),
+                                                               'gallery', $id);
+
+        $selfurl = Horde::selfUrl(true, false, true);
+        $count = $this->_view->gallery->countImages();
+        $date = $this->_view->gallery->getDate();
+
+        $html = '<div style="display:' . (($prefs->getValue('show_actions')) ? 'block' : 'none') . ';" id="gallery-actions">';
+
+        /* Attach the ajax action */
+        ob_start();
+        $imple = Horde_Ajax_Imple::factory(array('ansel', 'ToggleGalleryActions'), array('bindTo' => 'gallery-actions'));
+        $imple->attach();
+        $html .= ob_get_clean();
+
+        /* Buid the url parameters to the zip link */
+        $view_params = array(
+            'gallery' => $this->_view->gallery->id,
+            'view' => 'Gallery',
+            'slug' => $this->_view->gallery->get('slug'),
+            'page' => (!empty($this->_view->_params['page']) ? $this->_view->_params['page'] : 0));
+
+        /* Append the date information to the parameters if we need it */
+        $view_params = array_merge($view_params, $date);
+
+        $html .= '<ul style="list-style-type:none;">';
+
+        /* Bookmark link */
+        if ($registry->hasMethod('bookmarks/getAddUrl')) {
+             $api_params = array(
+                'url' => Ansel::getUrlFor('view', $view_params, true),
+                'title' => $this->_view->gallery->get('name'));
+
+            $url = $registry->call('bookmarks/getAddUrl', array($api_params));
+            if (!is_a($url, 'PEAR_Error')) {
+                $html .= '<li>' . Horde::link($url, '', 'widget') . Horde::img('trean.png', '', '', $registry->getImageDir('trean')) . ' ' . _("Add to bookmarks") . '</a></li>';
+            }
+        }
+
+        /* Download as ZIP link */
+        if (!empty($conf['gallery']['downloadzip']) &&
+            $this->_view->gallery->canDownload() &&
+            $count &&
+            $this->_view->gallery->hasFeature('zipdownload')) {
+
+            $zip_params = array_merge(array('actionID' => 'downloadzip'), $date);
+            $html .= '<li>' . Horde::link(Horde::applicationUrl(Horde_Util::addParameter($galleryurl, $zip_params)), '', 'widget') . Horde::img('mime/compressed.png', '', '', $GLOBALS['registry']->getImageDir('horde')) . ' ' .  _("Download as zip file") . '</a></li>';
+        }
+
+        /* Image upload, subgalleries, captions etc... */
+        if ($this->_view->gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+            /* Properties */
+            $html .= '<li>' . Horde::link(Horde_Util::addParameter($galleryurl, array('actionID' => 'modify', 'url' => $selfurl)), '', 'widget') . Horde::img('edit.png', '', '', $GLOBALS['registry']->getImageDir('horde')) . ' ' . _("Change properties") . '</a></li>';
+            if ($count) {
+                /* Captions */
+                if ($this->_view->gallery->hasFeature('image_captions')) {
+                    $params = array_merge(array('gallery' => $id), $date);
+                    $html .= '<li>' . Horde::link(Horde::applicationUrl(Horde_Util::addParameter('gallery/captions.php', $params)), '', 'widget') . Horde::img('text.png') . ' ' . _("Set captions") . ' ' . '</a></li>';
+                }
+
+                /* Sort */
+                if ($this->_view->gallery->hasFeature('sort_images')) {
+                    $sorturl = Horde_Util::addParameter(Horde::applicationUrl('gallery/sort.php'), array_merge(array('gallery' => $id), $date));
+                    $html .= '<li>' . Horde::link(Horde_Util::addParameter($sorturl, 'actionId' , 'getOrder'), '', 'widget') . Horde::img('arrow_switch.png') . ' ' . _("Sort photos") . '</a></li>';
+                }
+
+                /* Regenerate Thumbnails */
+                $html .= '<li>' . Horde::link(Horde_Util::addParameter($galleryurl, array('actionID' => 'generateThumbs')), '', 'widget') . Horde::img('reload.png', '', '', $GLOBALS['registry']->getImageDir('horde')) . ' ' . _("Reset all thumbnails") . '</a></li>';
+
+                /* Regenerate all views  */
+                $html .= '<li>' . Horde::link(Horde_Util::addParameter($galleryurl, array('actionID' => 'deleteCache')), '', 'widget') . Horde::img('reload.png', '', '', $GLOBALS['registry']->getImageDir('horde')) . ' ' . _("Regenerate all photo views") . '</a></li>';
+
+                /* Find faces */
+                if ($conf['faces']['driver'] && $this->_view->gallery->hasFeature('faces')) {
+                    $html .= '<li>' . Horde::link(Horde::applicationUrl(Horde_Util::addParameter('faces/gallery.php', array_merge($date, array('gallery' => $id, 'page' => (!empty($this->_view->_params['page']) ? $this->_view->_params['page'] : 0))))), '', 'widget') . Horde::img('user.png','', '', $GLOBALS['registry']->getImageDir('horde')) . ' ' . _("Find faces") . '</a></li>';
+                }
+
+            } /* end if ($count) {} */
+
+            if (Ansel::isAvailable('photo_stack') && $this->_view->gallery->hasFeature('stacks')) {
+                $html .= '<li>' . Horde::link(Horde_Util::addParameter($galleryurl, array('actionID' => 'generateDefault', 'url' => $selfurl)), '', 'widget') . Horde::img('reload.png', '', '', $GLOBALS['registry']->getImageDir('horde')) . ' ' . _("Reset default photo") . '</a></li>';
+            }
+        }
+
+        if ($this->_view->gallery->get('owner') == Horde_Auth::getAuth()) {
+            $html .= '<li>' . Horde::link('#','','popup widget', '', "popup('" . Horde_Util::addParameter(Horde::applicationUrl('perms.php'), 'cid', $this->_view->gallery->id) . "');") . Horde::img('perms.png', '', '', $GLOBALS['registry']->getImageDir('horde')) . ' ' . _("Set permissions") . '</a></li>';
+        } elseif (!empty($conf['report_content']['driver']) &&
+                  (($conf['report_content']['allow'] == 'authenticated' && Horde_Auth::isAuthenticated()) ||
+                   $conf['report_content']['allow'] == 'all')) {
+
+            $reporturl = Horde_Util::addParameter(Horde::applicationUrl('report.php'),
+                                            'gallery', $id);
+            $html .= '<li>' . Horde::link($reporturl, '', 'widget') . ' ' . _("Report") . "</a></li>\n";
+        }
+
+        if ($this->_view->gallery->hasPermission(Horde_Auth::getAuth(), PERMS_DELETE)) {
+            $html .= '<li>' . Horde::link(Horde_Util::addParameter($galleryurl, 'actionID', 'empty'), '', 'widget') . Horde::img('delete.png', '', '', $GLOBALS['registry']->getImageDir('horde')) . ' ' . _("Delete All Photos") . '</a></li>';
+            $html .= '<li>' . Horde::link(Horde_Util::addParameter($galleryurl, 'actionID', 'delete'), '', 'widget') . Horde::img('delete.png', '', '', $GLOBALS['registry']->getImageDir('horde')) . ' ' . _("Delete Entire Gallery") . '</a></li>';
+        }
+        $html .= '</ul></div>';
+
+        return $html;
+    }
+
+}
diff --git a/ansel/lib/Widget/Base.php b/ansel/lib/Widget/Base.php
new file mode 100644 (file)
index 0000000..b3d9bbc
--- /dev/null
@@ -0,0 +1 @@
+<?php
diff --git a/ansel/lib/Widget/GalleryFaces.php b/ansel/lib/Widget/GalleryFaces.php
new file mode 100644 (file)
index 0000000..477434d
--- /dev/null
@@ -0,0 +1,99 @@
+<?php
+/**
+ * Horde_Widget_GalleryFaces:: class to display a widget containing mini
+ * thumbnails of faces in the gallery.
+ *
+ * $Horde: ansel/lib/Widget/GalleryFaces.php,v 1.6 2009/07/08 18:28:46 slusarz Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * @author Duck <duck@obala.net>
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ *
+ * @package Ansel
+ */
+class Ansel_Widget_GalleryFaces extends Ansel_Widget {
+
+    /**
+     * @TODO
+     *
+     * @var unknown_type
+     */
+    var $_supported_views = array('Gallery');
+
+    /**
+     * Constructor
+     *
+     * @param array $params  Any parameters for this widget
+     * @return Ansel_Widget_ImageFaces
+     */
+    function Ansel_Widget_GalleryFaces($params)
+    {
+        parent::Ansel_Widget($params);
+        $this->_title = _("People in this gallery");
+    }
+
+    /**
+     * Return the HTML representing this widget.
+     *
+     * @return string  The HTML for this widget.
+     */
+    function html()
+    {   if ($GLOBALS['conf']['faces']['driver']) {
+            $html = $this->_getFaceNames();
+            return $this->_htmlBegin() . $html . $this->_htmlEnd();
+        } else {
+            return '';
+        }
+    }
+
+    /**
+     * Helper function for getting faces for this gallery.
+     *
+     * @return string  The HTML
+     */
+    function _getFaceNames()
+    {
+        if ($this->_view->resource->get('faces')) {
+            return '<div id="faces_widget_content">'
+                    . '<br /><em>' . _("No faces found") . '</em></div>';
+        }
+
+        require_once ANSEL_BASE . '/lib/Faces.php';
+        $faces = Ansel_Faces::factory();
+        if (is_a($faces, 'PEAR_Error')) {
+            return $faces->getMessage();
+        }
+
+        // Check for existing faces for this gallery.
+        $html = '<div style="display: block'
+            . ';background:' . $this->_style['background']
+            . ';width:100%;max-height:300px;overflow:auto;" id="faces_widget_content" >';
+
+        $images = $faces->getGalleryFaces($this->_view->resource->id);
+        if (is_a($images, 'PEAR_Error')) {
+            return $images->getMessage();
+        }
+
+        if ($this->_view->gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+            $link_text = (empty($images) ? _("Find faces") : _("Edit faces"));
+            $html .= '<a id="edit_faces" href="' . Horde_Util::addParameter(Horde::applicationUrl('faces/gallery.php'), 'gallery', $this->_view->gallery->id)
+                    . '" class="widget">' . $link_text . '</a>';
+        }
+
+        $faces_html = '<div id="faces-on-gallery">';
+
+        // Iterate over all the found faces and build the tiles.
+        shuffle($images);
+        foreach ($images as $face_id => $face) {
+            // Get the tile for this face
+            $html .= Ansel_Faces::getFaceTile($face);
+        }
+
+        // Close up the nodes
+        $html .= '</div></div>';
+
+        return $html;
+    }
+
+}
diff --git a/ansel/lib/Widget/Geodata.php b/ansel/lib/Widget/Geodata.php
new file mode 100644 (file)
index 0000000..113e973
--- /dev/null
@@ -0,0 +1,245 @@
+<?php
+/**
+ * Ansel_Widget_Geodata:: class to wrap the display of a Google map showing
+ * images with geolocation data.
+ *
+ * Copyright 2009 The Horde Project (http://www.horde.org)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_Widget_Geodata extends Ansel_Widget {
+
+    var $_supported_views = array('Image', 'Gallery');
+    var $_params = array('default_zoom' => 15,
+                         'max_auto_zoom' => 15);
+
+    function Ansel_Widget_Geodata($params)
+    {
+        parent::Ansel_Widget($params);
+        $this->_title = _("Location");
+    }
+
+    function attach($view)
+    {
+         // Don't even try if we don't have an api key
+        if (empty($GLOBALS['conf']['api']['googlemaps'])) {
+            return false;
+        }
+        parent::attach($view);
+
+        return true;
+    }
+
+    function html()
+    {
+        global $ansel_storage;
+
+        Horde::addScriptFile('prototype.js', 'horde');
+        Horde::addScriptFile('popup.js', 'horde');
+
+        $geodata = $ansel_storage->getImagesGeodata($this->_params['images']);
+        $url = Horde::applicationUrl('map_edit.php', true);
+        $rtext = _("Relocate this image");
+        $dtext = _("Delete geotag");
+
+        $imple = Horde_Ajax_Imple::factory(array('ansel', 'ImageSaveGeotag'), array());
+        $impleUrl = $imple->getUrl();
+
+        $permsEdit = $this->_view->gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT);
+        $viewType = $this->_view->viewType();
+
+        if (count($geodata) == 0 && $viewType != 'Image') {
+            return '';
+        } elseif (count($geodata) == 0) {
+            $noGeotag = true;
+        }
+
+        // Bring in googlemap.js now that we know we need it.
+//        $sfiles = &Ansel_Script_Files::singleton();
+//        $sfiles->addExternalScript('http://maps.google.com/maps?file=api&v=2&sensor=false&key=' . $GLOBALS['conf']['api']['googlemaps']);
+//        $sfiles->addExternalScript('http://gmaps-utility-library.googlecode.com/svn/trunk/markermanager/1.1/src/markermanager.js');
+        Horde::addExternalScriptFile('http://maps.google.com/maps?file=api&v=2&sensor=false&key=' . $GLOBALS['conf']['api']['googlemaps'], 'ansel');
+        Horde::addExternalScriptFile('http://gmaps-utility-library.googlecode.com/svn/trunk/markermanager/1.1/src/markermanager.js', 'ansel');
+        Horde::addScriptFile('googlemap.js');
+
+        $html = $this->_htmlBegin() . "\n";
+        $content = '<div id="ansel_geo_widget">';
+
+        // Add extra information to the JSON data to be sent:
+        foreach ($geodata as $id => $data) {
+            $geodata[$id]['icon'] = Ansel::getImageUrl($geodata[$id]['image_id'], 'mini', true);
+            $geodata[$id]['markerOnly'] = ($viewType == 'Image');
+            $geodata[$id]['link'] = Ansel::getUrlFor('view', array('view' => 'Image',
+                                                                   'gallery' => $this->_view->gallery->id,
+                                                                   'image' => $geodata[$id]['image_id']), true);
+        }
+
+        // If this is an image view, get the other gallery images
+        if ($viewType == 'Image') {
+            $image_id = $this->_view->resource->id;
+            $others = $this->_getGalleryImagesWithGeodata();
+            foreach ($others as $id => $data) {
+                if ($id != $image_id) {
+                    $others[$id]['icon'] = Ansel::getImageUrl($others[$id]['image_id'], 'mini', true);
+                    $others[$id]['link'] = Ansel::getUrlFor('view', array('view' => 'Image',
+                                                                         'gallery' => $this->_view->gallery->id,
+                                                                         'image' => $others[$id]['image_id']), true);
+                } else {
+                    unset($others[$id]);
+                }
+            }
+            $geodata = array_values(array_merge($geodata, $others));
+
+            if (empty($noGeotag)) {
+                $content .= '<div id="ansel_map"></div>';
+                $content .= '<div class="ansel_geolocation">';
+                $content .= '<div id="ansel_locationtext"></div>';
+                $content .= '<div id="ansel_latlng"></div>';
+                $content .= '<div id="ansel_relocate"></div><div id="ansel_deleteGeotag"></div></div>';
+                $content .= '<div id="ansel_map_small"></div>';
+
+            } elseif ($permsEdit) {
+                // Image view, but no geotags, provide ability to add it.
+                $addurl = Horde_Util::addParameter(Horde::applicationUrl('map_edit.php'), 'image', $this->_params['images'][0]);
+                $addLink = Horde::link($addurl, '', '', '', 'popup(\'' . Horde_Util::addParameter(Horde::applicationUrl('map_edit.php'), 'image', $this->_params['images'][0]) . '\', 750, 600); return false;');
+                $imgs = $ansel_storage->getRecentImagesGeodata(Horde_Auth::getAuth());
+                    if (count($imgs) > 0) {
+                        $imgsrc = '<div class="ansel_location_sameas">';
+                        foreach ($imgs as $id => $data) {
+                            if (!empty($data['image_location'])) {
+                                $title = $data['image_location'];
+                            } else {
+                                $title = $this->_point2Deg($data['image_latitude'], true) . ' ' . $this->_point2Deg($data['image_longitude']);
+                            }
+                            $imgsrc .= Horde::link($addurl, $title, '', '', "setLocation('" . $data['image_latitude'] . "', '" . $data['image_longitude'] . "');return false") . '<img src="' . Ansel::getImageUrl($id, 'mini', true) . '" alt="[image]" /></a>';
+                                                    }
+                        $imgsrc .= '</div>';
+                        $content .= sprintf(_("No location data present. Place using %s map %s or click on image to place at the same location."), $addLink, '</a>') . $imgsrc;
+                    } else {
+                        $content .= _("No location data present. You may add some ") . $addLink . _("here") . '</a>';
+                    }
+            } else {
+                // For now, just put up a notice. In future, maybe provide a link
+                // to suggest a location using the Report API?
+                $content .= _("No location data present.");
+            }
+
+        } else {
+            // Gallery view-------------
+            // Avoids undefined error when we build the js function below.
+            $image_id = 0;
+            $content .= '<div id="ansel_map"></div><div id="ansel_locationtext" style="min-height: 20px;"></div><div id="ansel_map_small"></div>';
+
+        }
+
+        $content .= '</div>';
+        $json = Horde_Serialize::serialize(array_values($geodata), Horde_Serialize::JSON);
+        $html .= <<<EOT
+        <script type="text/javascript">
+        var map = {};
+        var pageImages = {$json};
+        options = {
+            smallMap: 'ansel_map_small',
+            mainMap:  'ansel_map',
+            viewType: '{$viewType}',
+            relocateUrl: '{$url}',
+            relocateText: '{$rtext}',
+            deleteGeotagText: '{$dtext}',
+            hasEdit: {$permsEdit},
+            calculateMaxZoom: true,
+            updateEndpoint: '{$impleUrl}',
+            deleteGeotagCallback: function() {deleteLocation();}
+        };
+
+        function setLocation(lat, lng)
+        {
+            params = {
+                type: 'geotag',
+                img: {$image_id},
+                lat: lat,
+                lng: lng};
+
+            url = "{$impleUrl}";
+            new Ajax.Request(url, {
+                method: 'post',
+                parameters: params,
+                onComplete: function(transport) {
+                     if (!Object.isUndefined(ToolTips) ) {
+                         ToolTips.out();
+                     }
+                     if (transport.responseJSON.response == 1) {
+                        w = new Element('div');
+                        w.appendChild(new Element('div', {id: 'ansel_map'}));
+                        ag = new Element('div', {'class': 'ansel_geolocation'});
+                        ag.appendChild(new Element('div', {id: 'ansel_locationtext'}));
+                        ag.appendChild(new Element('div', {id: 'ansel_latlng'}));
+                        ag.appendChild(new Element('div', {id: 'ansel_relocate'}));
+                        ag.appendChild(new Element('div', {id: 'ansel_deleteGeotag'}));
+                        w.appendChild(ag);
+                        w.appendChild(new Element('div', {id: 'ansel_map_small'}));
+                        $('ansel_geo_widget').update(w);
+                        pageImages.unshift({image_id: {$image_id}, image_latitude: lat, image_longitude: lng, image_location:'', markerOnly:true});
+                        doMap(pageImages);
+                     }
+                 }
+            });
+        }
+
+        function deleteLocation() {
+            params = {
+                type: 'untag',
+                img: {$image_id}};
+
+            url = "{$impleUrl}";
+            new Ajax.Request(url, {
+                method: 'post',
+                parameters: params,
+                onComplete: function(transport) {
+                    if (transport.responseJSON.response == 1) {
+                        $('ansel_geo_widget').update(transport.responseJSON.message);
+                    }
+                }
+            });
+
+        }
+
+        function doMap(points) {
+            map = new Ansel_GMap(options);
+            map.getLocationCallback_ = map.getLocationCallback;
+            map.getLocationCallback = function(points, marker) {
+                map.getLocationCallback_(points, marker, Object.isUndefined(points.NoUpdate));
+            }.bind(map);
+            map.addPoints(points);
+            map.display();
+        }
+EOT;
+
+        if (empty($noGeotag)) {
+            $html .= "\n" . 'Event.observe(window, "load", function() {doMap(pageImages);});' . "\n";
+        }
+        $html .= '</script>' . "\n";
+        $html .= $content. $this->_htmlEnd();
+
+        return $html;
+    }
+
+    function _getGalleryImagesWithGeodata()
+    {
+        return $GLOBALS['ansel_storage']->getImagesGeodata(array(), $this->_view->gallery->id);
+    }
+
+    function _point2Deg($value, $lat = false)
+    {
+        $letter = $lat ? ($value > 0 ? "N" : "S") : ($value > 0 ? "E" : "W");
+        $value = abs($value);
+        $deg = floor($value);
+        $min = floor(($value - $deg) * 60);
+        $sec = ($value - $deg - $min / 60) * 3600;
+        return $deg . "&deg; " . $min . '\' ' . round($sec, 2) . '" ' . $letter;
+    }
+
+}
diff --git a/ansel/lib/Widget/ImageFaces.php b/ansel/lib/Widget/ImageFaces.php
new file mode 100644 (file)
index 0000000..acba0ac
--- /dev/null
@@ -0,0 +1,137 @@
+<?php
+/**
+ * Horde_Widget_ImageFaces:: class to display a widget containing mini
+ * thumbnails of faces in the image.
+ *
+ * $Horde: ansel/lib/Widget/ImageFaces.php,v 1.29 2009/07/30 13:15:10 mrubinsk Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * @author Duck <duck@obala.net>
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ *
+ * @package Ansel
+ */
+class Ansel_Widget_ImageFaces extends Ansel_Widget {
+
+    /**
+     * @TODO
+     *
+     * @var unknown_type
+     */
+    var $_supported_views = array('Image');
+
+    /**
+     * Constructor
+     *
+     * @param array $params  Any parameters for this widget
+     * @return Ansel_Widget_ImageFaces
+     */
+    function Ansel_Widget_ImageFaces($params)
+    {
+        parent::Ansel_Widget($params);
+        $this->_title = _("People in this photo");
+    }
+
+    /**
+     * Return the HTML representing this widget.
+     *
+     * @return string  The HTML for this widget.
+     */
+    function html()
+    {
+
+        if ($GLOBALS['conf']['faces']['driver']) {
+            $html = $this->_getFaceNames();
+            return $this->_htmlBegin() . $html . $this->_htmlEnd();
+        } else {
+            return '';
+        }
+    }
+
+    /**
+     * Helper function for getting faces for this image.
+     *
+     * @return string  The HTML
+     */
+    function _getFaceNames()
+    {
+        require_once ANSEL_BASE . '/lib/Faces.php';
+        $faces = Ansel_Faces::factory();
+        if (is_a($faces, 'PEAR_Error')) {
+            return $faces->getMessage();
+        }
+
+        // Check for existing faces for this image.
+        $html = '';
+        $images = $faces->getImageFacesData($this->_view->resource->id, true);
+        if (is_a($images, 'PEAR_Error')) {
+            return $images->getMessage();
+        }
+
+        // Generate the top ajax action links and attach the edit actions. Falls
+        // back on going to the find all faces in gallery page if no js...
+        // although, currently, *that* page requires js as well so...
+        // TODO: A way to 'close', or go back to, the normal widget view.
+        if ($this->_view->gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+            $link_text = (empty($images) ? _("Find faces") : _("Edit faces"));
+            $html .= '<a id="edit_faces" href="' . Horde_Util::addParameter(Horde::applicationUrl('faces/gallery.php'), 'gallery', $this->_view->gallery->id)
+                    . '" class="widget">' . $link_text . '</a> | '
+                    . Horde::link(Horde::applicationUrl(Horde_Util::addParameter('faces/custom.php', array('image' => $this->_view->resource->id, 'url' => $this->_params['selfUrl']))),'', 'widget')
+                    . _("Manual face selection") . '</a>';
+
+            // Attach the ajax edit actions
+            ob_start();
+            $imple = Horde_Ajax_Imple::factory(
+                array('ansel', 'EditFaces'),
+                array('image_id' => $this->_view->resource->id,
+                      'domid' => 'edit_faces',
+                      'selfUrl' => $this->_params['selfUrl']));
+
+            $imple->attach();
+            $html .= ob_get_clean();
+        }
+
+        // Build the main content area of the widget
+        $html .= '<div id="faces_widget_content">';
+        if (empty($images)) {
+            return $html .= '<br /><em>' . _("No faces found") . '</em></div>';
+        }
+
+        // Start the image overlay node to show the face rectangles
+        $faces_html = '<div id="faces-on-image">';
+
+        // Iterate over all the found faces and build the tiles.
+        foreach ($images as $face_id => $face) {
+
+            // Get the tile for this face
+            $html .= Ansel_Faces::getFaceTile($face);
+
+            // Build the overlay for the image
+            $faces_html .= '<div id="facediv' . $face_id . '" class="face-div" style="'
+                . 'width: ' . ($face['face_x2'] - $face['face_x1']) . 'px;'
+                . ' margin-left: ' . $face['face_x1'] . 'px; '
+                . ' height: ' . ($face['face_y2'] - $face['face_y1']) . 'px;'
+                . ' margin-top: ' . $face['face_y1'] . 'px;" >'
+                . '<div id="facedivname' . $face_id . '" class="face-div-name" style="display:none;">'
+                . $face['face_name'] . '</div></div>' . "\n";
+
+            // Attach events to the face tile for showing the overlay
+            $faces_html .= '<script type = "text/javascript">';
+            $faces_html .= '$(\'facediv' . $face_id . '\').observe(\'mouseover\', function() {showFace(' . $face_id . ')});'
+                . '$(\'facediv' . $face_id . '\').observe(\'mouseout\', function() {hideFace(' . $face_id . ')});'
+                . '$(\'face' . $face_id . '\').firstDescendant().observe(\'mouseover\', function() {showFace(' . $face_id . ')});'
+                . '$(\'face' . $face_id . '\').firstDescendant().observe(\'mouseout\', function() {hideFace(' . $face_id . ')});'
+                . "\n</script>\n";
+        }
+
+        // Close up the nodes
+        $html .= $faces_html . '</div></div>';
+
+        // Include the needed javascript
+        Horde::addScriptFile('imagefaces.js', 'ansel', true);
+
+        return $html;
+    }
+
+}
diff --git a/ansel/lib/Widget/Links.php b/ansel/lib/Widget/Links.php
new file mode 100644 (file)
index 0000000..f69381d
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+/**
+ * Ansel_Widget_links:: class to wrap the display of various feed links etc...
+ *
+ * $Horde: ansel/lib/Widget/Links.php,v 1.16 2009/07/30 18:02:15 mrubinsk Exp $
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_Widget_Links extends Ansel_Widget {
+
+    var $_supported_views = array('Gallery', 'Image');
+
+    function Ansel_Widget_Links($params)
+    {
+        parent::Ansel_Widget($params);
+        $this->_title = _("Links");
+    }
+
+    function html()
+    {
+        global $registry;
+
+        $feedurl = Horde::url('rss.php', true);
+        $owner = $this->_view->gallery->get('owner');
+        $html = $this->_htmlBegin();
+        $html .= Horde::link(Ansel::getUrlFor('rss_user', array('owner' => $owner))) . Horde::img('feed.png', '', '', $registry->getImageDir('horde')) . ' ' . sprintf(_("Recent photos by %s"), $owner) . '</a>';
+        $slug = $this->_view->gallery->get('slug');
+        $html .= '<br />' . Horde::link(Ansel::getUrlFor('rss_gallery', array('gallery' => $this->_view->gallery->id, 'slug' => $slug))) . ' ' .  Horde::img('feed.png', '', '', $registry->getImageDir('horde')) . ' ' . sprintf(_("Recent photos in %s"), htmlspecialchars($this->_view->gallery->get('name'), ENT_COMPAT, Horde_Nls::getCharset())) . '</a>';
+
+        /* Embed html */
+
+        if (empty($this->_view->_params['image_id'])) {
+            /* Gallery view */
+            $params = array('count' => 10);
+            if (!empty($slug))  {
+                $params['gallery_slug'] = $slug;
+            } else {
+                $params['gallery_id'] = $this->_view->gallery->id;
+            }
+        } else {
+            // This is an image view
+            $params = array('thumbsize' => 'screen',
+                            'images' => $this->_view->_params['image_id'],
+                            'count' => 10);
+
+        }
+
+        $embed = htmlentities(Ansel::embedCode($params));
+
+        $html .= '<div class="embedInput">' . _("Embed: ") . '<br /><input type="text" readonly="readonly" value="' . $embed
+            . '" /></div>';
+
+        $html .= $this->_htmlEnd();
+        return $html;
+    }
+
+}
+?>
diff --git a/ansel/lib/Widget/OtherGalleries.php b/ansel/lib/Widget/OtherGalleries.php
new file mode 100644 (file)
index 0000000..97890ab
--- /dev/null
@@ -0,0 +1,128 @@
+<?php
+/**
+ * Ansel_Widget_OtherGalleries:: class to display a widget containing mini
+ * thumbnails and links to other galleries owned by the same user as the
+ * currently viewed image/gallery.
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_Widget_OtherGalleries extends Ansel_Widget {
+
+    /**
+     * Override the parent class' attach method and set the owner in the
+     * title string.
+     *
+     * @param Ansel_View $view  The view we are attaching to
+     */
+    function attach($view)
+    {
+        parent::attach($view);
+
+        $owner = $this->_view->gallery->getOwner();
+        $name = $owner->getValue('fullname');
+        if (!$name) {
+            $name = $this->_view->gallery->get('owner');
+        }
+        $this->_title = sprintf(_("%s's Galleries"), $name);
+
+        return true;
+    }
+
+    /**
+     * Build the HTML for this widget.
+     *
+     * @return string  The HTML representing this widget.
+     */
+    function html()
+    {
+         if ($GLOBALS['conf']['ansel_cache']['usecache'] &&
+             ($widget = $GLOBALS['cache']->get('Ansel_OtherGalleries' . $this->_view->gallery->get('owner'))) !== false) {
+                 return  $widget;
+         }
+
+         $widget = $this->_htmlBegin() . $this->_getOtherGalleries() . $this->_htmlEnd();
+         if ($GLOBALS['conf']['ansel_cache']['usecache']) {
+             $GLOBALS['cache']->set('Ansel_OtherGalleries' . $this->_view->gallery->get('owner'), $widget);
+         }
+
+         return $widget;
+    }
+
+    /**
+     * Build the HTML for the other galleries widget content.
+     *
+     * @TODO Allow the sort order and maybe the count of galleries returned
+     *       to be configurable via the params array.
+     *
+     * @return string  The HTML
+     */
+    function _getOtherGalleries()
+    {
+        require_once 'Horde/Tree.php';
+
+        global $prefs;
+
+        $owner = $this->_view->gallery->get('owner');
+
+        /* Set up the tree */
+        $tree = Horde_Tree::singleton('otherAnselGalleries_' . md5($owner), 'javascript');
+        $tree->setOption(array('class' => 'anselWidgets'));
+        $gals = $GLOBALS['ansel_storage']->listGalleries(PERMS_SHOW, $owner,
+                                                         null, true, 0, 0,
+                                                         'name', 0);
+
+        $html = '<div style="display:'
+            . (($prefs->getValue('show_othergalleries')) ? 'block' : 'none')
+            . ';background:' . $this->_style['background']
+            . ';width:100%;max-height:300px;overflow:auto;" id="othergalleries" >';
+
+        foreach($gals as $gal) {
+            if (is_a($gal, 'PEAR_Error')) {
+                Horde::logMessage($gal, __FILE__, __LINE__, PEAR_LOG_ERR);
+                return '';
+            }
+
+            $parents = $gal->get('parents');
+            if (empty($parents)) {
+                $parent = null;
+            } else {
+                $parents = explode(':', $parents);
+                $parent = array_pop($parents);
+            }
+
+            $img = Ansel::getImageUrl($gal->getDefaultImage('ansel_default'), 'mini', true);
+            $link = Ansel::getUrlFor('view', array('gallery' => $gal->id,
+                                                   'slug' => $gal->get('slug'),
+                                                   'view' => 'Gallery'),
+                                     true);
+
+            $tree->addNode($gal->id, $parent, $gal->get('name'), null,
+                           ($gal->id == $this->_view->gallery->id),
+                           array('icon' => $img, 'icondir' => '', 'url' => $link));
+        }
+        ob_start();
+        $imple = Horde_Ajax_Imple::factory(array('ansel', 'ToggleOtherGalleries'), array('bindTo' => 'othergalleries'));
+        $imple->attach();
+
+        $tree->sort('label');
+        $tree->renderTree();
+        $html .= ob_get_clean();
+        $html .= '</div>';
+        $selfurl = Horde::selfUrl(true, true);
+        $html .=  '<div class="control"><a href="'
+                 . Horde_Util::addParameter($selfurl, 'actionID',
+                                     'show_actions')
+                 . '" id="othergalleries-toggle" class="'
+                 . (($GLOBALS['prefs']->getValue('show_othergalleries'))
+                 ? 'hide'
+                 : 'show') . '">&nbsp;</a></div>' . "\n";
+
+
+
+        return $html;
+    }
+
+}
diff --git a/ansel/lib/Widget/OwnerFaces.php b/ansel/lib/Widget/OwnerFaces.php
new file mode 100644 (file)
index 0000000..bc4476f
--- /dev/null
@@ -0,0 +1,69 @@
+<?php
+/**
+ * Ansel_Widget_OwnerFaces:: class to display a widget containing mini
+ * thumbnails of faces that have been tagged by the gallery owner.
+ *
+ * @author Duck <duck@obala.net>
+ * @package Ansel
+ */
+class Ansel_Widget_OwnerFaces extends Ansel_Widget {
+
+    var $_faces;
+    var $_count;
+    var $_owner;
+
+    /**
+     * Constructor
+     *
+     * @param array $params  Any parameters for this widget
+     * @return Ansel_Widget_ImageFaces
+     */
+    function Ansel_Widget_OwnerFaces($params)
+    {
+        parent::Ansel_Widget($params);
+
+        require_once ANSEL_BASE . '/lib/Faces.php';
+        $this->_faces = Ansel_Faces::factory();
+    }
+
+    /**
+     * Return the HTML representing this widget.
+     *
+     * @return string  The HTML for this widget.
+     */
+    function html()
+    {
+        if (!$GLOBALS['conf']['faces']['driver']) {
+            return '';
+        }
+
+        $this->_owner = $this->_view->gallery->get('owner');
+        $this->_count = $this->_faces->countOwnerFaces($this->_owner);
+        if (is_a($this->_count, 'PEAR_error')) {
+            $this->_count = 0;
+        }
+
+        $this->_title = '<a href="' . Horde_Util::addParameter(Horde::applicationUrl('faces/search/owner.php'), 'owner', $this->_owner) . '">'
+            . sprintf(_("People in galleries owned by %s (%d of %d)"),
+                      $this->_owner, min(12, $this->_count), number_format($this->_count))
+            . '</a>';
+        $html = $this->_htmlBegin();
+
+        if (empty($this->_count)) {
+            return null;
+        }
+
+        $results = $this->_faces->ownerFaces($this->_owner, 0, 12, true);
+        $html .= '<div style="display: block'
+            . ';background:' . $this->_style['background']
+            . ';width:100%;max-height:300px;overflow:auto;" id="faces_widget_content" >';
+        foreach ($results as $face_id => $face) {
+            $facename = htmlspecialchars($face['face_name']);
+            $html .= '<a href="' . $this->_faces->getLink($face) . '" title="' . $facename . '">'
+                    . '<img src="' . $this->_faces->getFaceUrl($face['image_id'], $face_id, 'mini')
+                    . '" style="padding-bottom: 5px; padding-left: 5px" alt="' . $facename . '" /></a>';
+        }
+
+        return $html . '</div>' . $this->_htmlEnd();
+    }
+}
diff --git a/ansel/lib/Widget/SimilarPhotos.php b/ansel/lib/Widget/SimilarPhotos.php
new file mode 100755 (executable)
index 0000000..5ad72b8
--- /dev/null
@@ -0,0 +1,97 @@
+<?php
+/**
+ * Horde_Widget_SimilarPhotos:: class to display a widget containing mini
+ * thumbnails of images that are similar, based on tags.
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_Widget_SimilarPhotos extends Ansel_Widget {
+
+    /**
+     * @TODO
+     *
+     * @var unknown_type
+     */
+    var $_supported_views = array('Image');
+
+    /**
+     * Constructor
+     *
+     * @param array $params  Any parameters for this widget
+     * @return Ansel_Widget_SimilarPhotos
+     */
+    function Ansel_Widget_SimilarPhotos($params)
+    {
+        parent::Ansel_Widget($params);
+        $this->_title = _("Similar Photos");
+    }
+
+    /**
+     * Return the HTML representing this widget.
+     *
+     * @return string  The HTML for this widget.
+     */
+    function html()
+    {
+        $html = $this->_htmlBegin();
+        $html .= '<div id="similar">' . $this->_getRelatedImages() . '</div>';
+        $html .= $this->_htmlEnd();
+        return $html;
+    }
+
+    /**
+     * Helper function for generating a widget of images related to this one.
+     *
+     * @TODO Rethink the way we determine if an image is related. This one is
+     *       not ideal, as it just pops tags off the tag list until all the tags
+     *       match. This could miss many related images.
+     *
+     * @return string  The HTML
+     */
+    function _getRelatedImages()
+    {
+        require_once ANSEL_BASE . '/lib/Tags.php';
+        global $ansel_storage;
+
+        $html = '';
+        $tags = array_values($this->_view->resource->getTags());
+        $imgs = Ansel_Tags::searchTags($tags);
+
+        while (count($imgs['images']) <= 5 && count($tags)) {
+            array_pop($tags);
+            $newImgs = Ansel_Tags::searchTags($tags);
+            $imgs['images'] = array_merge($imgs['images'], $newImgs['images']);
+        }
+        if (count($imgs['images'])) {
+            $i = 0;
+            foreach ($imgs['images'] as $imgId) {
+                if ($i >= min(count($imgs['images']), 5)) {
+                    break;
+                }
+                if ($imgId != $this->_view->resource->id) {
+                    $rImg = &$ansel_storage->getImage($imgId);
+                    if (is_a($rImg, 'PEAR_Error')) {
+                        continue;
+                    }
+                    $rGal = $ansel_storage->getGallery($rImg->gallery);
+                    if (is_a($rGal, 'PEAR_Error')) {
+                        continue;
+                    }
+                    $title = sprintf(_("%s from %s"), $rImg->filename, $rGal->get('name'));
+                    $html .= Horde::link(
+                        Ansel::getUrlFor('view',
+                                         array('image' => $imgId,
+                                               'view' => 'Image',
+                                               'gallery' => $rImg->gallery,
+                                               'slug' => $rGal->get('slug')),
+                                         true),
+                        $title)
+                    . '<img src="'. Ansel::getImageUrl($imgId, 'mini', true) . '" alt="' . htmlspecialchars($rImg->filename) . '" /></a>';
+                    $i++;
+                }
+            }
+        }
+        return $html;
+    }
+}
diff --git a/ansel/lib/Widget/Tags.php b/ansel/lib/Widget/Tags.php
new file mode 100644 (file)
index 0000000..8b3f7fc
--- /dev/null
@@ -0,0 +1,98 @@
+<?php
+require_once ANSEL_BASE . '/lib/Tags.php';
+
+/**
+ * Ansel_Widget_Tags:: class to display a tags widget in the image and gallery
+ * views.
+ *
+ * $Horde: ansel/lib/Widget/Tags.php,v 1.15 2009/07/28 15:15:04 mrubinsk Exp $
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+class Ansel_Widget_Tags extends Ansel_Widget {
+
+    var $_resourceType;
+
+    function Ansel_Widget_Tags($params)
+    {
+        parent::Ansel_Widget($params);
+        $this->_resourceType = $params['view'];
+        $this->_title = _("Tags");
+    }
+
+    /**
+     * Build the HTML for this widget
+     *
+     * @return string  The HTML representing this widget.
+     */
+    function html()
+    {
+        if ($this->_resourceType == 'image') {
+            $image_id = $this->_view->resource->id;
+        } else {
+            $image_id = null;
+        }
+
+        /* Build the tag widget */
+        $html = $this->_htmlBegin();
+        $html .= '<div id="tags">' . $this->_getTagHTML() . '</div>';
+        if ($this->_view->gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+            ob_start();
+
+            /* Attach the Ajax action */
+            $imple = Horde_Ajax_Imple::factory(array('ansel', 'TagActions'),
+                                               array('bindTo' => array('add' => 'tagbutton'),
+                                                     'gallery' => $this->_view->gallery->id,
+                                                     'image' => $image_id));
+            $imple->attach();
+            $html .= ob_get_clean();
+
+            // JS fallback is getting refactoring into xrequest.php
+            $actionUrl = Horde_Util::addParameter('image.php',
+                                                  array('image' => $this->_view->resource->id,
+                                                        'gallery' => $this->_view->gallery->id));
+            $html .= '<form name="tagform" action="' . $actionUrl . '" onsubmit="return !addTag();" method="post">';
+            $html .= '<input id="addtag" name="addtag" type="text" size="15" /> <input onclick="return !addTag();" name="tagbutton" id="tagbutton" class="button" value="' . _("Add") . '" type="submit" />';
+            $html .= '</form>';
+        }
+        $html .= $this->_htmlEnd();
+
+        return $html;
+    }
+
+
+    /**
+     * Helper function to build the list of tags
+     *
+     * @return string  The HTML representing the tag list.
+     */
+    function _getTagHTML()
+    {
+        global $registry;
+
+            /* Clear the tag cache? */
+        if (Horde_Util::getFormData('havesearch', 0) == 0) {
+            Ansel_Tags::clearSearch();
+        }
+
+        // TODO - Degrade the delete links to work without js
+        $hasEdit = $this->_view->gallery->hasPermission(Horde_Auth::getAuth(),
+                                                        PERMS_EDIT);
+        $owner = $this->_view->gallery->get('owner');
+        $tags = $this->_view->resource->getTags();
+        if (count($tags)) {
+            $tags = Ansel_Tags::listTagInfo(array_keys($tags));
+        }
+
+        $links = Ansel_Tags::getTagLinks($tags, 'add', $owner);
+        $html = '<ul>';
+        foreach ($tags as $tag_id => $taginfo) {
+            $html .= '<li>' . Horde::link($links[$tag_id], sprintf(ngettext("%d photo", "%d photos", $taginfo['total']), $taginfo['total'])) . $taginfo['tag_name'] . '</a>' . ($hasEdit ? '<a href="#" onclick="removeTag(' . $tag_id . ');">' . Horde::img('delete-small.png', _("Remove Tag"), '', $registry->getImageDir('horde')) . '</a>' : '') . '</li>';
+        }
+        $html .= '</ul>';
+
+        return $html;
+    }
+
+}
\ No newline at end of file
diff --git a/ansel/lib/XPPublisher.php b/ansel/lib/XPPublisher.php
new file mode 100644 (file)
index 0000000..d38f2b4
--- /dev/null
@@ -0,0 +1,57 @@
+<?php
+/**
+ * XPPublisher Wizard
+ *
+ * $Horde: ansel/lib/XPPublisher.php,v 1.3 2009/01/14 19:22:06 mrubinsk Exp $
+ *
+ * @package Ansel
+ */
+
+/**
+ * Builds a "Publish this file/folder to the web" handler for Windows XP+.
+ *
+ * @package Ansel
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ */
+class Horde_XPPublisher {
+
+    /**
+     * Generates a Windows Registry file that a user can double-click
+     * to add your provider to their list of Providers for the
+     * Publishing Wizard.
+     *
+     * @param string $appKey The unique name of your application. If
+     * people can use your application on multiple servers, make sure
+     * to include a hostname or something else server-specific in this
+     * string.
+     *
+     * @param string $displayName The name of your service in the
+     * Providers list.
+     *
+     * @param string $description Shows up as the description (2nd
+     * line) of your service in the Providers list.
+     *
+     * @param string $href The address of the wizard interface.
+     *
+     * @param string $icon The location of an icon for the
+     * service. Usually displayed at 32x32, will be scaled if it's not
+     * that size.
+     */
+    function sendRegFile($appKey, $displayName, $description, $href, $icon)
+    {
+        require_once 'Horde/Browser.php';
+        $browser = Horde_Browser::singleton();
+        $browser->downloadHeaders('install_registry.reg', 'application/octet-stream');
+
+        $lines = array(
+            'Windows Registry Editor Version 5.00',
+            '',
+            '[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\PublishingWizard\PublishingWizard\Providers\\' . $appKey . ']',
+            '"displayname"="' . $displayName . '"',
+            '"description"="' . $description . '"',
+            '"href"="' . $href . '"',
+            '"icon"="' . $icon . '"');
+        echo implode("\r\n", $lines) . "\r\n";
+    }
+
+}
diff --git a/ansel/lib/api.php b/ansel/lib/api.php
new file mode 100644 (file)
index 0000000..2511c3a
--- /dev/null
@@ -0,0 +1,1347 @@
+<?php
+/**
+ * Ansel external API interface.
+ *
+ * $Horde: ansel/lib/api.php,v 1.139 2009/07/28 20:16:50 jan Exp $
+ *
+ * Copyright 2004-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Jan Schneider <jan@horde.org>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ *
+ * @package Ansel
+ */
+
+$_services['perms'] = array(
+    'args' => array(),
+    'type' => '{urn:horde}stringArray'
+);
+
+$_services['browse'] = array(
+    'args' => array('path' => 'string'),
+    'type' => '{urn:horde}hashHash',
+);
+
+$_services['put'] = array(
+    'args' => array('path' => 'string', 'content' => 'string', 'content_type' => 'string'),
+    'type' => '{urn:horde}stringArray',
+);
+
+// $_services['path_delete'] = array(
+//     'args' => array('path' => 'string'),
+//     'type' => 'boolean',
+// );
+
+$_services['commentCallback'] = array(
+    'args' => array('image_id' => 'string'),
+    'type' => 'string'
+);
+
+$_services['hasComments'] = array(
+    'args' => array(),
+    'type' => 'boolean'
+);
+
+$_services['saveImage'] = array(
+    'args' => array('app'          => 'string',
+                    'gallery_id'   => 'string',
+                    'image'        => '{urn:horde}hashHash',
+                    'default'      => 'boolean',
+                    'gallery_data' => '{urn:horde}hashHash',
+                    'encoding'     => 'string',
+                    'slug'         => 'string',
+                    'compression'  => 'string',
+                    'skiphook'     => 'boolean'),
+    'type' => '{urn:horde}stringArray'
+);
+
+$_services['postBatchUpload'] = array(
+    'args' => array('image_ids' => '{urn:horde}hash'),
+    'type' => 'int'
+);
+
+$_services['createGallery'] = array(
+    'args' => array('app'        => 'string',
+                    'attributes' => '{urn:horde}hashHash',
+                    'perm'       => '{urn:horde}hashHash'),
+    'type' => 'int'
+);
+
+$_services['removeImage'] = array(
+    'args' => array('app'        => 'string',
+                    'gallery_id' => 'integer',
+                    'image_id'   => 'integer'),
+    'type' => 'int'
+);
+
+$_services['removeGallery'] = array(
+    'args' => array('app'        => 'string',
+                    'gallery_id' => 'integer'),
+    'type' => 'int'
+);
+
+$_services['getImageUrl'] = array(
+    'args' => array('app'        => 'string',
+                    'image_id'   => 'integer',
+                    'view'       => 'string',
+                    'full'       => 'boolean',
+                    'style'      => 'string'),
+    'type' => 'string'
+);
+
+$_services['getImageContent'] = array(
+    'args' => array('image_id'   => 'integer',
+                    'view'       => 'string',
+                    'style'      => 'string',
+                    'app'        => 'string'),
+    'type' => 'string'
+);
+
+$_services['count'] = array(
+    'args' => array('app'        => 'string',
+                    'gallery_id' => 'integer'),
+    'type' => 'int'
+);
+
+$_services['getDefaultImage'] = array(
+    'args' => array('app'        => 'string',
+                    'gallery_id' => 'integer',
+                    'style'      => 'string'),
+    'type' => 'string'
+);
+
+$_services['listGalleries'] = array(
+    'args' => array('app'        => 'string',
+                    'perm'       => 'integer',
+                    'parent'     => 'string',
+                    'allLevels'  => 'string',
+                    'from'       => 'integer',
+                    'count'      => 'integer'),
+    'type' => 'string'
+);
+
+$_services['getGalleries'] = array(
+    'args' => array('ids' => '{urn:horde}hash',
+                    'app' => 'string'),
+    'type' => '{urn:horde}hash'
+);
+
+$_services['selectGalleries'] = array(
+    'args' => array('app'        => 'string',
+                    'perm'       => 'integer',
+                    'parent'     => 'string',
+                    'allLevels'  => 'string',
+                    'from'       => 'integer',
+                    'count'      => 'integer'),
+    'type' => 'string'
+);
+
+$_services['listImages'] = array(
+    'args' => array('app'        => 'string',
+                    'gallery_id' => 'integer',
+                    'perm'       => 'integer',
+                    'view'       => 'string',
+                    'full'       => 'boolean',
+                    'from'       => 'integer',
+                    'count'      => 'integer',
+                    'style'      => 'string'),
+    'type' => 'string'
+);
+
+$_services['getRecentImages'] = array(
+    'args' => array('app' => 'string',
+                    'galleries' => '{urn:horde}hash',
+                    'perms' => 'integer',
+                    'view' => 'string',
+                    'full' => 'boolean',
+                    'limit' => 'integer',
+                    'style' => 'string',
+                    'slugs' => '{urn:horde}hashHash'),
+    'type' => '{urn:horde}hash'
+);
+
+$_services['countGalleries'] = array(
+    'args' => array('app'        => 'string',
+                    'perm'       => 'string',
+                    'attributes' => '{urn:horde}hash',
+                    'parent'     => 'string',
+                    'allLevels'  => 'boolean'),
+    'type' => 'int'
+);
+
+$_services['listTagInfo'] = array(
+    'args' => array('tags' => '{urn:horde}stringArray'),
+    'type' => '{urn:horde}hash'
+);
+
+$_services['searchTags'] = array(
+    'args' => array('tags' => '{urn:horde}stringArray',
+                    'resource_type' => 'string',
+                    'count' => 'int',
+                    'user' => 'string'),
+    'type' => '{urn:horde}hash'
+);
+
+$_services['galleryExists'] = array(
+    'args' => array('app' => 'string',
+                    'gallery_name' => 'string'),
+    'type' => 'boolean'
+);
+
+$_services['renderView'] = array(
+    'args' => array('parameters' => '{urn:horde]stringArray',
+                    'app' => 'string',
+                    'view' => 'string'),
+    'type' => 'string'
+);
+
+$_services['getGalleryStyles'] = array(
+    'args' => array(),
+    'type' => '{urn:horde}hash');
+
+/**
+ * Returns a list of available permissions.
+ *
+ * @return array  An array describing all available permissions.
+ */
+function _ansel_perms()
+{
+    $perms = array();
+    $perms['tree']['ansel']['admin'] = false;
+    $perms['title']['ansel:admin'] = _("Administrators");
+
+    return $perms;
+}
+
+/**
+ * Browse through Ansel's gallery tree.
+ *
+ * @param string $path       The level of the tree to browse.
+ * @param array $properties  The item properties to return. Defaults to 'name',
+ *                           'icon', and 'browseable'.
+ *
+ * @return array  The contents of $path
+ */
+function _ansel_browse($path = '', $properties = array())
+{
+    require_once dirname(__FILE__) . '/base.php';
+
+    // Default properties.
+    if (!$properties) {
+        $properties = array('name', 'icon', 'browseable');
+    }
+
+    if (substr($path, 0, 5) == 'ansel') {
+        $path = substr($path, 5);
+    }
+    $path = trim($path, '/');
+    $parts = explode('/', $path);
+
+    if (empty($path)) {
+        $owners = array();
+        $galleries = $GLOBALS['ansel_storage']->listGalleries(PERMS_SHOW, null, null, false);
+        foreach ($galleries  as $gallery) {
+            $owners[$gallery->data['share_owner']] = true;
+        }
+
+        $results = array();
+        foreach (array_keys($owners) as $owner) {
+            if (in_array('name', $properties)) {
+                $results['ansel/' . $owner]['name'] = $owner;
+            }
+            if (in_array('icon', $properties)) {
+                $results['ansel/' . $owner]['icon'] =
+                    $registry->getImageDir('horde') . '/user.png';
+            }
+            if (in_array('browseable', $properties)) {
+                $results['ansel/' . $owner]['browseable'] = true;
+            }
+            if (in_array('contenttype', $properties)) {
+                $results['ansel/' . $owner]['contenttype'] =
+                    'httpd/unix-directory';
+            }
+            if (in_array('contentlength', $properties)) {
+                $results['ansel/' . $owner]['contentlength'] = 0;
+            }
+            if (in_array('modified', $properties)) {
+                $results['ansel/' . $owner]['modified'] = time();
+            }
+            if (in_array('created', $properties)) {
+                $results['ansel/' . $owner]['created'] = 0;
+            }
+        }
+        return $results;
+
+    } else {
+        if (count($parts) == 1) {
+            // This request is for all galleries owned by the requested user.
+            $galleries = $GLOBALS['ansel_storage']->listGalleries(
+                PERMS_SHOW, $parts[0], null, false);
+            $images = array();
+        } elseif (_ansel_galleryExists(null, end($parts))) {
+            // This request if for a certain gallery, list all sub-galleries
+            // and images.
+            $gallery_id = end($parts);
+            $galleries = $GLOBALS['ansel_storage']->getGalleries(
+                array($gallery_id));
+            if (!isset($galleries[$gallery_id]) ||
+                !$galleries[$gallery_id]->hasPermission(Horde_Auth::getAuth(),
+                                                        PERMS_READ)) {
+                return PEAR::raiseError(_("Invalid gallery specified."), 404);
+            }
+            $galleries = $GLOBALS['ansel_storage']->listGalleries(
+                PERMS_SHOW, null, $gallery_id, false);
+
+            $images = _ansel_listImages(null, $gallery_id, PERMS_SHOW, 'mini');
+        } elseif (count($parts) > 2 &&
+                  _ansel_galleryExists(null, $parts[count($parts) - 2]) &&
+                  !is_a($image = $GLOBALS['ansel_storage']->getImage(end($parts)), 'PEAR_Error')) {
+            return array('data' => $image->raw(),
+                         'mimetype' => $image->type,
+                         'mtime' => $image->uploaded);
+        } else {
+            return PEAR::raiseError(_("File not found."), 404);
+        }
+
+        $results = array();
+        foreach ($galleries as $galleryId => $gallery) {
+            $retpath = 'ansel/' . implode('/', $parts) . '/' . $galleryId;
+            if (in_array('name', $properties)) {
+                $results[$retpath]['name'] = $gallery->data['attribute_name'];
+            }
+            if (in_array('displayname', $properties)) {
+                $results[$retpath]['displayname'] = rawurlencode(
+                    $gallery->data['attribute_name']);
+            }
+            if (in_array('icon', $properties)) {
+                $results[$retpath]['icon'] = $registry->getImageDir()
+                    . '/ansel.png';
+            }
+            if (in_array('browseable', $properties)) {
+                $results[$retpath]['browseable'] = $gallery->hasPermission(
+                    Horde_Auth::getAuth(), PERMS_READ);
+            }
+            if (in_array('contenttype', $properties)) {
+                $results[$retpath]['contenttype'] = 'httpd/unix-directory';
+            }
+            if (in_array('contentlength', $properties)) {
+                $results[$retpath]['contentlength'] = 0;
+            }
+            if (in_array('modified', $properties)) {
+                $results[$retpath]['modified'] = time();
+            }
+            if (in_array('created', $properties)) {
+                $results[$retpath]['created'] = 0;
+            }
+        }
+
+        foreach ($images as $imageId => $image) {
+            $retpath = 'ansel/' . implode('/', $parts) . '/' . $imageId;
+            if (in_array('name', $properties)) {
+                $results[$retpath]['name'] = $image['name'];
+            }
+            if (in_array('displayname', $properties)) {
+                $results[$retpath]['displayname'] = rawurlencode($image['name']);
+            }
+            if (in_array('icon', $properties)) {
+                $results[$retpath]['icon'] = Horde::url($image['url'], true);
+            }
+            if (in_array('browseable', $properties)) {
+                $results[$retpath]['browseable'] = false;
+            }
+            if (in_array('contenttype', $properties)) {
+                $results[$retpath]['contenttype'] = $image['type'];
+            }
+            if (in_array('contentlength', $properties)) {
+                $results[$retpath]['contentlength'] = 0;
+            }
+            if (in_array('modified', $properties)) {
+                $results[$retpath]['modified'] = $image['uploaded'];
+            }
+            if (in_array('created', $properties)) {
+                $results[$retpath]['created'] = $image['uploaded'];
+            }
+        }
+        return $results;
+
+    }
+
+    return PEAR::raiseError(_("File not found."), 404);
+}
+
+/**
+ * Saves an image into the gallery tree.
+ *
+ * @param string $path          The path where to PUT the file.
+ * @param string $content       The file content.
+ * @param string $content_type  The file's content type.
+ *
+ * @return array  The event UIDs, or a PEAR_Error on failure.
+ */
+function _ansel_put($path, $content, $content_type)
+{
+    require_once dirname(__FILE__) . '/base.php';
+
+    if (substr($path, 0, 5) == 'ansel') {
+        $path = substr($path, 9);
+    }
+    $path = trim($path, '/');
+    $parts = explode('/', $path);
+
+    if (count($parts) < 3) {
+        return PEAR::raiseError("Gallery does not exist");
+    }
+    $image_name = array_pop($parts);
+    $gallery_id = end($parts);
+    if (!$GLOBALS['ansel_storage']->galleryExists($gallery_id)) {
+        return PEAR::raiseError("Gallery does not exist");
+    }
+    $gallery = $GLOBALS['ansel_storage']->getGallery($gallery_id);
+    if (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+        return PEAR::raiseError(_("Access denied adding photos to \"%s\"."));
+    }
+
+    return $gallery->addImage(array('image_type' => $content_type,
+                                    'image_filename' => $image_name,
+                                    'image_caption' => '',
+                                    'data' => $content));
+}
+
+/**
+ * Callback for Agora comments.
+ *
+ * @param integer $image_id  Image id to check
+ *
+ * @return mixed Image filename on success | false on failure
+ */
+function _ansel_commentCallback($image_id)
+{
+    require_once dirname(__FILE__) . '/base.php';
+
+    if (!$GLOBALS['conf']['comments']['allow']) {
+        return false;
+    }
+
+    $image = $GLOBALS['ansel_storage']->getImage($image_id);
+    if (!$image || is_a($image, 'PEAR_Error')) {
+        return false;
+    }
+
+    return $image->filename;
+}
+
+/**
+ * Checks if applications allows comments
+ *
+ * @return boolean
+ */
+function _ansel_hasComments()
+{
+    if (($GLOBALS['conf']['comments']['allow'] == 'all' ||
+        ($GLOBALS['conf']['comments']['allow'] == 'authenticated' &&
+         Horde_Auth::getAuth()))) {
+        return true;
+    } else {
+        return false;
+    }
+}
+
+/**
+ * Returns decoded image data
+ *
+ * @param string $data         The id of the image.
+ * @param string $encoding     The encoding type for the image data.
+ *                             (none, base64, or binhex)
+ * @param string $compression  The compression type for the image data.
+ *                             (none, gzip, or lzf)
+ * @param boolean $upload      Process direction (true of encode/compress or false if decode/decompress)
+ *
+ * @return string  The image path.
+ */
+function _getImageData($data, $encoding = 'none', $compression = 'none', $upload = true)
+{
+    switch ($encoding) {
+    case 'base64':
+        $data = $upload ? base64_decode($data) : base64_encode($data);
+        break;
+
+    case 'binhex':
+        $data = $upload ? pack('H*', $data) : unpack('H*', $data);
+    }
+
+    switch ($compression) {
+    case 'gzip':
+        if (Horde_Util::loadExtension('zlib')) {
+            return $upload ? gzuncompress($data) : gzcompress($data);
+        }
+        break;
+
+    case 'lzf':
+        if (Horde_Util::loadExtension('lzf')) {
+            return $upload ? lzf_decompress($data) : lzf_compress($data);
+        }
+        break;
+
+    default:
+        return $data;
+    }
+}
+
+/**
+ * Stores an image in a gallery and returns gallery and image data.
+ *
+ * @param integer $app         Application used if null then use default.
+ * @param integer $gallery_id  The gallery id to add the image to.
+ * @param array $image         Image data array.  This can either be the return
+ *                             from Horde_Form_Type_image:: or an array with
+ *                             the following four fields:
+ *                             'filename', 'description', 'data', 'type'
+ * @param integer $default     Set this image as default in the gallery?
+ * @param array $gallery_data  Any gallery parameters to change at this time.
+ * @param string $encoding     The encoding type for the image data.
+ *                             (none, base64, or binhex)
+ * @param string $slug         Use gallery slug instead of id. (Pass '0' or null
+ *                             to gallery_id parameter).
+ * @param string $compression  The compression type for the image data.
+ *                             (none, gzip, or lzf)
+ *
+ * @return mixed  An array of image/gallery data || PEAR_Error
+ */
+function _ansel_saveImage($app = null, $gallery_id, $image, $default = false,
+                          $gallery_data = null, $encoding = null, $slug = null,
+                          $compression = 'none', $skiphook = false)
+{
+    require_once dirname(__FILE__) . '/base.php';
+
+    $image_data = null;
+
+    /* If no app is given use Ansel's own gallery which is initialized
+     * in base.php */
+    if (!is_null($app)) {
+        $GLOBALS['ansel_storage'] = new Ansel_Storage($app);
+    }
+
+    if (isset($image['filename']) &&
+        isset($image['description']) &&
+        isset($image['data']) &&
+        isset($image['type'])) {
+        Horde::logMessage(sprintf("Receiving image %s in _ansel_saveImage() with a raw filesize of %i", $image['filename'], strlen($image['data'])), __FILE__, __LINE__, PEAR_LOG_DEBUG);
+        $image_data = array('image_filename' => $image['filename'],
+                            'image_caption' => $image['description'],
+                            'image_type' => $image['type'],
+                            'data' => _getImageData($image['data'], $encoding, $compression, true));
+    } else {
+        Horde::logMessage(sprintf("Receiving image %s in _ansel_saveImage() with a raw filesize of %i", $image['file'], filesize($image['file'])), __FILE__, __LINE__, PEAR_LOG_DEBUG);
+    }
+
+    if (is_null($image_data) && getimagesize($image['file']) === false) {
+        return PEAR::raiseError(_("The file you uploaded does not appear to be a valid photo."));
+    }
+    if (empty($slug) && empty($gallery_id)) {
+        return PEAR::raiseError(_("A gallery to add this photo to is required."));
+    }
+    if (!empty($slug)) {
+        $gallery = $GLOBALS['ansel_storage']->getGalleryBySlug($slug);
+        if (is_a($gallery, 'PEAR_Error')) {
+            return $gallery;
+        }
+    } elseif ($GLOBALS['ansel_storage']->galleryExists($gallery_id)) {
+        $gallery = $GLOBALS['ansel_storage']->getGallery($gallery_id);
+        if (is_a($gallery, 'PEAR_Error')) {
+            return $gallery;
+        }
+    }
+    if (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+        return PEAR::raiseError(sprintf(_("Access denied adding photos to \"%s\"."), $gallery->get('name')));
+    }
+    if (!empty($gallery_data)) {
+        foreach ($gallery_data as $key => $value) {
+            $gallery->set($key, $value);
+        }
+        $gallery->save();
+    }
+
+    if (is_null($image_data)) {
+        $image_data = array(
+            'image_filename' => $image['name'],
+            'image_caption' => $image['name'],
+            'image_type' => $image['name']['type'],
+            'data' => file_get_contents($image['file']),
+        );
+    }
+
+    if (isset($image['tags']) && is_array($image['tags']) &&
+        count($image['tags'])) {
+            $image_data['tags'] = $image['tags'];
+    }
+
+    $image_id = $gallery->addImage($image_data, $default);
+    if (is_a($image_id, 'PEAR_Error')) {
+        return $image_id;
+    }
+
+    // Call the postupload hook if needed
+    if (!empty($GLOBALS['conf']['hooks']['postupload']) && !$skiphook) {
+        Horde::callHook('_ansel_hook_postupload', array(array($image_id)), 'ansel');
+    }
+
+    return array('image_id'   => (int)$image_id,
+                 'gallery_id' => (int)$gallery->id,
+                 'gallery_slug' => $gallery->get('slug'),
+                 'image_count' => (int)$gallery->countImages());
+}
+
+/**
+ * Notify Ansel that a group of images has just been uploaded. Used for when
+ * the _ansel_hook_postupload hook should be called with a group of recently
+ * uploaded images, as opposed to calling it once after each image is saved.
+ *
+ * @param array $image_ids  An array of image ids.
+ */
+function _ansel_postBatchUpload($image_ids)
+{
+    require_once dirname(__FILE__) . '/base.php';
+    if (!empty($conf['hooks']['postupload'])) {
+        return Horde::callHook('_ansel_hook_postupload', array($image_ids), 'ansel');
+    }
+}
+
+/**
+ * Removes an image from a gallery.
+ *
+ * @param string $app         Application scope to use, if not the default.
+ * @param integer $gallery_id The id of gallery.
+ * @param string $image_id    The id of image to remove.
+ */
+function _ansel_removeImage($app = null, $gallery_id, $image_id)
+{
+    require_once dirname(__FILE__) . '/base.php';
+
+    /* Check global Ansel permissions */
+    if (!($GLOBALS['perms']->getPermissions('ansel'))) {
+        return PEAR::raiseError(_("Access denied deleting galleries."));
+    }
+
+    /* If no app is given use Ansel's own gallery which is initialized in
+       base.php */
+    if (!is_null($app)) {
+        $GLOBALS['ansel_storage'] = new Ansel_Storage($app);
+    }
+
+    $image = $GLOBALS['ansel_storage']->getImage($image_id);
+    if (is_a($image, 'PEAR_Error')) {
+        return $image;
+    }
+    $gallery = $GLOBALS['ansel_storage']->getGallery($image->gallery);
+    if (is_a($gallery, 'PEAR_Error') ||
+        !$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_DELETE)) {
+
+        return PEAR::raiseError(sprintf(_("Access denied deleting photos from \"%s\"."), $gallery->get('name')));
+    }
+    return $gallery->removeImage($image);
+}
+
+/**
+ * Add a new gallery to any arbitrary application's Ansel_Shares.
+ *
+ * @param string $app            Application scope to use, if not the default.
+ * @param array $attributes      The gallery attributes
+ *                               (@see Ansel_Storage::createGallery).
+ * @param array $perm            An array of permission data if Ansel's defaults
+ *                               are not desired. Takes an array like:
+ *                               array('guest' => PERMS_SHOW | PERMS_READ,
+ *                                     'default' => PERMS_SHOW | PERMS_READ);
+ * @param integer $parent        The gallery id of the parent gallery, if any.
+ *
+ * @return mixed  The gallery id of the new gallery | PEAR_Error
+ */
+function _ansel_createGallery($app = null, $attributes = array(), $perm = null, $parent = null)
+{
+    require_once dirname(__FILE__) . '/base.php';
+
+    if (!(Horde_Auth::isAdmin() ||
+          (!$GLOBALS['perms']->exists('ansel') && Horde_Auth::getAuth()) ||
+          $GLOBALS['perms']->hasPermission('ansel', Horde_Auth::getAuth(), PERMS_EDIT))) {
+        return PEAR::raiseError(_("Access denied creating new galleries."));
+    }
+
+    if (!is_null($app)) {
+        $GLOBALS['ansel_storage'] = new Ansel_Storage($app);
+    }
+
+    if (!empty($perm)) {
+        // The name is inconsequential; it is only used as a container to
+        // represent permissions when passed to the Ansel backend.
+        $permobj = new Horde_Permission('');
+        $permobj->data = $perm;
+    } else {
+        $permobj = null;
+    }
+
+    $gallery = $GLOBALS['ansel_storage']->createGallery($attributes, $permobj, $parent);
+    if (is_a($gallery, 'PEAR_Error')) {
+        return $gallery;
+    }
+    return $gallery->id;
+}
+
+/**
+ * Removes a gallery and its images.
+ *
+ * @param string $app          Application scope to use, if not the default.
+ * @param integer $gallery_id  The id of gallery.
+ *
+ * @return mixed boolean true | PEAR_Error
+ */
+function _ansel_removeGallery($app = null, $gallery_id)
+{
+    require_once dirname(__FILE__) . '/base.php';
+
+    /* Check global Ansel permissions */
+    if (!($GLOBALS['perms']->getPermissions('ansel'))) {
+        return PEAR::raiseError(_("Access denied deleting galleries."));
+    }
+
+    /* If no app is given use Ansel's own gallery which is initialized in
+       base.php */
+    if (!is_null($app)) {
+        $GLOBALS['ansel_storage'] = new Ansel_Storage($app);
+    }
+
+    $gallery = $GLOBALS['ansel_storage']->getGallery($gallery_id);
+    if (is_a($gallery, 'PEAR_Error')) {
+        return PEAR::raiseError(sprintf(_("Access denied deleting gallery \"%s\"."),
+                                        $gallery->getMessage()));
+    } elseif (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_DELETE)) {
+        return PEAR::raiseError(sprintf(_("Access denied deleting gallery \"%s\"."),
+                                        $gallery->get('name')));
+    } else {
+        $imageList = $gallery->listImages();
+        if ($imageList) {
+            foreach ($imageList as $id) {
+                $gallery->removeImage($id);
+            }
+        }
+        $result = $GLOBALS['ansel_storage']->removeGallery($gallery);
+        if (!is_a($result, 'PEAR_Error')) {
+            return true;
+        } else {
+            return PEAR::raiseError(sprintf(_("There was a problem deleting %s: %s"),
+                                            $gallery->get('name'),
+                                            $result->getMessage()));
+        }
+    }
+}
+
+/**
+ * Returns the number of images in a gallery.
+ *
+ * @param integer $app          Application used; if null then use default.
+ * @param integer $gallery_id   The gallery id.
+ * @param string  $slug         The gallery slug.
+ *
+ * @return integer  The number of images in the gallery.
+ */
+function _ansel_count($app = null, $gallery_id = null, $slug = '')
+{
+    require_once dirname(__FILE__) . '/base.php';
+
+    /* If no app is given use Ansel's own gallery which is initialized in
+       base.php */
+    if (!is_null($app)) {
+        $GLOBALS['ansel_storage'] = new Ansel_Storage($app);
+    }
+
+    if (!empty($slug)) {
+        $gallery = $GLOBALS['ansel_storage']->getGalleryBySlug($slug);
+    } else {
+        $gallery = $GLOBALS['ansel_storage']->getGallery($gallery_id);
+    }
+
+    if (is_a($gallery, 'PEAR_Error')) {
+        return 0;
+    } else {
+        return (int)$gallery->countImages();
+    }
+}
+
+/**
+ * Returns the default image id of a gallery.
+ *
+ * @param string $app            Application scope to use, if not the default.
+ * @param integer $gallery_id    The gallery id.
+ * @param string $style          The named style.
+ * @param string $slug           The gallery slug.
+ *
+ * @return integer  The default image id.
+ */
+function _ansel_getDefaultImage($app = null, $gallery_id = null,
+                                $style = 'ansel_default', $slug = '')
+{
+    require_once dirname(__FILE__) . '/base.php';
+
+    /* If no app is given use Ansel's own gallery which is initialized in
+       base.php */
+    if (!is_null($app)) {
+        $GLOBALS['ansel_storage'] = new Ansel_Storage($app);
+    }
+
+    if (!empty($slug)) {
+        $gallery = $GLOBALS['ansel_storage']->getGalleryBySlug($slug);
+    } else {
+        $gallery = $GLOBALS['ansel_storage']->getGallery($gallery_id);
+    }
+
+    if (is_a($gallery, 'PEAR_Error')) {
+        return $gallery;
+    } else {
+        return $gallery->getDefaultImage($style);
+    }
+}
+
+/**
+ * Returns image URL.
+ *
+ * @param integer $app       Application used.
+ * @param integer $image_id  The id of the image.
+ * @param string $view       The view ('screen', 'thumb', 'full', etc) to show.
+ * @param boolean $full      Return a path that includes the server name?
+ * @param string $style      Use this gallery style
+ *
+ * @return string  The image path.
+ */
+function _ansel_getImageUrl($app = null, $image_id, $view = 'screen',
+                            $full = false, $style = null)
+{
+    require_once dirname(__FILE__) . '/base.php';
+
+    /* If no app is given use Ansel's own gallery which is initialized in
+       base.php */
+    if (!is_null($app)) {
+        $GLOBALS['ansel_storage'] = new Ansel_Storage($app);
+    }
+
+    return Ansel::getImageUrl($image_id, $view, $full, $style);
+}
+
+/**
+ * Returns image file content.
+ *
+ * @param integer $image_id  The id of the image.
+ * @param string $view       The view ('screen', 'thumb', 'full', etc) to show.
+ * @param string $style      Force use of this gallery style.
+ * @param integer $app       Application used.
+ * @param string $encoding     The encoding type for the image data.
+ *                             (none, base64, or binhex)
+ * @param string $compression  The compression type for the image data.
+ *                             (none, gzip, or lzf)
+ *
+ * @return string  The image path.
+ */
+function _ansel_getImageContent($image_id, $view = 'screen', $style = null,
+                                $app = null, $encoding = null, $compression = 'none')
+{
+    require_once dirname(__FILE__) . '/base.php';
+
+    /* If no app is given use Ansel's own gallery which is initialized in
+       base.php */
+    if (!is_null($app)) {
+        $GLOBALS['ansel_storage'] = new Ansel_Storage($app);
+    }
+
+    // Get image
+    $image = $GLOBALS['ansel_storage']->getImage($image_id);
+    if (is_a($image, 'PEAR_Error')) {
+        return $image;
+    }
+
+    // Get gallery
+    $gallery = $GLOBALS['ansel_storage']->getGallery($image->gallery);
+    if (is_a($gallery, 'PEAR_Error')) {
+        return $gallery;
+    }
+
+    // Check age and password
+    if (!$gallery->hasPasswd() || !$gallery->isOldEnough()) {
+        return PEAR::raiseError(_("Locked galleries are not viewable via the api."));
+    }
+
+    if ($view == 'full') {
+        // Check permissions for full view
+        if (!$gallery->canDownload()) {
+            return PEAR::RaiseError(sprintf(_("Access denied downloading photos from \"%s\"."), $gallery->get('name')));
+        }
+
+        $data = $GLOBALS['ansel_vfs']->read($image->getVFSPath('full'),
+                                            $image->getVFSName('full'));
+    } else {
+        // Load View
+        $result = $image->load($view, $style);
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        }
+
+        // Return image content
+        $data = $image->_image->raw();
+    }
+
+    if (is_a($data, 'PEAR_Error')) {
+        return $data;
+    }
+
+    return _getImageData($data, $encoding, $compression, false);
+}
+
+/**
+ * Returns a list of all galleries.
+ *
+ * @param string $app         Application scope to use, if not the default.
+ * @param integer $perm       The level of permissions to require for a gallery
+ *                            to be returned.
+ * @param integer $parent     The parent gallery id to start searching from.
+ *                            This should be either a gallery id or null.
+ * @param boolean $allLevels  Return all levels, or just the direct children of
+ *                            $parent?
+ * @param integer $from       The gallery to start listing at.
+ * @param integer $count      The number of galleries to return.
+ * @param array $attributes   Restrict the returned galleries to those matching
+ *                            $attributes. An array of attribute names => values
+ *
+ * @return array  An array of gallery information.
+ */
+function _ansel_listGalleries($app = null, $perm = PERMS_SHOW,
+                              $parent = null,
+                              $allLevels = true, $from = 0, $count = 0,
+                              $attributes = null, $sort_by = null, $direction = 0)
+{
+    require_once dirname(__FILE__) . '/base.php';
+
+    /* If no app is given use Ansel's own gallery which is initialized in
+       base.php */
+    if (!is_null($app)) {
+        $GLOBALS['ansel_storage'] = new Ansel_Storage($app);
+    }
+
+
+    $galleries = $GLOBALS['ansel_storage']->listGalleries(
+        $perm, $attributes, $parent, $allLevels, $from, $count, $sort_by, $direction);
+
+    if (is_a($galleries, 'PEAR_Error')) {
+        return $galleries;
+    }
+
+    $return = array();
+    foreach ($galleries as $gallery) {
+        $return[$gallery->id] = array_merge($gallery->data, array('crumbs' => $gallery->getGalleryCrumbData()));
+    }
+
+    return $return;
+}
+
+/**
+ * Returns an array of gallery information.
+ *
+ * @param array $ids   An array of gallery ids.
+ * @param string $app  Application scope to use, if not the default.
+ * @param array $slugs An array of gallery slugs.
+ *
+ * @return mixed  An array of gallery data arrays | PEAR_Error
+ */
+function _ansel_getGalleries($ids = array(), $app = null, $slugs = array())
+{
+    require_once dirname(__FILE__) . '/base.php';
+
+    if (!is_null($app)) {
+        $GLOBALS['ansel_storage'] = new Ansel_Storage($app);
+    }
+
+    if (count($slugs)) {
+        $results = $GLOBALS['ansel_storage']->getGalleriesBySlugs($slugs);
+    } else {
+        $results = $GLOBALS['ansel_storage']->getGalleries($ids);
+    }
+
+    if (is_a($results, 'PEAR_Error')) {
+        return $results;
+    }
+
+    /* We can't just return the results of the getGalleries call - we need to
+       ensure the caller has at least PERMS_READ on the galleries. */
+    $galleries = array();
+    foreach ($results as $gallery) {
+        if ($gallery->hasPermission(Horde_Auth::getAuth(), PERMS_READ)) {
+            $galleries[$gallery->id] = array_merge($gallery->data, array('crumbs' => $gallery->getGalleryCrumbData()));
+        }
+    }
+
+    return $galleries;
+}
+
+/**
+ * Returns a 'select' menu from the list of galleries created by
+ * _ansel_listGalleries().
+ *
+ *
+ * @param integer $app        Application used if null then use default.
+ * @param integer $perm       The permissions filter to use.
+ * @param string $parent      The parent share to start listing at.
+ * @param boolean $allLevels  Return all levels, or just the direct
+ * @param integer $from       The gallery to start listing at.
+ * @param integer $count      The number of galleries to return.
+ * @param boolean $default    The gallery_id of the  gallery that is
+ *                            selected by default in the returned option
+ *                            list.
+ */
+function _ansel_selectGalleries($app = null, $perm = PERMS_SHOW,
+                                $parent = null,
+                                $allLevels = true, $from = 0, $count = 0,
+                                $default = null)
+{
+    require_once dirname(__FILE__) . '/base.php';
+
+    /* If no app is given use Ansel's own gallery which is initialized in
+       base.php */
+    if (!is_null($app)) {
+        $GLOBALS['ansel_storage'] = new Ansel_Storage($app);
+    }
+
+    return Ansel::selectGalleries($default, $perm, null, $parent, $allLevels,
+                                  $from, $count);
+}
+
+/**
+ * Returns a list of all images in a gallery.
+ *
+ * The return has the URL because in a lot of cases you'll want the url
+ * also. Using api call getImageURL results in a lot of overhead when
+ * e.g. generating a select list.
+ *
+ * @param string $app          Application scope to use, if not the default.
+ * @param integer $gallery_id  Gallery id to get images from.
+ * @param integer $perm        The level of permissions to require for a
+ *                             gallery to return it.
+ * @param string $view         Viewsize to generate URLs for.
+ * @param boolean $full        Return a full URL
+ * @param integer $from        Start image.
+ * @param integer $count       End image.
+ * @param string $style        Use this gallery style.
+ * @param string $slug         Gallery slug
+ *
+ * @return array  Two dimensional array with image names ids (key) and urls.
+ */
+function _ansel_listImages($app = null, $gallery_id = null, $perm = PERMS_SHOW,
+                           $view = 'screen', $full = false, $from = 0,
+                           $count = 0, $style = null, $slug = '')
+{
+    require_once dirname(__FILE__) . '/base.php';
+
+    /* If no app is given use Ansel's own gallery which is initialized in
+       base.php */
+    if (!is_null($app)) {
+        $GLOBALS['ansel_storage'] = new Ansel_Storage($app);
+    }
+
+    /* Determine the default gallery when none is given. The first gallery in
+       the list is the default gallery. */
+    if (is_null($gallery_id) && empty($slug)) {
+        $galleries = $GLOBALS['ansel_storage']->listGalleries($perm);
+        if (!count($galleries)) {
+            return array();
+        }
+        $keys = array_keys($galleries);
+        $gallery_names = array_keys($galleries[$keys[0]]['galleries']);
+        $gallery_id = $gallery_names[0];
+    } elseif (!empty($slug)) {
+        $gallery = $GLOBALS['ansel_storage']->getGalleryBySlug($slug);
+    } else {
+        $gallery = $GLOBALS['ansel_storage']->getGallery($gallery_id);
+    }
+    if (is_a($gallery, 'PEAR_Error')) {
+        return $gallery;
+    }
+
+    $images = $gallery->listImages();
+    if (is_a($images, 'PEAR_Error')) {
+        return $images;
+    }
+
+    $counter = 0;
+    $imagelist = array();
+    foreach ($images as $id) {
+        $image = $GLOBALS['ansel_storage']->getImage($id);
+        if (is_a($image, 'PEAR_Error')) {
+            return $image;
+        }
+        $imagelist[$id]['name'] = $image->filename;
+        $imagelist[$id]['caption'] = $image->caption;
+        $imagelist[$id]['type'] = $image->type;
+        $imagelist[$id]['uploaded'] = $image->uploaded;
+        $imagelist[$id]['original_date'] = $image->originalDate;
+        $imagelist[$id]['url'] = Ansel::getImageUrl($id, $view, $full, $style);
+        if (!is_null($app) && $GLOBALS['conf']['vfs']['src'] != 'direct') {
+            $imagelist[$id]['url'] = Horde_Util::addParameter($imagelist[$id]['url'],
+                                                        'app', $app);
+        }
+    }
+    return $imagelist;
+}
+
+/**
+ * Return a list of recently added images
+ *
+ * @param string $app       Application used if null then use default.
+ * @param array $galleries  An array of gallery ids to check.  If left empty,
+ *                          will search all galleries with the given
+ *                          permissions for the current user.
+ * @param integer $perms    PERMS_* constant
+ * @param string $view      The type of image view to return.
+ * @param boolean $full     Return a full URL if this is true.
+ * @param integer  $limit   The maximum number of images to return.
+ * @param string $style     Force the use of this gallery style
+ * @param string $slugs     An array of gallery slugs
+ *
+ * @return array  An array of image objects.
+ */
+function _ansel_getRecentImages($app = null, $galleries = array(),
+                                $perms = PERMS_SHOW, $view = 'screen',
+                                $full = false, $limit = 10, $style = null,
+                                $slugs = array())
+{
+    require_once dirname(__FILE__) . '/base.php';
+    if (!is_null($app)) {
+        $GLOBALS['ansel_storage'] = new Ansel_Storage($app);
+    }
+    $images = $GLOBALS['ansel_storage']->getRecentImages($galleries, $limit, $slugs);
+    $imagelist = array();
+    foreach ($images as $image) {
+        $id = $image->id;
+        $imagelist[$id]['id'] = $id;
+        $imagelist[$id]['name'] = $image->filename;
+        $imagelist[$id]['url'] = Ansel::getImageUrl($id, $view, $full, $style);
+        $imagelist[$id]['caption'] = $image->caption;
+        $imagelist[$id]['filename'] = $image->filename;
+        $imagelist[$id]['gallery'] = $image->gallery;
+        $imagelist[$id]['uploaded'] = $image->uploaded;
+        $imagelist[$id]['original_date'] = $image->originalDate;
+
+        if (!is_null($app) && $GLOBALS['conf']['vfs']['src'] != 'direct') {
+            $imagelist[$id]['url'] = Horde_Util::addParameter($imagelist[$id]['url'],
+                                                        'app', $app);
+        }
+    }
+    return $imagelist;
+
+}
+
+/**
+ * Counts the number of galleries.
+ *
+ * @param string $app         Application scope to use, if not the default.
+ * @param integer $perm       The level of permissions to require for a gallery
+ *                            to return it.
+ * @param mixed $attributes   Restrict the galleries counted to those matching
+ *                            $attributes. An array of attribute/value pairs or
+ *                            a gallery owner username.
+ * @param integer $parent     The parent gallery id to start searching at.
+ * @param boolean $allLevels  Return all levels, or just the direct children of
+ *                            $parent?
+ *
+ * @return integer  Returns the number of matching galleries.
+ */
+function _ansel_countGalleries($app = null, $perm = PERMS_SHOW, $attributes = null,
+                               $parent = null, $allLevels = true)
+{
+    require_once dirname(__FILE__) . '/base.php';
+
+    /* If no app is given use Ansel's own gallery which is initialized
+     * in base.php */
+    if (!is_null($app)) {
+        $GLOBALS['ansel_storage'] = new Ansel_Storage($app);
+    }
+
+    return $GLOBALS['ansel_storage']->countGalleries(Horde_Auth::getAuth(), $perm,
+                                                     $attributes, $parent,
+                                                     $allLevels);
+}
+
+/**
+ * Retrieve the list of used tag_names, tag_ids and the total number
+ * of resources that are linked to that tag.
+ *
+ * @param array $tags  An optional array of tag_ids. If omitted, all tags
+ *                     will be included.
+ *
+ * @return mixed  An array containing tag_name, and total | PEAR_Error
+ */
+function _ansel_listTagInfo($tags = null)
+{
+    require_once dirname(__FILE__) . '/base.php';
+    require_once ANSEL_BASE . '/lib/Tags.php';
+
+    return Ansel_Tags::listTagInfo($tags);
+}
+
+/**
+ * Searches images/galleries tagged with all requested tags.
+ * Returns an application-agnostic array (useful for when doing a tag search
+ * across multiple applications) containing the following keys:
+ * <pre>
+ *  'title'    - The title for this resource.
+ *  'desc'     - A terse description of this resource.
+ *  'view_url' - The URL to view this resource.
+ *  'app'      - The Horde application this resource belongs to.
+ * </pre>
+ *
+ * The 'raw' results array can be returned instead by setting $raw = true.
+ *
+ * @param array $names           An array of tag_names to search for.
+ * @param integer $max           The maximum number of stories to return.
+ * @param integer $from          The number of the story to start with.
+ * @param string $resource_type  An array of channel_ids to limit the search to.
+ * @param string $user           Restrict results to resources owned by $user.
+ * @param boolean $raw           Return the raw story data?
+ * @param string $app            Application scope to use, if not the default.
+ *
+ * @return mixed  An array of results | PEAR_Error
+ */
+function _ansel_searchTags($names, $max = 10, $from = 0,
+                           $resource_type = 'all', $user = null, $raw = false,
+                           $app = null)
+{
+    require_once dirname(__FILE__) . '/base.php';
+    require_once ANSEL_BASE . '/lib/Tags.php';
+
+    if (!is_null($app)) {
+        $GLOBALS['ansel_storage'] = new Ansel_Storage($app);
+    } else {
+        $app = 'ansel';
+    }
+
+    $results = Ansel_Tags::searchTags($names, $max, $from,  $resource_type,
+                                      $user);
+
+    /* Check for error or if we requested the raw data array */
+    if (is_a($results, 'PEAR_Error') || $raw) {
+        return $results;
+    }
+
+    $return = array();
+    if (!empty($results['images'])) {
+        foreach ($results['images'] as $image_id) {
+            $image = $GLOBALS['ansel_storage']->getImage($image_id);
+            $desc = $image->caption;
+            $title = $image->filename;
+            $view_url = Ansel::getUrlFor('view',
+                                         array('gallery' => $image->gallery,
+                                               'image' => $image_id,
+                                               'view' => 'Image'),
+                                         true);
+            $return[] = array('title' => $image->filename,
+                              'desc'=> $image->caption,
+                              'view_url' => $view_url,
+                              'app' => $app);
+        }
+
+    }
+
+    if (!empty($results['galleries'])) {
+        foreach ($results['galleries'] as $gallery) {
+            $gal = $GLOBALS['ansel_storage']->getGallery($gallery);
+            $view_url = Horde_Util::addParameter(Horde::applicationUrl('view.php'), array('gallery' => $gallery,
+                                                                                    'view' => 'Gallery'));
+            $return[] = array('title' => $gal->get('name'),
+                              'desc' => $gal->get('desc'),
+                              'view_url' => $view_url,
+                              'app' => $app);
+
+        }
+    }
+
+
+    return $return;
+}
+
+/**
+ * Checks if the gallery exists
+ *
+ * @param string $app          Application scope to use, if not the default.
+ * @param integer $gallery_id  The gallery id
+ * @param string $slug         The gallery slug
+ *
+ * @return boolean
+ */
+function _ansel_galleryExists($app, $gallery_id = null, $slug = '')
+{
+    require_once dirname(__FILE__) . '/base.php';
+
+    if (!is_null($app)) {
+        $GLOBALS['ansel_storage'] = new Ansel_Storage($app);
+    }
+
+    return $GLOBALS['ansel_storage']->galleryExists($gallery_id, $slug);
+}
+
+/**
+ * Get a list of all configured styles.
+ *
+ * @return hash of style definitions.
+ */
+function _ansel_getGalleryStyles()
+{
+    require_once dirname(__FILE__) . '/base.php';
+
+    return Ansel::getAvailableStyles();
+}
+
+/**
+ * Renders a gallery view
+ *
+ * @param array $params         Any parameters that the view might need.
+ *                              @see Ansel_View_* classes for descriptions of
+ *                              available parameters to use here.
+ * @param string $app           Application scope to use, if not the default.
+ * @param string $view          The generic type of view we want.
+ *                              (Gallery, Image, List, Embedded)
+ *
+ * @return array  An array containing 'html' and 'crumbs' keys.
+ */
+function _ansel_renderView($params = array(), $app = null,
+                           $view = 'Gallery')
+{
+    require_once dirname(__FILE__) . '/base.php';
+
+    if (!is_null($app)) {
+        $GLOBALS['ansel_storage'] = new Ansel_Storage($app);
+    }
+
+    require_once ANSEL_BASE . '/lib/Views/' . basename($view) . '.php';
+    $classname = 'Ansel_View_' . basename($view);
+    $params['api'] = true;
+    $params['view'] = $view;
+    $trail = array();
+    $return = array();
+
+    if (is_callable(array($classname, 'makeView'))) {
+        $view = call_user_func_array(array($classname, 'makeView'), array($params));
+        if (is_a($view, 'PEAR_Error')) {
+            $return['html'] = $view->getMessage();
+            $return['crumbs'] = array();
+            return $return;
+        }
+
+        $return['html'] = $view->html();
+        if ($params['view'] == 'Gallery' || $params['view'] == 'Image') {
+            $trail = $view->getGalleryCrumbData();
+        }
+        $return['crumbs'] = $trail;
+
+        return $return;
+    } else {
+        return PEAR::raiseError(sprintf(
+            _("Unable to load the definition of %s."), $view));
+    }
+}
diff --git a/ansel/lib/base.php b/ansel/lib/base.php
new file mode 100644 (file)
index 0000000..33de8f2
--- /dev/null
@@ -0,0 +1,69 @@
+<?php
+/**
+ * This file brings in all of the dependencies that every Ansel script will need
+ * and sets up objects that all scripts use.
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ *
+ * @package Ansel
+ */
+
+// Check for a prior definition of HORDE_BASE (perhaps by an
+// auto_prepend_file definition for site customization).
+if (!defined('HORDE_BASE')) {
+    define('HORDE_BASE', dirname(__FILE__) . '/../..');
+}
+
+// Load the Horde Framework core, and set up inclusion paths.
+require_once HORDE_BASE . '/lib/core.php';
+
+// Registry
+$registry = Horde_Registry::singleton();
+try {
+    $registry->pushApp('ansel', array('check_perms' => (Horde_Util::nonInputVar('ansel_authentication') != 'none'), 'logintasks' => true));
+} catch (Horde_Exception $e) {
+        Horde_Auth::authenticationFailureRedirect('ansel', $e);
+}
+
+$conf = &$GLOBALS['conf'];
+if (!defined('ANSEL_TEMPLATES')) {
+    define('ANSEL_TEMPLATES', $registry->get('templates'));
+}
+
+// Notification system.
+$GLOBALS['notification'] = Horde_Notification::singleton();
+$GLOBALS['notification']->attach('status');
+
+// Find the base file path of Ansel.
+if (!defined('ANSEL_BASE')) {
+    define('ANSEL_BASE', dirname(__FILE__) . '/..');
+}
+
+// Ansel base libraries.
+require_once ANSEL_BASE . '/lib/Ansel.php';
+
+// Create a cache object if we need it.
+if ($conf['ansel_cache']['usecache']) {
+    $GLOBALS['cache'] = Horde_Cache::singleton($conf['cache']['driver'],
+                                               Horde::getDriverConfig('cache', $conf['cache']['driver']));
+}
+
+// Create db, share, and vfs instances.
+$GLOBALS['ansel_db'] = Ansel::getDb();
+if (is_a($GLOBALS['ansel_db'], 'PEAR_Error')) {
+    Horde::fatal($GLOBALS['ansel_db'], __FILE__, __LINE__, false);
+}
+$GLOBALS['ansel_storage'] = new Ansel_Storage();
+$GLOBALS['ansel_vfs'] = Ansel::getVFS();
+
+// Get list of available styles for this client.
+$GLOBALS['ansel_styles'] = Ansel::getAvailableStyles();
+if ($logger = Horde::getLogger()) {
+    $GLOBALS['ansel_vfs']->setLogger($logger, $GLOBALS['conf']['log']['priority']);
+}
diff --git a/ansel/lib/prefs.php b/ansel/lib/prefs.php
new file mode 100644 (file)
index 0000000..533dc8e
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+/**
+ * $Horde: ansel/lib/prefs.php,v 1.4 2009/06/10 00:33:03 mrubinsk Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @package Ansel
+ */
+
+function handle_default_category_select($updated)
+{
+    $default_category = Horde_Util::getFormData('default_category_select');
+    if (!is_null($default_category)) {
+        $GLOBALS['prefs']->setValue('default_category', $default_category);
+        return true;
+    }
+
+    return $updated;
+}
+
+function handle_default_gallerystyle_select($updated)
+{
+    $default_style = Horde_Util::getFormData('default_gallerystyle_select');
+    if (!is_null($default_style)) {
+        $GLOBALS['prefs']->setValue('default_gallerystyle', $default_style);
+        return true;
+    }
+
+    return $updated;
+}
+
+
diff --git a/ansel/lib/version.php b/ansel/lib/version.php
new file mode 100755 (executable)
index 0000000..6c33e4c
--- /dev/null
@@ -0,0 +1 @@
+<?php define('ANSEL_VERSION', '1.0-cvs') ?>
diff --git a/ansel/locale/de_DE/LC_MESSAGES/ansel.mo b/ansel/locale/de_DE/LC_MESSAGES/ansel.mo
new file mode 100644 (file)
index 0000000..07e435c
Binary files /dev/null and b/ansel/locale/de_DE/LC_MESSAGES/ansel.mo differ
diff --git a/ansel/locale/en_US/help.xml b/ansel/locale/en_US/help.xml
new file mode 100755 (executable)
index 0000000..298849f
--- /dev/null
@@ -0,0 +1,74 @@
+<?xml version="1.0" ?>
+<!-- $Horde: ansel/locale/en_US/help.xml,v 1.5 2006/10/29 18:02:19 chuck Exp $ -->
+<help>
+
+<entry id="overview">
+  <title>Overview</title>
+  <heading>Introduction</heading>
+
+  <para>
+    This application provides image manipulation, storage, and display
+    capabilities for you to have online photo albums and
+    galleries. You can resize, rotate, and otherwise alter your
+    images, organize them, name them, and give them descriptions.
+  </para>
+</entry>
+
+<entry id="upload">
+  <title>Uploading Images: Overview</title>
+
+  <para>
+    You can upload most common kinds of images. If there is a problem
+    with an uploaded file, you will be notified, so when in doubt,
+    give it a try. You also have two options if you have a lot of
+    images to upload at once. You can upload a ZIP file of all of the
+    images, and they will all be processed at once. Or, if you are
+    using Windows XP, you can use the <ref ENTRY="xppublish"
+    MODULE="ansel">XP Publishing Wizard</ref>.
+  </para>
+</entry>
+
+<entry id="xppublish">
+  <title>Uploading Images: Publish from Windows XP</title>
+  <heading>Using Windows XP's Publish to the Web feature</heading>
+
+  <para>
+    Windows XP comes with a nice feature that allows you to publish
+    content from your desktop directly to a web service.
+  </para>
+
+  <para><b>1. Setting it up</b></para>
+
+  <para>
+    Download the <href url="xppublish.php" app="ansel">XP
+    Configuration file</href>. Save it to your desktop, and make sure
+    that the file extension is .reg. If it is not, rename the file,
+    and if Windows asks you to confirm changing the type of the file,
+    say yes.
+  </para>
+
+  <para>
+    Double-click on install-registry.reg. You will be asked whether or
+    not you want to import these values into your registry. Click
+    &quot;OK&quot;. You will be told that the values were imported
+    successfully; click &quot;OK&quot; again. You are now ready to
+    upload files!
+  </para>
+
+  <para><b>2. Uploading</b></para>
+
+  <para>
+    Open Windows Explorer and browse to the folder that you want to
+    upload, or that contains the file(s) that you'd like to upload.
+    Make sure that you can see &quot;File and Folder Tasks&quot; on
+    the left of your Explorer window. You should see an option to
+    &quot;Publish this folder to the Web&quot; or, if you have a
+    specific file selected, &quot;Publish this file to the Web&quot;.
+    Click this link and then follow the instructions to select which
+    files you want to upload (you will get to select anything in the
+    folder during the process), log in, select a gallery or create a
+    new one, and publish the image(s).
+  </para>
+</entry>
+
+</help>
diff --git a/ansel/locale/es_ES/LC_MESSAGES/ansel.mo b/ansel/locale/es_ES/LC_MESSAGES/ansel.mo
new file mode 100755 (executable)
index 0000000..c9be2c1
Binary files /dev/null and b/ansel/locale/es_ES/LC_MESSAGES/ansel.mo differ
diff --git a/ansel/locale/es_ES/help.xml b/ansel/locale/es_ES/help.xml
new file mode 100755 (executable)
index 0000000..a98ee94
--- /dev/null
@@ -0,0 +1,47 @@
+<?xml version="1.0" ?>
+<!-- $Horde: ansel/locale/es_ES/help.xml,v 1.1 2008/03/21 00:28:27 jan Exp $ -->
+<help>
+
+<entry id="overview">
+  <title>Introducción</title>
+  <heading>Introducción</heading>
+
+  <para>
+         Esta aplicación le ofrece capacidades de manipulación, almacenamiento y visualización de imágenes para disponer de álbumes de fotos y galerías en línea. Puede redimensionarlas, girarlas y modificarlas de otras formas, organizarlas, renombrarlas y añadirles descripciones.
+  </para>
+</entry>
+
+<entry id="upload">
+  <title>Carga de imágenes: Introducción</title>
+
+  <para>
+         Puede cargar la mayoría de los formatos habituales de imágenes. Si hay algún problema con alguno de los archivos cargados se le comunicará, por lo que, ante la duda, haga una prueba. También dispone de dos opciones si tiene muchas imágenes que cargar de una vez. Puede cargar un archivo ZIP con todas las imágenes que se procesará de una sola vez o, si utiliza Windows XP puede utilizar el <ref ENTRY="xppublish" MODULE="ansel">Asistente de publicación XP</ref>.
+  </para>
+</entry>
+
+<entry id="xppublish">
+  <title>Carga de imágenes: Publicación desde Windows XP</title>
+  <heading>Uso de la característica Publicación en Web de Windows XP</heading>
+
+  <para>
+         Windows XP dispone de una útil característica que le permite publicar contenido en un servicio web directamente desde su escritorio.
+  </para>
+
+  <para><b>1. Configuración</b></para>
+
+  <para>
+         Descargue el <href url="xppublish.php" app="ansel">Archivo de configuración de XP</href>. Guárdelo en el escritorio y asegúrese de que su extensión es .reg. Si no lo es renombre el archivo y si Windows le consulta si desea confirmar el cambio de tipo de archivo, conteste que sí.
+  </para>
+
+  <para>
+         Haga doble-click en el archivo install-registry.reg. Se le consultará si desea importar esos valores al registro o no. Pulse &quot;Aceptar&quot;. Se le comunicará si los valores se importan correctamente; vuelva a pulsar &quot;Aceptar&quot;. ¡Ya está listo para cargar archivos!
+  </para>
+
+  <para><b>2. Carga</b></para>
+
+  <para>
+         Abra el Explorador de Windows y navegue hasta la carpeta que desea cargar o que contenga el/los archivo(s) que querría cargar. Asegúrese de que puede ver las &quot;Tareas de archivos y carpetas&quot; a la izquierda de la ventana del explorador. Debería poder ver la opción &quot;Publicar esta carpeta en la web&quot; o, si tiene un archivo concreto seleccionado, &quot;Publicar este archivo en la web&quot;. Pulse dicho vínculo y siga las instrucciones para seleccionar los archivos que desee cargar (se le permitirá seleccionar lo que quiera de la carpeta durante el proceso), inicie sesión, seleccione una galería o cree una nueva y publique la(s) imágen(es).
+  </para>
+</entry>
+
+</help>
diff --git a/ansel/locale/fi_FI/LC_MESSAGES/ansel.mo b/ansel/locale/fi_FI/LC_MESSAGES/ansel.mo
new file mode 100755 (executable)
index 0000000..1ef0c63
Binary files /dev/null and b/ansel/locale/fi_FI/LC_MESSAGES/ansel.mo differ
diff --git a/ansel/locale/fi_FI/help.xml b/ansel/locale/fi_FI/help.xml
new file mode 100755 (executable)
index 0000000..05d513f
--- /dev/null
@@ -0,0 +1,43 @@
+<?xml version="1.0"?>
+<!-- $Horde: ansel/locale/fi_FI/help.xml,v 1.2 2008/02/22 13:35:50 jan Exp $ -->
+<help>
+  <entry id="overview" md5="df2fe059a57c3b7fb286dd279e155bad" state="uptodate">
+  <title>Yleiskuva</title>
+  <heading>Esittely</heading>
+
+  <para>
+    T&#xE4;m&#xE4; ohjelma mahdollista kuvien k&#xE4;sittelyn, talletuksen ja n&#xE4;ytt&#xE4;misen. Voit tehd&#xE4; verkossa n&#xE4;kyv&#xE4;n valokuvakansion tai gallerian. Voit muuttaa kuvan kokoa, k&#xE4;&#xE4;nt&#xE4;&#xE4;, leikata tai muulla tavalla muuttaa kuvia. Voit organisoida kuvia, nimet&#xE4; niit&#xE4; tai kirjoittaa niille kuvauksen.
+  </para>
+</entry>
+  <entry id="upload" md5="ca8e50685a92d0dd33690d9a24d23fb9" state="uptodate">
+  <title>Kuvien tuonti</title>
+
+  <para>
+    Voit tuoda useimmin k&#xE4;ytettyj&#xE4; kuvatyyppej&#xE4;. Jos kuvan tuonnissa on jotain ongelmia, niin sinulle tulee ilmoitus. Jos ep&#xE4;ilet onko kuva oikeaa tyyppi&#xE4;, niin voit aina yritt&#xE4;&#xE4; tuoda sit&#xE4;. Jos aiot tuoda monta kuvaa kerrallaan, niin voit tehd&#xE4; sen ainakin muutamalla tavalla. Voit tuoda kuvat yhten&#xE4; ZIP-tiedostona ja ne k&#xE4;sitell&#xE4;&#xE4;n yhdell&#xE4; kerralla tai jos k&#xE4;yt&#xE4;t Windows XP, niin voi k&#xE4;ytt&#xE4;&#xE4; <ref ENTRY="xppublish" MODULE="ansel">XP Publishing Wizard</ref> toimintoa.
+  </para>
+</entry>
+  <entry id="xppublish" md5="f4067b7d23111b69a9b69920eb17d794" state="uptodate">
+  <title>Kuvien tuonti: Julkaise Windows XP:st&#xE4;</title>
+  <heading>K&#xE4;ytt&#xE4;en Windows XP:n Publish to the Web toimintoa</heading>
+
+  <para>
+    Windows XP on k&#xE4;tev&#xE4; toiminto, jolla voit julkaista aineistoa suoraan ty&#xF6;p&#xF6;yd&#xE4;lt&#xE4;si www-palveluihin.
+  </para>
+
+  <para><b>1. K&#xE4;ytt&#xF6;&#xF6;notto</b></para>
+
+  <para>
+    Tallenna <href url="xppublish.php" app="ansel">XP asetustiedosto</href>. Talleta se ty&#xF6;p&#xF6;yd&#xE4;llesi ja varmista ett&#xE4; tiedoston p&#xE4;&#xE4;te on .reg. Jos se ei ole, niin nime&#xE4; tiedosto uudestaan ja jos Windows kysyy varmistusta muutokselle, niin vastaa kysymykseen kyll&#xE4;.
+  </para>
+
+  <para>
+    Kaksoisnapsauta install-registry.reg. Sinulta kysyt&#xE4;&#xE4;n haluatko vied&#xE4; n&#xE4;m&#xE4; asetukset rekisteriin. Napsauta "OK". Sinulle pit&#xE4;isi nyt tulla ilmoitus, jossa ilmoitetaan ett&#xE4; asetukset on viety rekisteriin; Napauta j&#xE4;lleen "OK". T&#xE4;m&#xE4;n j&#xE4;lkeen asetukset pit&#xE4;isi olla kunnossa ja voit nyt tuoda tiedostoja.
+  </para>
+
+  <para><b>2. Tuonti</b></para>
+
+  <para>
+    Avaa Windows Explorer, mene kansioon, joka sil&#xE4;t&#xE4;&#xE4; ne tiedostot, jotka haluat tuoda. Tarkista viel&#xE4; ett&#xE4; Windows Explorer on vasemmalla osio "File and Folder Tasks". Sinulla pit&#xE4;isi nyt n&#xE4;ky&#xE4; toiminto "Publish this folder to the Web" tai jos olet valinnut vain yhden tiedoston, niin ""Publish this file to the Web". Napsauta t&#xE4;t&#xE4; linkki&#xE4; ja seuraa ruudulle tulevia ohjeita valitaksesi mitk&#xE4; tiedostot haluat tuoda (voita valita kansiosta mink&#xE4; tiedoston tahansa), kirjaudu sis&#xE4;&#xE4;n galleriaan tai luo uusi ja julkaise kuvat.
+  </para>
+</entry>
+</help>
diff --git a/ansel/locale/it_IT/LC_MESSAGES/ansel.mo b/ansel/locale/it_IT/LC_MESSAGES/ansel.mo
new file mode 100644 (file)
index 0000000..fb9ade5
Binary files /dev/null and b/ansel/locale/it_IT/LC_MESSAGES/ansel.mo differ
diff --git a/ansel/locale/ja_JP/LC_MESSAGES/ansel.mo b/ansel/locale/ja_JP/LC_MESSAGES/ansel.mo
new file mode 100755 (executable)
index 0000000..9e04126
Binary files /dev/null and b/ansel/locale/ja_JP/LC_MESSAGES/ansel.mo differ
diff --git a/ansel/locale/lt_LT/LC_MESSAGES/ansel.mo b/ansel/locale/lt_LT/LC_MESSAGES/ansel.mo
new file mode 100755 (executable)
index 0000000..4bdc5af
Binary files /dev/null and b/ansel/locale/lt_LT/LC_MESSAGES/ansel.mo differ
diff --git a/ansel/locale/sl_SI/LC_MESSAGES/ansel.mo b/ansel/locale/sl_SI/LC_MESSAGES/ansel.mo
new file mode 100755 (executable)
index 0000000..a0560ee
Binary files /dev/null and b/ansel/locale/sl_SI/LC_MESSAGES/ansel.mo differ
diff --git a/ansel/locale/sv_SE/LC_MESSAGES/ansel.mo b/ansel/locale/sv_SE/LC_MESSAGES/ansel.mo
new file mode 100755 (executable)
index 0000000..02949c6
Binary files /dev/null and b/ansel/locale/sv_SE/LC_MESSAGES/ansel.mo differ
diff --git a/ansel/locale/tr_TR/LC_MESSAGES/ansel.mo b/ansel/locale/tr_TR/LC_MESSAGES/ansel.mo
new file mode 100644 (file)
index 0000000..6fcb68d
Binary files /dev/null and b/ansel/locale/tr_TR/LC_MESSAGES/ansel.mo differ
diff --git a/ansel/locale/zh_TW/LC_MESSAGES/ansel.mo b/ansel/locale/zh_TW/LC_MESSAGES/ansel.mo
new file mode 100755 (executable)
index 0000000..8da5234
Binary files /dev/null and b/ansel/locale/zh_TW/LC_MESSAGES/ansel.mo differ
diff --git a/ansel/map_edit.php b/ansel/map_edit.php
new file mode 100644 (file)
index 0000000..3313b10
--- /dev/null
@@ -0,0 +1,172 @@
+<?php
+/**
+ * $Horde: ansel/map_edit.php,v 1.30 2009/07/30 16:18:59 mrubinsk Exp $
+ *
+ * Copyright 2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ */
+@define('ANSEL_BASE', dirname(__FILE__));
+require_once ANSEL_BASE . '/lib/base.php';
+
+/* Script includes */
+Horde::addExternalScriptFile('http://maps.google.com/maps?file=api&v=2&sensor=false&key=' . $GLOBALS['conf']['api']['googlemaps'], 'ansel');
+Horde::addScriptFile('prototype.js', 'horde');
+Horde::addScriptFile('googlemap.js');
+Horde::addScriptFile('googlemap_edit.js');
+
+$image_id = Horde_Util::getFormData('image');
+
+/* Sanity checks, perms etc... */
+if (empty($image_id)) {
+    Horde::fatal(_("An error has occured retrieving the image. Details have been logged."), __FILE__, __LINE__, true);
+}
+$image = $ansel_storage->getImage($image_id);
+if (is_a($image, 'PEAR_Error')) {
+    Horde::fatal(_("An error has occured retrieving the image. Details have been logged."), __FILE__, __LINE__, true);
+}
+$gallery = $ansel_storage->getGallery($image->gallery);
+if (is_a($gallery, 'PEAR_Error')) {
+    Horde::fatal(_("An error has occured retrieving the image. Details have been logged."), __FILE__, __LINE__, true);
+}
+if (!$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+    Horde::fatal(_("Not Authorized. Details have been logged for the server administrator."), __FILE__, __LINE__, true);
+}
+
+/* Determine if we already have a geotag or are we tagging it for the 1st time */
+if (empty($image->lat)) {
+    $geodata = array('image_id' => $image->id,
+                     'image_latitude' => "20",
+                     'image_longitude' => "40",
+                     'image_location' => '',
+                     'icon' => Ansel::getImageUrl($image->id, 'mini', true),
+                     'markerOnly' => true,
+                     'draggable' => true);
+    $isNew = 1;
+} else {
+    $geodata = array('image_id' => $image->id,
+                     'image_latitude' => $image->lat,
+                     'image_longitude' => $image->lng,
+                     'image_location' => $image->location,
+                     'icon' => Ansel::getImageUrl($image->id, 'mini', true),
+                     'markerOnly' => true,
+                     'draggable' => true);
+    $isNew = 0;
+}
+
+/* JSON representation of the image's geotag */
+$json = Horde_Serialize::serialize(array($geodata), Horde_Serialize::JSON);
+
+/* Gettext strings */
+$save = _("Save");
+$returnText = _("Return to Image View");
+$findText = _("Find");
+$fetchingText = _("Fetching location");
+$locateText = _("Locate image at:");
+$errorText = _("Unable to find location. Error code:");
+
+/* Links, img src etc...  */
+$returnLink = Ansel::getUrlFor('view', array('view' => 'Image',
+                                             'image' => $image_id,
+                                             'gallery' => $gallery->id));
+$image_tag = '<img src="' . Ansel::getImageUrl($image_id, 'thumb', true) . '" alt="[thumbnail]" />';
+/* Url for geotag ajax helper */
+$gt = Horde_Ajax_Imple::factory(array('ansel', 'ImageSaveGeotag'));
+$gtUrl = $gt->getUrl();
+
+$loadingImg = Horde::img('loading.gif', _("Loading..."), '', $registry->getImageDir());
+
+/* Obtain other geotagged images to possibly locate this image at */
+$imgs = $GLOBALS['ansel_storage']->getRecentImagesGeodata(Horde_Auth::getAuth());
+if (count($imgs) > 0) {
+    $other_images = '<div class="ansel_location_sameas">' . _("Click on a thumbnail to locate at the same point.") . '<br />';
+    foreach ($imgs as $id => $data) {
+        if ($id != $image_id) {
+            if (!empty($data['image_location'])) {
+                $title = $data['image_location'];
+            } else {
+                $title = _point2Deg($data['image_latitude'], true) . ' ' . _point2Deg($data['image_longitude']);
+            }
+            $other_images .= Horde::link('#', $title, '', '', "mapEdit.setLocation('" . $data['image_latitude'] . "', '" . $data['image_longitude'] . "', '" . $data['image_location'] . "');return false") . '<img src="' . Ansel::getImageUrl($id, 'mini', true) . '" alt="[thumbnail]" /></a>';
+        }
+    }
+    $other_images .= '</div>';
+} else {
+    $other_images = '';
+}
+
+
+/* Build the HTML */
+$html = <<<EOT
+<div id="status">&nbsp;</div>
+<div style="width:450px;float:left;">
+  <div id="ansel_map" style="width:450px;height:450px;"></div>
+</div>
+<div style="float:left;width:250px;min-height:450px;">
+ <div class="control">
+  <h4>{$locateText}</h4>
+  <form>
+   <input type="text" id="locationInput" name="locationInput" />
+   <div id="locationInput_results" class="autocomplete"></div>
+   <span id="locationInput_loading_img" style="display:none;">{$loadingImg}</span>
+   <input id="locationAction" value="{$findText}" class="button" type="submit" />
+  </form>
+ </div>
+ {$other_images}
+ <div class="control" style="vertical-align:bottom;">
+  <div style="text-align:center;margin-top:6px;">{$image_tag}</div>
+  <div class="ansel_geolocation">
+   <div id="ansel_locationtext">&nbsp;</div>
+   <div id="ansel_latlng">&nbsp;</div>
+  </div>
+ </div>
+</div>
+<div class="clear"></div>
+<div class="control">
+ <input class="button" id="saveButton" type="submit" value="{$save}" /><input class="button" type="submit" onclick="window.close();" value="{$returnText}" />
+</div>
+<script type="text/javascript">
+    var mapEdit;
+    Event.observe(document, "dom:loaded", function() {
+        var options = {
+            mainMap:  'ansel_map',
+            xurl: '{$gtUrl}',
+            image_id: {$image_id},
+            gettext: {fetching: '{$fetchingText}', errortext: '{$errorText}'},
+            points:  {$json},
+            isNew: {$isNew},
+            saveId: 'saveButton'
+        };
+
+        mapEdit = new Ansel_MapEdit(options);
+        $('locationInput').focus();
+    });
+</script>
+EOT;
+/* Autocompleter for locations we already have in our DB */
+$ac = Horde_Ajax_Imple::factory(array('ansel', 'LocationAutoCompleter'),
+                                array('triggerId' => 'locationInput',
+                                      'resultsId' => 'locationInput_results',
+                                      'map' => 'mapEdit'));
+$ac->attach();
+//$html .= Horde_Util::bufferOutput(array($ac, 'attach'));
+
+/* Start the output */
+include ANSEL_TEMPLATES . '/common-header.inc';
+echo '<div class="header">' . sprintf(_("Update position of %s"), $image->filename) . '</div>';
+echo $html;
+require $registry->get('templates', 'horde') . '/common-footer.inc';
+
+// Helper function for displaying Lat/Lng values
+function _point2Deg($value, $lat = false)
+{
+    $letter = $lat ? ($value > 0 ? "N" : "S") : ($value > 0 ? "E" : "W");
+    $value = abs($value);
+    $deg = floor($value);
+    $min = floor(($value - $deg) * 60);
+    $sec = ($value - $deg - $min / 60) * 3600;
+    return $deg . "&deg; " . $min . '\' ' . round($sec, 2) . '" ' . $letter;
+}
diff --git a/ansel/perms.php b/ansel/perms.php
new file mode 100644 (file)
index 0000000..540d2e1
--- /dev/null
@@ -0,0 +1,275 @@
+<?php
+/**
+ * $Horde: ansel/perms.php,v 1.27 2009/07/08 18:28:40 slusarz Exp $
+ *
+ * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ */
+
+$fieldsList['show'] = 0;
+$fieldsList['read'] = 1;
+$fieldsList['edit'] = 2;
+$fieldsList['delete'] = 3;
+
+define('ANSEL_BASE', dirname(__FILE__));
+require_once ANSEL_BASE . '/lib/base.php';
+require_once 'Horde/Group.php';
+
+$groups = &Group::singleton();
+$auth = Horde_Auth::singleton($conf['auth']['driver']);
+
+$form = null;
+$reload = false;
+$actionID = Horde_Util::getFormData('actionID', 'edit');
+switch ($actionID) {
+case 'edit':
+    $share = &$ansel_storage->getGallery(Horde_Util::getFormData('cid'));
+    if (!is_a($share, 'PEAR_Error')) {
+        $form = 'edit.inc';
+        $perm = &$share->getPermission();
+    } elseif (($share_name = Horde_Util::getFormData('share')) !== null) {
+        $share = &$ansel_storage->shares->getShare($share_name);
+        if (!is_a($share, 'PEAR_Error')) {
+            $form = 'edit.inc';
+            $perm = &$share->getPermission();
+        }
+    }
+
+    if (is_a($share, 'PEAR_Error')) {
+        $notification->push($share, 'horde.error');
+    } elseif (isset($share) && Horde_Auth::getAuth() != $share->get('owner')) {
+        exit('permission denied');
+    }
+    break;
+
+case 'editform':
+case 'editforminherit':
+    $share = &$ansel_storage->getGallery(Horde_Util::getFormData('cid'));
+    if (is_a($share, 'PEAR_Error')) {
+        $notification->push(_("Attempt to edit a non-existent share."), 'horde.error');
+    } else {
+        if (Horde_Auth::getAuth() != $share->get('owner')) {
+            exit('permission denied');
+        }
+        $perm = &$share->getPermission();
+
+        // Process owner and owner permissions.
+        $old_owner = $share->get('owner');
+        $new_owner = Horde_Util::getFormData('owner', $old_owner);
+        if ($old_owner !== $new_owner && !empty($new_owner)) {
+            if ($old_owner != Horde_Auth::getAuth() && !Horde_Auth::isAdmin()) {
+                $notification->push(_("Only the owner or system administrator may change ownership or owner permissions for a share"), 'horde.error');
+            } else {
+                $share->set('owner', $new_owner);
+                $share->save();
+                if (Horde_Util::getFormData('owner_show')) {
+                    $perm->addUserPermission($new_owner, PERMS_SHOW, false);
+                } else {
+                    $perm->removeUserPermission($new_owner, PERMS_SHOW, false);
+                }
+                if (Horde_Util::getFormData('owner_read')) {
+                    $perm->addUserPermission($new_owner, PERMS_READ, false);
+                } else {
+                    $perm->removeUserPermission($new_owner, PERMS_READ, false);
+                }
+                if (Horde_Util::getFormData('owner_edit')) {
+                    $perm->addUserPermission($new_owner, PERMS_EDIT, false);
+                } else {
+                    $perm->removeUserPermission($new_owner, PERMS_EDIT, false);
+                }
+                if (Horde_Util::getFormData('owner_delete')) {
+                    $perm->addUserPermission($new_owner, PERMS_DELETE, false);
+                } else {
+                    $perm->removeUserPermission($new_owner, PERMS_DELETE, false);
+                }
+            }
+        }
+
+        // Process default permissions.
+        if (Horde_Util::getFormData('default_show')) {
+            $perm->addDefaultPermission(PERMS_SHOW, false);
+        } else {
+            $perm->removeDefaultPermission(PERMS_SHOW, false);
+        }
+        if (Horde_Util::getFormData('default_read')) {
+            $perm->addDefaultPermission(PERMS_READ, false);
+        } else {
+            $perm->removeDefaultPermission(PERMS_READ, false);
+        }
+        if (Horde_Util::getFormData('default_edit')) {
+            $perm->addDefaultPermission(PERMS_EDIT, false);
+        } else {
+            $perm->removeDefaultPermission(PERMS_EDIT, false);
+        }
+        if (Horde_Util::getFormData('default_delete')) {
+            $perm->addDefaultPermission(PERMS_DELETE, false);
+        } else {
+            $perm->removeDefaultPermission(PERMS_DELETE, false);
+        }
+
+        // Process guest permissions.
+        if (Horde_Util::getFormData('guest_show')) {
+            $perm->addGuestPermission(PERMS_SHOW, false);
+        } else {
+            $perm->removeGuestPermission(PERMS_SHOW, false);
+        }
+        if (Horde_Util::getFormData('guest_read')) {
+            $perm->addGuestPermission(PERMS_READ, false);
+        } else {
+            $perm->removeGuestPermission(PERMS_READ, false);
+        }
+        if (Horde_Util::getFormData('guest_edit')) {
+            $perm->addGuestPermission(PERMS_EDIT, false);
+        } else {
+            $perm->removeGuestPermission(PERMS_EDIT, false);
+        }
+        if (Horde_Util::getFormData('guest_delete')) {
+            $perm->addGuestPermission(PERMS_DELETE, false);
+        } else {
+            $perm->removeGuestPermission(PERMS_DELETE, false);
+        }
+
+        // Process creator permissions.
+        if (Horde_Util::getFormData('creator_show')) {
+            $perm->addCreatorPermission(PERMS_SHOW, false);
+        } else {
+            $perm->removeCreatorPermission(PERMS_SHOW, false);
+        }
+        if (Horde_Util::getFormData('creator_read')) {
+            $perm->addCreatorPermission(PERMS_READ, false);
+        } else {
+            $perm->removeCreatorPermission(PERMS_READ, false);
+        }
+        if (Horde_Util::getFormData('creator_edit')) {
+            $perm->addCreatorPermission(PERMS_EDIT, false);
+        } else {
+            $perm->removeCreatorPermission(PERMS_EDIT, false);
+        }
+        if (Horde_Util::getFormData('creator_delete')) {
+            $perm->addCreatorPermission(PERMS_DELETE, false);
+        } else {
+            $perm->removeCreatorPermission(PERMS_DELETE, false);
+        }
+
+        // Process user permissions.
+        $u_names = Horde_Util::getFormData('u_names');
+        $u_show = Horde_Util::getFormData('u_show');
+        $u_read = Horde_Util::getFormData('u_read');
+        $u_edit = Horde_Util::getFormData('u_edit');
+        $u_delete = Horde_Util::getFormData('u_delete');
+
+        foreach ($u_names as $key => $user) {
+            // If the user is empty, or we've already set permissions
+            // via the owner_ options, don't do anything here.
+            if (empty($user) || $user == $new_owner) {
+                continue;
+            }
+
+            if (!empty($u_show[$key])) {
+                $perm->addUserPermission($user, PERMS_SHOW, false);
+            } else {
+                $perm->removeUserPermission($user, PERMS_SHOW, false);
+            }
+            if (!empty($u_read[$key])) {
+                $perm->addUserPermission($user, PERMS_READ, false);
+            } else {
+                $perm->removeUserPermission($user, PERMS_READ, false);
+            }
+            if (!empty($u_edit[$key])) {
+                $perm->addUserPermission($user, PERMS_EDIT, false);
+            } else {
+                $perm->removeUserPermission($user, PERMS_EDIT, false);
+            }
+            if (!empty($u_delete[$key])) {
+                $perm->addUserPermission($user, PERMS_DELETE, false);
+            } else {
+                $perm->removeUserPermission($user, PERMS_DELETE, false);
+            }
+        }
+
+        // Process group permissions.
+        $g_names = Horde_Util::getFormData('g_names');
+        $g_show = Horde_Util::getFormData('g_show');
+        $g_read = Horde_Util::getFormData('g_read');
+        $g_edit = Horde_Util::getFormData('g_edit');
+        $g_delete = Horde_Util::getFormData('g_delete');
+
+        foreach ($g_names as $key => $group) {
+            if (empty($group)) {
+                continue;
+            }
+
+            if (!empty($g_show[$key])) {
+                $perm->addGroupPermission($group, PERMS_SHOW, false);
+            } else {
+                $perm->removeGroupPermission($group, PERMS_SHOW, false);
+            }
+            if (!empty($g_read[$key])) {
+                $perm->addGroupPermission($group, PERMS_READ, false);
+            } else {
+                $perm->removeGroupPermission($group, PERMS_READ, false);
+            }
+            if (!empty($g_edit[$key])) {
+                $perm->addGroupPermission($group, PERMS_EDIT, false);
+            } else {
+                $perm->removeGroupPermission($group, PERMS_EDIT, false);
+            }
+            if (!empty($g_delete[$key])) {
+                $perm->addGroupPermission($group, PERMS_DELETE, false);
+            } else {
+                $perm->removeGroupPermission($group, PERMS_DELETE, false);
+            }
+        }
+
+        $share->setPermission($perm);
+        $share->save();
+
+        /* If we were asked to, push permissions to all child shares
+         * to. */
+        if ($actionID == 'editforminherit') {
+            $share->inheritPermissions();
+        }
+
+        $notification->push(sprintf(_("Updated %s."), $share->get('name')),
+                            'horde.success');
+        $form = 'edit.inc';
+    }
+    break;
+}
+
+if (is_a($share, 'PEAR_Error')) {
+    $title = _("Edit Permissions");
+} else {
+    $children = $GLOBALS['ansel_storage']->listGalleries(PERMS_READ, false,
+                                                         $share);
+    $title = sprintf(_("Edit Permissions for %s"), $share->get('name'));
+}
+
+if ($auth->hasCapability('list')) {
+    $userList = $auth->listUsers();
+    sort($userList);
+} else {
+    $userList = array();
+}
+
+$groupList = $groups->listGroups();
+asort($groupList);
+if (is_a($groupList, 'PEAR_Error')) {
+    Horde::logMessage($groupList, __FILE__, __LINE__, PEAR_LOG_NOTICE);
+    $groupList = array();
+}
+
+require $registry->get('templates', 'horde') . '/common-header.inc';
+$notification->notify(array('listeners' => 'status'));
+if (!empty($form)) {
+    /* Need to temporarily put the gallery name in the share so the form
+    * template will find it when rendering the Save button */
+    $share->set('name', $share->get('name'));
+    require $registry->get('templates', 'horde') . '/shares/' . $form;
+}
+
+require $registry->get('templates', 'horde') . '/common-footer.inc';
diff --git a/ansel/po/.cvsignore b/ansel/po/.cvsignore
new file mode 100755 (executable)
index 0000000..fd8854c
--- /dev/null
@@ -0,0 +1 @@
+messages.po
diff --git a/ansel/po/README b/ansel/po/README
new file mode 100755 (executable)
index 0000000..a985e94
--- /dev/null
@@ -0,0 +1 @@
+see horde/po/README
diff --git a/ansel/po/ansel.pot b/ansel/po/ansel.pot
new file mode 100644 (file)
index 0000000..3222978
--- /dev/null
@@ -0,0 +1,2571 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright YEAR Horde Project
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: dev@lists.horde.org\n"
+"POT-Creation-Date: 2008-08-07 14:52-0400\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
+
+#: scripts/recursive_import.php:111
+#, php-format
+msgid "\"%s\" is not a directory."
+msgstr ""
+
+#: templates/view/list.inc:3 templates/list/header.inc:3
+#: templates/group/category.inc:47 templates/group/owner.inc:52
+#, php-format
+msgid "%d Gallery"
+msgid_plural "%d Galleries"
+msgstr[0] ""
+msgstr[1] ""
+
+#: templates/group/header.inc:3
+#, php-format
+msgid "%d Group"
+msgid_plural "%d Groups"
+msgstr[0] ""
+msgstr[1] ""
+
+#: templates/view/gallerylightbox.inc:104 templates/view/results.inc:83
+#, php-format
+msgid "%d Item"
+msgid_plural "%d Items"
+msgstr[0] ""
+msgstr[1] ""
+
+#: lib/Exif.php:287
+#, php-format
+msgid "%d bytes"
+msgstr ""
+
+#: templates/tile/image.inc:11
+#, php-format
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+
+#: templates/faces/face.inc:7
+#, php-format
+msgid "%d gallery"
+msgid_plural "%d galleries"
+msgstr[0] ""
+msgstr[1] ""
+
+#: templates/view/gallery.inc:85
+#, php-format
+msgid "%d item"
+msgid_plural "%d items"
+msgstr[0] ""
+msgstr[1] ""
+
+#: templates/faces/search.inc:9 lib/XRequest/TagActions.php:115
+#: lib/Widget/Tags.php:93 lib/Views/Results.php:291
+#, php-format
+msgid "%d photo"
+msgid_plural "%d photos"
+msgstr[0] ""
+msgstr[1] ""
+
+#: img/upload.php:213
+#, php-format
+msgid "%d photo was uploaded."
+msgid_plural "%d photos were uploaded."
+msgstr[0] ""
+msgstr[1] ""
+
+#: lib/Exif.php:138 lib/Exif.php:144
+#, php-format
+msgid "%d pixels"
+msgstr ""
+
+#: lib/Exif.php:168 lib/Exif.php:183
+#, php-format
+msgid "%d sec"
+msgstr ""
+
+#: templates/view/list.inc:3 templates/list/header.inc:3
+#, php-format
+msgid "%d to %d of %d Galleries"
+msgstr ""
+
+#: templates/group/header.inc:3
+#, php-format
+msgid "%d to %d of %d Groups"
+msgstr ""
+
+#: templates/view/gallery.inc:85 templates/view/gallerylightbox.inc:104
+#: templates/view/results.inc:83
+#, php-format
+msgid "%d to %d of %d Items"
+msgstr ""
+
+#: gallery/sort.php:60
+#, php-format
+msgid "%s :: Sort"
+msgstr ""
+
+#: lib/Widget/SimilarPhotos.php:81
+#, php-format
+msgid "%s from %s"
+msgstr ""
+
+#: rss.php:112
+#, php-format
+msgid "%s on %s"
+msgstr ""
+
+#: templates/group/owner.inc:16 lib/Ansel.php:637
+#: lib/Widget/OtherGalleries.php:36 lib/Views/List.php:167
+#, php-format
+msgid "%s's Galleries"
+msgstr ""
+
+#: templates/faces/faces.inc:8
+#, php-format
+msgid "%s: faces %s"
+msgstr ""
+
+#: lib/Ansel.php:2466
+#, php-format
+msgid "(c) %s %s"
+msgstr ""
+
+#: scripts/ansel.php:326
+msgid "-a, --add[=filename]         Add local file to selected gallery"
+msgstr ""
+
+#: scripts/ansel.php:324
+msgid ""
+"-c, --create[=name/description/owner]\n"
+"                             Create gallery (and use it)  Combined with -g "
+"to create a subgallery."
+msgstr ""
+
+#: scripts/ansel.php:327
+msgid ""
+"-d, --dir[=directory]        Add all files from the directory to the "
+"selected\n"
+"                             gallery"
+msgstr ""
+
+#: scripts/recursive_import.php:190
+msgid ""
+"-d, --dir[=directory]        Recursively add all files from the directory, "
+"creating\n"
+"                             a gallery for each directory"
+msgstr ""
+
+#: scripts/ansel.php:325
+msgid "-g, --gallery[=shortname]    Select gallery to use"
+msgstr ""
+
+#: scripts/ansel.php:322 scripts/recursive_import.php:189
+msgid "-h, --help                   Show this help"
+msgstr ""
+
+#: scripts/ansel.php:323
+msgid ""
+"-l, --list                   List galleries or photos (if combined with -g)"
+msgstr ""
+
+#: scripts/ansel.php:329 scripts/recursive_import.php:192
+msgid "-p, --password[=password]    Horde login password"
+msgstr ""
+
+#: scripts/ansel.php:330
+msgid "-t, --caption[=caption]      Caption for photo (if combined with -a)"
+msgstr ""
+
+#: scripts/ansel.php:328 scripts/recursive_import.php:191
+msgid "-u, --username[=username]    Horde login username"
+msgstr ""
+
+#: lib/Exif.php:159
+#, php-format
+msgid "90 deg CCW"
+msgstr ""
+
+#: lib/Exif.php:161
+#, php-format
+msgid "90 deg CCW Mirrored"
+msgstr ""
+
+#: lib/Exif.php:163
+#, php-format
+msgid "90 deg CW"
+msgstr ""
+
+#: lib/Exif.php:157
+#, php-format
+msgid "90 deg CW Mirrored"
+msgstr ""
+
+#: config/styles.php.dist:116
+msgid "A Lightbox Inspired Style (No Background)"
+msgstr ""
+
+#: config/styles.php.dist:104
+msgid "A Lightbox Inspired Style (White Background)"
+msgstr ""
+
+#: lib/api.php:479
+msgid "A gallery to add this photo to is required."
+msgstr ""
+
+#: lib/Exif.php:130
+msgid "A-DEP"
+msgstr ""
+
+#: gallery.php:54
+#, php-format
+msgid "Access denied adding a gallery to \"%s\"."
+msgstr ""
+
+#: xppublish.php:178 scripts/ansel.php:134 lib/api.php:377 lib/api.php:470
+#, php-format
+msgid "Access denied adding photos to \"%s\"."
+msgstr ""
+
+#: lib/Ansel.php:1622
+msgid "Access denied adding tags to this gallery."
+msgstr ""
+
+#: lib/Ansel.php:2548
+msgid "Access denied adding tags to this photo."
+msgstr ""
+
+#: lib/Ansel.php:1113
+#, php-format
+msgid "Access denied copying photos to \"%s\"."
+msgstr ""
+
+#: lib/api.php:611 gallery/delete.php:28 gallery/delete.php:67
+#, php-format
+msgid "Access denied deleting gallery \"%s\"."
+msgstr ""
+
+#: image.php:533 lib/api.php:550 lib/Views/Results.php:125
+#, php-format
+msgid "Access denied deleting photos from \"%s\"."
+msgstr ""
+
+#: gallery.php:85 lib/Ansel.php:2357
+#, php-format
+msgid "Access denied downloading photos from \"%s\"."
+msgstr ""
+
+#: gallery.php:155
+msgid "Access denied editing galleries."
+msgstr ""
+
+#: lib/Faces.php:868 gallery/sort.php:32 faces/gallery.php:34
+#, php-format
+msgid "Access denied editing gallery \"%s\"."
+msgstr ""
+
+#: image.php:264 image.php:654 lib/Faces.php:557 lib/Faces.php:655
+#: lib/XRequest/EditFaces.php:134 faces/custom.php:29 faces/delete.php:29
+#: faces/name.php:28
+msgid "Access denied editing the photo."
+msgstr ""
+
+#: lib/Ansel.php:1070
+#, php-format
+msgid "Access denied moving photos to \"%s\"."
+msgstr ""
+
+#: lib/Ansel.php:1072
+#, php-format
+msgid "Access denied removing photos from \"%s\"."
+msgstr ""
+
+#: gallery.php:198
+#, php-format
+msgid "Access denied saving gallery \"%s\"."
+msgstr ""
+
+#: image.php:164 image.php:324 image.php:362
+#, php-format
+msgid "Access denied saving photo to \"%s\"."
+msgstr ""
+
+#: gallery/captions.php:33
+#, php-format
+msgid "Access denied setting captions for %s."
+msgstr ""
+
+#: lib/Views/Abstract.php:56
+#, php-format
+msgid "Access denied to gallery \"%s\"."
+msgstr ""
+
+#: lib/Ansel.php:1607 lib/Views/Gallery.php:97 lib/Block/gallery.php:143
+#: lib/Block/recent_comments.php:154 lib/Block/recently_added.php:190
+msgid "Access denied viewing this gallery."
+msgstr ""
+
+#: lib/Ansel.php:2527 img/download.php:26 img/full.php:26 img/index.php:25
+#: img/mini.php:25 img/prettythumb.php:26 img/screen.php:25 img/thumb.php:25
+msgid "Access denied viewing this photo."
+msgstr ""
+
+#: faces/claim.php:41 faces/report.php:43
+msgid "Action was cancelled."
+msgstr ""
+
+#: lib/Widget/Actions.php:22
+msgid "Actions"
+msgstr ""
+
+#: templates/view/gallery.inc:108 templates/view/gallerylightbox.inc:127
+#: templates/view/results.inc:97
+msgid "Actions: "
+msgstr ""
+
+#: lib/Widget/Tags.php:58
+msgid "Add"
+msgstr ""
+
+#: browse.php:26
+msgid "Add Content"
+msgstr ""
+
+#: img/upload.php:231
+msgid "Add Photo"
+msgstr ""
+
+#: templates/faces/gallery.inc:23
+msgid "Add a custom face range"
+msgstr ""
+
+#: lib/Widget/Actions.php:79
+msgid "Add to bookmarks"
+msgstr ""
+
+#: gallery.php:40 gallery.php:269
+msgid "Adding A New Gallery"
+msgstr ""
+
+#: gallery.php:73
+#, php-format
+msgid "Adding A Subgallery to %s"
+msgstr ""
+
+#: scripts/recursive_import.php:171
+msgid "Adding subdirectories:"
+msgstr ""
+
+#: lib/api.php:188
+msgid "Administrators"
+msgstr ""
+
+#: report.php:49
+msgid "Advertisement content"
+msgstr ""
+
+#: templates/view/gallery.inc:105 templates/view/gallerylightbox.inc:124
+#: templates/view/results.inc:93
+msgid "All"
+msgstr ""
+
+#: lib/Block/recent_comments.php:68 lib/Block/recently_added.php:73
+msgid "All Galleries"
+msgstr ""
+
+#: faces/search/all.php:17 faces/search/tabs.php:32
+msgid "All faces"
+msgstr ""
+
+#: templates/gallery/gallery.inc:79
+msgid "Allow all ages"
+msgstr ""
+
+#: templates/gallery/gallery.inc:101 config/prefs.php.dist:209
+msgid "Anyone"
+msgstr ""
+
+#: lib/Exif.php:345
+msgid "Aperture"
+msgstr ""
+
+#: templates/view/gallery.inc:32 templates/view/gallerylightbox.inc:33
+#: templates/view/results.inc:20
+msgid "Are you sure you want to delete the selected photos?"
+msgstr ""
+
+#: lib/Exif.php:358
+msgid "Artist"
+msgstr ""
+
+#: templates/view/list.inc:20
+msgid "Ascending"
+msgstr ""
+
+#: perms.php:53
+msgid "Attempt to edit a non-existent share."
+msgstr ""
+
+#: templates/gallery/gallery.inc:102 config/prefs.php.dist:210
+msgid "Authenticated users"
+msgstr ""
+
+#: config/prefs.php.dist:201
+msgid "Automatically watermark photos?"
+msgstr ""
+
+#: lib/Exif.php:126
+msgid "Av-priority"
+msgstr ""
+
+#: lib/Exif.php:230
+msgid "Average"
+msgstr ""
+
+#: templates/view/image.inc:86 templates/view/image.inc:87
+#: templates/view/image.inc:111 templates/view/image.inc:112
+#: templates/image/crop_image.inc:104 templates/image/edit_image.inc:20
+#: templates/image/resize_image.inc:49
+msgid "Back to gallery"
+msgstr ""
+
+#: image.php:580 image.php:627 lib/Views/Results.php:160
+#: lib/Views/Results.php:208
+msgid "Bad input."
+msgstr ""
+
+#: lib/Forms/Watermark.php:54 config/prefs.php.dist:178
+msgid "Bottom"
+msgstr ""
+
+#: lib/Ansel.php:524 config/prefs.php.dist:49
+msgid "Browse"
+msgstr ""
+
+#: lib/Ansel.php:645
+msgid "Browse Tags"
+msgstr ""
+
+#: lib/Exif.php:329
+msgid "Camera Make"
+msgstr ""
+
+#: lib/Exif.php:330
+msgid "Camera Model"
+msgstr ""
+
+#: lib/Exif.php:356
+msgid "Camera Orientation"
+msgstr ""
+
+#: lib/Ansel.php:464
+#, php-format
+msgid "Can't get unknown file type \"%s\"."
+msgstr ""
+
+#: disclamer.php:32 disclamer.php:35 report.php:47
+#: templates/image/crop_image.inc:105 templates/image/edit_image.inc:21
+#: templates/image/resize_image.inc:50
+#: templates/gallery/delete_confirmation.inc:11 templates/faces/custom.inc:53
+#: templates/faces/define.inc:38 faces/claim.php:40 faces/report.php:41
+#: faces/savecustom.php:27
+msgid "Cancel"
+msgstr ""
+
+#: templates/image/preview_cropimage.inc:20
+#: templates/image/preview_cropimage.inc:21
+#: templates/image/preview_image.inc:23 templates/image/preview_image.inc:24
+msgid "Cancel Change"
+msgstr ""
+
+#: faces/search/image_save.php:74
+msgid "Cannot read photo signature"
+msgstr ""
+
+#: faces/search/image_save.php:86
+msgid "Cannot save photo signature"
+msgstr ""
+
+#: faces/search/image.php:59 faces/search/image_save.php:66
+msgid "Cannot store search photo"
+msgstr ""
+
+#: lib/Forms/Image.php:39 lib/Forms/Upload.php:52
+msgid "Caption"
+msgstr ""
+
+#: gallery/captions.php:61
+msgid "Caption Editor"
+msgstr ""
+
+#: gallery/captions.php:52
+msgid "Captions Saved."
+msgstr ""
+
+#: config/prefs.php.dist:62
+msgid "Category"
+msgstr ""
+
+#: lib/Forms/Watermark.php:46 lib/Forms/Watermark.php:53
+#: config/prefs.php.dist:166 config/prefs.php.dist:177
+msgid "Center"
+msgstr ""
+
+#: lib/Exif.php:231
+msgid "Center Weighted Average"
+msgstr ""
+
+#: config/prefs.php.dist:14
+msgid ""
+"Change display options such as which view to display by default, how many "
+"photos to display on a page, and the default gallery style to use."
+msgstr ""
+
+#: lib/Widget/Actions.php:87
+msgid "Change properties"
+msgstr ""
+
+#: config/prefs.php.dist:23
+msgid ""
+"Change your user permission options such as who can download original "
+"photos, and what permissions newly created galleries should have by default."
+msgstr ""
+
+#: config/prefs.php.dist:30
+msgid "Change your watermark options."
+msgstr ""
+
+#: faces/savecustom.php:28
+msgid "Changes cancelled."
+msgstr ""
+
+#: templates/tile/face.inc:27 lib/Faces.php:1145 lib/Widget/ImageFaces.php:168
+msgid "Claim"
+msgstr ""
+
+#: lib/XRequest/EditCaption.php:43
+msgid "Click to add caption..."
+msgstr ""
+
+#: lib/Exif.php:244
+msgid "Cloudy"
+msgstr ""
+
+#: lib/Exif.php:295
+msgid "Color Sequential Area Sensor"
+msgstr ""
+
+#: lib/Exif.php:297
+msgid "Color Sequential Linear Sensor"
+msgstr ""
+
+#: lib/Exif.php:353
+msgid "Color Space"
+msgstr ""
+
+#: lib/Forms/Ecard.php:42
+msgid "Comments:"
+msgstr ""
+
+#: lib/Exif.php:265
+msgid "Compulsory Flash"
+msgstr ""
+
+#: lib/Exif.php:267
+msgid "Compulsory Flash, Return light detected"
+msgstr ""
+
+#: lib/Exif.php:266
+msgid "Compulsory Flash, Return light not detected"
+msgstr ""
+
+#: disclamer.php:25
+msgid "Content Disclaimer"
+msgstr ""
+
+#: lib/Report.php:98
+#, php-format
+msgid "Content abuse report in %s"
+msgstr ""
+
+#: disclamer.php:32
+#, php-format
+msgid "Continue - I'm over %d"
+msgstr ""
+
+#: lib/Views/Results.php:225
+#, php-format
+msgid "Copied %d photo from %s to %s"
+msgid_plural "Copied %d photos from %s to %s"
+msgstr[0] ""
+msgstr[1] ""
+
+#: image.php:634
+#, php-format
+msgid "Copied %d photo to %s"
+msgid_plural "Copied %d photos to %s"
+msgstr[0] ""
+msgstr[1] ""
+
+#: templates/view/gallery.inc:116 templates/view/gallerylightbox.inc:135
+#: templates/view/results.inc:103
+msgid "Copy"
+msgstr ""
+
+#: lib/Exif.php:357
+msgid "Copyright"
+msgstr ""
+
+#: report.php:52
+msgid "Copyright infringement"
+msgstr ""
+
+#: lib/Ansel.php:917
+#, php-format
+msgid "Could not save gallery, the slug, \"%s\", already exists."
+msgstr ""
+
+#: scripts/ansel.php:187
+msgid "Couldn't list galleries."
+msgstr ""
+
+#: scripts/ansel.php:44
+msgid "Couldn't read command-line options."
+msgstr ""
+
+#: templates/xppublish/list.inc:19
+msgid "Create"
+msgstr ""
+
+#: faces/custom.php:68 faces/search/image_define.php:25
+msgid "Create a new face"
+msgstr ""
+
+#: templates/xppublish/new.inc:8
+msgid "Create a new gallery"
+msgstr ""
+
+#: templates/xppublish/list.inc:18
+msgid "Create a new gallery:"
+msgstr ""
+
+#: lib/Widget/Actions.php:86
+msgid "Create a subgallery"
+msgstr ""
+
+#: lib/Tile/Gallery.php:114
+msgid "Created:"
+msgstr ""
+
+#: scripts/recursive_import.php:120
+#, php-format
+msgid "Creating gallery: \"%s\" (%s)"
+msgstr ""
+
+#: image.php:666 templates/image/crop_image.inc:126
+#: templates/image/edit_image.inc:43 templates/image/resize_image.inc:72
+msgid "Crop"
+msgstr ""
+
+#: lib/Exif.php:248
+msgid "Custom"
+msgstr ""
+
+#: lib/Forms/Watermark.php:32
+msgid "Custom Watermark"
+msgstr ""
+
+#: config/prefs.php.dist:42
+msgid ""
+"Custom text to display describing your galleries. This will be displayed in "
+"place of your username when grouping galleries by username."
+msgstr ""
+
+#: config/prefs.php.dist:113 config/prefs.php.dist:157
+msgid "Custom watermark to use for photos"
+msgstr ""
+
+#: templates/view/list.inc:17 lib/Block/recently_added.php:99
+msgid "Date"
+msgstr ""
+
+#: lib/Exif.php:336
+msgid "Date Photo Digitized"
+msgstr ""
+
+#: lib/Exif.php:334
+msgid "Date Photo Modified"
+msgstr ""
+
+#: lib/Exif.php:335
+msgid "Date Photo Taken"
+msgstr ""
+
+#: config/prefs.php.dist:129 config/styles.php.dist:56
+msgid "Default"
+msgstr ""
+
+#: config/prefs.php.dist:128
+msgid "Default style for galleries"
+msgstr ""
+
+#: templates/view/gallery.inc:110 templates/view/gallerylightbox.inc:129
+#: templates/view/image.inc:51 templates/view/results.inc:98
+#: templates/view/slideshow.inc:21 lib/Widget/Actions.php:115
+msgid "Delete"
+msgstr ""
+
+#: templates/faces/image.inc:7
+msgid "Delete face"
+msgstr ""
+
+#: image.php:542 lib/Views/Results.php:134
+msgid "Deleted the photo."
+msgstr ""
+
+#: templates/view/list.inc:22
+msgid "Descending"
+msgstr ""
+
+#: templates/image/crop_image.inc:146
+msgid "Desired dimensions:"
+msgstr ""
+
+#: config/prefs.php.dist:13
+msgid "Display Options"
+msgstr ""
+
+#: lib/Faces.php:1144 lib/Widget/ImageFaces.php:167
+msgid "Do you know someone in this photo?"
+msgstr ""
+
+#: report.php:20
+msgid "Do you really want to report this gallery?"
+msgstr ""
+
+#: report.php:31
+msgid "Do you really want to report this photo?"
+msgstr ""
+
+#: templates/view/image.inc:51 templates/view/slideshow.inc:21
+#, php-format
+msgid "Do you want to permanently delete \"%s\"?"
+msgstr ""
+
+#: gallery/sort.php:74
+msgid "Done"
+msgstr ""
+
+#: templates/view/image.inc:57 templates/view/slideshow.inc:29
+msgid "Download Original Photo"
+msgstr ""
+
+#: lib/Widget/Actions.php:82
+msgid "Download as zip file"
+msgstr ""
+
+#: gallery/sort.php:73
+msgid "Drag photos to the desired sort position."
+msgstr ""
+
+#: xppublish.php:206
+#, php-format
+msgid "ERROR: %s"
+msgstr ""
+
+#: lib/Exif.php:120
+msgid "Easy shooting"
+msgstr ""
+
+#: img/ecard.php:59
+msgid "Ecard - "
+msgstr ""
+
+#: templates/view/image.inc:48 templates/view/slideshow.inc:18
+#: templates/tile/image.inc:18
+msgid "Edit"
+msgstr ""
+
+#: image.php:280
+#, php-format
+msgid "Edit %s :: %s"
+msgstr ""
+
+#: perms.php:245
+msgid "Edit Permissions"
+msgstr ""
+
+#: perms.php:249
+#, php-format
+msgid "Edit Permissions for %s"
+msgstr ""
+
+#: image.php:467
+msgid "Edit Photo"
+msgstr ""
+
+#: image.php:180
+msgid "Edit a photo"
+msgstr ""
+
+#: templates/faces/image.inc:9
+msgid "Edit face region"
+msgstr ""
+
+#: lib/Widget/ImageFaces.php:72
+msgid "Edit faces"
+msgstr ""
+
+#: image.php:134
+#, php-format
+msgid "Edit properties :: %s"
+msgstr ""
+
+#: lib/Widget/Links.php:41
+msgid "Embed: "
+msgstr ""
+
+#: lib/Widget/Actions.php:114
+msgid "Empty"
+msgstr ""
+
+#: gallery/captions.php:26
+#, php-format
+msgid "Error accessing %s: %s"
+msgstr ""
+
+#: rss.php:35
+msgid "Error retrieving feed"
+msgstr ""
+
+#: templates/xppublish/list.inc:12
+msgid "Existing galleries:"
+msgstr ""
+
+#: lib/Exif.php:342
+msgid "Exposure"
+msgstr ""
+
+#: lib/Exif.php:348
+msgid "Exposure Bias"
+msgstr ""
+
+#: lib/Exif.php:349
+msgid "Exposure Mode"
+msgstr ""
+
+#: lib/Exif.php:346
+msgid "F-Number"
+msgstr ""
+
+#: faces/claim.php:58 faces/face.php:34 faces/report.php:60
+msgid "Face"
+msgstr ""
+
+#: lib/Faces.php:994 lib/Faces.php:1022
+msgid "Face does not exist"
+msgstr ""
+
+#: lib/Faces.php:46
+msgid "Face driver does not exist."
+msgstr ""
+
+#: faces/search/name.php:20
+msgid "Face name to search"
+msgstr ""
+
+#: faces/claim.php:63 faces/report.php:65
+msgid "Face name was not reported."
+msgstr ""
+
+#: faces/savecustom.php:54
+msgid "Face successfuly created"
+msgstr ""
+
+#: faces/savecustom.php:56
+msgid "Face successfuly updated"
+msgstr ""
+
+#: faces/search/image.php:21
+msgid "Face to search for"
+msgstr ""
+
+#: templates/faces/gallery.inc:7 lib/Ansel.php:564
+msgid "Faces"
+msgstr ""
+
+#: lib/Forms/Upload.php:67
+#, php-format
+msgid "File %s"
+msgstr ""
+
+#: lib/Exif.php:333
+msgid "File Size"
+msgstr ""
+
+#: lib/api.php:279 lib/api.php:345
+msgid "File not found."
+msgstr ""
+
+#: lib/Forms/Upload.php:47 lib/Forms/Upload.php:76
+msgid "File to upload"
+msgstr ""
+
+#: templates/faces/gallery.inc:5
+msgid "Filename"
+msgstr ""
+
+#: lib/Widget/Actions.php:94 lib/Widget/ImageFaces.php:72
+msgid "Find faces"
+msgstr ""
+
+#: templates/tile/face.inc:24
+msgid "Find other photos this face appears in"
+msgstr ""
+
+#: templates/tile/face.inc:34 templates/faces/face.inc:14 lib/Faces.php:1150
+#: lib/Widget/ImageFaces.php:173
+msgid "Find similar"
+msgstr ""
+
+#: lib/Exif.php:247 lib/Exif.php:262
+msgid "Flash"
+msgstr ""
+
+#: lib/Exif.php:351
+msgid "Flash Setting"
+msgstr ""
+
+#: lib/Exif.php:270
+msgid "Flash, Auto-Mode"
+msgstr ""
+
+#: lib/Exif.php:272
+msgid "Flash, Auto-Mode, Return light detected"
+msgstr ""
+
+#: lib/Exif.php:271
+msgid "Flash, Auto-Mode, Return light not detected"
+msgstr ""
+
+#: lib/Exif.php:264
+msgid "Flash, strobe return light detected"
+msgstr ""
+
+#: lib/Exif.php:263
+msgid "Flash, strobe return light not detected"
+msgstr ""
+
+#: templates/image/crop_image.inc:113 templates/image/edit_image.inc:29
+#: templates/image/resize_image.inc:58
+msgid "Flip"
+msgstr ""
+
+#: lib/Exif.php:246
+msgid "Fluorescent"
+msgstr ""
+
+#: lib/Exif.php:343
+msgid "Focal Length"
+msgstr ""
+
+#: lib/Exif.php:344
+msgid "Focal Length (35mm equiv)"
+msgstr ""
+
+#: faces/search/owner.php:23
+#, php-format
+msgid "From galleries of %s"
+msgstr ""
+
+#: faces/search/owner.php:21 faces/search/tabs.php:33
+msgid "From my galleries"
+msgstr ""
+
+#: templates/tile/image.inc:8
+msgid "From: "
+msgstr ""
+
+#: lib/Ansel.php:645 config/prefs.php.dist:50
+msgid "Galleries"
+msgstr ""
+
+#: templates/group/header.inc:6
+#, php-format
+msgid "Galleries grouped by %s"
+msgstr ""
+
+#: lib/Views/List.php:174 lib/Views/List.php:178
+#, php-format
+msgid "Galleries in category \"%s\""
+msgstr ""
+
+#: templates/faces/face.inc:33 lib/Block/gallery.php:3
+#: lib/Block/gallery.php:30 lib/Block/gallery.php:50
+#: lib/Block/recent_comments.php:31 lib/Block/recent_comments.php:54
+#: lib/Block/recently_added.php:28 lib/Block/recently_added.php:56
+#: lib/Block/recently_added.php:99
+msgid "Gallery"
+msgstr ""
+
+#: image.php:64 lib/Ansel.php:2864 img/upload.php:22
+#, php-format
+msgid "Gallery %s not found."
+msgstr ""
+
+#: templates/gallery/gallery.inc:77
+msgid "Gallery Ages"
+msgstr ""
+
+#: templates/gallery/gallery.inc:26
+msgid "Gallery Category"
+msgstr ""
+
+#: report.php:71 templates/gallery/gallery.inc:55 faces/claim.php:55
+#: faces/report.php:57
+msgid "Gallery Description"
+msgstr ""
+
+#: templates/gallery/gallery.inc:48
+msgid "Gallery Display Name"
+msgstr ""
+
+#: report.php:72
+msgid "Gallery Id"
+msgstr ""
+
+#: lib/Views/List.php:70 lib/Views/List.php:181
+msgid "Gallery List"
+msgstr ""
+
+#: report.php:70 lib/Block/my_galleries.php:53 faces/claim.php:54
+#: faces/report.php:56
+msgid "Gallery Name"
+msgstr ""
+
+#: templates/gallery/gallery.inc:17
+msgid "Gallery Parent"
+msgstr ""
+
+#: lib/Tile/Gallery.php:92
+msgid "Gallery Properties"
+msgstr ""
+
+#: templates/gallery/gallery.inc:61
+msgid "Gallery Slug"
+msgstr ""
+
+#: templates/gallery/gallery.inc:69
+msgid "Gallery Tags"
+msgstr ""
+
+#: lib/Block/gallery.php:140 lib/Block/recent_comments.php:151
+#: lib/Block/recently_added.php:187
+msgid "Gallery does not exist."
+msgstr ""
+
+#: gallery/sort.php:47
+msgid "Gallery sorted."
+msgstr ""
+
+#: report.php:79 report.php:85
+msgid "Gallery was not reported."
+msgstr ""
+
+#: report.php:82
+msgid "Gallery was reported."
+msgstr ""
+
+#: config/prefs.php.dist:12 config/prefs.php.dist:21 config/prefs.php.dist:28
+msgid "General Options"
+msgstr ""
+
+#: lib/Forms/Watermark.php:40 config/prefs.php.dist:191
+msgid "Giant"
+msgstr ""
+
+#: templates/image/crop_image.inc:119 templates/image/edit_image.inc:35
+#: templates/image/resize_image.inc:64
+msgid "Grayscale"
+msgstr ""
+
+#: templates/view/list.inc:10 templates/group/header.inc:11
+msgid "Group by: "
+msgstr ""
+
+#: config/prefs.php.dist:64
+msgid "Group galleries by"
+msgstr ""
+
+#: lib/Exif.php:338
+msgid "Height"
+msgstr ""
+
+#: lib/Forms/Watermark.php:48 config/prefs.php.dist:168
+msgid "Horizontal Alignment"
+msgstr ""
+
+#: faces/claim.php:53
+msgid "I know who is on one of your photos"
+msgstr ""
+
+#: lib/Exif.php:347
+msgid "ISO Setting"
+msgstr ""
+
+#: image.php:129
+msgid "Image not found."
+msgstr ""
+
+#: lib/Ansel.php:2120
+msgid "Incomplete photo"
+msgstr ""
+
+#: scripts/ansel.php:127
+#, php-format
+msgid "Invalid gallery \"%s\" specified."
+msgstr ""
+
+#: xppublish.php:87 xppublish.php:174 lib/api.php:267
+msgid "Invalid gallery specified."
+msgstr ""
+
+#: templates/image/crop_image.inc:161
+msgid "Landscape"
+msgstr ""
+
+#: lib/Forms/Watermark.php:39 config/prefs.php.dist:190
+msgid "Large"
+msgstr ""
+
+#: lib/Block/my_galleries.php:53
+msgid "Last Modified"
+msgstr ""
+
+#: img/upload.php:237
+msgid "Learn how to publish photos directly from Windows."
+msgstr ""
+
+#: lib/Forms/Watermark.php:45 config/prefs.php.dist:165
+msgid "Left"
+msgstr ""
+
+#: config/styles.php.dist:129 config/styles.php.dist:166
+msgid "Lightbox with Polaroids (No Background)"
+msgstr ""
+
+#: lib/Widget/Links.php:13
+msgid "Links"
+msgstr ""
+
+#: scripts/ansel.php:192
+msgid "Listing Gallery/Name"
+msgstr ""
+
+#: scripts/ansel.php:176
+#, php-format
+msgid "Listing photos in %s"
+msgstr ""
+
+#: templates/faces/gallery.inc:28 templates/faces/gallery.inc:40
+#: lib/XRequest/EditFaces.php:35
+msgid "Loading..."
+msgstr ""
+
+#: scripts/ansel.php:118 scripts/recursive_import.php:81
+#, php-format
+msgid "Logged in successfully as \"%s\"."
+msgstr ""
+
+#: scripts/ansel.php:114
+msgid "Login is incorrect."
+msgstr ""
+
+#: templates/xppublish/login.inc:2
+#, php-format
+msgid "Login to %s"
+msgstr ""
+
+#: lib/Forms/Image.php:37 lib/Forms/Upload.php:51
+msgid "Make this the default photo for this gallery?"
+msgstr ""
+
+#: scripts/ansel.php:320 scripts/recursive_import.php:187
+msgid ""
+"Mandatory arguments to long options are mandatory for short options too."
+msgstr ""
+
+#: templates/image/crop_image.inc:150 lib/Exif.php:128 lib/Exif.php:249
+msgid "Manual"
+msgstr ""
+
+#: lib/Widget/ImageFaces.php:76
+msgid "Manual face selection"
+msgstr ""
+
+#: lib/Forms/Upload.php:79
+msgid "Maximum file size:"
+msgstr ""
+
+#: lib/Block/recent_faces.php:27
+msgid "Maximum number of faces"
+msgstr ""
+
+#: lib/Block/my_galleries.php:25
+msgid "Maximum number of galleries"
+msgstr ""
+
+#: lib/Block/recently_added.php:33
+msgid "Maximum number of photos"
+msgstr ""
+
+#: lib/Block/gallery.php:35
+msgid "Maximum number of photos to display (0 means unlimited)"
+msgstr ""
+
+#: lib/Forms/Image.php:46 lib/Forms/Upload.php:48
+msgid "Maximum photo size:"
+msgstr ""
+
+#: lib/Forms/Upload.php:58
+#, php-format
+msgid "Maximum photo size: %s; with a total of: %s"
+msgstr ""
+
+#: lib/Forms/Watermark.php:38 config/prefs.php.dist:189
+msgid "Medium"
+msgstr ""
+
+#: lib/Exif.php:350
+msgid "Metering Mode"
+msgstr ""
+
+#: templates/image/crop_image.inc:116 templates/image/edit_image.inc:32
+#: templates/image/resize_image.inc:61
+msgid "Mirror"
+msgstr ""
+
+#: lib/Exif.php:151
+#, php-format
+msgid "Mirrored"
+msgstr ""
+
+#: lib/Views/Embedded.php:91
+msgid "Missing container parameter."
+msgstr ""
+
+#: lib/Tile/Gallery.php:116
+msgid "Modified"
+msgstr ""
+
+#: gallery.php:140
+#, php-format
+msgid "Modifying: %s"
+msgstr ""
+
+#: faces/search/image_define.php:34
+msgid ""
+"More then one face found in photo. Please note that you can search only one "
+"faceat a time."
+msgstr ""
+
+#: templates/view/gallery.inc:113 templates/view/gallerylightbox.inc:132
+#: templates/view/results.inc:100
+msgid "Move"
+msgstr ""
+
+#: image.php:587 lib/Views/Results.php:176
+#, php-format
+msgid "Moved %d photo from \"%s\" to \"%s\""
+msgid_plural "Moved %d photos from \"%s\" to \"%s\""
+msgstr[0] ""
+msgstr[1] ""
+
+#: lib/Exif.php:234
+msgid "Multi-Segment"
+msgstr ""
+
+#: lib/Exif.php:233
+msgid "Multi-Spot"
+msgstr ""
+
+#: lib/Forms/Upload.php:55
+msgid "Multiple Photos"
+msgstr ""
+
+#: templates/group/owner.inc:3 lib/Ansel.php:624 lib/Views/List.php:155
+#: lib/Block/my_galleries.php:3 lib/Block/my_galleries.php:37
+#: config/prefs.php.dist:51
+msgid "My Galleries"
+msgstr ""
+
+#: browse_edit.php:28
+msgid "My Photos :: Add Content"
+msgstr ""
+
+#: templates/view/list.inc:16
+msgid "Name"
+msgstr ""
+
+#: faces/search/named.php:17 faces/search/tabs.php:34
+msgid "Named faces"
+msgstr ""
+
+#: templates/xppublish/new.inc:21
+msgid "New Gallery Description"
+msgstr ""
+
+#: templates/xppublish/new.inc:12
+msgid "New Gallery Title"
+msgstr ""
+
+#: templates/view/image.inc:84 templates/view/image.inc:85
+#: templates/view/image.inc:109 templates/view/image.inc:110
+#: templates/view/slideshow.inc:42 templates/view/slideshow.inc:43
+msgid "Next"
+msgstr ""
+
+#: config/prefs.php.dist:200
+msgid "No"
+msgstr ""
+
+#: lib/Exif.php:261 lib/Exif.php:268 lib/Exif.php:269 lib/Exif.php:273
+msgid "No Flash"
+msgstr ""
+
+#: templates/faces/faces.inc:10 templates/faces/index.inc:14
+#: templates/faces/search.inc:18 lib/XRequest/EditFaces.php:120
+#: faces/image.php:52 faces/search/image.php:76
+msgid "No faces found"
+msgstr ""
+
+#: faces/search/image_define.php:36
+msgid "No faces found. Define you own."
+msgstr ""
+
+#: xppublish.php:184
+msgid "No file specified"
+msgstr ""
+
+#: lib/Views/Abstract.php:44 faces/gallery.php:24
+msgid "No gallery specified"
+msgstr ""
+
+#: templates/view/gallery.inc:106 templates/view/gallerylightbox.inc:125
+#: templates/view/results.inc:94 config/prefs.php.dist:63
+#: config/prefs.php.dist:221 config/prefs.php.dist:242
+msgid "None"
+msgstr ""
+
+#: config/prefs.php.dist:232
+msgid "None (Owner only)"
+msgstr ""
+
+#: lib/Exif.php:149
+#, php-format
+msgid "Normal (O deg)"
+msgstr ""
+
+#: lib/Exif.php:291
+msgid "Not defined"
+msgstr ""
+
+#: config/prefs.php.dist:89
+msgid "Number of faces per page"
+msgstr ""
+
+#: config/prefs.php.dist:97
+msgid "Number of groups per page"
+msgstr ""
+
+#: config/prefs.php.dist:81
+msgid "Number of tiles per page"
+msgstr ""
+
+#: config/prefs.php.dist:73
+msgid "Number of tiles per row"
+msgstr ""
+
+#: report.php:51
+msgid "Offensive content"
+msgstr ""
+
+#: lib/Exif.php:292
+msgid "One Chip Color Area Sensor"
+msgstr ""
+
+#: perms.php:65
+msgid ""
+"Only the owner or system administrator may change ownership or owner "
+"permissions for a share"
+msgstr ""
+
+#: templates/image/crop_image.inc:157
+msgid "Orientation:"
+msgstr ""
+
+#: lib/Exif.php:236
+msgid "Other"
+msgstr ""
+
+#: templates/tile/face.inc:24
+msgid "Others"
+msgstr ""
+
+#: templates/group/owner.inc:51
+#, php-format
+msgid "Owned by: %s"
+msgstr ""
+
+#: templates/view/list.inc:18 templates/tile/gallery.inc:11
+#: config/prefs.php.dist:61
+msgid "Owner"
+msgstr ""
+
+#: lib/Exif.php:235
+msgid "Partial"
+msgstr ""
+
+#: templates/xppublish/login.inc:14
+msgid "Password"
+msgstr ""
+
+#: templates/view/slideshow.inc:39
+msgid "Pause Slideshow"
+msgstr ""
+
+#: lib/Widget/OwnerFaces.php:34
+#, php-format
+msgid "People in galleries of %s (%d of %d)"
+msgstr ""
+
+#: lib/Widget/ImageFaces.php:33
+msgid "People in this photo"
+msgstr ""
+
+#: config/prefs.php.dist:22
+msgid "Permission Options"
+msgstr ""
+
+#: faces/claim.php:37 faces/claim.php:57
+msgid "Person"
+msgstr ""
+
+#: templates/faces/custom.inc:51
+msgid "Person in photo"
+msgstr ""
+
+#: templates/faces/face.inc:30 templates/faces/gallery.inc:6
+#: lib/Views/GalleryRenderers/GalleryLightbox.php:177
+#: lib/Block/recently_added.php:99
+msgid "Photo"
+msgstr ""
+
+#: lib/Block/my_galleries.php:53
+msgid "Photo Count"
+msgstr ""
+
+#: lib/Exif.php:332
+msgid "Photo Description"
+msgstr ""
+
+#: browse.php:22
+msgid "Photo Galleries"
+msgstr ""
+
+#: lib/Exif.php:331
+msgid "Photo Type"
+msgstr ""
+
+#: templates/xppublish/javascript.inc:63
+msgid "Photo Upload"
+msgstr ""
+
+#: disclamer.php:30
+#, php-format
+msgid "Photo content may be offensive. You must be over %d to continue."
+msgstr ""
+
+#: faces/search/image.php:42 faces/search/image_save.php:32
+msgid "Photo is too small. Search photo must be at least 50x50 pixels."
+msgstr ""
+
+#: lib/Ansel.php:3006
+msgid "Photo not found"
+msgstr ""
+
+#: faces/search/image_search.php:43
+msgid "Photo search"
+msgstr ""
+
+#: rss.php:148
+#, php-format
+msgid "Photos by %s"
+msgstr ""
+
+#: lib/Ansel.php:3043
+msgid "Photos not found"
+msgstr ""
+
+#: rss.php:178 rss.php:184
+#, php-format
+msgid "Photos tagged with %s on %s"
+msgstr ""
+
+#: templates/view/image.inc:80 templates/view/image.inc:105
+#: templates/view/slideshow.inc:38
+msgid "Play"
+msgstr ""
+
+#: faces/report.php:36
+msgid ""
+"Please describe the reasons. For example, you don't want to be mentioned "
+"etc..."
+msgstr ""
+
+#: xppublish.php:54
+msgid "Please enter your username and password."
+msgstr ""
+
+#: faces/search/image.php:20
+msgid ""
+"Please upload photo with the face to search for. You can search only one "
+"face per time."
+msgstr ""
+
+#: config/prefs.php.dist:133
+msgid "Polaroid"
+msgstr ""
+
+#: config/styles.php.dist:94
+msgid "Polaroid Style Thumbnails (White Background)"
+msgstr ""
+
+#: templates/image/crop_image.inc:162
+msgid "Portrait"
+msgstr ""
+
+#: xppublish.php:99 xppublish.php:149
+msgid "Press the \"Back\" button and try again."
+msgstr ""
+
+#: config/prefs.php.dist:130
+msgid "Pretty Thumbnails"
+msgstr ""
+
+#: config/styles.php.dist:64
+msgid "Pretty Thumbnails (No Background)"
+msgstr ""
+
+#: config/styles.php.dist:75
+msgid "Pretty Thumbnails (White Background)"
+msgstr ""
+
+#: config/prefs.php.dist:131
+msgid "Pretty Thumbnails on White Background"
+msgstr ""
+
+#: templates/image/crop_image.inc:177
+msgid "Preview"
+msgstr ""
+
+#: image.php:472 image.php:671
+#, php-format
+msgid "Preview changes for %s :: %s"
+msgstr ""
+
+#: templates/view/image.inc:82 templates/view/image.inc:83
+#: templates/view/image.inc:107 templates/view/image.inc:108
+#: templates/view/slideshow.inc:40 templates/view/slideshow.inc:41
+msgid "Previous"
+msgstr ""
+
+#: lib/Exif.php:122
+msgid "Program"
+msgstr ""
+
+#: templates/view/image.inc:47 templates/view/slideshow.inc:17
+#: templates/tile/image.inc:17
+msgid "Properties"
+msgstr ""
+
+#: templates/xppublish/list.inc:7
+msgid "Publish Photos to:"
+msgstr ""
+
+#: xppublish.php:35
+#, php-format
+msgid "Publish to %s"
+msgstr ""
+
+#: xppublish.php:24
+#, php-format
+msgid "Publish your photos to %s on %s."
+msgstr ""
+
+#: lib/Block/gallery.php:56
+msgid "Random Gallery"
+msgstr ""
+
+#: lib/Block/gallery.php:33
+msgid "Random gallery"
+msgstr ""
+
+#: lib/Block/random_photo.php:3 lib/Block/random_photo.php:26
+msgid "Random photo"
+msgstr ""
+
+#: config/prefs.php.dist:223 config/prefs.php.dist:244
+msgid "Read and write"
+msgstr ""
+
+#: config/prefs.php.dist:245
+msgid "Read, write, and delete"
+msgstr ""
+
+#: config/prefs.php.dist:222 config/prefs.php.dist:233
+#: config/prefs.php.dist:243
+msgid "Read-only"
+msgstr ""
+
+#: templates/gallery/delete_confirmation.inc:10
+msgid "Really Delete"
+msgstr ""
+
+#: templates/gallery/delete_confirmation.inc:10
+msgid "Really Empty"
+msgstr ""
+
+#: templates/gallery/delete_confirmation.inc:2
+#, php-format
+msgid "Really delete %s?"
+msgstr ""
+
+#: templates/gallery/delete_confirmation.inc:5
+msgid ""
+"Really delete this gallery and all of the photos in it? This action cannot "
+"be undone."
+msgstr ""
+
+#: templates/gallery/delete_confirmation.inc:2
+#, php-format
+msgid "Really empty %s?"
+msgstr ""
+
+#: templates/gallery/delete_confirmation.inc:5
+msgid ""
+"Really empty this gallery and all of the photos in it? This action cannot be "
+"undone."
+msgstr ""
+
+#: faces/report.php:36 faces/report.php:59
+msgid "Reason"
+msgstr ""
+
+#: lib/Block/recent_comments.php:71
+#, php-format
+msgid "Recent Comments In %s"
+msgstr ""
+
+#: lib/Block/recent_comments.php:5
+msgid "Recent Photo Comments"
+msgstr ""
+
+#: lib/Block/recent_faces.php:3 lib/Block/recent_faces.php:34
+msgid "Recent faces"
+msgstr ""
+
+#: lib/Widget/Links.php:23
+#, php-format
+msgid "Recent photos by %s"
+msgstr ""
+
+#: lib/Widget/Links.php:25
+#, php-format
+msgid "Recent photos in %s"
+msgstr ""
+
+#: lib/Block/recently_added.php:3
+msgid "Recently Added Photos"
+msgstr ""
+
+#: lib/Block/recently_added.php:75
+#, php-format
+msgid "Recently Added Photos From %s"
+msgstr ""
+
+#: rss.php:153
+#, php-format
+msgid "Recently added photos by %s on %s"
+msgstr ""
+
+#: rss.php:57 rss.php:62
+#, php-format
+msgid "Recently added photos on %s"
+msgstr ""
+
+#: lib/Exif.php:274
+msgid "Red Eye"
+msgstr ""
+
+#: lib/Exif.php:280
+msgid "Red Eye, Auto-Mode"
+msgstr ""
+
+#: lib/Exif.php:282
+msgid "Red Eye, Auto-Mode, Return light detected"
+msgstr ""
+
+#: lib/Exif.php:281
+msgid "Red Eye, Auto-Mode, Return light not detected"
+msgstr ""
+
+#: lib/Exif.php:277
+msgid "Red Eye, Compulsory Flash"
+msgstr ""
+
+#: lib/Exif.php:279
+msgid "Red Eye, Compulsory Flash, Return light detected"
+msgstr ""
+
+#: lib/Exif.php:278
+msgid "Red Eye, Compulsory Flash, Return light not detected"
+msgstr ""
+
+#: lib/Exif.php:276
+msgid "Red Eye, Return light detected"
+msgstr ""
+
+#: lib/Exif.php:275
+msgid "Red Eye, Return light not detected"
+msgstr ""
+
+#: templates/view/list.inc:5 templates/list/header.inc:5
+#: templates/group/header.inc:8
+msgid "Refresh List"
+msgstr ""
+
+#: templates/faces/gallery.inc:19
+msgid "Reget faces for this image"
+msgstr ""
+
+#: templates/view/results.inc:144
+msgid "Related Tags"
+msgstr ""
+
+#: lib/Tags.php:599
+#, php-format
+msgid "Remove %s from search"
+msgstr ""
+
+#: lib/XRequest/TagActions.php:115 lib/Widget/Tags.php:93
+msgid "Remove Tag"
+msgstr ""
+
+#: lib/Forms/Image.php:45
+msgid "Replace photo with this file"
+msgstr ""
+
+#: report.php:47 report.php:66 templates/view/image.inc:70
+#: templates/tile/face.inc:30 templates/faces/face.inc:9
+#: templates/faces/search.inc:10 lib/Widget/Actions.php:110
+msgid "Report"
+msgstr ""
+
+#: templates/tile/face.inc:30 templates/faces/face.inc:9
+#: templates/faces/search.inc:10
+msgid "Report abuse or submit a removal request"
+msgstr ""
+
+#: lib/Report.php:109
+msgid "Report by user"
+msgstr ""
+
+#: lib/Report.php:40
+msgid "Report driver does not exist."
+msgstr ""
+
+#: faces/report.php:31
+msgid "Report face"
+msgstr ""
+
+#: report.php:61 report.php:74
+msgid "Report reason"
+msgstr ""
+
+#: report.php:60 report.php:73
+msgid "Report type"
+msgstr ""
+
+#: templates/image/crop_image.inc:178 templates/image/resize_image.inc:87
+#: templates/faces/custom.inc:54 templates/faces/define.inc:39
+msgid "Reset"
+msgstr ""
+
+#: lib/Widget/Actions.php:92
+msgid "Reset all thumbnails"
+msgstr ""
+
+#: lib/Widget/Actions.php:98
+msgid "Reset default photo"
+msgstr ""
+
+#: templates/image/crop_image.inc:127 templates/image/edit_image.inc:44
+#: templates/image/resize_image.inc:74
+msgid "Resize"
+msgstr ""
+
+#: lib/Forms/Watermark.php:47 config/prefs.php.dist:167
+msgid "Right"
+msgstr ""
+
+#: templates/image/crop_image.inc:109 templates/image/edit_image.inc:25
+#: templates/image/resize_image.inc:54
+msgid "Rotate 180"
+msgstr ""
+
+#: templates/image/crop_image.inc:108 templates/image/edit_image.inc:24
+#: templates/image/resize_image.inc:53
+msgid "Rotate Left"
+msgstr ""
+
+#: templates/image/crop_image.inc:110 templates/image/edit_image.inc:26
+#: templates/image/resize_image.inc:55
+msgid "Rotate Right"
+msgstr ""
+
+#: templates/image/resize_image.inc:86 templates/faces/custom.inc:52
+#: lib/Forms/Image.php:26 lib/Forms/Watermark.php:26
+msgid "Save"
+msgstr ""
+
+#: templates/captions/captions.inc:47
+msgid "Save Captions"
+msgstr ""
+
+#: templates/image/preview_cropimage.inc:16
+#: templates/image/preview_cropimage.inc:17
+#: templates/image/preview_image.inc:19 templates/image/preview_image.inc:20
+msgid "Save Change"
+msgstr ""
+
+#: templates/gallery/gallery.inc:112
+msgid "Save Gallery"
+msgstr ""
+
+#: image.php:162
+msgid "Save Photo"
+msgstr ""
+
+#: templates/faces/image.inc:5
+msgid "Save face name"
+msgstr ""
+
+#: templates/faces/define.inc:37 faces/search/name.php:21
+msgid "Search"
+msgstr ""
+
+#: faces/search/name.php:47 faces/search/tabs.php:35
+msgid "Search by name"
+msgstr ""
+
+#: faces/search/tabs.php:37
+msgid "Search by photo"
+msgstr ""
+
+#: lib/Views/Results.php:86
+#, php-format
+msgid "Searching %s's photos tagged: "
+msgstr ""
+
+#: lib/Views/Results.php:87
+msgid "Searching all photos tagged: "
+msgstr ""
+
+#: faces/gallery.php:55
+#, php-format
+msgid "Searching for faces in %s"
+msgstr ""
+
+#: templates/view/gallery.inc:105 templates/view/gallerylightbox.inc:124
+#: templates/view/results.inc:93
+msgid "Select All"
+msgstr ""
+
+#: templates/view/gallery.inc:106 templates/view/gallerylightbox.inc:125
+#: templates/view/results.inc:94
+msgid "Select None"
+msgstr ""
+
+#: templates/tile/image.inc:5
+msgid "Select for Action"
+msgstr ""
+
+#: templates/view/gallery.inc:104 templates/view/gallerylightbox.inc:123
+#: templates/view/results.inc:92
+#, php-format
+msgid "Select: %s, %s"
+msgstr ""
+
+#: templates/view/gallery.inc:118 templates/view/gallerylightbox.inc:137
+#: templates/view/results.inc:105
+msgid "Selected photos to"
+msgstr ""
+
+#: lib/Forms/Ecard.php:25
+msgid "Send"
+msgstr ""
+
+#: img/ecard.php:102
+#, php-format
+msgid "Send Ecard :: %s"
+msgstr ""
+
+#: templates/view/image.inc:54 templates/view/slideshow.inc:27
+msgid "Send an Ecard"
+msgstr ""
+
+#: lib/Forms/Ecard.php:41
+msgid "Send ecard to the following address:"
+msgstr ""
+
+#: lib/Exif.php:354
+msgid "Sensing Method"
+msgstr ""
+
+#: templates/gallery/gallery.inc:71 lib/Forms/Upload.php:53
+msgid "Separate tags with commas."
+msgstr ""
+
+#: lib/Widget/Actions.php:89
+msgid "Set captions"
+msgstr ""
+
+#: lib/Widget/Actions.php:103
+msgid "Set permissions"
+msgstr ""
+
+#: config/styles.php.dist:84
+msgid "Shadowed Thumbnails (White Background)"
+msgstr ""
+
+#: config/prefs.php.dist:132
+msgid "Shadowed Thumbnails on White Background"
+msgstr ""
+
+#: config/prefs.php.dist:105
+msgid "Show EXIF data"
+msgstr ""
+
+#: lib/Exif.php:341
+msgid "Shutter Speed"
+msgstr ""
+
+#: lib/Widget/SimilarPhotos.php:27
+msgid "Similar Photos"
+msgstr ""
+
+#: templates/tile/face.inc:34 templates/faces/face.inc:14
+msgid "Similar faces"
+msgstr ""
+
+#: config/styles.php.dist:145
+msgid "Simple"
+msgstr ""
+
+#: config/styles.php.dist:154
+msgid "Simple Lightbox"
+msgstr ""
+
+#: lib/Forms/Upload.php:43
+msgid "Single Photo"
+msgstr ""
+
+#: templates/gallery/gallery.inc:64
+#, php-format
+msgid ""
+"Slugs allows direct access to this gallery by visiting: %s. <br /> Slug "
+"names may contain only letters, numbers or the _ (underscore) character."
+msgstr ""
+
+#: lib/Forms/Watermark.php:37 config/prefs.php.dist:188
+msgid "Small"
+msgstr ""
+
+#: templates/view/list.inc:16
+msgid "Sort by: "
+msgstr ""
+
+#: lib/Widget/Actions.php:91
+msgid "Sort images"
+msgstr ""
+
+#: lib/Exif.php:232
+msgid "Spot"
+msgstr ""
+
+#: templates/view/image.inc:79 templates/view/image.inc:105
+#: templates/view/slideshow.inc:38
+msgid "Start Slideshow"
+msgstr ""
+
+#: scripts/ansel.php:212 scripts/ansel.php:295
+#: scripts/recursive_import.php:156
+#, php-format
+msgid "Storing photo \"%s\"..."
+msgstr ""
+
+#: templates/gallery/gallery.inc:40
+msgid "Style for this gallery"
+msgstr ""
+
+#: scripts/ansel.php:235 scripts/recursive_import.php:166
+#, php-format
+msgid "Successfully added %d photo (%s) to gallery \"%s\" from \"%s\"."
+msgid_plural "Successfully added %d photos (%s) to gallery \"%s\" from \"%s\"."
+msgstr[0] ""
+msgstr[1] ""
+
+#: scripts/ansel.php:221
+#, php-format
+msgid "Successfully added photo \"%s\" to gallery \"%s\"."
+msgstr ""
+
+#: gallery/delete.php:49
+#, php-format
+msgid "Successfully deleted %s."
+msgstr ""
+
+#: lib/Exif.php:243
+msgid "Sunny"
+msgstr ""
+
+#: lib/Block/cloud.php:3 lib/Block/cloud.php:28
+msgid "Tag Cloud"
+msgstr ""
+
+#: lib/Widget/Tags.php:21 lib/Forms/Image.php:41 lib/Forms/Upload.php:53
+msgid "Tags"
+msgstr ""
+
+#: templates/tile/face.inc:27 faces/claim.php:32
+msgid "Tell us who is in this photo"
+msgstr ""
+
+#: report.php:50
+msgid "Terms and conditions infringement"
+msgstr ""
+
+#: scripts/ansel.php:262
+#, php-format
+msgid "The directory \"%s\" doesn't exist."
+msgstr ""
+
+#: scripts/ansel.php:240
+#, php-format
+msgid "The directory \"%s\" had no valid photos."
+msgstr ""
+
+#: scripts/ansel.php:279
+#, php-format
+msgid "The directory \"%s\" is empty."
+msgstr ""
+
+#: lib/Ansel.php:454
+#, php-format
+msgid "The file \"%s\" doesn't exist."
+msgstr ""
+
+#: image.php:198 lib/api.php:463 img/upload.php:170
+msgid "The file you uploaded does not appear to be a valid photo."
+msgstr ""
+
+#: scripts/recursive_import.php:125
+#, php-format
+msgid "The gallery \"%s\" (%s) was created successfully."
+msgstr ""
+
+#: lib/Ansel.php:2741
+#, php-format
+msgid "The gallery \"%s\" could not be created: %s"
+msgstr ""
+
+#: gallery.php:288 xppublish.php:123 scripts/ansel.php:156
+#: scripts/recursive_import.php:123
+#, php-format
+msgid "The gallery \"%s\" couldn't be created: %s"
+msgstr ""
+
+#: gallery.php:294 xppublish.php:127 scripts/ansel.php:161
+#, php-format
+msgid "The gallery \"%s\" was created successfully."
+msgstr ""
+
+#: gallery.php:239
+msgid "The gallery was saved."
+msgstr ""
+
+#: gallery.php:344
+msgid "The gallery's default photo has successfully been reset."
+msgstr ""
+
+#: gallery.php:361
+msgid "The gallery's thumbnails have successfully been reset."
+msgstr ""
+
+#: faces/report.php:68
+msgid "The owner of the photo was notified."
+msgstr ""
+
+#: faces/claim.php:66
+msgid "The owner of the photo, who will delegate the face name, was notified."
+msgstr ""
+
+#: lib/Ansel.php:2704
+#, php-format
+msgid "The slug \"%s\" already exists."
+msgstr ""
+
+#: img/upload.php:198
+msgid ""
+"The uploaded file appears to be empty. It may not exist on your computer."
+msgstr ""
+
+#: lib/Views/List.php:194 lib/Block/random_photo.php:35
+msgid "There are no photo galleries available."
+msgstr ""
+
+#: gallery.php:93
+#, php-format
+msgid "There are no photos in %s to download."
+msgstr ""
+
+#: templates/view/gallery.inc:122 templates/view/gallerylightbox.inc:141
+#: templates/view/results.inc:115 templates/captions/captions.inc:49
+msgid "There are no photos in this gallery."
+msgstr ""
+
+#: scripts/ansel.php:298
+#, php-format
+msgid "There was a problem adding the photo \"%s\" to gallery \"%s\"."
+msgstr ""
+
+#: scripts/ansel.php:215
+#, php-format
+msgid "There was a problem adding the photo \"%s\" to gallery \"%s\": %s"
+msgstr ""
+
+#: lib/api.php:624 gallery/delete.php:44
+#, php-format
+msgid "There was a problem deleting %s: %s"
+msgstr ""
+
+#: image.php:539 lib/Views/Results.php:131
+#, php-format
+msgid "There was a problem deleting photos: %s"
+msgstr ""
+
+#: img/upload.php:101 img/upload.php:156 img/upload.php:180
+#, php-format
+msgid "There was a problem saving the photo: %s"
+msgstr ""
+
+#: xppublish.php:195
+msgid "There was a problem uploading the photo."
+msgstr ""
+
+#: img/upload.php:196
+#, php-format
+msgid "There was a problem uploading the photo: %s"
+msgstr ""
+
+#: xppublish.php:91
+msgid "There was an error accessing the gallery"
+msgstr ""
+
+#: gallery/sort.php:27
+msgid "There was an error accessing the gallery."
+msgstr ""
+
+#: img/upload.php:61 img/upload.php:83 img/upload.php:117 img/upload.php:138
+#, php-format
+msgid "There was an error processing the uploaded archive: %s"
+msgstr ""
+
+#: image.php:209
+msgid "There was an error replacing the photo."
+msgstr ""
+
+#: img/ecard.php:95
+#, php-format
+msgid "There was an error sending your message: %s"
+msgstr ""
+
+#: lib/ImageView.php:89
+#, php-format
+msgid ""
+"This install does not support the %s feature. Please contact your "
+"administrator."
+msgstr ""
+
+#: lib/Exif.php:294
+msgid "Three Chip Color Area Sensor"
+msgstr ""
+
+#: lib/Forms/Watermark.php:36 config/prefs.php.dist:187
+msgid "Tiny"
+msgstr ""
+
+#: lib/Forms/Watermark.php:52 config/prefs.php.dist:176
+msgid "Top"
+msgstr ""
+
+#: templates/gallery/gallery.inc:20
+msgid "Top Level Gallery"
+msgstr ""
+
+#: lib/Exif.php:296
+msgid "Trilinear Sensor"
+msgstr ""
+
+#: lib/Exif.php:245
+msgid "Tungsten"
+msgstr ""
+
+#: lib/Exif.php:124
+msgid "Tv-priority"
+msgstr ""
+
+#: lib/Exif.php:293
+msgid "Two Chip Color Area Sensor"
+msgstr ""
+
+#: lib/api.php:1124 lib/ImageView.php:96 lib/Search.php:57 lib/Widget.php:75
+#, php-format
+msgid "Unable to load the definition of %s."
+msgstr ""
+
+#: rss.php:37
+msgid "Unable to retrieve requested feed"
+msgstr ""
+
+#: lib/Exif.php:307
+msgid "Uncalibrated"
+msgstr ""
+
+#: templates/gallery/gallery.inc:113
+msgid "Undo Changes"
+msgstr ""
+
+#: templates/group/category.inc:7 lib/Views/List.php:175
+msgid "Unfiled"
+msgstr ""
+
+#: lib/Exif.php:229
+msgid "Unknown"
+msgstr ""
+
+#: gallery.php:146
+msgid "Unknown gallery"
+msgstr ""
+
+#: lib/Ansel.php:2674 lib/tests/GalleryTest.php:42
+msgid "Unnamed"
+msgstr ""
+
+#: lib/Report.php:122
+msgid "Unsupported"
+msgstr ""
+
+#: xppublish.php:135
+msgid "Untitled"
+msgstr ""
+
+#: perms.php:237
+#, php-format
+msgid "Updated %s."
+msgstr ""
+
+#: lib/Forms/Upload.php:39 faces/search/image.php:22
+msgid "Upload"
+msgstr ""
+
+#: templates/xppublish/javascript.inc:63
+#, php-format
+msgid "Upload Photos to %s"
+msgstr ""
+
+#: faces/search/image.php:66
+msgid "Upload face photo"
+msgstr ""
+
+#: lib/Widget/Actions.php:85 img/upload.php:30
+msgid "Upload photos"
+msgstr ""
+
+#: lib/Exif.php:153
+#, php-format
+msgid "Upsidedown"
+msgstr ""
+
+#: lib/Exif.php:155
+#, php-format
+msgid "Upsidedown Mirrored"
+msgstr ""
+
+#: scripts/ansel.php:318 scripts/recursive_import.php:185
+#, php-format
+msgid "Usage: %s [OPTIONS]..."
+msgstr ""
+
+#: lib/Forms/Ecard.php:32
+msgid "Use the following return address:"
+msgstr ""
+
+#: lib/Exif.php:352
+msgid "User Comment"
+msgstr ""
+
+#: templates/gallery/gallery.inc:84
+#, php-format
+msgid "User must be over %d"
+msgstr ""
+
+#: templates/xppublish/login.inc:11
+msgid "Username"
+msgstr ""
+
+#: xppublish.php:50
+msgid "Username or password are incorrect."
+msgstr ""
+
+#: scripts/recursive_import.php:79
+msgid "Username or password is incorrect."
+msgstr ""
+
+#: templates/gallery/gallery.inc:103 config/prefs.php.dist:211
+msgid "Users with edit permissions"
+msgstr ""
+
+#: lib/Forms/Watermark.php:55 config/prefs.php.dist:179
+#: config/prefs.php.dist:192
+msgid "Vertical Alignment"
+msgstr ""
+
+#: lib/Block/gallery.php:96
+#, php-format
+msgid "View %s"
+msgstr ""
+
+#: templates/view/results.inc:81
+msgid "View All Results"
+msgstr ""
+
+#: lib/Block/random_photo.php:55
+msgid "View Photo"
+msgstr ""
+
+#: templates/view/results.inc:81
+msgid "View Results from All Users"
+msgstr ""
+
+#: templates/tile/image.inc:11
+msgid "View or leave comments"
+msgstr ""
+
+#: config/prefs.php.dist:53
+msgid "View to display by default"
+msgstr ""
+
+#: image.php:425 image.php:436 templates/image/crop_image.inc:123
+#: templates/image/edit_image.inc:39 templates/image/resize_image.inc:68
+msgid "Watermark"
+msgstr ""
+
+#: lib/Forms/Watermark.php:41
+msgid "Watermark Font"
+msgstr ""
+
+#: config/prefs.php.dist:29
+msgid "Watermark Options"
+msgstr ""
+
+#: config/prefs.php.dist:241
+msgid ""
+"When a new gallery is created, what default permissions should be given to "
+"groups that the user is a member of?"
+msgstr ""
+
+#: config/prefs.php.dist:220
+msgid ""
+"When a new gallery is created, what permissions should be given to "
+"authenticated users by default?"
+msgstr ""
+
+#: config/prefs.php.dist:231
+msgid ""
+"When a new gallery is created, what permissions should be given to guests by "
+"default?"
+msgstr ""
+
+#: lib/Exif.php:355
+msgid "White Balance"
+msgstr ""
+
+#: templates/gallery/gallery.inc:98 config/prefs.php.dist:212
+msgid "Who should be allowed to download original photos"
+msgstr ""
+
+#: lib/Exif.php:337
+msgid "Width"
+msgstr ""
+
+#: lib/Exif.php:339
+msgid "X Resolution"
+msgstr ""
+
+#: lib/Exif.php:340
+msgid "Y Resolution"
+msgstr ""
+
+#: config/prefs.php.dist:200
+msgid "Yes"
+msgstr ""
+
+#: lib/XRequest/EditFaces.php:159
+msgid "You are not allowed to edit this photo."
+msgstr ""
+
+#: gallery/delete.php:32
+#, php-format
+msgid "You cannot delete %s, it contains nested galleries."
+msgstr ""
+
+#: gallery.php:255
+#, php-format
+msgid "You do not have permission to add children to %s."
+msgstr ""
+
+#: lib/Faces/facedetect.php:34
+msgid "You do not have the facedetect extension enabled in PHP"
+msgstr ""
+
+#: lib/Faces/opencv.php:34
+msgid "You do not have the opencv extension enabled in PHP"
+msgstr ""
+
+#: img/ecard.php:68
+msgid ""
+"You have been sent an Ecard. To view the Ecard, you must be able to view "
+"text/html messages in your mail reader. If you are viewing this message, "
+"then most likely your mail reader does not support viewing text/html "
+"messages."
+msgstr ""
+
+#: lib/Views/List.php:189
+msgid "You have no photo galleries, add one!"
+msgstr ""
+
+#: templates/view/gallery.inc:58 templates/view/gallery.inc:72
+#: templates/view/gallerylightbox.inc:59 templates/view/gallerylightbox.inc:73
+#: templates/view/results.inc:46 templates/view/results.inc:60
+msgid "You must choose a gallery to move photos to."
+msgstr ""
+
+#: templates/view/gallery.inc:29 templates/view/gallerylightbox.inc:29
+msgid "You must choose photos to delete."
+msgstr ""
+
+#: lib/Ansel.php:64 lib/Ansel.php:72
+msgid "You must configure a VFS backend to use Ansel."
+msgstr ""
+
+#: img/ecard.php:44
+msgid "You must enter an e-mail address to send the message to."
+msgstr ""
+
+#: img/ecard.php:39
+msgid "You must enter your e-mail address."
+msgstr ""
+
+#: gallery.php:266
+msgid "You must provide a display name for your new gallery."
+msgstr ""
+
+#: scripts/recursive_import.php:88
+msgid "You must specify a valid directory."
+msgstr ""
+
+#: scripts/recursive_import.php:84
+msgid "You must specify a valid username and password."
+msgstr ""
+
+#: faces/search/image_define.php:21 faces/search/image_save.php:22
+#: faces/search/image_search.php:32
+msgid "You must upload the search photo first"
+msgstr ""
+
+#: lib/Forms/Upload.php:72
+msgid "Zip File Upload"
+msgstr ""
+
+#: lib/Ansel.php:532
+msgid "_Galleries"
+msgstr ""
+
+#: lib/Ansel.php:544
+msgid "_My Galleries"
+msgstr ""
+
+#: lib/Ansel.php:556
+msgid "_New Gallery"
+msgstr ""
+
+#: lib/Ansel.php:569
+msgid "_Print"
+msgstr ""
+
+#: templates/view/list.inc:12 templates/group/header.inc:15
+msgid "category"
+msgstr ""
+
+#: templates/image/crop_image.inc:172 templates/image/resize_image.inc:81
+#: templates/faces/define.inc:61
+msgid "height"
+msgstr ""
+
+#: xppublish.php:185
+msgid "image"
+msgstr ""
+
+#: templates/group/header.inc:18
+msgid "none"
+msgstr ""
+
+#: lib/Views/GalleryRenderers/GalleryLightbox.php:178
+msgid "of"
+msgstr ""
+
+#: templates/view/list.inc:11 templates/group/header.inc:13
+msgid "owner"
+msgstr ""
+
+#: lib/Exif.php:304
+msgid "sRGB"
+msgstr ""
+
+#: templates/image/crop_image.inc:166 templates/image/resize_image.inc:79
+#: templates/faces/define.inc:59
+msgid "width"
+msgstr ""
diff --git a/ansel/po/de_DE.po b/ansel/po/de_DE.po
new file mode 100644 (file)
index 0000000..a922a6c
--- /dev/null
@@ -0,0 +1,3629 @@
+# German translations for Ansel.
+# Copyright 2003-2009 The Horde Project
+# This file is distributed under the same license as the Ansel package.
+# Jan Schneider <jan@horde.org>, 2003-2008.
+#
+# caption: Fotounterschrift
+msgid ""
+msgstr ""
+"Project-Id-Version: Ansel 1.0-cvs\n"
+"Report-Msgid-Bugs-To: dev@lists.horde.org\n"
+"POT-Creation-Date: 2009-01-03 18:57+0100\n"
+"PO-Revision-Date: 2009-01-03 19:12+0100\n"
+"Last-Translator: Jan Schneider <jan@horde.org>\n"
+"Language-Team: i18n@lists.horde.org\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-1\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: scripts/recursive_import.php:149 scripts/remote_import.php:166
+#, php-format
+msgid "\"%s\" is not a directory."
+msgstr "\"%s\" ist kein Verzeichnis."
+
+#: lib/Exif.php:169
+#, php-format
+msgid "%4f sec"
+msgstr "%4f Sek"
+
+#: templates/group/owner.inc:57 templates/group/category.inc:51
+#: templates/view/list.inc:3 templates/list/header.inc:3
+#, php-format
+msgid "%d Gallery"
+msgid_plural "%d Galleries"
+msgstr[0] "%d Galerie"
+msgstr[1] "%d Galerien"
+
+#: templates/group/header.inc:3
+#, php-format
+msgid "%d Group"
+msgid_plural "%d Groups"
+msgstr[0] "%d Gruppe"
+msgstr[1] "%d Gruppen"
+
+#: templates/view/gallerylightbox.inc:110 templates/view/results.inc:67
+#, php-format
+msgid "%d Item"
+msgid_plural "%d Items"
+msgstr[0] "%d Element"
+msgstr[1] "%d Elemente"
+
+#: templates/tile/image.inc:11
+#, php-format
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] "%d Kommentar"
+msgstr[1] "%d Kommentare"
+
+#: lib/Exif.php:261
+#, php-format
+msgid "%d dots per unit"
+msgstr "%d Punkte pro Einheit"
+
+#: templates/faces/face.inc:7
+#, php-format
+msgid "%d gallery"
+msgid_plural "%d galleries"
+msgstr[0] "%d Galerie"
+msgstr[1] "%d Galerien"
+
+#: templates/view/gallery.inc:89
+#, php-format
+msgid "%d item"
+msgid_plural "%d items"
+msgstr[0] "%d Element"
+msgstr[1] "%d Elemente"
+
+#: lib/Exif.php:332 lib/Exif.php:334
+#, php-format
+msgid "%d mm"
+msgstr "%d mm"
+
+#: lib/Exif.php:263
+#, php-format
+msgid "%d per unit"
+msgstr "%d pro Einheit"
+
+#: templates/faces/search.inc:9 lib/XRequest/TagActions.php:115
+#: lib/Widget/Tags.php:93 lib/Views/Results.php:291
+#, php-format
+msgid "%d photo"
+msgid_plural "%d photos"
+msgstr[0] "%d Foto"
+msgstr[1] "%d Fotos"
+
+#: img/upload.php:223
+#, php-format
+msgid "%d photo was uploaded."
+msgid_plural "%d photos were uploaded."
+msgstr[0] "%d Foto wurde hochgeladen."
+msgstr[1] "%d Fotos wurden hochgeladen."
+
+#: lib/Exif.php:275
+#, php-format
+msgid "%d pixels"
+msgstr "%d Pixel"
+
+#: lib/Exif.php:164
+#, php-format
+msgid "%d sec"
+msgstr "%d Sek"
+
+#: templates/view/list.inc:3 templates/list/header.inc:3
+#, php-format
+msgid "%d to %d of %d Galleries"
+msgstr "%d bis %d von %d Galerien"
+
+#: templates/group/header.inc:3
+#, php-format
+msgid "%d to %d of %d Groups"
+msgstr "%d bis %d von %d Gruppen"
+
+#: templates/view/gallerylightbox.inc:110 templates/view/results.inc:67
+#, php-format
+msgid "%d to %d of %d Items"
+msgstr "%d bis %d von %d Elementen"
+
+#: templates/view/gallery.inc:89
+#, php-format
+msgid "%d to %d of %d items"
+msgstr "%d bis %d von %d Elementen"
+
+#: lib/Exif.php:171
+#, php-format
+msgid "%s / %s sec"
+msgstr "%s / %s Sek"
+
+#: gallery/sort.php:64
+#, php-format
+msgid "%s :: Sort"
+msgstr "%s :: Sortieren"
+
+#: lib/Widget/SimilarPhotos.php:81
+#, php-format
+msgid "%s from %s"
+msgstr "%s von %s"
+
+#: rss.php:112
+#, php-format
+msgid "%s on %s"
+msgstr "%s auf %s"
+
+#: templates/group/owner.inc:16 lib/Ansel.php:701
+#: lib/Widget/OtherGalleries.php:28 lib/Views/List.php:167
+#, php-format
+msgid "%s's Galleries"
+msgstr "Galerien von %s"
+
+#: templates/faces/faces.inc:8
+#, php-format
+msgid "%s: faces %s"
+msgstr "%s: %s Gesichter"
+
+#: lib/Ansel.php:2689
+#, php-format
+msgid "(c) %s %s"
+msgstr "(c) %s %s"
+
+#: scripts/ansel.php:326
+msgid "-a, --add[=filename]         Add local file to selected gallery"
+msgstr ""
+"-a, --add[=dateiname]          Lokale Datei zur ausgewählten Galerie\n"
+"                               hinzufügen"
+
+#: scripts/ansel.php:324
+msgid ""
+"-c, --create[=name/description/owner]\n"
+"                             Create gallery (and use it)  Combined with -g "
+"to create a subgallery."
+msgstr ""
+"-c, --create[=name/beschreibung/besitzer]  Galerie erstellen (und "
+"benutzen).\n"
+"                                           Mit -g um Unter-Galerien zu\n"
+"                                           erstellen"
+
+#: scripts/ansel.php:327
+msgid ""
+"-d, --dir[=directory]        Add all files from the directory to the "
+"selected\n"
+"                             gallery"
+msgstr ""
+"-d, --dir[=verzeichnis]        Alle Dateien des Verzeichnisses zur "
+"ausgewählten\n"
+"                               Galerie hinzufügen"
+
+#: scripts/recursive_import.php:226 scripts/remote_import.php:345
+msgid ""
+"-d, --dir[=directory]        Recursively add all files from the directory, "
+"creating\n"
+"                             a gallery for each directory"
+msgstr ""
+"-d, --dir[=verzeichnis]        Rekursiv alle Dateien des Verzeichnisses\n"
+"                               hinzufügen und dabei Galerien für jedes\n"
+"                               Verzeichnis anlegen"
+
+#: scripts/all_images_exif_to_tags.php:123
+msgid ""
+"-f, --fields[=exif_fields]   A ':' delimited list of exif fields to include "
+"DateTimeOriginal is default."
+msgstr ""
+"-f, --fields[=exif_fields]   Eine ':'-getrennte Liste von Exif-Feldern, die\n"
+"                             benutzt werden sollen. DateTimeOriginal ist\n"
+"                             Standard."
+
+#: scripts/remote_import.php:348
+msgid ""
+"-g, --gallery[=gallery_id]   The gallery id to add directory contents to"
+msgstr ""
+"-g, --gallery[=gallery_id]   Die ID der Galerie, zu der der "
+"Verzeichnisinhalt\n"
+"                             hinzugefügt werden soll"
+
+#: scripts/ansel.php:325
+msgid "-g, --gallery[=shortname]    Select gallery to use"
+msgstr "-g, --gallery[=kurzname]       Zu benutzende Galerie auswählen"
+
+#: scripts/recursive_import.php:225 scripts/ansel.php:322
+#: scripts/remote_import.php:344 scripts/all_images_exif_to_tags.php:120
+msgid "-h, --help                   Show this help"
+msgstr "-h, --help                     Diese Hilfe anzeigen"
+
+#: scripts/recursive_import.php:229
+msgid ""
+"-k, --keep                   Do not delete empty galleries after import is "
+"complete."
+msgstr ""
+"-k, --keep                   Leere Galerien nach dem Import nicht löschen."
+
+#: scripts/ansel.php:323
+msgid ""
+"-l, --list                   List galleries or photos (if combined with -g)"
+msgstr ""
+"-l, --list                     Galerien oder Fotos (wenn mit -g kombiniert)\n"
+"                               anzeigen"
+
+#: scripts/recursive_import.php:228 scripts/ansel.php:329
+#: scripts/remote_import.php:347 scripts/all_images_exif_to_tags.php:122
+msgid "-p, --password[=password]    Horde login password"
+msgstr "-p, --password[=passwort]      Horde-Passwort"
+
+#: scripts/remote_import.php:351
+msgid "-r, --remotehost[=url]       The url of the remote rpc server."
+msgstr "-r, --remotehost[=url]       Die URL des externen RPC-Servers"
+
+#: scripts/remote_import.php:349
+msgid ""
+"-s, --slug[=gallery_slug]    The gallery slug to add directory contents to"
+msgstr ""
+"-s, --slug[=gallery_slug]    Der Kurzname der Galerie, zu der der\n"
+"                             Verzeichnisinhalt hinzugefügt werden soll."
+
+#: scripts/ansel.php:330
+msgid "-t, --caption[=caption]      Caption for photo (if combined with -a)"
+msgstr ""
+"-t, --caption[=unterschrift]   Fotounterschrift (in Kombination mit -a)"
+
+#: scripts/recursive_import.php:227 scripts/ansel.php:328
+#: scripts/remote_import.php:346 scripts/all_images_exif_to_tags.php:121
+msgid "-u, --username[=username]    Horde login username"
+msgstr "-u, --username[=benutzername]  Horde-Benutzername"
+
+#: lib/Exif/canon.php:95
+msgid "/10s"
+msgstr "/10s"
+
+#: lib/Exif/panasonic.php:74 lib/Exif/panasonic.php:80
+msgid "1-area-focusing"
+msgstr "1-Bereich-Fokus"
+
+#: lib/Exif/panasonic.php:78
+msgid "1-area-focusing (High speed)"
+msgstr "1-Bereich-Fokus (Hochgeschwindigkeit)"
+
+#: lib/Exif/panasonic.php:293
+msgid "10s"
+msgstr "10s"
+
+#: lib/Exif/canon.php:308
+msgid "1st curtain sync"
+msgstr "Synchronisation auf den ersten Vorhang"
+
+#: lib/Exif/canon.php:305
+msgid "2nd(rear)-curtain sync used"
+msgstr "Synchronisation auf den zweiten (hinteren) Vorhang"
+
+#: lib/Exif/panasonic.php:295
+msgid "2s"
+msgstr "2s"
+
+#: lib/Exif/panasonic.php:76
+msgid "3-area-focusing (High speed)"
+msgstr "3-Bereichs-Fokus (Hochgeschwindigkeit)"
+
+#: lib/Exif/panasonic.php:72
+msgid "9-area-focusing"
+msgstr "9-Bereichs-Fokus"
+
+#: lib/Exif.php:290
+#, php-format
+msgid "90 deg CCW"
+msgstr "90° gegen UZS"
+
+#: lib/Exif.php:292
+#, php-format
+msgid "90 deg CCW Mirrored"
+msgstr "90° gegen UZS, gespiegelt"
+
+#: lib/Exif.php:294
+#, php-format
+msgid "90 deg CW"
+msgstr "90° im UZS"
+
+#: lib/Exif.php:288
+#, php-format
+msgid "90 deg CW Mirrored"
+msgstr "90° im UZS, gespiegelt"
+
+#: config/styles.php.dist:128
+msgid "A Lightbox Inspired Style (Drop Shadows, No Background)"
+msgstr "Ein von Lightbox inspirierter Stil (Schattenwurf, kein Hintergrund)"
+
+#: config/styles.php.dist:116
+msgid "A Lightbox Inspired Style (No Background)"
+msgstr "Ein von Lightbox inspirierter Stil (Kein Hintergrund)"
+
+#: config/styles.php.dist:104
+msgid "A Lightbox Inspired Style (White Background)"
+msgstr "Ein von Lightbox inspirierter Stil (Weißer Hintergrund)"
+
+#: lib/api.php:507
+msgid "A gallery to add this photo to is required."
+msgstr ""
+"Die Galerie, zu der dieses Foto hinzugefügt werden soll, muss angegeben "
+"werden."
+
+#: lib/Exif/fujifilm.php:167
+msgid "AE Good"
+msgstr "AE erfolgreich"
+
+#: lib/Exif/nikon.php:181
+msgid "AF non D"
+msgstr "AF ohne D"
+
+#: lib/Exif/canon.php:132
+msgid "AI Focus"
+msgstr "AI-Fokus"
+
+#: lib/Exif/canon.php:131
+msgid "AI Servo"
+msgstr "AI-Servo"
+
+#: gallery.php:57
+#, php-format
+msgid "Access denied adding a gallery to \"%s\"."
+msgstr "Zugriff verweigert beim Hinzufügen der Galerie zu \"%s\"."
+
+#: xppublish.php:178 scripts/ansel.php:134 lib/api.php:384 lib/api.php:521
+#, php-format
+msgid "Access denied adding photos to \"%s\"."
+msgstr "Zugriff verweigert beim Hinzufügen der Fotos zu \"%s\"."
+
+#: lib/Ansel.php:1675
+msgid "Access denied adding tags to this gallery."
+msgstr "Zugriff verweigert beim Hinzufügen von Tags zu dieser Galerie."
+
+#: lib/Ansel.php:2777
+msgid "Access denied adding tags to this photo."
+msgstr "Zugriff verweigert beim Hinzufügen von Tags zu diesem Foto."
+
+#: lib/Ansel.php:1313
+#, php-format
+msgid "Access denied copying photos to \"%s\"."
+msgstr "Zugriff verweigert beim Kopieren der Fotos nach \"%s\"."
+
+#: lib/api.php:617
+msgid "Access denied creating new galleries."
+msgstr "Zugriff verweigert beim Erstellen neuer Galerien."
+
+#: lib/api.php:574 lib/api.php:654
+msgid "Access denied deleting galleries."
+msgstr "Zugriff verweigert beim Löschen der Galerien."
+
+#: gallery/delete.php:28 gallery/delete.php:57 lib/api.php:666
+#, php-format
+msgid "Access denied deleting gallery \"%s\"."
+msgstr "Zugriff verweigert beim Löschen der Galerie \"%s\"."
+
+#: image.php:572 lib/api.php:591 lib/Views/Results.php:125
+#, php-format
+msgid "Access denied deleting photos from \"%s\"."
+msgstr "Zugriff verweigert beim Löschen der Fotos aus \"%s\"."
+
+#: gallery.php:96 lib/Ansel.php:2579
+#, php-format
+msgid "Access denied downloading photos from \"%s\"."
+msgstr "Zugriff verweigert beim Herunterladen der Fotos von \"%s\"."
+
+#: gallery.php:181
+msgid "Access denied editing galleries."
+msgstr "Zugriff verweigert beim Bearbeiten der Galerien."
+
+#: gallery/sort.php:32 faces/gallery.php:34 lib/Faces.php:847
+#, php-format
+msgid "Access denied editing gallery \"%s\"."
+msgstr "Zugriff verweigert beim Bearbeiten der Galerie \"%s\"."
+
+#: image.php:304 image.php:698 faces/custom.php:29 faces/delete.php:29
+#: faces/name.php:28 lib/Faces.php:540 lib/Faces.php:638
+#: lib/XRequest/EditFaces.php:134
+msgid "Access denied editing the photo."
+msgstr "Zugriff verweigert beim Bearbeiten des Fotos."
+
+#: lib/GalleryMode/Normal.php:167 lib/GalleryMode/Date.php:426
+#, php-format
+msgid "Access denied moving photos to \"%s\"."
+msgstr "Zugriff verweigert beim Verschieben der Fotos nach \"%s\"."
+
+#: lib/GalleryMode/Normal.php:169 lib/GalleryMode/Date.php:428
+#, php-format
+msgid "Access denied removing photos from \"%s\"."
+msgstr "Zugriff verweigert beim Entfernen der Fotos aus \"%s\"."
+
+#: gallery.php:224
+#, php-format
+msgid "Access denied saving gallery \"%s\"."
+msgstr "Zugriff verweigert beim Speichern der Galerie \"%s\"."
+
+#: image.php:176 image.php:358 image.php:399
+#, php-format
+msgid "Access denied saving photo to \"%s\"."
+msgstr "Zugriff verweigert beim Speichern des Fotos in \"%s\"."
+
+#: gallery/captions.php:33
+#, php-format
+msgid "Access denied setting captions for %s."
+msgstr "Zugriff verweigert beim Setzen der Fotosunterschrift für %s."
+
+#: lib/Views/Abstract.php:56
+#, php-format
+msgid "Access denied to gallery \"%s\"."
+msgstr "Zugriff auf Galerie \"%s\" verweigert."
+
+#: lib/Ansel.php:1660 lib/Views/Gallery.php:154 lib/Block/gallery.php:156
+#: lib/Block/recent_comments.php:154 lib/Block/recently_added.php:197
+msgid "Access denied viewing this gallery."
+msgstr "Zugriff auf diese Galerie verweigert."
+
+#: lib/Ansel.php:2756 img/full.php:26 img/prettythumb.php:26 img/mini.php:25
+#: img/index.php:25 img/thumb.php:25 img/download.php:26 img/screen.php:25
+msgid "Access denied viewing this photo."
+msgstr "Zugriff auf dieses Foto verweigert."
+
+#: lib/Exif.php:251
+msgid "Action"
+msgstr "Aktion"
+
+#: faces/report.php:43 faces/claim.php:41
+msgid "Action was cancelled."
+msgstr "Die Aktion wurde abgebrochen."
+
+#: templates/view/gallerylightbox.inc:136 templates/view/results.inc:81
+msgid "Actions: "
+msgstr "Aktionen: "
+
+#: lib/Widget/Tags.php:58
+msgid "Add"
+msgstr "Hinzufügen"
+
+#: browse.php:26
+msgid "Add Content"
+msgstr "Inhalt hinzufügen"
+
+#: img/upload.php:242
+msgid "Add Photo"
+msgstr "Foto Hinzufügen"
+
+#: templates/faces/gallery.inc:23
+msgid "Add a custom face range"
+msgstr "Ein Gesicht manuell hinzufügen"
+
+#: lib/Widget/Actions.php:122
+msgid "Add to bookmarks"
+msgstr "Zu Lesezeichen hinzufügen"
+
+#: gallery.php:43 gallery.php:300
+msgid "Adding A New Gallery"
+msgstr "Neue Galerie hinzufügen"
+
+#: gallery.php:79
+#, php-format
+msgid "Adding A Subgallery to %s"
+msgstr "Untergalerie zu %s hinzufügen"
+
+#: scripts/recursive_import.php:205 scripts/remote_import.php:276
+msgid "Adding subdirectories:"
+msgstr "Füge Unterverzeichnisse hinzu:"
+
+#: lib/api.php:194
+msgid "Administrators"
+msgstr "Administratoren"
+
+#: report.php:49
+msgid "Advertisement content"
+msgstr "Werbungsinhalte"
+
+#: templates/view/gallery.inc:117 templates/view/gallerylightbox.inc:133
+#: templates/view/results.inc:77
+msgid "All"
+msgstr "Alle"
+
+#: lib/Block/recent_comments.php:68 lib/Block/recently_added.php:75
+msgid "All Galleries"
+msgstr "allen Galerien"
+
+#: lib/GalleryMode/Date.php:123
+msgid "All dates"
+msgstr "Alle Daten"
+
+#: faces/search/tabs.php:32 faces/search/all.php:17
+msgid "All faces"
+msgstr "Alle Gesichter"
+
+#: templates/gallery/gallery.inc:87
+msgid "Allow all ages"
+msgstr "Alle Altersstufen erlauben"
+
+#: templates/gallery/gallery.inc:109 config/prefs.php.dist:226
+msgid "Anyone"
+msgstr "Jeder"
+
+#: lib/Exif.php:499
+msgid "Aperture"
+msgstr "Blende"
+
+#: lib/Exif.php:248 lib/Exif/panasonic.php:193
+msgid "Aperture Priority"
+msgstr "Blendenautomatik"
+
+#: lib/Exif/fujifilm.php:146
+msgid "Aperture Prority AE"
+msgstr "Blendenautomatik AE"
+
+#: templates/view/gallery.inc:32 templates/view/gallerylightbox.inc:33
+#: templates/view/results.inc:20
+msgid "Are you sure you want to delete the selected photos?"
+msgstr "Sind Sie sicher, dass Sie die ausgewählten Fotos löschen möchten?"
+
+#: lib/Exif.php:513
+msgid "Artist"
+msgstr "Künstler"
+
+#: templates/view/list.inc:26
+msgid "Ascending"
+msgstr "Aufsteigend"
+
+#: perms.php:53
+msgid "Attempt to edit a non-existent share."
+msgstr "Es wurde versucht, ein nicht existierendes Recht zu bearbeiten."
+
+#: templates/gallery/gallery.inc:110 config/prefs.php.dist:227
+msgid "Authenticated users"
+msgstr "Angemeldete Benutzer"
+
+#: lib/Exif.php:395 lib/Exif/fujifilm.php:95 lib/Exif/fujifilm.php:118
+#: lib/Exif/fujifilm.php:130 lib/Exif/fujifilm.php:140
+#: lib/Exif/panasonic.php:127 lib/Exif/panasonic.php:148
+#: lib/Exif/nikon.php:145 lib/Exif/canon.php:109 lib/Exif/canon.php:213
+#: lib/Exif/canon.php:242 lib/Exif/canon.php:346
+msgid "Auto"
+msgstr "Automatisch"
+
+#: lib/Exif/canon.php:113
+msgid "Auto + Red Eye Reduction"
+msgstr "Automatisch + Rote-Augen-Reduzierung"
+
+#: lib/Exif/fujifilm.php:162
+msgid "Auto Focus Good"
+msgstr "Autofokus erfolgreich"
+
+#: lib/Exif/canon.php:251
+msgid "Auto Selected"
+msgstr "Automatisch ausgewählt"
+
+#: lib/Exif.php:240
+msgid "Auto bracket"
+msgstr "Belichtungsreihe"
+
+#: lib/Exif.php:238
+msgid "Auto exposure"
+msgstr "Automatische Belichtung"
+
+#: lib/Exif/panasonic.php:154
+msgid "Auto, Continuous"
+msgstr "Automatisch, kontinuierlich"
+
+#: lib/Exif/panasonic.php:152
+msgid "Auto, Focus button"
+msgstr "Automatisch, Fokusschalter"
+
+#: lib/Exif/canon.php:265
+msgid "Auto-DEP"
+msgstr "Auto-DEP"
+
+#: config/prefs.php.dist:218
+msgid "Automatically watermark photos?"
+msgstr "Fotos automatisch mit Wasserzeichen markieren?"
+
+#: lib/Exif/canon.php:263
+msgid "Av"
+msgstr "Av"
+
+#: lib/Exif.php:357
+msgid "Average"
+msgstr "Durchschnitt"
+
+#: lib/Exif/panasonic.php:215
+msgid "Baby"
+msgstr "Baby"
+
+#: templates/image/crop_image.inc:92 templates/image/edit_image.inc:8
+#: templates/image/resize_image.inc:39 templates/view/image.inc:95
+#: templates/view/image.inc:96 templates/view/image.inc:121
+#: templates/view/image.inc:122
+msgid "Back to gallery"
+msgstr "Zurück zur Galerie"
+
+#: image.php:620 image.php:669 lib/Views/Results.php:160
+#: lib/Views/Results.php:208
+msgid "Bad input."
+msgstr "Fehlerhafte Eingabe."
+
+#: lib/Exif/canon.php:160
+msgid "Black & White"
+msgstr "Schwarz & Weiß"
+
+#: lib/Exif/panasonic.php:139 lib/Exif/panasonic.php:249
+msgid "Black and White"
+msgstr "Schwarz und Weiß"
+
+#: lib/Forms/Watermark.php:54 config/prefs.php.dist:195
+msgid "Bottom"
+msgstr "Unten"
+
+#: lib/Exif/nikon.php:131
+msgid "Bright+"
+msgstr "Helligkeit+"
+
+#: lib/Exif/nikon.php:132
+msgid "Bright-"
+msgstr "Helligkeit-"
+
+#: config/prefs.php.dist:57
+msgid "Browse"
+msgstr "Liste"
+
+#: lib/Ansel.php:756
+msgid "Browse Tags"
+msgstr "Tags anzeigen"
+
+#: lib/Exif.php:174
+msgid "Bulb"
+msgstr "Bulb"
+
+#: lib/Exif.php:482
+msgid "Camera Make"
+msgstr "Kamerahersteller"
+
+#: lib/Exif.php:483
+msgid "Camera Model"
+msgstr "Kameramodell"
+
+#: lib/Exif.php:511
+msgid "Camera Orientation"
+msgstr "Kameraausrichtung"
+
+#: scripts/remote_import.php:307 lib/Ansel.php:527
+#, php-format
+msgid "Can't get unknown file type \"%s\"."
+msgstr "Unbekannter Dateityp \"%s\" kann nicht verarbeitet werden."
+
+#: report.php:47 disclamer.php:34 disclamer.php:37
+#: templates/gallery/delete_confirmation.inc:13
+#: templates/image/crop_image.inc:93 templates/image/edit_image.inc:9
+#: templates/image/resize_image.inc:40 templates/faces/custom.inc:53
+#: templates/faces/define.inc:38 faces/report.php:41 faces/claim.php:40
+#: faces/savecustom.php:27 lib/Forms/Upload.php:39 img/upload.php:224
+msgid "Cancel"
+msgstr "Abbrechen"
+
+#: templates/image/preview_cropimage.inc:22
+#: templates/image/preview_cropimage.inc:23
+#: templates/image/preview_image.inc:25 templates/image/preview_image.inc:26
+msgid "Cancel Change"
+msgstr "Änderungen verwerfen"
+
+#: faces/search/image_save.php:74
+msgid "Cannot read photo signature"
+msgstr "Fotosignatur kann nicht gelesen werden"
+
+#: faces/search/image_save.php:86
+msgid "Cannot save photo signature"
+msgstr "Fotosignatur kann nicht gespeichert werden"
+
+#: faces/search/image_save.php:66 faces/search/image.php:59
+msgid "Cannot store search photo"
+msgstr "Das Suchfoto kann nicht gespeichert werden"
+
+#: lib/Forms/Image.php:39 lib/Forms/Upload.php:52
+msgid "Caption"
+msgstr "Fotounterschrift"
+
+#: gallery/captions.php:66
+msgid "Caption Editor"
+msgstr "Editor für Fotounterschriften"
+
+#: gallery/captions.php:56
+msgid "Captions Saved."
+msgstr "Fotounterschriften gespeichert."
+
+#: config/prefs.php.dist:70
+msgid "Category"
+msgstr "Kategorie"
+
+#: lib/Forms/Watermark.php:46 lib/Forms/Watermark.php:53
+#: lib/Exif/nikon.php:165 lib/Exif/canon.php:253 lib/Exif/canon.php:364
+#: config/prefs.php.dist:183 config/prefs.php.dist:194
+msgid "Center"
+msgstr "Zentriert"
+
+#: lib/Exif.php:358
+msgid "Center Weighted Average"
+msgstr "Zentral gewichteter Durchschnitt"
+
+#: lib/Exif/canon.php:235
+msgid "Center-weighted"
+msgstr "Zentral gewichtet"
+
+#: lib/Exif.php:269
+msgid "Centimeter"
+msgstr "Zentimeter"
+
+#: config/prefs.php.dist:14
+msgid ""
+"Change display options such as which view to display by default, how many "
+"photos to display on a page, and the default gallery style to use."
+msgstr ""
+"Legen Sie die Einstellungen für die Anzeige fest, z.B. welche Ansicht als "
+"erstes angzeigt, wie viele Fotos pro Seite angezeigt und welcher Galeriestil "
+"standardmäßig verwendet werden soll."
+
+#: config/prefs.php.dist:24
+msgid "Change options dealing with tags and exif data."
+msgstr "Legen Sie die Einstellungen für Tags und Exif-Daten fest."
+
+#: lib/Widget/Actions.php:135
+msgid "Change properties"
+msgstr "Eigenschaften ändern"
+
+#: config/prefs.php.dist:31
+msgid ""
+"Change your user permission options such as who can download original "
+"photos, and what permissions newly created galleries should have by default."
+msgstr ""
+"Legen Sie Ihre Rechte-Einstellungen fest, z.B. wer Originalfotos "
+"herunterladen darf oder welche Rechte neu erstellten Galerien zugewiesen "
+"werden sollen."
+
+#: config/prefs.php.dist:38
+msgid "Change your watermark options."
+msgstr "Legen Sie Ihre Wasserzeichen-Einstellungen fest."
+
+#: faces/savecustom.php:28
+msgid "Changes cancelled."
+msgstr "Änderungen abgebrochen."
+
+#: lib/Exif/fujifilm.php:107
+msgid "Chroma Saturation High"
+msgstr "Hohe Farbsättigung"
+
+#: lib/Exif/fujifilm.php:108
+msgid "Chroma Saturation Low(ORG)"
+msgstr "Niedrige Farbsättigung (ORG)"
+
+#: lib/Exif/fujifilm.php:106
+msgid "Chroma Saturation Normal(STD)"
+msgstr "Normale Farbsättigung (STD)"
+
+#: templates/tile/face.inc:27 lib/Faces.php:1125
+msgid "Claim"
+msgstr "Benennen"
+
+#: lib/XRequest/EditCaption.php:43
+msgid "Click to add caption..."
+msgstr "Klicken um Fotounterschrift hinzuzufügen..."
+
+#: lib/Exif/canon.php:243
+msgid "Close-up (Macro)"
+msgstr "Nahaufnahme (Makro)"
+
+#: lib/Exif/fujifilm.php:97 lib/Exif/panasonic.php:131 lib/Exif/nikon.php:150
+#: lib/Exif/canon.php:348
+msgid "Cloudy"
+msgstr "Bewölkt"
+
+#: lib/Exif.php:375
+msgid "Cloudy weather"
+msgstr "Wolkiges Wetter"
+
+#: lib/Exif/nikon.php:125
+msgid "Color"
+msgstr "Farbig"
+
+#: lib/Exif.php:451
+msgid "Color Sequential Area Sensor"
+msgstr "Color Sequential Area Sensor"
+
+#: lib/Exif.php:453
+msgid "Color Sequential Linear Sensor"
+msgstr "Color Sequential Linear Sensor"
+
+#: lib/Exif.php:508
+msgid "Color Space"
+msgstr "Farbraum"
+
+#: lib/Forms/Ecard.php:42
+msgid "Comments:"
+msgstr "Kommentare:"
+
+#: lib/Exif.php:410
+msgid "Compulsory Flash"
+msgstr "Erzwungener Blitz"
+
+#: lib/Exif.php:412
+msgid "Compulsory Flash, Return light detected"
+msgstr "Erzwungener Blitz, Reflektion gemessen"
+
+#: lib/Exif.php:411
+msgid "Compulsory Flash, Return light not detected"
+msgstr "Erzwungener Blitz, Reflektion nicht gemessen"
+
+#: disclamer.php:27
+msgid "Content Disclaimer"
+msgstr "Inhaltserklärung"
+
+#: lib/Report.php:98
+#, php-format
+msgid "Content abuse report in %s"
+msgstr "Anstößigen Inhalt in %s melden"
+
+#: disclamer.php:34
+#, php-format
+msgid "Continue - I'm over %d"
+msgstr "Weiter - Ich bin über %d"
+
+#: lib/Exif/canon.php:122 lib/Exif/canon.php:135 lib/Exif/canon.php:320
+msgid "Continuous"
+msgstr "Kontinuierlich"
+
+#: lib/Exif/fujifilm.php:113
+msgid "Contrast High(HARD)"
+msgstr "Hoher Kontrast (HARD)"
+
+#: lib/Exif/fujifilm.php:114
+msgid "Contrast Low(ORG)"
+msgstr "Niedriger Kontrast (ORG)"
+
+#: lib/Exif/fujifilm.php:112
+msgid "Contrast Normal(STD)"
+msgstr "Normaler Kontrast (STD)"
+
+#: lib/Exif/nikon.php:133
+msgid "Contrast+"
+msgstr "Kontrast+"
+
+#: lib/Exif/nikon.php:134
+msgid "Contrast-"
+msgstr "Kontrast-"
+
+#: lib/Exif/nikon.php:45
+msgid "Converter"
+msgstr "Konverter"
+
+#: lib/Exif/panasonic.php:247
+msgid "Cool"
+msgstr "Kühl"
+
+#: lib/Exif.php:379
+msgid "Cool white fluorescent"
+msgstr "Kühl-weiße Leuchtstoffröhre"
+
+#: lib/Views/Results.php:225
+#, php-format
+msgid "Copied %d photo from %s to %s"
+msgid_plural "Copied %d photos from %s to %s"
+msgstr[0] "%d Foto von %s nach %s kopiert"
+msgstr[1] "%d Fotos von %s nach %s kopiert"
+
+#: image.php:676
+#, php-format
+msgid "Copied %d photo to %s"
+msgid_plural "Copied %d photos to %s"
+msgstr[0] "%d Foto nach %s kopiert"
+msgstr[1] "%d Fotos nach %s kopiert"
+
+#: templates/view/gallerylightbox.inc:147 templates/view/results.inc:87
+#: lib/Views/GalleryRenderers/Gallery.php:128
+msgid "Copy"
+msgstr "Kopiere"
+
+#: lib/Exif.php:512
+msgid "Copyright"
+msgstr "Copyright"
+
+#: report.php:52
+msgid "Copyright infringement"
+msgstr "Copyrightverletzung"
+
+#: scripts/all_images_exif_to_tags.php:102
+#: scripts/all_images_exif_to_tags.php:106
+#, php-format
+msgid "Could not extract exif fields from %s: %s"
+msgstr "Exif-Felder konnten nicht aus %s ausgelesen werden: %s"
+
+#: scripts/remote_import.php:313
+msgid "Could not load the gzip extension"
+msgstr "Die gzip-Erweiterung konnte nicht geladen werden."
+
+#: scripts/remote_import.php:317
+msgid "Could not load the lzf extension"
+msgstr "Die lzf-Erweiterung konnte nicht geladen werden."
+
+#: lib/Ansel.php:1099
+#, php-format
+msgid "Could not save gallery, the slug, \"%s\", already exists."
+msgstr ""
+"Die Galerie konnte nicht gespeichert werden, der Kurzname \"%s\" existiert "
+"bereits."
+
+#: lib/Ansel.php:1092
+#, php-format
+msgid "Could not save gallery, the slug, \"%s\", contains invalid characters."
+msgstr ""
+"Die Galerie konnte nicht gespeichert werden, der Kurzname \"%s\" enthält "
+"ungültige Zeichen."
+
+#: scripts/ansel.php:187
+msgid "Couldn't list galleries."
+msgstr "Galerien konnten nicht angezeigt werden."
+
+#: scripts/ansel.php:44
+msgid "Couldn't read command-line options."
+msgstr "Die Kommandozeilenparameter konnten nicht gelesen werden."
+
+#: lib/Exif.php:1290
+msgid "Couldnt Find IFD1"
+msgstr "IFD1"
+
+#: lib/Exif.php:1337
+msgid "Couldnt Find InteroperabilityIFD"
+msgstr "InteroperabilityIFD nicht gefunden"
+
+#: lib/Exif.php:1253
+msgid "Couldnt Find SubIFD"
+msgstr "SubIFD nicht gefunden<"
+
+#: templates/xppublish/list.inc:19
+msgid "Create"
+msgstr "Erstellen"
+
+#: faces/custom.php:68 faces/search/image_define.php:25
+msgid "Create a new face"
+msgstr "Gesicht hinzufügen"
+
+#: templates/xppublish/new.inc:8
+msgid "Create a new gallery"
+msgstr "Galerie erstellen"
+
+#: templates/xppublish/list.inc:18
+msgid "Create a new gallery:"
+msgstr "Neue Galerie erstellen:"
+
+#: lib/Widget/Actions.php:54
+msgid "Create a subgallery"
+msgstr "Untergalerie erstellen"
+
+#: lib/Tile/Gallery.php:123
+msgid "Created:"
+msgstr "Erstellt:"
+
+#: scripts/recursive_import.php:154 scripts/remote_import.php:201
+#, php-format
+msgid "Creating gallery: \"%s\""
+msgstr "Erstelle Galerie: \"%s\""
+
+#: lib/Exif.php:250
+msgid "Creative"
+msgstr "Kreativ"
+
+#: image.php:710 templates/image/crop_image.inc:114
+#: templates/image/edit_image.inc:31 templates/image/resize_image.inc:62
+msgid "Crop"
+msgstr "Beschneiden"
+
+#: lib/Exif/fujifilm.php:102 lib/Exif/canon.php:352
+msgid "Custom"
+msgstr "Angepasst"
+
+#: lib/Forms/Watermark.php:32
+msgid "Custom Watermark"
+msgstr "Wasserzeichentext"
+
+#: config/prefs.php.dist:50
+msgid ""
+"Custom text to display describing your galleries. This will be displayed in "
+"place of your username when grouping galleries by username."
+msgstr ""
+"Frei wählbarer Text, der Ihre Galerien beschreibt. Dieser wird anstelle "
+"Ihres Namens angezeigt, wenn Galerien nach Benutzernamen gruppiert werden."
+
+#: config/prefs.php.dist:121 config/prefs.php.dist:174
+msgid "Custom watermark to use for photos"
+msgstr "Standard-Wasserzeichentext für Fotos"
+
+#: templates/view/list.inc:21 lib/Block/recently_added.php:101
+msgid "Date"
+msgstr "Datum"
+
+#: lib/Exif.php:489
+msgid "Date Photo Digitized"
+msgstr "Digitalisierungsdatum"
+
+#: lib/Exif.php:487
+msgid "Date Photo Modified"
+msgstr "Änderungsdatum"
+
+#: lib/Exif.php:488 config/prefs.php.dist:271
+msgid "Date Photo Taken"
+msgstr "Aufnahmedatum"
+
+#: lib/Exif.php:378
+msgid "Day white fluorescent"
+msgstr "Tages-weiße Leuchtstoffröhre"
+
+#: lib/Exif.php:370 lib/Exif/fujifilm.php:96 lib/Exif/panasonic.php:129
+#: lib/Exif/nikon.php:147
+msgid "Daylight"
+msgstr "Tageslicht"
+
+#: lib/Exif.php:377
+msgid "Daylight fluorescent"
+msgstr "Tageslicht-Leuchtstoffröhre"
+
+#: lib/Exif/fujifilm.php:98
+msgid "DaylightColor-fluorescence"
+msgstr "DaylightColor-Leuchtstoffröhre"
+
+#: lib/Exif/fujifilm.php:99
+msgid "DaywhiteColor-fluorescence"
+msgstr "DaywhiteColor-Leuchtstoffröhre"
+
+#: config/styles.php.dist:56
+msgid "Default"
+msgstr "Standard"
+
+#: templates/prefs/default_category_select.inc:7
+msgid "Default category for galleries"
+msgstr "Standardkategorie für Galerien"
+
+#: templates/prefs/default_gallerystyle_select.inc:3
+msgid "Default style for galleries"
+msgstr "Standardstil für Galerien"
+
+#: templates/view/image.inc:60 templates/view/gallerylightbox.inc:141
+#: templates/view/results.inc:82 templates/view/slideshow.inc:21
+#: lib/Views/GalleryRenderers/Gallery.php:122
+msgid "Delete"
+msgstr "Löschen"
+
+#: lib/Widget/Actions.php:179
+msgid "Delete All Photos"
+msgstr "Alle Fotos löschen"
+
+#: lib/Widget/Actions.php:180
+msgid "Delete Entire Gallery"
+msgstr "Gesamte Galerie löschen"
+
+#: templates/faces/image.inc:7
+msgid "Delete face"
+msgstr "Gesicht löschen"
+
+#: image.php:581 lib/Views/Results.php:134
+msgid "Deleted the photo."
+msgstr "Foto gelöscht."
+
+#: scripts/recursive_import.php:122 scripts/recursive_import.php:130
+#: scripts/remote_import.php:137 scripts/remote_import.php:145
+#, php-format
+msgid "Deleting empty gallery, \"%s\""
+msgstr "Lösche leere Galerie, \"%s\""
+
+#: templates/view/list.inc:30
+msgid "Descending"
+msgstr "Absteigend"
+
+#: templates/image/crop_image.inc:134
+msgid "Desired dimensions:"
+msgstr "Gewünschtes Seitenverhältnis:"
+
+#: lib/Exif/nikon.php:190
+msgid "Did Not Fire"
+msgstr "Nicht ausgelöst"
+
+#: lib/Exif.php:442
+msgid "Digital Still Camera"
+msgstr "Digitaler Fotoapparat"
+
+#: templates/gallery/gallery.inc:47
+msgid "Display Mode"
+msgstr "Anzeigemodus"
+
+#: config/prefs.php.dist:13
+msgid "Display Options"
+msgstr "Anzeige-Einstellungen"
+
+#: lib/Faces.php:1124
+msgid "Do you know someone in this photo?"
+msgstr "Kennen Sie jemanden auf diesem Foto?"
+
+#: report.php:20
+msgid "Do you really want to report this gallery?"
+msgstr "Möchten Sie wirklich diese Galerie melden?"
+
+#: report.php:31
+msgid "Do you really want to report this photo?"
+msgstr "Möchten Sie wirklich dieses Foto melden?"
+
+#: templates/view/image.inc:60 templates/view/slideshow.inc:21
+#, php-format
+msgid "Do you want to permanently delete \"%s\"?"
+msgstr "Möchten Sie \"%s\" endgültig löschen?"
+
+#: gallery/sort.php:81 scripts/all_images_exif_to_tags.php:109
+msgid "Done"
+msgstr "Fertig"
+
+#: templates/view/image.inc:66 templates/view/slideshow.inc:29
+msgid "Download Original Photo"
+msgstr "Originalfoto herunterladen"
+
+#: lib/Widget/Actions.php:129
+msgid "Download as zip file"
+msgstr "Als ZIP-Datei herunterladen"
+
+#: gallery.php:84
+msgid ""
+"Downloading zip files is not enabled. Talk to your server administrator."
+msgstr ""
+"Das Herunterladen von ZIP-Dateien ist nicht möglich. Bitte wenden Sie sich "
+"an Ihren Administrator."
+
+#: gallery/sort.php:80
+msgid "Drag photos to the desired sort position."
+msgstr "Ziehen Sie die Fotos an die gewünschte Position."
+
+#: xppublish.php:206
+#, php-format
+msgid "ERROR: %s"
+msgstr "FEHLER: %s"
+
+#: lib/Exif/canon.php:260
+msgid "EasyShoot"
+msgstr "EasyShoot"
+
+#: img/ecard.php:59
+msgid "Ecard - "
+msgstr "Grußkarte - "
+
+#: templates/view/image.inc:57 templates/view/slideshow.inc:18
+#: templates/tile/image.inc:18
+msgid "Edit"
+msgstr "Bearbeiten"
+
+#: image.php:314
+#, php-format
+msgid "Edit %s :: %s"
+msgstr "%s Bearbeiten :: %s"
+
+#: edit_dates.php:48 templates/view/gallerylightbox.inc:138
+#: lib/Views/GalleryRenderers/Gallery.php:119
+msgid "Edit Dates"
+msgstr "Daten bearbeiten"
+
+#: perms.php:245
+msgid "Edit Permissions"
+msgstr "Rechte bearbeiten"
+
+#: perms.php:249
+#, php-format
+msgid "Edit Permissions for %s"
+msgstr "Rechte für '%s' bearbeiten"
+
+#: image.php:506
+msgid "Edit Photo"
+msgstr "Foto bearbeiten"
+
+#: image.php:194
+msgid "Edit a photo"
+msgstr "Foto bearbeiten"
+
+#: templates/faces/image.inc:9
+msgid "Edit face region"
+msgstr "Gesicht bearbeiten"
+
+#: lib/Widget/GalleryFaces.php:76 lib/Widget/ImageFaces.php:77
+msgid "Edit faces"
+msgstr "Gesichter bearbeiten"
+
+#: image.php:144
+#, php-format
+msgid "Edit properties :: %s"
+msgstr "Eigenschaften bearbeiten :: %s"
+
+#: lib/Forms/ImageDate.php:31
+msgid "Editing dates for the following photos"
+msgstr "Daten für die folgenden Fotos bearbeiten"
+
+#: lib/Widget/Links.php:43
+msgid "Embed: "
+msgstr "Einbetten: "
+
+#: gallery/captions.php:26
+#, php-format
+msgid "Error accessing %s: %s"
+msgstr "Fehler beim Zugriff auf %s: %s"
+
+#: rss.php:35
+msgid "Error retrieving feed"
+msgstr "Fehler beim Lesen des Feeds"
+
+#: lib/Exif/canon.php:233
+msgid "Evaluative"
+msgstr "Bewertend"
+
+#: templates/xppublish/list.inc:12
+msgid "Existing galleries:"
+msgstr "Bestehende Galerien:"
+
+#: lib/Exif.php:496
+msgid "Exposure"
+msgstr "Belichtung"
+
+#: lib/Exif.php:502
+msgid "Exposure Bias"
+msgstr "Belichtungsabweichung"
+
+#: lib/Exif.php:503
+msgid "Exposure Mode"
+msgstr "Belichtungsmodus"
+
+#: lib/Exif.php:504
+msgid "Exposure Program"
+msgstr "Belichtungsprogramm"
+
+#: config/prefs.php.dist:272
+msgid "Exposure Time"
+msgstr "Belichtungszeit"
+
+#: lib/Exif/nikon.php:192
+msgid "External"
+msgstr "Extern"
+
+#: lib/Exif/canon.php:296
+msgid "External E-TTL"
+msgstr "Externer E-TTL"
+
+#: lib/Exif/canon.php:115
+msgid "External Flash"
+msgstr "Externer Blitz"
+
+#: scripts/all_images_exif_to_tags.php:104
+#, php-format
+msgid "Extracted exif fields from %s"
+msgstr "Exif-Felder aus %s ausgelesen"
+
+#: lib/Exif.php:500
+msgid "F-Number"
+msgstr "F-Nummer"
+
+#: lib/Exif/canon.php:302
+msgid "FP sync used"
+msgstr "FP-Synchronisation"
+
+#: faces/face.php:34 faces/report.php:60 faces/claim.php:58
+msgid "Face"
+msgstr "Gesicht"
+
+#: lib/Faces.php:973 lib/Faces.php:1001
+msgid "Face does not exist"
+msgstr "Gesicht existiert nicht"
+
+#: lib/Faces.php:46
+msgid "Face driver does not exist."
+msgstr "Treiber zur Gesichtserkennung existiert nicht."
+
+#: faces/search/name.php:20
+msgid "Face name to search"
+msgstr "Gesichtername"
+
+#: faces/report.php:65 faces/claim.php:63
+msgid "Face name was not reported."
+msgstr "Der Gesichtername wurde nicht gespeichert."
+
+#: faces/savecustom.php:54
+msgid "Face successfuly created"
+msgstr "Gesicht erfolgreich markiert"
+
+#: faces/savecustom.php:56
+msgid "Face successfuly updated"
+msgstr "Gesicht erfolgreich gelöscht"
+
+#: faces/search/image.php:21
+msgid "Face to search for"
+msgstr "Gesicht nach dem gesucht werden soll"
+
+#: templates/faces/gallery.inc:7
+msgid "Faces"
+msgstr "Gesichter"
+
+#: lib/Exif/canon.php:157
+msgid "Fast Shutter"
+msgstr "Schneller Verschluss"
+
+#: lib/Forms/Upload.php:67
+#, php-format
+msgid "File %s"
+msgstr "Datei %s"
+
+#: lib/Exif.php:486
+msgid "File Size"
+msgstr "Dateigröße"
+
+#: lib/api.php:286 lib/api.php:352
+msgid "File not found."
+msgstr "Datei nicht gefunden."
+
+#: lib/Exif.php:517
+msgid "File source"
+msgstr "Dateiquelle"
+
+#: lib/Forms/Upload.php:47 lib/Forms/Upload.php:76
+msgid "File to upload"
+msgstr "Hochzuladende Datei"
+
+#: templates/faces/gallery.inc:5
+msgid "Filename"
+msgstr "Dateiname"
+
+#: lib/Widget/Actions.php:157 lib/Widget/GalleryFaces.php:76
+#: lib/Widget/ImageFaces.php:77
+msgid "Find faces"
+msgstr "Gesichter finden"
+
+#: templates/tile/face.inc:24
+msgid "Find other photos this face appears in"
+msgstr "Andere Fotos suchen, in denen dieses Gesicht auftaucht"
+
+#: templates/faces/face.inc:14 templates/tile/face.inc:34 lib/Faces.php:1130
+msgid "Find similar"
+msgstr "Ähnliche suchen"
+
+#: lib/Exif/canon.php:101
+msgid "Fine"
+msgstr "Fein"
+
+#: lib/Exif.php:374
+msgid "Fine weather"
+msgstr "Schönes Wetter"
+
+#: lib/Exif/panasonic.php:205
+msgid "Fireworks"
+msgstr "Feuerwerk"
+
+#: lib/Exif/nikon.php:156
+msgid "Fisheye"
+msgstr "Fischauge"
+
+#: lib/Exif.php:373 lib/Exif.php:407 lib/Exif/panasonic.php:137
+#: lib/Exif/canon.php:351
+msgid "Flash"
+msgstr "Blitz"
+
+#: lib/Exif/canon.php:288
+msgid "Flash Did Not Fire"
+msgstr "Blitz hat nicht ausgelöst"
+
+#: lib/Exif/canon.php:289
+msgid "Flash Fired"
+msgstr "Blitz hat ausgelöst"
+
+#: lib/Exif.php:506
+msgid "Flash Setting"
+msgstr "Blitzeinstellungen"
+
+#: lib/Exif.php:415
+msgid "Flash, Auto-Mode"
+msgstr "Flash, Auto-Mode"
+
+#: lib/Exif.php:417
+msgid "Flash, Auto-Mode, Return light detected"
+msgstr "Flash, Auto-Mode, Return light detected"
+
+#: lib/Exif.php:416
+msgid "Flash, Auto-Mode, Return light not detected"
+msgstr "Flash, Auto-Mode, Return light not detected"
+
+#: lib/Exif.php:409
+msgid "Flash, strobe return light detected"
+msgstr "Flash, strobe return light detected"
+
+#: lib/Exif.php:408
+msgid "Flash, strobe return light not detected"
+msgstr "Flash, strobe return light not detected"
+
+#: templates/image/crop_image.inc:101 templates/image/edit_image.inc:17
+#: templates/image/resize_image.inc:48
+msgid "Flip"
+msgstr "Umdrehen"
+
+#: lib/Exif/nikon.php:149
+msgid "Flourescence"
+msgstr "Leuchtstoffröhre"
+
+#: lib/Exif.php:371 lib/Exif/canon.php:350
+msgid "Fluorescent"
+msgstr "Leuchtstoffröhre"
+
+#: lib/Exif.php:497
+msgid "Focal Length"
+msgstr "Brennweite"
+
+#: lib/Exif.php:498
+msgid "Focal Length (35mm equiv)"
+msgstr "Brennweite (entsprechend KB)"
+
+#: lib/Exif/panasonic.php:213
+msgid "Food"
+msgstr "Essen"
+
+#: faces/search/owner.php:23
+#, php-format
+msgid "From galleries of %s"
+msgstr "In Galerien von %s"
+
+#: faces/search/tabs.php:33 faces/search/owner.php:21
+msgid "From my galleries"
+msgstr "In meinen Galerien"
+
+#: templates/tile/image.inc:8
+msgid "From: "
+msgstr "Von: "
+
+#: lib/Exif/canon.php:154
+msgid "Full Auto"
+msgstr "Vollautomatik"
+
+#: lib/Ansel.php:753 config/prefs.php.dist:58
+msgid "Galleries"
+msgstr "Galerien"
+
+#: templates/group/header.inc:6
+#, php-format
+msgid "Galleries grouped by %s"
+msgstr "Galerien gruppiert nach %s"
+
+#: lib/Views/List.php:174 lib/Views/List.php:178
+#, php-format
+msgid "Galleries in category \"%s\""
+msgstr "Galerien in Kategorie \"%s\""
+
+#: templates/faces/face.inc:33 lib/Block/gallery.php:3
+#: lib/Block/gallery.php:30 lib/Block/gallery.php:57
+#: lib/Block/recent_comments.php:31 lib/Block/recent_comments.php:54
+#: lib/Block/recently_added.php:28 lib/Block/recently_added.php:58
+#: lib/Block/recently_added.php:101
+msgid "Gallery"
+msgstr "Galerie"
+
+#: image.php:65 scripts/remote_import.php:187 scripts/remote_import.php:196
+#: lib/Ansel.php:3109 img/upload_preview.php:19 img/upload.php:21
+#, php-format
+msgid "Gallery %s not found."
+msgstr "Galerie %s nicht gefunden."
+
+#: lib/Widget/Actions.php:15
+msgid "Gallery Actions"
+msgstr "Galerie-Aktionen"
+
+#: templates/gallery/gallery.inc:85
+msgid "Gallery Ages"
+msgstr "Galeriealter"
+
+#: templates/gallery/gallery.inc:26
+msgid "Gallery Category"
+msgstr "Galeriekategorie"
+
+#: report.php:71 templates/gallery/gallery.inc:63 faces/report.php:57
+#: faces/claim.php:55
+msgid "Gallery Description"
+msgstr "Galerie-Beschreibung"
+
+#: templates/gallery/gallery.inc:56
+msgid "Gallery Display Name"
+msgstr "Anzeigename der Galerie"
+
+#: report.php:72
+msgid "Gallery Id"
+msgstr "Galerie-Id"
+
+#: lib/Views/List.php:70 lib/Views/List.php:181
+msgid "Gallery List"
+msgstr "Galerieliste"
+
+#: report.php:70 faces/report.php:56 faces/claim.php:54
+#: lib/Block/my_galleries.php:53
+msgid "Gallery Name"
+msgstr "Galeriename"
+
+#: templates/gallery/gallery.inc:17
+msgid "Gallery Parent"
+msgstr "Übergalerie"
+
+#: templates/gallery/gallery.inc:118
+msgid "Gallery Password"
+msgstr "Galerie-Passwort"
+
+#: lib/Tile/Gallery.php:100
+msgid "Gallery Properties"
+msgstr "Galerieeigenschaften"
+
+#: templates/gallery/gallery.inc:69
+msgid "Gallery Slug"
+msgstr "Galeriekurzname"
+
+#: templates/gallery/gallery.inc:77
+msgid "Gallery Tags"
+msgstr "Galerietags"
+
+#: lib/Block/gallery.php:152 lib/Block/recent_comments.php:151
+#: lib/Block/recently_added.php:194
+msgid "Gallery does not exist."
+msgstr "Galerie existiert nicht."
+
+#: gallery/sort.php:49
+msgid "Gallery sorted."
+msgstr "Galerie umsortiert."
+
+#: report.php:79 report.php:85
+msgid "Gallery was not reported."
+msgstr "Die Galerie wurde nicht gemeldet."
+
+#: report.php:82
+msgid "Gallery was reported."
+msgstr "Die Galerie wurde gemeldet."
+
+#: config/prefs.php.dist:12 config/prefs.php.dist:22 config/prefs.php.dist:29
+#: config/prefs.php.dist:36
+msgid "General Options"
+msgstr "Allgemeine Einstellungen"
+
+#: lib/Forms/Watermark.php:40 config/prefs.php.dist:208
+msgid "Giant"
+msgstr "Riesig"
+
+#: templates/image/crop_image.inc:107 templates/image/edit_image.inc:23
+#: templates/image/resize_image.inc:54
+msgid "Grayscale"
+msgstr "Graustufen"
+
+#: templates/gallery/gallery.inc:51
+msgid "Group By Date"
+msgstr "Nach Datum gruppieren"
+
+#: templates/group/header.inc:11 templates/view/list.inc:11
+msgid "Group by:"
+msgstr "Gruppieren nach:"
+
+#: config/prefs.php.dist:72
+msgid "Group galleries by"
+msgstr "Galerien gruppieren nach"
+
+#: lib/Exif/panasonic.php:133
+msgid "Halogen"
+msgstr "Halogen"
+
+#: lib/Exif/fujifilm.php:90 lib/Exif/fujifilm.php:91
+msgid "Hard"
+msgstr "Hart"
+
+#: lib/Exif.php:491
+msgid "Height"
+msgstr "Höhe"
+
+#: lib/Exif/panasonic.php:114 lib/Exif/panasonic.php:273
+#: lib/Exif/panasonic.php:284 lib/Exif/sanyo.php:76 lib/Exif/canon.php:182
+#: lib/Exif/canon.php:191 lib/Exif/canon.php:199
+msgid "High"
+msgstr "Hoch"
+
+#: lib/Exif/panasonic.php:217
+msgid "High Sensitivity"
+msgstr "Hohe Empfindlichkeit"
+
+#: lib/Exif/panasonic.php:302
+msgid "Horizontal (normal)"
+msgstr "Horizontal (normal)"
+
+#: lib/Forms/Watermark.php:48 config/prefs.php.dist:185
+msgid "Horizontal Alignment"
+msgstr "Horizontale Ausrichtung"
+
+#: faces/claim.php:53
+msgid "I know who is on one of your photos"
+msgstr "Ich weiß, wer auf einem Deiner Fotos zu sehen ist"
+
+#: lib/Exif.php:501
+msgid "ISO Setting"
+msgstr "ISO-Einstellung"
+
+#: lib/Exif.php:388
+msgid "ISO studio tungsten"
+msgstr "ISO-genormtes Studio-Kunstlicht"
+
+#: lib/Exif.php:1309
+msgid "Illegal size for IFD1"
+msgstr "Ungültige Größe für IFD1"
+
+#: lib/Exif.php:1356
+msgid "Illegal size for InteroperabilityIFD"
+msgstr "Ungültige Größe für InteroperabilityIFD"
+
+#: lib/Exif.php:1272
+msgid "Illegal size for SubIFD"
+msgstr "Ungültige Größe für SubIFD"
+
+#: lib/Exif/fujifilm.php:101 lib/Exif/nikon.php:148
+msgid "Incandescense"
+msgstr "Glühlampe"
+
+#: lib/Exif.php:268
+msgid "Inch"
+msgstr "Inch"
+
+#: lib/Ansel.php:2270
+msgid "Incomplete photo"
+msgstr "Unvollständiges Foto"
+
+#: protect.php:32
+msgid "Incorrect password"
+msgstr "Falsches Passwort"
+
+#: lib/Exif/panasonic.php:262
+msgid "Infinite"
+msgstr "Unendlich"
+
+#: lib/Exif/canon.php:299
+msgid "Internal Flash"
+msgstr "Interner Blitz"
+
+#: scripts/ansel.php:127
+#, php-format
+msgid "Invalid gallery \"%s\" specified."
+msgstr "Ungültige Galerie \"%s\" angegeben."
+
+#: xppublish.php:87 xppublish.php:174 lib/api.php:273
+msgid "Invalid gallery specified."
+msgstr "Ungültige Galerie angegeben."
+
+#: templates/image/crop_image.inc:149 lib/Exif.php:253
+#: lib/Exif/fujifilm.php:142 lib/Exif/canon.php:156
+msgid "Landscape"
+msgstr "Quer"
+
+#: lib/Forms/Watermark.php:39 lib/Exif/canon.php:146 config/prefs.php.dist:207
+msgid "Large"
+msgstr "Groß"
+
+#: lib/Block/my_galleries.php:53
+msgid "Last Modified"
+msgstr "Letzte Änderung"
+
+#: lib/Exif.php:514
+msgid "Latitude"
+msgstr "Breitengrad"
+
+#: templates/image/upload.inc:7
+msgid "Learn how to publish photos directly from Windows."
+msgstr "Anleitung um Fotos direkt aus Windows hochzuladen."
+
+#: lib/Forms/Watermark.php:45 lib/Exif/canon.php:254 lib/Exif/canon.php:365
+#: config/prefs.php.dist:182
+msgid "Left"
+msgstr "Links"
+
+#: lib/Exif.php:516
+msgid "Light source"
+msgstr "Lichtquelle"
+
+#: config/styles.php.dist:142 config/styles.php.dist:179
+msgid "Lightbox with Polaroids (No Background)"
+msgstr "Lightbox mit Polaroids (Kein Hintergrund)"
+
+#: lib/Widget/Links.php:15
+msgid "Links"
+msgstr "Links"
+
+#: scripts/ansel.php:192
+msgid "Listing Gallery/Name"
+msgstr "Galerieliste"
+
+#: scripts/ansel.php:176
+#, php-format
+msgid "Listing photos in %s"
+msgstr "Fotos in %s anzeigen"
+
+#: templates/image/upload.inc:30
+msgid "Loading ..."
+msgstr "Lade..."
+
+#: templates/faces/gallery.inc:28 templates/faces/gallery.inc:39
+#: lib/XRequest/EditFaces.php:35
+msgid "Loading..."
+msgstr "Lade..."
+
+#: lib/Exif/canon.php:244
+msgid "Locked (Pan Mode)"
+msgstr "Fest (Panoramamodus)"
+
+#: lib/Views/Image.php:67 lib/Views/Image.php:92 lib/Views/Gallery.php:111
+#: lib/Views/Gallery.php:133
+msgid "Locked galleries are not viewable via the api."
+msgstr "Gesperrte Galerien können nicht über die API betrachtet werden."
+
+#: scripts/recursive_import.php:88 scripts/ansel.php:118
+#: scripts/all_images_exif_to_tags.php:79
+#, php-format
+msgid "Logged in successfully as \"%s\"."
+msgstr "Erfolgreich als \"%s\" angemeldet."
+
+#: scripts/ansel.php:114
+msgid "Login is incorrect."
+msgstr "Anmeldedaten sind nicht korrekt."
+
+#: templates/xppublish/login.inc:2
+#, php-format
+msgid "Login to %s"
+msgstr "Anmelden bei %s"
+
+#: lib/Exif.php:515
+msgid "Longitude"
+msgstr "Längengrad"
+
+#: lib/Exif/panasonic.php:271 lib/Exif/panasonic.php:282
+#: lib/Exif/canon.php:183 lib/Exif/canon.php:192 lib/Exif/canon.php:200
+msgid "Low"
+msgstr "Niedrig"
+
+#: lib/Exif/panasonic.php:260
+msgid "Low/High Quality"
+msgstr "Niedrige/Hohe Qualität"
+
+#: lib/Exif/panasonic.php:197 lib/Exif/canon.php:87
+msgid "Macro"
+msgstr "Makro"
+
+#: lib/Exif/canon.php:164
+msgid "Macro/Close-Up"
+msgstr "Makro/Nahaufnahme"
+
+#: lib/Forms/Image.php:37 lib/Forms/Upload.php:51
+msgid "Make this the default photo for this gallery?"
+msgstr "Soll dies das Standardfoto für diese Galerie sein?"
+
+#: scripts/recursive_import.php:223 scripts/ansel.php:320
+#: scripts/remote_import.php:342 scripts/all_images_exif_to_tags.php:118
+msgid ""
+"Mandatory arguments to long options are mandatory for short options too."
+msgstr ""
+"Pflichtwerte für lange Parameter sind auch für kurze Parameter verpflichtend."
+
+#: templates/image/crop_image.inc:138 lib/Exif.php:246 lib/Exif.php:396
+#: lib/Exif/fujifilm.php:131 lib/Exif/panasonic.php:135
+#: lib/Exif/panasonic.php:141 lib/Exif/panasonic.php:150
+#: lib/Exif/panasonic.php:199 lib/Exif/nikon.php:182 lib/Exif/canon.php:155
+#: lib/Exif/canon.php:241 lib/Exif/canon.php:264
+msgid "Manual"
+msgstr "Manuell"
+
+#: lib/Exif/fujifilm.php:148
+msgid "Manual Exposure"
+msgstr "Manuelle Belichtung"
+
+#: lib/Exif/canon.php:133 lib/Exif/canon.php:136 lib/Exif/canon.php:250
+msgid "Manual Focus"
+msgstr "Manueller Fokus"
+
+#: lib/Exif.php:239
+msgid "Manual exposure"
+msgstr "Manuelle Belichtung"
+
+#: lib/Widget/ImageFaces.php:81
+msgid "Manual face selection"
+msgstr "Manuelle Gesichtsauswahl"
+
+#: lib/Forms/Upload.php:79
+msgid "Maximum file size:"
+msgstr "Maximale Dateigröße:"
+
+#: lib/Block/recent_faces.php:27
+msgid "Maximum number of faces"
+msgstr "Maximale Gesichteranzahl"
+
+#: lib/Block/my_galleries.php:25
+msgid "Maximum number of galleries"
+msgstr "Maximale Galerieanzahl"
+
+#: lib/Block/recently_added.php:33
+msgid "Maximum number of photos"
+msgstr "Maximale Anzahl an Fotos"
+
+#: lib/Block/gallery.php:35
+msgid "Maximum number of photos to display (0 means unlimited)"
+msgstr "Maximale Anzahl der anzuzeigenden Fotos (0 = keine Begrenzung)"
+
+#: lib/Forms/Image.php:51 lib/Forms/Upload.php:48
+msgid "Maximum photo size:"
+msgstr "Maximale Fotogröße:"
+
+#: lib/Forms/Upload.php:58
+#, php-format
+msgid "Maximum photo size: %s; with a total of: %s"
+msgstr "Maximale Fotogröße: %s; mit einer Gesamtgröße von: %s"
+
+#: lib/Forms/Watermark.php:38 lib/Exif/canon.php:147 config/prefs.php.dist:206
+msgid "Medium"
+msgstr "Mittel"
+
+#: lib/Exif.php:505
+msgid "Metering Mode"
+msgstr "Messmodus"
+
+#: templates/image/crop_image.inc:104 templates/image/edit_image.inc:20
+#: templates/image/resize_image.inc:51
+msgid "Mirror"
+msgstr "Spiegeln"
+
+#: lib/Exif.php:282
+#, php-format
+msgid "Mirrored"
+msgstr "Gespiegelt"
+
+#: lib/Views/Embedded.php:90
+msgid "Missing container parameter."
+msgstr "Container-Parameter fehlt."
+
+#: lib/Exif/panasonic.php:161
+msgid "Mode 1"
+msgstr "Modus 1"
+
+#: lib/Exif/panasonic.php:165
+msgid "Mode 2"
+msgstr "Modus 2"
+
+#: lib/Tile/Gallery.php:125
+msgid "Modified"
+msgstr "Geändert"
+
+#: gallery.php:164
+#, php-format
+msgid "Modifying: %s"
+msgstr "Bearbeite: %s"
+
+#: lib/Exif/nikon.php:126
+msgid "Monochrome"
+msgstr "Monochrom"
+
+#: faces/search/image_define.php:38
+msgid ""
+"More then one face found in photo. Please note that you can search only one "
+"face at a time."
+msgstr ""
+"Mehr als ein Gesicht im Foto gefunden. Bitte beachten Sie, dass immer nur "
+"ein Gesicht auf einmal gesucht werden kann."
+
+#: templates/view/gallerylightbox.inc:144 templates/view/results.inc:84
+#: lib/Views/GalleryRenderers/Gallery.php:125
+msgid "Move"
+msgstr "Verschiebe"
+
+#: image.php:627 lib/Views/Results.php:176
+#, php-format
+msgid "Moved %d photo from \"%s\" to \"%s\""
+msgid_plural "Moved %d photos from \"%s\" to \"%s\""
+msgstr[0] "%d Foto von \"%s\" nach \"%s\" verschoben"
+msgstr[1] "%d Fotos von \"%s\" nach \"%s\" verschoben"
+
+#: lib/Exif.php:361
+msgid "Multi-Segment"
+msgstr "Multi-Segment"
+
+#: lib/Exif.php:360
+msgid "Multi-Spot"
+msgstr "Multi-Spot"
+
+#: lib/Forms/Upload.php:55
+msgid "Multiple Photos"
+msgstr "Mehrere Fotos"
+
+#: templates/group/owner.inc:3 lib/Ansel.php:688 lib/Views/List.php:155
+#: lib/Block/my_galleries.php:3 lib/Block/my_galleries.php:37
+#: config/prefs.php.dist:59
+msgid "My Galleries"
+msgstr "Meine Galerien"
+
+#: browse_edit.php:28
+msgid "My Photos :: Add Content"
+msgstr "Meine Fotos :: Inhalt hinzufügen"
+
+#: templates/view/list.inc:19
+msgid "Name"
+msgstr "Name"
+
+#: faces/search/tabs.php:34 faces/search/named.php:17
+msgid "Named faces"
+msgstr "Gesichter mit Namen"
+
+#: lib/Exif/panasonic.php:315
+msgid "Natural"
+msgstr "Natürlich"
+
+#: templates/xppublish/new.inc:21
+msgid "New Gallery Description"
+msgstr "Neue Galerie-Beschreibung"
+
+#: templates/xppublish/new.inc:12
+msgid "New Gallery Title"
+msgstr "Neuer Galerietitel"
+
+#: templates/view/image.inc:93 templates/view/image.inc:94
+#: templates/view/image.inc:119 templates/view/image.inc:120
+#: templates/view/slideshow.inc:42 templates/view/slideshow.inc:43
+msgid "Next"
+msgstr "Weiter"
+
+#: lib/Exif/fujifilm.php:144 lib/Exif/canon.php:159
+msgid "Night"
+msgstr "Nachts"
+
+#: lib/Exif/panasonic.php:189
+msgid "Night Portrait"
+msgstr "Nachtporträt"
+
+#: lib/Exif/panasonic.php:211
+msgid "Night Scenery"
+msgstr "Landschaft bei Nacht"
+
+#: lib/Exif/panasonic.php:230 config/prefs.php.dist:217
+msgid "No"
+msgstr "Nein"
+
+#: lib/Exif.php:406 lib/Exif.php:413 lib/Exif.php:414 lib/Exif.php:418
+msgid "No Flash"
+msgstr "Kein Blitz"
+
+#: lib/Exif/fujifilm.php:157
+msgid "No Warning"
+msgstr "Keine Warnung"
+
+#: templates/faces/index.inc:14 templates/faces/search.inc:18
+#: templates/faces/faces.inc:10 faces/image.php:52 faces/search/image.php:76
+#: lib/XRequest/EditFaces.php:120 lib/Widget/GalleryFaces.php:56
+#: lib/Widget/ImageFaces.php:56 lib/Widget/ImageFaces.php:97
+msgid "No faces found"
+msgstr "Keine Gesichter gefunden"
+
+#: faces/search/image_define.php:40
+msgid "No faces found. Define you own."
+msgstr "Keine Gesichter gefunden. Markieren Sie welche manuell."
+
+#: xppublish.php:184
+msgid "No file specified"
+msgstr "Keine Datei angegeben"
+
+#: faces/gallery.php:24 lib/Views/Abstract.php:44
+msgid "No gallery specified"
+msgstr "Keine Galerie angegeben"
+
+#: templates/view/gallery.inc:119 templates/view/gallerylightbox.inc:134
+#: templates/view/results.inc:78 lib/Exif/nikon.php:155 lib/Exif/canon.php:172
+#: config/prefs.php.dist:71 config/prefs.php.dist:238
+#: config/prefs.php.dist:259
+msgid "None"
+msgstr "Keine"
+
+#: config/prefs.php.dist:249
+msgid "None (Owner only)"
+msgstr "Keine (Nur Besitzer)"
+
+#: templates/gallery/gallery.inc:50 lib/Exif/fujifilm.php:89
+#: lib/Exif/panasonic.php:181 lib/Exif/panasonic.php:313 lib/Exif/sanyo.php:72
+#: lib/Exif/sanyo.php:80 lib/Exif/nikon.php:130 lib/Exif/canon.php:88
+#: lib/Exif/canon.php:100 lib/Exif/canon.php:181 lib/Exif/canon.php:190
+#: lib/Exif/canon.php:198
+msgid "Normal"
+msgstr "Normal"
+
+#: lib/Exif.php:280
+#, php-format
+msgid "Normal (O deg)"
+msgstr "Normal (0°)"
+
+#: lib/Exif.php:247
+msgid "Normal Program"
+msgstr "Normalprogramm"
+
+#: lib/Exif.php:447
+msgid "Not defined"
+msgstr "Unbekannt"
+
+#: config/prefs.php.dist:97
+msgid "Number of faces per page"
+msgstr "Anzahl der Gesichter pro Seite"
+
+#: config/prefs.php.dist:105
+msgid "Number of groups per page"
+msgstr "Anzahl der Gruppen pro Seite"
+
+#: lib/Block/cloud.php:25
+msgid "Number of tags to display"
+msgstr "Anzahl der anzuzeigenden Tags"
+
+#: config/prefs.php.dist:89
+msgid "Number of tiles per page"
+msgstr "Anzahl der Elemente pro Seite"
+
+#: config/prefs.php.dist:81
+msgid "Number of tiles per row"
+msgstr "Anzahl der Elemente pro Zeile"
+
+#: lib/Exif/fujifilm.php:120 lib/Exif/fujifilm.php:125
+#: lib/Exif/fujifilm.php:135 lib/Exif/fujifilm.php:152
+#: lib/Exif/panasonic.php:163 lib/Exif/panasonic.php:174
+#: lib/Exif/panasonic.php:243 lib/Exif/panasonic.php:258
+#: lib/Exif/panasonic.php:291 lib/Exif/canon.php:94 lib/Exif/canon.php:108
+msgid "Off"
+msgstr "Aus"
+
+#: report.php:51
+msgid "Offensive content"
+msgstr "Anstößiger Inhalt"
+
+#: lib/Exif/fujifilm.php:119 lib/Exif/fujifilm.php:126
+#: lib/Exif/fujifilm.php:136 lib/Exif/fujifilm.php:153
+#: lib/Exif/panasonic.php:172 lib/Exif/canon.php:110
+msgid "On"
+msgstr "An"
+
+#: lib/Exif/canon.php:114
+msgid "On + Red Eye Reduction"
+msgstr "An + Rote-Augen-Reduzierung"
+
+#: lib/Exif/nikon.php:193
+msgid "On Camera"
+msgstr "Auf Kamera"
+
+#: lib/Exif.php:448
+msgid "One Chip Color Area Sensor"
+msgstr "One Chip Color Area Sensor"
+
+#: lib/Exif/canon.php:130
+msgid "One-Shot"
+msgstr "One-Shot"
+
+#: perms.php:65
+msgid ""
+"Only the owner or system administrator may change ownership or owner "
+"permissions for a share"
+msgstr ""
+"Nur der Besitzer oder der Systemadministrator kann den Besitzer oder die "
+"Besitzerrechte ändern"
+
+#: templates/image/crop_image.inc:145
+msgid "Orientation:"
+msgstr "Ausrichtung:"
+
+#: lib/Forms/Image.php:42 lib/Forms/ImageDate.php:32
+msgid "Original Date"
+msgstr "Orginaldatum"
+
+#: lib/Exif.php:363
+msgid "Other"
+msgstr "Sonstige"
+
+#: templates/tile/face.inc:24
+msgid "Others"
+msgstr "Weitere"
+
+#: lib/Exif/fujifilm.php:163
+msgid "Out of Focus"
+msgstr "Unscharf"
+
+#: lib/Exif/fujifilm.php:168
+msgid "Over Exposure"
+msgstr "Überbelichtung"
+
+#: templates/group/owner.inc:56
+#, php-format
+msgid "Owned by: %s"
+msgstr "Besitzer: %s"
+
+#: templates/view/list.inc:23 templates/tile/gallery.inc:11
+#: config/prefs.php.dist:69
+msgid "Owner"
+msgstr "Besitzer"
+
+#: lib/Exif/canon.php:165
+msgid "Pan Focus"
+msgstr "Schwenkfokus"
+
+#: lib/Exif/panasonic.php:201
+msgid "Panning"
+msgstr "Schwenken"
+
+#: lib/Exif.php:362 lib/Exif/canon.php:234
+msgid "Partial"
+msgstr "Teilbereich"
+
+#: lib/Exif/panasonic.php:207
+msgid "Party"
+msgstr "Party"
+
+#: protect.php:27 templates/xppublish/login.inc:14
+msgid "Password"
+msgstr "Passwort"
+
+#: templates/view/slideshow.inc:39
+msgid "Pause Slideshow"
+msgstr "Diashow Pause"
+
+#: lib/Widget/OwnerFaces.php:34
+#, php-format
+msgid "People in galleries of %s (%d of %d)"
+msgstr "Personen in Galerien von %s (%d von %d)"
+
+#: lib/Widget/GalleryFaces.php:33
+msgid "People in this gallery"
+msgstr "Personen in dieser Galerie"
+
+#: lib/Widget/ImageFaces.php:33
+msgid "People in this photo"
+msgstr "Personen in diesem Foto"
+
+#: config/prefs.php.dist:30
+msgid "Permission Options"
+msgstr "Rechte-Einstellungen"
+
+#: faces/claim.php:37 faces/claim.php:57
+msgid "Person"
+msgstr "Person"
+
+#: templates/faces/custom.inc:51
+msgid "Person in photo"
+msgstr "Person im Foto"
+
+#: lib/Exif/panasonic.php:221
+msgid "Pet"
+msgstr "Haustier"
+
+#: templates/faces/face.inc:30 templates/faces/gallery.inc:6
+#: lib/Views/GalleryRenderers/GalleryLightbox.php:129
+#: lib/Views/EmbeddedRenderers/Mini.php:148 lib/Block/recently_added.php:101
+msgid "Photo"
+msgstr "Foto"
+
+#: lib/Block/my_galleries.php:53
+msgid "Photo Count"
+msgstr "Anzahl Fotos"
+
+#: lib/Exif.php:485
+msgid "Photo Description"
+msgstr "Fotobeschreibung"
+
+#: browse.php:22
+msgid "Photo Galleries"
+msgstr "Fotogalerien"
+
+#: lib/Exif.php:484
+msgid "Photo Type"
+msgstr "Fototyp"
+
+#: templates/xppublish/javascript.inc:63
+msgid "Photo Upload"
+msgstr "Fotos hochladen"
+
+#: disclamer.php:32
+#, php-format
+msgid "Photo content may be offensive. You must be over %d to continue."
+msgstr ""
+"Der Inhalt könnte als anstößig empfunden werden. Sie müssen über %d sein, um "
+"fortzufahren."
+
+#: faces/search/image_save.php:32 faces/search/image.php:42
+msgid "Photo is too small. Search photo must be at least 50x50 pixels."
+msgstr ""
+"Das Foto ist zu klein. Suchfotos müssen mindestens 50x50 Pixel groß sein."
+
+#: lib/Ansel.php:3298
+msgid "Photo not found"
+msgstr "Foto nicht gefunden"
+
+#: image.php:139
+msgid "Photo not found."
+msgstr "Foto nicht gefunden."
+
+#: faces/search/image_search.php:43
+msgid "Photo search"
+msgstr "Fotosuche"
+
+#: rss.php:148
+#, php-format
+msgid "Photos by %s"
+msgstr "Fotos nach %s"
+
+#: templates/image/upload.inc:25
+msgid "Photos in this gallery"
+msgstr "Fotos in dieser Galerie"
+
+#: lib/Ansel.php:3336
+msgid "Photos not found"
+msgstr "Fotos nicht gefunden"
+
+#: rss.php:178 rss.php:184
+#, php-format
+msgid "Photos tagged with %s on %s"
+msgstr "Mit %s getaggte Fotos auf %s"
+
+#: lib/Exif.php:267
+msgid "Pixels"
+msgstr "Pixel"
+
+#: templates/view/image.inc:89 templates/view/image.inc:115
+#: templates/view/slideshow.inc:38
+msgid "Play"
+msgstr "Play"
+
+#: faces/report.php:36
+msgid ""
+"Please describe the reasons. For example, you don't want to be mentioned "
+"etc..."
+msgstr ""
+"Bitte nennen Sie die Gründe, z.B. dass Sie nicht namentlich erwähnt werden "
+"möchten o.ä."
+
+#: xppublish.php:54
+msgid "Please enter your username and password."
+msgstr "Bitte geben Sie Ihren Benutzernamen und Ihr Passwort an."
+
+#: faces/search/image.php:20
+msgid ""
+"Please upload photo with the face to search for. You can search only one "
+"face per time."
+msgstr ""
+"Bitte laden Sie ein Foto hoch mit dem Gesicht, nach dem Sie suchen möchten. "
+"Sie können immer nur nach einem Gesicht auf einmal suchen."
+
+#: config/styles.php.dist:94
+msgid "Polaroid Style Thumbnails (White Background)"
+msgstr "Polaroid-Vorschau (Weißer Hintergrund)"
+
+#: templates/image/crop_image.inc:150 lib/Exif.php:252
+#: lib/Exif/fujifilm.php:141 lib/Exif/panasonic.php:183 lib/Exif/canon.php:162
+msgid "Portrait"
+msgstr "Hoch"
+
+#: lib/Exif/nikon.php:146
+msgid "Preset"
+msgstr "Voreingestellt"
+
+#: xppublish.php:99 xppublish.php:149
+msgid "Press the \"Back\" button and try again."
+msgstr "Drücken Sie den \"Zurück\"-Knopf und versuchen Sie es noch einmal."
+
+#: config/styles.php.dist:64
+msgid "Pretty Thumbnails (No Background)"
+msgstr "Hübsche Vorschau (Kein Hintergrund)"
+
+#: config/styles.php.dist:75
+msgid "Pretty Thumbnails (White Background)"
+msgstr "Hübsche Vorschau (Weißer Hintergrund)"
+
+#: templates/image/crop_image.inc:165
+msgid "Preview"
+msgstr "Vorschau"
+
+#: image.php:511 image.php:715
+#, php-format
+msgid "Preview changes for %s :: %s"
+msgstr "Vorschau der Änderungen für %s :: %s"
+
+#: templates/view/image.inc:91 templates/view/image.inc:92
+#: templates/view/image.inc:117 templates/view/image.inc:118
+#: templates/view/slideshow.inc:40 templates/view/slideshow.inc:41
+msgid "Previous"
+msgstr "Zurück"
+
+#: lib/Exif/panasonic.php:191 lib/Exif/canon.php:261
+msgid "Program"
+msgstr "Programm"
+
+#: lib/Exif/fujifilm.php:145
+msgid "Program AE"
+msgstr "Programm AE"
+
+#: templates/view/image.inc:56 templates/view/slideshow.inc:17
+#: templates/tile/image.inc:17
+msgid "Properties"
+msgstr "Eigenschaften"
+
+#: templates/xppublish/list.inc:7
+msgid "Publish Photos to:"
+msgstr "Fotos Veröffentlichen in:"
+
+#: xppublish.php:35
+#, php-format
+msgid "Publish to %s"
+msgstr "Veröffentlichen in %s"
+
+#: xppublish.php:24
+#, php-format
+msgid "Publish your photos to %s on %s."
+msgstr "Veröffentlichen Sie Ihre Fotos in %s auf %s."
+
+#: lib/Exif/panasonic.php:120
+msgid "RAW"
+msgstr "RAW"
+
+#: lib/Block/gallery.php:63
+msgid "Random Gallery"
+msgstr "Zufällige Galerie"
+
+#: lib/Block/gallery.php:33
+msgid "Random gallery"
+msgstr "Zufällige Galerie"
+
+#: lib/Block/random_photo.php:3 lib/Block/random_photo.php:26
+msgid "Random photo"
+msgstr "Zufälliges Foto"
+
+#: config/prefs.php.dist:240 config/prefs.php.dist:261
+msgid "Read and write"
+msgstr "Lesen und Schreiben"
+
+#: config/prefs.php.dist:262
+msgid "Read, write, and delete"
+msgstr "Lesen, Schreiben und Löschen"
+
+#: config/prefs.php.dist:239 config/prefs.php.dist:250
+#: config/prefs.php.dist:260
+msgid "Read-only"
+msgstr "Nur Lesen"
+
+#: templates/gallery/delete_confirmation.inc:12
+msgid "Really Delete"
+msgstr "Wirklich löschen"
+
+#: templates/gallery/delete_confirmation.inc:12
+msgid "Really Empty"
+msgstr "Wirklich löschen"
+
+#: templates/gallery/delete_confirmation.inc:3
+#, php-format
+msgid "Really delete %s and all of its subgalleries?"
+msgstr "%s und alle Untergalerien wirklich löschen?"
+
+#: templates/gallery/delete_confirmation.inc:7
+msgid ""
+"Really delete this gallery and all of the photos and subgalleries in it? "
+"This action cannot be undone."
+msgstr ""
+"Diese Galerie und alle enthaltenen Fotos und Untergalerien wirklich löschen? "
+"Dieser Vorgang kann nicht rückgängig gemacht werden."
+
+#: templates/gallery/delete_confirmation.inc:3
+#, php-format
+msgid "Really empty %s?"
+msgstr "%s wirklich löschen?"
+
+#: templates/gallery/delete_confirmation.inc:7
+msgid ""
+"Really empty this gallery and all of the photos in it? This action cannot be "
+"undone."
+msgstr ""
+"Diese Galerie wirklich leeren und alle enthaltenen Fotos löschen? Dieser "
+"Vorgang kann nicht rückgängig gemacht werden."
+
+#: faces/report.php:36 faces/report.php:59
+msgid "Reason"
+msgstr "Grund"
+
+#: lib/Block/recent_comments.php:71
+#, php-format
+msgid "Recent Comments In %s"
+msgstr "Neuste Kommentare in %s"
+
+#: lib/Block/recent_comments.php:5
+msgid "Recent Photo Comments"
+msgstr "Neueste Fotokommentare"
+
+#: lib/Block/recent_faces.php:3 lib/Block/recent_faces.php:34
+msgid "Recent faces"
+msgstr "Neueste Gesichter"
+
+#: lib/Widget/Links.php:25
+#, php-format
+msgid "Recent photos by %s"
+msgstr "Neuste Fotos von %s"
+
+#: lib/Widget/Links.php:27
+#, php-format
+msgid "Recent photos in %s"
+msgstr "Neuste Fotos in %s"
+
+#: lib/Block/recently_added.php:3
+msgid "Recently Added Photos"
+msgstr "Neueste Fotos"
+
+#: lib/Block/recently_added.php:77
+#, php-format
+msgid "Recently Added Photos From %s"
+msgstr "Neueste Fotos in %s"
+
+#: rss.php:153
+#, php-format
+msgid "Recently added photos by %s on %s"
+msgstr "Neueste Fotos von %s auf %s"
+
+#: rss.php:57 rss.php:62
+#, php-format
+msgid "Recently added photos on %s"
+msgstr "Neueste Fotos auf %s"
+
+#: lib/Exif.php:419
+msgid "Red Eye"
+msgstr "Rote Augen"
+
+#: lib/Exif/canon.php:111
+msgid "Red Eye Reduction"
+msgstr "Rote-Augen-Reduzierung"
+
+#: lib/Exif.php:425
+msgid "Red Eye, Auto-Mode"
+msgstr "Rote Augen, Automatischer Modus"
+
+#: lib/Exif.php:427
+msgid "Red Eye, Auto-Mode, Return light detected"
+msgstr "Rote Augen, Automatischer Modus, Reflektion gemessen"
+
+#: lib/Exif.php:426
+msgid "Red Eye, Auto-Mode, Return light not detected"
+msgstr "Rote Augen, Automatischer Modus, Reflektion nicht gemessen"
+
+#: lib/Exif.php:422
+msgid "Red Eye, Compulsory Flash"
+msgstr "Rote Augen"
+
+#: lib/Exif.php:424
+msgid "Red Eye, Compulsory Flash, Return light detected"
+msgstr "Rote Augen, erzwungener Blitz, Reflektion gemessen"
+
+#: lib/Exif.php:423
+msgid "Red Eye, Compulsory Flash, Return light not detected"
+msgstr "Rote Augen, erzwungener Blitz, Reflektion nicht gemessen"
+
+#: lib/Exif.php:421
+msgid "Red Eye, Return light detected"
+msgstr "Rote Augen, Reflektion gemessen"
+
+#: lib/Exif.php:420
+msgid "Red Eye, Return light not detected"
+msgstr "Rote Augen, Reflektion nicht gemessen"
+
+#: lib/Exif/fujifilm.php:121
+msgid "Red-Eye Reduction"
+msgstr "Rote-Augen-Reduzierung"
+
+#: templates/group/header.inc:8 templates/view/list.inc:5
+#: templates/list/header.inc:5
+msgid "Refresh List"
+msgstr "Liste aktualisieren"
+
+#: lib/Widget/Actions.php:153
+msgid "Regenerate all photo views"
+msgstr "Fotoansichten neu erstellen"
+
+#: templates/faces/gallery.inc:19
+msgid "Reget faces for this photo"
+msgstr "Gesichter für dieses Foto neu erkennen"
+
+#: templates/view/results.inc:128
+msgid "Related Tags"
+msgstr "Ähnliche Tags"
+
+#: lib/Tags.php:601
+#, php-format
+msgid "Remove %s from search"
+msgstr "%s aus der Suche entfernen"
+
+#: lib/XRequest/TagActions.php:115 lib/Widget/Tags.php:93
+msgid "Remove Tag"
+msgstr "Tag löschen"
+
+#: lib/Forms/Image.php:50
+msgid "Replace photo with this file"
+msgstr "Foto durch diese Datei ersetzen"
+
+#: report.php:47 report.php:66 templates/faces/face.inc:9
+#: templates/faces/search.inc:10 templates/view/image.inc:78
+#: templates/tile/face.inc:30 lib/Widget/Actions.php:175
+msgid "Report"
+msgstr "Melden"
+
+#: templates/faces/face.inc:9 templates/faces/search.inc:10
+#: templates/tile/face.inc:30
+msgid "Report abuse or submit a removal request"
+msgstr "Mißbrauch melden oder Entfernung beantragen"
+
+#: lib/Report.php:109
+msgid "Report by user"
+msgstr "Meldung durch Benutzer"
+
+#: lib/Report.php:40
+msgid "Report driver does not exist."
+msgstr "Der Bericht-Treiber existiert nicht."
+
+#: faces/report.php:31
+msgid "Report face"
+msgstr "Gesicht melden"
+
+#: report.php:61 report.php:74
+msgid "Report reason"
+msgstr "Meldegrund"
+
+#: report.php:60 report.php:73
+msgid "Report type"
+msgstr "Meldetyp"
+
+#: templates/image/crop_image.inc:166 templates/image/resize_image.inc:77
+#: templates/faces/custom.inc:54 templates/faces/define.inc:39
+msgid "Reset"
+msgstr "Zurücksetzen"
+
+#: lib/Widget/Actions.php:150
+msgid "Reset all thumbnails"
+msgstr "Vorschauen zurücksetzen"
+
+#: lib/Widget/Actions.php:163
+msgid "Reset default photo"
+msgstr "Standardfoto zurücksetzen"
+
+#: templates/image/crop_image.inc:115 templates/image/edit_image.inc:32
+#: templates/image/resize_image.inc:64
+msgid "Resize"
+msgstr "Größe ändern"
+
+#: lib/Exif.php:494
+msgid "Resolution Unit"
+msgstr "Auflösungseinheit"
+
+#: lib/Forms/Watermark.php:47 lib/Exif/canon.php:252 lib/Exif/canon.php:363
+#: config/prefs.php.dist:184
+msgid "Right"
+msgstr "Rechts"
+
+#: templates/image/crop_image.inc:97 templates/image/edit_image.inc:13
+#: templates/image/resize_image.inc:44
+msgid "Rotate 180"
+msgstr "Um 180° drehen"
+
+#: lib/Exif/panasonic.php:306
+msgid "Rotate 270 CW"
+msgstr "270° im Uhrzeigersinn gedreht"
+
+#: lib/Exif/panasonic.php:304
+msgid "Rotate 90 CW"
+msgstr "90° im Uhrzeigersinn gedreht"
+
+#: templates/image/crop_image.inc:96 templates/image/edit_image.inc:12
+#: templates/image/resize_image.inc:43
+msgid "Rotate Left"
+msgstr "Nach links drehen"
+
+#: templates/image/crop_image.inc:98 templates/image/edit_image.inc:14
+#: templates/image/resize_image.inc:45
+msgid "Rotate Right"
+msgstr "Nach rechts drehen"
+
+#: lib/Exif/nikon.php:119
+msgid "SXGA Basic"
+msgstr "SXGA Basis"
+
+#: lib/Exif/nikon.php:121
+msgid "SXGA Fine"
+msgstr "SXGA Fein"
+
+#: lib/Exif/nikon.php:120
+msgid "SXGA Normal"
+msgstr "SXGA Normal"
+
+#: templates/image/resize_image.inc:76 templates/faces/custom.inc:52
+#: lib/Forms/Image.php:26 lib/Forms/ImageDate.php:26
+#: lib/Forms/Watermark.php:26
+msgid "Save"
+msgstr "Speichern"
+
+#: templates/captions/captions.inc:50
+msgid "Save Captions"
+msgstr "Fotounterschriften speichern"
+
+#: templates/image/preview_cropimage.inc:18
+#: templates/image/preview_cropimage.inc:19
+#: templates/image/preview_image.inc:21 templates/image/preview_image.inc:22
+msgid "Save Change"
+msgstr "Änderungen speichern"
+
+#: templates/gallery/gallery.inc:125
+msgid "Save Gallery"
+msgstr "Galerie speichern"
+
+#: image.php:174
+msgid "Save Photo"
+msgstr "Foto Speichern"
+
+#: templates/faces/image.inc:5
+msgid "Save face name"
+msgstr "Gesichtername speichern"
+
+#: lib/Exif/panasonic.php:185
+msgid "Scenery"
+msgstr "Landschaft"
+
+#: templates/faces/define.inc:37 faces/search/name.php:21
+msgid "Search"
+msgstr "Suche"
+
+#: faces/search/tabs.php:35 faces/search/name.php:47
+msgid "Search by name"
+msgstr "Suche nach Name"
+
+#: faces/search/tabs.php:37
+msgid "Search by photo"
+msgstr "Suche nach Foto"
+
+#: lib/Views/Results.php:86
+#, php-format
+msgid "Searching %s's photos tagged: "
+msgstr "Suche Fotos von %s getaggt mit: "
+
+#: lib/Views/Results.php:87
+msgid "Searching all photos tagged: "
+msgstr "Suche alle Fotos getaggt mit: "
+
+#: faces/gallery.php:56
+#, php-format
+msgid "Searching for faces in %s"
+msgstr "Nach Gesichtern in %s suchen"
+
+#: templates/view/gallerylightbox.inc:133 templates/view/results.inc:77
+msgid "Select All"
+msgstr "Alle auswählen"
+
+#: templates/view/gallerylightbox.inc:134 templates/view/results.inc:78
+msgid "Select None"
+msgstr "Keine auswählen"
+
+#: templates/tile/image.inc:5
+msgid "Select for Action"
+msgstr "Für eine Aktion auswählen"
+
+#: templates/view/gallery.inc:115
+msgid "Select:"
+msgstr "Auswahl:"
+
+#: templates/view/gallerylightbox.inc:132 templates/view/results.inc:76
+#, php-format
+msgid "Select: %s, %s"
+msgstr "Auswahl: %s, %s"
+
+#: templates/view/gallery.inc:123 templates/view/gallerylightbox.inc:149
+#: templates/view/results.inc:89
+msgid "Selected photos to"
+msgstr "ausgewählte Fotos nach"
+
+#: lib/Forms/Ecard.php:25
+msgid "Send"
+msgstr "Senden"
+
+#: img/ecard.php:102
+#, php-format
+msgid "Send Ecard :: %s"
+msgstr "Grußkarte verschicken :: %s"
+
+#: templates/view/image.inc:63 templates/view/slideshow.inc:27
+msgid "Send an Ecard"
+msgstr "Grußkarte verschicken"
+
+#: lib/Forms/Ecard.php:41
+msgid "Send ecard to the following address:"
+msgstr "Grußkarte an folgende Adressen verschicken:"
+
+#: lib/Exif.php:509
+msgid "Sensing Method"
+msgstr "Sensortyp"
+
+#: templates/gallery/gallery.inc:79 lib/Forms/Upload.php:53
+msgid "Separate tags with commas."
+msgstr "Trennen Sie mehrere Tags durch Kommas."
+
+#: lib/Exif/panasonic.php:251 lib/Exif/canon.php:161
+msgid "Sepia"
+msgstr "Sepia"
+
+#: lib/Widget/Actions.php:140
+msgid "Set captions"
+msgstr "Fotounterschriften setzen"
+
+#: lib/Widget/Actions.php:168
+msgid "Set permissions"
+msgstr "Rechte festlegen"
+
+#: lib/Exif.php:376
+msgid "Shade"
+msgstr "Schatten"
+
+#: config/styles.php.dist:84
+msgid "Shadowed Thumbnails (White Background)"
+msgstr "Vorschau mit Schatten (Weißer Hintergrund)"
+
+#: config/prefs.php.dist:113
+msgid "Show EXIF data"
+msgstr "EXIF-Daten anzeigen"
+
+#: lib/Exif.php:249 lib/Exif/fujifilm.php:147 lib/Exif/panasonic.php:195
+msgid "Shutter Priority"
+msgstr "Verschlussprogramm"
+
+#: lib/Exif.php:495
+msgid "Shutter Speed"
+msgstr "Belichtungszeit"
+
+#: lib/Widget/SimilarPhotos.php:27
+msgid "Similar Photos"
+msgstr "Ähnliche Fotos"
+
+#: templates/faces/face.inc:14 templates/tile/face.inc:34
+msgid "Similar faces"
+msgstr "Ähnliche Gesichter"
+
+#: lib/Exif/panasonic.php:203 config/styles.php.dist:158
+msgid "Simple"
+msgstr "Einfach"
+
+#: config/styles.php.dist:167
+msgid "Simple Lightbox"
+msgstr "Lightbox einfach"
+
+#: lib/Exif/canon.php:134 lib/Exif/canon.php:319
+msgid "Single"
+msgstr "Einzelmessung"
+
+#: lib/Forms/Upload.php:43
+msgid "Single Photo"
+msgstr "Einzelnes Foto"
+
+#: lib/Exif/canon.php:121
+msgid "Single/Timer"
+msgstr "Einzeln/Timer"
+
+#: lib/Exif/canon.php:158
+msgid "Slow Shutter"
+msgstr "Langsamer Verschluss"
+
+#: lib/Exif/canon.php:112
+msgid "Slow Synchro"
+msgstr "Langsame Synchronisierung"
+
+#: templates/gallery/gallery.inc:73
+msgid "Slug names may contain only letters, numbers, @, or _ (underscore)."
+msgstr ""
+"Kurznamen dürfen nur Buchstaben, Zahlen, @ oder _ (Unterstrich) enthalten."
+
+#: templates/gallery/gallery.inc:72
+msgid "Slugs allows direct access to this gallery by visiting:"
+msgstr "Kurznamen ermöglichen den direkten Zugriff auf diese Galerie über:"
+
+#: lib/Forms/Watermark.php:37 lib/Exif/canon.php:148 config/prefs.php.dist:205
+msgid "Small"
+msgstr "Klein"
+
+#: lib/Exif/panasonic.php:209
+msgid "Snow"
+msgstr "Schnee"
+
+#: lib/Exif/fujifilm.php:87 lib/Exif/fujifilm.php:88
+msgid "Soft"
+msgstr "Weich"
+
+#: templates/view/list.inc:17
+msgid "Sort by:"
+msgstr "Sortieren nach:"
+
+#: lib/Widget/Actions.php:146
+msgid "Sort photos"
+msgstr "Fotos sortieren"
+
+#: lib/Exif/nikon.php:151
+msgid "SpeedLight"
+msgstr "SpeedLight"
+
+#: lib/Exif/canon.php:163
+msgid "Sport"
+msgstr "Sport"
+
+#: lib/Exif/fujifilm.php:143 lib/Exif/panasonic.php:187
+msgid "Sports"
+msgstr "Sport"
+
+#: lib/Exif.php:359
+msgid "Spot"
+msgstr "Spot"
+
+#: lib/Exif/panasonic.php:82
+msgid "Spot-focusing"
+msgstr "Spotfokus"
+
+#: lib/Exif/panasonic.php:116 lib/Exif/panasonic.php:269
+#: lib/Exif/panasonic.php:280
+msgid "Standard"
+msgstr "Standard"
+
+#: lib/Exif.php:381
+msgid "Standard light A"
+msgstr "Standardlicht A"
+
+#: lib/Exif.php:382
+msgid "Standard light B"
+msgstr "Standardlicht B"
+
+#: lib/Exif.php:383
+msgid "Standard light C"
+msgstr "Standardlicht C"
+
+#: templates/view/image.inc:88 templates/view/image.inc:115
+#: templates/view/slideshow.inc:38 lib/Widget/Actions.php:47
+msgid "Start Slideshow"
+msgstr "Diashow starten"
+
+#: scripts/recursive_import.php:190 scripts/ansel.php:212
+#: scripts/ansel.php:295 scripts/remote_import.php:251
+#, php-format
+msgid "Storing photo \"%s\"..."
+msgstr "Speichere Foto \"%s\"..."
+
+#: templates/gallery/gallery.inc:40
+msgid "Style for this gallery"
+msgstr "Stil für diese Galerie"
+
+#: scripts/recursive_import.php:200 scripts/ansel.php:235
+#: scripts/remote_import.php:271
+#, php-format
+msgid "Successfully added %d photo (%s) to gallery \"%s\" from \"%s\"."
+msgid_plural "Successfully added %d photos (%s) to gallery \"%s\" from \"%s\"."
+msgstr[0] ""
+"%d Foto (%s) wurde erfolgreich aus \"%4$s\" zur Galerie \"%3$s\" hinzugefügt."
+msgstr[1] ""
+"%d Fotos (%s) wurden erfolgreich aus \"%4$s\" zur Galerie \"%3$s\" "
+"hinzugefügt."
+
+#: scripts/ansel.php:221
+#, php-format
+msgid "Successfully added photo \"%s\" to gallery \"%s\"."
+msgstr "Foto \"%s\" wurde erfolgreich zu Galerie \"%s\" hinzugefügt."
+
+#: gallery/delete.php:39
+#, php-format
+msgid "Successfully deleted %s."
+msgstr "%s wurde erfolgreich gelöscht."
+
+#: gallery/delete.php:62
+#, php-format
+msgid "Successfully emptied \"%s\""
+msgstr "\"%s\" wurde erfolgreich geleert"
+
+#: edit_dates.php:76
+#, php-format
+msgid "Successfully modified the date on %d photos."
+msgstr "Das Datum von %d Fotos wurde erfolgreich aktualisiert."
+
+#: lib/Exif/canon.php:347
+msgid "Sunny"
+msgstr "Sonnig"
+
+#: lib/Exif/canon.php:102
+msgid "Superfine"
+msgstr "Sehr fein"
+
+#: lib/Block/cloud.php:3 lib/Block/cloud.php:33
+msgid "Tag Cloud"
+msgstr "Tagwolke"
+
+#: lib/Widget/Tags.php:21 lib/Forms/Image.php:46 lib/Forms/Upload.php:53
+msgid "Tags"
+msgstr "Tags"
+
+#: config/prefs.php.dist:23
+msgid "Tags and EXIF Options"
+msgstr "Tag- und Exif-Einstellungen"
+
+#: templates/tile/face.inc:27 faces/claim.php:32
+msgid "Tell us who is in this photo"
+msgstr "Wer ist auf dem Foto zu sehen?"
+
+#: report.php:50
+msgid "Terms and conditions infringement"
+msgstr "AGB-Verletzung"
+
+#: scripts/ansel.php:262
+#, php-format
+msgid "The directory \"%s\" doesn't exist."
+msgstr "Das Verzeichnis \"%s\" existiert nicht."
+
+#: scripts/ansel.php:240
+#, php-format
+msgid "The directory \"%s\" had no valid photos."
+msgstr "Das Verzeichnis \"%s\" enthält keine gültigen Fotos."
+
+#: scripts/ansel.php:279
+#, php-format
+msgid "The directory \"%s\" is empty."
+msgstr "Das Verzeichnis \"%s\" ist leer."
+
+#: scripts/remote_import.php:297 lib/Ansel.php:517
+#, php-format
+msgid "The file \"%s\" doesn't exist."
+msgstr "Die Datei \"%s\" existiert nicht."
+
+#: lib/Exif.php:1084
+msgid "The file could not be found."
+msgstr "Die Datei konnte nicht gefunden werden."
+
+#: image.php:210 lib/api.php:504 img/upload.php:180
+msgid "The file you uploaded does not appear to be a valid photo."
+msgstr ""
+"Die Datei, die Sie hochgeladen haben, scheint kein gültiges Foto zu sein."
+
+#: lib/Ansel.php:2979
+#, php-format
+msgid "The gallery \"%s\" could not be created: %s"
+msgstr "Die Galerie \"%s\" konnte nicht erstellt werden: %s"
+
+#: gallery.php:321 xppublish.php:123 scripts/recursive_import.php:157
+#: scripts/ansel.php:156 scripts/remote_import.php:210
+#, php-format
+msgid "The gallery \"%s\" couldn't be created: %s"
+msgstr "Die Galerie \"%s\" konnte nicht erstellt werden: %s"
+
+#: gallery.php:327 xppublish.php:127 scripts/recursive_import.php:159
+#: scripts/ansel.php:161 scripts/remote_import.php:212
+#, php-format
+msgid "The gallery \"%s\" was created successfully."
+msgstr "Die Galerie \"%s\" wurde erfolgreich erstellt."
+
+#: gallery.php:270
+msgid "The gallery was saved."
+msgstr "Die Galerie wurde gespeichert."
+
+#: gallery.php:383
+msgid "The gallery's default photo has successfully been reset."
+msgstr "Das Standardfoto dieser Galerie wurde erfolgreich zurückgesetzt."
+
+#: gallery.php:400
+msgid "The gallery's thumbnails have successfully been reset."
+msgstr "Die Vorschauen dieser Galerie wurde erfolgreich zurückgesetzt."
+
+#: gallery.php:423
+msgid "The gallery's views have successfully been reset."
+msgstr "Die Ansichten dieser Galerie wurden erfolgreich zurückgesetzt."
+
+#: faces/report.php:68
+msgid "The owner of the photo was notified."
+msgstr "Der Besitzer des Fotos wurde benachrichtigt."
+
+#: faces/claim.php:66
+msgid "The owner of the photo, who will delegate the face name, was notified."
+msgstr ""
+"Der Besitzer des Fotos, der den Gesichternamen weitergeben wird, wurde "
+"benachrichtigt."
+
+#: lib/Ansel.php:2942
+#, php-format
+msgid "The slug \"%s\" already exists."
+msgstr "Der Kurzname \"%s\" existiert bereits."
+
+#: img/upload.php:208
+msgid ""
+"The uploaded file appears to be empty. It may not exist on your computer."
+msgstr ""
+"Die hochgeladene Datei scheint leer zu sein, eventuell existiert sie nicht "
+"auf Ihrem Computer."
+
+#: lib/Views/List.php:197 lib/Block/random_photo.php:35
+msgid "There are no photo galleries available."
+msgstr "Es sind keine Fotogalerien verfügbar."
+
+#: gallery.php:116
+#, php-format
+msgid "There are no photos in %s to download."
+msgstr "In %s befinden sich keine Fotos zum Herunterladen."
+
+#: templates/captions/captions.inc:52 templates/view/gallery.inc:127
+#: templates/view/gallerylightbox.inc:153 templates/view/results.inc:99
+msgid "There are no photos in this gallery."
+msgstr "In dieser Galerie befinden sich keine Fotos."
+
+#: scripts/ansel.php:298
+#, php-format
+msgid "There was a problem adding the photo \"%s\" to gallery \"%s\"."
+msgstr ""
+"Beim Hinzufügen des Fotos \"%s\" zur Galerie \"%s\" ist ein Problem "
+"aufgetreten."
+
+#: scripts/ansel.php:215
+#, php-format
+msgid "There was a problem adding the photo \"%s\" to gallery \"%s\": %s"
+msgstr ""
+"Beim Hinzufügen des Fotos \"%s\" zur Galerie \"%s\" ist ein Problem "
+"aufgetreten: %s"
+
+#: gallery/delete.php:34 lib/api.php:679
+#, php-format
+msgid "There was a problem deleting %s: %s"
+msgstr "Beim Löschen von %s ist ein Problem aufgetreten: %s"
+
+#: image.php:578 lib/Views/Results.php:131
+#, php-format
+msgid "There was a problem deleting photos: %s"
+msgstr "Beim Löschen der Fotos ist ein Problem aufgetreten: %s"
+
+#: img/upload.php:105 img/upload.php:166 img/upload.php:190
+#, php-format
+msgid "There was a problem saving the photo: %s"
+msgstr "Beim Speichern des Fotos ist ein Problem aufgetreten: %s"
+
+#: xppublish.php:195
+msgid "There was a problem uploading the photo."
+msgstr "Beim Hochladen des Fotos ist ein Fehler aufgetreten."
+
+#: img/upload.php:206
+#, php-format
+msgid "There was a problem uploading the photo: %s"
+msgstr "Beim Hochladen des Fotos ist ein Fehler aufgetreten: %s"
+
+#: xppublish.php:91
+msgid "There was an error accessing the gallery"
+msgstr "Beim Zugriff auf die Galerie ist ein Fehler aufgetreten"
+
+#: gallery/sort.php:27
+msgid "There was an error accessing the gallery."
+msgstr "Beim Zugriff auf die Galerie ist ein Fehler aufgetreten."
+
+#: edit_dates.php:69
+#, php-format
+msgid "There was an error editing the dates: %s"
+msgstr "Beim Bearbeiten der Daten ist ein Fehler aufgetreten: %s"
+
+#: img/upload.php:59 img/upload.php:87 img/upload.php:121 img/upload.php:148
+#, php-format
+msgid "There was an error processing the uploaded archive: %s"
+msgstr ""
+"Bei der Verabeitung des hochgeladenen Archivs ist ein Fehler aufgetreten: %s"
+
+#: image.php:226
+msgid "There was an error replacing the photo."
+msgstr "Beim Ersetzen des Fotos is ist ein Fehler aufgetreten."
+
+#: img/ecard.php:95
+#, php-format
+msgid "There was an error sending your message: %s"
+msgstr "Beim Versenden der Nachricht ist ein Fehler aufgetreten: %s"
+
+#: scripts/remote_import.php:260
+#, php-format
+msgid "There was an unspecified error. The server returned: %s"
+msgstr "Ein unbekannter Fehler ist aufgetreten. Meldung des Servers: %s"
+
+#: protect.php:24
+msgid "This gallery is protected by a password. Please enter it below."
+msgstr ""
+"Diese Galerie ist durch ein Passwort geschützt. Bitte geben Sie dieses unten "
+"an."
+
+#: lib/ImageView.php:89
+#, php-format
+msgid ""
+"This install does not support the %s feature. Please contact your "
+"administrator."
+msgstr ""
+"Diese Installation unterstützt die Funktion \"%s\" nicht, bitte wenden Sie "
+"sich an Ihren Administrator."
+
+#: lib/Exif.php:450
+msgid "Three Chip Color Area Sensor"
+msgstr "Three Chip Color Area Sensor"
+
+#: lib/Forms/Watermark.php:36 config/prefs.php.dist:204
+msgid "Tiny"
+msgstr "Winzig"
+
+#: lib/Forms/Watermark.php:52 config/prefs.php.dist:193
+msgid "Top"
+msgstr "Oben"
+
+#: templates/gallery/gallery.inc:20
+msgid "Top Level Gallery"
+msgstr "Oberste Galerie"
+
+#: lib/Exif.php:452
+msgid "Trilinear Sensor"
+msgstr "Trilinear Sensor"
+
+#: lib/Exif.php:372 lib/Exif/canon.php:349
+msgid "Tungsten"
+msgstr "Glühlampen"
+
+#: lib/Exif/canon.php:262
+msgid "Tv"
+msgstr "Tv"
+
+#: lib/Exif.php:449
+msgid "Two Chip Color Area Sensor"
+msgstr "Two Chip Color Area Sensor"
+
+#: lib/Widget.php:75 lib/ImageView.php:96 lib/Search.php:57 lib/api.php:1208
+#, php-format
+msgid "Unable to load the definition of %s."
+msgstr "Der %s-Treiber konnte nicht geladen werden."
+
+#: rss.php:37
+msgid "Unable to retrieve requested feed"
+msgstr "Der gewünschte Feed konnte nicht gelesen werden"
+
+#: lib/Exif.php:460
+msgid "Uncalibrated"
+msgstr "Nicht kalibriert"
+
+#: lib/Exif/panasonic.php:219
+msgid "Underwater"
+msgstr "Unterwasser"
+
+#: templates/gallery/gallery.inc:126
+msgid "Undo Changes"
+msgstr "Änderungen rückgängig machen"
+
+#: templates/group/category.inc:7 lib/Views/List.php:175
+msgid "Unfiled"
+msgstr "Nicht zugeordnet"
+
+#: lib/Exif.php:241 lib/Exif.php:254 lib/Exif.php:270 lib/Exif.php:356
+#: lib/Exif.php:390 lib/Exif.php:397 lib/Exif.php:454 lib/Exif/fujifilm.php:92
+#: lib/Exif/fujifilm.php:103 lib/Exif/sanyo.php:73 lib/Exif/sanyo.php:77
+#: lib/Exif/sanyo.php:81 lib/Exif/olympus.php:83 lib/Exif/olympus.php:88
+#: lib/Exif/nikon.php:122 lib/Exif/nikon.php:127 lib/Exif/nikon.php:135
+#: lib/Exif/nikon.php:142 lib/Exif/nikon.php:152 lib/Exif/nikon.php:157
+#: lib/Exif/nikon.php:186 lib/Exif/nikon.php:191 lib/Exif/nikon.php:194
+#: lib/Exif/canon.php:89 lib/Exif/canon.php:103 lib/Exif/canon.php:116
+#: lib/Exif/canon.php:123 lib/Exif/canon.php:137 lib/Exif/canon.php:149
+#: lib/Exif/canon.php:166 lib/Exif/canon.php:175 lib/Exif/canon.php:184
+#: lib/Exif/canon.php:193 lib/Exif/canon.php:201 lib/Exif/canon.php:228
+#: lib/Exif/canon.php:236 lib/Exif/canon.php:245 lib/Exif/canon.php:255
+#: lib/Exif/canon.php:266 lib/Exif/canon.php:290 lib/Exif/canon.php:321
+#: lib/Exif/canon.php:353 lib/Exif/canon.php:390
+msgid "Unknown"
+msgstr "Unbekannt"
+
+#: lib/Exif/panasonic.php:84 lib/Exif/panasonic.php:122
+#: lib/Exif/panasonic.php:232 lib/Exif/panasonic.php:253
+#: lib/Exif/panasonic.php:264 lib/Exif/panasonic.php:275
+#: lib/Exif/panasonic.php:286 lib/Exif/panasonic.php:297
+#: lib/Exif/panasonic.php:308 lib/Exif/panasonic.php:317
+#, php-format
+msgid "Unknown (%s)"
+msgstr "Unbekannt (%s)"
+
+#: gallery.php:172
+msgid "Unknown gallery"
+msgstr "Unbekannte Galerie"
+
+#: lib/Exif/panasonic.php:143 lib/Exif/panasonic.php:156
+#: lib/Exif/panasonic.php:167 lib/Exif/panasonic.php:176
+#: lib/Exif/panasonic.php:223
+#, php-format
+msgid "Unknown(%s)"
+msgstr "Unbekannt(%s)"
+
+#: lib/Exif/fujifilm.php:109 lib/Exif/fujifilm.php:115
+#: lib/Exif/fujifilm.php:122 lib/Exif/fujifilm.php:127
+#: lib/Exif/fujifilm.php:132 lib/Exif/fujifilm.php:137
+#: lib/Exif/fujifilm.php:149 lib/Exif/fujifilm.php:154
+#: lib/Exif/fujifilm.php:159 lib/Exif/fujifilm.php:164
+#: lib/Exif/fujifilm.php:169
+msgid "Unknown: "
+msgstr "Unbekannt: "
+
+#: lib/Exif.php:364
+#, php-format
+msgid "Unknown: %s"
+msgstr "Unbekannt: %s"
+
+#: lib/Exif/canon.php:39
+#, php-format
+msgid "Unknown: (%s)"
+msgstr "Unbekannt: (%s)"
+
+#: lib/Ansel.php:2910 lib/tests/GalleryTest.php:42
+msgid "Unnamed"
+msgstr "Unbenannt"
+
+#: lib/Report.php:122
+msgid "Unsupported"
+msgstr "Nicht unterstützt"
+
+#: xppublish.php:135
+msgid "Untitled"
+msgstr "Unbenannt"
+
+#: perms.php:237
+#, php-format
+msgid "Updated %s."
+msgstr "%s wurde aktualisiert."
+
+#: faces/search/image.php:22 lib/Forms/Upload.php:39
+msgid "Upload"
+msgstr "Hochladen"
+
+#: templates/xppublish/javascript.inc:63
+#, php-format
+msgid "Upload Photos to %s"
+msgstr "Fotos nach %s hochladen"
+
+#: faces/search/image.php:66
+msgid "Upload face photo"
+msgstr "Foto mit Gesicht hochladen"
+
+#: lib/Widget/Actions.php:50 img/upload.php:29
+msgid "Upload photos"
+msgstr "Fotos hochladen"
+
+#: lib/Exif.php:284
+#, php-format
+msgid "Upsidedown"
+msgstr "Auf dem Kopf"
+
+#: lib/Exif.php:286
+#, php-format
+msgid "Upsidedown Mirrored"
+msgstr "Auf dem Kopf und gespiegelt"
+
+#: scripts/recursive_import.php:221 scripts/ansel.php:318
+#: scripts/remote_import.php:340 scripts/all_images_exif_to_tags.php:116
+#, php-format
+msgid "Usage: %s [OPTIONS]..."
+msgstr "Aufruf: %s [OPTIONEN]..."
+
+#: lib/Block/gallery.php:39
+msgid "Use a lightbox to view photos"
+msgstr "Lightbox benutzen um Fotos anzuzeigen"
+
+#: lib/Forms/Ecard.php:32
+msgid "Use the following return address:"
+msgstr "Folgende Antwortadresse benutzen:"
+
+#: lib/Exif.php:507
+msgid "User Comment"
+msgstr "Benutzerkommentar"
+
+#: templates/gallery/gallery.inc:92
+#, php-format
+msgid "User must be over %d"
+msgstr "Benutzer muss über %d sein"
+
+#: templates/xppublish/login.inc:11
+msgid "Username"
+msgstr "Benutzername"
+
+#: xppublish.php:50
+msgid "Username or password are incorrect."
+msgstr "Benutzername oder Passwort sind nicht richtig."
+
+#: scripts/recursive_import.php:86 scripts/all_images_exif_to_tags.php:77
+msgid "Username or password is incorrect."
+msgstr "Benutzername oder Passwort sind nicht richtig."
+
+#: templates/gallery/gallery.inc:111 config/prefs.php.dist:228
+msgid "Users with edit permissions"
+msgstr "Benutzer mit Leserechten"
+
+#: lib/Exif/nikon.php:116
+msgid "VGA Basic"
+msgstr "VGA Basis"
+
+#: lib/Exif/nikon.php:118
+msgid "VGA Fine"
+msgstr "VGA Fein"
+
+#: lib/Exif/nikon.php:117
+msgid "VGA Normal"
+msgstr "VGA Normal"
+
+#: lib/Forms/Watermark.php:55 config/prefs.php.dist:196
+#: config/prefs.php.dist:209
+msgid "Vertical Alignment"
+msgstr "Vertikale Ausrichtung"
+
+#: lib/Exif/panasonic.php:118
+msgid "Very High"
+msgstr "Sehr Hoch"
+
+#: lib/Block/gallery.php:105
+#, php-format
+msgid "View %s"
+msgstr "%s anzeigen"
+
+#: templates/view/results.inc:65
+msgid "View All Results"
+msgstr "Alle Ergebnisse anszeigen"
+
+#: lib/Block/random_photo.php:55
+msgid "View Photo"
+msgstr "Foto anzeigen"
+
+#: templates/view/results.inc:65
+msgid "View Results from All Users"
+msgstr "Ergebnisse von allen Benutzern anzeigen"
+
+#: templates/tile/image.inc:11
+msgid "View or leave comments"
+msgstr "Kommentare anzeigen oder hinzufügen"
+
+#: config/prefs.php.dist:61
+msgid "View to display by default"
+msgstr "Standardansicht"
+
+#: lib/Exif/panasonic.php:245
+msgid "Warm"
+msgstr "Warm"
+
+#: lib/Exif/fujifilm.php:158
+msgid "Warning"
+msgstr "Warnung"
+
+#: image.php:463 image.php:474 templates/image/crop_image.inc:111
+#: templates/image/edit_image.inc:27 templates/image/resize_image.inc:58
+msgid "Watermark"
+msgstr "Wasserzeichen"
+
+#: lib/Forms/Watermark.php:41
+msgid "Watermark Font"
+msgstr "Wasserzeichen-Schriftart"
+
+#: config/prefs.php.dist:37
+msgid "Watermark Options"
+msgstr "Wasserzeichen-Einstellungen"
+
+#: config/prefs.php.dist:258
+msgid ""
+"When a new gallery is created, what default permissions should be given to "
+"groups that the user is a member of?"
+msgstr ""
+"Welche Rechte sollen den Mitgliedern aller Gruppen, zu denen Sie gehören, "
+"erteilt werden, wenn eine neue Galerie angelegt wird?"
+
+#: config/prefs.php.dist:237
+msgid ""
+"When a new gallery is created, what permissions should be given to "
+"authenticated users by default?"
+msgstr ""
+"Welche Rechte sollen angemeldeten Benutzer erteilt werden, wenn eine neue "
+"Galerie angelegt wird?"
+
+#: config/prefs.php.dist:248
+msgid ""
+"When a new gallery is created, what permissions should be given to guests by "
+"default?"
+msgstr ""
+"Welche Rechte sollen Gästen erteilt werden, wenn eine neue Galerie angelegt "
+"wird?"
+
+#: config/prefs.php.dist:270
+msgid ""
+"Which EXIF fields should we automatically add as photo tags during upload?"
+msgstr ""
+"Welche Exif-Felder sollen beim Hochladen automatisch als Fototags verwendet "
+"werden?"
+
+#: lib/Exif.php:510
+msgid "White Balance"
+msgstr "Weißabgleich"
+
+#: lib/Exif.php:380
+msgid "White fluorescent"
+msgstr "Weiße Leuchtstoffröhre"
+
+#: lib/Exif/fujifilm.php:100
+msgid "White-fluorescence"
+msgstr "Weiße Leuchtstoffröhre"
+
+#: config/prefs.php.dist:229
+msgid "Who should be allowed to download original photos"
+msgstr "Wer soll Originalfotos herunterladen können?"
+
+#: templates/gallery/gallery.inc:106
+msgid "Who should be allowed to download original photos?"
+msgstr "Wer soll Originalfotos herunterladen können?"
+
+#: lib/Exif.php:490
+msgid "Width"
+msgstr "Breite"
+
+#: lib/Exif.php:492
+msgid "X Resolution"
+msgstr "X-Auflösung"
+
+#: lib/Exif.php:493
+msgid "Y Resolution"
+msgstr "Y-Auflösung"
+
+#: lib/Exif/panasonic.php:228 config/prefs.php.dist:217
+msgid "Yes"
+msgstr "Ja"
+
+#: edit_dates.php:25
+msgid "You are not allowed to edit these photos."
+msgstr "Sie dürfen diese Fotos nicht bearbeiten."
+
+#: lib/XRequest/EditFaces.php:159
+msgid "You are not allowed to edit this photo."
+msgstr "Sie dürfen dieses Foto nicht bearbeiten."
+
+#: img/upload.php:225
+msgid "You did not select any photos to upload."
+msgstr "Sie haben keine Fotos zum Hochladen ausgewählt."
+
+#: gallery.php:285
+#, php-format
+msgid "You do not have permission to add children to %s."
+msgstr "Sie haben nicht genügend Rechte, um Unterpunkte zu %s hinzuzufügen."
+
+#: lib/Faces/facedetect.php:34
+msgid "You do not have the facedetect extension enabled in PHP"
+msgstr "Sie haben keine facedetect-Erweiterung in PHP."
+
+#: lib/Faces/opencv.php:34
+msgid "You do not have the opencv extension enabled in PHP"
+msgstr "Sie haben keine opencv-Erweiterung in PHP."
+
+#: img/ecard.php:68
+msgid ""
+"You have been sent an Ecard. To view the Ecard, you must be able to view "
+"text/html messages in your mail reader. If you are viewing this message, "
+"then most likely your mail reader does not support viewing text/html "
+"messages."
+msgstr ""
+"Sie haben eine Grußkarte bekommen. Um diese Grußkarte sehen zu können, muss "
+"Ihr Emailprogramm in der Lage sein, HTML-Nachrichten anzuzeigen. Wenn Sie "
+"stattdessen diesen Text sehen, kann Ihr Emailprogramm vermutlich keine HTML-"
+"Nachrichten darstellen."
+
+#: lib/Views/List.php:192
+msgid "You have no photo galleries, add one!"
+msgstr "Sie haben noch keine Fotogalerien, legen Sie sich eine an!"
+
+#: templates/view/gallery.inc:42 templates/view/gallery.inc:56
+#: templates/view/gallerylightbox.inc:43 templates/view/gallerylightbox.inc:57
+#: templates/view/results.inc:30 templates/view/results.inc:44
+msgid "You must choose a gallery to move photos to."
+msgstr ""
+"Sie müssen eine Galerie auswählen, zu der die Fotos verschoben werden sollen."
+
+#: templates/view/gallery.inc:29 templates/view/gallerylightbox.inc:29
+msgid "You must choose photos to delete."
+msgstr "Sie müssen die Fotos auswählen, die gelöscht werden sollen."
+
+#: templates/view/gallery.inc:79 templates/view/gallerylightbox.inc:80
+msgid "You must choose photos to edit."
+msgstr "Sie müssen die Fotos auswählen, die bearbeitet werden sollen."
+
+#: lib/Ansel.php:67 lib/Ansel.php:75
+msgid "You must configure a VFS backend to use Ansel."
+msgstr "Sie müssen ein VFS-Backend konfigurieren, um Ansel benutzen zu können."
+
+#: img/ecard.php:44
+msgid "You must enter an e-mail address to send the message to."
+msgstr ""
+"Sie müssen eine Emailadresse eingeben, an die diese Nachricht geschickt "
+"werden soll."
+
+#: img/ecard.php:39
+msgid "You must enter your e-mail address."
+msgstr "Sie müssen Ihre Emailadresse angeben."
+
+#: scripts/all_images_exif_to_tags.php:86
+msgid "You must login with an administrative account."
+msgstr "Sie müssen sich mit einem Administratorenkonto anmelden."
+
+#: gallery.php:297
+msgid "You must provide a display name for your new gallery."
+msgstr "Sie müssen einen Anzeigenamen für die neue Galerie angeben."
+
+#: edit_dates.php:36
+msgid "You must select at least on photo to edit."
+msgstr "Sie müssen erst mindestens ein Foto auswählen."
+
+#: scripts/recursive_import.php:95 scripts/remote_import.php:119
+msgid "You must specify a valid directory."
+msgstr "Sie müssen ein gültiges Verzeichnis angeben."
+
+#: scripts/recursive_import.php:91 scripts/remote_import.php:111
+#: scripts/all_images_exif_to_tags.php:82
+msgid "You must specify a valid username and password."
+msgstr "Sie müssen einen gültigen Benutzernamen und Passwort angeben."
+
+#: scripts/remote_import.php:115
+msgid "You must specify the url for the remote Horde RPC server."
+msgstr "Sie müssen die URL für den externen Horde-RPC-Server angeben."
+
+#: faces/search/image_save.php:22 faces/search/image_search.php:32
+#: faces/search/image_define.php:21
+msgid "You must upload the search photo first"
+msgstr "Sie müssen erst das Suchfoto hochladen"
+
+#: lib/Forms/Upload.php:72
+msgid "Zip File Upload"
+msgstr "ZIP-Datei"
+
+#: lib/Ansel.php:587
+msgid "_Browse"
+msgstr "_Liste"
+
+#: lib/Ansel.php:627
+msgid "_Faces"
+msgstr "Ge_sichter"
+
+#: lib/Ansel.php:595
+msgid "_Galleries"
+msgstr "_Galerien"
+
+#: lib/Ansel.php:607
+msgid "_My Galleries"
+msgstr "_Meine Galerien"
+
+#: lib/Ansel.php:619
+msgid "_New Gallery"
+msgstr "_Neue Galerie"
+
+#: lib/Ansel.php:632
+msgid "_Print"
+msgstr "_Drucken"
+
+#: group.php:94 templates/view/list.inc:14
+msgid "category"
+msgstr "Kategorie"
+
+#: templates/image/crop_image.inc:160 templates/image/resize_image.inc:71
+#: templates/faces/define.inc:61
+msgid "height"
+msgstr "Höhe"
+
+#: group.php:97
+msgid "none"
+msgstr "Nichts"
+
+#: lib/Views/GalleryRenderers/GalleryLightbox.php:130
+#: lib/Views/EmbeddedRenderers/Mini.php:149
+msgid "of"
+msgstr "von"
+
+#: lib/Exif.php:389
+msgid "other light source"
+msgstr "Andere Lichtquelle"
+
+#: group.php:92 templates/view/list.inc:13
+msgid "owner"
+msgstr "Besitzer"
+
+#: xppublish.php:185
+msgid "photo"
+msgstr "Foto"
+
+#: lib/Exif.php:459
+msgid "sRGB"
+msgstr "sRGB"
+
+#: lib/Exif.php:950
+#, php-format
+msgid "version %d"
+msgstr "Version %d"
+
+#: templates/image/crop_image.inc:154 templates/image/resize_image.inc:69
+#: templates/faces/define.inc:59
+msgid "width"
+msgstr "Breite"
diff --git a/ansel/po/es_ES.po b/ansel/po/es_ES.po
new file mode 100644 (file)
index 0000000..4fb1b06
--- /dev/null
@@ -0,0 +1,2121 @@
+# Spanish translations for ansel package
+# Traducciones al español para el paquete ansel.
+# Copyright 2008-2009 The Horde Project
+# This file is distributed under the same license as the ansel package.
+# Automatically generated, 2008.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Ansel 1.0-cvs\n"
+"Report-Msgid-Bugs-To: dev@lists.horde.org\n"
+"POT-Creation-Date: 2008-03-17 12:22+0100\n"
+"PO-Revision-Date: 2008-03-17 12:22+0100\n"
+"Last-Translator: Manuel P. Ayala <mayala@unex.es>\n"
+"Language-Team: i18n@lists.horde.org\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-1\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: scripts/recursive_import.php:111
+#, php-format
+msgid "\"%s\" is not a directory."
+msgstr "\"%s\" no es un directorio."
+
+#: templates/group/category.inc:48 templates/group/owner.inc:52
+#, php-format
+msgid "%d Galleries"
+msgstr "%d galerías"
+
+#: lib/Exif.php:285
+#, php-format
+msgid "%d bytes"
+msgstr "%d bytes"
+
+#: templates/tile/image.inc:13
+#, php-format
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] "%d comentario"
+msgstr[1] "%d comentarios"
+
+#: templates/list/header.inc:3 templates/view/list.inc:3
+#, php-format
+msgid "%d galleries"
+msgstr "%d galerías"
+
+#: lib/Widget/Tags.php:90 lib/XRequest/TagActions.php:114
+#: lib/Views/Results.php:263
+#, php-format
+msgid "%d photo"
+msgid_plural "%d photos"
+msgstr[0] "%d foto"
+msgstr[1] "%d fotos"
+
+#: img/upload.php:193
+#, php-format
+msgid "%d photo was uploaded."
+msgid_plural "%d photos were uploaded."
+msgstr[0] "Se cargó %d foto."
+msgstr[1] "Se cargaron %d fotos."
+
+#: lib/Exif.php:136 lib/Exif.php:142
+#, php-format
+msgid "%d pixels"
+msgstr "%d píxeles"
+
+#: lib/Exif.php:166 lib/Exif.php:181
+#, php-format
+msgid "%d sec"
+msgstr "%d seg"
+
+#: templates/list/header.inc:3 templates/view/list.inc:3
+#, php-format
+msgid "%d to %d of %d galleries"
+msgstr "%d al %d de %d galerías"
+
+#: templates/group/header.inc:3
+#, php-format
+msgid "%d to %d of %d groups"
+msgstr "%d al %d de %d grupos"
+
+#: templates/view/results.inc:83
+#, php-format
+msgid "%d to %d of %d photos"
+msgstr "%d al %d de %d fotos"
+
+#: gallery/sort.php:53
+#, php-format
+msgid "%s :: Sort"
+msgstr "%s :: Ordenar"
+
+#: lib/Widget/SimilarPhotos.php:81 lib/Views/Image.php:250
+#, php-format
+msgid "%s from %s"
+msgstr "%s de %s"
+
+#: templates/view/results.inc:83 templates/view/gallery.inc:79
+#, php-format
+msgid "%s items"
+msgstr "%s elementos"
+
+#: rss.php:110
+#, php-format
+msgid "%s on %s"
+msgstr "%s en %s"
+
+#: list.php:49 templates/group/owner.inc:16 lib/Ansel.php:656
+#: lib/Views/List.php:106
+#, php-format
+msgid "%s's Galleries"
+msgstr "Galerías de %s"
+
+#: lib/Widget/OtherGalleries.php:21
+#, php-format
+msgid "%s's Other Galleries"
+msgstr "Otras galerías de %s"
+
+#: lib/Ansel.php:3358
+#, php-format
+msgid "(c) %s %s"
+msgstr "(c) %s %s"
+
+#: templates/xppublish/new.inc:14
+msgid "(must be unique)"
+msgstr "(tiene que ser único)"
+
+#: scripts/ansel.php:323
+msgid "-a, --add[=filename]         Add local file to selected gallery"
+msgstr ""
+"-a, --add[=archivo]          Añade un archivo local a la galería seleccionada"
+
+#: scripts/ansel.php:321
+msgid ""
+"-c, --create[=shortname/name/description/owner]\n"
+"                             Create gallery (and use it)  Combined with -g "
+"to create a subgallery."
+msgstr ""
+"-c, --create[=apodo/nombre/descripción]\n"
+"                             Crear una galería (y utilizarla) Combinado con -"
+"g para crear una galería secundaria."
+
+#: scripts/ansel.php:324
+msgid ""
+"-d, --dir[=directory]        Add all files from the directory to the "
+"selected\n"
+"                             gallery"
+msgstr ""
+"-d, --dir[=directorio]       Añadir todos los archivos del directorio a la "
+"galería\n"
+"                             seleccionada"
+
+#: scripts/recursive_import.php:190
+msgid ""
+"-d, --dir[=directory]        Recursively add all files from the directory, "
+"creating\n"
+"                             a gallery for each directory"
+msgstr ""
+"-d, --dir[=directorio]        Añadir todos los archivos del directorio de "
+"forma recursiva\n"
+"                              creando una galería para cada directorio"
+
+#: scripts/ansel.php:322
+msgid "-g, --gallery[=shortname]    Select gallery to use"
+msgstr "-g, --gallery[=apodo]        Seleccionar la galería utilizada"
+
+#: scripts/ansel.php:319 scripts/recursive_import.php:189
+msgid "-h, --help                   Show this help"
+msgstr "-h, --help                   Mostrar esta ayuda"
+
+#: scripts/ansel.php:320
+msgid ""
+"-l, --list                   List galleries or photos (if combined with -g)"
+msgstr ""
+"-l, --list                   Listar galerías o fotos (si se combina con -g)"
+
+#: scripts/ansel.php:326 scripts/recursive_import.php:192
+msgid "-p, --password[=password]    Horde login password"
+msgstr "-p, --password[=clave]       Clave de inicio de Horde"
+
+#: scripts/ansel.php:327
+msgid "-t, --caption[=caption]      Caption for photo (if combined with -a)"
+msgstr ""
+"-t, --caption[=comentario]   Comentario de la foto (si se combina con -a)"
+
+#: scripts/ansel.php:325 scripts/recursive_import.php:191
+msgid "-u, --username[=username]    Horde login username"
+msgstr "-u, --username[=usuario]     Usuario de inicio de Horde"
+
+#: templates/group/category.inc:48 templates/group/owner.inc:52
+msgid "1 Gallery"
+msgstr "1 galería"
+
+#: lib/Exif.php:157
+#, php-format
+msgid "90 deg CCW"
+msgstr "90 grados CCW"
+
+#: lib/Exif.php:159
+#, php-format
+msgid "90 deg CCW Mirrored"
+msgstr "90 grados CCW reflejado"
+
+#: lib/Exif.php:161
+#, php-format
+msgid "90 deg CW"
+msgstr "90 grados CW"
+
+#: lib/Exif.php:155
+#, php-format
+msgid "90 deg CW Mirrored"
+msgstr "90 grados CW reflejado"
+
+#: lib/api.php:262
+msgid "A gallery to add this photo to is required."
+msgstr "Se precisa una galería a la que añadir esta foto."
+
+#: lib/Exif.php:128
+msgid "A-DEP"
+msgstr "A-DEP"
+
+#: gallery/delete.php:28 gallery/delete.php:54 lib/api.php:393
+#, php-format
+msgid "Access denied deleting gallery %s."
+msgstr "Se ha denegado el acceso para eliminar la galería %s."
+
+#: lib/api.php:332
+msgid "Access denied deleting image."
+msgstr "Se ha denegado el acceso para eliminar la imagen."
+
+#: image.php:490 lib/Views/Results.php:121
+#, php-format
+msgid "Access denied deleting photo %s."
+msgstr "Se ha denegado el acceso para eliminar la foto %s."
+
+#: gallery.php:75
+#, php-format
+msgid "Access denied downloading %s."
+msgstr "Se ha denegado el acceso para descargar %s."
+
+#: gallery.php:134
+msgid "Access denied editing galleries."
+msgstr "Se ha denegado el acceso para modificar las galerías."
+
+#: gallery.php:166
+#, php-format
+msgid "Access denied saving gallery %s."
+msgstr "Se ha denegado el acceso para guardar la galería %s."
+
+#: image.php:219 image.php:316 image.php:340
+#, php-format
+msgid "Access denied saving photo to %s."
+msgstr "Se ha denegado el acceso para guardar la foto en %s."
+
+#: gallery/captions.php:33
+#, php-format
+msgid "Access denied setting captions for %s."
+msgstr "Se ha denegado el acceso para establecer comentarios para %s."
+
+#: templates/view/results.inc:97 templates/view/gallery.inc:95
+msgid "Actions: "
+msgstr "Acciones: "
+
+#: lib/Widget/Tags.php:55
+msgid "Add"
+msgstr "Añadir"
+
+#: browse.php:22
+msgid "Add Content"
+msgstr "Añadir contenido"
+
+#: img/upload.php:209
+msgid "Add Photo"
+msgstr "Añadir foto"
+
+#: gallery.php:36 gallery.php:210
+msgid "Adding A New Gallery"
+msgstr "Añadir una galería"
+
+#: gallery.php:65
+#, php-format
+msgid "Adding A Subgallery to %s"
+msgstr "Añadir a %s una galería secundaria"
+
+#: scripts/recursive_import.php:171
+msgid "Adding subdirectories:"
+msgstr "Añadir subdirectorios:"
+
+#: lib/api.php:167
+msgid "Administrators"
+msgstr "Administradores"
+
+#: report.php:48
+msgid "Advertisement content"
+msgstr "Contenido publicitario"
+
+#: templates/view/results.inc:93 templates/view/gallery.inc:92
+msgid "All"
+msgstr "Todas"
+
+#: lib/Block/recent_comments.php:63 lib/Block/recently_added.php:65
+msgid "All Galleries"
+msgstr "Todas las galerías"
+
+#: lib/Ansel.php:3887
+#, php-format
+msgid "An error occurred counting galleries: %s"
+msgstr "Se ha producido un error al contar las galerías: %s"
+
+#: lib/Ansel.php:3998
+#, php-format
+msgid "An error occurred listing galleries: %s"
+msgstr "Se ha producido un error al listar las galerías: %s"
+
+#: config/.bak/prefs.php.dist:200
+msgid "Anyone"
+msgstr "Cualquiera"
+
+#: lib/Exif.php:343
+msgid "Aperture"
+msgstr "Abertura"
+
+#: templates/view/results.inc:20 templates/view/gallery.inc:20
+msgid "Are you sure you want to delete the selected photos?"
+msgstr "¿Esta seguro de que desea eliminar las fotos seleccionadas?"
+
+#: lib/Exif.php:356
+msgid "Artist"
+msgstr "Artista"
+
+#: perms.php:53
+msgid "Attempt to edit a non-existent share."
+msgstr "Intenta modificar un recurso compartido inexistente."
+
+#: config/.bak/prefs.php.dist:201
+msgid "Authenticated users"
+msgstr "Usuarios autentificados"
+
+#: config/.bak/prefs.php.dist:192
+msgid "Automatically watermark images?"
+msgstr "¿Añadir marcas de agua a las imágenes de forma automática?"
+
+#: lib/Exif.php:124
+msgid "Av-priority"
+msgstr "Prioridad a la abertura"
+
+#: lib/Exif.php:228
+msgid "Average"
+msgstr "Promedio"
+
+#: templates/image/edit_image.inc:19 templates/view/image.inc:57
+#: templates/view/image.inc:58 templates/view/image.inc:80
+#: templates/view/image.inc:81
+msgid "Back to gallery"
+msgstr "Volver a la galería"
+
+#: image.php:537 image.php:579 lib/Views/Results.php:152
+#: lib/Views/Results.php:192
+msgid "Bad input."
+msgstr "Entrada incorrecta."
+
+#: image.php:82 config/.bak/prefs.php.dist:169
+msgid "Bottom"
+msgstr "Inferior"
+
+#: lib/Ansel.php:555 config/.bak/prefs.php.dist:48
+msgid "Browse"
+msgstr "Examinar"
+
+#: lib/Ansel.php:664
+msgid "Browse Tags"
+msgstr "Ver etiquetas"
+
+#: lib/Exif.php:327
+msgid "Camera Make"
+msgstr "Disparo de la cámara"
+
+#: lib/Exif.php:328
+msgid "Camera Model"
+msgstr "Modelo de la cámara"
+
+#: lib/Exif.php:354
+msgid "Camera Orientation"
+msgstr "Orientación de la cámara"
+
+#: lib/Ansel.php:495
+#, php-format
+msgid "Can't get unknown file type \"%s\"."
+msgstr "No se puede obtener el tipo de archivo desconocido \"%s\"."
+
+#: report.php:46 templates/gallery/delete_confirmation.inc:11
+#: templates/image/edit_image.inc:20
+msgid "Cancel"
+msgstr "Cancelar"
+
+#: templates/image/preview_image.inc:23 templates/image/preview_image.inc:24
+msgid "Cancel Change"
+msgstr "Cancelar cambio"
+
+#: image.php:38 lib/Forms/Upload.php:51
+msgid "Caption"
+msgstr "Comentario"
+
+#: gallery/captions.php:59
+msgid "Caption Editor"
+msgstr "Editor de comentarios"
+
+#: gallery/captions.php:52
+msgid "Captions Saved."
+msgstr "Se han guardado los comentarios."
+
+#: config/.bak/prefs.php.dist:61
+msgid "Category"
+msgstr "Categoría"
+
+#: image.php:74 image.php:81 config/.bak/prefs.php.dist:157
+#: config/.bak/prefs.php.dist:168
+msgid "Center"
+msgstr "Centro"
+
+#: lib/Exif.php:229
+msgid "Center Weighted Average"
+msgstr "Promedio ponderado al centro"
+
+#: lib/Widget/Actions.php:62
+msgid "Change properties"
+msgstr "Cambiar propiedades"
+
+#: config/.bak/prefs.php.dist:14
+msgid "Change your display options."
+msgstr "Cambiar opciones de visualización"
+
+#: config/.bak/prefs.php.dist:30
+msgid "Change your edit options."
+msgstr "Cambiar opciones de modificación."
+
+#: config/.bak/prefs.php.dist:22
+msgid "Change your watermark options."
+msgstr "Cambiar opciones de marcas de agua"
+
+#: lib/Exif.php:242
+msgid "Cloudy"
+msgstr "nuboso"
+
+#: lib/Exif.php:293
+msgid "Color Sequential Area Sensor"
+msgstr "Sensor superficial secuencial de color"
+
+#: lib/Exif.php:295
+msgid "Color Sequential Linear Sensor"
+msgstr "Sensor linear secuencial de color"
+
+#: lib/Exif.php:351
+msgid "Color Space"
+msgstr "Espacio de color"
+
+#: lib/Forms/Ecard.php:42
+msgid "Comments:"
+msgstr "Comentarios:"
+
+#: lib/Exif.php:263
+msgid "Compulsory Flash"
+msgstr "Flash forzado"
+
+#: lib/Exif.php:265
+msgid "Compulsory Flash, Return light detected"
+msgstr "Flash forzado, detección de luz reflejada"
+
+#: lib/Exif.php:264
+msgid "Compulsory Flash, Return light not detected"
+msgstr "Flash forzado, sin detección de luz reflejada"
+
+#: lib/Report.php:76
+#, php-format
+msgid "Content abuse report in %s"
+msgstr "Denuncia de contenido abusivo en %s"
+
+#: lib/Views/Results.php:206
+#, php-format
+msgid "Copied %d photo from %s to %s"
+msgid_plural "Copied %d photos from %s to %s"
+msgstr[0] "Copiada %d foto de %s a %s"
+msgstr[1] "Copiadas %d fotos de %s a %s"
+
+#: image.php:585
+#, php-format
+msgid "Copied one photo to %s"
+msgid_plural "Copied %d photos to %s"
+msgstr[0] "Copiada %d foto a %s"
+msgstr[1] "Copiadas %d fotos a %s"
+
+#: templates/view/results.inc:103 templates/view/gallery.inc:103
+msgid "Copy"
+msgstr "Copiar"
+
+#: lib/Exif.php:355
+msgid "Copyright"
+msgstr "Copyright"
+
+#: report.php:51
+msgid "Copyright infringement"
+msgstr "Infracción de Copyright"
+
+#: lib/Ansel.php:1968
+#, php-format
+msgid "Could not save gallery, the slug, %s, already exists."
+msgstr "No se puede guardar la galería, ya existe el ID %s."
+
+#: scripts/ansel.php:184
+msgid "Couldn't list galleries."
+msgstr "No se pueden listar las galerias."
+
+#: scripts/ansel.php:43
+msgid "Couldn't read command-line options."
+msgstr "No se pueden leer las opciones de la línea de comandos."
+
+#: templates/xppublish/list.inc:19
+msgid "Create"
+msgstr "Crear"
+
+#: templates/xppublish/new.inc:8
+msgid "Create a new Gallery"
+msgstr "Crear galería"
+
+#: templates/xppublish/list.inc:18
+msgid "Create a new gallery:"
+msgstr "Crear una galería:"
+
+#: lib/Widget/Actions.php:61
+msgid "Create a subgallery"
+msgstr "Crear una galería secundaria"
+
+#: lib/Tile/Gallery.php:104
+msgid "Created:"
+msgstr "Creada:"
+
+#: scripts/recursive_import.php:120
+#, php-format
+msgid "Creating gallery: \"%s\" (%s)"
+msgstr "Creando galería: \"%s\" (%s)"
+
+#: lib/Exif.php:246
+msgid "Custom"
+msgstr "Personalizado"
+
+#: image.php:60
+msgid "Custom Watermark"
+msgstr "Marca de agua personalizada"
+
+#: config/.bak/prefs.php.dist:41
+msgid ""
+"Custom text to display describing your galleries. This will be displayed in "
+"place of your username when grouping galleries by username."
+msgstr ""
+"Texto personalizado mostrado describiendo las galerías. Se mostrará en vez "
+"del nombre de usuario al agrupar las galerías por nombres de usuario."
+
+#: config/.bak/prefs.php.dist:104 config/.bak/prefs.php.dist:148
+msgid "Custom watermark to use for photos"
+msgstr "Marca de agua personalizada a utilizar en las fotos"
+
+#: lib/Exif.php:334
+msgid "Date Photo Digitized"
+msgstr "Fecha de digitalización de la foto"
+
+#: lib/Exif.php:332
+msgid "Date Photo Modified"
+msgstr "Fecha de modificación de la foto"
+
+#: lib/Exif.php:333
+msgid "Date Photo Taken"
+msgstr "Fecha de realización de la foto"
+
+#: scripts/create_serialized_styles.php:41 config/.bak/prefs.php.dist:127
+msgid "Default"
+msgstr "Por omisión"
+
+#: config/.bak/prefs.php.dist:126
+msgid "Default style for galleries"
+msgstr "Estilo por omisión de las galerias"
+
+#: templates/view/results.inc:98 templates/view/image.inc:28
+#: templates/view/slideshow.inc:25 templates/view/gallery.inc:97
+#: lib/Widget/Actions.php:83
+msgid "Delete"
+msgstr "Eliminar"
+
+#: image.php:499 lib/Views/Results.php:127
+msgid "Deleted the photo."
+msgstr "Fotografía eliminada."
+
+#: config/.bak/prefs.php.dist:13
+msgid "Display Options"
+msgstr "Opciones de Visualización"
+
+#: templates/view/gallery.inc:79
+#, php-format
+msgid "Displaying from %s to %s of %s items"
+msgstr "Mostrando desde %s al %s de %s elementos"
+
+#: report.php:21
+msgid "Do you really want to report this gallery?"
+msgstr "¿Seguro que desea comunicar esta galería?"
+
+#: report.php:25
+msgid "Do you really want to report this image?"
+msgstr "¿Seguro que desea comunicar esta imagen?"
+
+#: templates/view/image.inc:28 templates/view/slideshow.inc:25
+#, php-format
+msgid "Do you want to permanently delete %s?"
+msgstr "¿Desea borrar permanentemente %s?"
+
+#: gallery/sort.php:67
+msgid "Done"
+msgstr "Hecho"
+
+#: templates/view/image.inc:34 templates/view/slideshow.inc:33
+msgid "Download Full Photo"
+msgstr "Descargar foto completa"
+
+#: lib/Widget/Actions.php:57
+msgid "Download as .zip"
+msgstr "Descargar como .zip"
+
+#: gallery/sort.php:66
+msgid "Drag photos to the desired sort position."
+msgstr "Arrastre las fotos a la posición deseada."
+
+#: xppublish.php:212
+#, php-format
+msgid "ERROR: %s"
+msgstr "ERROR: %s"
+
+#: lib/Exif.php:118
+msgid "Easy shooting"
+msgstr "Disparo simple"
+
+#: img/ecard.php:60
+msgid "Ecard - "
+msgstr "Ecard - "
+
+#: templates/view/image.inc:25 templates/view/slideshow.inc:22
+#: templates/tile/image.inc:20
+msgid "Edit"
+msgstr "Modificar"
+
+#: image.php:305
+#, php-format
+msgid "Edit %s :: %s"
+msgstr "Modificar %s :: %s"
+
+#: config/.bak/prefs.php.dist:29
+msgid "Edit Options"
+msgstr "Opciones de modificación"
+
+#: perms.php:246
+msgid "Edit Permissions"
+msgstr "Modificar privilegios"
+
+#: perms.php:250
+#, php-format
+msgid "Edit Permissions for %s"
+msgstr "Modificar privilegios de %s"
+
+#: image.php:414
+msgid "Edit Photo"
+msgstr "Modificar foto"
+
+#: image.php:235
+msgid "Edit an photo"
+msgstr "Modificar una foto"
+
+#: image.php:189
+#, php-format
+msgid "Edit properties :: %s"
+msgstr "Modificar propiedades :: %s"
+
+#: lib/Widget/Actions.php:82
+msgid "Empty"
+msgstr "Vaciar"
+
+#: gallery/captions.php:26
+#, php-format
+msgid "Error accessing %s: %s"
+msgstr "Error al acceder a %s: %s"
+
+#: rss.php:35
+msgid "Error retrieving feed"
+msgstr "Error al recuperar la suscripción"
+
+#: templates/xppublish/list.inc:12
+msgid "Existing galleries:"
+msgstr "Galerías disponibles:"
+
+#: lib/Exif.php:340
+msgid "Exposure"
+msgstr "Exposición"
+
+#: lib/Exif.php:346
+msgid "Exposure Bias"
+msgstr "Ajuste de exposición"
+
+#: lib/Exif.php:347
+msgid "Exposure Mode"
+msgstr "Modo de exposición"
+
+#: lib/Exif.php:344
+msgid "F-Number"
+msgstr "Número f"
+
+#: lib/Forms/Upload.php:62
+#, php-format
+msgid "File %s"
+msgstr "Archivo %s"
+
+#: lib/Exif.php:331
+msgid "File Size"
+msgstr "Tamaño de archivo"
+
+#: lib/Forms/Upload.php:47 lib/Forms/Upload.php:71
+msgid "File to upload"
+msgstr "Archivo a cargar"
+
+#: lib/Exif.php:245 lib/Exif.php:260
+msgid "Flash"
+msgstr "Flash"
+
+#: lib/Exif.php:349
+msgid "Flash Setting"
+msgstr "Configuración del flash"
+
+#: lib/Exif.php:268
+msgid "Flash, Auto-Mode"
+msgstr "Flash, automático"
+
+#: lib/Exif.php:270
+msgid "Flash, Auto-Mode, Return light detected"
+msgstr "Flash, automático, detección de luz reflejada"
+
+#: lib/Exif.php:269
+msgid "Flash, Auto-Mode, Return light not detected"
+msgstr "Flash, automático, sin detección de luz reflejada"
+
+#: lib/Exif.php:262
+msgid "Flash, strobe return light detected"
+msgstr "Flash, estroboscópico, detección de luz reflejada"
+
+#: lib/Exif.php:261
+msgid "Flash, strobe return light not detected"
+msgstr "Flash, estroboscópico, sin detección de luz reflejada"
+
+#: templates/image/edit_image.inc:28
+msgid "Flip"
+msgstr "Voltear"
+
+#: lib/Exif.php:244
+msgid "Fluorescent"
+msgstr "Fluorescente"
+
+#: lib/Exif.php:341
+msgid "Focal Length"
+msgstr "Distancia focal"
+
+#: lib/Exif.php:342
+msgid "Focal Length (35mm equiv)"
+msgstr "Distancia focal (equiv. en cámaras de 35mm)"
+
+#: templates/tile/image.inc:10
+msgid "From: "
+msgstr "De: "
+
+#: lib/Ansel.php:664 config/.bak/prefs.php.dist:49
+msgid "Galleries"
+msgstr "Galerías"
+
+#: templates/group/header.inc:6
+#, php-format
+msgid "Galleries grouped by %s"
+msgstr "Galerías agrupadas por %s"
+
+#: list.php:56 list.php:59 lib/Views/List.php:113 lib/Views/List.php:117
+#, php-format
+msgid "Galleries in category %s"
+msgstr "Galerías de la categoría %s"
+
+#: lib/Block/recent_comments.php:31 lib/Block/recent_comments.php:51
+#: lib/Block/gallery.php:3 lib/Block/gallery.php:30 lib/Block/gallery.php:46
+#: lib/Block/recently_added.php:28 lib/Block/recently_added.php:52
+msgid "Gallery"
+msgstr "Galería"
+
+#: lib/Ansel.php:3588
+#, php-format
+msgid "Gallery %s could not be found."
+msgstr "No se pudo encontrar la galería %s."
+
+#: templates/gallery/gallery.inc:17
+msgid "Gallery Category"
+msgstr "Categoría de la galería"
+
+#: report.php:72 templates/gallery/gallery.inc:46
+msgid "Gallery Description"
+msgstr "Descripción de la galería"
+
+#: templates/gallery/gallery.inc:39
+msgid "Gallery Display Name"
+msgstr "Nombre público de la galería"
+
+#: report.php:73
+msgid "Gallery Id"
+msgstr "Id de la galería"
+
+#: list.php:64 list.php:140 lib/Views/List.php:122
+msgid "Gallery List"
+msgstr "Listado de galerías"
+
+#: report.php:71 lib/Block/my_galleries.php:49
+msgid "Gallery Name"
+msgstr "Nombre de la galería"
+
+#: lib/Tile/Gallery.php:82
+msgid "Gallery Properties"
+msgstr "Propiedades de la galería"
+
+#: templates/xppublish/new.inc:14
+msgid "Gallery Short Name"
+msgstr "Abreviatura de la galería"
+
+#: templates/gallery/gallery.inc:52
+msgid "Gallery Slug"
+msgstr "ID de la galería"
+
+#: templates/gallery/gallery.inc:60
+msgid "Gallery Tags"
+msgstr "Etiquetas de la galería"
+
+#: xppublish.php:121
+msgid "Gallery Unique name is already taken."
+msgstr "Ya está en uso el ID de galería."
+
+#: lib/Block/recent_comments.php:146 lib/Block/gallery.php:108
+#: lib/Block/recently_added.php:157
+msgid "Gallery does not exist."
+msgstr "La galería no existe."
+
+#: lib/Ansel.php:946
+msgid "Gallery names must be non-empty"
+msgstr "Los nombres de galería no pueden dejarse en blanco"
+
+#: image.php:420
+msgid "Gallery not found."
+msgstr "No se ha encontrado la galería."
+
+#: gallery/sort.php:42
+msgid "Gallery sorted."
+msgstr "Galería ordenada."
+
+#: report.php:80 report.php:86
+msgid "Gallery was not reported."
+msgstr "No se comunicó la galería."
+
+#: report.php:83
+msgid "Gallery was reported."
+msgstr "Se comunicó la galería."
+
+#: image.php:68 config/.bak/prefs.php.dist:182
+msgid "Giant"
+msgstr "Gigante"
+
+#: templates/image/edit_image.inc:34
+msgid "Grayscale"
+msgstr "Escala de grises"
+
+#: templates/group/header.inc:11 templates/view/list.inc:9
+msgid "Group by: "
+msgstr "Agrupar por: "
+
+#: config/.bak/prefs.php.dist:63 config/.bak/prefs.php.dist:203
+msgid "Group galleries by"
+msgstr "Agrupar galerías por"
+
+#: lib/Exif.php:336
+msgid "Height"
+msgstr "Altura"
+
+#: image.php:76 config/.bak/prefs.php.dist:159
+msgid "Horizontal Alignment"
+msgstr "Alineación horizontal"
+
+#: lib/Exif.php:345
+msgid "ISO Setting"
+msgstr "Configuración ISO"
+
+#: lib/Ansel.php:3126
+msgid "Incomplete photo"
+msgstr "Foto imcompleta"
+
+#: lib/Ansel.php:1212 lib/Ansel.php:1266
+msgid "Invalid data"
+msgstr "Datos no válidos"
+
+#: scripts/ansel.php:125
+#, php-format
+msgid "Invalid gallery \"%s\" specified."
+msgstr "La galería \"%s\" indicada no es válida."
+
+#: xppublish.php:87 xppublish.php:180
+msgid "Invalid gallery specified."
+msgstr "Se ha indicado una galería no válida."
+
+#: image.php:67 config/.bak/prefs.php.dist:181
+msgid "Large"
+msgstr "Grande"
+
+#: lib/Block/my_galleries.php:49
+msgid "Last Modified"
+msgstr "Última modificación"
+
+#: img/upload.php:215
+msgid "Learn how to publish photos directly from Windows."
+msgstr "Aprenda cómo publicar fotos directamente desde Windows."
+
+#: image.php:73 config/.bak/prefs.php.dist:156
+msgid "Left"
+msgstr "Izquierda"
+
+#: lib/Widget/Links.php:13
+msgid "Links"
+msgstr "Vínculos"
+
+#: lib/Views/List.php:60
+msgid "List TODO"
+msgstr "Lista TODO"
+
+#: scripts/ansel.php:189
+msgid "Listing Gallery/Name"
+msgstr "Listado de Galerías/Nombres"
+
+#: scripts/ansel.php:173
+#, php-format
+msgid "Listing photos in %s"
+msgstr "Listando fotos en %s"
+
+#: scripts/ansel.php:116 scripts/recursive_import.php:81
+#, php-format
+msgid "Logged in successfully as \"%s\"."
+msgstr "Se ha iniciado sesión correctamente como \"%s\"."
+
+#: scripts/ansel.php:112
+msgid "Login is incorrect."
+msgstr "Inicio de sesión incorrecto."
+
+#: templates/xppublish/login.inc:2
+#, php-format
+msgid "Login to %s"
+msgstr "Iniciar sesisón en %s"
+
+#: image.php:37 lib/Forms/Upload.php:50
+msgid "Make this the default photo for this gallery?"
+msgstr "¿Establecerla como foto por omisión de esta galería?"
+
+#: scripts/ansel.php:317 scripts/recursive_import.php:187
+msgid ""
+"Mandatory arguments to long options are mandatory for short options too."
+msgstr ""
+"Los argumentos obligatorios de las opciones largas son también obligatorios "
+"para las opciones cortas."
+
+#: lib/Exif.php:126 lib/Exif.php:247
+msgid "Manual"
+msgstr "Manual"
+
+#: lib/Forms/Upload.php:70
+msgid "Maximum file size"
+msgstr "Tamaño máximo de archivo"
+
+#: lib/Forms/Upload.php:46
+msgid "Maximum image size"
+msgstr "Tamaño máximo de imagen"
+
+#: lib/Block/recently_added.php:33
+msgid "Maximum number of photos"
+msgstr "Número máximo de fotos"
+
+#: image.php:66 config/.bak/prefs.php.dist:180
+msgid "Medium"
+msgstr "Media"
+
+#: lib/Exif.php:348
+msgid "Metering Mode"
+msgstr "Modo de medición"
+
+#: templates/image/edit_image.inc:31
+msgid "Mirror"
+msgstr "Reflejar"
+
+#: lib/Exif.php:149
+#, php-format
+msgid "Mirrored"
+msgstr "Reflejado"
+
+#: lib/Tile/Gallery.php:106
+msgid "Modified"
+msgstr "Modificada"
+
+#: gallery.php:123
+#, php-format
+msgid "Modifying: %s"
+msgstr "Modificando: %s"
+
+#: templates/view/results.inc:100 templates/view/gallery.inc:100
+msgid "Move"
+msgstr "Trasladar"
+
+#: lib/Views/Results.php:166
+#, php-format
+msgid "Moved %d photo from %s to %s"
+msgid_plural "Moved %d photos from %s to %s"
+msgstr[0] "Se ha trasladado %d foto de %s a %s"
+msgstr[1] "Se han trasladado %d fotos de %s a %s"
+
+#: image.php:543
+#, php-format
+msgid "Moved %d photo to %s"
+msgid_plural "Moved %d photos to %s"
+msgstr[0] "Se ha trasladado %d foto a %s"
+msgstr[1] "Se han trasladado %d fotos a %s"
+
+#: lib/Exif.php:232
+msgid "Multi-Segment"
+msgstr "Multi-Segment"
+
+#: lib/Exif.php:231
+msgid "Multi-Spot"
+msgstr "Multi-Spot"
+
+#: lib/Forms/Upload.php:54
+msgid "Multiple Photos"
+msgstr "Varias fotos"
+
+#: list.php:36 templates/group/owner.inc:3 lib/Ansel.php:643
+#: lib/Block/my_galleries.php:3 lib/Block/my_galleries.php:32
+#: lib/Views/List.php:93 config/.bak/prefs.php.dist:50
+msgid "My Galleries"
+msgstr "Mis galerías"
+
+#: browse_edit.php:28
+msgid "My Photos :: Add Content"
+msgstr "Mis foros :: Añadir contenido"
+
+#: templates/xppublish/new.inc:33
+msgid "New Gallery Description"
+msgstr "Nueva descripción de la galería"
+
+#: templates/xppublish/new.inc:24
+msgid "New Gallery Title"
+msgstr "Nuevo título de la galería"
+
+#: templates/view/image.inc:55 templates/view/image.inc:56
+#: templates/view/image.inc:78 templates/view/image.inc:79
+#: templates/view/slideshow.inc:46 templates/view/slideshow.inc:47
+msgid "Next"
+msgstr "Siguiente"
+
+#: config/.bak/prefs.php.dist:191
+msgid "No"
+msgstr "No"
+
+#: lib/Exif.php:259 lib/Exif.php:266 lib/Exif.php:267 lib/Exif.php:271
+msgid "No Flash"
+msgstr "Sin Flash"
+
+#: xppublish.php:123
+msgid "No Gallery Name Specified."
+msgstr "No se especificó un nombre de galería."
+
+#: xppublish.php:119
+msgid "No Gallery Unique Name Specified."
+msgstr "No se especificó un ID de galería."
+
+#: xppublish.php:190
+msgid "No file specified"
+msgstr "No se han especificado archivos"
+
+#: lib/Views/Abstract.php:34
+msgid "No gallery specified."
+msgstr "No se ha indicado ninguna galería."
+
+#: templates/view/results.inc:94 templates/view/gallery.inc:93
+#: config/.bak/prefs.php.dist:62
+msgid "None"
+msgstr "Ninguna"
+
+#: lib/Exif.php:147
+#, php-format
+msgid "Normal (O deg)"
+msgstr "Normal (0 grados)"
+
+#: lib/Exif.php:289
+msgid "Not defined"
+msgstr "No está definido"
+
+#: image.php:126 img/upload.php:22
+msgid "Not found or error accessing gallery."
+msgstr "No se ha encontrado o error al acceder a la galería."
+
+#: config/.bak/prefs.php.dist:88
+msgid "Number of groups per page"
+msgstr "Número de grupos por página"
+
+#: config/.bak/prefs.php.dist:80
+msgid "Number of tiles per page"
+msgstr "Número de miniaturas por página"
+
+#: config/.bak/prefs.php.dist:72
+msgid "Number of tiles per row"
+msgstr "Número de miniaturas por fila"
+
+#: report.php:50
+msgid "Offensive content"
+msgstr "Contenido ofensivo"
+
+#: lib/Exif.php:290
+msgid "One Chip Color Area Sensor"
+msgstr "Sensor de área de color monochip"
+
+#: perms.php:66
+msgid ""
+"Only the owner or system administrator may change ownership or owner "
+"permissions for a share"
+msgstr ""
+"Sólo el propietario o un administrador de sistemas puede cambiar la "
+"propiedad o los privilegios de propietario de un recurso compartido"
+
+#: lib/Exif.php:234
+msgid "Other"
+msgstr "Otro"
+
+#: config/.bak/prefs.php.dist:12 config/.bak/prefs.php.dist:28
+msgid "Other Options"
+msgstr "Otras opciones"
+
+#: templates/group/owner.inc:51
+#, php-format
+msgid "Owned by: %s <br />"
+msgstr "Propietario: %s <br />"
+
+#: templates/tile/gallery.inc:11 config/.bak/prefs.php.dist:60
+msgid "Owner"
+msgstr "Propietario"
+
+#: lib/Ansel.php:1297
+msgid "Parent does not exist."
+msgstr "No existe el padre."
+
+#: lib/Exif.php:233
+msgid "Partial"
+msgstr "Parcial"
+
+#: templates/xppublish/login.inc:14
+msgid "Password"
+msgstr "Contraseña"
+
+#: templates/view/slideshow.inc:43
+msgid "Pause"
+msgstr "Pausa"
+
+#: templates/view/slideshow.inc:43
+msgid "Pause Slideshow"
+msgstr "Pausar presentación"
+
+#: lib/Block/my_galleries.php:49
+msgid "Photo Count"
+msgstr "Número de fotos"
+
+#: lib/Exif.php:330
+msgid "Photo Description"
+msgstr "Descripción de la foto"
+
+#: browse.php:18
+msgid "Photo Galleries"
+msgstr "Galerías de fotos"
+
+#: lib/Exif.php:329
+msgid "Photo Type"
+msgstr "Tipo de foto"
+
+#: templates/xppublish/javascript.inc:63
+msgid "Photo Upload"
+msgstr "Cargar fotografías"
+
+#: lib/Ansel.php:3692
+msgid "Photo not found"
+msgstr "No se ha encontrado la foto"
+
+#: rss.php:146
+#, php-format
+msgid "Photos by %s"
+msgstr "Fotos de %s"
+
+#: lib/Ansel.php:2524 lib/Ansel.php:3728
+msgid "Photos not found"
+msgstr "No se han encontrado las fotos"
+
+#: rss.php:176 rss.php:182
+#, php-format
+msgid "Photos tagged with %s on %s"
+msgstr "Fotos etiquetadas con %s en %s"
+
+#: templates/view/image.inc:52 templates/view/image.inc:75
+#: templates/view/slideshow.inc:42
+msgid "Play"
+msgstr "Reproducir"
+
+#: xppublish.php:54
+msgid "Please enter your Username and Password."
+msgstr "Introduzca su nombre de usuario y contraseña."
+
+#: scripts/create_serialized_styles.php:71 config/.bak/prefs.php.dist:131
+msgid "Polaroid"
+msgstr "Polaroid"
+
+#: xppublish.php:99 xppublish.php:155
+msgid "Press the \"Back\" button and try again."
+msgstr "Pulse el botón \"Retroceder\" y pruebe otra vez."
+
+#: scripts/create_serialized_styles.php:48 config/.bak/prefs.php.dist:128
+msgid "Pretty Thumbnails"
+msgstr "Miniaturas bonitas"
+
+#: scripts/create_serialized_styles.php:56 config/.bak/prefs.php.dist:129
+msgid "Pretty Thumbnails on White Background"
+msgstr "Miniaturas bonitas en fondo blanco"
+
+#: image.php:427
+#, php-format
+msgid "Preview changes for %s :: %s"
+msgstr "Vista previa de los cambios de %s :: %s"
+
+#: templates/view/image.inc:53 templates/view/image.inc:54
+#: templates/view/image.inc:76 templates/view/image.inc:77
+#: templates/view/slideshow.inc:44 templates/view/slideshow.inc:45
+msgid "Previous"
+msgstr "Anterior"
+
+#: lib/Exif.php:120
+msgid "Program"
+msgstr "Programa"
+
+#: templates/view/image.inc:24 templates/view/slideshow.inc:21
+#: templates/tile/image.inc:19
+msgid "Properties"
+msgstr "Propiedades"
+
+#: templates/xppublish/list.inc:7
+msgid "Publish Photos to:"
+msgstr "Publicar fotos en:"
+
+#: xppublish.php:35
+#, php-format
+msgid "Publish to %s"
+msgstr "Publicar en %s"
+
+#: xppublish.php:24
+#, php-format
+msgid "Publish your photos to %s on %s."
+msgstr "Publicar las fotografías en %s de %s."
+
+#: lib/Block/gallery.php:33 lib/Block/gallery.php:52
+msgid "Random Gallery"
+msgstr "Galería aleatoria"
+
+#: lib/Block/random_photo.php:3 lib/Block/random_photo.php:26
+msgid "Random photo"
+msgstr "Foto aleatoria"
+
+#: templates/gallery/delete_confirmation.inc:10
+msgid "Really Delete"
+msgstr "Eliminar realmente"
+
+#: templates/gallery/delete_confirmation.inc:10
+msgid "Really Empty"
+msgstr "Vaciar realmente"
+
+#: templates/gallery/delete_confirmation.inc:2
+#, php-format
+msgid "Really delete %s?"
+msgstr "¿Eliminar realmente %s?"
+
+#: templates/gallery/delete_confirmation.inc:5
+msgid ""
+"Really delete this gallery and all of the photos in it? This action cannot "
+"be undone."
+msgstr ""
+"¿Eliminar realmente esta galería y todas las fotos que contenga? Esta acción "
+"no se puede deshacer."
+
+#: templates/gallery/delete_confirmation.inc:2
+#, php-format
+msgid "Really empty %s?"
+msgstr "¿Vaciar realmente %s?"
+
+#: templates/gallery/delete_confirmation.inc:5
+msgid ""
+"Really empty this gallery and all of the photos in it? This action cannot be "
+"undone."
+msgstr ""
+"¿Vaciar realmente esta galería y todas las fotos que contenga? Esta acción "
+"no se puede deshacer."
+
+#: lib/Block/recent_comments.php:66
+#, php-format
+msgid "Recent Comments In %s"
+msgstr "Comentarios recientes sobre %s"
+
+#: lib/Block/recent_comments.php:5
+msgid "Recent Photo Comments"
+msgstr "Comentarios recientes de la foto"
+
+#: lib/Widget/Links.php:23
+#, php-format
+msgid "Recent photos by %s"
+msgstr "Fotos recientes de %s"
+
+#: lib/Widget/Links.php:25
+#, php-format
+msgid "Recent photos in %s"
+msgstr "Fotos recientes en %s"
+
+#: lib/Block/recently_added.php:3
+msgid "Recently Added Photos"
+msgstr "Fotos añadidas recientemente"
+
+#: lib/Block/recently_added.php:67
+#, php-format
+msgid "Recently Added Photos From %s"
+msgstr "Fotos añadidas recientemente desde %s"
+
+#: rss.php:151
+#, php-format
+msgid "Recently added photos by %s on %s"
+msgstr "Fotos añadidas recientemente por %s en %s"
+
+#: rss.php:56 rss.php:61
+#, php-format
+msgid "Recently added photos on %s"
+msgstr "Fotografías añadidas recientemente a %s"
+
+#: lib/Exif.php:272
+msgid "Red Eye"
+msgstr "Ojos rojos"
+
+#: lib/Exif.php:278
+msgid "Red Eye, Auto-Mode"
+msgstr "Ojos rojos, modo automático"
+
+#: lib/Exif.php:280
+msgid "Red Eye, Auto-Mode, Return light detected"
+msgstr "Ojos rojos, modo automático, detección de luz reflejada"
+
+#: lib/Exif.php:279
+msgid "Red Eye, Auto-Mode, Return light not detected"
+msgstr "Ojos rojos, modo automático, sin detección de luz reflejada"
+
+#: lib/Exif.php:275
+msgid "Red Eye, Compulsory Flash"
+msgstr "Ojos rojos, flash obligatorio"
+
+#: lib/Exif.php:277
+msgid "Red Eye, Compulsory Flash, Return light detected"
+msgstr "Ojos rojos, flash obligatorio, detección de luz reflejada"
+
+#: lib/Exif.php:276
+msgid "Red Eye, Compulsory Flash, Return light not detected"
+msgstr "Ojos rojos, flash obligatorio, sin detección de luz reflejada"
+
+#: lib/Exif.php:274
+msgid "Red Eye, Return light detected"
+msgstr "Ojos rojos, detección de luz reflejada"
+
+#: lib/Exif.php:273
+msgid "Red Eye, Return light not detected"
+msgstr "Ojos rojos, sin detección de luz reflejada"
+
+#: templates/group/header.inc:8 templates/list/header.inc:5
+#: templates/view/list.inc:5
+msgid "Refresh List"
+msgstr "Actualizar lista"
+
+#: templates/view/results.inc:144
+msgid "Related Tags"
+msgstr "Etiquetas relacionadas"
+
+#: lib/Tags.php:568
+#, php-format
+msgid "Remove %s from search"
+msgstr "Eliminar %s de la búsqueda"
+
+#: lib/Widget/Tags.php:90 lib/XRequest/TagActions.php:114
+msgid "Remove Tag"
+msgstr "Eliminar etiqueta"
+
+#: report.php:46 report.php:66 templates/view/image.inc:44
+#: lib/Widget/Actions.php:78
+msgid "Report"
+msgstr "Denunciar"
+
+#: lib/Report.php:84
+msgid "Report by user"
+msgstr "Denunciante"
+
+#: lib/Report.php:28
+msgid "Report dirver does not exists."
+msgstr "No existe el dispositivo de denuncias."
+
+#: report.php:60 report.php:75
+msgid "Report reason"
+msgstr "Razón de la denuncia"
+
+#: report.php:59 report.php:74
+msgid "Report type"
+msgstr "Tipo de denuncia"
+
+#: lib/Widget/Actions.php:67
+msgid "Reset all thumbnails"
+msgstr "Reiniciar todas las miniaturas"
+
+#: lib/Widget/Actions.php:71
+msgid "Reset default photo"
+msgstr "Reiniciar foto por omisión"
+
+#: image.php:75 config/.bak/prefs.php.dist:158
+msgid "Right"
+msgstr "Derecha"
+
+#: templates/image/edit_image.inc:24
+msgid "Rotate 180"
+msgstr "Girar 180"
+
+#: templates/image/edit_image.inc:23
+msgid "Rotate Left"
+msgstr "Girar a la izquierda"
+
+#: templates/image/edit_image.inc:25
+msgid "Rotate Right"
+msgstr "Girar a la derecha"
+
+#: image.php:31 image.php:54
+msgid "Save"
+msgstr "Guardar"
+
+#: templates/captions/captions.inc:47
+msgid "Save Captions"
+msgstr "Guardar comentarios"
+
+#: templates/image/preview_image.inc:19 templates/image/preview_image.inc:20
+msgid "Save Change"
+msgstr "Guardar cambio"
+
+#: templates/gallery/gallery.inc:69
+msgid "Save Gallery"
+msgstr "Guardar galería"
+
+#: image.php:216
+msgid "Save Photo"
+msgstr "Guardar foto"
+
+#: lib/Views/Results.php:85
+#, php-format
+msgid "Searching %s's photos tagged: "
+msgstr "Buscando fotos de %s con etiquetas: "
+
+#: lib/Views/Results.php:85
+msgid "Searching all photos tagged: "
+msgstr "Buscando todas las fotos con etiquetas: "
+
+#: templates/view/results.inc:93 templates/view/gallery.inc:92
+msgid "Select All"
+msgstr "Seleccionar todas"
+
+#: templates/view/results.inc:94 templates/view/gallery.inc:93
+msgid "Select None"
+msgstr "Anular selección"
+
+#: templates/tile/image.inc:7
+msgid "Select for Action"
+msgstr "Seleccione una acción"
+
+#: templates/view/results.inc:92 templates/view/gallery.inc:91
+#, php-format
+msgid "Select: %s, %s"
+msgstr "Selección: %s, %s"
+
+#: templates/view/results.inc:105 templates/view/gallery.inc:105
+msgid "Selected photos to"
+msgstr "Fotos seleccionadas a"
+
+#: lib/Forms/Ecard.php:25
+msgid "Send"
+msgstr "Enviar"
+
+#: img/ecard.php:104
+#, php-format
+msgid "Send Ecard :: %s"
+msgstr "Enviar Ecard :: %s"
+
+#: templates/view/image.inc:32 templates/view/slideshow.inc:31
+msgid "Send an Ecard"
+msgstr "Enviar una Ecard"
+
+#: lib/Forms/Ecard.php:41
+msgid "Send ecard to the following address:"
+msgstr "Enviar ecard a la dirección siguiente:"
+
+#: lib/Exif.php:352
+msgid "Sensing Method"
+msgstr "Método de detección"
+
+#: templates/gallery/gallery.inc:62
+msgid "Separate tags with commas."
+msgstr "Separar etiquetas con comas."
+
+#: lib/Forms/Upload.php:52
+msgid "Seperate tags with commas."
+msgstr "Separar etiquetas con comas."
+
+#: lib/Widget/Actions.php:64
+msgid "Set captions"
+msgstr "Definir comentarios"
+
+#: lib/Widget/Actions.php:74
+msgid "Set permissions"
+msgstr "Definir permisos"
+
+#: scripts/create_serialized_styles.php:63 config/.bak/prefs.php.dist:130
+msgid "Shadowed Thumbnails on White Background"
+msgstr "Miniaturas sombreadas en fondo blanco"
+
+#: config/.bak/prefs.php.dist:96
+msgid "Show EXIF data"
+msgstr "Mostrar datos EXIF"
+
+#: lib/Exif.php:339
+msgid "Shutter Speed"
+msgstr "Velocidad de obturación"
+
+#: lib/Widget/SimilarPhotos.php:27
+msgid "Similar Photos"
+msgstr "Fotos similares"
+
+#: lib/Forms/Upload.php:43
+msgid "Single Photo"
+msgstr "Una foto"
+
+#: templates/gallery/gallery.inc:55
+#, php-format
+msgid ""
+"Slugs allows direct access to this gallery by visiting: %s. <br /> Slug "
+"names may contain only letters, numbers or the _ (underscore) character."
+msgstr ""
+"El ID de la galería (slugname) permite acceder a esta galería visitando: %s. "
+"<br /> Los IDs sólo pueden contener letras, números o el carácter _ "
+"(subrayado)."
+
+#: image.php:65 config/.bak/prefs.php.dist:179
+msgid "Small"
+msgstr "Pequeña"
+
+#: lib/Widget/Actions.php:66
+msgid "Sort images"
+msgstr "Ordenar imágenes"
+
+#: lib/Exif.php:230
+msgid "Spot"
+msgstr "Punto"
+
+#: templates/view/image.inc:52 templates/view/image.inc:75
+#: templates/view/slideshow.inc:42
+msgid "Start Slideshow"
+msgstr "Iniciar presentación"
+
+#: scripts/ansel.php:209 scripts/ansel.php:292
+#: scripts/recursive_import.php:156
+#, php-format
+msgid "Storing photo \"%s\"..."
+msgstr "Almacenando imagen \"%s\"..."
+
+#: templates/gallery/gallery.inc:31
+msgid "Style for this gallery"
+msgstr "Estilo de la galería"
+
+#: scripts/ansel.php:232 scripts/recursive_import.php:166
+#, php-format
+msgid "Successfully added %d photo (%s) to gallery \"%s\" from \"%s\"."
+msgid_plural "Successfully added %d photos (%s) to gallery \"%s\" from \"%s\"."
+msgstr[0] ""
+"Se ha añadido correctamente %d foto  (%s) a la galería \"%s\" desde \"%s\"."
+msgstr[1] ""
+"Se han añadido correctamente %d fotos (%s) a la galería \"%s\" desde \"%s\"."
+
+#: scripts/ansel.php:218
+#, php-format
+msgid "Successfully added photo \"%s\" to gallery \"%s\"."
+msgstr "Se ha añadido correctamente la foto \"%s\" a la galería \"%s\"."
+
+#: gallery/delete.php:42
+#, php-format
+msgid "Successfully deleted %s."
+msgstr "Se ha eliminado correctamente %s."
+
+#: lib/Exif.php:241
+msgid "Sunny"
+msgstr "Soleado"
+
+#: lib/Block/cloud.php:3 lib/Block/cloud.php:28
+msgid "Tag Cloud"
+msgstr "Conjunto de etiquetas"
+
+#: image.php:39 lib/Widget/Tags.php:19 lib/Forms/Upload.php:52
+msgid "Tags"
+msgstr "Etiquetas"
+
+#: report.php:49
+msgid "Terms and conditions infringement"
+msgstr "Infracción de los términos y condiciones"
+
+#: scripts/ansel.php:259
+#, php-format
+msgid "The directory \"%s\" doesn't exist."
+msgstr "No existe el directorio \"%s\"."
+
+#: scripts/ansel.php:237
+#, php-format
+msgid "The directory \"%s\" had no valid photos."
+msgstr "El directorio \"%s\" no contiene fotos válidas."
+
+#: scripts/ansel.php:276
+#, php-format
+msgid "The directory \"%s\" is empty."
+msgstr "El directorio \"%s\" está vacío."
+
+#: lib/Ansel.php:485
+#, php-format
+msgid "The file \"%s\" doesn't exist."
+msgstr "El archivo \"%s\" no existe."
+
+#: img/upload.php:161 lib/api.php:249
+msgid "The file you uploaded does not appear to be a valid photo."
+msgstr "El archivo que cargó no parece ser una foto válida."
+
+#: scripts/recursive_import.php:125
+#, php-format
+msgid "The gallery \"%s\" (%s) was created successfully."
+msgstr "Se ha creado correctamente la galería \"%s\" (%s)."
+
+#: lib/Ansel.php:144
+#, php-format
+msgid "The gallery \"%s\" could not be created: %s"
+msgstr "No se pudo crear la galería \"%s\": %s"
+
+#: gallery.php:227 xppublish.php:129 scripts/ansel.php:153
+#: scripts/recursive_import.php:123
+#, php-format
+msgid "The gallery \"%s\" couldn't be created: %s"
+msgstr "No se pudo crear la galería \"%s\": %s"
+
+#: gallery.php:233 xppublish.php:133 scripts/ansel.php:158
+#, php-format
+msgid "The gallery \"%s\" was created successfully."
+msgstr "Se ha creado correctamente la galería \"%s\"."
+
+#: gallery.php:184
+msgid "The gallery was saved."
+msgstr "Se guardó la galería."
+
+#: gallery.php:279
+msgid "The gallery's default photo has successfully been reset."
+msgstr "Se ha reiniciado correctamente la foto por omisión de la galería."
+
+#: gallery.php:294
+msgid "The gallery's thumbnails have successfully been reset."
+msgstr "Se han reiniciado correctamente las miniaturas de la galería."
+
+#: lib/Ansel.php:121
+#, php-format
+msgid "The slug, %s, already exists."
+msgstr "Ya existe el ID %s de galería."
+
+#: img/upload.php:185
+msgid ""
+"The uploaded file appears to be empty. It may not exist on your computer."
+msgstr ""
+"El archivo cargado parece estar vacío. Puede que no exista en el ordenador."
+
+#: list.php:76 lib/Block/random_photo.php:35 lib/Views/List.php:138
+msgid "There are no photo galleries available."
+msgstr "No hay galerías de fotografías disponibles."
+
+#: gallery.php:83
+#, php-format
+msgid "There are no photos in %s to download."
+msgstr "No hay fotos en %s para descargar."
+
+#: templates/view/results.inc:115 templates/view/gallerysimple.inc:7
+#: templates/view/gallery.inc:110 templates/captions/captions.inc:49
+msgid "There are no photos in this gallery."
+msgstr "En esta galería no hay fotos."
+
+#: scripts/ansel.php:295
+#, php-format
+msgid "There was a problem adding the photo \"%s\" to gallery \"%s\"."
+msgstr "Se produjo un problema al añadir la foto \"%s\" a la galería \"%s\"."
+
+#: scripts/ansel.php:212
+#, php-format
+msgid "There was a problem adding the photo \"%s\" to gallery \"%s\": %s"
+msgstr ""
+"Se produjo un problema al añadir la foto \"%s\" a la galería \"%s\": %s."
+
+#: lib/api.php:406
+#, php-format
+msgid "There was a problem deleting %s: %s"
+msgstr "Se produjo un problema al eliminar %s: %s"
+
+#: gallery/delete.php:40
+#, php-format
+msgid "There was a problem deleting %s: %s."
+msgstr "Se produjo un problema al eliminar %s: %s."
+
+#: image.php:496 lib/Views/Results.php:125
+#, php-format
+msgid "There was a problem deleting photos: %s"
+msgstr "Se produjo un problema eliminando fotos: %s"
+
+#: img/upload.php:95 img/upload.php:147 img/upload.php:171
+#, php-format
+msgid "There was a problem saving the photo: %s"
+msgstr "Se produjo un problema al guardar la foto: %s"
+
+#: xppublish.php:201
+msgid "There was a problem uploading the photo."
+msgstr "Se produjo un problema al cargar la foto."
+
+#: img/upload.php:183
+#, php-format
+msgid "There was a problem uploading the photo: %s"
+msgstr "Se produjo un problema al cargar la foto: %s"
+
+#: img/upload.php:58 img/upload.php:80 img/upload.php:111 img/upload.php:132
+#, php-format
+msgid "There was an error processing the uploaded archive: %s"
+msgstr "Se produjo un error al procesar el archivo cargado: %s"
+
+#: lib/Ansel.php:3635
+msgid "There was an error retrieving the galleries."
+msgstr "Se produjo un error al obtener las galerías."
+
+#: lib/Ansel.php:3609
+msgid "There was an error retrieving the gallery."
+msgstr "Se produjo un error al obtener la galería."
+
+#: img/ecard.php:96
+#, php-format
+msgid "There was an error sending your message: %s"
+msgstr "Se produjo un error enviando el mensaje: %s"
+
+#: lib/ImageView.php:77
+#, php-format
+msgid ""
+"This install does not support the %s feature. Please contact your "
+"administrator."
+msgstr ""
+"Esta instalación no soporta la característica %s. Póngase en contacto con el "
+"administrador."
+
+#: lib/Exif.php:292
+msgid "Three Chip Color Area Sensor"
+msgstr "Sensor de área de color de triple chip"
+
+#: image.php:64 config/.bak/prefs.php.dist:178
+msgid "Tiny"
+msgstr "Minúscula"
+
+#: image.php:80 config/.bak/prefs.php.dist:167
+msgid "Top"
+msgstr "Superior"
+
+#: lib/Exif.php:294
+msgid "Trilinear Sensor"
+msgstr "Sensor trilineal"
+
+#: lib/Exif.php:243
+msgid "Tungsten"
+msgstr "Tungsteno"
+
+#: lib/Exif.php:122
+msgid "Tv-priority"
+msgstr "Prioridad a la exposición"
+
+#: lib/Exif.php:291
+msgid "Two Chip Color Area Sensor"
+msgstr "Sensor de área de color de dos chips"
+
+#: lib/Ansel.php:154
+msgid "Unable to create gallery."
+msgstr "Incapaz de crear la galería."
+
+#: lib/Widget.php:70 lib/ImageView.php:82
+#, php-format
+msgid "Unable to load the definition of %s."
+msgstr "Incapaz de cargar la definición de %s."
+
+#: rss.php:37
+msgid "Unable to retrieve requested feed"
+msgstr "Incapaz de recuperar la suscripción solicitada"
+
+#: lib/Exif.php:305
+msgid "Uncalibrated"
+msgstr "Sin calibrar"
+
+#: templates/gallery/gallery.inc:70
+msgid "Undo Changes"
+msgstr "Deshacer cambios"
+
+#: list.php:56 templates/group/category.inc:9 lib/Views/List.php:114
+msgid "Unfiled"
+msgstr "Sin archivar"
+
+#: lib/Exif.php:227
+msgid "Unknown"
+msgstr "Desconocido"
+
+#: gallery.php:125
+msgid "Unknown gallery"
+msgstr "Galería desconocida"
+
+#: lib/Ansel.php:111 lib/tests/GalleryTest.php:42
+msgid "Unnamed"
+msgstr "Sin nombre"
+
+#: xppublish.php:141
+msgid "Untitled"
+msgstr "Sin título"
+
+#: perms.php:238
+#, php-format
+msgid "Updated %s."
+msgstr "%s actualizado."
+
+#: lib/Forms/Upload.php:39
+msgid "Upload"
+msgstr "Cargar"
+
+#: templates/xppublish/javascript.inc:63
+#, php-format
+msgid "Upload Photos to %s"
+msgstr "Cargar fotografías en %s"
+
+#: img/upload.php:31
+msgid "Upload new photos"
+msgstr "Cargar fotos"
+
+#: lib/Widget/Actions.php:60
+msgid "Upload photos"
+msgstr "Cargar fotos"
+
+#: lib/Exif.php:151
+#, php-format
+msgid "Upsidedown"
+msgstr "Voltear hacia abajo"
+
+#: lib/Exif.php:153
+#, php-format
+msgid "Upsidedown Mirrored"
+msgstr "Reflejar hacia abajo"
+
+#: scripts/ansel.php:315 scripts/recursive_import.php:185
+#, php-format
+msgid "Usage: %s [OPTIONS]..."
+msgstr "Uso: %s [OPCIONES]..."
+
+#: templates/xppublish/new.inc:17
+msgid "Use Default"
+msgstr "Usar valor por omisión"
+
+#: lib/Forms/Ecard.php:32
+msgid "Use the following return address:"
+msgstr "Utilice la siguiente dirección de devolución:"
+
+#: lib/Exif.php:350
+msgid "User Comment"
+msgstr "Comentario del usuario"
+
+#: templates/xppublish/login.inc:11
+msgid "Username"
+msgstr "Usuario"
+
+#: xppublish.php:50
+msgid "Username and Password are incorrect."
+msgstr "El nombre de usuario y la contraseña son incorrectos."
+
+#: scripts/recursive_import.php:79
+msgid "Username or password is incorrect."
+msgstr "El nombre de usuario o la contraseña son incorrectos."
+
+#: config/.bak/prefs.php.dist:202
+msgid "Users with edit permissions"
+msgstr "Usuarios con permisos de modificación"
+
+#: image.php:83 config/.bak/prefs.php.dist:170 config/.bak/prefs.php.dist:183
+msgid "Vertical Alignment"
+msgstr "Alineación vertical"
+
+#: lib/Block/gallery.php:75
+#, php-format
+msgid "View %s"
+msgstr "Ver %s"
+
+#: templates/view/results.inc:81
+msgid "View All Results"
+msgstr "Ver todos los resultados"
+
+#: lib/Block/random_photo.php:47
+msgid "View Photo"
+msgstr "Ver foto"
+
+#: templates/view/results.inc:81
+msgid "View Results from All Users"
+msgstr "Ver resultados de todos los usuarios"
+
+#: config/.bak/prefs.php.dist:52
+msgid "View to display by default"
+msgstr "Vista mostrada por omisión"
+
+#: image.php:377 image.php:383 templates/image/edit_image.inc:38
+msgid "Watermark"
+msgstr "Marca de agua"
+
+#: image.php:69
+msgid "Watermark Font"
+msgstr "Tipo de letra de marca de agua"
+
+#: config/.bak/prefs.php.dist:20 config/.bak/prefs.php.dist:21
+msgid "Watermark Options"
+msgstr "Opciones de marca de agua"
+
+#: lib/Exif.php:353
+msgid "White Balance"
+msgstr "Balance de blancos"
+
+#: lib/Exif.php:335
+msgid "Width"
+msgstr "Anchura"
+
+#: lib/Exif.php:337
+msgid "X Resolution"
+msgstr "Resolución X"
+
+#: lib/Exif.php:338
+msgid "Y Resolution"
+msgstr "Resolución Y"
+
+#: config/.bak/prefs.php.dist:191
+msgid "Yes"
+msgstr "Sí"
+
+#: lib/Ansel.php:2207
+#, php-format
+msgid "You are not allowed to copy photos to %s."
+msgstr "Carece de permisos para copiar fotos a %s."
+
+#: image.php:290
+msgid "You are not allowed to edit this photo."
+msgstr "Carece de permisos para modificar esta foto."
+
+#: lib/Ansel.php:2169
+#, php-format
+msgid "You are not allowed to move photos to %s."
+msgstr "Carece de permisos para trasladar fotos a %s."
+
+#: lib/Ansel.php:2171
+#, php-format
+msgid "You are not allowed to remove photos from %s."
+msgstr "Carece de permisos para eliminar fotos de %s."
+
+#: xppublish.php:91 xppublish.php:184
+msgid "You cannot add photos in that gallery."
+msgstr "No puede añadir fotografías a esta galería."
+
+#: scripts/ansel.php:132
+#, php-format
+msgid "You cannot add photos to the gallery \"%s\"."
+msgstr "No puede añadir fotos a la galería \"%s\"."
+
+#: gallery/delete.php:30
+#, php-format
+msgid "You cannot delete %s, it contains nested galleries."
+msgstr "No puede borrar %s, contiene galerías anidadas."
+
+#: lib/Views/Abstract.php:46
+msgid "You do not have permission to access this gallery."
+msgstr "Carece de permisos para acceder a esta galería."
+
+#: gallery.php:50 gallery.php:198
+#, php-format
+msgid "You do not have permission to add children to %s."
+msgstr "No dispone de privilegios para añadir descendientes a %s."
+
+#: lib/Ansel.php:2732
+msgid "You do not have permission to add tags to this gallery"
+msgstr "Carece de permisos para añadir etiquetas a esta galería"
+
+#: gallery/sort.php:27
+msgid "You do not have permission to change this gallery."
+msgstr "Carece de permisos para cambiar esta galería."
+
+#: lib/Ansel.php:2717
+msgid "You do not have permission to view this gallery"
+msgstr "Carece de permisos para ver esta galería"
+
+#: lib/api.php:844 lib/Block/recent_comments.php:149 lib/Block/gallery.php:111
+#: lib/Block/recently_added.php:160
+msgid "You do not have permission to view this gallery."
+msgstr "No dispone de privilegios para ver esta galería."
+
+#: lib/Ansel.php:3440
+msgid "You do not have permissions to add tags to this photo."
+msgstr "Carece de permisos para añadir etiquetas a esta foto."
+
+#: lib/Ansel.php:3419
+msgid "You do not have permissions to view this photo."
+msgstr "Carece de permisos para ver esta foto."
+
+#: img/ecard.php:69
+msgid ""
+"You have been sent an Ecard. To view the Ecard, you must be able to view "
+"text/html messages in your mail reader. If you are viewing this message, "
+"then most likely your mail reader does not support viewing text/html "
+"messages."
+msgstr ""
+"Se le ha enviado una Ecard. Para verla, tiene que se capaz de ver mensajes "
+"text/html en su lector de correo. Si está viendo este mensaje, significa "
+"probablemente que su lector de correo no permite ver mensajes text/html."
+
+#: list.php:72 lib/Views/List.php:131
+msgid "You have no photo galleries, add one!"
+msgstr "No dispone de galerías de fotos, ¡añada una!"
+
+#: templates/view/results.inc:46 templates/view/results.inc:60
+#: templates/view/gallery.inc:46 templates/view/gallery.inc:60
+msgid "You must choose a gallery to move photos to."
+msgstr "Tiene que seleccionar una galería a la que trasladar las fotos."
+
+#: lib/Ansel.php:37 lib/Ansel.php:45
+msgid "You must configure a VFS backend to use Ansel."
+msgstr "Tiene que configurar un motor VFS para Ansel."
+
+#: img/ecard.php:45
+msgid "You must enter an e-mail address to send the message to."
+msgstr ""
+"Tiene que introducir una dirección de correo a la que enviar el mensaje."
+
+#: img/ecard.php:40
+msgid "You must enter your e-mail address."
+msgstr "Tiene que introducir su dirección de correo."
+
+#: gallery.php:207
+msgid "You must provide a display name for your new gallery."
+msgstr "Tiene que indicar el nombre mostrado de su galería nueva."
+
+#: scripts/recursive_import.php:88
+msgid "You must specify a valid directory."
+msgstr "Tiene que indicar un directgorio válido."
+
+#: scripts/recursive_import.php:84
+msgid "You must specify a valid username and password."
+msgstr "Tiene que indicar un nombre de usuario y contraseña válidos."
+
+#: lib/Forms/Upload.php:67
+msgid "Zip File Upload"
+msgstr "Cargar archivo Zip"
+
+#: lib/Ansel.php:564
+msgid "_Galleries"
+msgstr "_Galerías"
+
+#: lib/Ansel.php:577
+msgid "_My Galleries"
+msgstr "_Mis galerías"
+
+#: lib/Ansel.php:589
+msgid "_New Gallery"
+msgstr "_Añadir galería"
+
+#: lib/Ansel.php:598
+msgid "_Print"
+msgstr "_Imprimir"
+
+#: templates/group/header.inc:15 templates/view/list.inc:11
+msgid "category"
+msgstr "categoría"
+
+#: xppublish.php:191
+msgid "image"
+msgstr "imagen"
+
+#: templates/group/header.inc:18
+msgid "none"
+msgstr "ninguna"
+
+#: templates/group/header.inc:13 templates/view/list.inc:10
+msgid "owner"
+msgstr "propietario"
+
+#: lib/Exif.php:302
+msgid "sRGB"
+msgstr "sRGB"
+
+#: lib/Forms/Upload.php:56
+msgid "with a total of"
+msgstr "con un total de"
diff --git a/ansel/po/fi_FI.po b/ansel/po/fi_FI.po
new file mode 100644 (file)
index 0000000..3060917
--- /dev/null
@@ -0,0 +1,1981 @@
+# Finnish translation for Ansel.
+# Copyright
+# Leena Heino <liinu@uta.fi>, 2004.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Ansel 0.1-cvs\n"
+"Report-Msgid-Bugs-To: dev@lists.horde.org\n"
+"POT-Creation-Date: 2005-03-14 11:02+0200\n"
+"PO-Revision-Date: 2004-12-10 12:59+0200\n"
+"Last-Translator: Leena Heino <liinu@uta.fi>\n"
+"Language-Team: Finnish <i18n@lists.horde.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=iso-8859-1\n"
+"Content-Transfer-Encoding: 8-bit\n"
+
+#: image.php:287
+#, php-format
+msgid "%d images were successfully extracted and uploaded."
+msgstr "%d kuvaa purettiin ja tuotiin onnistuneesti"
+
+#: templates/group/owner.inc:51 templates/group/category.inc:51
+#, php-format
+msgid "%s Galleries"
+msgstr "%s galleriat"
+
+#: lib/Exif.php:262
+#, php-format
+msgid "%s bytes"
+msgstr "%s tavua"
+
+#: templates/list/images.inc:4
+#, php-format
+msgid "%s images"
+msgstr "%s kuvaa"
+
+#: lib/Exif.php:123 lib/Exif.php:129
+#, php-format
+msgid "%s pixels"
+msgstr "%s pikseliä"
+
+#: lib/Exif.php:145 lib/Exif.php:160
+#, php-format
+msgid "%s sec"
+msgstr "%s sek"
+
+#: templates/view/subgallery_header.inc:7
+#, php-format
+msgid "%s to %s of %s subgalleries"
+msgstr "%s - %s / %s aligalleriaa"
+
+#: list.php:46 templates/group/owner.inc:12 lib/Ansel.php:444
+#, php-format
+msgid "%s's Galleries"
+msgstr "%s:n galleriat"
+
+#: gallery.php:257
+#, php-format
+msgid ""
+"'%s' is an invalid unique name; it may already be taken. Please choose a "
+"different unique name."
+msgstr ""
+"'%s' ei ole yksikäsitteinen nimi; Nimi voi olla jo varattu. Valitse uusi "
+"yksikäsitteinen nimi."
+
+#: lib/Gallery.php:1141
+#, php-format
+msgid "(c) %s %s"
+msgstr "(c) %s %s"
+
+#: templates/xppublish/new.inc:14
+msgid "(must be unique)"
+msgstr "(pitää olla yksikäsitteinen)"
+
+#: scripts/ansel.php:299
+msgid "-a, --add[=filename]         Add local file to selected gallery"
+msgstr ""
+"-a, --add[=tiedosto]         Lisää paikallinen tiedosto valittuun galleriaan"
+
+#: scripts/ansel.php:297
+msgid "-c, --create[=shortname/name/description]  Create gallery (and use it)"
+msgstr "-c, --create[=lyhytnimi/nimi/kuvaus] Luo galleria (ja käytä sitä)"
+
+#: scripts/ansel.php:300
+msgid ""
+"-d, --dir[=directory]        Add all files from the directory to the "
+"selected\n"
+"                             gallery"
+msgstr ""
+"-d, --dir[=hakemisto]        Lisää kaikki hakemiston tiedostot "
+"valittuun                              galleriaan"
+
+#: scripts/ansel.php:298
+msgid "-g, --gallery[=shortname]    Select gallery to use"
+msgstr "-g, --gallery[=lyhytnimi]    Valitse käytettävä galleria"
+
+#: scripts/ansel.php:295
+msgid "-h, --help                   Show this help"
+msgstr "-h, --help                   Näytä tämä ohje"
+
+#: scripts/ansel.php:296
+msgid ""
+"-l, --list                   List galleries or images (if combined with -g)"
+msgstr ""
+"-l, --list                   Listaa galleriat tai kuvat (jos -g on myös "
+"asennettu)"
+
+#: scripts/ansel.php:302
+msgid "-p, --password[=password]    Horde login password"
+msgstr "-p, --password[=salasana]    Horde salasana"
+
+#: scripts/ansel.php:301
+msgid "-u, --username[=username]    Horde login username"
+msgstr "-u, --username[=username]    Horde tunnus"
+
+#: templates/group/owner.inc:51 templates/group/category.inc:51
+#, fuzzy
+msgid "1 Gallery"
+msgstr "Galleria"
+
+#: templates/list/images.inc:4
+msgid "1 image"
+msgstr "1 kuva"
+
+#: image.php:285
+msgid "1 image was successfully extracted and uploaded."
+msgstr "1 kuva purettiin ja tuotiin onnistuneesti"
+
+#: templates/slideshow/slideshow.inc:233
+msgid "1 minute"
+msgstr "1 minuutti"
+
+#: templates/slideshow/slideshow.inc:224
+msgid "1 second"
+msgstr "1 sekuntti"
+
+#: templates/slideshow/slideshow.inc:229
+msgid "10 seconds"
+msgstr "10 sekunttia"
+
+#: templates/slideshow/slideshow.inc:230
+msgid "15 seconds"
+msgstr "15 sekunttia"
+
+#: templates/slideshow/slideshow.inc:225
+msgid "2 seconds"
+msgstr "2 sekunttia"
+
+#: templates/slideshow/slideshow.inc:226
+msgid "3 seconds"
+msgstr "3 sekunttia"
+
+#: templates/slideshow/slideshow.inc:231
+msgid "30 seconds"
+msgstr "30 sekunttia"
+
+#: templates/slideshow/slideshow.inc:227
+msgid "4 seconds"
+msgstr "4 sekunttia"
+
+#: templates/slideshow/slideshow.inc:232
+msgid "45 seconds"
+msgstr "45 sekunttia"
+
+#: templates/slideshow/slideshow.inc:228
+msgid "5 seconds"
+msgstr "5 sekunttia"
+
+#: lib/Exif.php:138
+#, php-format
+msgid "90 deg CCW"
+msgstr "90 aseteen CCW"
+
+#: lib/Exif.php:139
+#, php-format
+msgid "90 deg CCW Mirrored"
+msgstr "90 asteen CCW peilikuvana"
+
+#: lib/Exif.php:140
+#, php-format
+msgid "90 deg CW"
+msgstr "90 asteen CW"
+
+#: lib/Exif.php:137
+#, php-format
+msgid "90 deg CW Mirrored"
+msgstr "90 asteen CW peilikuvana"
+
+#: lib/Exif.php:115
+msgid "A-DEP"
+msgstr "A-DEP"
+
+#: gallery.php:311 lib/api.php:254
+#, php-format
+msgid "Access denied deleting gallery %s."
+msgstr "Gallerian %s poistaminen on estetty."
+
+#: image.php:533 image.php:566
+#, php-format
+msgid "Access denied deleting image %s."
+msgstr "Kuvan %s poistaminen on estetty."
+
+#: gallery.php:81
+#, php-format
+msgid "Access denied downloading %s."
+msgstr "%s tallentaminen on estetty."
+
+#: gallery.php:138
+msgid "Access denied editing galleries."
+msgstr "Gallerian muokkaaminen on estetty."
+
+#: gallery.php:194
+#, php-format
+msgid "Access denied saving gallery %s."
+msgstr "Galleriaan %s tallettaminen on estetty."
+
+#: image.php:198 image.php:378
+#, php-format
+msgid "Access denied saving image to %s."
+msgstr "Kuvien tallettaminen %s:ään on estetty"
+
+#: captions.php:29
+#, php-format
+msgid "Access denied setting captions for %s."
+msgstr "Kuvatekstin laittaminen %s on estetty."
+
+#: image.php:160
+msgid "Add Image"
+msgstr "Lisää kuva"
+
+#: templates/view/gallery.inc:93
+msgid "Add Subgallery"
+msgstr "Lisää aligalleria"
+
+#: view.php:112
+msgid "Add a comment"
+msgstr "Lisää kommentti"
+
+#: gallery.php:40 gallery.php:259 gallery.php:267
+msgid "Adding A New Gallery"
+msgstr "Lisätään uuteen galleriaan"
+
+#: gallery.php:73
+#, php-format
+msgid "Adding A Subgallery to %s"
+msgstr "Lisätään aligalleria %s:ään"
+
+#: search.php:83
+msgid "After"
+msgstr "Jälkeen"
+
+#: templates/view/navbar-gallery.inc:5
+msgid "All"
+msgstr "Kaikki"
+
+#: lib/Ansel.php:47
+#, php-format
+msgid "An error occurred listing galleries: %s"
+msgstr "Tapahtui virhe listattaessa gallerioita: %s"
+
+#: lib/Exif.php:321
+msgid "Aperture"
+msgstr "Aukko"
+
+#: templates/gallery/gallery.inc:96
+msgid "Are you sure you want to delete all of the images in this gallery?"
+msgstr "Oletko varma että haluat poistaa kaikki kuvat tästä galleriasta?"
+
+#: templates/view/gallery.inc:44
+msgid "Are you sure you want to delete the selected images?"
+msgstr "Oletko varma että haluat poistaa valitut kuvat?"
+
+#: templates/view/gallery.inc:102
+msgid ""
+"Are you sure you want to delete this gallery? All images in it will be "
+"deleted."
+msgstr ""
+"Oletko varma että haluat poistaa tämän gallerian? Kaikki siinä olevat kuvat "
+"poistetaan."
+
+#: lib/Exif.php:334
+msgid "Artist"
+msgstr "Taiteilija"
+
+#: config/prefs.php.dist:55
+msgid "Ascending"
+msgstr "Nouseva"
+
+#: perms.php:50
+msgid "Attempt to edit a non-existent share."
+msgstr "Yritettiin muokata olematonta jakoa."
+
+#: image.php:343
+#, php-format
+msgid "Attributes for %s :: %s"
+msgstr "Lisätiedot %s:lle :: %s"
+
+#: config/prefs.php.dist:112
+msgid "Automatically generate unique name for new galleries?"
+msgstr "Tee gallerioille automaattisesti yksilöivä nimi."
+
+#: lib/Exif.php:113
+msgid "Av-priority"
+msgstr "Av-prioriteetti"
+
+#: lib/Exif.php:205
+msgid "Average"
+msgstr "Keskiarvo"
+
+#: search.php:84
+msgid "Before"
+msgstr "ennen"
+
+#: search.php:53
+msgid "Begins With"
+msgstr "Alkaa"
+
+#: search.php:82
+msgid "Between"
+msgstr "Välillä"
+
+#: slideshow.php:35
+msgid "Blend"
+msgstr "Pehmennä"
+
+#: slideshow.php:36
+msgid "Blinds"
+msgstr "Kaihtimet"
+
+#: image.php:113
+msgid "Bottom"
+msgstr "Alin"
+
+#: lib/Exif.php:306
+msgid "Camera Make"
+msgstr "Kameran valmistaja"
+
+#: lib/Exif.php:307
+msgid "Camera Model"
+msgstr "Kameran malli"
+
+#: lib/Exif.php:332
+msgid "Camera Orientation"
+msgstr "Kameran asento"
+
+#: lib/Ansel.php:290
+#, php-format
+msgid "Can't get unknown file type '%s'."
+msgstr "Tuntematon tiedostotyyppi '%s'."
+
+#: templates/image/preview_image.inc:29
+msgid "Cancel Change"
+msgstr "Peruuta muutokset"
+
+#: image.php:37 image.php:65 templates/search/search.html:9
+msgid "Caption"
+msgstr "Kuvateksti"
+
+#: captions.php:52 templates/view/gallery.inc:95
+msgid "Caption Editor"
+msgstr "Kuvatekstieditori"
+
+#: templates/view/gallery.inc:95
+msgid "Captions"
+msgstr "Kuvatekstit"
+
+#: captions.php:47
+msgid "Captions Saved."
+msgstr "Kuvatekstiä talletettu."
+
+#: config/prefs.php.dist:66
+msgid "Category"
+msgstr "Kategoria"
+
+#: image.php:109
+msgid "Center"
+msgstr "Keskitetty"
+
+#: lib/Exif.php:206
+msgid "Center Weighted Average"
+msgstr "Keskipainotettu keskiarvo"
+
+#: config/prefs.php.dist:22
+msgid "Change your display options."
+msgstr "Voit muuttaa näkymän asetuksia"
+
+#: config/prefs.php.dist:30
+msgid "Change your gallery creation options."
+msgstr "Voit muuttaa miten gallerian luomiseen liittyviä asetuksia."
+
+#: config/prefs.php.dist:15
+msgid "Change your gallery sorting options."
+msgstr "Voit muttaa gallerian järjestykseen liittyviä asetuksia."
+
+#: slideshow.php:37
+msgid "Checkerboard"
+msgstr "Shakkilauta"
+
+#: templates/xppublish/list.inc:8
+msgid "Choose an existing gallery"
+msgstr "Valitse olemassaoleva galleria"
+
+#: templates/image/exif.inc:3
+msgid "Close"
+msgstr "Sulje"
+
+#: lib/Exif.php:219
+msgid "Cloudy"
+msgstr "Pilvinen"
+
+#: lib/Exif.php:270
+msgid "Color Sequential Area Sensor"
+msgstr "Värijärjestys tilasensori"
+
+#: lib/Exif.php:272
+msgid "Color Sequential Linear Sensor"
+msgstr "Värijärjestys lineaarisensori"
+
+#: lib/Exif.php:329
+msgid "Color Space"
+msgstr "Väriavaruus"
+
+#: ecard.php:42
+msgid "Comments:"
+msgstr "Kommentit:"
+
+#: lib/Exif.php:240
+msgid "Compulsory Flash"
+msgstr "Pakotettu salama"
+
+#: lib/Exif.php:242
+msgid "Compulsory Flash, Return light detected"
+msgstr "Pakotettu salama, heijastunutta valoa havaittu"
+
+#: lib/Exif.php:241
+msgid "Compulsory Flash, Return light not detected"
+msgstr "Pakotettu salama, heijastunutta valoa ei havaittu"
+
+#: search.php:55
+msgid "Contains"
+msgstr "Sisältää"
+
+#: image.php:614
+#, php-format
+msgid "Copied %s to %s."
+msgstr "Kopioitiin %s %s:ksi"
+
+#: templates/view/navbar-gallery.inc:19
+msgid "Copy"
+msgstr "Kopioi"
+
+#: lib/Exif.php:333
+msgid "Copyright"
+msgstr "Tekijänoikeus"
+
+#: scripts/ansel.php:165
+msgid "Couldn't list galleries."
+msgstr "Gallerioita ei voitu listata."
+
+#: scripts/ansel.php:42
+msgid "Couldn't read command-line options."
+msgstr "Komentoriviparametreja ei voitu lukea."
+
+#: templates/xppublish/list.inc:18
+msgid "Create"
+msgstr "Luo"
+
+#: templates/xppublish/new.inc:8
+msgid "Create a new Gallery"
+msgstr "Luo uusi galleria"
+
+#: templates/xppublish/list.inc:16
+msgid "Create a new gallery instead"
+msgstr "Luo uusi galleria sen sijaan"
+
+#: templates/list/images.inc:20
+msgid "Created"
+msgstr "Luotu"
+
+#: templates/view/gallery.inc:158 templates/view/navbar-image.inc:19
+#: templates/crop/crop.inc:44 lib/Ansel.php:485
+msgid "Crop"
+msgstr "Leikkaa"
+
+#: crop.php:56
+msgid "Crop Image"
+msgstr "Leikkaa kuvaa"
+
+#: templates/crop/crop.inc:55
+msgid "Cropped"
+msgstr "Leikattu"
+
+#: lib/Exif.php:223
+msgid "Custom"
+msgstr "Omatekoinen"
+
+#: image.php:102
+msgid "Custom Watermark"
+msgstr "Omatekoinen vesileima"
+
+#: config/prefs.php.dist:128
+msgid "Custom watermark to use for images"
+msgstr "Omatekoinen vesileima käytettäväksi kuvissa"
+
+#: config/prefs.php.dist:44
+msgid "Date Created"
+msgstr "Luontipäivämäärä"
+
+#: search.php:86
+msgid "Date Field"
+msgstr "Päivämääräkentät"
+
+#: search.php:61
+msgid "Date Fields"
+msgstr "Päiväyskentät"
+
+#: lib/Exif.php:312
+msgid "Date Image Digitized"
+msgstr "Digitalisoinnin päivämäärä"
+
+#: lib/Exif.php:310
+msgid "Date Image Modified"
+msgstr "Muokkauksen päivämäärä"
+
+#: lib/Exif.php:311
+msgid "Date Image Taken"
+msgstr "Kuvanoton päivämäärä"
+
+#: config/prefs.php.dist:45
+msgid "Date Modified"
+msgstr "Muokkauksen päivämäärä"
+
+#: search.php:87
+msgid "Date Operator"
+msgstr "Päivämäärätoiminto"
+
+#: search.php:36
+msgid "Date Uploaded"
+msgstr "Päivämäärä tallennettu"
+
+#: config/prefs.php.dist:46
+msgid "Default sorting criteria:"
+msgstr "Oletusjärjestys:"
+
+#: config/prefs.php.dist:57
+msgid "Default sorting direction:"
+msgstr "Oletusjärjestyksen suunta:"
+
+#: templates/slideshow/slideshow.inc:222
+msgid "Delay"
+msgstr "Viive"
+
+#: templates/view/gallery.inc:102 templates/view/navbar-gallery.inc:11
+#: templates/view/navbar-image.inc:23 templates/image/edit_image.inc:40
+msgid "Delete"
+msgstr "Poista"
+
+#: templates/view/gallery.inc:102
+msgid "Delete Gallery"
+msgstr "Poista galleria"
+
+#: image.php:537
+msgid "Deleted the image."
+msgstr "Poista kuva."
+
+#: config/prefs.php.dist:56
+msgid "Descending"
+msgstr "Laskeva"
+
+#: search.php:35 templates/list/gallery_headers.inc:22
+msgid "Description"
+msgstr "Kuvaus"
+
+#: slideshow.php:38
+msgid "Diagonal"
+msgstr "Diagonaalinen"
+
+#: config/prefs.php.dist:21
+msgid "Display Options"
+msgstr "Näkymän asetukset"
+
+#: templates/list/header.inc:7
+#, php-format
+msgid "Displaying from %s to %s of %s galleries"
+msgstr "Näytetään %s - %s / %s gallerioista"
+
+#: templates/group/header.inc:3
+#, php-format
+msgid "Displaying from %s to %s of %s groups"
+msgstr "Näytetään %s - %s / %s ryhmistä"
+
+#: templates/view/gallery.inc:78
+#, php-format
+msgid "Displaying from %s to %s of %s images"
+msgstr "Näytetään %s - %s / %s kuvista"
+
+#: templates/view/navbar-image.inc:23
+#, php-format
+msgid "Do you want to permanently delete %s?"
+msgstr "Haluatko lopullisesti poistaa %s"
+
+#: search.php:56
+msgid "Does Not Contain"
+msgstr "Ei sisällä"
+
+#: slideshow.php:39
+msgid "Doors"
+msgstr "Ovet"
+
+#: lib/Ansel.php:380
+msgid "Download"
+msgstr "Tallenna"
+
+#: templates/view/navbar-image.inc:32
+msgid "Download Full Image"
+msgstr "Tallenna koko kuva"
+
+#: templates/list/gallery_headers.inc:17
+#: templates/list/gallery_summaries.inc:11
+msgid "Download all images in a .zip file"
+msgstr "Tallenna kaikki kuvat .zip tiedostona"
+
+#: xppublish.php:210
+#, php-format
+msgid "ERROR: %s"
+msgstr "VIRHE: %s"
+
+#: lib/Exif.php:110
+msgid "Easy shooting"
+msgstr "Helppokuvaus"
+
+#: ecard.php:91
+msgid "Ecard - "
+msgstr "Ecard -"
+
+#: templates/view/gallery.inc:156 templates/view/navbar-image.inc:17
+#: lib/Ansel.php:480
+msgid "Edit"
+msgstr "Muokkaa"
+
+#: image.php:362
+#, php-format
+msgid "Edit %s :: %s"
+msgstr "Muokkaa %s :: %s"
+
+#: image.php:445
+msgid "Edit Image"
+msgstr "Muokkaa kuvaa"
+
+#: perms.php:241
+msgid "Edit Permissions"
+msgstr "Muokkaa oikeuksia"
+
+#: perms.php:244
+#, php-format
+msgid "Edit Permissions for %s"
+msgstr "Muokkaa '%s':n oikeuksia"
+
+#: image.php:212
+msgid "Edit an image"
+msgstr "Muokkaa kuvaa"
+
+#: image.php:173
+#, php-format
+msgid "Edit properties :: %s"
+msgstr "Muokkaa ominaisuuksia :: %s"
+
+#: templates/gallery/gallery.inc:87
+msgid "Empty Gallery"
+msgstr "Tyhjä galleria"
+
+#: search.php:89
+msgid "End Date"
+msgstr "Päättymispäivämäärä"
+
+#: search.php:54
+msgid "Ends With"
+msgstr "Päättyy"
+
+#: captions.php:23
+#, php-format
+msgid "Error accessing %s: %s"
+msgstr "Virhe avattaessa %s: %s"
+
+#: lib/Exif.php:318
+msgid "Exposure"
+msgstr "Valotus"
+
+#: lib/Exif.php:324
+msgid "Exposure Bias"
+msgstr "Valotuksen painotus"
+
+#: lib/Exif.php:325
+msgid "Exposure Mode"
+msgstr "Valotuksen tila"
+
+#: lib/Exif.php:322
+msgid "F-Number"
+msgstr "F-numero"
+
+#: image.php:71
+#, php-format
+msgid "File %s"
+msgstr "Tiedosto %s"
+
+#: lib/Exif.php:309
+msgid "File Size"
+msgstr "Tiedoston koko"
+
+#: image.php:61 image.php:79
+msgid "File to upload"
+msgstr "Tuotava tiedosto"
+
+#: search.php:37
+msgid "Filename"
+msgstr "Tiedostonnimi"
+
+#: lib/Exif.php:222 lib/Exif.php:237
+msgid "Flash"
+msgstr "Salama"
+
+#: lib/Exif.php:327
+msgid "Flash Setting"
+msgstr "Salaman asetukset"
+
+#: lib/Exif.php:245
+msgid "Flash, Auto-Mode"
+msgstr "Salama, automaattiasetus"
+
+#: lib/Exif.php:247
+msgid "Flash, Auto-Mode, Return light detected"
+msgstr "Salama, automaattiasetus, heijastunutta valoa havaittu"
+
+#: lib/Exif.php:246
+msgid "Flash, Auto-Mode, Return light not detected"
+msgstr "Salama, automaattiasetus, heijastunutta valoa ei havaittu"
+
+#: lib/Exif.php:239
+msgid "Flash, strobe return light detected"
+msgstr "Salama, välkkyvää valoa havaittu"
+
+#: lib/Exif.php:238
+msgid "Flash, strobe return light not detected"
+msgstr "Salama, välkkyvää valua ei havaittu"
+
+#: templates/image/edit_image.inc:26
+msgid "Flip"
+msgstr "Käännä ympäri"
+
+#: lib/Exif.php:221
+msgid "Fluorescent"
+msgstr "Loistava"
+
+#: lib/Exif.php:319
+msgid "Focal Length"
+msgstr "Polttoväli"
+
+#: lib/Exif.php:320
+msgid "Focal Length (35mm equiv)"
+msgstr "Polttoväli (vastaa 35mm)"
+
+#: templates/slideshow/slideshow.inc:87
+msgid "Forward direction"
+msgstr "Suunta eteenpäin"
+
+#: lib/Ansel.php:449
+#, fuzzy
+msgid "Galleries"
+msgstr "_Galleriat"
+
+#: templates/group/header.inc:6
+#, php-format
+msgid "Galleries grouped by %s"
+msgstr "Galleriat ryhmiteltynä %s mukaan"
+
+#: list.php:50 list.php:53
+#, fuzzy, php-format
+msgid "Galleries in category %s"
+msgstr "Gallerian kategoria"
+
+#: templates/search/search.html:8
+msgid "Gallery"
+msgstr "Galleria"
+
+#: templates/gallery/gallery.inc:31
+msgid "Gallery Category"
+msgstr "Gallerian kategoria"
+
+#: templates/gallery/gallery.inc:46 config/prefs.php.dist:42
+msgid "Gallery Description"
+msgstr "Gallerian kuvaus"
+
+#: templates/gallery/gallery.inc:39
+msgid "Gallery Display Name"
+msgstr "Gallerian näkymänimi"
+
+#: list.php:58 list.php:111
+msgid "Gallery List"
+msgstr "Galleria lista"
+
+#: search.php:38 config/prefs.php.dist:41
+msgid "Gallery Name"
+msgstr "Gallerian nimi"
+
+#: config/prefs.php.dist:29
+msgid "Gallery Options"
+msgstr "Gallerian asetukset"
+
+#: config/prefs.php.dist:43
+msgid "Gallery Owner"
+msgstr "Gallerian omistaja"
+
+#: templates/xppublish/new.inc:14
+msgid "Gallery Short Name"
+msgstr "Gallerian lyhyt nimi"
+
+#: templates/gallery/gallery.inc:18
+msgid "Gallery Short Name (must be unique)"
+msgstr "Gallerian lyhyt nimi (pitää olla yksilöivä)"
+
+#: lib/Ansel.php:148
+#, php-format
+msgid "Gallery Unique name '%s' already exists."
+msgstr "Gallerian yksilöivä nimi '%s' on jo olemassa."
+
+#: xppublish.php:121
+msgid "Gallery Unique name is already taken."
+msgstr "Gallerian yksilöivä nimi on jo käytössä."
+
+#: lib/Gallery.php:30
+msgid "Gallery names must be non-empty"
+msgstr "Gallerialla pitää olla nimi"
+
+#: image.php:451
+msgid "Gallery not found."
+msgstr "Galleriaa ei löytynyt."
+
+#: reorder.php:76
+msgid "Gallery re-ordered."
+msgstr "Galleria uudelleen järjestetty"
+
+#: image.php:105
+msgid "Giant"
+msgstr "Jättiläismäinen"
+
+#: slideshow.php:40
+msgid "Gradient"
+msgstr "Gradientti"
+
+#: templates/image/edit_image.inc:32
+msgid "Grayscale"
+msgstr "Harmaasävy"
+
+#: config/prefs.php.dist:68
+msgid "Group galleries by"
+msgstr "Ryhmitä galleriat"
+
+#: lib/Exif.php:314
+msgid "Height"
+msgstr "Korkeus"
+
+#: image.php:110
+msgid "Horizontal Alignment"
+msgstr "Horisontaalinen tasaus"
+
+#: lib/Exif.php:323
+msgid "ISO Setting"
+msgstr "ISO-asetus"
+
+#: templates/search/search.html:7
+msgid "Image"
+msgstr "Kuva"
+
+#: templates/view/navbar-image.inc:30
+msgid "Image Attributes"
+msgstr "Kuvan ominaisuudet"
+
+#: view.php:41
+msgid "Image Details"
+msgstr "Kuvan tiedot"
+
+#: lib/Exif.php:308
+msgid "Image Type"
+msgstr "Kuvan tyyppi"
+
+#: lib/Gallery.php:50
+msgid "Image names must be non-empty"
+msgstr "Kuvalla pitää olla nimi"
+
+#: view.php:56
+msgid "Image not found."
+msgstr "Kuvaa ei löytynyt."
+
+#: scripts/ansel.php:133
+#, php-format
+msgid "Invalid gallery '%s' specified."
+msgstr "Epäkelpo galleria '%s'."
+
+#: xppublish.php:87 xppublish.php:180
+msgid "Invalid gallery specified."
+msgstr "Epäkelpo galleriamääritys."
+
+#: slideshow.php:41
+msgid "Iris"
+msgstr "Iiris"
+
+#: search.php:51
+msgid "Is"
+msgstr "On"
+
+#: search.php:52
+msgid "Is Not"
+msgstr "Ei ole"
+
+#: image.php:105
+msgid "Large"
+msgstr "Suuri"
+
+#: image.php:109
+msgid "Left"
+msgstr "Vasen"
+
+#: templates/crop/crop.inc:22
+msgid "Left X"
+msgstr "Vasen X"
+
+#: scripts/ansel.php:170
+msgid "Listing Gallery/Name"
+msgstr "Listaa galleria/nimi"
+
+#: scripts/ansel.php:154
+#, php-format
+msgid "Listing images in %s"
+msgstr "Listataan kuvat %s:ssä"
+
+#: scripts/ansel.php:108
+#, php-format
+msgid "Logged in successfully as '%s'."
+msgstr "Kirjauduttiin sisään käyttäjänä '%s'."
+
+#: scripts/ansel.php:104
+msgid "Login is incorrect."
+msgstr "Sisäänkirjautuminen epäonnistui."
+
+#: templates/xppublish/login.inc:2
+#, php-format
+msgid "Login to %s"
+msgstr "Kirjaudu sisään %s"
+
+#: templates/slideshow/slideshow.inc:251
+msgid "Loop?"
+msgstr "Looppi?"
+
+#: templates/crop/crop.inc:52
+msgid "Lower Y"
+msgstr "Alin Y"
+
+#: image.php:36 image.php:64
+msgid "Make this the default image for this gallery?"
+msgstr "Laita tämä gallerian oletuskuvaksi."
+
+#: scripts/ansel.php:293
+msgid ""
+"Mandatory arguments to long options are mandatory for short options too."
+msgstr ""
+"Pakolliset optiot pitkille parametreille ovat pakollisia myös lyhyille "
+"parametreille."
+
+#: lib/Exif.php:114 lib/Exif.php:224
+msgid "Manual"
+msgstr "Käsin"
+
+#: search.php:171 search.php:238
+#, php-format
+msgid "Matched %s images"
+msgstr "Täsmäsi %s kuvaa"
+
+#: search.php:171 search.php:238
+msgid "Matched 1 image"
+msgstr "Täsmäsi 1 kuvaan"
+
+#: image.php:105
+msgid "Medium"
+msgstr "Keskikokoinen"
+
+#: lib/Exif.php:326
+msgid "Metering Mode"
+msgstr "Mittausmoodi"
+
+#: image.php:113
+msgid "Middle"
+msgstr "Keskellä"
+
+#: templates/image/edit_image.inc:29
+msgid "Mirror"
+msgstr "Peilikuva"
+
+#: lib/Exif.php:134
+#, php-format
+msgid "Mirrored"
+msgstr "Peilikuvana"
+
+#: templates/list/images.inc:21
+msgid "Modified"
+msgstr "Muokattu"
+
+#: templates/view/gallery.inc:94
+msgid "Modify"
+msgstr "Muuta"
+
+#: templates/view/gallery.inc:94
+msgid "Modify Gallery"
+msgstr "Muuta galleriaa"
+
+#: gallery.php:127
+#, php-format
+msgid "Modifying: %s"
+msgstr "Muutetaan: %s"
+
+#: templates/view/navbar-gallery.inc:13
+msgid "Move"
+msgstr "Siirrä"
+
+#: image.php:580
+#, php-format
+msgid "Moved %s to %s."
+msgstr "Siirrä %s %s:ksi."
+
+#: lib/Exif.php:209
+msgid "Multi-Segment"
+msgstr "Monisegmentti"
+
+#: lib/Exif.php:208
+msgid "Multi-Spot"
+msgstr "Monipiste"
+
+#: image.php:67
+msgid "Multiple Images"
+msgstr "Monia kuvia"
+
+#: list.php:38 templates/group/owner.inc:5 lib/Ansel.php:367 lib/Ansel.php:436
+#, php-format
+msgid "My Galleries"
+msgstr "Omat galleriat"
+
+#: templates/list/gallery_headers.inc:7
+msgid "Name"
+msgstr "Nimi"
+
+#: templates/xppublish/new.inc:33
+msgid "New Gallery Description"
+msgstr "Uuden gallerian kuvaus"
+
+#: templates/xppublish/new.inc:24
+msgid "New Gallery Title"
+msgstr "Uuden gallerian nimi"
+
+#: templates/view/description-image.inc:60
+#: templates/view/description-image.inc:61
+msgid "Next"
+msgstr "Seuraava"
+
+#: config/prefs.php.dist:110
+msgid "No"
+msgstr "Ei"
+
+#: lib/Exif.php:236 lib/Exif.php:243 lib/Exif.php:244 lib/Exif.php:248
+msgid "No Flash"
+msgstr "Ei salamaa"
+
+#: xppublish.php:123
+msgid "No Gallery Name Specified."
+msgstr "Gallerian nimeä ei ole määritelty"
+
+#: xppublish.php:119
+msgid "No Gallery Unique Name Specified."
+msgstr "Gallerian nimi ei ole yksilöivä."
+
+#: templates/image/exif.inc:11
+msgid "No embedded information found."
+msgstr "Sisällytettyä tietoa ei löytynyt"
+
+#: xppublish.php:190
+msgid "No file specified"
+msgstr "Tiedostoa ei määritelty"
+
+#: templates/view/navbar-gallery.inc:6 config/prefs.php.dist:67
+msgid "None"
+msgstr "Ei mitään"
+
+#: lib/Exif.php:133
+#, php-format
+msgid "Normal (O deg)"
+msgstr "Normaali (0 astetta)"
+
+#: lib/Exif.php:266
+msgid "Not defined"
+msgstr "Ei määritelty"
+
+#: crop.php:26 image.php:152
+msgid "Not found or error accessing gallery."
+msgstr "Galleriaa ei löytynyt tai virhe pääsyssä."
+
+#: config/prefs.php.dist:93
+msgid "Number of galleries per page"
+msgstr "Gallerioiden määrä sivulla"
+
+#: config/prefs.php.dist:101
+msgid "Number of groups per page"
+msgstr "Ryhmien määrä sivulla"
+
+#: config/prefs.php.dist:85
+msgid "Number of images per page"
+msgstr "Kuvien määrä sivulla"
+
+#: config/prefs.php.dist:77
+msgid "Number of images per row"
+msgstr "Kuvien määrä rivillä"
+
+#: lib/Exif.php:267
+msgid "One Chip Color Area Sensor"
+msgstr "Yhden värikennon sensori"
+
+#: perms.php:62
+msgid ""
+"Only the owner or system administrator may change ownership or owner "
+"permissions for a share"
+msgstr ""
+"Vain omistaja tai järjestelmän ylläpitäjä voi muuttaa jakojen omistajia tai "
+"oikeuksia"
+
+#: templates/crop/crop.inc:27
+msgid "Original"
+msgstr "Alkuperäinen"
+
+#: lib/Exif.php:211
+msgid "Other"
+msgstr "Muu"
+
+#: config/prefs.php.dist:13 config/prefs.php.dist:20 config/prefs.php.dist:28
+msgid "Other Options"
+msgstr "Muut asetukset"
+
+#: templates/list/images.inc:25 config/prefs.php.dist:65
+msgid "Owner"
+msgstr "Omistaja"
+
+#: templates/view/parents.inc:2
+msgid "Parent Galleries"
+msgstr "Päägalleriat"
+
+#: lib/Exif.php:210
+msgid "Partial"
+msgstr "Osittainen"
+
+#: templates/xppublish/login.inc:14
+msgid "Password"
+msgstr "Salasana"
+
+#: templates/view/gallery.inc:99
+msgid "Permissions"
+msgstr "Oikeudet"
+
+#: templates/xppublish/javascript.inc:63
+msgid "Photo Upload"
+msgstr "Valokuvan tallennus"
+
+#: slideshow.php:42
+msgid "Pinwheel"
+msgstr "Onnenpyörä"
+
+#: slideshow.php:43
+msgid "Pixelate"
+msgstr "Pikselöity"
+
+#: templates/slideshow/slideshow.inc:67
+msgid "Play"
+msgstr "Näytä"
+
+#: xppublish.php:54
+msgid "Please enter your Username and Password."
+msgstr "Anna käyttäjätunnus ja salasanasi."
+
+#: xppublish.php:99 xppublish.php:155
+msgid "Press the 'Back' button and try again."
+msgstr "Napsauta 'Back'-painiketta ja yritä uudestaan."
+
+#: image.php:457
+#, php-format
+msgid "Preview changes for %s :: %s"
+msgstr "Esikatse muutokset %s :: %s"
+
+#: templates/view/description-image.inc:46
+#: templates/view/description-image.inc:47
+msgid "Previous"
+msgstr "Edellinen"
+
+#: lib/Ansel.php:395
+msgid "Print"
+msgstr "Tulosta"
+
+#: templates/view/navbar-image.inc:31
+msgid "Print on Shutterfly"
+msgstr "Tulosta Shutterfly:lle"
+
+#: lib/Exif.php:111
+msgid "Program"
+msgstr "Ohjelma"
+
+#: templates/view/gallery.inc:155 templates/view/navbar-image.inc:16
+msgid "Properties"
+msgstr "Ominaisuudet"
+
+#: xppublish.php:35
+#, php-format
+msgid "Publish to %s"
+msgstr "Julkaise %s:ssä"
+
+#: xppublish.php:23
+#, php-format
+msgid "Publish your photos to %s on %s."
+msgstr "Julkaise valokuvasi %s %s:."
+
+#: slideshow.php:44
+msgid "Radial"
+msgstr "Radiaali"
+
+#: slideshow.php:45
+msgid "Rain"
+msgstr "Sade"
+
+#: slideshow.php:50
+msgid "Random"
+msgstr "Satunnainen"
+
+#: lib/Block/random_gallery.php:3 lib/Block/random_gallery.php:27
+msgid "Random gallery"
+msgstr "Satunnainen galleria"
+
+#: lib/Block/random_photo.php:3 lib/Block/random_photo.php:28
+msgid "Random photo"
+msgstr "Satunnainen"
+
+#: reorder.php:90
+#, php-format
+msgid "Re-order the images in %s"
+msgstr "Järjestä kuvan satunnaisti %s:ssä"
+
+#: lib/Exif.php:249
+msgid "Red Eye"
+msgstr "Punasilmäisyys"
+
+#: lib/Exif.php:255
+msgid "Red Eye, Auto-Mode"
+msgstr "Punasilmäisyys, automaattiasetus"
+
+#: lib/Exif.php:257
+msgid "Red Eye, Auto-Mode, Return light detected"
+msgstr "Punasilmäisyys, automaattiasetus, heijastunutta valoa havaittu"
+
+#: lib/Exif.php:256
+msgid "Red Eye, Auto-Mode, Return light not detected"
+msgstr "Punasilmäisyys, automaattiasetus, heijastunutta valoa ei havaittu"
+
+#: lib/Exif.php:252
+msgid "Red Eye, Compulsory Flash"
+msgstr "Punasilmäisyys, pakotettu salama"
+
+#: lib/Exif.php:254
+msgid "Red Eye, Compulsory Flash, Return light detected"
+msgstr "Punasilmäisyys, pakotettu salama, heijastunutta valoa havaittu"
+
+#: lib/Exif.php:253
+msgid "Red Eye, Compulsory Flash, Return light not detected"
+msgstr "Punasilmäisyys, pakotettu salama, heijastunutta valoa ei havaittu"
+
+#: lib/Exif.php:251
+msgid "Red Eye, Return light detected"
+msgstr "Punasilmäisyys, heijastunutta valoa havaittu"
+
+#: lib/Exif.php:250
+msgid "Red Eye, Return light not detected"
+msgstr "Punasilmäisyys, heijastunutta valoa ei havaittu"
+
+#: templates/view/gallery.inc:79
+msgid "Refresh Gallery View"
+msgstr "Päivitä gallerinäkymä"
+
+#: templates/view/gallery.inc:79 templates/list/header.inc:10
+#: templates/list/header.inc:11 templates/group/header.inc:7
+#: templates/group/header.inc:8
+msgid "Refresh List"
+msgstr "Päivitä lista"
+
+#: templates/view/gallery.inc:96
+msgid "Reorder"
+msgstr "Uudelleenjärjestä"
+
+#: templates/view/gallery.inc:96
+msgid "Reorder Gallery"
+msgstr "Uudelleenjärjestä galleria"
+
+#: templates/crop/crop.inc:3
+msgid "Resize selection"
+msgstr "Muuta valinnan kokoa"
+
+#: templates/slideshow/slideshow.inc:90 templates/slideshow/slideshow.inc:221
+msgid "Reverse direction"
+msgstr "Käännä suuntaan"
+
+#: image.php:109
+msgid "Right"
+msgstr "Oikea"
+
+#: templates/crop/crop.inc:50
+msgid "Right X"
+msgstr "Oikea X"
+
+#: templates/image/edit_image.inc:22
+msgid "Rotate 180"
+msgstr "Käännä 180"
+
+#: templates/image/edit_image.inc:21
+msgid "Rotate Left"
+msgstr "Käännä vastapäivään"
+
+#: templates/image/edit_image.inc:23
+msgid "Rotate Right"
+msgstr "Käännä myötäpäivään"
+
+#: image.php:29 image.php:52 image.php:96 reorder.php:26
+msgid "Save"
+msgstr "Talleta"
+
+#: templates/captions/captions.inc:50
+msgid "Save Captions"
+msgstr "Talleta kuvateksti"
+
+#: templates/image/preview_image.inc:24
+msgid "Save Change"
+msgstr "Talleta muutokset"
+
+#: templates/gallery/gallery.inc:85
+msgid "Save Gallery"
+msgstr "Talleta galleria"
+
+#: image.php:195
+msgid "Save Image"
+msgstr "Talleta kuva"
+
+#: templates/gallery/gallery.inc:74
+msgid "Screen Image Height"
+msgstr "Kuvanäytön korkeus"
+
+#: templates/gallery/gallery.inc:67
+msgid "Screen Image Width"
+msgstr "Kuvanäytön leveys"
+
+#: search.php:28
+msgid "Search"
+msgstr "Haku"
+
+#: search.php:60
+msgid "Search Type"
+msgstr "Haun tyyppi"
+
+#: search.php:97 search.php:102
+#, php-format
+msgid "Search for Images"
+msgstr "Hae kuvia"
+
+#: templates/view/navbar-gallery.inc:5
+msgid "Select All"
+msgstr "Valitse kaikki"
+
+#: templates/view/navbar-gallery.inc:6
+msgid "Select None"
+msgstr "Tyhjennä valinnat"
+
+#: templates/xppublish/list.inc:6
+msgid "Select the Gallery to publish images to"
+msgstr "Valitse galleria, johon kuvat julkaistaan"
+
+#: templates/view/navbar-gallery.inc:4
+#, fuzzy, php-format
+msgid "Select: %s, %s"
+msgstr "Valitse: %s | %s"
+
+#: templates/view/navbar-gallery.inc:20
+msgid "Selected images to"
+msgstr "Valitut kuvat"
+
+#: ecard.php:25
+msgid "Send"
+msgstr "Lähetä"
+
+#: ecard.php:141
+#, php-format
+msgid "Send Ecard :: %s"
+msgstr "Lähetä Ecard :: %s"
+
+#: templates/view/navbar-image.inc:28
+msgid "Send as Ecard"
+msgstr "Lähetä Ecard-muodossa"
+
+#: ecard.php:41
+msgid "Send ecard to the following address:"
+msgstr "Lähetä Ecard seuraaville vastaanottajille:"
+
+#: lib/Exif.php:330
+msgid "Sensing Method"
+msgstr "Tunnistusmetodi"
+
+#: templates/view/gallery.inc:99
+msgid "Set Permissions"
+msgstr "Aseta oikeudet"
+
+#: config/prefs.php.dist:120
+msgid "Show EXIF data"
+msgstr "Näytä EXIF-tiedot"
+
+#: lib/Exif.php:317
+msgid "Shutter Speed"
+msgstr "Sulkijan nopeus"
+
+#: image.php:58
+msgid "Single Image"
+msgstr "Yksittäinen kuva"
+
+#: slideshow.php:46
+msgid "Slide"
+msgstr "Dia"
+
+#: lib/Ansel.php:376
+msgid "Slide Show"
+msgstr "Kuvaesitys"
+
+#: templates/list/gallery_summaries.inc:7
+msgid "Slide show"
+msgstr "Kuvaesitys"
+
+#: slideshow.php:52
+#, php-format
+msgid "Slide show for %s"
+msgstr "Kuvaesitys %s"
+
+#: templates/slideshow/slideshow.inc:79
+msgid "Slide show is running"
+msgstr "Kuvaesitys on käynnissä"
+
+#: image.php:105
+msgid "Small"
+msgstr "Pieni"
+
+#: slideshow.php:47
+msgid "Snow"
+msgstr "Lumi"
+
+#: templates/list/gallery_headers.inc:6 templates/list/gallery_headers.inc:21
+msgid "Sort Direction"
+msgstr "Järjestyksen suunta"
+
+#: config/prefs.php.dist:14
+msgid "Sort Options"
+msgstr "Järjestyksen asetukset"
+
+#: templates/list/gallery_headers.inc:22
+msgid "Sort by Description"
+msgstr "Järjestä kuvauksen mukaan"
+
+#: templates/list/gallery_headers.inc:7
+msgid "Sort by Name"
+msgstr "Järjestä nimen mukaan"
+
+#: gallery.php:186
+#, php-format
+msgid "Specified screen image height is too large, value has been set to %s"
+msgstr "Ruudun määritelty korkeus on liian suuri, arvo on nyt %s"
+
+#: gallery.php:183
+#, php-format
+msgid "Specified screen image height is too small, value has been set to %s"
+msgstr "Ruudun määritelty korkeus on liian pieni, arvo on nyt %s"
+
+#: gallery.php:177
+#, php-format
+msgid "Specified screen image width is too large, value has been set to %s"
+msgstr "Ruudun määritelty leveys on liian suuri, arvo on nyt %s"
+
+#: gallery.php:174
+#, php-format
+msgid "Specified screen image width is too small, value has been set to %s"
+msgstr "Ruudun määritelty leveys on liian pieni, arvo on nyt %s"
+
+#: gallery.php:168
+#, php-format
+msgid "Specified thumbnail height is too large, value has been set to %s"
+msgstr "Pikkukuvan määritelty korkeus on liian suuri, arvo on nyt %s"
+
+#: gallery.php:165
+#, php-format
+msgid "Specified thumbnail height is too small, value has been set to %s"
+msgstr "Pikkukuvan määritelty korkeus on liian pieni, arvo on nyt %s"
+
+#: gallery.php:159
+#, php-format
+msgid "Specified thumbnail width is too large, value has been set to %s"
+msgstr "Pikkukuvan määritelty leveys on liian suuri, arvo on nyt %s"
+
+#: gallery.php:156
+#, php-format
+msgid "Specified thumbnail width is too small, value has been set to %s"
+msgstr "Pikkukuvan määritelty leveys on liian pieni, arvo on nyt %s"
+
+#: slideshow.php:48
+msgid "Spiral"
+msgstr "Spiraali"
+
+#: lib/Exif.php:207
+msgid "Spot"
+msgstr "Piste"
+
+#: search.php:88
+msgid "Start Date"
+msgstr "Alkamispäivämäärä"
+
+#: templates/slideshow/slideshow.inc:76 templates/slideshow/slideshow.inc:221
+msgid "Stop"
+msgstr "Stop"
+
+#: scripts/ansel.php:190 scripts/ansel.php:269
+#, php-format
+msgid "Storing image '%s'..."
+msgstr "Talletetaan kuva '%s'..."
+
+#: slideshow.php:49
+msgid "Stretch"
+msgstr "Venytä"
+
+#: templates/view/subgallery_header.inc:4
+#, php-format
+msgid "Subgalleries of %s"
+msgstr "%s:n aligalleriat"
+
+#: templates/view/gallery.inc:93
+msgid "Subgallery"
+msgstr "Aligalleria"
+
+#: scripts/ansel.php:210
+#, php-format
+msgid "Successfully added %d image(s) (%s) to gallery '%s' from '%s'."
+msgstr "Lisättiin onnistuneesti %d kuvaa (%s) galleriaan '%s' '%s':stä."
+
+#: scripts/ansel.php:197
+#, php-format
+msgid "Successfully added image '%s' to gallery '%s'."
+msgstr "Kuvan '%s' lisäys galleriaan '%s' onnistui."
+
+#: gallery.php:318
+#, php-format
+msgid "Successfully deleted %s."
+msgstr "Poistettiin onnistuneesti %s."
+
+#: lib/Exif.php:218
+msgid "Sunny"
+msgstr "Aurinkoinen"
+
+#: search.php:66
+msgid "Text Field"
+msgstr "Tekstikenttä"
+
+#: search.php:61
+msgid "Text Fields"
+msgstr "Tekstikentät"
+
+#: search.php:67
+msgid "Text Operator"
+msgstr "Tekstitoiminto"
+
+#: search.php:68
+msgid "Text Value"
+msgstr "Tekstin arvo"
+
+#: scripts/ansel.php:236
+#, php-format
+msgid "The directory '%s' doesn't exist."
+msgstr "Hakemistoa '%s' ei ole olemassa."
+
+#: scripts/ansel.php:215
+#, php-format
+msgid "The directory '%s' had no valid images."
+msgstr "Hakemistossa '%s' ei ollut kunnollisia kuvatiedostoja."
+
+#: scripts/ansel.php:253
+#, php-format
+msgid "The directory '%s' is empty."
+msgstr "Hakemisto '%s' on tyhjä."
+
+#: lib/Ansel.php:282
+#, php-format
+msgid "The file '%s' doesn't exist."
+msgstr "Tiedosto '%s' ei ole olemassa."
+
+#: image.php:295 lib/api.php:150
+msgid "The file you uploaded does not appear to be a valid image."
+msgstr "Tuotu tiedosto ei vaikuta kelvolliselta kuvatiedostolta."
+
+#: lib/Ansel.php:175
+#, php-format
+msgid "The gallery \"%s\" could not be created: %s"
+msgstr "Galleriaa \"%s\" ei voitu luoda: %s"
+
+#: gallery.php:283 xppublish.php:129 scripts/ansel.php:120
+#, php-format
+msgid "The gallery \"%s\" couldn't be created: %s"
+msgstr "Galleriaa \"%s\" ei voitu luoda: %s"
+
+#: gallery.php:288 xppublish.php:133 scripts/ansel.php:124
+#, php-format
+msgid "The gallery \"%s\" was created successfully."
+msgstr "Gallerian \"%s\" luonti onnistui."
+
+#: gallery.php:230
+msgid "The gallery was saved."
+msgstr "Galleria talletettiin"
+
+#: image.php:305
+msgid "The image was saved."
+msgstr "Kuva talletettiin"
+
+#: templates/slideshow/slideshow.inc:70
+msgid "The slide show is stopped, click [Play] to resume."
+msgstr "Kuvaesitys on pysäytetty, napsauta [Aloita] jatkaaksesi"
+
+#: image.php:320
+msgid ""
+"The uploaded file appears to be empty. It may not exist on your computer."
+msgstr ""
+"Tuotu tiedosto vaikuttaisi tyhjältä. Ehkä sitä ei ole olemassa "
+"tietokoneellasi."
+
+#: gallery.php:88
+#, php-format
+msgid "There are no images in %s to download."
+msgstr "%s:ssä ei ole kuvia tuotavaksi."
+
+#: slideshow.php:31
+msgid "There are no images in this gallery to show."
+msgstr "Tässä galleriassa ei ole kuvia näytettäväksi."
+
+#: templates/view/gallery.inc:111 templates/captions/captions.inc:52
+msgid "There are no images in this gallery."
+msgstr "Tässä galleriassa ei ole kuvia."
+
+#: list.php:63 lib/Block/random_photo.php:38 lib/Block/random_gallery.php:37
+msgid "There are no photo galleries available."
+msgstr "Valokuvagallerioita ei ole saatavilla."
+
+#: scripts/ansel.php:193 scripts/ansel.php:272
+#, php-format
+msgid "There was a problem adding the image '%s' to gallery '%s'."
+msgstr "Tapahtui virhe lisättäessä kuvaa '%s' galleriaan '%s'."
+
+#: image.php:616
+#, fuzzy, php-format
+msgid "There was a problem copying \"%s\": %s"
+msgstr "Tapahtui virhe kopioitaessa kuvaa: %s"
+
+#: image.php:578
+#, fuzzy, php-format
+msgid "There was a problem deleting \"%s\": %s"
+msgstr "Tapahtui virhe poistettaessa %s: %s"
+
+#: lib/api.php:267
+#, php-format
+msgid "There was a problem deleting %s: %s"
+msgstr "Tapahtui virhe poistettaessa %s: %s"
+
+#: gallery.php:325
+#, php-format
+msgid "There was a problem deleting %s: %s."
+msgstr "Tapahtui virhe poistettaessa %s: %s."
+
+#: image.php:539
+#, php-format
+msgid "There was a problem deleting images: %s"
+msgstr "Tapahtui virhe poistettaessa kuvaa %s"
+
+#: image.php:574
+#, fuzzy, php-format
+msgid "There was a problem moving \"%s\": %s"
+msgstr "Tapahtui virhe siirrettäessä kuvaa: %s"
+
+#: image.php:278 image.php:311
+#, php-format
+msgid "There was a problem saving the image: %s"
+msgstr "Tapahtui virhe talletettaessa kuvaa: %s"
+
+#: xppublish.php:199
+msgid "There was a problem uploading the image."
+msgstr "Tapahtui virhe tuotaessa kuvaa."
+
+#: image.php:318
+#, php-format
+msgid "There was a problem uploading the image: %s"
+msgstr "Tapahtui virhe tuotaessa kuvaa: %s"
+
+#: image.php:255 image.php:263
+#, php-format
+msgid "There was an error processing the uploaded archive: %s"
+msgstr "Tapahtui virhe käsiteltäessä tuotua arkistoa: %s"
+
+#: ecard.php:130
+#, php-format
+msgid "There was an error sending your message: %s"
+msgstr "Tapahtui virhe lähetettäessä viestiäsi: %s"
+
+#: lib/Exif.php:269
+msgid "Three Chip Color Area Sensor"
+msgstr "Kolmen värikennon sensori"
+
+#: search.php:164 search.php:231
+msgid "Thumbnail"
+msgstr "Pikkukuva"
+
+#: templates/gallery/gallery.inc:60
+msgid "Thumbnail Height"
+msgstr "Pikkukuvan korkeus"
+
+#: templates/gallery/gallery.inc:53
+msgid "Thumbnail Width"
+msgstr "Pikkukuvan leveys"
+
+#: image.php:105
+msgid "Tiny"
+msgstr "Pieni"
+
+#: lib/Ansel.php:342
+msgid "Today"
+msgstr "Tänään"
+
+#: image.php:113
+msgid "Top"
+msgstr "Ylös"
+
+#: templates/crop/crop.inc:24
+msgid "Top Y"
+msgstr "Ylin Y"
+
+#: templates/slideshow/slideshow.inc:245
+msgid "Transition"
+msgstr "Muutos"
+
+#: lib/Exif.php:271
+msgid "Trilinear Sensor"
+msgstr "Trilineaarinen sensori"
+
+#: lib/Exif.php:220
+msgid "Tungsten"
+msgstr "Tungsteeni"
+
+#: lib/Exif.php:112
+msgid "Tv-priority"
+msgstr "Tv-prioriteetti"
+
+#: lib/Exif.php:268
+msgid "Two Chip Color Area Sensor"
+msgstr "Kahden tunnistimen sensori"
+
+#: lib/Exif.php:282
+msgid "Uncalibrated"
+msgstr "Kalibroimaton"
+
+#: templates/gallery/gallery.inc:86
+msgid "Undo Changes"
+msgstr "Peru muutokset"
+
+#: list.php:50 templates/group/category.inc:8
+#, fuzzy
+msgid "Unfiled"
+msgstr "Nimetön"
+
+#: lib/Exif.php:204
+msgid "Unknown"
+msgstr "Tuntematon"
+
+#: gallery.php:129
+msgid "Unknown gallery"
+msgstr "Tuntematon galleria"
+
+#: lib/Ansel.php:163
+msgid "Unnamed"
+msgstr "Nimetön"
+
+#: xppublish.php:141
+msgid "Untitled"
+msgstr "Nimetön"
+
+#: perms.php:234
+#, php-format
+msgid "Updated %s."
+msgstr "Päivitettiin %s."
+
+#: templates/view/gallery.inc:92
+msgid "Upload"
+msgstr "Tuo"
+
+#: templates/xppublish/javascript.inc:63
+#, php-format
+msgid "Upload Photos to %s"
+msgstr "Tuo valokuvat %s:ään"
+
+#: image.php:166 image.php:225
+msgid "Upload new images"
+msgstr "Tuo uusia kuvia"
+
+#: lib/Exif.php:135
+#, php-format
+msgid "Upsidedown"
+msgstr "Ylösalaisin"
+
+#: lib/Exif.php:136
+#, php-format
+msgid "Upsidedown Mirrored"
+msgstr "Ylösalaisin peilikuvana"
+
+#: scripts/ansel.php:291
+#, php-format
+msgid "Usage: %s [OPTIONS]..."
+msgstr "Käyttö: %s [PARAMETRIT]..."
+
+#: templates/xppublish/new.inc:17 templates/gallery/gallery.inc:24
+msgid "Use Default"
+msgstr "Käytä oletuksia"
+
+#: ecard.php:32
+msgid "Use the following return address:"
+msgstr "Käytä seuraavaa palautusosoitetta:"
+
+#: lib/Exif.php:328
+msgid "User Comment"
+msgstr "Käyttäjän kommenttin"
+
+#: templates/xppublish/login.inc:11
+msgid "Username"
+msgstr "Käyttäjätunnus"
+
+#: xppublish.php:50
+msgid "Username and Password are incorrect."
+msgstr "Käyttäjätunnus ja salasana on väärin."
+
+#: image.php:114
+msgid "Vertical Alignment"
+msgstr "Vertikaalinen tasaus"
+
+#: templates/list/gallery_summaries.inc:2
+#: templates/list/gallery_summaries.inc:15 templates/list/images.inc:9
+#: templates/list/images.inc:12 templates/list/images.inc:17
+#: lib/Block/random_gallery.php:42
+#, php-format
+msgid "View %s"
+msgstr "Näytä %s"
+
+#: lib/Block/random_photo.php:54
+msgid "View Photo"
+msgstr "Katso kuva"
+
+#: image.php:415 image.php:421 templates/image/edit_image.inc:36
+msgid "Watermark"
+msgstr "Vesileima"
+
+#: image.php:106
+msgid "Watermark Font"
+msgstr "Vesileiman kirjaisin"
+
+#: lib/Exif.php:331
+msgid "White Balance"
+msgstr "Valkotasapaino"
+
+#: lib/Exif.php:313
+msgid "Width"
+msgstr "Leveys"
+
+#: lib/Exif.php:315
+msgid "X Resolution"
+msgstr "X resoluutio"
+
+#: lib/Exif.php:316
+msgid "Y Resolution"
+msgstr "Y resoluutio"
+
+#: config/prefs.php.dist:109
+msgid "Yes"
+msgstr "Kyllä"
+
+#: config/prefs.php.dist:111
+msgid "Yes, and don't prompt to change it"
+msgstr "Kyllä, äläkä kysy muuttaaksesi"
+
+#: lib/Ansel.php:344
+msgid "Yesterday"
+msgstr "Eilen"
+
+#: image.php:352
+msgid "You are not allowed to edit this image."
+msgstr "Sinulla ei ole oikeuksia muuttaa tätä kuvaa."
+
+#: xppublish.php:91 xppublish.php:184
+msgid "You cannot add photos in that gallery."
+msgstr "Sinä et voi lisätä kuvia tähän galleriaan."
+
+#: scripts/ansel.php:139
+#, php-format
+msgid "You cannot add photos to the gallery '%s'."
+msgstr "Sinä et voi lisätä kuvia galleriaan '%s'."
+
+#: gallery.php:313
+#, php-format
+msgid "You cannot delete %s, it contains nested galleries."
+msgstr "Et voi poistaa %s, koska se sisältää sisäkkäisiä gallerioita."
+
+#: gallery.php:53 gallery.php:245
+#, php-format
+msgid "You do not have permission to add children to %s."
+msgstr "Sinulla ei ole oikeuksia lisätä alitasoja %s:lle."
+
+#: gallery.php:348
+#, php-format
+msgid "You do not have permission to empty %s."
+msgstr "Sinulle ei ole oikeuksia tyhjentää %s."
+
+#: reorder.php:47 slideshow.php:24 view.php:27
+msgid "You do not have permission to view this gallery."
+msgstr "Sinulla ei ole oikeuksia katsella tätä galleriaa"
+
+#: ecard.php:102
+msgid ""
+"You have been sent an Ecard. To view the Ecard, you must be able to view "
+"text/html messages in your mail reader. If you are viewing this message, "
+"then most likely your mail reader does not support viewing text/html "
+"messages."
+msgstr ""
+"Olet saanut Ecard:in. Katsoaksesi Ecarc:n, sinun pitää pystyä katsomaan text/"
+"html muodossa olevia viestiä sähköpostiohjelmassasi. Jos näet tämän viestin, "
+"niin todennäköisesti sinun käyttämäsi ohjelma ei tue text/html viestejä."
+
+#: ecard.php:76
+msgid "You must enter an e-mail address to send the message to."
+msgstr "Sinun pitää antaa sähköpostiosoite, johon viesti lähetetään"
+
+#: ecard.php:71
+msgid "You must enter your e-mail address."
+msgstr "Sinun pitää antaa sähköpostiosoite"
+
+#: gallery.php:265
+msgid "You must provide a display name for your new gallery."
+msgstr "Sinun pitää antaa näkymän nimi galleriallesi."
+
+#: image.php:76
+msgid "Zip File Upload"
+msgstr "Zip-tiedosto tuonti"
+
+#: templates/crop/crop.inc:34
+msgid "Zoom"
+msgstr "Zoom"
+
+#: lib/Ansel.php:365
+msgid "_Galleries"
+msgstr "_Galleriat"
+
+#: lib/Ansel.php:390
+msgid "_New Gallery"
+msgstr "_Uusi galleria"
+
+#: lib/Ansel.php:399
+msgid "_Search"
+msgstr "_Haku"
+
+#: lib/Exif.php:279
+msgid "sRGB"
+msgstr "sRGB"
diff --git a/ansel/po/it_IT.po b/ansel/po/it_IT.po
new file mode 100644 (file)
index 0000000..1355542
--- /dev/null
@@ -0,0 +1,2785 @@
+# Italian translations for ansel package.
+# Copyright 2008-2009 The Horde Project
+# This file is distributed under the same license as the ansel package.
+# Fabio Pedretti <fabio.pedretti@ing.unibs.it>, 2008.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ansel v1.0-cvs\n"
+"Report-Msgid-Bugs-To: dev@lists.horde.org\n"
+"POT-Creation-Date: 2008-09-05 18:18+0200\n"
+"PO-Revision-Date: 2008-09-05 20:30+0100\n"
+"Last-Translator: Fabio Pedretti <fabio.pedretti@ing.unibs.it>\n"
+"Language-Team: i18n@lists.horde.org\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-1\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: scripts/recursive_import.php:149 scripts/remote_import.php:158
+#, php-format
+msgid "\"%s\" is not a directory."
+msgstr "\"%s\" non è una directory."
+
+#: templates/view/list.inc:3 templates/list/header.inc:3
+#: templates/group/category.inc:47 templates/group/owner.inc:52
+#, fuzzy, php-format
+msgid "%d Gallery"
+msgid_plural "%d Galleries"
+msgstr[0] "Nuova Categoria"
+msgstr[1] "Nuova Categoria"
+
+#: templates/group/header.inc:3
+#, php-format
+msgid "%d Group"
+msgid_plural "%d Groups"
+msgstr[0] ""
+msgstr[1] ""
+
+#: templates/view/gallerylightbox.inc:104 templates/view/results.inc:83
+#, fuzzy, php-format
+msgid "%d Item"
+msgid_plural "%d Items"
+msgstr[0] "%s minuti"
+msgstr[1] "%s minuti"
+
+#: lib/Exif.php:291
+#, php-format
+msgid "%d bytes"
+msgstr "%d byte"
+
+#: templates/tile/image.inc:11
+#, php-format
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] "%d commento"
+msgstr[1] "%d commenti"
+
+#: templates/faces/face.inc:7
+#, php-format
+msgid "%d gallery"
+msgid_plural "%d galleries"
+msgstr[0] "%d galleria"
+msgstr[1] "%d gallerie"
+
+#: templates/view/gallery.inc:85
+#, fuzzy, php-format
+msgid "%d item"
+msgid_plural "%d items"
+msgstr[0] "%s minuti"
+msgstr[1] "%s minuti"
+
+#: templates/faces/search.inc:9 lib/XRequest/TagActions.php:115
+#: lib/Widget/Tags.php:93 lib/Views/Results.php:291
+#, php-format
+msgid "%d photo"
+msgid_plural "%d photos"
+msgstr[0] "%d foto"
+msgstr[1] "%d foto"
+
+#: img/upload.php:213
+#, php-format
+msgid "%d photo was uploaded."
+msgid_plural "%d photos were uploaded."
+msgstr[0] "%d foto caricata."
+msgstr[1] "%d foto caricate."
+
+#: lib/Exif.php:142 lib/Exif.php:148
+#, php-format
+msgid "%d pixels"
+msgstr "%d pixel"
+
+#: templates/view/list.inc:3 templates/list/header.inc:3
+#, fuzzy, php-format
+msgid "%d to %d of %d Galleries"
+msgstr "%s a %s %s"
+
+#: templates/group/header.inc:3
+#, fuzzy, php-format
+msgid "%d to %d of %d Groups"
+msgstr "%s a %s %s"
+
+#: templates/view/gallery.inc:85 templates/view/gallerylightbox.inc:104
+#: templates/view/results.inc:83
+#, fuzzy, php-format
+msgid "%d to %d of %d Items"
+msgstr "%s a %s %s"
+
+#: gallery/sort.php:60
+#, php-format
+msgid "%s :: Sort"
+msgstr "%s :: Ordina"
+
+#: lib/Widget/SimilarPhotos.php:81
+#, php-format
+msgid "%s from %s"
+msgstr "da %s a %s"
+
+#: rss.php:112
+#, php-format
+msgid "%s on %s"
+msgstr "%s su %s"
+
+#: lib/Exif.php:172 lib/Exif.php:187
+#, fuzzy, php-format
+msgid "%s sec"
+msgstr "%s: %s"
+
+#: templates/group/owner.inc:16 lib/Ansel.php:653
+#: lib/Widget/OtherGalleries.php:36 lib/Views/List.php:167
+#, php-format
+msgid "%s's Galleries"
+msgstr "%s's Gallerie"
+
+#: templates/faces/faces.inc:8
+#, php-format
+msgid "%s: faces %s"
+msgstr "%s: facce %s"
+
+#: lib/Ansel.php:2541
+#, php-format
+msgid "(c) %s %s"
+msgstr "(c) %s %s"
+
+#: scripts/ansel.php:326
+msgid "-a, --add[=filename]         Add local file to selected gallery"
+msgstr ""
+"-a, --add[=filename]         Aggiungi file locale nella galleria selezionata"
+
+#: scripts/ansel.php:324
+msgid ""
+"-c, --create[=name/description/owner]\n"
+"                             Create gallery (and use it)  Combined with -g "
+"to create a subgallery."
+msgstr ""
+"-c, --create[=name/description/owner]\n"
+"                             Crea galleria (e la usa)  Combinato con -g crea "
+"una sottogalleria."
+
+#: scripts/ansel.php:327
+msgid ""
+"-d, --dir[=directory]        Add all files from the directory to the "
+"selected\n"
+"                             gallery"
+msgstr ""
+"-d, --dir[=directory]        Aggiungi tutti i file dalla directory alla "
+"galleria\n"
+"                             selezionata"
+
+#: scripts/recursive_import.php:226 scripts/remote_import.php:309
+msgid ""
+"-d, --dir[=directory]        Recursively add all files from the directory, "
+"creating\n"
+"                             a gallery for each directory"
+msgstr ""
+"-d, --dir[=directory]        Aggiunge ricorsivamente tutti i file dalla "
+"directory, creando\n"
+"                             una galleria per ogni directory"
+
+#: scripts/all_images_exif_to_tags.php:123
+msgid ""
+"-f, --fields[=exif_fields]   A ':' delimited list of exif fields to include "
+"DateTimeOriginal is default."
+msgstr ""
+
+#: scripts/remote_import.php:312
+msgid ""
+"-g, --gallery[=gallery_id    The gallery id to add directory contents to"
+msgstr ""
+
+#: scripts/ansel.php:325
+msgid "-g, --gallery[=shortname]    Select gallery to use"
+msgstr "-g, --gallery[=shortname]    Seleziona galleria da usare"
+
+#: scripts/all_images_exif_to_tags.php:120 scripts/ansel.php:322
+#: scripts/recursive_import.php:225 scripts/remote_import.php:308
+msgid "-h, --help                   Show this help"
+msgstr "-h, --help                   Mostra questo help"
+
+#: scripts/recursive_import.php:229
+msgid ""
+"-k, --keep                   Do not delete empty galleries after import is "
+"complete."
+msgstr ""
+
+#: scripts/ansel.php:323
+msgid ""
+"-l, --list                   List galleries or photos (if combined with -g)"
+msgstr ""
+"-l, --list                   Lista gallerie o foto (se combinato con -g)"
+
+#: scripts/all_images_exif_to_tags.php:122 scripts/ansel.php:329
+#: scripts/recursive_import.php:228 scripts/remote_import.php:311
+msgid "-p, --password[=password]    Horde login password"
+msgstr "-p, --password[=password]    Horde login password"
+
+#: scripts/remote_import.php:315
+msgid "-r, --remotehost[=url]       The url of the remote rpc server."
+msgstr ""
+
+#: scripts/remote_import.php:313
+msgid ""
+"-s, --slug[=gallery_slug]    The gallery slug to add directory contents to"
+msgstr ""
+
+#: scripts/ansel.php:330
+msgid "-t, --caption[=caption]      Caption for photo (if combined with -a)"
+msgstr ""
+"-t, --caption[=caption]      Contenitore per le foto (se combinato con -a)"
+
+#: scripts/all_images_exif_to_tags.php:121 scripts/ansel.php:328
+#: scripts/recursive_import.php:227 scripts/remote_import.php:310
+msgid "-u, --username[=username]    Horde login username"
+msgstr "-u, --username[=username]    Horde login username"
+
+#: lib/Exif.php:163
+#, php-format
+msgid "90 deg CCW"
+msgstr "90 gradi CCW"
+
+#: lib/Exif.php:165
+#, php-format
+msgid "90 deg CCW Mirrored"
+msgstr "90 gradi CCW Specchiati"
+
+#: lib/Exif.php:167
+#, php-format
+msgid "90 deg CW"
+msgstr "90 gradi CW"
+
+#: lib/Exif.php:161
+#, php-format
+msgid "90 deg CW Mirrored"
+msgstr "90 gradi CW Specchiati"
+
+#: config/styles.php.dist:116
+msgid "A Lightbox Inspired Style (No Background)"
+msgstr "Stile ispirato a una scatola di luce (senza sfondo)"
+
+#: config/styles.php.dist:104
+msgid "A Lightbox Inspired Style (White Background)"
+msgstr "Stile ispirato a una scatola di luce (sfondo bianco)"
+
+#: lib/api.php:483
+msgid "A gallery to add this photo to is required."
+msgstr "E' richiesta una galleria per aggiungere questa foto."
+
+#: lib/Exif.php:134
+msgid "A-DEP"
+msgstr "A-DEP"
+
+#: gallery.php:55
+#, fuzzy, php-format
+msgid "Access denied adding a gallery to \"%s\"."
+msgstr "Accesso negato al salvataggio di appunto in %s."
+
+#: xppublish.php:178 scripts/ansel.php:134 lib/api.php:378 lib/api.php:497
+#, fuzzy, php-format
+msgid "Access denied adding photos to \"%s\"."
+msgstr "Accesso negato salvano attivit&agrave; in %s."
+
+#: lib/Ansel.php:1647
+#, fuzzy
+msgid "Access denied adding tags to this gallery."
+msgstr "Accesso negato al salvataggio di appunto in %s."
+
+#: lib/Ansel.php:2629
+#, fuzzy
+msgid "Access denied adding tags to this photo."
+msgstr "Accesso negato al salvataggio di appunto in %s."
+
+#: lib/Ansel.php:1137
+#, fuzzy, php-format
+msgid "Access denied copying photos to \"%s\"."
+msgstr "Accesso negato salvano attivit&agrave; in %s."
+
+#: lib/api.php:596
+#, fuzzy
+msgid "Access denied creating new galleries."
+msgstr "Si è verificato un problema eliminando le attivit&agrave; %s."
+
+#: lib/api.php:554 lib/api.php:633
+#, fuzzy
+msgid "Access denied deleting galleries."
+msgstr "Si è verificato un problema eliminando le attivit&agrave; %s."
+
+#: lib/api.php:645 gallery/delete.php:28 gallery/delete.php:61
+#, fuzzy, php-format
+msgid "Access denied deleting gallery \"%s\"."
+msgstr "Si è verificato un problema eliminando le attivit&agrave; %s."
+
+#: image.php:531 lib/api.php:571 lib/Views/Results.php:125
+#, fuzzy, php-format
+msgid "Access denied deleting photos from \"%s\"."
+msgstr "Accesso negato salvano attivit&agrave; in %s."
+
+#: gallery.php:86 lib/Ansel.php:2432
+#, fuzzy, php-format
+msgid "Access denied downloading photos from \"%s\"."
+msgstr "Si è verificato un problema eliminando le attivit&agrave; %s."
+
+#: gallery.php:156
+msgid "Access denied editing galleries."
+msgstr "Accesso negato durante la modifica delle gallerie."
+
+#: lib/Faces.php:868 gallery/sort.php:32 faces/gallery.php:34
+#, fuzzy, php-format
+msgid "Access denied editing gallery \"%s\"."
+msgstr "Si è verificato un problema eliminando le attivit&agrave; %s."
+
+#: image.php:262 image.php:652 lib/Faces.php:557 lib/Faces.php:655
+#: lib/XRequest/EditFaces.php:134 faces/custom.php:29 faces/delete.php:29
+#: faces/name.php:28
+#, fuzzy
+msgid "Access denied editing the photo."
+msgstr "Si è verificato un problema eliminando le attivit&agrave; %s."
+
+#: lib/Ansel.php:1094
+#, fuzzy, php-format
+msgid "Access denied moving photos to \"%s\"."
+msgstr "Accesso negato salvano attivit&agrave; in %s."
+
+#: lib/Ansel.php:1096
+#, fuzzy, php-format
+msgid "Access denied removing photos from \"%s\"."
+msgstr "Accesso negato salvano attivit&agrave; in %s."
+
+#: gallery.php:199
+#, fuzzy, php-format
+msgid "Access denied saving gallery \"%s\"."
+msgstr "Accesso negato al salvataggio di appunto in %s."
+
+#: image.php:164 image.php:322 image.php:360
+#, fuzzy, php-format
+msgid "Access denied saving photo to \"%s\"."
+msgstr "Accesso negato al salvataggio di appunto in %s."
+
+#: gallery/captions.php:33
+#, php-format
+msgid "Access denied setting captions for %s."
+msgstr "Accesso negato durante l'impostazione delle didascalie per %s."
+
+#: lib/Views/Abstract.php:56
+#, fuzzy, php-format
+msgid "Access denied to gallery \"%s\"."
+msgstr "Si è verificato un problema eliminando le attivit&agrave; %s."
+
+#: lib/Ansel.php:1632 lib/Views/Gallery.php:97 lib/Block/gallery.php:143
+#: lib/Block/recent_comments.php:154 lib/Block/recently_added.php:190
+#, fuzzy
+msgid "Access denied viewing this gallery."
+msgstr "Accesso negato al salvataggio di appunto in %s."
+
+#: lib/Ansel.php:2608 img/download.php:26 img/full.php:26 img/index.php:25
+#: img/mini.php:25 img/prettythumb.php:26 img/screen.php:25 img/thumb.php:25
+#, fuzzy
+msgid "Access denied viewing this photo."
+msgstr "Accesso negato al salvataggio di appunto in %s."
+
+#: faces/claim.php:41 faces/report.php:43
+msgid "Action was cancelled."
+msgstr "Azione cancellata."
+
+#: lib/Widget/Actions.php:22
+msgid "Actions"
+msgstr "Azioni"
+
+#: templates/view/gallery.inc:108 templates/view/gallerylightbox.inc:127
+#: templates/view/results.inc:97
+msgid "Actions: "
+msgstr "Azioni: "
+
+#: lib/Widget/Tags.php:58
+msgid "Add"
+msgstr "Aggiungi"
+
+#: browse.php:26
+msgid "Add Content"
+msgstr "Aggiungi un contenuto"
+
+#: img/upload.php:231
+msgid "Add Photo"
+msgstr "Aggiungi Foto"
+
+#: templates/faces/gallery.inc:23
+msgid "Add a custom face range"
+msgstr "Aggiungi un face range personalizzato"
+
+#: lib/Widget/Actions.php:79
+msgid "Add to bookmarks"
+msgstr "Aggiungi ai Preferiti"
+
+#: gallery.php:41 gallery.php:274
+msgid "Adding A New Gallery"
+msgstr "Aggiungi Una Nuova Galleria"
+
+#: gallery.php:74
+#, php-format
+msgid "Adding A Subgallery to %s"
+msgstr "Aggiungi una sottogalleria a %s"
+
+#: scripts/recursive_import.php:205 scripts/remote_import.php:252
+msgid "Adding subdirectories:"
+msgstr "Aggiungi sottodirectory:"
+
+#: lib/api.php:188
+msgid "Administrators"
+msgstr "Amministratore"
+
+#: report.php:49
+msgid "Advertisement content"
+msgstr "Contenuto inserzione"
+
+#: templates/view/gallery.inc:105 templates/view/gallerylightbox.inc:124
+#: templates/view/results.inc:93
+msgid "All"
+msgstr "Tutti"
+
+#: lib/Block/recent_comments.php:68 lib/Block/recently_added.php:73
+msgid "All Galleries"
+msgstr "Tutte le gallerie"
+
+#: faces/search/all.php:17 faces/search/tabs.php:32
+msgid "All faces"
+msgstr "Tutte le facce"
+
+#: templates/gallery/gallery.inc:79
+#, fuzzy
+msgid "Allow all ages"
+msgstr "Tutte le Pagine"
+
+#: templates/gallery/gallery.inc:101 config/prefs.php.dist:229
+msgid "Anyone"
+msgstr "Chiunque"
+
+#: lib/Exif.php:349
+msgid "Aperture"
+msgstr "Fessura"
+
+#: templates/view/gallery.inc:32 templates/view/gallerylightbox.inc:33
+#: templates/view/results.inc:20
+msgid "Are you sure you want to delete the selected photos?"
+msgstr "Sei sicuro di voler eliminare le foto selezionate?"
+
+#: lib/Exif.php:362
+msgid "Artist"
+msgstr "Artista"
+
+#: templates/view/list.inc:20
+#, fuzzy
+msgid "Ascending"
+msgstr ""
+"#-#-#-#-#  it_IT.po (agora cvs-0.1)  #-#-#-#-#\n"
+"Crescente\n"
+"#-#-#-#-#  it_IT.po (Gollem 0.0.1-cvs)  #-#-#-#-#\n"
+"Crescente\n"
+"#-#-#-#-#  it_IT.po (IMP 4.0-cvs)  #-#-#-#-#\n"
+"Crescente\n"
+"#-#-#-#-#  it_IT.po (Mnemo 2.0-cvs)  #-#-#-#-#\n"
+"Crescente\n"
+"#-#-#-#-#  it_IT.po (Nag 0.0.2)  #-#-#-#-#\n"
+"Crescente\n"
+"#-#-#-#-#  it_IT.po (Turba 0.0.3)  #-#-#-#-#\n"
+"Ascendente\n"
+"#-#-#-#-#  it_IT.po (Whups 0.0.1-cvs)  #-#-#-#-#\n"
+"Crescente"
+
+#: perms.php:53
+msgid "Attempt to edit a non-existent share."
+msgstr "Tentativo di modificare una condivisione non esistente."
+
+#: templates/gallery/gallery.inc:102 config/prefs.php.dist:230
+msgid "Authenticated users"
+msgstr "Utente Autenticato"
+
+#: config/prefs.php.dist:221
+msgid "Automatically watermark photos?"
+msgstr ""
+
+#: lib/Exif.php:130
+msgid "Av-priority"
+msgstr "Priorità Av"
+
+#: lib/Exif.php:234
+msgid "Average"
+msgstr "Media"
+
+#: templates/view/image.inc:86 templates/view/image.inc:87
+#: templates/view/image.inc:111 templates/view/image.inc:112
+#: templates/image/crop_image.inc:104 templates/image/edit_image.inc:20
+#: templates/image/resize_image.inc:49
+msgid "Back to gallery"
+msgstr "Torna alla galleria"
+
+#: image.php:578 image.php:625 lib/Views/Results.php:160
+#: lib/Views/Results.php:208
+msgid "Bad input."
+msgstr "Input errato."
+
+#: lib/Forms/Watermark.php:54 config/prefs.php.dist:198
+msgid "Bottom"
+msgstr "Sotto"
+
+#: lib/Ansel.php:540 config/prefs.php.dist:57
+msgid "Browse"
+msgstr "Passa in Rassegna"
+
+#: lib/Ansel.php:661
+msgid "Browse Tags"
+msgstr "Passa in Rassegna i tag"
+
+#: lib/Exif.php:333
+msgid "Camera Make"
+msgstr "Fabbricazione macchina fotografica"
+
+#: lib/Exif.php:334
+msgid "Camera Model"
+msgstr "Modello macchina fotografica"
+
+#: lib/Exif.php:360
+msgid "Camera Orientation"
+msgstr "Orientazione macchina fotografica"
+
+#: scripts/remote_import.php:283 lib/Ansel.php:480
+#, php-format
+msgid "Can't get unknown file type \"%s\"."
+msgstr "Impossibile aprire il file, formato \"%s\" sconosciuto."
+
+#: disclamer.php:32 disclamer.php:35 report.php:47
+#: templates/image/crop_image.inc:105 templates/image/edit_image.inc:21
+#: templates/image/resize_image.inc:50
+#: templates/gallery/delete_confirmation.inc:11 templates/faces/custom.inc:53
+#: templates/faces/define.inc:38 faces/claim.php:40 faces/report.php:41
+#: faces/savecustom.php:27
+msgid "Cancel"
+msgstr "Cancella"
+
+#: templates/image/preview_cropimage.inc:20
+#: templates/image/preview_cropimage.inc:21
+#: templates/image/preview_image.inc:23 templates/image/preview_image.inc:24
+msgid "Cancel Change"
+msgstr "Annulla modifiche"
+
+#: faces/search/image_save.php:74
+#, fuzzy
+msgid "Cannot read photo signature"
+msgstr "Hai gia' votato in questo sondaggio."
+
+#: faces/search/image_save.php:86
+#, fuzzy
+msgid "Cannot save photo signature"
+msgstr "Hai gia' votato in questo sondaggio."
+
+#: faces/search/image.php:59 faces/search/image_save.php:66
+#, fuzzy
+msgid "Cannot store search photo"
+msgstr "Hai gia' votato in questo sondaggio."
+
+#: lib/Forms/Image.php:39 lib/Forms/Upload.php:52
+msgid "Caption"
+msgstr "Didascalia"
+
+#: gallery/captions.php:61
+msgid "Caption Editor"
+msgstr "Modificatore didascalie"
+
+#: gallery/captions.php:52
+msgid "Captions Saved."
+msgstr "Didascalie salvate."
+
+#: config/prefs.php.dist:70
+msgid "Category"
+msgstr "Categoria"
+
+#: lib/Forms/Watermark.php:46 lib/Forms/Watermark.php:53
+#: config/prefs.php.dist:186 config/prefs.php.dist:197
+msgid "Center"
+msgstr "Centrato"
+
+#: lib/Exif.php:235
+msgid "Center Weighted Average"
+msgstr "Media a pesatura centrata"
+
+#: config/prefs.php.dist:14
+msgid ""
+"Change display options such as which view to display by default, how many "
+"photos to display on a page, and the default gallery style to use."
+msgstr ""
+
+#: config/prefs.php.dist:24
+msgid "Change options dealing with tags and exif data."
+msgstr ""
+
+#: lib/Widget/Actions.php:87
+msgid "Change properties"
+msgstr "Cambia proprietà"
+
+#: config/prefs.php.dist:31
+msgid ""
+"Change your user permission options such as who can download original "
+"photos, and what permissions newly created galleries should have by default."
+msgstr ""
+
+#: config/prefs.php.dist:38
+msgid "Change your watermark options."
+msgstr "Cambia le tue opzioni di filigrana."
+
+#: faces/savecustom.php:28
+msgid "Changes cancelled."
+msgstr ""
+
+#: templates/tile/face.inc:27 lib/Faces.php:1140
+msgid "Claim"
+msgstr "Claim"
+
+#: lib/XRequest/EditCaption.php:43
+msgid "Click to add caption..."
+msgstr ""
+
+#: lib/Exif.php:248
+msgid "Cloudy"
+msgstr "Nuvoloso"
+
+#: lib/Exif.php:299
+msgid "Color Sequential Area Sensor"
+msgstr "Sensore d'area di colori sequenziale"
+
+#: lib/Exif.php:301
+msgid "Color Sequential Linear Sensor"
+msgstr "Sensore lineare di colori sequenziale"
+
+#: lib/Exif.php:357
+msgid "Color Space"
+msgstr "Spaziatura Colore"
+
+#: lib/Forms/Ecard.php:42
+msgid "Comments:"
+msgstr "Commenti:"
+
+#: lib/Exif.php:269
+msgid "Compulsory Flash"
+msgstr "Flash forzato"
+
+#: lib/Exif.php:271
+msgid "Compulsory Flash, Return light detected"
+msgstr "Flash forzato, rilevato ritorno di luce"
+
+#: lib/Exif.php:270
+msgid "Compulsory Flash, Return light not detected"
+msgstr "Flash forzato, ritorno di luce non rilevato"
+
+#: disclamer.php:25
+msgid "Content Disclaimer"
+msgstr "Contenuti responsabilità"
+
+#: lib/Report.php:98
+#, php-format
+msgid "Content abuse report in %s"
+msgstr "Abuso di Contenuto rapportato in %s"
+
+#: disclamer.php:32
+#, php-format
+msgid "Continue - I'm over %d"
+msgstr "Continua - Ho finito %d"
+
+#: lib/Views/Results.php:225
+#, php-format
+msgid "Copied %d photo from %s to %s"
+msgid_plural "Copied %d photos from %s to %s"
+msgstr[0] "Copiate %d foto da %s su %s"
+msgstr[1] "Copiate %d foto da %s su %s"
+
+#: image.php:632
+#, fuzzy, php-format
+msgid "Copied %d photo to %s"
+msgid_plural "Copied %d photos to %s"
+msgstr[0] "Copiando %s a %s"
+msgstr[1] "Copiando %s a %s"
+
+#: templates/view/gallery.inc:116 templates/view/gallerylightbox.inc:135
+#: templates/view/results.inc:103
+msgid "Copy"
+msgstr "Copia"
+
+#: lib/Exif.php:361
+msgid "Copyright"
+msgstr "Copyright"
+
+#: report.php:52
+msgid "Copyright infringement"
+msgstr "Violazione Copyright"
+
+#: scripts/all_images_exif_to_tags.php:102
+#: scripts/all_images_exif_to_tags.php:106
+#, php-format
+msgid "Could not extract exif fields from %s: %s"
+msgstr ""
+
+#: lib/Ansel.php:933
+#, fuzzy, php-format
+msgid "Could not save gallery, the slug, \"%s\", already exists."
+msgstr "La pagina \"%s\" esiste già."
+
+#: scripts/ansel.php:187
+msgid "Couldn't list galleries."
+msgstr "Impossibile elencare le gallerie."
+
+#: scripts/ansel.php:44
+msgid "Couldn't read command-line options."
+msgstr "Impossibile leggere l'opzione della linea di comando."
+
+#: templates/xppublish/list.inc:19
+msgid "Create"
+msgstr "Crea"
+
+#: faces/custom.php:68 faces/search/image_define.php:25
+#, fuzzy
+msgid "Create a new face"
+msgstr "Crea Nuova Categoria"
+
+#: templates/xppublish/new.inc:8
+#, fuzzy
+msgid "Create a new gallery"
+msgstr "Crea Nuova Categoria"
+
+#: templates/xppublish/list.inc:18
+msgid "Create a new gallery:"
+msgstr "Crea una nuova galleria"
+
+#: lib/Widget/Actions.php:86
+msgid "Create a subgallery"
+msgstr "Crea una sottogalleria"
+
+#: lib/Tile/Gallery.php:114
+msgid "Created:"
+msgstr "Creato:"
+
+#: scripts/recursive_import.php:154 scripts/remote_import.php:188
+#, fuzzy, php-format
+msgid "Creating gallery: \"%s\""
+msgstr "Creazione fallita: %s"
+
+#: image.php:664 templates/image/crop_image.inc:126
+#: templates/image/edit_image.inc:43 templates/image/resize_image.inc:72
+#, fuzzy
+msgid "Crop"
+msgstr "Copia"
+
+#: lib/Exif.php:252
+msgid "Custom"
+msgstr "Personalizzato"
+
+#: lib/Forms/Watermark.php:32
+msgid "Custom Watermark"
+msgstr "Filigrana personalizzata"
+
+#: config/prefs.php.dist:50
+msgid ""
+"Custom text to display describing your galleries. This will be displayed in "
+"place of your username when grouping galleries by username."
+msgstr ""
+"Testo personalizzato da visualizzare descrive le tue gallerie. Il testo "
+"verrà visualizzato nel posto del tuo username quando si raggruppano gallerie "
+"per username."
+
+#: config/prefs.php.dist:121 config/prefs.php.dist:177
+msgid "Custom watermark to use for photos"
+msgstr "Usare filigrana personalizzata per le foto"
+
+#: templates/view/list.inc:17 lib/Block/recently_added.php:99
+msgid "Date"
+msgstr "Data"
+
+#: lib/Exif.php:340
+msgid "Date Photo Digitized"
+msgstr "Data difitalizzazione foto"
+
+#: lib/Exif.php:338
+msgid "Date Photo Modified"
+msgstr "Data odifica foto"
+
+#: lib/Exif.php:339 config/prefs.php.dist:274
+msgid "Date Photo Taken"
+msgstr "Data in cui è stata fatta la foto"
+
+#: config/prefs.php.dist:137 config/styles.php.dist:56
+msgid "Default"
+msgstr "Default"
+
+#: templates/prefs/default_category_select.inc:7
+#, fuzzy
+msgid "Default category for galleries"
+msgstr "Non era possibile elencare i discussioni. %s"
+
+#: config/prefs.php.dist:136
+msgid "Default style for galleries"
+msgstr "Stile predefinito per le gallerie"
+
+#: templates/view/gallery.inc:110 templates/view/gallerylightbox.inc:129
+#: templates/view/image.inc:51 templates/view/results.inc:98
+#: templates/view/slideshow.inc:21 lib/Widget/Actions.php:115
+msgid "Delete"
+msgstr "Cancella"
+
+#: templates/faces/image.inc:7
+msgid "Delete face"
+msgstr "Elimina faccia"
+
+#: image.php:540 lib/Views/Results.php:134
+msgid "Deleted the photo."
+msgstr "Elimina la foto."
+
+#: scripts/recursive_import.php:122 scripts/recursive_import.php:130
+#: scripts/remote_import.php:129 scripts/remote_import.php:137
+#, fuzzy, php-format
+msgid "Deleting empty gallery, \"%s\""
+msgstr "Elimina Notizia"
+
+#: templates/view/list.inc:22
+#, fuzzy
+msgid "Descending"
+msgstr ""
+"#-#-#-#-#  it_IT.po (agora cvs-0.1)  #-#-#-#-#\n"
+"Discendente\n"
+"#-#-#-#-#  it_IT.po (Gollem 0.0.1-cvs)  #-#-#-#-#\n"
+"Decrescente\n"
+"#-#-#-#-#  it_IT.po (IMP 4.0-cvs)  #-#-#-#-#\n"
+"Decrescente\n"
+"#-#-#-#-#  it_IT.po (Mnemo 2.0-cvs)  #-#-#-#-#\n"
+"Discendente\n"
+"#-#-#-#-#  it_IT.po (Nag 0.0.2)  #-#-#-#-#\n"
+"Discendente\n"
+"#-#-#-#-#  it_IT.po (Turba 0.0.3)  #-#-#-#-#\n"
+"Discendente\n"
+"#-#-#-#-#  it_IT.po (Whups 0.0.1-cvs)  #-#-#-#-#\n"
+"Discendente"
+
+#: templates/image/crop_image.inc:146
+msgid "Desired dimensions:"
+msgstr ""
+
+#: config/prefs.php.dist:13
+msgid "Display Options"
+msgstr "Opzioni di Visualizzazione"
+
+#: lib/Faces.php:1139
+msgid "Do you know someone in this photo?"
+msgstr "Conosci qualcuno in questa foto?"
+
+#: report.php:20
+msgid "Do you really want to report this gallery?"
+msgstr "Vuoi veramente fare un resoconto di questa galleria?"
+
+#: report.php:31
+#, fuzzy
+msgid "Do you really want to report this photo?"
+msgstr "Sei sicuro di voler eliminare questa regola?"
+
+#: templates/view/image.inc:51 templates/view/slideshow.inc:21
+#, fuzzy, php-format
+msgid "Do you want to permanently delete \"%s\"?"
+msgstr "Sicuro di voler cancellare PERMANENTEMENTE questi messaggi?"
+
+#: scripts/all_images_exif_to_tags.php:109 gallery/sort.php:74
+msgid "Done"
+msgstr "Fatto"
+
+#: templates/view/image.inc:57 templates/view/slideshow.inc:29
+#, fuzzy
+msgid "Download Original Photo"
+msgstr "Scarica revisione %s"
+
+#: lib/Widget/Actions.php:82
+#, fuzzy
+msgid "Download as zip file"
+msgstr "Scarica differenza come: "
+
+#: gallery/sort.php:73
+msgid "Drag photos to the desired sort position."
+msgstr "trascina le foto nella desiderata posizione."
+
+#: xppublish.php:206
+#, php-format
+msgid "ERROR: %s"
+msgstr "ERRORE: %s"
+
+#: lib/Exif.php:124
+msgid "Easy shooting"
+msgstr "Scatto facile"
+
+#: img/ecard.php:59
+msgid "Ecard - "
+msgstr "Ecard - "
+
+#: templates/view/image.inc:48 templates/view/slideshow.inc:18
+#: templates/tile/image.inc:18
+msgid "Edit"
+msgstr "Modifica"
+
+#: image.php:278
+#, php-format
+msgid "Edit %s :: %s"
+msgstr "Modifica %s :: %s"
+
+#: perms.php:245
+msgid "Edit Permissions"
+msgstr "Modifica Permessi"
+
+#: perms.php:249
+#, php-format
+msgid "Edit Permissions for %s"
+msgstr "Modifica permessi per %s"
+
+#: image.php:465
+msgid "Edit Photo"
+msgstr "Modifica Foto"
+
+#: image.php:180
+#, fuzzy
+msgid "Edit a photo"
+msgstr "Modifica remota"
+
+#: templates/faces/image.inc:9
+msgid "Edit face region"
+msgstr "Modifica faccia regione"
+
+#: lib/Widget/ImageFaces.php:72
+msgid "Edit faces"
+msgstr "Modifica facce"
+
+#: image.php:134
+#, php-format
+msgid "Edit properties :: %s"
+msgstr "Modifica proprietà :: %s"
+
+#: lib/Widget/Links.php:41
+msgid "Embed: "
+msgstr ""
+
+#: lib/Widget/Actions.php:114
+msgid "Empty"
+msgstr "Svuota"
+
+#: gallery/captions.php:26
+#, php-format
+msgid "Error accessing %s: %s"
+msgstr "Errore nell'accesso a %s: %s"
+
+#: rss.php:35
+#, fuzzy
+msgid "Error retrieving feed"
+msgstr "Errore durante l'esecuzione dello script: (%d) %s"
+
+#: templates/xppublish/list.inc:12
+msgid "Existing galleries:"
+msgstr "Gallerie esistenti"
+
+#: lib/Exif.php:346
+msgid "Exposure"
+msgstr "Esposizione"
+
+#: lib/Exif.php:352
+msgid "Exposure Bias"
+msgstr "Effetti esposizione"
+
+#: lib/Exif.php:353
+msgid "Exposure Mode"
+msgstr "Modo esposizione"
+
+#: config/prefs.php.dist:275
+#, fuzzy
+msgid "Exposure Time"
+msgstr "Esporta attivit&agrave;"
+
+#: scripts/all_images_exif_to_tags.php:104
+#, php-format
+msgid "Extracted exif fields from %s"
+msgstr ""
+
+#: lib/Exif.php:350
+msgid "F-Number"
+msgstr "F-Numero"
+
+#: faces/claim.php:58 faces/face.php:34 faces/report.php:60
+msgid "Face"
+msgstr "Faccia"
+
+#: lib/Faces.php:994 lib/Faces.php:1022
+msgid "Face does not exist"
+msgstr "La faccia non esiste"
+
+#: lib/Faces.php:46
+#, fuzzy
+msgid "Face driver does not exist."
+msgstr "Riferimento \"%s\" non esistente."
+
+#: faces/search/name.php:20
+msgid "Face name to search"
+msgstr "Nome faccia da cercare"
+
+#: faces/claim.php:63 faces/report.php:65
+msgid "Face name was not reported."
+msgstr "Il nome della faccia non è riportato."
+
+#: faces/savecustom.php:54
+msgid "Face successfuly created"
+msgstr "Faccia creata con successo"
+
+#: faces/savecustom.php:56
+msgid "Face successfuly updated"
+msgstr "Faccia aggiornata con successo"
+
+#: faces/search/image.php:21
+msgid "Face to search for"
+msgstr "Faccia da cercare per"
+
+#: templates/faces/gallery.inc:7 lib/Ansel.php:580
+msgid "Faces"
+msgstr "Facce"
+
+#: lib/Forms/Upload.php:67
+#, php-format
+msgid "File %s"
+msgstr "File %s"
+
+#: lib/Exif.php:337
+msgid "File Size"
+msgstr "Dimensione File"
+
+#: lib/api.php:280 lib/api.php:346
+msgid "File not found."
+msgstr "File non trovato."
+
+#: lib/Forms/Upload.php:47 lib/Forms/Upload.php:76
+msgid "File to upload"
+msgstr "File da caricare"
+
+#: templates/faces/gallery.inc:5
+msgid "Filename"
+msgstr "Nome File"
+
+#: lib/Widget/Actions.php:94 lib/Widget/ImageFaces.php:72
+msgid "Find faces"
+msgstr "Trova facce"
+
+#: templates/tile/face.inc:24
+msgid "Find other photos this face appears in"
+msgstr ""
+
+#: templates/tile/face.inc:34 templates/faces/face.inc:14 lib/Faces.php:1145
+msgid "Find similar"
+msgstr "Trova simili"
+
+#: lib/Exif.php:251 lib/Exif.php:266
+msgid "Flash"
+msgstr "Flash"
+
+#: lib/Exif.php:355
+msgid "Flash Setting"
+msgstr "Impostazioni Flash"
+
+#: lib/Exif.php:274
+msgid "Flash, Auto-Mode"
+msgstr "Flash, modalità automatica"
+
+#: lib/Exif.php:276
+msgid "Flash, Auto-Mode, Return light detected"
+msgstr "Flash, modalità automatica, ritorno di luce rilevato"
+
+#: lib/Exif.php:275
+msgid "Flash, Auto-Mode, Return light not detected"
+msgstr "Flash, modalità automatica, ritorno di luce non rilevato"
+
+#: lib/Exif.php:268
+msgid "Flash, strobe return light detected"
+msgstr "Flash, ritorno di luce stroboscopica rilevato"
+
+#: lib/Exif.php:267
+msgid "Flash, strobe return light not detected"
+msgstr "Flash, ritorno di luce stroboscopica non rilevato"
+
+#: templates/image/crop_image.inc:113 templates/image/edit_image.inc:29
+#: templates/image/resize_image.inc:58
+msgid "Flip"
+msgstr "Invertire"
+
+#: lib/Exif.php:250
+msgid "Fluorescent"
+msgstr "Fluorescente"
+
+#: lib/Exif.php:347
+msgid "Focal Length"
+msgstr "Lunghezza focale"
+
+#: lib/Exif.php:348
+msgid "Focal Length (35mm equiv)"
+msgstr "Lunghezza focale (35mm equiv)"
+
+#: faces/search/owner.php:23
+#, fuzzy, php-format
+msgid "From galleries of %s"
+msgstr "[linea %s di %s]"
+
+#: faces/search/owner.php:21 faces/search/tabs.php:33
+msgid "From my galleries"
+msgstr "Dalle mie gallerie"
+
+#: templates/tile/image.inc:8
+msgid "From: "
+msgstr "Da: "
+
+#: lib/Ansel.php:661 config/prefs.php.dist:58
+msgid "Galleries"
+msgstr "Gallerie"
+
+#: templates/group/header.inc:6
+#, php-format
+msgid "Galleries grouped by %s"
+msgstr "Gallerie raggruppate per %s"
+
+#: lib/Views/List.php:174 lib/Views/List.php:178
+#, fuzzy, php-format
+msgid "Galleries in category \"%s\""
+msgstr "Nome File"
+
+#: templates/faces/face.inc:33 lib/Block/gallery.php:3
+#: lib/Block/gallery.php:30 lib/Block/gallery.php:50
+#: lib/Block/recent_comments.php:31 lib/Block/recent_comments.php:54
+#: lib/Block/recently_added.php:28 lib/Block/recently_added.php:56
+#: lib/Block/recently_added.php:99
+msgid "Gallery"
+msgstr "Galleria"
+
+#: image.php:64 scripts/remote_import.php:175 scripts/remote_import.php:183
+#: lib/Ansel.php:2949 img/upload.php:22
+#, php-format
+msgid "Gallery %s not found."
+msgstr "Galleria %s non trovata."
+
+#: templates/gallery/gallery.inc:77
+msgid "Gallery Ages"
+msgstr "Età galleria"
+
+#: templates/gallery/gallery.inc:26
+msgid "Gallery Category"
+msgstr "Categoria galleria"
+
+#: report.php:71 templates/gallery/gallery.inc:55 faces/claim.php:55
+#: faces/report.php:57
+msgid "Gallery Description"
+msgstr "Descrizione galleria"
+
+#: templates/gallery/gallery.inc:48
+msgid "Gallery Display Name"
+msgstr "Mostra nome galleria"
+
+#: report.php:72
+msgid "Gallery Id"
+msgstr "Id galleria"
+
+#: lib/Views/List.php:70 lib/Views/List.php:181
+msgid "Gallery List"
+msgstr "Lista gallerie"
+
+#: report.php:70 lib/Block/my_galleries.php:53 faces/claim.php:54
+#: faces/report.php:56
+msgid "Gallery Name"
+msgstr "Nome galleria"
+
+#: templates/gallery/gallery.inc:17
+msgid "Gallery Parent"
+msgstr "Genitori galleria"
+
+#: lib/Tile/Gallery.php:92
+msgid "Gallery Properties"
+msgstr "Proprietà galleria"
+
+#: templates/gallery/gallery.inc:61
+msgid "Gallery Slug"
+msgstr "Slug galleria"
+
+#: templates/gallery/gallery.inc:69
+msgid "Gallery Tags"
+msgstr "Tag galleria"
+
+#: lib/Block/gallery.php:140 lib/Block/recent_comments.php:151
+#: lib/Block/recently_added.php:187
+msgid "Gallery does not exist."
+msgstr "La galleria non esiste."
+
+#: gallery/sort.php:47
+msgid "Gallery sorted."
+msgstr "Galleria ordinata."
+
+#: report.php:79 report.php:85
+msgid "Gallery was not reported."
+msgstr "La galleria non è riferita."
+
+#: report.php:82
+msgid "Gallery was reported."
+msgstr "La galleria non è riferita."
+
+#: config/prefs.php.dist:12 config/prefs.php.dist:22 config/prefs.php.dist:29
+#: config/prefs.php.dist:36
+#, fuzzy
+msgid "General Options"
+msgstr "Salva Opzioni"
+
+#: lib/Forms/Watermark.php:40 config/prefs.php.dist:211
+msgid "Giant"
+msgstr "Gigante"
+
+#: templates/image/crop_image.inc:119 templates/image/edit_image.inc:35
+#: templates/image/resize_image.inc:64
+msgid "Grayscale"
+msgstr "Scala di griggi"
+
+#: templates/view/list.inc:10 templates/group/header.inc:11
+msgid "Group by: "
+msgstr "Raggruppa per: "
+
+#: config/prefs.php.dist:72
+msgid "Group galleries by"
+msgstr "Gruppo gallerie di"
+
+#: lib/Exif.php:342
+msgid "Height"
+msgstr "Altezza"
+
+#: lib/Forms/Watermark.php:48 config/prefs.php.dist:188
+msgid "Horizontal Alignment"
+msgstr "Allineamento orizzontale"
+
+#: faces/claim.php:53
+msgid "I know who is on one of your photos"
+msgstr ""
+
+#: lib/Exif.php:351
+msgid "ISO Setting"
+msgstr "Impostazioni ISO"
+
+#: image.php:129
+#, fuzzy
+msgid "Image not found."
+msgstr "Campagna non trovata."
+
+#: lib/Ansel.php:2151
+msgid "Incomplete photo"
+msgstr "Foto incompleta"
+
+#: scripts/ansel.php:127
+#, php-format
+msgid "Invalid gallery \"%s\" specified."
+msgstr "Galleria specificata \"%s\" invalida."
+
+#: xppublish.php:87 xppublish.php:174 lib/api.php:267
+msgid "Invalid gallery specified."
+msgstr "Galleria specificata invalida."
+
+#: templates/image/crop_image.inc:161
+msgid "Landscape"
+msgstr ""
+
+#: lib/Forms/Watermark.php:39 config/prefs.php.dist:210
+msgid "Large"
+msgstr "Larga"
+
+#: lib/Block/my_galleries.php:53
+msgid "Last Modified"
+msgstr "Ultima Modifica"
+
+#: img/upload.php:237
+msgid "Learn how to publish photos directly from Windows."
+msgstr "Impara come pubblicare foto direttamente da windows."
+
+#: lib/Forms/Watermark.php:45 config/prefs.php.dist:185
+msgid "Left"
+msgstr "Sinistra"
+
+#: config/styles.php.dist:129 config/styles.php.dist:166
+msgid "Lightbox with Polaroids (No Background)"
+msgstr "Scatol di luce con Polaroid (senza sfondo)"
+
+#: lib/Widget/Links.php:13
+msgid "Links"
+msgstr "Link"
+
+#: scripts/ansel.php:192
+msgid "Listing Gallery/Name"
+msgstr "Elenco Galleria/Nome"
+
+#: scripts/ansel.php:176
+#, php-format
+msgid "Listing photos in %s"
+msgstr "Elenco foto in %s"
+
+#: templates/faces/gallery.inc:28 templates/faces/gallery.inc:40
+#: lib/XRequest/EditFaces.php:35
+msgid "Loading..."
+msgstr "Caricamento..."
+
+#: scripts/all_images_exif_to_tags.php:79 scripts/ansel.php:118
+#: scripts/recursive_import.php:88
+#, php-format
+msgid "Logged in successfully as \"%s\"."
+msgstr "Autenticazione come \"%s\" eseguita con successo."
+
+#: scripts/ansel.php:114
+msgid "Login is incorrect."
+msgstr "Login incorretto."
+
+#: templates/xppublish/login.inc:2
+#, php-format
+msgid "Login to %s"
+msgstr "Autenticazione in %s"
+
+#: lib/Forms/Image.php:37 lib/Forms/Upload.php:51
+msgid "Make this the default photo for this gallery?"
+msgstr "Rendi questa la foto di default per la galleria?"
+
+#: scripts/all_images_exif_to_tags.php:118 scripts/ansel.php:320
+#: scripts/recursive_import.php:223 scripts/remote_import.php:306
+msgid ""
+"Mandatory arguments to long options are mandatory for short options too."
+msgstr ""
+"Argomenti obbligatori per le opzioni lunghe sono abbligatori anche per le "
+"opzioni corte."
+
+#: templates/image/crop_image.inc:150 lib/Exif.php:132 lib/Exif.php:253
+msgid "Manual"
+msgstr "Manuale"
+
+#: lib/Widget/ImageFaces.php:76
+msgid "Manual face selection"
+msgstr "Selezione facce manuale"
+
+#: lib/Forms/Upload.php:79
+msgid "Maximum file size:"
+msgstr "Dimensione massima del file:"
+
+#: lib/Block/recent_faces.php:27
+#, fuzzy
+msgid "Maximum number of faces"
+msgstr "Numero di caratteri"
+
+#: lib/Block/my_galleries.php:25
+msgid "Maximum number of galleries"
+msgstr "Numero Massimo di gallerie"
+
+#: lib/Block/recently_added.php:33
+msgid "Maximum number of photos"
+msgstr "Massimo numero di foto"
+
+#: lib/Block/gallery.php:35
+#, fuzzy
+msgid "Maximum number of photos to display (0 means unlimited)"
+msgstr "Numero di caratteri"
+
+#: lib/Forms/Image.php:46 lib/Forms/Upload.php:48
+#, fuzzy
+msgid "Maximum photo size:"
+msgstr "Numero di caratteri"
+
+#: lib/Forms/Upload.php:58
+#, fuzzy, php-format
+msgid "Maximum photo size: %s; with a total of: %s"
+msgstr "Numero di caratteri"
+
+#: lib/Forms/Watermark.php:38 config/prefs.php.dist:209
+msgid "Medium"
+msgstr "Medio"
+
+#: lib/Exif.php:354
+msgid "Metering Mode"
+msgstr "Modalità di misurazione"
+
+#: templates/image/crop_image.inc:116 templates/image/edit_image.inc:32
+#: templates/image/resize_image.inc:61
+msgid "Mirror"
+msgstr "Specchio"
+
+#: lib/Exif.php:155
+#, php-format
+msgid "Mirrored"
+msgstr "Specchiato"
+
+#: lib/Views/Embedded.php:91
+msgid "Missing container parameter."
+msgstr ""
+
+#: lib/Tile/Gallery.php:116
+msgid "Modified"
+msgstr "Modificato"
+
+#: gallery.php:141
+#, php-format
+msgid "Modifying: %s"
+msgstr "Modifica in corso: %s"
+
+#: faces/search/image_define.php:34
+msgid ""
+"More then one face found in photo. Please note that you can search only one "
+"faceat a time."
+msgstr ""
+
+#: templates/view/gallery.inc:113 templates/view/gallerylightbox.inc:132
+#: templates/view/results.inc:100
+msgid "Move"
+msgstr "Sposta"
+
+#: image.php:585 lib/Views/Results.php:176
+#, fuzzy, php-format
+msgid "Moved %d photo from \"%s\" to \"%s\""
+msgid_plural "Moved %d photos from \"%s\" to \"%s\""
+msgstr[0] "Ritorna alla versione %s di \"%s\"."
+msgstr[1] "Ritorna alla versione %s di \"%s\"."
+
+#: lib/Exif.php:238
+msgid "Multi-Segment"
+msgstr "Multi-Segmento"
+
+#: lib/Exif.php:237
+msgid "Multi-Spot"
+msgstr "Multi-Spot"
+
+#: lib/Forms/Upload.php:55
+msgid "Multiple Photos"
+msgstr "Foto multiple"
+
+#: templates/group/owner.inc:3 lib/Ansel.php:640 lib/Views/List.php:155
+#: lib/Block/my_galleries.php:3 lib/Block/my_galleries.php:37
+#: config/prefs.php.dist:59
+msgid "My Galleries"
+msgstr "Gallerie personali"
+
+#: browse_edit.php:28
+msgid "My Photos :: Add Content"
+msgstr "Foto personali :: Aggiungi elemento"
+
+#: templates/view/list.inc:16
+msgid "Name"
+msgstr "Nome"
+
+#: faces/search/named.php:17 faces/search/tabs.php:34
+msgid "Named faces"
+msgstr "Facce nominate"
+
+#: templates/xppublish/new.inc:21
+msgid "New Gallery Description"
+msgstr "Descrizione nuova galleria"
+
+#: templates/xppublish/new.inc:12
+msgid "New Gallery Title"
+msgstr "Titolo nuova Galleria"
+
+#: templates/view/image.inc:84 templates/view/image.inc:85
+#: templates/view/image.inc:109 templates/view/image.inc:110
+#: templates/view/slideshow.inc:42 templates/view/slideshow.inc:43
+msgid "Next"
+msgstr "Prossimo"
+
+#: config/prefs.php.dist:220
+msgid "No"
+msgstr "No"
+
+#: lib/Exif.php:265 lib/Exif.php:272 lib/Exif.php:273 lib/Exif.php:277
+msgid "No Flash"
+msgstr "Flash disattivato"
+
+#: templates/faces/faces.inc:10 templates/faces/index.inc:14
+#: templates/faces/search.inc:18 lib/XRequest/EditFaces.php:120
+#: faces/image.php:52 faces/search/image.php:76
+msgid "No faces found"
+msgstr "Nessuna faccia trovata"
+
+#: faces/search/image_define.php:36
+msgid "No faces found. Define you own."
+msgstr "Nessuna faccia trovata. Definiscine una personale."
+
+#: xppublish.php:184
+msgid "No file specified"
+msgstr "Nessun file specificato"
+
+#: lib/Views/Abstract.php:44 faces/gallery.php:24
+#, fuzzy
+msgid "No gallery specified"
+msgstr "Nessun campo specificato."
+
+#: templates/view/gallery.inc:106 templates/view/gallerylightbox.inc:125
+#: templates/view/results.inc:94 config/prefs.php.dist:71
+#: config/prefs.php.dist:241 config/prefs.php.dist:262
+msgid "None"
+msgstr "Nessuno"
+
+#: config/prefs.php.dist:252
+msgid "None (Owner only)"
+msgstr ""
+
+#: lib/Exif.php:153
+#, php-format
+msgid "Normal (O deg)"
+msgstr "Normale (0 gradi)"
+
+#: lib/Exif.php:295
+msgid "Not defined"
+msgstr "Non definito"
+
+#: config/prefs.php.dist:97
+msgid "Number of faces per page"
+msgstr "Numero di facce per pagina"
+
+#: config/prefs.php.dist:105
+msgid "Number of groups per page"
+msgstr "Numero di gruppi per pagina"
+
+#: lib/Block/cloud.php:25
+#, fuzzy
+msgid "Number of tags to display"
+msgstr "Nome della pagina wiki da visualizzare"
+
+#: config/prefs.php.dist:89
+msgid "Number of tiles per page"
+msgstr "Numero di mattonelle per pagina"
+
+#: config/prefs.php.dist:81
+msgid "Number of tiles per row"
+msgstr "Numero di mattonelle per riga"
+
+#: report.php:51
+msgid "Offensive content"
+msgstr "Contenuto offensivo"
+
+#: lib/Exif.php:296
+msgid "One Chip Color Area Sensor"
+msgstr "Un Chip Color Area Sensor"
+
+#: perms.php:65
+msgid ""
+"Only the owner or system administrator may change ownership or owner "
+"permissions for a share"
+msgstr ""
+"Solo il proprietario o l'amministratore di sistema può cambiare la proprietà "
+"o i permessi per una condivisione"
+
+#: templates/image/crop_image.inc:157
+#, fuzzy
+msgid "Orientation:"
+msgstr "Amministrazione Utenti"
+
+#: lib/Exif.php:240
+msgid "Other"
+msgstr "Altro"
+
+#: templates/tile/face.inc:24
+msgid "Others"
+msgstr "Altri"
+
+#: templates/group/owner.inc:51
+#, fuzzy, php-format
+msgid "Owned by: %s"
+msgstr "Cambiato: %s"
+
+#: templates/view/list.inc:18 templates/tile/gallery.inc:11
+#: config/prefs.php.dist:69
+msgid "Owner"
+msgstr "Proprietario"
+
+#: lib/Exif.php:239
+msgid "Partial"
+msgstr "Parziale"
+
+#: templates/xppublish/login.inc:14
+msgid "Password"
+msgstr "Password"
+
+#: templates/view/slideshow.inc:39
+msgid "Pause Slideshow"
+msgstr "Pausa presentazione"
+
+#: lib/Widget/OwnerFaces.php:34
+#, fuzzy, php-format
+msgid "People in galleries of %s (%d of %d)"
+msgstr "[linea %s di %s]"
+
+#: lib/Widget/ImageFaces.php:33
+msgid "People in this photo"
+msgstr "Persone in questa foto"
+
+#: config/prefs.php.dist:30
+#, fuzzy
+msgid "Permission Options"
+msgstr "Permessi"
+
+#: faces/claim.php:37 faces/claim.php:57
+msgid "Person"
+msgstr "Persona"
+
+#: templates/faces/custom.inc:51
+#, fuzzy
+msgid "Person in photo"
+msgstr "Permessi"
+
+#: templates/faces/face.inc:30 templates/faces/gallery.inc:6
+#: lib/Views/GalleryRenderers/GalleryLightbox.php:177
+#: lib/Block/recently_added.php:99
+#, fuzzy
+msgid "Photo"
+msgstr "Modifica remota"
+
+#: lib/Block/my_galleries.php:53
+msgid "Photo Count"
+msgstr "Conteggio foto"
+
+#: lib/Exif.php:336
+msgid "Photo Description"
+msgstr "Descrizione Foto"
+
+#: browse.php:22
+msgid "Photo Galleries"
+msgstr "Galleria Foto"
+
+#: lib/Exif.php:335
+msgid "Photo Type"
+msgstr "Tipo foto"
+
+#: templates/xppublish/javascript.inc:63
+msgid "Photo Upload"
+msgstr "carica foto"
+
+#: disclamer.php:30
+#, php-format
+msgid "Photo content may be offensive. You must be over %d to continue."
+msgstr ""
+
+#: faces/search/image.php:42 faces/search/image_save.php:32
+msgid "Photo is too small. Search photo must be at least 50x50 pixels."
+msgstr ""
+
+#: lib/Ansel.php:3119
+msgid "Photo not found"
+msgstr "Foto non trovata"
+
+#: faces/search/image_search.php:43
+#, fuzzy
+msgid "Photo search"
+msgstr "Upload"
+
+#: rss.php:148
+#, php-format
+msgid "Photos by %s"
+msgstr "Foto di %s"
+
+#: lib/Ansel.php:3156
+msgid "Photos not found"
+msgstr "Foto non trovate"
+
+#: rss.php:178 rss.php:184
+#, php-format
+msgid "Photos tagged with %s on %s"
+msgstr "Foto inserite con %s su %s"
+
+#: templates/view/image.inc:80 templates/view/image.inc:105
+#: templates/view/slideshow.inc:38
+msgid "Play"
+msgstr "Play"
+
+#: faces/report.php:36
+msgid ""
+"Please describe the reasons. For example, you don't want to be mentioned "
+"etc..."
+msgstr ""
+
+#: xppublish.php:54
+#, fuzzy
+msgid "Please enter your username and password."
+msgstr "Devi inserire il tuo nome utente e la password"
+
+#: faces/search/image.php:20
+msgid ""
+"Please upload photo with the face to search for. You can search only one "
+"face per time."
+msgstr ""
+
+#: config/prefs.php.dist:141
+msgid "Polaroid"
+msgstr "Polaroid"
+
+#: config/styles.php.dist:94
+msgid "Polaroid Style Thumbnails (White Background)"
+msgstr "Anteprima immagini stile Polaroid (sfondo bianco)"
+
+#: templates/image/crop_image.inc:162
+#, fuzzy
+msgid "Portrait"
+msgstr "Stampa"
+
+#: xppublish.php:99 xppublish.php:149
+msgid "Press the \"Back\" button and try again."
+msgstr "Premi il bottone \"Indietro\" e prova ancora."
+
+#: config/prefs.php.dist:138
+msgid "Pretty Thumbnails"
+msgstr "Buona anteprima"
+
+#: config/styles.php.dist:64
+msgid "Pretty Thumbnails (No Background)"
+msgstr "Buona anteprima (senza sfondo)"
+
+#: config/styles.php.dist:75
+msgid "Pretty Thumbnails (White Background)"
+msgstr "Buona anteprima (sfondo bianco)"
+
+#: config/prefs.php.dist:139
+msgid "Pretty Thumbnails on White Background"
+msgstr "Buona anteprima su sfondo bianco"
+
+#: templates/image/crop_image.inc:177
+msgid "Preview"
+msgstr "Anteprima"
+
+#: image.php:470 image.php:669
+#, php-format
+msgid "Preview changes for %s :: %s"
+msgstr "Anteprima cambiamenti per %s :: %s"
+
+#: templates/view/image.inc:82 templates/view/image.inc:83
+#: templates/view/image.inc:107 templates/view/image.inc:108
+#: templates/view/slideshow.inc:40 templates/view/slideshow.inc:41
+msgid "Previous"
+msgstr "Precedente"
+
+#: lib/Exif.php:126
+msgid "Program"
+msgstr "Programma"
+
+#: templates/view/image.inc:47 templates/view/slideshow.inc:17
+#: templates/tile/image.inc:17
+msgid "Properties"
+msgstr "Proprietà"
+
+#: templates/xppublish/list.inc:7
+msgid "Publish Photos to:"
+msgstr "Pubblica foto da:"
+
+#: xppublish.php:35
+#, php-format
+msgid "Publish to %s"
+msgstr "Pubblica da %s"
+
+#: xppublish.php:24
+#, php-format
+msgid "Publish your photos to %s on %s."
+msgstr "Pubblica le tue foto da %s su %s."
+
+#: lib/Block/gallery.php:56
+msgid "Random Gallery"
+msgstr "Galleria casuale"
+
+#: lib/Block/gallery.php:33
+#, fuzzy
+msgid "Random gallery"
+msgstr "Appunto sconosciuto"
+
+#: lib/Block/random_photo.php:3 lib/Block/random_photo.php:26
+msgid "Random photo"
+msgstr "Foto casuale"
+
+#: config/prefs.php.dist:243 config/prefs.php.dist:264
+msgid "Read and write"
+msgstr ""
+
+#: config/prefs.php.dist:265
+msgid "Read, write, and delete"
+msgstr ""
+
+#: config/prefs.php.dist:242 config/prefs.php.dist:253
+#: config/prefs.php.dist:263
+#, fuzzy
+msgid "Read-only"
+msgstr "Revisione"
+
+#: templates/gallery/delete_confirmation.inc:10
+msgid "Really Delete"
+msgstr "Elimina veramente"
+
+#: templates/gallery/delete_confirmation.inc:10
+msgid "Really Empty"
+msgstr "Vuota veramente"
+
+#: templates/gallery/delete_confirmation.inc:2
+#, php-format
+msgid "Really delete %s?"
+msgstr "Vuoi veramente eliminare %s?"
+
+#: templates/gallery/delete_confirmation.inc:5
+msgid ""
+"Really delete this gallery and all of the photos in it? This action cannot "
+"be undone."
+msgstr ""
+"Sicuro di voler eliminare questa galleria e tutte le sue foto? Questa "
+"operazione non può essere annullata."
+
+#: templates/gallery/delete_confirmation.inc:2
+#, php-format
+msgid "Really empty %s?"
+msgstr "Vuoi veramente svuotare %s?"
+
+#: templates/gallery/delete_confirmation.inc:5
+msgid ""
+"Really empty this gallery and all of the photos in it? This action cannot be "
+"undone."
+msgstr ""
+"Sicuro di voler svuotare questa galleria e tutte le sue foto? Questa "
+"operazione non può essere annullata."
+
+#: faces/report.php:36 faces/report.php:59
+msgid "Reason"
+msgstr "Ragione"
+
+#: lib/Block/recent_comments.php:71
+#, php-format
+msgid "Recent Comments In %s"
+msgstr "Commenti recenti in %s"
+
+#: lib/Block/recent_comments.php:5
+msgid "Recent Photo Comments"
+msgstr "Commenti foto recenti"
+
+#: lib/Block/recent_faces.php:3 lib/Block/recent_faces.php:34
+#, fuzzy
+msgid "Recent faces"
+msgstr "Cambiameti Recenti"
+
+#: lib/Widget/Links.php:23
+#, php-format
+msgid "Recent photos by %s"
+msgstr "Foto recenti da %s"
+
+#: lib/Widget/Links.php:25
+#, php-format
+msgid "Recent photos in %s"
+msgstr "Foto recenti in %s"
+
+#: lib/Block/recently_added.php:3
+msgid "Recently Added Photos"
+msgstr "Foto recenti aggiunte"
+
+#: lib/Block/recently_added.php:75
+#, php-format
+msgid "Recently Added Photos From %s"
+msgstr "Foto recenti aggiunte da %s"
+
+#: rss.php:153
+#, php-format
+msgid "Recently added photos by %s on %s"
+msgstr "Foto recenti aggiunte da %s su %s"
+
+#: rss.php:57 rss.php:62
+#, php-format
+msgid "Recently added photos on %s"
+msgstr "Foto recenti aggiunte su %s"
+
+#: lib/Exif.php:278
+msgid "Red Eye"
+msgstr "Occhi Rossi"
+
+#: lib/Exif.php:284
+msgid "Red Eye, Auto-Mode"
+msgstr "Occhi rossi, modalità automatica"
+
+#: lib/Exif.php:286
+msgid "Red Eye, Auto-Mode, Return light detected"
+msgstr "Occhi rossi, modalità automatica, ritorno di luce rilevato"
+
+#: lib/Exif.php:285
+msgid "Red Eye, Auto-Mode, Return light not detected"
+msgstr "Occhi rossi, modalità automatica, ritorno di luce non rilevato"
+
+#: lib/Exif.php:281
+msgid "Red Eye, Compulsory Flash"
+msgstr "Occhi rossi, Flash forzato"
+
+#: lib/Exif.php:283
+msgid "Red Eye, Compulsory Flash, Return light detected"
+msgstr "Occhi rossi, Flash forzato, ritorno di luce rilevato"
+
+#: lib/Exif.php:282
+msgid "Red Eye, Compulsory Flash, Return light not detected"
+msgstr "Occhi rossi, Flash forzato, ritorno di luce non rilevato"
+
+#: lib/Exif.php:280
+msgid "Red Eye, Return light detected"
+msgstr "Occhi rossi, ritorno di luce rilevato"
+
+#: lib/Exif.php:279
+msgid "Red Eye, Return light not detected"
+msgstr "Occhi rossi, ritorno di luce non rilevato"
+
+#: templates/view/list.inc:5 templates/list/header.inc:5
+#: templates/group/header.inc:8
+msgid "Refresh List"
+msgstr "Aggiorna Lista"
+
+#: templates/faces/gallery.inc:19
+msgid "Reget faces for this image"
+msgstr "Rifiuta facce per questa immagine"
+
+#: templates/view/results.inc:144
+msgid "Related Tags"
+msgstr "Etichette associate"
+
+#: lib/Tags.php:599
+#, php-format
+msgid "Remove %s from search"
+msgstr "Rimuovi %s dalla ricerca"
+
+#: lib/XRequest/TagActions.php:115 lib/Widget/Tags.php:93
+msgid "Remove Tag"
+msgstr "Rimuovi tag"
+
+#: lib/Forms/Image.php:45
+msgid "Replace photo with this file"
+msgstr ""
+
+#: report.php:47 report.php:66 templates/view/image.inc:70
+#: templates/tile/face.inc:30 templates/faces/face.inc:9
+#: templates/faces/search.inc:10 lib/Widget/Actions.php:110
+msgid "Report"
+msgstr "Rapporti"
+
+#: templates/tile/face.inc:30 templates/faces/face.inc:9
+#: templates/faces/search.inc:10
+msgid "Report abuse or submit a removal request"
+msgstr "Riporta abusi o sottoscrivi una richiesta di rimozione"
+
+#: lib/Report.php:109
+msgid "Report by user"
+msgstr "Rapporto dall'utente"
+
+#: lib/Report.php:40
+msgid "Report driver does not exist."
+msgstr "I driver del rapporto non esistono."
+
+#: faces/report.php:31
+msgid "Report face"
+msgstr "Rapporto faccia"
+
+#: report.php:61 report.php:74
+msgid "Report reason"
+msgstr "Rapporto ragione"
+
+#: report.php:60 report.php:73
+msgid "Report type"
+msgstr "Rapporto tipo"
+
+#: templates/image/crop_image.inc:178 templates/image/resize_image.inc:87
+#: templates/faces/custom.inc:54 templates/faces/define.inc:39
+msgid "Reset"
+msgstr "Reset"
+
+#: lib/Widget/Actions.php:92
+msgid "Reset all thumbnails"
+msgstr "Resetta tutte le anteprime"
+
+#: lib/Widget/Actions.php:98
+msgid "Reset default photo"
+msgstr "Resetta foto di default"
+
+#: templates/image/crop_image.inc:127 templates/image/edit_image.inc:44
+#: templates/image/resize_image.inc:74
+#, fuzzy
+msgid "Resize"
+msgstr "Azzera"
+
+#: lib/Forms/Watermark.php:47 config/prefs.php.dist:187
+msgid "Right"
+msgstr "destra"
+
+#: templates/image/crop_image.inc:109 templates/image/edit_image.inc:25
+#: templates/image/resize_image.inc:54
+msgid "Rotate 180"
+msgstr "Rotazione 180"
+
+#: templates/image/crop_image.inc:108 templates/image/edit_image.inc:24
+#: templates/image/resize_image.inc:53
+msgid "Rotate Left"
+msgstr "Rotazione a Sinistra"
+
+#: templates/image/crop_image.inc:110 templates/image/edit_image.inc:26
+#: templates/image/resize_image.inc:55
+msgid "Rotate Right"
+msgstr "Rotazione a Destra"
+
+#: templates/image/resize_image.inc:86 templates/faces/custom.inc:52
+#: lib/Forms/Image.php:26 lib/Forms/Watermark.php:26
+msgid "Save"
+msgstr "Salva"
+
+#: templates/captions/captions.inc:47
+msgid "Save Captions"
+msgstr "Salva didascalie"
+
+#: templates/image/preview_cropimage.inc:16
+#: templates/image/preview_cropimage.inc:17
+#: templates/image/preview_image.inc:19 templates/image/preview_image.inc:20
+msgid "Save Change"
+msgstr "Salva modifiche"
+
+#: templates/gallery/gallery.inc:112
+msgid "Save Gallery"
+msgstr "Salva galleria"
+
+#: image.php:162
+msgid "Save Photo"
+msgstr "Salva foto"
+
+#: templates/faces/image.inc:5
+msgid "Save face name"
+msgstr "Salva nome faccia"
+
+#: templates/faces/define.inc:37 faces/search/name.php:21
+msgid "Search"
+msgstr "Cerca"
+
+#: faces/search/name.php:47 faces/search/tabs.php:35
+msgid "Search by name"
+msgstr "Ricerca per nome"
+
+#: faces/search/tabs.php:37
+#, fuzzy
+msgid "Search by photo"
+msgstr ""
+"#-#-#-#-#  it_IT.po (Mnemo 2.0-cvs)  #-#-#-#-#\n"
+"Ricerca Testo\n"
+"#-#-#-#-#  it_IT.po (Nag 0.0.2)  #-#-#-#-#\n"
+"Cerca Testo"
+
+#: lib/Views/Results.php:86
+#, php-format
+msgid "Searching %s's photos tagged: "
+msgstr "Ricerca %s delle foto contrassegnate: "
+
+#: lib/Views/Results.php:87
+msgid "Searching all photos tagged: "
+msgstr "Ricerca tutte le foto contrassegnate: "
+
+#: faces/gallery.php:55
+#, php-format
+msgid "Searching for faces in %s"
+msgstr "Cerca facce in %s"
+
+#: templates/view/gallery.inc:105 templates/view/gallerylightbox.inc:124
+#: templates/view/results.inc:93
+msgid "Select All"
+msgstr "Seleziona tutto"
+
+#: templates/view/gallery.inc:106 templates/view/gallerylightbox.inc:125
+#: templates/view/results.inc:94
+msgid "Select None"
+msgstr "Deseleziona tutto"
+
+#: templates/tile/image.inc:5
+msgid "Select for Action"
+msgstr "Seleziona per azione"
+
+#: templates/view/gallery.inc:104 templates/view/gallerylightbox.inc:123
+#: templates/view/results.inc:92
+#, php-format
+msgid "Select: %s, %s"
+msgstr "Seleziona: %s, %s"
+
+#: templates/view/gallery.inc:118 templates/view/gallerylightbox.inc:137
+#: templates/view/results.inc:105
+msgid "Selected photos to"
+msgstr "Foto selezionate da"
+
+#: lib/Forms/Ecard.php:25
+msgid "Send"
+msgstr "Invia"
+
+#: img/ecard.php:102
+#, php-format
+msgid "Send Ecard :: %s"
+msgstr "Invia Ecard :: %s"
+
+#: templates/view/image.inc:54 templates/view/slideshow.inc:27
+msgid "Send an Ecard"
+msgstr "Invia una Ecard"
+
+#: lib/Forms/Ecard.php:41
+msgid "Send ecard to the following address:"
+msgstr "Invia Ecard al seguente indirizzo:"
+
+#: lib/Exif.php:358
+msgid "Sensing Method"
+msgstr "Metodo sensibile"
+
+#: templates/gallery/gallery.inc:71 lib/Forms/Upload.php:53
+msgid "Separate tags with commas."
+msgstr "Separa tag con virgolette."
+
+#: lib/Widget/Actions.php:89
+msgid "Set captions"
+msgstr "Imposta didascalie"
+
+#: lib/Widget/Actions.php:103
+msgid "Set permissions"
+msgstr "Imposta permessi"
+
+#: config/styles.php.dist:84
+msgid "Shadowed Thumbnails (White Background)"
+msgstr "Anteprime ombreggiate (sfondo bianco)"
+
+#: config/prefs.php.dist:140
+msgid "Shadowed Thumbnails on White Background"
+msgstr "Anteprime ombreggiate su sfondo bianco"
+
+#: config/prefs.php.dist:113
+msgid "Show EXIF data"
+msgstr "Mostra dati EXIF"
+
+#: lib/Exif.php:345
+msgid "Shutter Speed"
+msgstr "Velocità otturatore"
+
+#: lib/Widget/SimilarPhotos.php:27
+msgid "Similar Photos"
+msgstr "Foto simili"
+
+#: templates/tile/face.inc:34 templates/faces/face.inc:14
+#, fuzzy
+msgid "Similar faces"
+msgstr "Tutti i rami"
+
+#: config/styles.php.dist:145
+msgid "Simple"
+msgstr "Semplice"
+
+#: config/styles.php.dist:154
+msgid "Simple Lightbox"
+msgstr "Scatola di luce semplice"
+
+#: lib/Forms/Upload.php:43
+msgid "Single Photo"
+msgstr "Foto Singola"
+
+#: templates/gallery/gallery.inc:64
+#, php-format
+msgid ""
+"Slugs allows direct access to this gallery by visiting: %s. <br /> Slug "
+"names may contain only letters, numbers or the _ (underscore) character."
+msgstr ""
+"permetti accesso diretto a questa galleria dai visitatori: %s. <br /> I nomi "
+"possono contenere solo lettere, numeri o il carattere _ (underscore)."
+
+#: lib/Forms/Watermark.php:37 config/prefs.php.dist:208
+msgid "Small"
+msgstr "Piccola"
+
+#: templates/view/list.inc:16
+#, fuzzy
+msgid "Sort by: "
+msgstr "Ordina per nome"
+
+#: lib/Widget/Actions.php:91
+msgid "Sort images"
+msgstr "Ordina immagini"
+
+#: lib/Exif.php:236
+msgid "Spot"
+msgstr "Spot"
+
+#: templates/view/image.inc:79 templates/view/image.inc:105
+#: templates/view/slideshow.inc:38
+msgid "Start Slideshow"
+msgstr "Comincia presentazione"
+
+#: scripts/ansel.php:212 scripts/ansel.php:295
+#: scripts/recursive_import.php:190 scripts/remote_import.php:231
+#, php-format
+msgid "Storing photo \"%s\"..."
+msgstr "Salva foto \"%s\"..."
+
+#: templates/gallery/gallery.inc:40
+msgid "Style for this gallery"
+msgstr "Stile per questa galleria"
+
+#: scripts/ansel.php:235 scripts/recursive_import.php:200
+#: scripts/remote_import.php:247
+#, php-format
+msgid "Successfully added %d photo (%s) to gallery \"%s\" from \"%s\"."
+msgid_plural "Successfully added %d photos (%s) to gallery \"%s\" from \"%s\"."
+msgstr[0] "Agiunta con successo %d foto (%s) alla galleria \"%s\" da \"%s\"."
+msgstr[1] "Agiunte con successo %d foto (%s) alla galleria \"%s\" da \"%s\"."
+
+#: scripts/ansel.php:221
+#, php-format
+msgid "Successfully added photo \"%s\" to gallery \"%s\"."
+msgstr "Agiunta con successo foto \"%s\" alla galleria \"%s\"."
+
+#: gallery/delete.php:43
+#, php-format
+msgid "Successfully deleted %s."
+msgstr "%s eliminato con successo."
+
+#: gallery/delete.php:66
+#, fuzzy, php-format
+msgid "Successfully emptied \"%s\""
+msgstr "\"%s\" eliminato con successo."
+
+#: lib/Exif.php:247
+msgid "Sunny"
+msgstr "Soleggiato"
+
+#: lib/Block/cloud.php:3 lib/Block/cloud.php:33
+msgid "Tag Cloud"
+msgstr "Tag nuvoloso"
+
+#: lib/Widget/Tags.php:21 lib/Forms/Image.php:41 lib/Forms/Upload.php:53
+msgid "Tags"
+msgstr "Tag"
+
+#: config/prefs.php.dist:23
+msgid "Tags and EXIF Options"
+msgstr ""
+
+#: templates/tile/face.inc:27 faces/claim.php:32
+#, fuzzy
+msgid "Tell us who is in this photo"
+msgstr "Elimina una Pagina"
+
+#: report.php:50
+msgid "Terms and conditions infringement"
+msgstr "Termini e condizioni infrante"
+
+#: scripts/ansel.php:262
+#, php-format
+msgid "The directory \"%s\" doesn't exist."
+msgstr "La directory \"%s\" non esiste."
+
+#: scripts/ansel.php:240
+#, php-format
+msgid "The directory \"%s\" had no valid photos."
+msgstr "La directory \"%s\" non ha foto valide."
+
+#: scripts/ansel.php:279
+#, php-format
+msgid "The directory \"%s\" is empty."
+msgstr "La directory \"%s\" è vuota."
+
+#: scripts/remote_import.php:273 lib/Ansel.php:470
+#, php-format
+msgid "The file \"%s\" doesn't exist."
+msgstr "Il file \"%s\" non esiste."
+
+#: image.php:196 lib/api.php:480 img/upload.php:170
+msgid "The file you uploaded does not appear to be a valid photo."
+msgstr "Il file che hai caricato non sembra essere una foto valida."
+
+#: lib/Ansel.php:2823
+#, php-format
+msgid "The gallery \"%s\" could not be created: %s"
+msgstr "La galleria \"%s\" non può essere creata: %s"
+
+#: gallery.php:293 xppublish.php:123 scripts/ansel.php:156
+#: scripts/recursive_import.php:157 scripts/remote_import.php:197
+#, php-format
+msgid "The gallery \"%s\" couldn't be created: %s"
+msgstr "La galleria \"%s\" non può essere creata: %s"
+
+#: gallery.php:299 xppublish.php:127 scripts/ansel.php:161
+#: scripts/recursive_import.php:159 scripts/remote_import.php:199
+#, php-format
+msgid "The gallery \"%s\" was created successfully."
+msgstr "La galleria \"%s\" è stata creata con successo."
+
+#: gallery.php:243
+msgid "The gallery was saved."
+msgstr "La galleria è stata salvata."
+
+#: gallery.php:350
+msgid "The gallery's default photo has successfully been reset."
+msgstr "Le foto di default della galleria sono state resettate con successo."
+
+#: gallery.php:367
+msgid "The gallery's thumbnails have successfully been reset."
+msgstr "Le anteprime della galleria sono state resettate con successo."
+
+#: faces/report.php:68
+msgid "The owner of the photo was notified."
+msgstr "Il proprietario della foto è stato notificato."
+
+#: faces/claim.php:66
+msgid "The owner of the photo, who will delegate the face name, was notified."
+msgstr ""
+"Il proprietario della foto, che sarà delegato al nome della faccia, è stato "
+"notificato."
+
+#: lib/Ansel.php:2786
+#, php-format
+msgid "The slug \"%s\" already exists."
+msgstr "Lo slug \"%s\" esiste già."
+
+#: img/upload.php:198
+msgid ""
+"The uploaded file appears to be empty. It may not exist on your computer."
+msgstr ""
+"Il file caricato sembra essere vuoto. Potrebbe non esistere sul tuo computer."
+
+#: lib/Views/List.php:194 lib/Block/random_photo.php:35
+msgid "There are no photo galleries available."
+msgstr "Non ci sono gallerie di foto disponibili."
+
+#: gallery.php:94
+#, php-format
+msgid "There are no photos in %s to download."
+msgstr "Non ci sono foto in %s da scaricare."
+
+#: templates/view/gallery.inc:122 templates/view/gallerylightbox.inc:141
+#: templates/view/results.inc:115 templates/captions/captions.inc:49
+msgid "There are no photos in this gallery."
+msgstr "Non ci sono foto in questa galleria."
+
+#: scripts/ansel.php:298
+#, php-format
+msgid "There was a problem adding the photo \"%s\" to gallery \"%s\"."
+msgstr ""
+"Si è verificato un problema aggiungendo la foto \"%s\" alla galleria \"%s\"."
+
+#: scripts/ansel.php:215
+#, php-format
+msgid "There was a problem adding the photo \"%s\" to gallery \"%s\": %s"
+msgstr ""
+"Si è verificato un problema aggiungendo la foto \"%s\" alla galleria \"%s\": "
+"%s"
+
+#: lib/api.php:658 gallery/delete.php:38
+#, php-format
+msgid "There was a problem deleting %s: %s"
+msgstr "Si è verificato un problema eliminando %s: %s"
+
+#: image.php:537 lib/Views/Results.php:131
+#, php-format
+msgid "There was a problem deleting photos: %s"
+msgstr "Si è verificato un problema eliminando le foto: %s"
+
+#: img/upload.php:101 img/upload.php:156 img/upload.php:180
+#, php-format
+msgid "There was a problem saving the photo: %s"
+msgstr "Si è verificato un problema salvando la foto: %s."
+
+#: xppublish.php:195
+msgid "There was a problem uploading the photo."
+msgstr "Si è verificato un problema caricando la foto."
+
+#: img/upload.php:196
+#, php-format
+msgid "There was a problem uploading the photo: %s"
+msgstr "Si è verificato un problema caricando la foto: %s"
+
+#: xppublish.php:91
+#, fuzzy
+msgid "There was an error accessing the gallery"
+msgstr "Errore nell'importazione dell'evento: %s."
+
+#: gallery/sort.php:27
+#, fuzzy
+msgid "There was an error accessing the gallery."
+msgstr "Non ci sono messaggi in questa casella di posta."
+
+#: img/upload.php:61 img/upload.php:83 img/upload.php:117 img/upload.php:138
+#, php-format
+msgid "There was an error processing the uploaded archive: %s"
+msgstr "C'è stato un errere durante l'elaborazione dell'archivio caricato: %s"
+
+#: image.php:207
+#, fuzzy
+msgid "There was an error replacing the photo."
+msgstr "Si è verificato un problema salvando l'attivit&agrave;."
+
+#: img/ecard.php:95
+#, php-format
+msgid "There was an error sending your message: %s"
+msgstr "Si è verificato un errore inviando il messaggio: %s"
+
+#: lib/ImageView.php:89
+#, php-format
+msgid ""
+"This install does not support the %s feature. Please contact your "
+"administrator."
+msgstr ""
+"Questa installazione non supporta le caratteristiche di %s. Contatta il tuo "
+"amministratore."
+
+#: lib/Exif.php:298
+msgid "Three Chip Color Area Sensor"
+msgstr "Tre Chip Color Area Sensor"
+
+#: lib/Forms/Watermark.php:36 config/prefs.php.dist:207
+msgid "Tiny"
+msgstr "Minuscolo"
+
+#: lib/Forms/Watermark.php:52 config/prefs.php.dist:196
+msgid "Top"
+msgstr "Alto"
+
+#: templates/gallery/gallery.inc:20
+msgid "Top Level Gallery"
+msgstr "Galleria di alto livello"
+
+#: lib/Exif.php:300
+msgid "Trilinear Sensor"
+msgstr "Sensore trilineare"
+
+#: lib/Exif.php:249
+msgid "Tungsten"
+msgstr "Tungsteno"
+
+#: lib/Exif.php:128
+msgid "Tv-priority"
+msgstr "Priorità-Tv"
+
+#: lib/Exif.php:297
+msgid "Two Chip Color Area Sensor"
+msgstr "Due Chip Color Area Sensor"
+
+#: lib/ImageView.php:96 lib/Search.php:57 lib/Widget.php:75 lib/api.php:1171
+#, php-format
+msgid "Unable to load the definition of %s."
+msgstr "Impossibile caricare la definizione di %s."
+
+#: rss.php:37
+msgid "Unable to retrieve requested feed"
+msgstr "Impossibile recuperare la richiesta feed"
+
+#: lib/Exif.php:311
+msgid "Uncalibrated"
+msgstr "Non calibrato"
+
+#: templates/gallery/gallery.inc:113
+msgid "Undo Changes"
+msgstr "Annulla ultima modifica"
+
+#: templates/group/category.inc:7 lib/Views/List.php:175
+msgid "Unfiled"
+msgstr "Non classificato"
+
+#: lib/Exif.php:233
+msgid "Unknown"
+msgstr "Sconosciuto"
+
+#: gallery.php:147
+msgid "Unknown gallery"
+msgstr "Galleria sconosciuta"
+
+#: lib/Ansel.php:2756 lib/tests/GalleryTest.php:42
+msgid "Unnamed"
+msgstr "Senza nome"
+
+#: lib/Report.php:122
+msgid "Unsupported"
+msgstr "Non supportato."
+
+#: xppublish.php:135
+msgid "Untitled"
+msgstr "Senza titolo"
+
+#: perms.php:237
+#, php-format
+msgid "Updated %s."
+msgstr "%s Aggiornato."
+
+#: lib/Forms/Upload.php:39 faces/search/image.php:22
+msgid "Upload"
+msgstr "Upload"
+
+#: templates/xppublish/javascript.inc:63
+#, php-format
+msgid "Upload Photos to %s"
+msgstr "Carica foto in %s"
+
+#: faces/search/image.php:66
+#, fuzzy
+msgid "Upload face photo"
+msgstr "Impossibile copiare %s in %s"
+
+#: lib/Widget/Actions.php:85 img/upload.php:30
+msgid "Upload photos"
+msgstr "Carica foto"
+
+#: lib/Exif.php:157
+#, php-format
+msgid "Upsidedown"
+msgstr "Capovolto"
+
+#: lib/Exif.php:159
+#, php-format
+msgid "Upsidedown Mirrored"
+msgstr "Capovolto specchiato"
+
+#: scripts/all_images_exif_to_tags.php:116 scripts/ansel.php:318
+#: scripts/recursive_import.php:221 scripts/remote_import.php:304
+#, php-format
+msgid "Usage: %s [OPTIONS]..."
+msgstr "Utilità: %s [OPZIONI]..."
+
+#: lib/Forms/Ecard.php:32
+msgid "Use the following return address:"
+msgstr "Usa il seguente indirizzo di ritorno:"
+
+#: lib/Exif.php:356
+msgid "User Comment"
+msgstr "Commento utente"
+
+#: templates/gallery/gallery.inc:84
+#, php-format
+msgid "User must be over %d"
+msgstr "L'utente deve avere più di %d anni"
+
+#: templates/xppublish/login.inc:11
+msgid "Username"
+msgstr "Nome Utente"
+
+#: xppublish.php:50
+#, fuzzy
+msgid "Username or password are incorrect."
+msgstr "Nome Utente e/o Parola Chiave non corretti."
+
+#: scripts/all_images_exif_to_tags.php:77 scripts/recursive_import.php:86
+msgid "Username or password is incorrect."
+msgstr "Nome Utente o Password non corretti."
+
+#: templates/gallery/gallery.inc:103 config/prefs.php.dist:231
+msgid "Users with edit permissions"
+msgstr "Utente con permessi di modifica"
+
+#: lib/Forms/Watermark.php:55 config/prefs.php.dist:199
+#: config/prefs.php.dist:212
+msgid "Vertical Alignment"
+msgstr "Allineamento verticale"
+
+#: lib/Block/gallery.php:96
+#, php-format
+msgid "View %s"
+msgstr "Mostra %s"
+
+#: templates/view/results.inc:81
+msgid "View All Results"
+msgstr "Mostra tutti i risultati"
+
+#: lib/Block/random_photo.php:55
+msgid "View Photo"
+msgstr "Mostra foto"
+
+#: templates/view/results.inc:81
+msgid "View Results from All Users"
+msgstr "Mostra risultati di tutti gli utenti"
+
+#: templates/tile/image.inc:11
+msgid "View or leave comments"
+msgstr "Vedi o abbandona commenti"
+
+#: config/prefs.php.dist:61
+msgid "View to display by default"
+msgstr "Mostra la visualizzazione di default"
+
+#: image.php:423 image.php:434 templates/image/crop_image.inc:123
+#: templates/image/edit_image.inc:39 templates/image/resize_image.inc:68
+msgid "Watermark"
+msgstr "Filigrana"
+
+#: lib/Forms/Watermark.php:41
+msgid "Watermark Font"
+msgstr "Carattere filigrana"
+
+#: config/prefs.php.dist:37
+msgid "Watermark Options"
+msgstr "Opzioni filigrana"
+
+#: config/prefs.php.dist:261
+msgid ""
+"When a new gallery is created, what default permissions should be given to "
+"groups that the user is a member of?"
+msgstr ""
+
+#: config/prefs.php.dist:240
+msgid ""
+"When a new gallery is created, what permissions should be given to "
+"authenticated users by default?"
+msgstr ""
+
+#: config/prefs.php.dist:251
+msgid ""
+"When a new gallery is created, what permissions should be given to guests by "
+"default?"
+msgstr ""
+
+#: config/prefs.php.dist:273
+msgid ""
+"Which EXIF fields should we automatically add as image tags during upload?"
+msgstr ""
+
+#: lib/Exif.php:359
+msgid "White Balance"
+msgstr "Bilanciamento bianco"
+
+#: templates/gallery/gallery.inc:98 config/prefs.php.dist:232
+msgid "Who should be allowed to download original photos"
+msgstr ""
+
+#: lib/Exif.php:341
+msgid "Width"
+msgstr "Larghezza"
+
+#: lib/Exif.php:343
+msgid "X Resolution"
+msgstr "Risoluzione x"
+
+#: lib/Exif.php:344
+msgid "Y Resolution"
+msgstr "Risoluzione y"
+
+#: config/prefs.php.dist:220
+msgid "Yes"
+msgstr "Si"
+
+#: lib/XRequest/EditFaces.php:159
+msgid "You are not allowed to edit this photo."
+msgstr "Non sei autorizzato a modificare questa foto."
+
+#: gallery/delete.php:32
+#, php-format
+msgid "You cannot delete %s, it contains nested galleries."
+msgstr "Non puoi eliminare %s, contiene gallerie nidificate."
+
+#: gallery.php:259
+#, php-format
+msgid "You do not have permission to add children to %s."
+msgstr "Non hai il permesso di aggiungere figli a %s."
+
+#: lib/Faces/facedetect.php:34
+msgid "You do not have the facedetect extension enabled in PHP"
+msgstr "Non hai l'estensione facedetect abilitata in PHP"
+
+#: lib/Faces/opencv.php:34
+msgid "You do not have the opencv extension enabled in PHP"
+msgstr "Non hai l'estensione opencv abilitata in PHP"
+
+#: img/ecard.php:68
+msgid ""
+"You have been sent an Ecard. To view the Ecard, you must be able to view "
+"text/html messages in your mail reader. If you are viewing this message, "
+"then most likely your mail reader does not support viewing text/html "
+"messages."
+msgstr ""
+"Hai mandato un ecard. Per visualizzare l'ecard, devi essere in grado di "
+"vedere messaggi text/html nel tuo lettore mail. Se stai vedendo questo "
+"messaggio, molto facilmente il tuo lettore mail non support la "
+"visualizzazione di messaggi text/html."
+
+#: lib/Views/List.php:189
+msgid "You have no photo galleries, add one!"
+msgstr "Non hai gallerie di foto, aggiungine una!"
+
+#: templates/view/gallery.inc:58 templates/view/gallery.inc:72
+#: templates/view/gallerylightbox.inc:59 templates/view/gallerylightbox.inc:73
+#: templates/view/results.inc:46 templates/view/results.inc:60
+msgid "You must choose a gallery to move photos to."
+msgstr "Devi scegliere una galleria per spostare photo al suo interno."
+
+#: templates/view/gallery.inc:29 templates/view/gallerylightbox.inc:29
+msgid "You must choose photos to delete."
+msgstr ""
+
+#: lib/Ansel.php:66 lib/Ansel.php:74
+msgid "You must configure a VFS backend to use Ansel."
+msgstr "E' necessario configurare un backend VFS per usare Ansel."
+
+#: img/ecard.php:44
+msgid "You must enter an e-mail address to send the message to."
+msgstr "Devi inserire un indirizzo e-mail per mandargli un messaggio."
+
+#: img/ecard.php:39
+msgid "You must enter your e-mail address."
+msgstr "Devi specificare il tuo indirizzo e-mail."
+
+#: scripts/all_images_exif_to_tags.php:86
+msgid "You must login with an administrative account."
+msgstr ""
+
+#: gallery.php:271
+msgid "You must provide a display name for your new gallery."
+msgstr "Devi fornire un nome da visualizzare per le tue nuove gallerie."
+
+#: scripts/recursive_import.php:95 scripts/remote_import.php:110
+msgid "You must specify a valid directory."
+msgstr "Devi specificare una directory valida."
+
+#: scripts/all_images_exif_to_tags.php:82 scripts/recursive_import.php:91
+#: scripts/remote_import.php:102
+msgid "You must specify a valid username and password."
+msgstr "Devi specificare nome utente e password validi."
+
+#: scripts/remote_import.php:106
+msgid "You must specify the url for the remote Horde RPC server."
+msgstr ""
+
+#: faces/search/image_define.php:21 faces/search/image_save.php:22
+#: faces/search/image_search.php:32
+#, fuzzy
+msgid "You must upload the search photo first"
+msgstr "Hai gia' votato in questo sondaggio."
+
+#: lib/Forms/Upload.php:72
+msgid "Zip File Upload"
+msgstr "File zip caricato"
+
+#: lib/Ansel.php:548
+msgid "_Galleries"
+msgstr "_Gallerie"
+
+#: lib/Ansel.php:560
+msgid "_My Galleries"
+msgstr "Galleria _Personali"
+
+#: lib/Ansel.php:572
+msgid "_New Gallery"
+msgstr "_Nuova Galleria"
+
+#: lib/Ansel.php:585
+msgid "_Print"
+msgstr "_Stampa"
+
+#: templates/view/list.inc:12 templates/group/header.inc:15
+msgid "category"
+msgstr "categoria"
+
+#: templates/image/crop_image.inc:172 templates/image/resize_image.inc:81
+#: templates/faces/define.inc:61
+msgid "height"
+msgstr "altezza"
+
+#: xppublish.php:185
+msgid "image"
+msgstr "immagine"
+
+#: templates/group/header.inc:18
+msgid "none"
+msgstr "nessuno"
+
+#: lib/Views/GalleryRenderers/GalleryLightbox.php:178
+msgid "of"
+msgstr "di"
+
+#: templates/view/list.inc:11 templates/group/header.inc:13
+msgid "owner"
+msgstr "proprietario"
+
+#: lib/Exif.php:308
+msgid "sRGB"
+msgstr "sRGB"
+
+#: templates/image/crop_image.inc:166 templates/image/resize_image.inc:79
+#: templates/faces/define.inc:59
+msgid "width"
+msgstr "larghezza"
diff --git a/ansel/po/ja_JP.po b/ansel/po/ja_JP.po
new file mode 100644 (file)
index 0000000..a06515a
--- /dev/null
@@ -0,0 +1,1969 @@
+# Japanese translations for Ansel package.
+# Copyright 2008-2009 The Horde Project
+# This file is distributed under the same license as the Ansel package.
+# Automatically generated, 2008.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Ansel 1.0-cvs\n"
+"Report-Msgid-Bugs-To: dev@lists.horde.org\n"
+"POT-Creation-Date: 2008-02-08 10:50+0900\n"
+"PO-Revision-Date: 2008-01-31 22:30+0900\n"
+"Last-Translator: taguchi@tcltk.jp\n"
+"Language-Team: i18n@lists.horde.org\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=SHIFT_JIS\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: scripts/recursive_import.php:111
+#, php-format
+msgid "\"%s\" is not a directory."
+msgstr "\"%s\" \82Í\83f\83B\83\8c\83N\83g\83\8a\82Å\82Í\82 \82è\82Ü\82¹\82ñ\81B"
+
+#: templates/group/category.inc:51 templates/group/owner.inc:52
+#, php-format
+msgid "%d Galleries"
+msgstr "%d \83M\83\83\83\89\83\8a"
+
+#: lib/Exif.php:285
+#, php-format
+msgid "%d bytes"
+msgstr "%d \83o\83C\83g"
+
+#: templates/tile/image.inc:13
+#, php-format
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] "%d \83R\83\81\83\93\83g"
+msgstr[1] "%d \83R\83\81\83\93\83g"
+
+#: templates/list/header.inc:3
+#, php-format
+msgid "%d galleries"
+msgstr "%d \83M\83\83\83\89\83\8a"
+
+#: xrequest.php:24 lib/Views/Gallery.php:113 lib/Views/Image.php:209
+#: lib/Views/Results.php:256
+#, php-format
+msgid "%d photo"
+msgid_plural "%d photos"
+msgstr[0] "%d \8eÊ\90^"
+msgstr[1] "%d \8eÊ\90^"
+
+#: img/upload.php:192
+#, php-format
+msgid "%d photo was uploaded."
+msgid_plural "%d photos were uploaded."
+msgstr[0] "%d \96\87\82Ì\8eÊ\90^\82ª\83A\83b\83v\83\8d\81[\83h\82³\82ê\82Ü\82µ\82½\81B"
+msgstr[1] "%d \96\87\82Ì\8eÊ\90^\82ª\83A\83b\83v\83\8d\81[\83h\82³\82ê\82Ü\82µ\82½\81B"
+
+#: lib/Exif.php:136 lib/Exif.php:142
+#, php-format
+msgid "%d pixels"
+msgstr "%d \83s\83N\83Z\83\8b"
+
+#: lib/Exif.php:166 lib/Exif.php:181
+#, php-format
+msgid "%d sec"
+msgstr "%d \95b"
+
+#: templates/list/header.inc:3
+#, php-format
+msgid "%d to %d of %d galleries"
+msgstr "\83M\83\83\83\89\83\8a %d \81` %d / %d"
+
+#: templates/group/header.inc:3
+#, php-format
+msgid "%d to %d of %d groups"
+msgstr "\83O\83\8b\81[\83v %d \81` %d / %d"
+
+#: templates/view/results.inc:83
+#, php-format
+msgid "%d to %d of %d photos"
+msgstr "\8eÊ\90^ %d \81` %d / %d"
+
+#: gallery/sort.php:48
+#, php-format
+msgid "%s :: Sort"
+msgstr "%s :: \95À\82×\91Ö\82¦"
+
+#: xrequest.php:214 lib/Views/Image.php:275
+#, php-format
+msgid "%s from %s"
+msgstr "%s - %s\82æ\82è"
+
+#: templates/view/gallery.inc:118 templates/view/results.inc:83
+#, php-format
+msgid "%s items"
+msgstr "%s \83A\83C\83e\83\80"
+
+#: rss.php:99
+#, php-format
+msgid "%s on %s"
+msgstr "%s (%s)"
+
+#: list.php:47 lib/Ansel.php:440 templates/group/owner.inc:16
+#, php-format
+msgid "%s's Galleries"
+msgstr "%s \82Ì\83M\83\83\83\89\83\8a"
+
+#: lib/Ansel.php:3034
+#, php-format
+msgid "(c) %s %s"
+msgstr "(c) %s %s"
+
+#: templates/xppublish/new.inc:14
+msgid "(must be unique)"
+msgstr "(\91¼\82Æ\93¯\82\82Å\82Í\82¢\82¯\82Ü\82¹\82ñ)"
+
+#: scripts/ansel.php:323
+msgid "-a, --add[=filename]         Add local file to selected gallery"
+msgstr ""
+"-a, --add[=filename]         \83\8d\81[\83J\83\8b\83t\83@\83C\83\8b\82ð\91I\91ð\82³\82ê\82½\83M\83\83\83\89\83\8a\82É\92Ç\89Á"
+
+#: scripts/ansel.php:321
+msgid ""
+"-c, --create[=shortname/name/description/owner]\n"
+"                             Create gallery (and use it)  Combined with -g "
+"to create a subgallery."
+msgstr ""
+"-c, --create[=shortname/name/description/owner]\n"
+"                             \83M\83\83\83\89\83\8a\82ð\90\90¬(\82µ\81A\8eg\97p\82·\82é) -g \82Æ\91g\82Ý\8d\87\82í\82¹\82½\8fê"
+"\8d\87\81A\83T\83u\83M\83\83\83\89\83\8a\82ð\90\90¬\81B"
+
+#: scripts/ansel.php:324
+msgid ""
+"-d, --dir[=directory]        Add all files from the directory to the "
+"selected\n"
+"                             gallery"
+msgstr ""
+"-d, --dir[=directory]        \8ew\92è\82³\82ê\82½\83f\83B\83\8c\83N\83g\83\8a\93à\82Ì\82·\82×\82Ä\82Ì\83t\83@\83C\83\8b\82ð\81A\n"
+"\91I\91ð\82³\82ê\82Ä\82¢\82é\83M\83\83\83\89\83\8a\82É\92Ç\89Á\82·\82é\81B"
+
+#: scripts/recursive_import.php:190
+msgid ""
+"-d, --dir[=directory]        Recursively add all files from the directory, "
+"creating\n"
+"                             a gallery for each directory"
+msgstr ""
+"-d, --dir[=directory]        \8ew\92è\82³\82ê\82½\83f\83B\83\8c\83N\83g\83\8a\93à\82Ì\83T\83u\83f\83B\83\8c\83N\83g\83\8a\82ð\8dÄ"
+"\8bA\n"
+"\93I\82É\8c\9f\8dõ\82µ\81A\83f\83B\83\8c\83N\83g\83\8a\96\88\82É\83M\83\83\83\89\83\8a\82ð\90\90¬\81A\82·\82×\82Ä\82Ì\83t\83@\83C\83\8b\82ð\92Ç\89Á\82·\82é\81B"
+
+#: scripts/ansel.php:322
+msgid "-g, --gallery[=shortname]    Select gallery to use"
+msgstr "-g, --gallery[=shortname]    \8eg\97p\82·\82é\83M\83\83\83\89\83\8a\82ð\91I\91ð"
+
+#: scripts/ansel.php:319 scripts/recursive_import.php:189
+msgid "-h, --help                   Show this help"
+msgstr "-h, --help                   \82±\82Ì\83w\83\8b\83v\82ð\95\\8e¦"
+
+#: scripts/ansel.php:320
+msgid ""
+"-l, --list                   List galleries or photos (if combined with -g)"
+msgstr ""
+"-l, --list                   \83M\83\83\83\89\83\8a\81A\82Ü\82½\82Í\8eÊ\90^(-g\83I\83v\83V\83\87\83\93\8ew\92è\82Ì\8fê\8d\87)\82ð\83\8a"
+"\83X\83g\95\\8e¦\81B"
+
+#: scripts/ansel.php:326 scripts/recursive_import.php:192
+msgid "-p, --password[=password]    Horde login password"
+msgstr "-p, --password[=password]    Horde \83\8d\83O\83C\83\93\83p\83X\83\8f\81[\83h"
+
+#: scripts/ansel.php:327
+msgid "-t, --caption[=caption]      Caption for photo (if combined with -a)"
+msgstr ""
+"-t, --caption[=caption]      \8eÊ\90^\82Ì\83L\83\83\83v\83V\83\87\83\93(-a\82Æ\8b¤\82É\8ew\92è\82³\82ê\82½\8fê\8d\87)"
+
+#: scripts/ansel.php:325 scripts/recursive_import.php:191
+msgid "-u, --username[=username]    Horde login username"
+msgstr "-u, --username[=username]    Horde \83\8d\83O\83C\83\93\83\86\81[\83U\96¼"
+
+#: templates/group/category.inc:51 templates/group/owner.inc:52
+msgid "1 Gallery"
+msgstr "1 \83M\83\83\83\89\83\8a"
+
+#: lib/Exif.php:157
+#, php-format
+msgid "90 deg CCW"
+msgstr "90\93x CCW"
+
+#: lib/Exif.php:159
+#, php-format
+msgid "90 deg CCW Mirrored"
+msgstr "90\93x CCW \94½\93]"
+
+#: lib/Exif.php:161
+#, php-format
+msgid "90 deg CW"
+msgstr "90\93x CW"
+
+#: lib/Exif.php:155
+#, php-format
+msgid "90 deg CW Mirrored"
+msgstr "90\93x CW \94½\93]"
+
+#: lib/api.php:262
+msgid "A gallery to add this photo to is required."
+msgstr "\82±\82Ì\8eÊ\90^\82ð\92Ç\89Á\82·\82é\83M\83\83\83\89\83\8a\82ª\95K\97v\82Å\82·\81B"
+
+#: lib/Exif.php:128
+msgid "A-DEP"
+msgstr "A-DEP"
+
+#: gallery/delete.php:28 gallery/delete.php:54 lib/api.php:393
+#, php-format
+msgid "Access denied deleting gallery %s."
+msgstr "\83M\83\83\83\89\83\8a %s \82ð\8dí\8f\9c\82·\82é\8c \8cÀ\82ª\82 \82è\82Ü\82¹\82ñ\81B"
+
+#: lib/api.php:332
+msgid "Access denied deleting image."
+msgstr "\8eÊ\90^\82ð\8dí\8f\9c\82·\82é\8c \8cÀ\82ª\82 \82è\82Ü\82¹\82ñ\81B"
+
+#: image.php:441 lib/Views/Results.php:121
+#, php-format
+msgid "Access denied deleting photo %s."
+msgstr "\8eÊ\90^ %s \82ð\8dí\8f\9c\82·\82é\8c \8cÀ\82ª\82 \82è\82Ü\82¹\82ñ\81B"
+
+#: gallery.php:73
+#, php-format
+msgid "Access denied downloading %s."
+msgstr "%s \82ð\83_\83E\83\93\83\8d\81[\83h\82·\82é\8c \8cÀ\82ª\82 \82è\82Ü\82¹\82ñ\81B"
+
+#: gallery.php:131
+msgid "Access denied editing galleries."
+msgstr "\83M\83\83\83\89\83\8a\82ð\95Ò\8fW\82·\82é\8c \8cÀ\82ª\82 \82è\82Ü\82¹\82ñ\81B"
+
+#: gallery.php:161
+#, php-format
+msgid "Access denied saving gallery %s."
+msgstr "\83M\83\83\83\89\83\8a %s \82ð\95Û\91\82·\82é\8c \8cÀ\82ª\82 \82è\82Ü\82¹\82ñ\81B"
+
+#: image.php:198 image.php:287
+#, php-format
+msgid "Access denied saving photo to %s."
+msgstr "\8eÊ\90^ %s \82ð\95Û\91\82·\82é\8c \8cÀ\82ª\82 \82è\82Ü\82¹\82ñ\81B"
+
+#: gallery/captions.php:31
+#, php-format
+msgid "Access denied setting captions for %s."
+msgstr "%s\82Ì\83L\83\83\83v\83V\83\87\83\93\82ð\90Ý\92è\82·\82é\8c \8cÀ\82ª\82 \82è\82Ü\82¹\82ñ\81B"
+
+#: templates/view/gallery.inc:139 templates/view/results.inc:97
+msgid "Actions: "
+msgstr "\83A\83N\83V\83\87\83\93:"
+
+#: templates/view/image.inc:115
+msgid "Add"
+msgstr "\92Ç\89Á"
+
+#: browse.php:22
+msgid "Add Content"
+msgstr "\83R\83\81\83\93\83g\82ð\92Ç\89Á"
+
+#: img/upload.php:203
+msgid "Add Photo"
+msgstr "\8eÊ\90^\82ð\92Ç\89Á"
+
+#: gallery.php:35 gallery.php:198
+msgid "Adding A New Gallery"
+msgstr "\90V\8bK\83M\83\83\83\89\83\8a\82ð\92Ç\89Á"
+
+#: gallery.php:63
+#, php-format
+msgid "Adding A Subgallery to %s"
+msgstr "%s\82É\83T\83u\83M\83\83\83\89\83\8a\82ð\92Ç\89Á"
+
+#: scripts/recursive_import.php:171
+msgid "Adding subdirectories:"
+msgstr "\83T\83u\83f\83B\83\8c\83N\83g\83\8a\82ð\92Ç\89Á"
+
+#
+#: lib/api.php:167
+msgid "Administrators"
+msgstr "\8aÇ\97\9d\8eÒ"
+
+#: templates/view/gallery.inc:135 templates/view/results.inc:93
+msgid "All"
+msgstr "\82·\82×\82Ä"
+
+#: lib/Block/recent_comments.php:59 lib/Block/recently_added.php:62
+msgid "All Galleries"
+msgstr "\82·\82×\82Ä\82Ì\83M\83\83\83\89\83\8a"
+
+#: lib/Ansel.php:3508
+#, php-format
+msgid "An error occurred listing galleries: %s"
+msgstr "\83M\83\83\83\89\83\8a\82ð\8fo\97Í\82·\82é\8dÛ\82É\83G\83\89\81[\82ª\94­\90\82µ\82Ü\82µ\82½: %s"
+
+#: lib/Exif.php:343
+msgid "Aperture"
+msgstr "\83A\83p\83`\83\83"
+
+#: templates/view/gallery.inc:20 templates/view/results.inc:20
+msgid "Are you sure you want to delete the selected photos?"
+msgstr "\91I\91ð\82³\82ê\82½\8eÊ\90^\82ð\96{\93\96\82É\8dí\8f\9c\82µ\82Ü\82·\82©\81H"
+
+#: lib/Exif.php:356
+msgid "Artist"
+msgstr "\8dì\8eÒ"
+
+#: perms.php:53
+msgid "Attempt to edit a non-existent share."
+msgstr "\91\8dÝ\82µ\82È\82¢\8b¤\97L\82ð\95Ò\8fW\82µ\82æ\82¤\82Æ\82µ\82Ü\82µ\82½\81B"
+
+#: lib/Exif.php:124
+msgid "Av-priority"
+msgstr "Av-\97D\90æ"
+
+#: lib/Exif.php:228
+msgid "Average"
+msgstr "\95½\8bÏ"
+
+#: image.php:477 image.php:508 lib/Views/Results.php:151
+#: lib/Views/Results.php:189
+msgid "Bad input."
+msgstr "\8cë\82Á\82½\93ü\97Í\81B"
+
+#: image.php:71
+msgid "Bottom"
+msgstr "\89º"
+
+#: lib/Ansel.php:347 config/prefs.php.dist:33
+msgid "Browse"
+msgstr "\89{\97\97"
+
+#: lib/Ansel.php:448
+msgid "Browse Tags"
+msgstr "\83^\83O\82ð\89{\97\97"
+
+#: lib/Exif.php:327
+msgid "Camera Make"
+msgstr "\83J\83\81\83\89\90»\91¢\8c³"
+
+#: lib/Exif.php:328
+msgid "Camera Model"
+msgstr "\83J\83\81\83\89\8c^\94Ô"
+
+#: lib/Exif.php:354
+msgid "Camera Orientation"
+msgstr "\83J\83\81\83\89\95û\8cü"
+
+#: lib/Ansel.php:297
+#, php-format
+msgid "Can't get unknown file type \"%s\"."
+msgstr "\83t\83@\83C\83\8b\82Ì\8eí\95Ê\82ð\94»\95Ê\82Å\82«\82Ü\82¹\82ñ:\"%s\""
+
+#: templates/gallery/delete_confirmation.inc:11
+msgid "Cancel"
+msgstr "\83L\83\83\83\93\83Z\83\8b"
+
+#: templates/image/preview_image.inc:28
+msgid "Cancel Change"
+msgstr "\95Ï\8dX\82ð\83L\83\83\83\93\83Z\83\8b"
+
+#: image.php:38 lib/Forms/Upload.php:38
+msgid "Caption"
+msgstr "\83L\83\83\83v\83V\83\87\83\93"
+
+#: gallery/captions.php:54
+msgid "Caption Editor"
+msgstr "\83L\83\83\83v\83V\83\87\83\93\95Ò\8fW"
+
+#: gallery/captions.php:49
+msgid "Captions Saved."
+msgstr "\83L\83\83\83v\83V\83\87\83\93\82ð\95Û\91\81B"
+
+#: config/prefs.php.dist:46
+msgid "Category"
+msgstr "\83J\83e\83S\83\8a"
+
+#: image.php:67 image.php:71
+msgid "Center"
+msgstr "\92\86\89\9b"
+
+#: lib/Exif.php:229
+msgid "Center Weighted Average"
+msgstr "Center Weighted Average"
+
+#: lib/Views/Gallery.php:256
+msgid "Change gallery properties"
+msgstr "\83M\83\83\83\89\83\8a\82Ì\83v\83\8d\83p\83e\83B\82ð\95Ï\8dX"
+
+#: config/prefs.php.dist:14
+msgid "Change your display options."
+msgstr "\95\\8e¦\83I\83v\83V\83\87\83\93\82Ì\95Ï\8dX"
+
+#: lib/Views/Gallery.php:249
+msgid "Choose Action:"
+msgstr "\8f\88\97\9d\82ð\91I\91ð"
+
+#: lib/Exif.php:242
+msgid "Cloudy"
+msgstr "\93Ü\82è"
+
+#: lib/Exif.php:293
+msgid "Color Sequential Area Sensor"
+msgstr "\90F\8f\87\8e\9f\83G\83\8a\83A\83Z\83\93\83T"
+
+#: lib/Exif.php:295
+msgid "Color Sequential Linear Sensor"
+msgstr "\90F\8f\87\8e\9f\83\8a\83j\83A\83Z\83\93\83T"
+
+#: lib/Exif.php:351
+msgid "Color Space"
+msgstr "\90F\8bó\8aÔ"
+
+#
+#: lib/Forms/Ecard.php:42
+msgid "Comments:"
+msgstr "\83R\83\81\83\93\83g:"
+
+#: lib/Exif.php:263
+msgid "Compulsory Flash"
+msgstr "\8b­\90§\83t\83\89\83b\83V\83\85"
+
+#: lib/Exif.php:265
+msgid "Compulsory Flash, Return light detected"
+msgstr "\8b­\90§\83t\83\89\83b\83V\83\85\81A\83X\83g\83\8d\83{\83\8a\83^\81[\83\93\8c\9f\8fo"
+
+#: lib/Exif.php:264
+msgid "Compulsory Flash, Return light not detected"
+msgstr "\8b­\90§\83t\83\89\83b\83V\83\85\81A\83X\83g\83\8d\83{\83\8a\83^\81[\83\93\8c\9f\8fo\82¹\82¸"
+
+#: lib/Views/Results.php:203
+#, php-format
+msgid "Copied %d photo from %s to %s"
+msgid_plural "Copied %d photos from %s to %s"
+msgstr[0] "%d\96\87\82Ì\8eÊ\90^\82ð%s\82©\82ç%s\82Ö\83R\83s\81[\82µ\82Ü\82µ\82½"
+msgstr[1] "%d\96\87\82Ì\8eÊ\90^\82ð%s\82©\82ç%s\82Ö\83R\83s\81[\82µ\82Ü\82µ\82½"
+
+# php-format
+#: image.php:514
+#, php-format
+msgid "Copied one photo to %s"
+msgid_plural "Copied %d photos to %s"
+msgstr[0] "%d \96\87\82Ì\8eÊ\90^\82ð %s \82Ö\83R\83s\81[\82µ\82Ü\82µ\82½"
+msgstr[1] "%d \96\87\82Ì\8eÊ\90^\82ð %s \82Ö\83R\83s\81[\82µ\82Ü\82µ\82½"
+
+#
+#: templates/view/gallery.inc:147 templates/view/results.inc:103
+msgid "Copy"
+msgstr "\83R\83s\81["
+
+#: lib/Exif.php:355
+msgid "Copyright"
+msgstr "\92\98\8dì\8c "
+
+#
+#: scripts/ansel.php:184
+msgid "Couldn't list galleries."
+msgstr "\83M\83\83\83\89\83\8a\82ð\83\8a\83X\83g\95\\8e¦\82Å\82«\82Ü\82¹\82ñ\81B"
+
+#
+#
+#: scripts/ansel.php:43
+msgid "Couldn't read command-line options."
+msgstr "\83R\83}\83\93\83h\83\89\83C\83\93\83I\83v\83V\83\87\83\93\82ð\93Ç\82ß\82Ü\82¹\82ñ\81B"
+
+#
+#: templates/xppublish/list.inc:19
+msgid "Create"
+msgstr "\90\90¬"
+
+#
+#: templates/xppublish/new.inc:8
+msgid "Create a new Gallery"
+msgstr "\90V\8bK\83M\83\83\83\89\83\8a\82ð\90\90¬"
+
+#: templates/xppublish/list.inc:18
+msgid "Create a new gallery:"
+msgstr "\90V\8bK\83M\83\83\83\89\83\8a\90\90¬:"
+
+#: lib/Views/Gallery.php:255
+msgid "Create a subgallery"
+msgstr "\83T\83u\83M\83\83\83\89\83\8a\90\90¬"
+
+#: lib/Tile/Gallery.php:114
+msgid "Created:"
+msgstr "\90\90¬\8dÏ\82Ý:"
+
+#: scripts/recursive_import.php:120
+#, php-format
+msgid "Creating gallery: \"%s\" (%s)"
+msgstr "\83M\83\83\83\89\83\8a\90\90¬: \"%s\" (%s)"
+
+#: lib/Exif.php:246
+msgid "Custom"
+msgstr "\83J\83X\83^\83\80"
+
+#: image.php:60
+msgid "Custom Watermark"
+msgstr "\93d\8eq\93§\82©\82µ\82ð\95Ï\8dX"
+
+#: config/prefs.php.dist:26
+msgid ""
+"Custom text to display describing your galleries. This will be displayed in "
+"place of your username when grouping galleries by username."
+msgstr ""
+"\83M\83\83\83\89\83\8a\82ð\90à\96¾\82·\82é\95\8fÍ\82ð\95Ò\8fW\82µ\82Ä\89º\82³\82¢\81B\82±\82ê\82Í\81A\83\86\81[\83U\96¼\82Å\83M\83\83\83\89\83\8a\82ð\83O\83\8b\81[\83v"
+"\95ª\82¯\82·\82é\8dÛ\82É\81A\83\86\81[\83U\96¼\82Ì\91ã\82í\82è\82É\95\\8e¦\82³\82ê\82Ü\82·\81B"
+
+#: config/prefs.php.dist:89
+msgid "Custom watermark to use for photos"
+msgstr "\8eÊ\90^\82É\8eg\82í\82ê\82é\93d\8eq\93§\82©\82µ\82ð\95Ï\8dX"
+
+#: lib/Exif.php:334
+msgid "Date Photo Digitized"
+msgstr "\8eÊ\90^\83f\83W\83^\83C\83Y\93ú"
+
+#: lib/Exif.php:332
+msgid "Date Photo Modified"
+msgstr "\8eÊ\90^\95Ï\8dX\93ú"
+
+#: lib/Exif.php:333
+msgid "Date Photo Taken"
+msgstr "\8eÊ\90^\8eB\89e\93ú"
+
+#: scripts/create_serialized_styles.php:41 config/prefs.php.dist:112
+msgid "Default"
+msgstr "\83f\83t\83H\83\8b\83g"
+
+#: config/prefs.php.dist:111
+msgid "Default style for galleries"
+msgstr "\83M\83\83\83\89\83\8a\82Ì\83f\83t\83H\83\8b\83g\8c`\8e®"
+
+#: templates/view/gallery.inc:141 templates/view/image.inc:60
+#: templates/view/results.inc:98 templates/view/slideshow.inc:25
+msgid "Delete"
+msgstr "\8dí\8f\9c"
+
+#: lib/Views/Gallery.php:268
+msgid "Delete this gallery"
+msgstr "\82±\82Ì\83M\83\83\83\89\83\8a\82ð\8dí\8f\9c"
+
+#: image.php:450 lib/Views/Results.php:127
+msgid "Deleted the photo."
+msgstr "\8eÊ\90^\82ð\8dí\8f\9c"
+
+#
+#: config/prefs.php.dist:13
+msgid "Display Options"
+msgstr "\95\\8e¦\83I\83v\83V\83\87\83\93"
+
+#: templates/view/gallery.inc:118
+#, php-format
+msgid "Displaying from %s to %s of %s items"
+msgstr "%s\82©\82ç%s (\91S%s) \82Ì\83A\83C\83e\83\80\82ð\95\\8e¦"
+
+#: templates/view/image.inc:60 templates/view/slideshow.inc:25
+#, php-format
+msgid "Do you want to permanently delete %s?"
+msgstr "%s\82ð\96{\93\96\82É\8a®\91S\82É\8fÁ\8b\8e\82µ\82Ü\82·\82©\81H"
+
+#: gallery/sort.php:62
+msgid "Done"
+msgstr "\8a®\97¹"
+
+#: templates/view/image.inc:68 templates/view/slideshow.inc:33
+msgid "Download Full Photo"
+msgstr "\91S\8eÊ\90^\82ð\83_\83E\83\93\83\8d\81[\83h"
+
+#: lib/Views/Gallery.php:251
+msgid "Download as .zip"
+msgstr ".zip \82Å\83_\83E\83\93\83\8d\81[\83h"
+
+#: gallery/sort.php:61
+msgid "Drag photos to the desired sort position."
+msgstr "\96]\82Þ\95À\82Ñ\88Ê\92u\82É\8eÊ\90^\82ð\83h\83\8d\83b\83v\82µ\82Ä\89º\82³\82¢\81B"
+
+#: xppublish.php:210
+#, php-format
+msgid "ERROR: %s"
+msgstr "\83G\83\89\81[: %s"
+
+#: lib/Exif.php:118
+msgid "Easy shooting"
+msgstr "Easy shooting"
+
+#: img/ecard.php:60
+msgid "Ecard - "
+msgstr "E\83J\81[\83h -"
+
+#: templates/tile/image.inc:20 templates/view/image.inc:57
+#: templates/view/slideshow.inc:22
+msgid "Edit"
+msgstr "\95Ò\8fW"
+
+#: image.php:271
+#, php-format
+msgid "Edit %s :: %s"
+msgstr "%s \82ð\95Ò\8fW :: %s"
+
+#
+#: perms.php:246
+msgid "Edit Permissions"
+msgstr "\83A\83N\83Z\83X\8c \82Ì\95Ò\8fW"
+
+#: perms.php:250
+#, php-format
+msgid "Edit Permissions for %s"
+msgstr "%s \82Ì\83A\83N\83Z\83X\8c \82Ì\95Ò\8fW"
+
+#
+#: image.php:366
+msgid "Edit Photo"
+msgstr "\8eÊ\90^\82ð\95Ò\8fW"
+
+#
+#: image.php:209
+msgid "Edit an photo"
+msgstr "\8eÊ\90^\82ð\95Ò\8fW"
+
+#: image.php:169
+#, php-format
+msgid "Edit properties :: %s"
+msgstr "\83v\83\8d\83p\83e\83B\95Ò\8fW :: %s"
+
+#: lib/Views/Gallery.php:267
+msgid "Empty this gallery"
+msgstr "\82±\82Ì\83M\83\83\83\89\83\8a\82ð\8bó\82É\82·\82é"
+
+#: gallery/captions.php:25
+#, php-format
+msgid "Error accessing %s: %s"
+msgstr "%s \82Ì\83A\83N\83Z\83X\82Å\83G\83\89\81[: %s"
+
+#: rss.php:36
+msgid "Error retrieving feed"
+msgstr "\83t\83B\81[\83h\82Ì\8eó\82¯\8eæ\82è\82Å\83G\83\89\81["
+
+#
+#: templates/xppublish/list.inc:12
+msgid "Existing galleries:"
+msgstr "\91\8dÝ\82·\82é\83M\83\83\83\89\83\8a:"
+
+#: lib/Exif.php:340
+msgid "Exposure"
+msgstr "\98I\8fo"
+
+#: lib/Exif.php:346
+msgid "Exposure Bias"
+msgstr "\98I\8fo\83o\83C\83A\83X"
+
+#: lib/Exif.php:347
+msgid "Exposure Mode"
+msgstr "\98I\8fo\83\82\81[\83h"
+
+#: lib/Exif.php:344
+msgid "F-Number"
+msgstr "F\92l"
+
+#: lib/Forms/Upload.php:45
+#, php-format
+msgid "File %s"
+msgstr "\83t\83@\83C\83\8b %s"
+
+#
+#: lib/Exif.php:331
+msgid "File Size"
+msgstr "\83t\83@\83C\83\8b\83T\83C\83Y"
+
+#: lib/Forms/Upload.php:34 lib/Forms/Upload.php:53
+msgid "File to upload"
+msgstr "\83A\83b\83v\83\8d\81[\83h\82·\82é\83t\83@\83C\83\8b"
+
+#: lib/Exif.php:245 lib/Exif.php:260
+msgid "Flash"
+msgstr "\83t\83\89\83b\83V\83\85"
+
+#: lib/Exif.php:349
+msgid "Flash Setting"
+msgstr "\83t\83\89\83b\83V\83\85\90Ý\92è"
+
+#: lib/Exif.php:268
+msgid "Flash, Auto-Mode"
+msgstr "\83t\83\89\83b\83V\83\85\81A\8e©\93®\83\82\81[\83h"
+
+#: lib/Exif.php:270
+msgid "Flash, Auto-Mode, Return light detected"
+msgstr "\83t\83\89\83b\83V\83\85\81A\8e©\93®\83\82\81[\83h\81A\83X\83g\83\8d\83{\83\8a\83^\81[\83\93\8c\9f\8fo"
+
+#: lib/Exif.php:269
+msgid "Flash, Auto-Mode, Return light not detected"
+msgstr "\83t\83\89\83b\83V\83\85\81A\8e©\93®\83\82\81[\83h\81A\83X\83g\83\8d\83{\83\8a\83^\81[\83\93\8c\9f\8fo\82¹\82¸"
+
+#: lib/Exif.php:262
+msgid "Flash, strobe return light detected"
+msgstr "\83t\83\89\83b\83V\83\85\81A\83X\83g\83\8d\83{\81A\83X\83g\83\8d\83{\83\8a\83^\81[\83\93\8c\9f\8fo"
+
+#: lib/Exif.php:261
+msgid "Flash, strobe return light not detected"
+msgstr "\83t\83\89\83b\83V\83\85\81A\83X\83g\83\8d\83{\81A\83X\83g\83\8d\83{\83\8a\83^\81[\83\93\8c\9f\8fo\82¹\82¸"
+
+#: templates/image/edit_image.inc:22
+msgid "Flip"
+msgstr "\83t\83\8a\83b\83v"
+
+#: lib/Exif.php:244
+msgid "Fluorescent"
+msgstr "\8cu\8cõ\93\94"
+
+#: lib/Exif.php:341
+msgid "Focal Length"
+msgstr "\8fÅ\93_\8b\97\97£"
+
+#: lib/Exif.php:342
+msgid "Focal Length (35mm equiv)"
+msgstr "\8fÅ\93_\8b\97\97£(35mm\91\8a\93\96)"
+
+#: templates/tile/image.inc:10
+msgid "From: "
+msgstr "From: "
+
+#
+#: lib/Ansel.php:448 config/prefs.php.dist:34
+msgid "Galleries"
+msgstr "\83M\83\83\83\89\83\8a"
+
+#: templates/group/header.inc:6
+#, php-format
+msgid "Galleries grouped by %s"
+msgstr "%s\82Å\83O\83\8b\81[\83v\89»\82³\82ê\82½\83M\83\83\83\89\83\8a"
+
+#: list.php:54 list.php:57
+#, php-format
+msgid "Galleries in category %s"
+msgstr "\83J\83e\83S\83\8a%s\82É\91®\82·\82é\83M\83\83\83\89\83\8a"
+
+#
+#: lib/Block/gallery.php:3 lib/Block/gallery.php:30 lib/Block/gallery.php:46
+#: lib/Block/recent_comments.php:31 lib/Block/recent_comments.php:50
+#: lib/Block/recently_added.php:28 lib/Block/recently_added.php:51
+msgid "Gallery"
+msgstr "\83M\83\83\83\89\83\8a"
+
+#
+#: templates/gallery/gallery.inc:17
+msgid "Gallery Category"
+msgstr "\83M\83\83\83\89\83\8a\83J\83e\83S\83\8a"
+
+#: templates/gallery/gallery.inc:46
+msgid "Gallery Description"
+msgstr "\83M\83\83\83\89\83\8a\82Ì\90à\96¾"
+
+#: templates/gallery/gallery.inc:39
+msgid "Gallery Display Name"
+msgstr "\83M\83\83\83\89\83\8a\95\\8e¦\96¼"
+
+#
+#: list.php:62 list.php:136
+msgid "Gallery List"
+msgstr "\83M\83\83\83\89\83\8a\83\8a\83X\83g"
+
+#
+#: lib/Block/my_galleries.php:45
+msgid "Gallery Name"
+msgstr "\83M\83\83\83\89\83\8a\96¼"
+
+#
+#: lib/Tile/Gallery.php:89
+msgid "Gallery Properties"
+msgstr "\83M\83\83\83\89\83\8a\83v\83\8d\83p\83e\83B"
+
+#: templates/xppublish/new.inc:14
+msgid "Gallery Short Name"
+msgstr "\83M\83\83\83\89\83\8a\92Z\8fk\96¼"
+
+#
+#: templates/gallery/gallery.inc:52
+msgid "Gallery Tags"
+msgstr "\83M\83\83\83\89\83\8a\83^\83O"
+
+#: xppublish.php:121
+msgid "Gallery Unique name is already taken."
+msgstr "\83M\83\83\83\89\83\8a\82Ì\88ê\88Ó\96¼\82Í\8aù\82É\8eæ\82ç\82ê\82Ä\82¢\82Ü\82·\81B"
+
+#: lib/Block/gallery.php:101 lib/Block/recent_comments.php:138
+#: lib/Block/recently_added.php:151
+msgid "Gallery does not exist."
+msgstr "\83M\83\83\83\89\83\8a\82ª\91\8dÝ\82µ\82Ü\82¹\82ñ\81B"
+
+#: lib/Ansel.php:730
+msgid "Gallery names must be non-empty"
+msgstr "\83M\83\83\83\89\83\8a\96¼\82Í\8bó\82Å\82 \82Á\82Ä\82Í\82¢\82¯\82Ü\82¹\82ñ\81B"
+
+#: image.php:372
+msgid "Gallery not found."
+msgstr "\83M\83\83\83\89\83\8a\82ª\8c©\82Â\82©\82è\82Ü\82¹\82ñ\81B"
+
+#: gallery/sort.php:40
+msgid "Gallery sorted."
+msgstr "\83M\83\83\83\89\83\8a\82Í\95À\82×\91Ö\82¦\82ç\82ê\82Ü\82µ\82½\81B"
+
+#: image.php:63
+msgid "Giant"
+msgstr "\8b\90\91å"
+
+#: templates/image/edit_image.inc:28
+msgid "Grayscale"
+msgstr "\83O\83\8c\83C\83X\83P\81[\83\8b"
+
+#
+#: config/prefs.php.dist:48
+msgid "Group galleries by"
+msgstr "\83M\83\83\83\89\83\8a\82ð\83O\83\8b\81[\83v\95ª\82¯: "
+
+#: lib/Exif.php:336
+msgid "Height"
+msgstr "\8d\82\82³"
+
+#: image.php:68
+msgid "Horizontal Alignment"
+msgstr "\90\85\95½\88Ê\92u\8d\87\82í\82¹"
+
+#: lib/Exif.php:345
+msgid "ISO Setting"
+msgstr "ISO\90Ý\92è"
+
+#: lib/Ansel.php:2819
+msgid "Incomplete photo"
+msgstr "\95s\8a®\91S\82È\8eÊ\90^"
+
+#: lib/Ansel.php:1020 lib/Ansel.php:1069
+msgid "Invalid data"
+msgstr "\95s\90³\82È\83f\81[\83^"
+
+#: scripts/ansel.php:125
+#, php-format
+msgid "Invalid gallery \"%s\" specified."
+msgstr "\95s\90³\82È\83M\83\83\83\89\83\8a \"%s\" \82ª\8ew\92è\82³\82ê\82Ü\82µ\82½\81B"
+
+#: xppublish.php:87 xppublish.php:180
+msgid "Invalid gallery specified."
+msgstr "\95s\90³\82È\83M\83\83\83\89\83\8a\82ª\8ew\92è\82³\82ê\82Ü\82µ\82½\81B"
+
+#: image.php:63
+msgid "Large"
+msgstr "\91å\82«\82¢"
+
+#
+#: lib/Block/my_galleries.php:45
+msgid "Last Modified"
+msgstr "\8dÅ\8fI\95Ï\8dX"
+
+#: img/upload.php:209
+msgid "Learn how to publish photos directly from Windows."
+msgstr "Windows\82©\82ç\92¼\90Ú\8eÊ\90^\82ð\8cö\8aJ\82·\82é\82É\82Í\81B"
+
+#: image.php:67
+msgid "Left"
+msgstr "\8d¶"
+
+#: scripts/ansel.php:189
+msgid "Listing Gallery/Name"
+msgstr "\83M\83\83\83\89\83\8a/\96¼\91O\82ð\83\8a\83X\83g\95\\8e¦"
+
+#: scripts/ansel.php:173
+#, php-format
+msgid "Listing photos in %s"
+msgstr "%s \82Ì\8eÊ\90^\82ð\83\8a\83X\83g\95\\8e¦"
+
+#: scripts/ansel.php:116 scripts/recursive_import.php:81
+#, php-format
+msgid "Logged in successfully as \"%s\"."
+msgstr "\"%s\"\82Æ\82µ\82Ä\83\8d\83O\83C\83\93\82É\90¬\8c÷\82µ\82Ü\82µ\82½\81B"
+
+#: scripts/ansel.php:112
+msgid "Login is incorrect."
+msgstr "\83\8d\83O\83C\83\93\82Í\90³\82µ\82­\82 \82è\82Ü\82¹\82ñ\81B"
+
+#: templates/xppublish/login.inc:2
+#, php-format
+msgid "Login to %s"
+msgstr "%s\82É\83\8d\83O\83C\83\93"
+
+#: image.php:37 lib/Forms/Upload.php:37
+msgid "Make this the default photo for this gallery?"
+msgstr "\82±\82Ì\8eÊ\90^\82ð\82±\82Ì\83M\83\83\83\89\83\8a\82Ì\83f\83t\83H\83\8b\83g\8eÊ\90^\82É\82µ\82Ü\82·\82©\81H"
+
+#: scripts/ansel.php:317 scripts/recursive_import.php:187
+msgid ""
+"Mandatory arguments to long options are mandatory for short options too."
+msgstr ""
+"Mandatory arguments to long options are mandatory for short options too."
+
+#: lib/Exif.php:126 lib/Exif.php:247
+msgid "Manual"
+msgstr "\83}\83j\83\85\83A\83\8b"
+
+#: lib/Block/recently_added.php:33
+msgid "Maximum number of photos"
+msgstr "\8eÊ\90^\82Ì\8dÅ\91å\90\94"
+
+#: image.php:63
+msgid "Medium"
+msgstr "\92\86"
+
+#: lib/Exif.php:348
+msgid "Metering Mode"
+msgstr "\91ª\8cõ\83\82\81[\83h"
+
+#: templates/image/edit_image.inc:25
+msgid "Mirror"
+msgstr "\94½\93]"
+
+#: lib/Exif.php:149
+#, php-format
+msgid "Mirrored"
+msgstr "\94½\93]\8dÏ\82Ý"
+
+#
+#: lib/Tile/Gallery.php:117
+msgid "Modified"
+msgstr "\8dÅ\8fI\95Ï\8dX"
+
+#: gallery.php:120
+#, php-format
+msgid "Modifying: %s"
+msgstr "\95Ï\8dX: %s"
+
+#: templates/view/gallery.inc:144 templates/view/results.inc:100
+msgid "Move"
+msgstr "\88Ú\93®"
+
+#: lib/Views/Results.php:165
+#, php-format
+msgid "Moved %d photo from %s to %s"
+msgid_plural "Moved %d photos from %s to %s"
+msgstr[0] "%d \96\87\82Ì\8eÊ\90^\82ð%s\82©\82ç%s\82Ö\88Ú\93®"
+msgstr[1] "%d \96\87\82Ì\8eÊ\90^\82ð%s\82©\82ç%s\82Ö\88Ú\93®"
+
+#: image.php:483
+#, php-format
+msgid "Moved %d photo to %s"
+msgid_plural "Moved %d photos to %s"
+msgstr[0] "%d\96\87\82Ì\8eÊ\90^\82ð%s\82Ö\88Ú\93®"
+msgstr[1] "%d\96\87\82Ì\8eÊ\90^\82ð%s\82Ö\88Ú\93®"
+
+#: lib/Exif.php:232
+msgid "Multi-Segment"
+msgstr "\83}\83\8b\83`\83Z\83O\83\81\83\93\83g"
+
+#: lib/Exif.php:231
+msgid "Multi-Spot"
+msgstr "\83}\83\8b\83`\83X\83|\83b\83g"
+
+#: lib/Forms/Upload.php:41
+msgid "Multiple Photos"
+msgstr "\95¡\90\94\82Ì\8eÊ\90^"
+
+#
+#: list.php:34 lib/Ansel.php:427 lib/Block/my_galleries.php:3
+#: lib/Block/my_galleries.php:28 templates/group/owner.inc:3
+#: config/prefs.php.dist:35
+msgid "My Galleries"
+msgstr "\8e\84\82Ì\83M\83\83\83\89\83\8a"
+
+#
+#: browse_edit.php:28
+msgid "My Photos :: Add Content"
+msgstr "\8e\84\82Ì\83M\83\83\83\89\83\8a :: \83R\83\81\83\93\83g\82ð\92Ç\89Á"
+
+#: templates/xppublish/new.inc:33
+msgid "New Gallery Description"
+msgstr "\90V\8bK\83M\83\83\83\89\83\8a\82Ì\90à\96¾"
+
+#
+#: templates/xppublish/new.inc:24
+msgid "New Gallery Title"
+msgstr "\90V\8bK\83M\83\83\83\89\83\8a\82Ì\91è\96¼"
+
+#: templates/view/image.inc:81 templates/view/image.inc:82
+#: templates/view/slideshow.inc:46 templates/view/slideshow.inc:47
+msgid "Next"
+msgstr "\8e\9f"
+
+#: lib/Exif.php:259 lib/Exif.php:266 lib/Exif.php:267 lib/Exif.php:271
+msgid "No Flash"
+msgstr "\83t\83\89\83b\83V\83\85\97}\90§"
+
+#: xppublish.php:123
+msgid "No Gallery Name Specified."
+msgstr "\83M\83\83\83\89\83\8a\96¼\82ª\8ew\92è\82³\82ê\82Ä\82¢\82Ü\82¹\82ñ\81B"
+
+#: xppublish.php:119
+msgid "No Gallery Unique Name Specified."
+msgstr "\83M\83\83\83\89\83\8a\88ê\88Ó\96¼\82ª\8ew\92è\82³\82ê\82Ä\82¢\82Ü\82¹\82ñ\81B"
+
+#: xppublish.php:190
+msgid "No file specified"
+msgstr "\83t\83@\83C\83\8b\82ª\8ew\92è\82³\82ê\82Ä\82¢\82Ü\82¹\82ñ\81B"
+
+#: lib/Views/Abstract.php:22
+msgid "No gallery specified."
+msgstr "\83M\83\83\83\89\83\8a\82ª\8ew\92è\82³\82ê\82Ä\82¢\82Ü\82¹\82ñ\81B"
+
+#
+#: templates/view/gallery.inc:136 templates/view/results.inc:94
+#: config/prefs.php.dist:47
+msgid "None"
+msgstr "\82È\82µ"
+
+#: lib/Exif.php:147
+#, php-format
+msgid "Normal (O deg)"
+msgstr "\90³\88Ê (0\93x)"
+
+#: lib/Exif.php:289
+msgid "Not defined"
+msgstr "\92è\8b`\82³\82ê\82Ä\82¢\82Ü\82¹\82ñ"
+
+#: image.php:112 img/upload.php:22
+msgid "Not found or error accessing gallery."
+msgstr "\8c©\82Â\82©\82ç\82È\82¢\82©\81A\83M\83\83\83\89\83\8a\82Ö\82Ì\83A\83N\83Z\83X\82Å\83G\83\89\81[\82ª\82 \82è\82Ü\82µ\82½\81B"
+
+#: config/prefs.php.dist:73
+msgid "Number of groups per page"
+msgstr "\83y\81[\83W\82²\82Æ\82Ì\83O\83\8b\81[\83v\90\94"
+
+#: config/prefs.php.dist:65
+msgid "Number of tiles per page"
+msgstr "\83y\81[\83W\82²\82Æ\82Ì\83^\83C\83\8b\90\94"
+
+#: config/prefs.php.dist:57
+msgid "Number of tiles per row"
+msgstr "\8c\85\82²\82Æ\82Ì\83^\83C\83\8b\90\94"
+
+#: lib/Exif.php:290
+msgid "One Chip Color Area Sensor"
+msgstr "\92P\94Â\83J\83\89\81[\83Z\83\93\83T"
+
+#
+#: perms.php:66
+msgid ""
+"Only the owner or system administrator may change ownership or owner "
+"permissions for a share"
+msgstr "\8f\8a\97L\8eÒ\82©\83V\83X\83e\83\80\8aÇ\97\9d\8eÒ\82¾\82¯\82ª\8b¤\97L\82Ì\8f\8a\97L\8c \82Æ\83A\83N\83Z\83X\8c \82ð\95Ï\8dX\82Å\82«\82Ü\82·"
+
+#: lib/Exif.php:234
+msgid "Other"
+msgstr "\82»\82Ì\91¼"
+
+#
+#: config/prefs.php.dist:12
+msgid "Other Options"
+msgstr "\82»\82Ì\91¼\82Ì\83I\83v\83V\83\87\83\93"
+
+#: templates/group/owner.inc:51
+#, php-format
+msgid "Owned by: %s <br />"
+msgstr "\8f\8a\97L\8eÒ: %s <br />"
+
+#: config/prefs.php.dist:45 templates/tile/gallery.html:13
+msgid "Owner"
+msgstr "\8f\8a\97L\8eÒ"
+
+#: lib/Ansel.php:1100
+msgid "Parent does not exist."
+msgstr "\90e\82ª\91\8dÝ\82µ\82Ü\82¹\82ñ\81B"
+
+#: lib/Exif.php:233
+msgid "Partial"
+msgstr "\95\94\95ª"
+
+#: templates/xppublish/login.inc:14
+msgid "Password"
+msgstr "\83p\83X\83\8f\81[\83h"
+
+#: templates/view/slideshow.inc:43
+msgid "Pause"
+msgstr "\88ê\8e\9e\92â\8e~"
+
+#: templates/view/slideshow.inc:43
+msgid "Pause Slideshow"
+msgstr "\83X\83\89\83C\83h\83V\83\87\81[\82ð\88ê\8e\9e\92â\8e~"
+
+#: lib/Block/my_galleries.php:46
+msgid "Photo Count"
+msgstr "\8eÊ\90^\8cv\90\94"
+
+#: lib/Exif.php:330
+msgid "Photo Description"
+msgstr "\8eÊ\90^\82Ì\90à\96¾"
+
+#
+#: browse.php:18
+msgid "Photo Galleries"
+msgstr "\8eÊ\90^\83M\83\83\83\89\83\8a"
+
+#: lib/Exif.php:329
+msgid "Photo Type"
+msgstr "\8eÊ\90^\83^\83C\83v"
+
+#: templates/xppublish/javascript.inc:63
+msgid "Photo Upload"
+msgstr "\8eÊ\90^\83A\83b\83v\83\8d\81[\83h"
+
+#: lib/Ansel.php:3293
+msgid "Photo not found"
+msgstr "\8eÊ\90^\82ª\8c©\82Â\82©\82è\82Ü\82¹\82ñ"
+
+#: rss.php:131
+#, php-format
+msgid "Photos by %s"
+msgstr "%s\82É\82æ\82é\8eÊ\90^"
+
+#: lib/Ansel.php:2269 lib/Ansel.php:3325
+msgid "Photos not found"
+msgstr "\8eÊ\90^\82ª\8c©\82Â\82©\82è\82Ü\82¹\82ñ"
+
+#: rss.php:153 rss.php:159
+#, php-format
+msgid "Photos tagged with %s on %s"
+msgstr "\83^\83O%s\82ð\8e\9d\82Â%s\82Ì\8eÊ\90^"
+
+#: templates/view/image.inc:78 templates/view/slideshow.inc:42
+msgid "Play"
+msgstr "\8dÄ\90¶"
+
+#: xppublish.php:54
+msgid "Please enter your Username and Password."
+msgstr "\83\86\81[\83U\96¼\82Æ\83p\83X\83\8f\81[\83h\82ð\93ü\97Í\82µ\82Ä\82­\82¾\82³\82¢\81B"
+
+#: scripts/create_serialized_styles.php:71 config/prefs.php.dist:116
+msgid "Polaroid"
+msgstr "\83|\83\89\83\8d\83C\83h"
+
+#: xppublish.php:99 xppublish.php:155
+msgid "Press the \"Back\" button and try again."
+msgstr "\"\96ß\82é\"\83{\83^\83\93\82ð\89\9f\82µ\82Ä\8dÄ\8e\8e\8ds\82µ\82Ä\89º\82³\82¢\81B"
+
+#: scripts/create_serialized_styles.php:48 config/prefs.php.dist:113
+msgid "Pretty Thumbnails"
+msgstr "\83T\83\80\83l\83C\83\8b"
+
+#: scripts/create_serialized_styles.php:56 config/prefs.php.dist:114
+msgid "Pretty Thumbnails on White Background"
+msgstr "\94w\8ci\94\92\92n\82Ì\83T\83\80\83l\83C\83\8b"
+
+#: image.php:378
+#, php-format
+msgid "Preview changes for %s :: %s"
+msgstr "%s\82Ì\95Ï\8dX\82ð\83v\83\8c\83r\83\85\81[ :: %s"
+
+#: templates/view/image.inc:79 templates/view/image.inc:80
+#: templates/view/slideshow.inc:44 templates/view/slideshow.inc:45
+msgid "Previous"
+msgstr "\91O"
+
+#: lib/Exif.php:120
+msgid "Program"
+msgstr "\83v\83\8d\83O\83\89\83\80"
+
+#: templates/tile/image.inc:19 templates/view/image.inc:56
+#: templates/view/slideshow.inc:21
+msgid "Properties"
+msgstr "\83v\83\8d\83p\83e\83B"
+
+#: templates/xppublish/list.inc:7
+msgid "Publish Photos to:"
+msgstr "\8eÊ\90^\82ð\8cö\8aJ:"
+
+#: xppublish.php:35
+#, php-format
+msgid "Publish to %s"
+msgstr "%s\82Ö\8cö\8aJ"
+
+#: xppublish.php:24
+#, php-format
+msgid "Publish your photos to %s on %s."
+msgstr "\8eÊ\90^\82ð%s (%s) \82Ö\8cö\8aJ"
+
+#
+#: lib/Block/gallery.php:33 lib/Block/gallery.php:52
+msgid "Random Gallery"
+msgstr "\83\89\83\93\83_\83\80\83M\83\83\83\89\83\8a"
+
+#
+#: lib/Block/random_gallery.php:3 lib/Block/random_gallery.php:25
+msgid "Random gallery"
+msgstr "\83\89\83\93\83_\83\80\83M\83\83\83\89\83\8a"
+
+#
+#: lib/Block/random_photo.php:3 lib/Block/random_photo.php:26
+msgid "Random photo"
+msgstr "\83\89\83\93\83_\83\80\8eÊ\90^"
+
+#: templates/gallery/delete_confirmation.inc:10
+msgid "Really Delete"
+msgstr "\8a®\91S\8dí\8f\9c"
+
+#: templates/gallery/delete_confirmation.inc:10
+msgid "Really Empty"
+msgstr "\8a®\91S\94p\8aü"
+
+#: templates/gallery/delete_confirmation.inc:2
+#, php-format
+msgid "Really delete %s?"
+msgstr "\96{\93\96\82É%s\82ð\8dí\8f\9c\82µ\82Ü\82·\82©\81H"
+
+#: templates/gallery/delete_confirmation.inc:5
+msgid ""
+"Really delete this gallery and all of the photos in it? This action cannot "
+"be undone."
+msgstr ""
+"\96{\93\96\82É\82±\82Ì\83M\83\83\83\89\83\8a\82Æ\82»\82Ì\92\86\82Ì\8eÊ\90^\82ð\8dí\8f\9c\82µ\82Ü\82·\82©\81\82±\82Ì\8f\88\97\9d\82Í\8eæ\82è\8fÁ\82¹\82Ü\82¹\82ñ\81B"
+
+#: templates/gallery/delete_confirmation.inc:2
+#, php-format
+msgid "Really empty %s?"
+msgstr "\96{\93\96\82É%s\82ð\94p\8aü\82µ\82Ü\82·\82©\81H"
+
+#: templates/gallery/delete_confirmation.inc:5
+msgid ""
+"Really empty this gallery and all of the photos in it? This action cannot be "
+"undone."
+msgstr ""
+"\96{\93\96\82É\82±\82Ì\83M\83\83\83\89\83\8a\82Æ\82»\82Ì\92\86\82Ì\8eÊ\90^\82ð\94p\8aü\82µ\82Ü\82·\82©\81\82±\82Ì\91\80\8dì\82Í\8eæ\82è\8fÁ\82¹\82Ü\82¹\82ñ\81B"
+
+#: lib/Block/recent_comments.php:62
+#, php-format
+msgid "Recent Comments In %s"
+msgstr "%s\82Ì\8dÅ\90V\82Ì\83R\83\81\83\93\83g"
+
+#: lib/Block/recent_comments.php:5
+msgid "Recent Photo Comments"
+msgstr "\8dÅ\90V\82Ì\8eÊ\90^\83R\83\81\83\93\83g"
+
+#: templates/view/gallery.inc:200
+#, php-format
+msgid "Recent photos by %s"
+msgstr "%s\82É\82æ\82é\8dÅ\90V\82Ì\8eÊ\90^"
+
+#: templates/view/gallery.inc:201
+#, php-format
+msgid "Recent photos in %s"
+msgstr "%s\92\86\82Ì\8dÅ\90V\82Ì\8eÊ\90^"
+
+#
+#: lib/Block/recently_added.php:3
+msgid "Recently Added Photos"
+msgstr "\8dÅ\90V\82Ì\92Ç\89Á\8eÊ\90^"
+
+#: lib/Block/recently_added.php:64
+#, php-format
+msgid "Recently Added Photos From %s"
+msgstr "%s\82©\82ç\92Ç\89Á\82³\82ê\82½\8dÅ\90V\8eÊ\90^"
+
+#: rss.php:134
+#, php-format
+msgid "Recently added photos by %s on %s"
+msgstr "%s\82©\82ç%s\82Ö\92Ç\89Á\82³\82ê\82½\8dÅ\90V\8eÊ\90^"
+
+#: rss.php:57 rss.php:60
+#, php-format
+msgid "Recently added photos on %s"
+msgstr "%s\82Ö\92Ç\89Á\82³\82ê\82½\8dÅ\90V\8eÊ\90^"
+
+#: lib/Exif.php:272
+msgid "Red Eye"
+msgstr "\90Ô\96Ú"
+
+#: lib/Exif.php:278
+msgid "Red Eye, Auto-Mode"
+msgstr "\90Ô\96Ú\81A\8e©\93®\83\82\81[\83h"
+
+#: lib/Exif.php:280
+msgid "Red Eye, Auto-Mode, Return light detected"
+msgstr "\90Ô\96Ú\81A\8e©\93®\83\82\81[\83h\81A\83X\83g\83\8d\83{\83\8a\83^\81[\83\93\8c\9f\8fo"
+
+#: lib/Exif.php:279
+msgid "Red Eye, Auto-Mode, Return light not detected"
+msgstr "\90Ô\96Ú\81A\8e©\93®\83\82\81[\83h\81A\83X\83g\83\8d\83{\83\8a\83^\81[\83\93\8c\9f\8fo\82¹\82¸"
+
+#: lib/Exif.php:275
+msgid "Red Eye, Compulsory Flash"
+msgstr "\90Ô\96Ú\81A\8b­\90§\83t\83\89\83b\83V\83\85"
+
+#: lib/Exif.php:277
+msgid "Red Eye, Compulsory Flash, Return light detected"
+msgstr "\90Ô\96Ú\81A\8b­\90§\83t\83\89\83b\83V\83\85\81A\83X\83g\83\8d\83{\83\8a\83^\81[\83\93\8c\9f\8fo"
+
+#: lib/Exif.php:276
+msgid "Red Eye, Compulsory Flash, Return light not detected"
+msgstr "\90Ô\96Ú\81A\8b­\90§\83t\83\89\83b\83V\83\85\81A\83X\83g\83\8d\83{\83\8a\83^\81[\83\93\8c\9f\8fo\82¹\82¸"
+
+#: lib/Exif.php:274
+msgid "Red Eye, Return light detected"
+msgstr "\90Ô\96Ú\81A\83X\83g\83\8d\83{\83\8a\83^\81[\83\93\8c\9f\8fo"
+
+#: lib/Exif.php:273
+msgid "Red Eye, Return light not detected"
+msgstr "\90Ô\96Ú\81A\83X\83g\83\8d\83{\83\8a\83^\81[\83\93\8c\9f\8fo\82¹\82¸"
+
+#: templates/group/header.inc:7 templates/group/header.inc:8
+#: templates/list/header.inc:5
+msgid "Refresh List"
+msgstr "\83X\83g\83\8d\83{\83\8a\83^\81[\83\93\8c\9f\8fo"
+
+#: templates/view/results.inc:144
+msgid "Related Tags"
+msgstr "\8aÖ\98A\82·\82é\83^\83O"
+
+#: lib/Tags.php:563
+#, php-format
+msgid "Remove %s from search"
+msgstr "\8c\9f\8dõ\82©\82ç%s\82ð\8dí\8f\9c"
+
+#: xrequest.php:24 lib/Views/Gallery.php:103 lib/Views/Image.php:209
+msgid "Remove Tag"
+msgstr "\8dí\8f\9c\83^\83O"
+
+#: lib/Views/Gallery.php:261
+msgid "Reset default photo"
+msgstr "\83f\83t\83H\83\8b\83g\8eÊ\90^\82ð\83\8a\83Z\83b\83g"
+
+#: image.php:67
+msgid "Right"
+msgstr "\89E"
+
+#: templates/image/edit_image.inc:18
+msgid "Rotate 180"
+msgstr "180\93x\89ñ\93]"
+
+#: templates/image/edit_image.inc:17
+msgid "Rotate Left"
+msgstr "\8d\82É\89ñ\93]"
+
+#: templates/image/edit_image.inc:19
+msgid "Rotate Right"
+msgstr "\89E\82É\89ñ\93]"
+
+#: image.php:31 image.php:54
+msgid "Save"
+msgstr "\95Û\91¶"
+
+#
+#: templates/captions/captions.inc:47
+msgid "Save Captions"
+msgstr "\83L\83\83\83v\83V\83\87\83\93\82ð\95Û\91¶"
+
+#
+#: templates/image/preview_image.inc:23
+msgid "Save Change"
+msgstr "\95Ï\8dX\82ð\95Û\91¶"
+
+#
+#: templates/gallery/gallery.inc:59
+msgid "Save Gallery"
+msgstr "\83M\83\83\83\89\83\8a\82ð\95Û\91¶"
+
+#
+#: image.php:195
+msgid "Save Photo"
+msgstr "\8eÊ\90^\82ð\95Û\91¶"
+
+#: lib/Views/Results.php:85
+#, php-format
+msgid "Searching %s's photos tagged: "
+msgstr "\83^\83O\95t\82¯\82³\82ê\82½ %s\82Ì\8eÊ\90^\82ð\8c\9f\8dõ: "
+
+#: lib/Views/Results.php:85
+msgid "Searching all photos tagged: "
+msgstr "\83^\83O\95t\82¯\82³\82ê\82½\91S\82Ä\82Ì\8eÊ\90^\82ð\8c\9f\8dõ: "
+
+#
+#: templates/view/gallery.inc:135 templates/view/results.inc:93
+msgid "Select All"
+msgstr "\82·\82×\82Ä\91I\91ð"
+
+#: templates/view/gallery.inc:136 templates/view/results.inc:94
+msgid "Select None"
+msgstr "\91I\91ð\8eæ\82è\8fÁ\82µ"
+
+#: templates/tile/image.inc:7
+msgid "Select for Action"
+msgstr "\91\80\8dì\82ð\91I\91ð"
+
+#: templates/view/gallery.inc:134 templates/view/results.inc:92
+#, php-format
+msgid "Select: %s, %s"
+msgstr "\91I\91ð: %s\81A %s"
+
+#: templates/view/gallery.inc:149 templates/view/results.inc:105
+msgid "Selected photos to"
+msgstr "\91I\91ð\82³\82ê\82½\8eÊ\90^\82ð"
+
+#: lib/Forms/Ecard.php:25
+msgid "Send"
+msgstr "\91\97\90M"
+
+#: img/ecard.php:104
+#, php-format
+msgid "Send Ecard :: %s"
+msgstr "E\83J\81[\83h\91\97\90M :: %s"
+
+#: templates/view/image.inc:66 templates/view/slideshow.inc:31
+msgid "Send an Ecard"
+msgstr "E\83J\81[\83h\91\97\90M"
+
+#: lib/Forms/Ecard.php:41
+msgid "Send ecard to the following address:"
+msgstr "\88È\89º\82Ì\83A\83h\83\8c\83X\82ÉE\83J\81[\83h\91\97\90M:"
+
+#: lib/Exif.php:352
+msgid "Sensing Method"
+msgstr "\8c\9f\92m\95û\96@"
+
+#
+#: lib/Views/Gallery.php:257
+msgid "Set captions"
+msgstr "\83L\83\83\83v\83V\83\87\83\93\82ð\90Ý\92è"
+
+#
+#: lib/Views/Gallery.php:264
+msgid "Set gallery permissions"
+msgstr "\83M\83\83\83\89\83\8a\82Ì\8c \8cÀ\82ð\95Ï\8dX"
+
+#: scripts/create_serialized_styles.php:63 config/prefs.php.dist:115
+msgid "Shadowed Thumbnails on White Background"
+msgstr "\94\92\92n\94w\8ci\82Ì\8d\95\83T\83\80\83l\83C\83\8b"
+
+#: config/prefs.php.dist:81
+msgid "Show EXIF data"
+msgstr "EXIF\83f\81[\83^\95\\8e¦"
+
+#: lib/Exif.php:339
+msgid "Shutter Speed"
+msgstr "\83V\83\83\83b\83^\81[\91¬\93x"
+
+#: templates/view/image.inc:122
+msgid "Similar Photos"
+msgstr "\8e\97\82½\8eÊ\90^"
+
+#: lib/Forms/Upload.php:31
+msgid "Single Photo"
+msgstr "\83V\83\93\83O\83\8b\8eÊ\90^"
+
+#: image.php:63
+msgid "Small"
+msgstr "\8f¬"
+
+#: lib/Views/Gallery.php:258
+msgid "Sort images"
+msgstr "\8eÊ\90^\95À\82×\91Ö\82¦"
+
+#: lib/Exif.php:230
+msgid "Spot"
+msgstr "\83X\83|\83b\83g"
+
+#: templates/view/image.inc:78 templates/view/slideshow.inc:42
+msgid "Start Slideshow"
+msgstr "\83X\83\89\83C\83h\83V\83\87\81[\8aJ\8en"
+
+#: scripts/ansel.php:209 scripts/ansel.php:292
+#: scripts/recursive_import.php:156
+#, php-format
+msgid "Storing photo \"%s\"..."
+msgstr "\8eÊ\90^ \"%s\" ... \82ð\95À\82×\91Ö\82¦"
+
+#: templates/gallery/gallery.inc:31
+msgid "Style for this gallery"
+msgstr "\82±\82Ì\83M\83\83\83\89\83\8a\82Ì\83X\83^\83C\83\8b"
+
+#: templates/view/gallery.inc:199
+msgid "Subscribe"
+msgstr "\8dw\93Ç"
+
+#: scripts/ansel.php:232 scripts/recursive_import.php:166
+#, php-format
+msgid "Successfully added %d photo (%s) to gallery \"%s\" from \"%s\"."
+msgid_plural "Successfully added %d photos (%s) to gallery \"%s\" from \"%s\"."
+msgstr[0] ""
+"%d\96\87\82Ì\8eÊ\90^(%s)\82ð\83M\83\83\83\89\83\8a \"%s\" \82Ö \"%s\"\82©\82ç\92Ç\89Á\82·\82é\82±\82Æ\82É\90¬\8c÷\82µ\82Ü\82µ\82½\81B"
+msgstr[1] ""
+"%d\96\87\82Ì\8eÊ\90^(%s)\82ð\83M\83\83\83\89\83\8a \"%s\" \82Ö \"%s\"\82©\82ç\92Ç\89Á\82·\82é\82±\82Æ\82É\90¬\8c÷\82µ\82Ü\82µ\82½\81B"
+
+#: scripts/ansel.php:218
+#, php-format
+msgid "Successfully added photo \"%s\" to gallery \"%s\"."
+msgstr "\8eÊ\90^\"%s\"\82Ì\83M\83\83\83\89\83\8a\"%s\"\82Ö\82Ì\92Ç\89Á\82É\90¬\8c÷\82µ\82Ü\82µ\82½\81B"
+
+#: gallery/delete.php:42
+#, php-format
+msgid "Successfully deleted %s."
+msgstr "%s\82Ì\8dí\8f\9c\82É\90¬\8c÷\82µ\82Ü\82µ\82½\81B"
+
+#: lib/Exif.php:241
+msgid "Sunny"
+msgstr "\90°\82ê"
+
+#
+#: lib/Block/cloud.php:3 lib/Block/cloud.php:28
+msgid "Tag Cloud"
+msgstr "\83^\83O\83N\83\89\83E\83h"
+
+#: image.php:39 lib/Forms/Upload.php:39 templates/view/gallery.inc:184
+#: templates/view/image.inc:108
+msgid "Tags"
+msgstr "\83^\83O"
+
+#: scripts/ansel.php:259
+#, php-format
+msgid "The directory \"%s\" doesn't exist."
+msgstr "\83f\83B\83\8c\83N\83g\83\8a\"%s\"\82Í\91\8dÝ\82µ\82Ü\82¹\82ñ\81B"
+
+#: scripts/ansel.php:237
+#, php-format
+msgid "The directory \"%s\" had no valid photos."
+msgstr "\83f\83B\83\8c\83N\83g\83\8a\"%s\"\82É\82Í\81A\90³\82µ\82¢\8eÊ\90^\82ª\82 \82è\82Ü\82¹\82ñ\81B"
+
+#: scripts/ansel.php:276
+#, php-format
+msgid "The directory \"%s\" is empty."
+msgstr "\83f\83B\83\8c\83N\83g\83\8a\"%s\"\82Í\8bó\82Å\82·\81B"
+
+#: lib/Ansel.php:287
+#, php-format
+msgid "The file \"%s\" doesn't exist."
+msgstr "\83t\83@\83C\83\8b\"%s\"\82Í\91\8dÝ\82µ\82Ü\82¹\82ñ\81B"
+
+#: lib/api.php:249 img/upload.php:160
+msgid "The file you uploaded does not appear to be a valid photo."
+msgstr "\83A\83b\83v\83\8d\81[\83h\82³\82ê\82½\83t\83@\83C\83\8b\82Í\81A\90³\82µ\82¢\8eÊ\90^\83f\81[\83^\82É\8c©\82¦\82Ü\82¹\82ñ\81B"
+
+#: scripts/recursive_import.php:125
+#, php-format
+msgid "The gallery \"%s\" (%s) was created successfully."
+msgstr "\83M\83\83\83\89\83\8a \"%s\" (%s)\82Í\90³\8fí\82É\90\90¬\82³\82ê\82Ü\82µ\82½\81B"
+
+#: lib/Ansel.php:136
+#, php-format
+msgid "The gallery \"%s\" could not be created: %s"
+msgstr "\83M\83\83\83\89\83\8a\"%s\"\82Í\90\90¬\8fo\97\88\82Ü\82¹\82ñ\82Å\82µ\82½: %s"
+
+#: xppublish.php:129 gallery.php:214 scripts/ansel.php:153
+#: scripts/recursive_import.php:123
+#, php-format
+msgid "The gallery \"%s\" couldn't be created: %s"
+msgstr "\83M\83\83\83\89\83\8a\"%s\"\82Í\90\90¬\8fo\97\88\82Ü\82¹\82ñ\82Å\82µ\82½: %s"
+
+#: xppublish.php:133 gallery.php:220 scripts/ansel.php:158
+#, php-format
+msgid "The gallery \"%s\" was created successfully."
+msgstr "\83M\83\83\83\89\83\8a\"%s\"\82Í\90\90¬\82³\82ê\82Ü\82µ\82½\81B"
+
+#: gallery.php:174
+msgid "The gallery was saved."
+msgstr "\83M\83\83\83\89\83\8a\82Í\95Û\91\82³\82ê\82Ü\82µ\82½\81B"
+
+#: gallery.php:273
+msgid "The gallery's default photo has successfully been reset."
+msgstr "\83M\83\83\83\89\83\8a\82Ì\83f\83t\83H\83\8b\83g\8eÊ\90^\82Í\83\8a\83Z\83b\83g\82³\82ê\82Ü\82µ\82½\81B"
+
+#: img/upload.php:184
+msgid ""
+"The uploaded file appears to be empty. It may not exist on your computer."
+msgstr ""
+"\83A\83b\83v\83\8d\81[\83h\82³\82ê\82½\83t\83@\83C\83\8b\82Í\8bó\82Å\82·\81B\82 \82È\82½\82Ì\83R\83\93\83s\83\85\81[\83^\8fã\82É\91\8dÝ\82µ\82È\82¢\82Ì\82Å"
+"\82µ\82å\82¤\81B"
+
+#: list.php:74 lib/Block/random_gallery.php:34 lib/Block/random_photo.php:35
+msgid "There are no photo galleries available."
+msgstr "\97\98\97p\82Å\82«\82é\8eÊ\90^\83M\83\83\83\89\83\8a\82ª\82 \82è\82Ü\82¹\82ñ\81B"
+
+#: gallery.php:81
+#, php-format
+msgid "There are no photos in %s to download."
+msgstr "%s\82É\82Í\83_\83E\83\93\83\8d\81[\83h\82Å\82«\82é\8eÊ\90^\82Í\82 \82è\82Ü\82¹\82ñ\81B"
+
+#: templates/captions/captions.inc:49 templates/view/gallery.inc:159
+#: templates/view/gallerysimple.inc:7 templates/view/results.inc:115
+msgid "There are no photos in this gallery."
+msgstr "\82±\82Ì\83M\83\83\83\89\83\8a\82É\82Í\8eÊ\90^\82ª\82 \82è\82Ü\82¹\82ñ\81B"
+
+#: scripts/ansel.php:295
+#, php-format
+msgid "There was a problem adding the photo \"%s\" to gallery \"%s\"."
+msgstr "\8eÊ\90^\"%s\"\82Ì\83M\83\83\83\89\83\8a\"%s\"\82Ö\82Ì\92Ç\89Á\82Å\96â\91è\82ª\94­\90\82µ\82Ü\82µ\82½\81B"
+
+#: scripts/ansel.php:212
+#, php-format
+msgid "There was a problem adding the photo \"%s\" to gallery \"%s\": %s"
+msgstr "\8eÊ\90^\"%s\"\82Ì\83M\83\83\83\89\83\8a\"%s\"\82Ö\82Ì\92Ç\89Á\82Å\96â\91è\82ª\94­\90\82µ\82Ü\82µ\82½: %s"
+
+#: lib/api.php:406
+#, php-format
+msgid "There was a problem deleting %s: %s"
+msgstr "%s \82ð\8dí\8f\9c\92\86\82É\96â\91è\82ª\82 \82è\82Ü\82µ\82½: %s"
+
+#: gallery/delete.php:40
+#, php-format
+msgid "There was a problem deleting %s: %s."
+msgstr "%s \82ð\8dí\8f\9c\92\86\82É\96â\91è\82ª\82 \82è\82Ü\82µ\82½: %s\81B"
+
+#: image.php:447 lib/Views/Results.php:125
+#, php-format
+msgid "There was a problem deleting photos: %s"
+msgstr "\8eÊ\90^\82ð\8dí\8f\9c\92\86\82É\96â\91è\82ª\82 \82è\82Ü\82µ\82½: %s"
+
+#: img/upload.php:94 img/upload.php:146 img/upload.php:170
+#, php-format
+msgid "There was a problem saving the photo: %s"
+msgstr "\8eÊ\90^\82ð\95Û\91\92\86\82É\96â\91è\82ª\82 \82è\82Ü\82µ\82½: %s"
+
+#: xppublish.php:199
+msgid "There was a problem uploading the photo."
+msgstr "\8eÊ\90^\82ð\83A\83b\83v\83\8d\81[\83h\92\86\82É\96â\91è\82ª\82 \82è\82Ü\82µ\82½\81B"
+
+#: img/upload.php:182
+#, php-format
+msgid "There was a problem uploading the photo: %s"
+msgstr "\8eÊ\90^\82ð\83A\83b\83v\83\8d\81[\83h\92\86\82É\96â\91è\82ª\82 \82è\82Ü\82µ\82½: %s"
+
+#: img/upload.php:57 img/upload.php:79 img/upload.php:110 img/upload.php:131
+#, php-format
+msgid "There was an error processing the uploaded archive: %s"
+msgstr "\83A\83b\83v\83\8d\81[\83h\82³\82ê\82½\83A\81[\83J\83C\83u\82Ì\8f\88\97\9d\92\86\82É\96â\91è\82ª\82 \82è\82Ü\82µ\82½: %s"
+
+#: lib/Ansel.php:3245
+msgid "There was an error retrieving the galleries."
+msgstr "\83M\83\83\83\89\83\8a\82ð\8eó\90M\92\86\82É\83G\83\89\81[\82ª\94­\90\82µ\82Ü\82µ\82½\81B"
+
+#: img/ecard.php:96
+#, php-format
+msgid "There was an error sending your message: %s"
+msgstr "\83\81\81[\83\8b\82Ì\91\97\90M\92\86\82É\83G\83\89\81[\82ª\94­\90\82µ\82Ü\82µ\82½: %s"
+
+#: lib/ImageView.php:77
+#, php-format
+msgid ""
+"This install does not support the %s feature. Please contact your "
+"administrator."
+msgstr ""
+"\96{\83V\83X\83e\83\80\82Å\82Í%s\8b@\94\\82Í\83T\83|\81[\83g\82³\82ê\82Ä\82¢\82Ü\82¹\82ñ\81B\8aÇ\97\9d\8eÒ\82É\96â\82¢\8d\87\82í\82¹\82Ä\89º\82³\82¢\81B"
+
+#: lib/Exif.php:292
+msgid "Three Chip Color Area Sensor"
+msgstr "\82R\94Â\83J\83\89\81[\83Z\83\93\83T"
+
+#: image.php:63
+msgid "Tiny"
+msgstr "\8aÈ\88Õ"
+
+#: image.php:71
+msgid "Top"
+msgstr "\8fã"
+
+#: lib/Exif.php:294
+msgid "Trilinear Sensor"
+msgstr "\82R\90ü\83\8a\83j\83A\83Z\83\93\83T"
+
+#: lib/Exif.php:243
+msgid "Tungsten"
+msgstr "\83^\83\93\83O\83X\83e\83\93\93\94"
+
+#
+#: lib/Exif.php:122
+msgid "Tv-priority"
+msgstr "Tv-\97D\90æ"
+
+#: lib/Exif.php:291
+msgid "Two Chip Color Area Sensor"
+msgstr "2\94Â\83J\83\89\81[\83Z\83\93\83T"
+
+#: lib/Ansel.php:146
+msgid "Unable to create gallery."
+msgstr "\83M\83\83\83\89\83\8a\82ð\90\90¬\82Å\82«\82Ü\82¹\82ñ\81B"
+
+#: lib/ImageView.php:82
+#, php-format
+msgid "Unable to load the definition of %s."
+msgstr "%s \82Ì\92è\8b`\82ª\83\8d\81[\83h\82Å\82«\82Ü\82¹\82ñ\81B"
+
+#: rss.php:38
+msgid "Unable to retrieve requested feed"
+msgstr "\97v\8b\81\82³\82ê\82½\83t\83B\81[\83h\82ð\8eó\90M\82Å\82«\82Ü\82¹\82ñ"
+
+#: lib/Exif.php:305
+msgid "Uncalibrated"
+msgstr "\96¢\83L\83\83\83\8a\83u\83\8c\81[\83g"
+
+#
+#: templates/gallery/gallery.inc:60
+msgid "Undo Changes"
+msgstr "\95Ï\8dX\82ð\83L\83\83\83\93\83Z\83\8b"
+
+#: list.php:54 templates/group/category.inc:11
+msgid "Unfiled"
+msgstr "\96¢\90®\97\9d"
+
+#: lib/Exif.php:227
+msgid "Unknown"
+msgstr "\95s\96¾"
+
+#: gallery.php:122
+msgid "Unknown gallery"
+msgstr "\92m\82ç\82È\82¢\83M\83\83\83\89\83\8a"
+
+#
+#: lib/Ansel.php:111 lib/tests/GalleryTest.php:42
+msgid "Unnamed"
+msgstr "\96¼\91O\82È\82µ"
+
+#: xppublish.php:141
+msgid "Untitled"
+msgstr "\91è\96¼\82È\82µ"
+
+#: perms.php:238
+#, php-format
+msgid "Updated %s."
+msgstr "%s \82ð\8dX\90V\81B"
+
+#: lib/Forms/Upload.php:27
+msgid "Upload"
+msgstr "\83A\83b\83v\83\8d\81[\83h"
+
+#: templates/xppublish/javascript.inc:63
+#, php-format
+msgid "Upload Photos to %s"
+msgstr "%s\82Ö\8eÊ\90^\82ð\83A\83b\83v\83\8d\81[\83h"
+
+#: img/upload.php:30
+msgid "Upload new photos"
+msgstr "\90V\8bK\8eÊ\90^\82ð\83A\83b\83v\83\8d\81[\83h"
+
+#
+#: lib/Views/Gallery.php:254
+msgid "Upload photos"
+msgstr "\8eÊ\90^\83A\83b\83v\83\8d\81[\83h"
+
+#: lib/Exif.php:151
+#, php-format
+msgid "Upsidedown"
+msgstr "\8fã\89º\94½\93]"
+
+#: lib/Exif.php:153
+#, php-format
+msgid "Upsidedown Mirrored"
+msgstr "\8fã\89º\8d\89E\94½\93]"
+
+#: scripts/ansel.php:315 scripts/recursive_import.php:185
+#, php-format
+msgid "Usage: %s [OPTIONS]..."
+msgstr "\8eg\97p\96@: %s [OPTIONS]..."
+
+#: templates/xppublish/new.inc:17
+msgid "Use Default"
+msgstr "\83f\83t\83H\83\8b\83g\82ð\8eg\97p"
+
+#: lib/Forms/Ecard.php:32
+msgid "Use the following return address:"
+msgstr "\88È\89º\82Ì\95Ô\90M\83A\83h\83\8c\83X\82ð\8eg\97p:"
+
+#
+#: lib/Exif.php:350
+msgid "User Comment"
+msgstr "\83\86\81[\83U\83R\83\81\83\93\83g"
+
+#: templates/xppublish/login.inc:11
+msgid "Username"
+msgstr "\83\86\81[\83U\96¼"
+
+#: xppublish.php:50
+msgid "Username and Password are incorrect."
+msgstr "\83\86\81[\83U\96¼\82Æ\83p\83X\83\8f\81[\83h\82ª\95s\90³\82Å\82·\81B"
+
+#: scripts/recursive_import.php:79
+msgid "Username or password is incorrect."
+msgstr "\83\86\81[\83U\96¼\82©\83p\83X\83\8f\81[\83h\82ª\95s\90³\82Å\82·\81B"
+
+#: image.php:72
+msgid "Vertical Alignment"
+msgstr "\90\82\92¼\88Ê\92u\8d\87\82í\82¹"
+
+#: lib/Block/random_gallery.php:38 lib/Block/gallery.php:73
+#, php-format
+msgid "View %s"
+msgstr "%s \82ð\95\\8e¦"
+
+#: templates/view/results.inc:81
+msgid "View All Results"
+msgstr "\82·\82×\82Ä\82Ì\8c\8b\89Ê\82ð\95\\8e¦"
+
+#
+#: lib/Block/random_photo.php:48
+msgid "View Photo"
+msgstr "\8eÊ\90^\82ð\95\\8e¦"
+
+#: templates/view/results.inc:81
+msgid "View Results from All Users"
+msgstr "\91S\83\86\81[\83U\82©\82ç\82Ì\8c\8b\89Ê\82ð\95\\8e¦"
+
+#: config/prefs.php.dist:37
+msgid "View to display by default"
+msgstr "\83f\83t\83H\83\8b\83g\95\\8e¦"
+
+#: image.php:329 image.php:335 templates/image/edit_image.inc:32
+msgid "Watermark"
+msgstr "\93d\8eq\93§\82©\82µ"
+
+#: image.php:64
+msgid "Watermark Font"
+msgstr "\93d\8eq\93§\82©\82µ\97p\83t\83H\83\93\83g"
+
+#: lib/Exif.php:353
+msgid "White Balance"
+msgstr "\83z\83\8f\83C\83g\83o\83\89\83\93\83X"
+
+#: lib/Exif.php:335
+msgid "Width"
+msgstr "\95\9d"
+
+#: lib/Exif.php:337
+msgid "X Resolution"
+msgstr "X\89ð\91\9c\93x"
+
+#: lib/Exif.php:338
+msgid "Y Resolution"
+msgstr "Y\89ð\91\9c\93x"
+
+#: lib/Ansel.php:1960
+#, php-format
+msgid "You are not allowed to copy photos to %s."
+msgstr "%s\82Ö\8eÊ\90^\82ð\83R\83s\81[\82·\82é\8c \8cÀ\82ª\82 \82è\82Ü\82¹\82ñ\81B"
+
+#: image.php:261
+msgid "You are not allowed to edit this photo."
+msgstr "\82±\82Ì\8eÊ\90^\82ð\95Ò\8fW\82·\82é\8c \8cÀ\82ª\82 \82è\82Ü\82¹\82ñ\81B"
+
+#: lib/Ansel.php:1922
+#, php-format
+msgid "You are not allowed to move photos to %s."
+msgstr "%s\82Ö\8eÊ\90^\82ð\88Ú\93®\82·\82é\8c \8cÀ\82ª\82 \82è\82Ü\82¹\82ñ\81B"
+
+#: lib/Ansel.php:1924
+#, php-format
+msgid "You are not allowed to remove photos from %s."
+msgstr "%s\82©\82ç\8eÊ\90^\82ð\8dí\8f\9c\82·\82é\8c \8cÀ\82ª\82 \82è\82Ü\82¹\82ñ\81B"
+
+#: xppublish.php:91 xppublish.php:184
+msgid "You cannot add photos in that gallery."
+msgstr "\82±\82Ì\83M\83\83\83\89\83\8a\82É\8eÊ\90^\82ð\92Ç\89Á\82·\82é\8c \8cÀ\82ª\82 \82è\82Ü\82¹\82ñ\81B"
+
+#: scripts/ansel.php:132
+#, php-format
+msgid "You cannot add photos to the gallery \"%s\"."
+msgstr "\83M\83\83\83\89\83\8a\"%s\"\82É\8eÊ\90^\82ð\92Ç\89Á\82·\82é\8c \8cÀ\82ª\82 \82è\82Ü\82¹\82ñ\81B"
+
+#: gallery/delete.php:30
+#, php-format
+msgid "You cannot delete %s, it contains nested galleries."
+msgstr "\8eq\83M\83\83\83\89\83\8a\82ª\91\8dÝ\82·\82é\82½\82ß\81A%s\82ð\8dí\8f\9c\82Å\82«\82Ü\82¹\82ñ\81B"
+
+#: lib/Views/Abstract.php:29
+msgid "You do not have permission to access this gallery."
+msgstr "\82±\82Ì\83M\83\83\83\89\83\8a\82É\83A\83N\83Z\83X\82·\82é\8c \8cÀ\82ª\82 \82è\82Ü\82¹\82ñ\81B"
+
+#: gallery.php:49 gallery.php:186
+#, php-format
+msgid "You do not have permission to add children to %s."
+msgstr "%s\82É\8eq\82ð\92Ç\89Á\82·\82é\8c \8cÀ\82ª\82 \82è\82Ü\82¹\82ñ\81B"
+
+#: lib/Ansel.php:2477
+msgid "You do not have permission to add tags to this gallery"
+msgstr "\82±\82Ì\83M\83\83\83\89\83\8a\82É\83^\83O\82ð\82Â\82¯\82é\8c \8cÀ\82ª\82 \82è\82Ü\82¹\82ñ\81B"
+
+#: gallery/sort.php:26
+msgid "You do not have permission to change this gallery."
+msgstr "\82±\82Ì\83M\83\83\83\89\83\8a\82ð\95Ï\8dX\82·\82é\8c \8cÀ\82ª\82 \82è\82Ü\82¹\82ñ\81B"
+
+#: lib/Ansel.php:2462
+msgid "You do not have permission to view this gallery"
+msgstr "\82±\82Ì\83M\83\83\83\89\83\8a\82ð\89{\97\97\82·\82é\8c \8cÀ\82ª\82 \82è\82Ü\82¹\82ñ\81B"
+
+#: lib/api.php:842 lib/Block/gallery.php:104 lib/Block/recent_comments.php:141
+#: lib/Block/recently_added.php:154
+msgid "You do not have permission to view this gallery."
+msgstr "\82±\82Ì\83M\83\83\83\89\83\8a\82ð\89{\97\97\82·\82é\8c \8cÀ\82ª\82 \82è\82Ü\82¹\82ñ\81B"
+
+#: lib/Ansel.php:3116
+msgid "You do not have permissions to add tags to this photo."
+msgstr "\82±\82Ì\8eÊ\90^\82É\83^\83O\82ð\92Ç\89Á\82·\82é\8c \8cÀ\82ª\82 \82è\82Ü\82¹\82ñ\81B"
+
+#: lib/Ansel.php:3095
+msgid "You do not have permissions to view this photo."
+msgstr "\82±\82Ì\8eÊ\90^\82ð\8c©\82é\8c \8cÀ\82ª\82 \82è\82Ü\82¹\82ñ\81B"
+
+#: img/ecard.php:69
+msgid ""
+"You have been sent an Ecard. To view the Ecard, you must be able to view "
+"text/html messages in your mail reader. If you are viewing this message, "
+"then most likely your mail reader does not support viewing text/html "
+"messages."
+msgstr ""
+"\82 \82È\82½\82Í\81AE\83J\81[\83h\82ð\8eó\82¯\8eæ\82è\82Ü\82µ\82½\81B\82±\82ê\82ð\8c©\82é\82É\82Í\81A\82¨\8eg\82¢\82Ì\83\81\81[\83\89\82Å\81AHTML\83\81\81["
+"\83\8b\82Ì\95\\8e¦\82ª\83T\83|\81[\83g\82³\82ê\82Ä\82¢\82é\95K\97v\82ª\82 \82è\82Ü\82·\81B\82à\82µ\81A\8d¡\82±\82Ì\83\81\83b\83Z\81[\83W\82ð\93Ç\82Ü\82ê\82Ä"
+"\82¢\82é\82È\82ç\82Î\81A\82¨\8eg\82¢\82Ì\83\81\81[\83\89\82ÍHTML\83\81\81[\83\8b\82ð\83T\83|\81[\83g\82µ\82Ä\82¢\82È\82¢\82Æ\8dl\82¦\82ç\82ê\82Ü\82·\81B"
+
+#
+#: list.php:70
+msgid "You have no photo galleries, add one!"
+msgstr "\97\98\97p\82Å\82«\82é\8eÊ\90^\83M\83\83\83\89\83\8a\82ª\82 \82è\82Ü\82¹\82ñ\81B"
+
+#: templates/view/gallery.inc:46 templates/view/gallery.inc:60
+#: templates/view/results.inc:46 templates/view/results.inc:60
+msgid "You must choose a gallery to move photos to."
+msgstr "\8eÊ\90^\82ð\88Ú\93®\82·\82é\90æ\82Ì\83M\83\83\83\89\83\8a\82ð\91I\82ñ\82Å\82­\82¾\82³\82¢\81B"
+
+#: lib/Ansel.php:37 lib/Ansel.php:45
+msgid "You must configure a VFS backend to use Ansel."
+msgstr "VFS\83o\83b\83N\83G\83\93\83h\82ðAnsel\82ð\8eg\82¤\82æ\82¤\82É\90Ý\92è\82µ\82È\82¯\82ê\82Î\82È\82è\82Ü\82¹\82ñ\81B"
+
+#: img/ecard.php:45
+msgid "You must enter an e-mail address to send the message to."
+msgstr "\83\81\83b\83Z\81[\83W\82ð\91\97\90M\82·\82é\93d\8eq\83\81\81[\83\8b\83A\83h\83\8c\83X\82ð\93ü\97Í\82µ\82Ä\89º\82³\82¢\81B"
+
+#: img/ecard.php:40
+msgid "You must enter your e-mail address."
+msgstr "\82 \82È\82½\82Ì\93d\8eq\83\81\81[\83\8b\83A\83h\83\8c\83X\82ð\93ü\97Í\82µ\82Ä\82­\82¾\82³\82¢\81B"
+
+#: gallery.php:195
+msgid "You must provide a display name for your new gallery."
+msgstr "\90V\8bK\83M\83\83\83\89\83\8a\82Ì\95\\8e¦\96¼\82ð\8ew\92è\82µ\82È\82¯\82ê\82Î\82È\82è\82Ü\82¹\82ñ\81B"
+
+#: scripts/recursive_import.php:88
+msgid "You must specify a valid directory."
+msgstr "\90³\82µ\82¢\83f\83B\83\8c\83N\83g\83\8a\82ð\8ew\92è\82µ\82È\82¯\82ê\82Î\82È\82è\82Ü\82¹\82ñ\81B"
+
+#: scripts/recursive_import.php:84
+msgid "You must specify a valid username and password."
+msgstr "\90³\82µ\82¢\83\86\81[\83U\96¼\82Æ\83p\83X\83\8f\81[\83h\82ð\8ew\92è\82µ\82È\82¯\82ê\82Î\82È\82è\82Ü\82¹\82ñ\81B"
+
+#: lib/Forms/Upload.php:50
+msgid "Zip File Upload"
+msgstr "Zip\83t\83@\83C\83\8b\83A\83b\83v\83\8d\81[\83h"
+
+#
+#: lib/Ansel.php:355
+msgid "_Galleries"
+msgstr "_G\83M\83\83\83\89\83\8a"
+
+#
+#: lib/Ansel.php:366
+msgid "_My Galleries"
+msgstr "_M\8e\84\82Ì\83M\83\83\83\89\83\8a"
+
+#
+#: lib/Ansel.php:377
+msgid "_New Gallery"
+msgstr "_N\90V\8bK\83M\83\83\83\89\83\8a"
+
+#: lib/Ansel.php:386
+msgid "_Print"
+msgstr "_P\88ó\8dü"
+
+#: lib/Exif.php:302
+msgid "sRGB"
+msgstr "sRGB"
diff --git a/ansel/po/lt_LT.po b/ansel/po/lt_LT.po
new file mode 100644 (file)
index 0000000..f4bf4e9
--- /dev/null
@@ -0,0 +1,1701 @@
+# Lithuanian translations for Ansel package.
+# Copyright 2007-2009 The Horde Project
+# This file is distributed under the same license as the Ansel package.
+# Vilius Sumskas <vilius@lnk.lt>, 2004, 2007.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Ansel 1.0-cvs\n"
+"Report-Msgid-Bugs-To: dev@lists.horde.org\n"
+"POT-Creation-Date: 2007-11-23 15:19+0200\n"
+"PO-Revision-Date: 2007-11-23 15:28+0200\n"
+"Last-Translator: Vilius Sumskas <vilius@lnk.lt>\n"
+"Language-Team: Lithuanian <vilius@lnk.lt>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-13\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%"
+"100<10 || n%100>=20) ? 1 : 2);\n"
+
+#: gallery.php:182
+#, php-format
+msgid ""
+"\"%s\" is an invalid unique name; it may already be taken. Please choose a "
+"different unique name."
+msgstr "\"%s\" nëra unikalus pavadinimas. Pasirinkite kità."
+
+#: templates/group/owner.inc:55 templates/group/category.inc:51
+#, php-format
+msgid "%d Galleries"
+msgstr "%d galerijos"
+
+#: lib/Exif.php:285
+#, php-format
+msgid "%d bytes"
+msgstr "%d baitai(ø)"
+
+#: templates/tile/image.inc:10
+#, php-format
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] "%d komentaras"
+msgstr[1] "%d komentarai"
+msgstr[2] "%d komentarø"
+
+#: templates/list/header.inc:4
+#, php-format
+msgid "%d galleries"
+msgstr "%d galerijos"
+
+#: xrequest.php:24 lib/Views/Results.php:225 lib/Views/Gallery.php:100
+#, php-format
+msgid "%d image"
+msgid_plural "%d images"
+msgstr[0] "%d paveikslëlis"
+msgstr[1] "%d paveikslëlis"
+msgstr[2] "%d paveikslëliø"
+
+#: img/upload.php:136
+#, php-format
+msgid "%d images were uploaded."
+msgstr "Sëkmingai ákelti(a) %d paveikslëliai(iø)."
+
+#: lib/Exif.php:136 lib/Exif.php:142
+#, php-format
+msgid "%d pixels"
+msgstr "%d pikseliai(iø)"
+
+#: lib/Exif.php:166 lib/Exif.php:181
+#, php-format
+msgid "%d sec"
+msgstr "%d sek"
+
+#: templates/list/header.inc:4
+#, php-format
+msgid "%d to %d of %d galleries"
+msgstr "nuo %d iki %d ið %d galerijø"
+
+#: gallery/sort.php:48
+#, php-format
+msgid "%s :: Sort"
+msgstr "%s :: Rûðiuoti"
+
+#: rss.php:95
+#, php-format
+msgid "%s from %s"
+msgstr "%s ið %s"
+
+#: templates/view/gallery.inc:118 templates/view/results.inc:87
+#, php-format
+msgid "%s items"
+msgstr "%s objektai(ø)"
+
+#: list.php:48 lib/Ansel.php:501 templates/group/owner.inc:16
+#, php-format
+msgid "%s's Galleries"
+msgstr "%s galerijos"
+
+#: lib/Ansel.php:2733
+#, php-format
+msgid "(c) %s %s"
+msgstr "(c) %s %s"
+
+#: templates/xppublish/new.inc:14
+msgid "(must be unique)"
+msgstr "(turi bûti unikalus)"
+
+#: templates/group/owner.inc:55 templates/group/category.inc:51
+msgid "1 Gallery"
+msgstr "1 galerija"
+
+#: img/upload.php:134
+msgid "1 image was uploaded."
+msgstr "Sëkmingai ákeltas 1 paveikslëlis."
+
+#: lib/Exif.php:157
+#, php-format
+msgid "90 deg CCW"
+msgstr "90 laipsn. CCW"
+
+#: lib/Exif.php:159
+#, php-format
+msgid "90 deg CCW Mirrored"
+msgstr "90 laipsn. CCW veidrodinis"
+
+#: lib/Exif.php:161
+#, php-format
+msgid "90 deg CW"
+msgstr "90 laipsn. CW"
+
+#: lib/Exif.php:155
+#, php-format
+msgid "90 deg CW Mirrored"
+msgstr "90 laipsn. CW veidrodinis"
+
+#: lib/api.php:227
+msgid "A gallery to add this image to is required."
+msgstr "Nuotraukos ákëlimui reikia galerijos."
+
+#: lib/Exif.php:128
+msgid "A-DEP"
+msgstr "A-DEP"
+
+#: lib/api.php:348 gallery/delete.php:28 gallery/delete.php:54
+#, php-format
+msgid "Access denied deleting gallery %s."
+msgstr "Neturite teisiø iðtrinti galerijos %s."
+
+#: image.php:401 lib/Views/Results.php:91
+#, php-format
+msgid "Access denied deleting image %s."
+msgstr "Neturite teisiø iðtrinti paveikslëlio %s."
+
+#: gallery.php:69
+#, php-format
+msgid "Access denied downloading %s."
+msgstr "Neturite teisiø atsisiøsti %s."
+
+#: gallery.php:124
+msgid "Access denied editing galleries."
+msgstr "Neturite teisiø iðtrinti %s."
+
+#: gallery.php:142
+#, php-format
+msgid "Access denied saving gallery %s."
+msgstr "Neturite teisiø iðsaugoti galerijos %s."
+
+#: image.php:198 image.php:264
+#, php-format
+msgid "Access denied saving image to %s."
+msgstr "Neturite teisiø iðsaugoti paveikslëlio á %s."
+
+#: gallery/captions.php:31
+#, php-format
+msgid "Access denied setting captions for %s."
+msgstr "Neturite teisiø áraðyti %s apraðymo."
+
+#: templates/view/image.inc:72
+msgid "Add"
+msgstr "Pridëti"
+
+#: browse.php:23
+msgid "Add Content"
+msgstr "Pridëtá turiná"
+
+#: img/upload.php:147
+msgid "Add Image"
+msgstr "Pridëti paveikslëlá"
+
+#: gallery.php:35 gallery.php:184 gallery.php:192
+msgid "Adding A New Gallery"
+msgstr "Naujos galerijos kûrimas"
+
+#: gallery.php:61
+#, php-format
+msgid "Adding A Subgallery to %s"
+msgstr "Naujos galerijos kûrimas %s"
+
+#: templates/view/gallery.inc:158 templates/view/results.inc:93
+msgid "All"
+msgstr "Viskà"
+
+#: lib/Block/recent_comments.php:60 lib/Block/recently_added.php:60
+msgid "All Galleries"
+msgstr "Visos galerijos"
+
+#: lib/Ansel.php:126
+#, php-format
+msgid "An error occurred counting galleries: %s"
+msgstr "Klaida skaièiuojant galerijas: %s"
+
+#: lib/Ansel.php:66
+#, php-format
+msgid "An error occurred listing galleries: %s"
+msgstr "Klaida rodant galerijas: %s"
+
+#: lib/Exif.php:343
+msgid "Aperture"
+msgstr "Anga"
+
+#: templates/view/gallery.inc:20 templates/view/results.inc:20
+msgid "Are you sure you want to delete the selected images?"
+msgstr "Ar tikrai norite iðtrinti pasirinktus paveikslëlius?"
+
+#: lib/Exif.php:356
+msgid "Artist"
+msgstr "Artistas"
+
+#: perms.php:52
+msgid "Attempt to edit a non-existent share."
+msgstr "Bandote redaguoti neegzistuojantá elementà."
+
+#: lib/Exif.php:124
+msgid "Av-priority"
+msgstr "Av-svarba"
+
+#: lib/Exif.php:228
+msgid "Average"
+msgstr "Vidutinis"
+
+#: image.php:431 image.php:464 lib/Views/Results.php:121
+#: lib/Views/Results.php:159
+msgid "Bad input."
+msgstr "Blogi duomenys."
+
+#: image.php:71
+msgid "Bottom"
+msgstr "Virðuje"
+
+#: lib/Ansel.php:408 config/prefs.php.dist:34
+msgid "Browse"
+msgstr "Perþiûrëti"
+
+#: lib/Ansel.php:509
+msgid "Browse Tags"
+msgstr "Perþiûrëti þymes"
+
+#: lib/Views/Results.php:58
+#, php-format
+msgid "Browsing %s's images"
+msgstr "Rodomi %s paveikslëliai"
+
+#: lib/Views/Results.php:58
+msgid "Browsing all images"
+msgstr "Rodomi visi paveikslëliai"
+
+#: lib/Exif.php:327
+msgid "Camera Make"
+msgstr "Fotoaparato gamintojas"
+
+#: lib/Exif.php:328
+msgid "Camera Model"
+msgstr "Fotoaparato modelis"
+
+#: lib/Exif.php:354
+msgid "Camera Orientation"
+msgstr "Fotoaparato orientacija"
+
+#: lib/Ansel.php:357
+#, php-format
+msgid "Can't get unknown file type \"%s\"."
+msgstr "Nepavyko atpaþinti neþinomo failo tipo \"%s\"."
+
+#: templates/gallery/delete_confirmation.inc:11
+msgid "Cancel"
+msgstr "Atðaukti"
+
+#: templates/image/preview_image.inc:28
+msgid "Cancel Change"
+msgstr "Atðaukti pakeitimus"
+
+#: image.php:38 lib/Forms/Upload.php:38
+msgid "Caption"
+msgstr "Apraðymas"
+
+#: gallery/captions.php:54
+msgid "Caption Editor"
+msgstr "Apraðymo redaktorius"
+
+#: gallery/captions.php:49
+msgid "Captions Saved."
+msgstr "Apraðymai iðsaugoti."
+
+#: config/prefs.php.dist:47
+msgid "Category"
+msgstr "Kategorija"
+
+#: image.php:67 image.php:71
+msgid "Center"
+msgstr "Centre"
+
+#: lib/Exif.php:229
+msgid "Center Weighted Average"
+msgstr "Centrinis pusiausvyros taðkas"
+
+#: templates/view/gallery.inc:129
+msgid "Change gallery properties"
+msgstr "Pakeisti galerijos nustatymus"
+
+#: config/prefs.php.dist:15
+msgid "Change your display options."
+msgstr "Vaizdavimo nustatymø keitimas."
+
+#: templates/view/gallery.inc:121
+msgid "Choose Action:"
+msgstr "Pasirinkite veiksmà:"
+
+#: lib/Exif.php:242
+msgid "Cloudy"
+msgstr "Debesuota"
+
+#: lib/Exif.php:293
+msgid "Color Sequential Area Sensor"
+msgstr "Spalvø ploto sensorius"
+
+#: lib/Exif.php:295
+msgid "Color Sequential Linear Sensor"
+msgstr "Spalvø linijinis sensorius"
+
+#: lib/Exif.php:351
+msgid "Color Space"
+msgstr "Spalvø gylis"
+
+#: lib/Forms/Ecard.php:42
+msgid "Comments:"
+msgstr "Komentarai:"
+
+#: lib/Exif.php:263
+msgid "Compulsory Flash"
+msgstr "Priverstinë blykstë"
+
+#: lib/Exif.php:265
+msgid "Compulsory Flash, Return light detected"
+msgstr "Priverstinë blykstë, aptikra graþinta ðviesa"
+
+#: lib/Exif.php:264
+msgid "Compulsory Flash, Return light not detected"
+msgstr "Priverstinë blykstë, graþinta ðviesa neaptikta"
+
+#: lib/Views/Results.php:173
+#, php-format
+msgid "Copied %d image from %s to %s"
+msgid_plural "Copied %d images from %s to %s"
+msgstr[0] "Nukopijuotas %d paveikslëlis ið %s á %s"
+msgstr[1] "Nukopijuoti %d paveikslëliai ið %s á %s"
+msgstr[2] "Nukopijuota %d paveikslëliø ið %s á %s"
+
+#: image.php:473
+#, php-format
+msgid "Copied %d images to %s"
+msgstr "Nukopijuoti(a) %d paveikslëliai(iø) á %s."
+
+#: image.php:471
+#, php-format
+msgid "Copied one image to %s"
+msgstr "Nukopijuotas vienas paveikslëlis á %s."
+
+#: templates/view/gallery.inc:169 templates/view/results.inc:102
+msgid "Copy"
+msgstr "Kopijuoti"
+
+#: lib/Exif.php:355
+msgid "Copyright"
+msgstr "Kopijavimo teisës"
+
+#: templates/xppublish/list.inc:19
+msgid "Create"
+msgstr "Sukurti"
+
+#: templates/xppublish/new.inc:8
+msgid "Create a new Gallery"
+msgstr "Sukurti naujà galerijà"
+
+#: templates/xppublish/list.inc:18
+msgid "Create a new gallery:"
+msgstr "Sukurti naujà galerijà:"
+
+#: templates/view/gallery.inc:128
+msgid "Create a subgallery"
+msgstr "Sukurti gilesnæ galerijà"
+
+#: lib/Tile/Gallery.php:104
+msgid "Created:"
+msgstr "Sukurta:"
+
+#: lib/Exif.php:246
+msgid "Custom"
+msgstr "Kitas"
+
+#: image.php:60
+msgid "Custom Watermark"
+msgstr "Vandens þenklas"
+
+#: config/prefs.php.dist:27
+msgid ""
+"Custom text to display describing your galleries. This will be displayed in "
+"place of your username when grouping galleries by username."
+msgstr ""
+"Bet koks trumpas tekstas apibûdinantis jûsø galerijas. Jis bus naudojamas "
+"vietoj jûsø vartotojo vardo grupuojant galerijas pagal vartotojø vardus."
+
+#: config/prefs.php.dist:90
+msgid "Custom watermark to use for images"
+msgstr "Vandens þenklas naudojamas paveikslëliuose"
+
+#: lib/Exif.php:334
+msgid "Date Image Digitized"
+msgstr "Paveikslëlio skaitmenizavimo data"
+
+#: lib/Exif.php:332
+msgid "Date Image Modified"
+msgstr "Paveikslëlio redagavimo data"
+
+#: lib/Exif.php:333
+msgid "Date Image Taken"
+msgstr "Paveikslëlio padarymo data"
+
+#: config/prefs.php.dist:113
+msgid "Default"
+msgstr "Standartinë reikðmë"
+
+#: config/prefs.php.dist:112
+msgid "Default style for galleries"
+msgstr "Standartinis galerijø stilius"
+
+#: templates/view/image.inc:53 templates/view/gallery.inc:163
+#: templates/view/results.inc:97
+msgid "Delete"
+msgstr "Iðtrinti"
+
+#: templates/view/gallery.inc:141
+msgid "Delete this gallery"
+msgstr "Iðtrinti ðià galerijà"
+
+#: image.php:407 lib/Views/Results.php:97
+msgid "Deleted the image."
+msgstr "Paveikslëlis iðtrintas."
+
+#: config/prefs.php.dist:14
+msgid "Display Options"
+msgstr "Vaizdavimo nustatymai"
+
+#: templates/group/header.inc:3
+#, php-format
+msgid "Displaying from %s to %s of %s groups"
+msgstr "Rodomos nuo %s iki %s ið %s grupiø"
+
+#: templates/view/gallery.inc:118 templates/view/results.inc:87
+#, php-format
+msgid "Displaying from %s to %s of %s items"
+msgstr "Rodomi nuo %s iki %s ið %s objektø"
+
+#: templates/view/image.inc:53
+#, php-format
+msgid "Do you want to permanently delete %s?"
+msgstr "Ar tikrai norite visiðkai iðtrinti %s?"
+
+#: gallery/sort.php:62
+msgid "Done"
+msgstr "Atlikta"
+
+#: templates/view/image.inc:61
+msgid "Download Full Image"
+msgstr "Atsisiøsti pilnà paveikslëlá"
+
+#: templates/view/gallery.inc:124
+msgid "Download as .zip"
+msgstr "Atsisiøsti .zip formatu"
+
+#: gallery/sort.php:61
+msgid "Drag images to the desired sort position."
+msgstr "Nutempkite paveikslëlius á norimà rûðiavimo vietà."
+
+#: xppublish.php:210
+#, php-format
+msgid "ERROR: %s"
+msgstr "KLAIDA: %s"
+
+#: lib/Exif.php:118
+msgid "Easy shooting"
+msgstr "Greitas fotografavimas"
+
+#: img/ecard.php:60
+msgid "Ecard - "
+msgstr "El. atvirutë - "
+
+#: templates/tile/image.inc:18 templates/view/image.inc:50
+msgid "Edit"
+msgstr "Redaguoti"
+
+#: image.php:248
+#, php-format
+msgid "Edit %s :: %s"
+msgstr "%s :: %s redagavimas"
+
+#: image.php:330
+msgid "Edit Image"
+msgstr "Paveikslëlio redagavimas"
+
+#: perms.php:243
+msgid "Edit Permissions"
+msgstr "Teisiø keitimas"
+
+#: perms.php:246
+#, php-format
+msgid "Edit Permissions for %s"
+msgstr "%s teisiø keitimas"
+
+#: image.php:209
+msgid "Edit an image"
+msgstr "Redaguoti paveikslëlá"
+
+#: image.php:169
+#, php-format
+msgid "Edit properties :: %s"
+msgstr "Nustatymø keitimas :: %s"
+
+#: templates/view/gallery.inc:140
+msgid "Empty this gallery"
+msgstr "Iðvalyti ðià galerijà"
+
+#: gallery/captions.php:25
+#, php-format
+msgid "Error accessing %s: %s"
+msgstr "Klaida rodant %s: %s"
+
+#: rss.php:36
+msgid "Error retrieving feed"
+msgstr "Klaida gaunant duomenis"
+
+#: templates/xppublish/list.inc:12
+msgid "Existing galleries:"
+msgstr "Egzistuojanèios galerijos:"
+
+#: lib/Exif.php:340
+msgid "Exposure"
+msgstr "Ekspozicija"
+
+#: lib/Exif.php:346
+msgid "Exposure Bias"
+msgstr "Ekspozicijos ástriþumas"
+
+#: lib/Exif.php:347
+msgid "Exposure Mode"
+msgstr "Ekspozicijos reþimas"
+
+#: lib/Exif.php:344
+msgid "F-Number"
+msgstr "F-numeris"
+
+#: lib/Forms/Upload.php:45
+#, php-format
+msgid "File %s"
+msgstr "Failas %s"
+
+#: lib/Exif.php:331
+msgid "File Size"
+msgstr "Failo dydis"
+
+#: lib/Forms/Upload.php:34 lib/Forms/Upload.php:53
+msgid "File to upload"
+msgstr "Siunèiamas failas"
+
+#: lib/Exif.php:245 lib/Exif.php:260
+msgid "Flash"
+msgstr "Blykstë"
+
+#: lib/Exif.php:349
+msgid "Flash Setting"
+msgstr "Blykstës nustatymai"
+
+#: lib/Exif.php:268
+msgid "Flash, Auto-Mode"
+msgstr "Blykstë, automatinis reþimas"
+
+#: lib/Exif.php:270
+msgid "Flash, Auto-Mode, Return light detected"
+msgstr "Blykstë, automatinis reþimas, aptikta graþinta ðviesa"
+
+#: lib/Exif.php:269
+msgid "Flash, Auto-Mode, Return light not detected"
+msgstr "Blykstë, automatinis reþimas, graþinta ðviesa neaptikta"
+
+#: lib/Exif.php:262
+msgid "Flash, strobe return light detected"
+msgstr "Blykstë, strobas, aptikta graþinta ðviesa"
+
+#: lib/Exif.php:261
+msgid "Flash, strobe return light not detected"
+msgstr "Blykstë strobas, graþinta ðviesa neaptikta"
+
+#: templates/image/edit_image.inc:22
+msgid "Flip"
+msgstr "Apversti"
+
+#: lib/Exif.php:244
+msgid "Fluorescent"
+msgstr "Fluorescensinis"
+
+#: lib/Exif.php:341
+msgid "Focal Length"
+msgstr "Þidinio ilgis"
+
+#: lib/Exif.php:342
+msgid "Focal Length (35mm equiv)"
+msgstr "Þidinio ilgis (35mm)"
+
+#: templates/tile/image.inc:4
+msgid "From: "
+msgstr "Nuo: "
+
+#: lib/Ansel.php:509 config/prefs.php.dist:35
+msgid "Galleries"
+msgstr "Galerijos"
+
+#: templates/group/header.inc:6
+#, php-format
+msgid "Galleries grouped by %s"
+msgstr "Galerijos sugrupuotos pagal %s"
+
+#: list.php:55 list.php:58
+#, php-format
+msgid "Galleries in category %s"
+msgstr "Galerijos kategorijoje %s"
+
+#: lib/Block/recent_comments.php:31 lib/Block/recent_comments.php:51
+#: lib/Block/gallery.php:3 lib/Block/gallery.php:32 lib/Block/gallery.php:48
+#: lib/Block/recently_added.php:26 lib/Block/recently_added.php:49
+msgid "Gallery"
+msgstr "Galerija"
+
+#: templates/gallery/gallery.inc:17
+msgid "Gallery Category"
+msgstr "Galerijos kategorija"
+
+#: templates/gallery/gallery.inc:39
+msgid "Gallery Description"
+msgstr "Galerijos apraðymas"
+
+#: templates/gallery/gallery.inc:32
+msgid "Gallery Display Name"
+msgstr "Rodomas galerijos pavadinimas"
+
+#: list.php:63 list.php:127
+msgid "Gallery List"
+msgstr "Galerijø sàraðas"
+
+#: lib/Block/my_galleries.php:46
+msgid "Gallery Name"
+msgstr "Galerijos pavadinimas"
+
+#: lib/Tile/Gallery.php:80
+msgid "Gallery Properties"
+msgstr "Galerijos nustatymai"
+
+#: templates/xppublish/new.inc:14
+msgid "Gallery Short Name"
+msgstr "Trumpas galerijos pavadinimas"
+
+#: templates/gallery/gallery.inc:45
+msgid "Gallery Tags"
+msgstr "Galerijos þymës"
+
+#: lib/Ansel.php:188
+#, php-format
+msgid "Gallery Unique name \"%s\" already exists."
+msgstr "Galerijos unikalus pavadinimas \"%s\" jau egzistuoja."
+
+#: xppublish.php:121
+msgid "Gallery Unique name is already taken."
+msgstr "Galerijos unikalus pavadinimas jau uþimtas."
+
+#: lib/Block/recent_comments.php:135 lib/Block/gallery.php:105
+#: lib/Block/recently_added.php:138
+msgid "Gallery does not exist."
+msgstr "Galerija neegzistuoja."
+
+#: lib/Ansel.php:780
+msgid "Gallery names must be non-empty"
+msgstr "Galerijos pavadinimas negali bûti tuðèias"
+
+#: image.php:336
+msgid "Gallery not found."
+msgstr "Galerija nerasta."
+
+#: gallery/sort.php:40
+msgid "Gallery sorted."
+msgstr "Galerija surûðiuota."
+
+#: image.php:63
+msgid "Giant"
+msgstr "Milþiniðkas"
+
+#: templates/image/edit_image.inc:28
+msgid "Grayscale"
+msgstr "Nespalvotas"
+
+#: config/prefs.php.dist:49
+msgid "Group galleries by"
+msgstr "Grupuoti galerijas pagal"
+
+#: lib/Exif.php:336
+msgid "Height"
+msgstr "Aukðtis"
+
+#: image.php:68
+msgid "Horizontal Alignment"
+msgstr "Horizontalus lygiavimas"
+
+#: lib/Exif.php:345
+msgid "ISO Setting"
+msgstr "ISO nustatymai"
+
+#: lib/Block/my_galleries.php:46
+msgid "Image Count"
+msgstr "Paveikslëliø skaièius"
+
+#: lib/Exif.php:330
+msgid "Image Description"
+msgstr "Paveikslëlio apraðymas"
+
+#: lib/Exif.php:329
+msgid "Image Type"
+msgstr "Paveikslëlio tipas"
+
+#: lib/Ansel.php:819 lib/Ansel.php:2043
+msgid "Image not found"
+msgstr "Paveikslëlis nerastas"
+
+#: rss.php:127
+#, php-format
+msgid "Images by %s"
+msgstr "%s paveikslëliai"
+
+#: lib/Ansel.php:847 lib/Ansel.php:2006
+msgid "Images not found"
+msgstr "Paveikslëliai nerasti"
+
+#: rss.php:149 rss.php:155
+#, php-format
+msgid "Images tagged with %s on %s"
+msgstr "Paveikslëliai paþymëti %s %s"
+
+#: lib/Ansel.php:2526
+msgid "Incomplete image"
+msgstr "Nepilnas paveikslëlis"
+
+#: lib/Ansel.php:1137 lib/Ansel.php:1171
+msgid "Invalid data"
+msgstr "Neteisingi duomenys"
+
+#: xppublish.php:87 xppublish.php:180
+msgid "Invalid gallery specified."
+msgstr "Nurodytos galerijos nëra."
+
+#: image.php:63
+msgid "Large"
+msgstr "Didelis"
+
+#: lib/Block/my_galleries.php:46
+msgid "Last Modified"
+msgstr "Paskutiná kartà redaguota"
+
+#: img/upload.php:153
+msgid "Learn how to publish images directly from Windows."
+msgstr "Iðmokite ákelti paveikslëlius tiesiai ið Windows."
+
+#: image.php:67
+msgid "Left"
+msgstr "Kairëje"
+
+#: templates/xppublish/login.inc:2
+#, php-format
+msgid "Login to %s"
+msgstr "Prisijungti prie %s"
+
+#: image.php:37 lib/Forms/Upload.php:37
+msgid "Make this the default image for this gallery?"
+msgstr "Padaryti ðità paveikslëlá pagrindiniu galerijoje?"
+
+#: lib/Exif.php:126 lib/Exif.php:247
+msgid "Manual"
+msgstr "Rankinis"
+
+#: lib/Block/recently_added.php:31
+msgid "Maximum number of images"
+msgstr "Maksimalus paveikslëliø skaièius"
+
+#: image.php:63
+msgid "Medium"
+msgstr "Vidutinis"
+
+#: lib/Exif.php:348
+msgid "Metering Mode"
+msgstr "Atskaitos reþimas"
+
+#: templates/image/edit_image.inc:25
+msgid "Mirror"
+msgstr "Atspindys"
+
+#: lib/Exif.php:149
+#, php-format
+msgid "Mirrored"
+msgstr "Veidrodinis"
+
+#: lib/Tile/Gallery.php:107
+msgid "Modified"
+msgstr "Keista"
+
+#: gallery.php:113
+#, php-format
+msgid "Modifying: %s"
+msgstr "Keitimas: %s"
+
+#: templates/view/gallery.inc:166 templates/view/results.inc:99
+msgid "Move"
+msgstr "Perkelti"
+
+#: lib/Views/Results.php:135
+#, php-format
+msgid "Moved %d image from %s to %s"
+msgid_plural "Moved %d images from %s to %s"
+msgstr[0] "Perkeltas %d paveikslëlis ið %s á %s"
+msgstr[1] "Perkelti %d paveikslëliai ið %s á %s"
+msgstr[2] "Perkelta %d paveikslëliø ið %s á %s"
+
+#: image.php:440
+#, php-format
+msgid "Moved %d images to %s"
+msgstr "Perkelta(i) %d paveikslëliai(iø) á %s."
+
+#: image.php:438
+#, php-format
+msgid "Moved one image to %s"
+msgstr "Perkeltas vienas paveikslëlis á %s."
+
+#: lib/Exif.php:232
+msgid "Multi-Segment"
+msgstr "Multisegmentinis"
+
+#: lib/Exif.php:231
+msgid "Multi-Spot"
+msgstr "Multitaðkinis"
+
+#: lib/Forms/Upload.php:41
+msgid "Multiple Images"
+msgstr "Keletas paveikslëliø"
+
+#: list.php:35 lib/Ansel.php:488 lib/Block/my_galleries.php:3
+#: lib/Block/my_galleries.php:28 templates/group/owner.inc:3
+#: config/prefs.php.dist:36
+msgid "My Galleries"
+msgstr "Mano galerijos"
+
+#: browse_edit.php:28
+msgid "My Photos :: Add Content"
+msgstr "Mano nuotraukos :: Pridëti turiná"
+
+#: templates/xppublish/new.inc:33
+msgid "New Gallery Description"
+msgstr "Naujas galerijos apraðymas"
+
+#: templates/xppublish/new.inc:24
+msgid "New Gallery Title"
+msgstr "Naujas galerijos pavadinimas"
+
+#: templates/view/image.inc:95 templates/view/image.inc:96
+msgid "Next"
+msgstr "Sekantis"
+
+#: lib/Exif.php:259 lib/Exif.php:266 lib/Exif.php:267 lib/Exif.php:271
+msgid "No Flash"
+msgstr "Blykstë iðjungta"
+
+#: xppublish.php:123
+msgid "No Gallery Name Specified."
+msgstr "Neáraðëte galerijos pavadinimo."
+
+#: xppublish.php:119
+msgid "No Gallery Unique Name Specified."
+msgstr "Neáraðëte unikalaus galerijos pavadinimo."
+
+#: xppublish.php:190
+msgid "No file specified"
+msgstr "Nenurodëte failo"
+
+#: lib/Views/Abstract.php:18
+msgid "No gallery specified."
+msgstr "Nenurodyta galerija."
+
+#: templates/view/gallery.inc:159 templates/view/results.inc:94
+#: config/prefs.php.dist:48
+msgid "None"
+msgstr "Nieko"
+
+#: lib/Exif.php:147
+#, php-format
+msgid "Normal (O deg)"
+msgstr "Normalus (0 laipsn.)"
+
+#: lib/Exif.php:289
+msgid "Not defined"
+msgstr "Neapraðyta"
+
+#: image.php:112 img/upload.php:22
+msgid "Not found or error accessing gallery."
+msgstr "Nerasta arba klaida rodant galerijà."
+
+#: config/prefs.php.dist:74
+msgid "Number of groups per page"
+msgstr "Grupiø skaièius puslapyje"
+
+#: config/prefs.php.dist:66
+msgid "Number of tiles per page"
+msgstr "Objektø skaièius puslapyje"
+
+#: config/prefs.php.dist:58
+msgid "Number of tiles per row"
+msgstr "Objektø skaièius eilutëje"
+
+#: lib/Exif.php:290
+msgid "One Chip Color Area Sensor"
+msgstr "Vienos mikroschemos spalvø sensorius"
+
+#: perms.php:64
+msgid ""
+"Only the owner or system administrator may change ownership or owner "
+"permissions for a share"
+msgstr ""
+"Elemento savininko teises gali keisti tik jo savininkas arba sistemos "
+"administratorius"
+
+#: lib/Exif.php:234
+msgid "Other"
+msgstr "Kita"
+
+#: config/prefs.php.dist:13
+msgid "Other Options"
+msgstr "Kiti nustatymai"
+
+#: templates/group/owner.inc:54
+#, php-format
+msgid "Owned by: %s <br />"
+msgstr "Savininkas: %s <br />"
+
+#: config/prefs.php.dist:46 templates/tile/gallery.html:12
+msgid "Owner"
+msgstr "Savininkas"
+
+#: lib/Ansel.php:1202
+msgid "Parent does not exist."
+msgstr "Nëra aukðtesnio lygio."
+
+#: lib/Exif.php:233
+msgid "Partial"
+msgstr "Dalinis"
+
+#: templates/xppublish/login.inc:14
+msgid "Password"
+msgstr "Slaptaþodis"
+
+#: templates/view/image.inc:92
+msgid "Pause"
+msgstr "Sustabdyti"
+
+#: templates/view/image.inc:92
+msgid "Pause Slideshow"
+msgstr "Sustabdyti skaidriø perþiûrà"
+
+#: browse.php:19
+msgid "Photo Galleries"
+msgstr "Fotogalerijos"
+
+#: templates/xppublish/javascript.inc:63
+msgid "Photo Upload"
+msgstr "Paveikslëlio ákëlimas"
+
+#: templates/view/image.inc:91
+msgid "Play"
+msgstr "Paleisti"
+
+#: xppublish.php:54
+msgid "Please enter your Username and Password."
+msgstr "Nurodykite savo vartotojo vardà bei slaptaþodá."
+
+#: config/prefs.php.dist:117
+msgid "Polaroid"
+msgstr "Polaroid"
+
+#: xppublish.php:99 xppublish.php:155
+msgid "Press the \"Back\" button and try again."
+msgstr "Paspauskite mygtukà \"Atgal\" ir pabandykite dar kartà."
+
+#: config/prefs.php.dist:114
+msgid "Pretty Thumbs"
+msgstr "Graþûs paveikslëliai"
+
+#: config/prefs.php.dist:115
+msgid "Pretty Thumbs on White Background"
+msgstr "Graþûs paveikslëliai baltame fone"
+
+#: image.php:342
+#, php-format
+msgid "Preview changes for %s :: %s"
+msgstr "%s :: %s pakeitimø rodymas"
+
+#: templates/view/image.inc:93 templates/view/image.inc:94
+msgid "Previous"
+msgstr "Ankstesnis"
+
+#: lib/Exif.php:120
+msgid "Program"
+msgstr "Programa"
+
+#: templates/tile/image.inc:17 templates/view/image.inc:49
+msgid "Properties"
+msgstr "Nustatymai"
+
+#: templates/xppublish/list.inc:7
+msgid "Publish Images to:"
+msgstr "Publikuoti paveikslëlius á:"
+
+#: xppublish.php:35
+#, php-format
+msgid "Publish to %s"
+msgstr "Publikuoti %s"
+
+#: xppublish.php:24
+#, php-format
+msgid "Publish your photos to %s on %s."
+msgstr "Publikuoti paveikslëlius á %s %s."
+
+#: lib/Block/gallery.php:35 lib/Block/gallery.php:54
+msgid "Random Gallery"
+msgstr "Atsitiktinë galerija"
+
+#: lib/Block/random_gallery.php:3 lib/Block/random_gallery.php:27
+msgid "Random gallery"
+msgstr "Atsitiktinë galerija"
+
+#: lib/Block/random_photo.php:3 lib/Block/random_photo.php:28
+msgid "Random photo"
+msgstr "Atsitiktinis paveikslëlis"
+
+#: templates/gallery/delete_confirmation.inc:10
+msgid "Really Delete"
+msgstr "Tikrai iðtrinti"
+
+#: templates/gallery/delete_confirmation.inc:10
+msgid "Really Empty"
+msgstr "Tikrai iðvalyti"
+
+#: templates/gallery/delete_confirmation.inc:2
+#, php-format
+msgid "Really delete %s?"
+msgstr "Tikrai iðtrinti %s?"
+
+#: templates/gallery/delete_confirmation.inc:5
+msgid ""
+"Really delete this gallery and all of the images in it? This action cannot "
+"be undone."
+msgstr ""
+"Tikrai iðtrinti ðià galerijà ir visus paveikslëlius joje? Graþinti atgal "
+"nebegalësite."
+
+#: templates/gallery/delete_confirmation.inc:2
+#, php-format
+msgid "Really empty %s?"
+msgstr "Tikrai iðvalyti %s?"
+
+#: templates/gallery/delete_confirmation.inc:5
+msgid ""
+"Really empty this gallery and all of the images in it? This action cannot be "
+"undone."
+msgstr ""
+"Tikrai iðvalyti visus paveikslëlius ðioje galerijoje? Graþinti atgal "
+"nebegalësite."
+
+#: lib/Block/recent_comments.php:63
+#, php-format
+msgid "Recent Comments In %s"
+msgstr "Paskutiniai %s komentarai"
+
+#: lib/Block/recent_comments.php:5
+msgid "Recent Image Comments"
+msgstr "Paskutinis paveikslëliø turinys"
+
+#: templates/view/gallery.inc:223
+#, php-format
+msgid "Recent photos by %s"
+msgstr "Paskutiai %s paveikslëliai"
+
+#: templates/view/gallery.inc:224
+#, php-format
+msgid "Recent photos in %s"
+msgstr "Paskutiniai paveikslëliai galerijoje %s"
+
+#: lib/Block/recently_added.php:2
+msgid "Recently Added Images"
+msgstr "Vëliausiai ákelti paveikslëliai"
+
+#: lib/Block/recently_added.php:62
+#, php-format
+msgid "Recently Added Images From %s"
+msgstr "Vëliausiai ið %s ákelti paveikslëliai"
+
+#: rss.php:57 rss.php:60
+#, php-format
+msgid "Recently added images at %s"
+msgstr "Paskutiniai %s ákelti paveikslëliai"
+
+#: rss.php:130
+#, php-format
+msgid "Recently added images by %s on %s"
+msgstr "Paskutiniai %s %s ákelti paveikslëliai"
+
+#: lib/Exif.php:272
+msgid "Red Eye"
+msgstr "Raudonø akiø efektas"
+
+#: lib/Exif.php:278
+msgid "Red Eye, Auto-Mode"
+msgstr "Raudonø akiø efektas, automatinis reþimas"
+
+#: lib/Exif.php:280
+msgid "Red Eye, Auto-Mode, Return light detected"
+msgstr "Raudonø akiø efektas, automatinis reþimas, aptikta graþinama ðviesa"
+
+#: lib/Exif.php:279
+msgid "Red Eye, Auto-Mode, Return light not detected"
+msgstr "Raudonø akiø efektas, automatinis reþimas, graþinama ðviesa neaptikta"
+
+#: lib/Exif.php:275
+msgid "Red Eye, Compulsory Flash"
+msgstr "Raudonø akiø efektas, priverstinë blykstë"
+
+#: lib/Exif.php:277
+msgid "Red Eye, Compulsory Flash, Return light detected"
+msgstr "Raudonø akiø efektas, priverstinë blykstë, aptikra graþinama ðviesa"
+
+#: lib/Exif.php:276
+msgid "Red Eye, Compulsory Flash, Return light not detected"
+msgstr "Raudonø akiø efektas, priverstinë blykstæ, graþinama ðviesa neaptikta"
+
+#: lib/Exif.php:274
+msgid "Red Eye, Return light detected"
+msgstr "Raudonø akiø efektas, aptikta graþinama ðviesa"
+
+#: lib/Exif.php:273
+msgid "Red Eye, Return light not detected"
+msgstr "Raudonø akiø efektas, graþinama ðviesa neaptikta"
+
+#: templates/list/header.inc:6 templates/group/header.inc:7
+#: templates/group/header.inc:8
+msgid "Refresh List"
+msgstr "Atnaujinti sàraðà"
+
+#: templates/view/results.inc:87
+msgid "Refresh View"
+msgstr "Atnaujinti vaizdà"
+
+#: templates/view/results.inc:114
+msgid "Related Tags"
+msgstr "Susijusios þymës"
+
+#: xrequest.php:24 lib/Views/Gallery.php:90 lib/Views/Image.php:87
+msgid "Remove Tag"
+msgstr "Iðtrinti þymæ"
+
+#: lib/Tags.php:558
+msgid "Remove Tag From Search"
+msgstr "Paðalinti þymæ ið paieðkos"
+
+#: templates/view/gallery.inc:133
+msgid "Reset default image"
+msgstr "Nustatyti standartiná paveikslëlá ið naujo"
+
+#: image.php:67
+msgid "Right"
+msgstr "Deðinëje"
+
+#: templates/image/edit_image.inc:18
+msgid "Rotate 180"
+msgstr "Pasukti 180"
+
+#: templates/image/edit_image.inc:17
+msgid "Rotate Left"
+msgstr "Pasukti kairën"
+
+#: templates/image/edit_image.inc:19
+msgid "Rotate Right"
+msgstr "Pasukti deðinën"
+
+#: image.php:31 image.php:54
+msgid "Save"
+msgstr "Iðsaugoti"
+
+#: templates/captions/captions.inc:49
+msgid "Save Captions"
+msgstr "Iðsaugoti apraðymus"
+
+#: templates/image/preview_image.inc:23
+msgid "Save Change"
+msgstr "Iðsaugoti pakeitimus"
+
+#: templates/gallery/gallery.inc:52
+msgid "Save Gallery"
+msgstr "Iðsaugoti galerijà"
+
+#: image.php:195
+msgid "Save Image"
+msgstr "Iðsaugoti paveikslëlá"
+
+#: templates/view/gallery.inc:158 templates/view/results.inc:93
+msgid "Select All"
+msgstr "Paþymëti viskà"
+
+#: templates/view/gallery.inc:159 templates/view/results.inc:94
+msgid "Select None"
+msgstr "Nepaþymëti nieko"
+
+#: templates/view/gallery.inc:157 templates/view/results.inc:92
+#, php-format
+msgid "Select: %s, %s"
+msgstr "Pasirinkite: %s, %s"
+
+#: templates/view/gallery.inc:171 templates/view/results.inc:104
+msgid "Selected images to"
+msgstr "Pasirinktus paveikslëlius á"
+
+#: lib/Forms/Ecard.php:25
+msgid "Send"
+msgstr "Siøsti"
+
+#: img/ecard.php:104
+#, php-format
+msgid "Send Ecard :: %s"
+msgstr "El. atvirutës siuntimas :: %s"
+
+#: templates/view/image.inc:59
+msgid "Send an Ecard"
+msgstr "El. atvirutës siuntimas"
+
+#: lib/Forms/Ecard.php:41
+msgid "Send ecard to the following address:"
+msgstr "Siøsti el. atvirutæ ðiuo adresu:"
+
+#: lib/Exif.php:352
+msgid "Sensing Method"
+msgstr "Sensoriaus metodas"
+
+#: templates/view/gallery.inc:130
+msgid "Set captions"
+msgstr "Nustatyti pavadinimus"
+
+#: templates/view/gallery.inc:137
+msgid "Set gallery permissions"
+msgstr "Nustatyti galerijos teises"
+
+#: config/prefs.php.dist:116
+msgid "Shadowed Thumbs on White Background"
+msgstr "Paveikslëliai su ðeðëliu ant balto fono"
+
+#: config/prefs.php.dist:82
+msgid "Show EXIF data"
+msgstr "Rodyti EXIF duomenis"
+
+#: lib/Exif.php:339
+msgid "Shutter Speed"
+msgstr "Objektyvo greitis"
+
+#: lib/Forms/Upload.php:31
+msgid "Single Image"
+msgstr "Vienà paveikslëlá"
+
+#: image.php:63
+msgid "Small"
+msgstr "Maþas"
+
+#: templates/view/gallery.inc:131
+msgid "Sort images"
+msgstr "Surûðiuoti paveikslëlius"
+
+#: lib/Exif.php:230
+msgid "Spot"
+msgstr "Taðkas"
+
+#: templates/view/image.inc:91
+msgid "Start Slideshow"
+msgstr "Pradëti skaidriø perþiûrà"
+
+#: templates/gallery/gallery.inc:25
+msgid "Style for this gallery"
+msgstr "Ðios galerijos stilius"
+
+#: templates/view/gallery.inc:222
+msgid "Subscribe"
+msgstr "Prenumeruoti"
+
+#: gallery/delete.php:42
+#, php-format
+msgid "Successfully deleted %s."
+msgstr "%s sëkmingai iðtrinta."
+
+#: lib/Exif.php:241
+msgid "Sunny"
+msgstr "saulëta"
+
+#: lib/Block/cloud.php:2 lib/Block/cloud.php:26
+msgid "Tag Cloud"
+msgstr "Þymës"
+
+#: image.php:39 lib/Forms/Upload.php:39 templates/view/image.inc:66
+#: templates/view/gallery.inc:207
+msgid "Tags"
+msgstr "Þymës"
+
+#: lib/Ansel.php:348
+#, php-format
+msgid "The file \"%s\" doesn't exist."
+msgstr "Failas \"%s\" neegzistuoja."
+
+#: lib/api.php:214 img/upload.php:103
+msgid "The file you uploaded does not appear to be a valid image."
+msgstr "Nusiøstas failas nëra paveikslëlis arba neteisingas jo formatas."
+
+#: lib/Ansel.php:215
+#, php-format
+msgid "The gallery \"%s\" could not be created: %s"
+msgstr "Nepavyko sukurti galerijos \"%s\": %s"
+
+#: gallery.php:207 xppublish.php:129
+#, php-format
+msgid "The gallery \"%s\" couldn't be created: %s"
+msgstr "Nepavyko sukurti galerijos \"%s\": %s"
+
+#: gallery.php:213 xppublish.php:133
+#, php-format
+msgid "The gallery \"%s\" was created successfully."
+msgstr "Galerija \"%s\" sëkmingai sukurta."
+
+#: gallery.php:154
+msgid "The gallery was saved."
+msgstr "Galerija iðsaugota."
+
+#: gallery.php:262
+msgid "The gallery's default image has successfully been reset."
+msgstr "Galerijos standartinis paveikslëlis buvo nustatytas ið naujo."
+
+#: img/upload.php:126
+msgid ""
+"The uploaded file appears to be empty. It may not exist on your computer."
+msgstr "Failas tuðèias arba nëra jûsø vietiniame kompiuterio diske."
+
+#: gallery.php:76
+#, php-format
+msgid "There are no images in %s to download."
+msgstr "%s nëra paveikslëliø atsisiuntimui."
+
+#: templates/view/gallery.inc:181 templates/view/results.inc:121
+#: templates/captions/captions.inc:51
+msgid "There are no images in this gallery."
+msgstr "Ðioje galerijoje nëra paveikslëliø."
+
+#: list.php:68 lib/Block/random_gallery.php:37 lib/Block/random_photo.php:38
+msgid "There are no photo galleries available."
+msgstr "Galerijø nëra."
+
+#: lib/api.php:361
+#, php-format
+msgid "There was a problem deleting %s: %s"
+msgstr "Klaida trinant %s: %s"
+
+#: gallery/delete.php:40
+#, php-format
+msgid "There was a problem deleting %s: %s."
+msgstr "Klaida trinant %s: %s."
+
+#: image.php:405 lib/Views/Results.php:95
+#, php-format
+msgid "There was a problem deleting images: %s"
+msgstr "Klaida trinant paveikslëlius: %s"
+
+#: img/upload.php:96 img/upload.php:112
+#, php-format
+msgid "There was a problem saving the image: %s"
+msgstr "Klaida iðsaugant paveikslëlá: %s"
+
+#: xppublish.php:199
+msgid "There was a problem uploading the image."
+msgstr "Klaida siunèiant paveikslëlá."
+
+#: img/upload.php:124
+#, php-format
+msgid "There was a problem uploading the image: %s"
+msgstr "Klaida siunðiant paveikslëlá: %s"
+
+#: img/upload.php:62 img/upload.php:83
+#, php-format
+msgid "There was an error processing the uploaded archive: %s"
+msgstr "Klaida apdorojant nusiøstà archyvà: %s"
+
+#: img/ecard.php:96
+#, php-format
+msgid "There was an error sending your message: %s"
+msgstr "Klaida siunèiant þinutæ: %s"
+
+#: lib/Exif.php:292
+msgid "Three Chip Color Area Sensor"
+msgstr "Trijø mikroschemø spalvø sensorius"
+
+#: image.php:63
+msgid "Tiny"
+msgstr "Miniatiûrinis"
+
+#: image.php:71
+msgid "Top"
+msgstr "Virðuje"
+
+#: lib/Exif.php:294
+msgid "Trilinear Sensor"
+msgstr "Trigubas sensorius"
+
+#: lib/Exif.php:243
+msgid "Tungsten"
+msgstr "Volframas"
+
+#: lib/Exif.php:122
+msgid "Tv-priority"
+msgstr "Tv-svarba"
+
+#: lib/Exif.php:291
+msgid "Two Chip Color Area Sensor"
+msgstr "Dviejø mikroschemø spalvø sensorius"
+
+#: lib/ImageView.php:73
+#, php-format
+msgid "Unable to load the definition of %s."
+msgstr "Nepavyko uþkrauti %s apraðymo."
+
+#: rss.php:38
+msgid "Unable to retrieve requested feed"
+msgstr "Nepavyko gauti nurodytø duomenø"
+
+#: lib/Exif.php:305
+msgid "Uncalibrated"
+msgstr "Nesukalibruota"
+
+#: templates/gallery/gallery.inc:53
+msgid "Undo Changes"
+msgstr "Atðaukti pakeitimus"
+
+#: list.php:55 templates/group/category.inc:8
+msgid "Unfiled"
+msgstr "Nenurodyta"
+
+#: lib/Exif.php:227
+msgid "Unknown"
+msgstr "Neþinomas"
+
+#: gallery.php:115
+msgid "Unknown gallery"
+msgstr "Galerija neþinoma"
+
+#: lib/Ansel.php:203
+msgid "Unnamed"
+msgstr "Neávardintas"
+
+#: xppublish.php:141
+msgid "Untitled"
+msgstr "Nepavadinitas"
+
+#: perms.php:236
+#, php-format
+msgid "Updated %s."
+msgstr "%s atnaujinta."
+
+#: lib/Forms/Upload.php:27
+msgid "Upload"
+msgstr "Nusiøsti"
+
+#: templates/xppublish/javascript.inc:63
+#, php-format
+msgid "Upload Photos to %s"
+msgstr "Ákelti paveikslëlius á %s"
+
+#: templates/view/gallery.inc:127
+msgid "Upload images"
+msgstr "Ákelti paveikslëlius"
+
+#: img/upload.php:30
+msgid "Upload new images"
+msgstr "Naujø paveikslëliø ákëlimas"
+
+#: lib/Exif.php:151
+#, php-format
+msgid "Upsidedown"
+msgstr "Aukðtyn kojom"
+
+#: lib/Exif.php:153
+#, php-format
+msgid "Upsidedown Mirrored"
+msgstr "Veidrodinis aukðtyn kojom"
+
+#: templates/xppublish/new.inc:17
+msgid "Use Default"
+msgstr "Naudoti standartinæ reikðmæ"
+
+#: lib/Forms/Ecard.php:32
+msgid "Use the following return address:"
+msgstr "Atsakymui naudoti ðá adresà:"
+
+#: lib/Exif.php:350
+msgid "User Comment"
+msgstr "Vartotojø komentarai"
+
+#: templates/xppublish/login.inc:11
+msgid "Username"
+msgstr "Vartotojo vardas"
+
+#: xppublish.php:50
+msgid "Username and Password are incorrect."
+msgstr "Neteisingi vartotojo vardas ir slaptaþodis."
+
+#: image.php:72
+msgid "Vertical Alignment"
+msgstr "Vertikalus lygiavimas"
+
+#: lib/Block/gallery.php:76 lib/Block/random_gallery.php:41
+#, php-format
+msgid "View %s"
+msgstr "Rodyti %s"
+
+#: templates/view/results.inc:86
+msgid "View All Results"
+msgstr "Rodyti visus rezultatus"
+
+#: lib/Block/random_photo.php:52
+msgid "View Photo"
+msgstr "Rodyti paveikslëlá"
+
+#: templates/view/results.inc:86
+msgid "View Results from All Users"
+msgstr "Rodyti rezultatus ið visø vartotojø"
+
+#: config/prefs.php.dist:38
+msgid "View to display by default"
+msgstr "Standartinis vaizdavimas"
+
+#: image.php:302 image.php:308 templates/image/edit_image.inc:32
+msgid "Watermark"
+msgstr "Vandens þenklas"
+
+#: image.php:64
+msgid "Watermark Font"
+msgstr "Vandens þenklo ðriftas"
+
+#: lib/Exif.php:353
+msgid "White Balance"
+msgstr "Baltos spalvos balansas"
+
+#: lib/Exif.php:335
+msgid "Width"
+msgstr "Plotis"
+
+#: lib/Exif.php:337
+msgid "X Resolution"
+msgstr "X rezoliucija"
+
+#: lib/Exif.php:338
+msgid "Y Resolution"
+msgstr "Y rezoliucija"
+
+#: lib/Ansel.php:1730
+#, php-format
+msgid "You are not allowed to copy images to %s."
+msgstr "Jûs neturite teisiø kopijuoti paveikslëliø á %s."
+
+#: image.php:238
+msgid "You are not allowed to edit this image."
+msgstr "Jûs neturite teisiø keisti ðio paveikslëlio."
+
+#: lib/Ansel.php:1700
+#, php-format
+msgid "You are not allowed to move images to %s."
+msgstr "Jûs neturite teisiø perkelti paveikslëliø á %s."
+
+#: lib/Ansel.php:1702
+#, php-format
+msgid "You are not allowed to remove images from %s."
+msgstr "Jûs neturite teisiø iðtrinti paveikslëliø ið %s."
+
+#: xppublish.php:91 xppublish.php:184
+msgid "You cannot add photos in that gallery."
+msgstr "Jûs negalite pridëti paveikslëliø ðioje galerijoje."
+
+#: gallery/delete.php:30
+#, php-format
+msgid "You cannot delete %s, it contains nested galleries."
+msgstr "Jûs negalite iðtrinti %s, joje yra gilesniø galerijø."
+
+#: lib/Views/Abstract.php:25
+msgid "You do not have permission to access this gallery."
+msgstr "Jûs neturite teisiø perþiûrëti ðios galerijos."
+
+#: gallery.php:48 gallery.php:170
+#, php-format
+msgid "You do not have permission to add children to %s."
+msgstr "Jûs neturite teisiø pridëti galerijos prie %s."
+
+#: lib/Ansel.php:2185 lib/Ansel.php:2200
+msgid "You do not have permission to add tags to this gallery"
+msgstr "Jûs neturite teisiø pridëti þymiø ðiai galerijai"
+
+#: gallery/sort.php:26
+msgid "You do not have permission to change this gallery."
+msgstr "Jûs neturite teisiø keisti ðios galerijos."
+
+#: lib/Block/recent_comments.php:138 lib/Block/gallery.php:108
+#: lib/Block/recently_added.php:141
+msgid "You do not have permission to view this gallery."
+msgstr "Neturite teisiø perþiûrëti ðios galerijos."
+
+#: lib/Ansel.php:2800
+msgid "You do not have permissions to add tags to this image."
+msgstr "Jûs neturite teisiø pridëti þymiø ðiam paveikslëliui."
+
+#: lib/Ansel.php:2783
+msgid "You do not have permissions to view this image."
+msgstr "Jûs neturite teisiø perþiûrëti ðio paveikslëlio."
+
+#: img/ecard.php:69
+msgid ""
+"You have been sent an Ecard. To view the Ecard, you must be able to view "
+"text/html messages in your mail reader. If you are viewing this message, "
+"then most likely your mail reader does not support viewing text/html "
+"messages."
+msgstr ""
+"Jûs gavote el. atvirutæ. Norëdami jà perþiûrëti, jûsø el. paðto klientas "
+"privalo turëti galimybæ perþiûrëti HTML tipo laiðkus. Jeigu matote ðià "
+"þinutæ, tai jûsø el. paðto klientas neturi ðios galimybës."
+
+#: templates/view/gallery.inc:46 templates/view/gallery.inc:60
+#: templates/view/results.inc:46 templates/view/results.inc:60
+msgid "You must choose a gallery to move images to."
+msgstr "Paveikslëliø perkëlimui reikia pasirinkti galerijà."
+
+#: img/ecard.php:45
+msgid "You must enter an e-mail address to send the message to."
+msgstr "Norëdami iðsiøsti ðá laiðkà, privalote áraðyti el. paðto adresà."
+
+#: img/ecard.php:40
+msgid "You must enter your e-mail address."
+msgstr "Jûs turite áraðyti el. paðto adresà."
+
+#: gallery.php:190
+msgid "You must provide a display name for your new gallery."
+msgstr "Jûs turite nurodyti naujos galerijos rodomà pavadinimà."
+
+#: lib/Forms/Upload.php:50
+msgid "Zip File Upload"
+msgstr "ZIP failo siuntimas"
+
+#: lib/Ansel.php:416
+msgid "_Galleries"
+msgstr "_Galerijos"
+
+#: lib/Ansel.php:427
+msgid "_My Galleries"
+msgstr "Mano galerijos"
+
+#: lib/Ansel.php:438
+msgid "_New Gallery"
+msgstr "_Nauja galerija"
+
+#: lib/Ansel.php:447
+msgid "_Print"
+msgstr "_Spausdinti"
+
+#: lib/Exif.php:302
+msgid "sRGB"
+msgstr "sRGB"
diff --git a/ansel/po/sl_SI.po b/ansel/po/sl_SI.po
new file mode 100644 (file)
index 0000000..a4be162
--- /dev/null
@@ -0,0 +1,1994 @@
+# Slovenian translations for Ansel packaga
+# Slovenski prevod Ansel paketa
+# Copyright 2006-2009 The Horde Project
+# This file is distributed under the same license as the horde package.
+# Automatically generated, 2006.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ansel\n"
+"Report-Msgid-Bugs-To: dev@lists.horde.org\n"
+"POT-Creation-Date: 2006-07-25 11:30+0200\n"
+"PO-Revision-Date: 2006-04-30 10:32+0100\n"
+"Last-Translator: duck@obala.net\n"
+"Language-Team: sl_SI <duck@obala.net>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: gallery.php:247
+#, php-format
+msgid ""
+"\"%s\" is an invalid unique name; it may already be taken. Please choose a "
+"different unique name."
+msgstr ""
+"'%s' je napačno edinstveno ime galerije; lahko da je ze zasedeno. Izberite "
+"si drugo."
+
+#: scripts/recursive_import.php:111
+#, php-format
+msgid "\"%s\" is not a directory."
+msgstr "%s ni direktorij"
+
+#: templates/group/owner.inc:46 templates/group/category.inc:51
+#, php-format
+msgid "%d Galleries"
+msgstr "%d Gallerij"
+
+#: lib/Exif.php:286
+#, php-format
+msgid "%d bytes"
+msgstr "%d bajtov"
+
+#: templates/list/header.inc:7
+#, php-format
+msgid "%d galleries"
+msgstr "%d gallerij"
+
+#: templates/list/images.inc:4
+#, fuzzy, php-format
+msgid "%d images"
+msgstr "%s slik"
+
+#: img/upload.php:97
+#, php-format
+msgid "%d images were successfully extracted and uploaded."
+msgstr "%d slik je bilo uspeno dodanih"
+
+#: lib/Exif.php:137 lib/Exif.php:143
+#, php-format
+msgid "%d pixels"
+msgstr "%d pik"
+
+#: lib/Exif.php:167 lib/Exif.php:182
+#, php-format
+msgid "%d sec"
+msgstr "%d sekund"
+
+#: templates/list/header.inc:7
+#, php-format
+msgid "%d to %d of %d galleries"
+msgstr "%d do %d od %d podgalerij"
+
+#: templates/view/subgallery_header.inc:3
+#, php-format
+msgid "%d to %d of %d subgalleries"
+msgstr "%d do %d od %d podgalerij"
+
+#: gallery/sort.php:45
+#, php-format
+msgid "%s :: Sort"
+msgstr "%s :: Razvrsti"
+
+#: list.php:46 templates/group/owner.inc:10 lib/Ansel.php:477
+#, php-format
+msgid "%s's Galleries"
+msgstr "Galerije uporabnika %s"
+
+#: lib/Gallery.php:1181
+#, php-format
+msgid "(c) %s %s"
+msgstr "(c) %s %s"
+
+#: templates/xppublish/new.inc:14
+msgid "(must be unique)"
+msgstr "(mora biti edinstveno)"
+
+#: scripts/ansel.php:296
+msgid "-a, --add[=filename]         Add local file to selected gallery"
+msgstr ""
+
+#: scripts/ansel.php:294
+msgid "-c, --create[=shortname/name/description]  Create gallery (and use it)"
+msgstr ""
+
+#: scripts/ansel.php:297
+msgid ""
+"-d, --dir[=directory]        Add all files from the directory to the "
+"selected\n"
+"                             gallery"
+msgstr ""
+
+#: scripts/recursive_import.php:190
+msgid ""
+"-d, --dir[=directory]        Recursively add all files from the directory, "
+"creating\n"
+"                             a gallery for each directory"
+msgstr ""
+
+#: scripts/ansel.php:295
+msgid "-g, --gallery[=shortname]    Select gallery to use"
+msgstr ""
+
+#: scripts/recursive_import.php:189 scripts/ansel.php:292
+msgid "-h, --help                   Show this help"
+msgstr ""
+
+#: scripts/ansel.php:293
+msgid ""
+"-l, --list                   List galleries or images (if combined with -g)"
+msgstr ""
+
+#: scripts/recursive_import.php:192 scripts/ansel.php:299
+msgid "-p, --password[=password]    Horde login password"
+msgstr ""
+
+#: scripts/recursive_import.php:191 scripts/ansel.php:298
+msgid "-u, --username[=username]    Horde login username"
+msgstr ""
+
+#: templates/group/owner.inc:46 templates/group/category.inc:51
+msgid "1 Gallery"
+msgstr "1 Galerija"
+
+#: templates/list/images.inc:4
+msgid "1 image"
+msgstr "1 slika"
+
+#: img/upload.php:95
+msgid "1 image was successfully extracted and uploaded."
+msgstr "1 slika je bila uspeno dodana"
+
+#: templates/slideshow/slideshow.inc:233
+msgid "1 minute"
+msgstr "1 monuta"
+
+#: templates/slideshow/slideshow.inc:224
+msgid "1 second"
+msgstr "1 sekunda"
+
+#: templates/slideshow/slideshow.inc:229
+msgid "10 seconds"
+msgstr "10 sekund"
+
+#: templates/slideshow/slideshow.inc:230
+msgid "15 seconds"
+msgstr "15 sekund"
+
+#: templates/slideshow/slideshow.inc:225
+msgid "2 seconds"
+msgstr "2 sekundi"
+
+#: templates/slideshow/slideshow.inc:226
+msgid "3 seconds"
+msgstr "3 skunde"
+
+#: templates/slideshow/slideshow.inc:231
+msgid "30 seconds"
+msgstr "30 sekund"
+
+#: templates/slideshow/slideshow.inc:227
+msgid "4 seconds"
+msgstr "4 sekunde"
+
+#: templates/slideshow/slideshow.inc:232
+msgid "45 seconds"
+msgstr "45 skeund"
+
+#: templates/slideshow/slideshow.inc:228
+msgid "5 seconds"
+msgstr "5 skund"
+
+#: lib/Exif.php:158
+#, php-format
+msgid "90 deg CCW"
+msgstr "90 stopinj CCW"
+
+#: lib/Exif.php:160
+#, php-format
+msgid "90 deg CCW Mirrored"
+msgstr "90 stopinj CCW preslikano"
+
+#: lib/Exif.php:162
+#, php-format
+msgid "90 deg CW"
+msgstr "90 stopinj CW"
+
+#: lib/Exif.php:156
+#, php-format
+msgid "90 deg CW Mirrored"
+msgstr "90 stopinj CW prelsikano"
+
+#: lib/Exif.php:129
+msgid "A-DEP"
+msgstr "A-DEP"
+
+#: gallery/delete.php:19 lib/api.php:269
+#, php-format
+msgid "Access denied deleting gallery %s."
+msgstr "Ogled galerije %s vam ni dovoljen."
+
+#: image.php:337 image.php:371
+#, php-format
+msgid "Access denied deleting image %s."
+msgstr "Ogled slike %s vam ni dovoljen."
+
+#: gallery.php:71
+#, php-format
+msgid "Access denied downloading %s."
+msgstr "Snemanje %s vam ni dovoljeno."
+
+#: gallery.php:128
+msgid "Access denied editing galleries."
+msgstr "Urejanje galerij vam ni dovoljeno."
+
+#: gallery.php:184
+#, php-format
+msgid "Access denied saving gallery %s."
+msgstr "Shranjevanje galerije %s vam ni dovoljeno."
+
+#: image.php:142 image.php:204
+#, php-format
+msgid "Access denied saving image to %s."
+msgstr "Shranjevanje slike %s vam ni dovoljeno."
+
+#: gallery/captions.php:29
+#, php-format
+msgid "Access denied setting captions for %s."
+msgstr "Nastavnjanje poisa za %s vam ni dovoljeno"
+
+#: img/upload.php:141
+msgid "Add Image"
+msgstr "Dodaj sliko"
+
+#: gallery.php:35 gallery.php:249 gallery.php:257
+msgid "Adding A New Gallery"
+msgstr "Dodaj novo galerijo"
+
+#: gallery.php:63
+#, php-format
+msgid "Adding A Subgallery to %s"
+msgstr "Dodaj podgalerijo v %s"
+
+#: scripts/recursive_import.php:171
+msgid "Adding subdirectories:"
+msgstr "Dodajanje podirektorijev"
+
+#: search.php:83
+msgid "After"
+msgstr "Potem"
+
+#: templates/view/navbar-gallery.inc:6
+msgid "All"
+msgstr "Vse"
+
+#: lib/Ansel.php:140
+#, php-format
+msgid "An error occurred counting galleries: %s"
+msgstr "Napaka pri štetju galerij: %s"
+
+#: lib/Ansel.php:81
+#, php-format
+msgid "An error occurred listing galleries: %s"
+msgstr "Napaka v odpiranju galerije: %s"
+
+#: lib/Exif.php:346
+msgid "Aperture"
+msgstr "Zaslonka"
+
+#: templates/gallery/gallery.inc:79
+msgid "Are you sure you want to delete all of the images in this gallery?"
+msgstr "Resnično mislite izbrisati vse slike v galeriji."
+
+#: templates/view/gallery.inc:38
+msgid "Are you sure you want to delete the selected images?"
+msgstr "Resnično mislite izbrisati izbrane slike."
+
+#: lib/Exif.php:359
+msgid "Artist"
+msgstr "Izvajalec"
+
+#: config/prefs.php.dist:48
+msgid "Ascending"
+msgstr "Naračajoče"
+
+#: perms.php:50
+msgid "Attempt to edit a non-existent share."
+msgstr "Ni galerije."
+
+#: lib/Exif.php:125
+msgid "Av-priority"
+msgstr "Av"
+
+#: lib/Exif.php:229
+msgid "Average"
+msgstr "Povprečje"
+
+#: search.php:84
+msgid "Before"
+msgstr "Pred"
+
+#: search.php:53
+msgid "Begins With"
+msgstr "Nadaljuje z"
+
+#: search.php:82
+msgid "Between"
+msgstr "Vmes"
+
+#: slideshow.php:35
+msgid "Blend"
+msgstr "Zaslonka"
+
+#: slideshow.php:36
+msgid "Blinds"
+msgstr "Zaslonka"
+
+#: image.php:68
+msgid "Bottom"
+msgstr "Dno"
+
+#: lib/Exif.php:330
+msgid "Camera Make"
+msgstr "Izdelovalec fotoaprata"
+
+#: lib/Exif.php:331
+msgid "Camera Model"
+msgstr "Model fotoaparata"
+
+#: lib/Exif.php:357
+msgid "Camera Orientation"
+msgstr "Orjentacija"
+
+#: lib/Ansel.php:330
+#, php-format
+msgid "Can't get unknown file type \"%s\"."
+msgstr "Neznan format slike '%s'"
+
+#: templates/gallery/delete_confirmation.inc:10
+msgid "Cancel"
+msgstr "Prekliči"
+
+#: templates/image/preview_image.inc:28
+msgid "Cancel Change"
+msgstr "Prekliči spremembo"
+
+#: image.php:36 lib/Forms/Upload.php:40 templates/search/search.html:9
+msgid "Caption"
+msgstr "Opis slike"
+
+#: gallery/captions.php:52
+msgid "Caption Editor"
+msgstr "Urejavalnik opisov"
+
+#: gallery/captions.php:47
+msgid "Captions Saved."
+msgstr "Nazivi spravljeni"
+
+#: config/prefs.php.dist:59
+msgid "Category"
+msgstr "kategorija"
+
+#: image.php:64
+msgid "Center"
+msgstr "Sredina"
+
+#: lib/Exif.php:230
+msgid "Center Weighted Average"
+msgstr ""
+
+#: templates/view/gallery.inc:109
+msgid "Change gallery properties"
+msgstr "Spremeni lastnosti galerije"
+
+#: config/prefs.php.dist:22
+msgid "Change your display options."
+msgstr "Spremite nastavitve prikazovanja"
+
+#: config/prefs.php.dist:15
+msgid "Change your gallery sorting options."
+msgstr "Spremeni način sortiranja galerij"
+
+#: slideshow.php:37
+msgid "Checkerboard"
+msgstr ""
+
+#: templates/view/gallery.inc:101
+msgid "Choose Action:"
+msgstr "Izberi vkaz:"
+
+#: templates/xppublish/list.inc:8
+msgid "Choose an existing gallery"
+msgstr "Izberi obstoječe galerijo"
+
+#: lib/Exif.php:243
+msgid "Cloudy"
+msgstr "Oblačno"
+
+#: lib/Exif.php:294
+msgid "Color Sequential Area Sensor"
+msgstr ""
+
+#: lib/Exif.php:296
+msgid "Color Sequential Linear Sensor"
+msgstr ""
+
+#: lib/Exif.php:354
+msgid "Color Space"
+msgstr "Barvna shema"
+
+#: ecard.php:42
+msgid "Comments:"
+msgstr "Komentarji:"
+
+#: lib/Exif.php:264
+msgid "Compulsory Flash"
+msgstr ""
+
+#: lib/Exif.php:266
+msgid "Compulsory Flash, Return light detected"
+msgstr ""
+
+#: lib/Exif.php:265
+msgid "Compulsory Flash, Return light not detected"
+msgstr ""
+
+#: search.php:55
+msgid "Contains"
+msgstr "Vsebuje"
+
+#: image.php:425
+#, php-format
+msgid "Copied %s to %s."
+msgstr "%s porvojena v %s"
+
+#: templates/view/navbar-gallery.inc:20
+msgid "Copy"
+msgstr "Kopiraj"
+
+#: lib/Exif.php:358
+msgid "Copyright"
+msgstr "Avtorstvo"
+
+#: scripts/ansel.php:162
+msgid "Couldn't list galleries."
+msgstr "Ne morem prebrati spiseka galerij"
+
+#: scripts/ansel.php:40
+msgid "Couldn't read command-line options."
+msgstr "Ne morem prebrati ukaznih parameterjev"
+
+#: templates/xppublish/list.inc:18
+msgid "Create"
+msgstr "Tvori"
+
+#: templates/view/gallery.inc:108
+msgid "Create a Subgallery"
+msgstr "Tvori novo podgalerijo"
+
+#: templates/xppublish/new.inc:8
+msgid "Create a new Gallery"
+msgstr "Tvori novo galerijo"
+
+#: templates/xppublish/list.inc:16
+msgid "Create a new gallery instead"
+msgstr "Tvori novo galerijo"
+
+#: templates/list/images.inc:20
+msgid "Created"
+msgstr "Tvorijeno"
+
+#: scripts/recursive_import.php:120
+#, php-format
+msgid "Creating gallery: \"%s\" (%s)"
+msgstr "Ustvarjanje nove galerije: %s (%s)"
+
+#: templates/image/edit_image.inc:22 templates/crop/crop.inc:46
+#: lib/Ansel.php:518
+msgid "Crop"
+msgstr "Obrei"
+
+#: img/crop.php:56
+msgid "Crop Image"
+msgstr "Obrei sliko"
+
+#: templates/crop/crop.inc:56
+msgid "Cropped"
+msgstr "Obrezano"
+
+#: lib/Exif.php:247
+msgid "Custom"
+msgstr "Poljubno"
+
+#: image.php:57
+msgid "Custom Watermark"
+msgstr ""
+
+#: config/prefs.php.dist:110
+msgid "Custom watermark to use for images"
+msgstr ""
+
+#: config/prefs.php.dist:37
+msgid "Date Created"
+msgstr "Datum slikanja"
+
+#: search.php:86
+msgid "Date Field"
+msgstr "Datum slikanja"
+
+#: search.php:61
+msgid "Date Fields"
+msgstr "Datumi slikanja"
+
+#: lib/Exif.php:337
+msgid "Date Image Digitized"
+msgstr "Datum digitalizacije slike"
+
+#: lib/Exif.php:335
+msgid "Date Image Modified"
+msgstr "Datum spremembe"
+
+#: lib/Exif.php:336
+msgid "Date Image Taken"
+msgstr "Datum slikanja"
+
+#: config/prefs.php.dist:38
+msgid "Date Modified"
+msgstr "Datum spremembe"
+
+#: search.php:87
+msgid "Date Operator"
+msgstr "Datum"
+
+#: search.php:36
+msgid "Date Uploaded"
+msgstr "Datum objave"
+
+#: config/prefs.php.dist:39
+msgid "Default sorting criteria:"
+msgstr "Privzet parameter razvrščanja"
+
+#: config/prefs.php.dist:50
+msgid "Default sorting direction:"
+msgstr "Privzet smer razvrščanja"
+
+#: templates/slideshow/slideshow.inc:222
+msgid "Delay"
+msgstr "Zaksnitev"
+
+#: templates/image/edit_image.inc:44 templates/view/navbar-image.inc:20
+#: templates/view/navbar-gallery.inc:12
+msgid "Delete"
+msgstr "Zbriši"
+
+#: templates/view/gallery.inc:117
+msgid "Delete this gallery"
+msgstr "Zbriši galerijo"
+
+#: image.php:341
+msgid "Deleted the image."
+msgstr "Zbriši sliko"
+
+#: config/prefs.php.dist:49
+msgid "Descending"
+msgstr "Padajoče"
+
+#: search.php:35 templates/list/gallery_headers.inc:17
+msgid "Description"
+msgstr "Opis"
+
+#: slideshow.php:38
+msgid "Diagonal"
+msgstr "Diagonalno"
+
+#: config/prefs.php.dist:21
+msgid "Display Options"
+msgstr "Nastavitve prikazovanja"
+
+#: templates/group/header.inc:3
+#, php-format
+msgid "Displaying from %s to %s of %s groups"
+msgstr "Prikazujem %s do %s od %s galerij"
+
+#: templates/view/gallery.inc:98
+#, php-format
+msgid "Displaying from %s to %s of %s images"
+msgstr "Prikazujem od %s do %s od %s galerij"
+
+#: templates/view/navbar-image.inc:20
+#, php-format
+msgid "Do you want to permanently delete %s?"
+msgstr "Resnično mislite zbriati %s"
+
+#: search.php:56
+msgid "Does Not Contain"
+msgstr "Ne vsebuje"
+
+#: gallery/sort.php:59
+msgid "Done"
+msgstr "Končaj"
+
+#: slideshow.php:39
+msgid "Doors"
+msgstr "Vrata"
+
+#: templates/view/navbar-image.inc:28
+msgid "Download Full Image"
+msgstr "Shrani veliko sliko"
+
+#: templates/view/gallery.inc:104
+msgid "Download as .zip"
+msgstr "Shrani kot .zip"
+
+#: gallery/sort.php:58
+msgid "Drag images to the desired sort position."
+msgstr "Z miko premikate slike v eljeni vrstni red."
+
+#: xppublish.php:210
+#, php-format
+msgid "ERROR: %s"
+msgstr "NAPAKA: %s"
+
+#: lib/Exif.php:119
+msgid "Easy shooting"
+msgstr "Enostavno slikanje"
+
+#: ecard.php:91
+msgid "Ecard - "
+msgstr "E-razglednica -"
+
+#: templates/view/gallery.inc:172 templates/view/navbar-image.inc:17
+#: lib/Ansel.php:513
+msgid "Edit"
+msgstr "Uredi"
+
+#: image.php:188
+#, php-format
+msgid "Edit %s :: %s"
+msgstr "Uredi %s :: %s"
+
+#: image.php:271
+msgid "Edit Image"
+msgstr "Uredi sliko"
+
+#: perms.php:241
+msgid "Edit Permissions"
+msgstr "Uredi privilegije"
+
+#: perms.php:244
+#, php-format
+msgid "Edit Permissions for %s"
+msgstr "Uredi privilegije za %s"
+
+#: image.php:153
+msgid "Edit an image"
+msgstr "Uredi sliko"
+
+#: image.php:117
+#, php-format
+msgid "Edit properties :: %s"
+msgstr "Uredi lastnosti :: %s"
+
+#: templates/gallery/gallery.inc:70
+msgid "Empty Gallery"
+msgstr "Prazna galerija"
+
+#: search.php:89
+msgid "End Date"
+msgstr "Končmi datum"
+
+#: search.php:54
+msgid "Ends With"
+msgstr "Konča z"
+
+#: gallery/captions.php:23
+#, php-format
+msgid "Error accessing %s: %s"
+msgstr "Napaka pri dsotpu %s: %s"
+
+#: lib/Exif.php:343
+msgid "Exposure"
+msgstr "Ekspozitura"
+
+#: lib/Exif.php:349
+msgid "Exposure Bias"
+msgstr "Ekspozitura"
+
+#: lib/Exif.php:350
+msgid "Exposure Mode"
+msgstr "Načn ekspoziture"
+
+#: lib/Exif.php:347
+msgid "F-Number"
+msgstr "F-tevilka"
+
+#: lib/Forms/Upload.php:46
+#, php-format
+msgid "File %s"
+msgstr "Datoteka %s"
+
+#: lib/Exif.php:334
+msgid "File Size"
+msgstr "Velikost datoteke"
+
+#: lib/Forms/Upload.php:36 lib/Forms/Upload.php:54
+msgid "File to upload"
+msgstr "Datoteka"
+
+#: search.php:37
+msgid "Filename"
+msgstr "Datoteka"
+
+#: lib/Exif.php:246 lib/Exif.php:261
+msgid "Flash"
+msgstr "Bliskavica"
+
+#: lib/Exif.php:352
+msgid "Flash Setting"
+msgstr "Nastavitve bliskavice"
+
+#: lib/Exif.php:269
+msgid "Flash, Auto-Mode"
+msgstr ""
+
+#: lib/Exif.php:271
+msgid "Flash, Auto-Mode, Return light detected"
+msgstr ""
+
+#: lib/Exif.php:270
+msgid "Flash, Auto-Mode, Return light not detected"
+msgstr ""
+
+#: lib/Exif.php:263
+msgid "Flash, strobe return light detected"
+msgstr ""
+
+#: lib/Exif.php:262
+msgid "Flash, strobe return light not detected"
+msgstr ""
+
+#: templates/image/edit_image.inc:30
+msgid "Flip"
+msgstr "Prezrcali"
+
+#: lib/Exif.php:245
+msgid "Fluorescent"
+msgstr ""
+
+#: lib/Exif.php:344
+msgid "Focal Length"
+msgstr "Goriča razdalja"
+
+#: lib/Exif.php:345
+msgid "Focal Length (35mm equiv)"
+msgstr "Goriča razdalja (335mm)"
+
+#: templates/slideshow/slideshow.inc:87
+msgid "Forward direction"
+msgstr "Naprej"
+
+#: lib/Ansel.php:482
+msgid "Galleries"
+msgstr "Gallerije"
+
+#: templates/group/header.inc:6
+#, php-format
+msgid "Galleries grouped by %s"
+msgstr "Podgalerije zdruene po %s"
+
+#: list.php:50 list.php:53
+#, php-format
+msgid "Galleries in category %s"
+msgstr "Podgalerije v kategoriji %s"
+
+#: lib/Block/gallery.php:3 lib/Block/gallery.php:31 lib/Block/gallery.php:46
+#: templates/search/search.html:8
+msgid "Gallery"
+msgstr "Galerija"
+
+#: templates/gallery/gallery.inc:16
+msgid "Gallery Category"
+msgstr "Kategorija galerije"
+
+#: templates/gallery/gallery.inc:31 config/prefs.php.dist:35
+msgid "Gallery Description"
+msgstr "Opis galerije"
+
+#: templates/gallery/gallery.inc:24
+msgid "Gallery Display Name"
+msgstr "Ime galerije"
+
+#: list.php:58 list.php:111
+msgid "Gallery List"
+msgstr "Spisek galerij"
+
+#: search.php:38 config/prefs.php.dist:34
+msgid "Gallery Name"
+msgstr "Ime galerije"
+
+#: config/prefs.php.dist:36
+msgid "Gallery Owner"
+msgstr "Lastnik galerije"
+
+#: templates/xppublish/new.inc:14
+msgid "Gallery Short Name"
+msgstr "Ime galerije"
+
+#: lib/Ansel.php:194
+#, php-format
+msgid "Gallery Unique name \"%s\" already exists."
+msgstr "Galerija z imenom '%s' e obstja"
+
+#: xppublish.php:121
+msgid "Gallery Unique name is already taken."
+msgstr "Glerija s tem imenom e obstja"
+
+#: lib/Gallery.php:39
+msgid "Gallery names must be non-empty"
+msgstr "Imena galerij ne smejo biti prazna"
+
+#: image.php:277
+msgid "Gallery not found."
+msgstr "Ne najdem galerije"
+
+#: gallery/sort.php:37
+msgid "Gallery sorted."
+msgstr "Gallerija razvrena."
+
+#: image.php:60
+msgid "Giant"
+msgstr "Natisni"
+
+#: slideshow.php:40
+msgid "Gradient"
+msgstr "Natisni"
+
+#: templates/image/edit_image.inc:36
+msgid "Grayscale"
+msgstr "256 sivin"
+
+#: config/prefs.php.dist:61
+msgid "Group galleries by"
+msgstr "Grupiranje galerij"
+
+#: lib/Exif.php:339
+msgid "Height"
+msgstr "Viina"
+
+#: image.php:65
+msgid "Horizontal Alignment"
+msgstr "Horizontalna poravnava"
+
+#: lib/Exif.php:348
+msgid "ISO Setting"
+msgstr "ISO občtljivost"
+
+#: templates/search/search.html:7
+msgid "Image"
+msgstr "Slika"
+
+#: lib/Exif.php:333
+msgid "Image Description"
+msgstr "Opis slike"
+
+#: view.php:41
+msgid "Image Details"
+msgstr "Podatki o sliki"
+
+#: lib/Exif.php:332
+msgid "Image Type"
+msgstr "Tip slike"
+
+#: lib/Gallery.php:59
+msgid "Image names must be non-empty"
+msgstr "Ime slike mora biti ne-polna"
+
+#: view.php:56
+msgid "Image not found."
+msgstr "Slike ne najde"
+
+#: scripts/ansel.php:130
+#, php-format
+msgid "Invalid gallery \"%s\" specified."
+msgstr "Napača galerija '%s'"
+
+#: xppublish.php:87 xppublish.php:180
+msgid "Invalid gallery specified."
+msgstr "Napača galerija"
+
+#: slideshow.php:41
+#, fuzzy
+msgid "Iris"
+msgstr "je"
+
+#: search.php:51
+msgid "Is"
+msgstr "je"
+
+#: search.php:52
+msgid "Is Not"
+msgstr "Ni"
+
+#: image.php:60
+msgid "Large"
+msgstr "irok"
+
+#: image.php:64
+msgid "Left"
+msgstr "Levo"
+
+#: templates/crop/crop.inc:24
+msgid "Left X"
+msgstr "Levo X"
+
+#: scripts/ansel.php:167
+msgid "Listing Gallery/Name"
+msgstr "Spisek gelerij/imen"
+
+#: scripts/ansel.php:151
+#, php-format
+msgid "Listing images in %s"
+msgstr "Prikazujem slike v %s"
+
+#: scripts/recursive_import.php:81 scripts/ansel.php:105
+#, php-format
+msgid "Logged in successfully as \"%s\"."
+msgstr "Uspeno prijavljan kot %s."
+
+#: scripts/ansel.php:101
+msgid "Login is incorrect."
+msgstr "Prijava je nepravilna"
+
+#: templates/xppublish/login.inc:2
+#, php-format
+msgid "Login to %s"
+msgstr "Prijava v %s"
+
+#: templates/slideshow/slideshow.inc:251
+msgid "Loop?"
+msgstr "Ponavljam?"
+
+#: templates/crop/crop.inc:53
+msgid "Lower Y"
+msgstr "Spodnji Y"
+
+#: image.php:35 lib/Forms/Upload.php:39
+msgid "Make this the default image for this gallery?"
+msgstr "Izberi sliko za okono galeirje"
+
+#: scripts/recursive_import.php:187 scripts/ansel.php:290
+msgid ""
+"Mandatory arguments to long options are mandatory for short options too."
+msgstr ""
+
+#: lib/Exif.php:127 lib/Exif.php:248
+msgid "Manual"
+msgstr "Ročo"
+
+#: search.php:171 search.php:238
+#, php-format
+msgid "Matched %s images"
+msgstr "Najdenih %s slik"
+
+#: search.php:171 search.php:238
+msgid "Matched 1 image"
+msgstr "Najdena 1 slika"
+
+#: image.php:60
+msgid "Medium"
+msgstr "Srednji"
+
+#: lib/Exif.php:351
+msgid "Metering Mode"
+msgstr ""
+
+#: image.php:68
+msgid "Middle"
+msgstr "Sredinski"
+
+#: templates/image/edit_image.inc:33
+msgid "Mirror"
+msgstr "Zrcalo"
+
+#: lib/Exif.php:150
+#, php-format
+msgid "Mirrored"
+msgstr "Prezrcaljeno"
+
+#: templates/list/images.inc:21
+msgid "Modified"
+msgstr "Spremenjene"
+
+#: gallery.php:117
+#, php-format
+msgid "Modifying: %s"
+msgstr "Spremenjeni: %s"
+
+#: templates/view/navbar-gallery.inc:14
+msgid "Move"
+msgstr "Premakni"
+
+#: image.php:389
+#, php-format
+msgid "Moved %s to %s."
+msgstr "%s premaknjena v %s"
+
+#: lib/Exif.php:233
+msgid "Multi-Segment"
+msgstr ""
+
+#: lib/Exif.php:232
+msgid "Multi-Spot"
+msgstr ""
+
+#: lib/Forms/Upload.php:42
+msgid "Multiple Images"
+msgstr "Več slik hkrati"
+
+#: list.php:38 templates/group/owner.inc:3 lib/Ansel.php:469
+msgid "My Galleries"
+msgstr "Moje gallerije"
+
+#: templates/list/gallery_headers.inc:7
+msgid "Name"
+msgstr "Ime"
+
+#: templates/xppublish/new.inc:33
+msgid "New Gallery Description"
+msgstr "Nov opis galerije"
+
+#: templates/xppublish/new.inc:24
+msgid "New Gallery Title"
+msgstr "Nov naslov galerije"
+
+#: templates/view/description-image.inc:62
+#: templates/view/description-image.inc:63
+msgid "Next"
+msgstr "Naprej"
+
+#: lib/Exif.php:260 lib/Exif.php:267 lib/Exif.php:268 lib/Exif.php:272
+msgid "No Flash"
+msgstr "Brez bliskavice"
+
+#: xppublish.php:123
+msgid "No Gallery Name Specified."
+msgstr "Ni izbrane galerije"
+
+#: xppublish.php:119
+msgid "No Gallery Unique Name Specified."
+msgstr "Ni izbrane galerije"
+
+#: xppublish.php:190
+msgid "No file specified"
+msgstr "Ni izbrane datoteke"
+
+#: templates/view/navbar-gallery.inc:7 config/prefs.php.dist:60
+msgid "None"
+msgstr "Nič"
+
+#: lib/Exif.php:148
+#, php-format
+msgid "Normal (O deg)"
+msgstr "Normalno (0 stopinj)"
+
+#: lib/Exif.php:290
+msgid "Not defined"
+msgstr "Nedefinirano"
+
+#: image.php:107 img/upload.php:20 img/crop.php:26
+msgid "Not found or error accessing gallery."
+msgstr "Ne najdem ali ne morem dostopati do galerije"
+
+#: config/prefs.php.dist:86
+msgid "Number of galleries per page"
+msgstr "tevilo slik na strans "
+
+#: config/prefs.php.dist:94
+msgid "Number of groups per page"
+msgstr "Število slik na strans "
+
+#: config/prefs.php.dist:78
+msgid "Number of images per page"
+msgstr "Število slik na stran"
+
+#: config/prefs.php.dist:70
+msgid "Number of images per row"
+msgstr "Število slik na vrstico"
+
+#: lib/Exif.php:291
+msgid "One Chip Color Area Sensor"
+msgstr ""
+
+#: perms.php:62
+msgid ""
+"Only the owner or system administrator may change ownership or owner "
+"permissions for a share"
+msgstr "Samo lastnik ali sistemski administrator lahko spremeni lastnitvo"
+
+#: templates/crop/crop.inc:29
+msgid "Original"
+msgstr "Izvirna"
+
+#: lib/Exif.php:235
+msgid "Other"
+msgstr "Ostalo"
+
+#: config/prefs.php.dist:13 config/prefs.php.dist:20
+msgid "Other Options"
+msgstr "Ostale funkcije"
+
+#: templates/list/images.inc:25 config/prefs.php.dist:58
+msgid "Owner"
+msgstr "Lastnik"
+
+#: lib/Exif.php:234
+msgid "Partial"
+msgstr "Delni"
+
+#: templates/xppublish/login.inc:14
+msgid "Password"
+msgstr "Geslo"
+
+#: templates/xppublish/javascript.inc:63
+msgid "Photo Upload"
+msgstr "Naloži sliko"
+
+#: slideshow.php:42
+msgid "Pinwheel"
+msgstr ""
+
+#: slideshow.php:43
+msgid "Pixelate"
+msgstr ""
+
+#: templates/slideshow/slideshow.inc:67
+msgid "Play"
+msgstr "Predvajaj"
+
+#: xppublish.php:54
+msgid "Please enter your Username and Password."
+msgstr "Prosim vpiite vae uporabniko ime in geslo"
+
+#: xppublish.php:99 xppublish.php:155
+msgid "Press the \"Back\" button and try again."
+msgstr "Pojdi nazaj in poskusi e enkrat"
+
+#: image.php:283
+#, php-format
+msgid "Preview changes for %s :: %s"
+msgstr "Pregled srpememb za %s :: %s"
+
+#: templates/view/description-image.inc:48
+#: templates/view/description-image.inc:49
+msgid "Previous"
+msgstr "Prejnji"
+
+#: lib/Ansel.php:428
+msgid "Print"
+msgstr "Natisni"
+
+#: templates/view/navbar-image.inc:27
+msgid "Print on Shutterfly"
+msgstr "Natisni na Shutterfly"
+
+#: lib/Exif.php:121
+msgid "Program"
+msgstr "Program"
+
+#: templates/view/gallery.inc:171 templates/view/navbar-image.inc:16
+msgid "Properties"
+msgstr "Nastavitve"
+
+#: xppublish.php:35
+#, php-format
+msgid "Publish to %s"
+msgstr "Objavi v %s"
+
+#: xppublish.php:23
+#, php-format
+msgid "Publish your photos to %s on %s."
+msgstr "Publiciraj vae slive v %s na %s."
+
+#: slideshow.php:44
+#, fuzzy
+msgid "Radial"
+msgstr "Delni"
+
+#: slideshow.php:45
+msgid "Rain"
+msgstr "De"
+
+#: slideshow.php:50
+msgid "Random"
+msgstr "Naključe"
+
+#: lib/Block/gallery.php:34 lib/Block/gallery.php:51
+msgid "Random Gallery"
+msgstr "Poljubna galerija"
+
+#: lib/Block/random_gallery.php:3 lib/Block/random_gallery.php:27
+msgid "Random gallery"
+msgstr "Poljubna galerija"
+
+#: lib/Block/random_photo.php:3 lib/Block/random_photo.php:28
+msgid "Random photo"
+msgstr "Naključa slika"
+
+#: templates/gallery/delete_confirmation.inc:9
+msgid "Really Delete"
+msgstr "Zbriši"
+
+#: templates/gallery/delete_confirmation.inc:2
+#, php-format
+msgid "Really delete %s?"
+msgstr "Resnično elite zbrisati %s?"
+
+#: templates/gallery/delete_confirmation.inc:5
+msgid ""
+"Really delete this gallery and all of the images in it? This action cannot "
+"be undone."
+msgstr "Resnično elite zbrisati to galerijo in vse slike v njej?"
+
+#: lib/Exif.php:273
+msgid "Red Eye"
+msgstr "Rdeč oč"
+
+#: lib/Exif.php:279
+msgid "Red Eye, Auto-Mode"
+msgstr ""
+
+#: lib/Exif.php:281
+msgid "Red Eye, Auto-Mode, Return light detected"
+msgstr ""
+
+#: lib/Exif.php:280
+msgid "Red Eye, Auto-Mode, Return light not detected"
+msgstr ""
+
+#: lib/Exif.php:276
+msgid "Red Eye, Compulsory Flash"
+msgstr ""
+
+#: lib/Exif.php:278
+msgid "Red Eye, Compulsory Flash, Return light detected"
+msgstr ""
+
+#: lib/Exif.php:277
+msgid "Red Eye, Compulsory Flash, Return light not detected"
+msgstr ""
+
+#: lib/Exif.php:275
+msgid "Red Eye, Return light detected"
+msgstr ""
+
+#: lib/Exif.php:274
+msgid "Red Eye, Return light not detected"
+msgstr ""
+
+#: templates/group/header.inc:7 templates/group/header.inc:8
+#: templates/list/header.inc:10 templates/list/header.inc:11
+msgid "Refresh List"
+msgstr "Osveži stran"
+
+#: templates/crop/crop.inc:5
+msgid "Resize selection"
+msgstr "Sprememba velikosti"
+
+#: templates/slideshow/slideshow.inc:90 templates/slideshow/slideshow.inc:221
+msgid "Reverse direction"
+msgstr "Obratna smer - za nazaj"
+
+#: image.php:64
+msgid "Right"
+msgstr "Desno"
+
+#: templates/crop/crop.inc:51
+msgid "Right X"
+msgstr "Desno X"
+
+#: templates/image/edit_image.inc:26
+msgid "Rotate 180"
+msgstr "Obrni 180"
+
+#: templates/image/edit_image.inc:25
+msgid "Rotate Left"
+msgstr "Obrni levo"
+
+#: templates/image/edit_image.inc:27
+msgid "Rotate Right"
+msgstr "Obrni desno"
+
+#: image.php:29 image.php:51 lib/Forms/Upload.php:29
+msgid "Save"
+msgstr "Shrani"
+
+#: templates/captions/captions.inc:46
+msgid "Save Captions"
+msgstr "Shrani naslove"
+
+#: templates/image/preview_image.inc:23
+msgid "Save Change"
+msgstr "Shrani Spremembe"
+
+#: templates/gallery/gallery.inc:68
+msgid "Save Gallery"
+msgstr "Shrani galerijo"
+
+#: image.php:139
+msgid "Save Image"
+msgstr "Shrani sliko"
+
+#: templates/gallery/gallery.inc:59
+msgid "Screen Image Height"
+msgstr "Viina velike slike"
+
+#: templates/gallery/gallery.inc:52
+msgid "Screen Image Width"
+msgstr "irina velike slike"
+
+#: search.php:28
+msgid "Search"
+msgstr "Ič"
+
+#: search.php:60
+msgid "Search Type"
+msgstr "Način iskanja"
+
+#: search.php:97 search.php:102
+#, php-format
+msgid "Search for Images"
+msgstr "Išči slike"
+
+#: templates/view/navbar-gallery.inc:6
+msgid "Select All"
+msgstr "Izberi vse"
+
+#: templates/view/navbar-gallery.inc:7
+msgid "Select None"
+msgstr "Odznač vse"
+
+#: templates/xppublish/list.inc:6
+msgid "Select the Gallery to publish images to"
+msgstr "Izberite galerije kamor objavim slike"
+
+#: templates/view/navbar-gallery.inc:5
+#, php-format
+msgid "Select: %s, %s"
+msgstr "Izberi %s, %s"
+
+#: templates/view/navbar-gallery.inc:21
+msgid "Selected images to"
+msgstr "Izbrane slike v"
+
+#: ecard.php:25
+msgid "Send"
+msgstr "Polji"
+
+#: ecard.php:136
+#, php-format
+msgid "Send Ecard :: %s"
+msgstr "Polji E-razglednico :: %s"
+
+#: templates/view/navbar-image.inc:25
+msgid "Send as Ecard"
+msgstr "Polji kot E-razglenico"
+
+#: ecard.php:41
+msgid "Send ecard to the following address:"
+msgstr "Polji E-razglednico na sledeč naslov:"
+
+#: lib/Exif.php:355
+msgid "Sensing Method"
+msgstr ""
+
+#: templates/view/gallery.inc:110
+msgid "Set Captions"
+msgstr "Nastavi opise"
+
+#: templates/view/gallery.inc:114
+msgid "Set Gallery Permissions"
+msgstr "Nastavi pravice upogleda"
+
+#: config/prefs.php.dist:102
+msgid "Show EXIF data"
+msgstr "Prikai EXIF podatke"
+
+#: lib/Exif.php:342
+msgid "Shutter Speed"
+msgstr "Hitrost zaslonke"
+
+#: lib/Forms/Upload.php:33
+msgid "Single Image"
+msgstr "Ena slika"
+
+#: slideshow.php:46
+msgid "Slide"
+msgstr "Samodejni ogled"
+
+#: lib/Ansel.php:414
+msgid "Slide Show"
+msgstr "Samodejni ogled"
+
+#: templates/list/gallery_summaries.inc:7
+msgid "Slide show"
+msgstr "Samodejni ogled"
+
+#: slideshow.php:52
+#, php-format
+msgid "Slide show for %s"
+msgstr "Samodejni ogled za %s"
+
+#: templates/slideshow/slideshow.inc:79
+msgid "Slide show is running"
+msgstr "Samodejni ogled v teku"
+
+#: image.php:60
+msgid "Small"
+msgstr "Majhen"
+
+#: slideshow.php:47
+msgid "Snow"
+msgstr "Sneg"
+
+#: templates/list/gallery_headers.inc:6 templates/list/gallery_headers.inc:16
+msgid "Sort Direction"
+msgstr "Smer razvrščanja"
+
+#: templates/view/gallery.inc:111
+msgid "Sort Images"
+msgstr "Razvrsti slike"
+
+#: config/prefs.php.dist:14
+msgid "Sort Options"
+msgstr "Nastvavitve vrstnega redu"
+
+#: templates/list/gallery_headers.inc:17
+msgid "Sort by Description"
+msgstr "Uredi po opisu"
+
+#: templates/list/gallery_headers.inc:7
+msgid "Sort by Name"
+msgstr "Razvrsti po imenu"
+
+#: gallery.php:176
+#, php-format
+msgid "Specified screen image height is too large, value has been set to %s"
+msgstr "Izbrana velikost irine velike slike je prevelika. Največe lahko %s."
+
+#: gallery.php:173
+#, php-format
+msgid "Specified screen image height is too small, value has been set to %s"
+msgstr "Izbrana velikost irine velike slike je prevelika. Največe lahko %s."
+
+#: gallery.php:167
+#, php-format
+msgid "Specified screen image width is too large, value has been set to %s"
+msgstr "Izbrana velikost irine velike slike je prevelika. Največe lahko %s."
+
+#: gallery.php:164
+#, php-format
+msgid "Specified screen image width is too small, value has been set to %s"
+msgstr "Izbrana velikost irine velike slike je prevelika. Največe lahko %s."
+
+#: gallery.php:158
+#, php-format
+msgid "Specified thumbnail height is too large, value has been set to %s"
+msgstr "Izbrana velikost irine velike slike je prevelika. Največe lahko %s."
+
+#: gallery.php:155
+#, php-format
+msgid "Specified thumbnail height is too small, value has been set to %s"
+msgstr "Izbrana velikost irine velike slike je prevelika. Največe lahko %s."
+
+#: gallery.php:149
+#, php-format
+msgid "Specified thumbnail width is too large, value has been set to %s"
+msgstr "Izbrana velikost irine velike slike je prevelika. Največe lahko %s."
+
+#: gallery.php:146
+#, php-format
+msgid "Specified thumbnail width is too small, value has been set to %s"
+msgstr "Izbrana velikost irine velike slike je prevelika. Največe lahko %s."
+
+#: slideshow.php:48
+msgid "Spiral"
+msgstr ""
+
+#: lib/Exif.php:231
+msgid "Spot"
+msgstr "port"
+
+#: search.php:88
+msgid "Start Date"
+msgstr "Začetni datum"
+
+#: templates/slideshow/slideshow.inc:76 templates/slideshow/slideshow.inc:221
+msgid "Stop"
+msgstr "Ustavi"
+
+#: scripts/recursive_import.php:156 scripts/ansel.php:187
+#: scripts/ansel.php:266
+#, php-format
+msgid "Storing image \"%s\"..."
+msgstr "Shranjujem sliko '%s'..."
+
+#: slideshow.php:49
+#, fuzzy
+msgid "Stretch"
+msgstr "Ič"
+
+#: templates/view/subgallery_header.inc:5
+#, php-format
+msgid "Subgalleries of %s"
+msgstr "Podgalerije %s"
+
+#: scripts/recursive_import.php:166 scripts/ansel.php:207
+#, fuzzy, php-format
+msgid "Successfully added %d image(s) (%s) to gallery \"%s\" from \"%s\"."
+msgstr "Uspeno dodano %s slik (%s) v galerijo  \"%s\" iz \"%s\"."
+
+#: scripts/ansel.php:194
+#, php-format
+msgid "Successfully added image \"%s\" to gallery \"%s\"."
+msgstr "Upeno dodana slike '%s' v galerijo '%s'."
+
+#: gallery/delete.php:33
+#, php-format
+msgid "Successfully deleted %s."
+msgstr "Uspešno izbrisana %s."
+
+#: lib/Exif.php:242
+msgid "Sunny"
+msgstr "Sonči"
+
+#: search.php:66
+msgid "Text Field"
+msgstr "Tekstovno polje"
+
+#: search.php:61
+msgid "Text Fields"
+msgstr "Tekstovna polja"
+
+#: search.php:67
+msgid "Text Operator"
+msgstr "Operator"
+
+#: search.php:68
+msgid "Text Value"
+msgstr "Vrednost besedila"
+
+#: scripts/ansel.php:233
+#, php-format
+msgid "The directory \"%s\" doesn't exist."
+msgstr "Mapa '%s' ne obstaja"
+
+#: scripts/ansel.php:212
+#, php-format
+msgid "The directory \"%s\" had no valid images."
+msgstr "Mapa '%s' ne vsebuje slik"
+
+#: scripts/ansel.php:250
+#, php-format
+msgid "The directory \"%s\" is empty."
+msgstr "Mapa '%s' je prazna"
+
+#: lib/Ansel.php:321
+#, php-format
+msgid "The file \"%s\" doesn't exist."
+msgstr "Datoteka \"%s\" ne obstaja"
+
+#: lib/api.php:167 img/upload.php:104
+msgid "The file you uploaded does not appear to be a valid image."
+msgstr "Naloena daoteka ni poznan tip slike"
+
+#: scripts/recursive_import.php:125
+#, php-format
+msgid "The gallery \"%s\" (%s) was created successfully."
+msgstr "Galerija \"%s\" (%s) je bila uspeno tvojena."
+
+#: lib/Ansel.php:221
+#, php-format
+msgid "The gallery \"%s\" could not be created: %s"
+msgstr "Ne morem tvoriti galerijo '%s': %s"
+
+#: gallery.php:273 xppublish.php:129 scripts/recursive_import.php:123
+#: scripts/ansel.php:117
+#, php-format
+msgid "The gallery \"%s\" couldn't be created: %s"
+msgstr "Ne morem tvoriti galerijo '%s': %s"
+
+#: gallery.php:278 xppublish.php:133 scripts/ansel.php:121
+#, php-format
+msgid "The gallery \"%s\" was created successfully."
+msgstr "Galerijo '%s' je bila uspeno tvojenja."
+
+#: gallery.php:220
+msgid "The gallery was saved."
+msgstr "Galerija je bila shranjena"
+
+#: img/upload.php:111
+msgid "The image was saved."
+msgstr "Slika je bila shranjena"
+
+#: templates/slideshow/slideshow.inc:70
+msgid "The slide show is stopped, click [Play] to resume."
+msgstr "Samodejni ogled je bil ustavljen. kliknite [Predvajaj] za nadaljevanje"
+
+#: image.php:367 image.php:416
+msgid "The target gallery was not found."
+msgstr "Namenska galerija ni bila najdena."
+
+#: img/upload.php:126
+msgid ""
+"The uploaded file appears to be empty. It may not exist on your computer."
+msgstr "Naloena slika je prazna."
+
+#: gallery.php:78
+#, php-format
+msgid "There are no images in %s to download."
+msgstr "Ni slik v %s za shraniti"
+
+#: slideshow.php:31
+msgid "There are no images in this gallery to show."
+msgstr "Ni slik v galeriji."
+
+#: templates/captions/captions.inc:48 templates/view/gallery.inc:133
+msgid "There are no images in this gallery."
+msgstr "Ni slik v galeriji"
+
+#: list.php:63 lib/Block/random_photo.php:38 lib/Block/random_gallery.php:37
+msgid "There are no photo galleries available."
+msgstr "Ni galerij."
+
+#: scripts/ansel.php:190 scripts/ansel.php:269
+#, php-format
+msgid "There was a problem adding the image \"%s\" to gallery \"%s\"."
+msgstr "Prilo je do napake pri dodajanju slike '%s' v galerijo '%s'"
+
+#: image.php:427
+#, php-format
+msgid "There was a problem copying \"%s\": %s"
+msgstr "Problem pri kopiranju slik '%s': %s"
+
+#: image.php:387
+#, php-format
+msgid "There was a problem deleting \"%s\": %s"
+msgstr "Prilo je do napake pri birsanju %s: %s"
+
+#: lib/api.php:282
+#, php-format
+msgid "There was a problem deleting %s: %s"
+msgstr "Prilo je do napake pri brisanju %s: %s"
+
+#: gallery/delete.php:31
+#, php-format
+msgid "There was a problem deleting %s: %s."
+msgstr "Prilo je do napake pri brisanju %s: %s"
+
+#: image.php:343
+#, php-format
+msgid "There was a problem deleting images: %s"
+msgstr "Problem brisanja slik: %s"
+
+#: image.php:383
+#, php-format
+msgid "There was a problem moving \"%s\": %s"
+msgstr "Prilo je do napke pri premikanju slike '%s': %s"
+
+#: img/upload.php:89 img/upload.php:117
+#, php-format
+msgid "There was a problem saving the image: %s"
+msgstr "Problem shranjevanja slike: %s"
+
+#: xppublish.php:199
+msgid "There was a problem uploading the image."
+msgstr "Problem nalaganja slike"
+
+#: img/upload.php:124
+#, php-format
+msgid "There was a problem uploading the image: %s"
+msgstr "Problem nalaganja slike: %s"
+
+#: img/upload.php:56 img/upload.php:76
+#, php-format
+msgid "There was an error processing the uploaded archive: %s"
+msgstr "Napaka pri nalaganju arhiva: %s"
+
+#: ecard.php:127
+#, php-format
+msgid "There was an error sending your message: %s"
+msgstr "Problem pri poiljanju slik: %s"
+
+#: lib/Exif.php:293
+msgid "Three Chip Color Area Sensor"
+msgstr ""
+
+#: search.php:164 search.php:231
+msgid "Thumbnail"
+msgstr "Ikona"
+
+#: templates/gallery/gallery.inc:45
+msgid "Thumbnail Height"
+msgstr "Viina ikone"
+
+#: templates/gallery/gallery.inc:38
+msgid "Thumbnail Width"
+msgstr "irina ikone"
+
+#: image.php:60
+msgid "Tiny"
+msgstr "Majhna"
+
+#: lib/Ansel.php:382
+msgid "Today"
+msgstr "Danes"
+
+#: image.php:68
+msgid "Top"
+msgstr "Zgornje"
+
+#: templates/crop/crop.inc:26
+msgid "Top Y"
+msgstr "Zgornji Y"
+
+#: templates/slideshow/slideshow.inc:245
+msgid "Transition"
+msgstr "Tranzicija"
+
+#: lib/Exif.php:295
+msgid "Trilinear Sensor"
+msgstr ""
+
+#: lib/Exif.php:244
+msgid "Tungsten"
+msgstr ""
+
+#: lib/Exif.php:123
+msgid "Tv-priority"
+msgstr "Av"
+
+#: lib/Exif.php:292
+msgid "Two Chip Color Area Sensor"
+msgstr ""
+
+#: lib/Exif.php:306
+msgid "Uncalibrated"
+msgstr "Brez nalova"
+
+#: templates/gallery/gallery.inc:69
+msgid "Undo Changes"
+msgstr "Povrni spremembe"
+
+#: list.php:50 templates/group/category.inc:8
+msgid "Unfiled"
+msgstr "Brez nalova"
+
+#: lib/Exif.php:228
+msgid "Unknown"
+msgstr "Neznan"
+
+#: gallery.php:119
+msgid "Unknown gallery"
+msgstr "Neznana galerija"
+
+#: lib/Ansel.php:209
+msgid "Unnamed"
+msgstr "Neimenovana"
+
+#: xppublish.php:141
+msgid "Untitled"
+msgstr "Brez nalova"
+
+#: perms.php:234
+#, php-format
+msgid "Updated %s."
+msgstr "Ažurirana %s."
+
+#: templates/view/gallery.inc:107
+msgid "Upload Images"
+msgstr "Naloži nove slike"
+
+#: templates/xppublish/javascript.inc:63
+#, php-format
+msgid "Upload Photos to %s"
+msgstr "Naloži slike v %s"
+
+#: img/upload.php:28
+msgid "Upload new images"
+msgstr "Naloži nove slike"
+
+#: lib/Exif.php:152
+#, php-format
+msgid "Upsidedown"
+msgstr ""
+
+#: lib/Exif.php:154
+#, php-format
+msgid "Upsidedown Mirrored"
+msgstr ""
+
+#: scripts/recursive_import.php:185 scripts/ansel.php:288
+#, php-format
+msgid "Usage: %s [OPTIONS]..."
+msgstr ""
+
+#: templates/xppublish/new.inc:17
+msgid "Use Default"
+msgstr "Uporabi prenastavljeno"
+
+#: ecard.php:32
+msgid "Use the following return address:"
+msgstr "Uporabi povratni nalslov:"
+
+#: lib/Exif.php:353
+msgid "User Comment"
+msgstr "Komentar"
+
+#: templates/xppublish/login.inc:11
+msgid "Username"
+msgstr "Uporabniko ime"
+
+#: xppublish.php:50
+msgid "Username and Password are incorrect."
+msgstr "Uporabniko ime in geslo sta nepravilna"
+
+#: scripts/recursive_import.php:79
+msgid "Username or password is incorrect."
+msgstr "Uporabniko ime ali geslo je nepravilno"
+
+#: image.php:69
+msgid "Vertical Alignment"
+msgstr "Vertikalna poravnava"
+
+#: templates/list/gallery_summaries.inc:2
+#: templates/list/gallery_summaries.inc:10 templates/list/images.inc:9
+#: templates/list/images.inc:12 lib/Block/gallery.php:71
+#: lib/Block/random_gallery.php:42
+#, php-format
+msgid "View %s"
+msgstr "Preglej %s"
+
+#: lib/Block/random_photo.php:58
+msgid "View Photo"
+msgstr "Pogelj sliko"
+
+#: image.php:241 image.php:247 templates/image/edit_image.inc:40
+msgid "Watermark"
+msgstr ""
+
+#: image.php:61
+msgid "Watermark Font"
+msgstr ""
+
+#: lib/Exif.php:356
+msgid "White Balance"
+msgstr ""
+
+#: lib/Exif.php:338
+msgid "Width"
+msgstr "irina"
+
+#: lib/Exif.php:340
+msgid "X Resolution"
+msgstr "X resolucija"
+
+#: lib/Exif.php:341
+msgid "Y Resolution"
+msgstr "Y resolucija"
+
+#: lib/Ansel.php:384
+msgid "Yesterday"
+msgstr "Včraj"
+
+#: image.php:178
+msgid "You are not allowed to edit this image."
+msgstr "Nimate dovoljen za ogled izbrane slike."
+
+#: xppublish.php:91 xppublish.php:184
+msgid "You cannot add photos in that gallery."
+msgstr "Ne more vstaviti slik v to galerijo"
+
+#: scripts/ansel.php:136
+#, php-format
+msgid "You cannot add photos to the gallery \"%s\"."
+msgstr "Ne more dodati slik v galerijo '%s'"
+
+#: gallery/delete.php:21
+#, php-format
+msgid "You cannot delete %s, it contains nested galleries."
+msgstr "Ne morete zbrisati %s, ker vsebuje podgalerije."
+
+#: gallery.php:48 gallery.php:235
+#, php-format
+msgid "You do not have permission to add children to %s."
+msgstr "Nimate privilegija a dodati podgalerijo v %s"
+
+#: gallery/sort.php:24
+msgid "You do not have permission to change this gallery."
+msgstr "Nimate privilegija za ogled galerije %s"
+
+#: gallery.php:322
+#, php-format
+msgid "You do not have permission to empty %s."
+msgstr "Nimate dovljenja za sprazniti galerijo %s"
+
+#: slideshow.php:24 view.php:27 lib/Block/gallery.php:101
+msgid "You do not have permission to view this gallery."
+msgstr "Nimate privilegija za ogled galerije %s"
+
+#: ecard.php:100
+msgid ""
+"You have been sent an Ecard. To view the Ecard, you must be able to view "
+"text/html messages in your mail reader. If you are viewing this message, "
+"then most likely your mail reader does not support viewing text/html "
+"messages."
+msgstr ""
+"Poslali ste E-razglednico. Za njen ogled morate imeti pregledovalnik "
+"elektronske pote ki zna prikazovati text/html sporočla. č vidite to "
+"sporočlo, vrjetno na pregledovalnik tega ni sposoben."
+
+#: templates/view/gallery.inc:48
+msgid "You must choose a gallery to copy images to."
+msgstr "Izberite glerijo v katero skopiramo slike."
+
+#: templates/view/gallery.inc:78
+msgid "You must choose a gallery to move images to."
+msgstr "Izbrati morate galerijo v katero gre premakniti slike."
+
+#: ecard.php:76
+msgid "You must enter an e-mail address to send the message to."
+msgstr "Vnesti morate va email naslov."
+
+#: ecard.php:71
+msgid "You must enter your e-mail address."
+msgstr "Vnesti morate va email naslov."
+
+#: gallery.php:255
+msgid "You must provide a display name for your new gallery."
+msgstr "Navesti morate ime galerije"
+
+#: scripts/recursive_import.php:88
+msgid "You must specify a valid directory."
+msgstr ""
+
+#: scripts/recursive_import.php:84
+msgid "You must specify a valid username and password."
+msgstr ""
+
+#: lib/Forms/Upload.php:51
+msgid "Zip File Upload"
+msgstr "Naloži zip datoteko"
+
+#: templates/crop/crop.inc:36
+msgid "Zoom"
+msgstr "Povečva"
+
+#: lib/Ansel.php:403
+msgid "_Galleries"
+msgstr "_Gallerije"
+
+#: lib/Ansel.php:405
+msgid "_My Galleries"
+msgstr "Moje gallerije"
+
+#: lib/Ansel.php:423
+msgid "_New Gallery"
+msgstr "Nova galerija"
+
+#: lib/Ansel.php:432
+msgid "_Search"
+msgstr "Najdi"
+
+#: lib/Exif.php:303
+msgid "sRGB"
+msgstr "sRGB"
diff --git a/ansel/po/sv_SE.po b/ansel/po/sv_SE.po
new file mode 100644 (file)
index 0000000..5323858
--- /dev/null
@@ -0,0 +1,2007 @@
+# Horde Swedish translation
+# Copyright 2002-2005 Andreas Dahlén.
+# Andreas Dahlén <andreas@dahlen.ws>, 2005.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Horde 3.0-cvs\n"
+"Report-Msgid-Bugs-To: dev@lists.horde.org\n"
+"POT-Creation-Date: 2005-06-14 22:08+0200\n"
+"PO-Revision-Date: 2005-06-16 15:48+0100\n"
+"Last-Translator: Andreas Dahlén <andreas@dahlen.ws>\n"
+"Language-Team: Swedish <dev@lists.horde.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=iso-8859-1\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: scripts/recursive_import.php:110
+#, php-format
+msgid "\"%s\" is not a directory."
+msgstr "'%s' är inte en katalog"
+
+#: image.php:287
+#, php-format
+msgid "%d images were successfully extracted and uploaded."
+msgstr "%d bilder extraherades och laddades upp."
+
+#: templates/group/owner.inc:51 templates/group/category.inc:51
+#, php-format
+msgid "%s Galleries"
+msgstr "%s gallerier"
+
+#: lib/Exif.php:288
+#, php-format
+msgid "%s bytes"
+msgstr "%s bytes"
+
+#: templates/list/images.inc:4
+#, php-format
+msgid "%s images"
+msgstr "%s bilder"
+
+#: lib/Exif.php:141 lib/Exif.php:147
+#, php-format
+msgid "%s pixels"
+msgstr "%s pixlar"
+
+#: lib/Exif.php:171 lib/Exif.php:186
+#, php-format
+msgid "%s sec"
+msgstr "%s sekunder"
+
+#: templates/view/subgallery_header.inc:3
+#, php-format
+msgid "%s to %s of %s subgalleries"
+msgstr "%s till %s av %s undergallerier"
+
+#: list.php:46 lib/Ansel.php:476 templates/group/owner.inc:12
+#, php-format
+msgid "%s's Galleries"
+msgstr "%s's gallerier"
+
+#: gallery.php:257
+#, php-format
+msgid ""
+"'%s' is an invalid unique name; it may already be taken. Please choose a "
+"different unique name."
+msgstr "'%s' är ett felaktigt namn; det kanske redan används. Var god välj "
+
+#: lib/Gallery.php:1156
+#, php-format
+msgid "(c) %s %s"
+msgstr "(c) %s %s"
+
+#: templates/xppublish/new.inc:14
+msgid "(must be unique)"
+msgstr "(måste vara unikt)"
+
+#: scripts/ansel.php:296
+msgid "-a, --add[=filename]         Add local file to selected gallery"
+msgstr ""
+
+#: scripts/ansel.php:294
+msgid "-c, --create[=shortname/name/description]  Create gallery (and use it)"
+msgstr ""
+
+#: scripts/ansel.php:297
+msgid ""
+"-d, --dir[=directory]        Add all files from the directory to the "
+"selected\n"
+"                             gallery"
+msgstr ""
+
+#: scripts/recursive_import.php:189
+msgid ""
+"-d, --dir[=directory]        Recursively add all files from the directory, "
+"creating\n"
+"                             a gallery for each directory"
+msgstr ""
+
+#: scripts/ansel.php:295
+msgid "-g, --gallery[=shortname]    Select gallery to use"
+msgstr ""
+
+#: scripts/ansel.php:292 scripts/recursive_import.php:188
+msgid "-h, --help                   Show this help"
+msgstr ""
+
+#: scripts/ansel.php:293
+msgid ""
+"-l, --list                   List galleries or images (if combined with -g)"
+msgstr ""
+
+#: scripts/ansel.php:299 scripts/recursive_import.php:191
+msgid "-p, --password[=password]    Horde login password"
+msgstr ""
+
+#: scripts/ansel.php:298 scripts/recursive_import.php:190
+msgid "-u, --username[=username]    Horde login username"
+msgstr ""
+
+#: templates/group/owner.inc:51 templates/group/category.inc:51
+msgid "1 Gallery"
+msgstr "1 galleri"
+
+#: templates/list/images.inc:4
+msgid "1 image"
+msgstr "1 bild"
+
+#: image.php:285
+msgid "1 image was successfully extracted and uploaded."
+msgstr "1 bild extraherades och laddades upp."
+
+#: templates/slideshow/slideshow.inc:233
+msgid "1 minute"
+msgstr "1 minut"
+
+#: templates/slideshow/slideshow.inc:224
+msgid "1 second"
+msgstr "1 sekund"
+
+#: templates/slideshow/slideshow.inc:229
+msgid "10 seconds"
+msgstr "10 sekunder"
+
+#: templates/slideshow/slideshow.inc:230
+msgid "15 seconds"
+msgstr "15 sekunder"
+
+#: templates/slideshow/slideshow.inc:225
+msgid "2 seconds"
+msgstr "2 sekunder"
+
+#: templates/slideshow/slideshow.inc:226
+msgid "3 seconds"
+msgstr "3 sekunder"
+
+#: templates/slideshow/slideshow.inc:231
+msgid "30 seconds"
+msgstr "30 sekunder"
+
+#: templates/slideshow/slideshow.inc:227
+msgid "4 seconds"
+msgstr "4 sekunder"
+
+#: templates/slideshow/slideshow.inc:232
+msgid "45 seconds"
+msgstr "45 sekunder"
+
+#: templates/slideshow/slideshow.inc:228
+msgid "5 seconds"
+msgstr "5 sekunder"
+
+#: lib/Exif.php:162
+#, php-format
+msgid "90 deg CCW"
+msgstr ""
+
+#: lib/Exif.php:164
+#, php-format
+msgid "90 deg CCW Mirrored"
+msgstr ""
+
+#: lib/Exif.php:166
+#, php-format
+msgid "90 deg CW"
+msgstr ""
+
+#: lib/Exif.php:160
+#, php-format
+msgid "90 deg CW Mirrored"
+msgstr ""
+
+#: lib/Exif.php:133
+msgid "A-DEP"
+msgstr ""
+
+#: gallery.php:311 lib/api.php:259
+#, php-format
+msgid "Access denied deleting gallery %s."
+msgstr "Behörighet saknas att radera galleri %s."
+
+#: image.php:524 image.php:560
+#, php-format
+msgid "Access denied deleting image %s."
+msgstr "Behörighet saknas att radera bild %s."
+
+#: gallery.php:81
+#, php-format
+msgid "Access denied downloading %s."
+msgstr "Behörighet saknas att ladda ner %s."
+
+#: gallery.php:138
+msgid "Access denied editing galleries."
+msgstr "Behörighet saknas att ändra gallerier."
+
+#: gallery.php:194
+#, php-format
+msgid "Access denied saving gallery %s."
+msgstr "Behörighet saknas att spara galleri %s."
+
+#: image.php:198 image.php:369
+#, php-format
+msgid "Access denied saving image to %s."
+msgstr "Behörighet saknas att spara bild till %s."
+
+#: captions.php:29
+#, php-format
+msgid "Access denied setting captions for %s."
+msgstr "Behörighet saknas att sätta rubriker för %s."
+
+#: image.php:160
+msgid "Add Image"
+msgstr "Lägg till bild"
+
+#: templates/view/gallery.inc:101
+msgid "Add Subgallery"
+msgstr "Lägg till undergalleri"
+
+#: gallery.php:40 gallery.php:259 gallery.php:267
+msgid "Adding A New Gallery"
+msgstr "Lägger till nytt galleri"
+
+#: gallery.php:73
+#, php-format
+msgid "Adding A Subgallery to %s"
+msgstr "Lägger till undergalleri till %s"
+
+#: scripts/recursive_import.php:170
+msgid "Adding subdirectories:"
+msgstr "Lägger till underkataloger:"
+
+#: search.php:83
+msgid "After"
+msgstr "efter"
+
+#: templates/view/navbar-gallery.inc:5
+msgid "All"
+msgstr "Alla"
+
+#: lib/Ansel.php:121
+#, php-format
+msgid "An error occurred counting galleries: %s"
+msgstr "Fel vid räkning av gallerier: %s"
+
+#: lib/Ansel.php:62
+#, php-format
+msgid "An error occurred listing galleries: %s"
+msgstr "Fel vid listning av gallerier: %s"
+
+#: lib/Exif.php:347
+msgid "Aperture"
+msgstr "Bländare"
+
+#: templates/gallery/gallery.inc:93
+msgid "Are you sure you want to delete all of the images in this gallery?"
+msgstr "Är du säker att du vill radera alla bilder i aktuellt galleri?"
+
+#: templates/view/gallery.inc:43
+msgid "Are you sure you want to delete the selected images?"
+msgstr "Är du säker att du vill radera valda bilder?"
+
+#: templates/view/gallery.inc:110
+msgid ""
+"Are you sure you want to delete this gallery? All images in it will be "
+"deleted."
+msgstr ""
+"Är du säker att du vill radera aktuellt galleri? Alla bilder kommer att "
+"raderas."
+
+#: lib/Exif.php:360
+msgid "Artist"
+msgstr "Artist"
+
+#: config/prefs.php.dist:55
+msgid "Ascending"
+msgstr "Stigande"
+
+#: perms.php:50
+msgid "Attempt to edit a non-existent share."
+msgstr "Försök att ändra en utdelning som inte finns."
+
+#: config/prefs.php.dist:112
+msgid "Automatically generate unique name for new galleries?"
+msgstr "Generera iautomatiskt unika namn på nya gallerier?"
+
+#: lib/Exif.php:129
+msgid "Av-priority"
+msgstr "Av-prioritet"
+
+#: lib/Exif.php:231
+msgid "Average"
+msgstr ""
+
+#: search.php:84
+msgid "Before"
+msgstr "Före"
+
+#: search.php:53
+msgid "Begins With"
+msgstr "Börjar med"
+
+#: search.php:82
+msgid "Between"
+msgstr "Mellan"
+
+#: slideshow.php:35
+msgid "Blend"
+msgstr "Sammansmältning"
+
+#: slideshow.php:36
+msgid "Blinds"
+msgstr "Sammansmälter"
+
+#: image.php:113
+msgid "Bottom"
+msgstr "Botten"
+
+#: lib/Exif.php:332
+msgid "Camera Make"
+msgstr "Kameramärke"
+
+#: lib/Exif.php:333
+msgid "Camera Model"
+msgstr "Kameramodell"
+
+#: lib/Exif.php:358
+msgid "Camera Orientation"
+msgstr "Kameraorientering"
+
+#: lib/Ansel.php:321
+#, php-format
+msgid "Can't get unknown file type '%s'."
+msgstr "Kan inte hämta okänd filtyp '%s'."
+
+#: templates/image/preview_image.inc:29
+msgid "Cancel Change"
+msgstr "Avbryt ändring"
+
+#: image.php:37 image.php:65 templates/search/search.html:9
+msgid "Caption"
+msgstr "Rubrik"
+
+#: captions.php:52 templates/view/gallery.inc:103
+msgid "Caption Editor"
+msgstr "Rubrikeditor"
+
+#: templates/view/gallery.inc:103
+msgid "Captions"
+msgstr "Rubriker"
+
+#: captions.php:47
+msgid "Captions Saved."
+msgstr "Rubriker sparade."
+
+#: config/prefs.php.dist:66
+msgid "Category"
+msgstr "Kategori"
+
+#: image.php:109
+msgid "Center"
+msgstr "Centrerad"
+
+#: lib/Exif.php:232
+msgid "Center Weighted Average"
+msgstr ""
+
+#: config/prefs.php.dist:22
+msgid "Change your display options."
+msgstr "Ändra visningsinställningar."
+
+#: config/prefs.php.dist:30
+msgid "Change your gallery creation options."
+msgstr "Ändra inställningar för hur gallerier skapas."
+
+#: config/prefs.php.dist:15
+msgid "Change your gallery sorting options."
+msgstr "Ändra inställningar för sortering av gallerier."
+
+#: slideshow.php:37
+msgid "Checkerboard"
+msgstr "Sckackbräde"
+
+#: templates/xppublish/list.inc:8
+msgid "Choose an existing gallery"
+msgstr "Välj ett befintligt galleri"
+
+#: lib/Exif.php:245
+msgid "Cloudy"
+msgstr "Molningt"
+
+#: lib/Exif.php:296
+msgid "Color Sequential Area Sensor"
+msgstr ""
+
+#: lib/Exif.php:298
+msgid "Color Sequential Linear Sensor"
+msgstr ""
+
+#: lib/Exif.php:355
+msgid "Color Space"
+msgstr ""
+
+#: ecard.php:42
+msgid "Comments:"
+msgstr "Kommentarer:"
+
+#: lib/Exif.php:266
+msgid "Compulsory Flash"
+msgstr "Tvingande blixt"
+
+#: lib/Exif.php:268
+msgid "Compulsory Flash, Return light detected"
+msgstr ""
+
+#: lib/Exif.php:267
+msgid "Compulsory Flash, Return light not detected"
+msgstr ""
+
+#: search.php:55
+msgid "Contains"
+msgstr "Innehåller"
+
+#: image.php:612
+#, php-format
+msgid "Copied %s to %s."
+msgstr "Kopierade %s till %s."
+
+#: templates/view/navbar-gallery.inc:19
+msgid "Copy"
+msgstr "Kopiera"
+
+#: lib/Exif.php:359
+msgid "Copyright"
+msgstr "Copyright"
+
+#: scripts/ansel.php:162
+msgid "Couldn't list galleries."
+msgstr "Kan inte visa gallerier."
+
+#: scripts/ansel.php:40
+msgid "Couldn't read command-line options."
+msgstr "Kunde inte läsa kommandoraden."
+
+#: templates/xppublish/list.inc:18
+msgid "Create"
+msgstr "Skapa"
+
+#: templates/xppublish/new.inc:8
+msgid "Create a new Gallery"
+msgstr "Skapa nytt galleri"
+
+#: templates/xppublish/list.inc:16
+msgid "Create a new gallery instead"
+msgstr "Skapa nytt galleri istället"
+
+#: templates/list/images.inc:20
+msgid "Created"
+msgstr "Skapad"
+
+#: scripts/recursive_import.php:119
+#, php-format
+msgid "Creating gallery: \"%s\" (%s)"
+msgstr ""
+
+#: lib/Ansel.php:524 templates/image/edit_image.inc:24
+#: templates/crop/crop.inc:44
+msgid "Crop"
+msgstr "Beskära"
+
+#: crop.php:56
+msgid "Crop Image"
+msgstr "Beskär bild"
+
+#: templates/crop/crop.inc:55
+msgid "Cropped"
+msgstr "Beskuren"
+
+#: lib/Exif.php:249
+msgid "Custom"
+msgstr ""
+
+#: image.php:102
+msgid "Custom Watermark"
+msgstr "Vattenmärke"
+
+#: config/prefs.php.dist:128
+msgid "Custom watermark to use for images"
+msgstr "Vattenmärke som läggs till på bilder"
+
+#: config/prefs.php.dist:44
+msgid "Date Created"
+msgstr "Datum skapad"
+
+#: search.php:86
+msgid "Date Field"
+msgstr "Datumfält"
+
+#: search.php:61
+msgid "Date Fields"
+msgstr "Datumfält"
+
+#: lib/Exif.php:338
+msgid "Date Image Digitized"
+msgstr "Datum bild digitaliserades"
+
+#: lib/Exif.php:336
+msgid "Date Image Modified"
+msgstr "Datum bild ändrad"
+
+#: lib/Exif.php:337
+msgid "Date Image Taken"
+msgstr "Datum bild tagen"
+
+#: config/prefs.php.dist:45
+msgid "Date Modified"
+msgstr "Datum ändrar"
+
+#: search.php:87
+msgid "Date Operator"
+msgstr "Datum operator"
+
+#: search.php:36
+msgid "Date Uploaded"
+msgstr "Datum uppladdad"
+
+#: config/prefs.php.dist:46
+msgid "Default sorting criteria:"
+msgstr "Förvald sorteringsordning:"
+
+#: config/prefs.php.dist:57
+msgid "Default sorting direction:"
+msgstr "Förvald sorteringsriktning:"
+
+#: templates/slideshow/slideshow.inc:222
+msgid "Delay"
+msgstr "Fördröjning"
+
+#: templates/image/edit_image.inc:46 templates/view/gallery.inc:110
+#: templates/view/navbar-image.inc:19 templates/view/navbar-gallery.inc:11
+msgid "Delete"
+msgstr "Radera"
+
+#: templates/view/gallery.inc:110
+msgid "Delete Gallery"
+msgstr "Radera galleri"
+
+#: image.php:528
+msgid "Deleted the image."
+msgstr "Raderade bilden."
+
+#: config/prefs.php.dist:56
+msgid "Descending"
+msgstr "Fallande"
+
+#: search.php:35 templates/list/gallery_headers.inc:22
+msgid "Description"
+msgstr "Beskrivning"
+
+#: slideshow.php:38
+msgid "Diagonal"
+msgstr "Diagonal"
+
+#: config/prefs.php.dist:21
+msgid "Display Options"
+msgstr "Visningsinställningar"
+
+#: templates/list/header.inc:7
+#, php-format
+msgid "Displaying from %s to %s of %s galleries"
+msgstr "Visar från %s till %s av %s gallerier"
+
+#: templates/group/header.inc:3
+#, php-format
+msgid "Displaying from %s to %s of %s groups"
+msgstr "Visar från %s till %s av %s grupper"
+
+#: templates/view/gallery.inc:87
+#, php-format
+msgid "Displaying from %s to %s of %s images"
+msgstr "Visar från %s till %s av %s bilder"
+
+#: templates/view/navbar-image.inc:19
+#, php-format
+msgid "Do you want to permanently delete %s?"
+msgstr "Vill du radera %s?"
+
+#: search.php:56
+msgid "Does Not Contain"
+msgstr "Innehåller inte"
+
+#: slideshow.php:39
+msgid "Doors"
+msgstr "Döörar"
+
+#: lib/Ansel.php:411
+msgid "Download"
+msgstr "Hämta"
+
+#: templates/view/navbar-image.inc:27
+msgid "Download Full Image"
+msgstr "Ladda ner bilden"
+
+#: templates/list/gallery_headers.inc:17
+#: templates/list/gallery_summaries.inc:11
+msgid "Download all images in a .zip file"
+msgstr "Ladda ner alla bilder (som en zip-fil)"
+
+#: xppublish.php:210
+#, php-format
+msgid "ERROR: %s"
+msgstr "Fel: %s"
+
+#: lib/Exif.php:123
+msgid "Easy shooting"
+msgstr ""
+
+#: ecard.php:91
+msgid "Ecard - "
+msgstr "E-vukort - "
+
+#: lib/Ansel.php:519 templates/view/gallery.inc:163
+#: templates/view/navbar-image.inc:16
+msgid "Edit"
+msgstr "Ändra"
+
+#: image.php:353
+#, php-format
+msgid "Edit %s :: %s"
+msgstr "Ändra %s :: %s"
+
+#: image.php:436
+msgid "Edit Image"
+msgstr "Ändra bild"
+
+#: perms.php:241
+msgid "Edit Permissions"
+msgstr "Öndra behörighet"
+
+#: perms.php:244
+#, php-format
+msgid "Edit Permissions for %s"
+msgstr "Ändra behörigheter för %s"
+
+#: image.php:212
+msgid "Edit an image"
+msgstr "Ändra en bild"
+
+#: image.php:173
+#, php-format
+msgid "Edit properties :: %s"
+msgstr "Ändra egenskaper :: %s"
+
+#: templates/gallery/gallery.inc:86
+msgid "Empty Gallery"
+msgstr "Töm galleri"
+
+#: search.php:89
+msgid "End Date"
+msgstr "Slutdatum"
+
+#: search.php:54
+msgid "Ends With"
+msgstr "Slutar med"
+
+#: captions.php:23
+#, php-format
+msgid "Error accessing %s: %s"
+msgstr "Fel vid åtkomst av %s: %s"
+
+#: lib/Exif.php:344
+msgid "Exposure"
+msgstr "Exponering"
+
+#: lib/Exif.php:350
+msgid "Exposure Bias"
+msgstr "Diagonal exponering"
+
+#: lib/Exif.php:351
+msgid "Exposure Mode"
+msgstr "Exponeringsläge"
+
+#: lib/Exif.php:348
+msgid "F-Number"
+msgstr "F-nummer"
+
+#: image.php:71
+#, php-format
+msgid "File %s"
+msgstr "Fil %s"
+
+#: lib/Exif.php:335
+msgid "File Size"
+msgstr "Filstorlek"
+
+#: image.php:61 image.php:79
+msgid "File to upload"
+msgstr "Fil att ladda upp"
+
+#: search.php:37
+msgid "Filename"
+msgstr "Filnamn"
+
+#: lib/Exif.php:248 lib/Exif.php:263
+msgid "Flash"
+msgstr "Blixt"
+
+#: lib/Exif.php:353
+msgid "Flash Setting"
+msgstr "Blixtinställning"
+
+#: lib/Exif.php:271
+msgid "Flash, Auto-Mode"
+msgstr "Blixt, automatisk"
+
+#: lib/Exif.php:273
+msgid "Flash, Auto-Mode, Return light detected"
+msgstr ""
+
+#: lib/Exif.php:272
+msgid "Flash, Auto-Mode, Return light not detected"
+msgstr ""
+
+#: lib/Exif.php:265
+msgid "Flash, strobe return light detected"
+msgstr ""
+
+#: lib/Exif.php:264
+msgid "Flash, strobe return light not detected"
+msgstr ""
+
+#: templates/image/edit_image.inc:32
+msgid "Flip"
+msgstr ""
+
+#: lib/Exif.php:247
+msgid "Fluorescent"
+msgstr ""
+
+#: lib/Exif.php:345
+msgid "Focal Length"
+msgstr "Brännvidd"
+
+#: lib/Exif.php:346
+msgid "Focal Length (35mm equiv)"
+msgstr "Brännvidd (35 mm)"
+
+#: templates/slideshow/slideshow.inc:87
+msgid "Forward direction"
+msgstr "Riktning framåt"
+
+#: lib/Ansel.php:481
+msgid "Galleries"
+msgstr "Gallerier"
+
+#: templates/group/header.inc:6
+#, php-format
+msgid "Galleries grouped by %s"
+msgstr "Gallerier grupperade på %s"
+
+#: list.php:50 list.php:53
+#, php-format
+msgid "Galleries in category %s"
+msgstr "Gallerier med kategori %s"
+
+#: templates/search/search.html:8
+msgid "Gallery"
+msgstr "Galleri"
+
+#: templates/gallery/gallery.inc:32
+msgid "Gallery Category"
+msgstr "Kategori"
+
+#: templates/gallery/gallery.inc:47 config/prefs.php.dist:42
+msgid "Gallery Description"
+msgstr "Beskrivning"
+
+#: templates/gallery/gallery.inc:40
+msgid "Gallery Display Name"
+msgstr "Namn"
+
+#: list.php:58 list.php:111
+msgid "Gallery List"
+msgstr "Gallerilista"
+
+#: search.php:38 config/prefs.php.dist:41
+msgid "Gallery Name"
+msgstr "Gallerinamn"
+
+#: config/prefs.php.dist:29
+msgid "Gallery Options"
+msgstr "Galleriinställningar"
+
+#: config/prefs.php.dist:43
+msgid "Gallery Owner"
+msgstr "Ägare"
+
+#: templates/xppublish/new.inc:14
+msgid "Gallery Short Name"
+msgstr "Kortnamn"
+
+#: templates/gallery/gallery.inc:19
+msgid "Gallery Short Name (must be unique)"
+msgstr "Kortnamn (måste vara unikt)"
+
+#: lib/Ansel.php:179
+#, php-format
+msgid "Gallery Unique name '%s' already exists."
+msgstr "Unikt namn '%s' finns redan."
+
+#: xppublish.php:121
+msgid "Gallery Unique name is already taken."
+msgstr "Unikt namn på galleri finns redan."
+
+#: lib/Gallery.php:31
+msgid "Gallery names must be non-empty"
+msgstr "Namn på galleri får inte vara tomt"
+
+#: image.php:442
+msgid "Gallery not found."
+msgstr "Galleri saknas."
+
+#: reorder.php:68
+msgid "Gallery re-ordered."
+msgstr "Galleri omsorterat."
+
+#: image.php:105
+msgid "Giant"
+msgstr ""
+
+#: slideshow.php:40
+msgid "Gradient"
+msgstr "Gradient"
+
+#: templates/image/edit_image.inc:38
+msgid "Grayscale"
+msgstr "Gråskala"
+
+#: config/prefs.php.dist:68
+msgid "Group galleries by"
+msgstr "Grupper gallerier på"
+
+#: lib/Exif.php:340
+msgid "Height"
+msgstr "Höjd"
+
+#: image.php:110
+msgid "Horizontal Alignment"
+msgstr "Horisontell justering"
+
+#: lib/Exif.php:349
+msgid "ISO Setting"
+msgstr "ISO inställning"
+
+#: templates/search/search.html:7
+msgid "Image"
+msgstr "Bild"
+
+#: view.php:41
+msgid "Image Details"
+msgstr "Bilddetaljer"
+
+#: lib/Exif.php:334
+msgid "Image Type"
+msgstr "bildtyp"
+
+#: lib/Gallery.php:51
+msgid "Image names must be non-empty"
+msgstr "Namn på bild får inte vara tomt"
+
+#: view.php:56
+msgid "Image not found."
+msgstr "Bild saknas."
+
+#: scripts/ansel.php:130
+#, php-format
+msgid "Invalid gallery '%s' specified."
+msgstr "Felaktig galleri '%s' angivet."
+
+#: xppublish.php:87 xppublish.php:180
+msgid "Invalid gallery specified."
+msgstr "Felaktig galleri angivet."
+
+#: slideshow.php:41
+msgid "Iris"
+msgstr "Iris"
+
+#: search.php:51
+msgid "Is"
+msgstr "Är"
+
+#: search.php:52
+msgid "Is Not"
+msgstr "Är inte"
+
+#: image.php:105
+msgid "Large"
+msgstr "Stor"
+
+#: image.php:109
+msgid "Left"
+msgstr "Vänster"
+
+#: templates/crop/crop.inc:22
+msgid "Left X"
+msgstr "Vänster X"
+
+#: scripts/ansel.php:167
+msgid "Listing Gallery/Name"
+msgstr "Lista galleri/namn"
+
+#: scripts/ansel.php:151
+#, php-format
+msgid "Listing images in %s"
+msgstr "Visar bilder i %s"
+
+#: scripts/ansel.php:105 scripts/recursive_import.php:81
+#, php-format
+msgid "Logged in successfully as '%s'."
+msgstr "Loggade in som '%s'."
+
+#: scripts/ansel.php:101
+msgid "Login is incorrect."
+msgstr "Felaktig inloggning."
+
+#: templates/xppublish/login.inc:2
+#, php-format
+msgid "Login to %s"
+msgstr "Logga in på %s"
+
+#: templates/slideshow/slideshow.inc:251
+msgid "Loop?"
+msgstr "Loop?"
+
+#: templates/crop/crop.inc:52
+msgid "Lower Y"
+msgstr "Minska Y"
+
+#: image.php:36 image.php:64
+msgid "Make this the default image for this gallery?"
+msgstr "Sät bilden som default för aktuellt galleri?"
+
+#: scripts/ansel.php:290 scripts/recursive_import.php:186
+msgid ""
+"Mandatory arguments to long options are mandatory for short options too."
+msgstr ""
+
+#: lib/Exif.php:131 lib/Exif.php:250
+msgid "Manual"
+msgstr "Manuell"
+
+#: search.php:171 search.php:238
+#, php-format
+msgid "Matched %s images"
+msgstr "Matchade %s bilder"
+
+#: search.php:171 search.php:238
+msgid "Matched 1 image"
+msgstr "Matchade 1 bild"
+
+#: image.php:105
+msgid "Medium"
+msgstr "Medium"
+
+#: lib/Exif.php:352
+msgid "Metering Mode"
+msgstr ""
+
+#: image.php:113
+msgid "Middle"
+msgstr "Mitten"
+
+#: templates/image/edit_image.inc:35
+msgid "Mirror"
+msgstr "Spegla"
+
+#: lib/Exif.php:154
+#, php-format
+msgid "Mirrored"
+msgstr "Speglad"
+
+#: templates/list/images.inc:21
+msgid "Modified"
+msgstr "Ändrad"
+
+#: templates/view/gallery.inc:102
+msgid "Modify"
+msgstr "Ändra"
+
+#: templates/view/gallery.inc:102
+msgid "Modify Gallery"
+msgstr "Ändra galleri"
+
+#: gallery.php:127
+#, php-format
+msgid "Modifying: %s"
+msgstr "Ändrar: %s"
+
+#: templates/view/navbar-gallery.inc:13
+msgid "Move"
+msgstr "Flytta"
+
+#: image.php:574
+#, php-format
+msgid "Moved %s to %s."
+msgstr "Flyttade %s till %s."
+
+#: lib/Exif.php:235
+msgid "Multi-Segment"
+msgstr ""
+
+#: lib/Exif.php:234
+msgid "Multi-Spot"
+msgstr ""
+
+#: image.php:67
+msgid "Multiple Images"
+msgstr "Flera bilder"
+
+#: list.php:38 lib/Ansel.php:468 templates/group/owner.inc:5
+#, php-format
+msgid "My Galleries"
+msgstr "Mina gallerier"
+
+#: templates/list/gallery_headers.inc:7
+msgid "Name"
+msgstr "Namn"
+
+#: templates/xppublish/new.inc:33
+msgid "New Gallery Description"
+msgstr "Ny beskrivinig på galleri"
+
+#: templates/xppublish/new.inc:24
+msgid "New Gallery Title"
+msgstr "Titel på nytt galleri"
+
+#: templates/view/description-image.inc:60
+#: templates/view/description-image.inc:61
+msgid "Next"
+msgstr "Nästa"
+
+#: config/prefs.php.dist:110
+msgid "No"
+msgstr "Nej"
+
+#: lib/Exif.php:262 lib/Exif.php:269 lib/Exif.php:270 lib/Exif.php:274
+msgid "No Flash"
+msgstr "Ingen blixt"
+
+#: xppublish.php:123
+msgid "No Gallery Name Specified."
+msgstr "Inget namn angivet."
+
+#: xppublish.php:119
+msgid "No Gallery Unique Name Specified."
+msgstr "Inget unikt namn angivet."
+
+#: xppublish.php:190
+msgid "No file specified"
+msgstr "Inget fil angiven"
+
+#: templates/view/navbar-gallery.inc:6 config/prefs.php.dist:67
+msgid "None"
+msgstr "Ingen"
+
+#: lib/Exif.php:152
+#, php-format
+msgid "Normal (O deg)"
+msgstr "Normal (O grader)"
+
+#: lib/Exif.php:292
+msgid "Not defined"
+msgstr "Inte definierad"
+
+#: crop.php:26 image.php:152
+msgid "Not found or error accessing gallery."
+msgstr "Saknas eller fel vid årkomst av galleri."
+
+#: config/prefs.php.dist:93
+msgid "Number of galleries per page"
+msgstr "Antal gallerier per sida"
+
+#: config/prefs.php.dist:101
+msgid "Number of groups per page"
+msgstr "Antal grupper per sida"
+
+#: config/prefs.php.dist:85
+msgid "Number of images per page"
+msgstr "Antal bilder per sida"
+
+#: config/prefs.php.dist:77
+msgid "Number of images per row"
+msgstr "Antal bilder per rad"
+
+#: lib/Exif.php:293
+msgid "One Chip Color Area Sensor"
+msgstr ""
+
+#: perms.php:62
+msgid ""
+"Only the owner or system administrator may change ownership or owner "
+"permissions for a share"
+msgstr ""
+"Endast ägare eller systemadministratör kan ändra ägare eller behörigheter på "
+"en utdelning"
+
+#: templates/crop/crop.inc:27
+msgid "Original"
+msgstr "Orginal"
+
+#: lib/Exif.php:237
+msgid "Other"
+msgstr "Annan"
+
+#: config/prefs.php.dist:13 config/prefs.php.dist:20 config/prefs.php.dist:28
+msgid "Other Options"
+msgstr "Andra inställningar"
+
+#: templates/list/images.inc:25 config/prefs.php.dist:65
+msgid "Owner"
+msgstr "Ägare"
+
+#: lib/Exif.php:236
+msgid "Partial"
+msgstr "Delvis"
+
+#: templates/xppublish/login.inc:14
+msgid "Password"
+msgstr "Lösenord"
+
+#: templates/view/gallery.inc:107
+msgid "Permissions"
+msgstr "behörigheter"
+
+#: templates/xppublish/javascript.inc:63
+msgid "Photo Upload"
+msgstr "Bild uppladdad"
+
+#: slideshow.php:42
+msgid "Pinwheel"
+msgstr "Vindsnurra"
+
+#: slideshow.php:43
+msgid "Pixelate"
+msgstr "Punktvis"
+
+#: templates/slideshow/slideshow.inc:67
+msgid "Play"
+msgstr "Starta"
+
+#: xppublish.php:54
+msgid "Please enter your Username and Password."
+msgstr "Ange ditt användarnamn och lösenord."
+
+#: xppublish.php:99 xppublish.php:155
+msgid "Press the 'Back' button and try again."
+msgstr "Tryck på 'Tillbaka' och försök igen."
+
+#: image.php:448
+#, php-format
+msgid "Preview changes for %s :: %s"
+msgstr "Förhandsgranska ändringar för %s :: %s"
+
+#: templates/view/description-image.inc:46
+#: templates/view/description-image.inc:47
+msgid "Previous"
+msgstr "Föregående"
+
+#: lib/Ansel.php:426
+msgid "Print"
+msgstr "Skriv ut"
+
+#: templates/view/navbar-image.inc:26
+msgid "Print on Shutterfly"
+msgstr "Skriv ut hos Shutterfly"
+
+#: lib/Exif.php:125
+msgid "Program"
+msgstr "Program"
+
+#: templates/view/gallery.inc:162 templates/view/navbar-image.inc:15
+msgid "Properties"
+msgstr "Egenskaper"
+
+#: xppublish.php:35
+#, php-format
+msgid "Publish to %s"
+msgstr "Publicera till %s"
+
+#: xppublish.php:23
+#, php-format
+msgid "Publish your photos to %s on %s."
+msgstr "Publicera dina bilder till %s på %s."
+
+#: slideshow.php:44
+msgid "Radial"
+msgstr "Radial"
+
+#: slideshow.php:45
+msgid "Rain"
+msgstr "Regn"
+
+#: slideshow.php:50
+msgid "Random"
+msgstr "Slumpvis"
+
+#: lib/Block/random_gallery.php:3 lib/Block/random_gallery.php:27
+msgid "Random gallery"
+msgstr "Slumpvis galleri"
+
+#: lib/Block/random_photo.php:3 lib/Block/random_photo.php:28
+msgid "Random photo"
+msgstr "Slumpvis bild"
+
+#: reorder.php:77
+#, php-format
+msgid "Re-order the images in %s"
+msgstr "Ändra ordning på bilder i %s"
+
+#: lib/Exif.php:275
+msgid "Red Eye"
+msgstr "Röda ögon"
+
+#: lib/Exif.php:281
+msgid "Red Eye, Auto-Mode"
+msgstr "Röda ögon, automatiskt"
+
+#: lib/Exif.php:283
+msgid "Red Eye, Auto-Mode, Return light detected"
+msgstr ""
+
+#: lib/Exif.php:282
+msgid "Red Eye, Auto-Mode, Return light not detected"
+msgstr ""
+
+#: lib/Exif.php:278
+msgid "Red Eye, Compulsory Flash"
+msgstr "Röda ögon, Tvingande blixt"
+
+#: lib/Exif.php:280
+msgid "Red Eye, Compulsory Flash, Return light detected"
+msgstr ""
+
+#: lib/Exif.php:279
+msgid "Red Eye, Compulsory Flash, Return light not detected"
+msgstr ""
+
+#: lib/Exif.php:277
+msgid "Red Eye, Return light detected"
+msgstr ""
+
+#: lib/Exif.php:276
+msgid "Red Eye, Return light not detected"
+msgstr ""
+
+#: templates/view/gallery.inc:88
+msgid "Refresh Gallery View"
+msgstr "Förnya gallerivy"
+
+#: templates/list/header.inc:10 templates/list/header.inc:11
+#: templates/group/header.inc:7 templates/group/header.inc:8
+#: templates/view/gallery.inc:88
+msgid "Refresh List"
+msgstr "Förnya lista"
+
+#: templates/view/gallery.inc:104
+msgid "Reorder"
+msgstr "Ändra ordning"
+
+#: templates/view/gallery.inc:104
+msgid "Reorder Gallery"
+msgstr "Ändra ordning på galleri"
+
+#: templates/crop/crop.inc:3
+msgid "Resize selection"
+msgstr "Ändra storlek på markering"
+
+#: templates/slideshow/slideshow.inc:90 templates/slideshow/slideshow.inc:221
+msgid "Reverse direction"
+msgstr "Omvänd riktning"
+
+#: image.php:109
+msgid "Right"
+msgstr "Höger"
+
+#: templates/crop/crop.inc:50
+msgid "Right X"
+msgstr "Höger X"
+
+#: templates/image/edit_image.inc:28
+msgid "Rotate 180"
+msgstr "Rotera 180"
+
+#: templates/image/edit_image.inc:27
+msgid "Rotate Left"
+msgstr "Rotera vänster"
+
+#: templates/image/edit_image.inc:29
+msgid "Rotate Right"
+msgstr "Rotera höger"
+
+#: reorder.php:26 image.php:29 image.php:52 image.php:96
+msgid "Save"
+msgstr "Spara"
+
+#: templates/captions/captions.inc:50
+msgid "Save Captions"
+msgstr "Spara rubriker"
+
+#: templates/image/preview_image.inc:24
+msgid "Save Change"
+msgstr "Spara ändring"
+
+#: templates/gallery/gallery.inc:84
+msgid "Save Gallery"
+msgstr "Spara galleri"
+
+#: image.php:195
+msgid "Save Image"
+msgstr "Spara bild"
+
+#: templates/gallery/gallery.inc:75
+msgid "Screen Image Height"
+msgstr "Skärmbilds höjd"
+
+#: templates/gallery/gallery.inc:68
+msgid "Screen Image Width"
+msgstr "Skärmbilds bredd"
+
+#: search.php:28
+msgid "Search"
+msgstr "Sök"
+
+#: search.php:60
+msgid "Search Type"
+msgstr "Sök på"
+
+#: search.php:97 search.php:102
+#, php-format
+msgid "Search for Images"
+msgstr "Sök efter bilder"
+
+#: templates/view/navbar-gallery.inc:5
+msgid "Select All"
+msgstr "Välj alla"
+
+#: templates/view/navbar-gallery.inc:6
+msgid "Select None"
+msgstr "Välj ingen"
+
+#: templates/xppublish/list.inc:6
+msgid "Select the Gallery to publish images to"
+msgstr "Välj galleri att publicera bilder till"
+
+#: templates/view/navbar-gallery.inc:4
+#, php-format
+msgid "Select: %s, %s"
+msgstr "Välj: %s, %s"
+
+#: templates/view/navbar-gallery.inc:20
+msgid "Selected images to"
+msgstr "Vald bild till"
+
+#: ecard.php:25
+msgid "Send"
+msgstr "Skicka"
+
+#: ecard.php:141
+#, php-format
+msgid "Send Ecard :: %s"
+msgstr "Skiva e-vykort :: %s"
+
+#: templates/view/navbar-image.inc:24
+msgid "Send as Ecard"
+msgstr "Skicka som e-vykort"
+
+#: ecard.php:41
+msgid "Send ecard to the following address:"
+msgstr "Skicka e-vykort till följande adresser:"
+
+#: lib/Exif.php:356
+msgid "Sensing Method"
+msgstr ""
+
+#: templates/view/gallery.inc:107
+msgid "Set Permissions"
+msgstr "Sätt behörigheter"
+
+#: config/prefs.php.dist:120
+msgid "Show EXIF data"
+msgstr "Visa EXIF-infromation"
+
+#: lib/Exif.php:343
+msgid "Shutter Speed"
+msgstr ""
+
+#: image.php:58
+msgid "Single Image"
+msgstr "Enkel bild"
+
+#: slideshow.php:46
+msgid "Slide"
+msgstr "Bild"
+
+#: lib/Ansel.php:407
+msgid "Slide Show"
+msgstr "Bildspel"
+
+#: templates/list/gallery_summaries.inc:7
+msgid "Slide show"
+msgstr "Bildspel"
+
+#: slideshow.php:52
+#, php-format
+msgid "Slide show for %s"
+msgstr "Bildspel för %s"
+
+#: templates/slideshow/slideshow.inc:79
+msgid "Slide show is running"
+msgstr "Bildspel är aktivt"
+
+#: image.php:105
+msgid "Small"
+msgstr "Liten"
+
+#: slideshow.php:47
+msgid "Snow"
+msgstr "Snö"
+
+#: templates/list/gallery_headers.inc:6 templates/list/gallery_headers.inc:21
+msgid "Sort Direction"
+msgstr "Sorteringsriktning"
+
+#: config/prefs.php.dist:14
+msgid "Sort Options"
+msgstr "Sorteringsinställningar"
+
+#: templates/list/gallery_headers.inc:22
+msgid "Sort by Description"
+msgstr "Sortera på beskrivning"
+
+#: templates/list/gallery_headers.inc:7
+msgid "Sort by Name"
+msgstr "Sortera på namn"
+
+#: gallery.php:186
+#, php-format
+msgid "Specified screen image height is too large, value has been set to %s"
+msgstr ""
+"Angivet värde på skärmbilds höjd är för stort. Värdet har ändrats till %s"
+
+#: gallery.php:183
+#, php-format
+msgid "Specified screen image height is too small, value has been set to %s"
+msgstr ""
+"Angivet värde på skärmbilds höjd är för litet. Värdet har ändrats till %s"
+
+#: gallery.php:177
+#, php-format
+msgid "Specified screen image width is too large, value has been set to %s"
+msgstr ""
+"Angivet värde på skärmbilds bredd är för stort. Värdet har ändrats till %s"
+
+#: gallery.php:174
+#, php-format
+msgid "Specified screen image width is too small, value has been set to %s"
+msgstr ""
+"Angivet värde på skärmbilds bredd är för litet. Värdet har ändrats till %s"
+
+#: gallery.php:168
+#, php-format
+msgid "Specified thumbnail height is too large, value has been set to %s"
+msgstr ""
+"Angivet värde på miniatyrbilds höjd är för stort. Värdet har ändrats till %s"
+
+#: gallery.php:165
+#, php-format
+msgid "Specified thumbnail height is too small, value has been set to %s"
+msgstr ""
+"Angivet värde på miniatyrbilds höjd är för litet. Värdet har ändrats till %s"
+
+#: gallery.php:159
+#, php-format
+msgid "Specified thumbnail width is too large, value has been set to %s"
+msgstr ""
+"Angivet värde på miniatyrbilds bredd är för stort. Värdet har ändrats till %s"
+
+#: gallery.php:156
+#, php-format
+msgid "Specified thumbnail width is too small, value has been set to %s"
+msgstr ""
+"Angivet värde på miniatyrbilds bredd är för litet. Värdet har ändrats till %s"
+
+#: slideshow.php:48
+msgid "Spiral"
+msgstr "Spiral"
+
+#: lib/Exif.php:233
+msgid "Spot"
+msgstr ""
+
+#: search.php:88
+msgid "Start Date"
+msgstr "Startdatum"
+
+#: templates/slideshow/slideshow.inc:76 templates/slideshow/slideshow.inc:221
+msgid "Stop"
+msgstr "Stoppa"
+
+#: scripts/ansel.php:187 scripts/ansel.php:266
+#: scripts/recursive_import.php:155
+#, php-format
+msgid "Storing image '%s'..."
+msgstr "Sparar bild '%s'..."
+
+#: slideshow.php:49
+msgid "Stretch"
+msgstr "Sträck"
+
+#: templates/view/subgallery_header.inc:5
+#, php-format
+msgid "Subgalleries of %s"
+msgstr "Undergalleri till %s"
+
+#: templates/view/gallery.inc:101
+msgid "Subgallery"
+msgstr "Undergalleri"
+
+#: scripts/ansel.php:207 scripts/recursive_import.php:165
+#, php-format
+msgid "Successfully added %d image(s) (%s) to gallery '%s' from '%s'."
+msgstr "La till %d bild(er) (%s) till galleri '%s' från '%s'."
+
+#: scripts/ansel.php:194
+#, php-format
+msgid "Successfully added image '%s' to gallery '%s'."
+msgstr "La till bild '%s' till galleri '%s'."
+
+#: gallery.php:318
+#, php-format
+msgid "Successfully deleted %s."
+msgstr "'%s' raderades."
+
+#: lib/Exif.php:244
+msgid "Sunny"
+msgstr "Soligt"
+
+#: search.php:66
+msgid "Text Field"
+msgstr "Textfält"
+
+#: search.php:61
+msgid "Text Fields"
+msgstr "Textfält"
+
+#: search.php:67
+msgid "Text Operator"
+msgstr "Text operator"
+
+#: search.php:68
+msgid "Text Value"
+msgstr "Textvärde"
+
+#: scripts/ansel.php:233
+#, php-format
+msgid "The directory '%s' doesn't exist."
+msgstr "Katalogen '%s' existerar inte."
+
+#: scripts/ansel.php:212
+#, php-format
+msgid "The directory '%s' had no valid images."
+msgstr "Katalogen '%s' saknar godkända bilder."
+
+#: scripts/ansel.php:250
+#, php-format
+msgid "The directory '%s' is empty."
+msgstr "Katalogen '%s' är tom."
+
+#: lib/Ansel.php:312
+#, php-format
+msgid "The file '%s' doesn't exist."
+msgstr "Filen '%s' finns inte."
+
+#: image.php:295 lib/api.php:157
+msgid "The file you uploaded does not appear to be a valid image."
+msgstr "Filen verkar inte vara en godkänd bild-fil."
+
+#: scripts/recursive_import.php:124
+#, php-format
+msgid "The gallery \"%s\" (%s) was created successfully."
+msgstr "Galleri '%s' (%s) skapades."
+
+#: lib/Ansel.php:206
+#, php-format
+msgid "The gallery \"%s\" could not be created: %s"
+msgstr "Galleri '%s' kunde inte skapas: %s"
+
+#: xppublish.php:129 gallery.php:283 scripts/ansel.php:117
+#: scripts/recursive_import.php:122
+#, php-format
+msgid "The gallery \"%s\" couldn't be created: %s"
+msgstr "Galleri '%s' kunde inte skapas: %s"
+
+#: xppublish.php:133 gallery.php:288 scripts/ansel.php:121
+#, php-format
+msgid "The gallery \"%s\" was created successfully."
+msgstr "Galleri '%s' skapades."
+
+#: gallery.php:230
+msgid "The gallery was saved."
+msgstr "Galleri sparades."
+
+#: image.php:305
+msgid "The image was saved."
+msgstr "Bilden sparades."
+
+#: templates/slideshow/slideshow.inc:70
+msgid "The slide show is stopped, click [Play] to resume."
+msgstr "Bildspelet är stoppat, klicka på [Starta] för att starta igen."
+
+#: image.php:556 image.php:603
+msgid "The target gallery was not found."
+msgstr "Målgalleri saknas."
+
+#: image.php:320
+msgid ""
+"The uploaded file appears to be empty. It may not exist on your computer."
+msgstr "Uppladdad fil verkar vara tom. Den kanske inte finns på din dator."
+
+#: gallery.php:88
+#, php-format
+msgid "There are no images in %s to download."
+msgstr "Det finns inga bilder i %s att ladda ner."
+
+#: slideshow.php:31
+msgid "There are no images in this gallery to show."
+msgstr "Det finns inga bilder i aktuellt galleri."
+
+#: templates/captions/captions.inc:52 templates/view/gallery.inc:119
+msgid "There are no images in this gallery."
+msgstr "Det finns inga bilder i aktuellt galleri."
+
+#: list.php:63 lib/Block/random_gallery.php:37 lib/Block/random_photo.php:38
+msgid "There are no photo galleries available."
+msgstr "Inget galleri tillgängligt."
+
+#: scripts/ansel.php:190 scripts/ansel.php:269
+#, php-format
+msgid "There was a problem adding the image '%s' to gallery '%s'."
+msgstr "Problem med att lägga till '%s' till galleri '%s'."
+
+#: image.php:614
+#, php-format
+msgid "There was a problem copying \"%s\": %s"
+msgstr "Problem vid kopiering av '%s': %s"
+
+#: image.php:572
+#, php-format
+msgid "There was a problem deleting \"%s\": %s"
+msgstr "Problem vid radering av '%s': %s"
+
+#: lib/api.php:272
+#, php-format
+msgid "There was a problem deleting %s: %s"
+msgstr "Problem vid radering av '%s': %s"
+
+#: gallery.php:325
+#, php-format
+msgid "There was a problem deleting %s: %s."
+msgstr "Problem vid radering av '%s': %s"
+
+#: image.php:530
+#, php-format
+msgid "There was a problem deleting images: %s"
+msgstr "Problem vid radering av bilder: %s"
+
+#: image.php:568
+#, php-format
+msgid "There was a problem moving \"%s\": %s"
+msgstr "Problem vid flytt av '%s': %s"
+
+#: image.php:278 image.php:311
+#, php-format
+msgid "There was a problem saving the image: %s"
+msgstr "Fel uppstod när bilden skulle sparas: %s"
+
+#: xppublish.php:199
+msgid "There was a problem uploading the image."
+msgstr "Fel vid uppladdning av bilden."
+
+#: image.php:318
+#, php-format
+msgid "There was a problem uploading the image: %s"
+msgstr "Fel vid uppladdning av bilden: %s"
+
+#: image.php:255 image.php:263
+#, php-format
+msgid "There was an error processing the uploaded archive: %s"
+msgstr "Fel vid bearbetning av det uppladdade arkivet: %s"
+
+#: ecard.php:130
+#, php-format
+msgid "There was an error sending your message: %s"
+msgstr "Det uppstod ett fel vid skickning av ditt meddelande: %s"
+
+#: lib/Exif.php:295
+msgid "Three Chip Color Area Sensor"
+msgstr ""
+
+#: search.php:164 search.php:231
+msgid "Thumbnail"
+msgstr "Miniatyrbild"
+
+#: templates/gallery/gallery.inc:61
+msgid "Thumbnail Height"
+msgstr "Miniatyrbild, höjd"
+
+#: templates/gallery/gallery.inc:54
+msgid "Thumbnail Width"
+msgstr "Miniatyrbild, bredd"
+
+#: image.php:105
+msgid "Tiny"
+msgstr "Liten"
+
+#: lib/Ansel.php:373
+msgid "Today"
+msgstr "Idag"
+
+#: image.php:113
+msgid "Top"
+msgstr "Top"
+
+#: templates/crop/crop.inc:24
+msgid "Top Y"
+msgstr "Top Y"
+
+#: templates/slideshow/slideshow.inc:245
+msgid "Transition"
+msgstr "Övergång"
+
+#: lib/Exif.php:297
+msgid "Trilinear Sensor"
+msgstr ""
+
+#: lib/Exif.php:246
+msgid "Tungsten"
+msgstr ""
+
+#: lib/Exif.php:127
+msgid "Tv-priority"
+msgstr "Tv-prioritet"
+
+#: lib/Exif.php:294
+msgid "Two Chip Color Area Sensor"
+msgstr ""
+
+#: lib/Exif.php:308
+msgid "Uncalibrated"
+msgstr "Okalibrerad"
+
+#: templates/gallery/gallery.inc:85
+msgid "Undo Changes"
+msgstr "Ångra ändringar"
+
+#: list.php:50 templates/group/category.inc:8
+msgid "Unfiled"
+msgstr "Ej kategoriserad"
+
+#: lib/Exif.php:230
+msgid "Unknown"
+msgstr "Okänd"
+
+#: gallery.php:129
+msgid "Unknown gallery"
+msgstr "Okänt galleri"
+
+#: lib/Ansel.php:194
+msgid "Unnamed"
+msgstr "Namnlös"
+
+#: xppublish.php:141
+msgid "Untitled"
+msgstr "Namnlos"
+
+#: perms.php:234
+#, php-format
+msgid "Updated %s."
+msgstr "Uppdaterad %s."
+
+#: templates/view/gallery.inc:100
+msgid "Upload"
+msgstr "Ladda upp"
+
+#: templates/xppublish/javascript.inc:63
+#, php-format
+msgid "Upload Photos to %s"
+msgstr "Ladda upp bilder till %s"
+
+#: image.php:166 image.php:225
+msgid "Upload new images"
+msgstr "Ladda upp nya bilder"
+
+#: lib/Exif.php:156
+#, php-format
+msgid "Upsidedown"
+msgstr "Upp och ner"
+
+#: lib/Exif.php:158
+#, php-format
+msgid "Upsidedown Mirrored"
+msgstr "Upp och ner, spegelvänd"
+
+#: scripts/ansel.php:288 scripts/recursive_import.php:184
+#, php-format
+msgid "Usage: %s [OPTIONS]..."
+msgstr ""
+
+#: templates/gallery/gallery.inc:25 templates/xppublish/new.inc:17
+msgid "Use Default"
+msgstr "Använd förvalt"
+
+#: ecard.php:32
+msgid "Use the following return address:"
+msgstr "Använd följande avsändaradress:"
+
+#: lib/Exif.php:354
+msgid "User Comment"
+msgstr "Kommentar"
+
+#: templates/xppublish/login.inc:11
+msgid "Username"
+msgstr "Användarnamn"
+
+#: xppublish.php:50
+msgid "Username and Password are incorrect."
+msgstr "Felaktigt användarnamn och/eller lösenord."
+
+#: scripts/recursive_import.php:79
+msgid "Username or password is incorrect."
+msgstr "Felaktigt användarnamn och/eller lösenord."
+
+#: image.php:114
+msgid "Vertical Alignment"
+msgstr "Vertikal justering"
+
+#: lib/Block/random_gallery.php:42 templates/list/gallery_summaries.inc:2
+#: templates/list/gallery_summaries.inc:15 templates/list/images.inc:9
+#: templates/list/images.inc:12 templates/list/images.inc:17
+#, php-format
+msgid "View %s"
+msgstr "Visa %s"
+
+#: lib/Block/random_photo.php:58
+msgid "View Photo"
+msgstr "Visa bild"
+
+#: image.php:406 image.php:412 templates/image/edit_image.inc:42
+msgid "Watermark"
+msgstr "Vattenmärke"
+
+#: image.php:106
+msgid "Watermark Font"
+msgstr "Vattenmärke, typsnitt"
+
+#: lib/Exif.php:357
+msgid "White Balance"
+msgstr "Vitbalans"
+
+#: lib/Exif.php:339
+msgid "Width"
+msgstr "Bredd"
+
+#: lib/Exif.php:341
+msgid "X Resolution"
+msgstr "X-upplösning"
+
+#: lib/Exif.php:342
+msgid "Y Resolution"
+msgstr "Y-upplösning"
+
+#: config/prefs.php.dist:109
+msgid "Yes"
+msgstr "Ja"
+
+#: config/prefs.php.dist:111
+msgid "Yes, and don't prompt to change it"
+msgstr "Ja, och fråga inte om att ändra"
+
+#: lib/Ansel.php:375
+msgid "Yesterday"
+msgstr "i går"
+
+#: image.php:343
+msgid "You are not allowed to edit this image."
+msgstr "Du har inte behörighet att redigera aktuell bild."
+
+#: xppublish.php:91 xppublish.php:184
+msgid "You cannot add photos in that gallery."
+msgstr "Du kan inte lägga till bilder i galleriet."
+
+#: scripts/ansel.php:136
+#, php-format
+msgid "You cannot add photos to the gallery '%s'."
+msgstr "Du kan inte lägga till bilder i galleri '%s'."
+
+#: gallery.php:313
+#, php-format
+msgid "You cannot delete %s, it contains nested galleries."
+msgstr "Du kan inte radera %s, det innehåller gallerier."
+
+#: gallery.php:53 gallery.php:245
+#, php-format
+msgid "You do not have permission to add children to %s."
+msgstr "Du har inte behörighet att lägga till barnd till %s."
+
+#: gallery.php:348
+#, php-format
+msgid "You do not have permission to empty %s."
+msgstr "Behörighet saknas för att tömma %s."
+
+#: reorder.php:47 slideshow.php:24 view.php:27
+msgid "You do not have permission to view this gallery."
+msgstr "Du har inte behörighet att visa aktuellt galleri."
+
+#: ecard.php:102
+msgid ""
+"You have been sent an Ecard. To view the Ecard, you must be able to view "
+"text/html messages in your mail reader. If you are viewing this message, "
+"then most likely your mail reader does not support viewing text/html "
+"messages."
+msgstr ""
+
+#: templates/view/gallery.inc:53
+msgid "You must choose a gallery to copy images to."
+msgstr "Välj galleri att kopiera bilder till"
+
+#: templates/view/gallery.inc:67
+msgid "You must choose a gallery to move images to."
+msgstr "Välj galleri att flytta bilder till"
+
+#: ecard.php:76
+msgid "You must enter an e-mail address to send the message to."
+msgstr "Du måste ange en e-postadress att skicka meddelandet till."
+
+#: ecard.php:71
+msgid "You must enter your e-mail address."
+msgstr "Du måste ange din e-postadress."
+
+#: gallery.php:265
+msgid "You must provide a display name for your new gallery."
+msgstr "Du måste ange ett namn på ditt nya galleri."
+
+#: scripts/recursive_import.php:88
+msgid "You must specify a valid directory."
+msgstr "Välj en godkänd katalog."
+
+#: scripts/recursive_import.php:84
+msgid "You must specify a valid username and password."
+msgstr "Du måste ange ett godkänt användarnamn och lösenord."
+
+#: image.php:76
+msgid "Zip File Upload"
+msgstr "ZIP-fil uppladdad"
+
+#: templates/crop/crop.inc:34
+msgid "Zoom"
+msgstr "Zooma"
+
+#: lib/Ansel.php:396
+msgid "_Galleries"
+msgstr "_Galleri"
+
+#: lib/Ansel.php:398
+msgid "_My Galleries"
+msgstr "_Mina gallerier"
+
+#: lib/Ansel.php:421
+msgid "_New Gallery"
+msgstr "_Nytt galleri"
+
+#: lib/Ansel.php:430
+msgid "_Search"
+msgstr "_Sök"
+
+#: lib/Exif.php:305
+msgid "sRGB"
+msgstr "sRGB"
diff --git a/ansel/po/tr_TR.po b/ansel/po/tr_TR.po
new file mode 100644 (file)
index 0000000..ff2f030
--- /dev/null
@@ -0,0 +1,2792 @@
+# Turkish translations for Ansel.
+# Copyright 2008-2009 The Horde Project
+# This file is distributed under the same license as the Ansel package.
+# Akif Dinc <akif@dinc.org>, 2008.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ansel 1.0-cvs\n"
+"Report-Msgid-Bugs-To: dev@lists.horde.org\n"
+"POT-Creation-Date: 2008-10-28 21:10+0100\n"
+"PO-Revision-Date: 2008-10-28 23:12+0100\n"
+"Last-Translator: Akif Dinc <akif@dinc.org>\n"
+"Language-Team: i18n@lists.horde.org\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-9\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+#: scripts/remote_import.php:166 scripts/recursive_import.php:149
+#, php-format
+msgid "\"%s\" is not a directory."
+msgstr "\"%s\" Bir dizin deðil."
+
+#: templates/list/header.inc:3 templates/group/category.inc:47
+#: templates/group/owner.inc:52 templates/view/list.inc:3
+#, php-format
+msgid "%d Gallery"
+msgid_plural "%d Galleries"
+msgstr[0] "%d Galeri"
+
+#: templates/group/header.inc:3
+#, php-format
+msgid "%d Group"
+msgid_plural "%d Groups"
+msgstr[0] "%d Grup"
+
+#: templates/view/gallerylightbox.inc:110 templates/view/results.inc:67
+#, php-format
+msgid "%d Item"
+msgid_plural "%d Items"
+msgstr[0] "%d Öðe"
+
+#: lib/Exif.php:293
+#, php-format
+msgid "%d bytes"
+msgstr "%d bayt"
+
+#: templates/tile/image.inc:11
+#, php-format
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] "%d Yorum"
+
+#: templates/faces/face.inc:7
+#, php-format
+msgid "%d gallery"
+msgid_plural "%d galleries"
+msgstr[0] "%d galerý"
+
+#: templates/view/gallery.inc:87
+#, php-format
+msgid "%d item"
+msgid_plural "%d items"
+msgstr[0] "%d öðe"
+
+#: templates/faces/search.inc:9 lib/XRequest/TagActions.php:115
+#: lib/Views/Results.php:291 lib/Widget/Tags.php:93
+#, php-format
+msgid "%d photo"
+msgid_plural "%d photos"
+msgstr[0] "%d fotoðraf"
+
+#: img/upload.php:225
+#, php-format
+msgid "%d photo was uploaded."
+msgid_plural "%d photos were uploaded."
+msgstr[0] "%d fotoðraflar yüklendi."
+
+#: lib/Exif.php:144 lib/Exif.php:150
+#, php-format
+msgid "%d pixels"
+msgstr "%d piksel"
+
+#: templates/list/header.inc:3 templates/view/list.inc:3
+#, php-format
+msgid "%d to %d of %d Galleries"
+msgstr "%d - %d / %d galeri"
+
+#: templates/group/header.inc:3
+#, php-format
+msgid "%d to %d of %d Groups"
+msgstr "%d - %d / %d Gruplar"
+
+#: templates/view/gallerylightbox.inc:110 templates/view/gallery.inc:87
+#: templates/view/results.inc:67
+#, php-format
+msgid "%d to %d of %d Items"
+msgstr "%d - %d / %d fotoðraf"
+
+#: gallery/sort.php:64
+#, php-format
+msgid "%s :: Sort"
+msgstr "%s :: Sýrala"
+
+#: lib/Widget/SimilarPhotos.php:81
+#, php-format
+msgid "%s from %s"
+msgstr "%s / %s"
+
+#: rss.php:112
+#, php-format
+msgid "%s on %s"
+msgstr "%s on %s"
+
+#: lib/Exif.php:174 lib/Exif.php:189
+#, php-format
+msgid "%s sec"
+msgstr "%s san."
+
+#: templates/group/owner.inc:16 lib/Ansel.php:692 lib/Views/List.php:167
+#: lib/Widget/OtherGalleries.php:36
+#, php-format
+msgid "%s's Galleries"
+msgstr "%s'in galerileri"
+
+#: templates/faces/faces.inc:8
+#, php-format
+msgid "%s: faces %s"
+msgstr "%s: %s Sima"
+
+#: lib/Ansel.php:2686
+#, php-format
+msgid "(c) %s %s"
+msgstr "(c) %s %s"
+
+#: scripts/ansel.php:326
+msgid "-a, --add[=filename]         Add local file to selected gallery"
+msgstr "-a, --add[=dosyaadý]          Yerel dosyayý secilmiþ galeriye ekle"
+
+#: scripts/ansel.php:324
+msgid ""
+"-c, --create[=name/description/owner]\n"
+"                             Create gallery (and use it)  Combined with -g "
+"to create a subgallery."
+msgstr ""
+"-c, --create[=ad/baþlýk/sahibi]\n"
+"                             Galerie oluþtur (ve kullan)  -g ile beraber alt "
+"galeri oluþturulur"
+
+#: scripts/ansel.php:327
+msgid ""
+"-d, --dir[=directory]        Add all files from the directory to the "
+"selected\n"
+"                             gallery"
+msgstr "-d, --dir[=dizin]        Dizinden tüm dosyalarý seçilmiþ galeriye ekle"
+
+#: scripts/remote_import.php:340 scripts/recursive_import.php:226
+msgid ""
+"-d, --dir[=directory]        Recursively add all files from the directory, "
+"creating\n"
+"                             a gallery for each directory"
+msgstr ""
+"-d, --dir[=dizin]        Dizinden altdizinlere inerek tüm dosyalarý ekle, "
+"eklerken\n"
+"                             her dizin için galeri oluþtur"
+
+#: scripts/all_images_exif_to_tags.php:123
+msgid ""
+"-f, --fields[=exif_fields]   A ':' delimited list of exif fields to include "
+"DateTimeOriginal is default."
+msgstr ""
+
+#: scripts/remote_import.php:343
+msgid ""
+"-g, --gallery[=gallery_id    The gallery id to add directory contents to"
+msgstr ""
+
+#: scripts/ansel.php:325
+msgid "-g, --gallery[=shortname]    Select gallery to use"
+msgstr "-g, --gallery[=kýsaad]      Kullanýlacak galeri seç"
+
+#: scripts/remote_import.php:339 scripts/recursive_import.php:225
+#: scripts/all_images_exif_to_tags.php:120 scripts/ansel.php:322
+msgid "-h, --help                   Show this help"
+msgstr "-h, --help                     Bu yardýmý göster"
+
+#: scripts/recursive_import.php:229
+msgid ""
+"-k, --keep                   Do not delete empty galleries after import is "
+"complete."
+msgstr "-k, --keep                  Yükleme iþinden sonra boþ galerileri silme"
+
+#: scripts/ansel.php:323
+msgid ""
+"-l, --list                   List galleries or photos (if combined with -g)"
+msgstr ""
+"-l, --list                   Galeri veya fotoðraflarý listele (-g ile "
+"beraber kullanýldýðýnda)"
+
+#: scripts/remote_import.php:342 scripts/recursive_import.php:228
+#: scripts/all_images_exif_to_tags.php:122 scripts/ansel.php:329
+msgid "-p, --password[=password]    Horde login password"
+msgstr "-p, --password[=þifre]      Horde-Þifresi"
+
+#: scripts/remote_import.php:346
+msgid "-r, --remotehost[=url]       The url of the remote rpc server."
+msgstr ""
+"-r, --remotehost[=url]       Erisilmesi istenen rpc serverin url adresi."
+
+#: scripts/remote_import.php:344
+msgid ""
+"-s, --slug[=gallery_slug]    The gallery slug to add directory contents to"
+msgstr ""
+
+#: scripts/ansel.php:330
+msgid "-t, --caption[=caption]      Caption for photo (if combined with -a)"
+msgstr ""
+"-t, --caption[=baþlýk]      Fotoðraf için baþlýk (-a ile beraber "
+"kullanýldýðýnda)"
+
+#: scripts/remote_import.php:341 scripts/recursive_import.php:227
+#: scripts/all_images_exif_to_tags.php:121 scripts/ansel.php:328
+msgid "-u, --username[=username]    Horde login username"
+msgstr "-u, --username[=kullanýcýadý]  Horde kullanýcý adý"
+
+#: lib/Exif.php:165
+#, php-format
+msgid "90 deg CCW"
+msgstr "90° Saat Yönünün ters yönde"
+
+#: lib/Exif.php:167
+#, php-format
+msgid "90 deg CCW Mirrored"
+msgstr "90° saat yönünde döndürerek yansýt"
+
+#: lib/Exif.php:169
+#, php-format
+msgid "90 deg CW"
+msgstr "90° Saat Yönünde"
+
+#: lib/Exif.php:163
+#, php-format
+msgid "90 deg CW Mirrored"
+msgstr "90° saat yönünün tersi yönüne yansýt"
+
+#: config/styles.php.dist:128
+msgid "A Lightbox Inspired Style (Drop Shadows, No Background)"
+msgstr "Lightbox'a benzyen stil (Arkapanosu olmayan)"
+
+#: config/styles.php.dist:116
+msgid "A Lightbox Inspired Style (No Background)"
+msgstr "Lightbox'a benzeyen stil (Arkapanosuz)"
+
+#: config/styles.php.dist:104
+msgid "A Lightbox Inspired Style (White Background)"
+msgstr " Lightbox'a benzeyen stil (Beyaz arkapanolu)"
+
+#: lib/api.php:506
+msgid "A gallery to add this photo to is required."
+msgstr "Resmi eklemek istediðiniz galeri belirtilmeli."
+
+#: lib/Exif.php:136
+msgid "A-DEP"
+msgstr "A-DEP"
+
+#: gallery.php:56
+#, php-format
+msgid "Access denied adding a gallery to \"%s\"."
+msgstr "Galeriyi \"%s\" eklerken eriþim engellendi."
+
+#: xppublish.php:178 lib/api.php:384 lib/api.php:520 scripts/ansel.php:134
+#, php-format
+msgid "Access denied adding photos to \"%s\"."
+msgstr "Fotoðrafý \"%s\" eklerken eriþim engellendi."
+
+#: lib/Ansel.php:1728
+msgid "Access denied adding tags to this gallery."
+msgstr "Bu galeriye anahtarsözcük kaydedilmesine izin verilmedi."
+
+#: lib/Ansel.php:2774
+msgid "Access denied adding tags to this photo."
+msgstr "Bu fotoðrafa anahtarsözcük kaydedilmesine izin verilmedi ."
+
+#: lib/Ansel.php:1365
+#, php-format
+msgid "Access denied copying photos to \"%s\"."
+msgstr "Fotoðrafý \"%s\" kayd edilmesine izin verilmedi."
+
+#: lib/api.php:620
+msgid "Access denied creating new galleries."
+msgstr "Yeni galeri oluþturmaya müsade edilmedi."
+
+#: lib/api.php:578 lib/api.php:657
+msgid "Access denied deleting galleries."
+msgstr "Galerilerin silinmesine müsade verilmedi."
+
+#: lib/api.php:669 gallery/delete.php:28 gallery/delete.php:57
+#, php-format
+msgid "Access denied deleting gallery \"%s\"."
+msgstr "\"%s\" galerisinin silinmesi izin verilmedi."
+
+#: image.php:573 lib/api.php:595 lib/Views/Results.php:125
+#, php-format
+msgid "Access denied deleting photos from \"%s\"."
+msgstr "\"%s\" fotoðralarýn silinmesine müsade edilmedi."
+
+#: gallery.php:94 lib/Ansel.php:2576
+#, php-format
+msgid "Access denied downloading photos from \"%s\"."
+msgstr "\"%s\" den fotoðralarýn indirilmesine müsade edilmedi."
+
+#: gallery.php:178
+msgid "Access denied editing galleries."
+msgstr "Galerilerin düzenlemesine izin verilmedi."
+
+#: faces/gallery.php:34 lib/Faces.php:871 gallery/sort.php:32
+#, php-format
+msgid "Access denied editing gallery \"%s\"."
+msgstr "\"%s\" galerisini düzenlenmesine müsade edilmedi."
+
+#: image.php:304 image.php:699 faces/delete.php:29 faces/name.php:28
+#: faces/custom.php:29 lib/Faces.php:557 lib/Faces.php:655
+#: lib/XRequest/EditFaces.php:134
+msgid "Access denied editing the photo."
+msgstr "Fotoðrafýn düzenlemesine müsade edilmedi."
+
+#: lib/GalleryMode/Normal.php:142 lib/GalleryMode/Date.php:354
+#, php-format
+msgid "Access denied moving photos to \"%s\"."
+msgstr "Fotoðraflarýn \"%s\" taþýnmasýna müsade edilmedi."
+
+#: lib/GalleryMode/Normal.php:144 lib/GalleryMode/Date.php:356
+#, php-format
+msgid "Access denied removing photos from \"%s\"."
+msgstr "\"%s\" fotoðraflarýn silinmesine izin verilmedi."
+
+#: gallery.php:222
+#, php-format
+msgid "Access denied saving gallery \"%s\"."
+msgstr "\"%s\" galerisini kaydetmeye müsade edilmedi."
+
+#: image.php:176 image.php:358 image.php:399
+#, php-format
+msgid "Access denied saving photo to \"%s\"."
+msgstr "Fotoðrafýn \"%s\" kaydedilmesine izin verilmedi."
+
+#: gallery/captions.php:33
+#, php-format
+msgid "Access denied setting captions for %s."
+msgstr "%s baþlýðýný velirlenmesine müsade edilmedi."
+
+#: lib/Views/Abstract.php:56
+#, php-format
+msgid "Access denied to gallery \"%s\"."
+msgstr "\"%s\" galerisine izin verilmedi."
+
+#: lib/Ansel.php:1713 lib/Block/recently_added.php:190
+#: lib/Block/gallery.php:152 lib/Block/recent_comments.php:154
+#: lib/Views/Gallery.php:102
+msgid "Access denied viewing this gallery."
+msgstr "Bu galerinin görüntülenmesine izin verilmedi"
+
+#: img/screen.php:25 img/thumb.php:25 img/mini.php:25 img/index.php:25
+#: img/download.php:26 img/prettythumb.php:26 img/full.php:26
+#: lib/Ansel.php:2753
+msgid "Access denied viewing this photo."
+msgstr "Bu fotoðrafýn görüntülenmesine izin verilmedi."
+
+#: faces/report.php:43 faces/claim.php:41
+msgid "Action was cancelled."
+msgstr "Eylem durduruldu."
+
+#: templates/view/gallery.inc:114
+msgid "Actions for Selected Images: "
+msgstr "Ýþaretlenmiþ Fotoðraflar için Eylemler"
+
+#: templates/view/gallerylightbox.inc:136 templates/view/results.inc:81
+msgid "Actions: "
+msgstr "Eylemler: "
+
+#: lib/Widget/Tags.php:58
+msgid "Add"
+msgstr "Ekle"
+
+#: browse.php:26
+msgid "Add Content"
+msgstr "Ýçerik ekle"
+
+#: img/upload.php:243
+msgid "Add Photo"
+msgstr "Fotoðraf ekle"
+
+#: templates/faces/gallery.inc:23
+msgid "Add a custom face range"
+msgstr "Manuel sima ekle"
+
+#: lib/Widget/Actions.php:120
+msgid "Add to bookmarks"
+msgstr "Sýk kullanýlanlara ekle"
+
+#: gallery.php:42 gallery.php:298
+msgid "Adding A New Gallery"
+msgstr "Yeni galeri ekle"
+
+#: gallery.php:77
+#, php-format
+msgid "Adding A Subgallery to %s"
+msgstr "%s'e altgaleri ekle"
+
+#: scripts/remote_import.php:271 scripts/recursive_import.php:205
+msgid "Adding subdirectories:"
+msgstr "Altdizinler ekle:"
+
+#: lib/api.php:194
+msgid "Administrators"
+msgstr "Administratörler"
+
+#: report.php:49
+msgid "Advertisement content"
+msgstr "Reklam içerikleri"
+
+#: templates/view/gallerylightbox.inc:133 templates/view/gallery.inc:111
+#: templates/view/results.inc:77
+msgid "All"
+msgstr "Hepsi"
+
+#: lib/Block/recently_added.php:73 lib/Block/recent_comments.php:68
+msgid "All Galleries"
+msgstr "Tüm Galeriler"
+
+#: faces/search/tabs.php:32 faces/search/all.php:17
+msgid "All faces"
+msgstr "Tüm simalar"
+
+#: templates/gallery/gallery.inc:87
+msgid "Allow all ages"
+msgstr "Tüm yaþlara müsade et"
+
+#: templates/gallery/gallery.inc:109 config/prefs.php.dist:229
+msgid "Anyone"
+msgstr "Herkes"
+
+#: lib/Exif.php:351
+msgid "Aperture"
+msgstr "Objektif Açýklýðý"
+
+#: templates/view/gallerylightbox.inc:33 templates/view/gallery.inc:32
+#: templates/view/results.inc:20
+msgid "Are you sure you want to delete the selected photos?"
+msgstr "Seçilmiþ fotoðraflarý gerçekten silmek istiyor musun?"
+
+#: lib/Exif.php:364
+msgid "Artist"
+msgstr "Fotoðrafcý"
+
+#: templates/view/list.inc:20
+msgid "Ascending"
+msgstr "Yükselen"
+
+#: perms.php:53
+msgid "Attempt to edit a non-existent share."
+msgstr "Mevcut olmayan share düzenlenmeye çalýþýldý."
+
+#: templates/gallery/gallery.inc:110 config/prefs.php.dist:230
+msgid "Authenticated users"
+msgstr "Giriþ yapmýþ kullanýcýlar"
+
+#: config/prefs.php.dist:221
+msgid "Automatically watermark photos?"
+msgstr "Otomatik gizli damga girilsin mi?"
+
+#: lib/Exif.php:132
+msgid "Av-priority"
+msgstr "Av-Priorität"
+
+#: lib/Exif.php:236
+msgid "Average"
+msgstr "Ortalama"
+
+#: templates/image/resize_image.inc:39 templates/image/edit_image.inc:8
+#: templates/image/crop_image.inc:92 templates/view/image.inc:87
+#: templates/view/image.inc:88 templates/view/image.inc:113
+#: templates/view/image.inc:114
+msgid "Back to gallery"
+msgstr "Galeriye geri dön"
+
+#: image.php:621 image.php:670 lib/Views/Results.php:160
+#: lib/Views/Results.php:208
+msgid "Bad input."
+msgstr "Yanlýþ veri."
+
+#: lib/Forms/Watermark.php:54 config/prefs.php.dist:198
+msgid "Bottom"
+msgstr "Alt"
+
+#: config/prefs.php.dist:57
+msgid "Browse"
+msgstr "Listele"
+
+#: lib/Ansel.php:841
+msgid "Browse Tags"
+msgstr "Anahtarsözcükleri göster"
+
+#: lib/Exif.php:335
+msgid "Camera Make"
+msgstr "Kamerayý üreten"
+
+#: lib/Exif.php:336
+msgid "Camera Model"
+msgstr "Kamera modeli"
+
+#: lib/Exif.php:362
+msgid "Camera Orientation"
+msgstr "Kamera bakýþý"
+
+#: lib/Ansel.php:516 scripts/remote_import.php:302
+#, php-format
+msgid "Can't get unknown file type \"%s\"."
+msgstr "Tanýnmamýþ dosya tipi \"%s\" düzenlenemiyor."
+
+#: report.php:47 disclamer.php:32 disclamer.php:35
+#: templates/image/resize_image.inc:40 templates/image/edit_image.inc:9
+#: templates/image/crop_image.inc:93 templates/faces/custom.inc:53
+#: templates/faces/define.inc:38 templates/gallery/delete_confirmation.inc:13
+#: faces/savecustom.php:27 faces/report.php:41 faces/claim.php:40
+msgid "Cancel"
+msgstr "Ýptal"
+
+#: templates/image/preview_image.inc:25 templates/image/preview_image.inc:26
+#: templates/image/preview_cropimage.inc:22
+#: templates/image/preview_cropimage.inc:23
+msgid "Cancel Change"
+msgstr "Deðiþiklikleri Ýptal Et"
+
+#: faces/search/image_save.php:74
+msgid "Cannot read photo signature"
+msgstr "Fotoðraf imzasý okunamýyor"
+
+#: faces/search/image_save.php:86
+msgid "Cannot save photo signature"
+msgstr "Fotoðraf imzasý kaydedilemedi"
+
+#: faces/search/image_save.php:66 faces/search/image.php:59
+msgid "Cannot store search photo"
+msgstr "Arama fotoðrafý kaydedilemedi"
+
+#: lib/Forms/Upload.php:52 lib/Forms/Image.php:39
+msgid "Caption"
+msgstr "Fotoðrafbaþlýðý"
+
+#: gallery/captions.php:66
+msgid "Caption Editor"
+msgstr "Fotoðrafbaþlýðý için editör"
+
+#: gallery/captions.php:56
+msgid "Captions Saved."
+msgstr "Fotoðrafbaþlýðý kayýt edildi."
+
+#: config/prefs.php.dist:70
+msgid "Category"
+msgstr "kategori"
+
+#: lib/Forms/Watermark.php:46 lib/Forms/Watermark.php:53
+#: config/prefs.php.dist:186 config/prefs.php.dist:197
+msgid "Center"
+msgstr "Ortala"
+
+#: lib/Exif.php:237
+msgid "Center Weighted Average"
+msgstr "Santral Ortalama Aðýrlýk"
+
+#: config/prefs.php.dist:14
+msgid ""
+"Change display options such as which view to display by default, how many "
+"photos to display on a page, and the default gallery style to use."
+msgstr ""
+"Görüntüleme ayarlarýný deðiþtir, örneðin hangi standart görünüm, kaç "
+"fotoðraf bir sayfada görüntülensin ve galeri stilini seç gibi."
+
+#: config/prefs.php.dist:24
+msgid "Change options dealing with tags and exif data."
+msgstr "Exif verileri ve anahtarsözcüklerle ilgili ayarlarý deðiþ"
+
+#: lib/Widget/Actions.php:132
+msgid "Change properties"
+msgstr "Özellikleri deðiþtir"
+
+#: config/prefs.php.dist:31
+msgid ""
+"Change your user permission options such as who can download original "
+"photos, and what permissions newly created galleries should have by default."
+msgstr ""
+"Kullanýcý izinleleri seçeneklerini deðiþtirin. Örneðin kimler orijinal "
+"fotoðraflarý indirebilir ve hangi haklar/izinler yeni oluþturulmuþ "
+"galerilere standart olarak verilsin."
+
+#: config/prefs.php.dist:38
+msgid "Change your watermark options."
+msgstr "Gizli damga ayarlarýný deðiþtir."
+
+#: faces/savecustom.php:28
+msgid "Changes cancelled."
+msgstr "Deðiþiklikler iptal edildi."
+
+#: templates/tile/face.inc:27 lib/Faces.php:1149
+msgid "Claim"
+msgstr "Bildir"
+
+#: lib/XRequest/EditCaption.php:43
+msgid "Click to add caption..."
+msgstr "Baþlýk eklemek için týkla..."
+
+#: lib/Exif.php:250
+msgid "Cloudy"
+msgstr "Bulutlu"
+
+#: lib/Exif.php:301
+msgid "Color Sequential Area Sensor"
+msgstr ""
+
+#: lib/Exif.php:303
+msgid "Color Sequential Linear Sensor"
+msgstr ""
+
+#: lib/Exif.php:359
+msgid "Color Space"
+msgstr "Renk alaný"
+
+#: lib/Forms/Ecard.php:42
+msgid "Comments:"
+msgstr "Yorumlar:"
+
+#: lib/Exif.php:271
+msgid "Compulsory Flash"
+msgstr "Kompulsif Flaþ"
+
+#: lib/Exif.php:273
+msgid "Compulsory Flash, Return light detected"
+msgstr "Kompulsif Flaþ, Yansýma Iþýðý bulundu"
+
+#: lib/Exif.php:272
+msgid "Compulsory Flash, Return light not detected"
+msgstr "Kompulsif Flaþ, Yansýma Iþýðý bulunamadý"
+
+#: disclamer.php:25
+msgid "Content Disclaimer"
+msgstr "Ýçerik açýklamalarý"
+
+#: lib/Report.php:98
+#, php-format
+msgid "Content abuse report in %s"
+msgstr "%s deki yasa dýþý içeriði rapor et"
+
+#: disclamer.php:32
+#, php-format
+msgid "Continue - I'm over %d"
+msgstr "Devam - %d'den üzereyim"
+
+#: lib/Views/Results.php:225
+#, php-format
+msgid "Copied %d photo from %s to %s"
+msgid_plural "Copied %d photos from %s to %s"
+msgstr[0] "%d fotoðraf %s dan %s kopyalandý"
+
+#: image.php:677
+#, php-format
+msgid "Copied %d photo to %s"
+msgid_plural "Copied %d photos to %s"
+msgstr[0] "%d fotoðraf %s kopyalandý"
+
+#: templates/view/gallerylightbox.inc:147 templates/view/gallery.inc:125
+#: templates/view/results.inc:87
+msgid "Copy"
+msgstr "Kopyala"
+
+#: lib/Exif.php:363
+msgid "Copyright"
+msgstr "Telif hakký"
+
+#: report.php:52
+msgid "Copyright infringement"
+msgstr "Telif hakký ihlali"
+
+#: scripts/all_images_exif_to_tags.php:102
+#: scripts/all_images_exif_to_tags.php:106
+#, php-format
+msgid "Could not extract exif fields from %s: %s"
+msgstr "%s: %s'den exif verileri çýkarýlamadý"
+
+#: scripts/remote_import.php:308
+msgid "Could not load the gzip extension"
+msgstr "Gzip modülü yüklenemedi"
+
+#: scripts/remote_import.php:312
+msgid "Could not load the lzf extension"
+msgstr "lzf modülü yüklenemedi"
+
+#: lib/Ansel.php:1170
+#, php-format
+msgid "Could not save gallery, the slug, \"%s\", already exists."
+msgstr "Galeriyi kaydedemedim. Kullanýlan kýsa ad, \"%s\", zaten mevcut "
+
+#: scripts/ansel.php:187
+msgid "Couldn't list galleries."
+msgstr "Galeriler listelenemedi."
+
+#: scripts/ansel.php:44
+msgid "Couldn't read command-line options."
+msgstr "Komut satýrý seçenekleri okunamadý."
+
+#: templates/xppublish/list.inc:19
+msgid "Create"
+msgstr "Oluþtur"
+
+#: faces/custom.php:68 faces/search/image_define.php:25
+msgid "Create a new face"
+msgstr "Yeni sima oluþtur"
+
+#: templates/xppublish/new.inc:8
+msgid "Create a new gallery"
+msgstr "Yeni galeri oluþtur:"
+
+#: templates/xppublish/list.inc:18
+msgid "Create a new gallery:"
+msgstr "Yeni bir galeri oluþtur:"
+
+#: lib/Widget/Actions.php:64
+msgid "Create a subgallery"
+msgstr "Altgaleri oluþtur"
+
+#: lib/Tile/Gallery.php:119
+msgid "Created:"
+msgstr "Oluþturulma:"
+
+#: scripts/remote_import.php:196 scripts/recursive_import.php:154
+#, php-format
+msgid "Creating gallery: \"%s\""
+msgstr "Galeri oluþturuluyor: \"%s\""
+
+#: image.php:711 templates/image/resize_image.inc:62
+#: templates/image/edit_image.inc:31 templates/image/crop_image.inc:114
+msgid "Crop"
+msgstr "Kes"
+
+#: lib/Exif.php:254
+msgid "Custom"
+msgstr "Özel"
+
+#: lib/Forms/Watermark.php:32
+msgid "Custom Watermark"
+msgstr "Özel gizli damga"
+
+#: config/prefs.php.dist:50
+msgid ""
+"Custom text to display describing your galleries. This will be displayed in "
+"place of your username when grouping galleries by username."
+msgstr ""
+"Galerinizi tarif eden özgür yazý. Kullanýcý adýna göre gruplaþtýrýldýðýnda "
+"kullanýcý adýnýz yerine gösterileçektir."
+
+#: config/prefs.php.dist:121 config/prefs.php.dist:177
+msgid "Custom watermark to use for photos"
+msgstr "Fotoðraflarda kullanýlmasý için özel gizli damga"
+
+#: templates/view/list.inc:17 lib/Block/recently_added.php:99
+msgid "Date"
+msgstr "Tarih"
+
+#: lib/Exif.php:342
+msgid "Date Photo Digitized"
+msgstr "Dijitalleþtirme tarihi"
+
+#: lib/Exif.php:340
+msgid "Date Photo Modified"
+msgstr "Fotoðrafýn deðiþitirilme tarihi"
+
+#: lib/Exif.php:341 config/prefs.php.dist:274
+msgid "Date Photo Taken"
+msgstr "Çekiliþtarihi"
+
+#: config/prefs.php.dist:137 config/styles.php.dist:56
+msgid "Default"
+msgstr "Standart"
+
+#: templates/prefs/default_category_select.inc:7
+msgid "Default category for galleries"
+msgstr "Galeriler için standart kategori"
+
+#: config/prefs.php.dist:136
+msgid "Default style for galleries"
+msgstr "Galeriler için standart stil"
+
+#: templates/view/image.inc:52 templates/view/slideshow.inc:21
+#: templates/view/gallerylightbox.inc:141 templates/view/gallery.inc:119
+#: templates/view/results.inc:82 lib/Widget/Actions.php:170
+msgid "Delete"
+msgstr "Sil"
+
+#: templates/faces/image.inc:7
+msgid "Delete face"
+msgstr "Simayý sil"
+
+#: image.php:582 lib/Views/Results.php:134
+msgid "Deleted the photo."
+msgstr "Fotoðraf silindi."
+
+#: scripts/remote_import.php:137 scripts/remote_import.php:145
+#: scripts/recursive_import.php:122 scripts/recursive_import.php:130
+#, php-format
+msgid "Deleting empty gallery, \"%s\""
+msgstr "Silinecek boþ galeri: \"%s\""
+
+#: templates/view/list.inc:22
+msgid "Descending"
+msgstr "Azalan"
+
+#: templates/image/crop_image.inc:134
+msgid "Desired dimensions:"
+msgstr "Ýstenilen ebat:"
+
+#: templates/gallery/gallery.inc:47
+msgid "Display Mode"
+msgstr "Görüntüleme modu"
+
+#: config/prefs.php.dist:13
+msgid "Display Options"
+msgstr "Görüntüleme Ayarlarý"
+
+#: lib/Faces.php:1148
+msgid "Do you know someone in this photo?"
+msgstr "Bu fotoðrafta birisini tanýyor musunuz?"
+
+#: report.php:20
+msgid "Do you really want to report this gallery?"
+msgstr "Bu galeriyi gerçekten rapor etmek istiyor musun?"
+
+#: report.php:31
+msgid "Do you really want to report this photo?"
+msgstr "Bu fotoðrafý gerçekten bildirmek istiyor musunuz?"
+
+#: templates/view/image.inc:52 templates/view/slideshow.inc:21
+#, php-format
+msgid "Do you want to permanently delete \"%s\"?"
+msgstr "\"%s\" Tamamen silmek istediðinizden emin misiniz?"
+
+#: gallery/sort.php:81 scripts/all_images_exif_to_tags.php:109
+msgid "Done"
+msgstr "Bitti"
+
+#: templates/view/image.inc:58 templates/view/slideshow.inc:29
+msgid "Download Original Photo"
+msgstr "Orijinal fotoðrafý indir"
+
+#: lib/Widget/Actions.php:126
+msgid "Download as zip file"
+msgstr "Zip dosyasý olarak indir"
+
+#: gallery.php:82
+msgid ""
+"Downloading zip files is not enabled. Talk to your server administrator."
+msgstr ""
+
+#: gallery/sort.php:80
+msgid "Drag photos to the desired sort position."
+msgstr "Sýralamak istediðiniz þekilde fotoðraflarý yerleþtirin."
+
+#: xppublish.php:206
+#, php-format
+msgid "ERROR: %s"
+msgstr "HATA: %s"
+
+#: lib/Exif.php:126
+msgid "Easy shooting"
+msgstr "Easy shooting"
+
+#: img/ecard.php:59
+msgid "Ecard - "
+msgstr "E-Kartpostal - "
+
+#: templates/view/image.inc:49 templates/view/slideshow.inc:18
+#: templates/tile/image.inc:18
+msgid "Edit"
+msgstr "Deðiþtir"
+
+#: image.php:314
+#, php-format
+msgid "Edit %s :: %s"
+msgstr "%s deðiþtir :: %s"
+
+#: edit_dates.php:37 templates/view/gallerylightbox.inc:138
+#: templates/view/gallery.inc:116
+msgid "Edit Dates"
+msgstr "Tarihleri düzenle"
+
+#: perms.php:245
+msgid "Edit Permissions"
+msgstr "Ýzinleri deðiþtir"
+
+#: perms.php:249
+#, php-format
+msgid "Edit Permissions for %s"
+msgstr "'%s' izinlerini deðiþtir"
+
+#: image.php:507
+msgid "Edit Photo"
+msgstr "Fotoðrafý düzenle"
+
+#: image.php:194
+msgid "Edit a photo"
+msgstr "Bir fotoðrafý düzenle"
+
+#: templates/faces/image.inc:9
+msgid "Edit face region"
+msgstr "Sima alanýný düzenle"
+
+#: lib/Widget/GalleryFaces.php:76 lib/Widget/ImageFaces.php:77
+msgid "Edit faces"
+msgstr "Simalarý düzenle"
+
+#: image.php:144
+#, php-format
+msgid "Edit properties :: %s"
+msgstr "Özellikleri deðiþtir :: %s"
+
+#: lib/Forms/ImageDate.php:20
+msgid "Editing dates for the following images"
+msgstr "Þu fotoðraflarýn tarihleri düzenlenecek"
+
+#: lib/Widget/Links.php:43
+msgid "Embed: "
+msgstr "Göm: "
+
+#: lib/Widget/Actions.php:169
+msgid "Empty"
+msgstr "Boþalt"
+
+#: gallery/captions.php:26
+#, php-format
+msgid "Error accessing %s: %s"
+msgstr "%s eriþimde hata oluþtu: %s"
+
+#: rss.php:35
+msgid "Error retrieving feed"
+msgstr "Feed okunurken hata oluþtu"
+
+#: templates/xppublish/list.inc:12
+msgid "Existing galleries:"
+msgstr "Var olan galeriler:"
+
+#: lib/Exif.php:348
+msgid "Exposure"
+msgstr "Poz"
+
+#: lib/Exif.php:354
+msgid "Exposure Bias"
+msgstr "Poz Meyili"
+
+#: lib/Exif.php:355
+msgid "Exposure Mode"
+msgstr "Poz Modu"
+
+#: config/prefs.php.dist:275
+msgid "Exposure Time"
+msgstr "Poz Süresi"
+
+#: scripts/all_images_exif_to_tags.php:104
+#, php-format
+msgid "Extracted exif fields from %s"
+msgstr "%s'den exif verilierini oku"
+
+#: lib/Exif.php:352
+msgid "F-Number"
+msgstr "F-Numarasý"
+
+#: faces/report.php:60 faces/claim.php:58 faces/face.php:34
+msgid "Face"
+msgstr "Sima"
+
+#: lib/Faces.php:997 lib/Faces.php:1025
+msgid "Face does not exist"
+msgstr "Sima bulunamadý"
+
+#: lib/Faces.php:46
+msgid "Face driver does not exist."
+msgstr "Sima tanýma mekanizmasý için sürüçü mevcut deðil."
+
+#: faces/search/name.php:20
+msgid "Face name to search"
+msgstr "Aranmasý istenen simaadý"
+
+#: faces/report.php:65 faces/claim.php:63
+msgid "Face name was not reported."
+msgstr "Der Gesichtername wurde nicht gespeichert."
+
+#: faces/savecustom.php:54
+msgid "Face successfuly created"
+msgstr "Sima baþarýyla oluþturuldu"
+
+#: faces/savecustom.php:56
+msgid "Face successfuly updated"
+msgstr "Sima baþarýyla düzenlendi"
+
+#: faces/search/image.php:21
+msgid "Face to search for"
+msgstr "Aranmasý istenilen sima"
+
+#: templates/faces/gallery.inc:7
+msgid "Faces"
+msgstr "Simalar"
+
+#: lib/Forms/Upload.php:67
+#, php-format
+msgid "File %s"
+msgstr "Dosya %s"
+
+#: lib/Exif.php:339
+msgid "File Size"
+msgstr "Dosya boyutu"
+
+#: lib/api.php:286 lib/api.php:352
+msgid "File not found."
+msgstr "Dosya bulunamadý."
+
+#: lib/Forms/Upload.php:47 lib/Forms/Upload.php:76
+msgid "File to upload"
+msgstr "Yüklenecek dosya"
+
+#: templates/faces/gallery.inc:5
+msgid "Filename"
+msgstr "Dosya adý"
+
+#: lib/Widget/GalleryFaces.php:76 lib/Widget/ImageFaces.php:77
+#: lib/Widget/Actions.php:149
+msgid "Find faces"
+msgstr "Sima bul"
+
+#: templates/tile/face.inc:24
+msgid "Find other photos this face appears in"
+msgstr "Bu simanýn bulunduðu diðer fotoðraflarý bul"
+
+#: templates/faces/face.inc:14 templates/tile/face.inc:34 lib/Faces.php:1154
+msgid "Find similar"
+msgstr "Benzer ara"
+
+#: lib/Exif.php:253 lib/Exif.php:268
+msgid "Flash"
+msgstr "Flaþ"
+
+#: lib/Exif.php:357
+msgid "Flash Setting"
+msgstr "Flaþ Ayarlarý"
+
+#: lib/Exif.php:276
+msgid "Flash, Auto-Mode"
+msgstr "Flaþ, Oto-Modu"
+
+#: lib/Exif.php:278
+msgid "Flash, Auto-Mode, Return light detected"
+msgstr "Flaþ, Oto-Modu, Yansýtma Iþýðý Bulundu"
+
+#: lib/Exif.php:277
+msgid "Flash, Auto-Mode, Return light not detected"
+msgstr "Flaþ, Oto-Modu, Yansýtma Iþýðý Bulunamadý"
+
+#: lib/Exif.php:270
+msgid "Flash, strobe return light detected"
+msgstr ""
+
+#: lib/Exif.php:269
+msgid "Flash, strobe return light not detected"
+msgstr ""
+
+#: templates/image/resize_image.inc:48 templates/image/edit_image.inc:17
+#: templates/image/crop_image.inc:101
+msgid "Flip"
+msgstr "Çevir"
+
+#: lib/Exif.php:252
+msgid "Fluorescent"
+msgstr "Floresan Iþýk"
+
+#: lib/Exif.php:349
+msgid "Focal Length"
+msgstr "Odaklama Mesafesi"
+
+#: lib/Exif.php:350
+msgid "Focal Length (35mm equiv)"
+msgstr "Brennweite (entsprechend KB)"
+
+#: faces/search/owner.php:23
+#, php-format
+msgid "From galleries of %s"
+msgstr "%s'in galerilerinden"
+
+#: faces/search/tabs.php:33 faces/search/owner.php:21
+msgid "From my galleries"
+msgstr "Benim galerilerimden"
+
+#: templates/tile/image.inc:8
+msgid "From: "
+msgstr "Gönderen: "
+
+#: lib/Ansel.php:838 config/prefs.php.dist:58
+msgid "Galleries"
+msgstr "Galeriler"
+
+#: templates/group/header.inc:6
+#, php-format
+msgid "Galleries grouped by %s"
+msgstr "%s göre gruplaþtýrýlmýþ galeriler"
+
+#: lib/Views/List.php:174 lib/Views/List.php:178
+#, php-format
+msgid "Galleries in category \"%s\""
+msgstr "Kategoride galeri \"%s\""
+
+#: templates/faces/face.inc:33 lib/Block/recently_added.php:28
+#: lib/Block/recently_added.php:56 lib/Block/recently_added.php:99
+#: lib/Block/gallery.php:3 lib/Block/gallery.php:30 lib/Block/gallery.php:54
+#: lib/Block/recent_comments.php:31 lib/Block/recent_comments.php:54
+msgid "Gallery"
+msgstr "Galeri"
+
+#: image.php:65 img/upload.php:22 img/upload_preview.php:19 lib/Ansel.php:3103
+#: scripts/remote_import.php:183 scripts/remote_import.php:191
+#, php-format
+msgid "Gallery %s not found."
+msgstr "%s galerisi bulunamadý."
+
+#: lib/Widget/Actions.php:22
+msgid "Gallery Actions"
+msgstr "Galeri Eylemleri"
+
+#: templates/gallery/gallery.inc:85
+msgid "Gallery Ages"
+msgstr "Galeri Yaþý"
+
+#: templates/gallery/gallery.inc:26
+msgid "Gallery Category"
+msgstr "Galeri kategorisi"
+
+#: report.php:71 templates/gallery/gallery.inc:63 faces/report.php:57
+#: faces/claim.php:55
+msgid "Gallery Description"
+msgstr "Galeri-Tanýmlamasý"
+
+#: templates/gallery/gallery.inc:56
+msgid "Gallery Display Name"
+msgstr "Galeri Görüntüleme Adý"
+
+#: report.php:72
+msgid "Gallery Id"
+msgstr "Galeri-Id'si"
+
+#: lib/Views/List.php:70 lib/Views/List.php:181
+msgid "Gallery List"
+msgstr "Galeri Listesi"
+
+#: report.php:70 faces/report.php:56 faces/claim.php:54
+#: lib/Block/my_galleries.php:53
+msgid "Gallery Name"
+msgstr "Galeri adý"
+
+#: templates/gallery/gallery.inc:17
+msgid "Gallery Parent"
+msgstr "Üst galeri"
+
+#: lib/Tile/Gallery.php:96
+msgid "Gallery Properties"
+msgstr "Galeri Özellikleri"
+
+#: templates/gallery/gallery.inc:69
+msgid "Gallery Slug"
+msgstr "Kýsa Galeri Adý"
+
+#: templates/gallery/gallery.inc:77
+msgid "Gallery Tags"
+msgstr "Galerinin Anahtarsözcükleri"
+
+#: lib/Block/recently_added.php:187 lib/Block/gallery.php:149
+#: lib/Block/recent_comments.php:151
+msgid "Gallery does not exist."
+msgstr "Galeri mevcut deðil."
+
+#: gallery/sort.php:49
+msgid "Gallery sorted."
+msgstr "Galeri yeniden düzenlendi."
+
+#: report.php:79 report.php:85
+msgid "Gallery was not reported."
+msgstr "Bu galeri rapor edilmedi."
+
+#: report.php:82
+msgid "Gallery was reported."
+msgstr "Bu galeri rapor edildi."
+
+#: config/prefs.php.dist:12 config/prefs.php.dist:22 config/prefs.php.dist:29
+#: config/prefs.php.dist:36
+msgid "General Options"
+msgstr "Genel Seçenekler"
+
+#: lib/Forms/Watermark.php:40 config/prefs.php.dist:211
+msgid "Giant"
+msgstr "Çok Büyük"
+
+#: templates/image/resize_image.inc:54 templates/image/edit_image.inc:23
+#: templates/image/crop_image.inc:107
+msgid "Grayscale"
+msgstr "Gri Ton"
+
+#: templates/gallery/gallery.inc:51
+msgid "Group By Date"
+msgstr "Tarihe Göre Gruplaþtýr"
+
+#: templates/group/header.inc:11 templates/view/list.inc:10
+msgid "Group by: "
+msgstr "Gruplaþtýr: "
+
+#: config/prefs.php.dist:72
+msgid "Group galleries by"
+msgstr "Galerileri gruplaþtýr"
+
+#: lib/Exif.php:344
+msgid "Height"
+msgstr "Boy"
+
+#: lib/Forms/Watermark.php:48 config/prefs.php.dist:188
+msgid "Horizontal Alignment"
+msgstr "Yatay hizalandýr"
+
+#: faces/claim.php:53
+msgid "I know who is on one of your photos"
+msgstr "Fotoðraflarýnýn birinde birisini tanýyorum"
+
+#: lib/Exif.php:353
+msgid "ISO Setting"
+msgstr "ISO-Ayarlarý"
+
+#: image.php:139
+msgid "Image not found."
+msgstr "Fotoðraf bulunamadý."
+
+#: templates/image/upload.inc:24
+msgid "Images in this gallery"
+msgstr "Bu galerideki fotoðraflar"
+
+#: lib/Ansel.php:2277
+msgid "Incomplete photo"
+msgstr "Tam olmayan fotoðraf"
+
+#: scripts/ansel.php:127
+#, php-format
+msgid "Invalid gallery \"%s\" specified."
+msgstr "Gecersiz Galeri \"%s\" belirtildi."
+
+#: xppublish.php:87 xppublish.php:174 lib/api.php:273
+msgid "Invalid gallery specified."
+msgstr "Gecersiz galeri belirtildi."
+
+#: templates/image/crop_image.inc:149
+msgid "Landscape"
+msgstr "Manzara"
+
+#: lib/Forms/Watermark.php:39 config/prefs.php.dist:210
+msgid "Large"
+msgstr "Büyük"
+
+#: lib/Block/my_galleries.php:53
+msgid "Last Modified"
+msgstr "Son Deðiþtirme"
+
+#: templates/image/upload.inc:6
+msgid "Learn how to publish photos directly from Windows."
+msgstr "Fotoðraflarý Windows'dan direk yüklemeyi öðren."
+
+#: lib/Forms/Watermark.php:45 config/prefs.php.dist:185
+msgid "Left"
+msgstr "Sol"
+
+#: config/styles.php.dist:142 config/styles.php.dist:179
+msgid "Lightbox with Polaroids (No Background)"
+msgstr "Polaroidlý Lightbox (Arka panosuz)"
+
+#: lib/Widget/Links.php:15
+msgid "Links"
+msgstr "Linkler"
+
+#: scripts/ansel.php:192
+msgid "Listing Gallery/Name"
+msgstr "Galerilistesi/Ad"
+
+#: scripts/ansel.php:176
+#, php-format
+msgid "Listing photos in %s"
+msgstr "Fotoðraflý %s göster"
+
+#: templates/image/upload.inc:29
+msgid "Loading ..."
+msgstr "Yükleniyor ..."
+
+#: templates/faces/gallery.inc:28 templates/faces/gallery.inc:39
+#: lib/XRequest/EditFaces.php:35
+msgid "Loading..."
+msgstr "Yükleniyor..."
+
+#: scripts/recursive_import.php:88 scripts/all_images_exif_to_tags.php:79
+#: scripts/ansel.php:118
+#, php-format
+msgid "Logged in successfully as \"%s\"."
+msgstr "Baþarý ile \"%s\" olarak giriþ yapýldý."
+
+#: scripts/ansel.php:114
+msgid "Login is incorrect."
+msgstr "Giriþ bilgileri yanlýþ."
+
+#: templates/xppublish/login.inc:2
+#, php-format
+msgid "Login to %s"
+msgstr "%s giriþ yap"
+
+#: lib/Forms/Upload.php:51 lib/Forms/Image.php:37
+msgid "Make this the default photo for this gallery?"
+msgstr "Bu galeri için standard fotoðraf olarak kullanýlsýn mý?"
+
+#: scripts/remote_import.php:337 scripts/recursive_import.php:223
+#: scripts/all_images_exif_to_tags.php:118 scripts/ansel.php:320
+msgid ""
+"Mandatory arguments to long options are mandatory for short options too."
+msgstr ""
+
+#: templates/image/crop_image.inc:138 lib/Exif.php:134 lib/Exif.php:255
+msgid "Manual"
+msgstr "Manuel"
+
+#: lib/Widget/ImageFaces.php:81
+msgid "Manual face selection"
+msgstr "Kendiniz sima seçin"
+
+#: lib/Forms/Upload.php:79
+msgid "Maximum file size:"
+msgstr "Maksimum dosya ebatý:"
+
+#: lib/Block/recent_faces.php:27
+msgid "Maximum number of faces"
+msgstr "Maksimum sima sayýsý"
+
+#: lib/Block/my_galleries.php:25
+msgid "Maximum number of galleries"
+msgstr "Maksimum galeri sayýsý"
+
+#: lib/Block/recently_added.php:33
+msgid "Maximum number of photos"
+msgstr "Maksimum fotoðraf sayýsý"
+
+#: lib/Block/gallery.php:35
+msgid "Maximum number of photos to display (0 means unlimited)"
+msgstr "Görüntülemek istediðiniz maksimum fotoðraf sayýsý (0 = sýnýr yok)"
+
+#: lib/Forms/Upload.php:48 lib/Forms/Image.php:51
+msgid "Maximum photo size:"
+msgstr "Maksimum dosya boyutu:"
+
+#: lib/Forms/Upload.php:58
+#, php-format
+msgid "Maximum photo size: %s; with a total of: %s"
+msgstr "Maksimum fotoðraf ebatý: %s; tüm boyutu: %s"
+
+#: lib/Forms/Watermark.php:38 config/prefs.php.dist:209
+msgid "Medium"
+msgstr "Orta"
+
+#: lib/Exif.php:356
+msgid "Metering Mode"
+msgstr "Ölcüm modu"
+
+#: templates/image/resize_image.inc:51 templates/image/edit_image.inc:20
+#: templates/image/crop_image.inc:104
+msgid "Mirror"
+msgstr "Yansýtmak"
+
+#: lib/Exif.php:157
+#, php-format
+msgid "Mirrored"
+msgstr "Yansýtýlmýþ"
+
+#: lib/Views/Embedded.php:91
+msgid "Missing container parameter."
+msgstr "Konteyner parametresi eksik"
+
+#: lib/Tile/Gallery.php:121
+msgid "Modified"
+msgstr "Son Deðiþtirme"
+
+#: gallery.php:162
+#, php-format
+msgid "Modifying: %s"
+msgstr "Deðiþtiriliyor: %s"
+
+#: faces/search/image_define.php:34
+msgid ""
+"More then one face found in photo. Please note that you can search only one "
+"faceat a time."
+msgstr ""
+"Birden fazla sima bulundu. Lütfen birden fazla sima aramanýz mümkün "
+"olmadýðýný göz önünde bulundurun."
+
+#: templates/view/gallerylightbox.inc:144 templates/view/gallery.inc:122
+#: templates/view/results.inc:84
+msgid "Move"
+msgstr "Taþý"
+
+#: image.php:628 lib/Views/Results.php:176
+#, php-format
+msgid "Moved %d photo from \"%s\" to \"%s\""
+msgid_plural "Moved %d photos from \"%s\" to \"%s\""
+msgstr[0] "%d Fotoðraf \"%s\"'dan \"%s\"'a taþýndý"
+
+#: lib/Exif.php:240
+msgid "Multi-Segment"
+msgstr ""
+
+#: lib/Exif.php:239
+msgid "Multi-Spot"
+msgstr ""
+
+#: lib/Forms/Upload.php:55
+msgid "Multiple Photos"
+msgstr "Birden fazla fotoðraf"
+
+#: templates/group/owner.inc:3 lib/Ansel.php:679 lib/Block/my_galleries.php:3
+#: lib/Block/my_galleries.php:37 lib/Views/List.php:155
+#: config/prefs.php.dist:59
+msgid "My Galleries"
+msgstr "Benim Galerilerim"
+
+#: browse_edit.php:28
+msgid "My Photos :: Add Content"
+msgstr "Benim Fotoðraflarým :: Ýçerik Ekle"
+
+#: templates/view/list.inc:16
+msgid "Name"
+msgstr "Ad"
+
+#: faces/search/named.php:17 faces/search/tabs.php:34
+msgid "Named faces"
+msgstr "Ýsim verilmiþ simalar"
+
+#: templates/xppublish/new.inc:21
+msgid "New Gallery Description"
+msgstr "Yeni Galeri-Tanýmý"
+
+#: templates/xppublish/new.inc:12
+msgid "New Gallery Title"
+msgstr "Yeni Galeri Baþlýðý"
+
+#: templates/view/image.inc:85 templates/view/image.inc:86
+#: templates/view/image.inc:111 templates/view/image.inc:112
+#: templates/view/slideshow.inc:42 templates/view/slideshow.inc:43
+msgid "Next"
+msgstr "Ýleri"
+
+#: config/prefs.php.dist:220
+msgid "No"
+msgstr "Hayýr"
+
+#: lib/Exif.php:267 lib/Exif.php:274 lib/Exif.php:275 lib/Exif.php:279
+msgid "No Flash"
+msgstr "Flaþsýz"
+
+#: templates/faces/faces.inc:10 templates/faces/search.inc:18
+#: templates/faces/index.inc:14 faces/image.php:52 faces/search/image.php:76
+#: lib/XRequest/EditFaces.php:120 lib/Widget/GalleryFaces.php:56
+#: lib/Widget/ImageFaces.php:56 lib/Widget/ImageFaces.php:97
+msgid "No faces found"
+msgstr "Hiç bir sima bulunamadý"
+
+#: faces/search/image_define.php:36
+msgid "No faces found. Define you own."
+msgstr "Sima bulunamadý. Kendiniz belirleyin."
+
+#: xppublish.php:184
+msgid "No file specified"
+msgstr "Dosya belirlenmemiþ"
+
+#: faces/gallery.php:24 lib/Views/Abstract.php:44
+msgid "No gallery specified"
+msgstr "Galeri belirlenmedi."
+
+#: templates/view/gallerylightbox.inc:134 templates/view/gallery.inc:112
+#: templates/view/results.inc:78 config/prefs.php.dist:71
+#: config/prefs.php.dist:241 config/prefs.php.dist:262
+msgid "None"
+msgstr "Hiçbiri"
+
+#: config/prefs.php.dist:252
+msgid "None (Owner only)"
+msgstr "Hiç (Sadece sahibi)"
+
+#: templates/gallery/gallery.inc:50
+msgid "Normal"
+msgstr "Normal"
+
+#: lib/Exif.php:155
+#, php-format
+msgid "Normal (O deg)"
+msgstr "Normal (0°)"
+
+#: lib/Exif.php:297
+msgid "Not defined"
+msgstr "Belirsiz"
+
+#: config/prefs.php.dist:97
+msgid "Number of faces per page"
+msgstr "Bir sayfada gösterilmesi istenen sima sayýsý"
+
+#: config/prefs.php.dist:105
+msgid "Number of groups per page"
+msgstr "Bir sayfada gösterilmesi istenen grup sayýsý"
+
+#: lib/Block/cloud.php:25
+msgid "Number of tags to display"
+msgstr "Görüntülenilmesi istenen anahtarsözcük sayýsý"
+
+#: config/prefs.php.dist:89
+msgid "Number of tiles per page"
+msgstr "Bir sayfada öðe sayýsý"
+
+#: config/prefs.php.dist:81
+msgid "Number of tiles per row"
+msgstr "Satýr baþý öðe sayýsý"
+
+#: report.php:51
+msgid "Offensive content"
+msgstr "Rahatsýz edici içerik"
+
+#: lib/Exif.php:298
+msgid "One Chip Color Area Sensor"
+msgstr ""
+
+#: perms.php:65
+msgid ""
+"Only the owner or system administrator may change ownership or owner "
+"permissions for a share"
+msgstr ""
+"Sadece sahibi veya sistem administratörü kullanýcý haklarýný veya sahibini "
+"deðiþtirebilir."
+
+#: templates/image/crop_image.inc:145
+msgid "Orientation:"
+msgstr "Oryantasyon :"
+
+#: lib/Forms/ImageDate.php:21 lib/Forms/Image.php:42
+msgid "Original Date"
+msgstr "Orijinal veri"
+
+#: lib/Exif.php:242
+msgid "Other"
+msgstr "Diðer"
+
+#: templates/tile/face.inc:24
+msgid "Others"
+msgstr "Baþka"
+
+#: templates/group/owner.inc:51
+#, php-format
+msgid "Owned by: %s"
+msgstr "Sahibi: %s"
+
+#: templates/view/list.inc:18 templates/tile/gallery.inc:11
+#: config/prefs.php.dist:69
+msgid "Owner"
+msgstr "Sahibi"
+
+#: lib/Exif.php:241
+msgid "Partial"
+msgstr "Bir bölüm"
+
+#: templates/xppublish/login.inc:14
+msgid "Password"
+msgstr "Þifre"
+
+#: templates/view/slideshow.inc:39
+msgid "Pause Slideshow"
+msgstr "Diaþova Ara Ver"
+
+#: lib/Widget/OwnerFaces.php:34
+#, php-format
+msgid "People in galleries of %s (%d of %d)"
+msgstr "%s galerilerindeki þahýslar (%d / %d)"
+
+#: lib/Widget/GalleryFaces.php:33
+msgid "People in this gallery"
+msgstr "Bu galerideki þahýslar"
+
+#: lib/Widget/ImageFaces.php:33
+msgid "People in this photo"
+msgstr "Bu fotoðraftaki þahýslar"
+
+#: config/prefs.php.dist:30
+msgid "Permission Options"
+msgstr "Ýzin Seçenekleri"
+
+#: faces/claim.php:37 faces/claim.php:57
+msgid "Person"
+msgstr "Þahýs"
+
+#: templates/faces/custom.inc:51
+msgid "Person in photo"
+msgstr "Fotoðrafta olan þahýslar"
+
+#: templates/faces/face.inc:30 templates/faces/gallery.inc:6
+#: lib/Block/recently_added.php:99 lib/Views/EmbeddedRenderers/Mini.php:139
+#: lib/Views/GalleryRenderers/GalleryLightbox.php:129
+msgid "Photo"
+msgstr "Fotoðraf"
+
+#: lib/Block/my_galleries.php:53
+msgid "Photo Count"
+msgstr "Fotoðraf miktarý"
+
+#: lib/Exif.php:338
+msgid "Photo Description"
+msgstr "Fotoðraf betimlemesi"
+
+#: browse.php:22
+msgid "Photo Galleries"
+msgstr "Fotoðraf galerileri"
+
+#: lib/Exif.php:337
+msgid "Photo Type"
+msgstr "Fotoðraf türü"
+
+#: templates/xppublish/javascript.inc:63
+msgid "Photo Upload"
+msgstr "Fotoðraf yükle"
+
+#: disclamer.php:30
+#, php-format
+msgid "Photo content may be offensive. You must be over %d to continue."
+msgstr ""
+"Fotoðraf içerigi yaþýnýza müsait olmayabilir. Devam etmek için %d yaþ üstü "
+"olmalýsýnýz."
+
+#: faces/search/image_save.php:32 faces/search/image.php:42
+msgid "Photo is too small. Search photo must be at least 50x50 pixels."
+msgstr ""
+"Fotoðraf yeterince büyük deðil. Arama fotoðrafý en azýndan 50x50 piksel "
+"olmalý."
+
+#: lib/Ansel.php:3283
+msgid "Photo not found"
+msgstr "Fotoðraf bulunamadý"
+
+#: faces/search/image_search.php:43
+msgid "Photo search"
+msgstr "Fotoðraf arama"
+
+#: rss.php:148
+#, php-format
+msgid "Photos by %s"
+msgstr "%s göre fotoðraflar"
+
+#: lib/Ansel.php:3321
+msgid "Photos not found"
+msgstr "Fotoðraflar bulunamadý"
+
+#: rss.php:178 rss.php:184
+#, php-format
+msgid "Photos tagged with %s on %s"
+msgstr "%s anahtarsözcüklerini içeren %s'de fotoðraflar"
+
+#: templates/view/image.inc:81 templates/view/image.inc:107
+#: templates/view/slideshow.inc:38
+msgid "Play"
+msgstr "Baþlat"
+
+#: faces/report.php:36
+msgid ""
+"Please describe the reasons. For example, you don't want to be mentioned "
+"etc..."
+msgstr ""
+"Lütfen nedenleri belirtin. Örneðin, adýnýzýn geçmesini istemiyorsunuz gibi..."
+
+#: xppublish.php:54
+msgid "Please enter your username and password."
+msgstr "Lütfen kullanýcý adýnýzý ve þifrenizi girin."
+
+#: faces/search/image.php:20
+msgid ""
+"Please upload photo with the face to search for. You can search only one "
+"face per time."
+msgstr ""
+"Lütfen aranmasýný istediðiniz simayý içeren fotoðrafý yükleyin. Bir aramada "
+"sadece bir sima arayabilirsiniz."
+
+#: config/prefs.php.dist:141
+msgid "Polaroid"
+msgstr "Polaroid"
+
+#: config/styles.php.dist:94
+msgid "Polaroid Style Thumbnails (White Background)"
+msgstr "Polaroid-Önizlenim (Beyaz arkapano)"
+
+#: templates/image/crop_image.inc:150
+msgid "Portrait"
+msgstr "Portre"
+
+#: xppublish.php:99 xppublish.php:149
+msgid "Press the \"Back\" button and try again."
+msgstr "\"Geri\" tuþuna týklayýn ve bir daha deneyin."
+
+#: config/prefs.php.dist:138
+msgid "Pretty Thumbnails"
+msgstr "Nazik Önizlenim"
+
+#: config/styles.php.dist:64
+msgid "Pretty Thumbnails (No Background)"
+msgstr "Nazik Önizlenim (Arka panosuz)"
+
+#: config/styles.php.dist:75
+msgid "Pretty Thumbnails (White Background)"
+msgstr "Nazik Önizlenim (Beyaz arkapano)"
+
+#: config/prefs.php.dist:139
+msgid "Pretty Thumbnails on White Background"
+msgstr "Beyaz arkapanolu nazik önizlenim"
+
+#: templates/image/crop_image.inc:165
+msgid "Preview"
+msgstr "Önizlenim"
+
+#: image.php:512 image.php:716
+#, php-format
+msgid "Preview changes for %s :: %s"
+msgstr "Vorschau der Änderungen für %s :: %s"
+
+#: templates/view/image.inc:83 templates/view/image.inc:84
+#: templates/view/image.inc:109 templates/view/image.inc:110
+#: templates/view/slideshow.inc:40 templates/view/slideshow.inc:41
+msgid "Previous"
+msgstr "Geri"
+
+#: lib/Exif.php:128
+msgid "Program"
+msgstr "Proðram"
+
+#: templates/view/image.inc:48 templates/view/slideshow.inc:17
+#: templates/tile/image.inc:17
+msgid "Properties"
+msgstr "Özellikler"
+
+#: templates/xppublish/list.inc:7
+msgid "Publish Photos to:"
+msgstr "Fotoðraflarý yayýnla:"
+
+#: xppublish.php:35
+#, php-format
+msgid "Publish to %s"
+msgstr "%s'de yayýnla"
+
+#: xppublish.php:24
+#, php-format
+msgid "Publish your photos to %s on %s."
+msgstr "Fotoðraflarýnýzý %s / %s yayýnlayýn."
+
+#: lib/Block/gallery.php:60
+msgid "Random Gallery"
+msgstr "Rasgele Galerie"
+
+#: lib/Block/gallery.php:33
+msgid "Random gallery"
+msgstr "Rasgele galeri"
+
+#: lib/Block/random_photo.php:3 lib/Block/random_photo.php:26
+msgid "Random photo"
+msgstr "Rasgele Fotoðraf"
+
+#: config/prefs.php.dist:243 config/prefs.php.dist:264
+msgid "Read and write"
+msgstr "Oku ve yaz"
+
+#: config/prefs.php.dist:265
+msgid "Read, write, and delete"
+msgstr "Oku, Yaz ve Sil"
+
+#: config/prefs.php.dist:242 config/prefs.php.dist:253
+#: config/prefs.php.dist:263
+msgid "Read-only"
+msgstr "Sadece oku"
+
+#: templates/gallery/delete_confirmation.inc:12
+msgid "Really Delete"
+msgstr "Gerçekten Silinsin mi"
+
+#: templates/gallery/delete_confirmation.inc:12
+msgid "Really Empty"
+msgstr "Gerçekten Boþaltýlsýn mý"
+
+#: templates/gallery/delete_confirmation.inc:3
+#, php-format
+msgid "Really delete %s and all of its subgalleries?"
+msgstr "Gerçekten %s ve tüm altgalerileri silinsin mi?"
+
+#: templates/gallery/delete_confirmation.inc:7
+msgid ""
+"Really delete this gallery and all of the photos and subgalleries in it? "
+"This action cannot be undone."
+msgstr ""
+"Bu galeriyi ve tüm fotoðraflar ve altgaleriler silinsin mi? Bu iþlem sonrasý "
+"geri dönüþüm mümkün deðil."
+
+#: templates/gallery/delete_confirmation.inc:3
+#, php-format
+msgid "Really empty %s?"
+msgstr "%s gerçekten silinsin mi?"
+
+#: templates/gallery/delete_confirmation.inc:7
+msgid ""
+"Really empty this gallery and all of the photos in it? This action cannot be "
+"undone."
+msgstr ""
+"Bu galeri gerçekten boþaltýlsýn ve içindeki tüm resimler silinsin mi? Bu "
+"eylem sonrasý geri dönüþüm mümkün deðil."
+
+#: faces/report.php:36 faces/report.php:59
+msgid "Reason"
+msgstr "Neden"
+
+#: lib/Block/recent_comments.php:71
+#, php-format
+msgid "Recent Comments In %s"
+msgstr "%s'de son yorumlar"
+
+#: lib/Block/recent_comments.php:5
+msgid "Recent Photo Comments"
+msgstr "Son Fotoðraf Yorumlarý"
+
+#: lib/Block/recent_faces.php:3 lib/Block/recent_faces.php:34
+msgid "Recent faces"
+msgstr "Son simalar"
+
+#: lib/Widget/Links.php:25
+#, php-format
+msgid "Recent photos by %s"
+msgstr "%s'in son fotoðraflarý"
+
+#: lib/Widget/Links.php:27
+#, php-format
+msgid "Recent photos in %s"
+msgstr "%s'de son fotoðraflar"
+
+#: lib/Block/recently_added.php:3
+msgid "Recently Added Photos"
+msgstr "Son eklenen fotoðraflar"
+
+#: lib/Block/recently_added.php:75
+#, php-format
+msgid "Recently Added Photos From %s"
+msgstr "%s'den eklenen son fotoðraflar"
+
+#: rss.php:153
+#, php-format
+msgid "Recently added photos by %s on %s"
+msgstr "%s tarafýndan eklenen son fotoðraf %s"
+
+#: rss.php:57 rss.php:62
+#, php-format
+msgid "Recently added photos on %s"
+msgstr "%s olan son fotoðraflar"
+
+#: lib/Exif.php:280
+msgid "Red Eye"
+msgstr "Kýzýl Göz"
+
+#: lib/Exif.php:286
+msgid "Red Eye, Auto-Mode"
+msgstr "Kýzýl Göz, Oto-Ayar"
+
+#: lib/Exif.php:288
+msgid "Red Eye, Auto-Mode, Return light detected"
+msgstr "Kýzýl Göz, Oto-Ayar, Yansýyan ýþýk bulundu"
+
+#: lib/Exif.php:287
+msgid "Red Eye, Auto-Mode, Return light not detected"
+msgstr "Kýzýl Göz, Oto-Ayar, Yansýyan ýþýk bulunamadý"
+
+#: lib/Exif.php:283
+msgid "Red Eye, Compulsory Flash"
+msgstr "Kýzýl Göz, Kompulsif Flaþ"
+
+#: lib/Exif.php:285
+msgid "Red Eye, Compulsory Flash, Return light detected"
+msgstr "Kýzýl Göz, Kompulsif Flaþ, Yansýtma Iþýðý bulundu"
+
+#: lib/Exif.php:284
+msgid "Red Eye, Compulsory Flash, Return light not detected"
+msgstr "Kýzýl Göz, Kompulsif Flaþ, Yansýtma Iþýðý bulunamadý"
+
+#: lib/Exif.php:282
+msgid "Red Eye, Return light detected"
+msgstr "Kýzýl Göz, Yansýtma ýþýðý bulundu"
+
+#: lib/Exif.php:281
+msgid "Red Eye, Return light not detected"
+msgstr "Kýzýl Göz, Yansýtma ýþýðý bulunamadý"
+
+#: templates/list/header.inc:5 templates/group/header.inc:8
+#: templates/view/list.inc:5
+msgid "Refresh List"
+msgstr "Listeyi Yenile"
+
+#: templates/faces/gallery.inc:19
+msgid "Reget faces for this image"
+msgstr "Bu fotoðraf için simalar yeniden tanýnsýn"
+
+#: templates/view/results.inc:128
+msgid "Related Tags"
+msgstr "Benzer Anahtarsözcükleri"
+
+#: lib/Tags.php:600
+#, php-format
+msgid "Remove %s from search"
+msgstr "%s'yi aramadan çýkar"
+
+#: lib/XRequest/TagActions.php:115 lib/Widget/Tags.php:93
+msgid "Remove Tag"
+msgstr "Anahtarsözcüðü sil"
+
+#: lib/Forms/Image.php:50
+msgid "Replace photo with this file"
+msgstr "Fotoðrafý bu dosya ile deðiþtir"
+
+#: report.php:47 report.php:66 templates/faces/search.inc:10
+#: templates/faces/face.inc:9 templates/view/image.inc:70
+#: templates/tile/face.inc:30 lib/Widget/Actions.php:165
+msgid "Report"
+msgstr "Rapor"
+
+#: templates/faces/search.inc:10 templates/faces/face.inc:9
+#: templates/tile/face.inc:30
+msgid "Report abuse or submit a removal request"
+msgstr "Suistimal ediliþini rapor et veya silinmesini iste"
+
+#: lib/Report.php:109
+msgid "Report by user"
+msgstr "Kullanýcý tarafý rapor edilme"
+
+#: lib/Report.php:40
+msgid "Report driver does not exist."
+msgstr "Rapor etme sürücüsü mevcut deðil."
+
+#: faces/report.php:31
+msgid "Report face"
+msgstr "Simayý rapor et"
+
+#: report.php:61 report.php:74
+msgid "Report reason"
+msgstr "Rapor nedeni"
+
+#: report.php:60 report.php:73
+msgid "Report type"
+msgstr "Rapor tipi"
+
+#: templates/image/resize_image.inc:77 templates/image/crop_image.inc:166
+#: templates/faces/custom.inc:54 templates/faces/define.inc:39
+msgid "Reset"
+msgstr "Sýfýrla"
+
+#: lib/Widget/Actions.php:147
+msgid "Reset all thumbnails"
+msgstr "Tüm önizlenimleri yenile"
+
+#: lib/Widget/Actions.php:153
+msgid "Reset default photo"
+msgstr "Standartfotoðrafý gerial"
+
+#: templates/image/resize_image.inc:64 templates/image/edit_image.inc:32
+#: templates/image/crop_image.inc:115
+msgid "Resize"
+msgstr "Ebatý deðiþtir"
+
+#: lib/Forms/Watermark.php:47 config/prefs.php.dist:187
+msgid "Right"
+msgstr "Sað"
+
+#: templates/image/resize_image.inc:44 templates/image/edit_image.inc:13
+#: templates/image/crop_image.inc:97
+msgid "Rotate 180"
+msgstr "180° cevir"
+
+#: templates/image/resize_image.inc:43 templates/image/edit_image.inc:12
+#: templates/image/crop_image.inc:96
+msgid "Rotate Left"
+msgstr "Sola Döndür"
+
+#: templates/image/resize_image.inc:45 templates/image/edit_image.inc:14
+#: templates/image/crop_image.inc:98
+msgid "Rotate Right"
+msgstr "Saða Döndür"
+
+#: templates/image/resize_image.inc:76 templates/faces/custom.inc:52
+#: lib/Forms/Watermark.php:26 lib/Forms/ImageDate.php:15
+#: lib/Forms/Image.php:26
+msgid "Save"
+msgstr "Kaydet"
+
+#: templates/captions/captions.inc:50
+msgid "Save Captions"
+msgstr "Fotoðraf baþlýklarýný kaydet"
+
+#: templates/image/preview_image.inc:21 templates/image/preview_image.inc:22
+#: templates/image/preview_cropimage.inc:18
+#: templates/image/preview_cropimage.inc:19
+msgid "Save Change"
+msgstr "Deðiþiklikleri kaydet"
+
+#: templates/gallery/gallery.inc:120
+msgid "Save Gallery"
+msgstr "Galeriyi kaydet"
+
+#: image.php:174
+msgid "Save Photo"
+msgstr "Fotoðrafý kaydet"
+
+#: templates/faces/image.inc:5
+msgid "Save face name"
+msgstr "Sima adýný kaydet"
+
+#: templates/faces/define.inc:37 faces/search/name.php:21
+msgid "Search"
+msgstr "Ara"
+
+#: faces/search/tabs.php:35 faces/search/name.php:47
+msgid "Search by name"
+msgstr "Ad ile ara"
+
+#: faces/search/tabs.php:37
+msgid "Search by photo"
+msgstr "Fotoðraf ile ara"
+
+#: lib/Views/Results.php:86
+#, php-format
+msgid "Searching %s's photos tagged: "
+msgstr "%s'in anahtarsözcüklerinini içeren fotoðraflar aranýyor: "
+
+#: lib/Views/Results.php:87
+msgid "Searching all photos tagged: "
+msgstr "Þu anahtarsözcüklerini içeren fotoðraflarý ara: "
+
+#: faces/gallery.php:56
+#, php-format
+msgid "Searching for faces in %s"
+msgstr "%s'de sima ara"
+
+#: templates/view/gallerylightbox.inc:133 templates/view/gallery.inc:111
+#: templates/view/results.inc:77
+msgid "Select All"
+msgstr "Tümünü seç"
+
+#: templates/view/gallerylightbox.inc:134 templates/view/gallery.inc:112
+#: templates/view/results.inc:78
+msgid "Select None"
+msgstr "Hiçbirini seç"
+
+#: templates/tile/image.inc:5
+msgid "Select for Action"
+msgstr "Eylem için seç"
+
+#: templates/view/gallerylightbox.inc:132 templates/view/gallery.inc:110
+#: templates/view/results.inc:76
+#, php-format
+msgid "Select: %s, %s"
+msgstr "Seçim: %s, %s"
+
+#: templates/view/gallerylightbox.inc:149 templates/view/gallery.inc:127
+#: templates/view/results.inc:89
+msgid "Selected photos to"
+msgstr "Secilmiþ fotoðraflarý buraya"
+
+#: lib/Forms/Ecard.php:25
+msgid "Send"
+msgstr "Gönder"
+
+#: img/ecard.php:102
+#, php-format
+msgid "Send Ecard :: %s"
+msgstr "E-Kartpostal gönder :: %s"
+
+#: templates/view/image.inc:55 templates/view/slideshow.inc:27
+msgid "Send an Ecard"
+msgstr "E-Kartpostal gönder"
+
+#: lib/Forms/Ecard.php:41
+msgid "Send ecard to the following address:"
+msgstr "E-Kartpostalý þu adreslere gönder:"
+
+#: lib/Exif.php:360
+msgid "Sensing Method"
+msgstr "Sensortipi"
+
+#: templates/gallery/gallery.inc:79 lib/Forms/Upload.php:53
+msgid "Separate tags with commas."
+msgstr "Birden fazla anahtarsözcüklerini virgülle ayýr."
+
+#: lib/Widget/Actions.php:137
+msgid "Set captions"
+msgstr "Fotoðraf baþlýklarýný belirle"
+
+#: lib/Widget/Actions.php:158
+msgid "Set permissions"
+msgstr "Eriþim izinlemesini belirt"
+
+#: config/styles.php.dist:84
+msgid "Shadowed Thumbnails (White Background)"
+msgstr "Gölgeli önizlenim (Beyaz arkapano)"
+
+#: config/prefs.php.dist:140
+msgid "Shadowed Thumbnails on White Background"
+msgstr "Beyaz arka panolu gölgeli önizlenim"
+
+#: config/prefs.php.dist:113
+msgid "Show EXIF data"
+msgstr "EXIF-Verilerini göster"
+
+#: lib/Exif.php:347
+msgid "Shutter Speed"
+msgstr "Fotoðraf Çekme Hýzý"
+
+#: lib/Widget/SimilarPhotos.php:27
+msgid "Similar Photos"
+msgstr "Benzer Fotoðraflar"
+
+#: templates/faces/face.inc:14 templates/tile/face.inc:34
+msgid "Similar faces"
+msgstr "Benzer simalar"
+
+#: config/styles.php.dist:158
+msgid "Simple"
+msgstr "Basit"
+
+#: config/styles.php.dist:167
+msgid "Simple Lightbox"
+msgstr "Basit Lightbox"
+
+#: lib/Forms/Upload.php:43
+msgid "Single Photo"
+msgstr "Tek Fotoðraf"
+
+#: templates/gallery/gallery.inc:72
+#, php-format
+msgid ""
+"Slugs allows direct access to this gallery by visiting: %s. <br /> Slug "
+"names may contain only letters, numbers or the _ (underscore) character."
+msgstr ""
+"Kýsa ad; bu galeriye: %s.<br /> URL'iyle eriþilmesini saðlar. Kýsa harf, "
+"rakam ve _ (altçigi) içerebilir."
+
+#: lib/Forms/Watermark.php:37 config/prefs.php.dist:208
+msgid "Small"
+msgstr "Küçük"
+
+#: templates/view/list.inc:16
+msgid "Sort by: "
+msgstr "Sýralama: "
+
+#: lib/Widget/Actions.php:143
+msgid "Sort images"
+msgstr "Fotoðraflarý sýrala"
+
+#: lib/Exif.php:238
+msgid "Spot"
+msgstr "Spot"
+
+#: templates/view/image.inc:80 templates/view/image.inc:107
+#: templates/view/slideshow.inc:38 lib/Widget/Actions.php:57
+msgid "Start Slideshow"
+msgstr "Diaþov baþlat"
+
+#: scripts/remote_import.php:246 scripts/recursive_import.php:190
+#: scripts/ansel.php:212 scripts/ansel.php:295
+#, php-format
+msgid "Storing photo \"%s\"..."
+msgstr "Fotoðrafý kaydet \"%s\"..."
+
+#: templates/gallery/gallery.inc:40
+msgid "Style for this gallery"
+msgstr "Bu Galeri için stil"
+
+#: scripts/remote_import.php:266 scripts/recursive_import.php:200
+#: scripts/ansel.php:235
+#, php-format
+msgid "Successfully added %d photo (%s) to gallery \"%s\" from \"%s\"."
+msgid_plural "Successfully added %d photos (%s) to gallery \"%s\" from \"%s\"."
+msgstr[0] "%d Fotoðraf (%s) \"%4$s\"'den \"%3$s\" galerisine eklenmiþtir."
+
+#: scripts/ansel.php:221
+#, php-format
+msgid "Successfully added photo \"%s\" to gallery \"%s\"."
+msgstr "\"%s\" fotoðrafý baþarýyla \"%s\" galerisine eklenmiþtir."
+
+#: gallery/delete.php:39
+#, php-format
+msgid "Successfully deleted %s."
+msgstr "%s baþarýyla silindi."
+
+#: gallery/delete.php:62
+#, php-format
+msgid "Successfully emptied \"%s\""
+msgstr "\"%s\" baþarýyla boþaltýldý"
+
+#: edit_dates.php:65
+#, php-format
+msgid "Successfully modified the date on %d images."
+msgstr "Baþarýyla %d fotoðrafta tarih deðiþtirildi."
+
+#: lib/Exif.php:249
+msgid "Sunny"
+msgstr "Güneþli"
+
+#: lib/Block/cloud.php:3 lib/Block/cloud.php:33
+msgid "Tag Cloud"
+msgstr "Anahtarsözcük Daðýlýmý"
+
+#: lib/Forms/Upload.php:53 lib/Forms/Image.php:46 lib/Widget/Tags.php:21
+msgid "Tags"
+msgstr "Anahtarsözcükler"
+
+#: config/prefs.php.dist:23
+msgid "Tags and EXIF Options"
+msgstr "Anahtarsözcükler ve EXIF Ayarlarý"
+
+#: templates/tile/face.inc:27 faces/claim.php:32
+msgid "Tell us who is in this photo"
+msgstr "Bu fotoðrafta kimler görünüyor?"
+
+#: report.php:50
+msgid "Terms and conditions infringement"
+msgstr "Genel Anlaþma Ýhlali"
+
+#: scripts/ansel.php:262
+#, php-format
+msgid "The directory \"%s\" doesn't exist."
+msgstr "\"%s\" adlý dizin mevcut deðil."
+
+#: scripts/ansel.php:240
+#, php-format
+msgid "The directory \"%s\" had no valid photos."
+msgstr "\"%s\" adlý dizinde geçerli fotoðraf yok."
+
+#: scripts/ansel.php:279
+#, php-format
+msgid "The directory \"%s\" is empty."
+msgstr "\"%s\" adlý dizini boþ."
+
+#: lib/Ansel.php:506 scripts/remote_import.php:292
+#, php-format
+msgid "The file \"%s\" doesn't exist."
+msgstr "\"%s\" adlý dosya mevcut deðil."
+
+#: image.php:210 img/upload.php:182 lib/api.php:503
+msgid "The file you uploaded does not appear to be a valid photo."
+msgstr "Yüklediðiniz dosya büyük ihtimal geçerli bir fotoðraf deðil."
+
+#: lib/Ansel.php:2975
+#, php-format
+msgid "The gallery \"%s\" could not be created: %s"
+msgstr "\"%s\" adlý galeri oluþturulamadý: %s"
+
+#: gallery.php:318 xppublish.php:123 scripts/remote_import.php:205
+#: scripts/recursive_import.php:157 scripts/ansel.php:156
+#, php-format
+msgid "The gallery \"%s\" couldn't be created: %s"
+msgstr "\"%s\" adlý galeri oluþturulamadý: %s"
+
+#: gallery.php:324 xppublish.php:127 scripts/remote_import.php:207
+#: scripts/recursive_import.php:159 scripts/ansel.php:161
+#, php-format
+msgid "The gallery \"%s\" was created successfully."
+msgstr "\"%s\" adlý galeri baþarýyla oluþturuldu."
+
+#: gallery.php:267
+msgid "The gallery was saved."
+msgstr "Galeri kayedildi."
+
+#: gallery.php:375
+msgid "The gallery's default photo has successfully been reset."
+msgstr "Bu galerinin standart fotoðrafý baþarý ile yeni oluþturuldu."
+
+#: gallery.php:392
+msgid "The gallery's thumbnails have successfully been reset."
+msgstr "Bu galerinin ön izlenim fotosu baþarýyla yeni oluþturuldu."
+
+#: faces/report.php:68
+msgid "The owner of the photo was notified."
+msgstr "Fotoðraf sahibine haber iletilti."
+
+#: faces/claim.php:66
+msgid "The owner of the photo, who will delegate the face name, was notified."
+msgstr "Sima ismini belirleyen fotoðrafýn sahibine haber gönderildi."
+
+#: lib/Ansel.php:2938
+#, php-format
+msgid "The slug \"%s\" already exists."
+msgstr "Bu ad \"%s\" zaten mevcut."
+
+#: img/upload.php:210
+msgid ""
+"The uploaded file appears to be empty. It may not exist on your computer."
+msgstr "Yüklenen dosya boþ görünüyor. Bilgisayarýnýzda olmayabilir."
+
+#: lib/Block/random_photo.php:35 lib/Views/List.php:194
+msgid "There are no photo galleries available."
+msgstr "Mevcut fotoðraf galerisi yok."
+
+#: gallery.php:114
+#, php-format
+msgid "There are no photos in %s to download."
+msgstr "%s'de indirebileceðiniz fotoðraf yok."
+
+#: templates/view/gallerylightbox.inc:153 templates/view/gallery.inc:131
+#: templates/view/results.inc:99 templates/captions/captions.inc:52
+msgid "There are no photos in this gallery."
+msgstr "Bu galeride fotoðraf yok."
+
+#: scripts/ansel.php:298
+#, php-format
+msgid "There was a problem adding the photo \"%s\" to gallery \"%s\"."
+msgstr "\"%s\" adlý fotoðrafý \"%s\" galerisine eklerken bir sorun oluþtu."
+
+#: scripts/ansel.php:215
+#, php-format
+msgid "There was a problem adding the photo \"%s\" to gallery \"%s\": %s"
+msgstr "\"%s\" adlý fotoðrafý \"%s\" galerisine eklerken sorun oluþtu: %s"
+
+#: lib/api.php:682 gallery/delete.php:34
+#, php-format
+msgid "There was a problem deleting %s: %s"
+msgstr "%s silerken bir sorun oluþtu: %s"
+
+#: image.php:579 lib/Views/Results.php:131
+#, php-format
+msgid "There was a problem deleting photos: %s"
+msgstr "Fotoðraflarý silerken bir sorun oluþtu: %s"
+
+#: img/upload.php:107 img/upload.php:168 img/upload.php:192
+#, php-format
+msgid "There was a problem saving the photo: %s"
+msgstr "Fotoðraflarý kaydederken bir sorun oluþtu: %s"
+
+#: xppublish.php:195
+msgid "There was a problem uploading the photo."
+msgstr "Fotoðrafý yüklerken bir sorun oluþtu."
+
+#: img/upload.php:208
+#, php-format
+msgid "There was a problem uploading the photo: %s"
+msgstr "Fotoðrafý yüklerken bir sorun oluþtu: %s"
+
+#: xppublish.php:91
+msgid "There was an error accessing the gallery"
+msgstr "Galeriye eriþimde hata oluþtu"
+
+#: gallery/sort.php:27
+msgid "There was an error accessing the gallery."
+msgstr "Galeriye eriþimde hata oluþtu."
+
+#: edit_dates.php:58
+#, php-format
+msgid "There was an error editing the dates: %s"
+msgstr "Tarihler düzenlenirken hata oluþtu: %s"
+
+#: img/upload.php:61 img/upload.php:89 img/upload.php:123 img/upload.php:150
+#, php-format
+msgid "There was an error processing the uploaded archive: %s"
+msgstr "Yüklemeye çalýþtýðýnýz arþivi iþlerken sorun oluþtu: %s"
+
+#: image.php:226
+msgid "There was an error replacing the photo."
+msgstr "Fotoðrafý deðiþtiriken sorun oluþtu."
+
+#: img/ecard.php:95
+#, php-format
+msgid "There was an error sending your message: %s"
+msgstr "Ýletiyi gönderirken sorun oluþtu: %s"
+
+#: scripts/remote_import.php:255
+#, php-format
+msgid "There was an unspecified error. The server returned: %s"
+msgstr "Belirlenmemiþ hata oluþtu. Server þu hatayý verdi: %s."
+
+#: lib/ImageView.php:89
+#, php-format
+msgid ""
+"This install does not support the %s feature. Please contact your "
+"administrator."
+msgstr "Aradýðýnýz \"%s\" iþlem desteklenmiyor, administratörünüze baþvurun."
+
+#: lib/Exif.php:300
+msgid "Three Chip Color Area Sensor"
+msgstr "Üç Çip Color Area Sensor"
+
+#: lib/Forms/Watermark.php:36 config/prefs.php.dist:207
+msgid "Tiny"
+msgstr "Çok küçük"
+
+#: lib/Forms/Watermark.php:52 config/prefs.php.dist:196
+msgid "Top"
+msgstr "Üstte"
+
+#: templates/gallery/gallery.inc:20
+msgid "Top Level Gallery"
+msgstr "En Üst Galeri"
+
+#: lib/Exif.php:302
+msgid "Trilinear Sensor"
+msgstr ""
+
+#: lib/Exif.php:251
+msgid "Tungsten"
+msgstr "Volfram"
+
+#: lib/Exif.php:130
+msgid "Tv-priority"
+msgstr "Tv-öncelikli"
+
+#: lib/Exif.php:299
+msgid "Two Chip Color Area Sensor"
+msgstr "Two Chip Color Area Sensor"
+
+#: lib/Widget.php:75 lib/api.php:1200 lib/Search.php:57 lib/ImageView.php:96
+#, php-format
+msgid "Unable to load the definition of %s."
+msgstr "%s-sürücüsü yüklenemedi."
+
+#: rss.php:37
+msgid "Unable to retrieve requested feed"
+msgstr "Ýstenilen feed getirtilemedi"
+
+#: lib/Exif.php:313
+msgid "Uncalibrated"
+msgstr "Kalibresiz"
+
+#: templates/gallery/gallery.inc:121
+msgid "Undo Changes"
+msgstr "Deðiþiklikleri Geri al"
+
+#: templates/group/category.inc:7 lib/Views/List.php:175
+msgid "Unfiled"
+msgstr "Belirtilmemiþ"
+
+#: lib/Exif.php:235
+msgid "Unknown"
+msgstr "Bilinmiyor"
+
+#: gallery.php:169
+msgid "Unknown gallery"
+msgstr "Tanýnmayan Galeri"
+
+#: lib/Ansel.php:2907 lib/tests/GalleryTest.php:42
+msgid "Unnamed"
+msgstr "Adsýz"
+
+#: lib/Report.php:122
+msgid "Unsupported"
+msgstr "Desteklenmemiþ"
+
+#: xppublish.php:135
+msgid "Untitled"
+msgstr "Baþlýksýz"
+
+#: perms.php:237
+#, php-format
+msgid "Updated %s."
+msgstr "%s güncelleþtirildi."
+
+#: faces/search/image.php:22 lib/Forms/Upload.php:39
+msgid "Upload"
+msgstr "Yükle"
+
+#: templates/xppublish/javascript.inc:63
+#, php-format
+msgid "Upload Photos to %s"
+msgstr "Fotoðraflarý %s yükle"
+
+#: faces/search/image.php:66
+msgid "Upload face photo"
+msgstr "Yeni sima fotoðrafýný yükle"
+
+#: img/upload.php:30 lib/Widget/Actions.php:60
+msgid "Upload photos"
+msgstr "Fotoðraf yükle"
+
+#: lib/Exif.php:159
+#, php-format
+msgid "Upsidedown"
+msgstr "Üstü Alta"
+
+#: lib/Exif.php:161
+#, php-format
+msgid "Upsidedown Mirrored"
+msgstr "Üstü Alta Yansýtýlmýþ"
+
+#: scripts/remote_import.php:335 scripts/recursive_import.php:221
+#: scripts/all_images_exif_to_tags.php:116 scripts/ansel.php:318
+#, php-format
+msgid "Usage: %s [OPTIONS]..."
+msgstr "Kullanýlýþ: %s [OPTIONEN]..."
+
+#: lib/Block/gallery.php:39
+msgid "Use a lightbox to view images"
+msgstr "Fotoðraflara bakmak için bir lightbox kullan"
+
+#: lib/Forms/Ecard.php:32
+msgid "Use the following return address:"
+msgstr "Þu çevap adresi kullanýn:"
+
+#: lib/Exif.php:358
+msgid "User Comment"
+msgstr "Kullanýcý Yorumu"
+
+#: templates/gallery/gallery.inc:92
+#, php-format
+msgid "User must be over %d"
+msgstr "Kullanýcý %d üstünde olmalý"
+
+#: templates/xppublish/login.inc:11
+msgid "Username"
+msgstr "Kullanýcý adý"
+
+#: xppublish.php:50
+msgid "Username or password are incorrect."
+msgstr "Kullanýcý adý ve þifre yanlýþ."
+
+#: scripts/recursive_import.php:86 scripts/all_images_exif_to_tags.php:77
+msgid "Username or password is incorrect."
+msgstr "Kullanýcý adý veya þifre yanlýþ."
+
+#: templates/gallery/gallery.inc:111 config/prefs.php.dist:231
+msgid "Users with edit permissions"
+msgstr "Düzenleme izni olan kullanýcýlar"
+
+#: lib/Forms/Watermark.php:55 config/prefs.php.dist:199
+#: config/prefs.php.dist:212
+msgid "Vertical Alignment"
+msgstr "Dikey hizalandýr"
+
+#: lib/Block/gallery.php:102
+#, php-format
+msgid "View %s"
+msgstr "%s göster"
+
+#: templates/view/results.inc:65
+msgid "View All Results"
+msgstr "Tüm sonuçlarý göster"
+
+#: lib/Block/random_photo.php:55
+msgid "View Photo"
+msgstr "Fotoðrafý göster"
+
+#: templates/view/results.inc:65
+msgid "View Results from All Users"
+msgstr "Tüm kullanýcýlarýn sonucunu göster"
+
+#: templates/tile/image.inc:11
+msgid "View or leave comments"
+msgstr "Yorumlarý göster veya Yorum býrak"
+
+#: config/prefs.php.dist:61
+msgid "View to display by default"
+msgstr "Standart bakýþ"
+
+#: image.php:464 image.php:475 templates/image/resize_image.inc:58
+#: templates/image/edit_image.inc:27 templates/image/crop_image.inc:111
+msgid "Watermark"
+msgstr "Gizli damga"
+
+#: lib/Forms/Watermark.php:41
+msgid "Watermark Font"
+msgstr "Gizli damga fontu"
+
+#: config/prefs.php.dist:37
+msgid "Watermark Options"
+msgstr "Gizli Damga Seçenekleri"
+
+#: config/prefs.php.dist:261
+msgid ""
+"When a new gallery is created, what default permissions should be given to "
+"groups that the user is a member of?"
+msgstr ""
+"Yeni Galeri oluþturulduðunda, gruplara dahil olan kullanýcýlara hangi "
+"standart haklar verilsin?"
+
+#: config/prefs.php.dist:240
+msgid ""
+"When a new gallery is created, what permissions should be given to "
+"authenticated users by default?"
+msgstr ""
+"Yeni bir galeri oluþturulduðunda giriþ yapmýþ kullanýcýlara hangi standart "
+"haklar verilsin?"
+
+#: config/prefs.php.dist:251
+msgid ""
+"When a new gallery is created, what permissions should be given to guests by "
+"default?"
+msgstr ""
+"Yeni bir galeri oluþturulduðunda misafirlere hangi standart haklar verilsin?"
+
+#: config/prefs.php.dist:273
+msgid ""
+"Which EXIF fields should we automatically add as image tags during upload?"
+msgstr ""
+"Yüklemede esnasýnda hangi EXIF verileri otomatik olarak fotoðrafa "
+"anahtarsözcük olarak eklensin"
+
+#: lib/Exif.php:361
+msgid "White Balance"
+msgstr "Beyaz dengesi"
+
+#: templates/gallery/gallery.inc:106 config/prefs.php.dist:232
+msgid "Who should be allowed to download original photos"
+msgstr "Orijinal fotoðraflarý kimler indirebilsin?"
+
+#: lib/Exif.php:343
+msgid "Width"
+msgstr "Geniþlik"
+
+#: lib/Exif.php:345
+msgid "X Resolution"
+msgstr "X-Çözünürlüðü"
+
+#: lib/Exif.php:346
+msgid "Y Resolution"
+msgstr "Y-Çözünürlüðü"
+
+#: config/prefs.php.dist:220
+msgid "Yes"
+msgstr "Evet"
+
+#: edit_dates.php:14
+msgid "You are not allowed to edit these images."
+msgstr "Bu fotoðraflarý düzenlemeye izniniz yok."
+
+#: lib/XRequest/EditFaces.php:159
+msgid "You are not allowed to edit this photo."
+msgstr "Bu fotoðrafý düzenlemeye izniniz yok."
+
+#: gallery.php:283
+#, php-format
+msgid "You do not have permission to add children to %s."
+msgstr "%s alt noktalar eklemek için yeterli hakkýnýz yok."
+
+#: lib/Faces/facedetect.php:34
+msgid "You do not have the facedetect extension enabled in PHP"
+msgstr "PHP'de sima bulma modulleriniz aktif deðil."
+
+#: lib/Faces/opencv.php:34
+msgid "You do not have the opencv extension enabled in PHP"
+msgstr "PHP'de opencv desteðiniz yok."
+
+#: img/ecard.php:68
+msgid ""
+"You have been sent an Ecard. To view the Ecard, you must be able to view "
+"text/html messages in your mail reader. If you are viewing this message, "
+"then most likely your mail reader does not support viewing text/html "
+"messages."
+msgstr ""
+"Bir E-Kartpostalý aldýnýz. Bu Karpostalý görüntüleyebilmeniz için, E-Posta "
+"proðramýnýz HTML Ýletileri gösterebilmeli. HTML yerine bu yazýyý "
+"görüyorsanýz bir ihtimal E-Posta proðramýnýz HTML Ýletileri gösteremiyor."
+
+#: lib/Views/List.php:189
+msgid "You have no photo galleries, add one!"
+msgstr "Fotoðraf galeriniz yok. Galeri ekleyin!"
+
+#: templates/view/gallerylightbox.inc:43 templates/view/gallerylightbox.inc:57
+#: templates/view/gallery.inc:42 templates/view/gallery.inc:56
+#: templates/view/results.inc:30 templates/view/results.inc:44
+msgid "You must choose a gallery to move photos to."
+msgstr "Fotoðraflarý taþýyabilmeniz için bir galeri seçmelisiniz."
+
+#: templates/view/gallerylightbox.inc:29 templates/view/gallery.inc:29
+msgid "You must choose photos to delete."
+msgstr "Silmek istediðiniz fotoðraflarý seçmeniz gerekiyor."
+
+#: templates/view/gallerylightbox.inc:80 templates/view/gallery.inc:79
+msgid "You must choose photos to edit."
+msgstr "Düzenlemek istediðiniz fotoðraflarý seçmeniz gerekiyor."
+
+#: lib/Ansel.php:66 lib/Ansel.php:74
+msgid "You must configure a VFS backend to use Ansel."
+msgstr "Ansel'i kullanabilmeniz için bir VFS-Backend ayarlamanýz gerekiyor."
+
+#: img/ecard.php:44
+msgid "You must enter an e-mail address to send the message to."
+msgstr "Gönderilmesini istediðiniz geçerli E-Posta adresi girmelisiniz."
+
+#: img/ecard.php:39
+msgid "You must enter your e-mail address."
+msgstr "E-Posta adresinizi belirtmeniz gerekiyor."
+
+#: scripts/all_images_exif_to_tags.php:86
+msgid "You must login with an administrative account."
+msgstr "Yönetici giriþi ile giriþ yapmanýz gerekiyor"
+
+#: gallery.php:295
+msgid "You must provide a display name for your new gallery."
+msgstr "Sie müssen einen Anzeigenamen für die neue Galerie angeben."
+
+#: edit_dates.php:25
+msgid "You must select at least on photo to edit."
+msgstr "Düzenlemek için en azýndan bir fotoðraf seçmelisiniz."
+
+#: scripts/remote_import.php:119 scripts/recursive_import.php:95
+msgid "You must specify a valid directory."
+msgstr "Gecerli bir dizin belirtmelisiniz."
+
+#: scripts/remote_import.php:111 scripts/recursive_import.php:91
+#: scripts/all_images_exif_to_tags.php:82
+msgid "You must specify a valid username and password."
+msgstr "Gecerli bir kullanýcý adý ve þifre girmelisiniz."
+
+#: scripts/remote_import.php:115
+msgid "You must specify the url for the remote Horde RPC server."
+msgstr "Uzak Horde RPC server için url belirtmelisiniz."
+
+#: faces/search/image_search.php:32 faces/search/image_save.php:22
+#: faces/search/image_define.php:21
+msgid "You must upload the search photo first"
+msgstr "Ýlk önce arama fotoðrafý yüklemelisiniz"
+
+#: lib/Forms/Upload.php:72
+msgid "Zip File Upload"
+msgstr "ZIP-Dosyasý yükle"
+
+#: lib/Ansel.php:576
+msgid "_Browse"
+msgstr "_Gözat"
+
+#: lib/Ansel.php:616
+msgid "_Faces"
+msgstr "_Simalar"
+
+#: lib/Ansel.php:584
+msgid "_Galleries"
+msgstr "_Galeriler"
+
+#: lib/Ansel.php:596
+msgid "_My Galleries"
+msgstr "_Benim Galerilerim"
+
+#: lib/Ansel.php:608
+msgid "_New Gallery"
+msgstr "_Yeni Galeri"
+
+#: lib/Ansel.php:621
+msgid "_Print"
+msgstr "_Yazdýr"
+
+#: templates/group/header.inc:15 templates/view/list.inc:12
+msgid "category"
+msgstr "kategori"
+
+#: templates/image/resize_image.inc:71 templates/image/crop_image.inc:160
+#: templates/faces/define.inc:61
+msgid "height"
+msgstr "Boy"
+
+#: xppublish.php:185
+msgid "image"
+msgstr "resim"
+
+#: templates/group/header.inc:18
+msgid "none"
+msgstr "hiç"
+
+#: lib/Views/EmbeddedRenderers/Mini.php:140
+#: lib/Views/GalleryRenderers/GalleryLightbox.php:130
+msgid "of"
+msgstr "of"
+
+#: templates/group/header.inc:13 templates/view/list.inc:11
+msgid "owner"
+msgstr "sahibi"
+
+#: lib/Exif.php:310
+msgid "sRGB"
+msgstr "sRGB"
+
+#: templates/image/resize_image.inc:69 templates/image/crop_image.inc:154
+#: templates/faces/define.inc:59
+msgid "width"
+msgstr "geniþlik"
diff --git a/ansel/po/zh_TW.po b/ansel/po/zh_TW.po
new file mode 100644 (file)
index 0000000..9060ec4
--- /dev/null
@@ -0,0 +1,1576 @@
+# Ansel Traditional Chinese Translation
+# Copyright 2004 David Chang.±i¨}¤å,¥xÆW
+# David Chang <david@tmv.gov.tw>, 2004.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ansel 0.1-cvs\n"
+"Report-Msgid-Bugs-To: dev@lists.horde.org\n"
+"POT-Creation-Date: 2007-01-25 15:09+0800\n"
+"PO-Revision-Date: 2004-06-16 14:12+0800\n"
+"Last-Translator: David Chang <david@tmv.gov.tw>\n"
+"Language-Team: Traditional Chinese <i18n@lists.horde.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=BIG5\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: gallery.php:171
+#, php-format
+msgid ""
+"\"%s\" is an invalid unique name; it may already be taken. Please choose a "
+"different unique name."
+msgstr "\"%s\" ¤£¬O¤@­Ó¦³®Äªº°ß¤@¦WºÙ; ¸Ó¦WºÙ¥i¯à¤w³Q¨Ï¥Î. ½Ð¿ï¾Ü¥t¤@­Ó."
+
+#: scripts/recursive_import.php:111
+#, php-format
+msgid "\"%s\" is not a directory."
+msgstr "\"%s\" ¤£¬O¤@­Ó¥Ø¿ý."
+
+#: templates/group/category.inc:51 templates/group/owner.inc:46
+#, php-format
+msgid "%d Galleries"
+msgstr "%d ­ÓÃÀ´Y"
+
+#: lib/Exif.php:285
+#, php-format
+msgid "%d bytes"
+msgstr "%d ¦ì¤¸²Õ"
+
+#: templates/list/header.inc:7
+#, php-format
+msgid "%d galleries"
+msgstr "%d ­ÓÃÀ´Y"
+
+#: templates/list/images.inc:4
+#, php-format
+msgid "%d images"
+msgstr "%d ­Ó¼v¹³"
+
+#: img/upload.php:136
+#, php-format
+msgid "%d images were uploaded."
+msgstr "¤w¤W¶Ç %d  ­Ó¼v¹³."
+
+#: lib/Exif.php:136 lib/Exif.php:142
+#, php-format
+msgid "%d pixels"
+msgstr "%d µe¼Æ"
+
+#: lib/Exif.php:166 lib/Exif.php:181
+#, php-format
+msgid "%d sec"
+msgstr "%d ¬í"
+
+#: templates/list/header.inc:7
+#, php-format
+msgid "%d to %d of %d galleries"
+msgstr " %d ¨ì %d ¦@ %d ­ÓÃÀ´Y"
+
+#: templates/view/subgallery_header.inc:3
+#, php-format
+msgid "%d to %d of %d subgalleries"
+msgstr " %d ¨ì %d ¦@ %d ­Ó¦¸ÃÀ´Y"
+
+#: gallery/sort.php:47
+#, php-format
+msgid "%s :: Sort"
+msgstr "%s :: ±Æ¦C"
+
+#: templates/view/gallery.inc:66
+#, php-format
+msgid "%s images"
+msgstr "%s ­Ó¼v¹³"
+
+#: list.php:44 templates/group/owner.inc:10 lib/Ansel.php:424
+#, php-format
+msgid "%s's Galleries"
+msgstr "%sªº©Ò¦³ÃÀ´Y"
+
+#: lib/Ansel.php:2167
+#, php-format
+msgid "(c) %s %s"
+msgstr "(c) %s %s"
+
+#: templates/xppublish/new.inc:14
+msgid "(must be unique)"
+msgstr "(¥²¶·°ß¤@)"
+
+#: scripts/ansel.php:311
+msgid "-a, --add[=filename]         Add local file to selected gallery"
+msgstr "-a, --add[=filename]         ·s¼W¥»¦aÀɮרì¿ï¾ÜªºÃÀ´Y¤¤"
+
+#: scripts/ansel.php:309
+msgid ""
+"-c, --create[=shortname/name/description/owner]\n"
+"                             Create gallery (and use it)  Combined with -g "
+"to create a subgallery."
+msgstr ""
+"-c, --create[=ÃÀ´Y²ºÙ/Åã¥Ü¦WºÙ/´y­z/¾Ö¦³ªÌ]\n"
+"                             «Ø¥ßÃÀ´Y (¨Ã¨Ï¥Î) »P°Ñ¼Æ -g ¨Ö¥Î«Ø¥ß¤@­Ó¦¸ÃÀ´Y."
+
+#: scripts/ansel.php:312
+msgid ""
+"-d, --dir[=directory]        Add all files from the directory to the "
+"selected\n"
+"                             gallery"
+msgstr ""
+"-d, --dir[=¥Ø¿ý]       ·s¼W¥Ø¿ý¤¤ªº©Ò¦³Àɮרì©Ò¿ï¾Üªº\n"
+"                             ÃÀ´Y¤¤"
+
+#: scripts/recursive_import.php:190
+msgid ""
+"-d, --dir[=directory]        Recursively add all files from the directory, "
+"creating\n"
+"                             a gallery for each directory"
+msgstr ""
+"-d, --dir[=¥Ø¿ý]             ·s¼W¥Ø¿ý(§t¦¸¥Ø¿ý)¤¤ªº©Ò¦³ÀÉ,\n"
+"                             ¬°¨C­Ó¥Ø¿ý«Ø¥ßÃÀ´Y"
+
+#: scripts/ansel.php:310
+msgid "-g, --gallery[=shortname]    Select gallery to use"
+msgstr "-g, --gallery[=ÃÀ´Y²ºÙ]     ¿ï¾Ü±ý¨Ï¥ÎªºÃÀ´Y"
+
+#: scripts/ansel.php:307 scripts/recursive_import.php:189
+msgid "-h, --help                   Show this help"
+msgstr "-h, --help                   Åã¥Ü»¡©ú "
+
+#: scripts/ansel.php:308
+msgid ""
+"-l, --list                   List galleries or images (if combined with -g)"
+msgstr "-l, --list                   ¦C¥X©Ò¦³ÃÀ´Y©Î¼v¹³(¦pªG»P-g¦X¨Ö¨Ï¥Î)"
+
+#: scripts/ansel.php:314 scripts/recursive_import.php:192
+msgid "-p, --password[=password]    Horde login password"
+msgstr "-p, --password[=password]    Horde µn¤J±K½X"
+
+#: scripts/ansel.php:315
+msgid "-t, --caption[=caption]      Caption for image (if combined with -a)"
+msgstr "-t, --caption[=caption]      ¼v¹³»¡©ú (¦pªG»P-g¦X¨Ö¨Ï¥Î)"
+
+#: scripts/ansel.php:313 scripts/recursive_import.php:191
+msgid "-u, --username[=username]    Horde login username"
+msgstr "-u, --username[=username]    Horde µn¤J¦WºÙ"
+
+#: templates/group/category.inc:51 templates/group/owner.inc:46
+msgid "1 Gallery"
+msgstr "1 ­ÓÃÀ´Y"
+
+#: templates/list/images.inc:4
+msgid "1 image"
+msgstr "¤@­Ó¼v¹³"
+
+#: img/upload.php:134
+msgid "1 image was uploaded."
+msgstr "¤w¤W¶Ç 1 ­Ó¼v¹³."
+
+#: lib/Exif.php:157
+#, php-format
+msgid "90 deg CCW"
+msgstr "°f°w 90 «×"
+
+#: lib/Exif.php:159
+#, php-format
+msgid "90 deg CCW Mirrored"
+msgstr "°f®ÉÄÁ 90 «×Ãè®g"
+
+#: lib/Exif.php:161
+#, php-format
+msgid "90 deg CW"
+msgstr "¶¶®ÉÄÁ 90 «×"
+
+#: lib/Exif.php:155
+#, php-format
+msgid "90 deg CW Mirrored"
+msgstr "¶¶®ÉÄÁ 90 «×Ãè®g"
+
+#: lib/api.php:191
+msgid "A gallery to add this image to is required."
+msgstr "·s¼W¼v¹³«e¥²¶·¥ý­n«Ø¥ßÄÝ©ó¦Û¤vªºÃÀ´Y."
+
+#: lib/Exif.php:128
+msgid "A-DEP"
+msgstr "¦Û°ÊÃn¥ú´º²`Àu¥ý"
+
+#: lib/api.php:303 gallery/delete.php:21
+#, php-format
+msgid "Access denied deleting gallery %s."
+msgstr "µL§R°£ÃÀ´Y %s ªºÅv­­."
+
+#: image.php:341
+#, php-format
+msgid "Access denied deleting image %s."
+msgstr "µL§R°£¼v¹³ %s ªºÅv­­."
+
+#: gallery.php:65
+#, php-format
+msgid "Access denied downloading %s."
+msgstr "µL¤U¸ü %s ªºÅv­­."
+
+#: gallery.php:118
+msgid "Access denied editing galleries."
+msgstr "µL½s¿èÃÀ´YªºÅv­­."
+
+#: gallery.php:134
+#, php-format
+msgid "Access denied saving gallery %s."
+msgstr "µLÀx¦sÃÀ´Y %s ªºÅv­­."
+
+#: image.php:142 image.php:204
+#, php-format
+msgid "Access denied saving image to %s."
+msgstr "µLÀx¦s¼v¹³¨ì %s ªºÅv­­."
+
+#: gallery/captions.php:31
+#, php-format
+msgid "Access denied setting captions for %s."
+msgstr "µL³]©w %s »¡©ú¤å¦rªºÅv­­."
+
+#: img/upload.php:147
+msgid "Add Image"
+msgstr "·s¼W¼v¹³"
+
+#: gallery.php:33 gallery.php:173 gallery.php:181
+msgid "Adding A New Gallery"
+msgstr "·s¼W¤@­Ó·sÃÀ´Y"
+
+#: gallery.php:57
+#, php-format
+msgid "Adding A Subgallery to %s"
+msgstr "·s¼W¤@­Ó¦¸ÃÀ´Y¨ì %s"
+
+#: scripts/recursive_import.php:171
+msgid "Adding subdirectories:"
+msgstr "«Ø¥ß¦¸¥Ø¿ý¤¤:"
+
+#: templates/view/gallery.inc:102
+msgid "All"
+msgstr "©Ò¦³"
+
+#: lib/Ansel.php:124
+#, php-format
+msgid "An error occurred counting galleries: %s"
+msgstr "¦b­pºâÃÀ´Y: %s ®Éµo¥Í¤@­Ó¿ù»~"
+
+#: lib/Ansel.php:64
+#, php-format
+msgid "An error occurred listing galleries: %s"
+msgstr "¦b¦C¥XÃÀ´Y: %s ®Éµo¥Í¤@­Ó¿ù»~"
+
+#: lib/Exif.php:343
+msgid "Aperture"
+msgstr "¥ú°é"
+
+#: templates/gallery/gallery.inc:50
+msgid "Are you sure you want to delete all of the images in this gallery?"
+msgstr "§A½T©w­n§R°£³o­ÓÃÀ´Y¤¤ªº©Ò¦³¼v¹³¶Ü?"
+
+#: templates/view/gallery.inc:20
+msgid "Are you sure you want to delete the selected images?"
+msgstr "§A½T©w­n§R°£©Ò¿ï¾Üªº¼v¹³¶Ü?"
+
+#: lib/Exif.php:356
+msgid "Artist"
+msgstr "Äá¼v®v"
+
+#: perms.php:52
+msgid "Attempt to edit a non-existent share."
+msgstr "À|¸Õ½s¿è¤@­Ó¤£¦s¦bªº¦@¨É."
+
+#: lib/Exif.php:124
+msgid "Av-priority"
+msgstr "¦Û°ÊÃn¥ú-¥ú°éÀu¥ý"
+
+#: lib/Exif.php:228
+msgid "Average"
+msgstr "¥­§¡´ú¥ú"
+
+#: image.php:371
+msgid "Bad input."
+msgstr "¿ù»~ªº¿é¤J."
+
+#: image.php:70
+msgid "Bottom"
+msgstr "©³³¡"
+
+#: lib/Exif.php:327
+msgid "Camera Make"
+msgstr "¬Û¾÷»s³y°Ó"
+
+#: lib/Exif.php:328
+msgid "Camera Model"
+msgstr "¬Û¾÷«¬¸¹"
+
+#: lib/Exif.php:354
+msgid "Camera Orientation"
+msgstr "¬Û¾÷¤ô¥­"
+
+#: lib/Ansel.php:317
+#, php-format
+msgid "Can't get unknown file type \"%s\"."
+msgstr "µLªkÀò¨ú¥¼ª¾Àɮתº«¬ºA \"%s\"."
+
+#: templates/gallery/delete_confirmation.inc:10
+msgid "Cancel"
+msgstr "¨ú®ø"
+
+#: templates/image/preview_image.inc:28
+msgid "Cancel Change"
+msgstr "¨ú®øÅܧó"
+
+#: image.php:38 lib/Forms/Upload.php:38
+msgid "Caption"
+msgstr "»¡©ú"
+
+#: gallery/captions.php:54
+msgid "Caption Editor"
+msgstr "»¡©ú½s¿è¾¹"
+
+#: gallery/captions.php:49
+msgid "Captions Saved."
+msgstr "»¡©ú¤wÀx¦s"
+
+#: config/prefs.php.dist:27
+msgid "Category"
+msgstr "¤ÀÃþ"
+
+#: image.php:66
+msgid "Center"
+msgstr "¸m¤¤"
+
+#: lib/Exif.php:229
+msgid "Center Weighted Average"
+msgstr "¤¤¥¡°¾­«¥­§¡´ú¥ú"
+
+#: templates/view/gallery.inc:77
+msgid "Change gallery properties"
+msgstr "ÅܧóÃÀ´YÄÝ©Ê"
+
+#: config/prefs.php.dist:15
+msgid "Change your display options."
+msgstr "Åܧó§AªºÅã¥Ü¿ï¶µ."
+
+#: templates/view/gallery.inc:69
+msgid "Choose Action:"
+msgstr "¿ï¾Ü§@¥Î¤è¦¡:"
+
+#: lib/Exif.php:242
+msgid "Cloudy"
+msgstr "¦h¶³"
+
+#: lib/Exif.php:293
+msgid "Color Sequential Area Sensor"
+msgstr "¦â±m§Ç¦C°Ï°ì·PÀ³¾¹"
+
+#: lib/Exif.php:295
+msgid "Color Sequential Linear Sensor"
+msgstr "¦â±m§Ç¦C½u©Ê·PÀ³¾¹"
+
+#: lib/Exif.php:351
+msgid "Color Space"
+msgstr "¦â±mªÅ¶¡"
+
+#: lib/Forms/Ecard.php:42
+msgid "Comments:"
+msgstr "°T®§:"
+
+#: lib/Exif.php:263
+msgid "Compulsory Flash"
+msgstr "±j­¢°{¥ú"
+
+#: lib/Exif.php:265
+msgid "Compulsory Flash, Return light detected"
+msgstr "±j­¢°{¥ú, ¦^®g°{¥ú¤w°»´ú"
+
+#: lib/Exif.php:264
+msgid "Compulsory Flash, Return light not detected"
+msgstr "±j­¢°{¥ú, ¦^®g°{¥ú¥¼°»´ú"
+
+#: lib/Exif.php:355
+msgid "Copyright"
+msgstr "ª©Åv"
+
+#: scripts/ansel.php:176
+msgid "Couldn't list galleries."
+msgstr "µLªk¦C¥XÃÀ´Y."
+
+#: scripts/ansel.php:40
+msgid "Couldn't read command-line options."
+msgstr "µLªkŪ¨ú©R¥O¦Cªº¿ï¶µ."
+
+#: templates/xppublish/list.inc:19
+msgid "Create"
+msgstr "«Ø¥ß"
+
+#: templates/view/gallery.inc:76
+msgid "Create a Subgallery"
+msgstr "«Ø¥ß¤@­Ó¦¸ÃÀ´Y"
+
+#: templates/xppublish/new.inc:8
+msgid "Create a new Gallery"
+msgstr "«Ø¥ß¤@­Ó·sªºÃÀ´Y"
+
+#: templates/xppublish/list.inc:18
+msgid "Create a new gallery:"
+msgstr "«Ø¥ß¤@­Ó·sÃÀ´Y:"
+
+#: templates/list/images.inc:20
+msgid "Created"
+msgstr "«Ø¥ß¤é"
+
+#: scripts/recursive_import.php:120
+#, php-format
+msgid "Creating gallery: \"%s\" (%s)"
+msgstr "«Ø¥ßÃÀ´Y¤¤: \"%s\" (%s)"
+
+#: lib/Exif.php:246
+msgid "Custom"
+msgstr "¦Û­q"
+
+#: image.php:59
+msgid "Custom Watermark"
+msgstr "¦Û­q¯B¤ô¦L"
+
+#: config/prefs.php.dist:78
+msgid "Custom watermark to use for images"
+msgstr "¦Û­q¼v¹³ªº¯B¤ô¦L"
+
+#: lib/Exif.php:334
+msgid "Date Image Digitized"
+msgstr "¤é´Á¼v¹³¤w¼Æ¦ì¤Æ"
+
+#: lib/Exif.php:332
+msgid "Date Image Modified"
+msgstr "­×§ï¤é´Á"
+
+#: lib/Exif.php:333
+msgid "Date Image Taken"
+msgstr "©çÄá¤é´Á"
+
+#: templates/view/gallery.inc:107 templates/view/image.inc:23
+msgid "Delete"
+msgstr "§R°£"
+
+#: templates/view/gallery.inc:85
+msgid "Delete this gallery"
+msgstr "§R°£³o­ÓÃÀ´Y"
+
+#: image.php:347
+msgid "Deleted the image."
+msgstr "¼v¹³¤w§R°£."
+
+#: templates/list/gallery_headers.inc:6
+msgid "Description"
+msgstr "´y­z"
+
+#: config/prefs.php.dist:14
+msgid "Display Options"
+msgstr "Åã¥Ü¿ï¶µ"
+
+#: templates/group/header.inc:3
+#, php-format
+msgid "Displaying from %s to %s of %s groups"
+msgstr "Åã¥Ü %s ¨ì %s ¦@ %s ­Ó¸s²Õ"
+
+#: templates/view/gallery.inc:66
+#, php-format
+msgid "Displaying from %s to %s of %s images"
+msgstr "Åã¥Ü %s ¨ì %s ¦@ %s ­Ó¼v¹³"
+
+#: templates/view/image.inc:23
+#, php-format
+msgid "Do you want to permanently delete %s?"
+msgstr "§A¯uªº­n§R°£ %s ¶Ü?"
+
+#: gallery/sort.php:61
+msgid "Done"
+msgstr "§¹¦¨"
+
+#: templates/view/image.inc:32
+msgid "Download Full Image"
+msgstr "¤U¸ü§¹¾ãªº¼v¹³"
+
+#: templates/view/gallery.inc:72
+msgid "Download as .zip"
+msgstr "¤U¸ü¬° .zip"
+
+#: gallery/sort.php:60
+msgid "Drag images to the desired sort position."
+msgstr "©ì¦²¼v¹³¨ì¾A·íªº¦ì¸m."
+
+#: xppublish.php:210
+#, php-format
+msgid "ERROR: %s"
+msgstr "¿ù»~: %s"
+
+#: lib/Exif.php:118
+msgid "Easy shooting"
+msgstr "©ö©ç"
+
+#: img/ecard.php:60
+msgid "Ecard - "
+msgstr "¹q¤l¥d¤ù - "
+
+#: templates/view/gallery.inc:156 templates/view/image.inc:20
+msgid "Edit"
+msgstr "½s¿è"
+
+#: image.php:188
+#, php-format
+msgid "Edit %s :: %s"
+msgstr "½s¿è %s :: %s"
+
+#: image.php:270
+msgid "Edit Image"
+msgstr "½s¿è¼v¹³"
+
+#: perms.php:243
+msgid "Edit Permissions"
+msgstr "½s¿èÅv­­"
+
+#: perms.php:246
+#, php-format
+msgid "Edit Permissions for %s"
+msgstr "½s¿è %s ªºÅv­­"
+
+#: image.php:153
+msgid "Edit an image"
+msgstr "½s¿è¤@­Ó¼v¹³"
+
+#: image.php:119
+#, php-format
+msgid "Edit properties :: %s"
+msgstr "½s¿èÄݩʠ:: %s"
+
+#: templates/gallery/gallery.inc:42
+msgid "Empty Gallery"
+msgstr "²MªÅÃÀ´Y"
+
+#: gallery/captions.php:25
+#, php-format
+msgid "Error accessing %s: %s"
+msgstr "¿ù»~¦s¨ú %s: %s"
+
+#: templates/xppublish/list.inc:12
+msgid "Existing galleries:"
+msgstr "²{¦sªºÃÀ´Y:"
+
+#: lib/Exif.php:340
+msgid "Exposure"
+msgstr "Ãn¥ú"
+
+#: lib/Exif.php:346
+msgid "Exposure Bias"
+msgstr "Ãn¥ú¸ÉÀv"
+
+#: lib/Exif.php:347
+msgid "Exposure Mode"
+msgstr "Ãn¥ú¼Ò¦¡"
+
+#: lib/Exif.php:344
+msgid "F-Number"
+msgstr "§Öªù"
+
+#: lib/Forms/Upload.php:44
+#, php-format
+msgid "File %s"
+msgstr "ÀÉ®× %s"
+
+#: lib/Exif.php:331
+msgid "File Size"
+msgstr "Àɮפj¤p"
+
+#: lib/Forms/Upload.php:34 lib/Forms/Upload.php:52
+msgid "File to upload"
+msgstr "¿ï¾Ü­n¤W¶ÇªºÀÉ®×"
+
+#: lib/Exif.php:245 lib/Exif.php:260
+msgid "Flash"
+msgstr "°{¥ú¿O"
+
+#: lib/Exif.php:349
+msgid "Flash Setting"
+msgstr "°{¥ú¿O³]©w"
+
+#: lib/Exif.php:268
+msgid "Flash, Auto-Mode"
+msgstr "°{¥ú¿O,¦Û°Ê¼Ò¦¡"
+
+#: lib/Exif.php:270
+msgid "Flash, Auto-Mode, Return light detected"
+msgstr "°{¥ú¿O, ¦Û°Ê¼Ò¦¡, ¦^®g°{¥ú¤w°»´ú"
+
+#: lib/Exif.php:269
+msgid "Flash, Auto-Mode, Return light not detected"
+msgstr "°{¥ú¿O, ¦Û°Ê¼Ò¦¡, ¦^®g°{¥ú¥¼°»´ú"
+
+#: lib/Exif.php:262
+msgid "Flash, strobe return light detected"
+msgstr "°{¥ú¿O, ¦^®gÀW°{¥ú¤w°»´ú"
+
+#: lib/Exif.php:261
+msgid "Flash, strobe return light not detected"
+msgstr "°{¥ú¿O, ¦^®gÀW°{¥ú¥¼°»´ú"
+
+#: templates/image/edit_image.inc:22
+msgid "Flip"
+msgstr "¤W¤UÄA­Ë"
+
+#: lib/Exif.php:244
+msgid "Fluorescent"
+msgstr ""
+
+#: lib/Exif.php:341
+msgid "Focal Length"
+msgstr "µJ¶Z"
+
+#: lib/Exif.php:342
+msgid "Focal Length (35mm equiv)"
+msgstr "µJ¶Z (´«ºâ¦¨ 35mm ¬Û¾÷)"
+
+#: lib/Ansel.php:429
+msgid "Galleries"
+msgstr "©Ò¦³ÃÀ´Y"
+
+#: templates/group/header.inc:6
+#, php-format
+msgid "Galleries grouped by %s"
+msgstr "©Ò¦³ÃÀ´Y¤w¨Ì%s¬°¸s²Õ¹º¤À"
+
+#: list.php:48 list.php:51
+#, php-format
+msgid "Galleries in category %s"
+msgstr "¦b¤ÀÃþ%s¤¤ªºÃÀ´Y"
+
+#: lib/Block/gallery.php:3 lib/Block/gallery.php:32 lib/Block/gallery.php:48
+msgid "Gallery"
+msgstr "ÃÀ´Y"
+
+#: templates/gallery/gallery.inc:16
+msgid "Gallery Category"
+msgstr "ÃÀ´Y¤ÀÃþ"
+
+#: templates/gallery/gallery.inc:31
+msgid "Gallery Description"
+msgstr "ÃÀ´Y´y­z"
+
+#: templates/gallery/gallery.inc:24
+msgid "Gallery Display Name"
+msgstr "ÃÀ´YÅã¥Ü¦WºÙ"
+
+#: list.php:56 list.php:109
+msgid "Gallery List"
+msgstr "ÃÀ´Y²M³æ"
+
+#: templates/xppublish/new.inc:14
+msgid "Gallery Short Name"
+msgstr "ÃÀ´Y²ºÙ"
+
+#: lib/Ansel.php:184
+#, php-format
+msgid "Gallery Unique name \"%s\" already exists."
+msgstr "ÃÀ´Yªº°ß¤@¦WºÙ \"%s\" ¤w¦s¦b."
+
+#: xppublish.php:121
+msgid "Gallery Unique name is already taken."
+msgstr "ÃÀ´Yªº°ß¤@¦WºÙ¤w³Q©R¦W."
+
+#: lib/Block/gallery.php:103
+msgid "Gallery does not exist."
+msgstr "ÃÀ´Y¤£¦s¦b."
+
+#: lib/Ansel.php:590
+msgid "Gallery names must be non-empty"
+msgstr "ÃÀ´Y¦WºÙ¤£¥iªÅ¥Õ"
+
+#: image.php:276
+msgid "Gallery not found."
+msgstr "§ä¤£¨ìÃÀ´Y."
+
+#: gallery/sort.php:39
+msgid "Gallery sorted."
+msgstr "ÃÀ´Y¤w­«·s±Æ¦C."
+
+#: image.php:62
+msgid "Giant"
+msgstr "¥¨¤j"
+
+#: templates/image/edit_image.inc:28
+msgid "Grayscale"
+msgstr "¦Ç¶¥"
+
+#: config/prefs.php.dist:29
+msgid "Group galleries by"
+msgstr "¸s²ÕÃÀ´Y¨Ì"
+
+#: lib/Exif.php:336
+msgid "Height"
+msgstr "°ª«×"
+
+#: image.php:67
+msgid "Horizontal Alignment"
+msgstr "¤ô¥­¹ï»ô"
+
+#: lib/Exif.php:345
+msgid "ISO Setting"
+msgstr "ISO ·P¥ú«×³]©w"
+
+#: lib/Exif.php:330
+msgid "Image Description"
+msgstr "¼v¹³´y­z"
+
+#: lib/Exif.php:329
+msgid "Image Type"
+msgstr "¼v¹³Ãþ«¬"
+
+#: scripts/ansel.php:122
+#, php-format
+msgid "Invalid gallery \"%s\" specified."
+msgstr "«ü©wªºÃÀ´Y \"%s\" µL®Ä."
+
+#: xppublish.php:87 xppublish.php:180
+msgid "Invalid gallery specified."
+msgstr "«ü©wªºÃÀ´YµL®Ä."
+
+#: image.php:62
+msgid "Large"
+msgstr "¤j"
+
+#: img/upload.php:153
+msgid "Learn how to publish images directly from Windows."
+msgstr "¾Ç²ß¦p¦óª½±µ³z¹L Windows µo§G¼v¹³."
+
+#: image.php:66
+msgid "Left"
+msgstr "¥ªÃä"
+
+#: scripts/ansel.php:181
+msgid "Listing Gallery/Name"
+msgstr "¦C¥XÃÀ´Y/¦WºÙ"
+
+#: scripts/ansel.php:165
+#, php-format
+msgid "Listing images in %s"
+msgstr "¥¿¦b¦C¥X%s¤¤ªº©Ò¦³¼v¹³"
+
+#: scripts/ansel.php:113 scripts/recursive_import.php:81
+#, php-format
+msgid "Logged in successfully as \"%s\"."
+msgstr "¤w¦¨¥\µn¤J¬° \"%s\"."
+
+#: scripts/ansel.php:109
+msgid "Login is incorrect."
+msgstr "µn¤J¸ê®Æ¤£¥¿½T."
+
+#: templates/xppublish/login.inc:2
+#, php-format
+msgid "Login to %s"
+msgstr "µn¤J¨ì %s"
+
+#: image.php:37 lib/Forms/Upload.php:37
+msgid "Make this the default image for this gallery?"
+msgstr "·í§@³o­ÓÃÀ´Yªº¹w³]¼v¹³?"
+
+#: scripts/ansel.php:305 scripts/recursive_import.php:187
+msgid ""
+"Mandatory arguments to long options are mandatory for short options too."
+msgstr ""
+
+#: lib/Exif.php:126 lib/Exif.php:247
+msgid "Manual"
+msgstr "¤â°Ê"
+
+#: image.php:62
+msgid "Medium"
+msgstr "¤¤"
+
+#: lib/Exif.php:348
+msgid "Metering Mode"
+msgstr "´ú¥ú¼Ò¦¡"
+
+#: image.php:70
+msgid "Middle"
+msgstr "¤¤µ¥"
+
+#: templates/image/edit_image.inc:25
+msgid "Mirror"
+msgstr "¬M®g"
+
+#: lib/Exif.php:149
+#, php-format
+msgid "Mirrored"
+msgstr "¤wÃè®g"
+
+#: templates/list/images.inc:21
+msgid "Modified"
+msgstr "³Ìªñ§ó·s"
+
+#: gallery.php:107
+#, php-format
+msgid "Modifying: %s"
+msgstr "­×§ï¤¤: %s"
+
+#: templates/view/gallery.inc:109
+msgid "Move"
+msgstr "·h²¾"
+
+#: image.php:380
+#, php-format
+msgid "Moved %d images to %s"
+msgstr "¤w·h²¾ %d ­Ó¼v¹³¨ì %s"
+
+#: image.php:378
+#, php-format
+msgid "Moved one image to %s"
+msgstr "¤w·h²¾ 1 ­Ó¼v¹³ ¨ì %s"
+
+#: lib/Exif.php:232
+msgid "Multi-Segment"
+msgstr "¯x°}´ú¥ú"
+
+#: lib/Exif.php:231
+msgid "Multi-Spot"
+msgstr "¦hÂI¦¡´ú¥ú"
+
+#: lib/Forms/Upload.php:40
+msgid "Multiple Images"
+msgstr "¦h¼v¹³"
+
+#: list.php:36 templates/group/owner.inc:3 lib/Ansel.php:416
+msgid "My Galleries"
+msgstr "§ÚªºÃÀ´Y"
+
+#: templates/list/gallery_headers.inc:4
+msgid "Name"
+msgstr "¦WºÙ"
+
+#: templates/xppublish/new.inc:33
+msgid "New Gallery Description"
+msgstr "·sÃÀ´Yªº´y­z"
+
+#: templates/xppublish/new.inc:24
+msgid "New Gallery Title"
+msgstr "·sÃÀ´Yªº¼ÐÃD"
+
+#: templates/view/image.inc:59 templates/view/image.inc:60
+msgid "Next"
+msgstr "¤U¤@±i"
+
+#: lib/Exif.php:259 lib/Exif.php:266 lib/Exif.php:267 lib/Exif.php:271
+msgid "No Flash"
+msgstr "µL°{¥ú"
+
+#: xppublish.php:123
+msgid "No Gallery Name Specified."
+msgstr "¥¼«ü©wÃÀ´Y¦WºÙ."
+
+#: xppublish.php:119
+msgid "No Gallery Unique Name Specified."
+msgstr "¥¼«ü©wÃÀ´Yªº°ß¤@¦WºÙ."
+
+#: xppublish.php:190
+msgid "No file specified"
+msgstr "¥¼«ü©wÀÉ®×"
+
+#: templates/view/gallery.inc:103 config/prefs.php.dist:28
+msgid "None"
+msgstr "µL"
+
+#: lib/Exif.php:147
+#, php-format
+msgid "Normal (O deg)"
+msgstr "¥¿±` (0 «×)"
+
+#: lib/Exif.php:289
+msgid "Not defined"
+msgstr "¥¼©w¸q"
+
+#: image.php:109 img/upload.php:22
+msgid "Not found or error accessing gallery."
+msgstr "§ä¤£¨ì©Î¦s¨úÃÀ´Y®Éµo¥Í¿ù»~."
+
+#: config/prefs.php.dist:54
+msgid "Number of galleries per page"
+msgstr "¨C­¶ÃÀ´Y¼Æ¥Ø"
+
+#: config/prefs.php.dist:62
+msgid "Number of groups per page"
+msgstr "¨C­¶¸s²Õ¼Æ¥Ø"
+
+#: config/prefs.php.dist:46
+msgid "Number of images per page"
+msgstr "¨C­¶¼v¹³¼Æ¥Ø"
+
+#: config/prefs.php.dist:38
+msgid "Number of images per row"
+msgstr "¨C¦C¼v¹³¼Æ¥Ø"
+
+#: lib/Exif.php:290
+msgid "One Chip Color Area Sensor"
+msgstr "¤@¤ù¦â±m°Ï°ì·P´ú¾¹"
+
+#: perms.php:64
+msgid ""
+"Only the owner or system administrator may change ownership or owner "
+"permissions for a share"
+msgstr "¥u¦³¾Ö¦³ªÌ»P¨t²ÎºÞ²z­û¥i¥HÅܧó¦@¨ÉÅv­­"
+
+#: lib/Exif.php:234
+msgid "Other"
+msgstr "¨ä¥L"
+
+#: config/prefs.php.dist:13
+msgid "Other Options"
+msgstr "¨ä¥L¿ï¶µ"
+
+#: templates/list/images.inc:25 config/prefs.php.dist:26
+msgid "Owner"
+msgstr "¾Ö¦³¤H"
+
+#: lib/Exif.php:233
+msgid "Partial"
+msgstr "§½³¡"
+
+#: templates/xppublish/login.inc:14
+msgid "Password"
+msgstr "±K½X"
+
+#: templates/view/image.inc:45
+msgid "Pause"
+msgstr "¼È°±"
+
+#: templates/view/image.inc:45
+msgid "Pause Slideshow"
+msgstr "¼È°±¼·©ñ"
+
+#: templates/xppublish/javascript.inc:63
+msgid "Photo Upload"
+msgstr "¹Ï¤ù¤W¶Ç"
+
+#: templates/view/image.inc:44
+msgid "Play"
+msgstr "©ñ¬M"
+
+#: xppublish.php:54
+msgid "Please enter your Username and Password."
+msgstr "½Ð¿é¤J§Aªº¨Ï¥ÎªÌ¦WºÙ¤Î±K½X"
+
+#: xppublish.php:99 xppublish.php:155
+msgid "Press the \"Back\" button and try again."
+msgstr "ÂI¿ï \"«á°h\" Áä¦A¸Õ¤@¦¸"
+
+#: image.php:282
+#, php-format
+msgid "Preview changes for %s :: %s"
+msgstr "¹wÄý %s :: %s ªºÅܧó"
+
+#: templates/view/image.inc:51 templates/view/image.inc:52
+msgid "Previous"
+msgstr "¤W¤@±i"
+
+#: lib/Exif.php:120
+msgid "Program"
+msgstr "µ{¦¡Ãn¥ú"
+
+#: templates/view/gallery.inc:155 templates/view/image.inc:19
+msgid "Properties"
+msgstr "¤º®e"
+
+#: templates/xppublish/list.inc:7
+msgid "Publish Images to:"
+msgstr "µo¥¬¼v¹³¨ì :"
+
+#: xppublish.php:35
+#, php-format
+msgid "Publish to %s"
+msgstr "µo¥¬¨ì %s"
+
+#: xppublish.php:24
+#, php-format
+msgid "Publish your photos to %s on %s."
+msgstr "µo¥¬§Aªº·Ó¤ù¨ì %s ©ó %s."
+
+#: lib/Block/gallery.php:35 lib/Block/gallery.php:54
+msgid "Random Gallery"
+msgstr "ÀH¿ïÃÀ´Y"
+
+#: lib/Block/random_gallery.php:3 lib/Block/random_gallery.php:27
+msgid "Random gallery"
+msgstr "ÀH¿ïÃÀ´Y"
+
+#: lib/Block/random_photo.php:3 lib/Block/random_photo.php:28
+msgid "Random photo"
+msgstr "ÀH¿ï·Ó¤ù"
+
+#: templates/gallery/delete_confirmation.inc:9
+msgid "Really Delete"
+msgstr "½T©w§R°£"
+
+#: templates/gallery/delete_confirmation.inc:2
+#, php-format
+msgid "Really delete %s?"
+msgstr "½T©w§R°£ %s?"
+
+#: templates/gallery/delete_confirmation.inc:5
+msgid ""
+"Really delete this gallery and all of the images in it? This action cannot "
+"be undone."
+msgstr "¯uªº­n§R°£³o­ÓÃÀ´Y»P¨ä¤¤ªº¼v¹³¶Ü? ³o¶µ¾Þ§@±NµLªk«ì´_."
+
+#: lib/Exif.php:272
+msgid "Red Eye"
+msgstr "¬õ²´"
+
+#: lib/Exif.php:278
+msgid "Red Eye, Auto-Mode"
+msgstr "¬õ²´,¦Û°Ê¼Ò¦¡"
+
+#: lib/Exif.php:280
+msgid "Red Eye, Auto-Mode, Return light detected"
+msgstr "¬õ²´, ¦Û°Ê¼Ò¦¡, ¦^®g°{¥ú¤w°»´ú"
+
+#: lib/Exif.php:279
+msgid "Red Eye, Auto-Mode, Return light not detected"
+msgstr "¬õ²´, ¦Û°Ê¼Ò¦¡, ¦^®g°{¥ú¥¼°»´ú"
+
+#: lib/Exif.php:275
+msgid "Red Eye, Compulsory Flash"
+msgstr "¬õ²´, ±j­¢°{¥ú"
+
+#: lib/Exif.php:277
+msgid "Red Eye, Compulsory Flash, Return light detected"
+msgstr "¬õ²´, ±j­¢°{¥ú, ¦^®g°{¥ú¤w°»´ú"
+
+#: lib/Exif.php:276
+msgid "Red Eye, Compulsory Flash, Return light not detected"
+msgstr "¬õ²´, ±j­¢°{¥ú, ¦^®g°{¥ú¥¼°»´ú"
+
+#: lib/Exif.php:274
+msgid "Red Eye, Return light detected"
+msgstr "¬õ²´, ¦^®g°{¥ú¤w°»´ú"
+
+#: lib/Exif.php:273
+msgid "Red Eye, Return light not detected"
+msgstr "¬õ²´, ¦^®g°{¥ú¥¼°»´ú"
+
+#: templates/list/header.inc:10 templates/list/header.inc:11
+#: templates/group/header.inc:7 templates/group/header.inc:8
+msgid "Refresh List"
+msgstr "­«·s¾ã²z²M³æ"
+
+#: image.php:66
+msgid "Right"
+msgstr "¥kÃä"
+
+#: templates/image/edit_image.inc:18
+msgid "Rotate 180"
+msgstr "±ÛÂà 180 «×"
+
+#: templates/image/edit_image.inc:17
+msgid "Rotate Left"
+msgstr "°f®É°w 90 «×±ÛÂà"
+
+#: templates/image/edit_image.inc:19
+msgid "Rotate Right"
+msgstr "¶¶®É°w 90 «×±ÛÂà"
+
+#: image.php:31 image.php:53
+msgid "Save"
+msgstr "Àx¦s"
+
+#: templates/captions/captions.inc:45
+msgid "Save Captions"
+msgstr "Àx¦s»¡©ú"
+
+#: templates/image/preview_image.inc:23
+msgid "Save Change"
+msgstr "Àx¦sÅܧó"
+
+#: templates/gallery/gallery.inc:40
+msgid "Save Gallery"
+msgstr "Àx¦sÃÀ´Y"
+
+#: image.php:139
+msgid "Save Image"
+msgstr "Àx¦s¼v¹³"
+
+#: templates/view/gallery.inc:102
+msgid "Select All"
+msgstr "¥þ¿ï"
+
+#: templates/view/gallery.inc:103
+msgid "Select None"
+msgstr "¤£¿ï"
+
+#: templates/view/gallery.inc:101
+#, php-format
+msgid "Select: %s, %s"
+msgstr "¿ï¾Ü:%s,%s"
+
+#: templates/view/gallery.inc:110
+msgid "Selected images to"
+msgstr "¤w¿ï¾Üªº¼v¹³¨ì"
+
+#: lib/Forms/Ecard.php:25
+msgid "Send"
+msgstr "±Hµo"
+
+#: img/ecard.php:104
+#, php-format
+msgid "Send Ecard :: %s"
+msgstr "±Hµo¹q¤l¥d¤ù :: %s"
+
+#: templates/view/image.inc:30
+msgid "Send an Ecard"
+msgstr "±Hµo¤@«Ê¹q¤l¥d¤ù"
+
+#: lib/Forms/Ecard.php:41
+msgid "Send ecard to the following address:"
+msgstr "ªB¤Íªº E-mail:"
+
+#: lib/Exif.php:352
+msgid "Sensing Method"
+msgstr "·P´ú¤è¦¡"
+
+#: templates/view/gallery.inc:78
+msgid "Set Captions"
+msgstr "³]©w»¡©ú"
+
+#: templates/view/gallery.inc:82
+msgid "Set Gallery Permissions"
+msgstr "³]©wÃÀ´YÅv­­"
+
+#: config/prefs.php.dist:70
+msgid "Show EXIF data"
+msgstr "Åã¥Ü©çÄá¸ê®Æ"
+
+#: lib/Exif.php:339
+msgid "Shutter Speed"
+msgstr "§Öªù"
+
+#: lib/Forms/Upload.php:31
+msgid "Single Image"
+msgstr "³æ¼v¹³"
+
+#: image.php:62
+msgid "Small"
+msgstr "¤p"
+
+#: templates/view/gallery.inc:79
+msgid "Sort Images"
+msgstr "±Æ§Ç¼v¹³"
+
+#: lib/Exif.php:230
+msgid "Spot"
+msgstr "ÂI´ú¥ú"
+
+#: templates/view/image.inc:44
+msgid "Start Slideshow"
+msgstr "¼·©ñ"
+
+#: scripts/ansel.php:201 scripts/ansel.php:281
+#: scripts/recursive_import.php:156
+#, php-format
+msgid "Storing image \"%s\"..."
+msgstr "¼v¹³ \"%s\" Àx¦s¤¤..."
+
+#: templates/view/subgallery_header.inc:5
+#, php-format
+msgid "Subgalleries of %s"
+msgstr "%s ªº¦¸ÃÀ´Y"
+
+#: scripts/ansel.php:222 scripts/recursive_import.php:166
+#, php-format
+msgid "Successfully added %d image(s) (%s) to gallery \"%s\" from \"%s\"."
+msgstr "¤w¦¨¥\ªº±q \"%4$s\" ·s¼W %1$d ­Ó¼v¹³ (%2$s) ¨ìÃÀ´Y \"%3$s\" ¤¤."
+
+#: scripts/ansel.php:209
+#, php-format
+msgid "Successfully added image \"%s\" to gallery \"%s\"."
+msgstr "¼v¹³ \"%s\" ¤w·s¼W¨ìÃÀ´Y¤¤ \"%s\"."
+
+#: gallery/delete.php:35
+#, php-format
+msgid "Successfully deleted %s."
+msgstr "%s ¤w§R°£§¹¦¨."
+
+#: lib/Exif.php:241
+msgid "Sunny"
+msgstr "ÆA¶§¤Ñ"
+
+#: scripts/ansel.php:248
+#, php-format
+msgid "The directory \"%s\" doesn't exist."
+msgstr "¥Ø¿ý \"%s\" ¤£¦s¦b."
+
+#: scripts/ansel.php:227
+#, php-format
+msgid "The directory \"%s\" had no valid images."
+msgstr "¥Ø¿ý \"%s\" ¤¤µL¦³®Äªº¼v¹³ÀÉ."
+
+#: scripts/ansel.php:265
+#, php-format
+msgid "The directory \"%s\" is empty."
+msgstr "¥Ø¿ý \"%s\" ¬OªÅªº."
+
+#: lib/Ansel.php:308
+#, php-format
+msgid "The file \"%s\" doesn't exist."
+msgstr "ÀÉ®× \"%s\" ¤£¦s¦b."
+
+#: lib/api.php:178 img/upload.php:103
+msgid "The file you uploaded does not appear to be a valid image."
+msgstr "§A©Ò¤W¶ÇªºÀɮר䣬O¤@­Ó¦³®Äªº¼v¹³ÀÉ."
+
+#: scripts/recursive_import.php:125
+#, php-format
+msgid "The gallery \"%s\" (%s) was created successfully."
+msgstr "ÃÀ´Y \"%s\" (%s) ¤w«Ø¥ß§¹¦¨."
+
+#: lib/Ansel.php:211
+#, php-format
+msgid "The gallery \"%s\" could not be created: %s"
+msgstr "µLªk«Ø¥ßÃÀ´Y \"%s\" : %s"
+
+#: gallery.php:193 xppublish.php:129 scripts/ansel.php:147
+#: scripts/recursive_import.php:123
+#, php-format
+msgid "The gallery \"%s\" couldn't be created: %s"
+msgstr "µLªk«Ø¥ßÃÀ´Y \"%s\" : %s"
+
+#: gallery.php:198 xppublish.php:133 scripts/ansel.php:151
+#, php-format
+msgid "The gallery \"%s\" was created successfully."
+msgstr "ÃÀ´Y \"%s\" ¤w«Ø¥ß§¹¦¨."
+
+#: gallery.php:144
+msgid "The gallery was saved."
+msgstr "ÃÀ´Y¤wÀx¦s."
+
+#: img/upload.php:126
+msgid ""
+"The uploaded file appears to be empty. It may not exist on your computer."
+msgstr "©Ò±µ¦¬¨ìªº¬OªÅ¥ÕÀÉ®×. ¸ÓÀɮצb§Aªº¹q¸£¤W¥i¯à¤£¦s¦b."
+
+#: gallery.php:72
+#, php-format
+msgid "There are no images in %s to download."
+msgstr "¦b %s ¤¤¨S¦³¥ô¦ó¼v¹³¥i¨Ñ¤U¸ü."
+
+#: templates/view/gallery.inc:120 templates/captions/captions.inc:47
+msgid "There are no images in this gallery."
+msgstr "¦¹ÃÀ´Y¥Ø«e¨S¦³¥ô¦ó¼v¹³."
+
+#: list.php:61 lib/Block/random_gallery.php:37 lib/Block/random_photo.php:38
+msgid "There are no photo galleries available."
+msgstr "µL¥ô¦ó¥i¥ÎªºÃÀ´Y."
+
+#: scripts/ansel.php:284
+#, php-format
+msgid "There was a problem adding the image \"%s\" to gallery \"%s\"."
+msgstr "·s¼W¼v¹³ \"%s\" ¨ìÃÀ´Y \"%s\" ®Éµo¥Í¤@­Ó¿ù»~."
+
+#: scripts/ansel.php:204
+#, php-format
+msgid "There was a problem adding the image \"%s\" to gallery \"%s\": %s"
+msgstr "·s¼W¼v¹³ \"%s\" ¨ìÃÀ´Y \"%s\": %s ®Éµo¥Í¤@­Ó¿ù»~."
+
+#: lib/api.php:316
+#, php-format
+msgid "There was a problem deleting %s: %s"
+msgstr "§R°£ %s: %s ®Éµo¥Í¤@­Ó°ÝÃD"
+
+#: gallery/delete.php:33
+#, php-format
+msgid "There was a problem deleting %s: %s."
+msgstr "§R°£ %s: %s ®Éµo¥Í¤@­Ó¿ù»~."
+
+#: image.php:345
+#, php-format
+msgid "There was a problem deleting images: %s"
+msgstr "§R°£¼v¹³: %s ®Éµo¥Í¤@­Ó¿ù»~"
+
+#: img/upload.php:96 img/upload.php:112
+#, php-format
+msgid "There was a problem saving the image: %s"
+msgstr "Àx¦s¼v¹³®Éµo¥Í¤@­Ó¿ù»~: %s"
+
+#: xppublish.php:199
+msgid "There was a problem uploading the image."
+msgstr "¤W¶Ç¼v¹³®Éµo¥Í¤@­Ó¿ù»~."
+
+#: img/upload.php:124
+#, php-format
+msgid "There was a problem uploading the image: %s"
+msgstr "¤W¶Ç¼v¹³®Éµo¥Í¤@­Ó¿ù»~: %s"
+
+#: img/upload.php:62 img/upload.php:83
+#, php-format
+msgid "There was an error processing the uploaded archive: %s"
+msgstr "³B²z¤W¶ÇÀ£ÁYÀÉ: %s ®Éµo¥Í¤@­Ó¿ù»~."
+
+#: img/ecard.php:96
+#, php-format
+msgid "There was an error sending your message: %s"
+msgstr "¶Ç°e¶l¥ó®Éµo¥Í¤@­Ó¿ù»~: %s"
+
+#: lib/Exif.php:292
+msgid "Three Chip Color Area Sensor"
+msgstr "¤T¤ù¦â±m°Ï°ì·P´ú¾¹"
+
+#: image.php:62
+msgid "Tiny"
+msgstr "¼b¤p"
+
+#: image.php:70
+msgid "Top"
+msgstr "³»³¡"
+
+#: lib/Exif.php:294
+msgid "Trilinear Sensor"
+msgstr "¤T½u©Ê·P´ú¾¹"
+
+#: lib/Exif.php:243
+msgid "Tungsten"
+msgstr "Âëµ·¥ú"
+
+#: lib/Exif.php:122
+msgid "Tv-priority"
+msgstr "¦Û°ÊÃn¥ú-§ÖªùÀu¥ý"
+
+#: lib/Exif.php:291
+msgid "Two Chip Color Area Sensor"
+msgstr "¤G¤ù¦â±m°Ï°ì·P´ú¾¹"
+
+#: lib/Exif.php:305
+msgid "Uncalibrated"
+msgstr "µLªk´ú¶q"
+
+#: templates/gallery/gallery.inc:41
+msgid "Undo Changes"
+msgstr "¦^´_Åܧó"
+
+#: list.php:48 templates/group/category.inc:8
+msgid "Unfiled"
+msgstr "¥¼¤ÀÃþ"
+
+#: lib/Exif.php:227
+msgid "Unknown"
+msgstr "¥¼ª¾ªº"
+
+#: gallery.php:109
+msgid "Unknown gallery"
+msgstr "¥¼ª¾ªºÃÀ´Y"
+
+#: lib/Ansel.php:199
+msgid "Unnamed"
+msgstr "¥¼©R¦W"
+
+#: xppublish.php:141
+msgid "Untitled"
+msgstr "¬°©R¦W"
+
+#: perms.php:236
+#, php-format
+msgid "Updated %s."
+msgstr "%s ¤w§ó·s."
+
+#: lib/Forms/Upload.php:27
+msgid "Upload"
+msgstr "¤W¶Ç"
+
+#: templates/view/gallery.inc:75
+msgid "Upload Images"
+msgstr "¤W¶Ç¼v¹³"
+
+#: templates/xppublish/javascript.inc:63
+#, php-format
+msgid "Upload Photos to %s"
+msgstr "¤W¶Ç·Ó¤ù¨ì %s"
+
+#: img/upload.php:30
+msgid "Upload new images"
+msgstr "¤W¶Ç·s¼v¹³"
+
+#: lib/Exif.php:151
+#, php-format
+msgid "Upsidedown"
+msgstr "¤W¤UÄA­Ë"
+
+#: lib/Exif.php:153
+#, php-format
+msgid "Upsidedown Mirrored"
+msgstr "¤W¤U¥ª¥kÄA­Ë"
+
+#: scripts/ansel.php:303 scripts/recursive_import.php:185
+#, php-format
+msgid "Usage: %s [OPTIONS]..."
+msgstr "»yªk: %s [¿ï¶µ]..."
+
+#: templates/xppublish/new.inc:17
+msgid "Use Default"
+msgstr "¨Ï¥Î¹w³]­È"
+
+#: lib/Forms/Ecard.php:32
+msgid "Use the following return address:"
+msgstr "¨Ï¥Î¤U¦Cªº¦^ÂЦí§}:"
+
+#: lib/Exif.php:350
+msgid "User Comment"
+msgstr "¨Ï¥ÎªÌµù¸Ñ"
+
+#: templates/xppublish/login.inc:11
+msgid "Username"
+msgstr "¨Ï¥ÎªÌ¦WºÙ"
+
+#: xppublish.php:50
+msgid "Username and Password are incorrect."
+msgstr "¨Ï¥ÎªÌ¦WºÙ¤Î±K½X¤£¥¿½T."
+
+#: scripts/recursive_import.php:79
+msgid "Username or password is incorrect."
+msgstr "¨Ï¥ÎªÌ¦WºÙ¤Î±K½X¤£¥¿½T."
+
+#: image.php:71
+msgid "Vertical Alignment"
+msgstr "««ª½¹ï»ô"
+
+#: templates/list/gallery_summaries.inc:2
+#: templates/list/gallery_summaries.inc:7 templates/list/images.inc:9
+#: templates/list/images.inc:12 lib/Block/gallery.php:74
+#: lib/Block/random_gallery.php:42
+#, php-format
+msgid "View %s"
+msgstr "À˵ø %s"
+
+#: lib/Block/random_photo.php:51
+msgid "View Photo"
+msgstr "À˵ø·Ó¤ù"
+
+#: image.php:242 image.php:248 templates/image/edit_image.inc:32
+msgid "Watermark"
+msgstr "¯B¤ô¦L"
+
+#: image.php:63
+msgid "Watermark Font"
+msgstr "¯B¤ô¦L¦r«¬"
+
+#: lib/Exif.php:353
+msgid "White Balance"
+msgstr "¥Õ¥­¿Å"
+
+#: lib/Exif.php:335
+msgid "Width"
+msgstr "¼e«×"
+
+#: lib/Exif.php:337
+msgid "X Resolution"
+msgstr "X ¸ÑªR«×"
+
+#: lib/Exif.php:338
+msgid "Y Resolution"
+msgstr "Y ¸ÑªR«×"
+
+#: image.php:178
+msgid "You are not allowed to edit this image."
+msgstr "¤£¤¹³\§A½s¿è³o­Ó¼v¹³."
+
+#: lib/Ansel.php:1464
+#, php-format
+msgid "You are not allowed to move images to %s."
+msgstr "¤£¤¹³\§A·h²¾¼v¹³¨ì %s."
+
+#: lib/Ansel.php:1466
+#, php-format
+msgid "You are not allowed to remove images from %s."
+msgstr "¤£¤¹³\§A¦Û %s ¤¤²¾°£¼v¹³."
+
+#: xppublish.php:91 xppublish.php:184
+msgid "You cannot add photos in that gallery."
+msgstr "§AµLªk·s¼W¬Û¤ù¨ì¸ÓÃÀ´Y¤¤."
+
+#: scripts/ansel.php:128
+#, php-format
+msgid "You cannot add photos to the gallery \"%s\"."
+msgstr "§AµLªk·s¼W¬Û¤ù¨ì¸ÓÃÀ´Y¤¤ \"%s\"."
+
+#: gallery/delete.php:23
+#, php-format
+msgid "You cannot delete %s, it contains nested galleries."
+msgstr "©|¦³¦¸ÃÀ´Y¦s¦b,§AµLªk§R°£ %s."
+
+#: gallery.php:46 gallery.php:159
+#, php-format
+msgid "You do not have permission to add children to %s."
+msgstr "§A¨S¦³·s¼W¦¸%sªºÅv­­."
+
+#: gallery/sort.php:26
+msgid "You do not have permission to change this gallery."
+msgstr "§A¨S¦³Åܧó¦¹ÃÀ´YªºÅv­­."
+
+#: gallery.php:242
+#, php-format
+msgid "You do not have permission to empty %s."
+msgstr "§A¨S¦³²MªÅ %s ªºÅv­­."
+
+#: lib/Block/gallery.php:106
+msgid "You do not have permission to view this gallery."
+msgstr "§A¨S¦³À˵ø¦¹ÃÀ´YªºÅv­­."
+
+#: img/ecard.php:69
+msgid ""
+"You have been sent an Ecard. To view the Ecard, you must be able to view "
+"text/html messages in your mail reader. If you are viewing this message, "
+"then most likely your mail reader does not support viewing text/html "
+"messages."
+msgstr ""
+"§A¤w¸g¦¬¨ì¤F¤@«Ê¹q¤l¥d¤ù. ¦p±ýÀ˵ø³o«Ê¹q¤l¥d¤ù,§Aªº¹q¤l¶l¥ó¾\Ū³nÅ饲¶·¾Ö¦³ÀË"
+"µø text/html ªº¥\¯à.¦pªG§A²{¦b¬Ýªº¨ì³o¬q°T®§,³q±`¶H¼x§Aªº¶l¥ó¾\Ū³nÅé¨Ã¥¼¤ä´©"
+"¸Ó¥\¯à."
+
+#: templates/view/gallery.inc:46
+msgid "You must choose a gallery to move images to."
+msgstr "°õ¦æ·h²¾¼v¹³«e§A¥²¶·¥ý¿ï¾Ü¤@­Ó¤ÀÃþ."
+
+#: img/ecard.php:45
+msgid "You must enter an e-mail address to send the message to."
+msgstr "§A¥²¶·¿é¤J¤@­Ó¹q¤l¶l¥ó¦a§}¥H§Q±Hµo¶P¥d."
+
+#: img/ecard.php:40
+msgid "You must enter your e-mail address."
+msgstr "§A¥²¶·¿é¤J§Aªº E-mail ¦a§}."
+
+#: gallery.php:179
+msgid "You must provide a display name for your new gallery."
+msgstr "§A¥²¶·¬°§Aªº·sÃÀ´Y´£¨Ñ¤@­ÓÅã¥Ü¦WºÙ."
+
+#: scripts/recursive_import.php:88
+msgid "You must specify a valid directory."
+msgstr "§A¥²¶·«ü©w¤@­Ó¥i¥Îªº¥Ø¿ý."
+
+#: scripts/recursive_import.php:84
+msgid "You must specify a valid username and password."
+msgstr "½Ð«ü©w¤@²Õ¥i¥Îªº¨Ï¥ÎªÌ¦WºÙ¤Î¨ä±K½X."
+
+#: lib/Forms/Upload.php:49
+msgid "Zip File Upload"
+msgstr "Zip À£ÁYÀÉ"
+
+#: lib/Ansel.php:366
+msgid "_Galleries"
+msgstr "©Ò¦³ÃÀ´Y_G"
+
+#: lib/Ansel.php:368
+msgid "_My Galleries"
+msgstr "§ÚªºÃÀ´Y_M"
+
+#: lib/Ansel.php:375
+msgid "_New Gallery"
+msgstr "·s¼WÃÀ´Y_N"
+
+#: lib/Ansel.php:380
+msgid "_Print"
+msgstr "¦C¦L_P"
+
+#: lib/Exif.php:302
+msgid "sRGB"
+msgstr "sRGB"
diff --git a/ansel/preview.php b/ansel/preview.php
new file mode 100644 (file)
index 0000000..c989427
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+/**
+ * $Horde: ansel/preview.php,v 1.20 2009/07/13 17:18:38 mrubinsk Exp $
+ *
+ * Copyright 2007-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael Rubinsky <mrubinsk@horde.org>
+ */
+
+@define('ANSEL_BASE', dirname(__FILE__));
+require_once ANSEL_BASE . '/lib/base.php';
+$imageId = Horde_Util::getFormData('image');
+$image = &$ansel_storage->getImage($imageId);
+if (is_a($image, 'PEAR_Error')) {
+    Horde::logMessage($image, __LINE__, __FILE__, PEAR_LOG_ERR);
+    exit;
+}
+$gal = $ansel_storage->getGallery(abs($image->gallery));
+if (is_a($gal, 'PEAR_Error')) {
+    Horde::logMessage($image, __LINE__, __FILE__, PEAR_LOG_ERR);
+    exit;
+}
+$img = Ansel::getImageUrl($imageId, 'thumb', false);
+if (!is_a($img, 'PEAR_Error') &&
+        $gal->hasPermission(Horde_Auth::getAuth(), PERMS_SHOW) &&
+        !$gal->hasPasswd() &&
+        $gal->isOldEnough()) {
+    echo '<img src="' . $img . '" alt="' . htmlspecialchars($image->filename) . '" />';
+} else {
+    echo '';
+}
diff --git a/ansel/protect.php b/ansel/protect.php
new file mode 100644 (file)
index 0000000..337c166
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+/**
+ * $Horde: ansel/protect.php,v 1.4 2009/06/10 05:24:00 slusarz Exp $
+ *
+ * Copyright 2001-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Duck <duck@obala.net>
+ */
+
+require_once dirname(__FILE__) . '/lib/base.php';
+require_once 'Horde/Form.php';
+
+$vars = Horde_Variables::getDefaultVariables();
+$gallery = $ansel_storage->getGallery($vars->get('gallery'));
+if (is_a($gallery, 'PEAR_Error')) {
+    $notification->push($gallery->getMessage());
+    header('Location: ' . Horde::applicationUrl('list.php'));
+    exit;
+}
+$form = new Horde_Form($vars, _("This gallery is protected by a password. Please enter it below."));
+$form->addVariable($gallery->get('name'), 'name', 'description', false);
+$form->addVariable($gallery->get('desc'), 'desc', 'description', false);
+$form->addVariable(_("Password"), 'passwd', 'password', true);
+$form->addHidden('', 'url', 'text', true);
+$form->addHidden('', 'gallery', 'int', true);
+if ($form->validate()) {
+    if ($gallery->get('passwd') != $vars->get('passwd')) {
+        $notification->push(_("Incorrect password"), 'horde.warning');
+    } else {
+        $_SESSION['ansel']['passwd'][$gallery->id] = md5($vars->get('passwd'));
+        $url = $vars->get('url');
+        if (empty($url)) {
+            $url = Horde::applicationUrl(Horde_Util::addParameter('view.php', 'gallery', $gallery->id));
+        }
+        header('Location: ' . $url);
+        exit;
+    }
+}
+require ANSEL_TEMPLATES . '/common-header.inc';
+require ANSEL_TEMPLATES . '/menu.inc';
+echo '<div class="header">' . Ansel::getBreadCrumbs() . '</div>';
+$form->renderActive(null, null, null, 'post');
+require $registry->get('templates', 'horde') . '/common-footer.inc';
diff --git a/ansel/report.php b/ansel/report.php
new file mode 100644 (file)
index 0000000..3b82a1c
--- /dev/null
@@ -0,0 +1,93 @@
+<?php
+/**
+ * Report offensive content
+ *
+ * $Horde: ansel/report.php,v 1.7 2009/06/10 00:33:01 mrubinsk Exp $
+ *
+ * Copyright 2007-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author  Duck <duck@obala.net>
+ * @package Ansel
+ */
+
+require_once dirname(__FILE__) . '/lib/base.php';
+require_once 'Horde/Form.php';
+
+$title = _("Do you really want to report this gallery?");
+$gallery_id = (int)Horde_Util::getFormData('gallery');
+
+$gallery = $ansel_storage->getGallery($gallery_id);
+if (is_a($gallery, 'PEAR_Error')) {
+    $notification->push($gallery->getMessage());
+    header('Location: ' . Horde::applicationUrl('view.php?view=List', true));
+    exit;
+}
+
+if (($image_id = Horde_Util::getFormData('image')) !== null) {
+    $title = _("Do you really want to report this photo?");
+    $return_url = Ansel::getUrlFor('view',
+                                   array('view' => 'Image',
+                                         'image' => $image_id,
+                                         'gallery' => $gallery_id),
+                                   true);
+} else {
+    $style = $gallery->getStyle();
+    $return_url = Ansel::getUrlFor('view',
+                                      array('gallery' => $gallery_id,
+                                            'view' => 'Gallery'),
+                                      true);
+}
+
+$vars = Horde_Variables::getDefaultVariables();
+$form = new Horde_Form($vars, $title);
+$form->setButtons(array(_("Report"), _("Cancel")));
+
+$enum = array('advertisement' => _("Advertisement content"),
+              'terms' => _("Terms and conditions infringement"),
+              'offensive' => _("Offensive content"),
+              'copyright' => _("Copyright infringement"));
+
+$form->addVariable($gallery->get('name'), 'name', 'description', false);
+$form->addVariable($gallery->get('desc'), 'desc', 'description', false);
+
+$form->addHidden('', 'gallery', 'text', true, true);
+$vars->set('gallery', $gallery_id);
+
+$form->addVariable(_("Report type"), 'type', 'radio', true, false, null, array($enum));
+$form->addVariable(_("Report reason"), 'reason', 'longtext', true);
+
+$gallery_id = Horde_Util::getFormData('id');
+
+if ($form->validate()) {
+    if (Horde_Util::getFormData('submitbutton') == _("Report")) {
+        require ANSEL_BASE . '/lib/Report.php';
+        $report = Ansel_Report::factory();
+
+        $body = _("Gallery Name") . ': ' . $gallery->get('name') . "\n"
+            . _("Gallery Description") . ': ' . $gallery->get('desc') . "\n"
+            . _("Gallery Id") . ': ' . $gallery->id . "\n"
+            . _("Report type") . ': ' . $enum[$vars->get('type')] . "\n"
+            . _("Report reason") . ': ' . $vars->get('reason') . "\n"
+            . $return_url;
+
+        $result = $report->report($body);
+        if (is_a($result, 'PEAR_Error')) {
+            $notification->push(_("Gallery was not reported.") . ' ' .
+                                $result->getMessage(), 'horde.error');
+        } else {
+            $notification->push(_("Gallery was reported."), 'horde.success');
+        }
+    } else {
+        $notification->push(_("Gallery was not reported."), 'horde.warning');
+    }
+    header('Location: ' . $return_url);
+    exit;
+}
+
+require ANSEL_TEMPLATES . '/common-header.inc';
+require ANSEL_TEMPLATES . '/menu.inc';
+$form->renderActive(null, null, null, 'post');
+require $registry->get('templates', 'horde') . '/common-footer.inc';
diff --git a/ansel/rss.php b/ansel/rss.php
new file mode 100644 (file)
index 0000000..6b4118b
--- /dev/null
@@ -0,0 +1,297 @@
+<?php
+/**
+ * Ansel RSS feed. Note that we always return a 'normal' thumb image
+ * and not a prettythumb since we have no way of knowing what the client
+ * requesting this will be viewing the image on.
+ *
+ * $Horde: ansel/rss.php,v 1.55 2009/07/11 20:25:54 mrubinsk Exp $
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Ansel
+ */
+
+$session_control = 'readonly';
+require_once dirname(__FILE__) . '/lib/base.php';
+require_once ANSEL_BASE . '/lib/version.php';
+
+// Get form data
+$stream_type = Horde_Util::getFormData('stream_type', 'all');
+$id = Horde_Util::getFormData('id');
+$type = basename(Horde_Util::getFormData('type', 'rss2'));
+$slug = Horde_Util::getFormData('slug');
+$uid = md5($stream_type . $id . $type . Horde_Auth::getAuth());
+$filename = 'ansel_feed_template_' . $uid;
+if ($conf['ansel_cache']['usecache']) {
+    $cache_key = 'ansel_feed_template_' . $uid;
+    $rss = $cache->get($cache_key, $conf['cache']['default_lifetime']);
+    $filename = $cache->get($filename, $conf['cache']['default_lifetime']);
+}
+
+if (empty($rss)) {
+    // Assume failure
+    $params = array('last_modified' => time(),
+                    'name' => _("Error retrieving feed"),
+                    'link' => '',
+                    'desc' => _("Unable to retrieve requested feed"),
+                    'image_url' => Horde::img('alerts/error.png', '', '',
+                                              $registry->getImageDir('horde')),
+                    'image_link' => '',
+                    'image_alt' => '');
+    $author = '';
+
+    // Determine what we are requesting
+    // @TODO - category
+    switch ($stream_type) {
+    case 'all':
+        $images = $ansel_storage->getRecentImages();
+        if (is_a($images, 'PEAR_Error') || !count($images)) {
+            $images = array();
+        } else {
+            // Eventually would like the link to link to a search
+            // results page containing the same images present in this
+            // feed. For now, just link to the List view until some of
+            // the search code can be refactored.
+            $params = array('last_modified' => $images[0]->uploaded,
+                            'name' => sprintf(_("Recently added photos on %s"),
+                                              $conf['server']['name']),
+                            'link' => Ansel::getUrlFor('view',
+                                                       array('view' => 'List'),
+                                                       true),
+                            'desc' => sprintf(_("Recently added photos on %s"),
+                                              $conf['server']['name']),
+                            'image_url' => Ansel::getImageUrl($images[0]->id,
+                                                              'thumb', true),
+                            'image_alt' => $images[0]->caption,
+                            'image_link' => Ansel::getUrlFor(
+                                'view', array('image' => $images[0]->id,
+                                              'view' => 'Image',
+                                              'gallery' => $images[0]->gallery),
+                                true));
+        }
+        break;
+
+    case 'gallery':
+        // Retrieve latest from specified gallery
+        // Try a slug first.
+        if ($slug) {
+            $gallery = $ansel_storage->getGalleryBySlug($slug);
+        } elseif (is_numeric($id)) {
+            $gallery = $ansel_storage->getGallery($id);
+        }
+        if (!is_a($gallery, 'PEAR_Error') &&
+            $gallery->hasPermission(Horde_Auth::getAuth(), PERMS_SHOW) &&
+            !$gallery->hasPasswd() && $gallery->isOldEnough()) {
+            if (!$gallery->countImages() && $gallery->hasSubGalleries()) {
+                $subgalleries = $ansel_storage->listGalleries(PERMS_SHOW,
+                                                              null,
+                                                              $gallery);
+                $subs = array();
+                foreach ($subgalleries as $subgallery) {
+                    $subs[] = $subgallery->id;
+                }
+                $images = $ansel_storage->getRecentImages($subs);
+            } else {
+                $images = $gallery->getRecentImages();
+                $owner = &$gallery->getOwner();
+                $author = $owner->getValue('from_addr');
+            }
+        }
+
+        if (!isset($images) || is_a($images, 'PEAR_Error') || !count($images)) {
+            $images = array();
+        } else {
+            $style = $gallery->getStyle();
+            $viewurl = Ansel::getUrlFor('view',
+                                        array('view' => 'Gallery',
+                                              'gallery' => $id),
+                                        true);
+            $img = &$ansel_storage->getImage($gallery->getDefaultImage('ansel_default'));
+            if (is_a($img, 'PEAR_Error')) {
+                break;
+            }
+            $params = array('last_modified' => $gallery->get('last_modified'),
+                            'name' => sprintf(_("%s on %s"),
+                                              $gallery->get('name'),
+                                              $conf['server']['name']),
+                            'link' => $viewurl,
+                            'desc' => $gallery->get('desc'),
+                            'image_url' => Ansel::getImageUrl($img->id, 'thumb',
+                                                              true, 'ansel_default'),
+                            'image_alt' => $img->caption,
+                            'image_link' => Ansel::getUrlFor('view',
+                                                             array('image' => $img->id,
+                                                                   'gallery' => $img->gallery,
+                                                                   'view' => 'Image'),
+                                                             true));
+        }
+        break;
+
+    case 'user':
+        $shares = $ansel_storage->listGalleries(PERMS_SHOW, $id);
+        if (!is_a($shares, 'PEAR_Error')) {
+            $galleries = array();
+            foreach ($shares as $gallery) {
+                if ($gallery->isOldEnough() && !$gallery->hasPasswd()) {
+                    $galleries[] = $gallery->id;
+                }
+            }
+        }
+        $images = array();
+        if (isset($galleries) && count($galleries)) {
+            $images = $ansel_storage->getRecentImages($galleries);
+            if (!is_a($images, 'PEAR_Error') && count($images)) {
+                require_once('Horde/Identity.php');
+                $owner = &Identity::singleton('none', $id);
+                $name = $owner->getValue('fullname');
+                $author = $owner->getValue('from_addr');
+                if (!$name) {
+                    $name = $id;
+                }
+                $params = array('last_modified' => $images[0]->uploaded,
+                                'name' => sprintf(_("Photos by %s"),
+                                                  $name),
+                                'link' => Ansel::getUrlFor('view',
+                                                           array('view' => 'List',
+                                                                 'groupby' => 'owner',
+                                                                 'owner' => $id),
+                                                           true),
+                                'desc' => sprintf(_("Recently added photos by %s on %s"),
+                                                  $name, $conf['server']['name']),
+                                'image_url' => Ansel::getImageUrl($images[0]->id,
+                                                                  'thumb', true,
+                                                                  'ansel_default'),
+                                'image_alt' => $images[0]->caption,
+                                'image_link' => Ansel::getUrlFor(
+                                    'view', array('image' => $images[0]->id,
+                                                  'gallery' => $images[0]->gallery,
+                                                  'view' => 'Image'), true)
+                );
+            }
+        }
+        break;
+
+    case 'tag':
+        require_once ANSEL_BASE . '/lib/Tags.php';
+        $tag_id = array_values(Ansel_Tags::getTagIds(array($id)));
+        $images = Ansel_Tags::searchTagsById($tag_id, 10, 0, 'images');
+        $tag_id = array_pop($tag_id);
+        $images = $ansel_storage->getImages($images['images']);
+        if (!is_a($images, 'PEAR_Error') && count($images)) {
+            $tag_id = $tag_id[0];
+            $images = array_values($images);
+            $params = array('last_modified' => $images[0]->uploaded,
+                            'name' => sprintf(_("Photos tagged with %s on %s"),
+                                              $id, $conf['server']['name']),
+                            'link' => Ansel::getUrlFor('view',
+                                                       array('tag' => $id,
+                                                             'view' => 'Results'),
+                                                       true),
+                            'desc' => sprintf(_("Photos tagged with %s on %s"),
+                                              $id, $conf['server']['name']),
+                            'image_url' => Ansel::getImageUrl($images[0]->id,
+                                                              'thumb', true,
+                                                              'ansel_default'),
+                            'image_alt' => $images[0]->caption,
+                            'image_link' => Ansel::getUrlFor('view',
+                                                             array('view' => 'Image',
+                                                                   'image' => $images[0]->id,
+                                                                   'gallery' => $images[0]->gallery),
+                                                             true)
+                      );
+        }
+
+        // Do this here to avoid iterating the images twice
+        $galleries = array();
+        $imgs = array();
+        $cnt = count($images);
+        for ($i = 0; $i < $cnt; ++$i) {
+            $gallery_id = $images[$i]->gallery;
+            if (empty($galleries[$gallery_id])) {
+                $galleries[$gallery_id]['gallery'] = $GLOBALS['ansel_storage']->getGallery($gallery_id);
+                if (is_a($galleries[$gallery_id]['gallery'], 'PEAR_Error')) {
+                    continue;
+                }
+            }
+            if (!isset($galleries[$gallery_id]['perm'])) {
+                $galleries[$gallery_id]['perm'] =
+                    ($galleries[$gallery_id]['gallery']->hasPermission(Horde_Auth::getAuth(), PERMS_READ) &&
+                     $galleries[$gallery_id]['gallery']->isOldEnough() &&
+                     !$galleries[$gallery_id]['gallery']->hasPasswd());
+            }
+
+            if ($galleries[$gallery_id]['perm']) {
+                $imgs[$i]['link'] = Ansel::getUrlFor(
+                    'view',
+                    array('view' => 'Image',
+                          'gallery' => $images[$i]->gallery,
+                          'image' => $images[$i]->id), true);
+                $imgs[$i]['filename'] = $images[$i]->filename;
+                $imgs[$i]['caption'] = $images[$i]->caption;
+                $imgs[$i]['url'] = htmlspecialchars(Ansel::getImageUrl($images[$i]->id, 'screen', true));
+                $imgs[$i]['type'] = $images[$i]->getType('screen');
+                $imgs[$i]['author'] = $author;
+                $imgs[$i]['thumb'] = htmlspecialchars(Ansel::getImageUrl($images[$i]->id, 'thumb', true));
+                $imgs[$i]['latitude'] = $images[$i]->lat;
+                $imgs[$i]['longitude'] = $images[$i]->lng;
+            }
+        }
+
+    }
+
+    if (!isset($imgs)) {
+        $imgs = array();
+        $cnt = count($images);
+        for ($i = 0; $i < $cnt; ++$i) {
+            $imgs[$i]['link'] = Ansel::getUrlFor(
+                'view',
+                array('view' => 'Image',
+                      'gallery' => $images[$i]->gallery,
+                      'image' => $images[$i]->id), true);
+            $imgs[$i]['filename'] = $images[$i]->filename;
+            $imgs[$i]['caption'] = $images[$i]->caption;
+            $imgs[$i]['url'] = htmlspecialchars(Ansel::getImageUrl($images[$i]->id, 'screen', true));
+            $imgs[$i]['type'] = $images[$i]->getType('screen');
+            $imgs[$i]['author'] = $author;
+            $imgs[$i]['thumb'] = htmlspecialchars(Ansel::getImageUrl($images[$i]->id, 'thumb', true));
+            $imgs[$i]['latitude'] = $images[$i]->lat;
+            $imgs[$i]['longitude'] = $images[$i]->lng;
+        }
+    }
+
+    $charset = Horde_Nls::getCharset();
+    $xsl = $registry->get('themesuri') . '/feed-rss.xsl';
+    $stream_name = @htmlspecialchars($params['name'], ENT_COMPAT, Horde_Nls::getCharset());
+    $stream_desc = @htmlspecialchars($params['desc'], ENT_COMPAT, Horde_Nls::getCharset());
+    $stream_updated = htmlspecialchars(date('r', $params['last_modified']));
+    $stream_official = htmlspecialchars($params['link']);
+    $image_url = htmlspecialchars($params['image_url']);
+    $image_link = htmlspecialchars($params['image_link']);
+    $image_alt = htmlspecialchars($params['image_alt']);
+    $ansel = 'Ansel ' . ANSEL_VERSION . ' (http://www.horde.org/)';
+
+    if ($stream_type != 'all' && $type != 'rss2') {
+        $getparams = array('stream_type' => $stream_type, 'type' => $type);
+        if (isset($id)) {
+            $getparams['id'] = $id;
+        }
+    } else {
+        $getparams = array();
+    }
+    $stream_rss = Horde_Util::addParameter(Horde::applicationUrl('rss.php', true, -1), $getparams);
+    $stream_rss2 = Horde_Util::addParameter(Horde::applicationUrl('rss.php', true, -1), $getparams);
+    $images = $imgs;
+
+    ob_start();
+    include ANSEL_TEMPLATES . '/rss/' . $type . '.inc';
+    $rss = ob_get_clean();
+
+    if ($conf['ansel_cache']['usecache']) {
+        $cache->set($cache_key, $rss);
+        $cache->set($filename, $params['name']);
+    }
+}
+
+$browser->downloadHeaders($filename . '.rss', 'text/xml', true);
+echo $rss;
diff --git a/ansel/scripts/.htaccess b/ansel/scripts/.htaccess
new file mode 100755 (executable)
index 0000000..3a42882
--- /dev/null
@@ -0,0 +1 @@
+Deny from all
diff --git a/ansel/scripts/AnselPublish.scpt b/ansel/scripts/AnselPublish.scpt
new file mode 100644 (file)
index 0000000..3cc009b
Binary files /dev/null and b/ansel/scripts/AnselPublish.scpt differ
diff --git a/ansel/scripts/all_images_exif_to_tags.php b/ansel/scripts/all_images_exif_to_tags.php
new file mode 100755 (executable)
index 0000000..8c67bf2
--- /dev/null
@@ -0,0 +1,123 @@
+#!/usr/bin/env php
+<?php
+/**
+* $Horde: ansel/scripts/all_images_exif_to_tags.php,v 1.9 2009/07/08 18:28:47 slusarz Exp $
+*
+* Bare bones script to auto append an image's exif fields to it's tags.
+*
+* See the enclosed file COPYING for license information (GPL). If you
+* did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+*
+* @author Michael J. Rubinsky <mrubinsk@horde.org>
+*/
+@define('AUTH_HANDLER', true);
+@define('HORDE_BASE', dirname(__FILE__) . '/../..');
+@define('ANSEL_BASE', HORDE_BASE . '/ansel');
+
+// Do CLI checks and environment setup first.
+require_once HORDE_BASE . '/lib/core.php';
+
+// Make sure no one runs this from the web.
+if (!Horde_Cli::runningFromCLI()) {
+    exit("Must be run from the command line\n");
+}
+
+// Load the CLI environment.
+Horde_Cli::init();
+$cli = &Horde_Cli::singleton();
+
+/* Command line options */
+require_once 'Console/Getopt.php';
+$ret = Console_Getopt::getopt(Console_Getopt::readPHPArgv(), 'hu:p:f:',
+                              array('help', 'username=', 'password=', 'fields='));
+
+if (is_a($ret, 'PEAR_Error')) {
+    $cli->fatal($ret->getMessage());
+}
+
+/* Show help and exit if no arguments were set. */
+list($opts, $args) = $ret;
+if (!$opts) {
+    showHelp();
+    exit;
+}
+
+// Default to only DateTimeOriginal
+$exif_fields = array('DateTimeOriginal');
+foreach ($opts as $opt) {
+    list($optName, $optValue) = $opt;
+    switch ($optName) {
+    case 'u':
+    case '--username':
+        $username = $optValue;
+        break;
+
+    case 'p':
+    case '--password':
+        $password = $optValue;
+        break;
+    case 'h':
+    case '--help':
+        showHelp();
+        exit;
+    case '--fields':
+    case 'f':
+        $exif_fields = explode(':', $optValue);
+        break;
+    }
+}
+
+require_once ANSEL_BASE . '/lib/base.php';
+
+// Login to horde if username & password are set.
+if (!empty($username) && !empty($password)) {
+    $auth = Horde_Auth::singleton($conf['auth']['driver']);
+    if (!$auth->authenticate($username, array('password' => $password))) {
+        $cli->fatal(_("Username or password is incorrect."));
+    } else {
+        $cli->message(sprintf(_("Logged in successfully as \"%s\"."), $username), 'cli.success');
+    }
+} else {
+    $cli->fatal(_("You must specify a valid username and password."));
+}
+
+if (!Horde_Auth::isAdmin()) {
+    $cli->fatal(_("You must login with an administrative account."));
+}
+
+// Get the list of image ids that have exif data.
+$sql = 'SELECT DISTINCT image_id from ansel_image_attributes;';
+$results = $GLOBALS['ansel_db']->query($sql);
+if (is_a($results, 'PEAR_Error')) {
+    $cli->fatal($results->getMessage());
+}
+$image_ids = $results->fetchAll(MDB2_FETCHMODE_ASSOC);
+$results->free();
+foreach (array_values($image_ids) as $image_id) {
+    $image = $ansel_storage->getImage($image_id['image_id']);
+    if (!is_a($image, 'PEAR_Error')) {
+        $results = $image->exifToTags($exif_fields);
+        if (is_a($results, 'PEAR_Error')) {
+            $cli->message(sprintf(_("Could not extract exif fields from %s: %s"), $image_id['image_id'], $results->getMessage()), 'cli.error');
+        }
+        $cli->message(sprintf(_("Extracted exif fields from %s"), $image->filename), 'cli.success');
+    } else {
+        $cli->message(sprintf(_("Could not extract exif fields from %s: %s"), $image_id['image_id'], $image->getMessage()), 'cli.error');
+    }
+}
+$cli->message(_("Done"));
+exit;
+
+function showHelp()
+{
+    global $cli;
+
+    $cli->writeln(sprintf(_("Usage: %s [OPTIONS]..."), basename(__FILE__)));
+    $cli->writeln();
+    $cli->writeln(_("Mandatory arguments to long options are mandatory for short options too."));
+    $cli->writeln();
+    $cli->writeln(_("-h, --help                   Show this help"));
+    $cli->writeln(_("-u, --username[=username]    Horde login username"));
+    $cli->writeln(_("-p, --password[=password]    Horde login password"));
+    $cli->writeln(_("-f, --fields[=exif_fields]   A ':' delimited list of exif fields to include DateTimeOriginal is default."));
+}
diff --git a/ansel/scripts/ansel.php b/ansel/scripts/ansel.php
new file mode 100755 (executable)
index 0000000..260dd52
--- /dev/null
@@ -0,0 +1,330 @@
+#!/usr/bin/php
+<?php
+/**
+* $Horde: ansel/scripts/ansel.php,v 1.29 2009/07/30 19:50:16 mrubinsk Exp $
+*
+* This script interfaces with Ansel via the command-line
+*
+* See the enclosed file COPYING for license information (GPL). If you
+* did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+*
+* @author Vijay Mahrra <webmaster@stain.net>
+*/
+@define('HORDE_BASE', dirname(__FILE__) . '/../..');
+@define('ANSEL_BASE', HORDE_BASE . '/ansel');
+
+// Do CLI checks and environment setup first.
+require_once HORDE_BASE . '/lib/core.php';
+
+// Make sure no one runs this from the web.
+if (!Horde_Cli::runningFromCLI()) {
+    exit("Must be run from the command line\n");
+}
+
+// Load the CLI environment.
+Horde_Cli::init();
+$cli = &Horde_Cli::singleton();
+
+// Load Ansel.
+$ansel_authentication = 'none';
+require_once ANSEL_BASE . '/lib/base.php';
+
+// We accept the user name on the command-line.
+require_once 'Console/Getopt.php';
+$ret = Console_Getopt::getopt(Console_Getopt::readPHPArgv(),
+                              'hu:p:lc:g:a:d:t:',
+                              array('help', 'username=', 'password=', 'list',
+                                    'create=', 'gallery=', 'add=', 'dir=',
+                                    'caption='));
+
+if (is_a($ret, 'PEAR_Error')) {
+    var_dump($ret);
+    $error = _("Couldn't read command-line options.");
+    Horde::logMessage($error, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+    $cli->fatal($error);
+}
+
+// Show help and exit if no arguments were set.
+list($opts, $args) = $ret;
+if (!$opts) {
+    showHelp();
+    exit;
+}
+
+// Defaults/initialization.
+$caption = '';
+
+foreach ($opts as $opt) {
+    list($optName, $optValue) = $opt;
+    switch ($optName) {
+    case 'u':
+    case '--username':
+        $username = $optValue;
+        break;
+
+    case 'p':
+    case '--password':
+        $password = $optValue;
+        break;
+
+    case 'l':
+    case '--list':
+        $list = true;
+        break;
+
+    case 'c':
+    case '--create':
+        list($gallery_name, $gallery_desc, $gallery_owner) = explode('/', $optValue, 4);
+        $createGallery = true;
+        break;
+
+    case 'g':
+    case '--gallery':
+        $galleryId = $optValue;
+        break;
+
+    case 'a':
+    case '--add':
+        $file = $optValue;
+        break;
+
+    case 'd':
+    case '--dir':
+        $dir = $optValue;
+        break;
+
+    case 'h':
+    case '--help':
+        showHelp();
+        exit;
+
+    case 't':
+    case '--caption':
+        $caption = $optValue;
+        break;
+    }
+}
+
+// Login to horde if username & password are set.
+if (!empty($username) && !empty($password)) {
+    $auth = Horde_Auth::singleton($conf['auth']['driver']);
+    if (!$auth->authenticate($username, array('password' => $password))) {
+        $error = _("Login is incorrect.");
+        Horde::logMessage($error, __FILE__, __LINE__, PEAR_LOG_ERR);
+        $cli->fatal($error);
+    } else {
+        $msg = sprintf(_("Logged in successfully as \"%s\"."), $username);
+        Horde::logMessage($msg, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+        $cli->message($msg, 'cli.success');
+    }
+}
+
+// Choose the gallery to add to (or use the created one).
+if (!empty($galleryId)) {
+    if (!$ansel_storage->galleryExists($galleryId)) {
+        $error = sprintf(_("Invalid gallery \"%s\" specified."), $galleryId);
+        Horde::logMessage($error, __FILE__, __LINE__, PEAR_LOG_WARNING);
+        $cli->fatal($error);
+    } else {
+        $gallery = $ansel_storage->getGallery($galleryId);
+        if (is_a($gallery, 'PEAR_Error') ||
+            !$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+            $error = sprintf(_("Access denied adding photos to \"%s\"."),
+                             $galleryId);
+            Horde::logMessage($error, __FILE__, __LINE__, PEAR_LOG_WARNING);
+            $cli->fatal($error);
+        }
+    }
+}
+
+// Create a gallery.
+if (!empty($createGallery)) {
+
+    // See if we have selected a gallery parent.
+    if (!empty($galleryId)) {
+        $parent = $galleryId;
+    }
+
+    $attributes = array('name' => $gallery_name,
+                        'desc' => $gallery_desc,
+                        'owner' => $gallery_owner);
+    $gallery = $ansel_storage->createGallery($attributes, null, $parent);
+    if (is_a($gallery, 'PEAR_Error')) {
+        $galleryId = null;
+        $error = sprintf(_("The gallery \"%s\" couldn't be created: %s"),
+                         $gallery_name, $gallery->getMessage());
+        Horde::logMessage($error, __FILE__, __LINE__, PEAR_LOG_ERR);
+        $cli->fatal($error);
+    } else {
+        $msg = sprintf(_("The gallery \"%s\" was created successfully."),
+                       $gallery_name);
+        Horde::logMessage($msg, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+        $cli->message($msg, 'cli.success');
+    }
+}
+
+// List galleries/images.
+if (!empty($list)) {
+    if (!empty($gallery)) {
+        $images = $gallery->listImages();
+        if (is_a($images, 'PEAR_Error')) {
+            $cli->fatal($images->getMessage());
+        }
+
+        $cli->message(sprintf(_("Listing photos in %s"), $gallery->get('name')), 'cli.success');
+        $cli->writeln();
+
+        $images = array_keys($images);
+        foreach ($images as $id) {
+            $image = &$ansel_storage->getImage($id);
+            $cli->writeln(str_pad($image->filename, 30) . $image->getVFSPath() . '/' . $id);
+        }
+    } else {
+        $galleries = $GLOBALS['ansel_storage']->listGalleries();
+        if (is_a($galleries, 'PEAR_Error')) {
+            $error = _("Couldn't list galleries.");
+            Horde::logMessage($error, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+            $cli->fatal($error);
+        }
+
+        $cli->message(_("Listing Gallery/Name"), 'cli.success');
+        $cli->writeln();
+
+        foreach ($galleries as $id => $gallery) {
+            $name = $gallery->get('name');
+            $msg = "$id/$name";
+            $cli->writeln($msg);
+            Horde::logMessage($msg, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+        }
+    }
+}
+
+// Add an image from the filesystem.
+if (!empty($file) && isset($gallery) && !is_a($gallery, 'PEAR_Error')) {
+    $image = &Ansel::getImageFromFile($file, array('caption' => $caption));
+    if (is_a($image, 'PEAR_Error')) {
+        Horde::logMessage($image->getMessage(), __FILE__, __LINE__, PEAR_LOG_WARNING);
+        $cli->fatal($image->getMessage());
+    }
+
+    $cli->message(sprintf(_("Storing photo \"%s\"..."), $file), 'cli.message');
+    $image_id = $gallery->addImage($image);
+    if (is_a($image_id, 'PEAR_Error')) {
+        $error = sprintf(_("There was a problem adding the photo \"%s\" to gallery \"%s\": %s"),
+                         basename($file), $galleryId, $image_id->getMessage());
+        Horde::logMessage($error, __FILE__, __LINE__, PEAR_LOG_ERR);
+        $cli->fatal($error);
+    }
+
+    $msg = sprintf(_("Successfully added photo \"%s\" to gallery \"%s\"."),
+                   basename($file), $galleryId);
+    $cli->message($msg, 'cli.success');
+    Horde::logMessage($msg, __FILE__, __LINE__, PEAR_LOG_NOTICE);
+}
+
+// Add all images from a directory on the filesystem.
+if (!empty($dir) && isset($gallery) && !is_a($gallery, 'PEAR_Error')) {
+    $msg = addDirToGallery($dir, $gallery);
+    if (is_a($msg, 'PEAR_Error')) {
+        Horde::logMessage($msg->getMessage(), __FILE__, __LINE__, PEAR_LOG_ERR);
+        $cli->fatal($msg->getMessage());
+    }
+    if ($msg) {
+        $msg = sprintf(ngettext("Successfully added %d photo (%s) to gallery \"%s\" from \"%s\".", "Successfully added %d photos (%s) to gallery \"%s\" from \"%s\".", count($msg)),
+                       count($msg), join(', ', $msg), $galleryId, $dir);
+        $cli->message($msg, 'cli.success');
+        Horde::logMessage($msg, __FILE__, __LINE__, PEAR_LOG_NOTICE);
+    } else {
+        $msg = sprintf(_("The directory \"%s\" had no valid photos."), $dir);
+        $cli->message($msg, 'cli.warning');
+        Horde::logMessage($msg, __FILE__, __LINE__, PEAR_LOG_WARNING);
+    }
+}
+
+exit;
+
+
+/**
+ * Reads all images from a directory into the currently selected gallery.
+ *
+ * @param string $dir
+ * @param object $gallery &$ansel_shares->getShare($galleryId)
+ * @return array An array of all the images that were successfully added to
+ *               the gallery, or PEAR_Error
+ */
+function addDirToGallery($dir = '', &$gallery)
+{
+    global $cli, $galleryId, $ansel_shares;
+
+    if (!file_exists($dir)) {
+        $error = sprintf(_("The directory \"%s\" doesn't exist."), $dir);
+        Horde::logMessage($error, __FILE__, __LINE__, PEAR_LOG_ERR);
+        return PEAR::raiseError($error);
+    }
+
+    // Read all the files into an array.
+    $files_array = array();
+    $h = opendir($dir);
+    while (false !== ($file = readdir($h))) {
+        if ($file == '.' || $file == '..') {
+            continue;
+        }
+        $files_array[] = $file;
+    }
+    closedir($h);
+
+    if (!$files_array) {
+        $error = sprintf(_("The directory \"%s\" is empty."), $dir);
+        Horde::logMessage($error, __FILE__, __LINE__, PEAR_LOG_ERR);
+        return PEAR::raiseError($error);
+    }
+    chdir($dir);
+
+    // Process each file and upload to the gallery.
+    $added_images = array();
+    foreach ($files_array as $file) {
+        $image = Ansel::getImageFromFile($dir . '/' . $file);
+        if (is_a($image, 'PEAR_Error')) {
+            Horde::logMessage($image->getMessage(), __FILE__, __LINE__, PEAR_LOG_WARNING);
+            $cli->message($image->getMessage(), 'cli.error');
+            continue;
+        }
+
+        $cli->message(sprintf(_("Storing photo \"%s\"..."), $file), 'cli.message');
+        $image_id = $gallery->addImage($image);
+        if (is_a($image_id, 'PEAR_Error')) {
+            $error = sprintf(_("There was a problem adding the photo \"%s\" to gallery \"%s\"."),
+                             $file, $galleryId);
+            Horde::logMessage($error, __FILE__, __LINE__, PEAR_LOG_ERR);
+            $cli->message($image_id->getMessage(), 'cli.error');
+            continue;
+        }
+
+        $added_images[] = $file;
+    }
+
+    return $added_images;
+}
+
+/**
+ * Show the command line arguments that the script accepts.
+ */
+function showHelp()
+{
+    global $cli;
+
+    $cli->writeln(sprintf(_("Usage: %s [OPTIONS]..."), basename(__FILE__)));
+    $cli->writeln();
+    $cli->writeln(_("Mandatory arguments to long options are mandatory for short options too."));
+    $cli->writeln();
+    $cli->writeln(_("-h, --help                   Show this help"));
+    $cli->writeln(_("-l, --list                   List galleries or photos (if combined with -g)"));
+    $cli->writeln(_("-c, --create[=name/description/owner]\n                             Create gallery (and use it)  Combined with -g to create a subgallery."));
+    $cli->writeln(_("-g, --gallery[=shortname]    Select gallery to use"));
+    $cli->writeln(_("-a, --add[=filename]         Add local file to selected gallery"));
+    $cli->writeln(_("-d, --dir[=directory]        Add all files from the directory to the selected\n                             gallery"));
+    $cli->writeln(_("-u, --username[=username]    Horde login username"));
+    $cli->writeln(_("-p, --password[=password]    Horde login password"));
+    $cli->writeln(_("-t, --caption[=caption]      Caption for photo (if combined with -a)"));
+    $cli->writeln();
+}
diff --git a/ansel/scripts/garbage_collection.php b/ansel/scripts/garbage_collection.php
new file mode 100755 (executable)
index 0000000..9a104a0
--- /dev/null
@@ -0,0 +1,111 @@
+#!/usr/bin/php
+<?php
+/**
+ * $Horde: ansel/scripts/garbage_collection.php,v 1.8 2009/07/30 19:50:16 mrubinsk Exp $
+ *
+ * This script looks for images in the VFS that have no pointer in the
+ * database. Any non-referenced images it finds get moved to a garbage
+ * folder in Ansel's VFS directory.
+ *
+ * Make sure to run this as a user who has full permissions on the VFS
+ * directory.
+ */
+@define('ANSEL_BASE', dirname(__FILE__) . '/..');
+@define('HORDE_BASE', ANSEL_BASE . '/..');
+
+// Do CLI checks and environment setup first.
+require_once HORDE_BASE . '/lib/core.php';
+
+// Make sure no one runs this from the web.
+if (!Horde_Cli::runningFromCLI()) {
+    exit("Must be run from the command line\n");
+}
+
+// Load the CLI environment - make sure there's no time limit, init
+// some variables, etc.
+Horde_Cli::init();
+
+$ansel_authentication = 'none';
+require_once ANSEL_BASE . '/lib/base.php';
+require_once 'VFS.php';
+require_once 'Console/Getopt.php';
+
+// Default arguments.
+$move = false;
+$verbose = false;
+
+// Parse command-line arguments.
+$ret = Console_Getopt::getopt(Console_Getopt::readPHPArgv(), 'mv',
+                              array('move', 'verbose'));
+
+if (is_a($ret, 'PEAR_Error')) {
+    die("Couldn't read command-line options.\n");
+}
+list($opts, $args) = $ret;
+foreach ($opts as $opt) {
+    list($optName, $optValue) = $opt;
+    switch ($optName) {
+    case '--move':
+        $move = true;
+        break;
+
+    case 'v':
+    case '--verbose':
+        $verbose = true;
+    }
+}
+
+$vfs = &VFS::singleton($conf['vfs']['type'], Horde::getDriverConfig('vfs', $conf['vfs']['type']));
+$vfspath = '.horde/ansel/';
+$garbagepath = $vfspath . 'garbage/';
+
+$hash = $vfs->listFolder($vfspath, null, false, true);
+sort($hash);
+
+$count = 0;
+foreach ($hash as $dir) {
+    if ($dir['name'] == 'garbage') {
+        continue;
+    }
+    $images = $vfs->listFolder($vfspath . $dir['name'] . '/full/');
+    foreach ($images as $image) {
+        $image_id = strpos($image['name'], '.') ? substr($image['name'], 0, strpos($image['name'], '.')) : $image['name'];
+        $result = $ansel_db->queryOne('SELECT 1 FROM ansel_images WHERE image_id = ' . (int)$image_id);
+        if (!$result) {
+            if (!$count && !$vfs->isFolder($vfspath, 'garbage')) {
+                $vfs->createFolder($vfspath, 'garbage');
+            }
+
+            $count++;
+
+            if ($verbose) {
+                echo $vfspath . $image['name'] . ' -> ' . $garbagepath . $image['name'] . "\n";
+            }
+
+            if ($move) {
+                $vfs->move($vfspath . $dir['name'] . '/full/', $image['name'], $garbagepath);
+                $vfs->deleteFile($vfspath . $dir['name'] . '/screen/', $image['name']);
+                $vfs->deleteFile($vfspath . $dir['name'] . '/thumb/', $image['name']);
+                $vfs->deleteFile($vfspath . $dir['name'] . '/mini/', $image['name']);
+
+                // Try to clean directories too.
+                $vfs->deleteFolder($vfspath . $dir['name'], 'full');
+                $vfs->deleteFolder($vfspath . $dir['name'], 'screen');
+                $vfs->deleteFolder($vfspath . $dir['name'], 'thumb');
+                $vfs->deleteFolder($vfspath . $dir['name'], 'mini');
+                $vfs->deleteFolder($vfspath, $dir['name']);
+            }
+        }
+    }
+}
+
+if ($count) {
+    echo "\nFound dangling images";
+    if ($move) {
+        echo " and moved $count to $garbagepath.\n";
+    } else {
+        echo ", run this script with --move to clean them up.\n";
+    }
+} else {
+    echo "No cleanup necessary.\n";
+}
diff --git a/ansel/scripts/recursive_import.php b/ansel/scripts/recursive_import.php
new file mode 100755 (executable)
index 0000000..e79cbf1
--- /dev/null
@@ -0,0 +1,229 @@
+#!/usr/bin/php -q
+<?php
+/**
+* $Horde: ansel/scripts/recursive_import.php,v 1.15 2009/07/30 19:50:16 mrubinsk Exp $
+*
+* This script interfaces with Ansel via the command-line
+*
+* See the enclosed file COPYING for license information (GPL). If you
+* did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+*
+* @author Vijay Mahrra <webmaster@stain.net>
+*/
+@define('HORDE_BASE', dirname(__FILE__) . '/../..');
+@define('ANSEL_BASE', HORDE_BASE . '/ansel');
+
+// Do CLI checks and environment setup first.
+require_once HORDE_BASE . '/lib/core.php';
+
+// Make sure no one runs this from the web.
+if (!Horde_Cli::runningFromCLI()) {
+    exit("Must be run from the command line\n");
+}
+
+// Load the CLI environment.
+Horde_Cli::init();
+$cli = &Horde_Cli::singleton();
+
+// Load Ansel.
+$ansel_authentication = 'none';
+require_once ANSEL_BASE . '/lib/base.php';
+
+// We accept the user name on the command-line.
+require_once 'Console/Getopt.php';
+$ret = Console_Getopt::getopt(Console_Getopt::readPHPArgv(), 'hu:p:lc:g:a:d:k',
+                              array('help', 'username=', 'password=', 'dir=', 'keep'));
+
+if (is_a($ret, 'PEAR_Error')) {
+    $cli->fatal($ret->getMessage());
+}
+
+// Show help and exit if no arguments were set.
+list($opts, $args) = $ret;
+if (!$opts) {
+    showHelp();
+    exit;
+}
+
+// Delete empty galleries by default
+$keepEmpties = false;
+
+foreach ($opts as $opt) {
+    list($optName, $optValue) = $opt;
+    switch ($optName) {
+    case 'u':
+    case '--username':
+        $username = $optValue;
+        break;
+
+    case 'p':
+    case '--password':
+        $password = $optValue;
+        break;
+
+    case 'd':
+    case '--dir':
+        $dir = $optValue;
+        break;
+
+    case 'h':
+    case '--help':
+        showHelp();
+        exit;
+
+    case 'k':
+    case '--keep':
+        $keepEmpties = true;
+    }
+}
+
+// Login to horde if username & password are set.
+if (!empty($username) && !empty($password)) {
+    $auth = Horde_Auth::singleton($conf['auth']['driver']);
+    if (!$auth->authenticate($username, array('password' => $password))) {
+        $cli->fatal(_("Username or password is incorrect."));
+    } else {
+        $cli->message(sprintf(_("Logged in successfully as \"%s\"."), $username), 'cli.success');
+    }
+} else {
+    $cli->fatal(_("You must specify a valid username and password."));
+}
+
+if (empty($dir)) {
+    $cli->fatal(_("You must specify a valid directory."));
+}
+
+Horde_Nls::setCharset('utf-8');
+$gallery_id = processDirectory($dir);
+if (!$keepEmpties && !is_a($gallery_id, 'PEAR_Error')) {
+    $gallery = $ansel_storage->getGallery($gallery_id);
+    if (!is_a($gallery, 'PEAR_Error')) {
+        emptyGalleryCheck($gallery);
+    }
+}
+exit;
+
+/**
+ * Check for, and remove any empty galleries that may have been created during
+ * import.
+ *
+ */
+function emptyGalleryCheck($gallery)
+{
+    if ($gallery->hasSubGalleries()) {
+        $children = $GLOBALS['ansel_storage']->listGalleries(PERMS_SHOW, null, $gallery, false);
+        foreach ($children as $child) {
+            // First check all children to see if they are empty...
+            emptyGalleryCheck($child);
+            if (!$child->countImages() && !$child->hasSubGalleries()) {
+                $result = $GLOBALS['ansel_storage']->removeGallery($child);
+                $GLOBALS['cli']->message(sprintf(_("Deleting empty gallery, \"%s\""), $child->get('name')), 'cli.success');
+            }
+
+            // Refresh the gallery values since we mucked around a bit with it
+            $gallery = $GLOBALS['ansel_storage']->getGallery($gallery->getId());
+            // Now that any empty children are removed, see if we are empty
+            if (!$gallery->countImages() && !$gallery->hasSubGalleries()) {
+                $result = $GLOBALS['ansel_storage']->removeGallery($gallery);
+                $GLOBALS['cli']->message(sprintf(_("Deleting empty gallery, \"%s\""), $gallery->get('name')), 'cli.success');
+            }
+        }
+    }
+}
+/**
+ * Read all images from a directory into the currently selected
+ * gallery.
+ *
+ * @param string $dir  The directory to create a gallery for and import.
+ * @param integer $parent  Parent gallery id to attach the new gallery to.
+ *
+ * @return mixed  The gallery_id of the newly created gallery || PEAR_Error
+ */
+function processDirectory($dir, $parent = null)
+{
+    global $cli;
+
+    if (!is_dir($dir)) {
+        $cli->fatal(sprintf(_("\"%s\" is not a directory."), $dir));
+    }
+
+    // Create a gallery for this directory level.
+    $name = basename($dir);
+    $cli->message(sprintf(_("Creating gallery: \"%s\""), $name), 'cli.message');
+    $gallery = $GLOBALS['ansel_storage']->createGallery(array('name' => $name), null, $parent);
+    if (is_a($gallery, 'PEAR_Error')) {
+        $cli->fatal(sprintf(_("The gallery \"%s\" couldn't be created: %s"), $name, $gallery->getMessage()));
+    } else {
+        $cli->message(sprintf(_("The gallery \"%s\" was created successfully."), $name), 'cli.success');
+    }
+
+    // Read all the files into an array.
+    $files = array();
+    $directories = array();
+    $h = opendir($dir);
+    while (false !== ($entry = readdir($h))) {
+        if ($entry == '.' || $entry == '..') {
+            continue;
+        }
+        if (is_dir($dir . '/' . $entry)) {
+            $directories[] = $entry;
+        } else {
+            $files[] = $entry;
+        }
+    }
+    closedir($h);
+
+    if ($files) {
+        chdir($dir);
+
+        // Process each file and upload to the gallery.
+        $added_images = array();
+        foreach ($files as $file) {
+            $image = Ansel::getImageFromFile($dir . '/' . $file);
+            if (is_a($image, 'PEAR_Error')) {
+                $cli->message($image->getMessage(), 'cli.error');
+                continue;
+            }
+
+            $cli->message(sprintf(_("Storing photo \"%s\"..."), $file), 'cli.message');
+            $image_id = $gallery->addImage($image);
+            if (is_a($image_id, 'PEAR_Error')) {
+                $cli->message($image_id->getMessage(), 'cli.error');
+                continue;
+            }
+
+            $added_images[] = $file;
+        }
+
+        $cli->message(sprintf(ngettext("Successfully added %d photo (%s) to gallery \"%s\" from \"%s\".", "Successfully added %d photos (%s) to gallery \"%s\" from \"%s\".", count($added_images)),
+                              count($added_images), join(', ', $added_images), $gallery->get('name'), $dir), 'cli.success');
+    }
+
+    if ($directories) {
+        $cli->message(_("Adding subdirectories:"), 'cli.message');
+        foreach ($directories as $directory) {
+            processDirectory($dir . '/' . $directory, $gallery->id);
+        }
+    }
+
+    return $gallery->getId();
+}
+
+/**
+ * Show the command line arguments that the script accepts.
+ */
+function showHelp()
+{
+    global $cli;
+
+    $cli->writeln(sprintf(_("Usage: %s [OPTIONS]..."), basename(__FILE__)));
+    $cli->writeln();
+    $cli->writeln(_("Mandatory arguments to long options are mandatory for short options too."));
+    $cli->writeln();
+    $cli->writeln(_("-h, --help                   Show this help"));
+    $cli->writeln(_("-d, --dir[=directory]        Recursively add all files from the directory, creating\n                             a gallery for each directory"));
+    $cli->writeln(_("-u, --username[=username]    Horde login username"));
+    $cli->writeln(_("-p, --password[=password]    Horde login password"));
+    $cli->writeln(_("-k, --keep                   Do not delete empty galleries after import is complete."));
+    $cli->writeln();
+}
diff --git a/ansel/scripts/remote_import.php b/ansel/scripts/remote_import.php
new file mode 100755 (executable)
index 0000000..3033ea3
--- /dev/null
@@ -0,0 +1,349 @@
+#!/usr/bin/env php
+<?php
+/**
+* $Horde: ansel/scripts/remote_import.php,v 1.16 2009/06/10 19:57:52 slusarz Exp $
+*
+* This script allows for adding images to an Ansel install using an RPC
+* interface. This script requires Horde's CLI and RPC libraries along with
+* PEAR's Console_Getopt library.  You will need to make sure that those
+* libraries reside somewhere in PHP's include path.
+*
+* See the enclosed file COPYING for license information (GPL). If you
+* did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+*
+* @author Michael J. Rubinsky <mrubinsk@horde.org>
+*/
+
+/* Edit this to include your horde libs if they are not in your path */
+ini_set('include_path', '/var/www/pear' . PATH_SEPARATOR . ini_get('include_path'));
+
+/* Horde autoloading */
+require_once 'Horde/Autoloader.php';
+
+/* Horde_CLI */
+if (!Horde_Cli::runningFromCLI()) {
+    exit("Must be run from the command line\n");
+}
+Horde_Cli::init();
+$cli = &Horde_Cli::singleton();
+
+/* Command line options */
+$ret = Console_Getopt::getopt(Console_Getopt::readPHPArgv(), 'hu:p:g:s:d:kr:zl',
+                              array('help', 'username=', 'password=', 'gallery=', 'slug=', 'dir=', 'keep', 'remotehost=', 'gzip', 'lzf'));
+
+if (is_a($ret, 'PEAR_Error')) {
+    $cli->fatal($ret->getMessage());
+}
+
+/* Show help and exit if no arguments were set. */
+list($opts, $args) = $ret;
+if (!$opts) {
+    showHelp();
+    exit;
+}
+
+/* Delete empty galleries by default */
+$keepEmpties = false;
+
+/* Assume we are creating a new gallery */
+$gallery_id = null;
+$gallery_slug = null;
+$useCompression = 'none';
+
+foreach ($opts as $opt) {
+    list($optName, $optValue) = $opt;
+    switch ($optName) {
+    case 'u':
+    case '--username':
+        $username = $optValue;
+        break;
+
+    case 'p':
+    case '--password':
+        $password = $optValue;
+        break;
+
+    case 'd':
+    case '--dir':
+        $dir = $optValue;
+        break;
+
+    case 'h':
+    case '--help':
+        showHelp();
+        exit;
+
+    case 'k':
+    case '--keep':
+        $keepEmpties = true;
+        break;
+    case 'r':
+    case '--remotehost':
+        $rpc_endpoint = $optValue;
+        break;
+    case 'g':
+    case '--gallery':
+        $gallery_id = $optValue;
+        break;
+    case 's':
+    case '--slug':
+        $gallery_slug = $optValue;
+        break;
+    case 'z':
+    case '--gzip':
+        $useCompression = 'gzip';
+        break;
+    case 'l':
+    case 'lzf':
+        $useCompression = 'lzf';
+        break;
+    }
+}
+
+/* Sanity checks */
+if (!empty($username) && !empty($password)) {
+    $rpc_auth = array(
+        'user' => $username,
+        'pass' => $password);
+} else {
+    $cli->fatal(_("You must specify a valid username and password."));
+}
+
+if (empty($rpc_endpoint)) {
+    $cli->fatal(_("You must specify the url for the remote Horde RPC server."));
+}
+
+if (empty($dir)) {
+    $cli->fatal(_("You must specify a valid directory."));
+}
+
+processDirectory($dir, null, $gallery_id, $gallery_slug, $useCompression);
+
+/**
+ * Check for, and remove any empty galleries that may have been created during
+ * import.
+ */
+function emptyGalleryCheck($gallery)
+{
+    if ($gallery->hasSubGalleries()) {
+        $children = $GLOBALS['ansel_storage']->listGalleries(PERMS_SHOW, null, $gallery, false);
+        foreach ($children as $child) {
+            // First check all children to see if they are empty...
+            emptyGalleryCheck($child);
+            if (!$child->countImages() && !$child->hasSubGalleries()) {
+                $result = $GLOBALS['ansel_storage']->removeGallery($child);
+                $GLOBALS['cli']->message(sprintf(_("Deleting empty gallery, \"%s\""), $child->get('name')), 'cli.success');
+            }
+
+            // Refresh the gallery values since we mucked around a bit with it
+            $gallery = $GLOBALS['ansel_storage']->getGallery($gallery->getId());
+            // Now that any empty children are removed, see if we are empty
+            if (!$gallery->countImages() && !$gallery->hasSubGalleries()) {
+                $result = $GLOBALS['ansel_storage']->removeGallery($gallery);
+                $GLOBALS['cli']->message(sprintf(_("Deleting empty gallery, \"%s\""), $gallery->get('name')), 'cli.success');
+            }
+        }
+    }
+}
+
+/**
+ * Read all images from a directory into the currently selected gallery.
+ *
+ * @param string $dir          The directory to create a gallery for and import.
+ * @param integer $parent      Parent gallery id to attach the new gallery to.
+ * @param integer $gallery_id  Start at this gallery_id.
+ * @param string  $slug        Same as $gallery_id, except use this slug
+ *
+ * @return mixed  The gallery_id of the newly created gallery || PEAR_Error
+ */
+function processDirectory($dir, $parent = null, $gallery_id = null, $slug = null, $compress = 'none')
+{
+    global $cli, $rpc_auth, $rpc_endpoint;
+
+    if (!is_dir($dir)) {
+        $cli->fatal(sprintf(_("\"%s\" is not a directory."), $dir));
+    }
+
+    /* Create a gallery or use an existing one? */
+    if (!empty($gallery_id) || !empty($slug)) {
+        /* Start with an existing gallery */
+        $method = 'images.getGalleries';
+        $params = array(
+            is_null($gallery_id) ? null : array($gallery_id),
+            null,
+            is_null($slug) ? null : array($slug),
+        );
+        $result = Horde_RPC::request('jsonrpc', $rpc_endpoint, $method, $params, $rpc_auth);
+        if (is_a($result, 'PEAR_Error')) {
+            $cli->fatal($result->getMessage());
+        }
+        $result = $result->result;
+        if (is_a($result, 'PEAR_Error')) {
+            $cli->fatal($result->getMessage());
+        }
+        if (empty($result)) {
+            $cli->fatal(sprintf(_("Gallery %s not found."), (empty($slug) ? $gallery_id : $slug)));
+        }
+
+        /* Should have only one here, but jsonrpc returns an object, not array */
+        foreach ($result as $gallery_info) {
+           $name = $gallery_info->attribute_name;
+           $gallery_id = $gallery_info->share_id;
+        }
+        if (empty($name)) {
+            $cli->fatal(sprintf(_("Gallery %s not found."), (empty($slug) ? $gallery_id : $slug)));
+        }
+    } else {
+        /* Creating a new gallery */
+        $name = basename($dir);
+        $cli->message(sprintf(_("Creating gallery: \"%s\""), $name), 'cli.message');
+        $method = 'images.createGallery';
+        $params = array(null, array('name' => $name), null, $parent);
+        $result = Horde_RPC::request('jsonrpc', $rpc_endpoint, $method, $params, $rpc_auth);
+        if (is_a($result, 'PEAR_Error')) {
+            $cli->fatal($result->getMessage());
+        }
+        $gallery_id = $result->result;
+        if (is_a($gallery_id, 'PEAR_Error')) {
+            $cli->fatal(sprintf(_("The gallery \"%s\" couldn't be created: %s"), $name, $gallery_id->getMessage()));
+        } else {
+            $cli->message(sprintf(_("The gallery \"%s\" was created successfully."), $name), 'cli.success');
+        }
+    }
+
+    /* Get the files and directories */
+    $files = array();
+    $directories = array();
+    $h = opendir($dir);
+    while (false !== ($entry = readdir($h))) {
+        if ($entry == '.' ||
+            $entry == '..' ||
+            $entry == 'Thumbs.db' ||
+            $entry == '.DS_Store' ||
+            $entry == '.localized' ||
+            $entry == '__MACOSX' ||
+            strpos($entry, '.') === 1) {
+            continue;
+        }
+
+        if (is_dir($dir . '/' . $entry)) {
+            $directories[] = $entry;
+        } else {
+            $files[] = $entry;
+        }
+    }
+    closedir($h);
+
+    if ($files) {
+        chdir($dir);
+
+        // Process each file and upload to the gallery.
+        $added_images = array();
+        foreach ($files as $file) {
+            $image = getImageFromFile($dir . '/' . $file, $compress);
+            if (is_a($image, 'PEAR_Error')) {
+                $cli->message($image->getMessage(), 'cli.error');
+                continue;
+            }
+
+            $cli->message(sprintf(_("Storing photo \"%s\"..."), $file), 'cli.message');
+            $method = 'images.saveImage';
+            $params = array(null, $gallery_id, $image, false, null, 'binhex', $slug, $compress);
+            $result = Horde_RPC::request('jsonrpc', $rpc_endpoint, $method, $params, $rpc_auth);
+            if (is_a($result, 'PEAR_Error')) {
+                $cli->fatal($result->getMessage());
+            }
+
+            if (!is_a($result, 'stdClass')) {
+                $cli->fatal(sprintf(_("There was an unspecified error. The server returned: %s"), print_r($result, true)));
+            }
+            $image_id = $result->result;
+            if (is_a($image_id, 'PEAR_Error')) {
+                $cli->message($image_id->getMessage(), 'cli.error');
+                continue;
+            }
+
+            $added_images[] = $file;
+        }
+
+        $cli->message(sprintf(ngettext("Successfully added %d photo (%s) to gallery \"%s\" from \"%s\".", "Successfully added %d photos (%s) to gallery \"%s\" from \"%s\".", count($added_images)),
+                              count($added_images), join(', ', $added_images), $name, $dir), 'cli.success');
+    }
+
+    if ($directories) {
+        $cli->message(_("Adding subdirectories:"), 'cli.message');
+        foreach ($directories as $directory) {
+            processDirectory($dir . '/' . $directory, $gallery_id);
+        }
+    }
+
+    return $gallery_id;
+}
+
+/**
+ * Read an image from the filesystem.
+ *
+ * @TODO: pass in location of magic_db?
+ *
+ * @param string $file     The filename of the image.
+ *
+ * @return array  The image data of the file as an array or PEAR_Error
+ */
+function getImageFromFile($file, $compress = 'none')
+{
+    if (!file_exists($file)) {
+        return PEAR::raiseError(sprintf(_("The file \"%s\" doesn't exist."),
+                                $file));
+    }
+
+    global $conf, $cli;
+
+    // Get the mime type of the file (and make sure it's an image).
+    $mime_type = Horde_Mime_Magic::analyzeFile($file, isset($conf['mime']['magic_db']) ? $conf['mime']['magic_db'] : null);
+    if (strpos($mime_type, 'image') === false) {
+        return PEAR::raiseError(sprintf(_("Can't get unknown file type \"%s\"."), $file));
+    }
+
+    if ($compress == 'gzip' && Horde_Util::loadExtension('zlib')) {
+        $data = gzcompress(file_get_contents($file));
+    } elseif ($compress == 'gzip') {
+        $cli->fatal(_("Could not load the gzip extension"));
+    } elseif ($compress == 'lzf' && Horde_Util::loadExtension('lzf')) {
+        $data = lzf_compress(file_get_contents($file));
+    } elseif ($compress == 'lzf') {
+        $cli->fatal(_("Could not load the lzf extension"));
+    } else {
+        $data = file_get_contents($file);
+    }
+
+    $image = array('filename' => basename($file),
+                   'description' => '',
+                   'type' => $mime_type,
+                   'data' => bin2hex($data),
+                   );
+
+    return $image;
+}
+
+
+
+/**
+ * Show the command line arguments that the script accepts.
+ */
+function showHelp()
+{
+    global $cli;
+
+    $cli->writeln(sprintf(_("Usage: %s [OPTIONS]..."), basename(__FILE__)));
+    $cli->writeln();
+    $cli->writeln(_("Mandatory arguments to long options are mandatory for short options too."));
+    $cli->writeln();
+    $cli->writeln(_("-h, --help                   Show this help"));
+    $cli->writeln(_("-d, --dir[=directory]        Recursively add all files from the directory, creating\n                             a gallery for each directory"));
+    $cli->writeln(_("-u, --username[=username]    Horde login username"));
+    $cli->writeln(_("-p, --password[=password]    Horde login password"));
+    $cli->writeln(_("-g, --gallery[=gallery_id]   The gallery id to add directory contents to"));
+    $cli->writeln(_("-s, --slug[=gallery_slug]    The gallery slug to add directory contents to"));
+    //$cli->writeln(_("-k, --keep                   Do not delete empty galleries after import is complete."));
+    $cli->writeln(_("-r, --remotehost[=url]       The url of the remote rpc server."));
+}
diff --git a/ansel/scripts/sql/ansel.pgsql.sql b/ansel/scripts/sql/ansel.pgsql.sql
new file mode 100644 (file)
index 0000000..f1194f9
--- /dev/null
@@ -0,0 +1,143 @@
+-- $Horde: ansel/scripts/sql/ansel.pgsql.sql,v 1.11 2009/07/06 19:55:20 mrubinsk Exp $
+
+CREATE TABLE ansel_images (
+    image_id             INT NOT NULL,
+    gallery_id           INT NOT NULL,
+    image_filename       VARCHAR(255) NOT NULL,
+    image_type           VARCHAR(100) NOT NULL,
+    image_caption        TEXT,
+    image_uploaded_date  INT NOT NULL,
+    image_sort           INT NOT NULL,
+    image_faces          INT NOT NULL,
+    image_original_date  INT NOT NULL,
+    image_latitude VARCHAR(32),
+    image_longitude VARCHAR(32),
+    image_location VARCHAR(256),
+    image_geotag_date   INT,
+--
+    PRIMARY KEY (image_id)
+);
+CREATE INDEX ansel_images_gallery_idx ON ansel_images (gallery_id);
+CREATE INDEX ansel_images_gallery_image_idx ON ansel_images (image_id, gallery_id);
+CREATE INDEX ansel_images_uploaded_idx ON ansel_images (image_uploaded_date);
+CREATE INDEX ansel_images_taken_idx ON ansel_images (image_uploaded_date);
+
+CREATE TABLE ansel_image_attributes (
+    image_id             INT NOT NULL,
+    attr_name            VARCHAR(50) NOT NULL,
+    attr_value           VARCHAR(255),
+--
+    PRIMARY KEY (image_id, attr_name)
+);
+CREATE INDEX ansel_image_attributes_image_idx ON ansel_image_attributes (image_id);
+
+CREATE TABLE ansel_faces (
+    face_id              INT NOT NULL,
+    image_id             INT NOT NULL,
+    gallery_id           INT NOT NULL,
+    face_name            VARCHAR(255) NOT NULL,
+    face_x1              INT NOT NULL,
+    face_y1              INT NOT NULL,
+    face_x2              INT NOT NULL,
+    face_y2              INT NOT NULL,
+    face_signature       BYTEA,
+--
+    PRIMARY KEY  (face_id)
+);
+
+CREATE TABLE ansel_faces_index (
+    face_id INT NOT NULL,
+    index_position INT NOT NULL,
+    index_part BYTEA
+);
+CREATE INDEX ansel_faces_index_face_id_idx ON ansel_faces_index (face_id);
+CREATE INDEX ansel_faces_index_index_part_idx ON ansel_faces_index (index_part);
+CREATE INDEX ansel_faces_index_index_position_idx ON ansel_faces_index (index_position);
+
+CREATE TABLE ansel_shares (
+    share_id INT NOT NULL,
+    share_owner VARCHAR(255) NOT NULL,
+    share_parents VARCHAR(255) NULL,
+    perm_creator SMALLINT NOT NULL,
+    perm_default SMALLINT NOT NULL,
+    perm_guest SMALLINT NOT NULL,
+    share_flags SMALLINT DEFAULT 0 NOT NULL,
+    attribute_name VARCHAR(255) NOT NULL,
+    attribute_desc TEXT,
+    attribute_default INT,
+    attribute_default_type VARCHAR(6),
+    attribute_default_prettythumb TEXT,
+    attribute_style VARCHAR(255),
+    attribute_category VARCHAR(255) DEFAULT '' NOT NULL,
+    attribute_last_modified INT,
+    attribute_date_created INT,
+    attribute_images INT DEFAULT 0 NOT NULL,
+    attribute_has_subgalleries INT DEFAULT 0 NOT NULL,
+    attribute_slug VARCHAR(255),
+    attribute_age INT DEFAULT 0 NOT NULL,
+    attribute_download VARCHAR(255),
+    attribute_passwd VARCHAR(255),
+    attribute_faces INT DEFAULT 0 NOT NULL,
+    attribute_view_mode VARCHAR(255) DEFAULT 'Normal' NOT NULL,
+--
+    PRIMARY KEY (share_id)
+);
+CREATE INDEX ansel_shares_share_owner_idx ON ansel_shares (share_owner);
+CREATE INDEX ansel_shares_perm_creator_idx ON ansel_shares (perm_creator);
+CREATE INDEX ansel_shares_perm_default_idx ON ansel_shares (perm_default);
+CREATE INDEX ansel_shares_perm_guest_idx ON ansel_shares (perm_guest);
+CREATE INDEX ansel_shares_attribute_category_idx ON ansel_shares (attribute_category);
+CREATE INDEX ansel_shares_share_parents_idx ON ansel_shares (share_parents);
+
+CREATE TABLE ansel_shares_groups (
+    share_id INT NOT NULL,
+    group_uid VARCHAR(255) NOT NULL,
+    perm SMALLINT NOT NULL
+);
+CREATE INDEX ansel_shares_groups_share_id_idx ON ansel_shares_groups (share_id);
+CREATE INDEX ansel_shares_groups_group_uid_idx ON ansel_shares_groups (group_uid);
+CREATE INDEX ansel_shares_groups_perm_idx ON ansel_shares_groups (perm);
+
+
+CREATE TABLE ansel_shares_users (
+    share_id INT NOT NULL,
+    user_uid VARCHAR(255) NOT NULL,
+    perm SMALLINT NOT NULL
+);
+CREATE INDEX ansel_shares_users_share_id_idx ON ansel_shares_users (share_id);
+CREATE INDEX ansel_shares_users_user_uid_idx ON ansel_shares_users (user_uid);
+CREATE INDEX ansel_shares_users_perm_idx ON ansel_shares_users (perm);
+CREATE TABLE ansel_tags (
+    tag_id               INT NOT NULL,
+    tag_name             VARCHAR(255) NOT NULL,
+
+    PRIMARY KEY (tag_id)
+);
+
+CREATE TABLE ansel_galleries_tags (
+    gallery_id           INT NOT NULL,
+    tag_id               INT NOT NULL,
+--
+    PRIMARY KEY (gallery_id, tag_id)
+);
+
+CREATE TABLE ansel_images_tags (
+    image_id             INT NOT NULL,
+    tag_id               INT NOT NULL,
+--
+    PRIMARY KEY (image_id, tag_id)
+);
+
+CREATE TABLE ansel_images_geolocation (
+    image_id INT NOT NULL,
+    image_latitude varchar(32),
+    image_longitude varchar(32),
+
+    PRIMARY KEY (image_id)
+);
+
+CREATE TABLE ansel_datatree_transition (
+    datatree_id          INT NOT NULL,
+    image_id             INT NOT NULL,
+    vfsname              VARCHAR(255) NOT NULL
+);
diff --git a/ansel/scripts/sql/ansel.sql b/ansel/scripts/sql/ansel.sql
new file mode 100644 (file)
index 0000000..22fbc42
--- /dev/null
@@ -0,0 +1,147 @@
+-- $Horde: ansel/scripts/sql/ansel.sql,v 1.38 2009/07/07 17:13:50 mrubinsk Exp $
+
+CREATE TABLE ansel_images (
+    image_id             INT NOT NULL,
+    gallery_id           INT NOT NULL,
+    image_filename       VARCHAR(255) NOT NULL,
+    image_type           VARCHAR(100) NOT NULL,
+    image_caption        TEXT,
+    image_uploaded_date  INT NOT NULL,
+    image_sort           INT NOT NULL,
+    image_faces          INT DEFAULT 0 NOT NULL,
+    image_original_date  INT NOT NULL,
+    image_latitude VARCHAR(32),
+    image_longitude VARCHAR(32),
+    image_location VARCHAR(256),
+    image_geotag_date   INT,
+--
+    PRIMARY KEY (image_id)
+);
+CREATE INDEX ansel_images_gallery_idx ON ansel_images (gallery_id);
+CREATE INDEX ansel_images_gallery_image_idx ON ansel_images (image_id, gallery_id);
+CREATE INDEX ansel_images_uploaded_idx ON ansel_images (image_uploaded_date);
+CREATE INDEX ansel_images_taken_idx ON ansel_images (image_original_date);
+
+CREATE TABLE ansel_image_attributes (
+    image_id             INT NOT NULL,
+    attr_name            VARCHAR(50) NOT NULL,
+    attr_value           VARCHAR(255),
+--
+    PRIMARY KEY (image_id, attr_name)
+);
+CREATE INDEX ansel_image_attributes_image_idx ON ansel_image_attributes (image_id);
+
+CREATE TABLE ansel_faces (
+    face_id              INT NOT NULL,
+    image_id             INT NOT NULL,
+    gallery_id           INT NOT NULL,
+    face_name            VARCHAR(255) NOT NULL,
+    face_x1              INT NOT NULL,
+    face_y1              INT NOT NULL,
+    face_x2              INT NOT NULL,
+    face_y2              INT NOT NULL,
+    face_signature       BLOB,
+--
+    PRIMARY KEY  (face_id)
+);
+
+CREATE INDEX ansel_faces_index_index_image_idx ON ansel_faces (image_id);
+CREATE INDEX ansel_faces_index_index_gallery_idx ON ansel_faces (gallery_id);
+
+CREATE TABLE ansel_faces_index (
+    face_id INT NOT NULL,
+    index_position INT NOT NULL,
+    index_part BLOB
+);
+CREATE INDEX ansel_faces_index_face_id_idx ON ansel_faces_index (face_id);
+CREATE INDEX ansel_faces_index_index_part_idx ON ansel_faces_index (index_part (30));
+CREATE INDEX ansel_faces_index_index_position_idx ON ansel_faces_index (index_position);
+
+CREATE TABLE ansel_shares (
+    share_id INT NOT NULL,
+    share_owner VARCHAR(255) NOT NULL,
+    share_parents VARCHAR(255) NULL,
+    perm_creator SMALLINT NOT NULL,
+    perm_default SMALLINT NOT NULL,
+    perm_guest SMALLINT NOT NULL,
+    share_flags SMALLINT DEFAULT 0 NOT NULL,
+    attribute_name VARCHAR(255) NOT NULL,
+    attribute_desc TEXT,
+    attribute_default INT,
+    attribute_default_type VARCHAR(6),
+    attribute_default_prettythumb TEXT,
+    attribute_style VARCHAR(255),
+    attribute_category VARCHAR(255) DEFAULT '' NOT NULL,
+    attribute_last_modified INT,
+    attribute_date_created INT,
+    attribute_images INT DEFAULT 0 NOT NULL,
+    attribute_has_subgalleries INT DEFAULT 0 NOT NULL,
+    attribute_slug VARCHAR(255),
+    attribute_age INT DEFAULT 0 NOT NULL,
+    attribute_download VARCHAR(255),
+    attribute_passwd VARCHAR(255),
+    attribute_faces INT DEFAULT 0 NOT NULL,
+    attribute_view_mode VARCHAR(255) DEFAULT 'Normal' NOT NULL,
+--
+    PRIMARY KEY (share_id)
+);
+CREATE INDEX ansel_shares_share_owner_idx ON ansel_shares (share_owner);
+CREATE INDEX ansel_shares_perm_creator_idx ON ansel_shares (perm_creator);
+CREATE INDEX ansel_shares_perm_default_idx ON ansel_shares (perm_default);
+CREATE INDEX ansel_shares_perm_guest_idx ON ansel_shares (perm_guest);
+CREATE INDEX ansel_shares_attribute_category_idx ON ansel_shares (attribute_category);
+CREATE INDEX ansel_shares_share_parents_idx ON ansel_shares (share_parents);
+
+CREATE TABLE ansel_shares_groups (
+    share_id INT NOT NULL,
+    group_uid INT NOT NULL,
+    perm SMALLINT NOT NULL
+);
+CREATE INDEX ansel_shares_groups_share_id_idx ON ansel_shares_groups (share_id);
+CREATE INDEX ansel_shares_groups_group_uid_idx ON ansel_shares_groups (group_uid);
+CREATE INDEX ansel_shares_groups_perm_idx ON ansel_shares_groups (perm);
+
+
+CREATE TABLE ansel_shares_users (
+    share_id INT NOT NULL,
+    user_uid VARCHAR(255) NOT NULL,
+    perm SMALLINT NOT NULL
+);
+CREATE INDEX ansel_shares_users_share_id_idx ON ansel_shares_users (share_id);
+CREATE INDEX ansel_shares_users_user_uid_idx ON ansel_shares_users (user_uid);
+CREATE INDEX ansel_shares_users_perm_idx ON ansel_shares_users (perm);
+CREATE TABLE ansel_tags (
+    tag_id               INT NOT NULL,
+    tag_name             VARCHAR(255) NOT NULL,
+
+    PRIMARY KEY (tag_id)
+);
+
+CREATE TABLE ansel_galleries_tags (
+    gallery_id           INT NOT NULL,
+    tag_id               INT NOT NULL,
+--
+    PRIMARY KEY (gallery_id, tag_id)
+);
+
+CREATE TABLE ansel_images_tags (
+    image_id             INT NOT NULL,
+    tag_id               INT NOT NULL,
+--
+    PRIMARY KEY (image_id, tag_id)
+);
+
+CREATE TABLE ansel_images_geolocation (
+    image_id INT NOT NULL,
+    image_latitude VARCHAR(32),
+    image_longitude VARCHAR(32),
+    image_location VARCHAR(256),
+--
+    PRIMARY KEY (image_id)
+);
+
+CREATE TABLE ansel_datatree_transition (
+    datatree_id          INT NOT NULL,
+    image_id             INT NOT NULL,
+    vfsname              VARCHAR(255) NOT NULL
+);
diff --git a/ansel/scripts/upgrades/1.0_to_1.1.php b/ansel/scripts/upgrades/1.0_to_1.1.php
new file mode 100644 (file)
index 0000000..eb8bb4a
--- /dev/null
@@ -0,0 +1,59 @@
+#!/usr/bin/env php
+<?php
+/**
+ * Correct geolocation data
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ */
+@define('AUTH_HANDLER', true);
+@define('HORDE_BASE', dirname(__FILE__) . '/../../../');
+@define('ANSEL_BASE', HORDE_BASE . '/ansel');
+
+// Do CLI checks and environment setup first.
+require_once HORDE_BASE . '/lib/core.php';
+require_once 'Horde/CLI.php';
+
+// Make sure no one runs this from the web.
+if (!Horde_CLI::runningFromCLI()) {
+    exit("Must be run from the command line\n");
+}
+
+// Load the CLI environment.
+Horde_CLI::init();
+$cli = &Horde_CLI::singleton();
+
+require_once ANSEL_BASE . '/lib/base.php';
+
+// First update the tables
+$alter = array("ALTER TABLE ansel_images ADD COLUMN image_latitude VARCHAR(32) NOT NULL DEFAULT ''",
+               "ALTER TABLE ansel_images ADD COLUMN image_longitude VARCHAR(32) NOT NULL DEFAULT ''",
+               "ALTER TABLE ansel_images ADD COLUMN image_location VARCHAR(255) NOT NULL DEFAULT ''",
+               "ALTER TABLE ansel_images ADD COLUMN image_geotag_date INT NOT NULL DEFAULT 0");
+
+foreach ($alter as $sql) {
+    $cli->message(sprintf("Executing %s", $sql));
+    $ansel_db->exec($sql);
+}
+
+$sql = 'SELECT image_id, image_latitude, image_longitude FROM ansel_images_geolocation;';
+$results = $ansel_db->queryAll($sql, null, MDB2_FETCHMODE_ASSOC);
+$sql = $ansel_db->prepare('UPDATE ansel_images SET image_latitude = ?, image_longitude = ? WHERE image_id = ?');
+foreach ($results as $image) {
+    // Clean up from a bug in Exifer
+    if (strlen(trim($image['image_latitude'])) <= 1 || strlen(trim($image['image_longitude'])) <= 1) {
+        $cli->message(sprintf("Erroneous geoloction data for Image %d deleted", $image['image_id']), 'cli.message');
+        $ansel_db->query('DELETE FROM ansel_images_geolocation WHERE image_id = ' . $image['image_id']);
+    } else {
+        $image['image_latitude'] = (strpos($image['image_latitude'], 'S') !== false ? '-' : '') . $image['image_latitude'];
+        $image['image_latitude'] = str_replace(array('N', 'S'), array('', ''), $image['image_latitude']);
+        $image['image_longitude'] = (strpos($image['image_longitude'], 'W') !== false ? '-' : '') . $image['image_longitude'];
+        $image['image_longitude'] = str_replace(array('E', 'W'), array('', ''), $image['image_longitude']);
+        $cli->message(sprintf("Image %d updated. %s - %s", $image['image_id'], $image['image_latitude'], $image['image_longitude']), 'cli.message');
+        $sql->execute(array($image['image_latitude'], $image['image_longitude'], $image['image_id']));
+    }
+}
+
+$cli->message('Done.', 'cli.success');
diff --git a/ansel/scripts/upgrades/2008-06-04-faces.sql b/ansel/scripts/upgrades/2008-06-04-faces.sql
new file mode 100755 (executable)
index 0000000..3ba6b4b
--- /dev/null
@@ -0,0 +1,25 @@
+CREATE TABLE ansel_faces (
+    face_id              INT NOT NULL,
+    image_id             INT NOT NULL,
+    gallery_id           INT NOT NULL,
+    face_name            VARCHAR(255) NOT NULL,
+    face_x1              INT NOT NULL,
+    face_y1              INT NOT NULL,
+    face_x2              INT NOT NULL,
+    face_y2              INT NOT NULL,
+    face_signature       BLOB,
+--
+    PRIMARY KEY  (face_id)
+);
+
+CREATE TABLE ansel_faces_index (
+    face_id INT NOT NULL,
+    index_position INT NOT NULL,
+    index_part BLOB
+);
+CREATE INDEX ansel_faces_index_face_id_idx ON ansel_faces_index (face_id);
+CREATE INDEX ansel_faces_index_index_part_idx ON ansel_faces_index (index_part (30));
+CREATE INDEX ansel_faces_index_index_position_idx ON ansel_faces_index (index_position);
+
+ALTER TABLE ansel_shares ADD COLUMN attribute_faces INT NOT NULL;
+ALTER TABLE ansel_images ADD COLUMN image_faces INT NOT NULL;
\ No newline at end of file
diff --git a/ansel/scripts/upgrades/2008-06-17_fix_varchar_lengths.sql b/ansel/scripts/upgrades/2008-06-17_fix_varchar_lengths.sql
new file mode 100755 (executable)
index 0000000..0580aaf
--- /dev/null
@@ -0,0 +1,4 @@
+ALTER TABLE ansel_shares CHANGE share_owner share_owner VARCHAR(255);
+ALTER TABLE ansel_shares CHANGE attribute_style attribute_style VARCHAR(255);
+ALTER TABLE ansel_shares_users CHANGE user_uid user_uid VARCHAR(255);
+ALTER TABLE ansel_faces CHANGE face_name face_name VARCHAR(255) NOT NULL;
\ No newline at end of file
diff --git a/ansel/scripts/upgrades/2008-09-13_add_image_original_date.sql b/ansel/scripts/upgrades/2008-09-13_add_image_original_date.sql
new file mode 100644 (file)
index 0000000..31435c2
--- /dev/null
@@ -0,0 +1,8 @@
+ALTER TABLE ansel_images ADD COLUMN image_original_date INT NOT NULL;
+ALTER TABLE ansel_images CHANGE COLUMN image_uploaded image_uploaded_date  INT NOT NULL;
+
+CREATE INDEX ansel_images_original_idx ON ansel_images (image_original_date);
+ALTER TABLE ansel_images DROP index ansel_images_uploaded_idx;
+CREATE INDEX ansel_images_uploaded_date_idx ON ansel_images (image_uploaded_date);
+
+ALTER TABLE ansel_shares ADD COLUMN attribute_view_mode INT NOT NULL;
\ No newline at end of file
diff --git a/ansel/scripts/upgrades/2008-09-16_add_original_date_values.php b/ansel/scripts/upgrades/2008-09-16_add_original_date_values.php
new file mode 100755 (executable)
index 0000000..b1ff722
--- /dev/null
@@ -0,0 +1,48 @@
+#!/usr/bin/env php
+<?php
+/**
+* $Horde: ansel/scripts/upgrades/2008-09-16_add_original_date_values.php,v 1.3 2009/07/30 19:50:16 mrubinsk Exp $
+*
+* Bare bones script to populate the ansel_images.image_original_date field with
+* either the exif DateTimeOriginal field, or the
+* ansel_images.image_uploaded_date value if the exif field is not present.
+*
+* See the enclosed file COPYING for license information (GPL). If you
+* did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+*
+* @author Michael J. Rubinsky <mrubinsk@horde.org>
+*/
+@define('HORDE_BASE', dirname(__FILE__) . '/../../../');
+@define('ANSEL_BASE', HORDE_BASE . '/ansel');
+
+// Do CLI checks and environment setup first.
+require_once HORDE_BASE . '/lib/core.php';
+
+// Make sure no one runs this from the web.
+if (!Horde_Cli::runningFromCLI()) {
+    exit("Must be run from the command line\n");
+}
+
+// Load the CLI environment.
+Horde_Cli::init();
+$cli = &Horde_Cli::singleton();
+
+$ansel_authentication = 'none';
+require_once ANSEL_BASE . '/lib/base.php';
+
+$sql = 'SELECT image_id, image_original_date, image_uploaded_date FROM ansel_images';
+$results = $ansel_db->queryAll($sql, null, MDB2_FETCHMODE_ASSOC);
+foreach ($results as $image) {
+    $sql = "SELECT attr_value FROM ansel_image_attributes WHERE attr_name='DateTimeOriginal' AND image_id = " . (int)$image['image_id'];
+    $datetime = $ansel_db->queryOne($sql);
+    if (!$datetime) {
+        $datetime = $image['image_uploaded_date'];
+    }
+    $sql = 'UPDATE ansel_images SET image_original_date = ' . (int)$datetime . ' WHERE image_id = ' . (int)$image['image_id'];
+    $result = $ansel_db->exec($sql);
+    if (is_a($result, 'PEAR_Error')) {
+        $cli->fatal($result->getMessage());
+    }
+    $cli->message(sprintf("Image %d updated.", $image['image_id']), 'cli.message');
+}
+$cli->message('Done.', 'cli.success');
diff --git a/ansel/scripts/upgrades/2008-09-23_fix_group_uid.sql b/ansel/scripts/upgrades/2008-09-23_fix_group_uid.sql
new file mode 100644 (file)
index 0000000..d921605
--- /dev/null
@@ -0,0 +1,2 @@
+ALTER TABLE ansel_shares_groups CHANGE group_uid group_uid VARCHAR(255);
+
diff --git a/ansel/scripts/upgrades/2008-12-5_add_geolocation_tables.sql b/ansel/scripts/upgrades/2008-12-5_add_geolocation_tables.sql
new file mode 100644 (file)
index 0000000..e69bf93
--- /dev/null
@@ -0,0 +1,9 @@
+-- $Horde: ansel/scripts/upgrades/2008-12-5_add_geolocation_tables.sql,v 1.1 2008/12/05 19:42:23 mrubinsk Exp $
+
+CREATE TABLE ansel_images_geolocation (
+    image_id INT NOT NULL,
+    image_latitude varchar(32),
+    image_longitude varchar(32),
+
+    PRIMARY KEY (image_id)
+);
diff --git a/ansel/scripts/upgrades/2009-01-10_fix_view_mode.sql b/ansel/scripts/upgrades/2009-01-10_fix_view_mode.sql
new file mode 100644 (file)
index 0000000..6b14e0d
--- /dev/null
@@ -0,0 +1,5 @@
+ALTER TABLE ansel_shares CHANGE attribute_view_mode attribute_view_mode VARCHAR(255) DEFAULT 'Normal' NOT NULL;
+
+UPDATE ansel_shares SET attribute_view_mode = 'Normal' WHERE attribute_view_mode = '0';
+UPDATE ansel_shares SET attribute_view_mode = 'Date' WHERE attribute_view_mode = '1';
+
diff --git a/ansel/scripts/upgrades/2009-04-14_fix_view_mode.pgsql.sql b/ansel/scripts/upgrades/2009-04-14_fix_view_mode.pgsql.sql
new file mode 100644 (file)
index 0000000..2c9f094
--- /dev/null
@@ -0,0 +1,5 @@
+ALTER TABLE ansel_shares ALTER attribute_view_mode TYPE VARCHAR(255);
+ALTER TABLE ansel_shares ALTER attribute_view_mode SET DEFAULT 'Normal';
+UPDATE ansel_shares SET attribute_view_mode = 'Normal' WHERE attribute_view_mode = '0';
+UPDATE ansel_shares SET attribute_view_mode = 'Date' WHERE attribute_view_mode = '1';
+ALTER TABLE ansel_shares ALTER attribute_view_mode SET NOT NULL;
diff --git a/ansel/scripts/upgrades/2009-06-14_fix_geolocation_values.php b/ansel/scripts/upgrades/2009-06-14_fix_geolocation_values.php
new file mode 100755 (executable)
index 0000000..9453599
--- /dev/null
@@ -0,0 +1,50 @@
+#!/usr/bin/env php
+<?php
+/**
+ * Correct geolocation data
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ */
+@define('HORDE_BASE', dirname(__FILE__) . '/../../../');
+@define('ANSEL_BASE', HORDE_BASE . '/ansel');
+
+// Do CLI checks and environment setup first.
+require_once HORDE_BASE . '/lib/core.php';
+
+// Make sure no one runs this from the web.
+if (!Horde_Cli::runningFromCLI()) {
+    exit("Must be run from the command line\n");
+}
+
+// Load the CLI environment.
+Horde_Cli::init();
+$cli = &Horde_Cli::singleton();
+
+$ansel_authentication = 'none';
+require_once ANSEL_BASE . '/lib/base.php';
+
+$sql = 'SELECT image_id, image_latitude, image_longitude FROM ansel_images_geolocation;';
+$results = $ansel_db->queryAll($sql, null, MDB2_FETCHMODE_ASSOC);
+$sql = $ansel_db->prepare('UPDATE ansel_images_geolocation SET image_latitude = ?, image_longitude = ? WHERE image_id = ?');
+foreach ($results as $image) {
+    // Clean up from a bug in Exifer
+    if (strlen(trim($image['image_latitude']) <= 1) || strlen(trim($image['image_longitude']) <= 1)) {
+        $cli->message(sprintf("Erroneous geoloction data for Image %d deleted", $image['image_id']), 'cli.message');
+        $ansel_db->query('DELETE FROM ansel_images_geolocation WHERE image_id = ' . $image['image_id']);
+    } else {
+        $image['image_latitude'] = (strpos($image['image_latitude'], 'S') !== false ? '-' : '') . $image['image_latitude'];
+        $image['image_latitude'] = str_replace(array('N', 'S'), array('', ''), $image['image_latitude']);
+        $image['image_longitude'] = (strpos($image['image_longitude'], 'W') !== false ? '-' : '') . $image['image_longitude'];
+        $image['image_longitude'] = str_replace(array('E', 'W'), array('', ''), $image['image_longitude']);
+        $cli->message(sprintf("Image %d updated. %s - %s", $image['image_id'], $image['image_latitude'], $image['image_longitude']), 'cli.message');
+        $sql->execute(array($image['image_latitude'], $image['image_longitude'], $image['image_id']));
+    }
+}
+
+// Add the location column.
+$sql = 'ALTER TABLE ansel_images_geolocation ADD COLUMN image_location VARCHAR(255)';
+$ansel_db->exec($sql);
+$cli->message('Done.', 'cli.success');
\ No newline at end of file
diff --git a/ansel/scripts/upgrades/2009-06-22_add_geolocation_fields.sql b/ansel/scripts/upgrades/2009-06-22_add_geolocation_fields.sql
new file mode 100644 (file)
index 0000000..b915239
--- /dev/null
@@ -0,0 +1,3 @@
+ALTER TABLE ansel_images ADD COLUMN image_latitude VARCHAR(32) NOT NULL DEFAULT '';
+ALTER TABLE ansel_images ADD COLUMN image_longitude VARCHAR(32) NOT NULL DEFAULT '';
+ALTER TABLE ansel_images ADD COLUMN image_location VARCHAR(255) NOT NULL DEFAULT '';
diff --git a/ansel/scripts/upgrades/2009-06-22_move_geolocation_values.php b/ansel/scripts/upgrades/2009-06-22_move_geolocation_values.php
new file mode 100644 (file)
index 0000000..d9d73df
--- /dev/null
@@ -0,0 +1,37 @@
+#!/usr/bin/env php
+<?php
+/**
+ * Correct geolocation data
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ */
+@define('HORDE_BASE', dirname(__FILE__) . '/../../../');
+@define('ANSEL_BASE', HORDE_BASE . '/ansel');
+
+// Do CLI checks and environment setup first.
+require_once HORDE_BASE . '/lib/core.php';
+
+// Make sure no one runs this from the web.
+if (!Horde_Cli::runningFromCLI()) {
+    exit("Must be run from the command line\n");
+}
+
+// Load the CLI environment.
+Horde_Cli::init();
+$cli = &Horde_Cli::singleton();
+
+$ansel_authentication = 'none';
+require_once ANSEL_BASE . '/lib/base.php';
+
+$sql = 'SELECT image_id, image_latitude, image_longitude FROM ansel_images_geolocation;';
+$results = $ansel_db->queryAll($sql, null, MDB2_FETCHMODE_ASSOC);
+$sql = $ansel_db->prepare('UPDATE ansel_images SET image_latitude = ?, image_longitude = ? WHERE image_id = ?');
+foreach ($results as $image) {
+    $cli->message(sprintf("Image %d updated. %s - %s", $image['image_id'], $image['image_latitude'], $image['image_longitude']), 'cli.message');
+    $sql->execute(array($image['image_latitude'], $image['image_longitude'], $image['image_id']));
+}
+
+$cli->message('Done.', 'cli.success');
diff --git a/ansel/scripts/upgrades/2009-07-06_add_geolocation_timestamp.sql b/ansel/scripts/upgrades/2009-07-06_add_geolocation_timestamp.sql
new file mode 100644 (file)
index 0000000..ecc0034
--- /dev/null
@@ -0,0 +1 @@
+ALTER TABLE ansel_images ADD COLUMN image_geotag_date INT;
diff --git a/ansel/templates/captions/captions.inc b/ansel/templates/captions/captions.inc
new file mode 100644 (file)
index 0000000..6f752ab
--- /dev/null
@@ -0,0 +1,57 @@
+<form name="captions" action="captions.php" method="post">
+<?php Horde_Util::pformInput() ?>
+<input type="hidden" name="gallery" value="<?php echo htmlspecialchars($galleryId) ?>" />
+<input type="hidden" name="do" value="save" />
+<input type="hidden" name="year" value="<?php echo (empty($date['year']) ? 0 : $date['year']) ?>" />
+<input type="hidden" name="month" value="<?php echo (empty($date['month']) ? 0 : $date['month']) ?>" />
+<input type="hidden" name="day" value="<?php echo (empty($date['day']) ? 0 : $date['day']) ?>" />
+
+<h1 class="header">
+ <?php echo htmlspecialchars($title, ENT_COMPAT, Horde_Nls::getCharset()) ?>
+</h1>
+
+<?php if ($gallery->get('desc')): ?>
+<p class="box">
+ <?php echo Horde_Text_Filter::filter($gallery->get('desc'), 'text2html', array('parselevel' => Horde_Text_Filter_Text2html::MICRO)) ?>
+</p>
+<?php endif; ?>
+
+<table cellspacing="0" width="100%">
+ <tr>
+  <td width="100%" class="text">
+<?php
+if ($gallery->countImages()) {
+    echo '<table cellpadding="5" cellspacing="0" class="item" width="100%"><tr class="text">';
+
+    $count = 0;
+    $images = $gallery->getImages();
+    $style = $gallery->getStyle();
+
+    foreach ($images as $image) {
+        $thumb_url = Ansel::getImageUrl($image->id, $style['thumbstyle'], false, $style['name']);
+
+        echo '<td width="10%" align="right" valign="top">';
+        echo '<img src="' . $thumb_url . '" alt="' . htmlspecialchars($image->filename) . '" />' .
+            '</td><td width="40%" valign="top"><textarea name="img' . $image->id . '" rows="5" cols="25">' .
+            htmlspecialchars($image->caption, ENT_COMPAT, Horde_Nls::getCharset()) . '</textarea></td>';
+
+        $count++;
+        if (($count % 2) == 0) {
+            echo '</tr><tr class="' . (($count % 4 == 0) ? 'text' : 'item0') . '">';
+        }
+    }
+
+    $remainder = count($images) % 2;
+    for ($i = 0; $i < $remainder; $i++) {
+        $count++;
+        echo '<td>&nbsp;</td><td>&nbsp;</td>';
+    }
+
+    echo '</tr><tr class="' . (($count % 4 == 0) ? 'text' : 'item0') . '"><td align="center" colspan="4"><input type="submit" value="' . _("Save Captions") . '" class="button" /></tr></table>';
+} else {
+    echo '<em>' . _("There are no photos in this gallery.") . '</em>';
+} ?>
+  </td>
+ </tr>
+</table>
+</form>
diff --git a/ansel/templates/common-header.inc b/ansel/templates/common-header.inc
new file mode 100644 (file)
index 0000000..76dbb79
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+if (isset($language)) {
+    header('Content-type: text/html; charset=' . Horde_Nls::getCharset());
+    header('Vary: Accept-Language');
+}
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "DTD/xhtml1-transitional.dtd">
+<!--  Ansel: Copyright 2001-2009 The Horde Project.  Ansel is under the GPL.   -->
+<!-- Horde Project: http://www.horde.org/ | Ansel: http://www.horde.org/ansel/ -->
+<!--        GNU Public License: http://www.fsf.org/copyleft/gpl.html           -->
+<?php echo !empty($language) ? '<html lang="' . strtr($language, '_', '-') . '">' : '<html>' ?>
+<head>
+<?php
+
+$page_title = $registry->get('name');
+if (!empty($title)) $page_title .= ' :: ' . $title;
+if (!empty($refresh_time) && ($refresh_time > 0) && !empty($refresh_url)) {
+    echo "<meta http-equiv=\"refresh\" content=\"$refresh_time;url=$refresh_url\">\n";
+}
+Horde::includeScriptFiles();
+?>
+<title><?php echo htmlspecialchars($page_title, ENT_COMPAT, Horde_Nls::getCharset()) ?></title>
+<link href="<?php echo $GLOBALS['registry']->getImageDir() ?>/favicon.ico" rel="SHORTCUT ICON" />
+<link rel="SHORTCUT ICON" href="<?php echo $GLOBALS['registry']->getImageDir() ?>/favicon.ico" />
+<?php echo Ansel::stylesheetLinks() ?>
+<?php echo Horde::wrapInlineScript(Ansel::initJSVars());?>
+<?php Horde::outputInlineScript(); ?>
+</head>
+
+<body<?php if ($bc = Horde_Util::nonInputVar('bodyClass')) echo ' class="' . $bc . '"' ?><?php if ($bi = Horde_Util::nonInputVar('bodyId')) echo ' id="' . $bi . '"'; ?>>
diff --git a/ansel/templates/faces/custom.inc b/ansel/templates/faces/custom.inc
new file mode 100755 (executable)
index 0000000..c0d8696
--- /dev/null
@@ -0,0 +1,57 @@
+<h1 class="header"><?php echo $title ?></h1>
+<?php Ansel::attachStylesheet('cropper.css', true);?>
+
+<script type="text/javascript">
+
+function onEndCrop(coords, dimensions) {
+    $('x1').value = coords.x1;
+    $('y1').value = coords.y1;
+    $('x2').value = coords.x2;
+    $('y2').value = coords.y2;
+}
+
+Event.observe(
+    window,
+    'load',
+    function() {
+        new Cropper.ImgWithPreview(
+            'faceImage',
+            {
+                minWidth: 50,
+                minHeight: 50,
+                ratioDim: {
+                    x: 50,
+                    y: 50
+                },
+                onEndCrop: onEndCrop,
+                <?php if ($x1): ?>
+                onloadCoords: { x1: <?php echo $x1 ?>, y1: <?php echo $y1 ?>, x2: <?php echo $x2 ?>, y2: <?php echo $y2 ?> },
+                <?php endif; ?>
+                previewWrap: 'previewArea'
+            }
+        )
+    }
+);
+
+</script>
+<div style="float:left;width:70%;">
+  <img src="<?php echo Ansel::getImageUrl($image_id, 'screen') ?>" alt="<?php echo htmlspecialchars($image->filename) ?>" id="faceImage" />
+</div>
+<div style="float:left;width:25%;text-align:center;">
+   <form method="post" action="<?php echo $form_post ?>">
+      <input type="hidden" name="image_id" id="image_id" value="<?php echo (int)$image->id ?>" />
+      <input type="hidden" name="face_id" id="face_id" value="<?php echo (int)$face_id ?>" />
+      <input type="hidden" name="gallery_id" id="gallery_id" value="<?php echo (int)$image->gallery ?>" />
+      <input type="hidden" name="x1" id="x1" value="<?php echo $x1 ?>" />
+      <input type="hidden" name="y1" id="y1" value="<?php echo $y1 ?>" />
+      <input type="hidden" name="x2" id="x2" value="<?php echo $x2 ?>" />
+      <input type="hidden" name="y2" id="y2" value="<?php echo $y2 ?>" />
+      <div class="control">
+      <div id="previewArea"></div>
+      <?php echo _("Person in photo") ?>:<input type="text" name="name" id="name" value="<?php echo htmlspecialchars($name) ?>" /><br />
+       <input type="submit" name="submit" id="submit" value="<?php echo _("Save") ?>" />
+       <input type="submit" name="submit" id="submit" value="<?php echo _("Cancel") ?>" />
+       <input type="reset" name="reset" id="reset" value="<?php echo _("Reset") ?>" />
+      </div>
+   </form>
+</div>
diff --git a/ansel/templates/faces/define.inc b/ansel/templates/faces/define.inc
new file mode 100755 (executable)
index 0000000..ee7b87d
--- /dev/null
@@ -0,0 +1,69 @@
+<?php echo $tabs->render('image'); ?>
+<h1 class="header"><?php echo $title ?></h1>
+<link href="<?php echo $registry->getImageDir() ?>/../cropper.css" rel="stylesheet" type="text/css" />
+
+<script type="text/javascript">
+
+function onEndCrop(coords, dimensions ) {
+    $('x1').value = coords.x1;
+    $('y1').value = coords.y1;
+    $('x2').value = coords.x2;
+    $('y2').value = coords.y2;
+    $('width').value = dimensions.width;
+    $('height').value = dimensions.height;
+}
+
+window.observe('load', function() {
+    new Cropper.ImgWithPreview(
+        'faceImage',
+        { minWidth: <?php echo $conf['thumbnail']['width'] ?>,
+          minHeight: <?php echo $conf['thumbnail']['height'] ?>,
+          onEndCrop: onEndCrop,
+          <?php if ($x1): ?>
+          displayOnInit: true,
+          onloadCoords: { x1: <?php echo $x1 ?>, y1: <?php echo $y1 ?>, x2: <?php echo $x2 ?>, y2: <?php echo $y2 ?> },
+          <?php endif; ?>
+          previewWrap: 'previewArea' }
+    )
+});
+
+</script>
+
+<div><img src="<?php echo Horde::applicationUrl('faces/search/img.php'); ?>" alt="[icon]" id="faceImage" /></div>
+
+<form method="post" action="<?php echo Horde::applicationUrl('faces/search/image_save.php'); ?>">
+
+<div class="control" style="width: <?php echo $conf['screen']['height'] ?>px">
+ <input type="submit" name="submit" id="submit" value="<?php echo _("Search") ?>" />
+ <input type="submit" name="submit" id="submit" value="<?php echo _("Cancel") ?>" />
+ <input type="reset" name="reset" id="reset" value="<?php echo _("Reset") ?>" />
+</div>
+<table style="width: <?php echo $conf['screen']['height'] ?>px">
+ <tr valign="top">
+  <td style="text-align: center; width: 33%"><div id="previewArea"></div></td>
+  <td>
+   <table class="striped">
+    <tr>
+     <td>x1:</td>
+     <td><input type="text" name="x1" id="x1" size="4" readonly="readonly" value="<?php echo $x1 ?>" /></td>
+     <td>y1:</td>
+     <td><input type="text" name="y1" id="y1" size="4" readonly="readonly" value="<?php echo $y1 ?>"/></td>
+    </tr>
+    <tr>
+     <td>x2:</td>
+     <td><input type="text" name="x2" id="x2" size="4" readonly="readonly" value="<?php echo $x2 ?>"/></td>
+     <td>y2:</td>
+     <td><input type="text" name="y2" id="y2" size="4" readonly="readonly" value="<?php echo $y2 ?>"/></td>
+    </tr>
+    <tr>
+     <td><?php echo _("width") ?>:</td>
+     <td><input type="text" name="width" id="width" size="4" readonly="readonly" value="<?php echo $width ?>"/></td>
+     <td><?php echo _("height") ?></td>
+     <td><input type="text" name="height" id="height" size="4" readonly="readonly" value="<?php echo $height ?>"/></td>
+    </tr>
+   </table>
+  </td>
+ </tr>
+</table>
+</from>
+
diff --git a/ansel/templates/faces/face.inc b/ansel/templates/faces/face.inc
new file mode 100755 (executable)
index 0000000..b064dea
--- /dev/null
@@ -0,0 +1,42 @@
+<h1 class="header"><?php echo htmlspecialchars($title) ?></h1>
+<table>
+ <tr valign="top">
+  <td style="text-align: center">
+   <?php $facename = htmlspecialchars($face['face_name']);
+         echo $facebame;
+   ?><br />
+   <img src="<?php echo $faces->getFaceUrl($face['image_id'], $face_id) ?>" style="padding: 5px;" alt="<?php echo $facename?>" /><br />
+   <?php echo sprintf(ngettext("%d gallery", "%d galleries", count($face['galleries'])), count($face['galleries'])); ?><br />
+   <?php if ($GLOBALS['conf']['report_content']['driver']): ?>
+   <a href="<?php echo Horde_Util::addParameter(Horde::applicationUrl('faces/report.php'), 'face', $face_id) ?>" class="small" title="<?php echo _("Report abuse or submit a removal request") ?>"><?php echo _("Report") ?></a><br />
+   <?php
+    endif;
+    if ($conf['faces']['search']) {
+        echo '<a href="' . Horde_Util::addParameter(Horde::applicationUrl('faces/search/image_search.php'), 'face_id', $face_id)
+             . '" title="' . _("Find similar") . '">' . _("Similar faces") . '</a>';
+    }
+    ?>
+  </td>
+  <td>
+   <?php
+    $styleDef = Ansel::getStyleDefinition($prefs->getValue('default_gallerystyle'));
+    $style = $styleDef['name'];
+
+    foreach (array_keys($face['galleries']) as $gallery_id) {
+        foreach ($face['galleries'][$gallery_id] as $imageId) {
+            echo '<div style="float: left; text-align: center;"><img src="' . Ansel::getImageUrl($imageId, 'thumb', false, $style) . '" style="padding: 5px;" alt="' . $imageId . '" /><br />';
+            echo '<a href="' . Ansel::getUrlFor('view', array('view' => 'Image',
+                                                                    'gallery' => $gallery_id,
+                                                                    'image' => $imageId));
+
+            echo '">' . _("Photo") . '</a> / ';
+            echo '<a href="' . Ansel::getUrlFor('view', array('view' => 'Gallery',
+                                                              'gallery' => $gallery_id));
+            echo '">' . _("Gallery") . '</a></div>';
+        }
+    }
+
+    ?>
+  </td>
+ </tr>
+</table>
diff --git a/ansel/templates/faces/faces.inc b/ansel/templates/faces/faces.inc
new file mode 100755 (executable)
index 0000000..c033cce
--- /dev/null
@@ -0,0 +1,19 @@
+<?php
+echo $tabs->render();
+
+if (isset($form)) {
+    $form->renderActive(null, null, null, 'post');
+}
+
+echo '<h1 class="header">' . sprintf(_("%s: faces %s"), $title, number_format($count)) . '</h1>';
+if (empty($results)) {
+    echo _("No faces found");
+} else {
+    echo $pager->render();
+    echo '<div style="float:left;width:100%;">';
+    foreach ($results as $face_id => $face) {
+        include ANSEL_TEMPLATES . '/tile/face.inc';
+    }
+    echo '</div>';
+    echo $pager->render();
+}
diff --git a/ansel/templates/faces/gallery.inc b/ansel/templates/faces/gallery.inc
new file mode 100644 (file)
index 0000000..d4d3736
--- /dev/null
@@ -0,0 +1,71 @@
+<h1 class="header"><?php echo $title ?></h1>
+<?php echo $pager->render() ?>
+<table class="striped" style="width: 100%">
+ <tr>
+    <th><?php echo _("Filename") ?></th>
+    <th><?php echo _("Photo") ?></th>
+    <th><?php echo _("Faces") ?></th>
+ </tr>
+ <?php
+ $imageids = array();
+ foreach ($images as $image):
+    $imageids[$image->id] = $image->id;
+ ?>
+    <tr>
+     <td style="text-align: center">
+     <?php echo $image->filename; ?><br /><br />
+     <?php if ($autogenerate): ?>
+      <a href="javascript:void(0)" onclick="proccessImageFaces(<?php echo $image->id ?>, 1)">
+      <img src="<?php echo $reloadimage ?>" alt="[icon]" /><?php echo _("Reget faces for this photo") ?></a><br />
+     <?php endif; ?>
+
+     <a href="<?php echo Horde_Util::addParameter($customurl, array('image' => $image->id, 'gallery' => $image->gallery)) ?>">
+     <img src="<?php echo $customimage ?>" alt="[icon]" /><?php echo _("Add a custom face range") ?></a><br />
+    </td>
+    <td style="text-align: center">
+    <a href="<?php echo Ansel::getUrlFor('view', array('view' => 'Image', 'image' => $image->id, 'gallery' => $image->gallery)); ?>" target="_blank">
+    <img src="<?php echo Ansel::getImageUrl($image->id, 'thumb'); ?>" alt="<?php echo htmlspecialchars($image->filename)?>" /></a></td>
+    <td><div id="processface<?php echo $image->id ?>" name="processface<?php echo $image->id ?>"><?php echo _("Loading...") ?></div></td>
+    </tr>
+ <?php endforeach; ?>
+</table>
+<?php echo $pager->render() ?>
+<script type="text/javascript">
+//<![CDATA[
+var imgids = <?php echo Horde_Serialize::serialize($imageids, Horde_Serialize::JSON, Horde_Nls::getCharset()); ?>;
+
+function proccessImageFaces(image_id, reget) {
+    var url = '<?php echo Horde::applicationUrl('faces/image.php'); ?>';
+    $('processface' + image_id).innerHTML = '<?php echo _("Loading...") ?>';
+    var params = new Object();
+    params.reload = reget;
+    params.image = image_id;
+    new Ajax.Updater({success:'processface' + image_id},
+                     url,
+                     {
+                        method: 'post',
+                        parameters: params
+                      });
+
+}
+
+function deleteFace(image_id, face_id) {
+    var url = '<?php echo Horde_Util::addParameter(Horde::applicationUrl('faces/delete.php'), 'face', null, false); ?>';
+    new Ajax.Request(url + face_id + '&image=' + image_id);
+    $('face' + face_id).remove();
+}
+
+function setFaceName(image_id, face_id) {
+    var params = 'action=setname/face=' + face_id + '/image=' + image_id + '/facename=' + encodeURIComponent($F('facename' + face_id));
+    var url = '<?php echo Horde::applicationUrl(Horde_Util::addParameter('xrequest.php', array('requestType' => 'EditFaces'))); ?>';
+    new Ajax.Updater({success: 'face' + face_id}, url + '/' + params);
+}
+
+Event.observe(window, 'load', function() {
+    for (i in imgids) {
+        proccessImageFaces(i, 0);
+    };
+});
+
+//]]>
+</script>
diff --git a/ansel/templates/faces/image.inc b/ansel/templates/faces/image.inc
new file mode 100755 (executable)
index 0000000..272ca21
--- /dev/null
@@ -0,0 +1,12 @@
+<?php foreach ($result as $face_id => $face_name):?>
+    <div id="face<?php echo $face_id ?>">
+    <img src="<?php echo $autocreate ? $faces->getFaceUrl($image_id, $face_id) : Horde_Util::addParameter(Horde::applicationUrl('faces/img.php'), 'face', $face_id); ?>" alt="<?php echo $face_id ?>" style="padding-bottom: 5px; padding-left: 5px; width: 50px; height: 50px;" />
+    <input type="text" name="facename<?php echo $face_id ?>" id="facename<?php echo $face_id ?>" size="15" value="<?php echo $face_name ?>" />
+    <a href="javascript:void(0)" onclick="Ansel.setFaceName(<?php echo $image_id ?>, <?php echo $face_id ?>)" title="<?php echo _("Save face name") ?>">
+      <img src="<?php echo $imgdir ?>/save.png" alt="[icon]" /></a>
+    <a href="javascript:void(0)" onclick="Ansel.deleteFace(<?php echo $image_id ?>, <?php echo $face_id ?>)" title="<?php echo _("Delete face") ?>">
+      <img src="<?php echo $imgdir ?>/delete.png" alt="[icon]" /></a>
+    <a href="<?php echo Horde_Util::addParameter($customurl, array('face' => $face_id, 'image' => $image_id, 'url' => (!empty($url) ? $url : '')), null, false) ?>" title="<?php echo _("Edit face region") ?>">
+      <img src="<?php echo $imgdir ?>/edit.png" alt="[icon]" /></a>
+    </div>
+<?php endforeach;?>
diff --git a/ansel/templates/faces/index.inc b/ansel/templates/faces/index.inc
new file mode 100755 (executable)
index 0000000..b7d25a6
--- /dev/null
@@ -0,0 +1,21 @@
+<?php echo $tabs->render(Horde_Util::getGet('search_faces', 'all')); ?>
+<h1 class="header">
+<div style="float: right">
+<form>
+<input name="name" type="text" value="<?php echo $name ?>" />
+<input type="image" name="submit" src="<?php echo $registry->getImageDir('horde'); ?>/search.png" />
+</form>
+</div>
+<?php echo $title ?>
+</h1>
+<?php
+
+if (empty($results)) {
+    echo _("No faces found");
+} else {
+    foreach ($results as $face_id => $face) {
+        echo '<a href="' . $faces->getLink($face) . '" title="' . $face['face_name'] . '">'
+                . '<img src="' . $faces->getFaceUrl($face['image_id'], $face_id, 'mini')
+                . '" style="padding: 5px;" alt="' . htmlspecialchars($face['face_name']) . '" /></a>';
+    }
+}
diff --git a/ansel/templates/faces/search.inc b/ansel/templates/faces/search.inc
new file mode 100755 (executable)
index 0000000..f4f759a
--- /dev/null
@@ -0,0 +1,32 @@
+<?php echo $tabs->render('image'); ?>
+<h1 class="header"><?php echo $title ?></h1>
+<table style="width: 100%">
+ <tr valign="top">
+  <td style="text-align:center;">
+   <?php if ($face_id): ?>
+     <?php $facename = htmlspecialchars($face['face_name']);
+           echo $facename;
+     ?>
+     <br />
+     <img src="<?php echo $faces->getFaceUrl($face['image_id'], $face_id) ?>" style="padding: 5px;" alt="<?php echo $facename?>" /><br />
+     <?php echo sprintf(ngettext("%d photo", "%d photos", count($results)), count($results)); ?><br />
+     <a href="<?php echo Horde_Util::addParameter(Horde::applicationUrl('faces/report.php'), 'face', $face_id) ?>" class="small" title="<?php echo _("Report abuse or submit a removal request") ?>"><?php echo _("Report") ?></a>
+   <?php else: ?>
+     <img src="<?php echo Horde_Util::addParameter(Horde::applicationUrl('faces/search/img.php'), 'thumb', 1) ?>" style="padding: 5px;" alt="<?php echo $title ?>" style="width: 50px; height 50px;" /></a>
+   <?php endif; ?>
+  </td>
+<td>
+<?php
+if (empty($results)) {
+    echo _("No faces found");
+} else {
+    echo $pager->render();
+    foreach ($results as $face_id => $face) {
+        include ANSEL_TEMPLATES . '/tile/face.inc';
+    }
+    echo $pager->render();
+}
+?>
+</td>
+</tr>
+</table>
diff --git a/ansel/templates/gallery/delete_confirmation.inc b/ansel/templates/gallery/delete_confirmation.inc
new file mode 100644 (file)
index 0000000..8fd7737
--- /dev/null
@@ -0,0 +1,15 @@
+<h1 class="header">
+ <?php $gallery = $ansel_storage->getGallery($galleryId);
+       printf($actionID == 'delete' ? _("Really delete %s and all of its subgalleries?") : _("Really empty %s?"), htmlspecialchars($gallery->get('name')))
+ ?>
+</h1>
+<div class="headerbox" style="padding:10px">
+ <p><?php echo $actionID == 'delete' ? _("Really delete this gallery and all of the photos and subgalleries in it? This action cannot be undone.") : _("Really empty this gallery and all of the photos in it? This action cannot be undone.") ?></p>
+<form method="post" action="gallery/delete.php">
+<?php echo Horde_Util::formInput() ?>
+<input type="hidden" name="action" value="<?php echo $actionID ?>" />
+<input type="hidden" name="gallery" value="<?php echo htmlspecialchars($galleryId) ?>" />
+<input type="submit" class="button" value="<?php echo $actionID == 'delete' ? _("Really Delete") : _("Really Empty") ?>" />
+<input type="button" class="button" value="<?php echo _("Cancel") ?>" onclick="history.back()" />
+</form>
+</div>
diff --git a/ansel/templates/gallery/gallery.inc b/ansel/templates/gallery/gallery.inc
new file mode 100644 (file)
index 0000000..c75e264
--- /dev/null
@@ -0,0 +1,130 @@
+<form method="post" name="gallery" action="<?php echo Horde::selfUrl() ?>" onsubmit="return checkCategory();">
+<?php Horde_Util::pformInput() ?>
+<input type="hidden" name="actionID" value="save" />
+<?php if ($actionID == 'addchild'): ?>
+<input type="hidden" name="parent" value="<?php echo (int)$parentId ?>" />
+<?php elseif ($actionID != 'add'): ?>
+<input type="hidden" name="gallery" value="<?php echo isset($galleryId) ? (int)$galleryId : '' ?>" />
+<?php endif; ?>
+<input type="hidden" name="url" value="<?php echo htmlspecialchars(Horde_Util::getFormData('url')) ?>" />
+
+<h1 class="header">
+ <?php echo htmlspecialchars($title, ENT_COMPAT, Horde_Nls::getCharset()) ?>
+</h1>
+
+<table cellspacing="0" width="100%" class="striped headerbox">
+<tr>
+ <td align="right" valign="top"><strong><?php echo _("Gallery Parent") ?></strong>&nbsp;</td>
+ <td>
+  <select name="gallery_parent" id="gallery_parent">
+   <option value=""><?php echo _("Top Level Gallery") ?></option>
+   <?php echo Ansel::selectGalleries($gallery_parent, PERMS_EDIT, null, null, true, 0, 0, $galleryId) ?>
+  </select>
+ </td>
+</tr>
+<tr>
+  <td align="right" valign="top"><strong><?php echo _("Gallery Category") ?></strong>&nbsp;</td>
+  <td>
+    <input type="hidden" name="new_category" value="" />
+    <?php echo Prefs_CategoryManager::getJavaScript('gallery', 'gallery_category') .
+               Prefs_CategoryManager::getSelect("gallery_category", $gallery_category) ?>
+  </td>
+</tr>
+
+<?php
+if ($prefs->isLocked('default_gallerystyle')) {
+    echo '<input type="hidden" name="gallery_style" value="' . $prefs->getValue('default_gallerystyle') . '" />';
+} else {
+?>
+<tr>
+  <td align="right" valign="top"><strong><?php echo _("Style for this gallery") ?></strong>&nbsp;</td>
+  <td>
+   <?php echo Ansel::getStyleSelect('gallery_style', $gallery_thumbstyle) ?>
+  </td>
+</tr>
+<?php } ?>
+<tr>
+  <td align="right" valign="top"><strong><?php echo _("Display Mode") ?></strong></td>
+  <td>
+    <select name="view_mode">
+     <option value="Normal" <?php echo ((empty($gallery_mode) || $gallery_mode == 'Normal') ? 'selected="selected"' : '') ?>><?php echo _("Normal") ?></option>
+     <option value="Date" <?php echo ($gallery_mode == 'Date' ? 'selected="selected"' : '') ?>><?php echo _("Group By Date") ?></option>
+    </select>
+  </td>
+</tr>
+<tr>
+  <td align="right" valign="top"><?php echo Horde::img('required.png', '', '', $registry->getImageDir('horde')) ?><strong><?php echo _("Gallery Display Name") ?></strong>&nbsp;</td>
+  <td>
+    <input name="gallery_name" id="gallery_name" type="text" value="<?php echo htmlspecialchars($gallery_name) ?>" size="50" maxlength="100" />
+  </td>
+</tr>
+
+<tr>
+  <td align="right" valign="top"><strong><?php echo _("Gallery Description") ?></strong>&nbsp;</td>
+  <td>
+    <textarea name="gallery_desc" cols="50" rows="5"><?php echo htmlspecialchars($gallery_desc) ?></textarea>
+  </td>
+</tr>
+<tr>
+  <td align="right" valign="top"><strong id="slug_flag"><?php echo _("Gallery Slug") ?></strong>&nbsp;</td>
+  <td>
+   <input name="gallery_slug" id="gallery_slug" type="text" value="<?php echo htmlspecialchars($gallery_slug) ?>" size="50" /><br />
+   <?php if ($GLOBALS['conf']['urls']['pretty'] == 'rewrite') echo _("Slugs allows direct access to this gallery by visiting:") . ': ' . Horde::applicationUrl('gallery/slugname', true) ?><br />
+   <?php echo _("Slug names may contain only letters, numbers, @, or _ (underscore).") ?>
+  </td>
+ </tr>
+<tr>
+  <td align="right" valign="top"><strong><?php echo _("Gallery Tags") ?></strong>&nbsp;</td>
+  <td><input name="gallery_tags" type="text" value="<?php echo htmlspecialchars($gallery_tags) ?>" size="50" /><br />
+   <?php echo _("Separate tags with commas."); ?>
+ </td>
+</tr>
+
+<?php if (!empty($conf['ages']['limits'])) { ?>
+<tr>
+  <td align="right" valign="top"><strong><?php echo _("Gallery Ages") ?></strong>&nbsp;</td>
+  <td><select name="gallery_age">
+   <option value="0" <?php echo (empty($gallery_age) ? 'selected="selected"' : '') ?>><?php echo _("Allow all ages") ?></option>
+<?php
+    foreach ($conf['ages']['limits'] as $age) {
+        echo '<option value="' . $age . '"'
+                . ($gallery_age == $age ? ' selected="selected"' : '' )
+                . '>' . sprintf(_("User must be over %d"), $age) . '</option>';
+    }
+?>
+</select>
+ </td>
+</tr>
+<?php } ?>
+
+<?php
+if ($prefs->isLocked('default_download')) {
+    echo '<input type="hidden" name="default_download" value="' . $prefs->getValue('default_download') . '" />';
+} else {
+?>
+<tr>
+  <td align="right" valign="top"><strong><?php echo _("Who should be allowed to download original photos?") ?></strong>&nbsp;</td>
+  <td>
+    <select name="gallery_download">
+        <option value="all" <?php if ($gallery_download == 'all')  echo 'selected="selected"'; ?>><?php echo _("Anyone") ?></option>
+        <option value="authenticated" <?php if ($gallery_download == 'authenticated') echo 'selected="selected"'; ?>><?php echo _("Authenticated users") ?></option>
+        <option value="edit" <?php if ($gallery_download == 'edit') echo 'selected="selected"'; ?>><?php echo _("Users with edit permissions") ?></option>
+    </select>
+  </td>
+</tr>
+<?php } ?>
+<?php if (empty($gallery) || Horde_Auth::getAuth() == $gallery->get('owner')): ?>
+<tr>
+  <td align="right" valign="top"><strong><?php echo _("Gallery Password") ?></strong>&nbsp;</td>
+  <td><input name="gallery_passwd" type="password" value="<?php echo htmlspecialchars($gallery_passwd) ?>" size="50" /></td>
+</tr>
+<?php endif; ?>
+<tr>
+  <td></td>
+  <td>
+   <input type="submit" id="gallery_submit" name="gallery_submit" class="button" value="<?php echo _("Save Gallery") ?>" />&nbsp;
+   <input type="reset" class="button" value="<?php echo _("Undo Changes") ?>"  />&nbsp;
+  </td>
+</tr>
+</table>
+</form>
diff --git a/ansel/templates/group/category.inc b/ansel/templates/group/category.inc
new file mode 100644 (file)
index 0000000..8d6f908
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+
+$num_galleries = $GLOBALS['ansel_storage']->countGalleries(
+    Horde_Auth::getAuth(), PERMS_SHOW, array('category' => $group), null, false);
+
+if (empty($group)) {
+    $category_title = _("Unfiled");
+} else {
+    $category_title = $group;
+}
+
+$params = array('groupby' => 'category',
+                'category' => $group,
+                'view' => 'List');
+if (empty($group)) {
+    $params['special'] = 'unfiled';
+}
+$viewurl = Ansel::getUrlFor('view', $params, true);
+$link = Horde::link($viewurl, $category_title, 'light');
+
+$preview_gallery = array();
+if ($num_galleries < 5) {
+    $preview_gallery = $GLOBALS['ansel_storage']->listGalleries(
+                            PERMS_SHOW, array('category' => $group), null, false);
+} else {
+    $preview_gallery = $GLOBALS['ansel_storage']->listGalleries(
+                            PERMS_SHOW, array('category' => $group), null,
+                            false, rand(0, $num_galleries - 4), 4);
+}
+$mini_count = 0;
+?>
+
+<td class="galleryGroupsTile" width="<?php echo round(100 / $prefs->getValue('tilesperrow')); ?>%" valign="top">
+ <table>
+  <tr>
+<?php foreach ($preview_gallery as $galleryId => $gallery): ?>
+   <td align="center">
+    <?php if ($gallery->isOldEnough() && !$gallery->hasPasswd()): ?>
+     <?php echo $link ?><img src="<?php echo Ansel::getImageUrl($gallery->getDefaultImage('ansel_default'), 'mini', false, 'ansel_default'); ?>" alt="<?php echo htmlspecialchars($gallery->get('name'))?>" /></a>
+    <?php else: ?>
+     <?php echo $link . Horde::img('gallery-locked-mini.png') ?></a>
+    <?php endif; ?>
+   </td>
+<?php if (!(++$mini_count % 2)) echo "  </tr>\n  <tr>\n" ?>
+<?php endforeach; ?>
+  </tr>
+ </table>
+
+ <p class="light">
+  <strong><?php echo $link . $category_title ?></a></strong><br />
+  <?php echo sprintf(ngettext("%d Gallery", "%d Galleries", $num_galleries), $num_galleries) ?>
+ </p>
+</td>
+
+<?php if (!(++$count % $prefs->getValue('tilesperrow'))) echo "</tr>\n<tr>\n" ?>
diff --git a/ansel/templates/group/footer.inc b/ansel/templates/group/footer.inc
new file mode 100755 (executable)
index 0000000..1e2128c
--- /dev/null
@@ -0,0 +1 @@
+</tr></table>
diff --git a/ansel/templates/group/header.inc b/ansel/templates/group/header.inc
new file mode 100644 (file)
index 0000000..fa6be95
--- /dev/null
@@ -0,0 +1,14 @@
+<div class="header">
+ <span class="rightFloat">
+   <?php ($groups_perpage < $num_groups) ? printf(_("%d to %d of %d Groups"), $start, $end, $num_groups) : printf(ngettext("%d Group", "%d Groups", $num_groups), $num_groups) ?>
+ </span>
+<?php
+printf(_("Galleries grouped by %s"), $groupby);
+echo ' ' . Horde::link(Ansel::getUrlFor('group', array())) .
+Horde::img('reload.png', _("Refresh List"), null, $registry->getImageDir('horde')); ?></a>
+</div>
+<div class="anselActions widget">
+  <?php echo _("Group by:") . ' ' . implode(' | ', $groupby_links) ?>
+</div>
+
+<table id="galleryGroups"><tr>
diff --git a/ansel/templates/group/owner.inc b/ansel/templates/group/owner.inc
new file mode 100644 (file)
index 0000000..d903fef
--- /dev/null
@@ -0,0 +1,63 @@
+<?php
+if ($group == Horde_Auth::getAuth()) {
+    $owner_title = _("My Galleries");
+    $custom = '';
+} elseif (!empty($GLOBALS['conf']['gallery']['customlabel'])) {
+    $uprefs = &Prefs::singleton($GLOBALS['conf']['prefs']['driver'],
+                                'ansel',
+                                $group, '', null, false);
+    $custom = $uprefs->getValue('grouptitle');
+    $identity = &Identity::singleton('none', $group);
+    $fullname = $identity->getValue('fullname');
+    if (!$fullname) {
+        $fullname = $group;
+    }
+    if (!$custom) {
+        $owner_title = sprintf(_("%s's Galleries"), htmlspecialchars($fullname));
+    } else {
+        $owner_title = $custom;
+    }
+} else {
+    $owner_title = sprintf(_("%s's Galleries"), $group);
+}
+
+$link = Horde::link(Ansel::getUrlFor('view', array('groupby' => $groupby,
+                                                   'owner' => $group,
+                                                   'view' => 'List')));
+
+$num_galleries = $ansel_storage->countGalleries(Horde_Auth::getAuth(), PERMS_SHOW,
+                                                $group, null, false);
+if ($num_galleries < 5) {
+    $mini_galleries = $ansel_storage->listGalleries(PERMS_SHOW,
+                                                    $group, null, false);
+} else {
+    $mini_galleries = $GLOBALS['ansel_storage']->listGalleries(
+        PERMS_SHOW, $group, null, false, rand(0, $num_galleries - 4), 4);
+}
+$mini_galleries = array_values($mini_galleries);
+?>
+
+<td class="galleryGroupsTile" width="<?php echo round(100 / $prefs->getValue('tilesperrow')) ?>%" valign="top">
+ <table>
+  <tr>
+<?php for ($i = 0; $i < count($mini_galleries); ++$i): ?>
+   <td align="center">
+    <?php if ($mini_galleries[$i]->isOldEnough() && !$mini_galleries[$i]->hasPasswd()): ?>
+     <?php echo $link ?><img src="<?php echo Ansel::getImageUrl($mini_galleries[$i]->getDefaultImage('ansel_default'), 'mini', false, 'ansel_default') ?>" alt="<?php echo $owner_title?>" /></a>
+    <?php else: ?>
+      <?php echo $link . Horde::img('gallery-locked-mini.png') ?></a>
+    <?php endif; ?>
+   </td>
+
+<?php if ($i == 1) echo "  </tr>\n  <tr>\n" ?>
+<?php endfor; ?>
+  </tr>
+ </table>
+
+ <p class="light">
+  <strong><?php echo $link . $owner_title ?></a></strong><br /><?php echo ((!empty($custom)) ? sprintf(_("Owned by: %s"), $fullname) . '<br />' : '') ?>
+  <?php echo sprintf(ngettext("%d Gallery", "%d Galleries", $num_galleries), $num_galleries) ?>
+ </p>
+</td>
+
+<?php if (!(++$count % $prefs->getValue('tilesperrow'))) echo "</tr>\n<tr>\n" ?>
diff --git a/ansel/templates/group/pager.inc b/ansel/templates/group/pager.inc
new file mode 100755 (executable)
index 0000000..147e19f
--- /dev/null
@@ -0,0 +1,4 @@
+<?php
+if ($num_groups && $pager = $group_pager->render()) {
+    echo $pager;
+}
diff --git a/ansel/templates/image/crop_image.inc b/ansel/templates/image/crop_image.inc
new file mode 100644 (file)
index 0000000..35a0c9c
--- /dev/null
@@ -0,0 +1,169 @@
+<script type="text/javascript">
+
+var imageDimensions = new Array({x:0, y:0}, {x:4, y:6}, {x:5, y:7}, {x:8, y:10});
+
+var imageCropper = {
+
+    // The current cropper object
+    currentCropper: null,
+
+    // Original Coords in case we request a reset
+    x1: <?php echo $x1 ?>,
+    x2: <?php echo $x2 ?>,
+    y1: <?php echo $y1 ?>,
+    y2: <?php echo $y2 ?>,
+    ratioX: 0,
+    ratioY: 0,
+
+    // Initialize the object
+    init: function()
+    {
+        this.attachCropper();
+        $('reset').observe('click', this.reset.bindAsEventListener(imageCropper));
+        changeRatio();
+    },
+
+    // Resets cropper to the original coords
+    reset: function()
+    {
+         $('x1').value = this.x1;
+         $('x2').value = this.x2;
+         $('y1').value = this.y1;
+         $('y2').value = this.y2;
+         this.attachCropper();
+    },
+
+    // Restrict to a particular aspect ratio
+    setAspectRatio: function(x, y)
+    {
+        this.ratioX = x;
+        this.ratioY = y;
+        this.attachCropper();
+    },
+
+    // Initialize the Cropper object
+    attachCropper: function() {
+        if (this.currentCropper != null) {
+            this.currentCropper.remove();
+        }
+        this.currentCropper = new Cropper.Img(
+            'edit_image',
+            {
+                onloadCoords: { x1: $F('x1'), y1: $F('y1'), x2: $F('x2'), y2: $F('y2') },
+                displayOnInit: true,
+                onEndCrop: function(coords, dimensions) {
+                    $('x1').value = coords.x1;
+                    $('y1').value = coords.y1;
+                    $('x2').value = coords.x2;
+                    $('y2').value = coords.y2;
+                    $('width').value = dimensions.width;
+                    $('height').value = dimensions.height;
+                },
+                ratioDim: {x: this.ratioX, y:this.ratioY}
+            });
+     }
+}
+
+document.observe('dom:loaded', function() { imageCropper.init()});
+
+function changeRatio()
+{
+    // First check if we are manual, then disable the orientation selector
+    if ($F('ratio') > 0) {
+        $('orientation').enable();
+    } else {
+        $('orientation').disable();
+    }
+
+    if ($F('orientation') == 'portrait') {
+        imageCropper.setAspectRatio(imageDimensions[$F('ratio')].x, imageDimensions[$F('ratio')].y);
+    } else {
+        imageCropper.setAspectRatio(imageDimensions[$F('ratio')].y, imageDimensions[$F('ratio')].x);
+    }
+}
+</script>
+
+<h1 class="header"><?php echo htmlspecialchars($title, ENT_COMPAT, Horde_Nls::getCharset()) ?></h1>
+<div class="control anselActions" style="text-align: center">
+<?php
+$style = $gallery->getStyle();
+$image_src = Ansel::getImageUrl($image_id, 'full');
+
+echo Horde::link($galleryurl) . Horde::img('galleries.png') . ' ' . _("Back to gallery") . '</a> ';
+echo Horde::link($imageview_url) . Horde::img('galleries.png') . ' ' . _("Cancel") . '</a> ';
+
+if (Ansel::isAvailable('rotate')) {
+    echo Horde::link(Horde_Util::addParameter($imageurl, 'actionID', 'previewrotate270')) . Horde::img('image/rotate-270.png', '', '', $registry->getImageDir('horde')) . ' ' . _("Rotate Left") . '</a> ';
+    echo Horde::link(Horde_Util::addParameter($imageurl, 'actionID', 'previewrotate180')) . Horde::img('image/rotate-180.png', '', '', $registry->getImageDir('horde')) . ' ' . _("Rotate 180") . '</a> ';
+    echo Horde::link(Horde_Util::addParameter($imageurl, 'actionID', 'previewrotate90')) . Horde::img('image/rotate-90.png', '', '', $registry->getImageDir('horde')) . ' ' . _("Rotate Right") . '</a> ';
+}
+if (Ansel::isAvailable('flip')) {
+    echo Horde::link(Horde_Util::addParameter($imageurl, 'actionID', 'previewflip')) . Horde::img('image/flip.png', '', '', $registry->getImageDir('horde')) . ' ' . _("Flip") . '</a> ';
+}
+if (Ansel::isAvailable('mirror')) {
+    echo Horde::link(Horde_Util::addParameter($imageurl, 'actionID', 'previewmirror')) . Horde::img('image/mirror.png', '', '', $registry->getImageDir('horde')) . ' ' . _("Mirror") . '</a> ';
+}
+if (Ansel::isAvailable('grayscale')) {
+    echo Horde::link(Horde_Util::addParameter($imageurl, 'actionID', 'previewgrayscale')) . Horde::img('image/grayscale.png', '', '', $registry->getImageDir('horde')) . ' ' . _("Grayscale") . '</a> ';
+}
+if (Ansel::isAvailable('text_watermark')) {
+    Horde::addScriptFile('popup.js', 'horde', true);
+    echo Horde::link(Horde::applicationUrl(Horde_Util::addParameter($imageurl, array('actionID' => 'setwatermark', 'share' => $gallery->getName()))), '', '', '_blank', 'return !popup(this.href, 450, 250);') . Horde::img('text.png') . ' ' . _("Watermark") . '</a> ';
+}
+$selfUrl = Horde::selfUrl(true);
+echo Horde::link(Horde::applicationUrl(Horde_Util::addParameter($selfUrl, array('actionID' => 'cropedit')))) . Horde::img('image/crop.png', '', '', $registry->getImageDir('horde')) . _("Crop") . '</a>';
+echo Horde::link(Horde::applicationUrl(Horde_Util::addParameter($selfUrl, array('actionID' => 'resizeedit')))) . Horde::img('image/ratio.png', '', '', $registry->getImageDir('horde')) . _("Resize") . '</a>';
+?>
+</div>
+<br />
+<div style="width:70%;float:left;">
+  <div>
+    <?php echo '<img src="' . $image_src . '" id="edit_image" alt="[image]" />' ?>
+  </div>
+  <br />
+    <?php $caption = $image->caption ? $image->caption : $image->filename; echo Horde_Text_Filter::filter($caption, 'text2html', array('parselevel' => Horde_Text_Filter_Text2html::MICRO)) ?>
+</div>
+<div style="text-align:center;width:25%;float:left;position:relative;">
+<form method="post" action="<?php echo Horde::applicationUrl(Horde_Util::addParameter($imageurl, 'actionID', 'previewcrop'));?>">
+  <input type="hidden" name="image_id" id="image_id" value="<?php echo (int)$image_id ?>" />
+  <input type="hidden" name="x1" id="x1" value="<?php echo $x1 ?>" />
+  <input type="hidden" name="y1" id="y1" value="<?php echo $y1 ?>" />
+  <input type="hidden" name="x2" id="x2" value="<?php echo $x2 ?>" />
+  <input type="hidden" name="y2" id="y2" value="<?php echo $y2 ?>" />
+   <div style="width:50%;float:left;text-align:right;">
+    <?php echo _("Desired dimensions:")?>
+   </div>
+   <div style="width:50%;float:left;text-align:left;">
+    <select name="ratio" style="width:100px;" id="ratio" onchange="changeRatio()">
+     <option value="0"><?php echo _("Manual"); ?></option>
+     <option value="1">2:3</option>
+     <option value="2">5:7</option>
+     <option value="3">8:10</option>
+    </select><br />
+   </div>
+   <div style="width:50%;float:left;text-align:right;">
+    <?php echo _("Orientation:") ?>
+   </div>
+   <div style="width:50%;float:left;text-align:left;">
+    <select name="orientation" style="width:100px;" id="orientation" onchange="changeRatio()">
+     <option value="landscape"><?php echo _("Landscape") ?></option>
+     <option value="portrait"><?php echo _("Portrait") ?></option>
+    </select><br />
+   </div>
+   <div style="width:50%;float:left;text-align:right;">
+      <?php echo _("width") ?>:
+     </div>
+     <div style="width:50%;float:left;text-align:left;">
+      <input type="text" name="width" id="width" readonly="readonly" style="width:100px;" value="<?php echo (int)$geometry['width'] ?>"/><br />
+     </div>
+     <div style="width:50%;float:left;text-align:right;">
+      <?php echo _("height") ?>:
+      </div>
+      <div style="width:50%;float:left;text-align:left;">
+      <input type="text" name="height" id="height" readonly="readonly" style="width:100px;" value="<?php echo (int)$geometry['height'] ?>"/><br />
+     </div>
+      <input type="submit" name="submit" id="submit" value="<?php echo _("Preview") ?>" />
+      <input type="reset" name="reset" id="reset" value="<?php echo _("Reset") ?>" />
+    </form>
+ </div>
+</div>
diff --git a/ansel/templates/image/edit_image.inc b/ansel/templates/image/edit_image.inc
new file mode 100644 (file)
index 0000000..ef09a2f
--- /dev/null
@@ -0,0 +1,41 @@
+<h1 class="header"><?php echo htmlspecialchars($title, ENT_COMPAT, Horde_Nls::getCharset()) ?></h1>
+<div class="control anselActions" style="text-align: center">
+<div id="prevDiv"></div>
+<?php
+$style = $gallery->getStyle();
+$image_src = Ansel::getImageUrl($image_id, 'screen');
+
+echo Horde::link($galleryurl) . Horde::img('galleries.png') . ' ' . _("Back to gallery") . '</a> ';
+echo Horde::link($imageview_url) . Horde::img('galleries.png') . ' ' . _("Cancel") . '</a> ';
+
+if (Ansel::isAvailable('rotate')) {
+    echo Horde::link(Horde_Util::addParameter($imageurl, 'actionID', 'previewrotate270')) . Horde::img('image/rotate-270.png', '', '', $registry->getImageDir('horde')) . ' ' . _("Rotate Left") . '</a> ';
+    echo Horde::link(Horde_Util::addParameter($imageurl, 'actionID', 'previewrotate180')) . Horde::img('image/rotate-180.png', '', '', $registry->getImageDir('horde')) . ' ' . _("Rotate 180") . '</a> ';
+    echo Horde::link(Horde_Util::addParameter($imageurl, 'actionID', 'previewrotate90')) . Horde::img('image/rotate-90.png', '', '', $registry->getImageDir('horde')) . ' ' . _("Rotate Right") . '</a> ';
+}
+if (Ansel::isAvailable('flip')) {
+    echo Horde::link(Horde_Util::addParameter($imageurl, 'actionID', 'previewflip')) . Horde::img('image/flip.png', '', '', $registry->getImageDir('horde')) . ' ' . _("Flip") . '</a> ';
+}
+if (Ansel::isAvailable('mirror')) {
+    echo Horde::link(Horde_Util::addParameter($imageurl, 'actionID', 'previewmirror')) . Horde::img('image/mirror.png', '', '', $registry->getImageDir('horde')) . ' ' . _("Mirror") . '</a> ';
+}
+if (Ansel::isAvailable('grayscale')) {
+    echo Horde::link(Horde_Util::addParameter($imageurl, 'actionID', 'previewgrayscale')) . Horde::img('image/grayscale.png', '', '', $registry->getImageDir('horde')) . ' ' . _("Grayscale") . '</a> ';
+}
+if (Ansel::isAvailable('TextWatermark')) {
+    Horde::addScriptFile('popup.js', 'horde', true);
+    echo Horde::link(Horde::applicationUrl(Horde_Util::addParameter($imageurl, array('actionID' => 'setwatermark', 'share' => $gallery->getName()))), '', '', '_blank', 'return !popup(this.href, 450, 250);') . Horde::img('text.png') . ' ' . _("Watermark") . '</a> ';
+}
+
+$selfUrl = Horde::selfUrl(true);
+echo Horde::link(Horde::applicationUrl(Horde_Util::addParameter($selfUrl, array('actionID' => 'cropedit')))) . Horde::img('image/crop.png', '', '', $registry->getImageDir('horde')) . _("Crop") . '</a>';
+echo Horde::link(Horde::applicationUrl(Horde_Util::addParameter($selfUrl, array('actionID' => 'resizeedit')))) . Horde::img('image/ratio.png', '', '', $registry->getImageDir('horde')) . _("Resize") . '</a>';
+
+?>
+</div>
+<br />
+<div style="text-align: center;">
+  <?php echo '<img src="' . $image_src . '" id="edit_image" alt="[image]" />' ?>
+  <br />
+  <?php $caption = $image->caption ? $image->caption : $image->filename; echo Horde_Text_Filter::filter($caption, 'text2html', array('parselevel' => Horde_Text_Filter_Text2html::MICRO)) ?>
+</div>
diff --git a/ansel/templates/image/preview_cropimage.inc b/ansel/templates/image/preview_cropimage.inc
new file mode 100644 (file)
index 0000000..83c95cc
--- /dev/null
@@ -0,0 +1,32 @@
+<h1 class="header"><?php echo htmlspecialchars($title, ENT_COMPAT, Horde_Nls::getCharset()) ?></h1>
+<?php
+$base_url = Horde_Util::addParameter('image.php', array_merge(
+                                            array('gallery' => $gallery_id,
+                                                  'image' => $image_id,
+                                                  'page' => $page,
+                                                  'params' => $params),
+                                            $date));
+
+$edit_url = Horde_Util::addParameter($base_url, 'actionID', 'editimage');
+$save_url = Horde_Util::addParameter($base_url, 'actionID', $action);
+$image_url = Horde::applicationUrl(Horde_Util::addParameter($base_url, array('actionID' => 'image' . $action,
+                                                                       'view' => 'full')));
+?>
+<div class="control anselActions" style="text-align:center;">
+<?php
+if ($gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+    echo Horde::link(Horde::applicationUrl($save_url), _("Save Change"))
+             . Horde::img('save.png', '', '', $registry->getImageDir('horde')) . ' ' . _("Save Change") . '</a> ';
+}
+if ($gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+    echo Horde::link(Horde::applicationUrl($edit_url), _("Cancel Change"))
+             . Horde::img('delete.png', '', '', $registry->getImageDir('horde')) . ' ' . _("Cancel Change") . '</a>';
+}
+?>
+</div>
+<br />
+<div align="center">
+<?php echo '<img src="' . $image_url . '" alt="[preview]" />'?>
+<br />
+<?php $caption = $image->caption ? $image->caption : $image->filename; echo Horde_Text_Filter::filter($caption, 'text2html', array('parselevel' => Horde_Text_Filter_Text2html::MICRO)) ?>
+</div>
diff --git a/ansel/templates/image/preview_image.inc b/ansel/templates/image/preview_image.inc
new file mode 100644 (file)
index 0000000..4b0e848
--- /dev/null
@@ -0,0 +1,35 @@
+<h1 class="header"><?php echo htmlspecialchars($title, ENT_COMPAT, Horde_Nls::getCharset()) ?></h1>
+<?php
+$base_url = Horde_Util::addParameter('image.php', array_merge(
+                                            array('gallery' => $gallery_id,
+                                                  'image' => $image_id,
+                                                  'page' => $page,
+                                                  'watermark' => $watermark,
+                                                  'whalign' => $watermark_halign,
+                                                  'wvalign' => $watermark_valign,
+                                                  'font' => $watermark_font),
+                                            $date));
+
+$edit_url = Horde_Util::addParameter($base_url, 'actionID', 'editimage');
+$save_url = Horde_Util::addParameter($base_url, 'actionID', $action);
+$image_url = Horde::applicationUrl(Horde_Util::addParameter($base_url, array('actionID' => 'image' . $action,
+                                                                             'view' => 'screen')));
+?>
+<div class="control anselActions" style="text-align:center;">
+<?php
+if ($gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+    echo Horde::link(Horde::applicationUrl($save_url), _("Save Change"))
+             . Horde::img('save.png', '', '', $registry->getImageDir('horde')) . ' ' . _("Save Change") . '</a> ';
+}
+if ($gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+    echo Horde::link(Horde::applicationUrl($edit_url), _("Cancel Change"))
+             . Horde::img('delete.png', '', '', $registry->getImageDir('horde')) . ' ' . _("Cancel Change") . '</a>';
+}
+?>
+</div>
+<br />
+<div align="center">
+<?php echo '<img src="' . $image_url . '" alt="[preview]" />' ?>
+<br />
+<?php $caption = $image->caption ? $image->caption : $image->filename; echo Horde_Text_Filter::filter($caption, 'text2html', array('parselevel' => Horde_Text_Filter_Text2html::MICRO)) ?>
+</div>
diff --git a/ansel/templates/image/resize_image.inc b/ansel/templates/image/resize_image.inc
new file mode 100644 (file)
index 0000000..33d47ae
--- /dev/null
@@ -0,0 +1,84 @@
+<script type="text/javascript">
+document.observe(
+    'dom:loaded',
+    function()
+    {
+        document.slider = new Control.Slider(
+            'handle1',
+            'slider-track',
+            {
+                range: $R(1, <?php echo $geometry['height'] ?>),
+                sliderValue: <?php echo $geometry['height'] ?>,
+                axis:'horizontal',
+                onChange: function(value) {resizeImage(value)},
+                onSlide: function(value) {resizeImage(value)}
+            }
+        );
+    });
+
+function resizeImage(value)
+{
+    $('edit_image').height = value;
+    $('width').value = $('edit_image').width;
+    $('height').value = $('edit_image').height;
+
+}
+
+function resetImage()
+{
+    document.slider.setValue(<?php echo $geometry['height'] ?>);
+}
+</script>
+<h1 class="header"><?php echo htmlspecialchars($title, ENT_COMPAT, Horde_Nls::getCharset()) ?></h1>
+<div class="control anselActions" style="text-align: center">
+<?php
+
+$style = $gallery->getStyle();
+$image_src = Ansel::getImageUrl($image_id, 'full');
+
+echo Horde::link($galleryurl) . Horde::img('galleries.png') . ' ' . _("Back to gallery") . '</a> ';
+echo Horde::link($imageview_url) . Horde::img('galleries.png') . ' ' . _("Cancel") . '</a> ';
+
+if (Ansel::isAvailable('rotate')) {
+    echo Horde::link(Horde_Util::addParameter($imageurl, 'actionID', 'previewrotate270')) . Horde::img('image/rotate-270.png', '', '', $registry->getImageDir('horde')) . ' ' . _("Rotate Left") . '</a> ';
+    echo Horde::link(Horde_Util::addParameter($imageurl, 'actionID', 'previewrotate180')) . Horde::img('image/rotate-180.png', '', '', $registry->getImageDir('horde')) . ' ' . _("Rotate 180") . '</a> ';
+    echo Horde::link(Horde_Util::addParameter($imageurl, 'actionID', 'previewrotate90')) . Horde::img('image/rotate-90.png', '', '', $registry->getImageDir('horde')) . ' ' . _("Rotate Right") . '</a> ';
+}
+if (Ansel::isAvailable('flip')) {
+    echo Horde::link(Horde_Util::addParameter($imageurl, 'actionID', 'previewflip')) . Horde::img('image/flip.png', '', '', $registry->getImageDir('horde')) . ' ' . _("Flip") . '</a> ';
+}
+if (Ansel::isAvailable('mirror')) {
+    echo Horde::link(Horde_Util::addParameter($imageurl, 'actionID', 'previewmirror')) . Horde::img('image/mirror.png', '', '', $registry->getImageDir('horde')) . ' ' . _("Mirror") . '</a> ';
+}
+if (Ansel::isAvailable('grayscale')) {
+    echo Horde::link(Horde_Util::addParameter($imageurl, 'actionID', 'previewgrayscale')) . Horde::img('image/grayscale.png', '', '', $registry->getImageDir('horde')) . ' ' . _("Grayscale") . '</a> ';
+}
+if (Ansel::isAvailable('text_watermark')) {
+    Horde::addScriptFile('popup.js', 'horde', true);
+    echo Horde::link(Horde::applicationUrl(Horde_Util::addParameter($imageurl, array('actionID' => 'setwatermark', 'share' => $gallery->getName()))), '', '', '_blank', 'return !popup(this.href, 450, 250);') . Horde::img('text.png') . ' ' . _("Watermark") . '</a> ';
+}
+
+$selfUrl = Horde::selfUrl(true);
+echo Horde::link(Horde::applicationUrl(Horde_Util::addParameter($selfUrl, array('actionID' => 'cropedit')))) . Horde::img('image/crop.png', '', '', $registry->getImageDir('horde')) . _("Crop") . '</a>';
+
+echo Horde::link(Horde::applicationUrl(Horde_Util::addParameter($selfUrl, array('actionID' => 'resizeedit')))) . Horde::img('image/ratio.png', '', '', $registry->getImageDir('horde')) . _("Resize") . '</a>';
+?>
+</div>
+<form method="post" action="<?php echo Horde::applicationUrl(Horde_Util::addParameter($imageurl, 'actionID', 'resize'));?>">
+<div style="width:100%;text-align:center;margin-top:5px;margin-bottom:5px;">
+  <?php echo _("width") ?>:
+  <input type="text" name="width" id="width" size="5" readonly="readonly" value="<?php echo (int)$geometry['width'] ?>"/>
+  <?php echo _("height") ?>:
+  <input type="text" name="height" id="height" size="5" readonly="readonly" value="<?php echo (int)$geometry['height'] ?>"/>
+  <div id="slider-track">
+   <div id="handle1" class="sliderHandle"></div>
+  </div>
+   <input type="submit" name="submit" id="submit" value="<?php echo _("Save") ?>" />
+  <input type="reset" name="reset" id="reset" value="<?php echo _("Reset") ?>" onclick="resetImage()"/>
+</div>
+
+<div style="width: 100%;float:left;text-align: center;padding-top:10px;">
+ <?php echo '<img src="' . $image_src . '" id="edit_image" alt="[image]" />' ?>
+ <br />
+ <?php $caption = $image->caption ? $image->caption : $image->filename; echo Horde_Text_Filter::filter($caption, 'text2html', array('parselevel' => Horde_Text_Filter_Text2html::MICRO)) ?>
+</div>
diff --git a/ansel/templates/image/upload.inc b/ansel/templates/image/upload.inc
new file mode 100644 (file)
index 0000000..b239320
--- /dev/null
@@ -0,0 +1,180 @@
+<div class="header" id="galleryHeader"><span class="breadcrumbs"><?php echo $breadcrumbs ?></span></div>
+<table width="100%" cellspacing="0">
+<tr><td style="vertical-align:top;padding:0;">
+<?php
+$form->renderActive(null, null, 'upload.php', 'post', 'multipart/form-data');
+if ($browser->getPlatform() == 'win' && Horde::showService('help')) {
+    echo '<div class="box" style="margin:8px; padding:8px"><h3>' . Horde_Help::link('ansel', 'xppublish') . ' ' . _("Learn how to publish photos directly from Windows.") . '</h3></div>';
+}
+
+if (!empty($haveImages)) {
+    Horde::addScriptFile('prototype.js', 'horde', true);
+    Horde::addScriptFile('effects.js', 'horde', true);
+    Horde::addScriptFile('carousel.js', 'ansel', true);
+}
+
+$graphic_dir = Horde::applicationUrl($GLOBALS['registry']->getImageDir(), true);
+$lbcssurl = Horde::url($GLOBALS['registry']->get('themesuri', 'ansel') . '/lightbox.css', true);
+?>
+</td>
+<td style="vertical-align:top;width:40%;padding:0;">
+  <div class="header">&nbsp;</div>
+  <div class="anselWidgets">
+    <div id="horizontal_carousel">
+     <div class="anselWidget" style="height:150px;">
+      <h2 class="header tagTitle"><?php echo _("Photos in this gallery") . ' (' . $gallery->countImages() . ')' ?></h2>
+      <br />
+      <div class="previous_button"></div>
+      <div class="container">
+       <ul></ul>
+       <div id="spinner" style="display: none;"><?php echo _("Loading ...") ?></div>
+      </div>
+      <div class="next_button"></div>
+     </div>
+    </div>
+  </div>
+</td>
+</tr></table>
+<?php if (!empty($haveImages)):?>
+<script type="text/javascript">
+// <![CDATA[
+// Delay response
+Ajax.Request.prototype.originalInitialize = Ajax.Request.prototype.initialize;
+Ajax.Request.prototype.initialize = function(url, options) {
+    options.onSuccess = options.onSuccess.wrap(function(proceed, request, json) {
+        proceed.curry(request, json).delay(1);
+    });
+    this.originalInitialize(url, options);
+}
+
+// Mock ajax response
+Ajax.Response.prototype._getHeaderJSON = function() {
+    var nbElements = <?php echo $gallery->countImages() ?>;
+    var from = this.request.parameters.from;
+    var to   = Math.min(nbElements, this.request.parameters.to);
+    return {html: this.responseText, from: from, to: to, more: to != nbElements};
+}
+
+var carousel = null;
+
+function runTest() {
+    updateCarouselSize();
+    carousel = new UI.Ajax.Carousel("horizontal_carousel", {url: "<?php echo Horde_Util::addParameter(Horde::applicationUrl('img/upload_preview.php'), 'gallery', $gallery->id) ?>", elementSize: 90})
+        .observe("request:started", function() {
+            $('spinner').show().morph("opacity:0.8", {duration:0.5});
+        })
+        .observe("request:ended", function() {
+            $('spinner').morph("opacity:0", {duration:0.5, afterFinish: function(obj) { obj.element.hide(); }});
+        });
+}
+
+function resized() {
+    updateCarouselSize();
+    if (carousel)
+        carousel.updateSize();
+}
+
+function updateCarouselSize() {
+    // Get the width of the viewport, then match the percentage of the
+    // table cell the carousel appears in. We can't do this by referencing the
+    // carousel's parent because that won't be sized correctly until the
+    // carousel is sized correctly. Classic chicken and egg....
+    var dim = document.viewport.getDimensions();
+    dim.width *= .4;
+
+    // size the carousel
+    $("horizontal_carousel").style.width = (Math.floor(dim.width/90) * 90) + "px";
+    $$("#horizontal_carousel .container").first().style.width =  (Math.floor((dim.width - 100) / 90)) * 90 +  "px";
+}
+Event.observe(window, "load", runTest);
+Event.observe(window, "resize", resized);
+// ]]>
+</script>
+
+<style type="text/css">
+/* Horizontal Carousel */
+#horizontal_carousel {
+  float: left;
+  width: 100%;
+  height: 50px;
+  padding: 3px;
+  position: relative;
+}
+
+#horizontal_carousel .container {
+  float: left;
+  width: 100%;
+  position: relative;
+  overflow: hidden;
+}
+
+#horizontal_carousel ul {
+  margin: 0;
+  padding: 0;
+  width: 100000px;
+  position: relative;
+  top: 0;
+  left: 0;
+}
+
+#horizontal_carousel ul li {
+  width:90px;
+  text-align: center;
+  list-style: none;
+  float: left;
+}
+
+#horizontal_carousel .previous_button {
+  float:left;
+  margin: 5px;
+  padding: 2px;
+  width: 16px;
+  height: 16px;
+  background: url(<?php echo $registry->getImageDir() ?>/slideshow_prev.png);
+  background-repeat: no-repeat;
+  z-index: 100;
+  cursor: pointer;
+}
+
+#horizontal_carousel .previous_button_over {
+  background-color: #e9e9e9;
+}
+
+#horizontal_carousel .previous_button_disabled {
+  cursor: crosshair;
+}
+
+#horizontal_carousel .next_button {
+  float:left;
+  margin: 5px;
+  padding: 2px;
+  width: 16px;
+  height: 16px;
+  background: url(<?php echo $registry->getImageDir() ?>/slideshow_next.png);
+  background-repeat: no-repeat;
+  z-index: 100;
+  cursor: pointer;
+}
+
+#horizontal_carousel .next_button_over {
+  background-color: #e9e9e9;
+}
+
+#horizontal_carousel .next_button_disabled {
+  cursor: crosshair;
+}
+
+#spinner {
+  position: absolute;
+  top: 0px;
+  left: 50px;
+  width: 100%;
+  background: #FFF;
+  z-index: 10;
+  text-align: center;
+  font-size: 150%;
+  opacity: 0;
+}
+
+</style>
+<?php endif;?>
\ No newline at end of file
diff --git a/ansel/templates/list/footer.inc b/ansel/templates/list/footer.inc
new file mode 100755 (executable)
index 0000000..1fe0470
--- /dev/null
@@ -0,0 +1,2 @@
+</tr></table>
+<br />
diff --git a/ansel/templates/list/header.inc b/ansel/templates/list/header.inc
new file mode 100644 (file)
index 0000000..4426ca4
--- /dev/null
@@ -0,0 +1,7 @@
+<div class="header">
+ <span class="rightFloat">
+  <?php ($galleries_perpage < $num_galleries) ? printf(_("%d to %d of %d Galleries"), $start, $end, $num_galleries) : printf(ngettext("%d Gallery", "%d Galleries", $num_galleries), $num_galleries) ?>
+ </span>
+ <?php echo @htmlspecialchars($list_title, ENT_COMPAT, Horde_Nls::getCharset()) . ' ' . Horde::link(Horde::applicationUrl($refresh_link), _("Refresh List")) . Horde::img('reload.png', _("Refresh List"), null, $registry->getImageDir('horde')) . '</a>' ?>
+</div>
+<table width="100%" style="background:<?php echo $styleDef['background'] ?>;"><tr>
diff --git a/ansel/templates/list/pager.inc b/ansel/templates/list/pager.inc
new file mode 100755 (executable)
index 0000000..21825dd
--- /dev/null
@@ -0,0 +1,4 @@
+<?php
+if ($num_galleries && $pager = $gallery_pager->render()) {
+    echo $pager;
+}
diff --git a/ansel/templates/menu.inc b/ansel/templates/menu.inc
new file mode 100644 (file)
index 0000000..e04ca0e
--- /dev/null
@@ -0,0 +1,6 @@
+<div id="menu">
+ <?php echo Ansel::getMenu('string') ?>
+</div>
+
+<?php
+$GLOBALS['notification']->notify(array('listeners' => 'status'));
diff --git a/ansel/templates/prefs/default_category_select.inc b/ansel/templates/prefs/default_category_select.inc
new file mode 100644 (file)
index 0000000..e6dbdc9
--- /dev/null
@@ -0,0 +1,8 @@
+<?php
+
+require_once 'Horde/Prefs/CategoryManager.php';
+$cManager = new Prefs_CategoryManager();
+$categories = $cManager->getSelect('default_category_select', $GLOBALS['prefs']->getValue('default_category'));
+
+echo _("Default category for galleries");
+echo $categories;
diff --git a/ansel/templates/prefs/default_gallerystyle_select.inc b/ansel/templates/prefs/default_gallerystyle_select.inc
new file mode 100644 (file)
index 0000000..1305d2e
--- /dev/null
@@ -0,0 +1,4 @@
+<?php
+$style_select = Ansel::getStyleSelect('default_gallerystyle_select', $GLOBALS['prefs']->getValue('default_gallerystyle'));
+echo _("Default style for galleries");
+echo $style_select . '<br />';
\ No newline at end of file
diff --git a/ansel/templates/rss/rss.inc b/ansel/templates/rss/rss.inc
new file mode 100644 (file)
index 0000000..cda17d4
--- /dev/null
@@ -0,0 +1,26 @@
+<?php echo '<?xml version="1.0" encoding="' . $charset . '"?>' ?>
+
+<?php echo '<?xml-stylesheet href="' . $xsl . '" type="text/xsl"?>' ?>
+
+<rss version="0.91">
+ <channel>
+  <title><?php echo $stream_name ?></title>
+  <description><?php echo $stream_desc ?></description>
+  <image>
+   <url><?php echo $image_url ?></url>
+   <link><?php echo $image_link ?></link>
+   <title><?php echo $stream_desc ?></title>
+  </image>
+  <link><?php echo $stream_official ?></link>
+  <atom:link rel="self" type="application/rss+xml" title="<?php echo $stream_name ?>" href="<?php echo $stream_rss ?>" xmlns:atom="http://www.w3.org/2005/Atom"></atom:link>
+  <pubDate><?php echo $stream_updated ?></pubDate>
+  <?php foreach($imgs as $image): ?>
+  <item>
+   <title><?php echo $image['filename'] ?></title>
+   <description><?php echo htmlspecialchars('<img src="' . $image['url'] . '" />') . $image['caption'] ?></description>
+   <link><?php echo htmlspecialchars($image['link']) ?></link>
+  </item>
+  <?php endforeach; ?>
+ </channel>
+</rss>
+
diff --git a/ansel/templates/rss/rss2.inc b/ansel/templates/rss/rss2.inc
new file mode 100755 (executable)
index 0000000..a524745
--- /dev/null
@@ -0,0 +1,38 @@
+<?php echo '<?xml version="1.0" encoding="' . $charset . '"?>' ?>
+
+<?php echo '<?xml-stylesheet href="' . $xsl . '" type="text/xsl"?>' ?>
+
+<rss version="2.0" xmlns:media="http://search.yahoo.com/mrss/" xmlns:georss="http://www.georss.org/georss">
+ <channel>
+  <title><?php echo $stream_name ?></title>
+  <description><?php echo $stream_desc ?></description>
+  <image>
+   <url><?php echo $image_url ?></url>
+   <link><?php echo $image_link ?></link>
+   <title><?php echo $stream_name ?></title>
+  </image>
+  <link><?php echo $stream_official ?></link>
+  <atom:link rel="self" type="application/rss+xml" title="<?php echo $stream_name ?>" href="<?php echo $stream_rss ?>" xmlns:atom="http://www.w3.org/2005/Atom"></atom:link>
+  <pubDate><?php echo $stream_updated ?></pubDate>
+  <generator><?php echo $ansel ?></generator>
+  <?php foreach($imgs as $image): ?>
+  <item>
+   <title><?php echo $image['filename'] ?></title>
+   <description><?php echo htmlspecialchars('<img src="' . $image['url'] . '" />') . $image['caption'] ?></description>
+   <link><?php echo htmlspecialchars($image['link']) ?></link>
+   <?php if (!empty($image['author'])): ?>
+   <author><?php echo $image['author'] ?></author>
+   <?php endif; ?>
+   <guid isPermaLink="true"><?php echo htmlspecialchars($image['link']) ?></guid>
+   <media:content url="<?php echo $image['url'] ?>" type="<?php echo $image['type'] ?>"/>
+   <media:thumbnail url="<?php echo $image['thumb'] ?>"/>
+   <media:title type="plain"><?php echo $image['filename'] ?></media:title>
+   <media:description type="html"><?php echo $image['caption'] ?></media:description>
+   <?php if(!empty($image['latitude'])):?>
+   <georss:point><?php echo $image['latitude'] . ' ' . $image['longitude']?></georss:point>
+   <?php endif;?>
+  </item>
+  <?php endforeach; ?>
+ </channel>
+</rss>
+
diff --git a/ansel/templates/tile/dategallery.inc b/ansel/templates/tile/dategallery.inc
new file mode 100644 (file)
index 0000000..f3f83be
--- /dev/null
@@ -0,0 +1,4 @@
+<div class="gallery-tile">
+ <?php echo $image_link ?>
+ <div class="gallery-tile-caption"><?php echo $text_link ?><br /> (<?php echo (int)$gallery_count ?>)</div>
+</div>
diff --git a/ansel/templates/tile/face.inc b/ansel/templates/tile/face.inc
new file mode 100755 (executable)
index 0000000..f8d9d7c
--- /dev/null
@@ -0,0 +1,38 @@
+<div class="face-tile">
+<?php
+$face_url = $faces->getLink($face);
+$facename = htmlspecialchars($face['face_name']);
+echo '<a href="' . $face_url . '" title="' . $facename . '">'
+    . '<img src="' . $faces->getFaceUrl($face['image_id'], $face_id)
+    . '" style="padding: 5px; width: 50px; height: 50px;" alt="' . $facename . '" /></a><br />';
+if (isset($face['similarity'])) {
+    echo '<span class="similarity-';
+
+    // the values if similarity are fliped
+    if ($face['similarity'] >= PUZZLE_CVEC_SIMILARITY_HIGH_THRESHOLD) {
+        echo 'lower';
+    } elseif ($face['similarity'] >= PUZZLE_CVEC_SIMILARITY_THRESHOLD) {
+        echo 'low';
+    } elseif ($face['similarity'] >= PUZZLE_CVEC_SIMILARITY_LOW_THRESHOLD) {
+        echo 'medium';
+    } else {
+        echo 'high';
+    }
+
+    echo '">' . number_format((1 - $face['similarity']) * 100) . '%</span><br />';
+}
+if ($face['face_name']) {
+    echo '<a href="' . $face_url . '" title="' . _("Find other photos this face appears in") . '">' . _("Others") . '</a><br />';
+} elseif ($GLOBALS['conf']['report_content']['driver']) {
+    echo '<a href="' . Horde_Util::addParameter(Horde::applicationUrl('faces/claim.php'), 'face', $face_id)
+                . '" title="' . _("Tell us who is in this photo") . '">' . _("Claim") . '</a><br />';
+
+    echo '<a href="' . Horde_Util::addParameter(Horde::applicationUrl('faces/report.php'), 'face', $face_id)
+                . '" title="' . _("Report abuse or submit a removal request") . '">' . _("Report") . '</a><br />';
+}
+if ($conf['faces']['search']) {
+    echo '<a href="' . Horde_Util::addParameter(Horde::applicationUrl('faces/search/image_search.php'), 'face_id', $face_id)
+        . '" title="' . _("Find similar") . '">' . _("Similar faces") . '</a>';
+}
+?>
+</div>
diff --git a/ansel/templates/tile/gallery.inc b/ansel/templates/tile/gallery.inc
new file mode 100755 (executable)
index 0000000..a928a32
--- /dev/null
@@ -0,0 +1,14 @@
+<div class="gallery-tile">
+ <?php echo $image_link ?>
+ <div class="gallery-tile-caption"><?php echo $text_link ?> (<?php echo (int)$gallery_count ?>)</div>
+ <div class="gallery-tile-stats">
+<?php if (isset($properties_link)): ?>
+  <?php echo $properties_link ?><br />
+<?php endif; ?>
+  <?php echo $created ?><br />
+  <?php echo $modified ?><br />
+<?php if (isset($owner_link)): ?>
+  <?php echo _("Owner") ?>: <?php echo $owner_link ?> <br />
+<?php endif; ?>
+ </div>
+</div>
diff --git a/ansel/templates/tile/gallerymini.inc b/ansel/templates/tile/gallerymini.inc
new file mode 100755 (executable)
index 0000000..58f1b12
--- /dev/null
@@ -0,0 +1,4 @@
+<div style="padding: 5px; background-color: <?php echo $background_color ?>;">
+ <span style="width: 50%;"><?php echo $image_link ?></span>
+ <span style="width: 50%;"><?php echo $text_link ?></span>
+</div>
diff --git a/ansel/templates/tile/image.inc b/ansel/templates/tile/image.inc
new file mode 100644 (file)
index 0000000..c8f305b
--- /dev/null
@@ -0,0 +1,24 @@
+<?php echo '<div class="image-tile" id="imagetile_' . (int)$image->id . '">' . Horde::link($view_url, $image->filename, '', '', $imgOnClick, $title, '', $imgAttributes) . '<img src="' . $thumb_url  . '" alt="' . htmlspecialchars($image->filename) . '" /></a>'; ?>
+ <div style="valign: bottom;">
+ <div class="image-tile-caption" id="<?php echo (int)$image->id . 'caption'?>"><?php echo $imageCaption ?></div>
+<?php if (Horde_Auth::getAuth() || $option_select): ?>
+<div><label><input type="checkbox" class="checkbox" name="image[<?php echo (int)$image->id ?>]" /> <?php echo _("Select for Action") ?></label></div>
+<?php endif; ?>
+<?php if ($view != 'Gallery'): ?>
+<div><?php echo _("From: ") . Horde::link($gallery_url) . $parent->get('name') ?></a></div>
+<?php endif; ?>
+<?php if (($conf['comments']['allow'] == 'all' || ($conf['comments']['allow'] == 'authenticated' && Horde_Auth::getAuth())) && empty($params['hide_comments'])): ?>
+ <div><?php echo Horde::link($img_view_url . '#messagediv', _("View or leave comments")) ?> <?php printf(ngettext("%d comment", "%d comments", $image->commentCount), $image->commentCount) ?></a></div>
+<?php endif;
+if ($option_select): ?>
+ <div>
+  <span class="light">
+   <?php if ($option_edit): ?>
+    <?php echo Horde::link(Horde::applicationUrl(Horde_Util::addParameter($image_url, array('actionID' => 'modify'))), '', '', '_blank', 'popup(this.href); return false;') . _("Properties") ?></a>
+    | <?php echo Horde::link(Horde::applicationUrl(Horde_Util::addParameter($image_url, 'actionID', 'editimage'))) . _("Edit") ?></a>
+   <?php endif; ?>
+  </span>
+ </div>
+<?php endif; ?>
+</div>
+</div>
diff --git a/ansel/templates/view/gallery.inc b/ansel/templates/view/gallery.inc
new file mode 100644 (file)
index 0000000..ee100b1
--- /dev/null
@@ -0,0 +1,172 @@
+<?php if (empty($this->view->_params['api'])): ?>
+<script type="text/javascript">
+var loading;
+
+function selectAll()
+{
+    for (var i = 0; i < document.forms['gallery'].elements.length; ++i) {
+        document.forms['gallery'].elements[i].checked = true;
+    }
+}
+
+function selectNone()
+{
+    for (var i = 0; i < document.forms['gallery'].elements.length; ++i) {
+        document.forms['gallery'].elements[i].checked = false;
+    }
+}
+
+function deleteSelected()
+{
+    var haveImages = false;
+    for (var i = 0; i< document.forms['gallery'].elements.length; ++i) {
+        if (document.forms['gallery'].elements[i].checked) {
+            haveImages = true;
+            break;
+        }
+    }
+    if (!haveImages) {
+        alert('<?php echo addslashes(_("You must choose photos to delete.")) ?>');
+        return false;
+    }
+    if (loading == null && confirm('<?php echo addslashes(_("Are you sure you want to delete the selected photos?")) ?>')) {
+        loading = true;
+        document.forms['gallery'].actionID.value = 'delete';
+        document.forms['gallery'].submit();
+        loading = false;
+    }
+}
+
+function moveSelected()
+{
+    if (document.forms['gallery'].new_gallery.selectedIndex == 0) {
+        window.alert('<?php echo addslashes(_("You must choose a gallery to move photos to.")) ?>');
+        return false;
+    }
+
+    if (!loading) {
+        loading = true;
+        document.forms['gallery'].actionID.value = 'move';
+        document.forms['gallery'].submit();
+        loading = false;
+    }
+}
+
+function copySelected()
+{
+    if (document.forms['gallery'].new_gallery.selectedIndex == 0) {
+        window.alert('<?php echo addslashes(_("You must choose a gallery to move photos to.")) ?>');
+        return false;
+    }
+
+    if (!loading) {
+        loading = true;
+        document.forms['gallery'].actionID.value = 'copy';
+        document.forms['gallery'].submit();
+        loading = false;
+    }
+}
+
+function editDates()
+{
+    var haveImages = false;
+    var imageDateUrl = '<?php echo Horde::applicationUrl(Horde_Util::addParameter('edit_dates.php', array('gallery' => $this->galleryId))) ?>';
+    for (var i = 0; i< document.forms['gallery'].elements.length; ++i) {
+       if (document.forms['gallery'].elements[i].checked == true &&
+           document.forms['gallery'].elements[i].type == 'checkbox') {
+            haveImages = true;
+            imageDateUrl = imageDateUrl + '&' + document.forms['gallery'].elements[i].name + '=true';
+        }
+    }
+    if (!haveImages) {
+        alert('<?php echo addslashes(_("You must choose photos to edit.")) ?>');
+        return false;
+    }
+    popup(imageDateUrl, 600, 250);
+}
+
+function downloadSelected()
+{
+    if (!loading) {
+        loading = true;
+        document.forms['gallery'].actionID.value = 'downloadzip';
+        document.forms['gallery'].submit();
+        loading = false;
+    }
+
+}
+
+
+</script>
+<?php endif; ?>
+<div id="galleryHeader" class="header">
+  <?php if ($this->numTiles): ?>
+  <span class="rightFloat">
+  <?php echo $this->numTiles > $this->perpage ? sprintf(_("%d to %d of %d items"), $this->pagestart, $this->pageend, $this->numTiles) : sprintf(ngettext("%d item", "%d items", $this->numTiles), $this->numTiles) ?>
+  </span>
+  <?php endif; ?>
+  <?php echo Ansel::getBreadCrumbs(); ?>
+</div>
+<?php if ($this->view->gallery->get('desc')): ?>
+    <div id="galleryDescription" class="gallery-desc">
+    <?php echo Horde_Text_Filter::filter($this->view->gallery->get('desc'), 'text2html', array('parselevel' => Horde_Text_Filter_Text2html::MICRO)) ?>
+    </div>
+<?php endif; ?>
+<table cellspacing="0" width="100%">
+ <tr>
+  <td style="vertical-align:top;width:<?php echo ($this->view->countWidgets() ? "75%" : "100%") ?>;">
+    <?php if (empty($this->view->_params['api']) && $option_select && $this->numTiles): ?>
+    <form name="gallery" action="<?php echo Horde::applicationUrl('image.php', true)?>" method="get">
+    <?php echo Horde_Util::formInput() ?>
+    <input type="hidden" name="actionID" value="" />
+    <input type="hidden" name="gallery" value="<?php echo htmlspecialchars($this->galleryId) ?>" />
+    <input type="hidden" name="page" value="<?php echo htmlspecialchars($this->page) ?>" />
+    <input type="hidden" name="year" value="<?php echo (empty($this->view->_params['year']) ? 0 : $this->view->_params['year']) ?>" />
+    <input type="hidden" name="month" value="<?php echo (empty($this->view->_params['month']) ? 0 : $this->view->_params['month']) ?>" />
+    <input type="hidden" name="day" value="<?php echo (empty($this->view->_params['day']) ? 0 : $this->view->_params['day']) ?>" />
+
+    <?php endif; ?>
+    <?php if (!empty($option_select) && $this->numTiles): ?>
+    <table class="anselActions" cellspacing="0" width="100%"><tr><td>
+    <?php echo '<span class="widget">' . _("Select:") . '</span> '
+              . Horde::link('#', '', 'widget', '', 'selectAll(); return false;')
+              . _("All") . '</a> | '
+              . Horde::link('#', '', 'widget', '', 'selectNone(); return false;')
+              . _("None") . '</a>'; ?>
+    </td>
+    <td align="right">
+      <?php echo implode(' | ', $action_links); ?>
+      <select name="new_gallery"><option value="-1"><?php echo _("Selected photos to") ?></option><?php echo Ansel::selectGalleries(null, PERMS_EDIT) ?></select>
+      </td></tr></table>
+    <?php endif; ?>
+    <?php if (!$this->numTiles): ?>
+      <div class="text"><em><?php echo _("There are no photos in this gallery.") ?></em></div>
+    <?php else: ?>
+      <table width="100%" style="background-color:<?php echo $this->style['background'] ?>;">
+        <tr><td colspan="<?php echo $tilesperrow ?>"><?php echo $pager->render() ?></td></tr>
+        <tr>
+          <?php
+          foreach ($this->children as $child) {
+              echo '<td width="' . $cellwidth . '%" class="ansel-tile">'
+                  . $child->getTile($this->view->gallery, $this->style['name'], false, $this->view->_params) . '</td>';
+              if (!(++$count % $tilesperrow)) {
+                echo '</tr><tr>';
+              }
+          }
+          while ($count % $tilesperrow) {
+              echo '<td width="' . $cellwidth . '%" valign="top">&nbsp;</td>';
+              $count++;
+          }?>
+        </tr>
+        <tr><td colspan="<?php echo $tilesperrow ?>"><?php echo $pager->render() ?></td></tr>
+      </table>
+    <?php endif; ?>
+    <?php if (!empty($option_select) && $this->numTiles): ?>
+      </form>
+    <?php endif; ?>
+   </td>
+   <td class="anselWidgets">
+    <?php echo $this->view->renderWidgets() ?>
+   </td>
+  </tr>
+ </table>
diff --git a/ansel/templates/view/gallerylightbox.inc b/ansel/templates/view/gallerylightbox.inc
new file mode 100644 (file)
index 0000000..c3e1f93
--- /dev/null
@@ -0,0 +1,203 @@
+<?php if (empty($this->view->_params['api'])): ?>
+<script type="text/javascript">
+var loading;
+
+function selectAll()
+{
+    for (var i = 0; i < document.gallery.elements.length; ++i) {
+        document.gallery.elements[i].checked = true;
+    }
+}
+
+function selectNone()
+{
+    for (var i = 0; i < document.gallery.elements.length; ++i) {
+        document.gallery.elements[i].checked = false;
+    }
+}
+
+function deleteSelected()
+{
+    var haveImages = false;
+    for (var i = 0; i< document.gallery.elements.length; ++i) {
+        if (document.gallery.elements[i].checked) {
+            haveImages = true;
+            break;
+        }
+    }
+    if (!haveImages) {
+        alert('<?php echo addslashes(_("You must choose photos to delete.")) ?>');
+        return false;
+    }
+
+    if (loading == null && confirm('<?php echo addslashes(_("Are you sure you want to delete the selected photos?")) ?>')) {
+        loading = true;
+        document.gallery.actionID.value = 'delete';
+        document.gallery.submit();
+    }
+}
+
+function moveSelected()
+{
+    if (document.gallery.new_gallery.selectedIndex == 0) {
+        window.alert('<?php echo addslashes(_("You must choose a gallery to move photos to.")) ?>');
+        return false;
+    }
+
+    if (loading == null) {
+        loading = true;
+        document.gallery.actionID.value = 'move';
+        document.gallery.submit();
+    }
+}
+
+function copySelected()
+{
+    if (document.gallery.new_gallery.selectedIndex == 0) {
+        window.alert('<?php echo addslashes(_("You must choose a gallery to move photos to.")) ?>');
+        return false;
+    }
+
+    if (loading == null) {
+        loading = true;
+        document.gallery.actionID.value = 'copy';
+        document.gallery.submit();
+    }
+}
+
+function editDates()
+{
+    var haveImages = false;
+    var imageDateUrl = '<?php echo Horde::applicationUrl(Horde_Util::addParameter('edit_dates.php', array('gallery' => $this->galleryId))) ?>';
+    for (var i = 0; i< document.forms['gallery'].elements.length; ++i) {
+       if (document.forms['gallery'].elements[i].checked == true &&
+           document.forms['gallery'].elements[i].type == 'checkbox') {
+            haveImages = true;
+            imageDateUrl = imageDateUrl + '&' + document.forms['gallery'].elements[i].name + '=true';
+        }
+    }
+    if (!haveImages) {
+        alert('<?php echo addslashes(_("You must choose photos to edit.")) ?>');
+        return false;
+    }
+    popup(imageDateUrl, 600, 250);
+}
+
+function downloadSelected()
+{
+    if (!loading) {
+        loading = true;
+        document.forms['gallery'].actionID.value = 'downloadzip';
+        document.forms['gallery'].submit();
+        loading = false;
+    }
+
+}
+</script>
+<?php endif; ?>
+<script type="text/javascript">
+    //<![CDATA[
+    LightboxOptions = {
+        gallery_json: <?php echo $json; ?>,
+        fileLoadingImage:        '<?php echo $jsvars['graphics_dir'];?>/lightbox/loading.gif',
+        fileBottomNavCloseImage: '<?php echo $jsvars['graphics_dir'];?>/lightbox/closelabel.gif',
+
+        overlayOpacity: 0.8,   // controls transparency of shadow overlay
+        animate: true,         // toggles resizing animations
+        resizeSpeed: 7,        // controls the speed of the image resizing animations (1=slowest and 10=fastest)
+        borderSize: 10,         //if you adjust the padding in the CSS, you will need to update this variable
+
+        // Used to write: Image # of #.
+        labelImage: "<?php echo $jsvars['image_text'];?>",
+        labelOf: "<?php echo $jsvars['of_text'];?>",
+        //URL to return to when the lightbox closes
+        returnURL: "<?php echo $jsvars['gallery_url']; ?>",
+        startPage: "<?php echo $jsvars['start_page']; ?>"
+    };
+    //]]>
+    <?php if($GLOBALS['browser']->getBrowser() != 'msie'):?>
+      document.observe('dom:loaded', function () { document.lb = new Lightbox(LightboxOptions); if (window.location.hash.length) document.lb.start(window.location.hash.substring(1));});
+    <?php else:?>
+      Event.observe(window, 'load', function () { document.lb = new Lightbox(LightboxOptions); if (window.location.hash.length) document.lb.start(window.location.hash.substring(1));});
+    <?php endif; ?>
+</script>
+<div class="header" id="galleryHeader">
+ <?php echo ($this->numTiles ? '<span class="rightFloat">' . ($this->numTiles > $this->perpage ? sprintf(_("%d to %d of %d Items"), $this->pagestart, $this->pageend, $this->numTiles) : sprintf(ngettext("%d Item", "%d Items", $this->numTiles), $this->numTiles)) . '</span>' : '') . Ansel::getBreadCrumbs(); ?>
+</div>
+<?php if ($this->view->gallery->get('desc')): ?>
+<div class="gallery-desc" id="galleryDescription">
+<?php echo Horde_Text_Filter::filter($this->view->gallery->get('desc'), 'text2html', array('parselevel' => Horde_Text_Filter_Text2html::MICRO)) ?>
+</div>
+<?php endif;?>
+<table width="100%" cellspacing="0">
+ <tr>
+  <td style="vertical-align:top;width:<?php echo ($this->view->countWidgets() ? "75%" : "100%") ?>;">
+    <?php if (empty($this->view->_params['api']) && $option_select && $this->numTiles): ?>
+    <form name="gallery" action="<?php echo Horde::applicationUrl('image.php', true)?>" method="get">
+    <?php echo Horde_Util::formInput() ?>
+    <input type="hidden" name="actionID" value="" />
+    <input type="hidden" name="gallery" value="<?php echo $this->galleryId ?>" />
+    <input type="hidden" name="page" value="<?php echo htmlspecialchars($this->page) ?>" />
+    <input type="hidden" name="year" value="<?php echo (empty($this->view->_params['year']) ? 0 : $this->view->_params['year']) ?>" />
+    <input type="hidden" name="month" value="<?php echo (empty($this->view->_params['month']) ? 0 : $this->view->_params['month']) ?>" />
+    <input type="hidden" name="day" value="<?php echo (empty($this->view->_params['day']) ? 0 : $this->view->_params['day']) ?>" />
+    <?php endif; ?>
+    <?php if (!empty($option_select) && $this->numTiles): ?>
+        <table class="anselActions" cellspacing="0" width="100%"><tr><td>
+        <?php printf(_("Select: %s, %s"),
+                       Horde::link('#', _("Select All"), '', '', 'selectAll(); return false;') . _("All") . '</a>',
+                       Horde::link('#', _("Select None"), '', '', 'selectNone(); return false;') . _("None") . '</a>');?>
+       </td><td align="right">
+        <?php if ($option_delete || $option_move || $option_copy) echo _("Actions: ") ?>
+          <?php if ($GLOBALS['conf']['gallery']['downloadzip']): ?>
+            <?php echo Horde::link('#', '', 'widget', '', 'downloadSelected(); return false;') . _("Download selected photos")?></a> |
+          <?php endif; ?>
+          <?php if ($option_edit): ?>
+           <?php echo Horde::link('#', _("Edit Dates"), 'widget', '', 'editDates(); return false;') . _("Edit Dates"); ?></a> |
+          <?php endif; ?>
+          <?php if ($option_delete): ?>
+            <?php echo Horde::link('#', _("Delete"), 'widget', '', 'deleteSelected(); return false;') . _("Delete"); ?></a>
+          <?php endif; ?>
+          <?php if ($option_move): ?>
+          | <?php echo Horde::link('#', _("Move"), 'widget', '', 'moveSelected(); return false;') . _("Move"); ?></a>
+          <?php endif; ?>
+          <?php if ($option_copy): ?>
+     |      <?php echo Horde::link('#', _("Copy"), 'widget', '', 'copySelected(); return false;') . _("Copy"); ?></a>
+          <?php endif; ?>
+          <select name="new_gallery"><option value="-1"><?php echo _("Selected photos to") ?></option><?php echo Ansel::selectGalleries($this->galleryId, PERMS_EDIT) ?></select>
+          </td></tr></table>
+        <?php endif; ?>
+     <?php if (!$this->numTiles): ?>
+      <div class="text"><em><?php echo _("There are no photos in this gallery.") ?></em></div>
+     <?php else: ?>
+      <table width="100%" style="background-color:<?php echo $this->style['background'] ?>;">
+       <tr><td colspan="<?php echo $tilesperrow ?>"><?php echo $pager->render() ?></td></tr>
+       <tr>
+       <?php
+       foreach ($this->children as $child) {
+           echo '<td width="' . $cellwidth . '%" class="ansel-tile">'
+               . $child->getTile($this->view->gallery, $this->style['name'], false, $this->view->_params) . '</td>';
+           if (!(++$count % $tilesperrow)) {
+                echo '</tr><tr>';
+           }
+       }
+       while ($count % $tilesperrow) {
+          echo '<td width="' . $cellwidth . '%" valign="top">&nbsp;</td>';
+          $count++;
+       }?>
+       </tr>
+       <tr><td colspan="<?php echo $tilesperrow ?>"><?php echo $pager->render() ?></td></tr>
+      </table>
+     <?php endif; ?>
+     <?php if (!empty($option_select) && $this->numTiles): ?>
+       </form>
+     <?php endif; ?>
+   </td>
+   <td class="anselWidgets">
+     <?php echo $this->view->renderWidgets() ?>
+   </td>
+ </tr>
+</table>
+<?php if ($this->view->countWidgets()): ?>
+  </div>
+<?php endif; ?>
diff --git a/ansel/templates/view/galleryvimeo.inc b/ansel/templates/view/galleryvimeo.inc
new file mode 100644 (file)
index 0000000..ed30277
--- /dev/null
@@ -0,0 +1,62 @@
+<script type="text/javascript">
+    //<![CDATA[
+     var clips = [];
+    <?php foreach(array_reverse($this->_thumbs) as $video):?>
+        clips.push(<?php echo $this->_json[$video['clip_id']]?>);
+    <?php endforeach ?>
+
+    function showVideo(index)
+    {
+         RedBox.showHtml('<div id="RB_info">' + clips[index]['html'] + '</div>');
+         $('RB_window').setStyle({top: '100px'});
+         $('RB_overlay').observe('click', function() {RedBox.close()});
+         return false;
+    }
+    //]]>
+
+</script>
+<div class="header" id="galleryHeader">
+ <?php echo ($this->numTiles ? '<span class="rightFloat">' . ($this->numTiles > $this->perpage ? sprintf(_("%d to %d of %d Items"), $this->pagestart, $this->pageend, $this->numTiles) : sprintf(ngettext("%d Item", "%d Items", $this->numTiles), $this->numTiles)) . '</span>' : '') . Ansel::getBreadCrumbs(); ?>
+</div>
+<?php if ($this->view->gallery->get('desc')): ?>
+<div class="gallery-desc" id="galleryDescription">
+<?php echo Text_Filter::filter($this->view->gallery->get('desc'), 'text2html', array('parselevel' => TEXT_HTML_MICRO)) ?>
+</div>
+<?php endif;?>
+<table width="100%" cellspacing="0">
+ <tr>
+  <td style="vertical-align:top;width:<?php echo ($this->view->countWidgets() ? "75%" : "100%") ?>;">
+     <?php if (!$this->numTiles): ?>
+      <div class="text"><em><?php echo _("There are no videos in this gallery.") ?></em></div>
+     <?php else: ?>
+      <table width="100%" style="background-color:<?php echo $this->style['background'] ?>;">
+       <tr><td colspan="<?php echo $tilesperrow ?>"><?php echo $pager->render() ?></td></tr>
+       <tr>
+       <?php
+       $cnt = 0;
+       foreach ($this->children as $child) {
+           echo '<td width="' . $cellwidth . '%" class="ansel-tile">'
+               . $this->getTile($child, new Horde_Support_Array($this->_thumbs[$child->caption]), $cnt++) . '</td>';
+           if (!(++$count % $tilesperrow)) {
+                echo '</tr><tr>';
+           }
+       }
+       while ($count % $tilesperrow) {
+          echo '<td width="' . $cellwidth . '%" valign="top">&nbsp;</td>';
+          $count++;
+       }?>
+       </tr>
+       <tr><td colspan="<?php echo $tilesperrow ?>"><?php echo $pager->render() ?></td></tr>
+      </table>
+     <?php endif;?>
+   </td>
+   <td class="anselWidgets">
+      <?php if (empty($this->view->_params['api'])):?>
+     <?php echo $this->view->renderWidgets(); ?>
+     <?php endif;?>
+   </td>
+ </tr>
+</table>
+<?php if ($this->view->countWidgets()): ?>
+  </div>
+<?php endif; ?>
diff --git a/ansel/templates/view/image.inc b/ansel/templates/view/image.inc
new file mode 100644 (file)
index 0000000..c621c04
--- /dev/null
@@ -0,0 +1,144 @@
+<script type="text/javascript">
+document.observe('dom:loaded', function() {
+        <?php if (empty($this->_params['hide_slideshow'])): ?>
+        $$('.ssPlay').each(function(n) { n.show();});
+        <?php endif; ?>
+        Event.observe($('photodiv'), 'load', function() {
+                new Effect.Appear($('photodiv'), { duration: 0.5,
+                                                   afterFinish: function() {$$('.imgloading').each(function(n) { n.setStyle({visibility: 'hidden'});});
+                                                                            new Effect.Appear($('Caption'), { duration: 0.5 });
+                                                                             }});
+                nextImg = new Image();
+                prvImg = new Image();
+                nextImg.src = "<?php echo $nextImgUrl ?>";
+                prvImg.src = "<?php echo $prvImgUrl ?>";
+        });
+        new Effect.Opacity('photodiv', {to: 0, duration: 0.5, afterFinish: function() {$('photodiv').src = "<?php echo $imageUrl ?>"} });
+
+        // Arrow keys for navigation
+        document.observe('keydown', arrowHandler);
+});
+
+function arrowHandler(e)
+{
+    if (e.altKey || e.shiftKey || e.ctrlKey) {
+        return;
+    }
+
+    theElement = Event.element(e);
+    switch (theElement.tagName) {
+    case 'INPUT':
+    case 'SELECT':
+    case 'TEXTAREA':
+        return;
+    }
+    switch (e.keyCode || e.charCode) {
+    case Event.KEY_LEFT:
+        if ($('PrevLink')) {
+            document.location.href = $('PrevLink').href;
+        }
+        break;
+
+    case Event.KEY_RIGHT:
+        if ($('NextLink')) {
+            document.location.href = $('NextLink').href;
+        }
+        break;
+    }
+}
+</script>
+<h1 class="header" id="PhotoName"><?php echo Ansel::getBreadCrumbs(); ?></h1>
+<table style="width: 100%; border-collapse: collapse">
+ <tr>
+  <td valign="top" style="background-color:<?php echo $style['background']; ?>;">
+    <?php
+        $needSeperator = false;
+        $actionHTML = '';
+        if ($this->gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT) && empty($this->_params['api'])) {
+            $needSeperator = true;
+            $actionHTML .= Horde::link(Horde::applicationUrl(Horde_Util::addParameter($imageActionUrl, array('actionID' => 'modify', 'ret' => 'image'))), '', '', '_blank', 'popup(this.href); return false;', '', '', array('id' => 'image_properties_link')) . _("Properties") . '</a>';
+            $actionHTML .= ' | ' . Horde::link(Horde::applicationUrl(Horde_Util::addParameter($imageActionUrl, 'actionID', 'editimage')), '', '', '', '', '', '', array('id' => 'image_edit_link')) . _("Edit") . '</a>';
+        }
+        if ($this->gallery->hasPermission(Horde_Auth::getAuth(), PERMS_DELETE) && empty($this->_params['api'])) {
+            $actionHTML .= ' | ' . Horde::link(Horde::applicationUrl(Horde_Util::addParameter($imageActionUrl, 'actionID', 'delete')), '', '', '', 'return window.confirm(\'' . addslashes(sprintf(_("Do you want to permanently delete ''%s''?"), $this->resource->filename)) . '\');', '', '', array('id' => 'image_delete_link')) . _("Delete") . '</a>';
+        }
+        if (!empty($conf['ecard']['enable']) && !empty($ecardurl)) {
+            $actionHTML .= (empty($this->_params['api']) && $needSeperator ? ' | ' : '') . Horde::link($ecardurl, '', '', '_blank', '', '', '', array('id' => 'image_ecard_link')) . _("Send an Ecard"); $needSeperator = true . '</a>';
+        }
+        if ($this->gallery->canDownload()) {
+            $actionHTML .= (empty($this->_params['api']) && $needSeperator ? ' | ' : '') . Horde::link(Horde::applicationUrl(Horde_Util::addParameter('img/download.php', 'image', $imageId), true), '', '', '', '', '', '', array('id' => 'image_download_link')) . _("Download Original Photo"); $needSeperator = true . '</a>';
+        }
+        if (empty($this->_params['api']) && $this->gallery->get('owner') != Horde_Auth::getAuth() &&
+            !empty($GLOBALS['conf']['report_content']['driver']) &&
+            (($conf['report_content']['allow'] == 'authenticated' && Horde_Auth::isAuthenticated()) ||
+             $conf['report_content']['allow'] == 'all')) {
+
+            $reporturl = Horde_Util::addParameter(
+                Horde::applicationUrl('report.php'),
+                                      array('gallery' =>  $this->gallery->id,
+                                      'image' => $imageId));
+
+            $actionHTML .=  ($needSeperator ? ' | ' : '' ) . Horde::link($reporturl) . _("Report") . '</a>';
+        }
+        if (strlen($actionHTML)) {
+            echo '<div class="control anselActions" style="text-align:center;">' . $actionHTML . '</div>';
+        }
+?>
+    <div class="slideControls">
+     <?php echo Horde::fullSrcImg('loading.gif', array('dir' => $registry->getImageDir('horde'), 'attr' => 'class="imgloading"'));?>
+     <?php
+     if (empty($this->_params['hide_slideshow'])) {
+         echo '<a style="display:none;" href="' . $slideshow_url . '" class="ssPlay" title="' . _("Start Slideshow") . '">' .
+               Horde::fullSrcImg('slideshow_play.png', array('attr' => 'alt="' .  _("Play") . '"')) . '</a>';
+     }
+     echo '<a href="' . $prev_url . '" alt="<" id="PrevLink" title="' . _("Previous") . '">' .
+           Horde::fullSrcImg('slideshow_prev.png', array('attr' => 'alt="' . _("Previous") . '"')) . '</a>';
+     echo '<a href="' . $next_url . '" alt=">" id="NextLink"  title="' . _("Next") . '">' .
+           Horde::fullSrcImg('slideshow_next.png', array('attr' => 'alt="' . _("Next") . '"')) . '</a>';
+     echo '<a href="' . $galleryurl . '" id="GalleryLink" title="' . _("Back to gallery") . '">' .
+           Horde::fullSrcImg('galleries.png', array('attr' => 'alt="' . _("Back to gallery") . '"')) . '</a>';
+     ?>
+
+    </div>
+    <div id="Container">
+     <noscript>
+      <?php echo '<img src="' . $imageUrl . '" alt="' . htmlspecialchars($this->resource->filename) . '" />' ?>
+     </noscript>
+     <?php echo Horde::img('blank.gif', '', array('id' => 'photodiv', 'width' => $geometry['width'], 'height' => $geometry['height']), $registry->getImageDir('horde')) ?>
+     <div id="CaptionContainer" style="width:<?php echo $geometry['width']?>px;">
+     <p id="Caption" style="display:none;">
+       <?php echo Horde_Text_Filter::filter($this->resource->caption, 'text2html', array('parselevel' => Horde_Text_Filter_Text2html::MICRO)) ?>
+     </p>
+     </div>
+    </div>
+    <div class="slideControls">
+     <?php echo Horde::fullSrcImg('loading.gif', array('dir' => $registry->getImageDir('horde'), 'attr' => 'class="imgloading"'));?>
+     <?php
+     if (empty($this->_params['hide_slideshow'])) {
+         echo '<a style="display:none;" href="' . $slideshow_url . '" class="ssPlay" title="' . _("Start Slideshow") . '">' .
+               Horde::fullSrcImg('slideshow_play.png', array('attr' => 'alt="' .  _("Play") . '"')) . '</a>';
+     }
+     echo '<a href="' . $prev_url . '" alt="<" id="PrevLink" title="' . _("Previous") . '">' .
+           Horde::fullSrcImg('slideshow_prev.png', array('attr' => 'alt="' . _("Previous") . '"')) . '</a>';
+     echo '<a href="' . $next_url . '" alt=">" id="NextLink"  title="' . _("Next") . '">' .
+           Horde::fullSrcImg('slideshow_next.png', array('attr' => 'alt="' . _("Next") . '"')) . '</a>';
+     echo '<a href="' . $galleryurl . '" id="GalleryLink" title="' . _("Back to gallery") . '">' .
+           Horde::fullSrcImg('galleries.png', array('attr' => 'alt="' . _("Back to gallery") . '"')) . '</a>';
+     ?>
+    </div>
+    <?php
+     if ($prefs->getValue('showexif')) {
+             echo '<br class="spacer" /><div id="exif">' . $exifHtml . '</div>';
+     } ?>
+     <div id="messagediv">
+      <?php echo $commentHtml; ?>
+     </div>
+  </td>
+  <?php if ($this->countWidgets()): ?>
+  <td width="20%" valign="top">
+    <!-- Widgets -->
+   <?php echo $this->renderWidgets() ?>
+  </td>
+  <?php endif ?>
+ </tr>
+</table>
diff --git a/ansel/templates/view/list.inc b/ansel/templates/view/list.inc
new file mode 100644 (file)
index 0000000..5c187a6
--- /dev/null
@@ -0,0 +1,68 @@
+<div id="listHeader" class="header">
+ <span id="listCounts" class="rightFloat">
+   <?php if ($galleries_perpage < $num_galleries) printf(_("%d to %d of %d Galleries"), $start, $end, $num_galleries); else printf(ngettext("%d Gallery", "%d Galleries", $num_galleries), $num_galleries) ?>
+ </span>
+ <?php if (empty($this->_params['api'])) echo Ansel::getBreadcrumbs() . ' ' . Horde::link(Horde::applicationUrl($refresh_link), _("Refresh List")) . Horde::img('reload.png', _("Refresh List"), null, $registry->getImageDir('horde')) . '</a>' ?>
+</div>
+<?php if (empty($this->_params['api'])): ?>
+<div class="anselActions widget">
+  <?php if ($groupby == 'none'): ?>
+  <div class="rightFloat">
+    <?php echo _("Group by:") . ' '
+       . Horde::link(Ansel::getUrlFor('group', array('actionID' => 'groupby', 'groupby' => 'owner')))
+       . _("owner") . '</a> | '
+       . Horde::link(Ansel::getUrlFor('group', array('actionID' => 'groupby', 'groupby' => 'category'))) . _("category") . '</a>'; ?>
+  </div>
+  <?php endif; ?>
+  <?php echo _("Sort by:") . ' '
+      . Horde::link(Horde::applicationUrl(Horde_Util::addParameter($refresh_link, 'sort', 'name')))
+      . _("Name") . '</a> | '
+      . Horde::link(Horde::applicationUrl(Horde_Util::addParameter($refresh_link, 'sort', 'last_modified')))
+      . _("Date") . '</a> | '
+      . Horde::link(Horde::applicationUrl(Horde_Util::addParameter($refresh_link, 'sort', 'owner')))
+      . _("Owner") . '</a>';
+  if ($sortdir) {
+      echo Horde::link(Horde::applicationUrl(Horde_Util::addParameter($refresh_link, array('sort' => $sortby, 'sort_dir' => 0))))
+          . Horde::img('za.png', _("Ascending"), '', $registry->getImageDir('horde'))
+          . '</a>';
+  } else {
+      echo Horde::link(Horde::applicationUrl(Horde_Util::addParameter($refresh_link, array('sort' => $sortby, 'sort_dir' => 1))))
+          . Horde::img('az.png', _("Descending"), '', $registry->getImageDir('horde'))
+          . '</a>';
+   }
+ ?>
+</div>
+<?php endif; ?>
+<?php if ($this->countWidgets()): ?>
+<div style="float:right;width:25%;"><?php echo $this->renderWidgets() ?></div>
+<div style="float:right:width:73%;">
+<?php endif; ?>
+<?php echo $pager->render() ?>
+<table width="<?php echo ($this->countWidgets() ? 73 : 100) ?>%" style="background-color:<?php echo $style['background'] ?>;">
+ <tr>
+ <?php
+ $tilesperrow = $prefs->getValue('tilesperrow');
+ $cellwidth = round(100 / $tilesperrow);
+ $count = 0;
+ foreach ($gallerylist as $galleryId => $gallery) {
+     echo '<td width="' . $width . '%" class="ansel-tile">' . $gallery->getTile(null, $style['name'], false, $this->_params) . '</td>';
+     if (!(++$count % $prefs->getValue('tilesperrow'))) {
+         if ($count < $num_galleries) {
+             echo '</tr><tr>';
+         }
+     }
+ }
+ $tilesperrow = $prefs->getValue('tilesperrow');
+ while ($count++ % $tilesperrow) {
+     echo '<td width="' . $width . '%">&nbsp;</td>';
+ } ?>
+ </tr>
+ <tr>
+   <td align="center" colspan="<?php echo $tilesperrow?>">
+     <?php echo $pager->render() ?>
+   </td>
+ </tr>
+</table>
+<?php if ($this->countWidgets()): ?>
+</div>
+<?php endif; ?>
diff --git a/ansel/templates/view/results.inc b/ansel/templates/view/results.inc
new file mode 100644 (file)
index 0000000..d5415eb
--- /dev/null
@@ -0,0 +1,140 @@
+<script type="text/javascript">
+var loading;
+
+function selectAll()
+{
+    for (var i = 0; i < document.gallery.elements.length; ++i) {
+        document.gallery.elements[i].checked = true;
+    }
+}
+
+function selectNone()
+{
+    for (var i = 0; i < document.gallery.elements.length; ++i) {
+        document.gallery.elements[i].checked = false;
+    }
+}
+
+function deleteSelected()
+{
+    if (loading == null && confirm('<?php echo addslashes(_("Are you sure you want to delete the selected photos?")) ?>')) {
+        loading = true;
+        document.gallery.actionID.value = 'delete';
+        document.gallery.submit();
+    }
+}
+
+function moveSelected()
+{
+    if (document.gallery.new_gallery.selectedIndex == 0) {
+        window.alert('<?php echo addslashes(_("You must choose a gallery to move photos to.")) ?>');
+        return false;
+    }
+
+    if (loading == null) {
+        loading = true;
+        document.gallery.actionID.value = 'move';
+        document.gallery.submit();
+    }
+}
+
+function copySelected()
+{
+    if (document.gallery.new_gallery.selectedIndex == 0) {
+        window.alert('<?php echo addslashes(_("You must choose a gallery to move photos to.")) ?>');
+        return false;
+    }
+
+    if (loading == null) {
+        loading = true;
+        document.gallery.actionID.value = 'copy';
+        document.gallery.submit();
+    }
+}
+</script>
+<?php if ($numimages): ?>
+<form name="gallery" action="<?php Horde::applicationUrl('view.php', true)?>" method="get">
+<?php echo Horde_Util::formInput() ?>
+<input type="hidden" name="actionID" value="" />
+<input type="hidden" name="view" value="Results" />
+<?php endif; ?>
+
+<div class="header">
+<?php
+/* Build link, if needed, for browsing all users' results */
+$allLink = (!empty($this->_owner)) ? Horde::link(Horde_Util::addParameter(Horde::selfUrl(), array('view' => 'Results')), _("View Results from All Users")) . ' (' . _("View All Results") . ')</a>' : '';
+if ($total) {
+    echo '<span class="rightFloat">' . ($total > $perpage ? sprintf(_("%d to %d of %d Items"), $pagestart, $pageend, $total) : sprintf(ngettext("%d Item", "%d Items", $total), $total)) . '<small>' . $allLink . '</small></span>';
+}
+echo htmlspecialchars($this->getTitle(), ENT_COMPAT, Horde_Nls::getCharset()) . $this->_search->getTagTrail();
+?>
+</div>
+
+<table class="control" width="100%" cellspacing="0">
+ <tr>
+  <td>
+    <?php printf(_("Select: %s, %s"),
+                   Horde::link('#', _("Select All"), '', '', 'selectAll(); return false;') . _("All") . '</a>',
+                   Horde::link('#', _("Select None"), '', '', 'selectNone(); return false;') . _("None") . '</a>');?>
+  </td>
+  <td class="rightAlign">
+<?php echo _("Actions: ") ?>
+    <?php echo Horde::link('#', _("Delete"), 'widget', '', 'deleteSelected(); return false;') . _("Delete"); ?></a>
+<?php if ($option_move): ?>
+ |  <?php echo Horde::link('#', _("Move"), 'widget', '', 'moveSelected(); return false;') . _("Move"); ?></a>
+<?php endif; ?>
+<?php if ($option_copy): ?>
+ |  <?php echo Horde::link('#', _("Copy"), 'widget', '', 'copySelected(); return false;') . _("Copy"); ?></a>
+<?php endif; ?>
+    <select name="new_gallery"><option value="-1"><?php echo _("Selected photos to") ?></option><?php echo Ansel::selectGalleries(null, PERMS_EDIT) ?></select>
+  </td>
+ </tr>
+</table>
+
+<table width="100%">
+ <tbody>
+  <tr>
+   <td valign="top">
+    <?php if (!$total): ?>
+     <div class="text"><em><?php echo _("There are no photos in this gallery.") ?></em></div>
+    <?php else: ?>
+     <?php echo $pager->render() ?>
+     <table width="100%" style="background-color:<?php echo $styleDef['background'] ?>;"><tr>
+      <?php
+        $tilesperrow = $prefs->getValue('tilesperrow');
+        $cellwidth = round(100 / $tilesperrow);
+        $count = 0;
+        foreach ($results as $result) {
+            echo '<td width="' . $cellwidth . '%" valign="top">' . $result->getTile(null, $style, false, $this->_params) . '</td>';
+            if (!(++$count % $tilesperrow)) {
+                if ($count < $total) {
+                    echo '</tr><tr>';
+                }
+            }
+        }
+
+        while ($count++ % $tilesperrow) {
+            echo '<td>&nbsp;</td>';
+        } ?>
+      </tr>
+     </table>
+     <?php echo $pager->render() ?>
+    <?php endif; ?>
+   </td>
+   <td width="20%" valign="top">
+    <div id="anselWidgets">
+     <?php /* Tags if we are using related */ if ($conf['tags']['relatedtags']): ?>
+      <div style="background-color:<?php echo $styleDef['background'] ?>;">
+       <h2 class="header tagTitle"><?php echo _("Related Tags") ?></h2>
+       <div id="tags"><?php echo $rtaghtml ?></div>
+      </div>
+     <?php endif; ?>
+    </div>
+   </td>
+  </tr>
+ </tbody>
+</table>
+
+<?php if ($numimages): ?>
+</form>
+<?php endif; ?>
diff --git a/ansel/templates/view/slideshow.inc b/ansel/templates/view/slideshow.inc
new file mode 100644 (file)
index 0000000..a94bff5
--- /dev/null
@@ -0,0 +1,63 @@
+<script type="text/javascript">
+//<![CDATA[
+SlideController.initialize(<?php echo $this->json() ?>, <?php echo $imageIndex ?>, "<?php echo $GLOBALS['registry']->get('webroot')?>", <?php echo $this->gallery->id ?>, "<?php echo $this->gallery->getName()?>");
+//]]>
+
+function stopSlideShow()
+{
+    location.href = "<?php echo Horde::applicationUrl($galleryurl, true) ?>&image=" + SlideController.photos[SlideController.photoId][3] + "&view=Image&page=" + SlideController.photos[SlideController.photoId][4];
+}
+</script>
+
+<h1 class="header"><?php echo Ansel::getBreadcrumbs() ?></h1>
+<table width="100%" cellspacing="0">
+ <tr class="control">
+  <td>
+    <?php if ($this->gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)): ?>
+        <?php echo Horde::link(Horde::applicationUrl(Horde_Util::addParameter($imageActionUrl, array('actionID' => 'modify', 'share' => $this->gallery->getName(), 'ret' => 'image'))), '', '', '_blank', 'SlideController.pause();popup(this.href); return false;', '', '', array('id' => 'image_properties_link')) . _("Properties") ?></a>
+      | <?php echo Horde::link(Horde::applicationUrl(Horde_Util::addParameter($imageActionUrl, 'actionID', 'editimage')), '', '', '', '', '', '', array('id' => 'image_edit_link')) . _("Edit") ?></a>
+    <?php endif; ?>
+    <?php if ($this->gallery->hasPermission(Horde_Auth::getAuth(), PERMS_DELETE)): ?>
+      | <?php echo Horde::link(Horde::applicationUrl(Horde_Util::addParameter($imageActionUrl, 'actionID', 'delete')), '', '', '', 'return window.confirm(\'' . addslashes(sprintf(_("Do you want to permanently delete \"%s\"?"), $this->image->filename)) . '\');', '', '', array('id' => 'image_delete_link')) . _("Delete") ?></a>
+    <?php endif; ?>
+  </td>
+  <td class="rightAlign">
+
+<?php if (!empty($conf['ecard']['enable'])): ?>
+    <?php echo Horde::link($ecardurl, '', '', '_blank', 'SlideController.pause();popup(this.href); return false;', '', '', array('id' => 'image_ecard_link')) . _("Send an Ecard") ?></a> |
+<?php endif; ?>
+    <?php echo Horde::link(Horde::applicationUrl(Horde_Util::addParameter('img/download.php', 'image', $imageId)), '', '', '', 'SlideController.pause();', '', '', array('id' => 'image_download_link')) . _("Download Original Photo") ?></a>
+  </td>
+ </tr>
+</table>
+<table width="100%" cellpadding="0" cellspacing="0" style="background-color:<?php echo $style['background']; ?>;">
+ <tr>
+  <td>
+    <div class="slideControls">
+     <?php
+     echo '<a href="#" id="ssPlay" onclick="SlideController.play(); return false;" style="display:none;" title="' . _("Start Slideshow") . '">' . Horde::img('slideshow_play.png', _("Play")) . '</a>';
+     echo '<a href="#" id="ssPause" onclick="stopSlideShow(); return false;" title="' . _("Pause Slideshow") . '">' . Horde::img('slideshow_pause.png', _("Pause Slideshow")) . '</a>';
+     echo '<a href="#" id="PrevLink" onclick="SlideController.prev(); return false;"  title="' . _("Previous") . '">' .
+           Horde::img('slideshow_prev.png', _("Previous")) . '</a>';
+     echo '<a href="#" id="NextLink" onclick="SlideController.next(); return false;" title="' . _("Next") . '">' .
+           Horde::img('slideshow_next.png', _("Next")) . '</a>';
+     ?>
+    </div>
+   </td>
+  </tr>
+  <tr>
+   <td style="margin:5px;height:<?php echo $GLOBALS['conf']['screen']['height'] ?>px;vertical-align:top;">
+     <div id="Container">
+      <?php echo Horde::img('blank.gif', '', array('id' => 'Photo', 'style' => 'display:none;'), $registry->getImageDir('horde')) ?>
+     </div>
+   </td>
+  </tr>
+  <tr>
+   <td>
+     <div id="CaptionContainer" style="width:200px;">
+      <p id="Caption"></p>
+     </div>
+   </td>
+  </tr>
+</table>
+
diff --git a/ansel/templates/xppublish/javascript.inc b/ansel/templates/xppublish/javascript.inc
new file mode 100755 (executable)
index 0000000..45a40df
--- /dev/null
@@ -0,0 +1,69 @@
+<div id="content"></div>
+<script>
+
+function publish()
+{
+    var xml = window.external.Property('TransferManifest');
+    var files = xml.selectNodes('transfermanifest/filelist/file');
+
+    for (i = 0; i < files.length; i++) {
+        var postTag = xml.createNode(1, 'post', '');
+        postTag.setAttribute('href', '<?php echo Horde::applicationUrl('xppublish.php?1=1', true) ?>&gallery=' + folder.gallery.value);
+        postTag.setAttribute('name', 'imagefile');
+
+        var dataTag = xml.createNode(1, 'formdata', '');
+        dataTag.setAttribute('name', '<?php echo addslashes(session_name()) ?>');
+        dataTag.text = '<?php echo addslashes(session_id()) ?>';
+        postTag.appendChild(dataTag);
+
+        var dataTag = xml.createNode(1, 'formdata', '');
+        dataTag.setAttribute('name', 'cmd');
+        dataTag.text = 'add';
+        postTag.appendChild(dataTag);
+
+        var dataTag = xml.createNode(1, 'formdata', '');
+        dataTag.setAttribute('name', 'action');
+        dataTag.text = 'SAVE';
+        postTag.appendChild(dataTag);
+
+        files.item(i).appendChild(postTag);
+    }
+
+    var uploadTag = xml.createNode(1, 'uploadinfo', '');
+    var htmluiTag = xml.createNode(1, 'htmlui', '');
+    htmluiTag.text = '<?php echo Horde::applicationUrl('view.php?view=Gallery', true) ?>&gallery=' + folder.gallery.value;
+    uploadTag.appendChild(htmluiTag);
+
+    xml.documentElement.appendChild(uploadTag);
+
+    window.external.Property('TransferManifest') = xml;
+    window.external.SetWizardButtons(true, true, true);
+    content.innerHtml = xml;
+    window.external.FinalNext();
+}
+
+function OnBack()
+{
+    <?php echo $PUBLISH_ONBACK ?>
+    window.external.SetWizardButtons(false, true, false);
+}
+
+function OnNext()
+{
+    <?php echo $PUBLISH_ONNEXT ?>
+}
+
+function OnCancel()
+{
+    content.innerHtml += '<br />OnCancel';
+}
+
+function window.onload()
+{
+    window.external.SetHeaderText('<?php echo addslashes($registry->get('name')) . ' :: ' . _("Photo Upload") ?>', '<?php echo addslashes(sprintf(_("Upload Photos to %s"), $registry->get('name'))) ?>');
+    window.external.SetWizardButtons(<?php echo $PUBLISH_BUTTONS ?>);
+}
+
+<?php echo $PUBLISH_CMD ?>
+
+</script>
diff --git a/ansel/templates/xppublish/list.inc b/ansel/templates/xppublish/list.inc
new file mode 100755 (executable)
index 0000000..ccd1919
--- /dev/null
@@ -0,0 +1,24 @@
+<div style="margin:5px">
+<form id="folder" name="folder" method="post" action="<?php echo Horde::applicationUrl('xppublish.php', true) ?>">
+<input type="hidden" name="cmd" value="select" />
+<?php Horde_Util::pformInput() ?>
+
+<h1 class="header">
+ <?php echo _("Publish Photos to:") ?>
+</h1>
+
+<div class="headerbox" style="padding:5px">
+ <p>
+  <label style="font-weight:bold" for="gallery"><?php echo _("Existing galleries:") ?></label><br />
+  <select id="gallery" name="gallery">
+   <?php echo Ansel::selectGalleries(null, PERMS_EDIT) ?>
+  </select>
+ </p>
+ <p>
+  <label style="font-weight:bold" for="create"><?php echo _("Create a new gallery:") ?></label><br />
+  <input type="button" class="button" value="<?php echo _("Create") ?>" onclick="folder.cmd.value='new'; folder.submit();" />
+ </p>
+</div>
+
+</form>
+</div>
diff --git a/ansel/templates/xppublish/login.inc b/ansel/templates/xppublish/login.inc
new file mode 100755 (executable)
index 0000000..dce43d8
--- /dev/null
@@ -0,0 +1,18 @@
+<center>
+<span class="header"><?php printf(_("Login to %s"), $registry->get('name')) ?></span>
+<br />
+<form method="post" action="xppublish.php" id="login" name="login">
+<?php Horde_Util::pformInput() ?>
+<input type="hidden" name="lcid" value="<?php echo Horde_Util::getFormData('lcid') ?>" />
+<input type="hidden" name="langid" value="<?php echo Horde_Util::getFormData('langid') ?>" />
+<input type="hidden" name="cmd" value="login" />
+<table>
+ <tr>
+  <td class="light"><?php echo _("Username") ?>:</td><td><input type="text" name="username" value="" /></td>
+ </tr>
+ <tr>
+  <td class="light"><?php echo _("Password") ?>:</td><td><input type="password" name="password" value="" /></td>
+ </tr>
+</table>
+</form>
+</center>
diff --git a/ansel/templates/xppublish/new.inc b/ansel/templates/xppublish/new.inc
new file mode 100755 (executable)
index 0000000..098b74d
--- /dev/null
@@ -0,0 +1,29 @@
+<center>
+<form id="folder" name="folder">
+<?php Horde_Util::pformInput() ?>
+<input type="hidden" name="cmd" value="new" />
+<input type="hidden" name="create" value="1" />
+<table>
+<tr>
+  <td colspan="2" align="center" class="header"><?php echo _("Create a new gallery") ?></td>
+</tr>
+<tr>
+  <td class="light" align="right" valign="top">
+    <?php echo _("New Gallery Title") ?>
+  </td>
+  <td align="left">
+    <input id="gallery_name" type="text" name="gallery_name" value="<?php echo htmlspecialchars(Horde_Util::getFormData('gallery_name')) ?>" size="50" maxlength="100" />
+  </td>
+</tr>
+
+<tr>
+  <td class="light" align="right" valign="top">
+    <?php echo _("New Gallery Description") ?>
+  </td>
+  <td align="left">
+    <textarea name="gallery_desc" cols="33" rows="5"><?php echo htmlspecialchars($gallery_desc, ENT_COMPAT, Horde_Nls::getCharset()) ?></textarea>
+  </td>
+</tr>
+</table>
+</form>
+</center>
diff --git a/ansel/test.php b/ansel/test.php
new file mode 100644 (file)
index 0000000..5de965b
--- /dev/null
@@ -0,0 +1,125 @@
+<?php
+/**
+ * $Horde: ansel/test.php,v 1.27 2009/07/13 20:39:08 mrubinsk Exp $
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ */
+
+/* Include Horde's core.php file. */
+include_once '../lib/core.php';
+
+/* We should have loaded the String class, from the Horde_Util
+ * package, in core.php. If Horde_String:: isn't defined, then we're not
+ * finding some critical libraries. */
+if (!class_exists('Horde_String')) {
+    echo '<br /><h2 style="color:red">The Horde_Util package was not found. If PHP\'s error_reporting setting is high enough and display_errors is on, there should be error messages printed above that may help you in debugging the problem. If you are simply missing these files, then you need to get the <a href="http://cvs.horde.org/cvs.php/framework">framework</a> module from <a href="http://www.horde.org/source/">Horde CVS</a>, and install the packages in it with the install-packages.php script.</h2>';
+    exit;
+}
+
+/* Initialize the Horde_Test:: class. */
+if (!is_readable('../lib/Test.php')) {
+    echo 'ERROR: You must install Horde before running this script.';
+    exit;
+}
+require_once '../lib/Test.php';
+$horde_test = new Horde_Test;
+
+/* Ansel version. */
+$module = 'Ansel';
+require_once './lib/version.php';
+$module_version = ANSEL_VERSION;
+
+/* Ansel configuration files. */
+$file_list = array(
+    'config/conf.php' => 'The file <code>./config/conf.php</code> appears to be missing. You probably just forgot to generate it using the Horde config system - see docs/INSTALL for details. While you do that, take a look at the settings and make sure they are appropriate for your site.',
+    'config/prefs.php' => 'The file <code>./config/prefs.php</code> appears to be missing. You probably just forgot to copy <code>./config/prefs.php.dist</code> over. While you do that, take a look at the settings and make sure they are appropriate for your site.'
+);
+
+require TEST_TEMPLATES . 'header.inc';
+require TEST_TEMPLATES . 'version.inc';
+
+/* Display versions of other Horde applications. */
+$app_list = array(
+    'horde' => array(
+        'error' => 'Ansel requires at least Horde 3.2.2',
+        'version' => '3.2.2'
+    ),
+    'agora' => array(
+        'error' => 'Agora provides the ability for users to comment on images.',
+        'version' => '1.0')
+);
+$app_output = $horde_test->requiredAppCheck($app_list);
+?>
+<h1>Other Horde Applications</h1>
+<ul>
+    <?php echo $app_output ?>
+</ul>
+
+<?php
+/* PHP module capabilities. */
+$module_list = array(
+    'gd' => array(
+        'descrip' => 'GD Support',
+        'error' => 'You need either GD2 support in PHP, or an external driver like ImageMagick.  Either recompile PHP with GD2 support, or make sure that the path to ImageMagick\'s convert utility is set in horde/config/conf.php.'
+    ),
+    'imagick' => array(
+        'descrip' => 'Imagick Library',
+        'required' => false,
+        'error' => 'Ansel can make use of the Imagick Library, if it is installed on your system.  It is highly recommended to use either ImageMagick\'s convert utility or the Imagick php library for faster results.'
+    ),
+    'zip' => array(
+        'descrip' => 'Zip Support',
+        'required' => false,
+        'error' => 'Ansel can make use of PHP\'s Zip extension for more efficiently processing uploaded ZIP files..'
+    ),
+    'opencv' => array(
+        'descrip' => 'OpenCV Library',
+        'required' => false,
+        'error' => 'Ansel can make use of the OpenCV PHP extension for automatically detecting human faces in images. You need either this library or the one immediately below to detect human faces.'
+    ),
+    'facedetect' => array(
+        'descrip' => 'Facedetect Face Detection Library',
+        'required' => false,
+        'error' => 'Ansel can make use of the Facedetect PHP extension for automatically detecting human faces in images.  You need either OpenCV (above) or Facedetect to detect human faces.'
+    ),
+    'libpuzzle' => array(
+        'descrip' => 'Puzzle Library',
+        'required' => false,
+        'error' => 'Ansel can make use of the libpuzzle PHP extension for finding similar images based on image content.'
+    )
+);
+
+/* PEAR */
+$pear_list = array('MDB2' => array(
+                        'path' => 'MDB2.php',
+                        'required' => true,
+                        'error' => 'You do not have the MDB2 package installed on your system. In addition to this package, you will need the appropriate MDB2_Driver package for your database backend.'),
+);
+
+/* Display PHP Version information. */
+$php_info = $horde_test->getPhpVersionInformation();
+require TEST_TEMPLATES . 'php_version.inc';
+
+?>
+<h1>PHP Module Capabilities</h1>
+<ul>
+    <?php echo $horde_test->phpModuleCheck($module_list) ?>
+</ul>
+
+<h1>Ansel Configuration Files</h1>
+<ul>
+    <?php echo $horde_test->requiredFileCheck($file_list) ?>
+</ul>
+
+<h1>PEAR Modules</h1>
+<ul>
+    <?php echo $horde_test->PEARModuleCheck($pear_list) ?>
+</ul>
+
+<?php
+require TEST_TEMPLATES . 'footer.inc';
diff --git a/ansel/themes/cropper.css b/ansel/themes/cropper.css
new file mode 100755 (executable)
index 0000000..5bf0302
--- /dev/null
@@ -0,0 +1,174 @@
+.imgCrop_wrap {
+    position: relative;
+    cursor: crosshair;
+    margin: auto;
+}
+
+/* an extra classname is applied for Opera < 9.0 to fix it's lack of opacity support */
+.imgCrop_wrap.opera8 .imgCrop_overlay,
+.imgCrop_wrap.opera8 .imgCrop_clickArea {
+    background-color: transparent;
+}
+
+/* fix for IE displaying all boxes at line-height by default, although they are still 1 pixel high until we combine them with the pointless span */
+.imgCrop_wrap,
+.imgCrop_wrap * {
+    font-size: 0;
+}
+
+.imgCrop_overlay {
+    background-color: #000;
+    opacity: 0.5;
+    filter:alpha(opacity=50);
+    position: absolute;
+    width: 100%;
+    height: 100%;
+}
+
+.imgCrop_selArea {
+    position: absolute;
+    cursor: move;
+    z-index: 2;
+}
+
+/* clickArea is all a fix for IE 5.5 & 6 to allow the user to click on the given area */
+.imgCrop_clickArea {
+    width: 100%;
+    height: 100%;
+    background-color: #FFF;
+    opacity: 0.01;
+    filter:alpha(opacity=01);
+}
+
+.imgCrop_marqueeHoriz {
+    position: absolute;
+    width: 100%;
+    height: 1px;
+    background: transparent url(marqueeHoriz.gif) repeat-x 0 0;
+    z-index: 3;
+}
+
+.imgCrop_marqueeVert {
+    position: absolute;
+    height: 100%;
+    width: 1px;
+    background: transparent url(marqueeVert.gif) repeat-y 0 0;
+    z-index: 3;
+}
+
+/*
+ *  FIX MARCHING ANTS IN IE
+ *     As IE <6 tries to load background images we can uncomment the follwoing hack
+ *  to remove that issue, not as pretty - but is anything in IE?
+ *  And yes I do know that 'filter' is evil, but it will make it look semi decent in IE
+ *
+* html .imgCrop_marqueeHoriz,
+* html .imgCrop_marqueeVert {
+    background: transparent;
+    filter: Invert;
+}
+* html .imgCrop_marqueeNorth { border-top: 1px dashed #000; }
+* html .imgCrop_marqueeEast  { border-right: 1px dashed #000; }
+* html .imgCrop_marqueeSouth { border-bottom: 1px dashed #000; }
+* html .imgCrop_marqueeWest  { border-left: 1px dashed #000; }
+*/
+
+.imgCrop_marqueeNorth { top: 0; left: 0; }
+.imgCrop_marqueeEast  { top: 0; right: 0; }
+.imgCrop_marqueeSouth { bottom: 0px; left: 0; }
+.imgCrop_marqueeWest  { top: 0; left: 0; }
+
+
+.imgCrop_handle {
+    position: absolute;
+    border: 1px solid #333;
+    width: 6px;
+    height: 6px;
+    background: #FFF;
+    opacity: 0.5;
+    filter:alpha(opacity=50);
+    z-index: 4;
+}
+
+/* fix IE 5 box model */
+* html .imgCrop_handle {
+    width: 8px;
+    height: 8px;
+    wid\th: 6px;
+    hei\ght: 6px;
+}
+
+.imgCrop_handleN {
+    top: -3px;
+    left: 0;
+    /* margin-left: 49%;    @done_in_js */
+    cursor: n-resize;
+}
+
+.imgCrop_handleNE {
+    top: -3px;
+    right: -3px;
+    cursor: ne-resize;
+}
+
+.imgCrop_handleE {
+    top: 0;
+    right: -3px;
+    /* margin-top: 49%;    @done_in_js */
+    cursor: e-resize;
+}
+
+.imgCrop_handleSE {
+    right: -3px;
+    bottom: -3px;
+    cursor: se-resize;
+}
+
+.imgCrop_handleS {
+    right: 0;
+    bottom: -3px;
+    /* margin-right: 49%; @done_in_js */
+    cursor: s-resize;
+}
+
+.imgCrop_handleSW {
+    left: -3px;
+    bottom: -3px;
+    cursor: sw-resize;
+}
+
+.imgCrop_handleW {
+    top: 0;
+    left: -3px;
+    /* margin-top: 49%;  @done_in_js */
+    cursor: w-resize;
+}
+
+.imgCrop_handleNW {
+    top: -3px;
+    left: -3px;
+    cursor: nw-resize;
+}
+
+/**
+ * Create an area to click & drag around on as the default browser behaviour is to let you drag the image
+ */
+.imgCrop_dragArea {
+    width: 100%;
+    height: 100%;
+    z-index: 200;
+    position: absolute;
+    top: 0;
+    left: 0;
+}
+
+.imgCrop_previewWrap {
+    /* width: 200px;  @done_in_js */
+    /* height: 200px; @done_in_js */
+    overflow: hidden;
+    position: relative;
+}
+
+.imgCrop_previewWrap img {
+    position: absolute;
+}
\ No newline at end of file
diff --git a/ansel/themes/embed.css b/ansel/themes/embed.css
new file mode 100755 (executable)
index 0000000..016845e
--- /dev/null
@@ -0,0 +1,14 @@
+/* CSS rules for js embedded gallery views */
+.anselGalleryWidget {
+    margin: 5px;
+    text-align: center;
+}
+.anselGaleryWidget a {
+    text-decoration: none;
+    color: blue;
+}
+.anselGalleryWidget img {
+    border: 2px solid #ccc;
+    margin: 2px;
+    padding: 2px;
+}
\ No newline at end of file
diff --git a/ansel/themes/feed-rss.xsl b/ansel/themes/feed-rss.xsl
new file mode 100755 (executable)
index 0000000..018a71f
--- /dev/null
@@ -0,0 +1,84 @@
+<?xml version="1.0"?>
+
+<xsl:stylesheet version="1.0"
+  xmlns="http://www.w3.org/1999/xhtml"
+  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+  xmlns:rss="http://purl.org/rss/1.0/"
+  xmlns:atom="http://www.w3.org/2005/Atom">
+
+  <xsl:output indent="yes" encoding="UTF-8"/>
+
+  <xsl:template match="/rss|/atom:feed">
+    <html>
+      <head>
+        <title>
+          <xsl:value-of select="/rss/channel/title"/>
+        </title>
+        <style type="text/css">
+          img {
+              border: 0;
+              padding: 5px;
+          }
+        </style>
+      </head>
+      <body>
+        <p>
+          You're viewing an XML content feed which is
+          intended to be viewed within a feed aggregator.
+        </p>
+
+        <xsl:variable name="link" select="/rss/channel/link"/>
+        <h3>Subscribe to <a href="{$link}"><xsl:value-of select="/rss/channel/title"/></a></h3>
+        <xsl:variable name="cimage" select="/rss/channel/image/url"/>
+        <div style="float:right;"><img src="{$cimage}"/></div>
+
+        <p>
+          Subscribe now in your favorite RSS aggregator:
+        </p>
+
+        <xsl:variable name="resource" select="/rss/channel/atom:link"/>
+
+        <div>
+          <a href="http://www.rojo.com/add-subscription?resource={$resource}">
+            <img src="http://www.rojo.com/skins/static/images/add-to-rojo.gif" alt="Subscribe in Rojo"/>
+          </a>
+
+          <a href="http://add.my.yahoo.com/rss?url={$resource}">
+            <img src="http://us.i1.yimg.com/us.yimg.com/i/us/my/addtomyyahoo4.gif" alt="Add to My yahoo" />
+          </a>
+
+          <a href="http://www.newsgator.com/ngs/subscriber/subext.aspx?url={$resource}">
+            <img src="http://www.newsgator.com/images/ngsub1.gif" alt="Subscribe in NewsGator Online"/>
+          </a>
+
+          <a href="http://www.bloglines.com/sub/{$resource}">
+            <img src="http://www.bloglines.com/images/sub_modern5.gif" alt="Subscribe with Bloglines"/>
+          </a>
+
+          <a href="http://fusion.google.com/add?feedurl={$resource}">
+            <img src="http://buttons.googlesyndication.com/fusion/add.gif" alt="Subscribe with Google Reader"/>
+          </a>
+        </div>
+
+        <p>
+          <h3>Preview</h3>
+        </p>
+
+        <xsl:apply-templates select="/rss/channel/item" />
+
+      </body>
+    </html>
+  </xsl:template>
+
+  <xsl:template match="item">
+    <xsl:variable name="link" select="link"/>
+    <p>
+      <a href="{$link}">
+        <xsl:value-of select="title"/>
+      </a>
+      <br />
+      <xsl:value-of select="description"/>
+    </p>
+  </xsl:template>
+
+</xsl:stylesheet>
diff --git a/ansel/themes/graphics/add.png b/ansel/themes/graphics/add.png
new file mode 100644 (file)
index 0000000..392c3c6
Binary files /dev/null and b/ansel/themes/graphics/add.png differ
diff --git a/ansel/themes/graphics/ansel.png b/ansel/themes/graphics/ansel.png
new file mode 100644 (file)
index 0000000..f05b029
Binary files /dev/null and b/ansel/themes/graphics/ansel.png differ
diff --git a/ansel/themes/graphics/arrow_switch.png b/ansel/themes/graphics/arrow_switch.png
new file mode 100644 (file)
index 0000000..258c16c
Binary files /dev/null and b/ansel/themes/graphics/arrow_switch.png differ
diff --git a/ansel/themes/graphics/browse.png b/ansel/themes/graphics/browse.png
new file mode 100644 (file)
index 0000000..64cf33f
Binary files /dev/null and b/ansel/themes/graphics/browse.png differ
diff --git a/ansel/themes/graphics/down.png b/ansel/themes/graphics/down.png
new file mode 100755 (executable)
index 0000000..89a2e93
Binary files /dev/null and b/ansel/themes/graphics/down.png differ
diff --git a/ansel/themes/graphics/favicon.ico b/ansel/themes/graphics/favicon.ico
new file mode 100644 (file)
index 0000000..b57cf51
Binary files /dev/null and b/ansel/themes/graphics/favicon.ico differ
diff --git a/ansel/themes/graphics/galleries.png b/ansel/themes/graphics/galleries.png
new file mode 100644 (file)
index 0000000..64cf33f
Binary files /dev/null and b/ansel/themes/graphics/galleries.png differ
diff --git a/ansel/themes/graphics/gallery-locked-mini.png b/ansel/themes/graphics/gallery-locked-mini.png
new file mode 100644 (file)
index 0000000..71438cd
Binary files /dev/null and b/ansel/themes/graphics/gallery-locked-mini.png differ
diff --git a/ansel/themes/graphics/gallery-locked.png b/ansel/themes/graphics/gallery-locked.png
new file mode 100644 (file)
index 0000000..90910bd
Binary files /dev/null and b/ansel/themes/graphics/gallery-locked.png differ
diff --git a/ansel/themes/graphics/image_add.png b/ansel/themes/graphics/image_add.png
new file mode 100755 (executable)
index 0000000..263e356
Binary files /dev/null and b/ansel/themes/graphics/image_add.png differ
diff --git a/ansel/themes/graphics/lightbox/bullet.gif b/ansel/themes/graphics/lightbox/bullet.gif
new file mode 100644 (file)
index 0000000..bf8e3c6
Binary files /dev/null and b/ansel/themes/graphics/lightbox/bullet.gif differ
diff --git a/ansel/themes/graphics/lightbox/close.gif b/ansel/themes/graphics/lightbox/close.gif
new file mode 100644 (file)
index 0000000..ca517b6
Binary files /dev/null and b/ansel/themes/graphics/lightbox/close.gif differ
diff --git a/ansel/themes/graphics/lightbox/closelabel.gif b/ansel/themes/graphics/lightbox/closelabel.gif
new file mode 100644 (file)
index 0000000..87b4f8b
Binary files /dev/null and b/ansel/themes/graphics/lightbox/closelabel.gif differ
diff --git a/ansel/themes/graphics/lightbox/loading.gif b/ansel/themes/graphics/lightbox/loading.gif
new file mode 100644 (file)
index 0000000..f864d5f
Binary files /dev/null and b/ansel/themes/graphics/lightbox/loading.gif differ
diff --git a/ansel/themes/graphics/lightbox/nextlabel.gif b/ansel/themes/graphics/lightbox/nextlabel.gif
new file mode 100644 (file)
index 0000000..6c40e51
Binary files /dev/null and b/ansel/themes/graphics/lightbox/nextlabel.gif differ
diff --git a/ansel/themes/graphics/lightbox/prevlabel.gif b/ansel/themes/graphics/lightbox/prevlabel.gif
new file mode 100644 (file)
index 0000000..51a31c2
Binary files /dev/null and b/ansel/themes/graphics/lightbox/prevlabel.gif differ
diff --git a/ansel/themes/graphics/loading.gif b/ansel/themes/graphics/loading.gif
new file mode 100644 (file)
index 0000000..1560b64
Binary files /dev/null and b/ansel/themes/graphics/loading.gif differ
diff --git a/ansel/themes/graphics/mini-error.png b/ansel/themes/graphics/mini-error.png
new file mode 100644 (file)
index 0000000..71438cd
Binary files /dev/null and b/ansel/themes/graphics/mini-error.png differ
diff --git a/ansel/themes/graphics/minus.png b/ansel/themes/graphics/minus.png
new file mode 100755 (executable)
index 0000000..3217046
Binary files /dev/null and b/ansel/themes/graphics/minus.png differ
diff --git a/ansel/themes/graphics/mygalleries.png b/ansel/themes/graphics/mygalleries.png
new file mode 100644 (file)
index 0000000..64cf33f
Binary files /dev/null and b/ansel/themes/graphics/mygalleries.png differ
diff --git a/ansel/themes/graphics/plus.png b/ansel/themes/graphics/plus.png
new file mode 100755 (executable)
index 0000000..263e356
Binary files /dev/null and b/ansel/themes/graphics/plus.png differ
diff --git a/ansel/themes/graphics/point.png b/ansel/themes/graphics/point.png
new file mode 100644 (file)
index 0000000..9707ea2
Binary files /dev/null and b/ansel/themes/graphics/point.png differ
diff --git a/ansel/themes/graphics/prettythumb-error.png b/ansel/themes/graphics/prettythumb-error.png
new file mode 100755 (executable)
index 0000000..90910bd
Binary files /dev/null and b/ansel/themes/graphics/prettythumb-error.png differ
diff --git a/ansel/themes/graphics/problem.png b/ansel/themes/graphics/problem.png
new file mode 100755 (executable)
index 0000000..16a2189
Binary files /dev/null and b/ansel/themes/graphics/problem.png differ
diff --git a/ansel/themes/graphics/resize.png b/ansel/themes/graphics/resize.png
new file mode 100644 (file)
index 0000000..6bee5de
Binary files /dev/null and b/ansel/themes/graphics/resize.png differ
diff --git a/ansel/themes/graphics/scaler_slider.gif b/ansel/themes/graphics/scaler_slider.gif
new file mode 100755 (executable)
index 0000000..42e205b
Binary files /dev/null and b/ansel/themes/graphics/scaler_slider.gif differ
diff --git a/ansel/themes/graphics/scaler_slider_track.gif b/ansel/themes/graphics/scaler_slider_track.gif
new file mode 100755 (executable)
index 0000000..571e405
Binary files /dev/null and b/ansel/themes/graphics/scaler_slider_track.gif differ
diff --git a/ansel/themes/graphics/screen-error.png b/ansel/themes/graphics/screen-error.png
new file mode 100644 (file)
index 0000000..90910bd
Binary files /dev/null and b/ansel/themes/graphics/screen-error.png differ
diff --git a/ansel/themes/graphics/slideshow_next.png b/ansel/themes/graphics/slideshow_next.png
new file mode 100644 (file)
index 0000000..c888268
Binary files /dev/null and b/ansel/themes/graphics/slideshow_next.png differ
diff --git a/ansel/themes/graphics/slideshow_pause.png b/ansel/themes/graphics/slideshow_pause.png
new file mode 100644 (file)
index 0000000..6bd15d9
Binary files /dev/null and b/ansel/themes/graphics/slideshow_pause.png differ
diff --git a/ansel/themes/graphics/slideshow_play.png b/ansel/themes/graphics/slideshow_play.png
new file mode 100644 (file)
index 0000000..57cfe4f
Binary files /dev/null and b/ansel/themes/graphics/slideshow_play.png differ
diff --git a/ansel/themes/graphics/slideshow_prev.png b/ansel/themes/graphics/slideshow_prev.png
new file mode 100644 (file)
index 0000000..9a5fee4
Binary files /dev/null and b/ansel/themes/graphics/slideshow_prev.png differ
diff --git a/ansel/themes/graphics/success.png b/ansel/themes/graphics/success.png
new file mode 100755 (executable)
index 0000000..4af9076
Binary files /dev/null and b/ansel/themes/graphics/success.png differ
diff --git a/ansel/themes/graphics/text.png b/ansel/themes/graphics/text.png
new file mode 100644 (file)
index 0000000..d2fda89
Binary files /dev/null and b/ansel/themes/graphics/text.png differ
diff --git a/ansel/themes/graphics/thumb-error.png b/ansel/themes/graphics/thumb-error.png
new file mode 100644 (file)
index 0000000..90910bd
Binary files /dev/null and b/ansel/themes/graphics/thumb-error.png differ
diff --git a/ansel/themes/graphics/up.png b/ansel/themes/graphics/up.png
new file mode 100755 (executable)
index 0000000..50ca91b
Binary files /dev/null and b/ansel/themes/graphics/up.png differ
diff --git a/ansel/themes/lightbox.css b/ansel/themes/lightbox.css
new file mode 100644 (file)
index 0000000..d9976b8
--- /dev/null
@@ -0,0 +1,112 @@
+/* CSS for lightbox */
+#lightbox {
+    position: absolute;
+    left: 0;
+    width: 100%;
+    z-index: 100;
+    text-align: center;
+    line-height: 0pt;
+}
+#lightbox img {
+    width: auto;
+    height: auto;
+}
+#lightbox a img {
+    border: none;
+}
+
+#outerImageContainer {
+     position: relative;
+     background-color: #fff;
+     width: 250px;
+     height: 250px;
+     margin: 0 auto;
+}
+#imageContainer { padding: 10px; }
+
+#loading {
+    position: absolute;
+    top: 40%;
+    left: 0%;
+    height: 25%;
+    width: 100%;
+    text-align: center;
+    line-height: 0;
+}
+#hoverNav {
+    position: absolute;
+    top: 0;
+    left: 0;
+    height: 100%;
+    width: 100%;
+    z-index: 10;
+}
+#imageContainer>#hoverNav { left: 0; }
+#hoverNav a { outline: none; }
+
+#prevLink, #nextLink{
+    width: 49%;
+    height: 100%;
+    background-image: url(data:image/gif;base64,AAAA);
+    /* Trick IE into showing hover */
+    display: block;
+}
+#prevLink {
+    left: 0;
+    float: left;
+}
+#nextLink {
+     right: 0;
+     float: right;
+}
+#prevLink:hover, #prevLink:visited:hover {
+     background: url(graphics/lightbox/prevlabel.gif) left 15% no-repeat;
+}
+#nextLink:hover, #nextLink:visited:hover {
+    background: url(graphics/lightbox/nextlabel.gif) right 15% no-repeat;
+}
+
+#imageDataContainer {
+    font: 10px Verdana, Helvetica, sans-serif;
+    background-color: #fff;
+    margin: 0 auto;
+    line-height: 1.4em;
+    overflow: auto;
+    width: 100%;
+}
+
+#imageData {
+    padding: 0 10px;
+    color: #666;
+}
+#imageData #imageDetails {
+    width: 70%;
+    float: left;
+    text-align: left;
+}
+
+#imageData #caption {
+    font-weight: bold;
+    font-size: 130%;
+}
+#imageData #numberDisplay {
+    display: block;
+    clear: left;
+    padding-bottom: 1.0em;
+}
+#imageData #bottomNavClose {
+    width: 66px;
+    float: right;
+    padding-bottom: 0.7em;
+    outline: none;
+}
+
+#overlay {
+    position: absolute;
+    top: 0;
+    left: 0;
+    z-index: 90;
+    width: 100%;
+    height: 500px;
+    background-color: #000;
+}
\ No newline at end of file
diff --git a/ansel/themes/screen.css b/ansel/themes/screen.css
new file mode 100644 (file)
index 0000000..2710d00
--- /dev/null
@@ -0,0 +1,403 @@
+/**
+ * $Horde: ansel/themes/screen.css,v 1.82 2009/07/16 19:20:55 mrubinsk Exp $
+ */
+
+/* Table CSS. */
+th {
+    text-align: left;
+}
+
+.coord {
+    font-size: 90%;
+    border: 1px solid #999;
+    color: #aaa;
+}
+
+.instructions {
+    border: 1px solid #ccc;
+    padding: 2px;
+    margin: 8px;
+}
+.instructions p {
+    margin: 0;
+    background: #e9e9e9;
+}
+.breadcrumbs a, .thiscrumb {
+    text-decoration: none;
+    font-weight: bold;
+    font-size: 90%;
+}
+
+.instructions {
+    margin: 10px;
+    font-weight: bold;
+}
+
+/* Slideshow controls. */
+.slideControls {
+    text-align: center;
+    margin: 5px;
+    padding: 2px;
+}
+.slideControls a {
+    margin: 2px;
+    padding: 3px;
+}
+.slideControls a:hover {
+    background: #e9e9e9;
+    padding: 2px;
+    border: 1px solid #ccc;
+}
+
+/* Gallery Group view. */
+#galleryGroups {
+    width: 100%;
+}
+#galleryGroups table {
+    border-collapse: collapse;
+}
+#galleryGroups td {
+    margin: 0;
+}
+.galleryGroupsTile {
+    padding: 1em;
+}
+
+#Container {
+    text-align: center;
+}
+#CaptionContainer {
+    margin: 0 auto;
+    text-align: left;
+    font-size: 125%;
+}
+#Caption {
+    padding: 1em;
+}
+
+#sortContainer {
+    margin: 10px;
+    border: 1px solid #ccc;
+}
+#sortContainer div {
+    cursor: move;
+    margin: 10px;
+    height: 170px;
+    float: left;
+}
+#sortContainer a {
+    cursor: move;
+}
+
+/* Widgets */
+.anselWidget {
+    border: 1px solid #ccc;
+}
+.anselWidget .control {
+    background: #eee;
+    text-align: center;
+    border: 0;
+}
+.anselWidgets {
+    margin-right: 5px;
+    margin-left:5px;
+    vertical-align: top;
+}
+.anselWidgets .header {
+    background: none;
+    color: #333333;
+}
+.anselWidgets .header a {
+    color: #333333;
+}
+.anselWidgets #similar img {
+    padding: 1px;
+}
+.embedInput {
+    border-top: 1px solid #ccc;
+    color: #aaa;
+}
+.embedInput input {
+    color: #aaa;
+    width: 250px;
+    margin: 5px;
+}
+.tagTitle {
+    font-size: 100%;
+    border: none;
+}
+#tags {
+    padding: 4px;
+}
+#tags li {
+    list-style: none;
+}
+#tagform {
+    white-space: nowrap;
+    padding: 4px;
+}
+.tag-list {
+    display: inline;
+    margin: 0;
+    padding: 0;
+    list-style-type: none;
+    font-size: 80%;
+}
+.tag-list li {
+    margin: 0;
+    padding: 0 .2em;
+    display: inline;
+}
+.tag-list img {
+    vertical-align: middle;
+}
+#gallery-actions a:hover {
+    text-decoration: underline;
+}
+#gallery-actions a.widget {
+    color: #000;
+    white-space: normal;
+}
+#gallery-actions-toggle.show, #othergalleries-toggle.show {
+    background: url('graphics/down.png') center center no-repeat;
+    display: block;
+    padding-left: 15px;
+}
+#gallery-actions-toggle.hide, #othergalleries-toggle.hide {
+    background: url('graphics/up.png') center center no-repeat;
+    display: block;
+    padding-left: 15px;
+}
+#gallery-actions-toggle:hover, #othergalleries-toggle:hover {
+    text-decoration: none;
+}
+#othergalleries .treeRow img {
+    margin: 5px;
+}
+
+a.earliest:link, a.earliest:visited, a.earliest:hover, a.earliest:active {
+    color: #ccc;
+}
+
+a.earlier:link, a.earlier:visited, a.earlier:hover, a.earlier:active {
+    color: #99c;
+}
+
+a.later:link, a.later:visited, a.later:hover, a.later:active {
+    color: #99f;
+}
+
+a.latest:link, a.latest:visited, a.latest:hover, a.latest:active {
+    color: #00f;
+}
+
+#ansel_preview {
+    position: absolute;
+}
+
+/* Face recognition/detection rules */
+.face-tile {
+    height: 100px;
+    margin: 5px;
+    float: left;
+}
+.face-div {
+    position:absolute;
+}
+.face-div-name {
+    text-align: center;
+    background: grey;
+    color: white;
+}
+.shown {
+    border: medium solid white;
+}
+
+.similarity-high {
+    color: red;
+}
+.similarity-medium {
+    color: green;
+}
+.similarity-low {
+    color: #ffcc00;
+}
+.similarity-lower {
+    color: #cccccc;
+}
+.bordered-facethumb {
+    border: 1px solid black;
+    margin: 2px;
+    float: left;
+}
+
+.ansel-tile {
+    padding-top: 10px;
+    text-align: center;
+}
+
+/* Gallery styles */
+.gallery-desc {
+    background: #fff;
+    padding: 1em;
+}
+
+/* Gallery tiles */
+.gallery-tile-caption {
+    margin: 0;
+    padding: 0;
+    font-weight: bold;
+    font-size: 100%;
+}
+.gallery-tile p {
+    margin: 0;
+    padding: 0;
+}
+
+/* Image tiles */
+.image-tile-caption {
+    display: block;
+    font-size: 90%;
+    padding: .2em;
+}
+.ansel-tile img {
+    padding: 3px;
+}
+.image-tile-highlight {
+    background-color: red;
+}
+
+/* Actions */
+.anselActions {
+    margin-left: 0;
+    background: #e9e9e9;
+    padding: 4px;
+    border: 1px #cccccc solid;
+}
+.anselActions a, .anselActions a:visited, .anselActions label {
+    white-space: nowrap;
+}
+.anselActions a img {
+    padding-left: 2px;
+    padding-right: 2px;
+}
+
+.success {
+    background: url('graphics/success.png') center left no-repeat;
+    padding-left: 15px;
+}
+.problem {
+    background: url('graphics/problem.png') center left no-repeat;
+    padding-left: 15px;
+}
+
+/* For in place editing */
+form.inplaceeditor-form {
+    background: none;
+}
+form.inplaceeditor-form input[type="submit"] {
+    background:#AAFFAA none repeat scroll 0%;
+    border:1px solid #000000;
+    cursor:pointer;
+    font-weight:bold;
+    padding:2px;
+}
+form.inplaceeditor-form a {
+    background:#FFAAAA none repeat scroll 0%;
+    border:1px solid #000000;
+    cursor:pointer;
+    font-weight:bold;
+    padding:2px;
+}
+.inplaceeditor-empty {
+    font-style: italic;
+    color: #999;
+}
+
+/* Image resizer/slider */
+#slider-track {
+    background: url('graphics/scaler_slider_track.gif') no-repeat;
+    width:200px;
+    margin:10px 0;
+    position: relative;
+    height:18px;
+    margin: 4px auto 4px auto;
+}
+
+.sliderHandle {
+    background: url('graphics/scaler_slider.gif') center left no-repeat;
+    cursor:move;
+    position: absolute;
+    width: 18px;
+    height: 18px;
+}
+
+ /* Google Map stuff */
+#ansel_map {
+    width:100%;
+    height:250px;
+    overflow:hidden;
+    border-bottom: 1px solid #ccc;
+}
+#ansel_map_small {
+    width:100%;
+    height:80px;
+    margin-top:10px;
+    overflow:hidden;
+    border-top: 1px solid #ccc;
+}
+.ansel_location_sameas {
+    background-color: #979797;
+    width: 100%;
+}
+.ansel_location_sameas img {
+    border: 1px solid white;
+    margin: 5px;
+}
+.ansel_geolocation {
+    background: url(http://maps.gstatic.com/intl/en_us/mapfiles/marker.png) center left no-repeat;
+    margin:10px 0 6px;
+    padding:0 0 6px 30px;
+    min-height: 37px;
+}
+#ansel_latlng {
+    color:#666666;
+    font-size:90%;
+    white-space: nowrap;
+    margin-right: 5px;
+}
+
+#ansel_locationtext {
+    margin: 0;
+    font-size: 80%;
+}
+
+#ansel_deleteGeotag {
+    font-size: 70%;
+    font-style: italic;
+}
+
+/* Autocomplete styles. */
+.autocomplete {
+    background: #f4f4f4;
+    border: 1px solid #d4d4d4;
+    position: absolute;
+    padding: 2px 2px 2px 2px;
+}
+.autocomplete ul {
+    margin: 1px;
+    padding: 1px;
+    list-style-type: none;
+}
+.autocomplete ul li {
+    padding-top: 3px;
+    padding-bottom: 3px;
+    cursor: pointer;
+    text-decoration: underline;
+    font-size: 90%;
+    color: #000;
+}
+.autocomplete ul .selected {
+    background: gray;
+    color: #fff;
+}
diff --git a/ansel/themes/silver/graphics/add.png b/ansel/themes/silver/graphics/add.png
new file mode 100644 (file)
index 0000000..529fe8f
Binary files /dev/null and b/ansel/themes/silver/graphics/add.png differ
diff --git a/ansel/themes/silver/graphics/ansel.png b/ansel/themes/silver/graphics/ansel.png
new file mode 100644 (file)
index 0000000..184860d
Binary files /dev/null and b/ansel/themes/silver/graphics/ansel.png differ
diff --git a/ansel/themes/silver/graphics/arrow_switch.png b/ansel/themes/silver/graphics/arrow_switch.png
new file mode 100644 (file)
index 0000000..258c16c
Binary files /dev/null and b/ansel/themes/silver/graphics/arrow_switch.png differ
diff --git a/ansel/themes/silver/graphics/browse.png b/ansel/themes/silver/graphics/browse.png
new file mode 100644 (file)
index 0000000..184860d
Binary files /dev/null and b/ansel/themes/silver/graphics/browse.png differ
diff --git a/ansel/themes/silver/graphics/galleries.png b/ansel/themes/silver/graphics/galleries.png
new file mode 100644 (file)
index 0000000..d9591c1
Binary files /dev/null and b/ansel/themes/silver/graphics/galleries.png differ
diff --git a/ansel/themes/silver/graphics/image_add.png b/ansel/themes/silver/graphics/image_add.png
new file mode 100644 (file)
index 0000000..fc5d613
Binary files /dev/null and b/ansel/themes/silver/graphics/image_add.png differ
diff --git a/ansel/themes/silver/graphics/lightbox/bullet.gif b/ansel/themes/silver/graphics/lightbox/bullet.gif
new file mode 100755 (executable)
index 0000000..bf8e3c6
Binary files /dev/null and b/ansel/themes/silver/graphics/lightbox/bullet.gif differ
diff --git a/ansel/themes/silver/graphics/lightbox/close.gif b/ansel/themes/silver/graphics/lightbox/close.gif
new file mode 100755 (executable)
index 0000000..ca517b6
Binary files /dev/null and b/ansel/themes/silver/graphics/lightbox/close.gif differ
diff --git a/ansel/themes/silver/graphics/lightbox/closelabel.gif b/ansel/themes/silver/graphics/lightbox/closelabel.gif
new file mode 100755 (executable)
index 0000000..87b4f8b
Binary files /dev/null and b/ansel/themes/silver/graphics/lightbox/closelabel.gif differ
diff --git a/ansel/themes/silver/graphics/lightbox/loading.gif b/ansel/themes/silver/graphics/lightbox/loading.gif
new file mode 100755 (executable)
index 0000000..f864d5f
Binary files /dev/null and b/ansel/themes/silver/graphics/lightbox/loading.gif differ
diff --git a/ansel/themes/silver/graphics/lightbox/nextlabel.gif b/ansel/themes/silver/graphics/lightbox/nextlabel.gif
new file mode 100755 (executable)
index 0000000..6c40e51
Binary files /dev/null and b/ansel/themes/silver/graphics/lightbox/nextlabel.gif differ
diff --git a/ansel/themes/silver/graphics/lightbox/prevlabel.gif b/ansel/themes/silver/graphics/lightbox/prevlabel.gif
new file mode 100755 (executable)
index 0000000..51a31c2
Binary files /dev/null and b/ansel/themes/silver/graphics/lightbox/prevlabel.gif differ
diff --git a/ansel/themes/silver/graphics/mini-error.png b/ansel/themes/silver/graphics/mini-error.png
new file mode 100644 (file)
index 0000000..71438cd
Binary files /dev/null and b/ansel/themes/silver/graphics/mini-error.png differ
diff --git a/ansel/themes/silver/graphics/mygalleries.png b/ansel/themes/silver/graphics/mygalleries.png
new file mode 100644 (file)
index 0000000..d9591c1
Binary files /dev/null and b/ansel/themes/silver/graphics/mygalleries.png differ
diff --git a/ansel/themes/silver/graphics/slideshow_next.png b/ansel/themes/silver/graphics/slideshow_next.png
new file mode 100755 (executable)
index 0000000..31f7fd3
Binary files /dev/null and b/ansel/themes/silver/graphics/slideshow_next.png differ
diff --git a/ansel/themes/silver/graphics/slideshow_pause.png b/ansel/themes/silver/graphics/slideshow_pause.png
new file mode 100755 (executable)
index 0000000..2d9ce9c
Binary files /dev/null and b/ansel/themes/silver/graphics/slideshow_pause.png differ
diff --git a/ansel/themes/silver/graphics/slideshow_play.png b/ansel/themes/silver/graphics/slideshow_play.png
new file mode 100755 (executable)
index 0000000..0846555
Binary files /dev/null and b/ansel/themes/silver/graphics/slideshow_play.png differ
diff --git a/ansel/themes/silver/graphics/slideshow_prev.png b/ansel/themes/silver/graphics/slideshow_prev.png
new file mode 100755 (executable)
index 0000000..c029447
Binary files /dev/null and b/ansel/themes/silver/graphics/slideshow_prev.png differ
diff --git a/ansel/themes/silver/graphics/text.png b/ansel/themes/silver/graphics/text.png
new file mode 100644 (file)
index 0000000..d2fda89
Binary files /dev/null and b/ansel/themes/silver/graphics/text.png differ
diff --git a/ansel/themes/silver/themed_graphics b/ansel/themes/silver/themed_graphics
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/ansel/themes/tango-blue/graphics/add.png b/ansel/themes/tango-blue/graphics/add.png
new file mode 100644 (file)
index 0000000..628f4d5
Binary files /dev/null and b/ansel/themes/tango-blue/graphics/add.png differ
diff --git a/ansel/themes/tango-blue/graphics/ansel.png b/ansel/themes/tango-blue/graphics/ansel.png
new file mode 100644 (file)
index 0000000..ab40463
Binary files /dev/null and b/ansel/themes/tango-blue/graphics/ansel.png differ
diff --git a/ansel/themes/tango-blue/graphics/browse.png b/ansel/themes/tango-blue/graphics/browse.png
new file mode 100644 (file)
index 0000000..68da502
Binary files /dev/null and b/ansel/themes/tango-blue/graphics/browse.png differ
diff --git a/ansel/themes/tango-blue/graphics/down.png b/ansel/themes/tango-blue/graphics/down.png
new file mode 100755 (executable)
index 0000000..6f2a1a3
Binary files /dev/null and b/ansel/themes/tango-blue/graphics/down.png differ
diff --git a/ansel/themes/tango-blue/graphics/galleries.png b/ansel/themes/tango-blue/graphics/galleries.png
new file mode 100644 (file)
index 0000000..68da502
Binary files /dev/null and b/ansel/themes/tango-blue/graphics/galleries.png differ
diff --git a/ansel/themes/tango-blue/graphics/image_add.png b/ansel/themes/tango-blue/graphics/image_add.png
new file mode 100755 (executable)
index 0000000..263e356
Binary files /dev/null and b/ansel/themes/tango-blue/graphics/image_add.png differ
diff --git a/ansel/themes/tango-blue/graphics/lightbox/bullet.gif b/ansel/themes/tango-blue/graphics/lightbox/bullet.gif
new file mode 100755 (executable)
index 0000000..bf8e3c6
Binary files /dev/null and b/ansel/themes/tango-blue/graphics/lightbox/bullet.gif differ
diff --git a/ansel/themes/tango-blue/graphics/lightbox/close.gif b/ansel/themes/tango-blue/graphics/lightbox/close.gif
new file mode 100755 (executable)
index 0000000..ca517b6
Binary files /dev/null and b/ansel/themes/tango-blue/graphics/lightbox/close.gif differ
diff --git a/ansel/themes/tango-blue/graphics/lightbox/closelabel.gif b/ansel/themes/tango-blue/graphics/lightbox/closelabel.gif
new file mode 100755 (executable)
index 0000000..87b4f8b
Binary files /dev/null and b/ansel/themes/tango-blue/graphics/lightbox/closelabel.gif differ
diff --git a/ansel/themes/tango-blue/graphics/lightbox/loading.gif b/ansel/themes/tango-blue/graphics/lightbox/loading.gif
new file mode 100755 (executable)
index 0000000..f864d5f
Binary files /dev/null and b/ansel/themes/tango-blue/graphics/lightbox/loading.gif differ
diff --git a/ansel/themes/tango-blue/graphics/lightbox/nextlabel.gif b/ansel/themes/tango-blue/graphics/lightbox/nextlabel.gif
new file mode 100755 (executable)
index 0000000..6c40e51
Binary files /dev/null and b/ansel/themes/tango-blue/graphics/lightbox/nextlabel.gif differ
diff --git a/ansel/themes/tango-blue/graphics/lightbox/prevlabel.gif b/ansel/themes/tango-blue/graphics/lightbox/prevlabel.gif
new file mode 100755 (executable)
index 0000000..51a31c2
Binary files /dev/null and b/ansel/themes/tango-blue/graphics/lightbox/prevlabel.gif differ
diff --git a/ansel/themes/tango-blue/graphics/mini-error.png b/ansel/themes/tango-blue/graphics/mini-error.png
new file mode 100755 (executable)
index 0000000..71438cd
Binary files /dev/null and b/ansel/themes/tango-blue/graphics/mini-error.png differ
diff --git a/ansel/themes/tango-blue/graphics/mygalleries.png b/ansel/themes/tango-blue/graphics/mygalleries.png
new file mode 100644 (file)
index 0000000..68da502
Binary files /dev/null and b/ansel/themes/tango-blue/graphics/mygalleries.png differ
diff --git a/ansel/themes/tango-blue/graphics/prettythumb-error.png b/ansel/themes/tango-blue/graphics/prettythumb-error.png
new file mode 100755 (executable)
index 0000000..90910bd
Binary files /dev/null and b/ansel/themes/tango-blue/graphics/prettythumb-error.png differ
diff --git a/ansel/themes/tango-blue/graphics/slideshow_next.png b/ansel/themes/tango-blue/graphics/slideshow_next.png
new file mode 100755 (executable)
index 0000000..4a2f9d4
Binary files /dev/null and b/ansel/themes/tango-blue/graphics/slideshow_next.png differ
diff --git a/ansel/themes/tango-blue/graphics/slideshow_pause.png b/ansel/themes/tango-blue/graphics/slideshow_pause.png
new file mode 100755 (executable)
index 0000000..ec61099
Binary files /dev/null and b/ansel/themes/tango-blue/graphics/slideshow_pause.png differ
diff --git a/ansel/themes/tango-blue/graphics/slideshow_play.png b/ansel/themes/tango-blue/graphics/slideshow_play.png
new file mode 100755 (executable)
index 0000000..f8c8ec6
Binary files /dev/null and b/ansel/themes/tango-blue/graphics/slideshow_play.png differ
diff --git a/ansel/themes/tango-blue/graphics/slideshow_prev.png b/ansel/themes/tango-blue/graphics/slideshow_prev.png
new file mode 100755 (executable)
index 0000000..15d1584
Binary files /dev/null and b/ansel/themes/tango-blue/graphics/slideshow_prev.png differ
diff --git a/ansel/themes/tango-blue/graphics/text.png b/ansel/themes/tango-blue/graphics/text.png
new file mode 100644 (file)
index 0000000..d2fda89
Binary files /dev/null and b/ansel/themes/tango-blue/graphics/text.png differ
diff --git a/ansel/themes/tango-blue/graphics/thumb-error.png b/ansel/themes/tango-blue/graphics/thumb-error.png
new file mode 100755 (executable)
index 0000000..90910bd
Binary files /dev/null and b/ansel/themes/tango-blue/graphics/thumb-error.png differ
diff --git a/ansel/themes/tango-blue/graphics/up.png b/ansel/themes/tango-blue/graphics/up.png
new file mode 100755 (executable)
index 0000000..048898a
Binary files /dev/null and b/ansel/themes/tango-blue/graphics/up.png differ
diff --git a/ansel/themes/tango-blue/screen.css b/ansel/themes/tango-blue/screen.css
new file mode 100644 (file)
index 0000000..6978a20
--- /dev/null
@@ -0,0 +1,26 @@
+a.earliest:link, a.earliest:visited, a.earliest:hover, a.earliest:active {
+    color: #eee;
+}
+
+.headerbox table.linedRow td
+{
+    background-color: #a8c4e1;
+    color: #fff;
+}
+
+.headerbox table.linedRow td a {
+    color: #fff;
+}
+
+form {
+    background-color: #5d7cba;
+}
+
+#tagSelector {
+    background-color: #a8c4e1;
+}
+
+#tagSelector form {
+    background-color: #a8c4e1;
+}
+
diff --git a/ansel/themes/tango-blue/themed_graphics b/ansel/themes/tango-blue/themed_graphics
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/ansel/view.php b/ansel/view.php
new file mode 100644 (file)
index 0000000..a5e52ac
--- /dev/null
@@ -0,0 +1,57 @@
+<?php
+/**
+ * $Horde: ansel/view.php,v 1.111 2009/06/10 00:33:01 mrubinsk Exp $
+ *
+ * Copyright 2001-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ */
+
+@define('ANSEL_BASE', dirname(__FILE__));
+require_once ANSEL_BASE . '/lib/base.php';
+
+$viewname = basename(Horde_Util::getFormData('view', 'Gallery'));
+include_once ANSEL_BASE . '/lib/Views/' . $viewname . '.php';
+$view = 'Ansel_View_' . $viewname;
+if (!$view || !class_exists($view)) {
+    header('HTTP/1.0 404 Not Found');
+    echo 'Not Found';
+    exit;
+}
+
+/*
+ * All parameters get passed into the View via a $params array so we can
+ * pass the same parameters easily via API calls.
+ */
+$params['page'] = Horde_Util::getFormData('page', 0);
+$params['sort'] = Horde_Util::getFormData('sort');
+$params['sort_dir'] = Horde_Util::getFormData('sort_dir', 0);
+$params['year'] = Horde_Util::getFormData('year', 0);
+$params['month'] = Horde_Util::getFormData('month', 0);
+$params['day'] = Horde_Util::getFormData('day', 0);
+$params['view'] = $viewname;
+$params['gallery_view'] = Horde_Util::getFormData('gallery_view');
+$params['gallery_id'] = Horde_Util::getFormData('gallery');
+$params['gallery_slug'] = Horde_Util::getFormData('slug');
+$params['force_grouping'] = Horde_Util::getFormData('force_grouping');
+$params['image_id'] = Horde_Util::getFormData('image');
+
+$view = call_user_func(array($view, 'makeView'), $params);
+if (is_a($view, 'PEAR_Error')) {
+    require ANSEL_TEMPLATES . '/common-header.inc';
+    require ANSEL_TEMPLATES . '/menu.inc';
+    echo '<br /><em>' . htmlspecialchars($view->getMessage()) . '</em>';
+    require $registry->get('templates', 'horde') . '/common-footer.inc';
+    exit;
+}
+
+$title = $view->getTitle();
+$view_html = $view->html();
+Horde::addScriptFile('popup.js', 'horde', true);
+require ANSEL_TEMPLATES . '/common-header.inc';
+require ANSEL_TEMPLATES . '/menu.inc';
+echo $view_html;
+require $registry->get('templates', 'horde') . '/common-footer.inc';
diff --git a/ansel/xppublish.php b/ansel/xppublish.php
new file mode 100644 (file)
index 0000000..1817249
--- /dev/null
@@ -0,0 +1,213 @@
+<?php
+/**
+ * $Horde: ansel/xppublish.php,v 1.50 2009/07/08 18:28:40 slusarz Exp $
+ *
+ * Copyright 2001-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ */
+
+@define('AUTH_HANDLER', true);
+@define('ANSEL_BASE', dirname(__FILE__));
+require_once ANSEL_BASE . '/lib/base.php';
+require_once ANSEL_BASE . '/lib/XPPublisher.php';
+
+$cmd = Horde_Util::getFormData('cmd');
+if (empty($cmd)) {
+    $publisher = new Horde_XPPublisher();
+    $publisher->sendRegFile(
+        $registry->getApp() . '-' . $conf['server']['name'],
+        $registry->get('name'),
+        sprintf(_("Publish your photos to %s on %s."), $registry->get('name'), $conf['server']['name']),
+        Horde::applicationUrl(Horde_Util::addParameter('xppublish.php', 'cmd', 'publish'), true, -1),
+        Horde::url($registry->getImageDir() . '/favicon.ico', true, -1));
+    exit;
+}
+
+$PUBLISH_BUTTONS = 'false,true,false';
+$PUBLISH_ONBACK = '';
+$PUBLISH_ONNEXT = '';
+$PUBLISH_CMD = '';
+
+$title = sprintf(_("Publish to %s"), $registry->get('name'));
+require ANSEL_TEMPLATES . '/common-header.inc';
+
+// Check for a login.
+if ($cmd == 'login') {
+    $username = Horde_Util::getFormData('username');
+    $password = Horde_Util::getFormData('password');
+    if ($username && $password) {
+        $auth = Horde_Auth::singleton($conf['auth']['driver']);
+        if ($auth->authenticate($username,
+                                array('password' => $password))) {
+            $cmd = 'list';
+            $PUBLISH_BUTTONS = 'true,true,false';
+            $PUBLISH_ONBACK = 'history.go(-1);';
+        } else {
+            echo '<span class="form-error">' . _("Username or password are incorrect.") . '</span>';
+            $PUBLISH_BUTTONS = 'false,true,false';
+        }
+    } else {
+        echo '<span class="form-error">'. _("Please enter your username and password.") . '</span>';
+        $PUBLISH_BUTTONS = 'false,true,false';
+    }
+}
+
+// If we don't have a valid login, print the login form.
+if (!Horde_Auth::isAuthenticated()) {
+    $PUBLISH_ONNEXT = 'login.submit();';
+    $PUBLISH_CMD = 'login.username.focus();';
+    require ANSEL_TEMPLATES . '/xppublish/login.inc';
+    require ANSEL_TEMPLATES . '/xppublish/javascript.inc';
+    require $registry->get('templates', 'horde') . '/common-footer.inc';
+    exit;
+}
+
+// If we already have a login (through sessions or whatever), and this
+// is the initial request, assume we want to list galleries.
+if ($cmd == 'publish') {
+    $cmd = 'list';
+}
+
+// We're listing galleries.
+$galleryId = Horde_Util::getFormData('gallery');
+if ($cmd == 'list') {
+    $PUBLISH_ONNEXT = 'folder.submit();';
+    $PUBLISH_ONBACK = 'window.location.href="' . Horde::applicationUrl('xppublish.php?cmd=publish', true) . '";';
+    $PUBLISH_BUTTONS = 'true,true,true';
+    require ANSEL_TEMPLATES . '/xppublish/list.inc';
+}
+
+// Check if a gallery was selected from the list.
+if ($cmd == 'select') {
+    if (!$galleryId || !$ansel_storage->galleryExists($galleryId)) {
+        $error = _("Invalid gallery specified.") . "<br />\n";
+    } else {
+        $gallery = $ansel_storage->getGallery($galleryId);
+        if (is_a($gallery, 'PEAR_ERROR')) {
+            $error = _("There was an error accessing the gallery");
+        } else {
+            $error = false;
+        }
+    }
+
+    if ($error) {
+        echo '<span class="form-error">' . $error . '</span><br />';
+        echo _("Press the \"Back\" button and try again.");
+        $PUBLISH_ONBACK = 'window.location.href="' . Horde::applicationUrl('xppublish.php?cmd=list', true) . '";';
+        $PUBLISH_BUTTONS = 'true,false,true';
+    } else {
+        echo '<form id="folder">';
+        Horde_Util::pformInput();
+        echo '<input type="hidden" name="gallery" value="' . $galleryId . '" />';
+        echo '</form>';
+
+        $PUBLISH_CMD = 'publish();';
+    }
+}
+
+// We're creating a new gallery.
+if ($cmd == 'new') {
+    $create = Horde_Util::getFormData('create');
+    $galleryId = Horde_Util::getFormData('gallery_id');
+    $gallery_name = Horde_Util::getFormData('gallery_name');
+    $gallery_desc = Horde_Util::getFormData('gallery_desc');
+    if ($create) {
+        /* Creating a new gallery. */
+        $gallery = $ansel_storage->createGallery(array('name' => $gallery_name,
+                                                       'desc' => $gallery_desc));
+        if (is_a($gallery, 'PEAR_Error')) {
+            $error = sprintf(_("The gallery \"%s\" couldn't be created: %s"), $gallery_name, $gallery->getMessage());
+            Horde::logMessage($error, __FILE__, __LINE__, PEAR_LOG_ERR);
+        } else {
+            $galleryId = $gallery->id;
+            $msg = sprintf(_("The gallery \"%s\" was created successfully."), $gallery_name);
+            Horde::logMessage($msg, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+        }
+    } else {
+        if (empty($galleryId) && $prefs->getValue('autoname')) {
+            $galleryId = md5(microtime());
+        }
+        if (!$gallery_name) {
+            $gallery_name = _("Untitled");
+        }
+        $PUBLISH_CMD = 'folder.gallery_name.focus(); folder.gallery_name.select();';
+        $PUBLISH_ONNEXT = 'folder.submit();';
+        $PUBLISH_ONBACK = 'window.location.href="' . Horde::applicationUrl('xppublish.php?cmd=list', true) . '";';
+        $PUBLISH_BUTTONS = 'true,true,true';
+        require ANSEL_TEMPLATES . '/xppublish/new.inc';
+        require ANSEL_TEMPLATES . '/xppublish/javascript.inc';
+        require $registry->get('templates', 'horde') . '/common-footer.inc';
+        exit;
+    }
+
+    if ($error) {
+        echo '<span class="form-error">' . $error . '</span><br />';
+        echo _("Press the \"Back\" button and try again.");
+        echo '<form id="folder">';
+        Horde_Util::pformInput();
+        echo '<input type="hidden" name="cmd" value="new" />';
+        echo '<input type="hidden" name="gallery_name" value="' . $gallery_name . '" />';
+        echo '</form>';
+        $PUBLISH_ONBACK = 'folder.submit();';
+        $PUBLISH_BUTTONS = 'true,false,true';
+    } else {
+        echo '<form id="folder">';
+        Horde_Util::pformInput();
+        echo '<input type="hidden" name="gallery" value="' . $galleryId . '" />';
+        echo '<input type="hidden" name="cmd" value="list" />';
+        echo '</form>';
+
+        $PUBLISH_CMD = 'folder.submit();';
+    }
+}
+
+// We're adding a photo.
+if ($cmd == 'add') {
+    $galleryId = Horde_Util::getFormData('gallery');
+    $name = isset($_FILES['imagefile']['name']) ? Horde_Util::dispelMagicQuotes($_FILES['imagefile']['name']) : null;
+    $file = isset($_FILES['imagefile']['tmp_name']) ? $_FILES['imagefile']['tmp_name'] : null;
+    if (!$galleryId || !$ansel_storage->galleryExists($galleryId)) {
+        $error = _("Invalid gallery specified.") . "<br />\n";
+    } else {
+        $gallery = $ansel_storage->getGallery($galleryId);
+        if (is_a($gallery, 'PEAR_ERROR') || !$gallery->hasPermission(Horde_Auth::getAuth(), PERMS_EDIT)) {
+            $error = sprintf(_("Access denied adding photos to \"%s\"."), $gallery->get('name'));
+        } else {
+            $error = false;
+        }
+    }
+    if (!$name || $error) {
+        $error = _("No file specified");
+    } elseif (is_a($result = Horde_Browser::wasFileUploaded('imagefile', _("photo")), 'PEAR_Error')) {
+        $error = $result->getMessage();
+    } else {
+        $image = &Ansel::getImageFromFile($file, array('image_filename' => $name));
+        if (is_a($image, 'PEAR_Error')) {
+            $error = $image->getMessage();
+        }  else {
+            $gallery = $ansel_storage->getGallery($galleryId);
+            $image_id = $gallery->addImage($image);
+            if (is_a($image_id, 'PEAR_Error')) {
+                $error = _("There was a problem uploading the photo.");
+            } else {
+                $error = false;
+            }
+            if (is_a($image_id, 'PEAR_Error')) {
+                $image_id = $image_id->getMessage();
+            }
+        }
+    }
+
+    if ($error) {
+        printf(_("ERROR: %s"), $error);
+    } else {
+        echo 'SUCCESS';
+    }
+}
+
+require ANSEL_TEMPLATES . '/xppublish/javascript.inc';
+require $registry->get('templates', 'horde') . '/common-footer.inc';