aboutsummaryrefslogtreecommitdiffstats
path: root/Zotlabs
diff options
context:
space:
mode:
authorMario <mario@mariovavti.com>2020-03-02 09:50:11 +0000
committerMario <mario@mariovavti.com>2020-03-02 09:50:11 +0000
commitba566fd64bbb57fb52779a3f29f30e730cab7744 (patch)
tree85ff6e77a4a0bbbdde7638055270f5a3a2b33f55 /Zotlabs
parent19bb9e018152ce528846fb955b58d76f1bb6bdec (diff)
parent85c07d57fe0f468eb8cc84584f3636b590aa929f (diff)
downloadvolse-hubzilla-ba566fd64bbb57fb52779a3f29f30e730cab7744.tar.gz
volse-hubzilla-ba566fd64bbb57fb52779a3f29f30e730cab7744.tar.bz2
volse-hubzilla-ba566fd64bbb57fb52779a3f29f30e730cab7744.zip
Merge branch 'dev' into z6connect
Diffstat (limited to 'Zotlabs')
-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
15 files changed, 872 insertions, 288 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())