1: <?php
2: /**
3: * Base class for all Horde_Share drivers.
4: *
5: * Copyright 2002-2012 Horde LLC (http://www.horde.org/)
6: * Copyright 2002-2007 Infoteck Internet <webmaster@infoteck.qc.ca>
7: *
8: * See the enclosed file COPYING for license information (LGPL). If you
9: * did not receive this file, see http://www.horde.org/licenses/lgpl21.
10: *
11: * @author Joel Vandal <joel@scopserv.com>
12: * @author Mike Cochrame <mike@graftonhall.co.nz>
13: * @author Chuck Hagenbuch <chuck@horde.org>
14: * @author Jan Schneider <jan@horde.org>
15: * @author Gunnar Wrobel <wrobel@pardus.de>
16: * @author Michael J. Rubinsky <mrubinsk@horde.org>
17: * @package Share
18: */
19: abstract class Horde_Share_Base
20: {
21: /**
22: * The application we're managing shares for.
23: *
24: * @var string
25: */
26: protected $_app;
27:
28: /**
29: * The root of the Share tree.
30: *
31: * @var mixed
32: */
33: protected $_root = null;
34:
35: /**
36: * A cache of all shares that have been retrieved, so we don't hit the
37: * backend again and again for them.
38: *
39: * @var array
40: */
41: protected $_cache = array();
42:
43: /**
44: * Id-name-map of already cached share objects.
45: *
46: * @var array
47: */
48: protected $_shareMap = array();
49:
50: /**
51: * Cache used for listShares().
52: *
53: * @var array
54: */
55: protected $_listcache = array();
56:
57: /**
58: * A list of objects that we're currently sorting, for reference during the
59: * sorting algorithm.
60: *
61: * @var array
62: */
63: protected $_sortList;
64:
65: /**
66: * The Horde_Share_Object subclass to instantiate objects as
67: *
68: * @var string
69: */
70: protected $_shareObject;
71:
72: /**
73: * The Horde_Perms object
74: *
75: * @var Horde_Perms_Base
76: */
77: protected $_permsObject;
78:
79: /**
80: * The current user
81: *
82: * @var string
83: */
84: protected $_user;
85:
86: /**
87: * The Horde_Group driver
88: *
89: * @var Horde_Group_Base
90: */
91: protected $_groups;
92:
93: /**
94: * A callback that is passed to the share objects for setting the objects'
95: * Horde_Share object.
96: *
97: * @var callback
98: */
99: protected $_shareCallback;
100:
101: /**
102: * Logger
103: *
104: * @var Horde_Log_Logger
105: */
106: protected $_logger;
107:
108: /**
109: * Configured callbacks. We currently support:
110: *<pre>
111: * add - Called immediately before a new share is added. Receives the
112: * share object as a parameter.
113: * modify - Called immediately before a share object's changes are saved
114: * to storage. Receives the share object as a parameter.
115: * remove - Called immediately before a share is removed from storage.
116: * Receives the share object as a parameter.
117: * list - Called immediately after a list of shares is received from
118: * storage. Passed the userid, share list, and any parameters
119: * passed to the listShare call. Should return the (possibly
120: * modified) share list. @see listShares() for more
121: * info.
122: *</pre>
123: *
124: * @var array
125: */
126: protected $_callbacks;
127:
128: /**
129: * Constructor.
130: *
131: * @param string $app The application that the shares belong
132: * to.
133: * @param string $user The current user.
134: * @param Horde_Perms_Base $perms The permissions object.
135: * @param Horde_Group_Base $groups The Horde_Group driver.
136: *
137: */
138: public function __construct($app, $user, Horde_Perms_Base $perms,
139: Horde_Group_Base $groups)
140: {
141: $this->_app = $app;
142: $this->_user = $user;
143: $this->_permsObject = $perms;
144: $this->_groups = $groups;
145: $this->_logger = new Horde_Support_Stub();
146: }
147:
148: /**
149: * Set a logger object.
150: *
151: * @inject
152: *
153: * @var Horde_Log_Logger $logger The logger object.
154: */
155: public function setLogger(Horde_Log_Logger $logger)
156: {
157: $this->_logger = $logger;
158: }
159:
160: /**
161: * (Re)connects the share object to this share driver.
162: *
163: * @param Horde_Share_Object $object
164: */
165: public function initShareObject(Horde_Share_Object $object)
166: {
167: $object->setShareOb(empty($this->_shareCallback) ? $this : $this->_shareCallback);
168: }
169:
170: public function setShareCallback($callback)
171: {
172: $this->_shareCallback = $callback;
173: }
174:
175: /**
176: * Returns the application we're managing shares for.
177: *
178: * @return string The application this share belongs to.
179: */
180: public function getApp()
181: {
182: return $this->_app;
183: }
184:
185: /**
186: * Returns a Horde_Share_Object object corresponding to the given share
187: * name, with the details retrieved appropriately.
188: *
189: * @param string $name The name of the share to retrieve.
190: *
191: * @return Horde_Share_Object The requested share.
192: */
193: public function getShare($name)
194: {
195: if (isset($this->_cache[$name])) {
196: return $this->_cache[$name];
197: }
198:
199: $share = $this->_getShare($name);
200: $this->_shareMap[$share->getId()] = $name;
201: $this->_cache[$name] = $share;
202:
203: return $share;
204: }
205:
206: /**
207: * Returns a Horde_Share_Object object corresponding to the given
208: * share name, with the details retrieved appropriately.
209: *
210: * @param string $name The name of the share to retrieve.
211: *
212: * @return Horde_Share_Object The requested share.
213: * @throws Horde_Exception_NotFound
214: * @throws Horde_Share_Exception
215: */
216: abstract protected function _getShare($name);
217:
218: /**
219: * Returns a Horde_Share_Object object corresponding to the given unique
220: * ID, with the details retrieved appropriately.
221: *
222: * @param string $cid The id of the share to retrieve.
223: *
224: * @return Horde_Share_Object The requested share.
225: */
226: public function getShareById($cid)
227: {
228: if (!isset($this->_shareMap[$cid])) {
229: $share = $this->_getShareById($cid);
230: $name = $share->getName();
231: $this->_cache[$name] = $share;
232: $this->_shareMap[$cid] = $name;
233: }
234:
235: return $this->_cache[$this->_shareMap[$cid]];
236: }
237:
238: /**
239: * Returns a Horde_Share_Object object corresponding to the given
240: * unique ID, with the details retrieved appropriately.
241: *
242: * @param integer $id The id of the share to retrieve.
243: *
244: * @return Horde_Share_Object_sql The requested share.
245: * @throws Horde_Share_Exception, Horde_Exception_NotFound
246: */
247: abstract protected function _getShareById($id);
248:
249: /**
250: * Returns an array of Horde_Share_Object objects corresponding to the
251: * given set of unique IDs, with the details retrieved appropriately.
252: *
253: * @param array $cids The array of ids to retrieve.
254: *
255: * @return array The requested shares.
256: */
257: public function getShares(array $cids)
258: {
259: $all_shares = $missing_ids = array();
260: foreach ($cids as $cid) {
261: if (!isset($this->_shareMap[$cid])) {
262: $missing_ids[] = $cid;
263: }
264: }
265:
266: if (count($missing_ids)) {
267: $shares = $this->_getShares($missing_ids);
268: foreach (array_keys($shares) as $key) {
269: $this->_cache[$key] = $shares[$key];
270: $this->_shareMap[$shares[$key]->getId()] = $key;
271: }
272: }
273:
274: foreach ($cids as $cid) {
275: $all_shares[$this->_shareMap[$cid]] = $this->_cache[$this->_shareMap[$cid]];
276: }
277:
278: return $all_shares;
279: }
280:
281: /**
282: * Returns an array of Horde_Share_Object objects corresponding
283: * to the given set of unique IDs, with the details retrieved
284: * appropriately.
285: *
286: * @param array $ids The array of ids to retrieve.
287: *
288: * @return array The requested shares.
289: * @throws Horde_Share_Exception
290: */
291: abstract protected function _getShares(array $ids);
292:
293: /**
294: * Lists *all* shares for the current app/share, regardless of
295: * permissions.
296: *
297: * This is for admin functionality and scripting tools, and shouldn't be
298: * called from user-level code!
299: *
300: * @return array All shares for the current app/share.
301: */
302: public function listAllShares()
303: {
304: $shares = $this->_listAllShares();
305: $this->_sortList = $shares;
306: uasort($shares, array($this, '_sortShares'));
307: $this->_sortList = null;
308:
309: return $shares;
310: }
311:
312: /**
313: * Lists *all* shares for the current app/share, regardless of permissions.
314: *
315: * @return array All shares for the current app/share.
316: * @throws Horde_Share_Exception
317: */
318: abstract protected function _listAllShares();
319:
320: /**
321: * Returns an array of all shares that $userid has access to.
322: *
323: * @param string $userid The userid of the user to check access for. An
324: * empty value for the userid will only return shares
325: * with guest access.
326: * @param array $params Additional parameters for the search.
327: *<pre>
328: * 'perm' Require this level of permissions. Horde_Perms constant.
329: * 'attributes' Restrict shares to these attributes. A hash or username.
330: * 'from' Offset. Start at this share
331: * 'count' Limit. Only return this many.
332: * 'sort_by' Sort by attribute.
333: * 'direction' Sort by direction.
334: *</pre>
335: *
336: * @return array The shares the user has access to.
337: */
338: public function listShares($userid, array $params = array())
339: {
340: $params = array_merge(array('perm' => Horde_Perms::SHOW,
341: 'attributes' => null,
342: 'from' => 0,
343: 'count' => 0,
344: 'sort_by' => null,
345: 'direction' => 0),
346: $params);
347:
348: $shares = $this->_listShares($userid, $params);
349: if (!count($shares)) {
350: return $shares;
351: }
352:
353: $shares = $this->getShares($shares);
354: if (is_null($params['sort_by'])) {
355: $this->_sortList = $shares;
356: uasort($shares, array($this, '_sortShares'));
357: $this->_sortList = null;
358: }
359:
360: // Run the results through the callback, if configured.
361: if (!empty($this->_callbacks['list'])) {
362: return $this->runCallback('list', array($userid, $shares, $params));
363: }
364:
365: return $shares;
366: }
367:
368: /**
369: * Returns an array of all shares that $userid has access to.
370: *
371: * @param string $userid The userid of the user to check access for.
372: * @param array $params See listShares().
373: *
374: * @return array The shares the user has access to.
375: */
376: abstract protected function _listShares($userid, array $params = array());
377:
378: /**
379: * Returns an array of all system shares.
380: *
381: * @return array All system shares.
382: */
383: public function listSystemShares()
384: {
385: return array();
386: }
387:
388: /**
389: * Returns the number of shares that $userid has access to.
390: *
391: * @param string $userid The userid of the user to check access for.
392: * @param integer $perm The level of permissions required.
393: * @param mixed $attributes Restrict the shares counted to those
394: * matching $attributes. An array of
395: * attribute/values pairs or a share owner
396: * username.
397: *
398: * @return integer The number of shares
399: */
400: public function countShares($userid, $perm = Horde_Perms::SHOW,
401: $attributes = null)
402: {
403: return count($this->_listShares($userid, array('perm' => $perm, 'attributes' => $attributes)));
404: }
405:
406: /**
407: * Returns a new share object.
408: *
409: * @param string $owner The share owner name.
410: * @param string $share_name The share's name.
411: * @param string $name_attribute The name displayed to the user.
412: *
413: * @return Horde_Share_Object A new share object.
414: * @throws Horde_Share_Exception
415: */
416: public function newShare($owner, $share_name = '', $name_attribute = '')
417: {
418: $share = $this->_newShare($share_name);
419: $share->set('owner', $owner);
420: $share->set('name', $name_attribute);
421: return $share;
422: }
423:
424: /**
425: * Returns a new share object.
426: *
427: * @param string $name The share's name.
428: *
429: * @return Horde_Share_Object A new share object
430: * @throws InvalidArgumentException
431: */
432: abstract protected function _newShare($name);
433:
434: /**
435: * Adds a share to the shares system.
436: *
437: * The share must first be created with newShare(), and have any initial
438: * details added to it, before this function is called.
439: *
440: * @param Horde_Share_Object $share The new share object.
441: *
442: * @throws Horde_Share_Exception
443: */
444: public function addShare(Horde_Share_Object $share)
445: {
446: // Run the results through the callback, if configured.
447: $this->runCallback('add', array($share));
448: $this->_addShare($share);
449:
450: /* Store new share in the caches. */
451: $id = $share->getId();
452: $name = $share->getName();
453: $this->_cache[$name] = $share;
454: $this->_shareMap[$id] = $name;
455:
456: /* Reset caches that depend on unknown criteria. */
457: $this->expireListCache();
458: }
459:
460: /**
461: * Adds a share to the shares system.
462: *
463: * The share must first be created with
464: * Horde_Share_sql::_newShare(), and have any initial details added
465: * to it, before this function is called.
466: *
467: * @param Horde_Share_Object $share The new share object.
468: */
469: abstract protected function _addShare(Horde_Share_Object $share);
470:
471: /**
472: * Renames a share in the shares system.
473: *
474: * @param Horde_Share_Object $share The share to rename.
475: * @param string $name The share's new name.
476: *
477: * @throws Horde_Share_Exception
478: */
479: public function renameShare(Horde_Share_Object $share, $name)
480: {
481: /* Move share in the caches. */
482: unset($this->_cache[$share->getName()]);
483: $this->_cache[$name] = $share;
484:
485: /* Reset caches that depend on unknown criteria. */
486: $this->expireListCache();
487:
488: $this->_renameShare($share, $name);
489: }
490:
491: /**
492: * Renames a share in the shares system.
493: *
494: * @param Horde_Share_Object $share The share to rename.
495: * @param string $name The share's new name.
496: *
497: * @throws Horde_Share_Exception
498: */
499: abstract protected function _renameShare(Horde_Share_Object $share, $name);
500:
501: /**
502: * Removes a share from the shares system permanently.
503: *
504: * @param Horde_Share_Object $share The share to remove.
505: *
506: * @throws Horde_Share_Exception
507: */
508: public function removeShare(Horde_Share_Object $share)
509: {
510: // Run the results through the callback, if configured.
511: $this->runCallback('remove', array($share));
512:
513: /* Remove share from the caches. */
514: $id = $share->getId();
515: unset($this->_shareMap[$id]);
516: unset($this->_cache[$share->getName()]);
517:
518: /* Reset caches that depend on unknown criteria. */
519: $this->expireListCache();
520:
521: $this->_removeShare($share);
522: }
523:
524: /**
525: * Removes a share from the shares system permanently.
526: *
527: * @param Horde_Share_Object $share The share to remove.
528: *
529: * @throws Horde_Share_Exception
530: */
531: abstract protected function _removeShare(Horde_Share_Object $share);
532:
533: /**
534: * Checks if a share name exists in the system.
535: *
536: * @param string $share The share name to check.
537: *
538: * @return boolean True if the share exists.
539: */
540: public function exists($share)
541: {
542: if (isset($this->_cache[$share])) {
543: return true;
544: }
545:
546: return $this->_exists($share);
547: }
548:
549: /**
550: * Check that a share id exists in the system.
551: *
552: * @param integer $id The share id
553: *
554: * @return boolean True if the share exists.
555: */
556: public function idExists($id)
557: {
558: if (isset($this->_shareMap[$id])) {
559: return true;
560: }
561:
562: return $this->_idExists($id);
563: }
564:
565:
566: /**
567: * Checks if a share exists in the system.
568: *
569: * @param string $share The share to check.
570: *
571: * @return boolean True if the share exists.
572: * @throws Horde_Share_Exception
573: */
574: abstract protected function _exists($share);
575:
576: /**
577: * Check that a share id exists in the system.
578: *
579: * @param integer $id The share id
580: *
581: * @return boolean True if the share exists.
582: */
583: abstract protected function _idExists($id);
584:
585: /**
586: * Finds out what rights the given user has to this object.
587: *
588: * @see Horde_Perms::getPermissions
589: *
590: * @param mixed $share The share that should be checked for the users
591: * permissions.
592: * @param string $user The user to check for.
593: *
594: * @return mixed A bitmask of permissions, a permission value, or an array
595: * of permission values the user has, depending on the
596: * permission type and whether the permission value is
597: * ambiguous. False if there is no such permsission.
598: */
599: public function getPermissions($share, $user = null)
600: {
601: if (!($share instanceof Horde_Share_Object)) {
602: $share = $this->getShare($share);
603: }
604:
605: return $this->_permsObject->getPermissions($share->getPermission(), $user);
606: }
607:
608: /**
609: * Set the class type to use for creating share objects.
610: *
611: * @var string $classname The classname to use.
612: */
613: public function setShareClass($classname)
614: {
615: $this->_shareObject = $classname;
616: }
617:
618: /**
619: * Getter for Horde_Perms object
620: *
621: * @return Horde_Perms_Base
622: */
623: public function getPermsObject()
624: {
625: return $this->_permsObject;
626: }
627:
628: /**
629: * Convert TO the storage driver's charset. Individual share objects should
630: * implement this method if needed.
631: *
632: * @param array $data Data to be converted.
633: */
634: public function toDriverCharset($data)
635: {
636: return $data;
637: }
638:
639: /**
640: * Add a callback to the collection
641: *
642: * @param string $type
643: * @param array $callback
644: */
645: public function addCallback($type, $callback)
646: {
647: $this->_callbacks[$type] = $callback;
648: }
649:
650: /**
651: * Returns the share's list cache.
652: *
653: * @return array
654: */
655: public function getListCache()
656: {
657: return $this->_listcache;
658: }
659:
660: /**
661: * Set the list cache.
662: *
663: * @param array $cache
664: */
665: public function setListCache($cache)
666: {
667: $this->_listcache = $cache;
668: }
669:
670: /**
671: * Resets the internal caches.
672: */
673: public function resetCache()
674: {
675: $this->_cache = $this->_shareMap = array();
676: $this->expireListCache();
677: }
678:
679: /**
680: * Give public access to call the share callbacks. Needed to run the
681: * callbacks from the Horde_Share_Object objects.
682: *
683: * @param string $type The callback to run
684: * @param array $params The parameters to pass to the callback.
685: *
686: * @return mixed
687: */
688: public function runCallback($type, $params)
689: {
690: if (!empty($this->_callbacks[$type])) {
691: return call_user_func_array($this->_callbacks[$type], $params);
692: }
693: }
694:
695: /**
696: * Expire the current list cache. This would be needed anytime a share is
697: * either added, deleted, had a change in owner, parent, or perms.
698: */
699: public function expireListCache()
700: {
701: $this->_listcache = array();
702: }
703:
704: /**
705: * Utility function to be used with uasort() for sorting arrays of
706: * Horde_Share objects.
707: *
708: * Example:
709: * <code>
710: * uasort($list, array('Horde_Share', '_sortShares'));
711: * </code>
712: */
713: protected function _sortShares($a, $b)
714: {
715: $aParts = explode(':', $a->getName());
716: $bParts = explode(':', $b->getName());
717:
718: $min = min(count($aParts), count($bParts));
719: $idA = '';
720: $idB = '';
721: for ($i = 0; $i < $min; $i++) {
722: if ($idA) {
723: $idA .= ':';
724: $idB .= ':';
725: }
726: $idA .= $aParts[$i];
727: $idB .= $bParts[$i];
728:
729: if ($idA != $idB) {
730: $curA = isset($this->_sortList[$idA]) ? $this->_sortList[$idA]->get('name') : '';
731: $curB = isset($this->_sortList[$idB]) ? $this->_sortList[$idB]->get('name') : '';
732: return strnatcasecmp($curA, $curB);
733: }
734: }
735:
736: return count($aParts) > count($bParts);
737: }
738: }
739: