]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/Facebook/facebookutil.php
Merge branch 'master' into 0.9.x
[quix0rs-gnu-social.git] / plugins / Facebook / facebookutil.php
1 <?php
2 /*
3  * StatusNet - the distributed open-source microblogging tool
4  * Copyright (C) 2008, 2009, StatusNet, Inc.
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 require_once INSTALLDIR . '/plugins/Facebook/facebook/facebook.php';
21 require_once INSTALLDIR . '/plugins/Facebook/facebookaction.php';
22 require_once INSTALLDIR . '/lib/noticelist.php';
23
24 define("FACEBOOK_SERVICE", 2); // Facebook is foreign_service ID 2
25 define("FACEBOOK_NOTICE_PREFIX", 1);
26 define("FACEBOOK_PROMPTED_UPDATE_PREF", 2);
27
28 function getFacebook()
29 {
30     static $facebook = null;
31
32     $apikey = common_config('facebook', 'apikey');
33     $secret = common_config('facebook', 'secret');
34
35     if ($facebook === null) {
36         $facebook = new Facebook($apikey, $secret);
37     }
38
39     if (empty($facebook)) {
40         common_log(LOG_ERR, 'Could not make new Facebook client obj!',
41             __FILE__);
42     }
43
44     return $facebook;
45 }
46
47 function isFacebookBound($notice, $flink) {
48     if (empty($flink)) {
49         return false;
50     }
51
52     // Avoid a loop
53     if ($notice->source == 'Facebook') {
54         common_log(LOG_INFO, "Skipping notice $notice->id because its " .
55                    'source is Facebook.');
56         return false;
57     }
58
59     // If the user does not want to broadcast to Facebook, move along
60     if (!($flink->noticesync & FOREIGN_NOTICE_SEND == FOREIGN_NOTICE_SEND)) {
61         common_log(LOG_INFO, "Skipping notice $notice->id " .
62             'because user has FOREIGN_NOTICE_SEND bit off.');
63         return false;
64     }
65
66     // If it's not a reply, or if the user WANTS to send @-replies,
67     // then, yeah, it can go to Facebook.
68     if (!preg_match('/@[a-zA-Z0-9_]{1,15}\b/u', $notice->content) ||
69         ($flink->noticesync & FOREIGN_NOTICE_SEND_REPLY)) {
70         return true;
71     }
72
73     return false;
74 }
75
76 function facebookBroadcastNotice($notice)
77 {
78     $facebook = getFacebook();
79     $flink = Foreign_link::getByUserID(
80         $notice->profile_id,
81         FACEBOOK_SERVICE
82     );
83
84     if (isFacebookBound($notice, $flink)) {
85         // Okay, we're good to go, update the FB status
86         $fbuid = $flink->foreign_id;
87         $user = $flink->getUser();
88
89         try {
90             // Check permissions
91             common_debug(
92                 'FacebookPlugin - checking for publish_stream permission for user '
93                 . "$user->nickname ($user->id), Facebook UID: $fbuid"
94             );
95
96             // NOTE: $facebook->api_client->users_hasAppPermission('publish_stream', $fbuid)
97             // has been returning bogus results, so we're using FQL to check for
98             // publish_stream permission now
99             $fql = "SELECT publish_stream FROM permissions WHERE uid = $fbuid";
100             $result = $facebook->api_client->fql_query($fql);
101
102             $canPublish = 0;
103
104             if (!empty($result)) {
105                 $canPublish = $result[0]['publish_stream'];
106             }
107
108             if ($canPublish == 1) {
109                 common_debug(
110                     "FacebookPlugin - $user->nickname ($user->id), Facebook UID: $fbuid "
111                     . 'has publish_stream permission.'
112                 );
113             } else {
114                 common_debug(
115                     "FacebookPlugin - $user->nickname ($user->id), Facebook UID: $fbuid "
116                     . 'does NOT have publish_stream permission. Facebook '
117                     . 'returned: ' . var_export($result, true)
118                 );
119             }
120
121             common_debug(
122                 'FacebookPlugin - checking for status_update permission for user '
123                 . "$user->nickname ($user->id), Facebook UID: $fbuid. "
124             );
125
126             $canUpdate = $facebook->api_client->users_hasAppPermission(
127                 'status_update',
128                 $fbuid
129             );
130
131             if ($canUpdate == 1) {
132                 common_debug(
133                     "FacebookPlugin - $user->nickname ($user->id), Facebook UID: $fbuid "
134                     . 'has status_update permission.'
135                 );
136             } else {
137                 common_debug(
138                     "FacebookPlugin - $user->nickname ($user->id), Facebook UID: $fbuid "
139                     .'does NOT have status_update permission. Facebook '
140                     . 'returned: ' . var_export($canPublish, true)
141                 );
142             }
143
144             // Post to Facebook
145             if ($notice->hasAttachments() && $canPublish == 1) {
146                 publishStream($notice, $user, $fbuid);
147             } elseif ($canUpdate == 1 || $canPublish == 1) {
148                 statusUpdate($notice, $user, $fbuid);
149             } else {
150                 $msg = "FacebookPlugin - Not sending notice $notice->id to Facebook " .
151                   "because user $user->nickname has not given the " .
152                   'Facebook app \'status_update\' or \'publish_stream\' permission.';
153                 common_log(LOG_WARNING, $msg);
154             }
155
156             // Finally, attempt to update the user's profile box
157             if ($canPublish == 1 || $canUpdate == 1) {
158                 updateProfileBox($facebook, $flink, $notice, $user);
159             }
160
161         } catch (FacebookRestClientException $e) {
162             return handleFacebookError($e, $notice, $flink);
163         }
164     }
165
166     return true;
167 }
168
169 function handleFacebookError($e, $notice, $flink)
170 {
171     $fbuid  = $flink->foreign_id;
172     $user   = $flink->getUser();
173     $code   = $e->getCode();
174     $errmsg = $e->getMessage();
175
176     // XXX: Check for any others?
177     switch($code) {
178      case 100: // Invalid parameter
179         $msg = "FacebookPlugin - Facebook claims notice %d was posted with an invalid parameter (error code 100):"
180             . "\"%s\" (Notice details: nickname=%s, user ID=%d, Facebook ID=%d, notice content=\"%s\"). "
181             . "Removing notice from the Facebook queue for safety.";
182         common_log(
183             LOG_ERR, sprintf(
184                 $msg,
185                 $notice->id,
186                 $errmsg,
187                 $user->nickname,
188                 $user->id,
189                 $fbuid,
190                 $notice->content
191             )
192         );
193         return true;
194         break;
195      case 200: // Permissions error
196      case 250: // Updating status requires the extended permission status_update
197         remove_facebook_app($flink);
198         return true; // dequeue
199         break;
200      case 341: // Feed action request limit reached
201             $msg = "FacebookPlugin - User %s (User ID=%d, Facebook ID=%d) has exceeded "
202                    . "his/her limit for posting notices to Facebook today. Dequeuing "
203                    . "notice %d.";
204             common_log(
205                 LOG_INFO, sprintf(
206                     $msg,
207                     $user->nickname,
208                     $user->id,
209                     $fbuid,
210                     $notice->id
211                 )
212             );
213         // @fixme: We want to rety at a later time when the throttling has expired
214         // instead of just giving up.
215         return true;
216         break;
217      default:
218         $msg = "FacebookPlugin - Facebook returned an error we don't know how to deal with while trying to "
219             . "post notice %d. Error code: %d, error message: \"%s\". (Notice details: "
220             . "nickname=%s, user ID=%d, Facebook ID=%d, notice content=\"%s\"). Removing notice "
221             . "from the Facebook queue for safety.";
222         common_log(
223             LOG_ERR, sprintf(
224                 $msg,
225                 $notice->id,
226                 $code,
227                 $errmsg,
228                 $user->nickname,
229                 $user->id,
230                 $fbuid,
231                 $notice->content
232             )
233         );
234         return true; // dequeue
235         break;
236     }
237 }
238
239 function statusUpdate($notice, $user, $fbuid)
240 {
241     common_debug(
242         "FacebookPlugin - Attempting to post notice $notice->id "
243         . "as a status update for $user->nickname ($user->id), "
244         . "Facebook UID: $fbuid"
245     );
246
247     $facebook = getFacebook();
248     $result = $facebook->api_client->users_setStatus(
249          $notice->content,
250          $fbuid,
251          false,
252          true
253     );
254
255     common_debug('Facebook returned: ' . var_export($result, true));
256
257     common_log(
258         LOG_INFO,
259         "FacebookPlugin - Posted notice $notice->id as a status "
260         . "update for $user->nickname ($user->id), "
261         . "Facebook UID: $fbuid"
262     );
263 }
264
265 function publishStream($notice, $user, $fbuid)
266 {
267     common_debug(
268         "FacebookPlugin - Attempting to post notice $notice->id "
269         . "as stream item with attachment for $user->nickname ($user->id), "
270         . "Facebook UID: $fbuid"
271     );
272
273     $fbattachment = format_attachments($notice->attachments());
274
275     $facebook = getFacebook();
276     $facebook->api_client->stream_publish(
277         $notice->content,
278         $fbattachment,
279         null,
280         null,
281         $fbuid
282     );
283
284     common_log(
285         LOG_INFO,
286         "FacebookPlugin - Posted notice $notice->id as a stream "
287         . "item with attachment for $user->nickname ($user->id), "
288         . "Facebook UID: $fbuid"
289     );
290 }
291
292 function updateProfileBox($facebook, $flink, $notice, $user) {
293
294     $facebook = getFacebook();
295     $fbaction = new FacebookAction(
296         $output = 'php://output',
297         $indent = null,
298         $facebook,
299         $flink
300     );
301
302     $fbuid = $flink->foreign_id;
303
304     common_debug(
305           'FacebookPlugin - Attempting to update profile box with '
306           . "content from notice $notice->id for $user->nickname ($user->id), "
307           . "Facebook UID: $fbuid"
308     );
309
310     $fbaction->updateProfileBox($notice);
311
312     common_debug(
313         'FacebookPlugin - finished updating profile box for '
314         . "$user->nickname ($user->id) Facebook UID: $fbuid"
315     );
316
317 }
318
319 function format_attachments($attachments)
320 {
321     $fbattachment          = array();
322     $fbattachment['media'] = array();
323
324     foreach($attachments as $attachment)
325     {
326         if($enclosure = $attachment->getEnclosure()){
327             $fbmedia = get_fbmedia_for_attachment($enclosure);
328         }else{
329             $fbmedia = get_fbmedia_for_attachment($attachment);
330         }
331         if($fbmedia){
332             $fbattachment['media'][]=$fbmedia;
333         }else{
334             $fbattachment['name'] = ($attachment->title ?
335                                   $attachment->title : $attachment->url);
336             $fbattachment['href'] = $attachment->url;
337         }
338     }
339     if(count($fbattachment['media'])>0){
340         unset($fbattachment['name']);
341         unset($fbattachment['href']);
342     }
343     return $fbattachment;
344 }
345
346 /**
347 * given an File objects, returns an associative array suitable for Facebook media
348 */
349 function get_fbmedia_for_attachment($attachment)
350 {
351     $fbmedia    = array();
352
353     if (strncmp($attachment->mimetype, 'image/', strlen('image/')) == 0) {
354         $fbmedia['type']         = 'image';
355         $fbmedia['src']          = $attachment->url;
356         $fbmedia['href']         = $attachment->url;
357     } else if ($attachment->mimetype == 'audio/mpeg') {
358         $fbmedia['type']         = 'mp3';
359         $fbmedia['src']          = $attachment->url;
360     }else if ($attachment->mimetype == 'application/x-shockwave-flash') {
361         $fbmedia['type']         = 'flash';
362
363         // http://wiki.developers.facebook.com/index.php/Attachment_%28Streams%29
364         // says that imgsrc is required... but we have no value to put in it
365         // $fbmedia['imgsrc']='';
366
367         $fbmedia['swfsrc']       = $attachment->url;
368     }else{
369         return false;
370     }
371     return $fbmedia;
372 }
373
374 function remove_facebook_app($flink)
375 {
376
377     $user = $flink->getUser();
378
379     common_log(LOG_INFO, 'Removing Facebook App Foreign link for ' .
380         "user $user->nickname (user id: $user->id).");
381
382     $result = $flink->delete();
383
384     if (empty($result)) {
385         common_log(LOG_ERR, 'Could not remove Facebook App ' .
386             "Foreign_link for $user->nickname (user id: $user->id)!");
387         common_log_db_error($flink, 'DELETE', __FILE__);
388     }
389
390     // Notify the user that we are removing their FB app access
391
392     $result = mail_facebook_app_removed($user);
393
394     if (!$result) {
395
396         $msg = 'Unable to send email to notify ' .
397             "$user->nickname (user id: $user->id) " .
398             'that their Facebook app link was ' .
399             'removed!';
400
401         common_log(LOG_WARNING, $msg);
402     }
403 }
404
405 /**
406  * Send a mail message to notify a user that her Facebook Application
407  * access has been removed.
408  *
409  * @param User $user   user whose Facebook app link has been removed
410  *
411  * @return boolean success flag
412  */
413 function mail_facebook_app_removed($user)
414 {
415     $profile = $user->getProfile();
416
417     $site_name = common_config('site', 'name');
418
419     common_switch_locale($user->language);
420
421     $subject = sprintf(
422         _m('Your %1$s Facebook application access has been disabled.',
423             $site_name));
424
425     $body = sprintf(_m("Hi, %1\$s. We're sorry to inform you that we are " .
426         'unable to update your Facebook status from %2$s, and have disabled ' .
427         'the Facebook application for your account. This may be because ' .
428         'you have removed the Facebook application\'s authorization, or ' .
429         'have deleted your Facebook account.  You can re-enable the ' .
430         'Facebook application and automatic status updating by ' .
431         "re-installing the %2\$s Facebook application.\n\nRegards,\n\n%2\$s"),
432         $user->nickname, $site_name);
433
434     common_switch_locale();
435     return mail_to_user($user, $subject, $body);
436 }