3 * @file include/NotificationsManager.php
5 require_once('include/html2plain.php');
6 require_once("include/datetime.php");
7 require_once("include/bbcode.php");
8 require_once("include/dbm.php");
11 * @brief Read and write notifications from/to database
13 class NotificationsManager {
16 public function __construct() {
21 * @brief set some extra note properties
23 * @param array $notes array of note arrays from db
24 * @return array Copy of input array with added properties
26 * Set some extra properties to note array from db:
27 * - timestamp as int in default TZ
28 * - date_rel : relative date string
29 * - msg_html: message as html string
30 * - msg_plain: message as plain text string
32 private function _set_extra($notes) {
34 foreach($notes as $n) {
35 $local_time = datetime_convert('UTC',date_default_timezone_get(),$n['date']);
36 $n['timestamp'] = strtotime($local_time);
37 $n['date_rel'] = relative_date($n['date']);
38 $n['msg_html'] = bbcode($n['msg'], false, false, false, false);
39 $n['msg_plain'] = explode("\n",trim(html2plain($n['msg_html'], 0)))[0];
48 * @brief get all notifications for local_user()
50 * @param array $filter optional Array "column name"=>value: filter query by columns values
51 * @param string $order optional Space separated list of column to sort by. prepend name with "+" to sort ASC, "-" to sort DESC. Default to "-date"
52 * @param string $limit optional Query limits
54 * @return array of results or false on errors
56 public function getAll($filter = array(), $order="-date", $limit="") {
57 $filter_str = array();
59 foreach($filter as $column => $value) {
60 $filter_str[] = sprintf("`%s` = '%s'", $column, dbesc($value));
62 if (count($filter_str)>0) {
63 $filter_sql = "AND ".implode(" AND ", $filter_str);
66 $aOrder = explode(" ", $order);
68 foreach($aOrder as $o) {
78 $asOrder[] = "$o $dir";
80 $order_sql = implode(", ", $asOrder);
82 if ($limit!="") $limit = " LIMIT ".$limit;
84 $r = q("SELECT * FROM `notify` WHERE `uid` = %d $filter_sql ORDER BY $order_sql $limit",
87 if ($r!==false && count($r)>0) return $this->_set_extra($r);
92 * @brief get one note for local_user() by $id value
95 * @return array note values or null if not found
97 public function getByID($id) {
98 $r = q("SELECT * FROM `notify` WHERE `id` = %d AND `uid` = %d LIMIT 1",
102 if($r!==false && count($r)>0) {
103 return $this->_set_extra($r)[0];
109 * @brief set seen state of $note of local_user()
112 * @param bool $seen optional true or false, default true
113 * @return bool true on success, false on errors
115 public function setSeen($note, $seen = true) {
116 return q("UPDATE `notify` SET `seen` = %d WHERE ( `link` = '%s' OR ( `parent` != 0 AND `parent` = %d AND `otype` = '%s' )) AND `uid` = %d",
118 dbesc($note['link']),
119 intval($note['parent']),
120 dbesc($note['otype']),
126 * @brief set seen state of all notifications of local_user()
128 * @param bool $seen optional true or false. default true
129 * @return bool true on success, false on error
131 public function setAllSeen($seen = true) {
132 return q("UPDATE `notify` SET `seen` = %d WHERE `uid` = %d",
139 * @brief List of pages for the Notifications TabBar
142 * @return array with with notifications TabBar data
144 public function getTabs() {
147 'label' => t('System'),
148 'url'=>'notifications/system',
149 'sel'=> (($this->a->argv[1] == 'system') ? 'active' : ''),
150 'id' => 'system-tab',
154 'label' => t('Network'),
155 'url'=>'notifications/network',
156 'sel'=> (($this->a->argv[1] == 'network') ? 'active' : ''),
157 'id' => 'network-tab',
161 'label' => t('Personal'),
162 'url'=>'notifications/personal',
163 'sel'=> (($this->a->argv[1] == 'personal') ? 'active' : ''),
164 'id' => 'personal-tab',
168 'label' => t('Home'),
169 'url' => 'notifications/home',
170 'sel'=> (($this->a->argv[1] == 'home') ? 'active' : ''),
175 'label' => t('Introductions'),
176 'url' => 'notifications/intros',
177 'sel'=> (($this->a->argv[1] == 'intros') ? 'active' : ''),
187 * @brief Format the notification query in an usable array
189 * @param array $notifs The array from the db query
190 * @param string $ident The notifications identifier (e.g. network)
192 * string 'label' => The type of the notification
193 * string 'link' => URL to the source
194 * string 'image' => The avatar image
195 * string 'text' => The notification text
196 * string 'when' => Relative date of the notification
197 * bool 'seen' => Is the notification marked as "seen"
199 private function format($notifs, $ident = "") {
204 if (dbm::is_result($notifs)) {
206 foreach ($notifs as $it) {
207 // Because we use different db tables for the notification query
208 // we have sometimes $it['unseen'] and sometimes $it['seen].
209 // So we will have to transform $it['unseen']
211 $it['seen'] = ($it['unseen'] > 0 ? false : true);
213 // Depending on the identifier of the notification we need to use different defaults
216 $default_item_label = 'notify';
217 $default_item_link = $this->a->get_baseurl(true).'/notify/view/'. $it['id'];
218 $default_item_image = proxy_url($it['photo'], false, PROXY_SIZE_MICRO);
219 $default_item_text = strip_tags(bbcode($it['msg']));
220 $default_item_when = relative_date($it['date']);
221 $default_tpl = $tpl_notify;
225 $default_item_label = 'comment';
226 $default_item_link = $this->a->get_baseurl(true).'/display/'.$it['pguid'];
227 $default_item_image = proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO);
228 $default_item_text = sprintf( t("%s commented on %s's post"), $it['author-name'], $it['pname']);
229 $default_item_when = relative_date($it['created']);
230 $default_tpl = $tpl_item_comments;
234 $default_item_label = (($it['id'] == $it['parent']) ? 'post' : 'comment');
235 $default_item_link = $this->a->get_baseurl(true).'/display/'.$it['pguid'];
236 $default_item_image = proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO);
237 $default_item_text = (($it['id'] == $it['parent'])
238 ? sprintf( t("%s created a new post"), $it['author-name'])
239 : sprintf( t("%s commented on %s's post"), $it['author-name'], $it['pname']));
240 $default_item_when = relative_date($it['created']);
241 $default_tpl = (($it['id'] == $it['parent']) ? $tpl_item_posts : $tpl_item_comments);
245 // Transform the different types of notification in an usable array
250 'link' => $this->a->get_baseurl(true).'/display/'.$it['pguid'],
251 '$image' => proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO),
252 'text' => sprintf( t("%s liked %s's post"), $it['author-name'], $it['pname']),
253 'when' => relative_date($it['created']),
254 'seen' => $it['seen']
258 case ACTIVITY_DISLIKE:
260 'label' => 'dislike',
261 'link' => $this->a->get_baseurl(true).'/display/'.$it['pguid'],
262 'image' => proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO),
263 'text' => sprintf( t("%s disliked %s's post"), $it['author-name'], $it['pname']),
264 'when' => relative_date($it['created']),
265 'seen' => $it['seen']
269 case ACTIVITY_ATTEND:
272 'link' => $this->a->get_baseurl(true).'/display/'.$it['pguid'],
273 'image' => proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO),
274 'text' => sprintf( t("%s is attending %s's event"), $it['author-name'], $it['pname']),
275 'when' => relative_date($it['created']),
276 'seen' => $it['seen']
280 case ACTIVITY_ATTENDNO:
282 'label' => 'attendno',
283 'link' => $this->a->get_baseurl(true).'/display/'.$it['pguid'],
284 'image' => proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO),
285 'text' => sprintf( t("%s is not attending %s's event"), $it['author-name'], $it['pname']),
286 'when' => relative_date($it['created']),
287 'seen' => $it['seen']
291 case ACTIVITY_ATTENDMAYBE:
293 'label' => 'attendmaybe',
294 'link' => $this->a->get_baseurl(true).'/display/'.$it['pguid'],
295 'image' => proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO),
296 'text' => sprintf( t("%s may attend %s's event"), $it['author-name'], $it['pname']),
297 'when' => relative_date($it['created']),
298 'seen' => $it['seen']
302 case ACTIVITY_FRIEND:
303 $xmlhead="<"."?xml version='1.0' encoding='UTF-8' ?".">";
304 $obj = parse_xml_string($xmlhead.$it['object']);
305 $it['fname'] = $obj->title;
309 'link' => $this->a->get_baseurl(true).'/display/'.$it['pguid'],
310 'image' => proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO),
311 'text' => sprintf( t("%s is now friends with %s"), $it['author-name'], $it['fname']),
312 'when' => relative_date($it['created']),
313 'seen' => $it['seen']
319 'label' => $default_item_label,
320 'link' => $default_item_link,
321 'image' => $default_item_image,
322 'text' => $default_item_text,
323 'when' => $default_item_when,
324 'seen' => $it['seen']
337 * @brief Total number of network notifications
339 * If 0 only include notifications into the query
340 * which arn't marked as "seen" into
341 * @return int Number of network notifications
343 private function networkTotal($seen = 0) {
345 $sql_seen = " AND `item`.`unseen` = 1 ";
347 $r = q("SELECT COUNT(*) AS `total`
348 FROM `item` INNER JOIN `item` AS `pitem` ON `pitem`.`id`=`item`.`parent`
349 WHERE `item`.`visible` = 1 AND `pitem`.`parent` != 0 AND
350 `item`.`deleted` = 0 AND `item`.`uid` = %d AND `item`.`wall` = 0
355 if(dbm::is_result($r))
356 return $r[0]['total'];
362 * @brief Get network notifications
365 * If 0 only include notifications into the query
366 * which arn't marked as "seen" into
367 * @param int $start Start the query at this point
368 * @param int $limit Maximum number of query results
370 * @return array Query output
372 public function networkNotifs($seen = 0, $start = 0, $limit = 20) {
374 $total = $this->networkTotal($seen);
377 $sql_seen = " AND `item`.`unseen` = 1 ";
380 $r = q("SELECT `item`.`id`,`item`.`parent`, `item`.`verb`, `item`.`author-name`, `item`.`unseen`,
381 `item`.`author-link`, `item`.`author-avatar`, `item`.`created`, `item`.`object` AS `object`,
382 `pitem`.`author-name` AS `pname`, `pitem`.`author-link` AS `plink`, `pitem`.`guid` AS `pguid`
383 FROM `item` INNER JOIN `item` AS `pitem` ON `pitem`.`id`=`item`.`parent`
384 WHERE `item`.`visible` = 1 AND `pitem`.`parent` != 0 AND
385 `item`.`deleted` = 0 AND `item`.`uid` = %d AND `item`.`wall` = 0
387 ORDER BY `item`.`created` DESC LIMIT %d, %d ",
388 intval(local_user()),
393 if(dbm::is_result($r)) {
394 $notifs = $this->format($r, $ident);
396 'notifications' => $notifs,
406 * @brief Total number of system notifications
408 * If 0 only include notifications into the query
409 * which arn't marked as "seen" into
410 * @return int Number of system notifications
412 private function systemTotal($seen = 0) {
414 $sql_seen = " AND `seen` = 0 ";
416 $r = q("SELECT COUNT(*) AS `total` FROM `notify` WHERE `uid` = %d $sql_seen",
420 if(dbm::is_result($r))
421 return $r[0]['total'];
427 * @brief Get system notifications
430 * If 0 only include notifications into the query
431 * which arn't marked as "seen" into
432 * @param int $start Start the query at this point
433 * @param int $limit Maximum number of query results
435 * @return array Query output
437 public function systemNotifs($seen = 0, $start = 0, $limit = 20) {
439 $total = $this->systemTotal($seen);
442 $sql_seen = " AND `seen` = 0 ";
444 $r = q("SELECT `id`, `photo`, `msg`, `date`, `seen` FROM `notify`
445 WHERE `uid` = %d $sql_seen ORDER BY `date` DESC LIMIT %d, %d ",
446 intval(local_user()),
451 if(dbm::is_result($r)) {
452 $notifs = $this->format($r, $ident);
454 'notifications' => $notifs,
464 * @brief Addional SQL query string for the personal notifications
466 * @return string The additional sql query
468 private function _personal_sql_extra() {
469 $myurl = $this->a->get_baseurl(true) . '/profile/'. $this->a->user['nickname'];
470 $myurl = substr($myurl,strpos($myurl,'://')+3);
471 $myurl = str_replace(array('www.','.'),array('','\\.'),$myurl);
472 $diasp_url = str_replace('/profile/','/u/',$myurl);
473 $sql_extra .= sprintf(" AND ( `item`.`author-link` regexp '%s' or `item`.`tag` regexp '%s' or `item`.`tag` regexp '%s' ) ",
475 dbesc($myurl . '\\]'),
476 dbesc($diasp_url . '\\]')
483 * @brief Total number of personal notifications
485 * If 0 only include notifications into the query
486 * which arn't marked as "seen" into
487 * @return int Number of personal notifications
489 private function personalTotal($seen = 0) {
490 $sql_extra .= $this->_personal_sql_extra();
493 $sql_seen = " AND `item`.`unseen` = 1 ";
495 $r = q("SELECT COUNT(*) AS `total`
496 FROM `item` INNER JOIN `item` AS `pitem` ON `pitem`.`id`=`item`.`parent`
497 WHERE `item`.`visible` = 1
500 AND `item`.`deleted` = 0 AND `item`.`uid` = %d AND `item`.`wall` = 0 " ,
504 if(dbm::is_result($r))
505 return $r[0]['total'];
511 * @brief Get personal notifications
514 * If 0 only include notifications into the query
515 * which arn't marked as "seen" into
516 * @param int $start Start the query at this point
517 * @param int $limit Maximum number of query results
519 * @return array Query output
521 public function personalNotifs($seen = 0, $start = 0, $limit = 20) {
524 $sql_extra .= $this->_personal_sql_extra();
527 $sql_seen = " AND `item`.`unseen` = 1 ";
529 $r = q("SELECT `item`.`id`,`item`.`parent`, `item`.`verb`, `item`.`author-name`, `item`.`unseen`,
530 `item`.`author-link`, `item`.`author-avatar`, `item`.`created`, `item`.`object` AS `object`,
531 `pitem`.`author-name` AS `pname`, `pitem`.`author-link` AS `plink`, `pitem`.`guid` AS `pguid`,
532 FROM `item` INNER JOIN `item` AS `pitem` ON `pitem`.`id`=`item`.`parent`
533 WHERE `item`.`visible` = 1
536 AND `item`.`deleted` = 0 AND `item`.`uid` = %d AND `item`.`wall` = 0
537 ORDER BY `item`.`created` DESC LIMIT %d, %d " ,
538 intval(local_user()),
543 if(dbm::is_result($r)) {
544 $notifs = $this->format($r, $ident);
546 'notifications' => $notifs,
556 * @brief Total number of home notifications
558 * If 0 only include notifications into the query
559 * which arn't marked as "seen" into
560 * @return int Number of home notifications
562 private function homeTotal($seen = 0) {
564 $sql_seen = " AND `item`.`unseen` = 1 ";
566 $r = q("SELECT COUNT(*) AS `total` FROM `item`
567 WHERE `item`.`visible` = 1 AND
568 `item`.`deleted` = 0 AND `item`.`uid` = %d AND `item`.`wall` = 1
573 if(dbm::is_result($r))
574 return $r[0]['total'];
580 * @brief Get home notifications
583 * If 0 only include notifications into the query
584 * which arn't marked as "seen" into
585 * @param int $start Start the query at this point
586 * @param int $limit Maximum number of query results
588 * @return array Query output
590 public function homeNotifs($seen = 0, $start = 0, $limit = 20) {
592 $total = $this->homeTotal($seen);
595 $sql_seen = " AND `item`.`unseen` = 1 ";
597 $total = $this->homeTotal($seen);
599 $r = q("SELECT `item`.`id`,`item`.`parent`, `item`.`verb`, `item`.`author-name`, `item`.`unseen`,
600 `item`.`author-link`, `item`.`author-avatar`, `item`.`created`, `item`.`object` as `object`,
601 `pitem`.`author-name` as `pname`, `pitem`.`author-link` as `plink`, `pitem`.`guid` as `pguid`
602 FROM `item` INNER JOIN `item` as `pitem` ON `pitem`.`id`=`item`.`parent`
603 WHERE `item`.`visible` = 1 AND
604 `item`.`deleted` = 0 AND `item`.`uid` = %d AND `item`.`wall` = 1
606 ORDER BY `item`.`created` DESC LIMIT %d, %d ",
607 intval(local_user()),
612 if(dbm::is_result($r)) {
613 $notifs = $this->format($r, $ident);
615 'notifications' => $notifs,