PageRenderTime 52ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/facebook/facebook.php

https://github.com/chiefdome/friendica-addons
PHP | 2140 lines | 1511 code | 364 blank | 265 comment | 359 complexity | f3269af831971b0e333a03f064bc0858 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-3.0, GPL-2.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * Name: Facebook Connector
  4. * Version: 1.3
  5. * Author: Mike Macgirvin <http://macgirvin.com/profile/mike>
  6. * Author: Tobias Hößl <https://github.com/CatoTH/>
  7. *
  8. */
  9. /**
  10. * Installing the Friendica/Facebook connector
  11. *
  12. * Detailed instructions how to use this plugin can be found at
  13. * https://github.com/friendica/friendica/wiki/How-to:-Friendica%E2%80%99s-Facebook-connector
  14. *
  15. * Vidoes and embeds will not be posted if there is no other content. Links
  16. * and images will be converted to a format suitable for the Facebook API and
  17. * long posts truncated - with a link to view the full post.
  18. *
  19. * Facebook contacts will not be able to view private photos, as they are not able to
  20. * authenticate to your site to establish identity. We will address this
  21. * in a future release.
  22. */
  23. /** TODO
  24. * - Implement a method for the administrator to delete all configuration data the plugin has created,
  25. * e.g. the app_access_token
  26. */
  27. // Size of maximum post length increased
  28. // see http://www.facebook.com/schrep/posts/203969696349811
  29. // define('FACEBOOK_MAXPOSTLEN', 420);
  30. define('FACEBOOK_MAXPOSTLEN', 63206);
  31. define('FACEBOOK_SESSION_ERR_NOTIFICATION_INTERVAL', 259200); // 3 days
  32. define('FACEBOOK_DEFAULT_POLL_INTERVAL', 60); // given in minutes
  33. define('FACEBOOK_MIN_POLL_INTERVAL', 5);
  34. define('FACEBOOK_RTU_ERR_MAIL_AFTER_MINUTES', 180); // 3 hours
  35. require_once('include/security.php');
  36. function facebook_install() {
  37. register_hook('post_local', 'addon/facebook/facebook.php', 'facebook_post_local');
  38. register_hook('notifier_normal', 'addon/facebook/facebook.php', 'facebook_post_hook');
  39. register_hook('jot_networks', 'addon/facebook/facebook.php', 'facebook_jot_nets');
  40. register_hook('connector_settings', 'addon/facebook/facebook.php', 'facebook_plugin_settings');
  41. register_hook('cron', 'addon/facebook/facebook.php', 'facebook_cron');
  42. register_hook('enotify', 'addon/facebook/facebook.php', 'facebook_enotify');
  43. register_hook('queue_predeliver', 'addon/facebook/facebook.php', 'fb_queue_hook');
  44. }
  45. function facebook_uninstall() {
  46. unregister_hook('post_local', 'addon/facebook/facebook.php', 'facebook_post_local');
  47. unregister_hook('notifier_normal', 'addon/facebook/facebook.php', 'facebook_post_hook');
  48. unregister_hook('jot_networks', 'addon/facebook/facebook.php', 'facebook_jot_nets');
  49. unregister_hook('connector_settings', 'addon/facebook/facebook.php', 'facebook_plugin_settings');
  50. unregister_hook('cron', 'addon/facebook/facebook.php', 'facebook_cron');
  51. unregister_hook('enotify', 'addon/facebook/facebook.php', 'facebook_enotify');
  52. unregister_hook('queue_predeliver', 'addon/facebook/facebook.php', 'fb_queue_hook');
  53. // hook moved
  54. unregister_hook('post_local_end', 'addon/facebook/facebook.php', 'facebook_post_hook');
  55. unregister_hook('plugin_settings', 'addon/facebook/facebook.php', 'facebook_plugin_settings');
  56. }
  57. /* declare the facebook_module function so that /facebook url requests will land here */
  58. function facebook_module() {}
  59. // If a->argv[1] is a nickname, this is a callback from Facebook oauth requests.
  60. // If $_REQUEST["realtime_cb"] is set, this is a callback from the Real-Time Updates API
  61. /**
  62. * @param App $a
  63. */
  64. function facebook_init(&$a) {
  65. if (x($_REQUEST, "realtime_cb") && x($_REQUEST, "realtime_cb")) {
  66. logger("facebook_init: Facebook Real-Time callback called", LOGGER_DEBUG);
  67. if (x($_REQUEST, "hub_verify_token")) {
  68. // this is the verification callback while registering for real time updates
  69. $verify_token = get_config('facebook', 'cb_verify_token');
  70. if ($verify_token != $_REQUEST["hub_verify_token"]) {
  71. logger('facebook_init: Wrong Facebook Callback Verifier - expected ' . $verify_token . ', got ' . $_REQUEST["hub_verify_token"]);
  72. return;
  73. }
  74. if (x($_REQUEST, "hub_challenge")) {
  75. logger('facebook_init: Answering Challenge: ' . $_REQUEST["hub_challenge"], LOGGER_DATA);
  76. echo $_REQUEST["hub_challenge"];
  77. die();
  78. }
  79. }
  80. require_once('include/items.php');
  81. // this is a status update
  82. $content = file_get_contents("php://input");
  83. if (is_numeric($content)) $content = file_get_contents("php://input");
  84. $js = json_decode($content);
  85. logger(print_r($js, true), LOGGER_DATA);
  86. if (!isset($js->object) || $js->object != "user" || !isset($js->entry)) {
  87. logger('facebook_init: Could not parse Real-Time Update data', LOGGER_DEBUG);
  88. return;
  89. }
  90. $affected_users = array("feed" => array(), "friends" => array());
  91. foreach ($js->entry as $entry) {
  92. $fbuser = $entry->uid;
  93. foreach ($entry->changed_fields as $field) {
  94. if (!isset($affected_users[$field])) {
  95. logger('facebook_init: Unknown field "' . $field . '"');
  96. continue;
  97. }
  98. if (in_array($fbuser, $affected_users[$field])) continue;
  99. $r = q("SELECT `uid` FROM `pconfig` WHERE `cat` = 'facebook' AND `k` = 'self_id' AND `v` = '%s' LIMIT 1", dbesc($fbuser));
  100. if(! count($r))
  101. continue;
  102. $uid = $r[0]['uid'];
  103. $access_token = get_pconfig($uid,'facebook','access_token');
  104. if(! $access_token)
  105. return;
  106. switch ($field) {
  107. case "feed":
  108. logger('facebook_init: FB-User ' . $fbuser . ' / feed', LOGGER_DEBUG);
  109. if(! get_pconfig($uid,'facebook','no_wall')) {
  110. $private_wall = intval(get_pconfig($uid,'facebook','private_wall'));
  111. $s = fetch_url('https://graph.facebook.com/me/feed?access_token=' . $access_token);
  112. if($s) {
  113. $j = json_decode($s);
  114. if (isset($j->data)) {
  115. logger('facebook_init: wall: ' . print_r($j,true), LOGGER_DATA);
  116. fb_consume_stream($uid,$j,($private_wall) ? false : true);
  117. } else {
  118. logger('facebook_init: wall: got no data from Facebook: ' . print_r($j,true), LOGGER_NORMAL);
  119. }
  120. }
  121. }
  122. break;
  123. case "friends":
  124. logger('facebook_init: FB-User ' . $fbuser . ' / friends', LOGGER_DEBUG);
  125. fb_get_friends($uid, false);
  126. set_pconfig($uid,'facebook','friend_check',time());
  127. break;
  128. default:
  129. logger('facebook_init: Unknown callback field for ' . $fbuser, LOGGER_NORMAL);
  130. }
  131. $affected_users[$field][] = $fbuser;
  132. }
  133. }
  134. }
  135. if($a->argc != 2)
  136. return;
  137. $nick = $a->argv[1];
  138. if(strlen($nick))
  139. $r = q("SELECT `uid` FROM `user` WHERE `nickname` = '%s' LIMIT 1",
  140. dbesc($nick)
  141. );
  142. if(!(isset($r) && count($r)))
  143. return;
  144. $uid = $r[0]['uid'];
  145. $auth_code = (x($_GET, 'code') ? $_GET['code'] : '');
  146. $error = (x($_GET, 'error_description') ? $_GET['error_description'] : '');
  147. if($error)
  148. logger('facebook_init: Error: ' . $error);
  149. if($auth_code && $uid) {
  150. $appid = get_config('facebook','appid');
  151. $appsecret = get_config('facebook', 'appsecret');
  152. $x = fetch_url('https://graph.facebook.com/oauth/access_token?client_id='
  153. . $appid . '&client_secret=' . $appsecret . '&redirect_uri='
  154. . urlencode($a->get_baseurl() . '/facebook/' . $nick)
  155. . '&code=' . $auth_code);
  156. logger('facebook_init: returned access token: ' . $x, LOGGER_DATA);
  157. if(strpos($x,'access_token=') !== false) {
  158. $token = str_replace('access_token=', '', $x);
  159. if(strpos($token,'&') !== false)
  160. $token = substr($token,0,strpos($token,'&'));
  161. set_pconfig($uid,'facebook','access_token',$token);
  162. set_pconfig($uid,'facebook','post','1');
  163. if(get_pconfig($uid,'facebook','no_linking') === false)
  164. set_pconfig($uid,'facebook','no_linking',1);
  165. fb_get_self($uid);
  166. fb_get_friends($uid, true);
  167. fb_consume_all($uid);
  168. }
  169. }
  170. }
  171. /**
  172. * @param int $uid
  173. */
  174. function fb_get_self($uid) {
  175. $access_token = get_pconfig($uid,'facebook','access_token');
  176. if(! $access_token)
  177. return;
  178. $s = fetch_url('https://graph.facebook.com/me/?access_token=' . $access_token);
  179. if($s) {
  180. $j = json_decode($s);
  181. set_pconfig($uid,'facebook','self_id',(string) $j->id);
  182. }
  183. }
  184. /**
  185. * @param int $uid
  186. * @param string $access_token
  187. * @param array $persons
  188. */
  189. function fb_get_friends_sync_new($uid, $access_token, $persons) {
  190. $persons_todo = array();
  191. foreach ($persons as $person) {
  192. $link = 'http://facebook.com/profile.php?id=' . $person->id;
  193. $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' LIMIT 1",
  194. intval($uid),
  195. dbesc($link)
  196. );
  197. if (count($r) == 0) {
  198. logger('fb_get_friends: new contact found: ' . $link, LOGGER_DEBUG);
  199. $persons_todo[] = $person;
  200. }
  201. if (count($persons_todo) > 0) fb_get_friends_sync_full($uid, $access_token, $persons_todo);
  202. }
  203. }
  204. /**
  205. * @param int $uid
  206. * @param object $contact
  207. */
  208. function fb_get_friends_sync_parsecontact($uid, $contact) {
  209. $contact->link = 'http://facebook.com/profile.php?id=' . $contact->id;
  210. // If its a page then set the first name from the username
  211. if (!$contact->first_name and $contact->username)
  212. $contact->first_name = $contact->username;
  213. // check if we already have a contact
  214. $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' LIMIT 1",
  215. intval($uid),
  216. dbesc($contact->link)
  217. );
  218. if(count($r)) {
  219. // update profile photos once every two weeks as we have no notification of when they change.
  220. $update_photo = (($r[0]['avatar-date'] < datetime_convert('','','now -14 days')) ? true : false);
  221. // check that we have all the photos, this has been known to fail on occasion
  222. if((! $r[0]['photo']) || (! $r[0]['thumb']) || (! $r[0]['micro']) || ($update_photo)) {
  223. require_once("Photo.php");
  224. $photos = import_profile_photo('https://graph.facebook.com/' . $contact->id . '/picture', $uid, $r[0]['id']);
  225. q("UPDATE `contact` SET `photo` = '%s',
  226. `thumb` = '%s',
  227. `micro` = '%s',
  228. `name-date` = '%s',
  229. `uri-date` = '%s',
  230. `avatar-date` = '%s'
  231. WHERE `id` = %d LIMIT 1
  232. ",
  233. dbesc($photos[0]),
  234. dbesc($photos[1]),
  235. dbesc($photos[2]),
  236. dbesc(datetime_convert()),
  237. dbesc(datetime_convert()),
  238. dbesc(datetime_convert()),
  239. intval($r[0]['id'])
  240. );
  241. }
  242. return;
  243. }
  244. else {
  245. // create contact record
  246. q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `alias`, `notify`, `poll`,
  247. `name`, `nick`, `photo`, `network`, `rel`, `priority`,
  248. `writable`, `blocked`, `readonly`, `pending` )
  249. VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, 0, 0, 0 ) ",
  250. intval($uid),
  251. dbesc(datetime_convert()),
  252. dbesc($contact->link),
  253. dbesc(normalise_link($contact->link)),
  254. dbesc(''),
  255. dbesc(''),
  256. dbesc($contact->id),
  257. dbesc('facebook ' . $contact->id),
  258. dbesc($contact->name),
  259. dbesc(($contact->nickname) ? $contact->nickname : mb_convert_case($contact->first_name, MB_CASE_LOWER, "UTF-8")),
  260. dbesc('https://graph.facebook.com/' . $contact->id . '/picture'),
  261. dbesc(NETWORK_FACEBOOK),
  262. intval(CONTACT_IS_FRIEND),
  263. intval(1),
  264. intval(1)
  265. );
  266. }
  267. $r = q("SELECT * FROM `contact` WHERE `url` = '%s' AND `uid` = %d LIMIT 1",
  268. dbesc($contact->link),
  269. intval($uid)
  270. );
  271. if(! count($r)) {
  272. return;
  273. }
  274. $contact_id = $r[0]['id'];
  275. $g = q("select def_gid from user where uid = %d limit 1",
  276. intval($uid)
  277. );
  278. if($g && intval($g[0]['def_gid'])) {
  279. require_once('include/group.php');
  280. group_add_member($uid,'',$contact_id,$g[0]['def_gid']);
  281. }
  282. require_once("Photo.php");
  283. $photos = import_profile_photo($r[0]['photo'],$uid,$contact_id);
  284. q("UPDATE `contact` SET `photo` = '%s',
  285. `thumb` = '%s',
  286. `micro` = '%s',
  287. `name-date` = '%s',
  288. `uri-date` = '%s',
  289. `avatar-date` = '%s'
  290. WHERE `id` = %d LIMIT 1
  291. ",
  292. dbesc($photos[0]),
  293. dbesc($photos[1]),
  294. dbesc($photos[2]),
  295. dbesc(datetime_convert()),
  296. dbesc(datetime_convert()),
  297. dbesc(datetime_convert()),
  298. intval($contact_id)
  299. );
  300. }
  301. /**
  302. * @param int $uid
  303. * @param string $access_token
  304. * @param array $persons
  305. */
  306. function fb_get_friends_sync_full($uid, $access_token, $persons) {
  307. if (count($persons) == 0) return;
  308. $nums = Ceil(count($persons) / 50);
  309. for ($i = 0; $i < $nums; $i++) {
  310. $batch_request = array();
  311. for ($j = $i * 50; $j < ($i+1) * 50 && $j < count($persons); $j++) $batch_request[] = array('method'=>'GET', 'relative_url'=>$persons[$j]->id);
  312. $s = post_url('https://graph.facebook.com/', array('access_token' => $access_token, 'batch' => json_encode($batch_request)));
  313. if($s) {
  314. $results = json_decode($s);
  315. logger('fb_get_friends: info: ' . print_r($results,true), LOGGER_DATA);
  316. if(count($results)) {
  317. foreach ($results as $contact) {
  318. if ($contact->code != 200) logger('fb_get_friends: not found: ' . print_r($contact,true), LOGGER_DEBUG);
  319. else fb_get_friends_sync_parsecontact($uid, json_decode($contact->body));
  320. }
  321. }
  322. }
  323. }
  324. }
  325. // if $fullsync is true, only new contacts are searched for
  326. /**
  327. * @param int $uid
  328. * @param bool $fullsync
  329. */
  330. function fb_get_friends($uid, $fullsync = true) {
  331. $r = q("SELECT `uid` FROM `user` WHERE `uid` = %d AND `account_expired` = 0 LIMIT 1",
  332. intval($uid)
  333. );
  334. if(! count($r))
  335. return;
  336. $access_token = get_pconfig($uid,'facebook','access_token');
  337. $no_linking = get_pconfig($uid,'facebook','no_linking');
  338. if($no_linking)
  339. return;
  340. if(! $access_token)
  341. return;
  342. $s = fetch_url('https://graph.facebook.com/me/friends?access_token=' . $access_token);
  343. if($s) {
  344. logger('facebook: fb_gwet_friends: ' . $s, LOGGER_DATA);
  345. $j = json_decode($s);
  346. logger('facebook: fb_get_friends: json: ' . print_r($j,true), LOGGER_DATA);
  347. if(! $j->data)
  348. return;
  349. $persons_todo = array();
  350. foreach($j->data as $person) $persons_todo[] = $person;
  351. if ($fullsync)
  352. fb_get_friends_sync_full($uid, $access_token, $persons_todo);
  353. else
  354. fb_get_friends_sync_new($uid, $access_token, $persons_todo);
  355. }
  356. }
  357. // This is the POST method to the facebook settings page
  358. // Content is posted to Facebook in the function facebook_post_hook()
  359. /**
  360. * @param App $a
  361. */
  362. function facebook_post(&$a) {
  363. $uid = local_user();
  364. if($uid){
  365. $fb_limited = get_config('facebook','crestrict');
  366. $value = ((x($_POST,'post_by_default')) ? intval($_POST['post_by_default']) : 0);
  367. set_pconfig($uid,'facebook','post_by_default', $value);
  368. $no_linking = get_pconfig($uid,'facebook','no_linking');
  369. $no_wall = ((x($_POST,'facebook_no_wall')) ? intval($_POST['facebook_no_wall']) : 0);
  370. set_pconfig($uid,'facebook','no_wall',$no_wall);
  371. $private_wall = ((x($_POST,'facebook_private_wall')) ? intval($_POST['facebook_private_wall']) : 0);
  372. set_pconfig($uid,'facebook','private_wall',$private_wall);
  373. set_pconfig($uid,'facebook','blocked_apps',escape_tags(trim($_POST['blocked_apps'])));
  374. $linkvalue = ((x($_POST,'facebook_linking')) ? intval($_POST['facebook_linking']) : 0);
  375. if($fb_limited) {
  376. if($linkvalue == 0)
  377. set_pconfig($uid,'facebook','no_linking', 1);
  378. }
  379. else
  380. set_pconfig($uid,'facebook','no_linking', (($linkvalue) ? 0 : 1));
  381. // FB linkage was allowed but has just been turned off - remove all FB contacts and posts
  382. if((! intval($no_linking)) && (! intval($linkvalue))) {
  383. $r = q("SELECT `id` FROM `contact` WHERE `uid` = %d AND `network` = '%s' ",
  384. intval($uid),
  385. dbesc(NETWORK_FACEBOOK)
  386. );
  387. if(count($r)) {
  388. require_once('include/Contact.php');
  389. foreach($r as $rr)
  390. contact_remove($rr['id']);
  391. }
  392. }
  393. elseif(intval($no_linking) && intval($linkvalue)) {
  394. // FB linkage is now allowed - import stuff.
  395. fb_get_self($uid);
  396. fb_get_friends($uid, true);
  397. fb_consume_all($uid);
  398. }
  399. info( t('Settings updated.') . EOL);
  400. }
  401. return;
  402. }
  403. // Facebook settings form
  404. /**
  405. * @param App $a
  406. * @return string
  407. */
  408. function facebook_content(&$a) {
  409. if(! local_user()) {
  410. notice( t('Permission denied.') . EOL);
  411. return '';
  412. }
  413. if(! service_class_allows(local_user(),'facebook_connect')) {
  414. notice( t('Permission denied.') . EOL);
  415. return upgrade_bool_message();
  416. }
  417. if($a->argc > 1 && $a->argv[1] === 'remove') {
  418. del_pconfig(local_user(),'facebook','post');
  419. info( t('Facebook disabled') . EOL);
  420. }
  421. if($a->argc > 1 && $a->argv[1] === 'friends') {
  422. fb_get_friends(local_user(), true);
  423. info( t('Updating contacts') . EOL);
  424. }
  425. $fb_limited = get_config('facebook','restrict');
  426. $o = '';
  427. $fb_installed = false;
  428. if (get_pconfig(local_user(),'facebook','post')) {
  429. $access_token = get_pconfig(local_user(),'facebook','access_token');
  430. if ($access_token) {
  431. $s = fetch_url('https://graph.facebook.com/me/feed?access_token=' . $access_token);
  432. if($s) {
  433. $j = json_decode($s);
  434. if (isset($j->data)) $fb_installed = true;
  435. }
  436. }
  437. }
  438. $appid = get_config('facebook','appid');
  439. if(! $appid) {
  440. notice( t('Facebook API key is missing.') . EOL);
  441. return '';
  442. }
  443. $a->page['htmlhead'] .= '<link rel="stylesheet" type="text/css" href="'
  444. . $a->get_baseurl() . '/addon/facebook/facebook.css' . '" media="all" />' . "\r\n";
  445. $o .= '<h3>' . t('Facebook Connect') . '</h3>';
  446. if(! $fb_installed) {
  447. $o .= '<div id="facebook-enable-wrapper">';
  448. $o .= '<a href="https://www.facebook.com/dialog/oauth?client_id=' . $appid . '&redirect_uri='
  449. . $a->get_baseurl() . '/facebook/' . $a->user['nickname'] . '&scope=publish_stream,read_stream,offline_access">' . t('Install Facebook connector for this account.') . '</a>';
  450. $o .= '</div>';
  451. }
  452. if($fb_installed) {
  453. $o .= '<div id="facebook-disable-wrapper">';
  454. $o .= '<a href="' . $a->get_baseurl() . '/facebook/remove' . '">' . t('Remove Facebook connector') . '</a></div>';
  455. $o .= '<div id="facebook-enable-wrapper">';
  456. $o .= '<a href="https://www.facebook.com/dialog/oauth?client_id=' . $appid . '&redirect_uri='
  457. . $a->get_baseurl() . '/facebook/' . $a->user['nickname'] . '&scope=publish_stream,read_stream,offline_access">' . t('Re-authenticate [This is necessary whenever your Facebook password is changed.]') . '</a>';
  458. $o .= '</div>';
  459. $o .= '<div id="facebook-post-default-form">';
  460. $o .= '<form action="facebook" method="post" >';
  461. $post_by_default = get_pconfig(local_user(),'facebook','post_by_default');
  462. $checked = (($post_by_default) ? ' checked="checked" ' : '');
  463. $o .= '<input type="checkbox" name="post_by_default" value="1"' . $checked . '/>' . ' ' . t('Post to Facebook by default') . EOL;
  464. $no_linking = get_pconfig(local_user(),'facebook','no_linking');
  465. $checked = (($no_linking) ? '' : ' checked="checked" ');
  466. if($fb_limited) {
  467. if($no_linking) {
  468. $o .= EOL . '<strong>' . t('Facebook friend linking has been disabled on this site. The following settings will have no effect.') . '</strong>' . EOL;
  469. $checked .= " disabled ";
  470. }
  471. else {
  472. $o .= EOL . '<strong>' . t('Facebook friend linking has been disabled on this site. If you disable it, you will be unable to re-enable it.') . '</strong>' . EOL;
  473. }
  474. }
  475. $o .= '<input type="checkbox" name="facebook_linking" value="1"' . $checked . '/>' . ' ' . t('Link all your Facebook friends and conversations on this website') . EOL ;
  476. $o .= '<p>' . t('Facebook conversations consist of your <em>profile wall</em> and your friend <em>stream</em>.');
  477. $o .= ' ' . t('On this website, your Facebook friend stream is only visible to you.');
  478. $o .= ' ' . t('The following settings determine the privacy of your Facebook profile wall on this website.') . '</p>';
  479. $private_wall = get_pconfig(local_user(),'facebook','private_wall');
  480. $checked = (($private_wall) ? ' checked="checked" ' : '');
  481. $o .= '<input type="checkbox" name="facebook_private_wall" value="1"' . $checked . '/>' . ' ' . t('On this website your Facebook profile wall conversations will only be visible to you') . EOL ;
  482. $no_wall = get_pconfig(local_user(),'facebook','no_wall');
  483. $checked = (($no_wall) ? ' checked="checked" ' : '');
  484. $o .= '<input type="checkbox" name="facebook_no_wall" value="1"' . $checked . '/>' . ' ' . t('Do not import your Facebook profile wall conversations') . EOL ;
  485. $o .= '<p>' . t('If you choose to link conversations and leave both of these boxes unchecked, your Facebook profile wall will be merged with your profile wall on this website and your privacy settings on this website will be used to determine who may see the conversations.') . '</p>';
  486. $blocked_apps = get_pconfig(local_user(),'facebook','blocked_apps');
  487. $o .= '<div><label id="blocked-apps-label" for="blocked-apps">' . t('Comma separated applications to ignore') . ' </label></div>';
  488. $o .= '<div><textarea id="blocked-apps" name="blocked_apps" >' . htmlspecialchars($blocked_apps) . '</textarea></div>';
  489. $o .= '<input type="submit" name="submit" value="' . t('Submit') . '" /></form></div>';
  490. }
  491. return $o;
  492. }
  493. /**
  494. * @param App $a
  495. * @param null|object $b
  496. * @return mixed
  497. */
  498. function facebook_cron($a,$b) {
  499. $last = get_config('facebook','last_poll');
  500. $poll_interval = intval(get_config('facebook','poll_interval'));
  501. if(! $poll_interval)
  502. $poll_interval = FACEBOOK_DEFAULT_POLL_INTERVAL;
  503. if($last) {
  504. $next = $last + ($poll_interval * 60);
  505. if($next > time())
  506. return;
  507. }
  508. logger('facebook_cron');
  509. // Find the FB users on this site and randomize in case one of them
  510. // uses an obscene amount of memory. It may kill this queue run
  511. // but hopefully we'll get a few others through on each run.
  512. $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'facebook' AND `k` = 'post' AND `v` = '1' ORDER BY RAND() ");
  513. if(count($r)) {
  514. foreach($r as $rr) {
  515. if(get_pconfig($rr['uid'],'facebook','no_linking'))
  516. continue;
  517. $ab = intval(get_config('system','account_abandon_days'));
  518. if($ab > 0) {
  519. $z = q("SELECT `uid` FROM `user` WHERE `uid` = %d AND `login_date` > UTC_TIMESTAMP() - INTERVAL %d DAY LIMIT 1",
  520. intval($rr['uid']),
  521. intval($ab)
  522. );
  523. if(! count($z))
  524. continue;
  525. }
  526. // check for new friends once a day
  527. $last_friend_check = get_pconfig($rr['uid'],'facebook','friend_check');
  528. if($last_friend_check)
  529. $next_friend_check = $last_friend_check + 86400;
  530. else
  531. $next_friend_check = 0;
  532. if($next_friend_check <= time()) {
  533. fb_get_friends($rr['uid'], true);
  534. set_pconfig($rr['uid'],'facebook','friend_check',time());
  535. }
  536. fb_consume_all($rr['uid']);
  537. }
  538. }
  539. if (get_config('facebook', 'realtime_active') == 1) {
  540. if (!facebook_check_realtime_active()) {
  541. logger('facebook_cron: Facebook is not sending Real-Time Updates any more, although it is supposed to. Trying to fix it...', LOGGER_NORMAL);
  542. facebook_subscription_add_users();
  543. if (facebook_check_realtime_active())
  544. logger('facebook_cron: Successful', LOGGER_NORMAL);
  545. else {
  546. logger('facebook_cron: Failed', LOGGER_NORMAL);
  547. $first_err = get_config('facebook', 'realtime_first_err');
  548. if (!$first_err) {
  549. $first_err = time();
  550. set_config('facebook', 'realtime_first_err', $first_err);
  551. }
  552. $first_err_ago = (time() - $first_err);
  553. if(strlen($a->config['admin_email']) && !get_config('facebook', 'realtime_err_mailsent') && $first_err_ago > (FACEBOOK_RTU_ERR_MAIL_AFTER_MINUTES * 60)) {
  554. mail($a->config['admin_email'], t('Problems with Facebook Real-Time Updates'),
  555. "Hi!\n\nThere's a problem with the Facebook Real-Time Updates that cannot be solved automatically. Maybe a permission issue?\n\nPlease try to re-activate it on " . $a->config["system"]["url"] . "/admin/plugins/facebook\n\nThis e-mail will only be sent once.",
  556. 'From: ' . t('Administrator') . '@' . $_SERVER['SERVER_NAME'] . "\n"
  557. . 'Content-type: text/plain; charset=UTF-8' . "\n"
  558. . 'Content-transfer-encoding: 8bit'
  559. );
  560. set_config('facebook', 'realtime_err_mailsent', 1);
  561. }
  562. }
  563. } else { // !facebook_check_realtime_active()
  564. del_config('facebook', 'realtime_err_mailsent');
  565. del_config('facebook', 'realtime_first_err');
  566. }
  567. }
  568. set_config('facebook','last_poll', time());
  569. }
  570. /**
  571. * @param App $a
  572. * @param null|object $b
  573. */
  574. function facebook_plugin_settings(&$a,&$b) {
  575. $b .= '<div class="settings-block">';
  576. $b .= '<h3>' . t('Facebook') . '</h3>';
  577. $b .= '<a href="facebook">' . t('Facebook Connector Settings') . '</a><br />';
  578. $b .= '</div>';
  579. }
  580. /**
  581. * @param App $a
  582. * @param null|object $o
  583. */
  584. function facebook_plugin_admin(&$a, &$o){
  585. $o = '<input type="hidden" name="form_security_token" value="' . get_form_security_token("fbsave") . '">';
  586. $o .= '<h4>' . t('Facebook API Key') . '</h4>';
  587. $appid = get_config('facebook', 'appid' );
  588. $appsecret = get_config('facebook', 'appsecret' );
  589. $poll_interval = get_config('facebook', 'poll_interval' );
  590. $sync_comments = get_config('facebook', 'sync_comments' );
  591. if (!$poll_interval) $poll_interval = FACEBOOK_DEFAULT_POLL_INTERVAL;
  592. $ret1 = q("SELECT `v` FROM `config` WHERE `cat` = 'facebook' AND `k` = 'appid' LIMIT 1");
  593. $ret2 = q("SELECT `v` FROM `config` WHERE `cat` = 'facebook' AND `k` = 'appsecret' LIMIT 1");
  594. if ((count($ret1) > 0 && $ret1[0]['v'] != $appid) || (count($ret2) > 0 && $ret2[0]['v'] != $appsecret)) $o .= t('Error: it appears that you have specified the App-ID and -Secret in your .htconfig.php file. As long as they are specified there, they cannot be set using this form.<br><br>');
  595. $working_connection = false;
  596. if ($appid && $appsecret) {
  597. $subs = facebook_subscriptions_get();
  598. if ($subs === null) $o .= t('Error: the given API Key seems to be incorrect (the application access token could not be retrieved).') . '<br>';
  599. elseif (is_array($subs)) {
  600. $o .= t('The given API Key seems to work correctly.') . '<br>';
  601. $working_connection = true;
  602. } else $o .= t('The correctness of the API Key could not be detected. Something strange\'s going on.') . '<br>';
  603. }
  604. $o .= '<label for="fb_appid">' . t('App-ID / API-Key') . '</label><input id="fb_appid" name="appid" type="text" value="' . escape_tags($appid ? $appid : "") . '"><br style="clear: both;">';
  605. $o .= '<label for="fb_appsecret">' . t('Application secret') . '</label><input id="fb_appsecret" name="appsecret" type="text" value="' . escape_tags($appsecret ? $appsecret : "") . '"><br style="clear: both;">';
  606. $o .= '<label for="fb_poll_interval">' . sprintf(t('Polling Interval in minutes (minimum %1$s minutes)'), FACEBOOK_MIN_POLL_INTERVAL) . '</label><input name="poll_interval" id="fb_poll_interval" type="number" min="' . FACEBOOK_MIN_POLL_INTERVAL . '" value="' . $poll_interval . '"><br style="clear: both;">';
  607. $o .= '<label for="fb_sync_comments">' . t('Synchronize comments (no comments on Facebook are missed, at the cost of increased system load)') . '</label><input name="sync_comments" id="fb_sync_comments" type="checkbox" ' . ($sync_comments ? 'checked' : '') . '><br style="clear: both;">';
  608. $o .= '<input type="submit" name="fb_save_keys" value="' . t('Save') . '">';
  609. if ($working_connection) {
  610. $o .= '<h4>' . t('Real-Time Updates') . '</h4>';
  611. $activated = facebook_check_realtime_active();
  612. if ($activated) {
  613. $o .= t('Real-Time Updates are activated.') . '<br><br>';
  614. $o .= '<input type="submit" name="real_time_deactivate" value="' . t('Deactivate Real-Time Updates') . '">';
  615. } else {
  616. $o .= t('Real-Time Updates not activated.') . '<br><input type="submit" name="real_time_activate" value="' . t('Activate Real-Time Updates') . '">';
  617. }
  618. }
  619. }
  620. /**
  621. * @param App $a
  622. */
  623. function facebook_plugin_admin_post(&$a){
  624. check_form_security_token_redirectOnErr('/admin/plugins/facebook', 'fbsave');
  625. if (x($_REQUEST,'fb_save_keys')) {
  626. set_config('facebook', 'appid', $_REQUEST['appid']);
  627. set_config('facebook', 'appsecret', $_REQUEST['appsecret']);
  628. $poll_interval = IntVal($_REQUEST['poll_interval']);
  629. if ($poll_interval >= FACEBOOK_MIN_POLL_INTERVAL) set_config('facebook', 'poll_interval', $poll_interval);
  630. set_config('facebook', 'sync_comments', (x($_REQUEST, 'sync_comments') ? 1 : 0));
  631. del_config('facebook', 'app_access_token');
  632. info(t('The new values have been saved.'));
  633. }
  634. if (x($_REQUEST,'real_time_activate')) {
  635. facebook_subscription_add_users();
  636. }
  637. if (x($_REQUEST,'real_time_deactivate')) {
  638. facebook_subscription_del_users();
  639. }
  640. }
  641. /**
  642. * @param App $a
  643. * @param object $b
  644. * @return mixed
  645. */
  646. function facebook_jot_nets(&$a,&$b) {
  647. if(! local_user())
  648. return;
  649. $fb_post = get_pconfig(local_user(),'facebook','post');
  650. if(intval($fb_post) == 1) {
  651. $fb_defpost = get_pconfig(local_user(),'facebook','post_by_default');
  652. $selected = ((intval($fb_defpost) == 1) ? ' checked="checked" ' : '');
  653. $b .= '<div class="profile-jot-net"><input type="checkbox" name="facebook_enable"' . $selected . ' value="1" /> '
  654. . t('Post to Facebook') . '</div>';
  655. }
  656. }
  657. /**
  658. * @param App $a
  659. * @param object $b
  660. * @return mixed
  661. */
  662. function facebook_post_hook(&$a,&$b) {
  663. if($b['deleted'] || ($b['created'] !== $b['edited']))
  664. return;
  665. /**
  666. * Post to Facebook stream
  667. */
  668. require_once('include/group.php');
  669. require_once('include/html2plain.php');
  670. logger('Facebook post');
  671. $reply = false;
  672. $likes = false;
  673. $deny_arr = array();
  674. $allow_arr = array();
  675. $toplevel = (($b['id'] == $b['parent']) ? true : false);
  676. $linking = ((get_pconfig($b['uid'],'facebook','no_linking')) ? 0 : 1);
  677. if((! $toplevel) && ($linking)) {
  678. $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1",
  679. intval($b['parent']),
  680. intval($b['uid'])
  681. );
  682. if(count($r) && substr($r[0]['uri'],0,4) === 'fb::')
  683. $reply = substr($r[0]['uri'],4);
  684. elseif(count($r) && substr($r[0]['extid'],0,4) === 'fb::')
  685. $reply = substr($r[0]['extid'],4);
  686. else
  687. return;
  688. $u = q("SELECT * FROM user where uid = %d limit 1",
  689. intval($b['uid'])
  690. );
  691. if(! count($u))
  692. return;
  693. // only accept comments from the item owner. Other contacts are unknown to FB.
  694. if(! link_compare($b['author-link'], $a->get_baseurl() . '/profile/' . $u[0]['nickname']))
  695. return;
  696. logger('facebook reply id=' . $reply);
  697. }
  698. if(strstr($b['postopts'],'facebook') || ($b['private']) || ($reply)) {
  699. if($b['private'] && $reply === false) {
  700. $allow_people = expand_acl($b['allow_cid']);
  701. $allow_groups = expand_groups(expand_acl($b['allow_gid']));
  702. $deny_people = expand_acl($b['deny_cid']);
  703. $deny_groups = expand_groups(expand_acl($b['deny_gid']));
  704. $recipients = array_unique(array_merge($allow_people,$allow_groups));
  705. $deny = array_unique(array_merge($deny_people,$deny_groups));
  706. $allow_str = dbesc(implode(', ',$recipients));
  707. if($allow_str) {
  708. $r = q("SELECT `notify` FROM `contact` WHERE `id` IN ( $allow_str ) AND `network` = 'face'");
  709. if(count($r))
  710. foreach($r as $rr)
  711. $allow_arr[] = $rr['notify'];
  712. }
  713. $deny_str = dbesc(implode(', ',$deny));
  714. if($deny_str) {
  715. $r = q("SELECT `notify` FROM `contact` WHERE `id` IN ( $deny_str ) AND `network` = 'face'");
  716. if(count($r))
  717. foreach($r as $rr)
  718. $deny_arr[] = $rr['notify'];
  719. }
  720. if(count($deny_arr) && (! count($allow_arr))) {
  721. // One or more FB folks were denied access but nobody on FB was specifically allowed access.
  722. // This might cause the post to be open to public on Facebook, but only to selected members
  723. // on another network. Since this could potentially leak a post to somebody who was denied,
  724. // we will skip posting it to Facebook with a slightly vague but relevant message that will
  725. // hopefully lead somebody to this code comment for a better explanation of what went wrong.
  726. notice( t('Post to Facebook cancelled because of multi-network access permission conflict.') . EOL);
  727. return;
  728. }
  729. // if it's a private message but no Facebook members are allowed or denied, skip Facebook post
  730. if((! count($allow_arr)) && (! count($deny_arr)))
  731. return;
  732. }
  733. if($b['verb'] == ACTIVITY_LIKE)
  734. $likes = true;
  735. $appid = get_config('facebook', 'appid' );
  736. $secret = get_config('facebook', 'appsecret' );
  737. if($appid && $secret) {
  738. logger('facebook: have appid+secret');
  739. $fb_token = get_pconfig($b['uid'],'facebook','access_token');
  740. // post to facebook if it's a public post and we've ticked the 'post to Facebook' box,
  741. // or it's a private message with facebook participants
  742. // or it's a reply or likes action to an existing facebook post
  743. if($fb_token && ($toplevel || $b['private'] || $reply)) {
  744. logger('facebook: able to post');
  745. require_once('library/facebook.php');
  746. require_once('include/bbcode.php');
  747. $msg = $b['body'];
  748. logger('Facebook post: original msg=' . $msg, LOGGER_DATA);
  749. // make links readable before we strip the code
  750. // unless it's a dislike - just send the text as a comment
  751. // if($b['verb'] == ACTIVITY_DISLIKE)
  752. // $msg = trim(strip_tags(bbcode($msg)));
  753. // Old code
  754. /*$search_str = $a->get_baseurl() . '/search';
  755. if(preg_match("/\[url=(.*?)\](.*?)\[\/url\]/is",$msg,$matches)) {
  756. // don't use hashtags for message link
  757. if(strpos($matches[2],$search_str) === false) {
  758. $link = $matches[1];
  759. if(substr($matches[2],0,5) != '[img]')
  760. $linkname = $matches[2];
  761. }
  762. }
  763. // strip tag links to avoid link clutter, this really should be
  764. // configurable because we're losing information
  765. $msg = preg_replace("/\#\[url=(.*?)\](.*?)\[\/url\]/is",'#$2',$msg);
  766. // provide the link separately for normal links
  767. $msg = preg_replace("/\[url=(.*?)\](.*?)\[\/url\]/is",'$2 $1',$msg);
  768. if(preg_match("/\[img\](.*?)\[\/img\]/is",$msg,$matches))
  769. $image = $matches[1];
  770. $msg = preg_replace("/\[img\](.*?)\[\/img\]/is", t('Image: ') . '$1', $msg);
  771. if((strpos($link,z_root()) !== false) && (! $image))
  772. $image = $a->get_baseurl() . '/images/friendica-64.jpg';
  773. $msg = trim(strip_tags(bbcode($msg)));*/
  774. // New code
  775. // Looking for the first image
  776. $image = '';
  777. if(preg_match("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/is",$b['body'],$matches))
  778. $image = $matches[3];
  779. if ($image == '')
  780. if(preg_match("/\[img\](.*?)\[\/img\]/is",$b['body'],$matches))
  781. $image = $matches[1];
  782. // When saved into the database the content is sent through htmlspecialchars
  783. // That means that we have to decode all image-urls
  784. $image = htmlspecialchars_decode($image);
  785. // Checking for a bookmark element
  786. $body = $b['body'];
  787. if (strpos($body, "[bookmark") !== false) {
  788. // splitting the text in two parts:
  789. // before and after the bookmark
  790. $pos = strpos($body, "[bookmark");
  791. $body1 = substr($body, 0, $pos);
  792. $body2 = substr($body, $pos);
  793. // Removing the bookmark and all quotes after the bookmark
  794. // they are mostly only the content after the bookmark.
  795. $body2 = preg_replace("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/ism",'',$body2);
  796. $body2 = preg_replace("/\[quote\=([^\]]*)\](.*?)\[\/quote\]/ism",'',$body2);
  797. $body2 = preg_replace("/\[quote\](.*?)\[\/quote\]/ism",'',$body2);
  798. $body = $body1.$body2;
  799. }
  800. // At first convert the text to html
  801. $html = bbcode($body, false, false);
  802. // Then convert it to plain text
  803. $msg = trim($b['title']." \n\n".html2plain($html, 0, true));
  804. $msg = html_entity_decode($msg,ENT_QUOTES,'UTF-8');
  805. // Removing multiple newlines
  806. while (strpos($msg, "\n\n\n") !== false)
  807. $msg = str_replace("\n\n\n", "\n\n", $msg);
  808. // add any attachments as text urls
  809. $arr = explode(',',$b['attach']);
  810. if(count($arr)) {
  811. $msg .= "\n";
  812. foreach($arr as $r) {
  813. $matches = false;
  814. $cnt = preg_match('|\[attach\]href=\"(.*?)\" size=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"\[\/attach\]|',$r,$matches);
  815. if($cnt) {
  816. $msg .= "\n".$matches[1];
  817. }
  818. }
  819. }
  820. $link = '';
  821. $linkname = '';
  822. // look for bookmark-bbcode and handle it with priority
  823. if(preg_match("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/is",$b['body'],$matches)) {
  824. $link = $matches[1];
  825. $linkname = $matches[2];
  826. }
  827. // If there is no bookmark element then take the first link
  828. if ($link == '') {
  829. $links = collecturls($html);
  830. if (sizeof($links) > 0) {
  831. reset($links);
  832. $link = current($links);
  833. }
  834. }
  835. // Remove trailing and leading spaces
  836. $msg = trim($msg);
  837. // Since facebook increased the maxpostlen massively this never should happen again :)
  838. if (strlen($msg) > FACEBOOK_MAXPOSTLEN) {
  839. require_once('library/slinky.php');
  840. $display_url = $b['plink'];
  841. $slinky = new Slinky( $display_url );
  842. // setup a cascade of shortening services
  843. // try to get a short link from these services
  844. // in the order ur1.ca, trim, id.gd, tinyurl
  845. $slinky->set_cascade( array( new Slinky_UR1ca(), new Slinky_Trim(), new Slinky_IsGd(), new Slinky_TinyURL() ) );
  846. $shortlink = $slinky->short();
  847. // the new message will be shortened such that "... $shortlink"
  848. // will fit into the character limit
  849. $msg = substr($msg, 0, FACEBOOK_MAXPOSTLEN - strlen($shortlink) - 4);
  850. $msg .= '... ' . $shortlink;
  851. }
  852. // Fallback - if message is empty
  853. if(!strlen($msg))
  854. $msg = $linkname;
  855. if(!strlen($msg))
  856. $msg = $link;
  857. if(!strlen($msg))
  858. $msg = $image;
  859. // If there is nothing to post then exit
  860. if(!strlen($msg))
  861. return;
  862. logger('Facebook post: msg=' . $msg, LOGGER_DATA);
  863. if($likes) {
  864. $postvars = array('access_token' => $fb_token);
  865. }
  866. else {
  867. // message, picture, link, name, caption, description, source, place, tags
  868. $postvars = array(
  869. 'access_token' => $fb_token,
  870. 'message' => $msg
  871. );
  872. if(trim($image) != "") {
  873. $postvars['picture'] = $image;
  874. }
  875. if(trim($link) != "") {
  876. $postvars['link'] = $link;
  877. // The following doesn't work - why?
  878. if ((stristr($link,'youtube')) || (stristr($link,'youtu.be')) || (stristr($link,'vimeo'))) {
  879. $postvars['source'] = $link;
  880. }
  881. }
  882. if(trim($linkname) != "")
  883. $postvars['name'] = $linkname;
  884. }
  885. if(($b['private']) && ($toplevel)) {
  886. $postvars['privacy'] = '{"value": "CUSTOM", "friends": "SOME_FRIENDS"';
  887. if(count($allow_arr))
  888. $postvars['privacy'] .= ',"allow": "' . implode(',',$allow_arr) . '"';
  889. if(count($deny_arr))
  890. $postvars['privacy'] .= ',"deny": "' . implode(',',$deny_arr) . '"';
  891. $postvars['privacy'] .= '}';
  892. }
  893. if($reply) {
  894. $url = 'https://graph.facebook.com/' . $reply . '/' . (($likes) ? 'likes' : 'comments');
  895. } else if (($link != "") or ($image != "") or ($b['title'] == '') or (strlen($msg) < 500)) {
  896. $url = 'https://graph.facebook.com/me/feed';
  897. if($b['plink'])
  898. $postvars['actions'] = '{"name": "' . t('View on Friendica') . '", "link": "' . $b['plink'] . '"}';
  899. } else {
  900. // if its only a message and a subject and the message is larger than 500 characters then post it as note
  901. $postvars = array(
  902. 'access_token' => $fb_token,
  903. 'message' => bbcode($b['body'], false, false),
  904. 'subject' => $b['title'],
  905. );
  906. $url = 'https://graph.facebook.com/me/notes';
  907. }
  908. logger('facebook: post to ' . $url);
  909. logger('facebook: postvars: ' . print_r($postvars,true));
  910. // "test_mode" prevents anything from actually being posted.
  911. // Otherwise, let's do it.
  912. if(! get_config('facebook','test_mode')) {
  913. $x = post_url($url, $postvars);
  914. logger('Facebook post returns: ' . $x, LOGGER_DEBUG);
  915. $retj = json_decode($x);
  916. if($retj->id) {
  917. q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d LIMIT 1",
  918. dbesc('fb::' . $retj->id),
  919. intval($b['id'])
  920. );
  921. }
  922. else {
  923. if(! $likes) {
  924. $s = serialize(array('url' => $url, 'item' => $b['id'], 'post' => $postvars));
  925. require_once('include/queue_fn.php');
  926. add_to_queue($a->contact,NETWORK_FACEBOOK,$s);
  927. notice( t('Facebook post failed. Queued for retry.') . EOL);
  928. }
  929. if (isset($retj->error) && $retj->error->type == "OAuthException" && $retj->error->code == 190) {
  930. logger('Facebook session has expired due to changed password.', LOGGER_DEBUG);
  931. $last_notification = get_pconfig($b['uid'], 'facebook', 'session_expired_mailsent');
  932. if (!$last_notification || $last_notification < (time() - FACEBOOK_SESSION_ERR_NOTIFICATION_INTERVAL)) {
  933. require_once('include/enotify.php');
  934. $r = q("SELECT * FROM `user` WHERE `uid` = %d LIMIT 1", intval($b['uid']) );
  935. notification(array(
  936. 'uid' => $b['uid'],
  937. 'type' => NOTIFY_SYSTEM,
  938. 'system_type' => 'facebook_connection_invalid',
  939. 'language' => $r[0]['language'],
  940. 'to_name' => $r[0]['username'],
  941. 'to_email' => $r[0]['email'],
  942. 'source_name' => t('Administrator'),
  943. 'source_link' => $a->config["system"]["url"],
  944. 'source_photo' => $a->config["system"]["url"] . '/images/person-80.jpg',
  945. ));
  946. set_pconfig($b['uid'], 'facebook', 'session_expired_mailsent', time());
  947. } else logger('Facebook: No notification, as the last one was sent on ' . $last_notification, LOGGER_DEBUG);
  948. }
  949. }
  950. }
  951. }
  952. }
  953. }
  954. }
  955. /**
  956. * @param App $app
  957. * @param object $data
  958. */
  959. function facebook_enotify(&$app, &$data) {
  960. if (x($data, 'params') && $data['params']['type'] == NOTIFY_SYSTEM && x($data['params'], 'system_type') && $data['params']['system_type'] == 'facebook_connection_invalid') {
  961. $data['itemlink'] = '/facebook';
  962. $data['epreamble'] = $data['preamble'] = t('Your Facebook connection became invalid. Please Re-authenticate.');
  963. $data['subject'] = t('Facebook connection became invalid');
  964. $data['body'] = sprintf( t("Hi %1\$s,\n\nThe connection between your accounts on %2\$s and Facebook became invalid. This usually happens after you change your Facebook-password. To enable the connection again, you have to %3\$sre-authenticate the Facebook-connector%4\$s."), $data['params']['to_name'], "[url=" . $app->config["system"]["url"] . "]" . $app->config["sitename"] . "[/url]", "[url=" . $app->config["system"]["url"] . "/facebook]", "[/url]");
  965. }
  966. }
  967. /**
  968. * @param App $a
  969. * @param object $b
  970. */
  971. function facebook_post_local(&$a,&$b) {
  972. // Figure out if Facebook posting is enabled for this post and file it in 'postopts'
  973. // where we will discover it during background delivery.
  974. // This can only be triggered by a local user posting to their own wall.
  975. if((local_user()) && (local_user() == $b['uid'])) {
  976. $fb_post = intval(get_pconfig(local_user(),'facebook','post'));
  977. $fb_enable = (($fb_post && x($_REQUEST,'facebook_enable')) ? intval($_REQUEST['facebook_enable']) : 0);
  978. // if API is used, default to the chosen settings
  979. // but allow a specific override
  980. if($_REQUEST['api_source'] && intval(get_pconfig(local_user(),'facebook','post_by_default'))) {
  981. if(! x($_REQUEST,'facebook_enable'))
  982. $fb_enable = 1;
  983. }
  984. if(! $fb_enable)
  985. return;
  986. if(strlen($b['postopts']))
  987. $b['postopts'] .= ',';
  988. $b['postopts'] .= 'facebook';
  989. }
  990. }
  991. /**
  992. * @param App $a
  993. * @param object $b
  994. */
  995. function fb_queue_hook(&$a,&$b) {
  996. $qi = q("SELECT * FROM `queue` WHERE `network` = '%s'",
  997. dbesc(NETWORK_FACEBOOK)
  998. );
  999. if(! count($qi))
  1000. return;
  1001. require_once('include/queue_fn.php');
  1002. foreach($qi as $x) {
  1003. if($x['network'] !== NETWORK_FACEBOOK)
  1004. continue;
  1005. logger('facebook_queue: run');
  1006. $r = q("SELECT `user`.* FROM `user` LEFT JOIN `contact` on `contact`.`uid` = `user`.`uid`
  1007. WHERE `contact`.`self` = 1 AND `contact`.`id` = %d LIMIT 1",
  1008. intval($x['cid'])
  1009. );
  1010. if(! count($r))
  1011. continue;
  1012. $user = $r[0];
  1013. $appid = get_config('facebook', 'appid' );
  1014. $secret = get_config('facebook', 'appsecret' );
  1015. if($appid && $secret) {
  1016. $fb_post = intval(get_pconfig($user['uid'],'facebook','post'));
  1017. $fb_token = get_pconfig($user['uid'],'facebook','access_token');
  1018. if($fb_post && $fb_token) {
  1019. logger('facebook_queue: able to post');
  1020. require_once('library/facebook.php');
  1021. $z = unserialize($x['content']);
  1022. $item = $z['item'];
  1023. $j = post_url($z['url'],$z['post']);
  1024. $retj = json_decode($j);
  1025. if($retj->id) {
  1026. q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d LIMIT 1",
  1027. dbesc('fb::' . $retj->id),
  1028. intval($item)
  1029. );
  1030. logger('facebook_queue: success: ' . $j);
  1031. remove_queue_item($x['id']);
  1032. }
  1033. else {
  1034. logger('facebook_queue: failed: ' . $j);
  1035. update_queue_time($x['id']);
  1036. }
  1037. }
  1038. }
  1039. }
  1040. }
  1041. /**
  1042. * @param string $access_token
  1043. * @param int $since
  1044. * @return object
  1045. */
  1046. function fb_get_timeline($access_token, &$since) {
  1047. $entries = new stdClass();
  1048. $entries->data = array();
  1049. $newest = 0;
  1050. $url = 'https://graph.facebook.com/me/home?access_token='.$access_token;
  1051. if ($since != 0)
  1052. $url .= "&since=".$since;
  1053. do {
  1054. $s = fetch_url($url);
  1055. $j = json_decode($s);
  1056. $oldestdate = time();
  1057. if (isset($j->data))
  1058. foreach ($j->data as $entry) {
  1059. $created = strtotime($entry->created_time);
  1060. if ($newest < $created)
  1061. $newest = $created;
  1062. if ($created >= $since)
  1063. $entries->data[] = $entry;
  1064. if ($created <= $oldestdate)
  1065. $oldestdate = $created;
  1066. }
  1067. else
  1068. break;
  1069. $url = (isset($j->paging) && isset($j->paging->next) ? $j->paging->next : '');
  1070. } while (($oldestdate > $since) and ($since != 0) and ($url != ''));
  1071. if ($newest > $since)
  1072. $since = $newest;
  1073. return($entries);
  1074. }
  1075. /**
  1076. * @param int $uid
  1077. */
  1078. function fb_consume_all($uid) {
  1079. require_once('include/items.php');
  1080. $access_token = get_pconfig($uid,'facebook','access_token');
  1081. if(! $access_token)
  1082. return;
  1083. if(! get_pconfig($uid,'facebook','no_wall')) {
  1084. $private_wall = intval(get_pconfig($uid,'facebook','private_wall'));
  1085. $s = fetch_url('https://graph.facebook.com/me/feed?access_token=' . $access_token);
  1086. if($s) {
  1087. $j = json_decode($s);
  1088. if (isset($j->data)) {
  1089. logger('fb_consume_stream: wall: ' . print_r($j,true), LOGGER_DATA);
  1090. fb_consume_stream($uid,$j,($private_wall) ? false : true);
  1091. } else {
  1092. logger('fb_consume_stream: wall: got no data from Facebook: ' . print_r($j,true), LOGGER_NORMAL);
  1093. }
  1094. }
  1095. }
  1096. // Get the last date
  1097. $lastdate = get_pconfig($uid,'facebook','lastdate');
  1098. // fetch all items since the last date
  1099. $j = fb_get_timeline($access_token, $lastdate);
  1100. if (isset($j->data)) {
  1101. logger('fb_consume_stream: feed: ' . print_r($j,true), LOGGER_DATA);
  1102. fb_consume_stream($uid,$j,false);
  1103. // Write back the last date
  1104. set_pconfig($uid,'facebook','lastdate', $lastdate);
  1105. } else
  1106. logger('fb_consume_stream: feed: got no data from Facebook: ' . print_r($j,true), LOGGER_NORMAL);
  1107. }
  1108. /**
  1109. * @param int $uid
  1110. * @param string $link
  1111. * @return string
  1112. */
  1113. function fb_get_photo($uid,$link) {
  1114. $access_token = get_pconfig($uid,'facebook','access_token');
  1115. if(! $access_token || (! stristr($link,'facebook.com/photo.php')))
  1116. return "";
  1117. //return "\n" . '[url=' . $link . ']' . t('link') . '[/url]';
  1118. $ret = preg_match('/fbid=([0-9]*)/',$link,$match);
  1119. if($ret)
  1120. $photo_id = $match[1];
  1121. else
  1122. return "";
  1123. $x = fetch_url('https://graph.facebook.com/' . $photo_id . '?access_token=' . $access_token);
  1124. $j = json_decode($x);
  1125. if($j->picture)
  1126. return "\n\n" . '[url=' . $link . '][img]' . $j->picture . '[/img][/url]';
  1127. //else
  1128. // return "\n" . '[url=' . $link . ']' . t('link') . '[/url]';
  1129. return "";
  1130. }
  1131. /**
  1132. * @param App $a
  1133. * @param array $user
  1134. * @param array $self
  1135. * @param string $fb_id
  1136. * @param bool $wall
  1137. * @param array $orig_post
  1138. * @param object $cmnt
  1139. */
  1140. function fb_consume_comment(&$a, &$user, &$self, $fb_id, $wall, &$orig_post, &$cmnt) {
  1141. if(! $orig_post)
  1142. return;
  1143. $top_item = $orig_post['id'];
  1144. $uid = IntVal($user[0]['uid']);
  1145. $r = q("SELECT * FROM `item` WHERE `uid` = %d AND ( `uri` = '%s' OR `extid` = '%s' ) LIMIT 1",
  1146. intval($uid),
  1147. dbesc('fb::' . $cmnt->id),
  1148. dbesc('fb::' . $cmnt->id)
  1149. );
  1150. if(count($r))
  1151. return;
  1152. $cmntdata = array();
  1153. $cmntdata['parent'] = $top_item;
  1154. $cmntdata['verb'] = ACTIVITY_POST;
  1155. $cmntdata['gravity'] = 6;
  1156. $cmntdata['uid'] = $uid;
  1157. $cmntdata['wall'] = (($wall) ? 1 : 0);
  1158. $cmntdata['uri'] = 'fb::' . $cmnt->id;
  1159. $cmntdata['parent-uri'] = $orig_post['uri'];
  1160. if($cmnt->from->id == $fb_id) {
  1161. $cmntdata['contact-id'] = $self[0]['id'];
  1162. }
  1163. else {
  1164. $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d LIMIT 1",
  1165. dbesc($cmnt->from->id),
  1166. intval($uid)
  1167. );
  1168. if(count($r)) {
  1169. $cmntdata['contact-id'] = $r[0]['id'];
  1170. if($r[0]['blocked'] || $r[0]['readonly'])
  1171. return;
  1172. }
  1173. }
  1174. if(! x($cmntdata,'contact-id'))
  1175. $cmntdata['contact-id'] = $orig_post['contact-id'];
  1176. $cmntdata['app'] = 'facebook';
  1177. $cmntdata['created'] = datetime_convert('UTC','UTC',$cmnt->created_time);
  1178. $cmntdata['edited'] = datetime_convert('UTC','UTC',$cmnt->created_time);
  1179. $cmntdata['verb'] = ACTIVITY_POST;
  1180. $cmntdata['author-name'] = $cmnt->from->name;
  1181. $cmntdata['author-link'] = 'http://facebook.com/profile.php?id=' . $cmnt->from->id;
  1182. $cmntdata['author-avatar'] = 'https://graph.facebook.com/' . $cmnt->from->id . '/picture';
  1183. $cmntdata['body'] = $cmnt->message;
  1184. $i

Large files files are truncated, but you can click here to view the full file