Friendica Communications Platform (please note that this is a clone of the repository at github, issues are handled there) https://friendi.ca
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1021 lines
32 KiB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
  1. <?php
  2. /**
  3. * Name: Facebook Connector
  4. * Version: 1.0
  5. * Author: Mike Macgirvin <http://macgirvin.com/profile/mike>
  6. */
  7. /**
  8. * Installing the Friendika/Facebook connector
  9. *
  10. * 1. register an API key for your site from developer.facebook.com
  11. * a. We'd be very happy if you include "Friendika" in the application name
  12. * to increase name recognition. The Friendika icons are also present
  13. * in the images directory and may be uploaded as a Facebook app icon.
  14. * Use images/friendika-16.jpg for the Icon and images/friendika-128.jpg for the Logo.
  15. * b. The url should be your site URL with a trailing slash.
  16. * You may use http://portal.friendika.com/privacy as the privacy policy
  17. * URL unless your site has different requirements, and
  18. * http://portal.friendika.com as the Terms of Service URL unless
  19. * you have different requirements. (Friendika is a software application
  20. * and does not require Terms of Service, though your installation of it might).
  21. * c. Set the following values in your .htconfig.php file
  22. * $a->config['facebook']['appid'] = 'xxxxxxxxxxx';
  23. * $a->config['facebook']['appsecret'] = 'xxxxxxxxxxxxxxx';
  24. * Replace with the settings Facebook gives you.
  25. * d. Navigate to Set Web->Site URL & Domain -> Website Settings. Set
  26. * Site URL to yoursubdomain.yourdomain.com. Set Site Domain to your
  27. * yourdomain.com.
  28. * 2. Enable the facebook plugin by including it in .htconfig.php - e.g.
  29. * $a->config['system']['addon'] = 'plugin1,plugin2,facebook';
  30. * 3. Visit the Facebook Settings section of the "Settings->Plugin Settings" page.
  31. * and click 'Install Facebook Connector'.
  32. * 4. This will ask you to login to Facebook and grant permission to the
  33. * plugin to do its stuff. Allow it to do so.
  34. * 5. You're done. To turn it off visit the Plugin Settings page again and
  35. * 'Remove Facebook posting'.
  36. *
  37. * Vidoes and embeds will not be posted if there is no other content. Links
  38. * and images will be converted to a format suitable for the Facebook API and
  39. * long posts truncated - with a link to view the full post.
  40. *
  41. * Facebook contacts will not be able to view private photos, as they are not able to
  42. * authenticate to your site to establish identity. We will address this
  43. * in a future release.
  44. */
  45. define('FACEBOOK_MAXPOSTLEN', 420);
  46. function facebook_install() {
  47. register_hook('post_local_end', 'addon/facebook/facebook.php', 'facebook_post_hook');
  48. register_hook('jot_networks', 'addon/facebook/facebook.php', 'facebook_jot_nets');
  49. register_hook('plugin_settings', 'addon/facebook/facebook.php', 'facebook_plugin_settings');
  50. register_hook('cron', 'addon/facebook/facebook.php', 'facebook_cron');
  51. register_hook('queue_predeliver', 'addon/facebook/facebook.php', 'fb_queue_hook');
  52. }
  53. function facebook_uninstall() {
  54. unregister_hook('post_local_end', 'addon/facebook/facebook.php', 'facebook_post_hook');
  55. unregister_hook('jot_networks', 'addon/facebook/facebook.php', 'facebook_jot_nets');
  56. unregister_hook('plugin_settings', 'addon/facebook/facebook.php', 'facebook_plugin_settings');
  57. unregister_hook('cron', 'addon/facebook/facebook.php', 'facebook_cron');
  58. unregister_hook('queue_predeliver', 'addon/facebook/facebook.php', 'fb_queue_hook');
  59. }
  60. /* declare the facebook_module function so that /facebook url requests will land here */
  61. function facebook_module() {}
  62. /* If a->argv[1] is a nickname, this is a callback from Facebook oauth requests. */
  63. function facebook_init(&$a) {
  64. if($a->argc != 2)
  65. return;
  66. $nick = $a->argv[1];
  67. if(strlen($nick))
  68. $r = q("SELECT `uid` FROM `user` WHERE `nickname` = '%s' LIMIT 1",
  69. dbesc($nick)
  70. );
  71. if(! count($r))
  72. return;
  73. $uid = $r[0]['uid'];
  74. $auth_code = (($_GET['code']) ? $_GET['code'] : '');
  75. $error = (($_GET['error_description']) ? $_GET['error_description'] : '');
  76. if($error)
  77. logger('facebook_init: Error: ' . $error);
  78. if($auth_code && $uid) {
  79. $appid = get_config('facebook','appid');
  80. $appsecret = get_config('facebook', 'appsecret');
  81. $x = fetch_url('https://graph.facebook.com/oauth/access_token?client_id='
  82. . $appid . '&client_secret=' . $appsecret . '&redirect_uri='
  83. . urlencode($a->get_baseurl() . '/facebook/' . $nick)
  84. . '&code=' . $auth_code);
  85. logger('facebook_init: returned access token: ' . $x, LOGGER_DATA);
  86. if(strpos($x,'access_token=') !== false) {
  87. $token = str_replace('access_token=', '', $x);
  88. if(strpos($token,'&') !== false)
  89. $token = substr($token,0,strpos($token,'&'));
  90. set_pconfig($uid,'facebook','access_token',$token);
  91. set_pconfig($uid,'facebook','post','1');
  92. set_pconfig($uid,'facebook','no_linking',1);
  93. fb_get_self($uid);
  94. fb_get_friends($uid);
  95. fb_consume_all($uid);
  96. }
  97. }
  98. }
  99. function fb_get_self($uid) {
  100. $access_token = get_pconfig($uid,'facebook','access_token');
  101. if(! $access_token)
  102. return;
  103. $s = fetch_url('https://graph.facebook.com/me/?access_token=' . $access_token);
  104. if($s) {
  105. $j = json_decode($s);
  106. set_pconfig($uid,'facebook','self_id',(string) $j->id);
  107. }
  108. }
  109. function fb_get_friends($uid) {
  110. $access_token = get_pconfig($uid,'facebook','access_token');
  111. $no_linking = get_pconfig($uid,'facebook','no_linking');
  112. if($no_linking)
  113. return;
  114. if(! $access_token)
  115. return;
  116. $s = fetch_url('https://graph.facebook.com/me/friends?access_token=' . $access_token);
  117. if($s) {
  118. logger('facebook: fb_get_friends: ' . $s, LOGGER_DATA);
  119. $j = json_decode($s);
  120. logger('facebook: fb_get_friends: json: ' . print_r($j,true), LOGGER_DATA);
  121. if(! $j->data)
  122. return;
  123. foreach($j->data as $person) {
  124. $s = fetch_url('https://graph.facebook.com/' . $person->id . '?access_token=' . $access_token);
  125. if($s) {
  126. $jp = json_decode($s);
  127. logger('fb_get_friends: info: ' . print_r($jp,true), LOGGER_DATA);
  128. // always use numeric link for consistency
  129. $jp->link = 'http://facebook.com/profile.php?id=' . $person->id;
  130. // check if we already have a contact
  131. $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' LIMIT 1",
  132. intval($uid),
  133. dbesc($jp->link)
  134. );
  135. if(count($r)) {
  136. // check that we have all the photos, this has been known to fail on occasion
  137. if((! $r[0]['photo']) || (! $r[0]['thumb']) || (! $r[0]['micro'])) {
  138. require_once("Photo.php");
  139. $photos = import_profile_photo('https://graph.facebook.com/' . $jp->id . '/picture', $uid, $r[0]['id']);
  140. $r = q("UPDATE `contact` SET `photo` = '%s',
  141. `thumb` = '%s',
  142. `micro` = '%s',
  143. `name-date` = '%s',
  144. `uri-date` = '%s',
  145. `avatar-date` = '%s'
  146. WHERE `id` = %d LIMIT 1
  147. ",
  148. dbesc($photos[0]),
  149. dbesc($photos[1]),
  150. dbesc($photos[2]),
  151. dbesc(datetime_convert()),
  152. dbesc(datetime_convert()),
  153. dbesc(datetime_convert()),
  154. intval($r[0]['id'])
  155. );
  156. }
  157. continue;
  158. }
  159. else {
  160. // create contact record
  161. $r = q("INSERT INTO `contact` ( `uid`, `created`, `url`, `addr`, `alias`, `notify`, `poll`,
  162. `name`, `nick`, `photo`, `network`, `rel`, `priority`,
  163. `writable`, `blocked`, `readonly`, `pending` )
  164. VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, 0, 0, 0 ) ",
  165. intval($uid),
  166. dbesc(datetime_convert()),
  167. dbesc($jp->link),
  168. dbesc(''),
  169. dbesc(''),
  170. dbesc($jp->id),
  171. dbesc('facebook ' . $jp->id),
  172. dbesc($jp->name),
  173. dbesc(($jp->nickname) ? $jp->nickname : strtolower($jp->first_name)),
  174. dbesc('https://graph.facebook.com/' . $jp->id . '/picture'),
  175. dbesc(NETWORK_FACEBOOK),
  176. intval(CONTACT_IS_FRIEND),
  177. intval(1),
  178. intval(1)
  179. );
  180. }
  181. $r = q("SELECT * FROM `contact` WHERE `url` = '%s' AND `uid` = %d LIMIT 1",
  182. dbesc($jp->link),
  183. intval($uid)
  184. );
  185. if(! count($r)) {
  186. continue;
  187. }
  188. $contact = $r[0];
  189. $contact_id = $r[0]['id'];
  190. require_once("Photo.php");
  191. $photos = import_profile_photo($r[0]['photo'],$uid,$contact_id);
  192. $r = q("UPDATE `contact` SET `photo` = '%s',
  193. `thumb` = '%s',
  194. `micro` = '%s',
  195. `name-date` = '%s',
  196. `uri-date` = '%s',
  197. `avatar-date` = '%s'
  198. WHERE `id` = %d LIMIT 1
  199. ",
  200. dbesc($photos[0]),
  201. dbesc($photos[1]),
  202. dbesc($photos[2]),
  203. dbesc(datetime_convert()),
  204. dbesc(datetime_convert()),
  205. dbesc(datetime_convert()),
  206. intval($contact_id)
  207. );
  208. }
  209. }
  210. }
  211. }
  212. // This is the POST method to the facebook settings page
  213. // Content is posted to Facebook in the function facebook_post_hook()
  214. function facebook_post(&$a) {
  215. $uid = local_user();
  216. if($uid){
  217. $value = ((x($_POST,'post_by_default')) ? intval($_POST['post_by_default']) : 0);
  218. set_pconfig($uid,'facebook','post_by_default', $value);
  219. $no_linking = get_pconfig($uid,'facebook','no_linking');
  220. $linkvalue = ((x($_POST,'facebook_linking')) ? intval($_POST['facebook_linking']) : 0);
  221. set_pconfig($uid,'facebook','no_linking', (($linkvalue) ? 0 : 1));
  222. // FB linkage was allowed but has just been turned off - remove all FB contacts and posts
  223. if((! intval($no_linking)) && (! intval($linkvalue))) {
  224. $r = q("SELECT `id` FROM `contact` WHERE `uid` = %d AND `network` = '%s' ",
  225. intval($uid),
  226. dbesc(NETWORK_FACEBOOK)
  227. );
  228. if(count($r)) {
  229. require_once('include/Contact.php');
  230. foreach($r as $rr)
  231. contact_remove($rr['id']);
  232. }
  233. }
  234. elseif(intval($no_linking) && intval($linkvalue)) {
  235. // FB linkage is now allowed - import stuff.
  236. fb_get_self($uid);
  237. fb_get_friends($uid);
  238. fb_consume_all($uid);
  239. }
  240. info( t('Settings updated.') . EOL);
  241. }
  242. return;
  243. }
  244. // Facebook settings form
  245. function facebook_content(&$a) {
  246. if(! local_user()) {
  247. notice( t('Permission denied.') . EOL);
  248. return '';
  249. }
  250. if($a->argc > 1 && $a->argv[1] === 'remove') {
  251. del_pconfig(local_user(),'facebook','post');
  252. info( t('Facebook disabled') . EOL);
  253. }
  254. if($a->argc > 1 && $a->argv[1] === 'friends') {
  255. fb_get_friends(local_user());
  256. info( t('Updating contacts') . EOL);
  257. }
  258. $fb_installed = get_pconfig(local_user(),'facebook','post');
  259. $appid = get_config('facebook','appid');
  260. if(! $appid) {
  261. notice( t('Facebook API key is missing.') . EOL);
  262. return '';
  263. }
  264. $a->page['htmlhead'] .= '<link rel="stylesheet" type="text/css" href="'
  265. . $a->get_baseurl() . '/addon/facebook/facebook.css' . '" media="all" />' . "\r\n";
  266. $o .= '<h3>' . t('Facebook Connect') . '</h3>';
  267. if(! $fb_installed) {
  268. $o .= '<div id="facebook-enable-wrapper">';
  269. $o .= '<a href="https://www.facebook.com/dialog/oauth?client_id=' . $appid . '&redirect_uri='
  270. . $a->get_baseurl() . '/facebook/' . $a->user['nickname'] . '&scope=publish_stream,read_stream,offline_access">' . t('Install Facebook connector for this account.') . '</a>';
  271. $o .= '</div>';
  272. }
  273. if($fb_installed) {
  274. $o .= '<div id="facebook-disable-wrapper">';
  275. $o .= '<a href="' . $a->get_baseurl() . '/facebook/remove' . '">' . t('Remove Facebook connector') . '</a></div>';
  276. $o .= '<div id="facebook-post-default-form">';
  277. $o .= '<form action="facebook" method="post" >';
  278. $post_by_default = get_pconfig(local_user(),'facebook','post_by_default');
  279. $checked = (($post_by_default) ? ' checked="checked" ' : '');
  280. $o .= '<input type="checkbox" name="post_by_default" value="1"' . $checked . '/>' . ' ' . t('Post to Facebook by default') . EOL;
  281. $no_linking = get_pconfig(local_user(),'facebook','no_linking');
  282. $checked = (($no_linking) ? '' : ' checked="checked" ');
  283. $o .= '<input type="checkbox" name="facebook_linking" value="1"' . $checked . '/>' . ' ' . t('Link all your Facebook friends and conversations') . EOL ;
  284. $hidden = (($a->user['hidewall'] || get_config('system','block_public')) ? true : false);
  285. if(! $hidden) {
  286. $o .= EOL;
  287. $o .= t('Warning: Your Facebook privacy settings can not be imported.') . EOL;
  288. $o .= t('Linked Facebook items <strong>may</strong> be publicly visible, depending on your privacy settings for this website/account.') . EOL;
  289. }
  290. $o .= '<input type="submit" name="submit" value="' . t('Submit') . '" /></form></div>';
  291. }
  292. return $o;
  293. }
  294. function facebook_cron($a,$b) {
  295. $last = get_config('facebook','last_poll');
  296. $poll_interval = intval(get_config('facebook','poll_interval'));
  297. if(! $poll_interval)
  298. $poll_interval = 3600;
  299. if($last) {
  300. $next = $last + $poll_interval;
  301. if($next > time())
  302. return;
  303. }
  304. logger('facebook_cron');
  305. // Find the FB users on this site and randomize in case one of them
  306. // uses an obscene amount of memory. It may kill this queue run
  307. // but hopefully we'll get a few others through on each run.
  308. $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'facebook' AND `k` = 'post' AND `v` = '1' ORDER BY RAND() ");
  309. if(count($r)) {
  310. foreach($r as $rr) {
  311. if(get_pconfig($rr['uid'],'facebook','no_linking'))
  312. continue;
  313. // check for new friends once a day
  314. $last_friend_check = get_pconfig($rr['uid'],'facebook','friend_check');
  315. if($last_friend_check)
  316. $next_friend_check = $last_friend_check + 86400;
  317. if($next_friend_check <= time()) {
  318. fb_get_friends($rr['uid']);
  319. set_pconfig($rr['uid'],'facebook','friend_check',time());
  320. }
  321. fb_consume_all($rr['uid']);
  322. }
  323. }
  324. set_config('facebook','last_poll', time());
  325. }
  326. function facebook_plugin_settings(&$a,&$b) {
  327. $b .= '<div class="settings-block">';
  328. $b .= '<h3>' . t('Facebook') . '</h3>';
  329. $b .= '<a href="facebook">' . t('Facebook Connector Settings') . '</a><br />';
  330. $b .= '</div>';
  331. }
  332. function facebook_jot_nets(&$a,&$b) {
  333. if(! local_user())
  334. return;
  335. $fb_post = get_pconfig(local_user(),'facebook','post');
  336. if(intval($fb_post) == 1) {
  337. $fb_defpost = get_pconfig(local_user(),'facebook','post_by_default');
  338. $selected = ((intval($fb_defpost) == 1) ? ' checked="checked" ' : '');
  339. $b .= '<div class="profile-jot-net"><input type="checkbox" name="facebook_enable"' . $selected . 'value="1" /> '
  340. . t('Post to Facebook') . '</div>';
  341. }
  342. }
  343. function facebook_post_hook(&$a,&$b) {
  344. /**
  345. * Post to Facebook stream
  346. */
  347. require_once('include/group.php');
  348. logger('Facebook post');
  349. $reply = false;
  350. $likes = false;
  351. if((local_user()) && (local_user() == $b['uid'])) {
  352. // Facebook is not considered a private network
  353. if($b['prvnets'] && $b['private'])
  354. return;
  355. if($b['parent']) {
  356. $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1",
  357. intval($b['parent']),
  358. intval(local_user())
  359. );
  360. if(count($r) && substr($r[0]['uri'],0,4) === 'fb::')
  361. $reply = substr($r[0]['uri'],4);
  362. elseif(count($r) && substr($r[0]['extid'],0,4) === 'fb::')
  363. $reply = substr($r[0]['extid'],4);
  364. else
  365. return;
  366. logger('facebook reply id=' . $reply);
  367. }
  368. if($b['private'] && $reply === false) {
  369. $allow_people = expand_acl($b['allow_cid']);
  370. $allow_groups = expand_groups(expand_acl($b['allow_gid']));
  371. $deny_people = expand_acl($b['deny_cid']);
  372. $deny_groups = expand_groups(expand_acl($b['deny_gid']));
  373. $recipients = array_unique(array_merge($allow_people,$allow_groups));
  374. $deny = array_unique(array_merge($deny_people,$deny_groups));
  375. $allow_str = dbesc(implode(', ',$recipients));
  376. if($allow_str) {
  377. $r = q("SELECT `notify` FROM `contact` WHERE `id` IN ( $allow_str ) AND `network` = 'face'");
  378. $allow_arr = array();
  379. if(count($r))
  380. foreach($r as $rr)
  381. $allow_arr[] = $rr['notify'];
  382. }
  383. $deny_str = dbesc(implode(', ',$deny));
  384. if($deny_str) {
  385. $r = q("SELECT `notify` FROM `contact` WHERE `id` IN ( $deny_str ) AND `network` = 'face'");
  386. $deny_arr = array();
  387. if(count($r))
  388. foreach($r as $rr)
  389. $deny_arr[] = $rr['notify'];
  390. }
  391. if(count($deny_arr) && (! count($allow_arr))) {
  392. // One or more FB folks were denied access but nobody on FB was specifically allowed access.
  393. // This might cause the post to be open to public on Facebook, but only to selected members
  394. // on another network. Since this could potentially leak a post to somebody who was denied,
  395. // we will skip posting it to Facebook with a slightly vague but relevant message that will
  396. // hopefully lead somebody to this code comment for a better explanation of what went wrong.
  397. notice( t('Post to Facebook cancelled because of multi-network access permission conflict.') . EOL);
  398. return;
  399. }
  400. // if it's a private message but no Facebook members are allowed or denied, skip Facebook post
  401. if((! count($allow_arr)) && (! count($deny_arr)))
  402. return;
  403. }
  404. if($b['verb'] == ACTIVITY_LIKE)
  405. $likes = true;
  406. $appid = get_config('facebook', 'appid' );
  407. $secret = get_config('facebook', 'appsecret' );
  408. if($appid && $secret) {
  409. logger('facebook: have appid+secret');
  410. $fb_post = intval(get_pconfig(local_user(),'facebook','post'));
  411. $fb_enable = (($fb_post && x($_POST,'facebook_enable')) ? intval($_POST['facebook_enable']) : 0);
  412. $fb_token = get_pconfig(local_user(),'facebook','access_token');
  413. // if API is used, default to the chosen settings
  414. if($_POST['api_source'] && intval(get_pconfig(local_user(),'facebook','post_by_default')))
  415. $fb_enable = 1;
  416. logger('facebook: $fb_post: ' . $fb_post . ' $fb_enable: ' . $fb_enable . ' $fb_token: ' . $fb_token,LOGGER_DEBUG);
  417. // post to facebook if it's a public post and we've ticked the 'post to Facebook' box,
  418. // or it's a private message with facebook participants
  419. // or it's a reply or likes action to an existing facebook post
  420. if($fb_post && $fb_token && ($fb_enable || $b['private'] || $reply)) {
  421. logger('facebook: able to post');
  422. require_once('library/facebook.php');
  423. require_once('include/bbcode.php');
  424. $msg = $b['body'];
  425. logger('Facebook post: original msg=' . $msg, LOGGER_DATA);
  426. // make links readable before we strip the code
  427. // unless it's a dislike - just send the text as a comment
  428. if($b['verb'] == ACTIVITY_DISLIKE)
  429. $msg = trim(strip_tags(bbcode($msg)));
  430. $search_str = $a->get_baseurl() . '/search';
  431. if(preg_match("/\[url=(.*?)\](.*?)\[\/url\]/is",$msg,$matches)) {
  432. // don't use hashtags for message link
  433. if(strpos($matches[2],$search_str) === false) {
  434. $link = $matches[1];
  435. if(substr($matches[2],0,5) != '[img]')
  436. $linkname = $matches[2];
  437. }
  438. }
  439. $msg = preg_replace("/\[url=(.*?)\](.*?)\[\/url\]/is",'$2 $1',$msg);
  440. if(preg_match("/\[img\](.*?)\[\/img\]/is",$msg,$matches))
  441. $image = $matches[1];
  442. $msg = preg_replace("/\[img\](.*?)\[\/img\]/is", t('Image: ') . '$1', $msg);
  443. if((strpos($link,z_root()) !== false) && (! $image))
  444. $image = $a->get_baseurl() . '/images/friendika-64.jpg';
  445. $msg = trim(strip_tags(bbcode($msg)));
  446. $msg = html_entity_decode($msg,ENT_QUOTES,'UTF-8');
  447. // add any attachments as text urls
  448. $arr = explode(',',$b['attach']);
  449. if(count($arr)) {
  450. $msg .= "\n";
  451. foreach($arr as $r) {
  452. $matches = false;
  453. $cnt = preg_match('|\[attach\]href=\"(.*?)\" size=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"\[\/attach\]|',$r,$matches);
  454. if($cnt) {
  455. $msg .= $matches[1];
  456. }
  457. }
  458. }
  459. if (strlen($msg) > FACEBOOK_MAXPOSTLEN) {
  460. $shortlink = "";
  461. require_once('library/slinky.php');
  462. $display_url = $a->get_baseurl() . '/display/' . $a->user['nickname'] . '/' . $b['id'];
  463. $slinky = new Slinky( $display_url );
  464. // setup a cascade of shortening services
  465. // try to get a short link from these services
  466. // in the order ur1.ca, trim, id.gd, tinyurl
  467. $slinky->set_cascade( array( new Slinky_UR1ca(), new Slinky_Trim(), new Slinky_IsGd(), new Slinky_TinyURL() ) );
  468. $shortlink = $slinky->short();
  469. // the new message will be shortened such that "... $shortlink"
  470. // will fit into the character limit
  471. $msg = substr($msg, 0, FACEBOOK_MAXPOSTLEN - strlen($shortlink) - 4);
  472. $msg .= '... ' . $shortlink;
  473. }
  474. if(! strlen($msg))
  475. return;
  476. logger('Facebook post: msg=' . $msg, LOGGER_DATA);
  477. if($likes) {
  478. $postvars = array('access_token' => $fb_token);
  479. }
  480. else {
  481. $postvars = array(
  482. 'access_token' => $fb_token,
  483. 'message' => $msg
  484. );
  485. if(isset($image))
  486. $postvars['picture'] = $image;
  487. if(isset($link))
  488. $postvars['link'] = $link;
  489. if(isset($linkname))
  490. $postvars['name'] = $linkname;
  491. }
  492. if(($b['private']) && (! $b['parent'])) {
  493. $postvars['privacy'] = '{"value": "CUSTOM", "friends": "SOME_FRIENDS"';
  494. if(count($allow_arr))
  495. $postvars['privacy'] .= ',"allow": "' . implode(',',$allow_arr) . '"';
  496. if(count($deny_arr))
  497. $postvars['privacy'] .= ',"deny": "' . implode(',',$deny_arr) . '"';
  498. $postvars['privacy'] .= '}';
  499. }
  500. if($reply) {
  501. $url = 'https://graph.facebook.com/' . $reply . '/' . (($likes) ? 'likes' : 'comments');
  502. }
  503. else {
  504. $url = 'https://graph.facebook.com/me/feed';
  505. if($b['plink'])
  506. $postvars['actions'] = '{"name": "' . t('View on Friendika') . '", "link": "' . $b['plink'] . '"}';
  507. }
  508. logger('facebook: post to ' . $url);
  509. logger('facebook: postvars: ' . print_r($postvars,true));
  510. // "test_mode" prevents anything from actually being posted.
  511. // Otherwise, let's do it.
  512. if(! get_config('facebook','test_mode')) {
  513. $x = post_url($url, $postvars);
  514. $retj = json_decode($x);
  515. if($retj->id) {
  516. q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d LIMIT 1",
  517. dbesc('fb::' . $retj->id),
  518. intval($b['id'])
  519. );
  520. }
  521. else {
  522. if(! $likes) {
  523. $s = serialize(array('url' => $url, 'item' => $b['id'], 'post' => $postvars));
  524. q("INSERT INTO `queue` ( `network`, `cid`, `created`, `last`, `content`)
  525. VALUES ( '%s', %d, '%s', '%s', '%s') ",
  526. dbesc(NETWORK_FACEBOOK),
  527. intval($a->contact),
  528. dbesc(datetime_convert()),
  529. dbesc(datetime_convert()),
  530. dbesc($s)
  531. );
  532. notice( t('Facebook post failed. Queued for retry.') . EOL);
  533. }
  534. }
  535. logger('Facebook post returns: ' . $x, LOGGER_DEBUG);
  536. }
  537. }
  538. }
  539. }
  540. }
  541. function fb_queue_hook(&$a,&$b) {
  542. $qi = q("SELECT * FROM `queue` WHERE `network` = '%s'",
  543. dbesc(NETWORK_FACEBOOK)
  544. );
  545. if(! count($qi))
  546. return;
  547. require_once('include/queue_fn.php');
  548. foreach($qi as $x) {
  549. if($x['network'] !== NETWORK_FACEBOOK)
  550. continue;
  551. logger('facebook_queue: run');
  552. $r = q("SELECT `user`.* FROM `user` LEFT JOIN `contact` on `contact`.`uid` = `user`.`uid`
  553. WHERE `contact`.`self` = 1 AND `contact`.`id` = %d LIMIT 1",
  554. intval($x['cid'])
  555. );
  556. if(! count($r))
  557. continue;
  558. $user = $r[0];
  559. $appid = get_config('facebook', 'appid' );
  560. $secret = get_config('facebook', 'appsecret' );
  561. if($appid && $secret) {
  562. $fb_post = intval(get_pconfig($user['uid'],'facebook','post'));
  563. $fb_token = get_pconfig($user['uid'],'facebook','access_token');
  564. if($fb_post && $fb_token) {
  565. logger('facebook_queue: able to post');
  566. require_once('library/facebook.php');
  567. $z = unserialize($x['content']);
  568. $item = $z['item'];
  569. $j = post_url($z['url'],$z['post']);
  570. $retj = json_decode($j);
  571. if($retj->id) {
  572. q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d LIMIT 1",
  573. dbesc('fb::' . $retj->id),
  574. intval($item)
  575. );
  576. logger('facebook_queue: success: ' . $j);
  577. remove_queue_item($x['id']);
  578. }
  579. else {
  580. logger('facebook_queue: failed: ' . $j);
  581. update_queue_time($x['id']);
  582. }
  583. }
  584. }
  585. }
  586. }
  587. function fb_consume_all($uid) {
  588. require_once('include/items.php');
  589. $access_token = get_pconfig($uid,'facebook','access_token');
  590. if(! $access_token)
  591. return;
  592. $s = fetch_url('https://graph.facebook.com/me/feed?access_token=' . $access_token);
  593. if($s) {
  594. $j = json_decode($s);
  595. logger('fb_consume_stream: wall: ' . print_r($j,true), LOGGER_DATA);
  596. fb_consume_stream($uid,$j,true);
  597. }
  598. $s = fetch_url('https://graph.facebook.com/me/home?access_token=' . $access_token);
  599. if($s) {
  600. $j = json_decode($s);
  601. logger('fb_consume_stream: feed: ' . print_r($j,true), LOGGER_DATA);
  602. fb_consume_stream($uid,$j,false);
  603. }
  604. }
  605. function fb_consume_stream($uid,$j,$wall = false) {
  606. $a = get_app();
  607. $no_linking = get_pconfig($uid,'facebook','no_linking');
  608. if($no_linking)
  609. return;
  610. $self = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
  611. intval($uid)
  612. );
  613. $user = q("SELECT `nickname`, `blockwall` FROM `user` WHERE `uid` = %d LIMIT 1",
  614. intval($uid)
  615. );
  616. if(count($user))
  617. $my_local_url = $a->get_baseurl() . '/profile/' . $user[0]['nickname'];
  618. $self_id = get_pconfig($uid,'facebook','self_id');
  619. if(! count($j->data) || (! strlen($self_id)))
  620. return;
  621. foreach($j->data as $entry) {
  622. logger('fb_consume: entry: ' . print_r($entry,true), LOGGER_DATA);
  623. $datarray = array();
  624. $r = q("SELECT * FROM `item` WHERE ( `uri` = '%s' OR `extid` = '%s') AND `uid` = %d LIMIT 1",
  625. dbesc('fb::' . $entry->id),
  626. dbesc('fb::' . $entry->id),
  627. intval($uid)
  628. );
  629. if(count($r)) {
  630. $post_exists = true;
  631. $orig_post = $r[0];
  632. $top_item = $r[0]['id'];
  633. }
  634. else {
  635. $post_exists = false;
  636. $orig_post = null;
  637. }
  638. if(! $orig_post) {
  639. $datarray['gravity'] = 0;
  640. $datarray['uid'] = $uid;
  641. $datarray['wall'] = (($wall) ? 1 : 0);
  642. $datarray['uri'] = $datarray['parent-uri'] = 'fb::' . $entry->id;
  643. $from = $entry->from;
  644. if($from->id == $self_id)
  645. $datarray['contact-id'] = $self[0]['id'];
  646. else {
  647. $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d AND `blocked` = 0 AND `readonly` = 0 LIMIT 1",
  648. dbesc($from->id),
  649. intval($uid)
  650. );
  651. if(count($r))
  652. $datarray['contact-id'] = $r[0]['id'];
  653. }
  654. // don't store post if we don't have a contact
  655. if(! x($datarray,'contact-id')) {
  656. logger('no contact: post ignored');
  657. continue;
  658. }
  659. $datarray['verb'] = ACTIVITY_POST;
  660. if($wall) {
  661. $datarray['owner-name'] = $self[0]['name'];
  662. $datarray['owner-link'] = $self[0]['url'];
  663. $datarray['owner-avatar'] = $self[0]['thumb'];
  664. }
  665. if(isset($entry->application) && isset($entry->application->name) && strlen($entry->application->name))
  666. $datarray['app'] = strip_tags($entry->application->name);
  667. else
  668. $datarray['app'] = 'facebook';
  669. $datarray['author-name'] = $from->name;
  670. $datarray['author-link'] = 'http://facebook.com/profile.php?id=' . $from->id;
  671. $datarray['author-avatar'] = 'https://graph.facebook.com/' . $from->id . '/picture';
  672. $datarray['plink'] = $datarray['author-link'] . '&v=wall&story_fbid=' . substr($entry->id,strpos($entry->id,'_') + 1);
  673. $datarray['body'] = $entry->message;
  674. if($entry->picture)
  675. $datarray['body'] .= "\n\n" . '[img]' . $entry->picture . '[/img]';
  676. if($entry->link)
  677. $datarray['body'] .= "\n" . linkify($entry->link);
  678. if($entry->name)
  679. $datarray['body'] .= "\n" . $entry->name;
  680. if($entry->caption)
  681. $datarray['body'] .= "\n" . $entry->caption;
  682. if($entry->description)
  683. $datarray['body'] .= "\n" . $entry->description;
  684. $datarray['created'] = datetime_convert('UTC','UTC',$entry->created_time);
  685. $datarray['edited'] = datetime_convert('UTC','UTC',$entry->updated_time);
  686. // If the entry has a privacy policy, we cannot assume who can or cannot see it,
  687. // as the identities are from a foreign system. Mark it as private to the owner.
  688. if($entry->privacy && $entry->privacy->value !== 'EVERYONE') {
  689. $datarray['private'] = 1;
  690. $datarray['allow_cid'] = '<' . $uid . '>';
  691. }
  692. $top_item = item_store($datarray);
  693. $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1",
  694. intval($top_item),
  695. intval($uid)
  696. );
  697. if(count($r)) {
  698. $orig_post = $r[0];
  699. logger('fb: new top level item posted');
  700. }
  701. }
  702. if(isset($entry->likes) && isset($entry->likes->data))
  703. $likers = $entry->likes->data;
  704. else
  705. $likers = null;
  706. if(isset($entry->comments) && isset($entry->comments->data))
  707. $comments = $entry->comments->data;
  708. else
  709. $comments = null;
  710. if(is_array($likers)) {
  711. foreach($likers as $likes) {
  712. if(! $orig_post)
  713. continue;
  714. // If we posted the like locally, it will be found with our url, not the FB url.
  715. $second_url = (($likes->id == $self_id) ? $self[0]['url'] : 'http://facebook.com/profile.php?id=' . $likes->id);
  716. $r = q("SELECT * FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `verb` = '%s'
  717. AND ( `author-link` = '%s' OR `author-link` = '%s' ) LIMIT 1",
  718. dbesc($orig_post['uri']),
  719. intval($uid),
  720. dbesc(ACTIVITY_LIKE),
  721. dbesc('http://facebook.com/profile.php?id=' . $likes->id),
  722. dbesc($second_url)
  723. );
  724. if(count($r))
  725. continue;
  726. $likedata = array();
  727. $likedata['parent'] = $top_item;
  728. $likedata['verb'] = ACTIVITY_LIKE;
  729. $likedata['gravity'] = 3;
  730. $likedata['uid'] = $uid;
  731. $likedata['wall'] = (($wall) ? 1 : 0);
  732. $likedata['uri'] = item_new_uri($a->get_baseurl(), $uid);
  733. $likedata['parent-uri'] = $orig_post['uri'];
  734. if($likes->id == $self_id)
  735. $likedata['contact-id'] = $self[0]['id'];
  736. else {
  737. $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d AND `blocked` = 0 AND `readonly` = 0 LIMIT 1",
  738. dbesc($likes->id),
  739. intval($uid)
  740. );
  741. if(count($r))
  742. $likedata['contact-id'] = $r[0]['id'];
  743. }
  744. if(! x($likedata,'contact-id'))
  745. $likedata['contact-id'] = $orig_post['contact-id'];
  746. $likedata['app'] = 'facebook';
  747. $likedata['verb'] = ACTIVITY_LIKE;
  748. $likedata['author-name'] = $likes->name;
  749. $likedata['author-link'] = 'http://facebook.com/profile.php?id=' . $likes->id;
  750. $likedata['author-avatar'] = 'https://graph.facebook.com/' . $likes->id . '/picture';
  751. $author = '[url=' . $likedata['author-link'] . ']' . $likedata['author-name'] . '[/url]';
  752. $objauthor = '[url=' . $orig_post['author-link'] . ']' . $orig_post['author-name'] . '[/url]';
  753. $post_type = t('status');
  754. $plink = '[url=' . $orig_post['plink'] . ']' . $post_type . '[/url]';
  755. $likedata['object-type'] = ACTIVITY_OBJ_NOTE;
  756. $likedata['body'] = sprintf( t('%1$s likes %2$s\'s %3$s'), $author, $objauthor, $plink);
  757. $likedata['object'] = '<object><type>' . ACTIVITY_OBJ_NOTE . '</type><local>1</local>' .
  758. '<id>' . $orig_post['uri'] . '</id><link>' . xmlify('<link rel="alternate" type="text/html" href="' . xmlify($orig_post['plink']) . '" />') . '</link><title>' . $orig_post['title'] . '</title><content>' . $orig_post['body'] . '</content></object>';
  759. $item = item_store($likedata);
  760. }
  761. }
  762. if(is_array($comments)) {
  763. foreach($comments as $cmnt) {
  764. if(! $orig_post)
  765. continue;
  766. $r = q("SELECT * FROM `item` WHERE `uid` = %d AND ( `uri` = '%s' OR `extid` = '%s' ) LIMIT 1",
  767. intval($uid),
  768. dbesc('fb::' . $cmnt->id),
  769. dbesc('fb::' . $cmnt->id)
  770. );
  771. if(count($r))
  772. continue;
  773. $cmntdata = array();
  774. $cmntdata['parent'] = $top_item;
  775. $cmntdata['verb'] = ACTIVITY_POST;
  776. $cmntdata['gravity'] = 6;
  777. $cmntdata['uid'] = $uid;
  778. $cmntdata['wall'] = (($wall) ? 1 : 0);
  779. $cmntdata['uri'] = 'fb::' . $cmnt->id;
  780. $cmntdata['parent-uri'] = $orig_post['uri'];
  781. if($cmnt->from->id == $self_id) {
  782. $cmntdata['contact-id'] = $self[0]['id'];
  783. }
  784. else {
  785. $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d LIMIT 1",
  786. dbesc($cmnt->from->id),
  787. intval($uid)
  788. );
  789. if(count($r)) {
  790. $cmntdata['contact-id'] = $r[0]['id'];
  791. if($r[0]['blocked'] || $r[0]['readonly'])
  792. continue;
  793. }
  794. }
  795. if(! x($cmntdata,'contact-id'))
  796. $cmntdata['contact-id'] = $orig_post['contact-id'];
  797. $cmntdata['app'] = 'facebook';
  798. $cmntdata['created'] = datetime_convert('UTC','UTC',$cmnt->created_time);
  799. $cmntdata['edited'] = datetime_convert('UTC','UTC',$cmnt->created_time);
  800. $cmntdata['verb'] = ACTIVITY_POST;
  801. $cmntdata['author-name'] = $cmnt->from->name;
  802. $cmntdata['author-link'] = 'http://facebook.com/profile.php?id=' . $cmnt->from->id;
  803. $cmntdata['author-avatar'] = 'https://graph.facebook.com/' . $cmnt->from->id . '/picture';
  804. $cmntdata['body'] = $cmnt->message;
  805. $item = item_store($cmntdata);
  806. }
  807. }
  808. }
  809. }