$ git clone http://thingshare.ion.nu/thingshare.git
commit 3ef20aa79f714eb209a0149111517edc6f7660f0
Author: Alicia <...>
Date: Sat Jan 30 01:09:55 2021 +0100
Added support for notifications.
diff --git a/Licenses b/Licenses
index e58aa7d..830c5d5 100644
--- a/Licenses
+++ b/Licenses
@@ -4,4 +4,5 @@ External resources used by Thingshare, and their respective licenses:
parsedown: under the terms of the MIT license (https://github.com/erusev/parsedown)
mdjs: under the terms of the Apache license (https://github.com/hangxingliu/mdjs)
icons/default.svg: "Editor, software, text icon" from the Macaron icon set by Goescat Wei, under the terms of the Creative Commons Attribution 3.0 Unported (https://www.iconfinder.com/icons/3246744/editor_software_text_icon)
+icons/bell.svg/png: "Alarm, alert, attention, bell, notification, notifications, ring icon" by DIVYA A under the terms of the Creative Commons Attribution 3.0 Unported (https://www.iconfinder.com/icons/4964018/alarm_alert_attention_bell_notification_notifications_ring_icon)
icons/scad.png: GNU GPLv2 (https://github.com/openscad/openscad/blob/master/icons/SCAD.png)
diff --git a/db.php b/db.php
index 345f8b2..ffdd2b4 100644
--- a/db.php
+++ b/db.php
@@ -2,7 +2,7 @@
/*
This file is part of Thingshare, a federated system for sharing data for home manufacturing (e.g. 3D models to 3D print)
https://thingshare.ion.nu/
- Copyright (C) 2020 Alicia <...>
+ Copyright (C) 2020-2021 Alicia <...>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
@@ -130,6 +130,12 @@ function db_create_tables()
sent datetime,
removed boolean);');
mysqli_query($db, 'create table filtertags(tag text);');
+ mysqli_query($db, 'create table notifications(id integer primary key auto_increment,
+ user integer,
+ message text,
+ link text,
+ sent datetime,
+ seen boolean);');
}
function db_getuser($id)
diff --git a/docs/RPCs b/docs/RPCs
index 35fde7b..c5fc97f 100644
--- a/docs/RPCs
+++ b/docs/RPCs
@@ -142,3 +142,11 @@ Signed request format:
}
Return format:
{"status":"OK"} or {"error":<Error message>}
+
+RPC: notification/<Username>
+Send a notification (about a comment, maybe the only thing notifications will be needed for)
+Signed request format:
+{
+ "message": <Message>,
+ "link": <...>
+}
diff --git a/head.php b/head.php
index 4784a3f..23e63cb 100644
--- a/head.php
+++ b/head.php
@@ -2,7 +2,7 @@
/*
This file is part of Thingshare, a federated system for sharing data for home manufacturing (e.g. 3D models to 3D print)
https://thingshare.ion.nu/
- Copyright (C) 2020 Alicia <...>
+ Copyright (C) 2020-2021 Alicia <...>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
@@ -20,6 +20,34 @@
include_once('config.php');
$menu=''; // Additional menu items depending on privileges/just being logged in
$unreadmsgs='';
+$notifications='';
+$notifications_new=false;
+function timeago($time)
+{
+ $time=time()-strtotime($time);
+ $unit='';
+ if($time>3600*47)
+ {
+ $time=round($time/(3600*24));
+ $unit='day';
+ }
+ elseif($time>3600)
+ {
+ $time=round($time/3600);
+ $unit='hour';
+ }
+ elseif($time>60)
+ {
+ $time=round($time/60);
+ $unit='minute';
+ }
+ if($unit!='')
+ {
+ // TODO: Use gettext pluralization
+ return $time.' '.$unit.($time==1?'':'s').' ago';
+ }
+ return 'just now';
+}
if(isset($_COOKIE['PHPSESSID'])) // TODO: See if there's a better way to check if there is a session to resume
{
session_start();
@@ -39,6 +67,28 @@ if(isset($_COOKIE['PHPSESSID'])) // TODO: See if there's a better way to check i
// Check for unread messages
$res=mysqli_query($db, 'select id from messages where user='.(int)$_SESSION['id'].' and !msgread limit 1');
if(mysqli_num_rows($res)>0){$unreadmsgs=' class="highlight"';}
+ // Check for notifications
+ if(isset($_GET['notification']))
+ {
+ // TODO: Is there a cleaner way to update 'seen' than passing the ID in GET?
+ mysqli_query($db, 'update notifications set seen=true where user='.(int)$_SESSION['id'].' and id='.(int)$_GET['notification']);
+ }
+ $res=mysqli_query($db, 'select id, message, link, sent, seen from notifications where user='.(int)$_SESSION['id'].' order by sent desc limit 20');
+ while($row=mysqli_fetch_assoc($res))
+ {
+ $link=$row['link'];
+ if(!$row['seen'])
+ {
+ $notifications_new=true;
+ if($pos=strpos($link, '#'))
+ {
+ $link=substr($link,0,$pos).'?notification='.$row['id'].substr($link,$pos);
+ }else{
+ $link.='?notification='.$row['id'];
+ }
+ }
+ $notifications.=timeago($row['sent']).' <a href="'.BASEURL.$link.'"><div class="notification'.($row['seen']?'':' notification-unseen').'">'.$row['message'].'</div></a><br />';
+ }
}
}
$loginlink=BASEURL.'/login?returnto='.urlencode($_SERVER['REQUEST_URI']);
@@ -69,6 +119,12 @@ if($path[1]=='tag')
<?=$menu?>
</div>
<div id="user"><?php if(isset($_SESSION['name'])){ ?>
+ <div class="dropdown-container">
+ <div class="dropdown">
+ <img src="<?=BASEURL?>/icons/bell.svg" width="32" height="32" onerror="this.src='<?=BASEURL?>/icons/bell.png';" style="display:block; opacity:<?=($notifications_new?1:0.5)?>;" />
+ <?=$notifications?>
+ </div>
+ </div>
<!-- Link to user settings, messages -->
<a href="<?=BASEURL?>/user/<...>">My profile</a>
<a href="<?=BASEURL?>/messages"<?=$unreadmsgs?>>Messages</a>
diff --git a/icons/bell.png b/icons/bell.png
new file mode 100644
index 0000000..85c1346
Binary files /dev/null and b/icons/bell.png differ
diff --git a/icons/bell.svg b/icons/bell.svg
new file mode 100644
index 0000000..363c2e5
--- /dev/null
+++ b/icons/bell.svg
@@ -0,0 +1 @@
+<?xml version="1.0" ?><svg height="24" version="1.1" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><title/><path d="M18 15.984l2.016 2.016v0.984h-16.031v-0.984l2.016-2.016v-4.969c0-3.094 1.641-5.625 4.5-6.328v-0.703c0-0.844 0.656-1.5 1.5-1.5s1.5 0.656 1.5 1.5v0.703c2.859 0.703 4.5 3.281 4.5 6.328v4.969zM12 21.984c-1.125 0-2.016-0.891-2.016-1.969h4.031c0 1.078-0.938 1.969-2.016 1.969z"/></svg>
\ No newline at end of file
diff --git a/index.php b/index.php
index 082f6a1..e6e1c4f 100644
--- a/index.php
+++ b/index.php
@@ -2,7 +2,7 @@
/*
This file is part of Thingshare, a federated system for sharing data for home manufacturing (e.g. 3D models to 3D print)
https://thingshare.ion.nu/
- Copyright (C) 2020 Alicia <...>
+ Copyright (C) 2020-2021 Alicia <...>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
@@ -72,6 +72,7 @@ switch($path[1])
case 'messages': include('rpc_messages.php'); exit();
case 'peers': include('rpc_peers.php'); exit();
case 'comments': include('rpc_comments.php'); exit();
+ case 'notification': include('rpc_notification.php'); exit();
}
header('HTTP/1.1 404 Not found');
die('{"httpresponse":404,"error":"Not found"}');
diff --git a/rpc_comments.php b/rpc_comments.php
index 37ac376..283d8ec 100644
--- a/rpc_comments.php
+++ b/rpc_comments.php
@@ -2,7 +2,7 @@
/*
This file is part of Thingshare, a federated system for sharing data for home manufacturing (e.g. 3D models to 3D print)
https://thingshare.ion.nu/
- Copyright (C) 2020 Alicia <...>
+ Copyright (C) 2020-2021 Alicia <...>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
@@ -54,6 +54,28 @@ if(isset($_POST['data'])) // Posting a comment
if(mysqli_query($db, $q))
{
print('{"status":"OK"}');
+ // Notify the parent commenter or thing's owner
+ $link='/thing/'.$thing.'@'.DOMAIN.'#comment'.mysqli_insert_id($db);
+ $name=getdisplayname($obj['from'].'@'.$peer);
+ $res=mysqli_query($db, 'select name, user from things where thingid='.$thing.' order by posted desc limit 1');
+ $thingrow=mysqli_fetch_assoc($res);
+ $thingname=$thingrow['name'];
+ if($replyto)
+ {
+ $res=mysqli_query($db, 'select sender from comments where id='.$replyto);
+ $row=mysqli_fetch_row($res);
+ $touser=explode('@', $row[0]);
+ if(count($touser)==2)
+ {
+ $msg=Array('message'=>$name.' replied to your comment on '.$thingname,
+ 'link'=>$link);
+ rpc_post($touser[1], 'notification/'.$touser[0], $msg);
+ }
+ }else{
+ $msg=Array('message'=>$name.' commented on '.$thingname,
+ 'link'=>$link);
+ rpc_post(DOMAIN, 'notification/'.db_getuser($thingrow['user']), $msg);
+ }
}else{
print('{"error":"Database error"}');
}
diff --git a/rpc_notification.php b/rpc_notification.php
new file mode 100644
index 0000000..0f47efc
--- /dev/null
+++ b/rpc_notification.php
@@ -0,0 +1,43 @@
+<...>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+include_once('rpc.php');
+include_once('db.php');
+$obj=rpc_verifypost($peer);
+// Find recipient user ID
+$to=mysqli_real_escape_string($db, $path[3]);
+$res=mysqli_query($db, 'select id from users where name="'.$to.'"');
+$res=mysqli_fetch_row($res);
+if(!$res){header('HTTP/1.1 404 Not found'); die('{"error":"User not found"}');}
+// TODO: Check that the link refers to something on the sender node?
+// Prepare values for DB
+$user=(int)$res[0];
+$msg=mysqli_real_escape_string($db, $obj['message']);
+$link=mysqli_real_escape_string($db, $obj['link']);
+$timestamp=mysqli_real_escape_string($db, date('Y-m-d H:i:s'));
+// Store notification
+$q='insert into notifications(user, message, link, sent, seen) ';
+$q.='values('.$user.', "'.$msg.'", "'.$link.'", "'.$timestamp.'", false)';
+if(mysqli_query($db, $q))
+{
+ print('{"status":"OK"}');
+}else{
+ print('{"error":"Database error"}');
+}
+?>
diff --git a/style.css b/style.css
index 882e9fe..f316e7e 100644
--- a/style.css
+++ b/style.css
@@ -1,7 +1,7 @@
/*
This file is part of Thingshare, a federated system for sharing data for home manufacturing (e.g. 3D models to 3D print)
https://thingshare.ion.nu/
- Copyright (C) 2020 Alicia <...>
+ Copyright (C) 2020-2021 Alicia <...>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
@@ -171,3 +171,31 @@ a.highlight {
a.pagenav {
margin-right:12px;
}
+div.dropdown-container {
+ display:inline-block;
+ vertical-align:top;
+ margin-top:-8px;
+ width:32px;
+ height:32px;
+}
+div.dropdown {
+ width:32px;
+ height:32px;
+ overflow:hidden;
+ position:absolute;
+}
+div.dropdown:hover {
+ overflow-y:auto;
+ height:250px;
+ width:auto;
+ background-color:#e0e0e0;
+ border-radius:5px;
+ box-shadow:#000000 0px 15px 20px 0px;
+ z-index:20;
+}
+div.notification {
+ display:inline;
+}
+div.notification-unseen {
+ font-weight:bold;
+}