aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Zotlabs/Daemon/Notifier.php12
-rw-r--r--Zotlabs/Lib/Activity.php220
-rw-r--r--Zotlabs/Lib/Enotify.php5
-rw-r--r--Zotlabs/Lib/Libsync.php12
-rw-r--r--Zotlabs/Lib/Libzot.php20
-rw-r--r--Zotlabs/Lib/NativeWikiPage.php7
-rw-r--r--Zotlabs/Lib/System.php11
-rw-r--r--Zotlabs/Lib/ThreadItem.php4
-rw-r--r--Zotlabs/Module/Apschema.php5
-rw-r--r--Zotlabs/Module/Cdav.php531
-rw-r--r--Zotlabs/Module/Item.php133
-rw-r--r--Zotlabs/Module/Network.php24
-rw-r--r--Zotlabs/Module/Vote.php143
-rw-r--r--Zotlabs/Module/Well_known.php5
-rw-r--r--Zotlabs/Widget/Activity_filter.php28
-rwxr-xr-xboot.php4
-rw-r--r--images/article.gifbin1060 -> 0 bytes
-rw-r--r--images/audio.gifbin559 -> 0 bytes
-rw-r--r--images/b_block.gifbin83 -> 0 bytes
-rw-r--r--images/b_drop.gifbin138 -> 0 bytes
-rw-r--r--images/b_drop.pngbin311 -> 0 bytes
-rw-r--r--images/b_drophide.gifbin111 -> 0 bytes
-rw-r--r--images/b_dropshow.gifbin138 -> 0 bytes
-rw-r--r--images/b_edit.gifbin311 -> 0 bytes
-rw-r--r--images/b_edit.pngbin451 -> 0 bytes
-rw-r--r--images/bug-x.gifbin134 -> 0 bytes
-rw-r--r--images/calendar.pngbin853 -> 0 bytes
-rw-r--r--images/camera-icon.gifbin1015 -> 0 bytes
-rw-r--r--images/checkbox-checked-32.pngbin1397 -> 0 bytes
-rw-r--r--images/checkbox-unchecked-32.pngbin1063 -> 0 bytes
-rw-r--r--images/connect-bg.pngbin689 -> 0 bytes
-rw-r--r--images/content-types.pngbin3892 -> 0 bytes
-rw-r--r--images/default-group-mm.pngbin598 -> 0 bytes
-rw-r--r--images/document.gifbin1362 -> 0 bytes
-rw-r--r--images/ghash-32.pngbin1824 -> 0 bytes
-rw-r--r--images/globe.gifbin1025 -> 0 bytes
-rw-r--r--images/hide_off.pngbin281 -> 0 bytes
-rw-r--r--images/hide_on.pngbin277 -> 0 bytes
-rw-r--r--images/hubzilla_house_arrows.pngbin22076 -> 0 bytes
-rw-r--r--images/hz-bookmark-32.pngbin973 -> 0 bytes
-rw-r--r--images/icons.pngbin12638 -> 0 bytes
-rw-r--r--images/larrow.gifbin211 -> 0 bytes
-rw-r--r--images/larrw.gifbin1004 -> 0 bytes
-rw-r--r--images/link-icon.gifbin145 -> 0 bytes
-rw-r--r--images/lock_icon.gifbin932 -> 0 bytes
-rw-r--r--images/lrarrow.gifbin236 -> 0 bytes
-rw-r--r--images/mapicon.gifbin1042 -> 0 bytes
-rw-r--r--images/no.gifbin631 -> 0 bytes
-rw-r--r--images/noglobe.gifbin606 -> 0 bytes
-rw-r--r--images/nosign.jpgbin6498 -> 0 bytes
-rw-r--r--images/nosign.pngbin17868 -> 0 bytes
-rw-r--r--images/onoff.jpgbin502 -> 0 bytes
-rw-r--r--images/pen.pngbin252 -> 0 bytes
-rw-r--r--images/pencil.gifbin553 -> 0 bytes
-rw-r--r--images/penhover.pngbin270 -> 0 bytes
-rw-r--r--images/people.gifbin1478 -> 0 bytes
-rw-r--r--images/plugin.pngbin2043 -> 0 bytes
-rw-r--r--images/rarrow.gifbin212 -> 0 bytes
-rw-r--r--images/rarrw.gifbin999 -> 0 bytes
-rw-r--r--images/recycle.gifbin612 -> 0 bytes
-rw-r--r--images/red_antiprism.pngbin120847 -> 0 bytes
-rw-r--r--images/red_antiprism.xcfbin659160 -> 0 bytes
-rw-r--r--images/redmatrix_logo.svg85
-rw-r--r--images/remote-link.gifbin237 -> 0 bytes
-rw-r--r--images/rhash-16.pngbin679 -> 0 bytes
-rw-r--r--images/rhash-32.pngbin1322 -> 0 bytes
-rw-r--r--images/rhash-64.pngbin3115 -> 0 bytes
-rw-r--r--images/rhash.xcfbin94324 -> 0 bytes
-rw-r--r--images/rm-16.pngbin676 -> 0 bytes
-rw-r--r--images/rm-32.pngbin1186 -> 0 bytes
-rw-r--r--images/rm-64.pngbin1897 -> 0 bytes
-rw-r--r--images/rm-old.pngbin12441 -> 0 bytes
-rw-r--r--images/rm-transparent-dark-background.pngbin6370 -> 0 bytes
-rw-r--r--images/rm-transparent-large-old.pngbin42706 -> 0 bytes
-rw-r--r--images/rm.pngbin6362 -> 0 bytes
-rw-r--r--images/rm.svg132
-rw-r--r--images/rm300.pngbin6426 -> 0 bytes
-rw-r--r--images/rotator.gifbin826 -> 0 bytes
-rw-r--r--images/search_18.pngbin3302 -> 0 bytes
-rw-r--r--images/selected.pngbin502 -> 0 bytes
-rw-r--r--images/share.gifbin155 -> 0 bytes
-rw-r--r--images/show_all_off.pngbin539 -> 0 bytes
-rw-r--r--images/show_all_on.pngbin464 -> 0 bytes
-rw-r--r--images/show_off.pngbin244 -> 0 bytes
-rw-r--r--images/show_on.pngbin218 -> 0 bytes
-rw-r--r--images/spencil.gifbin497 -> 0 bytes
-rw-r--r--images/star.pngbin388 -> 0 bytes
-rw-r--r--images/star_dummy.pngbin183 -> 0 bytes
-rw-r--r--images/tag.pngbin528 -> 0 bytes
-rw-r--r--images/tag_b.pngbin346 -> 0 bytes
-rw-r--r--images/tools.pngbin490 -> 0 bytes
-rw-r--r--images/unlock_icon.gifbin938 -> 0 bytes
-rw-r--r--images/video.gifbin257 -> 0 bytes
-rw-r--r--include/bbcode.php142
-rw-r--r--include/cdav.php186
-rw-r--r--include/channel.php42
-rw-r--r--include/conversation.php38
-rw-r--r--include/features.php16
-rw-r--r--include/import.php138
-rwxr-xr-xinclude/items.php6
-rw-r--r--include/language.php7
-rw-r--r--include/nav.php2
-rw-r--r--include/text.php105
-rw-r--r--include/zid.php1
-rw-r--r--include/zot.php6
-rw-r--r--view/css/conversation.css21
-rw-r--r--view/js/autocomplete.js2
-rw-r--r--view/js/main.js20
-rw-r--r--view/ru/hmessages.po12
-rw-r--r--view/ru/hstrings.php10
-rw-r--r--view/theme/redbasic/css/style.css4
-rwxr-xr-xview/tpl/jot-header.tpl19
-rwxr-xr-xview/tpl/jot.tpl40
113 files changed, 1621 insertions, 577 deletions
diff --git a/Zotlabs/Daemon/Notifier.php b/Zotlabs/Daemon/Notifier.php
index 1d0be10d9..00c6fb077 100644
--- a/Zotlabs/Daemon/Notifier.php
+++ b/Zotlabs/Daemon/Notifier.php
@@ -3,6 +3,7 @@
namespace Zotlabs\Daemon;
use Zotlabs\Lib\Libzot;
+use Zotlabs\Lib\Activity;
require_once('include/queue_fn.php');
require_once('include/html2plain.php');
@@ -366,9 +367,18 @@ class Notifier {
$activity = json_decode($m,true);
}
else {
- $activity = \Zotlabs\Lib\Activity::encode_activity($target_item);
+ $activity = array_merge(['@context' => [
+ ACTIVITYSTREAMS_JSONLD_REV,
+ 'https://w3id.org/security/v1',
+ z_root() . ZOT_APSCHEMA_REV
+ ]], Activity::encode_activity($target_item)
+ );
}
+ logger('target_item: ' . print_r($target_item,true), LOGGER_DEBUG);
+ logger('encoded: ' . print_r($activity,true), LOGGER_DEBUG);
+
+
// Send comments to the owner to re-deliver to everybody in the conversation
// We only do this if the item in question originated on this site. This prevents looping.
// To clarify, a site accepting a new comment is responsible for sending it to the owner for relay.
diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php
index 43315a87f..02ec7614e 100644
--- a/Zotlabs/Lib/Activity.php
+++ b/Zotlabs/Lib/Activity.php
@@ -325,6 +325,22 @@ class Activity {
$ret['type'] = $objtype;
+ if ($objtype === 'Question') {
+ if ($i['obj']) {
+ if (is_array($i['obj'])) {
+ $ret = $i['obj'];
+ }
+ else {
+ $ret = json_decode($i['obj'],true);
+ }
+
+ if(array_path_exists('actor/id',$ret)) {
+ $ret['actor'] = $ret['actor']['id'];
+ }
+ }
+ }
+
+
$ret['id'] = ((strpos($i['mid'],'http') === 0) ? $i['mid'] : z_root() . '/item/' . urlencode($i['mid']));
if($i['title'])
@@ -488,10 +504,47 @@ class Activity {
}
}
}
+ if ($item['iconfig']) {
+ foreach ($item['iconfig'] as $att) {
+ if ($att['sharing']) {
+ $value = ((preg_match('|^a:[0-9]+:{.*}$|s', $att['v'])) ? unserialize($att['v']) : $att['v']);
+ $ret[] = [ 'type' => 'PropertyValue', 'name' => 'zot.' . $att['cat'] . '.' . $att['k'], 'value' => $value ];
+ }
+ }
+ }
return $ret;
}
+ static function decode_iconfig($item) {
+
+ $ret = [];
+
+ if (is_array($item['attachment']) && $item['attachment']) {
+ $ptr = $item['attachment'];
+ if (! array_key_exists(0,$ptr)) {
+ $ptr = [ $ptr ];
+ }
+ foreach ($ptr as $att) {
+ $entry = [];
+ if ($att['type'] === 'PropertyValue') {
+ if (array_key_exists('name',$att) && $att['name']) {
+ $key = explode('.',$att['name']);
+ if (count($key) === 3 && $key[0] === 'zot') {
+ $entry['cat'] = $key[1];
+ $entry['k'] = $key[2];
+ $entry['v'] = $att['value'];
+ $entry['sharing'] = '1';
+ $ret[] = $entry;
+ }
+ }
+ }
+ }
+ }
+ return $ret;
+ }
+
+
static function decode_attachment($item) {
@@ -576,8 +629,15 @@ class Activity {
}
}
-
- $ret['id'] = ((strpos($i['mid'],'http') === 0) ? $i['mid'] : z_root() . '/activity/' . urlencode($i['mid']));
+ if (strpos($i['mid'],z_root() . '/item/') !== false) {
+ $ret['id'] = str_replace('/item/','/activity/',$i['mid']);
+ }
+ elseif (strpos($i['mid'],z_root() . '/event/') !== false) {
+ $ret['id'] = str_replace('/event/','/activity/',$i['mid']);
+ }
+ else {
+ $ret['id'] = ((strpos($i['mid'],'http') === 0) ? $i['mid'] : z_root() . '/activity/' . urlencode($i['mid']));
+ }
if($i['title'])
$ret['name'] = html2plain(bbcode($i['title'], [ 'cache' => true ]));
@@ -912,7 +972,7 @@ class Activity {
'http://purl.org/zot/activity/file' => 'zot:File',
'http://purl.org/zot/activity/mood' => 'zot:Mood',
'Invite' => 'Invite',
-
+ 'Question' => 'Question'
];
call_hooks('activity_obj_decode_mapper',$objs);
@@ -932,10 +992,6 @@ class Activity {
static function activity_obj_mapper($obj) {
- if(strpos($obj,'/') === false) {
- return $obj;
- }
-
$objs = [
'http://activitystrea.ms/schema/1.0/note' => 'Note',
'http://activitystrea.ms/schema/1.0/comment' => 'Note',
@@ -951,11 +1007,21 @@ class Activity {
'http://purl.org/zot/activity/thing' => 'Object',
'http://purl.org/zot/activity/file' => 'zot:File',
'http://purl.org/zot/activity/mood' => 'zot:Mood',
- 'Invite' => 'Invite',
+ 'Invite' => 'Invite',
+ 'Question' => 'Question'
];
call_hooks('activity_obj_mapper',$objs);
+ if ($obj === 'Answer') {
+ return 'Note';
+ }
+
+ if (strpos($obj,'/') === false) {
+ return $obj;
+ }
+
+
if(array_key_exists($obj,$objs)) {
return $objs[$obj];
}
@@ -1606,6 +1672,101 @@ class Activity {
}
+
+ static function update_poll($item,$post) {
+ $multi = false;
+ $mid = $post['mid'];
+ $content = $post['title'];
+
+ if (! $item) {
+ return false;
+ }
+
+ $o = json_decode($item['obj'],true);
+ if ($o && array_key_exists('anyOf',$o)) {
+ $multi = true;
+ }
+
+ $r = q("select mid, title from item where parent_mid = '%s' and author_xchan = '%s'",
+ dbesc($item['mid']),
+ dbesc($post['author_xchan'])
+ );
+
+ // prevent any duplicate votes by same author for oneOf and duplicate votes with same author and same answer for anyOf
+
+ if ($r) {
+ if ($multi) {
+ foreach ($r as $rv) {
+ if ($rv['title'] === $content && $rv['mid'] !== $mid) {
+ return false;
+ }
+ }
+ }
+ else {
+ foreach ($r as $rv) {
+ if ($rv['mid'] !== $mid) {
+ return false;
+ }
+ }
+ }
+ }
+
+ $answer_found = false;
+ $found = false;
+ if ($multi) {
+ for ($c = 0; $c < count($o['anyOf']); $c ++) {
+ if ($o['anyOf'][$c]['name'] === $content) {
+ $answer_found = true;
+ if (is_array($o['anyOf'][$c]['replies'])) {
+ foreach($o['anyOf'][$c]['replies'] as $reply) {
+ if(is_array($reply) && array_key_exists('id',$reply) && $reply['id'] === $mid) {
+ $found = true;
+ }
+ }
+ }
+
+ if (! $found) {
+ $o['anyOf'][$c]['replies']['totalItems'] ++;
+ $o['anyOf'][$c]['replies']['items'][] = [ 'id' => $mid, 'type' => 'Note' ];
+ }
+ }
+ }
+ }
+ else {
+ for ($c = 0; $c < count($o['oneOf']); $c ++) {
+ if ($o['oneOf'][$c]['name'] === $content) {
+ $answer_found = true;
+ if (is_array($o['oneOf'][$c]['replies'])) {
+ foreach($o['oneOf'][$c]['replies'] as $reply) {
+ if(is_array($reply) && array_key_exists('id',$reply) && $reply['id'] === $mid) {
+ $found = true;
+ }
+ }
+ }
+
+ if (! $found) {
+ $o['oneOf'][$c]['replies']['totalItems'] ++;
+ $o['oneOf'][$c]['replies']['items'][] = [ 'id' => $mid, 'type' => 'Note' ];
+ }
+ }
+ }
+ }
+ logger('updated_poll: ' . print_r($o,true),LOGGER_DATA);
+ if ($answer_found && ! $found) {
+ $x = q("update item set obj = '%s', edited = '%s' where id = %d",
+ dbesc(json_encode($o)),
+ dbesc(datetime_convert()),
+ intval($item['id'])
+ );
+ Master::Summon( [ 'Notifier', 'wall-new', $item['id'] ] );
+ return true;
+ }
+
+ return false;
+ }
+
+
+
static function decode_note($act) {
$response_activity = false;
@@ -1644,7 +1805,6 @@ class Activity {
$s['expires'] = datetime_convert('UTC','UTC',$act->obj['expires']);
}
-
if(in_array($act->type, [ 'Like', 'Dislike', 'Flag', 'Block', 'Announce', 'Accept', 'Reject', 'TentativeAccept', 'emojiReaction' ])) {
$response_activity = true;
@@ -1711,11 +1871,17 @@ class Activity {
$s['verb'] = self::activity_decode_mapper($act->type);
+ // Mastodon does not provide update timestamps when updating poll tallies which means race conditions may occur here.
+ if ($act->type === 'Update' && $act->obj['type'] === 'Question' && $s['edited'] === $s['created']) {
+ $s['edited'] = datetime_convert();
+ }
if($act->type === 'Tombstone' || $act->type === 'Delete' || ($act->type === 'Create' && $act->obj['type'] === 'Tombstone')) {
$s['item_deleted'] = 1;
}
+
+
$s['obj_type'] = self::activity_obj_decode_mapper($act->obj['type']);
if($s['obj_type'] === ACTIVITY_OBJ_NOTE && $s['mid'] !== $s['parent_mid']) {
$s['obj_type'] = ACTIVITY_OBJ_COMMENT;
@@ -1787,16 +1953,33 @@ class Activity {
}
}
- $a = self::decode_attachment($act->obj);
- if($a) {
- $s['attach'] = $a;
- }
+ }
+
+ $a = self::decode_attachment($act->obj);
+ if ($a) {
+ $s['attach'] = $a;
+ }
+
+ $a = self::decode_iconfig($act->obj);
+ if ($a) {
+ $s['iconfig'] = $a;
}
if($act->obj['type'] === 'Note' && $s['attach']) {
$s['body'] .= self::bb_attach($s['attach'],$s['body']);
}
+ if ($act->obj['type'] === 'Question' && in_array($act->type,['Create','Update'])) {
+ if ($act->obj['endTime']) {
+ $s['comments_closed'] = datetime_convert('UTC','UTC', $act->obj['endTime']);
+ }
+ }
+
+ if ($act->obj['closed']) {
+ $s['comments_closed'] = datetime_convert('UTC','UTC', $act->obj['closed']);
+ }
+
+
// we will need a hook here to extract magnet links e.g. peertube
// right now just link to the largest mp4 we find that will fit in our
@@ -2086,7 +2269,7 @@ class Activity {
set_iconfig($item,'activitypub','recips',$act->raw_recips);
if(! $is_parent) {
- $p = q("select parent_mid from item where mid = '%s' and uid = %d limit 1",
+ $p = q("select parent_mid, id, obj_type from item where mid = '%s' and uid = %d limit 1",
dbesc($item['parent_mid']),
intval($item['uid'])
);
@@ -2116,6 +2299,15 @@ class Activity {
// $s['thr_parent'] = $s['mid'];
}
}
+
+
+ if ($p[0]['obj_type'] === 'Question') {
+ if ($item['obj_type'] === ACTIVITY_OBJ_NOTE && $item['title'] && (! $item['content'])) {
+ $item['obj_type'] = 'Answer';
+ }
+ }
+
+
if($p[0]['parent_mid'] !== $item['parent_mid']) {
$item['thr_parent'] = $item['parent_mid'];
}
diff --git a/Zotlabs/Lib/Enotify.php b/Zotlabs/Lib/Enotify.php
index f6f8ad0cb..85e90d67c 100644
--- a/Zotlabs/Lib/Enotify.php
+++ b/Zotlabs/Lib/Enotify.php
@@ -810,8 +810,9 @@ class Enotify {
}
else {
$itemem_text = (($item['item_thread_top'])
- ? t('created a new post')
- : sprintf( t('commented on %s\'s post'), '[bdi]' . $item['owner']['xchan_name'] . '[/bdi]'));
+ ? (($item['obj_type'] === 'Question') ? t('created a new poll') : t('created a new post'))
+ : (($item['obj_type'] === 'Answer') ? sprintf( t('voted on %s\'s poll'), '[bdi]' . $item['owner']['xchan_name'] . '[/bdi]') : sprintf( t('commented on %s\'s post'), '[bdi]' . $item['owner']['xchan_name'] . '[/bdi]'))
+ );
if($item['verb'] === ACTIVITY_SHARE) {
$itemem_text = sprintf( t('repeated %s\'s post'), '[bdi]' . $item['author']['xchan_name'] . '[/bdi]');
diff --git a/Zotlabs/Lib/Libsync.php b/Zotlabs/Lib/Libsync.php
index d93270bc5..c39720735 100644
--- a/Zotlabs/Lib/Libsync.php
+++ b/Zotlabs/Lib/Libsync.php
@@ -83,7 +83,7 @@ class Libsync {
$info = (($packet) ? $packet : array());
$info['type'] = 'sync';
- $info['encoding'] = 'red'; // note: not zot, this packet is very platform specific
+ $info['encoding'] = 'hz'; // note: not zot, this packet is very platform specific
$info['relocate'] = ['channel_address' => $channel['channel_address'], 'url' => z_root() ];
if(array_key_exists($uid,\App::$config) && array_key_exists('transient',\App::$config[$uid])) {
@@ -144,7 +144,7 @@ class Libsync {
foreach($synchubs as $hub) {
$hash = random_string();
- $n = Libzot::build_packet($channel,'sync',$env_recips,json_encode($info),'red',$hub['hubloc_sitekey'],$hub['site_crypto']);
+ $n = Libzot::build_packet($channel,'sync',$env_recips,json_encode($info),'hz',$hub['hubloc_sitekey'],$hub['site_crypto']);
Queue::insert(array(
'hash' => $hash,
'account_id' => $channel['channel_account_id'],
@@ -244,7 +244,13 @@ class Libsync {
if(array_key_exists('app',$arr) && $arr['app'])
sync_apps($channel,$arr['app']);
-
+
+ if(array_key_exists('addressbook',$arr) && $arr['addressbook'])
+ sync_addressbook($channel,$arr['addressbook']);
+
+ if(array_key_exists('calendar',$arr) && $arr['calendar'])
+ sync_calendar($channel,$arr['calendar']);
+
if(array_key_exists('chatroom',$arr) && $arr['chatroom'])
sync_chatrooms($channel,$arr['chatroom']);
diff --git a/Zotlabs/Lib/Libzot.php b/Zotlabs/Lib/Libzot.php
index ad00aa97a..42e706754 100644
--- a/Zotlabs/Lib/Libzot.php
+++ b/Zotlabs/Lib/Libzot.php
@@ -1277,7 +1277,12 @@ class Libzot {
logger('Channel sync received: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG);
logger('Channel sync recipients: ' . print_r($deliveries,true), LOGGER_DATA, LOG_DEBUG);
- $result = Libsync::process_channel_sync_delivery($env['sender'],$arr,$deliveries);
+ if ($env['encoding'] === 'hz') {
+ $result = Libsync::process_channel_sync_delivery($env['sender'],$arr,$deliveries);
+ }
+ else {
+ logger('sync packet type not supported.');
+ }
}
}
if ($result) {
@@ -1608,10 +1613,11 @@ class Libzot {
// As a side effect we will also do a preliminary check that we have the top-level-post, otherwise
// processing it is pointless.
- $r = q("select route, id, owner_xchan, item_private from item where mid = '%s' and uid = %d limit 1",
+ $r = q("select route, id, parent_mid, mid, owner_xchan, item_private, obj_type from item where mid = '%s' and uid = %d limit 1",
dbesc($arr['parent_mid']),
intval($channel['channel_id'])
);
+
if(! $r) {
$DR->update('comment parent not found');
$result[] = $DR->get();
@@ -1634,6 +1640,16 @@ class Libzot {
continue;
}
+ if ($r[0]['obj_type'] === 'Question') {
+ // route checking doesn't work correctly here because we've changed the privacy
+ $r[0]['route'] = EMPTY_STR;
+ // If this is a poll response, convert the obj_type to our (internal-only) "Answer" type
+ if ($arr['obj_type'] === ACTIVITY_OBJ_COMMENT && $arr['title'] && (! $arr['body'])) {
+ $arr['obj_type'] = 'Answer';
+ }
+ }
+
+
if($relay || $friendofriend || (intval($r[0]['item_private']) === 0 && intval($arr['item_private']) === 0)) {
// reset the route in case it travelled a great distance upstream
// use our parent's route so when we go back downstream we'll match
diff --git a/Zotlabs/Lib/NativeWikiPage.php b/Zotlabs/Lib/NativeWikiPage.php
index dddd26af3..d84cc50a8 100644
--- a/Zotlabs/Lib/NativeWikiPage.php
+++ b/Zotlabs/Lib/NativeWikiPage.php
@@ -530,8 +530,11 @@ class NativeWikiPage {
foreach ($match[1] as $m) {
// TODO: Why do we need to double urlencode for this to work?
//$pageURLs[] = urlencode(urlencode(escape_tags($m)));
- $pageURLs[] = Zlib\NativeWiki::name_encode(escape_tags($m));
- $pages[] = $m;
+ $titleUri = explode('|',$m);
+ $page = $titleUri[0] ?? '';
+ $title = $titleUri[1] ?? $page;
+ $pageURLs[] = Zlib\NativeWiki::name_encode(escape_tags($page));
+ $pages[] = $title;
}
$idx = 0;
while(strpos($s,'[[') !== false) {
diff --git a/Zotlabs/Lib/System.php b/Zotlabs/Lib/System.php
index 7bf1343bb..3cc46fbda 100644
--- a/Zotlabs/Lib/System.php
+++ b/Zotlabs/Lib/System.php
@@ -5,9 +5,14 @@ namespace Zotlabs\Lib;
class System {
static public function get_platform_name() {
- if(is_array(\App::$config) && is_array(\App::$config['system']) && array_key_exists('platform_name',\App::$config['system']))
- return \App::$config['system']['platform_name'];
- return PLATFORM_NAME;
+ static $platform_name = '';
+ if(empty($platform_name)) {
+ if(is_array(\App::$config) && is_array(\App::$config['system']) && array_key_exists('platform_name',\App::$config['system']))
+ $platform_name = \App::$config['system']['platform_name'];
+ else
+ $platform_name = PLATFORM_NAME;
+ }
+ return $platform_name;
}
static public function get_site_name() {
diff --git a/Zotlabs/Lib/ThreadItem.php b/Zotlabs/Lib/ThreadItem.php
index 2386a1f0d..dee7cda56 100644
--- a/Zotlabs/Lib/ThreadItem.php
+++ b/Zotlabs/Lib/ThreadItem.php
@@ -204,6 +204,10 @@ class ThreadItem {
}
}
+ if($item['obj_type'] === 'Question') {
+ $response_verbs[] = 'answer';
+ }
+
$consensus = (intval($item['item_consensus']) ? true : false);
if($consensus) {
$response_verbs[] = 'agree';
diff --git a/Zotlabs/Module/Apschema.php b/Zotlabs/Module/Apschema.php
index 756057a8a..6b0325d44 100644
--- a/Zotlabs/Module/Apschema.php
+++ b/Zotlabs/Module/Apschema.php
@@ -29,7 +29,10 @@ class Apschema extends \Zotlabs\Web\Controller {
'emojiReaction' => 'zot:emojiReaction',
'expires' => 'zot:expires',
'directMessage' => 'zot:directMessage',
-
+ 'schema' => 'http://schema.org#',
+ 'PropertyValue' => 'schema:PropertyValue',
+ 'value' => 'schema:value',
+
'magicEnv' => [
'@id' => 'zot:magicEnv',
'@type' => '@id'
diff --git a/Zotlabs/Module/Cdav.php b/Zotlabs/Module/Cdav.php
index af40689c1..ac73b8a5b 100644
--- a/Zotlabs/Module/Cdav.php
+++ b/Zotlabs/Module/Cdav.php
@@ -10,6 +10,7 @@ require_once('include/event.php');
require_once('include/auth.php');
require_once('include/security.php');
+require_once('include/cdav.php');
class Cdav extends Controller {
@@ -156,6 +157,79 @@ class Cdav extends Controller {
}
}
+
+ // Track CDAV updates from remote clients
+
+ $httpmethod = $_SERVER['REQUEST_METHOD'];
+
+ if($httpmethod === 'PUT' || $httpmethod === 'DELETE') {
+
+ $httpuri = $_SERVER['REQUEST_URI'];
+
+ logger("debug: method: " . $httpmethod, LOGGER_DEBUG);
+ logger("debug: uri: " . $httpuri, LOGGER_DEBUG);
+
+ if(strpos($httpuri, 'cdav/addressbooks')) {
+ $sync = 'addressbook';
+ $cdavtable = 'addressbooks';
+ }
+ elseif(strpos($httpuri, 'cdav/calendars')) {
+ $sync = 'calendar';
+ $cdavtable = 'calendarinstances';
+ }
+ else
+ $sync = false;
+
+ if($sync) {
+
+ $uri = basename($httpuri);
+ $httpbody = file_get_contents('php://input');
+
+ logger("debug: body: " . $httpbody, LOGGER_DEBUG);
+
+ if($x = get_cdav_id($principalUri, explode("/", $httpuri)[4], $cdavtable)) {
+
+ $cdavdata = $this->get_cdav_data($x['id'], $cdavtable);
+
+ $etag = (isset($_SERVER['HTTP_IF_MATCH']) ? $_SERVER['HTTP_IF_MATCH'] : false);
+
+ // delete
+ if($httpmethod === 'DELETE' && $cdavdata['etag'] == $etag)
+ build_sync_packet($channel['channel_id'], [
+ $sync => [
+ 'action' => 'delete_card',
+ 'uri' => $cdavdata['uri'],
+ 'carduri' => $uri
+ ]
+ ]);
+ else {
+ if($etag) {
+ // update
+ if($cdavdata['etag'] !== $etag)
+ build_sync_packet($channel['channel_id'], [
+ $sync => [
+ 'action' => 'update_card',
+ 'uri' => $cdavdata['uri'],
+ 'carduri' => $uri,
+ 'card' => $httpbody
+ ]
+ ]);
+ }
+ else {
+ // new
+ build_sync_packet($channel['channel_id'], [
+ $sync => [
+ 'action' => 'import',
+ 'uri' => $cdavdata['uri'],
+ 'ids' => [ $uri ],
+ 'card' => $httpbody
+ ]
+ ]);
+ }
+ }
+ }
+ }
+ }
$principalBackend = new \Sabre\DAVACL\PrincipalBackend\PDO($pdo);
@@ -262,6 +336,14 @@ class Cdav extends Controller {
// set new calendar to be visible
set_pconfig(local_channel(), 'cdav_calendar' , $id[0], 1);
+
+ build_sync_packet($channel['channel_id'], [
+ 'calendar' => [
+ 'action' => 'create',
+ 'uri' => $calendarUri,
+ 'properties' => $properties
+ ]
+ ]);
}
//create new calendar object via ajax request
@@ -272,6 +354,8 @@ class Cdav extends Controller {
if(!cdav_perms($id[0],$calendars,true))
return;
+ $cdavdata = $this->get_cdav_data($id[0], 'calendarinstances');
+
$timezone = ((x($_POST,'timezone_select')) ? escape_tags(trim($_POST['timezone_select'])) : '');
$tz = (($timezone) ? $timezone : date_default_timezone_get());
@@ -327,9 +411,17 @@ class Cdav extends Controller {
$vcalendar->VEVENT->DTSTART['TZID'] = $tz;
$calendarData = $vcalendar->serialize();
-
$caldavBackend->createCalendarObject($id, $objectUri, $calendarData);
+ build_sync_packet($channel['channel_id'], [
+ 'calendar' => [
+ 'action' => 'import',
+ 'uri' => $cdavdata['uri'],
+ 'ids' => [ $objectUri ],
+ 'card' => $calendarData
+ ]
+ ]);
+
killme();
}
@@ -341,17 +433,24 @@ class Cdav extends Controller {
if(! cdav_perms($id[0],$calendars))
return;
+ $cdavdata = $this->get_cdav_data($id[0], 'calendarinstances');
+
$mutations = [
'{DAV:}displayname' => $_REQUEST['{DAV:}displayname'],
'{http://apple.com/ns/ical/}calendar-color' => $_REQUEST['color']
];
$patch = new \Sabre\DAV\PropPatch($mutations);
-
$caldavBackend->updateCalendar($id, $patch);
-
$patch->commit();
+ build_sync_packet($channel['channel_id'], [
+ 'calendar' => [
+ 'action' => 'edit',
+ 'uri' => $cdavdata['uri'],
+ 'mutations' => $mutations,
+ ]
+ ]);
}
//edit calendar object via ajax request
@@ -359,9 +458,11 @@ class Cdav extends Controller {
$id = explode(':', $_REQUEST['target']);
- if(!cdav_perms($id[0],$calendars,true))
+ if(! cdav_perms($id[0],$calendars,true))
return;
+ $cdavdata = $this->get_cdav_data($id[0], 'calendarinstances');
+
$timezone = ((x($_POST,'timezone_select')) ? escape_tags(trim($_POST['timezone_select'])) : '');
$tz = (($timezone) ? $timezone : date_default_timezone_get());
@@ -407,9 +508,17 @@ class Cdav extends Controller {
$vcalendar->VEVENT->LOCATION = $location;
$calendarData = $vcalendar->serialize();
-
$caldavBackend->updateCalendarObject($id, $uri, $calendarData);
+ build_sync_packet($channel['channel_id'], [
+ 'calendar' => [
+ 'action' => 'update_card',
+ 'uri' => $cdavdata['uri'],
+ 'carduri' => $uri,
+ 'card' => $calendarData
+ ]
+ ]);
+
killme();
}
@@ -418,13 +527,23 @@ class Cdav extends Controller {
$id = explode(':', $_REQUEST['target']);
- if(!cdav_perms($id[0],$calendars,true))
+ if(! cdav_perms($id[0],$calendars,true))
return;
+ $cdavdata = $this->get_cdav_data($id[0], 'calendarinstances');
+
$uri = $_REQUEST['uri'];
$caldavBackend->deleteCalendarObject($id, $uri);
+ build_sync_packet($channel['channel_id'], [
+ 'calendar' => [
+ 'action' => 'delete_card',
+ 'uri' => $cdavdata['uri'],
+ 'carduri' => $uri
+ ]
+ ]);
+
killme();
}
@@ -433,9 +552,11 @@ class Cdav extends Controller {
$id = [$_REQUEST['id'][0], $_REQUEST['id'][1]];
- if(!cdav_perms($id[0],$calendars,true))
+ if(! cdav_perms($id[0],$calendars,true))
return;
+ $cdavdata = $this->get_cdav_data($id[0], 'calendarinstances');
+
$timezone = ((x($_POST,'timezone_select')) ? escape_tags(trim($_POST['timezone_select'])) : '');
$tz = (($timezone) ? $timezone : date_default_timezone_get());
@@ -471,9 +592,17 @@ class Cdav extends Controller {
unset($vcalendar->VEVENT->DTEND);
$calendarData = $vcalendar->serialize();
-
$caldavBackend->updateCalendarObject($id, $uri, $calendarData);
+ build_sync_packet($channel['channel_id'], [
+ 'calendar' => [
+ 'action' => 'update_card',
+ 'uri' => $cdavdata['uri'],
+ 'carduri' => $uri,
+ 'card' => $calendarData
+ ]
+ ]);
+
killme();
}
@@ -523,6 +652,14 @@ class Cdav extends Controller {
$properties = ['{DAV:}displayname' => $_REQUEST['{DAV:}displayname']];
$carddavBackend->createAddressBook($principalUri, $addressbookUri, $properties);
+
+ build_sync_packet($channel['channel_id'], [
+ 'addressbook' => [
+ 'action' => 'create',
+ 'uri' => $addressbookUri,
+ 'properties' => $properties
+ ]
+ ]);
}
//edit addressbook
@@ -533,21 +670,32 @@ class Cdav extends Controller {
if(! cdav_perms($id,$addressbooks))
return;
+ $cdavdata = $this->get_cdav_data($id, 'addressbooks');
+
$mutations = [
'{DAV:}displayname' => $_REQUEST['{DAV:}displayname']
];
$patch = new \Sabre\DAV\PropPatch($mutations);
-
$carddavBackend->updateAddressBook($id, $patch);
-
$patch->commit();
+
+ build_sync_packet($channel['channel_id'], [
+ 'addressbook' => [
+ 'action' => 'edit',
+ 'uri' => $cdavdata['uri'],
+ 'mutations' => $mutations,
+ ]
+ ]);
}
//create addressbook card
if($_REQUEST['create'] && $_REQUEST['target'] && $_REQUEST['fn']) {
+
$id = $_REQUEST['target'];
+ $cdavdata = $this->get_cdav_data($id, 'addressbooks');
+
do {
$duplicate = false;
$uri = random_string(40) . '.vcf';
@@ -569,86 +717,21 @@ class Cdav extends Controller {
'N' => array_reverse(explode(' ', $fn))
]);
- $org = $_REQUEST['org'];
- if($org) {
- $vcard->ORG = $org;
- }
-
- $title = $_REQUEST['title'];
- if($title) {
- $vcard->TITLE = $title;
- }
-
- $tel = $_REQUEST['tel'];
- $tel_type = $_REQUEST['tel_type'];
- if($tel) {
- $i = 0;
- foreach($tel as $item) {
- if($item) {
- $vcard->add('TEL', $item, ['type' => $tel_type[$i]]);
- }
- $i++;
- }
- }
+ $fields = $this->request_to_array($_REQUEST);
- $email = $_REQUEST['email'];
- $email_type = $_REQUEST['email_type'];
- if($email) {
- $i = 0;
- foreach($email as $item) {
- if($item) {
- $vcard->add('EMAIL', $item, ['type' => $email_type[$i]]);
- }
- $i++;
- }
- }
-
- $impp = $_REQUEST['impp'];
- $impp_type = $_REQUEST['impp_type'];
- if($impp) {
- $i = 0;
- foreach($impp as $item) {
- if($item) {
- $vcard->add('IMPP', $item, ['type' => $impp_type[$i]]);
- }
- $i++;
- }
- }
-
- $url = $_REQUEST['url'];
- $url_type = $_REQUEST['url_type'];
- if($url) {
- $i = 0;
- foreach($url as $item) {
- if($item) {
- $vcard->add('URL', $item, ['type' => $url_type[$i]]);
- }
- $i++;
- }
- }
-
- $adr = $_REQUEST['adr'];
- $adr_type = $_REQUEST['adr_type'];
-
- if($adr) {
- $i = 0;
- foreach($adr as $item) {
- if($item) {
- $vcard->add('ADR', $item, ['type' => $adr_type[$i]]);
- }
- $i++;
- }
- }
-
- $note = $_REQUEST['note'];
- if($note) {
- $vcard->NOTE = $note;
- }
+ process_cdav_card($fields, $vcard);
$cardData = $vcard->serialize();
-
$carddavBackend->createCard($id, $uri, $cardData);
+ build_sync_packet($channel['channel_id'], [
+ 'addressbook' => [
+ 'action' => 'import',
+ 'uri' => $cdavdata['uri'],
+ 'ids' => [ $uri ],
+ 'card' => $cardData
+ ]
+ ]);
}
//edit addressbook card
@@ -656,9 +739,11 @@ class Cdav extends Controller {
$id = $_REQUEST['target'];
- if(!cdav_perms($id,$addressbooks))
+ if(! cdav_perms($id,$addressbooks))
return;
+ $cdavdata = $this->get_cdav_data($id, 'addressbooks');
+
$uri = $_REQUEST['uri'];
$object = $carddavBackend->getCard($id, $uri);
@@ -670,113 +755,23 @@ class Cdav extends Controller {
$vcard->N = array_reverse(explode(' ', $fn));
}
- $org = $_REQUEST['org'];
- if($org) {
- $vcard->ORG = $org;
- }
- else {
- unset($vcard->ORG);
- }
-
- $title = $_REQUEST['title'];
- if($title) {
- $vcard->TITLE = $title;
- }
- else {
- unset($vcard->TITLE);
- }
-
- $tel = $_REQUEST['tel'];
- $tel_type = $_REQUEST['tel_type'];
- if($tel) {
- $i = 0;
- unset($vcard->TEL);
- foreach($tel as $item) {
- if($item) {
- $vcard->add('TEL', $item, ['type' => $tel_type[$i]]);
- }
- $i++;
- }
- }
- else {
- unset($vcard->TEL);
- }
-
- $email = $_REQUEST['email'];
- $email_type = $_REQUEST['email_type'];
- if($email) {
- $i = 0;
- unset($vcard->EMAIL);
- foreach($email as $item) {
- if($item) {
- $vcard->add('EMAIL', $item, ['type' => $email_type[$i]]);
- }
- $i++;
- }
- }
- else {
- unset($vcard->EMAIL);
- }
-
- $impp = $_REQUEST['impp'];
- $impp_type = $_REQUEST['impp_type'];
- if($impp) {
- $i = 0;
- unset($vcard->IMPP);
- foreach($impp as $item) {
- if($item) {
- $vcard->add('IMPP', $item, ['type' => $impp_type[$i]]);
- }
- $i++;
- }
- }
- else {
- unset($vcard->IMPP);
- }
-
- $url = $_REQUEST['url'];
- $url_type = $_REQUEST['url_type'];
- if($url) {
- $i = 0;
- unset($vcard->URL);
- foreach($url as $item) {
- if($item) {
- $vcard->add('URL', $item, ['type' => $url_type[$i]]);
- }
- $i++;
- }
- }
- else {
- unset($vcard->URL);
- }
-
- $adr = $_REQUEST['adr'];
- $adr_type = $_REQUEST['adr_type'];
- if($adr) {
- $i = 0;
- unset($vcard->ADR);
- foreach($adr as $item) {
- if($item) {
- $vcard->add('ADR', $item, ['type' => $adr_type[$i]]);
- }
- $i++;
- }
- }
- else {
- unset($vcard->ADR);
- }
+ $fields = $this->request_to_array($_REQUEST);
- $note = $_REQUEST['note'];
- if($note) {
- $vcard->NOTE = $note;
- }
- else {
- unset($vcard->NOTE);
- }
+ process_cdav_card($fields, $vcard, true);
$cardData = $vcard->serialize();
$carddavBackend->updateCard($id, $uri, $cardData);
+
+ build_sync_packet($channel['channel_id'], [
+ 'addressbook' => [
+ 'action' => 'update_card',
+ 'uri' => $cdavdata['uri'],
+ 'carduri' => $uri,
+ 'card' => $cardData
+ ]
+ ]);
+
}
//delete addressbook card
@@ -784,12 +779,22 @@ class Cdav extends Controller {
$id = $_REQUEST['target'];
- if(!cdav_perms($id,$addressbooks))
+ if(! cdav_perms($id,$addressbooks))
return;
+ $cdavdata = $this->get_cdav_data($id, 'addressbooks');
+
$uri = $_REQUEST['uri'];
$carddavBackend->deleteCard($id, $uri);
+
+ build_sync_packet($channel['channel_id'], [
+ 'addressbook' => [
+ 'action' => 'delete_card',
+ 'uri' => $cdavdata['uri'],
+ 'carduri' => $uri
+ ]
+ ]);
}
}
@@ -799,6 +804,8 @@ class Cdav extends Controller {
$src = $_FILES['userfile']['tmp_name'];
if($src) {
+
+ $carddata = @file_get_contents($src);
if($_REQUEST['c_upload']) {
if($_REQUEST['target'] == 'channel_calendar') {
@@ -812,76 +819,42 @@ class Cdav extends Controller {
return;
}
- $id = explode(':', $_REQUEST['target']);
+ $id = explode(':', $_REQUEST['target'])[0];
$ext = 'ics';
$table = 'calendarobjects';
$column = 'calendarid';
- $objects = new \Sabre\VObject\Splitter\ICalendar(@file_get_contents($src));
+ $sync = 'calendar';
+ $objects = new \Sabre\VObject\Splitter\ICalendar($carddata);
$profile = \Sabre\VObject\Node::PROFILE_CALDAV;
$backend = new \Sabre\CalDAV\Backend\PDO($pdo);
+
+ $cdavdata = $this->get_cdav_data($id, 'calendarinstances');
}
if($_REQUEST['a_upload']) {
- $id[] = intval($_REQUEST['target']);
+ $id = intval($_REQUEST['target']);
$ext = 'vcf';
$table = 'cards';
$column = 'addressbookid';
- $objects = new \Sabre\VObject\Splitter\VCard(@file_get_contents($src));
+ $sync = 'addressbook';
+ $objects = new \Sabre\VObject\Splitter\VCard($carddata);
$profile = \Sabre\VObject\Node::PROFILE_CARDDAV;
$backend = new \Sabre\CardDAV\Backend\PDO($pdo);
+
+ $cdavdata = $this->get_cdav_data($id, 'addressbooks');
}
-
- while ($object = $objects->getNext()) {
-
- if($_REQUEST['a_upload']) {
- $object = $object->convert(\Sabre\VObject\Document::VCARD40);
- }
-
- $ret = $object->validate($profile & \Sabre\VObject\Node::REPAIR);
-
- //level 3 Means that the document is invalid,
- //level 2 means a warning. A warning means it's valid but it could cause interopability issues,
- //level 1 means that there was a problem earlier, but the problem was automatically repaired.
-
- if($ret[0]['level'] < 3) {
- do {
- $duplicate = false;
- $objectUri = random_string(40) . '.' . $ext;
-
- $r = q("SELECT uri FROM $table WHERE $column = %d AND uri = '%s' LIMIT 1",
- dbesc($id[0]),
- dbesc($objectUri)
- );
-
- if (count($r))
- $duplicate = true;
- } while ($duplicate == true);
-
- if($_REQUEST['c_upload']) {
- $backend->createCalendarObject($id, $objectUri, $object->serialize());
- }
-
- if($_REQUEST['a_upload']) {
- $backend->createCard($id[0], $objectUri, $object->serialize());
- }
- }
- else {
- if($_REQUEST['c_upload']) {
- notice( '<strong>' . t('INVALID EVENT DISMISSED!') . '</strong>' . EOL .
- '<strong>' . t('Summary: ') . '</strong>' . (($object->VEVENT->SUMMARY) ? $object->VEVENT->SUMMARY : t('Unknown')) . EOL .
- '<strong>' . t('Date: ') . '</strong>' . (($object->VEVENT->DTSTART) ? $object->VEVENT->DTSTART : t('Unknown')) . EOL .
- '<strong>' . t('Reason: ') . '</strong>' . $ret[0]['message'] . EOL
- );
- }
-
- if($_REQUEST['a_upload']) {
- notice( '<strong>' . t('INVALID CARD DISMISSED!') . '</strong>' . EOL .
- '<strong>' . t('Name: ') . '</strong>' . (($object->FN) ? $object->FN : t('Unknown')) . EOL .
- '<strong>' . t('Reason: ') . '</strong>' . $ret[0]['message'] . EOL
- );
- }
- }
- }
+
+ $ids = [];
+ import_cdav_card($id, $ext, $table, $column, $objects, $profile, $backend, $ids, true);
+
+ build_sync_packet($channel['channel_id'], [
+ $sync => [
+ 'action' => 'import',
+ 'uri' => $cdavdata['uri'],
+ 'ids' => $ids,
+ 'card' => $carddata
+ ]
+ ]);
}
@unlink($src);
}
@@ -1190,7 +1163,18 @@ class Cdav extends Controller {
if(! cdav_perms($id,$calendars))
killme();
- set_pconfig(local_channel(), 'cdav_calendar' , argv(3), argv(4));
+ $cdavdata = $this->get_cdav_data($id, 'calendarinstances');
+
+ set_pconfig(local_channel(), 'cdav_calendar', $id, argv(4));
+
+ build_sync_packet(local_channel(), [
+ 'calendar' => [
+ 'action' => 'switch',
+ 'uri' => $cdavdata['uri'],
+ 'switch' => intval(argv(4))
+ ]
+ ]);
+
killme();
}
@@ -1201,7 +1185,18 @@ class Cdav extends Controller {
if(! cdav_perms($id[0],$calendars))
killme();
+ // get metadata before we delete it
+ $cdavdata = $this->get_cdav_data($id[0], 'calendarinstances');
+
$caldavBackend->deleteCalendar($id);
+
+ build_sync_packet($channel['channel_id'], [
+ 'calendar' => [
+ 'action' => 'drop',
+ 'uri' => $cdavdata['uri']
+ ]
+ ]);
+
killme();
}
@@ -1408,7 +1403,19 @@ class Cdav extends Controller {
if(! cdav_perms($id,$addressbooks))
return;
+ // get metadata before we delete it
+ $cdavdata = $this->get_cdav_data($id, 'addressbooks');
+
$carddavBackend->deleteAddressBook($id);
+
+ if($cdavdata)
+ build_sync_packet($channel['channel_id'], [
+ 'addressbook' => [
+ 'action' => 'drop',
+ 'uri' => $cdavdata['uri']
+ ]
+ ]);
+
killme();
}
@@ -1460,4 +1467,36 @@ class Cdav extends Controller {
}
+ function get_cdav_data($id, $table) {
+
+ $r = q("SELECT * FROM $table WHERE id = %d LIMIT 1",
+ intval($id)
+ );
+
+ if(! $r)
+ return false;
+
+ return $r[0];
+ }
+
+ function request_to_array($req) {
+
+ $f = [];
+
+ $f['org'] = $req['org'];
+ $f['title'] = $req['title'];
+ $f['tel'] = $req['tel'];
+ $f['tel_type'] = $req['tel_type'];
+ $f['email'] = $req['email'];
+ $f['email_type'] = $req['email_type'];
+ $f['impp'] = $req['impp'];
+ $f['impp_type'] = $req['impp_type'];
+ $f['url'] = $req['url'];
+ $f['url_type'] = $req['url_type'];
+ $f['adr'] = $req['adr'];
+ $f['adr_type'] = $req['adr_type'];
+ $f['note'] = $req['note'];
+
+ return $f;
+ }
}
diff --git a/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php
index 1a25e54df..86b5c1c7a 100644
--- a/Zotlabs/Module/Item.php
+++ b/Zotlabs/Module/Item.php
@@ -271,7 +271,9 @@ class Item extends Controller {
$consensus = intval($_REQUEST['consensus']);
$nocomment = intval($_REQUEST['nocomment']);
-
+
+ $is_poll = ((trim($_REQUEST['poll_answers'][0]) != '' && trim($_REQUEST['poll_answers'][1]) != '') ? true : false);
+
// 'origin' (if non-zero) indicates that this network is where the message originated,
// for the purpose of relaying comments to other conversation members.
// If using the API from a device (leaf node) you must set origin to 1 (default) or leave unset.
@@ -718,7 +720,6 @@ class Item extends Controller {
// BBCODE alert: the following functions assume bbcode input
// and will require alternatives for alternative content-types (text/html, text/markdown, text/plain, etc.)
// we may need virtual or template classes to implement the possible alternatives
-
if(strpos($body,'[/summary]') !== false) {
$match = '';
@@ -922,6 +923,27 @@ class Item extends Controller {
}
+ if($is_poll) {
+ $poll = [
+ 'question' => $body,
+ 'answers' => $_REQUEST['poll_answers'],
+ 'multiple_answers' => $_REQUEST['poll_multiple_answers'],
+ 'expire_value' => $_REQUEST['poll_expire_value'],
+ 'expire_unit' => $_REQUEST['poll_expire_unit']
+ ];
+ $obj = $this->extract_poll_data($poll, [ 'item_private' => $private, 'allow_cid' => $str_contact_allow, 'allow_gid' => $str_contact_deny ]);
+ }
+ else {
+ $obj = $this->extract_bb_poll_data($body,[ 'item_private' => $private, 'allow_cid' => $str_contact_allow, 'allow_gid' => $str_contact_deny ]);
+ }
+
+ if ($obj) {
+ $obj['url'] = $mid;
+ $obj['attributedTo'] = channel_url($channel);
+ $datarray['obj'] = $obj;
+ $obj_type = 'Question';
+ }
+
if(! $parent_mid) {
$parent_mid = $mid;
}
@@ -970,7 +992,11 @@ class Item extends Controller {
$plink = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . gen_link_id($mid);
$plink = substr($plink,0,190);
}
-
+
+ if ($datarray['obj']) {
+ $datarray['obj']['id'] = $mid;
+ }
+
$datarray['aid'] = $channel['channel_account_id'];
$datarray['uid'] = $profile_uid;
$datarray['uuid'] = $uuid;
@@ -1387,5 +1413,104 @@ class Item extends Controller {
return $ret;
}
-
+ function extract_bb_poll_data(&$body,$item) {
+
+ $multiple = false;
+
+ if (strpos($body,'[/question]') === false && strpos($body,'[/answer]') === false) {
+ return false;
+ }
+ if (strpos($body,'[nobb]') !== false) {
+ return false;
+ }
+
+
+ $obj = [];
+ $ptr = [];
+ $matches = null;
+ $obj['type'] = 'Question';
+
+ if (preg_match_all('/\[answer\](.*?)\[\/answer\]/ism',$body,$matches,PREG_SET_ORDER)) {
+ foreach ($matches as $match) {
+ $ptr[] = [ 'name' => $match[1], 'type' => 'Note', 'replies' => [ 'type' => 'Collection', 'totalItems' => 0 ]];
+ $body = str_replace('[answer]' . $match[1] . '[/answer]', EMPTY_STR, $body);
+ }
+ }
+
+ $matches = null;
+
+ if (preg_match('/\[question\](.*?)\[\/question\]/ism',$body,$matches)) {
+ $obj['content'] = bbcode($matches[1]);
+ $body = str_replace('[question]' . $matches[1] . '[/question]', $matches[1], $body);
+ $obj['oneOf'] = $ptr;
+ }
+
+ $matches = null;
+
+ if (preg_match('/\[question=multiple\](.*?)\[\/question\]/ism',$body,$matches)) {
+ $obj['content'] = bbcode($matches[1]);
+ $body = str_replace('[question=multiple]' . $matches[1] . '[/question]', $matches[1], $body);
+ $obj['anyOf'] = $ptr;
+ }
+
+ $matches = null;
+
+ if (preg_match('/\[ends\](.*?)\[\/ends\]/ism',$body,$matches)) {
+ $obj['endTime'] = datetime_convert(date_default_timezone_get(),'UTC', $matches[1],ATOM_TIME);
+ $body = str_replace('[ends]' . $matches[1] . '[/ends]', EMPTY_STR, $body);
+ }
+
+
+ if ($item['item_private']) {
+ $obj['to'] = Activity::map_acl($item);
+ }
+ else {
+ $obj['to'] = [ ACTIVITY_PUBLIC_INBOX ];
+ }
+
+ return $obj;
+
+ }
+
+
+ function extract_poll_data($poll, $item) {
+
+ $multiple = intval($poll['multiple_answers']);
+ $expire_value = intval($poll['expire_value']);
+ $expire_unit = $poll['expire_unit'];
+ $question = $poll['question'];
+ $answers = $poll['answers'];
+
+ $obj = [];
+ $ptr = [];
+ $obj['type'] = 'Question';
+ $obj['content'] = bbcode($question);
+
+ foreach($answers as $answer) {
+ if(trim($answer))
+ $ptr[] = [ 'name' => escape_tags($answer), 'type' => 'Note', 'replies' => [ 'type' => 'Collection', 'totalItems' => 0 ]];
+ }
+
+ if($multiple) {
+ $obj['anyOf'] = $ptr;
+ }
+ else {
+ $obj['oneOf'] = $ptr;
+ }
+
+ $obj['endTime'] = datetime_convert(date_default_timezone_get(), 'UTC', 'now + ' . $expire_value . ' ' . $expire_unit, ATOM_TIME);
+
+ if ($item['item_private']) {
+ $obj['to'] = Activity::map_acl($item);
+ }
+ else {
+ $obj['to'] = [ ACTIVITY_PUBLIC_INBOX ];
+ }
+
+ return $obj;
+
+ }
+
+
+
}
diff --git a/Zotlabs/Module/Network.php b/Zotlabs/Module/Network.php
index a8efd0d9e..adfdc011b 100644
--- a/Zotlabs/Module/Network.php
+++ b/Zotlabs/Module/Network.php
@@ -340,7 +340,7 @@ class Network extends \Zotlabs\Web\Controller {
// The special div is needed for liveUpdate to kick in for this page.
// We only launch liveUpdate if you aren't filtering in some incompatible
// way and also you aren't writing a comment (discovered in javascript).
-
+
$maxheight = get_pconfig(local_channel(),'system','network_divmore_height');
if(! $maxheight)
$maxheight = 400;
@@ -411,10 +411,24 @@ class Network extends \Zotlabs\Web\Controller {
}
}
- if($verb) {
- $sql_extra .= sprintf(" AND item.verb like '%s' ",
- dbesc(protect_sprintf('%' . $verb . '%'))
- );
+ if ($verb) {
+
+ // the presence of a leading dot in the verb determines
+ // whether to match the type of activity or the child object.
+ // The name 'verb' is a holdover from the earlier XML
+ // ActivityStreams specification.
+
+ if (substr($verb,0,1) === '.') {
+ $verb = substr($verb,1);
+ $sql_extra .= sprintf(" AND item.obj_type like '%s' ",
+ dbesc(protect_sprintf('%' . $verb . '%'))
+ );
+ }
+ else {
+ $sql_extra .= sprintf(" AND item.verb like '%s' ",
+ dbesc(protect_sprintf('%' . $verb . '%'))
+ );
+ }
}
if(strlen($file)) {
diff --git a/Zotlabs/Module/Vote.php b/Zotlabs/Module/Vote.php
new file mode 100644
index 000000000..d67a6f176
--- /dev/null
+++ b/Zotlabs/Module/Vote.php
@@ -0,0 +1,143 @@
+<?php
+namespace Zotlabs\Module;
+
+use App;
+use Zotlabs\Web\Controller;
+use Zotlabs\Lib\Activity;
+use Zotlabs\Daemon\Master;
+use Zotlabs\Lib\Libsync;
+
+class Vote extends Controller {
+
+ function init() {
+
+ $ret = [ 'success' => false, 'message' => EMPTY_STR ];
+
+ $channel = App::get_channel();
+
+ if (! $channel) {
+ $ret['message'] = t('Permission denied.');
+ json_return_and_die($ret);
+ }
+
+
+ $fetch = null;
+ $id = argv(1);
+ $response = $_REQUEST['answer'];
+
+ if ($id) {
+ $fetch = q("select * from item where id = %d limit 1",
+ intval($id)
+ );
+ }
+
+
+ if ($fetch && $fetch[0]['obj_type'] === 'Question') {
+ $obj = json_decode($fetch[0]['obj'],true);
+
+ }
+ else {
+ $ret['message'] = t('Poll not found.');
+ json_return_and_die($ret);
+ }
+
+ $valid = false;
+
+ if ($obj['oneOf']) {
+ foreach($obj['oneOf'] as $selection) {
+ // logger('selection: ' . $selection);
+ // logger('response: ' . $response);
+ if($selection['name'] && $selection['name'] === $response) {
+ $valid = true;
+ }
+ }
+ }
+
+ $choices = [];
+ if ($obj['anyOf']) {
+ foreach ($obj['anyOf'] as $selection) {
+ $choices[] = $selection['name'];
+ }
+ foreach ($response as $res) {
+ if (! in_array($res,$choices)) {
+ $valid = false;
+ break;
+ }
+ $valid = true;
+ }
+ }
+
+ if (! $valid) {
+ $ret['message'] = t('Invalid response.');
+ json_return_and_die($ret);
+ }
+
+ if (! is_array($response)) {
+ $response = [ $response ];
+ }
+
+ foreach ($response as $res) {
+
+ $item = [];
+
+
+ $item['aid'] = $channel['channel_account_id'];
+ $item['uid'] = $channel['channel_id'];
+ $item['item_origin'] = 1;
+ $item['parent'] = $fetch[0]['id'];
+ $item['parent_mid'] = $fetch[0]['mid'];
+ $item['thr_parent'] = $fetch[0]['mid'];
+ $item['uuid'] = new_uuid();
+ $item['mid'] = z_root() . '/item/' . $item['uuid'];
+ $item['verb'] = 'Create';
+ $item['title'] = $res;
+ $item['author_xchan'] = $channel['channel_hash'];
+ $item['owner_xchan'] = $fetch[0]['author_xchan'];
+ $item['allow_cid'] = '<' . $fetch[0]['author_xchan'] . '>';
+ $item['item_private'] = 1;
+
+
+ $item['obj_type'] = 'Note';
+ $item['author'] = channelx_by_n($channel['channel_id']);
+
+ $item['obj'] = Activity::encode_item($item);
+
+ // now reset the placeholders
+
+ $item['verb'] = ACTIVITY_POST;
+ $item['obj_type'] = 'Answer';
+ unset($item['author']);
+
+
+ $x = item_store($item);
+
+
+ retain_item($fetch[0]['id']);
+
+ if($x['success']) {
+ $itemid = $x['item_id'];
+ Master::Summon( [ 'Notifier', 'like', $itemid ] );
+ }
+
+ $r = q("select * from item where id = %d",
+ intval($itemid)
+ );
+ if ($r) {
+ xchan_query($r);
+ $sync_item = fetch_post_tags($r);
+ Libsync::build_sync_packet($channel['channel_id'], [ 'item' => [ encode_item($sync_item[0],true) ] ]);
+ }
+ }
+ $ret['success'] = true;
+ $ret['message'] = t('Response submitted. Updates may not appear instantly.');
+ json_return_and_die($ret);
+ }
+}
+
+
+
+
+
+
+
+
diff --git a/Zotlabs/Module/Well_known.php b/Zotlabs/Module/Well_known.php
index 140ab260d..0d7b222b8 100644
--- a/Zotlabs/Module/Well_known.php
+++ b/Zotlabs/Module/Well_known.php
@@ -65,11 +65,6 @@ class Well_known extends \Zotlabs\Web\Controller {
killme();
case 'caldav':
- if ($_SERVER['REQUEST_METHOD'] == 'PROPFIND') {
- http_status('301', 'moved permanently');
- goaway(z_root() . '/cdav');
- };
-
case 'carddav':
if ($_SERVER['REQUEST_METHOD'] == 'PROPFIND') {
http_status('301', 'moved permanently');
diff --git a/Zotlabs/Widget/Activity_filter.php b/Zotlabs/Widget/Activity_filter.php
index 0fc60ca9b..002a642cb 100644
--- a/Zotlabs/Widget/Activity_filter.php
+++ b/Zotlabs/Widget/Activity_filter.php
@@ -22,6 +22,13 @@ class Activity_filter {
$filter_active = 'dm';
}
+ if(x($_GET,'verb')) {
+ $events_active = (($_GET['verb'] == '.Event') ? 'active' : '');
+ $polls_active = (($_GET['verb'] == '.Question') ? 'active' : '');
+ $filter_active = (($events_active) ? 'events' : 'polls');
+ }
+
+
$tabs[] = [
'label' => t('Direct Messages'),
'icon' => 'envelope-o',
@@ -30,6 +37,27 @@ class Activity_filter {
'title' => t('Show direct (private) messages')
];
+ if(feature_enabled(local_channel(),'events_tab')) {
+ $tabs[] = [
+ 'label' => t('Events'),
+ 'icon' => 'calendar',
+ 'url' => z_root() . '/' . $cmd . '/?verb=%2EEvent',
+ 'sel' => $events_active,
+ 'title' => t('Show posts that include events')
+ ];
+ }
+
+ if(feature_enabled(local_channel(),'polls_tab')) {
+ $tabs[] = [
+ 'label' => t('Polls'),
+ 'icon' => 'bar-chart',
+ 'url' => z_root() . '/' . $cmd . '/?verb=%2EQuestion',
+ 'sel' => $polls_active,
+ 'title' => t('Show posts that include polls')
+ ];
+ }
+
+
if(Apps::system_app_installed(local_channel(), 'Privacy Groups')) {
$groups = q("SELECT * FROM pgrp WHERE deleted = 0 AND uid = %d ORDER BY gname ASC",
intval(local_channel())
diff --git a/boot.php b/boot.php
index 245b7aa9f..ec96045e9 100755
--- a/boot.php
+++ b/boot.php
@@ -50,7 +50,7 @@ require_once('include/attach.php');
require_once('include/bbcode.php');
define ( 'PLATFORM_NAME', 'hubzilla' );
-define ( 'STD_VERSION', '4.7.1' );
+define ( 'STD_VERSION', '4.7.3' );
define ( 'ZOT_REVISION', '6.0a' );
define ( 'DB_UPDATE_VERSION', 1235 );
@@ -473,7 +473,7 @@ define ( 'NAMESPACE_YMEDIA', 'http://search.yahoo.com/mrss/' );
define ( 'ACTIVITYSTREAMS_JSONLD_REV', 'https://www.w3.org/ns/activitystreams' );
-define ( 'ZOT_APSCHEMA_REV', '/apschema/v1.8' );
+define ( 'ZOT_APSCHEMA_REV', '/apschema/v1.9' );
/**
* activity stream defines
*/
diff --git a/images/article.gif b/images/article.gif
deleted file mode 100644
index 91aeef000..000000000
--- a/images/article.gif
+++ /dev/null
Binary files differ
diff --git a/images/audio.gif b/images/audio.gif
deleted file mode 100644
index 4be977116..000000000
--- a/images/audio.gif
+++ /dev/null
Binary files differ
diff --git a/images/b_block.gif b/images/b_block.gif
deleted file mode 100644
index 3bc7c056b..000000000
--- a/images/b_block.gif
+++ /dev/null
Binary files differ
diff --git a/images/b_drop.gif b/images/b_drop.gif
deleted file mode 100644
index b08c68b62..000000000
--- a/images/b_drop.gif
+++ /dev/null
Binary files differ
diff --git a/images/b_drop.png b/images/b_drop.png
deleted file mode 100644
index 6fc4d3b20..000000000
--- a/images/b_drop.png
+++ /dev/null
Binary files differ
diff --git a/images/b_drophide.gif b/images/b_drophide.gif
deleted file mode 100644
index 1207a935b..000000000
--- a/images/b_drophide.gif
+++ /dev/null
Binary files differ
diff --git a/images/b_dropshow.gif b/images/b_dropshow.gif
deleted file mode 100644
index b08c68b62..000000000
--- a/images/b_dropshow.gif
+++ /dev/null
Binary files differ
diff --git a/images/b_edit.gif b/images/b_edit.gif
deleted file mode 100644
index 79cb3c144..000000000
--- a/images/b_edit.gif
+++ /dev/null
Binary files differ
diff --git a/images/b_edit.png b/images/b_edit.png
deleted file mode 100644
index 05711a094..000000000
--- a/images/b_edit.png
+++ /dev/null
Binary files differ
diff --git a/images/bug-x.gif b/images/bug-x.gif
deleted file mode 100644
index 10936caa7..000000000
--- a/images/bug-x.gif
+++ /dev/null
Binary files differ
diff --git a/images/calendar.png b/images/calendar.png
deleted file mode 100644
index fbf52933d..000000000
--- a/images/calendar.png
+++ /dev/null
Binary files differ
diff --git a/images/camera-icon.gif b/images/camera-icon.gif
deleted file mode 100644
index a4adf9adf..000000000
--- a/images/camera-icon.gif
+++ /dev/null
Binary files differ
diff --git a/images/checkbox-checked-32.png b/images/checkbox-checked-32.png
deleted file mode 100644
index 01e51c203..000000000
--- a/images/checkbox-checked-32.png
+++ /dev/null
Binary files differ
diff --git a/images/checkbox-unchecked-32.png b/images/checkbox-unchecked-32.png
deleted file mode 100644
index 80ce92243..000000000
--- a/images/checkbox-unchecked-32.png
+++ /dev/null
Binary files differ
diff --git a/images/connect-bg.png b/images/connect-bg.png
deleted file mode 100644
index 0611c73e5..000000000
--- a/images/connect-bg.png
+++ /dev/null
Binary files differ
diff --git a/images/content-types.png b/images/content-types.png
deleted file mode 100644
index e46eba610..000000000
--- a/images/content-types.png
+++ /dev/null
Binary files differ
diff --git a/images/default-group-mm.png b/images/default-group-mm.png
deleted file mode 100644
index bfc8b335a..000000000
--- a/images/default-group-mm.png
+++ /dev/null
Binary files differ
diff --git a/images/document.gif b/images/document.gif
deleted file mode 100644
index 02d940968..000000000
--- a/images/document.gif
+++ /dev/null
Binary files differ
diff --git a/images/ghash-32.png b/images/ghash-32.png
deleted file mode 100644
index 6b4913b82..000000000
--- a/images/ghash-32.png
+++ /dev/null
Binary files differ
diff --git a/images/globe.gif b/images/globe.gif
deleted file mode 100644
index 3f17c5d32..000000000
--- a/images/globe.gif
+++ /dev/null
Binary files differ
diff --git a/images/hide_off.png b/images/hide_off.png
deleted file mode 100644
index d504ca2e2..000000000
--- a/images/hide_off.png
+++ /dev/null
Binary files differ
diff --git a/images/hide_on.png b/images/hide_on.png
deleted file mode 100644
index 960744c06..000000000
--- a/images/hide_on.png
+++ /dev/null
Binary files differ
diff --git a/images/hubzilla_house_arrows.png b/images/hubzilla_house_arrows.png
deleted file mode 100644
index 56402a96b..000000000
--- a/images/hubzilla_house_arrows.png
+++ /dev/null
Binary files differ
diff --git a/images/hz-bookmark-32.png b/images/hz-bookmark-32.png
deleted file mode 100644
index f3a09d9b4..000000000
--- a/images/hz-bookmark-32.png
+++ /dev/null
Binary files differ
diff --git a/images/icons.png b/images/icons.png
deleted file mode 100644
index 34d77a136..000000000
--- a/images/icons.png
+++ /dev/null
Binary files differ
diff --git a/images/larrow.gif b/images/larrow.gif
deleted file mode 100644
index ab08bb57e..000000000
--- a/images/larrow.gif
+++ /dev/null
Binary files differ
diff --git a/images/larrw.gif b/images/larrw.gif
deleted file mode 100644
index 08902d772..000000000
--- a/images/larrw.gif
+++ /dev/null
Binary files differ
diff --git a/images/link-icon.gif b/images/link-icon.gif
deleted file mode 100644
index c012d716e..000000000
--- a/images/link-icon.gif
+++ /dev/null
Binary files differ
diff --git a/images/lock_icon.gif b/images/lock_icon.gif
deleted file mode 100644
index b6b1b7fed..000000000
--- a/images/lock_icon.gif
+++ /dev/null
Binary files differ
diff --git a/images/lrarrow.gif b/images/lrarrow.gif
deleted file mode 100644
index fa2676944..000000000
--- a/images/lrarrow.gif
+++ /dev/null
Binary files differ
diff --git a/images/mapicon.gif b/images/mapicon.gif
deleted file mode 100644
index dd20c209b..000000000
--- a/images/mapicon.gif
+++ /dev/null
Binary files differ
diff --git a/images/no.gif b/images/no.gif
deleted file mode 100644
index eb0f2b0e9..000000000
--- a/images/no.gif
+++ /dev/null
Binary files differ
diff --git a/images/noglobe.gif b/images/noglobe.gif
deleted file mode 100644
index 81e176567..000000000
--- a/images/noglobe.gif
+++ /dev/null
Binary files differ
diff --git a/images/nosign.jpg b/images/nosign.jpg
deleted file mode 100644
index b73629332..000000000
--- a/images/nosign.jpg
+++ /dev/null
Binary files differ
diff --git a/images/nosign.png b/images/nosign.png
deleted file mode 100644
index 773c9514d..000000000
--- a/images/nosign.png
+++ /dev/null
Binary files differ
diff --git a/images/onoff.jpg b/images/onoff.jpg
deleted file mode 100644
index 7884912a7..000000000
--- a/images/onoff.jpg
+++ /dev/null
Binary files differ
diff --git a/images/pen.png b/images/pen.png
deleted file mode 100644
index 46b404941..000000000
--- a/images/pen.png
+++ /dev/null
Binary files differ
diff --git a/images/pencil.gif b/images/pencil.gif
deleted file mode 100644
index 26bfb0c9a..000000000
--- a/images/pencil.gif
+++ /dev/null
Binary files differ
diff --git a/images/penhover.png b/images/penhover.png
deleted file mode 100644
index be48d77b4..000000000
--- a/images/penhover.png
+++ /dev/null
Binary files differ
diff --git a/images/people.gif b/images/people.gif
deleted file mode 100644
index cac31db2a..000000000
--- a/images/people.gif
+++ /dev/null
Binary files differ
diff --git a/images/plugin.png b/images/plugin.png
deleted file mode 100644
index 08b09e060..000000000
--- a/images/plugin.png
+++ /dev/null
Binary files differ
diff --git a/images/rarrow.gif b/images/rarrow.gif
deleted file mode 100644
index a2d5df3e9..000000000
--- a/images/rarrow.gif
+++ /dev/null
Binary files differ
diff --git a/images/rarrw.gif b/images/rarrw.gif
deleted file mode 100644
index 849238c2d..000000000
--- a/images/rarrw.gif
+++ /dev/null
Binary files differ
diff --git a/images/recycle.gif b/images/recycle.gif
deleted file mode 100644
index 01b3e13b4..000000000
--- a/images/recycle.gif
+++ /dev/null
Binary files differ
diff --git a/images/red_antiprism.png b/images/red_antiprism.png
deleted file mode 100644
index 80637adf5..000000000
--- a/images/red_antiprism.png
+++ /dev/null
Binary files differ
diff --git a/images/red_antiprism.xcf b/images/red_antiprism.xcf
deleted file mode 100644
index bd3dd826c..000000000
--- a/images/red_antiprism.xcf
+++ /dev/null
Binary files differ
diff --git a/images/redmatrix_logo.svg b/images/redmatrix_logo.svg
deleted file mode 100644
index 840c3e624..000000000
--- a/images/redmatrix_logo.svg
+++ /dev/null
@@ -1,85 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="200"
- height="200"
- id="svg3053"
- version="1.1"
- inkscape:version="0.48.4 r9939"
- sodipodi:docname="hubzilla.svg">
- <defs
- id="defs3055" />
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="1.979899"
- inkscape:cx="35.049163"
- inkscape:cy="27.799654"
- inkscape:document-units="px"
- inkscape:current-layer="layer1"
- showgrid="false"
- fit-margin-top="0"
- fit-margin-left="0"
- fit-margin-right="0"
- fit-margin-bottom="0"
- inkscape:window-width="1871"
- inkscape:window-height="1056"
- inkscape:window-x="49"
- inkscape:window-y="24"
- inkscape:window-maximized="1" />
- <metadata
- id="metadata3058">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- inkscape:label="Laag 1"
- inkscape:groupmode="layer"
- id="layer1"
- transform="translate(-315.00002,-392.36223)"
- style="display:inline">
- <g
- id="g2985"
- transform="matrix(4.7619048,0,0,4.7619048,-1370.7143,-2042.6958)">
- <path
- style="fill:#c60032;fill-opacity:1"
- d="m 218.0851,397.84091 c 0,12.77893 -10.00215,23.1383 -22.34043,23.1383 -12.33827,0 -22.34042,-10.35937 -22.34042,-23.1383 0,-12.77893 10.00215,-23.1383 22.34042,-23.1383 12.33828,0 22.34043,10.35937 22.34043,23.1383 z"
- sodipodi:ry="23.138298"
- sodipodi:rx="22.340425"
- sodipodi:cy="397.84091"
- sodipodi:cx="195.74467"
- id="path3028-4-5-3"
- sodipodi:type="arc"
- transform="matrix(0.94,0,0,0.9075862,191.00001,171.28726)" />
- <g
- id="text3003-0-4-0"
- style="font-size:46px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans"
- transform="translate(52.138256,-208.57143)">
- <path
- inkscape:connector-curvature="0"
- id="path3008"
- style="font-size:45.09999847px;font-variant:normal;font-weight:bold;font-stretch:normal;fill:#ffffff;font-family:generic;-inkscape-font-specification:generic Bold"
- d="m 322.85053,756.03406 4.7355,0 1.3079,-7.9827 4.8708,0 0,-4.4649 -4.1492,0 1.0373,-6.4944 4.9159,0 0,-4.4649 -4.1492,0 1.1275,-7.0356 -4.7355,0 -1.1275,7.0356 -5.1865,0 1.1275,-7.0356 -4.7355,0 -1.1275,7.0356 -5.0963,0 0,4.4649 4.3296,0 -1.0373,6.4944 -5.0963,0 0,4.4649 4.3747,0 -1.3079,7.9827 4.7355,0 1.3079,-7.9827 5.1865,0 -1.3079,7.9827 m 2.0295,-12.4476 -5.1865,0 1.0373,-6.4944 5.1865,0 -1.0373,6.4944" />
- </g>
- </g>
- </g>
-</svg>
diff --git a/images/remote-link.gif b/images/remote-link.gif
deleted file mode 100644
index 64de29aee..000000000
--- a/images/remote-link.gif
+++ /dev/null
Binary files differ
diff --git a/images/rhash-16.png b/images/rhash-16.png
deleted file mode 100644
index 23dd9e4b3..000000000
--- a/images/rhash-16.png
+++ /dev/null
Binary files differ
diff --git a/images/rhash-32.png b/images/rhash-32.png
deleted file mode 100644
index 564556d48..000000000
--- a/images/rhash-32.png
+++ /dev/null
Binary files differ
diff --git a/images/rhash-64.png b/images/rhash-64.png
deleted file mode 100644
index 2e8396760..000000000
--- a/images/rhash-64.png
+++ /dev/null
Binary files differ
diff --git a/images/rhash.xcf b/images/rhash.xcf
deleted file mode 100644
index 960552290..000000000
--- a/images/rhash.xcf
+++ /dev/null
Binary files differ
diff --git a/images/rm-16.png b/images/rm-16.png
deleted file mode 100644
index 9361ef2b7..000000000
--- a/images/rm-16.png
+++ /dev/null
Binary files differ
diff --git a/images/rm-32.png b/images/rm-32.png
deleted file mode 100644
index 8416edd97..000000000
--- a/images/rm-32.png
+++ /dev/null
Binary files differ
diff --git a/images/rm-64.png b/images/rm-64.png
deleted file mode 100644
index 8021f9d55..000000000
--- a/images/rm-64.png
+++ /dev/null
Binary files differ
diff --git a/images/rm-old.png b/images/rm-old.png
deleted file mode 100644
index 29caaf3f6..000000000
--- a/images/rm-old.png
+++ /dev/null
Binary files differ
diff --git a/images/rm-transparent-dark-background.png b/images/rm-transparent-dark-background.png
deleted file mode 100644
index 7f94ccdd9..000000000
--- a/images/rm-transparent-dark-background.png
+++ /dev/null
Binary files differ
diff --git a/images/rm-transparent-large-old.png b/images/rm-transparent-large-old.png
deleted file mode 100644
index 8844af8f0..000000000
--- a/images/rm-transparent-large-old.png
+++ /dev/null
Binary files differ
diff --git a/images/rm.png b/images/rm.png
deleted file mode 100644
index 99ef21328..000000000
--- a/images/rm.png
+++ /dev/null
Binary files differ
diff --git a/images/rm.svg b/images/rm.svg
deleted file mode 100644
index c056a621e..000000000
--- a/images/rm.svg
+++ /dev/null
@@ -1,132 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="215.94055"
- height="50"
- id="svg3877"
- version="1.1"
- inkscape:version="0.48.4 r9939"
- sodipodi:docname="hashlogo.svg"
- inkscape:export-filename="/run/user/1000/gvfs/sftp:host=jeroenpraat.nl,port=69,user=root/var/www/hubzilla/assets/hashlogo2.png"
- inkscape:export-xdpi="156.42857"
- inkscape:export-ydpi="156.42857">
- <defs
- id="defs3" />
- <sodipodi:namedview
- inkscape:document-units="mm"
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="3.7759502"
- inkscape:cx="84.10176"
- inkscape:cy="24.800256"
- inkscape:current-layer="layer1"
- showgrid="false"
- inkscape:window-width="1533"
- inkscape:window-height="656"
- inkscape:window-x="49"
- inkscape:window-y="171"
- inkscape:window-maximized="0"
- units="px"
- fit-margin-top="4"
- fit-margin-left="4"
- fit-margin-right="4"
- fit-margin-bottom="4" />
- <metadata
- id="metadata4">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- inkscape:label="Layer 1"
- inkscape:groupmode="layer"
- id="layer1"
- transform="translate(-240.69473,-715.93361)">
- <path
- transform="matrix(0.94,0,0,0.9075862,138.86175,379.85869)"
- sodipodi:type="arc"
- id="path3028-4-5-3"
- sodipodi:cx="195.74467"
- sodipodi:cy="397.84091"
- sodipodi:rx="22.340425"
- sodipodi:ry="23.138298"
- d="m 218.0851,397.84091 c 0,12.77893 -10.00215,23.1383 -22.34043,23.1383 -12.33827,0 -22.34042,-10.35937 -22.34042,-23.1383 0,-12.77893 10.00215,-23.1383 22.34042,-23.1383 12.33828,0 22.34043,10.35937 22.34043,23.1383 z"
- style="fill:#c60032;fill-opacity:1" />
- <path
- inkscape:connector-curvature="0"
- id="path2998"
- style="font-size:40px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#3c3c3c;fill-opacity:1;stroke:none;font-family:Designosaur;-inkscape-font-specification:Designosaur"
- d="m 248.69473,755.61224 c 0.56,0 0.8,-0.24 0.8,-0.8 l 0,-12.24 c 0,-2.51999 1.32001,-3.88 3.8,-3.88 0.76,0 1.64,0.44 1.64,-0.6 l 0,-3 c 0,-0.56 -0.16,-0.6 -0.72,-0.6 -1.72,0.04 -3.72,10e-6 -5.44,2.64 l -0.08,0 -0.4,-1.52 c -0.24,-0.52 -0.24,-0.8 -0.8,-0.8 l -2,0 c -0.56,0 -0.8,0.24 -0.8,0.8 l 0,19.2 c 0,0.56 0.24,0.8 0.8,0.8 l 3.2,0" />
- <path
- inkscape:connector-curvature="0"
- id="path3000"
- style="font-size:40px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#3c3c3c;fill-opacity:1;stroke:none;font-family:Designosaur;-inkscape-font-specification:Designosaur"
- d="m 272.47473,751.81224 c -0.12,-0.16 -0.24,-0.32 -1.04,-0.08 -1.64,0.52 -3.16,1 -4.72,1 -3.51999,0 -4.72,-2.48 -5.04,-5.84 l 8.2,0 c 3.32,0 4.36,-0.92 4.36,-4 0,-5.79999 -3.72,-8.6 -8.56,-8.6 -6.47999,0 -8.88,4.96001 -8.88,11.12 0,5.6 2.00001,10.68 9.4,10.68 3.4,0 6.32,-1.08 7.08,-1.96 0.32,-0.36 0.28,-0.68 0.04,-1.04 l -0.84,-1.28 m -10.8,-8.28 c 0.24,-3.15999 1.28001,-5.88 4.2,-5.88 2.12,0 3.52,2.16001 3.52,4.52 0,1.2 -0.32,1.36 -1.56,1.36 l -6.16,0" />
- <path
- inkscape:connector-curvature="0"
- id="path3002"
- style="font-size:40px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#3c3c3c;fill-opacity:1;stroke:none;font-family:Designosaur;-inkscape-font-specification:Designosaur"
- d="m 296.25973,755.61224 c 0.56,0 0.8,-0.24 0.8,-0.8 l 0,-28 c 0,-0.56 -0.24,-0.8 -0.8,-0.8 l -3.2,0 c -0.56,0 -0.8,0.24 -0.8,0.8 l 0,10.44 -0.08,0 c -1,-1.51999 -2.76,-2.92 -6.16,-2.92 -4.63999,0 -8.6,2.64001 -8.6,10.96 0,8.24 3.92001,10.84 8.48,10.84 3.04,0 5.44,-0.92 7.08,-3 l 0.08,0 0.4,1.68 c 0.12,0.56 0.24,0.8 0.8,0.8 l 2,0 m -9.52,-2.84 c -2.91999,0 -4.52,-1.44 -4.52,-7.48 0,-6.23999 1.72001,-7.6 4.48,-7.6 3.52,0 5.56,2.16001 5.56,7.6 0,5.32 -1.96,7.48 -5.52,7.48" />
- <g
- style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#3c3c3c;fill-opacity:1;stroke:none;font-family:Sans"
- id="text3016">
- <path
- d="m 375.14875,755.65222 c 0.52,0 0.76,-0.24 0.76,-0.8 l 0,-12.64 c 0,-4.99999 -3.28001,-7.84 -7.24,-7.84 -1.92,0 -4.04,1.2 -5.12,3.28 -1.28,-2.12 -3.44,-3.28 -5.92,-3.28 -1.72,0 -3.72,1.04 -4.52,2.92 l -0.08,0 -0.4,-1.64 c -0.12,-0.56 -0.24,-0.8 -0.8,-0.8 l -2,0 c -0.56,0 -0.8,0.24 -0.8,0.8 l 0,19.2 c 0,0.56 0.24,0.8 0.8,0.8 l 3.2,0 c 0.56,0 0.8,-0.24 0.8,-0.8 l 0,-10.88 c 0,-4.11999 1.12,-6.24 3.4,-6.24 2.12,0 2.84,2.76 2.84,5.84 l 0,11.28 c 0,0.56 0.24,0.8 0.76,0.8 l 3.28,0 c 0.52,0 0.76,-0.24 0.76,-0.8 l 0,-11.16 c 0.04,-3.91999 1.2,-5.96 3.4,-5.96 2.12,0 2.84,2.76 2.84,5.84 l 0,11.28 c 0,0.56 0.24,0.8 0.76,0.8 l 3.28,0"
- style="font-variant:normal;font-stretch:normal;fill:#3c3c3c;font-family:Designosaur;-inkscape-font-specification:Designosaur"
- id="path3011"
- inkscape:connector-curvature="0" />
- <path
- d="m 396.04812,755.65222 c 0.56,0 0.8,-0.24 0.8,-0.8 l 0,-9.84 c 0,-5.71999 -1.76,-10.68 -8.68,-10.68 -3.19999,0 -6.12,1.08 -6.92,1.96 -0.32,0.36 -0.28,0.68 -0.04,1.04 l 0.84,1.28 c 0.12,0.16 0.28,0.32 1.04,0.08 1.8,-0.56 3.24,-1 4.52,-1 3.32,0 4.16,3 4.36,4.68 l -4.12,0 c -4.55999,0 -8.36,1.36001 -8.36,6.52 0,4.92 3.48001,7.24 7.52,7.24 2.68,0 4.72,-1 5.76,-2.92 l 0.08,0 0.4,1.64 c 0.12,0.56 0.24,0.8 0.8,0.8 l 2,0 m -4,-8.76 c 0,4.08 -1.48,5.88 -3.92,5.88 -2.83999,0 -3.8,-2.4 -3.8,-4 0,-1.92 1.4,-3.04 3.52,-3.04 l 4.2,0 0,1.16"
- style="font-variant:normal;font-stretch:normal;fill:#3c3c3c;font-family:Designosaur;-inkscape-font-specification:Designosaur"
- id="path3013"
- inkscape:connector-curvature="0" />
- <path
- d="m 408.715,755.65222 c 0.56,0 0.8,-0.24 0.8,-0.8 l 0,-1.76 c 0,-0.56 -0.24,-0.8 -0.8,-0.8 l -0.28,0 c -0.92,0 -1.92,-0.8 -1.92,-2.76 l 0,-11.52 2.52,0 c 0.56,0 0.8,-0.24 0.8,-0.8 l 0,-1.56 c 0,-0.56 -0.24,-0.8 -0.8,-0.8 l -2.52,0 0,-4.28 c 0,-0.56 -0.28,-1 -0.8,-0.8 l -3.2,1.2 c -0.52,0.2 -0.8,0.24 -0.8,0.8 l 0,3.08 -2.08,0 c -0.56,0 -0.8,0.24 -0.8,0.8 l 0,1.56 c 0,0.56 0.24,0.8 0.8,0.8 l 2.08,0 0,11.4 c 0,4.2 1.92,6.24 5.52,6.24 l 1.48,0"
- style="font-variant:normal;font-stretch:normal;fill:#3c3c3c;font-family:Designosaur;-inkscape-font-specification:Designosaur"
- id="path3015"
- inkscape:connector-curvature="0" />
- <path
- d="m 417.01312,755.65222 c 0.56,0 0.8,-0.24 0.8,-0.8 l 0,-12.24 c 0,-2.52 1.32001,-3.88 3.8,-3.88 0.76,0 1.64,0.44 1.64,-0.6 l 0,-3 c 0,-0.56 -0.16,-0.6 -0.72,-0.6 -1.72,0.04 -3.72,0 -5.44,2.64 l -0.08,0 -0.4,-1.52 c -0.24,-0.52 -0.24,-0.8 -0.8,-0.8 l -2,0 c -0.56,0 -0.8,0.24 -0.8,0.8 l 0,19.2 c 0,0.56 0.24,0.8 0.8,0.8 l 3.2,0"
- style="font-variant:normal;font-stretch:normal;fill:#3c3c3c;font-family:Designosaur;-inkscape-font-specification:Designosaur"
- id="path3017"
- inkscape:connector-curvature="0" />
- <path
- d="m 430.06,730.85222 c 0.56,0 0.8,-0.24 0.8,-0.8 l 0,-3.2 c 0,-0.56 -0.24,-0.8 -0.8,-0.8 l -3.2,0 c -0.56,0 -0.8,0.24 -0.8,0.8 l 0,3.2 c 0,0.56 0.24,0.8 0.8,0.8 l 3.2,0 m 0,24.8 c 0.56,0 0.8,-0.24 0.8,-0.8 l 0,-19.2 c 0,-0.56 -0.24,-0.8 -0.8,-0.8 l -3.2,0 c -0.56,0 -0.8,0.24 -0.8,0.8 l 0,19.2 c 0,0.56 0.24,0.8 0.8,0.8 l 3.2,0"
- style="font-variant:normal;font-stretch:normal;fill:#3c3c3c;font-family:Designosaur;-inkscape-font-specification:Designosaur"
- id="path3019"
- inkscape:connector-curvature="0" />
- <path
- d="m 452.12812,755.65222 c 0.56,0 0.64,-0.2 0.32,-0.64 l -6.72,-10 6.4,-9.52 c 0.32,-0.44 0.32,-0.64 -0.32,-0.64 l -4.16,0 c -0.52,0 -0.96,0.2 -1.24,0.68 l -3.44,6.8 -3.24,-6.8 c -0.28,-0.48 -0.64,-0.68 -1.2,-0.68 l -4.4,0 c -0.56,0 -0.64,0.2 -0.32,0.64 l 6.4,9.52 -6.72,10 c -0.32,0.44 -0.16,0.64 0.32,0.64 l 4.16,0 c 0.52,0 0.96,-0.2 1.24,-0.68 l 3.76,-7.08 3.56,7.08 c 0.28,0.48 0.64,0.68 1.2,0.68 l 4.4,0"
- style="font-variant:normal;font-stretch:normal;fill:#3c3c3c;font-family:Designosaur;-inkscape-font-specification:Designosaur"
- id="path3021"
- inkscape:connector-curvature="0" />
- </g>
- <g
- style="font-size:46px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans"
- id="text3003-0-4-0">
- <path
- d="m 322.85053,756.03406 4.7355,0 1.3079,-7.9827 4.8708,0 0,-4.4649 -4.1492,0 1.0373,-6.4944 4.9159,0 0,-4.4649 -4.1492,0 1.1275,-7.0356 -4.7355,0 -1.1275,7.0356 -5.1865,0 1.1275,-7.0356 -4.7355,0 -1.1275,7.0356 -5.0963,0 0,4.4649 4.3296,0 -1.0373,6.4944 -5.0963,0 0,4.4649 4.3747,0 -1.3079,7.9827 4.7355,0 1.3079,-7.9827 5.1865,0 -1.3079,7.9827 m 2.0295,-12.4476 -5.1865,0 1.0373,-6.4944 5.1865,0 -1.0373,6.4944"
- style="font-size:45.09999847px;font-variant:normal;font-weight:bold;font-stretch:normal;fill:#ffffff;font-family:generic;-inkscape-font-specification:generic Bold"
- id="path3008"
- inkscape:connector-curvature="0" />
- </g>
- </g>
-</svg>
diff --git a/images/rm300.png b/images/rm300.png
deleted file mode 100644
index bc2600348..000000000
--- a/images/rm300.png
+++ /dev/null
Binary files differ
diff --git a/images/rotator.gif b/images/rotator.gif
deleted file mode 100644
index 3797ec3e4..000000000
--- a/images/rotator.gif
+++ /dev/null
Binary files differ
diff --git a/images/search_18.png b/images/search_18.png
deleted file mode 100644
index 539739670..000000000
--- a/images/search_18.png
+++ /dev/null
Binary files differ
diff --git a/images/selected.png b/images/selected.png
deleted file mode 100644
index 79a7c77c6..000000000
--- a/images/selected.png
+++ /dev/null
Binary files differ
diff --git a/images/share.gif b/images/share.gif
deleted file mode 100644
index 035fa2e38..000000000
--- a/images/share.gif
+++ /dev/null
Binary files differ
diff --git a/images/show_all_off.png b/images/show_all_off.png
deleted file mode 100644
index cc96d28f0..000000000
--- a/images/show_all_off.png
+++ /dev/null
Binary files differ
diff --git a/images/show_all_on.png b/images/show_all_on.png
deleted file mode 100644
index 87a7710b5..000000000
--- a/images/show_all_on.png
+++ /dev/null
Binary files differ
diff --git a/images/show_off.png b/images/show_off.png
deleted file mode 100644
index 4bcf47123..000000000
--- a/images/show_off.png
+++ /dev/null
Binary files differ
diff --git a/images/show_on.png b/images/show_on.png
deleted file mode 100644
index b8d1f5bac..000000000
--- a/images/show_on.png
+++ /dev/null
Binary files differ
diff --git a/images/spencil.gif b/images/spencil.gif
deleted file mode 100644
index 0a2551ac0..000000000
--- a/images/spencil.gif
+++ /dev/null
Binary files differ
diff --git a/images/star.png b/images/star.png
deleted file mode 100644
index 0b00cb189..000000000
--- a/images/star.png
+++ /dev/null
Binary files differ
diff --git a/images/star_dummy.png b/images/star_dummy.png
deleted file mode 100644
index ce11f30d4..000000000
--- a/images/star_dummy.png
+++ /dev/null
Binary files differ
diff --git a/images/tag.png b/images/tag.png
deleted file mode 100644
index 40c5fd44e..000000000
--- a/images/tag.png
+++ /dev/null
Binary files differ
diff --git a/images/tag_b.png b/images/tag_b.png
deleted file mode 100644
index 66c03415d..000000000
--- a/images/tag_b.png
+++ /dev/null
Binary files differ
diff --git a/images/tools.png b/images/tools.png
deleted file mode 100644
index c17094a81..000000000
--- a/images/tools.png
+++ /dev/null
Binary files differ
diff --git a/images/unlock_icon.gif b/images/unlock_icon.gif
deleted file mode 100644
index 254ac8bfd..000000000
--- a/images/unlock_icon.gif
+++ /dev/null
Binary files differ
diff --git a/images/video.gif b/images/video.gif
deleted file mode 100644
index e4d5e8cdd..000000000
--- a/images/video.gif
+++ /dev/null
Binary files differ
diff --git a/include/bbcode.php b/include/bbcode.php
index 301ffbd24..b2e3f1d3b 100644
--- a/include/bbcode.php
+++ b/include/bbcode.php
@@ -655,6 +655,109 @@ function bb_observer($Text) {
return $Text;
}
+function bb_imgoptions($match) {
+
+ // $Text = preg_replace_callback("/\[([zi])mg([ \=])(.*?)\](.*?)\[\/[zi]mg\]/ism",'bb_imgoptions',$Text);
+ // alt text cannot contain ']'
+
+ // [img|zmg=wwwxhhh float=left|right alt=alt text]url[/img|zmg]
+
+ $local_match = null;
+ $width = 0;
+ $float = false;
+ $alt = false;
+
+ $style = EMPTY_STR;
+
+ $attributes = $match[3];
+
+ $x = preg_match("/alt='(.*?)'/ism", $attributes, $matches);
+ if ($x) {
+ $alt = $matches[1];
+ }
+
+ $x = preg_match("/alt=\&quot\;(.*?)\&quot\;/ism", $attributes, $matches);
+ if ($x) {
+ $alt = $matches[1];
+ }
+
+ $x = preg_match("/width='(.*?)'/ism", $attributes, $matches);
+ if ($x) {
+ $width = $matches[1];
+ }
+
+ $x = preg_match("/width=\&quot\;(.*?)\&quot\;/ism", $attributes, $matches);
+ if ($x) {
+ $width = $matches[1];
+ }
+
+ $x = preg_match("/height='(.*?)'/ism", $attributes, $matches);
+ if ($x) {
+ $height = $matches[1];
+ }
+
+ $x = preg_match("/height=\&quot\;(.*?)\&quot\;/ism", $attributes, $matches);
+ if ($x) {
+ $height = $matches[1];
+ }
+
+ $x = preg_match("/style='(.*?)'/ism", $attributes, $matches);
+ if ($x) {
+ $style = $matches[1];
+ }
+
+ $x = preg_match("/style=\&quot\;(.*?)\&quot\;/ism", $attributes, $matches);
+ if ($x) {
+ $style = $matches[1];
+ }
+
+ // legacy img options
+
+ if ($match[2] === '=') {
+ // pull out (optional) legacy size declarations first
+ if (preg_match("/([0-9]*)x([0-9]*)/ism",$match[3],$local_match)) {
+ $width = intval($local_match[1]);
+ }
+ $match[3] = substr($match[3],strpos($match[3],' '));
+ }
+
+ // then (optional) legacy float specifiers
+ if ($n = strpos($match[3],'float=left') !== false) {
+ $float = 'left';
+ $match[3] = substr($match[3],$n + 10);
+ }
+ if ($n = strpos($match[3],'float=right') !== false) {
+ $float = 'right';
+ $match[3] = substr($match[3],$n + 11);
+ }
+
+ // finally alt text which extends to the close of the tag
+ if ((! $alt) && ($n = strpos($match[3],'alt=') !== false)) {
+ $alt = substr($match[3],$n + 4);
+ }
+
+ // now assemble the resulting img tag from these components
+
+ $output = '<img ' . (($match[1] === 'z') ? 'class="zrl" ' : '') . ' ';
+
+ if ($width) {
+ $style .= 'width: 100%; max-width: ' . $width . 'px; ';
+ }
+ else {
+ $style .= 'max-width: 100%; ';
+ }
+ if ($float) {
+ $style .= 'float: ' . $float . '; ';
+ }
+
+ $output .= (($style) ? 'style="' . $style . '" ' : '') . 'alt="' . htmlentities(($alt) ? $alt : t('Image/photo'),ENT_COMPAT,'UTF-8') . '" ';
+
+ $output .= 'src="' . $match[4] . '" >';
+
+ return $output;
+
+}
+
function bb_code_protect($s) {
return 'b64.^9e%.' . base64_encode($s) . '.b64.$9e%';
}
@@ -1237,45 +1340,20 @@ function bbcode($Text, $options = []) {
if (strpos($Text,'[/img]') !== false) {
$Text = preg_replace("/\[img\](.*?)\[\/img\]/ism", '<img style="max-width: 100%;" src="$1" alt="' . t('Image/photo') . '" />', $Text);
}
- if (strpos($Text,'[/zmg]') !== false) {
- $Text = preg_replace("/\[zmg\](.*?)\[\/zmg\]/ism", '<img class="zrl" style="max-width: 100%;" src="$1" alt="' . t('Image/photo') . '" />', $Text);
- }
-
- // [img float={left, right}]pathtoimage[/img]
- if (strpos($Text,'[/img]') !== false) {
- $Text = preg_replace("/\[img float=left\](.*?)\[\/img\]/ism", '<img src="$1" style="max-width: 100%; float: left;" alt="' . t('Image/photo') . '" />', $Text);
- }
+ // [img=pathtoimage]image description[/img]
if (strpos($Text,'[/img]') !== false) {
- $Text = preg_replace("/\[img float=right\](.*?)\[\/img\]/ism", '<img src="$1" style="max-width: 100%; float: right;" alt="' . t('Image/photo') . '" />', $Text);
- }
- if (strpos($Text,'[/zmg]') !== false) {
- $Text = preg_replace("/\[zmg float=left\](.*?)\[\/zmg\]/ism", '<img class="zrl" src="$1" style="max-width: 100%; float: left;" alt="' . t('Image/photo') . '" />', $Text);
+ $Text = preg_replace("/\[img=http(.*?)\](.*?)\[\/img\]/ism", '<img style="max-width: 100%;" src="http$1" alt="$2" title="$2"/>', $Text);
}
+ // [zmg]pathtoimage[/zmg]
if (strpos($Text,'[/zmg]') !== false) {
- $Text = preg_replace("/\[zmg float=right\](.*?)\[\/zmg\]/ism", '<img class="zrl" src="$1" style="max-width: 100%; float: right;" alt="' . t('Image/photo') . '" />', $Text);
- }
-
- // [img=widthxheight]pathtoimage[/img]
- if (strpos($Text,'[/img]') !== false) {
- $Text = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", '<img src="$3" style="width: 100%; max-width: $1px;" alt="' . t('Image/photo') . '" />', $Text);
+ $Text = preg_replace("/\[zmg\](.*?)\[\/zmg\]/ism", '<img class="zrl" style="max-width: 100%;" src="$1" alt="' . t('Image/photo') . '" />', $Text);
}
+ // [zmg=pathtoimage]image description[/zmg]
if (strpos($Text,'[/zmg]') !== false) {
- $Text = preg_replace("/\[zmg\=([0-9]*)x([0-9]*)\](.*?)\[\/zmg\]/ism", '<img class="zrl" src="$3" style="width: 100%; max-width: $1px;" alt="' . t('Image/photo') . '" />', $Text);
+ $Text = preg_replace("/\[zmg=http(.*?)\](.*?)\[\/zmg\]/ism", '<img class="zrl" style="max-width: 100%;" src="http$1" alt="$2" title="$2"/>', $Text);
}
- // [img=widthxheight float={left, right}]pathtoimage[/img]
- if (strpos($Text,'[/img]') !== false) {
- $Text = preg_replace("/\[img\=([0-9]*)x([0-9]*) float=left\](.*?)\[\/img\]/ism", '<img src="$3" style="width: 100%; max-width: $1px; float: left;" alt="' . t('Image/photo') . '" />', $Text);
- }
- if (strpos($Text,'[/img]') !== false) {
- $Text = preg_replace("/\[img\=([0-9]*)x([0-9]*) float=right\](.*?)\[\/img\]/ism", '<img src="$3" style="width: 100%; max-width: $1px; float: right;" alt="' . t('Image/photo') . '" />', $Text);
- }
- if (strpos($Text,'[/zmg]') !== false) {
- $Text = preg_replace("/\[zmg\=([0-9]*)x([0-9]*) float=left\](.*?)\[\/zmg\]/ism", '<img class="zrl" src="$3" style="width: 100%; max-width: $1px; float: left;" alt="' . t('Image/photo') . '" />', $Text);
- }
- if (strpos($Text,'[/zmg]') !== false) {
- $Text = preg_replace("/\[zmg\=([0-9]*)x([0-9]*) float=right\](.*?)\[\/zmg\]/ism", '<img class="zrl" src="$3" style="width: 100%; max-width: $1px; float: right;" alt="' . t('Image/photo') . '" />', $Text);
- }
+ $Text = preg_replace_callback("/\[([zi])mg([ \=])(.*?)\](.*?)\[\/[zi]mg\]/ism",'bb_imgoptions',$Text);
// style (sanitized)
if (strpos($Text,'[/style]') !== false) {
diff --git a/include/cdav.php b/include/cdav.php
new file mode 100644
index 000000000..ef248a9fe
--- /dev/null
+++ b/include/cdav.php
@@ -0,0 +1,186 @@
+<?php
+
+/**
+ * @brief Process CardDAV card
+ *
+ * @param array $f fields
+ * @param obj $vcard SabreDAV object
+ * @param bool $edit update card
+ *
+ */
+
+function process_cdav_card($f, &$vcard, $edit = false) {
+
+ if($f['org'])
+ $vcard->ORG = $f['org'];
+ else
+ if($edit)
+ unset($vcard->ORG);
+
+
+ if($f['title'])
+ $vcard->TITLE = $f['title'];
+ else
+ if($edit)
+ unset($vcard->TITLE);
+
+ if($edit)
+ unset($vcard->TEL);
+ if($f['tel']) {
+ $i = 0;
+ foreach($f['tel'] as $item) {
+ if($item) {
+ $vcard->add('TEL', $item, ['type' => $f['tel_type'][$i]]);
+ }
+ $i++;
+ }
+ }
+
+ if($edit)
+ unset($vcard->EMAIL);
+ if($f['email']) {
+ $i = 0;
+ foreach($f['email'] as $item) {
+ if($item) {
+ $vcard->add('EMAIL', $item, ['type' => $f['email_type'][$i]]);
+ }
+ $i++;
+ }
+ }
+
+ if($edit)
+ unset($vcard->IMPP);
+ if($f['impp']) {
+ $i = 0;
+ foreach($f['impp'] as $item) {
+ if($item) {
+ $vcard->add('IMPP', $item, ['type' => $f['impp_type'][$i]]);
+ }
+ $i++;
+ }
+ }
+
+ if($edit)
+ unset($vcard->URL);
+ if($f['url']) {
+ $i = 0;
+ foreach($f['url'] as $item) {
+ if($item) {
+ $vcard->add('URL', $item, ['type' => $f['url_type'][$i]]);
+ }
+ $i++;
+ }
+ }
+
+ if($edit)
+ unset($vcard->ADR);
+ if($f['adr']) {
+ $i = 0;
+ foreach($f['adr'] as $item) {
+ if($item) {
+ $vcard->add('ADR', $item, ['type' => $f['adr_type'][$i]]);
+ }
+ $i++;
+ }
+ }
+
+ if($f['note']) {
+ $vcard->NOTE = $f['note'];
+ }
+ else
+ if($edit)
+ unset($vcard->NOTE);
+}
+
+
+/**
+ * @brief Import CardDAV or CalDAV card
+ *
+ * @param mixed $id card id
+ * @param str $ext card extension
+ * @param str $table name
+ * @param str $column name
+ * @param obj $objects
+ * @param str $profile
+ * @param obj $backend
+ * @param array $ids
+ * @param bool $notice
+ *
+ */
+
+function import_cdav_card($id, $ext, $table, $column, $objects, $profile, $backend, &$ids, $notice = false) {
+
+ $i = 0;
+ $newid = (count($ids) ? false : true);
+
+ while ($object = $objects->getNext()) {
+
+ if($_REQUEST['a_upload'])
+ $object = $object->convert(\Sabre\VObject\Document::VCARD40);
+
+ $ret = $object->validate($profile & \Sabre\VObject\Node::REPAIR);
+
+ //level 3 Means that the document is invalid,
+ //level 2 means a warning. A warning means it's valid but it could cause interopability issues,
+ //level 1 means that there was a problem earlier, but the problem was automatically repaired.
+
+ if($ret[0]['level'] < 3) {
+
+ if($newid) {
+ do {
+ $duplicate = false;
+ $objectUri = random_string(40) . '.' . $ext;
+
+ $r = q("SELECT uri FROM $table WHERE $column = %d AND uri = '%s' LIMIT 1",
+ dbesc($id),
+ dbesc($objectUri)
+ );
+ if (count($r))
+ $duplicate = true;
+ } while ($duplicate == true);
+ $ids[$i] = $objectUri;
+ }
+ else
+ $objectUri = $ids[$i];
+
+ $i++;
+
+ if($ext == 'ics')
+ $backend->createCalendarObject($id, $objectUri, $object->serialize());
+
+ if($ext == 'vcf')
+ $backend->createCard($id, $objectUri, $object->serialize());
+ }
+ else {
+ if($notice && $ext == 'ics') {
+ notice(
+ '<strong>' . t('INVALID EVENT DISMISSED!') . '</strong>' . EOL .
+ '<strong>' . t('Summary: ') . '</strong>' . (($object->VEVENT->SUMMARY) ? $object->VEVENT->SUMMARY : t('Unknown')) . EOL .
+ '<strong>' . t('Date: ') . '</strong>' . (($object->VEVENT->DTSTART) ? $object->VEVENT->DTSTART : t('Unknown')) . EOL .
+ '<strong>' . t('Reason: ') . '</strong>' . $ret[0]['message'] . EOL
+ );
+ }
+
+ if($notice && $exp == 'vcf') {
+ notice(
+ '<strong>' . t('INVALID CARD DISMISSED!') . '</strong>' . EOL .
+ '<strong>' . t('Name: ') . '</strong>' . (($object->FN) ? $object->FN : t('Unknown')) . EOL .
+ '<strong>' . t('Reason: ') . '</strong>' . $ret[0]['message'] . EOL
+ );
+ }
+ }
+ }
+}
+
+
+function get_cdav_id($principaluri, $uri, $table) {
+
+ $r = q("SELECT * FROM $table WHERE principaluri = '%s' AND uri = '%s' LIMIT 1",
+ dbesc($principaluri),
+ dbesc($uri)
+ );
+ if(! $r)
+ return false;
+
+ return $r[0];
+}
diff --git a/include/channel.php b/include/channel.php
index 61603fb42..4aad64f3d 100644
--- a/include/channel.php
+++ b/include/channel.php
@@ -923,11 +923,38 @@ function identity_basic_export($channel_id, $sections = null, $zap_compat = fals
$ret['abook'] = $r;
for($x = 0; $x < count($ret['abook']); $x ++) {
+
$xchans[] = $ret['abook'][$x]['abook_xchan'];
+ $my_perms = [];
+ $their_perms = [];
+ $newconfig = [];
$abconfig = load_abconfig($channel_id,$ret['abook'][$x]['abook_xchan']);
- if($abconfig)
- $ret['abook'][$x]['abconfig'] = $abconfig;
+ if($abconfig) {
+ foreach ($abconfig as $abc) {
+
+ if ($abc['cat'] === 'my_perms' && intval($abc['v'])) {
+ $my_perms[] = $abc['k'];
+ continue;
+ }
+ if ($abc['cat'] === 'their_perms' && intval($abc['v'])) {
+ $their_perms[] = $abc['k'];
+ continue;
+ }
+ if ($zap_compat && preg_match('|^a:[0-9]+:{.*}$|s', $abc['v'])) {
+ $abc['v'] = serialise(unserialize($abc['v']));
+ }
+ $newconfig[] = $abc;
+ }
+
+ $ret['abook'][$x]['abconfig'] = $newconfig;
+ if ($zap_compat) {
+ $ret['abook'][$x]['abconfig'][] = [ 'chan' => $channel_id, 'xchan' => $ret['abook'][$x]['abook_chan'], 'cat' => 'system', 'k' => 'my_perms', 'v' => implode(',',$my_perms) ];
+ $ret['abook'][$x]['abconfig'][] = [ 'chan' => $channel_id, 'xchan' => $ret['abook'][$x]['abook_chan'], 'cat' => 'system', 'k' => 'their_perms', 'v' => implode(',',$their_perms) ];
+ }
+ }
+
translate_abook_perms_outbound($ret['abook'][$x]);
+
}
// pick up the zot6 xchan and hublocs also
@@ -967,8 +994,17 @@ function identity_basic_export($channel_id, $sections = null, $zap_compat = fals
$r = q("select * from pconfig where uid = %d",
intval($channel_id)
);
- if($r)
+
+ if($r) {
+ if ($zap_compat) {
+ for($x = 0; $x < count($r); $x ++) {
+ if (preg_match('|^a:[0-9]+:{.*}$|s', $r[$x]['v'])) {
+ $r[$x]['v'] = serialise(unserialize($r[$x]['v']));
+ }
+ }
+ }
$ret['config'] = $r;
+ }
// All other term types will be included in items, if requested.
diff --git a/include/conversation.php b/include/conversation.php
index 07d43e660..62d4b405f 100644
--- a/include/conversation.php
+++ b/include/conversation.php
@@ -413,12 +413,18 @@ function visible_activity($item) {
if(intval($item['item_notshown']))
return false;
+ if ($item['obj_type'] === 'Answer') {
+ return false;
+ }
+
foreach($hidden_activities as $act) {
if((activity_match($item['verb'], $act)) && ($item['mid'] != $item['parent_mid'])) {
return false;
}
}
+
+
if(is_edit_activity($item))
return false;
@@ -619,11 +625,17 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa
$items = $cb['items'];
- $conv_responses = array(
- 'like' => array('title' => t('Likes','title')),'dislike' => array('title' => t('Dislikes','title')),
- 'agree' => array('title' => t('Agree','title')),'disagree' => array('title' => t('Disagree','title')), 'abstain' => array('title' => t('Abstain','title')),
- 'attendyes' => array('title' => t('Attending','title')), 'attendno' => array('title' => t('Not attending','title')), 'attendmaybe' => array('title' => t('Might attend','title'))
- );
+ $conv_responses = [
+ 'like' => ['title' => t('Likes','title')],
+ 'dislike' => ['title' => t('Dislikes','title')],
+ 'agree' => ['title' => t('Agree','title')],
+ 'disagree' => ['title' => t('Disagree','title')],
+ 'abstain' => ['title' => t('Abstain','title')],
+ 'attendyes' => ['title' => t('Attending','title')],
+ 'attendno' => ['title' => t('Not attending','title')],
+ 'attendmaybe' => ['title' => t('Might attend','title')],
+ 'answer' => []
+ ];
// array with html for each thread (parent+comments)
@@ -1130,7 +1142,7 @@ function builtin_activity_puller($item, &$conv_responses) {
// if this item is a post or comment there's nothing for us to do here, just return.
- if(activity_match($item['verb'],ACTIVITY_POST))
+ if(activity_match($item['verb'],ACTIVITY_POST) && $item['obj_type'] !== 'Answer')
return;
foreach($conv_responses as $mode => $v) {
@@ -1162,6 +1174,9 @@ function builtin_activity_puller($item, &$conv_responses) {
case 'attendmaybe':
$verb = ACTIVITY_ATTENDMAYBE;
break;
+ case 'answer':
+ $verb = ACTIVITY_POST;
+ break;
default:
return;
break;
@@ -1177,9 +1192,11 @@ function builtin_activity_puller($item, &$conv_responses) {
if(! $item['thr_parent'])
$item['thr_parent'] = $item['parent_mid'];
-
$conv_responses[$mode]['mids'][$item['thr_parent']][] = 'b64.' . base64url_encode($item['mid']);
+ if($item['obj_type'] === 'Answer')
+ continue;
+
if(! ((isset($conv_responses[$mode][$item['thr_parent'] . '-l']))
&& (is_array($conv_responses[$mode][$item['thr_parent'] . '-l']))))
$conv_responses[$mode][$item['thr_parent'] . '-l'] = array();
@@ -1276,7 +1293,7 @@ function hz_status_editor($a, $x, $popup = false) {
$feature_voting = feature_enabled($x['profile_uid'], 'consensus_tools');
if(x($x, 'hide_voting'))
$feature_voting = false;
-
+
$feature_nocomment = feature_enabled($x['profile_uid'], 'disable_comments');
if(x($x, 'disable_comments'))
$feature_nocomment = false;
@@ -1424,6 +1441,11 @@ function hz_status_editor($a, $x, $popup = false) {
'$embedPhotosModalOK' => t('OK'),
'$setloc' => $setloc,
'$voting' => t('Toggle voting'),
+ '$poll' => t('Toggle poll'),
+ '$poll_option_label' => t('Option'),
+ '$poll_add_option_label' => t('Add option'),
+ '$poll_expire_unit_label' => [t('Minutes'), t('Hours'), t('Days')],
+ '$multiple_answers' => ['poll_multiple_answers', t("Allow multiple answers"), '', '', [t('No'), t('Yes')]],
'$feature_voting' => $feature_voting,
'$consensus' => ((array_key_exists('item',$x)) ? $x['item']['item_consensus'] : 0),
'$nocommenttitle' => t('Disable comments'),
diff --git a/include/features.php b/include/features.php
index 87df0c50d..c6cfcf822 100644
--- a/include/features.php
+++ b/include/features.php
@@ -299,6 +299,22 @@ function get_features($filtered = true, $level = (-1)) {
t('Network'),
[
+ 'events_tab',
+ t('Events Filter'),
+ t('Ability to display only events'),
+ false,
+ get_config('feature_lock','events_tab')
+ ],
+
+ [
+ 'polls_tab',
+ t('Polls Filter'),
+ t('Ability to display only polls'),
+ false,
+ get_config('feature_lock','polls_tab')
+ ],
+
+ [
'savedsearch',
t('Saved Searches'),
t('Save search terms for re-use'),
diff --git a/include/import.php b/include/import.php
index 6a3895b9f..bfe71963f 100644
--- a/include/import.php
+++ b/include/import.php
@@ -1503,6 +1503,144 @@ function sync_files($channel, $files) {
}
}
+
+/**
+ * @brief Synchronize addressbooks.
+ *
+ * @param array $channel
+ * @param array $data
+ */
+function sync_addressbook($channel, $data) {
+
+ if(! \Zotlabs\Lib\Apps::system_app_installed($channel['channel_id'], 'CardDAV'))
+ return;
+
+ logger("debug: " . print_r($data,true), LOGGER_DEBUG);
+
+ require_once('include/cdav.php');
+
+ $principalUri = 'principals/' . $channel['channel_address'];
+
+ if($data['action'] !== 'create') {
+ $id = get_cdav_id($principalUri, $data['uri'], 'addressbooks');
+ if(! $id)
+ return;
+ }
+
+ $pdo = \DBA::$dba->db;
+
+ $carddavBackend = new \Sabre\CardDAV\Backend\PDO($pdo);
+ $addressbooks = $carddavBackend->getAddressBooksForUser($principalUri);
+
+ switch($data['action']) {
+
+ case 'create':
+ $carddavBackend->createAddressBook($principalUri, $data['uri'], $data['properties']);
+ break;
+
+ case 'drop':
+ $carddavBackend->deleteAddressBook($id);
+ break;
+
+ case 'edit':
+ $patch = new \Sabre\DAV\PropPatch($data['mutations']);
+ $carddavBackend->updateAddressBook($id, $patch);
+ $patch->commit();
+ break;
+
+ case 'delete_card':
+ $carddavBackend->deleteCard($id, $data['carduri']);
+ break;
+
+ case 'update_card':
+ $vcard = \Sabre\VObject\Reader::read($data['card']);
+ $object = $vcard->convert(\Sabre\VObject\Document::VCARD40);
+ $cardData = $vcard->serialize();
+ $carddavBackend->updateCard($id, $data['carduri'], $cardData);
+ break;
+
+ case 'import':
+ $objects = new \Sabre\VObject\Splitter\VCard($data['card']);
+ $profile = \Sabre\VObject\Node::PROFILE_CARDDAV;
+ import_cdav_card($id, 'vcf', 'cards', 'addressbookid', $objects, $profile, $carddavBackend, $data['ids']);
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+/**
+ * @brief Synchronize calendars.
+ *
+ * @param array $channel
+ * @param array $data
+ */
+function sync_calendar($channel, $data) {
+
+ if(! \Zotlabs\Lib\Apps::system_app_installed($channel['channel_id'], 'Calendar'))
+ return;
+
+ logger("debug: " . print_r($data,true), LOGGER_DEBUG);
+
+ require_once('include/cdav.php');
+
+ $principalUri = 'principals/' . $channel['channel_address'];
+
+ if($data['action'] !== 'create') {
+ $x = get_cdav_id($principalUri, $data['uri'], 'calendarinstances');
+ if(! $x)
+ return;
+ $id = [ $x['id'], $x['calendarid'] ];
+ }
+
+ $pdo = \DBA::$dba->db;
+
+ $caldavBackend = new \Sabre\CalDAV\Backend\PDO($pdo);
+ $calendars = $caldavBackend->getCalendarsForUser($principalUri);
+
+ switch($data['action']) {
+
+ case 'create':
+ $id = $caldavBackend->createCalendar($principalUri, $data['uri'], $data['properties']);
+ set_pconfig($channel['channel_id'], 'cdav_calendar', $id[0], 1);
+ break;
+
+ case 'drop':
+ $caldavBackend->deleteCalendar($id);
+ break;
+
+ case 'edit':
+ $patch = new \Sabre\DAV\PropPatch($data['mutations']);
+ $caldavBackend->updateCalendar($id, $patch);
+ $patch->commit();
+ break;
+
+ case 'delete_card':
+ $caldavBackend->deleteCalendarObject($id, $data['carduri']);
+ break;
+
+ case 'update_card':
+ $caldavBackend->updateCalendarObject($id, $data['carduri'], $data['card']);
+ break;
+
+ case 'switch':
+ set_pconfig($channel['channel_id'], 'cdav_calendar', $id[0], $data['switch']);
+ break;
+
+ case 'import':
+ $objects = new \Sabre\VObject\Splitter\ICalendar($data['card']);
+ $profile = \Sabre\VObject\Node::PROFILE_CALDAV;
+ import_cdav_card($id, 'ics', 'calendarobjects', 'calendarid', $objects, $profile, $caldavBackend, $data['ids']);
+ break;
+
+ default:
+ break;
+ }
+}
+
+
/**
* @brief Rename a key in an array.
*
diff --git a/include/items.php b/include/items.php
index 9f90b2f3b..9768fdf23 100755
--- a/include/items.php
+++ b/include/items.php
@@ -9,6 +9,7 @@ use Zotlabs\Lib\MarkdownSoap;
use Zotlabs\Lib\MessageFilter;
use Zotlabs\Lib\ThreadListener;
use Zotlabs\Lib\IConfig;
+use Zotlabs\Lib\Activity;
use Zotlabs\Access\PermissionLimits;
use Zotlabs\Access\AccessList;
use Zotlabs\Daemon\Master;
@@ -1947,6 +1948,11 @@ function item_store($arr, $allow_exec = false, $deliver = true) {
if(intval($r[0]['item_uplink']) && (! $r[0]['item_private']))
$arr['item_private'] = 0;
+
+ if(in_array($arr['obj_type'], ['Note','Answer']) && $r[0]['obj_type'] === 'Question' && intval($r[0]['item_wall'])) {
+ Activity::update_poll($r[0],$arr);
+ }
+
}
else {
logger('item_store: item parent was not found - ignoring item');
diff --git a/include/language.php b/include/language.php
index e9d62e434..622b9614d 100644
--- a/include/language.php
+++ b/include/language.php
@@ -217,9 +217,10 @@ function t($s, $ctx = '') {
*/
function translate_projectname($s) {
-
- return str_replace(array('$projectname','$Projectname'),array(Zotlabs\Lib\System::get_platform_name(),ucfirst(Zotlabs\Lib\System::get_platform_name())),$s);
-
+ if(strpos($s,'rojectname') !== false) {
+ return str_replace(array('$projectname','$Projectname'),array(Zotlabs\Lib\System::get_platform_name(),ucfirst(Zotlabs\Lib\System::get_platform_name())),$s);
+ }
+ return $s;
}
diff --git a/include/nav.php b/include/nav.php
index 672cc2689..bd4d000f7 100644
--- a/include/nav.php
+++ b/include/nav.php
@@ -59,6 +59,8 @@ function nav($template = 'default') {
if($banner === false)
$banner = get_config('system','sitename');
+
+ call_hooks('get_banner',$banner);
App::$page['header'] .= replace_macros(get_markup_template('hdr.tpl'), array(
//we could additionally use this to display important system notifications e.g. for updates
diff --git a/include/text.php b/include/text.php
index 87ed9f658..1475ff334 100644
--- a/include/text.php
+++ b/include/text.php
@@ -1731,6 +1731,11 @@ function prepare_body(&$item,$attach = false,$opts = false) {
}
}
+ $poll = (($item['obj_type'] === 'Question' && in_array($item['verb'],[ ACTIVITY_POST, ACTIVITY_UPDATE, ACTIVITY_SHARE ])) ? format_poll($item, $s, $opts) : false);
+ if ($poll) {
+ $s = $poll;
+ }
+
$event = (($item['obj_type'] === ACTIVITY_OBJ_EVENT) ? format_event_obj($item['obj']) : false);
$prep_arr = [
@@ -1814,6 +1819,89 @@ function prepare_binary($item) {
}
+function format_poll($item,$s,$opts) {
+
+ if (! is_array($item['obj'])) {
+ $act = json_decode($item['obj'],true);
+ }
+ else {
+ $act = $item['obj'];
+ }
+
+ if (! is_array($act)) {
+ return EMPTY_STR;
+ }
+
+ $commentable = can_comment_on_post(((local_channel()) ? get_observer_hash() : EMPTY_STR),$item);
+
+ //logger('format_poll: ' . print_r($item,true));
+ $activated = ((local_channel() && local_channel() == $item['uid']) ? true : false);
+ $output = $s . EOL. EOL;
+
+ if ($act['type'] === 'Question') {
+ if ($activated and $commentable) {
+ $output .= '<form id="question-form-' . $item['id'] . '" >';
+ }
+ if (array_key_exists('anyOf',$act) && is_array($act['anyOf'])) {
+ foreach ($act['anyOf'] as $poll) {
+ if (array_key_exists('name',$poll) && $poll['name']) {
+ $text = html2plain(purify_html($poll['name']),256);
+ if (array_path_exists('replies/totalItems',$poll)) {
+ $total = $poll['replies']['totalItems'];
+ }
+ else {
+ $total = 0;
+ }
+ if ($activated && $commentable) {
+ $output .= '<input type="checkbox" name="answer[]" value="' . htmlspecialchars($text) . '"> ' . $text . '</input>' . ' (' . $total . ')' . EOL;
+ }
+ else {
+ $output .= '[ ] ' . $text . ' (' . $total . ')' . EOL;
+ }
+ }
+ }
+ }
+ if (array_key_exists('oneOf',$act) && is_array($act['oneOf'])) {
+ foreach ($act['oneOf'] as $poll) {
+ if (array_key_exists('name',$poll) && $poll['name']) {
+ $text = html2plain(purify_html($poll['name']),256);
+ if (array_path_exists('replies/totalItems',$poll)) {
+ $total = $poll['replies']['totalItems'];
+ }
+ else {
+ $total = 0;
+ }
+ if ($activated && $commentable) {
+ $output .= '<input type="radio" name="answer" value="' . htmlspecialchars($text) . '"> ' . $text . '</input>' . ' (' . $total . ')' . EOL;
+ }
+ else {
+ $output .= '( ) ' . $text . ' (' . $total . ')' . EOL;
+ }
+ }
+ }
+ }
+ if ($item['comments_closed'] > NULL_DATE) {
+ $t = datetime_convert('UTC',date_default_timezone_get(), $item['comments_closed'], 'Y-m-d h:i');
+ $closed = ((datetime_convert() > $item['comments_closed']) ? true : false);
+ if ($closed) {
+ $message = t('Poll has ended.');
+ }
+ else {
+ $message = sprintf(t('Poll ends: %s'),$t);
+ }
+ $output .= EOL . '<div>' . $message . '</div>';
+ }
+ if ($activated and $commentable) {
+ $output .= EOL . '<input type="button" class="btn btn-std btn-success" name="vote" value="vote" onclick="submitPoll(' . $item['id'] . '); return false;">'. '</form>';
+ }
+
+ }
+ return $output;
+}
+
+
+
+
/**
* @brief Given a text string, convert from content_type to HTML.
*
@@ -3644,7 +3732,7 @@ function array_path_exists($str,$arr) {
if($search) {
foreach($search as $s) {
- if(array_key_exists($s,$ptr)) {
+ if($ptr && array_key_exists($s,$ptr)) {
$ptr = $ptr[$s];
}
else {
@@ -3694,3 +3782,18 @@ function svg2bb($s) {
}
return EMPTY_STR;
}
+
+
+
+function serialise($x) {
+ return ((is_array($x)) ? 'json:' . json_encode($x) : $x);
+}
+
+function unserialise($x) {
+ if (is_array($x)) {
+ return $x;
+ }
+ $y = ((substr($x,0,5) === 'json:') ? json_decode(substr($x,5),true) : '');
+ return ((is_array($y)) ? $y : $x);
+}
+
diff --git a/include/zid.php b/include/zid.php
index 3b3dd8554..325af5580 100644
--- a/include/zid.php
+++ b/include/zid.php
@@ -1,6 +1,7 @@
<?php
use Zotlabs\Lib\Verify;
+use Zotlabs\Zot\Finger;
function is_matrix_url($url) {
diff --git a/include/zot.php b/include/zot.php
index d08146287..5d5ac8424 100644
--- a/include/zot.php
+++ b/include/zot.php
@@ -3611,6 +3611,12 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) {
if(array_key_exists('app',$arr) && $arr['app'])
sync_apps($channel,$arr['app']);
+ if(array_key_exists('addressbook',$arr) && $arr['addressbook'])
+ sync_addressbook($channel,$arr['addressbook']);
+
+ if(array_key_exists('calendar',$arr) && $arr['calendar'])
+ sync_calendar($channel,$arr['calendar']);
+
if(array_key_exists('chatroom',$arr) && $arr['chatroom'])
sync_chatrooms($channel,$arr['chatroom']);
diff --git a/view/css/conversation.css b/view/css/conversation.css
index f7804f5dd..77e56e200 100644
--- a/view/css/conversation.css
+++ b/view/css/conversation.css
@@ -1,15 +1,19 @@
/* jot */
+.jothidden {
+ display:none;
+}
-.jothidden input[type="text"] {
- border: 0px;
- margin: 0px;
- height: 2.5rem;
- width: 100%;
+.jot-poll-option {
+ position: relative;
}
-.jothidden {
- display:none;
+.poll-option-close {
+ position: absolute;
+ right: 0;
+ top: 0;
+ padding: 0.25rem 0 0.25rem 0.5rem;
+ cursor: pointer;
}
#jot-title-wrap,
@@ -18,7 +22,8 @@
border-bottom: 1px solid rgba(0, 0, 0, .2);
}
-#jot-attachment-wrap {
+#jot-attachment-wrap,
+#jot-poll-wrap {
border-top: 1px solid rgba(0, 0, 0, .2);
}
diff --git a/view/js/autocomplete.js b/view/js/autocomplete.js
index 278a0a176..c194338d6 100644
--- a/view/js/autocomplete.js
+++ b/view/js/autocomplete.js
@@ -372,7 +372,7 @@ function string2bb(element) {
return;
if(type=='bbcode') {
- var open_close_elements = ['bold', 'italic', 'underline', 'overline', 'strike', 'superscript', 'subscript', 'quote', 'code', 'open', 'spoiler', 'summary', 'map', 'nobb', 'list', 'checklist', 'ul', 'ol', 'dl', 'li', 'table', 'tr', 'th', 'td', 'center', 'color', 'font', 'size', 'zrl', 'zmg', 'rpost', 'qr', 'observer', 'observer.language','embed', 'highlight', 'url', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
+ var open_close_elements = ['bold', 'italic', 'underline', 'overline', 'strike', 'superscript', 'subscript', 'quote', 'code', 'open', 'spoiler', 'summary', 'map', 'nobb', 'list', 'checklist', 'question', 'answer', 'ul', 'ol', 'dl', 'li', 'table', 'tr', 'th', 'td', 'center', 'color', 'font', 'size', 'zrl', 'zmg', 'rpost', 'qr', 'observer', 'observer.language','embed', 'highlight', 'url', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
var open_elements = ['observer.baseurl', 'observer.address', 'observer.photo', 'observer.name', 'observer.webname', 'observer.url', '*', 'hr' ];
var elements = open_close_elements.concat(open_elements);
diff --git a/view/js/main.js b/view/js/main.js
index 4ec7a71aa..94fd940b2 100644
--- a/view/js/main.js
+++ b/view/js/main.js
@@ -581,8 +581,10 @@ function contextualHelpFocus(target, openSidePanel) {
function updatePageItems(mode, data) {
if(mode === 'append') {
+ newitemcount = 0;
$(data).each(function() {
$('#page-end').before($(this));
+ newitemcount++;
});
if(loadingPage) {
@@ -593,6 +595,10 @@ function updatePageItems(mode, data) {
var e = document.getElementById('content-complete');
if(e) {
pageHasMoreContent = false;
+ } else {
+ if (newitemcount < 1) {
+ pageUpdate();
+ }
}
collapseHeight();
@@ -1267,6 +1273,20 @@ function filestorage(event, nick, id) {
});
}
+function submitPoll(id) {
+
+ $.post('vote/' + id,
+ $('#question-form-' + id).serialize(),
+ function(data) {
+ $.jGrowl(data.message, { sticky: false, theme: ((data.success) ? 'info' : 'notice'), life: 10000 });
+ if(timer) clearTimeout(timer);
+ timer = setTimeout(updateInit,1500);
+ }
+ );
+
+}
+
+
function post_comment(id) {
unpause();
commentBusy = true;
diff --git a/view/ru/hmessages.po b/view/ru/hmessages.po
index d8ddafa96..764228ad2 100644
--- a/view/ru/hmessages.po
+++ b/view/ru/hmessages.po
@@ -9,7 +9,7 @@ msgstr ""
"Project-Id-Version: hubzilla\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-01-19 11:37+0200\n"
-"PO-Revision-Date: 2020-01-19 11:43+0200\n"
+"PO-Revision-Date: 2020-02-25 23:38+0200\n"
"Last-Translator: Max Kostikov <max@kostikov.co>\n"
"Language-Team: Russian (http://www.transifex.com/Friendica/hubzilla/language/ru/)\n"
"MIME-Version: 1.0\n"
@@ -9583,11 +9583,11 @@ msgstr "Результаты поиска для: %s"
#: ../../Zotlabs/Module/Notes.php:56
msgid "Notes App"
-msgstr "Приложение \"Заметки\""
+msgstr "Приложение \"Записки\""
#: ../../Zotlabs/Module/Notes.php:57
msgid "A simple notes app with a widget (note: notes are not encrypted)"
-msgstr "Простое приложение для заметок с виджетом (примечание: заметки не зашифрованы)"
+msgstr "Простое приложение для записок с виджетом (примечание: записки не зашифрованы)"
#: ../../Zotlabs/Module/Moderate.php:65
msgid "Comment approved"
@@ -11015,7 +11015,7 @@ msgstr "Форумы"
#: ../../Zotlabs/Widget/Notes.php:21 ../../Zotlabs/Lib/Apps.php:368
#: ../../extend/addon/hzaddons/workflow/workflow.php:2592
msgid "Notes"
-msgstr "Заметки"
+msgstr "Записки"
#: ../../Zotlabs/Widget/Suggestions.php:53
msgid "Suggestions"
@@ -12268,11 +12268,11 @@ msgstr "Восстановить"
#: ../../Zotlabs/Lib/Apps.php:567
msgid "Add to app-tray"
-msgstr "Добавить в app-tray"
+msgstr "Добавить в правое меню"
#: ../../Zotlabs/Lib/Apps.php:568
msgid "Remove from app-tray"
-msgstr "Удалить из app-tray"
+msgstr "Удалить из правого меню"
#: ../../Zotlabs/Lib/Apps.php:569
msgid "Pin to navbar"
diff --git a/view/ru/hstrings.php b/view/ru/hstrings.php
index 122cb48a5..62585df9b 100644
--- a/view/ru/hstrings.php
+++ b/view/ru/hstrings.php
@@ -2146,8 +2146,8 @@ App::$strings["Welcome to Hubzilla!"] = "Добро пожаловать в Hubz
App::$strings["You have got no unseen posts..."] = "У вас нет непросмотренных публикаций...";
App::$strings["Items tagged with: %s"] = "Объекты помечены как: %s";
App::$strings["Search results for: %s"] = "Результаты поиска для: %s";
-App::$strings["Notes App"] = "Приложение \"Заметки\"";
-App::$strings["A simple notes app with a widget (note: notes are not encrypted)"] = "Простое приложение для заметок с виджетом (примечание: заметки не зашифрованы)";
+App::$strings["Notes App"] = "Приложение \"Записки\"";
+App::$strings["A simple notes app with a widget (note: notes are not encrypted)"] = "Простое приложение для записок с виджетом (примечание: записки не зашифрованы)";
App::$strings["Comment approved"] = "Комментарий одобрен";
App::$strings["Comment deleted"] = "Комментарий удалён";
App::$strings["Webpages App"] = "Приложение \"Веб-страницы\"";
@@ -2480,7 +2480,7 @@ App::$strings["View your connections and/or add somebody whose address you alrea
App::$strings["View your personal stream (this may be empty until you add some connections)"] = "Ваш персональный поток (может быть пуст пока вы не добавите контакты)";
App::$strings["View the public stream. Warning: this content is not moderated"] = "Просмотр публичного потока. Предупреждение: этот контент не модерируется";
App::$strings["Forums"] = "Форумы";
-App::$strings["Notes"] = "Заметки";
+App::$strings["Notes"] = "Записки";
App::$strings["Suggestions"] = "Рекомендации";
App::$strings["See more..."] = "Просмотреть больше...";
App::$strings["New Network Activity"] = "Новая сетевая активность";
@@ -2772,8 +2772,8 @@ App::$strings["My Chatrooms"] = "Мои чаты";
App::$strings["Channel Export"] = "Экспорт канала";
App::$strings["Purchase"] = "Купить";
App::$strings["Undelete"] = "Восстановить";
-App::$strings["Add to app-tray"] = "Добавить в app-tray";
-App::$strings["Remove from app-tray"] = "Удалить из app-tray";
+App::$strings["Add to app-tray"] = "Добавить в правое меню";
+App::$strings["Remove from app-tray"] = "Удалить из правого меню";
App::$strings["Pin to navbar"] = "Добавить на панель навигации";
App::$strings["Unpin from navbar"] = "Удалить с панели навигации";
App::$strings["Privacy conflict. Discretion advised."] = "Конфиликт настроек конфиденциальности.";
diff --git a/view/theme/redbasic/css/style.css b/view/theme/redbasic/css/style.css
index 3bee84378..1ababecfc 100644
--- a/view/theme/redbasic/css/style.css
+++ b/view/theme/redbasic/css/style.css
@@ -1605,6 +1605,10 @@ dl.bb-dl > dd > li {
font-size: 100%;
}
+.bootstrap-tagsinput input {
+ height: 2.5rem;
+}
+
/* Abusing theme-green is less work than makeing a new new one */
.theme-green .back-bar .selected-bar {
background-color: #000000;
diff --git a/view/tpl/jot-header.tpl b/view/tpl/jot-header.tpl
index 7b1f4ee05..d519fd666 100755
--- a/view/tpl/jot-header.tpl
+++ b/view/tpl/jot-header.tpl
@@ -58,6 +58,9 @@ var activeCommentText = '';
$('#id_mimetype').on('load', jotSetMime);
$('#id_mimetype').on('change', jotSetMime);
+ $('#jot-add-option').on('click', jotAddOption);
+ $(document).on('click', '.poll-option-close', jotRemoveOption);
+
function jotSetMime() {
var mtype = $('#id_mimetype').val();
if(mtype == 'text/bbcode')
@@ -122,6 +125,7 @@ var activeCommentText = '';
activeCommentID = 0;
},
});
+
});
function deleteCheckedItems() {
@@ -309,6 +313,7 @@ var activeCommentText = '';
function itemCancel() {
$("#jot-title").val('');
$("#profile-jot-text").val('');
+ $(".jot-poll-option input").val('');
$("#jot-category").tagsinput('removeAll');
postSaveChanges('clean');
@@ -317,6 +322,7 @@ var activeCommentText = '';
$(".jothidden").hide();
$("#profile-jot-text").removeClass('jot-expanded');
$("#profile-jot-tools").addClass('d-none');
+ $("#jot-poll-wrap").addClass('d-none');
$("#jot-preview-content").html('').hide();
editor = false;
{{else}}
@@ -512,6 +518,19 @@ var activeCommentText = '';
}
+ function initPoll() {
+ $('#jot-poll-wrap').toggleClass('d-none');
+ }
+
+ function jotAddOption() {
+ var option = '<div class="jot-poll-option form-group"><input class="w-100 border-0" name="poll_answers[]" type="text" value="" placeholder="Option"><div class="poll-option-close"><i class="fa fa-close"></i></div></div>';
+ $('#jot-poll-options').append(option);
+ }
+
+ function jotRemoveOption(e) {
+ $(this).closest('.jot-poll-option').remove();
+ }
+
</script>
<script>
diff --git a/view/tpl/jot.tpl b/view/tpl/jot.tpl
index b4616db6d..4a9717c01 100755
--- a/view/tpl/jot.tpl
+++ b/view/tpl/jot.tpl
@@ -30,15 +30,15 @@
{{if $webpage}}
<div id="jot-pagetitle-wrap" class="jothidden">
- <input name="pagetitle" id="jot-pagetitle" type="text" placeholder="{{$placeholdpagetitle}}" value="{{$pagetitle}}">
+ <input class="w-100 border-0" name="pagetitle" id="jot-pagetitle" type="text" placeholder="{{$placeholdpagetitle}}" value="{{$pagetitle}}">
</div>
{{/if}}
<div id="jot-title-wrap" class="jothidden">
- <input name="title" id="jot-title" type="text" placeholder="{{$placeholdertitle}}" tabindex="1" value="{{$title}}">
+ <input class="w-100 border-0" name="title" id="jot-title" type="text" placeholder="{{$placeholdertitle}}" tabindex="1" value="{{$title}}">
</div>
{{if $catsenabled}}
<div id="jot-category-wrap" class="jothidden">
- <input name="category" id="jot-category" type="text" placeholder="{{$placeholdercategory}}" value="{{$category}}" data-role="cat-tagsinput">
+ <input class="w-100 border-0" name="category" id="jot-category" type="text" placeholder="{{$placeholdercategory}}" value="{{$category}}" data-role="cat-tagsinput">
</div>
{{/if}}
<div id="jot-text-wrap">
@@ -59,6 +59,34 @@
<input class="jot-attachment" name="attachment" id="jot-attachment" type="text" value="{{$attachment}}" readonly="readonly" onclick="this.select();">
</div>
{{/if}}
+ <div id="jot-poll-wrap" class="p-2 d-none">
+ <div id="jot-poll-options">
+ <div class="jot-poll-option form-group">
+ <input class="w-100 border-0" name="poll_answers[]" type="text" value="" placeholder="{{$poll_option_label}}">
+ </div>
+ <div class="jot-poll-option form-group">
+ <input class="w-100 border-0" name="poll_answers[]" type="text" value="" placeholder="{{$poll_option_label}}">
+ </div>
+ </div>
+ {{include file="field_checkbox.tpl" field=$multiple_answers}}
+ <div id="jot-poll-tools" class="clearfix">
+ <div id="poll-tools-left" class="float-left">
+ <button id="jot-add-option" class="btn btn-outline-secondary btn-sm" type="button">
+ <i class="fa fa-plus"></i> {{$poll_add_option_label}}
+ </button>
+ </div>
+ <div id="poll-tools-right" class="float-right">
+ <div class="input-group">
+ <input type="text" name="poll_expire_value" class="form-control" value="10" size="3">
+ <select class="form-control" id="duration-select" name="poll_expire_unit">
+ <option value="Minutes">{{$poll_expire_unit_label.0}}</option>
+ <option value="Hours">{{$poll_expire_unit_label.1}}</option>
+ <option value="Days" selected="selected">{{$poll_expire_unit_label.2}}</option>
+ </select>
+ </div>
+ </div>
+ </div>
+ </div>
<div id="profile-jot-submit-wrapper" class="clearfix p-2 jothidden">
<div id="profile-jot-submit-left" class="btn-toolbar float-left">
{{if $bbcode}}
@@ -132,6 +160,11 @@
<i id="profile-voting" class="fa fa-square-o jot-icons"></i>
</button>
{{/if}}
+
+ <button type="button" id="profile-poll-wrapper" class="btn btn-outline-secondary btn-sm" title="{{$poll}}" onclick="initPoll();">
+ <i id="profile-poll" class="fa fa-bar-chart jot-icons"></i>
+ </button>
+
{{if $feature_nocomment}}
<button id="profile-nocomment-wrapper" class="btn btn-outline-secondary btn-sm" title="{{$nocommenttitle}}" onclick="toggleNoComment();return false;">
<i id="profile-nocomment" class="fa fa-comments jot-icons"></i>
@@ -176,6 +209,7 @@
{{if $feature_voting}}
<a class="dropdown-item" href="#" onclick="toggleVoting(); return false;"><i id="profile-voting-sub" class="fa fa-square-o"></i>&nbsp;{{$voting}}</a>
{{/if}}
+ <a class="dropdown-item" href="#" onclick="initPoll(); return false"><i id="profile-poll" class="fa fa-bar-chart jot-icons"></i>&nbsp;{{$poll}}</a>
{{if $feature_nocomment}}
<a class="dropdown-item" href="#" onclick="toggleNoComment(); return false;"><i id="profile-nocomment-sub" class="fa fa-comments"></i>&nbsp;{{$nocommenttitlesub}}</a>
{{/if}}