aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Zotlabs/Lib/Apps.php5
-rw-r--r--Zotlabs/Lib/Enotify.php20
-rw-r--r--Zotlabs/Lib/ThreadStream.php8
-rw-r--r--Zotlabs/Module/Acl.php4
-rw-r--r--Zotlabs/Module/Home.php2
-rw-r--r--Zotlabs/Module/Hq.php52
-rw-r--r--Zotlabs/Module/Network.php4
-rw-r--r--Zotlabs/Module/Notify.php34
-rw-r--r--Zotlabs/Module/Sse_bs.php12
-rw-r--r--Zotlabs/Web/WebServer.php2
-rw-r--r--Zotlabs/Widget/Hq_controls.php33
-rw-r--r--Zotlabs/Widget/Messages.php181
-rw-r--r--Zotlabs/Widget/Notes.php6
-rw-r--r--Zotlabs/Widget/Notifications.php2
-rw-r--r--app/hq.apd6
-rw-r--r--boot.php5
-rw-r--r--include/items.php2
-rw-r--r--include/text.php27
-rw-r--r--view/css/conversation.css3
-rw-r--r--view/css/mod_dm.css3
-rw-r--r--view/css/widgets.css7
-rw-r--r--view/js/autocomplete.js15
-rw-r--r--view/js/main.js439
-rw-r--r--view/js/mod_hq.js26
-rw-r--r--view/pdl/mod_hq.pdl8
-rw-r--r--view/tpl/head.tpl1
-rw-r--r--view/tpl/hq_controls.tpl13
-rw-r--r--view/tpl/messages_widget.tpl123
-rw-r--r--view/tpl/notes.tpl12
-rw-r--r--view/tpl/notifications_widget.tpl455
30 files changed, 954 insertions, 556 deletions
diff --git a/Zotlabs/Lib/Apps.php b/Zotlabs/Lib/Apps.php
index bd51f0896..30c108cc1 100644
--- a/Zotlabs/Lib/Apps.php
+++ b/Zotlabs/Lib/Apps.php
@@ -76,7 +76,8 @@ class Apps {
'Directory',
'Search',
'Help',
- 'Profile Photo'
+ 'Profile Photo',
+ 'HQ'
]);
/**
@@ -374,7 +375,7 @@ class Apps {
'Permission Categories' => t('Permission Categories'),
'Public Stream' => t('Public Stream'),
'My Chatrooms' => t('My Chatrooms'),
- 'Channel Export' => t('Channel Export')
+ 'Channel Export' => t('Channel Export'),
);
if(array_key_exists('name',$arr)) {
diff --git a/Zotlabs/Lib/Enotify.php b/Zotlabs/Lib/Enotify.php
index 7e33f09b8..50b564bde 100644
--- a/Zotlabs/Lib/Enotify.php
+++ b/Zotlabs/Lib/Enotify.php
@@ -124,14 +124,14 @@ class Enotify {
if ($params['type'] == NOTIFY_MAIL) {
logger('notification: mail');
- $subject = sprintf( t('[$Projectname:Notify] New mail received at %s'),$sitename);
-
- $preamble = sprintf( t('%1$s sent you a new private message at %2$s.'), $sender['xchan_name'],$sitename);
- $epreamble = sprintf( t('%1$s sent you %2$s.'),'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', '[zrl=$itemlink]' . t('a private message') . '[/zrl]');
- $sitelink = t('Please visit %s to view and/or reply to your private messages.');
- $tsitelink = sprintf( $sitelink, $siteurl . '/mail/' . $params['item']['id'] );
- $hsitelink = sprintf( $sitelink, '<a href="' . $siteurl . '/mail/' . $params['item']['id'] . '">' . $sitename . '</a>');
- $itemlink = $siteurl . '/mail/' . $params['item']['id'];
+ $subject = sprintf( t('[$Projectname:Notify] New direct message received at %s'),$sitename);
+
+ $preamble = sprintf( t('%1$s sent you a new direct message at %2$s.'), $sender['xchan_name'],$sitename);
+ $epreamble = sprintf( t('%1$s sent you %2$s.'),'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', '[zrl=$itemlink]' . t('a direct message') . '[/zrl]');
+ $sitelink = t('Please visit %s to view and/or reply to your direct messages.');
+ $tsitelink = sprintf( $sitelink, $siteurl . '/hq/' . gen_link_id($params['item']['mid']));
+ $hsitelink = sprintf( $sitelink, '<a href="' . $siteurl . '/hq/' . gen_link_id($params['item']['mid']) . '">' . $sitename . '</a>');
+ $itemlink = $siteurl . '/hq/' . gen_link_id($params['item']['mid']);
}
if ($params['type'] == NOTIFY_COMMENT) {
@@ -886,7 +886,7 @@ class Enotify {
$b64mid = ((strpos($mid, 'b64.') === 0) ? $mid : 'b64.' . base64url_encode($mid));
$x = [
- 'notify_link' => z_root() . '/notify/view/' . $tt['id'],
+ 'notify_link' => (($tt['ntype'] === NOTIFY_MAIL) ? $tt['link'] : z_root() . '/notify/view/' . $tt['id']),
'name' => $tt['xname'],
'url' => $tt['url'],
'photo' => $tt['photo'],
@@ -945,7 +945,7 @@ class Enotify {
'photo' => $rr['xchan_photo_s'],
'when' => datetime_convert('UTC', date_default_timezone_get(), $rr['created']),
'hclass' => (intval($rr['mail_seen']) ? 'notify-seen' : 'notify-unseen'),
- 'message' => t('sent you a private message'),
+ 'message' => t('sent you a direct message'),
];
return $x;
diff --git a/Zotlabs/Lib/ThreadStream.php b/Zotlabs/Lib/ThreadStream.php
index 68b2c70dd..7fe8fcc2e 100644
--- a/Zotlabs/Lib/ThreadStream.php
+++ b/Zotlabs/Lib/ThreadStream.php
@@ -77,7 +77,7 @@ class ThreadStream {
$this->reload = $_SESSION['return_url'];
break;
case 'display':
- // in this mode we set profile_owner after initialisation (from conversation()) and then
+ // in this mode we set profile_owner after initialisation (from conversation()) and then
// pull some trickery which allows us to re-invoke this function afterward
// it's an ugly hack so @FIXME
$this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments');
@@ -170,14 +170,14 @@ class ThreadStream {
* Only add things that will be displayed
*/
-
+
if(($item->get_data_value('id') != $item->get_data_value('parent')) && (activity_match($item->get_data_value('verb'),ACTIVITY_LIKE) || activity_match($item->get_data_value('verb'),ACTIVITY_DISLIKE))) {
return false;
}
$item->set_commentable(false);
$ob_hash = (($this->observer) ? $this->observer['xchan_hash'] : '');
-
+
if(! comments_are_now_closed($item->get_data())) {
if(($item->get_data_value('author_xchan') === $ob_hash) || ($item->get_data_value('owner_xchan') === $ob_hash))
$item->set_commentable(true);
@@ -194,7 +194,7 @@ class ThreadStream {
}
if($this->mode === 'pubstream' && (! local_channel())) {
$item->set_commentable(false);
- }
+ }
$item->set_conversation($this);
diff --git a/Zotlabs/Module/Acl.php b/Zotlabs/Module/Acl.php
index 862a97bdc..7373de899 100644
--- a/Zotlabs/Module/Acl.php
+++ b/Zotlabs/Module/Acl.php
@@ -222,6 +222,7 @@ class Acl extends \Zotlabs\Web\Controller {
WHERE (abook_channel = %d $extra_channels_sql) AND abook_blocked = 0 and abook_pending = 0 and xchan_deleted = 0 $sql_extra2 order by $order_extra2 xchan_name asc" ,
intval(local_channel())
);
+
if($r2)
$r = array_merge($r2,$r);
@@ -282,13 +283,12 @@ class Acl extends \Zotlabs\Web\Controller {
}
}
elseif($type == 'm') {
-
$r = array();
$z = q("SELECT xchan_hash as hash, xchan_name as name, xchan_network as net, xchan_addr as nick, xchan_photo_s as micro, xchan_url as url
FROM abook left join xchan on abook_xchan = xchan_hash
WHERE abook_channel = %d
and xchan_deleted = 0
- and xchan_network IN ('zot', 'diaspora', 'friendica-over-diaspora')
+ and not xchan_network IN ('rss', 'anon', 'unknown')
$sql_extra3
ORDER BY xchan_name ASC ",
intval(local_channel())
diff --git a/Zotlabs/Module/Home.php b/Zotlabs/Module/Home.php
index 2bfab986f..315d05af6 100644
--- a/Zotlabs/Module/Home.php
+++ b/Zotlabs/Module/Home.php
@@ -40,7 +40,7 @@ class Home extends Controller {
if (!$dest)
$dest = get_config('system', 'startpage');
if (!$dest)
- $dest = z_root() . '/network';
+ $dest = z_root() . '/hq';
goaway($dest);
}
diff --git a/Zotlabs/Module/Hq.php b/Zotlabs/Module/Hq.php
index a2c4100ad..a8d321b24 100644
--- a/Zotlabs/Module/Hq.php
+++ b/Zotlabs/Module/Hq.php
@@ -1,6 +1,10 @@
<?php
namespace Zotlabs\Module;
+use App;
+use Zotlabs\Widget\Messages;
+
+
require_once("include/bbcode.php");
require_once('include/security.php');
require_once('include/conversation.php');
@@ -14,23 +18,7 @@ class Hq extends \Zotlabs\Web\Controller {
if(! local_channel())
return;
- \App::$profile_uid = local_channel();
- }
-
- function post() {
-
- if(!local_channel())
- return;
-
- if($_REQUEST['notify_id']) {
- q("update notify set seen = 1 where id = %d and uid = %d",
- intval($_REQUEST['notify_id']),
- intval(local_channel())
- );
- }
-
- killme();
-
+ App::$profile_uid = local_channel();
}
function get($update = 0, $load = false) {
@@ -42,8 +30,9 @@ class Hq extends \Zotlabs\Web\Controller {
$item_hash = argv(1);
}
- if($_REQUEST['mid'])
+ if(isset($_REQUEST['mid'])) {
$item_hash = $_REQUEST['mid'];
+ }
$item_normal = item_normal();
$item_normal_update = item_normal_update();
@@ -55,7 +44,6 @@ class Hq extends \Zotlabs\Web\Controller {
ORDER BY created DESC LIMIT 1",
intval(local_channel())
);
-
if($r[0]['mid']) {
$item_hash = 'b64.' . base64url_encode($r[0]['mid']);
}
@@ -96,7 +84,7 @@ class Hq extends \Zotlabs\Web\Controller {
}
if(! $update) {
- $channel = \App::get_channel();
+ $channel = App::get_channel();
$channel_acl = [
'allow_cid' => $channel['channel_allow_cid'],
@@ -116,7 +104,7 @@ class Hq extends \Zotlabs\Web\Controller {
'bang' => '',
'visitor' => true,
'profile_uid' => local_channel(),
- 'return_path' => 'hq',
+ 'return_path' => 'hq', //(($dm_mode) ? 'dm' : 'hq'),
'expanded' => true,
'editor_autocomplete' => true,
'bbco_autocomplete' => 'bbcode',
@@ -127,8 +115,6 @@ class Hq extends \Zotlabs\Web\Controller {
$o = replace_macros(get_markup_template("hq.tpl"),
[
- '$no_messages' => (($target_item) ? false : true),
- '$no_messages_label' => [ t('Welcome to Hubzilla!'), t('You have got no unseen posts...') ],
'$editor' => status_editor($a,$x,false,'Hq')
]
);
@@ -137,6 +123,7 @@ class Hq extends \Zotlabs\Web\Controller {
if(! $update && ! $load) {
+ //$app = (($dm_mode) ? 'Direct Messages' : 'Start');
nav_set_selected('HQ');
if($target_item) {
@@ -153,11 +140,11 @@ class Hq extends \Zotlabs\Web\Controller {
$o .= '<div id="live-hq"></div>' . "\r\n";
$o .= "<script> var profile_uid = " . local_channel()
- . "; var netargs = '?f='; var profile_page = " . \App::$pager['page'] . ";</script>\r\n";
+ . "; var netargs = '?f='; var profile_page = " . App::$pager['page'] . ";</script>\r\n";
- \App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"),[
+ App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"),[
'$baseurl' => z_root(),
- '$pgtype' => 'hq',
+ '$pgtype' => 'hq', //(($dm_mode) ? 'dm' : 'hq'),
'$uid' => local_channel(),
'$gid' => '0',
'$cid' => '0',
@@ -267,4 +254,17 @@ class Hq extends \Zotlabs\Web\Controller {
}
+ function post() {
+ if (!local_channel())
+ return;
+
+ $options['offset'] = $_REQUEST['offset'];
+ $options['dm'] = $_REQUEST['dm'];
+ $options['type'] = $_REQUEST['type'];
+
+ $ret = Messages::get_messages_page($options);
+
+ json_return_and_die($ret);
+ }
+
}
diff --git a/Zotlabs/Module/Network.php b/Zotlabs/Module/Network.php
index a21095940..be954cbfd 100644
--- a/Zotlabs/Module/Network.php
+++ b/Zotlabs/Module/Network.php
@@ -388,6 +388,10 @@ class Network extends \Zotlabs\Web\Controller {
if ($dm) {
$sql_extra .= " AND item_private = 2 ";
}
+ else {
+ $sql_extra .= " AND item_private IN (0, 1) ";
+ }
+
if($conv) {
$item_thread_top = '';
diff --git a/Zotlabs/Module/Notify.php b/Zotlabs/Module/Notify.php
index cffcc8099..5bfcec4f7 100644
--- a/Zotlabs/Module/Notify.php
+++ b/Zotlabs/Module/Notify.php
@@ -8,7 +8,15 @@ class Notify extends \Zotlabs\Web\Controller {
function init() {
if(! local_channel())
return;
-
+
+ if($_REQUEST['notify_id']) {
+ q("update notify set seen = 1 where id = %d and uid = %d",
+ intval($_REQUEST['notify_id']),
+ intval(local_channel())
+ );
+ killme();
+ }
+
if(argc() > 2 && argv(1) === 'view' && intval(argv(2))) {
$r = q("select * from notify where id = %d and uid = %d limit 1",
intval(argv(2)),
@@ -29,24 +37,24 @@ class Notify extends \Zotlabs\Web\Controller {
}
goaway(z_root());
}
-
-
+
+
}
-
-
+
+
function get() {
if(! local_channel())
return login();
-
+
$notif_tpl = get_markup_template('notifications.tpl');
-
+
$not_tpl = get_markup_template('notify.tpl');
require_once('include/bbcode.php');
-
+
$r = q("SELECT * from notify where uid = %d and seen = 0 order by created desc",
intval(local_channel())
);
-
+
if($r) {
foreach ($r as $it) {
$notif_content .= replace_macros($not_tpl,array(
@@ -56,18 +64,18 @@ class Notify extends \Zotlabs\Web\Controller {
'$item_when' => relative_date($it['created'])
));
}
- }
+ }
else {
$notif_content .= t('No more system notifications.');
}
-
+
$o .= replace_macros($notif_tpl,array(
'$notif_header' => t('System Notifications'),
'$tabs' => '', // $tabs,
'$notif_content' => $notif_content,
));
-
+
return $o;
-
+
}
}
diff --git a/Zotlabs/Module/Sse_bs.php b/Zotlabs/Module/Sse_bs.php
index 3316a6beb..ca86f4f1f 100644
--- a/Zotlabs/Module/Sse_bs.php
+++ b/Zotlabs/Module/Sse_bs.php
@@ -37,7 +37,7 @@ class Sse_bs extends Controller {
self::$vnotify = get_pconfig(self::$uid, 'system', 'vnotify', -1);
self::$evdays = intval(get_pconfig(self::$uid, 'system', 'evdays'));
- self::$limit = 50;
+ self::$limit = 30;
self::$offset = 0;
self::$xchans = '';
@@ -57,8 +57,6 @@ class Sse_bs extends Controller {
if(intval(argv(2)) > 0)
self::$offset = argv(2);
- else
- $_SESSION['sse_loadtime'] = datetime_convert();
$network = false;
$dm = false;
@@ -178,7 +176,7 @@ class Sse_bs extends Controller {
$sql_extra2
ORDER BY created DESC LIMIT $limit OFFSET $offset",
intval(self::$uid),
- dbescdate($_SESSION['sse_loadtime']),
+ dbescdate($_SESSION['page_loadtime']),
dbesc(self::$ob_hash)
);
@@ -254,7 +252,7 @@ class Sse_bs extends Controller {
$sql_extra2
ORDER BY created DESC LIMIT $limit OFFSET $offset",
intval(self::$uid),
- dbescdate($_SESSION['sse_loadtime']),
+ dbescdate($_SESSION['page_loadtime']),
dbesc(self::$ob_hash)
);
@@ -330,7 +328,7 @@ class Sse_bs extends Controller {
$sql_extra2
ORDER BY created DESC LIMIT $limit OFFSET $offset",
intval(self::$uid),
- dbescdate($_SESSION['sse_loadtime']),
+ dbescdate($_SESSION['page_loadtime']),
dbesc(self::$ob_hash)
);
@@ -417,7 +415,7 @@ class Sse_bs extends Controller {
$sql_extra2
ORDER BY created DESC LIMIT $limit OFFSET $offset",
intval($sys['channel_id']),
- dbescdate($_SESSION['sse_loadtime']),
+ dbescdate($_SESSION['page_loadtime']),
dbesc(self::$ob_hash),
dbescdate($_SESSION['static_loadtime'])
);
diff --git a/Zotlabs/Web/WebServer.php b/Zotlabs/Web/WebServer.php
index de0d5a883..685f75897 100644
--- a/Zotlabs/Web/WebServer.php
+++ b/Zotlabs/Web/WebServer.php
@@ -39,6 +39,8 @@ class WebServer {
register_shutdown_function('session_write_close');
}
+ $_SESSION['page_loadtime'] = datetime_convert();
+
/**
* Language was set earlier, but we can over-ride it in the session.
* We have to do it here because the session was just now opened.
diff --git a/Zotlabs/Widget/Hq_controls.php b/Zotlabs/Widget/Hq_controls.php
index 0caa54a1a..781e2a0bd 100644
--- a/Zotlabs/Widget/Hq_controls.php
+++ b/Zotlabs/Widget/Hq_controls.php
@@ -9,17 +9,32 @@ class Hq_controls {
if (! local_channel())
return;
+ $entries = [
+ 'toggle_editor' => [
+ 'label' => t('Toggle post editor'),
+ 'id' => 'jot-toggle',
+ 'href' => '#',
+ 'class' => 'btn btn-outline-primary',
+ 'type' => 'button',
+ 'icon' => 'pencil',
+ 'extra' => 'data-toggle="button"'
+ ]
+ ];
+
+
+ $entries['toggle_notes'] = [
+ 'label' => t('Toggle personal notes'),
+ 'id' => 'notes-toggle',
+ 'href' => '#',
+ 'class' => 'btn btn-outline-primary',
+ 'type' => 'button',
+ 'icon' => 'sticky-note-o',
+ 'extra' => 'data-toggle="button"'
+ ];
+
return replace_macros(get_markup_template('hq_controls.tpl'),
[
- '$title' => t('HQ Control Panel'),
- '$menu' => [
- 'create' => [
- 'label' => t('Create a new post'),
- 'id' => 'jot-toggle',
- 'href' => '#',
- 'class' => ''
- ]
- ]
+ '$entries' => $entries
]
);
}
diff --git a/Zotlabs/Widget/Messages.php b/Zotlabs/Widget/Messages.php
new file mode 100644
index 000000000..6117bfa73
--- /dev/null
+++ b/Zotlabs/Widget/Messages.php
@@ -0,0 +1,181 @@
+<?php
+
+namespace Zotlabs\Widget;
+
+use App;
+use Zotlabs\Lib\IConfig;
+
+class Messages {
+
+ public static function widget($arr) {
+ if (!local_channel())
+ return EMPTY_STR;
+
+ $o = '';
+ $page = self::get_messages_page($options);
+
+ if (!$page['entries'])
+ return $o;
+
+ $tpl = get_markup_template('messages_widget.tpl');
+ $o .= replace_macros($tpl, [
+ '$entries' => $page['entries'],
+ '$offset' => $page['offset'],
+ '$feature_star' => feature_enabled(local_channel(), 'star_posts'),
+ '$strings' => [
+ 'messages_title' => t('Public and restricted messages'),
+ 'direct_messages_title' => t('Direct messages'),
+ 'starred_messages_title' => t('Starred messages'),
+ 'loading' => t('Loading')
+ ]
+ ]);
+
+ return $o;
+ }
+
+ public static function get_messages_page($options) {
+ if (!local_channel())
+ return;
+
+ if ($options['offset'] == -1) {
+ return;
+ }
+
+ $channel = App::get_channel();
+ $item_normal = item_normal();
+ $entries = [];
+ $limit = 30;
+
+ $offset = 0;
+ if ($options['offset']) {
+ $offset = intval($options['offset']);
+ }
+
+ $loadtime = (($offset) ? $_SESSION['page_loadtime'] : datetime_convert());
+
+ switch($options['type']) {
+ case 'direct':
+ $type_sql = ' AND item_private = 2 ';
+ break;
+ case 'starred':
+ $type_sql = ' AND item_starred = 1 ';
+ break;
+ default:
+ $type_sql = ' AND item_private IN (0, 1) ';
+ }
+
+ $items = q("SELECT * FROM item WHERE uid = %d
+ AND created <= '%s'
+ $type_sql
+ AND item_thread_top = 1
+ $item_normal
+ ORDER BY created DESC
+ LIMIT $limit OFFSET $offset",
+ intval(local_channel()),
+ dbescdate($loadtime)
+ );
+
+ xchan_query($items, false);
+
+ $i = 0;
+
+ foreach($items as $item) {
+
+ $info = '';
+ if ($options['type'] == 'direct') {
+ $info .= self::get_dm_recipients($channel, $item);
+ }
+
+ if($item['owner_xchan'] !== $item['author_xchan']) {
+ $info .= t('via') . ' ' . $item['owner']['xchan_name'];
+ }
+
+ $summary = $item['title'];
+ if (!$summary) {
+ $summary = $item['summary'];
+ }
+ if (!$summary) {
+ $summary = htmlentities(html2plain(bbcode($item['body']), 75, true), ENT_QUOTES, 'UTF-8', false);
+ }
+ if (!$summary) {
+ $summary = t('Sorry, there is no text preview available for this post');
+ }
+ $summary = substr_words($summary, 68);
+
+ switch(intval($item['item_private'])) {
+ case 1:
+ $icon = '<i class="fa fa-lock"></i>';
+ break;
+ case 2:
+ $icon = '<i class="fa fa-envelope-o"></i>';
+ break;
+ default:
+ $icon = '';
+ }
+
+ $entries[$i]['author_name'] = $item['author']['xchan_name'];
+ $entries[$i]['author_addr'] = (($item['author']['xchan_addr']) ? $item['author']['xchan_addr'] : $item['author']['xchan_url']);
+ $entries[$i]['info'] = $info;
+ $entries[$i]['created'] = datetime_convert('UTC', date_default_timezone_get(), $item['created']);
+ $entries[$i]['summary'] = $summary;
+ $entries[$i]['b64mid'] = gen_link_id($item['mid']);
+ $entries[$i]['href'] = z_root() . '/hq/' . gen_link_id($item['mid']);
+ $entries[$i]['icon'] = $icon;
+
+ $i++;
+ }
+
+ $result = [
+ 'offset' => ((count($entries) < $limit) ? -1 : intval($offset + $limit)),
+ 'entries' => $entries
+ ];
+
+ return $result;
+ }
+
+ public static function get_dm_recipients($channel, $item) {
+
+ if($channel['channel_hash'] === $item['owner']['xchan_hash']) {
+ // we are the owner, get the recipients from the item
+ $recips = expand_acl($item['allow_cid']);
+ if (is_array($recips)) {
+ array_unshift($recips, $item['owner']['xchan_hash']);
+ $column = 'xchan_hash';
+ }
+ }
+ else {
+ $recips = IConfig::Get($item, 'activitypub', 'recips');
+ if (isset($recips['to']) && is_array($recips['to'])) {
+ $recips = $recips['to'];
+ array_unshift($recips, $item['owner']['xchan_url']);
+ $column = 'xchan_url';
+ }
+ else {
+ $hookinfo = [
+ 'item' => $item,
+ 'recips' => null,
+ 'column' => ''
+ ];
+
+ call_hooks('direct_message_recipients', $hookinfo);
+
+ $recips = $hookinfo['recips'];
+ $column = $hookinfo['column'];
+ }
+ }
+
+ if(is_array($recips)) {
+ stringify_array_elms($recips, true);
+
+ $query_str = implode(',', $recips);
+ $xchans = dbq("SELECT DISTINCT xchan_name FROM xchan WHERE $column IN ($query_str)");
+
+ foreach($xchans as $xchan) {
+ $recipients .= $xchan['xchan_name'] . ', ';
+ }
+ }
+
+ return trim($recipients, ', ');
+ }
+
+}
diff --git a/Zotlabs/Widget/Notes.php b/Zotlabs/Widget/Notes.php
index 238008d81..b2c8eda86 100644
--- a/Zotlabs/Widget/Notes.php
+++ b/Zotlabs/Widget/Notes.php
@@ -10,9 +10,6 @@ class Notes {
if(! local_channel())
return EMPTY_STR;
- if(! Apps::system_app_installed(local_channel(), 'Notes'))
- return EMPTY_STR;
-
$text = get_pconfig(local_channel(),'notes','text');
$tpl = get_markup_template('notes.tpl');
@@ -21,7 +18,8 @@ class Notes {
'$banner' => t('Notes'),
'$text' => $text,
'$save' => t('Save'),
- '$app' => ((isset($arr['app'])) ? true : false)
+ '$app' => ((isset($arr['app'])) ? true : false),
+ '$hidden' => ((isset($arr['hidden'])) ? true : false)
));
return $o;
diff --git a/Zotlabs/Widget/Notifications.php b/Zotlabs/Widget/Notifications.php
index d59312148..56b1f9caa 100644
--- a/Zotlabs/Widget/Notifications.php
+++ b/Zotlabs/Widget/Notifications.php
@@ -161,11 +161,9 @@ class Notifications {
}
$o = replace_macros(get_markup_template('notifications_widget.tpl'), [
- '$module' => \App::$module,
'$notifications' => $notifications,
'$no_notifications' => t('Sorry, you have got no notifications at the moment'),
'$loading' => t('Loading'),
- '$startpage' => ($channel ? $channel['channel_startpage'] : '')
]);
return $o;
diff --git a/app/hq.apd b/app/hq.apd
new file mode 100644
index 000000000..b9e9f806f
--- /dev/null
+++ b/app/hq.apd
@@ -0,0 +1,6 @@
+version: 1
+url: $baseurl/hq
+requires: local_channel
+name: HQ
+photo: icon:user-circle-o
+categories: nav_pinned_app
diff --git a/boot.php b/boot.php
index 75b3c93b5..5d7941747 100644
--- a/boot.php
+++ b/boot.php
@@ -53,7 +53,7 @@ require_once('include/bbcode.php');
require_once('include/items.php');
define ( 'PLATFORM_NAME', 'hubzilla' );
-define ( 'STD_VERSION', '5.9.1' );
+define ( 'STD_VERSION', '5.9.4' );
define ( 'ZOT_REVISION', '6.0' );
define ( 'DB_UPDATE_VERSION', 1245 );
@@ -1232,7 +1232,8 @@ class App {
'$js_strings' => js_strings(),
'$zid' => get_my_address(),
'$channel_id' => self::$profile['uid'] ?? 0,
- '$auto_save_draft' => ((isset(self::$profile['uid']) && feature_enabled(self::$profile['uid'], 'auto_save_draft')) ? "true" : "false")
+ '$auto_save_draft' => ((isset(self::$profile['uid']) && feature_enabled(self::$profile['uid'], 'auto_save_draft')) ? "true" : "false"),
+ '$module' => App::$module
]
) . ((isset(self::$page['htmlhead'])) ? self::$page['htmlhead'] : '');
diff --git a/include/items.php b/include/items.php
index 15de6c730..7fa3a8e71 100644
--- a/include/items.php
+++ b/include/items.php
@@ -2494,7 +2494,7 @@ function send_status_notifications($post_id,$item) {
Enotify::submit(array(
- 'type' => NOTIFY_COMMENT,
+ 'type' => ((intval($item['item_private']) === 2) ? NOTIFY_MAIL : NOTIFY_COMMENT),
'from_xchan' => $item['author_xchan'],
'to_xchan' => $r[0]['channel_hash'],
'item' => $item,
diff --git a/include/text.php b/include/text.php
index 8dc5ee188..a0c2689af 100644
--- a/include/text.php
+++ b/include/text.php
@@ -3880,3 +3880,30 @@ function sanitize_text_field($str) {
return preg_replace('/\s+/S', ' ', $str);
}
+/**
+ * @brief shortens a string to $max_length without cutting off words
+ * @param string $str
+ * @param intval $max_length
+ * @param string $suffix (optional)
+
+ * @return string
+ */
+function substr_words($str, $max_length, $suffix = '...') {
+
+ if (strlen($str) > $max_length) {
+ $words = preg_split('/\s/', $str);
+ $ret = '';
+ $i = 0;
+ while (true) {
+ $length = (strlen($ret) + strlen($words[$i]));
+ if ($length > $max_length) {
+ break;
+ }
+ $ret .= " " . $words[$i];
+ ++$i;
+ }
+ $ret .= $suffix;
+ }
+
+ return (($ret) ? $ret : $str);
+}
diff --git a/view/css/conversation.css b/view/css/conversation.css
index 43bc96e57..dbb930fc7 100644
--- a/view/css/conversation.css
+++ b/view/css/conversation.css
@@ -34,6 +34,8 @@
#jot-pagetitle-wrap input,
#jot-customjotheaders-wrap {
padding: 0.5rem;
+ outline: none;
+
}
#jot-text-wrap {
@@ -55,6 +57,7 @@
padding: 0.5rem;
width: 100%;
display: inherit;
+ outline: none;
}
#profile-jot-text.jot-expanded {
diff --git a/view/css/mod_dm.css b/view/css/mod_dm.css
new file mode 100644
index 000000000..dde242d4e
--- /dev/null
+++ b/view/css/mod_dm.css
@@ -0,0 +1,3 @@
+#jot-popup {
+ display: none;
+}
diff --git a/view/css/widgets.css b/view/css/widgets.css
index 30e7e6972..a677bb457 100644
--- a/view/css/widgets.css
+++ b/view/css/widgets.css
@@ -38,15 +38,16 @@ li:hover .widget-nav-pills-icons {
margin-top: 10px;
}
-/* notes */
+/* notes */
#note-text {
border: 1px solid rgba(0,0,0,.125);
padding: 5px;
width: 100%;
- resize: none;
+ resize: vertical;
min-height: 250px;
- overflow: hidden;
+ overflow: auto;
+ outline: none;
}
/* saved searches */
diff --git a/view/js/autocomplete.js b/view/js/autocomplete.js
index c45c47518..f20c45982 100644
--- a/view/js/autocomplete.js
+++ b/view/js/autocomplete.js
@@ -162,8 +162,8 @@ function string2bb(element) {
if (typeof extra_channels === 'undefined') extra_channels = false;
// Autocomplete contacts
- contacts = {
- match: /(^|\s)(@\!*)([^ \n]{3,})$/,
+ channels = {
+ match: /(^(?=[^\!]{2})|\s)(@)([^ \n]{3,})$/,
index: 3,
cache: true,
search: function(term, callback) { contact_search(term, callback, backend_url, 'c', extra_channels, spinelement=false); },
@@ -171,6 +171,15 @@ function string2bb(element) {
template: contact_format
};
+ contacts = {
+ match: /(^|\s)(@\!)([^ \n]{3,})$/,
+ index: 3,
+ cache: true,
+ search: function(term, callback) { contact_search(term, callback, backend_url, 'm', extra_channels, spinelement=false); },
+ replace: editor_replace,
+ template: contact_format
+ };
+
// Autocomplete hashtags
tags = {
match: /(^|\s)(\#)([^ \n]{2,})$/,
@@ -202,7 +211,7 @@ function string2bb(element) {
maxCount: 100
}
});
- textcomplete.register([contacts,smilies,tags]);
+ textcomplete.register([channels,contacts,smilies,tags]);
});
};
})( jQuery );
diff --git a/view/js/main.js b/view/js/main.js
index b1cba33af..a3f238a45 100644
--- a/view/js/main.js
+++ b/view/js/main.js
@@ -26,13 +26,6 @@ var followUpPageLoad = false;
var window_needs_alert = true;
var expanded_items = [];
-var sse_bs_active = false;
-var sse_offset = 0;
-var sse_type;
-var sse_partial_result = false;
-var sse_rmids = [];
-var sse_fallback_interval;
-
var page_cache = {};
// take care of tab/window reloads on channel change
@@ -93,7 +86,7 @@ $(document).ready(function() {
if (tao.zin.syslc == '') {
$('.zinlcx').append(tao.zin.axim);
$.ajax({
- type: 'POST', url: 'lang',
+ type: 'POST', url: 'lang',
data: { zinlc: '??' }
}).done( function(re) {
tao.zin.re = JSON.parse(re);
@@ -117,7 +110,7 @@ $(document).ready(function() {
tao.zin.me = e.target.id.substr(5);
$('#right_aside_wrapper').append(tao.zin.axim);
$.ajax({
- type: 'POST', url: 'lang',
+ type: 'POST', url: 'lang',
data: { zinlc: tao.zin.me }
}).done( function(re) {
tao.zin.re = JSON.parse(re);
@@ -161,110 +154,59 @@ $(document).ready(function() {
jQuery.timeago.settings.allowFuture = true;
- if(sse_enabled) {
- if(typeof(window.SharedWorker) === 'undefined') {
- // notifications with multiple tabs open will not work very well in this scenario
- var evtSource = new EventSource('/sse');
-
- evtSource.addEventListener('notifications', function(e) {
- var obj = JSON.parse(e.data);
- sse_handleNotifications(obj, false, false);
- }, false);
+ $(document).on('click', '.notification, .message', function(e) {
+ let b64mid = $(this).data('b64mid');
+ let notify_id = $(this).data('notify_id');
+ let path = $(this)[0].pathname.split('/')[1];
+ let stateObj = { b64mid: b64mid };
+ let singlethread_modules = ['display', 'hq', 'dm'];
+ let redirect_modules = ['display', 'notify'];
- document.addEventListener('visibilitychange', function() {
- if (!document.hidden) {
- sse_offset = 0;
- sse_bs_init();
- }
- }, false);
+ if(! b64mid && ! notify_id)
+ return;
+ if(redirect_modules.indexOf(path) !== -1) {
+ path = 'hq';
}
- else {
- var myWorker = new SharedWorker('/view/js/sse_worker.js', localUser);
- myWorker.port.onmessage = function(e) {
- obj = e.data;
- console.log(obj);
- sse_handleNotifications(obj, false, false);
- }
-
- myWorker.onerror = function(e) {
- myWorker.port.close();
- }
+ if(notify_id != null) {
+ $.ajax({
+ type: 'post',
+ url: 'notify',
+ data: {
+ 'notify_id' : notify_id
+ },
+ async: ((module !== path) ? false : true)
+ });
+ }
- myWorker.port.start();
+ if (module !== path) {
+ e.preventDefault();
+ window.location.href = path + '/' + b64mid;
}
- }
- else {
- if (!document.hidden)
- sse_fallback_interval = setInterval(sse_fallback, updateInterval);
+ else {
- document.addEventListener('visibilitychange', function() {
- if (document.hidden) {
- clearInterval(sse_fallback_interval);
- }
- else {
- sse_offset = 0;
- sse_bs_init();
- sse_fallback_interval = setInterval(sse_fallback, updateInterval);
+ if (singlethread_modules.indexOf(module) !== -1) {
+ history.pushState(stateObj, '', module + '/' + b64mid);
+ $('.message').removeClass('active');
+ $('[data-b64mid="' + b64mid + '"].message').addClass('active');
}
- }, false);
- }
-
- $('.notification-link').on('click', { replace: true, followup: false }, sse_bs_notifications);
-
- $('.notification-filter').on('keypress', function(e) {
- if(e.which == 13) { // enter
- this.blur();
- sse_offset = 0;
- $("#nav-" + sse_type + "-menu").html('');
- $("#nav-" + sse_type + "-loading").show();
-
- var cn_val = $('#cn-' + sse_type + '-input').length ? $('#cn-' + sse_type + '-input').val().toString().toLowerCase() : '';
+ if (b64mid) {
- $.get('/sse_bs/' + sse_type + '/' + sse_offset + '?nquery=' + encodeURIComponent(cn_val), function(obj) {
- console.log('sse: bootstraping ' + sse_type);
- console.log(obj);
+ e.preventDefault();
- sse_bs_active = false;
- sse_partial_result = true;
- sse_offset = obj[sse_type].offset;
- if(sse_offset < 0)
- $("#nav-" + sse_type + "-loading").hide();
-
- sse_handleNotifications(obj, true, false);
-
- });
+ if(! page_load) {
+ prepareLiveUpdate(b64mid, notify_id);
+ }
+ }
}
});
- $('.notifications-textinput-clear').on('click', function(e) {
- if(! sse_partial_result)
- return;
-
- $("#nav-" + sse_type + "-menu").html('');
- $("#nav-" + sse_type + "-loading").show();
- $.get('/sse_bs/' + sse_type, function(obj) {
- console.log('sse: bootstraping ' + sse_type);
- console.log(obj);
-
- sse_bs_active = false;
- sse_partial_result = false;
- sse_offset = obj[sse_type].offset;
- if(sse_offset < 0)
- $("#nav-" + sse_type + "-loading").hide();
-
- sse_handleNotifications(obj, true, false);
-
- });
- });
-
- $('.notification-content').on('scroll', function() {
- if(this.scrollTop > this.scrollHeight - this.clientHeight - (this.scrollHeight/7)) {
- sse_bs_notifications(sse_type, false, true);
- }
- });
+ window.onpopstate = function(e) {
+ if(e.state !== null && e.state.b64mid !== bParam_mid)
+ prepareLiveUpdate(e.state.b64mid, '');
+ };
//mod_mail only
$(".mail-conv-detail .autotime").timeago();
@@ -967,6 +909,20 @@ function updateInit() {
}
}
+function prepareLiveUpdate(b64mid, notify_id) {
+ $(document).scrollTop(0);
+ $('.thread-wrapper').remove();
+ bParam_mid = b64mid;
+ mode = 'replace';
+ page_load = true;
+ if (module == 'hq') {
+ liveUpdate(notify_id);
+ }
+ if (module == 'display'|| module == 'dm') {
+ liveUpdate();
+ }
+}
+
function liveUpdate(notify_id) {
if(typeof profile_uid === 'undefined') profile_uid = false; /* Should probably be unified with channelId defined in head.tpl */
@@ -1052,7 +1008,7 @@ function liveUpdate(notify_id) {
if(typeof notify_id !== 'undefined' && notify_id !== 'undefined') {
$.post(
- "hq",
+ "notify",
{
"notify_id" : notify_id
}
@@ -1760,288 +1716,6 @@ function zid(s) {
return s;
}
-function sse_bs_init() {
- if(sessionStorage.getItem('notification_open') !== null || typeof sse_type !== 'undefined' ) {
- if(typeof sse_type === 'undefined')
- sse_type = sessionStorage.getItem('notification_open');
-
- $("#nav-" + sse_type + "-sub").addClass('show');
- sse_bs_notifications(sse_type, true, false);
- }
- else {
- sse_bs_counts();
- }
-}
-
-function sse_bs_counts() {
- if(sse_bs_active)
- return;
-
- sse_bs_active = true;
-
- $.ajax({
- type: 'post',
- url: '/sse_bs',
- data: { sse_rmids }
- }).done( function(obj) {
- console.log(obj);
- sse_bs_active = false;
- sse_rmids = [];
- sse_handleNotifications(obj, true, false);
- });
-}
-
-function sse_bs_notifications(e, replace, followup) {
-
- if(sse_bs_active)
- return;
-
-
- var manual = false;
-
- if(typeof replace === 'undefined')
- replace = e.data.replace;
-
- if(typeof followup === 'undefined')
- followup = e.data.followup;
-
- if(typeof e === 'string') {
- sse_type = e;
- }
- else {
- manual = true;
- sse_offset = 0;
- sse_type = e.target.dataset.sse_type;
- }
-
- if(typeof sse_type === 'undefined')
- return;
-
- if(followup || !manual || !($('#nav-' + sse_type + '-sub').hasClass('collapse') && $('#nav-' + sse_type + '-sub').hasClass('show'))) {
-
- if(sse_offset >= 0) {
- $("#nav-" + sse_type + "-loading").show();
- }
-
- sessionStorage.setItem('notification_open', sse_type);
- if(sse_offset !== -1 || replace) {
-
- var cn_val = (($('#cn-' + sse_type + '-input').length && sse_partial_result) ? $('#cn-' + sse_type + '-input').val().toString().toLowerCase() : '');
-
- $("#nav-" + sse_type + "-loading").show();
-
- sse_bs_active = true;
-
- $.ajax({
- type: 'post',
- url: '/sse_bs/' + sse_type + '/' + sse_offset,
- nquery: encodeURIComponent(cn_val),
- data: { sse_rmids }
- }).done(function(obj) {
- console.log('sse: bootstraping ' + sse_type);
- console.log(obj);
- sse_bs_active = false;
- sse_rmids = [];
- $("#nav-" + sse_type + "-loading").hide();
- sse_offset = obj[sse_type].offset;
- sse_handleNotifications(obj, replace, followup);
- });
- }
- else
- $("#nav-" + sse_type + "-loading").hide();
-
- }
- else {
- sessionStorage.removeItem('notification_open');
- }
-}
-
-function sse_handleNotifications(obj, replace, followup) {
-
- var primary_notifications = ['dm', 'home', 'intros', 'register', 'notify', 'files'];
- var secondary_notifications = ['network', 'forums', 'all_events', 'pubs'];
- var all_notifications = primary_notifications.concat(secondary_notifications);
-
- all_notifications.forEach(function(type, index) {
- if(typeof obj[type] === typeof undefined)
- return true;
-
- if(obj[type].count) {
- $('.' + type + '-button').fadeIn();
- if(replace || followup)
- $('.' + type + '-update').html(Number(obj[type].count));
- else
- $('.' + type + '-update').html(Number(obj[type].count) + Number($('.' + type + '-update').html()));
- }
- else {
- $('.' + type + '-update').html('0');
- $('.' + type + '-button').fadeOut(function() {
- sse_setNotificationsStatus();
- });
- }
- if(obj[type].notifications.length)
- sse_handleNotificationsItems(type, obj[type].notifications, replace, followup);
- });
-
- sse_setNotificationsStatus();
-
- // notice and info
- $.jGrowl.defaults.closerTemplate = '<div>[ ' + aStr.closeAll + ']</div>';
-
- if(obj.notice) {
- $(obj.notice.notifications).each(function() {
- $.jGrowl(this, { sticky: true, theme: 'notice' });
- });
- }
-
- if(obj.info) {
- $(obj.info.notifications).each(function(){
- $.jGrowl(this, { sticky: false, theme: 'info', life: 10000 });
- });
- }
-
- // load more notifications if visible notifications count becomes low
- if(sse_type && sse_offset != -1 && $('#nav-' + sse_type + '-menu').children().length <= 20) {
- sse_offset = 0;
- sse_bs_notifications(sse_type, false, true);
- }
-
-
-}
-
-function sse_handleNotificationsItems(notifyType, data, replace, followup) {
-
- var notifications_tpl = ((notifyType == 'forums') ? decodeURIComponent($("#nav-notifications-forums-template[rel=template]").html().replace('data-src', 'src')) : decodeURIComponent($("#nav-notifications-template[rel=template]").html().replace('data-src', 'src')));
- var notify_menu = $("#nav-" + notifyType + "-menu");
- var notify_loading = $("#nav-" + notifyType + "-loading");
- var notify_count = $("." + notifyType + "-update");
-
- if(replace && !followup) {
- notify_menu.html('');
- notify_loading.hide();
- }
-
- $(data).each(function() {
-
- // do not add a notification if it is already present
- if($('#nav-' + notifyType + '-menu .notification[data-b64mid=\'' + this.b64mid + '\']').length)
- return true;
-
- html = notifications_tpl.format(this.notify_link,this.photo,this.name,this.addr,this.message,this.when,this.hclass,this.b64mid,this.notify_id,this.thread_top,this.unseen,this.private_forum, encodeURIComponent(this.mids), this.body);
- notify_menu.append(html);
- });
-
- if(!replace && !followup) {
- $("#nav-" + notifyType + "-menu .notification").sort(function(a,b) {
- a = new Date(a.dataset.when);
- b = new Date(b.dataset.when);
- return a > b ? -1 : a < b ? 1 : 0;
- }).appendTo('#nav-' + notifyType + '-menu');
- }
-
- $(document.body).trigger("sticky_kit:recalc");
- $("#nav-" + notifyType + "-menu .notifications-autotime").timeago();
-
- if($('#tt-' + notifyType + '-only').hasClass('active'))
- $('#nav-' + notifyType + '-menu [data-thread_top=false]').addClass('tt-filter-active');
-
- if($('#cn-' + notifyType + '-input').length) {
- var filter = $('#cn-' + notifyType + '-input').val().toString().toLowerCase();
- if(filter) {
- filter = filter.indexOf('%') == 0 ? filter.substring(1) : filter;
-
- $('#nav-' + notifyType + '-menu .notification').each(function(i, el) {
- var cn = $(el).data('contact_name').toString().toLowerCase();
- var ca = $(el).data('contact_addr').toString().toLowerCase();
- if(cn.indexOf(filter) === -1 && ca.indexOf(filter) === -1)
- $(el).addClass('cn-filter-active');
- else
- $(el).removeClass('cn-filter-active');
- });
- }
- }
-}
-
-function sse_updateNotifications(type, mid) {
-
- if(type === 'pubs')
- return true;
-
- if(type === 'notify' && (mid !== bParam_mid || sse_type !== 'notify'))
- return true;
-/*
- var count = Number($('.' + type + '-update').html());
-
- count--;
-
- if(count < 1) {
- $('.' + type + '-update').html(count);
- $('.' + type + '-button').fadeOut(function() {
- sse_setNotificationsStatus();
- });
- }
- else {
- $('.' + type + '-update').html(count);
- }
-*/
-
- $('#nav-' + type + '-menu .notification[data-b64mid=\'' + mid + '\']').fadeOut(function() {
- this.remove();
- });
-
-}
-
-function sse_setNotificationsStatus() {
- var primary_notifications = ['dm', 'home', 'intros', 'register', 'notify', 'files'];
- var secondary_notifications = ['network', 'forums', 'all_events', 'pubs'];
- var all_notifications = primary_notifications.concat(secondary_notifications);
-
- var primary_available = false;
- var any_available = false;
-
- all_notifications.forEach(function(type, index) {
- if($('.' + type + '-button').css('display') == 'block') {
- any_available = true;
- if(primary_notifications.indexOf(type) > -1)
- primary_available = true;
- }
- });
-
- if(primary_available) {
- $('.notifications-btn-icon').removeClass('fa-exclamation-circle');
- $('.notifications-btn-icon').addClass('fa-exclamation-triangle');
- }
- else {
- $('.notifications-btn-icon').removeClass('fa-exclamation-triangle');
- $('.notifications-btn-icon').addClass('fa-exclamation-circle');
- }
-
- if(any_available) {
- $('.notifications-btn').css('opacity', 1);
- $('#no_notifications').hide();
- $('#notifications').show();
- }
- else {
- $('.notifications-btn').css('opacity', 0.5);
- $('#navbar-collapse-1').removeClass('show');
- $('#no_notifications').show();
- $('#notifications').hide();
- }
-
-}
-
-function sse_fallback() {
- $.get('/sse', function(obj) {
- if(! obj)
- return;
-
- console.log('sse fallback');
- console.log(obj);
-
- sse_handleNotifications(obj, false, false);
- });
-}
-
function makeid(length) {
var result = '';
var characters = 'abcdef0123456789';
@@ -2051,3 +1725,4 @@ function makeid(length) {
}
return result;
}
+
diff --git a/view/js/mod_hq.js b/view/js/mod_hq.js
index b321382bd..ddcde4fcd 100644
--- a/view/js/mod_hq.js
+++ b/view/js/mod_hq.js
@@ -1,10 +1,20 @@
-$(document).on('click', '#jot-toggle', function(e) {
- e.preventDefault();
- e.stopPropagation();
-
- $(this).toggleClass('active');
- $(window).scrollTop(0);
- $('#jot-popup').toggle();
- $('#profile-jot-text').focus();
+$(document).ready(function() {
+
+ $(document).on('click', '#jot-toggle', function(e) {
+ e.preventDefault();
+ e.stopPropagation();
+ $(window).scrollTop(0);
+ $('#jot-popup').toggle();
+ $('#profile-jot-text').focus();
+
+ });
+
+ $(document).on('click', '#notes-toggle', function(e) {
+ e.preventDefault();
+ e.stopPropagation();
+ $(window).scrollTop(0);
+ $('#personal-notes').toggleClass('d-none');
+ $('#note-text').focus();
+ });
});
diff --git a/view/pdl/mod_hq.pdl b/view/pdl/mod_hq.pdl
index 1bcdb2c65..348b6c525 100644
--- a/view/pdl/mod_hq.pdl
+++ b/view/pdl/mod_hq.pdl
@@ -1,6 +1,14 @@
[region=aside]
+[widget=messages][/widget]
[widget=hq_controls][/widget]
[/region]
+[region=content]
+[widget=notes]
+ [var=hidden]1[/var]
+ [var=app]1[/var]
+[/widget]
+$content
+[/region]
[region=right_aside]
[widget=notifications][/widget]
[widget=newmember][/widget]
diff --git a/view/tpl/head.tpl b/view/tpl/head.tpl
index ecb30afd3..bf61a7ee4 100644
--- a/view/tpl/head.tpl
+++ b/view/tpl/head.tpl
@@ -17,6 +17,7 @@
var channelId = {{if $channel_id}}{{$channel_id}}{{else}}false{{/if}};{{* Used in e.g. autocomplete *}}
var preloadImages = {{$preload_images}};
var auto_save_draft = {{$auto_save_draft}};
+ {{if $module}}var module = '{{$module}}';{{/if}}
</script>
diff --git a/view/tpl/hq_controls.tpl b/view/tpl/hq_controls.tpl
index d7f6d436f..f3860a949 100644
--- a/view/tpl/hq_controls.tpl
+++ b/view/tpl/hq_controls.tpl
@@ -1,8 +1,7 @@
-<div class="widget">
- <h3>{{$title}}</h3>
- <ul class="nav nav-pills flex-column">
- {{foreach $menu as $m}}
- <li class="nav-item"><a href="{{$m.href}}" id="{{$m.id}}" class="nav-link{{if $m.class}} {{$m.class}}{{/if}}">{{$m.label}}</a></li>
- {{/foreach}}
- </ul>
+<div class="d-grid gap-2 mb-3">
+ {{foreach $entries as $e}}
+ <button id="{{$e.id}}" class="{{$e.class}} rounded-circle" type="{{$e.type}}" title="{{$e.label}}"{{if $extra}} {{$extra}}{{/if}}>
+ {{if $e.icon}}<i class="fa fa-{{$e.icon}}"></i>{{/if}}
+ </button>
+ {{/foreach}}
</div>
diff --git a/view/tpl/messages_widget.tpl b/view/tpl/messages_widget.tpl
new file mode 100644
index 000000000..7ba02e78e
--- /dev/null
+++ b/view/tpl/messages_widget.tpl
@@ -0,0 +1,123 @@
+<ul class="nav nav-tabs nav-fill">
+ <li class="nav-item">
+ <a class="nav-link active messages-type" href="#" title="{{$strings.messages_title}}" data-messages_type="">
+ <i class="fa fa-fw fa-comment-o"></i>
+ </a>
+ </li>
+ <li class="nav-item">
+ <a class="nav-link messages-type" href="#" title="{{$strings.direct_messages_title}}" data-messages_type="direct">
+ <i class="fa fa-fw fa-envelope-o"></i>
+ </a>
+ </li>
+ {{if $feature_star}}
+ <li class="nav-item">
+ <a class="nav-link messages-type" href="#" title="{{$strings.starred_messages_title}}" data-messages_type="starred">
+ <i class="fa fa-fw fa-star"></i>
+ </a>
+ </li>
+ {{/if}}
+</ul>
+<div id="messages-widget" class="border border-top-0 overflow-auto mb-3" style="height: 70vh;">
+ <div id="messages-template" rel="template" class="d-none">
+ <a href="{6}" class="list-group-item list-group-item-action message" data-b64mid="{0}">
+ <div class="d-flex w-100 justify-content-between">
+ <div class="mb-1 text-truncate" title="{5}">
+ {7}
+ <strong>{4}</strong>
+ </div>
+ <small class="messages-timeago text-nowrap" title="{1}"></small>
+ </div>
+ <div class="mb-1">
+ <div class="text-break">{2}</div>
+ </div>
+ <small>{3}</small>
+ </a>
+ </div>
+ <div id="dm-container" class="list-group list-group-flush" data-offset="10">
+ {{foreach $entries as $e}}
+ <a href="{{$e.href}}" class="list-group-item list-group-item-action message" data-b64mid="{{$e.b64mid}}">
+ <div class="d-flex w-100 justify-content-between">
+ <div class="mb-1 text-truncate" title="{{$e.author_addr}}">
+ {{$e.icon}}
+ <strong>{{$e.author_name}}</strong>
+ </div>
+ <small class="messages-timeago text-nowrap" title="{{$e.created}}"></small>
+ </div>
+ <div class="mb-1">
+ <div class="text-break">{{$e.summary}}</div>
+ </div>
+ <small>{{$e.info}}</small>
+ </a>
+ {{/foreach}}
+ <div id="messages-loading" class="list-group-item" style="display: none;">
+ {{$strings.loading}}<span class="jumping-dots"><span class="dot-1">.</span><span class="dot-2">.</span><span class="dot-3">.</span></span>
+ </div>
+ </div>
+</div>
+<script>
+ var messages_offset = {{$offset}};
+ var get_messages_page_active = false;
+ var messages_type;
+
+ $('#messages-widget').on('scroll', function() {
+ if(this.scrollTop > this.scrollHeight - this.clientHeight - (this.scrollHeight/7)) {
+ get_messages_page('hq');
+ }
+ });
+
+ $(document).on('click', '.messages-type', function(e) {
+ e.preventDefault();
+ $('.messages-type').removeClass('active');
+ $(this).addClass('active');
+ messages_offset = 0;
+ messages_type = $(this).data('messages_type');
+ $('#dm-container .message').remove();
+ get_messages_page();
+ });
+
+ $('.messages-timeago').timeago();
+ $('.message[data-b64mid=\'' + bParam_mid + '\']').addClass('active');
+
+ function get_messages_page() {
+ if (get_messages_page_active)
+ return;
+
+ if (messages_offset === -1)
+ return;
+
+ get_messages_page_active = true;
+ $('#messages-loading').show();
+ $.ajax({
+ type: 'post',
+ url: 'hq',
+ data: {
+ offset: messages_offset,
+ type: messages_type
+ }
+ }).done(function(obj) {
+ get_messages_page_active = false;
+ messages_offset = obj.offset;
+ console.log(obj);
+ let html;
+ let tpl = $('#messages-template[rel=template]').html();
+ obj.entries.forEach(function(e) {
+ html = tpl.format(
+ e.b64mid,
+ e.created,
+ e.summary,
+ e.info,
+ e.author_name,
+ e.author_addr,
+ e.href,
+ e.icon
+ );
+ $('#messages-loading').before(html);
+ });
+ $('.message[data-b64mid=\'' + bParam_mid + '\']').addClass('active');
+ $('#messages-loading').hide();
+ $('.messages-timeago').timeago();
+
+ });
+ }
+
+</script>
diff --git a/view/tpl/notes.tpl b/view/tpl/notes.tpl
index 4bee02aa0..0ae0604ef 100644
--- a/view/tpl/notes.tpl
+++ b/view/tpl/notes.tpl
@@ -1,24 +1,18 @@
{{if $app}}
-<div class="generic-content-wrapper">
+<div id="personal-notes" class="generic-content-wrapper{{if $hidden}} d-none{{/if}}">
<div class="section-title-wrapper">
<h2>{{$banner}}</h2>
</div>
<div class="section-content-wrapper">
{{else}}
-<div class="widget">
+<div id="personal-notes" class="widget{{if $hidden}} d-none{{/if}}">
<h3>{{$banner}}</h3>
{{/if}}
- <textarea name="note_text" id="note-text">{{$text}}</textarea>
+ <textarea name="note_text" id="note-text" class="{{if $app}}form-control border-0{{/if}}">{{$text}}</textarea>
<script>
var noteSaveTimer = null;
var noteText = $('#note-text');
- $(document).ready(function(e){
- noteText.on('change keyup keydown paste cut', function () {
- noteText.height(0).height(noteText[0].scrollHeight);
- }).change();
- });
-
$(document).on('focusout',"#note-text",function(e){
if(noteSaveTimer)
clearTimeout(noteSaveTimer);
diff --git a/view/tpl/notifications_widget.tpl b/view/tpl/notifications_widget.tpl
index 1092bc155..0d922e416 100644
--- a/view/tpl/notifications_widget.tpl
+++ b/view/tpl/notifications_widget.tpl
@@ -1,9 +1,17 @@
-{{if $notifications}}
<script>
- var notifications_parent;
+ var sse_bs_active = false;
+ var sse_offset = 0;
+ var sse_type;
+ var sse_partial_result = false;
+ var sse_rmids = [];
+ var sse_fallback_interval;
$(document).ready(function() {
- notifications_parent = $('#notifications_wrapper')[0].parentElement.id;
+ let notifications_parent;
+ if ($('#notifications_wrapper').length) {
+ notifications_parent = $('#notifications_wrapper')[0].parentElement.id;
+ }
+
$('.notifications-btn').click(function() {
if($('#notifications_wrapper').hasClass('fs')) {
$('#notifications_wrapper').prependTo('#' + notifications_parent);
@@ -23,60 +31,118 @@
}
});
- window.onpopstate = function(e) {
- if(e.state !== null && e.state.b64mid !== bParam_mid)
- getData(e.state.b64mid, '');
- };
- });
+ $(document).on('click', '.notification', function() {
+ if($('#notifications_wrapper').hasClass('fs')) {
+ $('#notifications_wrapper').prependTo('#' + notifications_parent).removeClass('fs');
+ }
+ });
- {{if $module == 'display' || $module == 'hq' || $startpage == 'hq'}}
- $(document).on('click', '.notification', function(e) {
- var b64mid = $(this).data('b64mid');
- var notify_id = $(this).data('notify_id');
- var path = $(this)[0].pathname.substr(1,7);
- var stateObj = { b64mid: b64mid };
+ if(sse_enabled) {
+ if(typeof(window.SharedWorker) === 'undefined') {
+ // notifications with multiple tabs open will not work very well in this scenario
+ var evtSource = new EventSource('/sse');
- if(! b64mid && ! notify_id)
- return;
+ evtSource.addEventListener('notifications', function(e) {
+ var obj = JSON.parse(e.data);
+ sse_handleNotifications(obj, false, false);
+ }, false);
- {{if $module != 'hq' && $startpage == 'hq'}}
- e.preventDefault();
- if(notify_id != null) {
- $.post(
- "hq",
- {
- "notify_id" : notify_id
+ document.addEventListener('visibilitychange', function() {
+ if (!document.hidden) {
+ sse_offset = 0;
+ sse_bs_init();
}
- );
+ }, false);
+
}
- window.location.href = 'hq/' + b64mid;
- return;
- {{else}}
- {{if $module == 'display'}}
- history.pushState(stateObj, '', 'display/' + b64mid);
- {{/if}}
-
- {{if $module == 'hq'}}
- history.pushState(stateObj, '', 'hq/' + b64mid);
- {{/if}}
-
- {{if $module == 'hq'}}
- if(b64mid) {
- {{else}}
- if(path === 'display' && b64mid) {
- {{/if}}
- e.preventDefault();
-
- if(! page_load) {
- getData(b64mid, notify_id);
+ else {
+ var myWorker = new SharedWorker('/view/js/sse_worker.js', localUser);
+
+ myWorker.port.onmessage = function(e) {
+ obj = e.data;
+ console.log(obj);
+ sse_handleNotifications(obj, false, false);
+ }
+
+ myWorker.onerror = function(e) {
+ myWorker.port.close();
+ }
+
+ myWorker.port.start();
+ }
+ }
+ else {
+ if (!document.hidden)
+ sse_fallback_interval = setInterval(sse_fallback, updateInterval);
+
+ document.addEventListener('visibilitychange', function() {
+ if (document.hidden) {
+ clearInterval(sse_fallback_interval);
}
+ else {
+ sse_offset = 0;
+ sse_bs_init();
+ sse_fallback_interval = setInterval(sse_fallback, updateInterval);
+ }
+
+ }, false);
+ }
+
+ $('.notification-link').on('click', { replace: true, followup: false }, sse_bs_notifications);
+
+ $('.notification-filter').on('keypress', function(e) {
+ if(e.which == 13) { // enter
+ this.blur();
+ sse_offset = 0;
+ $("#nav-" + sse_type + "-menu").html('');
+ $("#nav-" + sse_type + "-loading").show();
+
+ var cn_val = $('#cn-' + sse_type + '-input').length ? $('#cn-' + sse_type + '-input').val().toString().toLowerCase() : '';
+
+ $.get('/sse_bs/' + sse_type + '/' + sse_offset + '?nquery=' + encodeURIComponent(cn_val), function(obj) {
+ console.log('sse: bootstraping ' + sse_type);
+ console.log(obj);
+
+ sse_bs_active = false;
+ sse_partial_result = true;
+ sse_offset = obj[sse_type].offset;
+ if(sse_offset < 0)
+ $("#nav-" + sse_type + "-loading").hide();
+
+ sse_handleNotifications(obj, true, false);
+
+ });
+ }
+ });
+
+ $('.notifications-textinput-clear').on('click', function(e) {
+ if(! sse_partial_result)
+ return;
+
+ $("#nav-" + sse_type + "-menu").html('');
+ $("#nav-" + sse_type + "-loading").show();
+ $.get('/sse_bs/' + sse_type, function(obj) {
+ console.log('sse: bootstraping ' + sse_type);
+ console.log(obj);
+
+ sse_bs_active = false;
+ sse_partial_result = false;
+ sse_offset = obj[sse_type].offset;
+ if(sse_offset < 0)
+ $("#nav-" + sse_type + "-loading").hide();
+
+ sse_handleNotifications(obj, true, false);
+
+ });
+ });
- if($('#notifications_wrapper').hasClass('fs'))
- $('#notifications_wrapper').prependTo('#' + notifications_parent).removeClass('fs');
+ $('.notification-content').on('scroll', function() {
+ if(this.scrollTop > this.scrollHeight - this.clientHeight - (this.scrollHeight/7)) {
+ sse_bs_notifications(sse_type, false, true);
}
- {{/if}}
+ });
+
});
- {{/if}}
{{foreach $notifications as $notification}}
{{if $notification.filter}}
@@ -91,12 +157,14 @@
}
});
+
$(document).on('click', '#cn-{{$notification.type}}-input-clear', function(e) {
$('#cn-{{$notification.type}}-input').val('');
$('#cn-{{$notification.type}}-only').removeClass('active sticky-top');
$("#nav-{{$notification.type}}-menu .notification").removeClass('cn-filter-active');
$('#cn-{{$notification.type}}-input-clear').addClass('d-none');
});
+
$(document).on('input', '#cn-{{$notification.type}}-input', function(e) {
var val = $('#cn-{{$notification.type}}-input').val().toString().toLowerCase();
if(val) {
@@ -122,18 +190,284 @@
{{/if}}
{{/foreach}}
- function getData(b64mid, notify_id) {
- $(document).scrollTop(0);
- $('.thread-wrapper').remove();
- bParam_mid = b64mid;
- mode = 'replace';
- page_load = true;
- {{if $module == 'hq'}}
- liveUpdate(notify_id);
- {{/if}}
- {{if $module == 'display'}}
- liveUpdate();
- {{/if}}
+ function sse_bs_init() {
+ if(sessionStorage.getItem('notification_open') !== null || typeof sse_type !== 'undefined' ) {
+ if(typeof sse_type === 'undefined')
+ sse_type = sessionStorage.getItem('notification_open');
+
+ $("#nav-" + sse_type + "-sub").addClass('show');
+ sse_bs_notifications(sse_type, true, false);
+ }
+ else {
+ sse_bs_counts();
+ }
+ }
+
+ function sse_bs_counts() {
+ if(sse_bs_active)
+ return;
+
+ sse_bs_active = true;
+
+ $.ajax({
+ type: 'post',
+ url: '/sse_bs',
+ data: { sse_rmids }
+ }).done( function(obj) {
+ console.log(obj);
+ sse_bs_active = false;
+ sse_rmids = [];
+ sse_handleNotifications(obj, true, false);
+ });
+ }
+
+ function sse_bs_notifications(e, replace, followup) {
+
+ if(sse_bs_active)
+ return;
+
+ var manual = false;
+
+ if(typeof replace === 'undefined')
+ replace = e.data.replace;
+
+ if(typeof followup === 'undefined')
+ followup = e.data.followup;
+
+ if(typeof e === 'string') {
+ sse_type = e;
+ }
+ else {
+ manual = true;
+ sse_offset = 0;
+ sse_type = e.target.dataset.sse_type;
+ }
+
+ if(typeof sse_type === 'undefined')
+ return;
+
+ if(followup || !manual || !($('#nav-' + sse_type + '-sub').hasClass('collapse') && $('#nav-' + sse_type + '-sub').hasClass('show'))) {
+
+ if(sse_offset >= 0) {
+ $("#nav-" + sse_type + "-loading").show();
+ }
+
+ sessionStorage.setItem('notification_open', sse_type);
+ if(sse_offset !== -1 || replace) {
+
+ var cn_val = (($('#cn-' + sse_type + '-input').length && sse_partial_result) ? $('#cn-' + sse_type + '-input').val().toString().toLowerCase() : '');
+
+ $("#nav-" + sse_type + "-loading").show();
+
+ sse_bs_active = true;
+
+ $.ajax({
+ type: 'post',
+ url: '/sse_bs/' + sse_type + '/' + sse_offset,
+ nquery: encodeURIComponent(cn_val),
+ data: { sse_rmids }
+ }).done(function(obj) {
+ console.log('sse: bootstraping ' + sse_type);
+ console.log(obj);
+ sse_bs_active = false;
+ sse_rmids = [];
+ $("#nav-" + sse_type + "-loading").hide();
+ sse_offset = obj[sse_type].offset;
+ sse_handleNotifications(obj, replace, followup);
+ });
+ }
+ else
+ $("#nav-" + sse_type + "-loading").hide();
+
+ }
+ else {
+ sessionStorage.removeItem('notification_open');
+ }
+ }
+
+ function sse_handleNotifications(obj, replace, followup) {
+
+ var primary_notifications = ['dm', 'home', 'intros', 'register', 'notify', 'files'];
+ var secondary_notifications = ['network', 'forums', 'all_events', 'pubs'];
+ var all_notifications = primary_notifications.concat(secondary_notifications);
+
+ all_notifications.forEach(function(type, index) {
+ if(typeof obj[type] === typeof undefined)
+ return true;
+
+ if(obj[type].count) {
+ $('.' + type + '-button').fadeIn();
+ if(replace || followup)
+ $('.' + type + '-update').html(Number(obj[type].count));
+ else
+ $('.' + type + '-update').html(Number(obj[type].count) + Number($('.' + type + '-update').html()));
+ }
+ else {
+ $('.' + type + '-update').html('0');
+ $('.' + type + '-button').fadeOut(function() {
+ sse_setNotificationsStatus();
+ });
+ }
+ if(obj[type].notifications.length)
+ sse_handleNotificationsItems(type, obj[type].notifications, replace, followup);
+ });
+
+ sse_setNotificationsStatus();
+
+ // notice and info
+ $.jGrowl.defaults.closerTemplate = '<div>[ ' + aStr.closeAll + ']</div>';
+
+ if(obj.notice) {
+ $(obj.notice.notifications).each(function() {
+ $.jGrowl(this, { sticky: true, theme: 'notice' });
+ });
+ }
+
+ if(obj.info) {
+ $(obj.info.notifications).each(function(){
+ $.jGrowl(this, { sticky: false, theme: 'info', life: 10000 });
+ });
+ }
+
+ // load more notifications if visible notifications count becomes low
+ if(sse_type && sse_offset != -1 && $('#nav-' + sse_type + '-menu').children().length < 15) {
+ sse_offset = 0;
+ sse_bs_notifications(sse_type, false, true);
+ }
+
+
+ }
+
+ function sse_handleNotificationsItems(notifyType, data, replace, followup) {
+
+ var notifications_tpl = ((notifyType == 'forums') ? decodeURIComponent($("#nav-notifications-forums-template[rel=template]").html().replace('data-src', 'src')) : decodeURIComponent($("#nav-notifications-template[rel=template]").html().replace('data-src', 'src')));
+ var notify_menu = $("#nav-" + notifyType + "-menu");
+ var notify_loading = $("#nav-" + notifyType + "-loading");
+ var notify_count = $("." + notifyType + "-update");
+
+ if(replace && !followup) {
+ notify_menu.html('');
+ notify_loading.hide();
+ }
+
+ $(data).each(function() {
+
+ // do not add a notification if it is already present
+ if($('#nav-' + notifyType + '-menu .notification[data-b64mid=\'' + this.b64mid + '\']').length)
+ return true;
+
+ html = notifications_tpl.format(this.notify_link,this.photo,this.name,this.addr,this.message,this.when,this.hclass,this.b64mid,this.notify_id,this.thread_top,this.unseen,this.private_forum, encodeURIComponent(this.mids), this.body);
+ notify_menu.append(html);
+ });
+
+ if(!replace && !followup) {
+ $("#nav-" + notifyType + "-menu .notification").sort(function(a,b) {
+ a = new Date(a.dataset.when);
+ b = new Date(b.dataset.when);
+ return a > b ? -1 : a < b ? 1 : 0;
+ }).appendTo('#nav-' + notifyType + '-menu');
+ }
+
+ $("#nav-" + notifyType + "-menu .notifications-autotime").timeago();
+
+ if($('#tt-' + notifyType + '-only').hasClass('active'))
+ $('#nav-' + notifyType + '-menu [data-thread_top=false]').addClass('tt-filter-active');
+
+ if($('#cn-' + notifyType + '-input').length) {
+ var filter = $('#cn-' + notifyType + '-input').val().toString().toLowerCase();
+ if(filter) {
+ filter = filter.indexOf('%') == 0 ? filter.substring(1) : filter;
+
+ $('#nav-' + notifyType + '-menu .notification').each(function(i, el) {
+ var cn = $(el).data('contact_name').toString().toLowerCase();
+ var ca = $(el).data('contact_addr').toString().toLowerCase();
+ if(cn.indexOf(filter) === -1 && ca.indexOf(filter) === -1)
+ $(el).addClass('cn-filter-active');
+ else
+ $(el).removeClass('cn-filter-active');
+ });
+ }
+ }
+ }
+
+ function sse_updateNotifications(type, mid) {
+
+ if(type === 'pubs')
+ return true;
+
+ if(type === 'notify' && (mid !== bParam_mid || sse_type !== 'notify'))
+ return true;
+ /*
+ var count = Number($('.' + type + '-update').html());
+
+ count--;
+
+ if(count < 1) {
+ $('.' + type + '-update').html(count);
+ $('.' + type + '-button').fadeOut(function() {
+ sse_setNotificationsStatus();
+ });
+ }
+ else {
+ $('.' + type + '-update').html(count);
+ }
+ */
+
+ $('#nav-' + type + '-menu .notification[data-b64mid=\'' + mid + '\']').fadeOut(function() {
+ this.remove();
+ });
+
+ }
+
+ function sse_setNotificationsStatus() {
+ var primary_notifications = ['dm', 'home', 'intros', 'register', 'notify', 'files'];
+ var secondary_notifications = ['network', 'forums', 'all_events', 'pubs'];
+ var all_notifications = primary_notifications.concat(secondary_notifications);
+
+ var primary_available = false;
+ var any_available = false;
+
+ all_notifications.forEach(function(type, index) {
+ if($('.' + type + '-button').css('display') == 'block') {
+ any_available = true;
+ if(primary_notifications.indexOf(type) > -1)
+ primary_available = true;
+ }
+ });
+
+ if(primary_available) {
+ $('.notifications-btn-icon').removeClass('fa-exclamation-circle');
+ $('.notifications-btn-icon').addClass('fa-exclamation-triangle');
+ }
+ else {
+ $('.notifications-btn-icon').removeClass('fa-exclamation-triangle');
+ $('.notifications-btn-icon').addClass('fa-exclamation-circle');
+ }
+
+ if(any_available) {
+ $('.notifications-btn').css('opacity', 1);
+ $('#no_notifications').hide();
+ $('#notifications').show();
+ }
+ else {
+ $('.notifications-btn').css('opacity', 0.5);
+ $('#navbar-collapse-1').removeClass('show');
+ $('#no_notifications').show();
+ $('#notifications').hide();
+ }
+
+ }
+
+ function sse_fallback() {
+ $.get('/sse', function(obj) {
+ if(! obj)
+ return;
+
+ console.log('sse fallback');
+ console.log(obj);
+
+ sse_handleNotifications(obj, false, false);
+ });
}
</script>
@@ -199,4 +533,3 @@
{{/foreach}}
</div>
</div>
-{{/if}}