]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/FacebookBridge/lib/facebookclient.php
Merge branch '0.9.x' of gitorious.org:statusnet/mainline into 0.9.x
[quix0rs-gnu-social.git] / plugins / FacebookBridge / lib / facebookclient.php
1 <?php
2 /**
3  * StatusNet, the distributed open-source microblogging tool
4  *
5  * Class for communicating with Facebook
6  *
7  * PHP version 5
8  *
9  * LICENCE: This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU Affero General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Affero General Public License for more details.
18  *
19  * You should have received a copy of the GNU Affero General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  *
22  * @category  Plugin
23  * @package   StatusNet
24  * @author    Craig Andrews <candrews@integralblue.com>
25  * @author    Zach Copley <zach@status.net>
26  * @copyright 2009-2010 StatusNet, Inc.
27  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
28  * @link      http://status.net/
29  */
30
31 if (!defined('STATUSNET')) {
32     exit(1);
33 }
34
35 /**
36  * Class for communication with Facebook
37  *
38  * @category Plugin
39  * @package  StatusNet
40  * @author   Zach Copley <zach@status.net>
41  * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
42  * @link     http://status.net/
43  */
44 class Facebookclient
45 {
46     protected $facebook      = null; // Facebook Graph client obj
47     protected $flink         = null; // Foreign_link StatusNet -> Facebook
48     protected $notice        = null; // The user's notice
49     protected $user          = null; // Sender of the notice
50
51     function __construct($notice)
52     {
53         $this->facebook = self::getFacebook();
54         $this->notice   = $notice;
55
56         $this->flink = Foreign_link::getByUserID(
57             $notice->profile_id,
58             FACEBOOK_SERVICE
59         );
60
61         if (!empty($this->flink)) {
62             $this->user = $this->flink->getUser();
63         }
64     }
65
66     /*
67      * Get an instance of the Facebook Graph SDK object
68      *
69      * @param string $appId     Application
70      * @param string $secret    Facebook API secret
71      *
72      * @return Facebook A Facebook SDK obj
73      */
74     static function getFacebook($appId = null, $secret = null)
75     {
76         // Check defaults and configuration for application ID and secret
77         if (empty($appId)) {
78             $appId = common_config('facebook', 'appid');
79         }
80
81         if (empty($secret)) {
82             $secret = common_config('facebook', 'secret');
83         }
84
85         // If there's no app ID and secret set in the local config, look
86         // for a global one
87         if (empty($appId) || empty($secret)) {
88             $appId  = common_config('facebook', 'global_appid');
89             $secret = common_config('facebook', 'global_secret');
90         }
91
92         return new Facebook(
93             array(
94                'appId'  => $appId,
95                'secret' => $secret,
96                'cookie' => true
97             )
98         );
99     }
100
101     /*
102      * Broadcast a notice to Facebook
103      *
104      * @param Notice $notice    the notice to send
105      */
106     static function facebookBroadcastNotice($notice)
107     {
108         $client = new Facebookclient($notice);
109         return $client->sendNotice();
110     }
111
112     /*
113      * Should the notice go to Facebook?
114      */
115     function isFacebookBound() {
116
117         if (empty($this->flink)) {
118             // User hasn't setup bridging
119             return false;
120         }
121
122         // Avoid a loop
123         if ($this->notice->source == 'Facebook') {
124             common_log(
125                 LOG_INFO,
126                 sprintf(
127                     'Skipping notice %d because its source is Facebook.',
128                     $this->notice->id
129                 ),
130                 __FILE__
131             );
132             return false;
133         }
134
135         // If the user does not want to broadcast to Facebook, move along
136         if (!($this->flink->noticesync & FOREIGN_NOTICE_SEND == FOREIGN_NOTICE_SEND)) {
137             common_log(
138                 LOG_INFO,
139                 sprintf(
140                     'Skipping notice %d because user has FOREIGN_NOTICE_SEND bit off.',
141                     $this->notice->id
142                 ),
143                 __FILE__
144             );
145             return false;
146         }
147
148         // If it's not a reply, or if the user WANTS to send @-replies,
149         // then, yeah, it can go to Facebook.
150         if (!preg_match('/@[a-zA-Z0-9_]{1,15}\b/u', $this->notice->content) ||
151             ($this->flink->noticesync & FOREIGN_NOTICE_SEND_REPLY)) {
152             return true;
153         }
154
155         return false;
156     }
157
158     /*
159      * Determine whether we should send this notice using the Graph API or the
160      * old REST API and then dispatch
161      */
162     function sendNotice()
163     {
164         // If there's nothing in the credentials field try to send via
165         // the Old Rest API
166
167         if ($this->isFacebookBound()) {
168             common_debug("notice is facebook bound", __FILE__);
169             if (empty($this->flink->credentials)) {
170                 return $this->sendOldRest();
171             } else {
172
173                 // Otherwise we most likely have an access token
174                 return $this->sendGraph();
175             }
176         }
177     }
178
179     /*
180      * Send a notice to Facebook using the Graph API
181      */
182     function sendGraph()
183     {
184         try {
185
186             $fbuid = $this->flink->foreign_id;
187
188             common_debug(
189                 sprintf(
190                     "Attempting use Graph API to post notice %d as a stream item for %s (%d), fbuid %d",
191                     $this->notice->id,
192                     $this->user->nickname,
193                     $this->user->id,
194                     $fbuid
195                 ),
196                 __FILE__
197             );
198
199             $params = array(
200                 'access_token' => $this->flink->credentials,
201                 // XXX: Need to worrry about length of the message?
202                 'message'      => $this->notice->content
203             );
204
205             $attachments = $this->notice->attachments();
206
207             if (!empty($attachments)) {
208
209                 // We can only send one attachment with the Graph API :(
210
211                 $first = array_shift($attachments);
212
213                 if (substr($first->mimetype, 0, 6) == 'image/'
214                     || in_array(
215                         $first->mimetype,
216                         array('application/x-shockwave-flash', 'audio/mpeg' ))) {
217
218                    $params['picture'] = $first->url;
219                    $params['caption'] = 'Click for full size';
220                    $params['source']  = $first->url;
221                 }
222
223             }
224
225             $result = $this->facebook->api(
226                 sprintf('/%s/feed', $fbuid), 'post', $params
227             );
228
229             // Save a mapping
230             Notice_to_item::saveNew($this->notice->id, $result['id']);
231
232             common_log(
233                 LOG_INFO,
234                 sprintf(
235                     "Posted notice %d as a stream item for %s (%d), fbuid %d",
236                     $this->notice->id,
237                     $this->user->nickname,
238                     $this->user->id,
239                     $fbuid
240                 ),
241                 __FILE__
242             );
243
244         } catch (FacebookApiException $e) {
245             return $this->handleFacebookError($e);
246         }
247
248         return true;
249     }
250
251     /*
252      * Send a notice to Facebook using the deprecated Old REST API. We need this
253      * for backwards compatibility. Users who signed up for Facebook bridging
254      * using the old Facebook Canvas application do not have an OAuth 2.0
255      * access token.
256      */
257     function sendOldRest()
258     {
259         try {
260
261             $canPublish = $this->checkPermission('publish_stream');
262             $canUpdate  = $this->checkPermission('status_update');
263
264             // We prefer to use stream.publish, because it can handle
265             // attachments and returns the ID of the published item
266
267             if ($canPublish == 1) {
268                 $this->restPublishStream();
269             } else if ($canUpdate == 1) {
270                 // as a last resort we can just update the user's "status"
271                 $this->restStatusUpdate();
272             } else {
273
274                 $msg = 'Not sending notice %d to Facebook because user %s '
275                      . '(%d), fbuid %d,  does not have \'status_update\' '
276                      . 'or \'publish_stream\' permission.';
277
278                 common_log(
279                     LOG_WARNING,
280                     sprintf(
281                         $msg,
282                         $this->notice->id,
283                         $this->user->nickname,
284                         $this->user->id,
285                         $this->flink->foreign_id
286                     ),
287                     __FILE__
288                 );
289             }
290
291         } catch (FacebookApiException $e) {
292             return $this->handleFacebookError($e);
293         }
294
295         return true;
296     }
297
298     /*
299      * Query Facebook to to see if a user has permission
300      *
301      *
302      *
303      * @param $permission the permission to check for - must be either
304      *                    public_stream or status_update
305      *
306      * @return boolean result
307      */
308     function checkPermission($permission)
309     {
310         if (!in_array($permission, array('publish_stream', 'status_update'))) {
311              throw new ServerException("No such permission!");
312         }
313
314         $fbuid = $this->flink->foreign_id;
315
316         common_debug(
317             sprintf(
318                 'Checking for %s permission for user %s (%d), fbuid %d',
319                 $permission,
320                 $this->user->nickname,
321                 $this->user->id,
322                 $fbuid
323             ),
324             __FILE__
325         );
326
327         $hasPermission = $this->facebook->api(
328             array(
329                 'method'   => 'users.hasAppPermission',
330                 'ext_perm' => $permission,
331                 'uid'      => $fbuid
332             )
333         );
334
335         if ($hasPermission == 1) {
336
337             common_debug(
338                 sprintf(
339                     '%s (%d), fbuid %d has %s permission',
340                     $permission,
341                     $this->user->nickname,
342                     $this->user->id,
343                     $fbuid
344                 ),
345                 __FILE__
346             );
347
348             return true;
349
350         } else {
351
352             $logMsg = '%s (%d), fbuid $fbuid does NOT have %s permission.'
353                     . 'Facebook returned: %s';
354
355             common_debug(
356                 sprintf(
357                     $logMsg,
358                     $this->user->nickname,
359                     $this->user->id,
360                     $permission,
361                     $fbuid,
362                     var_export($result, true)
363                 ),
364                 __FILE__
365             );
366
367             return false;
368
369         }
370     }
371
372     /*
373      * Handle a Facebook API Exception
374      *
375      * @param FacebookApiException $e the exception
376      *
377      */
378     function handleFacebookError($e)
379     {
380         $fbuid  = $this->flink->foreign_id;
381         $errmsg = $e->getMessage();
382         $code   = $e->getCode();
383
384         // The Facebook PHP SDK seems to always set the code attribute
385         // of the Exception to 0; they put the real error code in
386         // the message. Gar!
387         if ($code == 0) {
388             preg_match('/^\(#(?<code>\d+)\)/', $errmsg, $matches);
389             $code = $matches['code'];
390         }
391
392         // XXX: Check for any others?
393         switch($code) {
394          case 100: // Invalid parameter
395             $msg = 'Facebook claims notice %d was posted with an invalid '
396                  . 'parameter (error code 100 - %s) Notice details: '
397                  . '[nickname=%s, user id=%d, fbuid=%d, content="%s"]. '
398                  . 'Dequeing.';
399             common_log(
400                 LOG_ERR, sprintf(
401                     $msg,
402                     $this->notice->id,
403                     $errmsg,
404                     $this->user->nickname,
405                     $this->user->id,
406                     $fbuid,
407                     $this->notice->content
408                 ),
409                 __FILE__
410             );
411             return true;
412             break;
413          case 200: // Permissions error
414          case 250: // Updating status requires the extended permission status_update
415             $this->disconnect();
416             return true; // dequeue
417             break;
418          case 341: // Feed action request limit reached
419                 $msg = '%s (userid=%d, fbuid=%d) has exceeded his/her limit '
420                      . 'for posting notices to Facebook today. Dequeuing '
421                      . 'notice %d';
422                 common_log(
423                     LOG_INFO, sprintf(
424                         $msg,
425                         $user->nickname,
426                         $user->id,
427                         $fbuid,
428                         $this->notice->id
429                     ),
430                     __FILE__
431                 );
432             // @fixme: We want to rety at a later time when the throttling has expired
433             // instead of just giving up.
434             return true;
435             break;
436          default:
437             $msg = 'Facebook returned an error we don\'t know how to deal with '
438                  . 'when posting notice %d. Error code: %d, error message: "%s"'
439                  . ' Notice details: [nickname=%s, user id=%d, fbuid=%d, '
440                  . 'notice content="%s"]. Dequeing.';
441             common_log(
442                 LOG_ERR, sprintf(
443                     $msg,
444                     $this->notice->id,
445                     $code,
446                     $errmsg,
447                     $this->user->nickname,
448                     $this->user->id,
449                     $fbuid,
450                     $this->notice->content
451                 ),
452                 __FILE__
453             );
454             return true; // dequeue
455             break;
456         }
457     }
458
459     /*
460      * Publish a notice to Facebook as a status update
461      *
462      * This is the least preferable way to send a notice to Facebook because
463      * it doesn't support attachments and the API method doesn't return
464      * the ID of the post on Facebook.
465      *
466      */
467     function restStatusUpdate()
468     {
469         $fbuid = $this->flink->foreign_id;
470
471         common_debug(
472             sprintf(
473                 "Attempting to post notice %d as a status update for %s (%d), fbuid %d",
474                 $this->notice->id,
475                 $this->user->nickname,
476                 $this->user->id,
477                 $fbuid
478             ),
479             __FILE__
480         );
481
482         $result = $this->facebook->api(
483             array(
484                 'method'               => 'users.setStatus',
485                 'status'               => $this->formatMessage(),
486                 'status_includes_verb' => true,
487                 'uid'                  => $fbuid
488             )
489         );
490
491         if ($result == 1) { // 1 is success
492
493             common_log(
494                 LOG_INFO,
495                 sprintf(
496                     "Posted notice %s as a status update for %s (%d), fbuid %d",
497                     $this->notice->id,
498                     $this->user->nickname,
499                     $this->user->id,
500                     $fbuid
501                 ),
502                 __FILE__
503             );
504
505             // There is no item ID returned for status update so we can't
506             // save a Notice_to_item mapping
507
508         } else {
509
510             $msg = sprintf(
511                 "Error posting notice %s as a status update for %s (%d), fbuid %d - error code: %s",
512                 $this->notice->id,
513                 $this->user->nickname,
514                 $this->user->id,
515                 $fbuid,
516                 $result // will contain 0, or an error
517             );
518
519             throw new FacebookApiException($msg, $result);
520         }
521     }
522
523     /*
524      * Publish a notice to a Facebook user's stream using the old REST API
525      */
526     function restPublishStream()
527     {
528         $fbuid = $this->flink->foreign_id;
529
530         common_debug(
531             sprintf(
532                 'Attempting to post notice %d as stream item for %s (%d) fbuid %d',
533                 $this->notice->id,
534                 $this->user->nickname,
535                 $this->user->id,
536                 $fbuid
537             ),
538             __FILE__
539         );
540
541         $fbattachment = $this->formatAttachments();
542
543         $result = $this->facebook->api(
544             array(
545                 'method'     => 'stream.publish',
546                 'message'    => $this->formatMessage(),
547                 'attachment' => $fbattachment,
548                 'uid'        => $fbuid
549             )
550         );
551
552         if (!empty($result)) { // result will contain the item ID
553
554             // Save a mapping
555             Notice_to_item::saveNew($this->notice->id, $result);
556
557             common_log(
558                 LOG_INFO,
559                 sprintf(
560                     'Posted notice %d as a %s for %s (%d), fbuid %d',
561                     $this->notice->id,
562                     empty($fbattachment) ? 'stream item' : 'stream item with attachment',
563                     $this->user->nickname,
564                     $this->user->id,
565                     $fbuid
566                 ),
567                 __FILE__
568             );
569
570         } else {
571
572             $msg = sprintf(
573                 'Could not post notice %d as a %s for %s (%d), fbuid %d - error code: %s',
574                 $this->notice->id,
575                 empty($fbattachment) ? 'stream item' : 'stream item with attachment',
576                 $this->user->nickname,
577                 $this->user->id,
578                 $result, // result will contain an error code
579                 $fbuid
580             );
581
582             throw new FacebookApiException($msg, $result);
583         }
584     }
585
586     /*
587      * Format the text message of a stream item so it's appropriate for
588      * sending to Facebook. If the notice is too long, truncate it, and
589      * add a linkback to the original notice at the end.
590      *
591      * @return String $txt the formated message
592      */
593     function formatMessage()
594     {
595         // Start with the plaintext source of this notice...
596         $txt = $this->notice->content;
597
598         // Facebook has a 420-char hardcoded max.
599         if (mb_strlen($statustxt) > 420) {
600             $noticeUrl = common_shorten_url($this->notice->uri);
601             $urlLen = mb_strlen($noticeUrl);
602             $txt = mb_substr($statustxt, 0, 420 - ($urlLen + 3)) . ' â€¦ ' . $noticeUrl;
603         }
604
605         return $txt;
606     }
607
608     /*
609      * Format attachments for the old REST API stream.publish method
610      *
611      * Note: Old REST API supports multiple attachments per post
612      *
613      */
614     function formatAttachments()
615     {
616         $attachments = $this->notice->attachments();
617
618         $fbattachment          = array();
619         $fbattachment['media'] = array();
620
621         foreach($attachments as $attachment)
622         {
623             if($enclosure = $attachment->getEnclosure()){
624                 $fbmedia = $this->getFacebookMedia($enclosure);
625             }else{
626                 $fbmedia = $this->getFacebookMedia($attachment);
627             }
628             if($fbmedia){
629                 $fbattachment['media'][]=$fbmedia;
630             }else{
631                 $fbattachment['name'] = ($attachment->title ?
632                                       $attachment->title : $attachment->url);
633                 $fbattachment['href'] = $attachment->url;
634             }
635         }
636         if(count($fbattachment['media'])>0){
637             unset($fbattachment['name']);
638             unset($fbattachment['href']);
639         }
640         return $fbattachment;
641     }
642
643     /**
644      * given a File objects, returns an associative array suitable for Facebook media
645      */
646     function getFacebookMedia($attachment)
647     {
648         $fbmedia    = array();
649
650         if (strncmp($attachment->mimetype, 'image/', strlen('image/')) == 0) {
651             $fbmedia['type']         = 'image';
652             $fbmedia['src']          = $attachment->url;
653             $fbmedia['href']         = $attachment->url;
654         } else if ($attachment->mimetype == 'audio/mpeg') {
655             $fbmedia['type']         = 'mp3';
656             $fbmedia['src']          = $attachment->url;
657         }else if ($attachment->mimetype == 'application/x-shockwave-flash') {
658             $fbmedia['type']         = 'flash';
659
660             // http://wiki.developers.facebook.com/index.php/Attachment_%28Streams%29
661             // says that imgsrc is required... but we have no value to put in it
662             // $fbmedia['imgsrc']='';
663
664             $fbmedia['swfsrc']       = $attachment->url;
665         }else{
666             return false;
667         }
668         return $fbmedia;
669     }
670
671     /*
672      * Disconnect a user from Facebook by deleting his Foreign_link.
673      * Notifies the user his account has been disconnected by email.
674      */
675     function disconnect()
676     {
677         $fbuid = $this->flink->foreign_id;
678
679         common_log(
680             LOG_INFO,
681             sprintf(
682                 'Removing Facebook link for %s (%d), fbuid %d',
683                 $this->user->nickname,
684                 $this->user->id,
685                 $fbuid
686             ),
687             __FILE__
688         );
689
690         $result = $this->flink->delete();
691
692         if (empty($result)) {
693             common_log(
694                 LOG_ERR,
695                 sprintf(
696                     'Could not remove Facebook link for %s (%d), fbuid %d',
697                     $this->user->nickname,
698                     $this->user->id,
699                     $fbuid
700                 ),
701                 __FILE__
702             );
703             common_log_db_error($flink, 'DELETE', __FILE__);
704         }
705
706         // Notify the user that we are removing their Facebook link
707         if (!empty($this->user->email)) {
708             $result = $this->mailFacebookDisconnect();
709
710             if (!$result) {
711
712                 $msg = 'Unable to send email to notify %s (%d), fbuid %d '
713                      . 'about his/her Facebook link being removed.';
714
715                 common_log(
716                     LOG_WARNING,
717                     sprintf(
718                         $msg,
719                         $this->user->nickname,
720                         $this->user->id,
721                         $fbuid
722                     ),
723                     __FILE__
724                 );
725             }
726
727         } else {
728
729             $msg = 'Unable to send email to notify %s (%d), fbuid %d '
730                  . 'about his/her Facebook link being removed because the '
731                  . 'user has not set an email address.';
732
733             common_log(
734                 LOG_WARNING,
735                 sprintf(
736                     $msg,
737                     $this->user->nickname,
738                     $this->user->id,
739                     $fbuid
740                 ),
741                 __FILE__
742             );
743         }
744     }
745
746     /**
747      * Send a mail message to notify a user that her Facebook link
748      * has been terminated.
749      *
750      * @return boolean success flag
751      */
752     function mailFacebookDisconnect()
753     {
754         $profile = $this->user->getProfile();
755
756         $siteName = common_config('site', 'name');
757
758         common_switch_locale($this->user->language);
759
760         $subject = _m('Your Facebook connection has been removed');
761
762         $msg = <<<BODY
763 Hi %1$s,
764
765 We're sorry to inform you we are unable to publish your notice to
766 Facebook, and have removed the connection between your %2$s account and
767 Facebook.
768
769 This may have happened because you have removed permission for %2$s
770 to post on your behalf, or perhaps you have deactivated your Facebook
771 account. You can reconnect your %s account to Facebook at any time by
772 logging in with Facebook again.
773
774 Sincerely,
775
776 %2$s
777 BODY;
778         $body = sprintf(
779             _m($msg),
780             $this->user->nickname,
781             $siteName
782         );
783
784         common_switch_locale();
785
786         $result = mail_to_user($this->user, $subject, $body);
787
788         if (empty($this->user->password)) {
789             $result = self::emailWarn($this->user);
790         }
791
792         return $result;
793     }
794
795     /*
796      * Send the user an email warning that their account has been
797      * disconnected and he/she has no way to login and must contact
798      * the site administrator for help.
799      *
800      * @param User $user the deauthorizing user
801      *
802      */
803     static function emailWarn($user)
804     {
805         $profile = $user->getProfile();
806
807         $siteName  = common_config('site', 'name');
808         $siteEmail = common_config('site', 'email');
809
810         if (empty($siteEmail)) {
811             common_log(
812                 LOG_WARNING,
813                     "No site email address configured. Please set one."
814             );
815         }
816
817         common_switch_locale($user->language);
818
819         $subject = _m('Contact the %s administrator to retrieve your account');
820
821         $msg = <<<BODY
822 Hi %1$s,
823
824 We've noticed you have deauthorized the Facebook connection for your
825 %2$s account.  You have not set a password for your %2$s account yet, so
826 you will not be able to login. If you wish to continue using your %2$s
827 account, please contact the site administrator (%3$s) to set a password.
828
829 Sincerely,
830
831 %2$s
832 BODY;
833         $body = sprintf(
834             _m($msg),
835             $user->nickname,
836             $siteName,
837             $siteEmail
838         );
839
840         common_switch_locale();
841
842         if (mail_to_user($user, $subject, $body)) {
843             common_log(
844                 LOG_INFO,
845                 sprintf(
846                     'Sent account lockout warning to %s (%d)',
847                     $user->nickname,
848                     $user->id
849                 ),
850                 __FILE__
851             );
852         } else {
853             common_log(
854                 LOG_WARNING,
855                 sprintf(
856                     'Unable to send account lockout warning to %s (%d)',
857                     $user->nickname,
858                     $user->id
859                 ),
860                 __FILE__
861             );
862         }
863     }
864
865     /*
866      * Check to see if we have a mapping to a copy of this notice
867      * on Facebook
868      *
869      * @param Notice $notice the notice to check
870      *
871      * @return mixed null if it can't find one, or the id of the Facebook
872      *               stream item
873      */
874     static function facebookStatusId($notice)
875     {
876         $n2i = Notice_to_item::staticGet('notice_id', $notice->id);
877
878         if (empty($n2i)) {
879             return null;
880         } else {
881             return $n2i->item_id;
882         }
883     }
884
885     /*
886      * Save a Foreign_user record of a Facebook user
887      *
888      * @param object $fbuser a Facebook Graph API user obj
889      *                       See: http://developers.facebook.com/docs/reference/api/user
890      * @return mixed $result Id or key
891      *
892      */
893     static function addFacebookUser($fbuser)
894     {
895         // remove any existing, possibly outdated, record
896         $luser = Foreign_user::getForeignUser($fbuser['id'], FACEBOOK_SERVICE);
897
898         if (!empty($luser)) {
899
900             $result = $luser->delete();
901
902             if ($result != false) {
903                 common_log(
904                     LOG_INFO,
905                     sprintf(
906                         'Removed old Facebook user: %s, fbuid %d',
907                         $fbuid['name'],
908                         $fbuid['id']
909                     ),
910                     __FILE__
911                 );
912             }
913         }
914
915         $fuser = new Foreign_user();
916
917         $fuser->nickname = $fbuser['name'];
918         $fuser->uri      = $fbuser['link'];
919         $fuser->id       = $fbuser['id'];
920         $fuser->service  = FACEBOOK_SERVICE;
921         $fuser->created  = common_sql_now();
922
923         $result = $fuser->insert();
924
925         if (empty($result)) {
926             common_log(
927                 LOG_WARNING,
928                     sprintf(
929                         'Failed to add new Facebook user: %s, fbuid %d',
930                         $fbuser['name'],
931                         $fbuser['id']
932                     ),
933                     __FILE__
934             );
935
936             common_log_db_error($fuser, 'INSERT', __FILE__);
937         } else {
938             common_log(
939                 LOG_INFO,
940                 sprintf(
941                     'Added new Facebook user: %s, fbuid %d',
942                     $fbuser['name'],
943                     $fbuser['id']
944                 ),
945                 __FILE__
946             );
947         }
948
949         return $result;
950     }
951
952     /*
953      * Remove an item from a Facebook user's feed if we have a mapping
954      * for it.
955      */
956     function streamRemove()
957     {
958         $n2i = Notice_to_item::staticGet('notice_id', $this->notice->id);
959
960         if (!empty($this->flink) && !empty($n2i)) {
961
962             try {
963
964                 $result = $this->facebook->api(
965                     array(
966                         'method'  => 'stream.remove',
967                         'post_id' => $n2i->item_id,
968                         'uid'     => $this->flink->foreign_id
969                     )
970                 );
971
972                 if (!empty($result) && result == true) {
973
974                     common_log(
975                       LOG_INFO,
976                         sprintf(
977                             'Deleted Facebook item: %s for %s (%d), fbuid %d',
978                             $n2i->item_id,
979                             $this->user->nickname,
980                             $this->user->id,
981                             $this->flink->foreign_id
982                         ),
983                         __FILE__
984                     );
985
986                     $n2i->delete();
987
988                 } else {
989                     throw new FaceboookApiException(var_export($result, true));
990                 }
991
992             } catch (FacebookApiException $e) {
993                 common_log(
994                   LOG_WARNING,
995                     sprintf(
996                         'Could not deleted Facebook item: %s for %s (%d), '
997                             . 'fbuid %d - (API error: %s) item already deleted '
998                             . 'on Facebook? ',
999                         $n2i->item_id,
1000                         $this->user->nickname,
1001                         $this->user->id,
1002                         $this->flink->foreign_id,
1003                         $e
1004                     ),
1005                     __FILE__
1006                 );
1007             }
1008         }
1009     }
1010
1011     /*
1012      * Like an item in a Facebook user's feed if we have a mapping
1013      * for it.
1014      */
1015     function like()
1016     {
1017         $n2i = Notice_to_item::staticGet('notice_id', $this->notice->id);
1018
1019         if (!empty($this->flink) && !empty($n2i)) {
1020
1021             try {
1022
1023                 $result = $this->facebook->api(
1024                     array(
1025                         'method'  => 'stream.addlike',
1026                         'post_id' => $n2i->item_id,
1027                         'uid'     => $this->flink->foreign_id
1028                     )
1029                 );
1030
1031                 if (!empty($result) && result == true) {
1032
1033                     common_log(
1034                       LOG_INFO,
1035                         sprintf(
1036                             'Added like for item: %s for %s (%d), fbuid %d',
1037                             $n2i->item_id,
1038                             $this->user->nickname,
1039                             $this->user->id,
1040                             $this->flink->foreign_id
1041                         ),
1042                         __FILE__
1043                     );
1044
1045                 } else {
1046                     throw new FacebookApiException(var_export($result, true));
1047                 }
1048
1049             } catch (FacebookApiException $e) {
1050                 common_log(
1051                   LOG_WARNING,
1052                     sprintf(
1053                         'Could not like Facebook item: %s for %s (%d), '
1054                             . 'fbuid %d (API error: %s)',
1055                         $n2i->item_id,
1056                         $this->user->nickname,
1057                         $this->user->id,
1058                         $this->flink->foreign_id,
1059                         $e
1060                     ),
1061                     __FILE__
1062                 );
1063             }
1064         }
1065     }
1066
1067     /*
1068      * Unlike an item in a Facebook user's feed if we have a mapping
1069      * for it.
1070      */
1071     function unLike()
1072     {
1073         $n2i = Notice_to_item::staticGet('notice_id', $this->notice->id);
1074
1075         if (!empty($this->flink) && !empty($n2i)) {
1076
1077             try {
1078
1079                 $result = $this->facebook->api(
1080                     array(
1081                         'method'  => 'stream.removeLike',
1082                         'post_id' => $n2i->item_id,
1083                         'uid'     => $this->flink->foreign_id
1084                     )
1085                 );
1086
1087                 if (!empty($result) && result == true) {
1088
1089                     common_log(
1090                       LOG_INFO,
1091                         sprintf(
1092                             'Removed like for item: %s for %s (%d), fbuid %d',
1093                             $n2i->item_id,
1094                             $this->user->nickname,
1095                             $this->user->id,
1096                             $this->flink->foreign_id
1097                         ),
1098                         __FILE__
1099                     );
1100
1101                 } else {
1102                     throw new FacebookApiException(var_export($result, true));
1103                 }
1104
1105             } catch (FacebookApiException $e) {
1106                   common_log(
1107                   LOG_WARNING,
1108                     sprintf(
1109                         'Could not remove like for Facebook item: %s for %s '
1110                           . '(%d), fbuid %d (API error: %s)',
1111                         $n2i->item_id,
1112                         $this->user->nickname,
1113                         $this->user->id,
1114                         $this->flink->foreign_id,
1115                         $e
1116                     ),
1117                     __FILE__
1118                 );
1119             }
1120         }
1121     }
1122
1123 }