diff options
-rw-r--r-- | Zotlabs/Daemon/Notifier.php | 12 | ||||
-rw-r--r-- | Zotlabs/Lib/Activity.php | 220 | ||||
-rw-r--r-- | Zotlabs/Lib/Enotify.php | 5 | ||||
-rw-r--r-- | Zotlabs/Lib/Libsync.php | 12 | ||||
-rw-r--r-- | Zotlabs/Lib/Libzot.php | 20 | ||||
-rw-r--r-- | Zotlabs/Lib/NativeWikiPage.php | 7 | ||||
-rw-r--r-- | Zotlabs/Lib/System.php | 11 | ||||
-rw-r--r-- | Zotlabs/Lib/ThreadItem.php | 4 | ||||
-rw-r--r-- | Zotlabs/Module/Apschema.php | 5 | ||||
-rw-r--r-- | Zotlabs/Module/Cdav.php | 531 | ||||
-rw-r--r-- | Zotlabs/Module/Item.php | 133 | ||||
-rw-r--r-- | Zotlabs/Module/Network.php | 24 | ||||
-rw-r--r-- | Zotlabs/Module/Vote.php | 143 | ||||
-rw-r--r-- | Zotlabs/Module/Well_known.php | 5 | ||||
-rw-r--r-- | Zotlabs/Widget/Activity_filter.php | 28 | ||||
-rwxr-xr-x | boot.php | 4 | ||||
-rw-r--r-- | images/article.gif | bin | 1060 -> 0 bytes | |||
-rw-r--r-- | images/audio.gif | bin | 559 -> 0 bytes | |||
-rw-r--r-- | images/b_block.gif | bin | 83 -> 0 bytes | |||
-rw-r--r-- | images/b_drop.gif | bin | 138 -> 0 bytes | |||
-rw-r--r-- | images/b_drop.png | bin | 311 -> 0 bytes | |||
-rw-r--r-- | images/b_drophide.gif | bin | 111 -> 0 bytes | |||
-rw-r--r-- | images/b_dropshow.gif | bin | 138 -> 0 bytes | |||
-rw-r--r-- | images/b_edit.gif | bin | 311 -> 0 bytes | |||
-rw-r--r-- | images/b_edit.png | bin | 451 -> 0 bytes | |||
-rw-r--r-- | images/bug-x.gif | bin | 134 -> 0 bytes | |||
-rw-r--r-- | images/calendar.png | bin | 853 -> 0 bytes | |||
-rw-r--r-- | images/camera-icon.gif | bin | 1015 -> 0 bytes | |||
-rw-r--r-- | images/checkbox-checked-32.png | bin | 1397 -> 0 bytes | |||
-rw-r--r-- | images/checkbox-unchecked-32.png | bin | 1063 -> 0 bytes | |||
-rw-r--r-- | images/connect-bg.png | bin | 689 -> 0 bytes | |||
-rw-r--r-- | images/content-types.png | bin | 3892 -> 0 bytes | |||
-rw-r--r-- | images/default-group-mm.png | bin | 598 -> 0 bytes | |||
-rw-r--r-- | images/document.gif | bin | 1362 -> 0 bytes | |||
-rw-r--r-- | images/ghash-32.png | bin | 1824 -> 0 bytes | |||
-rw-r--r-- | images/globe.gif | bin | 1025 -> 0 bytes | |||
-rw-r--r-- | images/hide_off.png | bin | 281 -> 0 bytes | |||
-rw-r--r-- | images/hide_on.png | bin | 277 -> 0 bytes | |||
-rw-r--r-- | images/hubzilla_house_arrows.png | bin | 22076 -> 0 bytes | |||
-rw-r--r-- | images/hz-bookmark-32.png | bin | 973 -> 0 bytes | |||
-rw-r--r-- | images/icons.png | bin | 12638 -> 0 bytes | |||
-rw-r--r-- | images/larrow.gif | bin | 211 -> 0 bytes | |||
-rw-r--r-- | images/larrw.gif | bin | 1004 -> 0 bytes | |||
-rw-r--r-- | images/link-icon.gif | bin | 145 -> 0 bytes | |||
-rw-r--r-- | images/lock_icon.gif | bin | 932 -> 0 bytes | |||
-rw-r--r-- | images/lrarrow.gif | bin | 236 -> 0 bytes | |||
-rw-r--r-- | images/mapicon.gif | bin | 1042 -> 0 bytes | |||
-rw-r--r-- | images/no.gif | bin | 631 -> 0 bytes | |||
-rw-r--r-- | images/noglobe.gif | bin | 606 -> 0 bytes | |||
-rw-r--r-- | images/nosign.jpg | bin | 6498 -> 0 bytes | |||
-rw-r--r-- | images/nosign.png | bin | 17868 -> 0 bytes | |||
-rw-r--r-- | images/onoff.jpg | bin | 502 -> 0 bytes | |||
-rw-r--r-- | images/pen.png | bin | 252 -> 0 bytes | |||
-rw-r--r-- | images/pencil.gif | bin | 553 -> 0 bytes | |||
-rw-r--r-- | images/penhover.png | bin | 270 -> 0 bytes | |||
-rw-r--r-- | images/people.gif | bin | 1478 -> 0 bytes | |||
-rw-r--r-- | images/plugin.png | bin | 2043 -> 0 bytes | |||
-rw-r--r-- | images/rarrow.gif | bin | 212 -> 0 bytes | |||
-rw-r--r-- | images/rarrw.gif | bin | 999 -> 0 bytes | |||
-rw-r--r-- | images/recycle.gif | bin | 612 -> 0 bytes | |||
-rw-r--r-- | images/red_antiprism.png | bin | 120847 -> 0 bytes | |||
-rw-r--r-- | images/red_antiprism.xcf | bin | 659160 -> 0 bytes | |||
-rw-r--r-- | images/redmatrix_logo.svg | 85 | ||||
-rw-r--r-- | images/remote-link.gif | bin | 237 -> 0 bytes | |||
-rw-r--r-- | images/rhash-16.png | bin | 679 -> 0 bytes | |||
-rw-r--r-- | images/rhash-32.png | bin | 1322 -> 0 bytes | |||
-rw-r--r-- | images/rhash-64.png | bin | 3115 -> 0 bytes | |||
-rw-r--r-- | images/rhash.xcf | bin | 94324 -> 0 bytes | |||
-rw-r--r-- | images/rm-16.png | bin | 676 -> 0 bytes | |||
-rw-r--r-- | images/rm-32.png | bin | 1186 -> 0 bytes | |||
-rw-r--r-- | images/rm-64.png | bin | 1897 -> 0 bytes | |||
-rw-r--r-- | images/rm-old.png | bin | 12441 -> 0 bytes | |||
-rw-r--r-- | images/rm-transparent-dark-background.png | bin | 6370 -> 0 bytes | |||
-rw-r--r-- | images/rm-transparent-large-old.png | bin | 42706 -> 0 bytes | |||
-rw-r--r-- | images/rm.png | bin | 6362 -> 0 bytes | |||
-rw-r--r-- | images/rm.svg | 132 | ||||
-rw-r--r-- | images/rm300.png | bin | 6426 -> 0 bytes | |||
-rw-r--r-- | images/rotator.gif | bin | 826 -> 0 bytes | |||
-rw-r--r-- | images/search_18.png | bin | 3302 -> 0 bytes | |||
-rw-r--r-- | images/selected.png | bin | 502 -> 0 bytes | |||
-rw-r--r-- | images/share.gif | bin | 155 -> 0 bytes | |||
-rw-r--r-- | images/show_all_off.png | bin | 539 -> 0 bytes | |||
-rw-r--r-- | images/show_all_on.png | bin | 464 -> 0 bytes | |||
-rw-r--r-- | images/show_off.png | bin | 244 -> 0 bytes | |||
-rw-r--r-- | images/show_on.png | bin | 218 -> 0 bytes | |||
-rw-r--r-- | images/spencil.gif | bin | 497 -> 0 bytes | |||
-rw-r--r-- | images/star.png | bin | 388 -> 0 bytes | |||
-rw-r--r-- | images/star_dummy.png | bin | 183 -> 0 bytes | |||
-rw-r--r-- | images/tag.png | bin | 528 -> 0 bytes | |||
-rw-r--r-- | images/tag_b.png | bin | 346 -> 0 bytes | |||
-rw-r--r-- | images/tools.png | bin | 490 -> 0 bytes | |||
-rw-r--r-- | images/unlock_icon.gif | bin | 938 -> 0 bytes | |||
-rw-r--r-- | images/video.gif | bin | 257 -> 0 bytes | |||
-rw-r--r-- | include/bbcode.php | 142 | ||||
-rw-r--r-- | include/cdav.php | 186 | ||||
-rw-r--r-- | include/channel.php | 42 | ||||
-rw-r--r-- | include/conversation.php | 38 | ||||
-rw-r--r-- | include/features.php | 16 | ||||
-rw-r--r-- | include/import.php | 138 | ||||
-rwxr-xr-x | include/items.php | 6 | ||||
-rw-r--r-- | include/language.php | 7 | ||||
-rw-r--r-- | include/nav.php | 2 | ||||
-rw-r--r-- | include/text.php | 105 | ||||
-rw-r--r-- | include/zid.php | 1 | ||||
-rw-r--r-- | include/zot.php | 6 | ||||
-rw-r--r-- | view/css/conversation.css | 21 | ||||
-rw-r--r-- | view/js/autocomplete.js | 2 | ||||
-rw-r--r-- | view/js/main.js | 20 | ||||
-rw-r--r-- | view/ru/hmessages.po | 12 | ||||
-rw-r--r-- | view/ru/hstrings.php | 10 | ||||
-rw-r--r-- | view/theme/redbasic/css/style.css | 4 | ||||
-rwxr-xr-x | view/tpl/jot-header.tpl | 19 | ||||
-rwxr-xr-x | view/tpl/jot.tpl | 40 |
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()) @@ -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 Binary files differdeleted file mode 100644 index 91aeef000..000000000 --- a/images/article.gif +++ /dev/null diff --git a/images/audio.gif b/images/audio.gif Binary files differdeleted file mode 100644 index 4be977116..000000000 --- a/images/audio.gif +++ /dev/null diff --git a/images/b_block.gif b/images/b_block.gif Binary files differdeleted file mode 100644 index 3bc7c056b..000000000 --- a/images/b_block.gif +++ /dev/null diff --git a/images/b_drop.gif b/images/b_drop.gif Binary files differdeleted file mode 100644 index b08c68b62..000000000 --- a/images/b_drop.gif +++ /dev/null diff --git a/images/b_drop.png b/images/b_drop.png Binary files differdeleted file mode 100644 index 6fc4d3b20..000000000 --- a/images/b_drop.png +++ /dev/null diff --git a/images/b_drophide.gif b/images/b_drophide.gif Binary files differdeleted file mode 100644 index 1207a935b..000000000 --- a/images/b_drophide.gif +++ /dev/null diff --git a/images/b_dropshow.gif b/images/b_dropshow.gif Binary files differdeleted file mode 100644 index b08c68b62..000000000 --- a/images/b_dropshow.gif +++ /dev/null diff --git a/images/b_edit.gif b/images/b_edit.gif Binary files differdeleted file mode 100644 index 79cb3c144..000000000 --- a/images/b_edit.gif +++ /dev/null diff --git a/images/b_edit.png b/images/b_edit.png Binary files differdeleted file mode 100644 index 05711a094..000000000 --- a/images/b_edit.png +++ /dev/null diff --git a/images/bug-x.gif b/images/bug-x.gif Binary files differdeleted file mode 100644 index 10936caa7..000000000 --- a/images/bug-x.gif +++ /dev/null diff --git a/images/calendar.png b/images/calendar.png Binary files differdeleted file mode 100644 index fbf52933d..000000000 --- a/images/calendar.png +++ /dev/null diff --git a/images/camera-icon.gif b/images/camera-icon.gif Binary files differdeleted file mode 100644 index a4adf9adf..000000000 --- a/images/camera-icon.gif +++ /dev/null diff --git a/images/checkbox-checked-32.png b/images/checkbox-checked-32.png Binary files differdeleted file mode 100644 index 01e51c203..000000000 --- a/images/checkbox-checked-32.png +++ /dev/null diff --git a/images/checkbox-unchecked-32.png b/images/checkbox-unchecked-32.png Binary files differdeleted file mode 100644 index 80ce92243..000000000 --- a/images/checkbox-unchecked-32.png +++ /dev/null diff --git a/images/connect-bg.png b/images/connect-bg.png Binary files differdeleted file mode 100644 index 0611c73e5..000000000 --- a/images/connect-bg.png +++ /dev/null diff --git a/images/content-types.png b/images/content-types.png Binary files differdeleted file mode 100644 index e46eba610..000000000 --- a/images/content-types.png +++ /dev/null diff --git a/images/default-group-mm.png b/images/default-group-mm.png Binary files differdeleted file mode 100644 index bfc8b335a..000000000 --- a/images/default-group-mm.png +++ /dev/null diff --git a/images/document.gif b/images/document.gif Binary files differdeleted file mode 100644 index 02d940968..000000000 --- a/images/document.gif +++ /dev/null diff --git a/images/ghash-32.png b/images/ghash-32.png Binary files differdeleted file mode 100644 index 6b4913b82..000000000 --- a/images/ghash-32.png +++ /dev/null diff --git a/images/globe.gif b/images/globe.gif Binary files differdeleted file mode 100644 index 3f17c5d32..000000000 --- a/images/globe.gif +++ /dev/null diff --git a/images/hide_off.png b/images/hide_off.png Binary files differdeleted file mode 100644 index d504ca2e2..000000000 --- a/images/hide_off.png +++ /dev/null diff --git a/images/hide_on.png b/images/hide_on.png Binary files differdeleted file mode 100644 index 960744c06..000000000 --- a/images/hide_on.png +++ /dev/null diff --git a/images/hubzilla_house_arrows.png b/images/hubzilla_house_arrows.png Binary files differdeleted file mode 100644 index 56402a96b..000000000 --- a/images/hubzilla_house_arrows.png +++ /dev/null diff --git a/images/hz-bookmark-32.png b/images/hz-bookmark-32.png Binary files differdeleted file mode 100644 index f3a09d9b4..000000000 --- a/images/hz-bookmark-32.png +++ /dev/null diff --git a/images/icons.png b/images/icons.png Binary files differdeleted file mode 100644 index 34d77a136..000000000 --- a/images/icons.png +++ /dev/null diff --git a/images/larrow.gif b/images/larrow.gif Binary files differdeleted file mode 100644 index ab08bb57e..000000000 --- a/images/larrow.gif +++ /dev/null diff --git a/images/larrw.gif b/images/larrw.gif Binary files differdeleted file mode 100644 index 08902d772..000000000 --- a/images/larrw.gif +++ /dev/null diff --git a/images/link-icon.gif b/images/link-icon.gif Binary files differdeleted file mode 100644 index c012d716e..000000000 --- a/images/link-icon.gif +++ /dev/null diff --git a/images/lock_icon.gif b/images/lock_icon.gif Binary files differdeleted file mode 100644 index b6b1b7fed..000000000 --- a/images/lock_icon.gif +++ /dev/null diff --git a/images/lrarrow.gif b/images/lrarrow.gif Binary files differdeleted file mode 100644 index fa2676944..000000000 --- a/images/lrarrow.gif +++ /dev/null diff --git a/images/mapicon.gif b/images/mapicon.gif Binary files differdeleted file mode 100644 index dd20c209b..000000000 --- a/images/mapicon.gif +++ /dev/null diff --git a/images/no.gif b/images/no.gif Binary files differdeleted file mode 100644 index eb0f2b0e9..000000000 --- a/images/no.gif +++ /dev/null diff --git a/images/noglobe.gif b/images/noglobe.gif Binary files differdeleted file mode 100644 index 81e176567..000000000 --- a/images/noglobe.gif +++ /dev/null diff --git a/images/nosign.jpg b/images/nosign.jpg Binary files differdeleted file mode 100644 index b73629332..000000000 --- a/images/nosign.jpg +++ /dev/null diff --git a/images/nosign.png b/images/nosign.png Binary files differdeleted file mode 100644 index 773c9514d..000000000 --- a/images/nosign.png +++ /dev/null diff --git a/images/onoff.jpg b/images/onoff.jpg Binary files differdeleted file mode 100644 index 7884912a7..000000000 --- a/images/onoff.jpg +++ /dev/null diff --git a/images/pen.png b/images/pen.png Binary files differdeleted file mode 100644 index 46b404941..000000000 --- a/images/pen.png +++ /dev/null diff --git a/images/pencil.gif b/images/pencil.gif Binary files differdeleted file mode 100644 index 26bfb0c9a..000000000 --- a/images/pencil.gif +++ /dev/null diff --git a/images/penhover.png b/images/penhover.png Binary files differdeleted file mode 100644 index be48d77b4..000000000 --- a/images/penhover.png +++ /dev/null diff --git a/images/people.gif b/images/people.gif Binary files differdeleted file mode 100644 index cac31db2a..000000000 --- a/images/people.gif +++ /dev/null diff --git a/images/plugin.png b/images/plugin.png Binary files differdeleted file mode 100644 index 08b09e060..000000000 --- a/images/plugin.png +++ /dev/null diff --git a/images/rarrow.gif b/images/rarrow.gif Binary files differdeleted file mode 100644 index a2d5df3e9..000000000 --- a/images/rarrow.gif +++ /dev/null diff --git a/images/rarrw.gif b/images/rarrw.gif Binary files differdeleted file mode 100644 index 849238c2d..000000000 --- a/images/rarrw.gif +++ /dev/null diff --git a/images/recycle.gif b/images/recycle.gif Binary files differdeleted file mode 100644 index 01b3e13b4..000000000 --- a/images/recycle.gif +++ /dev/null diff --git a/images/red_antiprism.png b/images/red_antiprism.png Binary files differdeleted file mode 100644 index 80637adf5..000000000 --- a/images/red_antiprism.png +++ /dev/null diff --git a/images/red_antiprism.xcf b/images/red_antiprism.xcf Binary files differdeleted file mode 100644 index bd3dd826c..000000000 --- a/images/red_antiprism.xcf +++ /dev/null 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 Binary files differdeleted file mode 100644 index 64de29aee..000000000 --- a/images/remote-link.gif +++ /dev/null diff --git a/images/rhash-16.png b/images/rhash-16.png Binary files differdeleted file mode 100644 index 23dd9e4b3..000000000 --- a/images/rhash-16.png +++ /dev/null diff --git a/images/rhash-32.png b/images/rhash-32.png Binary files differdeleted file mode 100644 index 564556d48..000000000 --- a/images/rhash-32.png +++ /dev/null diff --git a/images/rhash-64.png b/images/rhash-64.png Binary files differdeleted file mode 100644 index 2e8396760..000000000 --- a/images/rhash-64.png +++ /dev/null diff --git a/images/rhash.xcf b/images/rhash.xcf Binary files differdeleted file mode 100644 index 960552290..000000000 --- a/images/rhash.xcf +++ /dev/null diff --git a/images/rm-16.png b/images/rm-16.png Binary files differdeleted file mode 100644 index 9361ef2b7..000000000 --- a/images/rm-16.png +++ /dev/null diff --git a/images/rm-32.png b/images/rm-32.png Binary files differdeleted file mode 100644 index 8416edd97..000000000 --- a/images/rm-32.png +++ /dev/null diff --git a/images/rm-64.png b/images/rm-64.png Binary files differdeleted file mode 100644 index 8021f9d55..000000000 --- a/images/rm-64.png +++ /dev/null diff --git a/images/rm-old.png b/images/rm-old.png Binary files differdeleted file mode 100644 index 29caaf3f6..000000000 --- a/images/rm-old.png +++ /dev/null diff --git a/images/rm-transparent-dark-background.png b/images/rm-transparent-dark-background.png Binary files differdeleted file mode 100644 index 7f94ccdd9..000000000 --- a/images/rm-transparent-dark-background.png +++ /dev/null diff --git a/images/rm-transparent-large-old.png b/images/rm-transparent-large-old.png Binary files differdeleted file mode 100644 index 8844af8f0..000000000 --- a/images/rm-transparent-large-old.png +++ /dev/null diff --git a/images/rm.png b/images/rm.png Binary files differdeleted file mode 100644 index 99ef21328..000000000 --- a/images/rm.png +++ /dev/null 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 Binary files differdeleted file mode 100644 index bc2600348..000000000 --- a/images/rm300.png +++ /dev/null diff --git a/images/rotator.gif b/images/rotator.gif Binary files differdeleted file mode 100644 index 3797ec3e4..000000000 --- a/images/rotator.gif +++ /dev/null diff --git a/images/search_18.png b/images/search_18.png Binary files differdeleted file mode 100644 index 539739670..000000000 --- a/images/search_18.png +++ /dev/null diff --git a/images/selected.png b/images/selected.png Binary files differdeleted file mode 100644 index 79a7c77c6..000000000 --- a/images/selected.png +++ /dev/null diff --git a/images/share.gif b/images/share.gif Binary files differdeleted file mode 100644 index 035fa2e38..000000000 --- a/images/share.gif +++ /dev/null diff --git a/images/show_all_off.png b/images/show_all_off.png Binary files differdeleted file mode 100644 index cc96d28f0..000000000 --- a/images/show_all_off.png +++ /dev/null diff --git a/images/show_all_on.png b/images/show_all_on.png Binary files differdeleted file mode 100644 index 87a7710b5..000000000 --- a/images/show_all_on.png +++ /dev/null diff --git a/images/show_off.png b/images/show_off.png Binary files differdeleted file mode 100644 index 4bcf47123..000000000 --- a/images/show_off.png +++ /dev/null diff --git a/images/show_on.png b/images/show_on.png Binary files differdeleted file mode 100644 index b8d1f5bac..000000000 --- a/images/show_on.png +++ /dev/null diff --git a/images/spencil.gif b/images/spencil.gif Binary files differdeleted file mode 100644 index 0a2551ac0..000000000 --- a/images/spencil.gif +++ /dev/null diff --git a/images/star.png b/images/star.png Binary files differdeleted file mode 100644 index 0b00cb189..000000000 --- a/images/star.png +++ /dev/null diff --git a/images/star_dummy.png b/images/star_dummy.png Binary files differdeleted file mode 100644 index ce11f30d4..000000000 --- a/images/star_dummy.png +++ /dev/null diff --git a/images/tag.png b/images/tag.png Binary files differdeleted file mode 100644 index 40c5fd44e..000000000 --- a/images/tag.png +++ /dev/null diff --git a/images/tag_b.png b/images/tag_b.png Binary files differdeleted file mode 100644 index 66c03415d..000000000 --- a/images/tag_b.png +++ /dev/null diff --git a/images/tools.png b/images/tools.png Binary files differdeleted file mode 100644 index c17094a81..000000000 --- a/images/tools.png +++ /dev/null diff --git a/images/unlock_icon.gif b/images/unlock_icon.gif Binary files differdeleted file mode 100644 index 254ac8bfd..000000000 --- a/images/unlock_icon.gif +++ /dev/null diff --git a/images/video.gif b/images/video.gif Binary files differdeleted file mode 100644 index e4d5e8cdd..000000000 --- a/images/video.gif +++ /dev/null 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=\"\;(.*?)\"\;/ism", $attributes, $matches); + if ($x) { + $alt = $matches[1]; + } + + $x = preg_match("/width='(.*?)'/ism", $attributes, $matches); + if ($x) { + $width = $matches[1]; + } + + $x = preg_match("/width=\"\;(.*?)\"\;/ism", $attributes, $matches); + if ($x) { + $width = $matches[1]; + } + + $x = preg_match("/height='(.*?)'/ism", $attributes, $matches); + if ($x) { + $height = $matches[1]; + } + + $x = preg_match("/height=\"\;(.*?)\"\;/ism", $attributes, $matches); + if ($x) { + $height = $matches[1]; + } + + $x = preg_match("/style='(.*?)'/ism", $attributes, $matches); + if ($x) { + $style = $matches[1]; + } + + $x = preg_match("/style=\"\;(.*?)\"\;/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> {{$voting}}</a> {{/if}} + <a class="dropdown-item" href="#" onclick="initPoll(); return false"><i id="profile-poll" class="fa fa-bar-chart jot-icons"></i> {{$poll}}</a> {{if $feature_nocomment}} <a class="dropdown-item" href="#" onclick="toggleNoComment(); return false;"><i id="profile-nocomment-sub" class="fa fa-comments"></i> {{$nocommenttitlesub}}</a> {{/if}} |