1: <?php
2: /**
3: * Read-only Turba directory driver for Facebook friends. Requires a Horde
4: * application to be setup on Facebook and configured in horde/config/conf.php.
5: *
6: * Of limited utility since email addresses are not retrievable via the
7: * Facebook API, unless the user allows the Horde application to access it -
8: * and even then, it's a proxied email address.
9: *
10: * Copyright 2009-2012 Horde LLC (http://www.horde.org/)
11: *
12: * See the enclosed file LICENSE for license information (ASL). If you did
13: * did not receive this file, see http://www.horde.org/licenses/apache.
14: *
15: * @author Michael J. Rubinsky <mrubinsk@horde.org>
16: * @author Jan Schneider <jan@horde.org>
17: * @category Horde
18: * @license http://www.horde.org/licenses/apache ASL
19: * @package Turba
20: */
21: class Turba_Driver_Facebook extends Turba_Driver
22: {
23: /**
24: * TODO
25: *
26: * @var Horde_Service_Facebook
27: */
28: private $_facebook;
29:
30: /**
31: * Constructor
32: *
33: * @param string $name
34: * @param array $params
35: */
36: public function __construct($name = '', array $params = array())
37: {
38: parent::__construct($name, $params);
39: $this->_facebook = $params['storage'];
40: unset($params['storage']);
41: }
42:
43: /**
44: * Checks if the current user has the requested permissions on this
45: * source.
46: *
47: * @param integer $perm The permission to check for.
48: *
49: * @return boolean True if the user has permission, otherwise false.
50: */
51: public function hasPermission($perm)
52: {
53: switch ($perm) {
54: case Horde_Perms::DELETE:
55: case Horde_Perms::EDIT:
56: return false;
57:
58: default:
59: return true;
60: }
61: }
62:
63: /**
64: * Searches with the given criteria and returns a
65: * filtered list of results. If the criteria parameter is an empty array,
66: * all records will be returned.
67: *
68: * @param array $criteria Array containing the search criteria.
69: * @param array $fields List of fields to return.
70: * @param array $blobFields A list of fields that contain binary data.
71: * @param boolean $count_only Only return the count of matching items.
72: * @return array Hash containing the search results.
73: * @throws Turba_Exception
74: */
75: protected function _search(array $criteria, array $fields, array $blobFields = array(), $count_only = false)
76: {
77: $results = $this->_getAddressBook($fields);
78:
79: foreach ($results as $key => $contact) {
80: $found = !isset($criteria['OR']);
81: foreach ($criteria as $op => $vals) {
82: if ($op == 'AND') {
83: foreach ($vals as $val) {
84: if (isset($contact[$val['field']])) {
85: switch ($val['op']) {
86: case 'LIKE':
87: if (stristr($contact[$val['field']], $val['test']) === false) {
88: continue 4;
89: }
90: $found = true;
91: break;
92: }
93: }
94: }
95: } elseif ($op == 'OR') {
96: foreach ($vals as $val) {
97: if (isset($contact[$val['field']])) {
98: switch ($val['op']) {
99: case 'LIKE':
100: if (empty($val['test']) ||
101: stristr($contact[$val['field']], $val['test']) !== false) {
102: $found = true;
103: break 3;
104: }
105: }
106: }
107: }
108: }
109: }
110: if ($found) {
111: $results[$key] = $contact;
112: }
113: }
114:
115: return $count_only ? count($results) : $results;
116: }
117:
118: /**
119: * Reads the given data from the address book and returns the results.
120: *
121: * @param string $key The primary key field to use.
122: * @param mixed $ids The ids of the contacts to load.
123: * @param string $owner Only return contacts owned by this user.
124: * @param array $fields List of fields to return.
125: * @param array $blobFields Array of fields containing binary data.
126: *
127: * @return array Hash containing the search results.
128: * @throws Turba_Exception
129: */
130: protected function _read($key, $ids, $owner, array $fields,
131: array $blobFields = array())
132: {
133: return $this->_getEntry($ids, $fields);
134: }
135:
136: /**
137: * TODO
138: */
139: protected function _getEntry(array $keys, array $fields)
140: {
141: $cleanfields = implode(', ', $this->_prepareFields($fields));
142: $fql = 'SELECT ' . $cleanfields . ' FROM user WHERE uid IN (' . implode(', ', $keys) . ')';
143: try {
144: $results = array($this->_fqlToTurba($fields, current($this->_facebook->fql->run($fql))));
145: return $results;
146: } catch (Horde_Service_Facebook_Exception $e) {
147: Horde::logMessage($e, 'ERR');
148: throw new Turba_Exception($e);
149: }
150: }
151:
152: /**
153: * TODO
154: */
155: protected function _getAddressBook(array $fields = array())
156: {
157: $cleanfields = implode(', ', $this->_prepareFields($fields));
158: try {
159: $fql = 'SELECT ' . $cleanfields . ' FROM user WHERE uid IN ('
160: . 'SELECT uid2 FROM friend WHERE uid1=' . $this->_facebook->auth->getLoggedInUser() . ')';
161: $results = $this->_facebook->fql->run($fql);
162: } catch (Horde_Service_Facebook_Exception $e) {
163: Horde::logMessage($e, 'ERR');
164: if ($e->getCode() == Horde_Service_Facebook_ErrorCodes::API_EC_PARAM_SESSION_KEY) {
165: throw new Turba_Exception(_("You are not connected to Facebook. Create a Facebook connection in the Global Preferences."));
166: }
167: throw new Turba_Exception($e);
168: }
169:
170: // Now pull out the results that are arrays
171: $addressbook = array();
172: foreach ($results as &$result) {
173: $addressbook[$result['uid']] = $this->_fqlToTurba($fields, $result);
174: }
175:
176: return $addressbook;
177: }
178:
179: protected function _fqlToTurba($fields, $result)
180: {
181: //$remove = array();
182: foreach ($fields as $field) {
183: if (strpos($field, '.') !== false) {
184: $key = substr($field, 0, strpos($field, '.'));
185: $subfield = substr($field, strpos($field, '.') + 1);
186: $result[$field] = $result[$key][$subfield];
187: }
188: }
189: if (!empty($result['birthday_date'])) {
190: // Make sure the birthdate is in a standard format that
191: // listDateObjects will understand.
192: $bday = new Horde_Date($result['birthday_date']);
193: $result['birthday_date'] = $bday->format('Y-m-d');
194: }
195:
196: return $result;
197: }
198:
199: /**
200: * Parse out a fields array for use in a FB FQL query.
201: *
202: * @param array $fields The fields, as configured in backends.php
203: *
204: *
205: */
206: protected function _prepareFields($fields)
207: {
208: return array_map(array($this, '_prepareCallback'), $fields);
209: }
210:
211: static public function _prepareCallback($field)
212: {
213: if (strpos($field, '.') === false) {
214: return $field;
215: }
216: return substr($field, 0, strpos($field, '.'));
217: }
218: }
219: