Overview

Packages

  • Ansel
  • None

Classes

  • Ansel
  • Ansel_Ajax_Application
  • Ansel_Ajax_Imple_EditCaption
  • Ansel_Ajax_Imple_EditFaces
  • Ansel_Ajax_Imple_EditGalleryFaces
  • Ansel_Ajax_Imple_Embed
  • Ansel_Ajax_Imple_GallerySlugCheck
  • Ansel_Ajax_Imple_ImageSaveGeotag
  • Ansel_Ajax_Imple_LocationAutoCompleter
  • Ansel_Ajax_Imple_MapLayerSelect
  • Ansel_Ajax_Imple_TagActions
  • Ansel_Ajax_Imple_ToggleGalleryActions
  • Ansel_Ajax_Imple_ToggleOtherGalleries
  • Ansel_Ajax_Imple_UploadNotification
  • Ansel_Api
  • Ansel_Exception
  • Ansel_Faces
  • Ansel_Faces_Base
  • Ansel_Faces_Facedetect
  • Ansel_Faces_User
  • Ansel_Factory_Faces
  • Ansel_Factory_Storage
  • Ansel_Factory_Styles
  • Ansel_Form_Ecard
  • Ansel_Form_Image
  • Ansel_Form_ImageDate
  • Ansel_Form_Upload
  • Ansel_Gallery
  • Ansel_Gallery_Decorator_Date
  • Ansel_GalleryMode_Base
  • Ansel_GalleryMode_Date
  • Ansel_GalleryMode_Normal
  • Ansel_Image
  • Ansel_ImageGenerator
  • Ansel_ImageGenerator_Mini
  • Ansel_ImageGenerator_PolaroidThumb
  • Ansel_ImageGenerator_PolaroidThumbStack
  • Ansel_ImageGenerator_RoundedThumb
  • Ansel_ImageGenerator_RoundedThumbStack
  • Ansel_ImageGenerator_Screen
  • Ansel_ImageGenerator_ShadowThumb
  • Ansel_ImageGenerator_ShadowThumbStack
  • Ansel_ImageGenerator_SquareThumb
  • Ansel_ImageGenerator_Thumb
  • Ansel_LoginTasks_SystemTask_Upgrade
  • Ansel_Report
  • Ansel_Report_letter
  • Ansel_Report_mail
  • Ansel_Report_tickets
  • Ansel_Search
  • Ansel_Search_exif
  • Ansel_Search_Tag
  • Ansel_Storage
  • Ansel_Style
  • Ansel_Tagger
  • Ansel_Test
  • Ansel_Tile_DateGallery
  • Ansel_Tile_Gallery
  • Ansel_Tile_Image
  • Ansel_View_Ansel
  • Ansel_View_Base
  • Ansel_View_EmbeddedRenderer_GalleryLink
  • Ansel_View_EmbeddedRenderer_Mini
  • Ansel_View_EmbeddedRenderer_Slideshow
  • Ansel_View_Gallery
  • Ansel_View_GalleryProperties
  • Ansel_View_GalleryRenderer_Base
  • Ansel_View_GalleryRenderer_Gallery
  • Ansel_View_GalleryRenderer_GalleryLightbox
  • Ansel_View_Image
  • Ansel_View_List
  • Ansel_View_Results
  • Ansel_View_Slideshow
  • Ansel_View_Upload
  • Ansel_Widget
  • Ansel_Widget_Actions
  • Ansel_Widget_Base
  • Ansel_Widget_GalleryFaces
  • Ansel_Widget_Geotag
  • Ansel_Widget_ImageFaces
  • Ansel_Widget_Links
  • Ansel_Widget_OtherGalleries
  • Ansel_Widget_OwnerFaces
  • Ansel_Widget_SimilarPhotos
  • Ansel_Widget_Tags
  • Ansel_XPPublisher
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * The Ansel_Tagger:: class wraps Ansel's interaction with the Content/Tagger
  4:  * system.
  5:  *
  6:  * Copyright 2010-2012 Horde LLC (http://www.horde.org/)
  7:  *
  8:  * See the enclosed file COPYING for license information (GPL). If you
  9:  * did not receive this file, see http://www.horde.org/licenses/gpl.
 10:  *
 11:  * @author  Michael J Rubinsky <mrubinsk@horde.org>
 12:  * @category Horde
 13:  * @license  http://www.horde.org/licenses/gpl GPL
 14:  * @package  Ansel
 15:  */
 16: class Ansel_Tagger
 17: {
 18:     /**
 19:      * Local cache of the type name => ids from Content, so we don't have to
 20:      * query for them each time.
 21:      *
 22:      * @var array
 23:      */
 24:     protected $_type_ids = array();
 25: 
 26:     /**
 27:      * Local reference to the tagger.
 28:      *
 29:      * @var Content_Tagger
 30:      */
 31:     protected $_tagger;
 32: 
 33:     /**
 34:      * Constructor.
 35:      *
 36:      * @return Ansel_Tagger
 37:      */
 38:     public function __construct(Content_Tagger $tagger)
 39:     {
 40:         // Remember the types to avoid having Content query them again.
 41:         $key = 'ansel.tagger.type_ids';
 42:         $ids = $GLOBALS['injector']->getInstance('Horde_Cache')->get($key, 360);
 43:         if ($ids) {
 44:             $this->_type_ids = unserialize($ids);
 45:         } else {
 46:             $type_mgr = $GLOBALS['injector']
 47:                 ->getInstance('Content_Types_Manager');
 48:             try {
 49:                 $types = $type_mgr->ensureTypes(array('gallery', 'image'));
 50:             } catch (Content_Exception $e) {
 51:                 throw new Ansel_Exception($e);
 52:             }
 53:             $this->_type_ids = array(
 54:                 'gallery' => (int)$types[0],
 55:                 'image' => (int)$types[1]);
 56:             $GLOBALS['injector']->getInstance('Horde_Cache')
 57:                 ->set($key, serialize($this->_type_ids));
 58:         }
 59: 
 60:         $this->_tagger = $tagger;
 61:     }
 62: 
 63:     /**
 64:      * Tags an ansel object with any number of tags.
 65:      *
 66:      * @param string $localId       The identifier of the ansel object.
 67:      * @param string|array $tags    Either a single tag string or an array of
 68:      *                              tags.
 69:      * @param string $owner         The tag owner (should normally be the owner
 70:      *                              of the resource).
 71:      * @param string $content_type  The type of object we are tagging
 72:      *                              (gallery/image).
 73:      *
 74:      * @return void
 75:      * @throws Ansel_Exception
 76:      */
 77:     public function tag($localId, $tags, $owner, $content_type = 'image')
 78:     {
 79:         if (empty($tags)) {
 80:             return;
 81:         }
 82: 
 83:         if (!is_array($tags)) {
 84:             $tags = $this->_tagger->splitTags($tags);
 85:         }
 86: 
 87:         try {
 88:             $this->_tagger->tag(
 89:                 $owner,
 90:                 array(
 91:                     'object' => $localId,
 92:                     'type' => $this->_type_ids[$content_type]),
 93:                 $tags);
 94:         } catch (Content_Exception $e) {
 95:             throw new Ansel_Exception($e);
 96:         }
 97:     }
 98: 
 99:     /**
100:      * Retrieves the tags on given object(s).
101:      *
102:      * @param mixed  $localId  Either the identifier of the ansel object or
103:      *                         an array of identifiers.
104:      * @param string $type     The type of object $localId represents.
105:      *
106:      * @return array A tag_id => tag_name hash, possibly wrapped in a localid hash.
107:      * @throws Ansel_Exception
108:      */
109:     public function getTags($localId, $type = 'image')
110:     {
111:         try {
112:             if (is_array($localId)) {
113:                 foreach ($localId as &$lid) {
114:                     $lid = (string)$lid;
115:                 }
116:                 return $this->_tagger->getTagsByObjects($localId, $type);
117:             }
118: 
119:             return $this->_tagger->getTags(
120:                 array(
121:                     'objectId' => array(
122:                         'object' => (string)$localId,
123:                         'type' => $this->_type_ids[$type]
124:                     )
125:                 )
126:             );
127:         } catch (Content_Exception $e) {
128:             throw new Ansel_Exception($e);
129:         }
130:     }
131: 
132:     /**
133:      * Retrieve a set of tags that are related to the specifed set. A tag is
134:      * related if resources tagged with the specified set are also tagged with
135:      * the tag being considered. Used to "browse" tagged resources.
136:      *
137:      * @param array $tags   An array of tags to check. This would represent the
138:      *                      current "directory" of tags while browsing.
139:      * @param string $user  The resource must be owned by this user.
140:      *
141:      * @return array  An tag_id => tag_name hash
142:      */
143:     public function browseTags($tags, $user)
144:     {
145:         try {
146:             $tags = array_values($this->_tagger->getTagIds($tags));
147:             $gtags = $this->_tagger->browseTags($tags, $this->_type_ids['gallery'], $user);
148:             $itags = $this->_tagger->browseTags($tags, $this->_type_ids['image'], $user);
149:         } catch (Content_Exception $e) {
150:             throw new Ansel_Exception($e);
151:         }
152:         /* Can't use array_merge here since it would renumber the array keys */
153:         foreach ($gtags as $id => $name) {
154:             if (empty($itags[$id])) {
155:                 $itags[$id] = $name;
156:             }
157:         }
158: 
159:         return $itags;
160:     }
161: 
162:     /**
163:      * Get tag ids for the specified tag names.
164:      *
165:      * @param string|array $tags  Either a tag_name or array of tag_names.
166:      *
167:      * @return array  A tag_id => tag_name hash.
168:      * @throws Ansel_Exception
169:      */
170:     public function getTagIds($tags)
171:     {
172:         try {
173:             return $this->_tagger->getTagIds($tags);
174:         } catch (Content_Exception $e) {
175:             throw new Ansel_Exception($e);
176:         }
177:     }
178: 
179:     /**
180:      * Untag a resource.
181:      *
182:      * Removes the tag regardless of the user that added the tag.
183:      *
184:      * @param string $localId       The ansel object identifier.
185:      * @param mixed $tags           Either a tag_id, tag_name or an array.
186:      * @param string $content_type  The type of object that $localId represents.
187:      *
188:      * @return void
189:      */
190:     public function untag($localId, $tags, $content_type = 'image')
191:     {
192:         try {
193:             $this->_tagger->removeTagFromObject(
194:                 array('object' => $localId, 'type' => $this->_type_ids[$content_type]), $tags);
195:         } catch (Content_Exception $e) {
196:             throw new Ansel_Exception($e);
197:         }
198:     }
199: 
200:     /**
201:      * Tags the given resource with *only* the tags provided, removing any
202:      * tags that are already present but not in the list.
203:      *
204:      * @param string $localId  The identifier for the ansel object.
205:      * @param mixed $tags      Either a tag_id, tag_name, or array of tag_ids.
206:      * @param string $owner    The tag owner - should normally be the resource
207:      *                         owner.
208:      * @param $content_type    The type of object that $localId represents.
209:      */
210:     public function replaceTags($localId, $tags, $owner, $content_type = 'image')
211:     {
212:         /* First get a list of existing tags. */
213:         $existing_tags = $this->getTags($localId, $content_type);
214: 
215:         if (!is_array($tags)) {
216:             $tags = $this->_tagger->splitTags($tags);
217:         }
218:         $remove = array();
219:         foreach ($existing_tags as $tag_id => $existing_tag) {
220:             $found = false;
221:             foreach ($tags as $tag_text) {
222:                 if ($existing_tag == $tag_text) {
223:                     $found = true;
224:                     break;
225:                 }
226:             }
227:             /* Remove any tags that were not found in the passed in list. */
228:             if (!$found) {
229:                 $remove[] = $tag_id;
230:             }
231:         }
232: 
233:         $this->untag($localId, $remove, $content_type);
234:         $add = array();
235:         foreach ($tags as $tag_text) {
236:             $found = false;
237:             foreach ($existing_tags as $existing_tag) {
238:                 if ($tag_text == $existing_tag) {
239:                     $found = true;
240:                     break;
241:                 }
242:             }
243:             if (!$found) {
244:                 $add[] = $tag_text;
245:             }
246:         }
247: 
248:         $this->tag($localId, $add, $owner, $content_type);
249:     }
250: 
251:     /**
252:      * Returns tags belonging to the current user beginning with $token.
253:      *
254:      * Used for autocomplete code.
255:      *
256:      * @param string $token  The token to match the start of the tag with.
257:      *
258:      * @return array  A tag_id => tag_name hash
259:      * @throws Ansel_Exception
260:      */
261:     public function listTags($token)
262:     {
263:         try {
264:             return $this->_tagger->getTags(array('q' => $token, 'userId' => $GLOBALS['registry']->getAuth()));
265:         } catch (Content_Tagger $e) {
266:             throw new Ansel_Exception($e);
267:         }
268:     }
269: 
270:     /**
271:      * Returns the data needed to build a tag cloud based on the specified
272:      * user's tag dataset.
273:      *
274:      * @param string $user    The user whose tags should be included.
275:      *                        If null, all users' tags are returned.
276:      * @param integer $limit  The maximum number of tags to include.
277:      *
278:      * @return Array An array of hashes, each containing tag_id, tag_name, and count.
279:      * @throws Ansel_Exception
280:      */
281:     public function getCloud($user, $limit = 5)
282:     {
283:         $filter = array('limit' => $limit,
284:                         'typeId' => array_values($this->_type_ids));
285:         if (!empty($user)) {
286:             $filter['userId'] = $user;
287:         }
288:         try {
289:             return $this->_tagger->getTagCloud($filter);
290:         } catch (Content_Exception $e) {
291:             throw new Ansel_Exception($e);
292:         }
293:     }
294: 
295:     /**
296:      * Returns cloud-like information, but only for a specified set of tags.
297:      * Useful for displaying the counts of other images tagged with the same
298:      * tag as the currently displayed image.
299:      *
300:      * @param array $tags     An array of either tag names or ids.
301:      * @param integer $limit  Limit results to this many.
302:      *
303:      * @return array  An array of hashes, tag_id, tag_name, and count.
304:      * @throws Ansel_Exception
305:      */
306:     public function getTagInfo($tags = null, $limit = 500, $type = null, $user = null)
307:     {
308:         $filter = array(
309:             'typeId' => empty($type) ? array_values($this->_type_ids) : $this->_type_ids[$type],
310:             'tagIds' => $tags,
311:             'limit' => $limit,
312:             'userId' => $user);
313: 
314:         try {
315:             return $this->_tagger->getTagCloud($filter);
316:         } catch (Content_Exception $e) {
317:             throw new Ansel_Exception($e);
318:         }
319:     }
320: 
321:     /**
322:      * Searches for resources that are tagged with all of the requested tags.
323:      *
324:      * @param array $tags    Either a tag_id, tag_name or an array.
325:      * @param array $filter  Array of filter parameters.
326:      *                       - type (string) - 'gallery' or 'image'
327:      *                       - user (array) - only include objects owned by
328:      *                         these users.
329:      *
330:      * @return  A hash of 'gallery' and 'image' ids.
331:      * @throws Ansel_Exception
332:      */
333:     public function search($tags, $filter = array())
334:     {
335:         $args = array();
336: 
337:         /* These filters are mutually exclusive */
338:         if (!empty($filter['user'])) {
339:             $args['userId'] = $filter['user'];
340:         } elseif (!empty($filter['gallery'])) {
341:             // Only events located in specific galleries
342:             if (!is_array($filter['gallery'])) {
343:                 $filter['gallery'] = array($filter['gallery']);
344:             }
345:             $args['gallery'] = $filter['gallery'];
346:         }
347: 
348:         try {
349:             /* Add the tags to the search */
350:             $args['tagId'] = $this->_tagger->getTagIds($tags);
351: 
352:             /* Restrict to images or galleries */
353:             $gal_results = $image_results = array();
354:             if (empty($filter['type']) || $filter['type'] == 'gallery') {
355:                 $args['typeId'] = $this->_type_ids['gallery'];
356:                 $gal_results = $this->_tagger->getObjects($args);
357:             }
358: 
359:             if (empty($filter['type']) || $filter['type'] == 'image') {
360:                 $args['typeId'] = $this->_type_ids['image'];
361:                 $image_results = $this->_tagger->getObjects($args);
362:             }
363:         } catch (Content_Exception $e) {
364:             throw new Ansel_Exception($e);
365:         }
366: 
367:         /* TODO: Filter out images whose gallery has already matched? */
368:         $results = array('galleries' => array_values($gal_results),
369:                          'images' => array_values($image_results));
370: 
371:         return $results;
372:     }
373: 
374:     /**
375:      * List image ids of images related (via similar tags) to the specified
376:      * image
377:      *
378:      * @param Ansel_Image $image  The image to get related images for.
379:      * @param bolean $ownerOnly   Only return images owned by the specified
380:      *                            image's owner.
381:      *
382:      * @return array  An array of 'image' and 'rank' keys..
383:      */
384:     public function listRelatedImages(Ansel_Image $image, $ownerOnly = true)
385:     {
386:         $args = array('typeId' => 'image', 'limit' => 10);
387:         if ($ownerOnly) {
388:             $gallery = $GLOBALS['injector']->getInstance('Ansel_Storage')->getGallery($image->gallery);
389:             $args['userId'] = $gallery->get('owner');
390:         }
391: 
392:         try {
393:             $ids = $GLOBALS['injector']->getInstance('Content_Tagger')->getSimilarObjects(array('object' => (string)$image->id, 'type' => 'image'), $args);
394:         } catch (Content_Exception $e) {
395:             throw new Ansel_Exception($e);
396:         }
397: 
398:         if (count($ids) == 0) {
399:             return array();
400:         }
401: 
402:         try {
403:             $images = $GLOBALS['injector']->getInstance('Ansel_Storage')->getImages(array('ids' => array_keys($ids)));
404:         } catch (Horde_Exception_NotFound $e) {
405:             $images = array();
406:         }
407: 
408:         $results = array();
409:         foreach ($images as $key => $image) {
410:             $results[] = array('image' => $image, 'rank' => $ids[$key]);
411:         }
412:         return $results;
413:     }
414: }
415: 
API documentation generated by ApiGen