1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
14:
15: 16: 17:
18: class Horde_Share_Sql extends Horde_Share_Base
19: {
20:
21: const SQL_FLAG_USERS = 1;
22:
23:
24: const SQL_FLAG_GROUPS = 2;
25:
26:
27: const VERSION = 1;
28:
29: 30: 31: 32: 33:
34: protected $_db;
35:
36: 37: 38: 39: 40:
41: protected $_table;
42:
43: 44: 45: 46: 47:
48: protected $_shareObject = 'Horde_Share_Object_Sql';
49:
50: 51: 52: 53:
54: public function __construct($app, $user, Horde_Perms_Base $perms,
55: Horde_Group_Base $groups)
56: {
57: parent::__construct($app, $user, $perms, $groups);
58: $this->_table = $this->_app . '_shares';
59: }
60:
61: 62: 63: 64: 65:
66: public function setTable($table)
67: {
68: $this->_table = $table;
69: }
70:
71: 72: 73: 74: 75:
76: public function getTable()
77: {
78: return $this->_table;
79: }
80:
81: public function setStorage(Horde_Db_Adapter $db)
82: {
83: $this->_db = $db;
84: }
85:
86: 87: 88: 89:
90: public function getStorage()
91: {
92: return $this->_db;
93: }
94:
95: 96: 97: 98: 99:
100: protected function _hasUsers($share)
101: {
102: return $share['share_flags'] & self::SQL_FLAG_USERS;
103: }
104:
105: 106: 107:
108: protected function _hasGroups($share)
109: {
110: return $share['share_flags'] & self::SQL_FLAG_GROUPS;
111: }
112:
113: 114: 115: 116: 117: 118: 119:
120: protected function _getShareUsers(&$share)
121: {
122: if (!$this->_hasUsers($share)) {
123: return;
124: }
125:
126: try {
127: $rows = $this->_db->selectAll('SELECT * FROM ' . $this->_table . '_users WHERE share_id = ?', array($share['share_id']));
128: } catch (Horde_Db_Exception $e) {
129: throw new Horde_Share_Exception($e->getMessage());
130: }
131:
132: foreach ($rows as $row) {
133: $share['perm']['users'][$row['user_uid']] = $this->_buildPermsFromRow($row);
134: }
135: }
136:
137: 138: 139: 140: 141: 142: 143:
144: protected function _getShareGroups(&$share)
145: {
146: if (!$this->_hasGroups($share)) {
147: return;
148: }
149:
150: try {
151: $rows = $this->_db->selectAll('SELECT * FROM ' . $this->_table . '_groups WHERE share_id = ?', array($share['share_id']));
152: } catch (Horde_Db_Exception $e) {
153: throw new Horde_Share_Exception($e->getMessage());
154: }
155:
156: foreach ($rows as $row) {
157: $share['perm']['groups'][$row['group_uid']] = $this->_buildPermsFromRow($row);
158: }
159: }
160:
161: 162: 163: 164: 165: 166: 167: 168: 169: 170:
171: protected function _getShare($name)
172: {
173: try {
174: $results = $this->_db->selectOne('SELECT * FROM ' . $this->_table . ' WHERE share_name = ?', array($name));
175: } catch (Horde_Db_Exception $e) {
176: throw new Horde_Share_Exception($e->getMessage());
177: }
178: if (!$results) {
179: $this->_logger->err(sprintf('Share name %s not found', $name));
180: throw new Horde_Exception_NotFound(sprintf('Share name %s not found', $name));
181: }
182: $data = $this->_fromDriverCharset($results);
183: $this->_loadPermissions($data);
184:
185: return $this->_createObject($data);
186: }
187:
188: protected function _createObject(array $data = array())
189: {
190: $object = new $this->_shareObject($data);
191: $this->initShareObject($object);
192:
193: return $object;
194: }
195:
196: 197: 198: 199: 200:
201: protected function _loadPermissions(&$data)
202: {
203: $this->_getShareUsers($data);
204: $this->_getShareGroups($data);
205: $this->_getSharePerms($data);
206: }
207:
208: protected function _getSharePerms(&$data)
209: {
210: $data['perm']['type'] = 'matrix';
211: $data['perm']['default'] = isset($data['perm_default']) ? (int)$data['perm_default'] : 0;
212: $data['perm']['guest'] = isset($data['perm_guest']) ? (int)$data['perm_guest'] : 0;
213: $data['perm']['creator'] = isset($data['perm_creator']) ? (int)$data['perm_creator'] : 0;
214: unset($data['perm_creator'], $data['perm_guest'], $data['perm_default']);
215: }
216:
217: 218: 219: 220: 221: 222: 223: 224: 225:
226: protected function _getShareById($id)
227: {
228: try {
229: $results = $this->_db->selectOne('SELECT * FROM ' . $this->_table . ' WHERE share_id = ?', array($id));
230: } catch (Horde_Db_Exception $e) {
231: throw new Horde_Share_Exception($e->getMessage());
232: }
233: if (!$results) {
234: $this->_logger->err(sprintf('Share id %s not found', $id));
235: throw new Horde_Exception_NotFound();
236: }
237: $data = $this->_fromDriverCharset($results);
238: $this->_loadPermissions($data);
239:
240: return $this->_createObject($data);
241: }
242:
243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253:
254: protected function _getShares(array $ids)
255: {
256: try {
257: $rows = $this->_db->selectAll('SELECT * FROM ' . $this->_table . ' WHERE share_id IN (' . str_repeat('?, ', count($ids) - 1) . '?)', $ids);
258: } catch (Horde_Db_Exception $e) {
259: throw new Horde_Share_Exception($e->getMessage());
260: }
261:
262: $sharelist = array();
263: foreach ($rows as $share) {
264: $this->_loadPermissions($share);
265: $sharelist[$share['share_name']] = $this->_createObject($share);
266: }
267:
268: return $sharelist;
269: }
270:
271: 272: 273: 274: 275: 276: 277: 278: 279:
280: public function listAllShares()
281: {
282: return $this->_listAllShares();
283: }
284:
285: 286: 287: 288: 289: 290:
291: protected function _listAllShares()
292: {
293: $shares = array();
294:
295: try {
296: $rows = $this->_db->selectAll('SELECT * FROM ' . $this->_table . ' ORDER BY share_name ASC');
297: } catch (Horde_Db_Exception $e) {
298: throw new Horde_Share_Exception($e->getMessage());
299: }
300:
301: foreach ($rows as $share) {
302: $shares[(int)$share['share_id']] = $this->_fromDriverCharset($share);
303: }
304:
305:
306: try {
307: $rows = $this->_db->selectAll('SELECT * FROM ' . $this->_table . '_users');
308: } catch (Horde_Db_Exception $e) {
309: throw new Horde_Share_Exception($e);
310: }
311: foreach ($rows as $row) {
312: $shares[$row['share_id']]['perm']['users'][$row['user_uid']] = $this->_buildPermsFromRow($row);
313: }
314:
315:
316: try {
317: $rows = $this->_db->selectAll('SELECT * FROM ' . $this->_table . '_groups');
318: } catch (Horde_Db_Exception $e) {
319: throw new Horde_Share_Exception($e->getMessage());
320: }
321: foreach ($rows as $row) {
322: $shares[$row['share_id']]['perm']['groups'][$row['group_uid']] = $this->_buildPermsFromRow($row);
323: }
324:
325: $sharelist = array();
326: foreach ($shares as $data) {
327: $this->_getSharePerms($data);
328: if (!empty($data['share_name'])) {
329: $sharelist[$data['share_name']] = $this->_createObject($data);
330: } else {
331: $sharelist[] = $this->_createObject($data);
332: }
333: }
334:
335: return $sharelist;
336: }
337:
338: 339: 340: 341: 342: 343: 344: 345: 346: 347: 348: 349: 350: 351: 352: 353: 354: 355: 356: 357: 358: 359:
360: public function listShares($userid, array $params = array())
361: {
362: $params = array_merge(array('perm' => Horde_Perms::SHOW,
363: 'attributes' => null,
364: 'from' => 0,
365: 'count' => 0,
366: 'sort_by' => null,
367: 'direction' => 0,
368: 'parent' => null,
369: 'all_levels' => true),
370: $params);
371: $key = md5(serialize(array($userid, $params)));
372: if (isset($this->_listcache[$key])) {
373: return $this->_listcache[$key];
374: }
375: $shares = array();
376: if (is_null($params['sort_by'])) {
377: $sortfield = 's.share_id';
378: } elseif ($params['sort_by'] == 'owner' || $params['sort_by'] == 'id') {
379: $sortfield = 's.share_' . $params['sort_by'];
380: } else {
381: $sortfield = 's.attribute_' . $params['sort_by'];
382: }
383:
384: $query = 'SELECT DISTINCT s.* '
385: . $this->getShareCriteria($userid, $params['perm'], $params['attributes'], $params['parent'], $params['all_levels'])
386: . ' ORDER BY ' . $sortfield
387: . (($params['direction'] == 0) ? ' ASC' : ' DESC');
388:
389: $query = $this->_db->addLimitOffset($query, array('limit' => $params['count'], 'offset' => $params['from']));
390: try {
391: $rows = $this->_db->selectAll($query);
392: } catch (Horde_Db_Exception $e) {
393: throw new Horde_Share_Exception($e->getMessage());
394: }
395:
396: $users = array();
397: $groups = array();
398: foreach ($rows as $share) {
399: $shares[(int)$share['share_id']] = $this->_fromDriverCharset($share);
400: if ($this->_hasUsers($share)) {
401: $users[] = (int)$share['share_id'];
402: }
403: if ($this->_hasGroups($share)) {
404: $groups[] = (int)$share['share_id'];
405: }
406: }
407:
408:
409: if (!empty($users)) {
410: $query = 'SELECT share_id, user_uid, perm FROM ' . $this->_table
411: . '_users WHERE share_id IN (' . str_repeat('?,', count($users) - 1) . '?)';
412:
413: try {
414: $rows = $this->_db->selectAll($query, $users);
415: } catch (Horde_Db_Exception $e) {
416: throw new Horde_Share_Exception($e->getMessage());
417: }
418: foreach ($rows as $share) {
419: $shares[$share['share_id']]['perm']['users'][$share['user_uid']] = (int)$share['perm'];
420: }
421: }
422:
423:
424: if (!empty($groups)) {
425: $query = 'SELECT share_id, group_uid, perm FROM ' . $this->_table
426: . '_groups WHERE share_id IN (' . str_repeat('?,', count($groups) - 1) . '?)';
427: try {
428: $rows = $this->_db->selectAll($query, $groups);
429: } catch (Horde_Db_Exception $e) {
430: throw new Horde_Share_Exception($e->getMessage());
431: }
432: foreach ($rows as $share) {
433: $shares[$share['share_id']]['perm']['groups'][$share['group_uid']] = (int)$share['perm'];
434: }
435: }
436:
437: $sharelist = array();
438: foreach ($shares as $id => $data) {
439: $this->_getSharePerms($data);
440: $sharelist[$data['share_name']] = $this->_createObject($data);
441: }
442: unset($shares);
443:
444:
445: if (!empty($this->_callbacks['list'])) {
446: $sharelist = $this->runCallback('list', array($userid, $sharelist, $params));
447: }
448:
449: $this->_listcache[$key] = $sharelist;
450:
451: return $sharelist;
452: }
453:
454: 455: 456: 457: 458: 459: 460: 461: 462: 463: 464: 465: 466: 467: 468:
469: public function listOwners($perm = Horde_Perms::SHOW, $parent = null, $allLevels = true,
470: $from = 0, $count = 0)
471: {
472: $sql = 'SELECT DISTINCT(s.share_owner) '
473: . $this->getShareCriteria($this->_user, $perm, null, $parent, $allLevels);
474:
475: if ($count) {
476: $sql = $this->_db->addLimitOffset($sql, array('limit' => $count, 'offset' => $from));
477: }
478:
479: try {
480: $allowners = $this->_db->selectValues($sql);
481: } catch (Horde_Db_Exception $e) {
482: throw new Horde_Share_Exception($e);
483: }
484:
485: $owners = array();
486: foreach ($allowners as $owner) {
487: if ($this->countShares($this->_user, $perm, $owner, $parent, $allLevels)) {
488: $owners[] = $owner;
489: }
490: }
491:
492: return $owners;
493: }
494:
495: 496: 497: 498: 499: 500: 501: 502: 503: 504: 505: 506: 507:
508: public function countOwners($perm = Horde_Perms::SHOW, $parent = null, $allLevels = true)
509: {
510: $sql = 'SELECT COUNT(DISTINCT(s.share_owner)) '
511: . $this->getShareCriteria($this->_user, $perm, null, $parent, $allLevels);
512:
513: try {
514: $results = $this->_db->selectValue($sql);
515: } catch (Horde_Db_Exception $e) {
516: throw new Horde_Share_Exception($e);
517: }
518:
519: return $results;
520: }
521:
522: 523: 524: 525: 526: 527: 528:
529: public function getParent(Horde_Share_Object $child)
530: {
531: $parents = $child->get('parents');
532:
533:
534: if (empty($parents)) {
535: return null;
536: }
537: $parents = explode(':', $parents);
538:
539: return $this->getShareById(array_pop($parents));
540: }
541:
542: 543: 544: 545: 546: 547: 548: 549:
550: protected function _listShares($userid, array $params = array())
551: {
552:
553:
554: }
555:
556: 557: 558: 559: 560: 561:
562: public function listSystemShares()
563: {
564: $query = 'SELECT * FROM ' . $this->_table . ' WHERE share_owner IS NULL';
565: try {
566: $rows = $this->_db->selectAll($query);
567: } catch (Horde_Db_Exception $e) {
568: throw new Horde_Share_Exception($e->getMessage());;
569: }
570:
571: $sharelist = array();
572: foreach ($rows as $share) {
573: $data = $this->_fromDriverCharset($share);
574: $this->_getSharePerms($data);
575: $sharelist[$data['share_name']] = $this->_createObject($data);
576: }
577:
578: return $sharelist;
579: }
580:
581: 582: 583: 584: 585: 586: 587: 588: 589: 590: 591: 592: 593: 594: 595: 596: 597:
598: public function countShares($userid, $perm = Horde_Perms::SHOW,
599: $attributes = null, $parent = null, $allLevels = true)
600: {
601: $query = 'SELECT COUNT(DISTINCT s.share_id) '
602: . $this->getShareCriteria($userid, $perm, $attributes, $parent, $allLevels);
603:
604: try {
605: $this->_db->selectValue($query);
606: } catch (Horde_Db_Exception $e) {
607: throw new Horde_Share_Exception($e);
608: }
609:
610: return $this->_db->selectValue($query);
611: }
612:
613: 614: 615: 616: 617: 618: 619: 620:
621: protected function _newShare($name)
622: {
623: if (empty($name)) {
624: throw new InvalidArgumentException('Share names must be non-empty');
625: }
626:
627: return $this->_createObject(array('share_name' => $name));
628: }
629:
630: 631: 632: 633: 634: 635: 636: 637: 638:
639: protected function _addShare(Horde_Share_Object $share)
640: {
641: $share->save();
642: }
643:
644: 645: 646: 647: 648: 649: 650:
651: public function removeShare(Horde_Share_Object $share)
652: {
653:
654: foreach ($share->getChildren(null, null, true) as $child) {
655: $this->removeShare($child);
656: }
657:
658:
659: $this->runCallback('remove', array($share));
660:
661:
662: $id = $share->getId();
663: unset($this->_shareMap[$id]);
664: unset($this->_cache[$share->getName()]);
665:
666:
667: $this->expireListCache();
668:
669: $this->_removeShare($share);
670: }
671:
672: 673: 674: 675: 676: 677: 678: 679:
680: protected function _removeShare(Horde_Share_Object $share)
681: {
682: $params = array($share->getId());
683: $tables = array($this->_table,
684: $this->_table . '_users',
685: $this->_table . '_groups');
686: foreach ($tables as $table) {
687: try {
688: $this->_db->delete('DELETE FROM ' . $table . ' WHERE share_id = ?', $params);
689: } catch (Horde_Db_Exception $e) {
690: throw new Horde_Share_Exception($e->getMessage());
691: }
692: }
693: }
694:
695: 696: 697: 698: 699: 700: 701: 702:
703: protected function _renameShare(Horde_Share_Object $share, $name)
704: {
705: try {
706: $this->_db->update(
707: 'UPDATE ' . $this->_table . ' SET share_name = ? WHERE share_id = ?',
708: array($name, $share->getId()));
709: } catch (Horde_Db_Exception $e) {
710: throw new Horde_Share_Exception($e);
711: }
712: }
713:
714: 715: 716: 717: 718: 719: 720: 721:
722: protected function _exists($share)
723: {
724: try {
725: return (boolean)$this->_db->selectOne('SELECT 1 FROM ' . $this->_table . ' WHERE share_name = ?', array($share));
726: } catch (Horde_Db_Exception $e) {
727: throw new Horde_Share_Exception($e);
728: }
729: }
730:
731: 732: 733: 734: 735: 736: 737:
738: protected function _idExists($id)
739: {
740: try {
741: return (boolean)$this->_db->selectOne('SELECT 1 FROM ' . $this->_table . ' WHERE share_id = ?', array($id));
742: } catch (Horde_Db_Exception $e) {
743: throw new Horde_Share_Exception($e);
744: }
745: }
746:
747: 748: 749: 750: 751: 752: 753: 754: 755: 756: 757: 758: 759: 760: 761: 762: 763: 764:
765: public function getShareCriteria($userid, $perm = Horde_Perms::SHOW,
766: $attributes = null, $parent = null,
767: $allLevels = true)
768: {
769: $query = $where = '';
770: if (!is_null($perm)) {
771: list($query, $where) = $this->_getUserAndGroupCriteria($userid, $perm);
772: }
773: $query = ' FROM ' . $this->_table . ' s ' . $query;
774:
775:
776: $attributes = $this->_toDriverKeys($attributes);
777:
778:
779: $attributes = $this->toDriverCharset($attributes);
780:
781: if (is_array($attributes)) {
782:
783: if (!empty($where)) {
784: $where = ' (' . $where . ') ';
785: }
786: foreach ($attributes as $key => $value) {
787: if (is_array($value)) {
788: $value = array_map(array($this->_db, 'quote'), $value);
789: $where .= ' AND ' . $key . ' IN (' . implode(', ', $value) . ')';
790: } else {
791: $where .= ' AND ' . $key . ' = ' . $this->_db->quote($value);
792: }
793: }
794: } elseif (!empty($attributes)) {
795:
796: $where = (!empty($where) ? ' (' . $where . ') AND ' : ' ') . 's.share_owner = ' . $this->_db->quote($attributes);
797: }
798:
799:
800: if ($parent != null) {
801: if (!($parent instanceof Horde_Share_Object)) {
802: $parent = $this->getShareById($parent);
803: }
804:
805:
806:
807: $parents = $parent->get('parents') . ':' . $parent->getId();
808: if ($allLevels) {
809: $where_parent = '(share_parents = ' . $this->_db->quote($parents)
810: . ' OR share_parents LIKE ' . $this->_db->quote($parents . ':%') . ')';
811: } else {
812: $where_parent = 's.share_parents = ' . $this->_db->quote($parents);
813: }
814: } elseif (!$allLevels) {
815:
816: $where_parent = "(s.share_parents = '' OR s.share_parents IS NULL)";
817: }
818:
819: if (!empty($where_parent)) {
820: if (empty($where)) {
821: $where = $where_parent;
822: } else {
823: $where = '(' . $where . ') AND ' . $where_parent;
824: }
825: }
826:
827: return $query . ' WHERE ' . $where;
828: }
829:
830: 831: 832: 833: 834: 835: 836: 837:
838: protected function _getUserAndGroupCriteria($userid,
839: $perm = Horde_Perms::SHOW)
840: {
841: $query = $where = '';
842:
843: if (empty($userid)) {
844: $where = '(' . $this->_db->buildClause('s.perm_guest', '&', $perm) . ')';
845: } else {
846:
847: $where .= 's.share_owner = ' . $this->_db->quote($userid);
848:
849:
850: $where .= ' OR (' . $this->_db->buildClause('s.perm_creator', '&', $perm) . ')';
851:
852:
853: $where .= ' OR (' . $this->_db->buildClause('s.perm_default', '&', $perm) . ')';
854:
855:
856: $query .= ' LEFT JOIN ' . $this->_table . '_users u ON u.share_id = s.share_id';
857: $where .= ' OR ( u.user_uid = ' . $this->_db->quote($userid)
858: . ' AND (' . $this->_db->buildClause('u.perm', '&', $perm) . '))';
859:
860:
861: try {
862: $groups = $this->_groups->listGroups($userid);
863: if ($groups) {
864:
865: $ids = array_keys($groups);
866: $group_ids = array();
867: foreach ($ids as $id) {
868: $group_ids[] = $this->_db->quote((string)$id);
869: }
870: $query .= ' LEFT JOIN ' . $this->_table . '_groups g ON g.share_id = s.share_id';
871: $where .= ' OR (g.group_uid IN (' . implode(',', $group_ids)
872: . ') AND (' . $this->_db->buildClause('g.perm', '&', $perm) . '))';
873: }
874: } catch (Horde_Group_Exception $e) {
875: $this->_logger->err($e);
876: }
877: }
878:
879: return array($query, $where);
880: }
881:
882: 883: 884: 885: 886: 887: 888:
889: protected function _buildPermsFromRow($row)
890: {
891: return (int)$row['perm'];
892: }
893:
894: 895: 896:
897: protected function _fromDriverCharset($data)
898: {
899: foreach ($data as $key => &$value) {
900: if (substr($key, 0, 9) == 'attribute') {
901: $value = Horde_String::convertCharset(
902: $value, $this->_db->getOption('charset'), 'UTF-8');
903: }
904: }
905:
906: return $data;
907: }
908:
909: 910: 911: 912: 913:
914: public function toDriverCharset($data)
915: {
916: if (!is_array($data)) {
917: return $data;
918: }
919:
920: foreach ($data as $key => &$value) {
921: if (substr($key, 0, 9) == 'attribute') {
922: $value = Horde_String::convertCharset(
923: $value, 'UTF-8', $this->_db->getOption('charset'));
924: }
925: }
926:
927: return $data;
928: }
929:
930: 931: 932: 933: 934: 935: 936: 937:
938: protected function _toDriverKeys($data)
939: {
940: if (!is_array($data)) {
941: return $data;
942: }
943:
944: $driver_keys = array();
945: foreach ($data as $key => $value) {
946: if ($key == 'owner') {
947: $driver_keys['share_owner'] = $value;
948: } else {
949: $driver_keys['attribute_' . $key] = $value;
950: }
951: }
952:
953: return $driver_keys;
954: }
955:
956: }
957: