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.
 
 
 
 
 
 

919 lines
26 KiB

  1. <?php
  2. /**
  3. * @file src/Core/NotificationsManager.php
  4. * @brief Methods for read and write notifications from/to database
  5. * or for formatting notifications
  6. */
  7. namespace Friendica\Core;
  8. use Friendica\Core\Pconfig;
  9. use Friendica\Core\System;
  10. use Friendica\Database\DBM;
  11. require_once 'include/html2plain.php';
  12. require_once 'include/datetime.php';
  13. require_once 'include/bbcode.php';
  14. require_once 'include/Contact.php';
  15. /**
  16. * @brief Methods for read and write notifications from/to database
  17. * or for formatting notifications
  18. */
  19. class NotificationsManager
  20. {
  21. private $a;
  22. /**
  23. * Constructor
  24. */
  25. public function __construct()
  26. {
  27. $this->a = get_app();
  28. }
  29. /**
  30. * @brief set some extra note properties
  31. *
  32. * @param array $notes array of note arrays from db
  33. * @return array Copy of input array with added properties
  34. *
  35. * Set some extra properties to note array from db:
  36. * - timestamp as int in default TZ
  37. * - date_rel : relative date string
  38. * - msg_html: message as html string
  39. * - msg_plain: message as plain text string
  40. */
  41. private function _set_extra($notes)
  42. {
  43. $rets = array();
  44. foreach ($notes as $n) {
  45. $local_time = datetime_convert('UTC', date_default_timezone_get(), $n['date']);
  46. $n['timestamp'] = strtotime($local_time);
  47. $n['date_rel'] = relative_date($n['date']);
  48. $n['msg_html'] = bbcode($n['msg'], false, false, false, false);
  49. $n['msg_plain'] = explode("\n", trim(html2plain($n['msg_html'], 0)))[0];
  50. $rets[] = $n;
  51. }
  52. return $rets;
  53. }
  54. /**
  55. * @brief Get all notifications for local_user()
  56. *
  57. * @param array $filter optional Array "column name"=>value: filter query by columns values
  58. * @param string $order optional Space separated list of column to sort by.
  59. * Prepend name with "+" to sort ASC, "-" to sort DESC. Default to "-date"
  60. * @param string $limit optional Query limits
  61. *
  62. * @return array of results or false on errors
  63. */
  64. public function getAll($filter = array(), $order = "-date", $limit = "")
  65. {
  66. $filter_str = array();
  67. $filter_sql = "";
  68. foreach ($filter as $column => $value) {
  69. $filter_str[] = sprintf("`%s` = '%s'", $column, dbesc($value));
  70. }
  71. if (count($filter_str)>0) {
  72. $filter_sql = "AND ".implode(" AND ", $filter_str);
  73. }
  74. $aOrder = explode(" ", $order);
  75. $asOrder = array();
  76. foreach ($aOrder as $o) {
  77. $dir = "asc";
  78. if ($o[0]==="-") {
  79. $dir = "desc";
  80. $o = substr($o, 1);
  81. }
  82. if ($o[0]==="+") {
  83. $dir = "asc";
  84. $o = substr($o, 1);
  85. }
  86. $asOrder[] = "$o $dir";
  87. }
  88. $order_sql = implode(", ", $asOrder);
  89. if ($limit != "") {
  90. $limit = " LIMIT ".$limit;
  91. }
  92. $r = q(
  93. "SELECT * FROM `notify` WHERE `uid` = %d $filter_sql ORDER BY $order_sql $limit",
  94. intval(local_user())
  95. );
  96. if (DBM::is_result($r)) {
  97. return $this->_set_extra($r);
  98. }
  99. return false;
  100. }
  101. /**
  102. * @brief Get one note for local_user() by $id value
  103. *
  104. * @param int $id identity
  105. * @return array note values or null if not found
  106. */
  107. public function getByID($id)
  108. {
  109. $r = q(
  110. "SELECT * FROM `notify` WHERE `id` = %d AND `uid` = %d LIMIT 1",
  111. intval($id),
  112. intval(local_user())
  113. );
  114. if (DBM::is_result($r)) {
  115. return $this->_set_extra($r)[0];
  116. }
  117. return null;
  118. }
  119. /**
  120. * @brief set seen state of $note of local_user()
  121. *
  122. * @param array $note note array
  123. * @param bool $seen optional true or false, default true
  124. * @return bool true on success, false on errors
  125. */
  126. public function setSeen($note, $seen = true)
  127. {
  128. return q(
  129. "UPDATE `notify` SET `seen` = %d WHERE ( `link` = '%s' OR ( `parent` != 0 AND `parent` = %d AND `otype` = '%s' )) AND `uid` = %d",
  130. intval($seen),
  131. dbesc($note['link']),
  132. intval($note['parent']),
  133. dbesc($note['otype']),
  134. intval(local_user())
  135. );
  136. }
  137. /**
  138. * @brief set seen state of all notifications of local_user()
  139. *
  140. * @param bool $seen optional true or false. default true
  141. * @return bool true on success, false on error
  142. */
  143. public function setAllSeen($seen = true)
  144. {
  145. return q(
  146. "UPDATE `notify` SET `seen` = %d WHERE `uid` = %d",
  147. intval($seen),
  148. intval(local_user())
  149. );
  150. }
  151. /**
  152. * @brief List of pages for the Notifications TabBar
  153. *
  154. * @return array with with notifications TabBar data
  155. */
  156. public function getTabs()
  157. {
  158. $tabs = array(
  159. array(
  160. 'label' => t('System'),
  161. 'url'=>'notifications/system',
  162. 'sel'=> (($this->a->argv[1] == 'system') ? 'active' : ''),
  163. 'id' => 'system-tab',
  164. 'accesskey' => 'y',
  165. ),
  166. array(
  167. 'label' => t('Network'),
  168. 'url'=>'notifications/network',
  169. 'sel'=> (($this->a->argv[1] == 'network') ? 'active' : ''),
  170. 'id' => 'network-tab',
  171. 'accesskey' => 'w',
  172. ),
  173. array(
  174. 'label' => t('Personal'),
  175. 'url'=>'notifications/personal',
  176. 'sel'=> (($this->a->argv[1] == 'personal') ? 'active' : ''),
  177. 'id' => 'personal-tab',
  178. 'accesskey' => 'r',
  179. ),
  180. array(
  181. 'label' => t('Home'),
  182. 'url' => 'notifications/home',
  183. 'sel'=> (($this->a->argv[1] == 'home') ? 'active' : ''),
  184. 'id' => 'home-tab',
  185. 'accesskey' => 'h',
  186. ),
  187. array(
  188. 'label' => t('Introductions'),
  189. 'url' => 'notifications/intros',
  190. 'sel'=> (($this->a->argv[1] == 'intros') ? 'active' : ''),
  191. 'id' => 'intro-tab',
  192. 'accesskey' => 'i',
  193. ),
  194. );
  195. return $tabs;
  196. }
  197. /**
  198. * @brief Format the notification query in an usable array
  199. *
  200. * @param array $notifs The array from the db query
  201. * @param string $ident The notifications identifier (e.g. network)
  202. * @return array
  203. * string 'label' => The type of the notification
  204. * string 'link' => URL to the source
  205. * string 'image' => The avatar image
  206. * string 'url' => The profile url of the contact
  207. * string 'text' => The notification text
  208. * string 'when' => The date of the notification
  209. * string 'ago' => T relative date of the notification
  210. * bool 'seen' => Is the notification marked as "seen"
  211. */
  212. private function formatNotifs($notifs, $ident = "")
  213. {
  214. $notif = array();
  215. $arr = array();
  216. if (DBM::is_result($notifs)) {
  217. foreach ($notifs as $it) {
  218. // Because we use different db tables for the notification query
  219. // we have sometimes $it['unseen'] and sometimes $it['seen].
  220. // So we will have to transform $it['unseen']
  221. if (array_key_exists('unseen', $it)) {
  222. $it['seen'] = ($it['unseen'] > 0 ? false : true);
  223. }
  224. // Depending on the identifier of the notification we need to use different defaults
  225. switch ($ident) {
  226. case 'system':
  227. $default_item_label = 'notify';
  228. $default_item_link = System::baseUrl(true).'/notify/view/'. $it['id'];
  229. $default_item_image = proxy_url($it['photo'], false, PROXY_SIZE_MICRO);
  230. $default_item_url = $it['url'];
  231. $default_item_text = strip_tags(bbcode($it['msg']));
  232. $default_item_when = datetime_convert('UTC', date_default_timezone_get(), $it['date'], 'r');
  233. $default_item_ago = relative_date($it['date']);
  234. break;
  235. case 'home':
  236. $default_item_label = 'comment';
  237. $default_item_link = System::baseUrl(true).'/display/'.$it['pguid'];
  238. $default_item_image = proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO);
  239. $default_item_url = $it['author-link'];
  240. $default_item_text = sprintf(t("%s commented on %s's post"), $it['author-name'], $it['pname']);
  241. $default_item_when = datetime_convert('UTC', date_default_timezone_get(), $it['created'], 'r');
  242. $default_item_ago = relative_date($it['created']);
  243. break;
  244. default:
  245. $default_item_label = (($it['id'] == $it['parent']) ? 'post' : 'comment');
  246. $default_item_link = System::baseUrl(true).'/display/'.$it['pguid'];
  247. $default_item_image = proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO);
  248. $default_item_url = $it['author-link'];
  249. $default_item_text = (($it['id'] == $it['parent'])
  250. ? sprintf(t("%s created a new post"), $it['author-name'])
  251. : sprintf(t("%s commented on %s's post"), $it['author-name'], $it['pname']));
  252. $default_item_when = datetime_convert('UTC', date_default_timezone_get(), $it['created'], 'r');
  253. $default_item_ago = relative_date($it['created']);
  254. }
  255. // Transform the different types of notification in an usable array
  256. switch ($it['verb']) {
  257. case ACTIVITY_LIKE:
  258. $notif = array(
  259. 'label' => 'like',
  260. 'link' => System::baseUrl(true).'/display/'.$it['pguid'],
  261. 'image' => proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO),
  262. 'url' => $it['author-link'],
  263. 'text' => sprintf(t("%s liked %s's post"), $it['author-name'], $it['pname']),
  264. 'when' => $default_item_when,
  265. 'ago' => $default_item_ago,
  266. 'seen' => $it['seen']
  267. );
  268. break;
  269. case ACTIVITY_DISLIKE:
  270. $notif = array(
  271. 'label' => 'dislike',
  272. 'link' => System::baseUrl(true).'/display/'.$it['pguid'],
  273. 'image' => proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO),
  274. 'url' => $it['author-link'],
  275. 'text' => sprintf(t("%s disliked %s's post"), $it['author-name'], $it['pname']),
  276. 'when' => $default_item_when,
  277. 'ago' => $default_item_ago,
  278. 'seen' => $it['seen']
  279. );
  280. break;
  281. case ACTIVITY_ATTEND:
  282. $notif = array(
  283. 'label' => 'attend',
  284. 'link' => System::baseUrl(true).'/display/'.$it['pguid'],
  285. 'image' => proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO),
  286. 'url' => $it['author-link'],
  287. 'text' => sprintf(t("%s is attending %s's event"), $it['author-name'], $it['pname']),
  288. 'when' => $default_item_when,
  289. 'ago' => $default_item_ago,
  290. 'seen' => $it['seen']
  291. );
  292. break;
  293. case ACTIVITY_ATTENDNO:
  294. $notif = array(
  295. 'label' => 'attendno',
  296. 'link' => System::baseUrl(true).'/display/'.$it['pguid'],
  297. 'image' => proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO),
  298. 'url' => $it['author-link'],
  299. 'text' => sprintf(t("%s is not attending %s's event"), $it['author-name'], $it['pname']),
  300. 'when' => $default_item_when,
  301. 'ago' => $default_item_ago,
  302. 'seen' => $it['seen']
  303. );
  304. break;
  305. case ACTIVITY_ATTENDMAYBE:
  306. $notif = array(
  307. 'label' => 'attendmaybe',
  308. 'link' => System::baseUrl(true).'/display/'.$it['pguid'],
  309. 'image' => proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO),
  310. 'url' => $it['author-link'],
  311. 'text' => sprintf(t("%s may attend %s's event"), $it['author-name'], $it['pname']),
  312. 'when' => $default_item_when,
  313. 'ago' => $default_item_ago,
  314. 'seen' => $it['seen']
  315. );
  316. break;
  317. case ACTIVITY_FRIEND:
  318. $xmlhead="<"."?xml version='1.0' encoding='UTF-8' ?".">";
  319. $obj = parse_xml_string($xmlhead.$it['object']);
  320. $it['fname'] = $obj->title;
  321. $notif = array(
  322. 'label' => 'friend',
  323. 'link' => System::baseUrl(true).'/display/'.$it['pguid'],
  324. 'image' => proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO),
  325. 'url' => $it['author-link'],
  326. 'text' => sprintf(t("%s is now friends with %s"), $it['author-name'], $it['fname']),
  327. 'when' => $default_item_when,
  328. 'ago' => $default_item_ago,
  329. 'seen' => $it['seen']
  330. );
  331. break;
  332. default:
  333. $notif = array(
  334. 'label' => $default_item_label,
  335. 'link' => $default_item_link,
  336. 'image' => $default_item_image,
  337. 'url' => $default_item_url,
  338. 'text' => $default_item_text,
  339. 'when' => $default_item_when,
  340. 'ago' => $default_item_ago,
  341. 'seen' => $it['seen']
  342. );
  343. }
  344. $arr[] = $notif;
  345. }
  346. }
  347. return $arr;
  348. }
  349. /**
  350. * @brief Total number of network notifications
  351. * @param int|string $seen If 0 only include notifications into the query
  352. * which aren't marked as "seen"
  353. *
  354. * @return int Number of network notifications
  355. */
  356. private function networkTotal($seen = 0)
  357. {
  358. $sql_seen = "";
  359. if ($seen === 0) {
  360. $sql_seen = " AND `item`.`unseen` = 1 ";
  361. }
  362. $r = q(
  363. "SELECT COUNT(*) AS `total`
  364. FROM `item` INNER JOIN `item` AS `pitem` ON `pitem`.`id`=`item`.`parent`
  365. WHERE `item`.`visible` = 1 AND `pitem`.`parent` != 0 AND
  366. `item`.`deleted` = 0 AND `item`.`uid` = %d AND `item`.`wall` = 0
  367. $sql_seen",
  368. intval(local_user())
  369. );
  370. if (DBM::is_result($r)) {
  371. return $r[0]['total'];
  372. }
  373. return 0;
  374. }
  375. /**
  376. * @brief Get network notifications
  377. *
  378. * @param int|string $seen If 0 only include notifications into the query
  379. * which aren't marked as "seen"
  380. * @param int $start Start the query at this point
  381. * @param int $limit Maximum number of query results
  382. *
  383. * @return array with
  384. * string 'ident' => Notification identifier
  385. * int 'total' => Total number of available network notifications
  386. * array 'notifications' => Network notifications
  387. */
  388. public function networkNotifs($seen = 0, $start = 0, $limit = 80)
  389. {
  390. $ident = 'network';
  391. $total = $this->networkTotal($seen);
  392. $notifs = array();
  393. $sql_seen = "";
  394. if ($seen === 0) {
  395. $sql_seen = " AND `item`.`unseen` = 1 ";
  396. }
  397. $r = q(
  398. "SELECT `item`.`id`,`item`.`parent`, `item`.`verb`, `item`.`author-name`, `item`.`unseen`,
  399. `item`.`author-link`, `item`.`author-avatar`, `item`.`created`, `item`.`object` AS `object`,
  400. `pitem`.`author-name` AS `pname`, `pitem`.`author-link` AS `plink`, `pitem`.`guid` AS `pguid`
  401. FROM `item` INNER JOIN `item` AS `pitem` ON `pitem`.`id`=`item`.`parent`
  402. WHERE `item`.`visible` = 1 AND `pitem`.`parent` != 0 AND
  403. `item`.`deleted` = 0 AND `item`.`uid` = %d AND `item`.`wall` = 0
  404. $sql_seen
  405. ORDER BY `item`.`created` DESC LIMIT %d, %d ",
  406. intval(local_user()),
  407. intval($start),
  408. intval($limit)
  409. );
  410. if (DBM::is_result($r)) {
  411. $notifs = $this->formatNotifs($r, $ident);
  412. }
  413. $arr = array (
  414. 'notifications' => $notifs,
  415. 'ident' => $ident,
  416. 'total' => $total,
  417. );
  418. return $arr;
  419. }
  420. /**
  421. * @brief Total number of system notifications
  422. * @param int|string $seen If 0 only include notifications into the query
  423. * which aren't marked as "seen"
  424. *
  425. * @return int Number of system notifications
  426. */
  427. private function systemTotal($seen = 0)
  428. {
  429. $sql_seen = "";
  430. if ($seen === 0) {
  431. $sql_seen = " AND `seen` = 0 ";
  432. }
  433. $r = q(
  434. "SELECT COUNT(*) AS `total` FROM `notify` WHERE `uid` = %d $sql_seen",
  435. intval(local_user())
  436. );
  437. if (DBM::is_result($r)) {
  438. return $r[0]['total'];
  439. }
  440. return 0;
  441. }
  442. /**
  443. * @brief Get system notifications
  444. *
  445. * @param int|string $seen If 0 only include notifications into the query
  446. * which aren't marked as "seen"
  447. * @param int $start Start the query at this point
  448. * @param int $limit Maximum number of query results
  449. *
  450. * @return array with
  451. * string 'ident' => Notification identifier
  452. * int 'total' => Total number of available system notifications
  453. * array 'notifications' => System notifications
  454. */
  455. public function systemNotifs($seen = 0, $start = 0, $limit = 80)
  456. {
  457. $ident = 'system';
  458. $total = $this->systemTotal($seen);
  459. $notifs = array();
  460. $sql_seen = "";
  461. if ($seen === 0) {
  462. $sql_seen = " AND `seen` = 0 ";
  463. }
  464. $r = q(
  465. "SELECT `id`, `url`, `photo`, `msg`, `date`, `seen` FROM `notify`
  466. WHERE `uid` = %d $sql_seen ORDER BY `date` DESC LIMIT %d, %d ",
  467. intval(local_user()),
  468. intval($start),
  469. intval($limit)
  470. );
  471. if (DBM::is_result($r)) {
  472. $notifs = $this->formatNotifs($r, $ident);
  473. }
  474. $arr = array (
  475. 'notifications' => $notifs,
  476. 'ident' => $ident,
  477. 'total' => $total,
  478. );
  479. return $arr;
  480. }
  481. /**
  482. * @brief Addional SQL query string for the personal notifications
  483. *
  484. * @return string The additional sql query
  485. */
  486. private function personalSqlExtra()
  487. {
  488. $myurl = System::baseUrl(true) . '/profile/'. $this->a->user['nickname'];
  489. $myurl = substr($myurl, strpos($myurl, '://') + 3);
  490. $myurl = str_replace(array('www.','.'), array('','\\.'), $myurl);
  491. $diasp_url = str_replace('/profile/', '/u/', $myurl);
  492. $sql_extra = sprintf(
  493. " AND ( `item`.`author-link` regexp '%s' OR `item`.`tag` regexp '%s' OR `item`.`tag` regexp '%s' ) ",
  494. dbesc($myurl . '$'),
  495. dbesc($myurl . '\\]'),
  496. dbesc($diasp_url . '\\]')
  497. );
  498. return $sql_extra;
  499. }
  500. /**
  501. * @brief Total number of personal notifications
  502. * @param int|string $seen If 0 only include notifications into the query
  503. * which aren't marked as "seen"
  504. *
  505. * @return int Number of personal notifications
  506. */
  507. private function personalTotal($seen = 0)
  508. {
  509. $sql_seen = "";
  510. $sql_extra = $this->personalSqlExtra();
  511. if ($seen === 0) {
  512. $sql_seen = " AND `item`.`unseen` = 1 ";
  513. }
  514. $r = q(
  515. "SELECT COUNT(*) AS `total`
  516. FROM `item` INNER JOIN `item` AS `pitem` ON `pitem`.`id`=`item`.`parent`
  517. WHERE `item`.`visible` = 1
  518. $sql_extra
  519. $sql_seen
  520. AND `item`.`deleted` = 0 AND `item`.`uid` = %d AND `item`.`wall` = 0 ",
  521. intval(local_user())
  522. );
  523. if (DBM::is_result($r)) {
  524. return $r[0]['total'];
  525. }
  526. return 0;
  527. }
  528. /**
  529. * @brief Get personal notifications
  530. *
  531. * @param int|string $seen If 0 only include notifications into the query
  532. * which aren't marked as "seen"
  533. * @param int $start Start the query at this point
  534. * @param int $limit Maximum number of query results
  535. *
  536. * @return array with
  537. * string 'ident' => Notification identifier
  538. * int 'total' => Total number of available personal notifications
  539. * array 'notifications' => Personal notifications
  540. */
  541. public function personalNotifs($seen = 0, $start = 0, $limit = 80)
  542. {
  543. $ident = 'personal';
  544. $total = $this->personalTotal($seen);
  545. $sql_extra = $this->personalSqlExtra();
  546. $notifs = array();
  547. $sql_seen = "";
  548. if ($seen === 0) {
  549. $sql_seen = " AND `item`.`unseen` = 1 ";
  550. }
  551. $r = q(
  552. "SELECT `item`.`id`,`item`.`parent`, `item`.`verb`, `item`.`author-name`, `item`.`unseen`,
  553. `item`.`author-link`, `item`.`author-avatar`, `item`.`created`, `item`.`object` AS `object`,
  554. `pitem`.`author-name` AS `pname`, `pitem`.`author-link` AS `plink`, `pitem`.`guid` AS `pguid`
  555. FROM `item` INNER JOIN `item` AS `pitem` ON `pitem`.`id`=`item`.`parent`
  556. WHERE `item`.`visible` = 1
  557. $sql_extra
  558. $sql_seen
  559. AND `item`.`deleted` = 0 AND `item`.`uid` = %d AND `item`.`wall` = 0
  560. ORDER BY `item`.`created` DESC LIMIT %d, %d ",
  561. intval(local_user()),
  562. intval($start),
  563. intval($limit)
  564. );
  565. if (DBM::is_result($r)) {
  566. $notifs = $this->formatNotifs($r, $ident);
  567. }
  568. $arr = array (
  569. 'notifications' => $notifs,
  570. 'ident' => $ident,
  571. 'total' => $total,
  572. );
  573. return $arr;
  574. }
  575. /**
  576. * @brief Total number of home notifications
  577. * @param int|string $seen If 0 only include notifications into the query
  578. * which aren't marked as "seen"
  579. *
  580. * @return int Number of home notifications
  581. */
  582. private function homeTotal($seen = 0)
  583. {
  584. $sql_seen = "";
  585. if ($seen === 0) {
  586. $sql_seen = " AND `item`.`unseen` = 1 ";
  587. }
  588. $r = q(
  589. "SELECT COUNT(*) AS `total` FROM `item`
  590. WHERE `item`.`visible` = 1 AND
  591. `item`.`deleted` = 0 AND `item`.`uid` = %d AND `item`.`wall` = 1
  592. $sql_seen",
  593. intval(local_user())
  594. );
  595. if (DBM::is_result($r)) {
  596. return $r[0]['total'];
  597. }
  598. return 0;
  599. }
  600. /**
  601. * @brief Get home notifications
  602. *
  603. * @param int|string $seen If 0 only include notifications into the query
  604. * which aren't marked as "seen"
  605. * @param int $start Start the query at this point
  606. * @param int $limit Maximum number of query results
  607. *
  608. * @return array with
  609. * string 'ident' => Notification identifier
  610. * int 'total' => Total number of available home notifications
  611. * array 'notifications' => Home notifications
  612. */
  613. public function homeNotifs($seen = 0, $start = 0, $limit = 80)
  614. {
  615. $ident = 'home';
  616. $total = $this->homeTotal($seen);
  617. $notifs = array();
  618. $sql_seen = "";
  619. if ($seen === 0) {
  620. $sql_seen = " AND `item`.`unseen` = 1 ";
  621. }
  622. $r = q(
  623. "SELECT `item`.`id`,`item`.`parent`, `item`.`verb`, `item`.`author-name`, `item`.`unseen`,
  624. `item`.`author-link`, `item`.`author-avatar`, `item`.`created`, `item`.`object` AS `object`,
  625. `pitem`.`author-name` AS `pname`, `pitem`.`author-link` AS `plink`, `pitem`.`guid` AS `pguid`
  626. FROM `item` INNER JOIN `item` AS `pitem` ON `pitem`.`id`=`item`.`parent`
  627. WHERE `item`.`visible` = 1 AND
  628. `item`.`deleted` = 0 AND `item`.`uid` = %d AND `item`.`wall` = 1
  629. $sql_seen
  630. ORDER BY `item`.`created` DESC LIMIT %d, %d ",
  631. intval(local_user()),
  632. intval($start),
  633. intval($limit)
  634. );
  635. if (DBM::is_result($r)) {
  636. $notifs = $this->formatNotifs($r, $ident);
  637. }
  638. $arr = array (
  639. 'notifications' => $notifs,
  640. 'ident' => $ident,
  641. 'total' => $total,
  642. );
  643. return $arr;
  644. }
  645. /**
  646. * @brief Total number of introductions
  647. * @param bool $all If false only include introductions into the query
  648. * which aren't marked as ignored
  649. *
  650. * @return int Number of introductions
  651. */
  652. private function introTotal($all = false)
  653. {
  654. $sql_extra = "";
  655. if (!$all) {
  656. $sql_extra = " AND `ignore` = 0 ";
  657. }
  658. $r = q(
  659. "SELECT COUNT(*) AS `total` FROM `intro`
  660. WHERE `intro`.`uid` = %d $sql_extra AND `intro`.`blocked` = 0 ",
  661. intval($_SESSION['uid'])
  662. );
  663. if (DBM::is_result($r)) {
  664. return $r[0]['total'];
  665. }
  666. return 0;
  667. }
  668. /**
  669. * @brief Get introductions
  670. *
  671. * @param bool $all If false only include introductions into the query
  672. * which aren't marked as ignored
  673. * @param int $start Start the query at this point
  674. * @param int $limit Maximum number of query results
  675. *
  676. * @return array with
  677. * string 'ident' => Notification identifier
  678. * int 'total' => Total number of available introductions
  679. * array 'notifications' => Introductions
  680. */
  681. public function introNotifs($all = false, $start = 0, $limit = 80)
  682. {
  683. $ident = 'introductions';
  684. $total = $this->introTotal($seen);
  685. $notifs = array();
  686. $sql_extra = "";
  687. if (!$all) {
  688. $sql_extra = " AND `ignore` = 0 ";
  689. }
  690. /// @todo Fetch contact details by "get_contact_details_by_url" instead of queries to contact, fcontact and gcontact
  691. $r = q(
  692. "SELECT `intro`.`id` AS `intro_id`, `intro`.*, `contact`.*,
  693. `fcontact`.`name` AS `fname`, `fcontact`.`url` AS `furl`,
  694. `fcontact`.`photo` AS `fphoto`, `fcontact`.`request` AS `frequest`,
  695. `gcontact`.`location` AS `glocation`, `gcontact`.`about` AS `gabout`,
  696. `gcontact`.`keywords` AS `gkeywords`, `gcontact`.`gender` AS `ggender`,
  697. `gcontact`.`network` AS `gnetwork`, `gcontact`.`addr` AS `gaddr`
  698. FROM `intro`
  699. LEFT JOIN `contact` ON `contact`.`id` = `intro`.`contact-id`
  700. LEFT JOIN `gcontact` ON `gcontact`.`nurl` = `contact`.`nurl`
  701. LEFT JOIN `fcontact` ON `intro`.`fid` = `fcontact`.`id`
  702. WHERE `intro`.`uid` = %d $sql_extra AND `intro`.`blocked` = 0
  703. LIMIT %d, %d",
  704. intval($_SESSION['uid']),
  705. intval($start),
  706. intval($limit)
  707. );
  708. if (DBM::is_result($r)) {
  709. $notifs = $this->formatIntros($r);
  710. }
  711. $arr = array (
  712. 'ident' => $ident,
  713. 'total' => $total,
  714. 'notifications' => $notifs,
  715. );
  716. return $arr;
  717. }
  718. /**
  719. * @brief Format the notification query in an usable array
  720. *
  721. * @param array $intros The array from the db query
  722. * @return array with the introductions
  723. */
  724. private function formatIntros($intros)
  725. {
  726. $knowyou = '';
  727. foreach ($intros as $it) {
  728. // There are two kind of introduction. Contacts suggested by other contacts and normal connection requests.
  729. // We have to distinguish between these two because they use different data.
  730. // Contact suggestions
  731. if ($it['fid']) {
  732. $return_addr = bin2hex($this->a->user['nickname'] . '@' . $this->a->get_hostname() . (($this->a->path) ? '/' . $this->a->path : ''));
  733. $intro = array(
  734. 'label' => 'friend_suggestion',
  735. 'notify_type' => t('Friend Suggestion'),
  736. 'intro_id' => $it['intro_id'],
  737. 'madeby' => $it['name'],
  738. 'contact_id' => $it['contact-id'],
  739. 'photo' => ((x($it, 'fphoto')) ? proxy_url($it['fphoto'], false, PROXY_SIZE_SMALL) : "images/person-175.jpg"),
  740. 'name' => $it['fname'],
  741. 'url' => zrl($it['furl']),
  742. 'hidden' => $it['hidden'] == 1,
  743. 'post_newfriend' => (intval(PConfig::get(local_user(), 'system', 'post_newfriend')) ? '1' : 0),
  744. 'knowyou' => $knowyou,
  745. 'note' => $it['note'],
  746. 'request' => $it['frequest'] . '?addr=' . $return_addr,
  747. );
  748. // Normal connection requests
  749. } else {
  750. $it = $this->getMissingIntroData($it);
  751. // Don't show these data until you are connected. Diaspora is doing the same.
  752. if ($it['gnetwork'] === NETWORK_DIASPORA) {
  753. $it['glocation'] = "";
  754. $it['gabout'] = "";
  755. $it['ggender'] = "";
  756. }
  757. $intro = array(
  758. 'label' => (($it['network'] !== NETWORK_OSTATUS) ? 'friend_request' : 'follower'),
  759. 'notify_type' => (($it['network'] !== NETWORK_OSTATUS) ? t('Friend/Connect Request') : t('New Follower')),
  760. 'dfrn_id' => $it['issued-id'],
  761. 'uid' => $_SESSION['uid'],
  762. 'intro_id' => $it['intro_id'],
  763. 'contact_id' => $it['contact-id'],
  764. 'photo' => ((x($it, 'photo')) ? proxy_url($it['photo'], false, PROXY_SIZE_SMALL) : "images/person-175.jpg"),
  765. 'name' => $it['name'],
  766. 'location' => bbcode($it['glocation'], false, false),
  767. 'about' => bbcode($it['gabout'], false, false),
  768. 'keywords' => $it['gkeywords'],
  769. 'gender' => $it['ggender'],
  770. 'hidden' => $it['hidden'] == 1,
  771. 'post_newfriend' => (intval(PConfig::get(local_user(), 'system', 'post_newfriend')) ? '1' : 0),
  772. 'url' => $it['url'],
  773. 'zrl' => zrl($it['url']),
  774. 'addr' => $it['gaddr'],
  775. 'network' => $it['gnetwork'],
  776. 'knowyou' => $it['knowyou'],
  777. 'note' => $it['note'],
  778. );
  779. }
  780. $arr[] = $intro;
  781. }
  782. return $arr;
  783. }
  784. /**
  785. * @brief Check for missing contact data and try to fetch the data from
  786. * from other sources
  787. *
  788. * @param array $arr The input array with the intro data
  789. *
  790. * @return array The array with the intro data
  791. */
  792. private function getMissingIntroData($arr)
  793. {
  794. // If the network and the addr isn't available from the gcontact
  795. // table entry, take the one of the contact table entry
  796. if ($arr['gnetwork'] == "") {
  797. $arr['gnetwork'] = $arr['network'];
  798. }
  799. if ($arr['gaddr'] == "") {
  800. $arr['gaddr'] = $arr['addr'];
  801. }
  802. // If the network and addr is still not available
  803. // get the missing data data from other sources
  804. if ($arr['gnetwork'] == "" || $arr['gaddr'] == "") {
  805. $ret = get_contact_details_by_url($arr['url']);
  806. if ($arr['gnetwork'] == "" && $ret['network'] != "") {
  807. $arr['gnetwork'] = $ret['network'];
  808. }
  809. if ($arr['gaddr'] == "" && $ret['addr'] != "") {
  810. $arr['gaddr'] = $ret['addr'];
  811. }
  812. }
  813. return $arr;
  814. }
  815. }