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