diff options
Diffstat (limited to 'include')
-rw-r--r-- | include/activities.php | 126 | ||||
-rw-r--r-- | include/api_zot.php | 2 | ||||
-rw-r--r-- | include/attach.php | 167 | ||||
-rw-r--r-- | include/bbcode.php | 828 | ||||
-rw-r--r-- | include/channel.php | 16 | ||||
-rw-r--r-- | include/contact_widgets.php | 2 | ||||
-rw-r--r-- | include/conversation.php | 325 | ||||
-rw-r--r-- | include/dba/dba_transaction.php | 64 | ||||
-rw-r--r-- | include/event.php | 13 | ||||
-rw-r--r-- | include/feedutils.php | 40 | ||||
-rw-r--r-- | include/html2bbcode.php | 90 | ||||
-rw-r--r-- | include/html2plain.php | 5 | ||||
-rw-r--r-- | include/import.php | 6 | ||||
-rw-r--r-- | include/items.php | 205 | ||||
-rw-r--r-- | include/markdown.php | 23 | ||||
-rw-r--r-- | include/network.php | 55 | ||||
-rw-r--r-- | include/oembed.php | 11 | ||||
-rw-r--r-- | include/photo/photo_driver.php | 9 | ||||
-rw-r--r-- | include/photos.php | 6 | ||||
-rw-r--r-- | include/plugin.php | 32 | ||||
-rw-r--r-- | include/sharedwithme.php | 32 | ||||
-rw-r--r-- | include/taxonomy.php | 4 | ||||
-rw-r--r-- | include/text.php | 270 | ||||
-rw-r--r-- | include/xchan.php | 2 |
24 files changed, 983 insertions, 1350 deletions
diff --git a/include/activities.php b/include/activities.php index 68c995338..c06a8f6c4 100644 --- a/include/activities.php +++ b/include/activities.php @@ -1,95 +1,83 @@ <?php /** @file */ -function profile_activity($changed, $value) { +use Zotlabs\Lib\Activity; +use Zotlabs\Daemon\Master; - if(! local_channel() || ! is_array($changed) || ! count($changed)) - return; +function profile_activity($changed, $value) { - if(! get_pconfig(local_channel(),'system','post_profilechange')) + if (!local_channel() || !is_array($changed) || !count($changed)) { return; + } - require_once('include/items.php'); - - $self = App::get_channel(); - - if(! count($self)) + if (!get_pconfig(local_channel(), 'system', 'post_profilechange')) { return; + } - $arr = array(); - $arr['uuid'] = item_message_id(); - $arr['mid'] = $arr['parent_mid'] = z_root() . '/item/' . $arr['uuid']; - $arr['uid'] = local_channel(); - $arr['aid'] = $self['channel_account_id']; - $arr['owner_xchan'] = $arr['author_xchan'] = $self['xchan_hash']; - - $arr['item_wall'] = 1; - $arr['item_origin'] = 1; - $arr['item_thread_top'] = 1; - $arr['verb'] = ACTIVITY_UPDATE; - $arr['obj_type'] = ACTIVITY_OBJ_PROFILE; + $channel = App::get_channel(); - $arr['plink'] = z_root() . '/channel/' . $self['channel_address'] . '/?f=&mid=' . urlencode($arr['mid']); - - $A = '[url=' . z_root() . '/channel/' . $self['channel_address'] . ']' . $self['channel_name'] . '[/url]'; + $arr['verb'] = 'Update'; + $arr['obj_type'] = 'Profile'; + $channel_link = '[url=' . z_root() . '/channel/' . $channel['channel_address'] . ']' . $channel['channel_name'] . '[/url]'; $changes = ''; $t = count($changed); $z = 0; - foreach($changed as $ch) { - if(strlen($changes)) { - if ($z == ($t - 1)) + $photo = false; + foreach ($changed as $ch) { + if (strlen($changes)) { + if ($z == ($t - 1)) { $changes .= t(' and '); - else - $changes .= ', '; + } else { + $changes .= t(', '); + } + } + + if (in_array($ch, [t('Profile Photo'), t('Cover Photo')])) { + $photo = true; + $photo_size = (($ch === t('Profile Photo')) ? 4 : 8); } - $z ++; + + $z++; $changes .= $ch; } - $prof = '[url=' . z_root() . '/profile/' . $self['channel_address'] . ']' . t('public profile') . '[/url]'; + $profile_link = '[url=' . z_root() . '/profile/' . $channel['channel_address'] . ']' . t('public profile') . '[/url]'; - if($t == 1 && strlen($value)) { + if ($t == 1 && strlen($value)) { // if it's a url, the HTML quotes will mess it up, so link it and don't try and zidify it because we don't know what it points to. - $value = preg_replace_callback("/([^\]\='".'"'."]|^|\#\^)(https?\:\/\/[a-zA-Z0-9\pL\:\/\-\?\&\;\.\=\@\_\~\#\%\$\!\+\,]+)/ismu", 'red_zrl_callback', $value); + $value = preg_replace_callback("/([^='" . '"' . "]|^|#\^)(https?:\/\/[a-zA-Z0-9\pL:\/\-?&;.=@_~#%\$!+,]+)/ismu", 'red_zrl_callback', $value); // take out the bookmark indicator - if(substr($value,0,2) === '#^') - $value = str_replace('#^','',$value); + if (str_starts_with($value, '#^')) { + $value = str_replace('#^', '', $value); + } - $message = sprintf( t('%1$s changed %2$s to “%3$s”'), $A, $changes, $value); - $message .= "\n\n" . sprintf( t('Visit %1$s\'s %2$s'), $A, $prof); - } - else - $message = sprintf( t('%1$s has an updated %2$s, changing %3$s.'), $A, $prof, $changes); - - - $arr['body'] = $message; - - $links = array(); - $links[] = array('rel' => 'alternate', 'type' => 'text/html', - 'href' => z_root() . '/profile/' . $self['channel_address']); - $links[] = array('rel' => 'photo', 'type' => $self['xchan_photo_mimetype'], - 'href' => $self['xchan_photo_l']); - - $arr['object'] = json_encode(array( - 'type' => ACTIVITY_OBJ_PROFILE, - 'title' => $self['channel_name'], - 'id' => $self['xchan_url'] . '/' . $self['xchan_hash'], - 'link' => $links - )); - - - $arr['allow_cid'] = $self['channel_allow_cid']; - $arr['allow_gid'] = $self['channel_allow_gid']; - $arr['deny_cid'] = $self['channel_deny_cid']; - $arr['deny_gid'] = $self['channel_deny_gid']; - - $res = item_store($arr); - $i = $res['item_id']; - - if($i) { - // FIXME - limit delivery in notifier.php to those specificed in the perms argument - Zotlabs\Daemon\Master::Summon(array('Notifier','activity', $i, 'PERMS_R_PROFILE')); + if ($photo) { + $value = "\n\n" . '[zmg=' . z_root() . '/photo/' . $value . '-' . $photo_size . ']' . $ch . ' ' . $channel['xchan_name'] . '[/zmg]'; + } + else { + $value = '"' . $value . '"'; + } + + $message = sprintf(t('%1$s %2$s has been updated to %3$s.'), $channel_link . '\'s' . (($photo) ? '' : ' ' . $profile_link), strtolower($changes), $value); + + } else { + $message = sprintf(t('%1$s updated the %2$s. Changed %3$s.'), $channel_link, $profile_link, strtolower($changes)); } + $arr['body'] = $message; + + $arr['obj'] = [ + 'type' => 'Profile', + 'content' => bbcode($message), + 'source' => [ + 'content' => $message, + 'mediaType' => 'text/bbcode' + ], + 'describes' => Activity::encode_person($channel), + 'url' => z_root() . '/profile/' . $channel['channel_address'] + ]; + + post_activity_item($arr); + } diff --git a/include/api_zot.php b/include/api_zot.php index 7a217854f..22692b962 100644 --- a/include/api_zot.php +++ b/include/api_zot.php @@ -561,7 +561,7 @@ $mod = new Zotlabs\Module\Wall_attach(); $media = $mod->post(); if($media) - $_REQUEST['body'] .= "\n\n" . $media; + $_REQUEST['body'] = $media . "\n\n" . $_REQUEST['body']; } $mod = new Zotlabs\Module\Item(); diff --git a/include/attach.php b/include/attach.php index 9baebe2a0..e5a2900b3 100644 --- a/include/attach.php +++ b/include/attach.php @@ -1854,169 +1854,6 @@ function pipe_streams($in, $out, $bufsize = 16384) { return $size; } -/** - * @brief Activity for files. - * - * @param int $channel_id - * @param array $object - * @param string $allow_cid - * @param string $allow_gid - * @param string $deny_cid - * @param string $deny_gid - * @param string $verb - * @param boolean $notify - */ -/* -function file_activity($channel_id, $object, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $verb, $notify) { - - require_once('include/items.php'); - - $poster = App::get_observer(); - - //if we got no object something went wrong - if(!$object) - return; - - //turn strings into arrays - $arr_allow_cid = expand_acl($allow_cid); - $arr_allow_gid = expand_acl($allow_gid); - $arr_deny_cid = expand_acl($deny_cid); - $arr_deny_gid = expand_acl($deny_gid); - - //filter out receivers which do not have permission to view filestorage - $arr_allow_cid = check_list_permissions($channel_id, $arr_allow_cid, 'view_storage'); - - $is_dir = (intval($object['is_dir']) ? true : false); - - //do not send activity for folders for now - if($is_dir) - return; - - //check for recursive perms if we are in a folder - if($object['folder']) { - - $folder_hash = $object['folder']; - - $r_perms = attach_recursive_perms($arr_allow_cid, $arr_allow_gid, $arr_deny_cid, $arr_deny_gid, $folder_hash); - - if($r_perms === false) //nobody has recursive perms - nobody must be notified - return; - - //split up returned perms - $arr_allow_cid = $r_perms['allow_cid']; - $arr_allow_gid = $r_perms['allow_gid']; - $arr_deny_cid = $r_perms['deny_cid']; - $arr_deny_gid = $r_perms['deny_gid']; - - //filter out receivers which do not have permission to view filestorage - $arr_allow_cid = check_list_permissions($channel_id, $arr_allow_cid, 'view_storage'); - } - - $uuid = item_message_id(); - $mid = z_root() . '/item/' . $uuid; - - $objtype = 'ACTIVITY_OBJ_FILE'; - - $arr = array(); - $arr['aid'] = get_account_id(); - $arr['uid'] = $channel_id; - $arr['uuid'] = $uuid; - $arr['item_wall'] = 1; - $arr['item_origin'] = 1; - $arr['item_unseen'] = 1; - $arr['author_xchan'] = $poster['xchan_hash']; - $arr['owner_xchan'] = $poster['xchan_hash']; - $arr['title'] = ''; - $arr['item_notshown'] = 1; - $arr['obj_type'] = $objtype; - $arr['resource_id'] = $object['hash']; - $arr['resource_type'] = 'attach'; - - $private = (($arr_allow_cid[0] || $arr_allow_gid[0] || $arr_deny_cid[0] || $arr_deny_gid[0]) ? 1 : 0); - - $jsonobject = json_encode($object); - - //check if item for this object exists - $y = q("SELECT mid FROM item WHERE verb = '%s' AND obj_type = '%s' AND resource_id = '%s' AND uid = %d LIMIT 1", - dbesc(ACTIVITY_POST), - dbesc($objtype), - dbesc($object['hash']), - intval(local_channel()) - ); - - if($y) { - $update = true; - $object['d_mid'] = $y[0]['mid']; //attach mid of the old object - $u_jsonobject = json_encode($object); - - //we have got the relevant info - delete the old item before we create the new one - q("DELETE FROM item WHERE obj_type = '%s' AND verb = '%s' AND mid = '%s'", - dbesc(ACTIVITY_OBJ_FILE), - dbesc(ACTIVITY_POST), - dbesc($y[0]['mid']) - ); - - } - - //send update activity and create a new one - if($update && $verb == 'post' ) { - //updates should be sent to everybody with recursive perms and all eventual former allowed members ($object['allow_cid'] etc.). - $u_arr_allow_cid = array_unique(array_merge($arr_allow_cid, expand_acl($object['allow_cid']))); - $u_arr_allow_gid = array_unique(array_merge($arr_allow_gid, expand_acl($object['allow_gid']))); - $u_arr_deny_cid = array_unique(array_merge($arr_deny_cid, expand_acl($object['deny_cid']))); - $u_arr_deny_gid = array_unique(array_merge($arr_deny_gid, expand_acl($object['deny_gid']))); - - $private = (($u_arr_allow_cid[0] || $u_arr_allow_gid[0] || $u_arr_deny_cid[0] || $u_arr_deny_gid[0]) ? 1 : 0); - - $uuid = item_message_id(); - $u_mid = z_root() . '/item/' . $uuid; - - $arr['uuid'] = $uuid; - $arr['mid'] = $u_mid; - $arr['parent_mid'] = $u_mid; - $arr['allow_cid'] = perms2str($u_arr_allow_cid); - $arr['allow_gid'] = perms2str($u_arr_allow_gid); - $arr['deny_cid'] = perms2str($u_arr_deny_cid); - $arr['deny_gid'] = perms2str($u_arr_deny_gid); - $arr['item_private'] = $private; - $arr['verb'] = ACTIVITY_UPDATE; - $arr['obj'] = $u_jsonobject; - $arr['body'] = ''; - - post_activity_item($arr); - - $update = false; - } - - //don't create new activity if notify was not enabled - if(! $notify) { - return; - } - - //don't create new activity if we have an update request but there is no item to update - //this can e.g. happen when deleting images - if(! $y && $verb == 'update') { - return; - } - - $arr['mid'] = $mid; - $arr['parent_mid'] = $mid; - $arr['allow_cid'] = perms2str($arr_allow_cid); - $arr['allow_gid'] = perms2str($arr_allow_gid); - $arr['deny_cid'] = perms2str($arr_deny_cid); - $arr['deny_gid'] = perms2str($arr_deny_gid); - $arr['item_private'] = $private; - $arr['verb'] = (($update) ? ACTIVITY_UPDATE : ACTIVITY_POST); - $arr['obj'] = (($update) ? $u_jsonobject : $jsonobject); - $arr['body'] = ''; - - post_activity_item($arr); - - return; -} -*/ - - function attach_store_item($channel, $observer, $file) { @@ -2127,7 +1964,7 @@ function attach_store_item($channel, $observer, $file) { $arr['item_origin'] = 1; $arr['item_thread_top'] = 1; $arr['item_private'] = (($file['allow_cid'] || $file['allow_gid'] || $file['deny_cid'] || $file['deny_gid']) ? 1 : 0); - $arr['verb'] = ACTIVITY_CREATE; + $arr['verb'] = 'Create'; $arr['obj_type'] = $type; $arr['title'] = $file['filename']; @@ -2773,7 +2610,7 @@ function attach_move($channel_id, $resource_id, $new_folder_hash, $newname = '', intval($channel_id) ); - q("update photo set content = CASE imgscale WHEN 0 THEN '%s' ELSE CONCAT('%s', '-', imgscale) END where resource_id = '%s' and uid = %d and os_storage = 1", + q("update photo set content = CASE imgscale WHEN 0 THEN %s ELSE CONCAT(%s, '-', imgscale) END where resource_id = '%s' and uid = %d and os_storage = 1", dbescbin($newstorepath), dbescbin($newstorepath), dbesc($resource_id), diff --git a/include/bbcode.php b/include/bbcode.php index e0a0fe9a1..79cb82c0b 100644 --- a/include/bbcode.php +++ b/include/bbcode.php @@ -246,42 +246,47 @@ function bb_replace_images($body, $images) { function bb_parse_crypt($match) { $matches = []; - $attributes = $match[1]; + $hint = ''; + $algorithm = ''; + $payload = $match[1]; - $algorithm = ""; + if (isset($match[2])) { + // backwards compatibility - preg_match("/alg='(.*?)'/ism", $attributes, $matches); - if ($matches[1] != "") - $algorithm = $matches[1]; + $attributes = $match[1]; + $payload = $match[2]; - preg_match("/alg=\"\;(.*?)\"\;/ism", $attributes, $matches); - if ($matches[1] != "") - $algorithm = $matches[1]; + preg_match("/alg='(.*?)'/ism", $attributes, $matches); + $algorithm = $matches[1] ?? ''; - $hint = ""; + if (!$algorithm) { + preg_match("/alg=\"\;(.*?)\"\;/ism", $attributes, $matches); + $algorithm = $matches[1] ?? ''; + } - preg_match("/hint='(.*?)'/ism", $attributes, $matches); - if ($matches[1] != "") - $hint = $matches[1]; - preg_match("/hint=\"\;(.*?)\"\;/ism", $attributes, $matches); - if ($matches[1] != "") - $hint = $matches[1]; + preg_match("/hint='(.*?)'/ism", $attributes, $matches); + $hint = $matches[1] ?? ''; + + if (!$hint) { + preg_match("/hint=\"\;(.*?)\"\;/ism", $attributes, $matches); + $hint = $matches[1] ?? ''; + } + } - $x = random_string(); + $x = random_string(32); - $f = 'hz_decrypt'; + $onclick = 'onclick="sodium_decrypt(\'' . $payload . '\',\'#' . $x . '\');"'; - //legacy cryptojs support - if(plugin_is_installed('cryptojs')) { - $f = ((in_array($algorithm, ['AES-128-CCM', 'rot13', 'triple-rot13'])) ? 'hz_decrypt' : 'red_decrypt'); + if (in_array($algorithm, ['AES-128-CCM', 'rot13', 'triple-rot13'])) { + // backwards compatibility + $onclick = 'onclick="hz_decrypt(\'' . $algorithm . '\',\'' . $hint . '\',\'' . $payload . '\',\'#' . $x . '\');"'; } - $onclick = 'onclick="' . $f . '(\'' . $algorithm . '\',\'' . $hint . '\',\'' . $match[2] . '\',\'#' . $x . '\');"'; $label = t('Encrypted content'); - $Text = '<br /><div id="' . $x . '"><img class="cursor-pointer" src="' . z_root() . '/images/lock_icon.svg" ' . $onclick . ' alt="' . $label . '" title="' . $label . '" /></div><br />'; + $text = '<div id="' . $x . '" class="encrypted-content"><img class="cursor-pointer" src="' . z_root() . '/images/lock_icon.svg" ' . $onclick . ' alt="' . $label . '" title="' . $label . '" /></div>'; - return $Text; + return $text; } /** @@ -292,20 +297,20 @@ function bb_parse_crypt($match) { */ function bb_parse_b64_crypt($match) { - if(empty($match[2])) + if(empty($match[1])) { return; + } - $r .= '<code>'; - $r .= '----- ENCRYPTED CONTENT -----' . '<br>'; - $r .= $match[2] . '<br>'; - $r .= '----- END ENCRYPTED CONTENT -----'; - $r .= '</code>'; + $r = '-----BEGIN ENCRYPTED MESSAGE-----' . "\n"; + $r .= $match[1] . "\n"; + $r .= '-----END ENCRYPTED MESSAGE-----' . "\n"; + + $r = '<code>' . str_replace("\n", '<br>', wordwrap($r, 75, "\n", true)) . '</code>'; return $r; } - function bb_parse_app($match) { $app = Zotlabs\Lib\Apps::app_decode($match[1]); @@ -809,36 +814,56 @@ function oblanguage_necallback($matches) { return ''; } -function bb_observer($Text) { - - $observer = App::get_observer(); +/** + * Process [observer] tags in the source text. + * + * @param string $text The source BBCode text. + * @param ?array $observer Array containing the observer we render for, or + * null if none. + * + * @return A string where the [observer] tags have been processed. + */ +function bb_observer(string $text, ?array $observer): string { - if ((strpos($Text,'[/observer]') !== false) || (strpos($Text,'[/rpost]') !== false)) { + if ((strpos($text,'[/observer]') !== false) || (strpos($text,'[/rpost]') !== false)) { + $text = preg_replace_callback("/\[observer\.language\=(.*?)\](.*?)\[\/observer\]/ism",'oblanguage_callback', $text); + $text = preg_replace_callback("/\[observer\.language\!\=(.*?)\](.*?)\[\/observer\]/ism",'oblanguage_necallback', $text); if ($observer) { - $Text = preg_replace("/\[observer\=1\](.*?)\[\/observer\]/ism", '$1', $Text); - $Text = preg_replace("/\[observer\=0\].*?\[\/observer\]/ism", '', $Text); - $Text = preg_replace_callback("/\[rpost(=(.*?))?\](.*?)\[\/rpost\]/ism", 'rpost_callback', $Text); + $text = preg_replace("/\[observer\=1\](.*?)\[\/observer\]/ism", '$1', $text); + $text = preg_replace("/\[observer\=0\].*?\[\/observer\]/ism", '', $text); + $text = preg_replace_callback("/\[rpost(=(.*?))?\](.*?)\[\/rpost\]/ism", 'rpost_callback', $text); } else { - $Text = preg_replace("/\[observer\=1\].*?\[\/observer\]/ism", '', $Text); - $Text = preg_replace("/\[observer\=0\](.*?)\[\/observer\]/ism", '$1', $Text); - $Text = preg_replace("/\[rpost(=.*?)?\](.*?)\[\/rpost\]/ism", '', $Text); + $text = preg_replace("/\[observer\=1\].*?\[\/observer\]/ism", '', $text); + $text = preg_replace("/\[observer\=0\](.*?)\[\/observer\]/ism", '$1', $text); + $text = preg_replace("/\[rpost(=.*?)?\](.*?)\[\/rpost\]/ism", '', $text); } } - $channel = App::get_channel(); + return $text; +} + +/** + * Process [channel] tags in the source text. + * + * @param string $text The source BBCode text. + * @param ?array $channel Array containing the channel we render for, or + * null if none. + * + * @return A string where the [channel] tags have been processed. + */ +function bb_channel(string $text, ?array $channel): string { - if (strpos($Text,'[/channel]') !== false) { + if (strpos($text,'[/channel]') !== false) { if ($channel) { - $Text = preg_replace("/\[channel\=1\](.*?)\[\/channel\]/ism", '$1', $Text); - $Text = preg_replace("/\[channel\=0\].*?\[\/channel\]/ism", '', $Text); + $text = preg_replace("/\[channel\=1\](.*?)\[\/channel\]/ism", '$1', $text); + $text = preg_replace("/\[channel\=0\].*?\[\/channel\]/ism", '', $text); } else { - $Text = preg_replace("/\[channel\=1\].*?\[\/channel\]/ism", '', $Text); - $Text = preg_replace("/\[channel\=0\](.*?)\[\/channel\]/ism", '$1', $Text); + $text = preg_replace("/\[channel\=1\].*?\[\/channel\]/ism", '', $text); + $text = preg_replace("/\[channel\=0\](.*?)\[\/channel\]/ism", '$1', $text); } } - - return $Text; + return $text; } function bb_imgoptions($match) { @@ -849,11 +874,13 @@ function bb_imgoptions($match) { // [img|zmg=wwwxhhh float=left|right alt=alt text]url[/img|zmg] $local_match = null; - $width = 0; - $float = false; - $alt = false; - - $style = EMPTY_STR; + $width = EMPTY_STR; + $height = EMPTY_STR; + $float = EMPTY_STR; + $alt = EMPTY_STR; + $title = EMPTY_STR; + $style = EMPTY_STR; + $class = EMPTY_STR; $attributes = $match[3]; @@ -862,6 +889,16 @@ function bb_imgoptions($match) { $alt = $matches[1]; } + $x = preg_match("/title=\"\;(.*?)\"\;/ism", $attributes, $matches); + if ($x) { + $title = $matches[1]; + } + + $x = preg_match("/title='(.*?)'/ism", $attributes, $matches); + if ($x) { + $title = $matches[1]; + } + $x = preg_match("/alt=\"\;(.*?)\"\;/ism", $attributes, $matches); if ($x) { $alt = $matches[1]; @@ -887,16 +924,27 @@ function bb_imgoptions($match) { $height = $matches[1]; } + $x = preg_match("/class='(.*?)'/ism", $attributes, $matches); + if ($x) { + $class = $matches[1]; + } + + $x = preg_match("/class=\"\;(.*?)\"\;/ism", $attributes, $matches); + if ($x) { + $class = $matches[1]; + } + +/* should probably sanitize css somehow $x = preg_match("/style='(.*?)'/ism", $attributes, $matches); if ($x) { - $style = $matches[1]; + $style = $matches[1] . ' '; } $x = preg_match("/style=\"\;(.*?)\"\;/ism", $attributes, $matches); if ($x) { - $style = $matches[1]; + $style = $matches[1] . ' '; } - +*/ // legacy img options if ($match[2] === '=') { @@ -912,6 +960,7 @@ function bb_imgoptions($match) { $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); @@ -927,18 +976,25 @@ function bb_imgoptions($match) { $output = '<img ' . (($match[1] === 'z') ? 'class="zrl" loading="eager"' : '') . ' '; if ($width) { - $style .= 'width: 100%; max-width: ' . $width . 'px; '; + $style .= 'width: ' . intval($width) . 'px; '; } - else { - $style .= 'max-width: 100%; '; + + if ($height) { + $style .= 'height: ' . intval($height) . 'px; '; } + if ($float) { $style .= 'float: ' . $float . '; '; } - $output .= (($style) ? 'style="' . $style . '" ' : '') . 'alt="' . htmlentities(($alt) ? $alt : t('Image/photo'),ENT_COMPAT,'UTF-8') . '" '; + $style .= 'max-width: 100%;'; + + $output .= 'style="' . $style . '" '; + $output .= 'alt="' . htmlentities(($alt ? $alt : t('Image/photo')), ENT_COMPAT, 'UTF-8') . '" '; + $output .= 'title="' . htmlentities($title, ENT_COMPAT, 'UTF-8') . '" '; + $output .= 'class="' . htmlentities($class, ENT_COMPAT, 'UTF-8') . '" '; - $output .= 'src="' . $match[4] . '" >'; + $output .= 'src="' . $match[4] . '" />'; return $output; @@ -957,7 +1013,7 @@ function bb_code_unprotect_sub($match) { } function bb_code($match) { - if(strpos($match[0], PHP_EOL)) + if(strpos($match[0], PHP_EOL) !== false) return '<pre><code>' . bb_code_protect(trim($match[1])) . '</code></pre>'; else return '<code class="inline-code">' . bb_code_protect(trim($match[1])) . '</code>'; @@ -998,6 +1054,15 @@ function bb_fixtable_lf($match) { } +function bb_fix_lf($match) { + // remove extraneous whitespace between element tags since newlines will all + // be converted to '<br />' and turn your neatly crafted tables into a whole lot of + // empty space. + + $new_content = str_replace(["\n\r", "\n", "\r"], '', $match[1]); + return str_replace($match[1], $new_content, $match[0]); +} + function bbtopoll($s) { $pl = []; @@ -1095,145 +1160,140 @@ function parseIdentityAwareHTML($Text) { } -function bbcode($Text, $options = []) { +/** + * Converts Hubzilla flavoured BBCode to HTML. + * + * @param string $text BBCode formatted text + * + * @param array $options Optional arguments to controll how the conversion + * is to be done. + * - `tryoembed` (`true`) - Whether we should try to generate link previews/oembeds. + * - `cache` (`false`) - Avoids some processing if true, unsure about the significancd of it for now. + * - `newwin` (`true`) - Wether links should open in a new window (`target="_blank"`) + * + * @return A string containing the resulting HTML code. + */ +function bbcode($text, $options = []) { + + if (!$text) { + return EMPTY_STR; + } if(! is_array($options)) { $options = []; } - $preserve_nl = ((array_key_exists('preserve_nl',$options)) ? $options['preserve_nl'] : false); $tryoembed = ((array_key_exists('tryoembed',$options)) ? $options['tryoembed'] : true); $cache = ((array_key_exists('cache',$options)) ? $options['cache'] : false); $newwin = ((array_key_exists('newwin',$options)) ? $options['newwin'] : true); $target = (($newwin) ? ' target="_blank" ' : ''); - call_hooks('bbcode_filter', $Text); + /** + * @hooks bbcode_filter + * * _string_ **bbcode** - The raw bbcode input before converting it to HTML. + */ + call_hooks('bbcode_filter', $text); if(isset($options['drop_media'])) { - if (strpos($Text,'[/img]') !== false) { - $Text = preg_replace('/\[img(.*?)\[\/(img)\]/ism', '', $Text); + if (strpos($text,'[/img]') !== false) { + $text = preg_replace('/\[img(.*?)\[\/(img)\]/ism', '', $text); } - if (strpos($Text,'[/audio]') !== false) { - $Text = preg_replace('/\[audio(.*?)\[\/(audio)\]/ism', '', $Text); + if (strpos($text,'[/audio]') !== false) { + $text = preg_replace('/\[audio(.*?)\[\/(audio)\]/ism', '', $text); } - if (strpos($Text,'[/video]') !== false) { - $Text = preg_replace('/\[video(.*?)\[\/(video)\]/ism', '', $Text); + if (strpos($text,'[/video]') !== false) { + $text = preg_replace('/\[video(.*?)\[\/(video)\]/ism', '', $text); } - if (strpos($Text,'[/zmg]') !== false) { - $Text = preg_replace('/\[zmg(.*?)\[\/(zmg)\]/ism', '', $Text); + if (strpos($text,'[/zmg]') !== false) { + $text = preg_replace('/\[zmg(.*?)\[\/(zmg)\]/ism', '', $text); } - if (strpos($Text,'[/zaudio]') !== false) { - $Text = preg_replace('/\[zaudio(.*?)\[\/(zaudio)\]/ism', '', $Text); + if (strpos($text,'[/zaudio]') !== false) { + $text = preg_replace('/\[zaudio(.*?)\[\/(zaudio)\]/ism', '', $text); } - if (strpos($Text,'[/zvideo]') !== false) { - $Text = preg_replace('/\[zvideo(.*?)\[\/(zvideo)\]/ism', '', $Text); + if (strpos($text,'[/zvideo]') !== false) { + $text = preg_replace('/\[zvideo(.*?)\[\/(zvideo)\]/ism', '', $text); } } - - - $Text = bb_format_attachdata($Text); + $text = bb_format_attachdata($text); // If we find any event code, turn it into an event. // After we're finished processing the bbcode we'll // replace all of the event code with a reformatted version. - $ev = bbtoevent($Text); + $ev = bbtoevent($text); // and the same with polls - $pl = bbtopoll($Text); + $pl = bbtopoll($text); - // process [observer] tags before we do anything else because we might - // be stripping away stuff that then doesn't need to be worked on anymore - - if($cache) - $observer = false; - else + if($cache) { + $observer = null; + $channel = null; + } else { $observer = App::get_observer(); - - if ((strpos($Text,'[/observer]') !== false) || (strpos($Text,'[/rpost]') !== false)) { - $Text = preg_replace_callback("/\[observer\.language\=(.*?)\](.*?)\[\/observer\]/ism",'oblanguage_callback', $Text); - $Text = preg_replace_callback("/\[observer\.language\!\=(.*?)\](.*?)\[\/observer\]/ism",'oblanguage_necallback', $Text); - - if ($observer) { - $Text = preg_replace("/\[observer\=1\](.*?)\[\/observer\]/ism", '$1', $Text); - $Text = preg_replace("/\[observer\=0\].*?\[\/observer\]/ism", '', $Text); - $Text = preg_replace_callback("/\[rpost(=(.*?))?\](.*?)\[\/rpost\]/ism", 'rpost_callback', $Text); - } else { - $Text = preg_replace("/\[observer\=1\].*?\[\/observer\]/ism", '', $Text); - $Text = preg_replace("/\[observer\=0\](.*?)\[\/observer\]/ism", '$1', $Text); - $Text = preg_replace("/\[rpost(=.*?)?\](.*?)\[\/rpost\]/ism", '', $Text); - } + $channel = App::get_channel(); } - if($cache) - $channel = false; - else - $channel = App::get_channel(); + // process [observer] and [channel] tags before we do anything else because + // we might be stripping away stuff that then doesn't need to be worked on + // anymore - if (strpos($Text,'[/channel]') !== false) { - if ($channel) { - $Text = preg_replace("/\[channel\=1\](.*?)\[\/channel\]/ism", '$1', $Text); - $Text = preg_replace("/\[channel\=0\].*?\[\/channel\]/ism", '', $Text); - } else { - $Text = preg_replace("/\[channel\=1\].*?\[\/channel\]/ism", '', $Text); - $Text = preg_replace("/\[channel\=0\](.*?)\[\/channel\]/ism", '$1', $Text); - } - } + $text = bb_observer($text, $observer); + $text = bb_channel($text, $channel); - $x = bb_extract_images($Text); - $Text = $x['body']; + $x = bb_extract_images($text); + $text = $x['body']; $saved_images = $x['images']; - $Text = str_replace(array('[baseurl]','[sitename]'),array(z_root(),get_config('system','sitename')),$Text); + $text = str_replace(array('[baseurl]','[sitename]'),array(z_root(),get_config('system','sitename')),$text); // Replace any html brackets with HTML Entities to prevent executing HTML or script // Don't use strip_tags here because it breaks [url] search by replacing & with amp - $Text = str_replace("<", "<", $Text); - $Text = str_replace(">", ">", $Text); - $Text = preg_replace_callback("/\[table\](.*?)\[\/table\]/ism",'bb_fixtable_lf',$Text); - $Text = str_replace(array("\t", " "), array(" ", " "), $Text); + $text = str_replace("<", "<", $text); + $text = str_replace(">", ">", $text); + + $text = str_replace(array("\t", " "), array(" ", " "), $text); // Check for [code] text here, before the linefeeds are messed with. // The highlighter will unescape and re-escape the content. - if (strpos($Text,'[code=') !== false) { - $Text = preg_replace_callback("/\[code=(.*?)\](.*?)\[\/code\]/ism", 'bb_highlight', $Text); + if (strpos($text,'[code=') !== false) { + $text = preg_replace_callback("/\[code=(.*?)\](.*?)\[\/code\]/ism", 'bb_highlight', $text); } // Check for [code] text - if (strpos($Text,'[code]') !== false) { - $Text = preg_replace_callback("/\[code\](.*?)\[\/code\]/ism", 'bb_code', $Text); + if (strpos($text,'[code]') !== false) { + $text = preg_replace_callback("/\[code\](.*?)\[\/code\]/ism", 'bb_code', $text); } // Check for [code options] text - if (strpos($Text,'[code ') !== false) { - $Text = preg_replace_callback("/\[code(.*?)\](.*?)\[\/code\]/ism", 'bb_code_options', $Text); + if (strpos($text,'[code ') !== false) { + $text = preg_replace_callback("/\[code(.*?)\](.*?)\[\/code\]/ism", 'bb_code_options', $text); } // Hide all [noparse] contained bbtags by spacefying them - if (strpos($Text,'[noparse]') !== false) { - $Text = preg_replace_callback("/\[noparse\](.*?)\[\/noparse\]/ism", 'bb_spacefy',$Text); + if (strpos($text,'[noparse]') !== false) { + $text = preg_replace_callback("/\[noparse\](.*?)\[\/noparse\]/ism", 'bb_spacefy',$text); } - if (strpos($Text,'[nobb]') !== false) { - $Text = preg_replace_callback("/\[nobb\](.*?)\[\/nobb\]/ism", 'bb_spacefy',$Text); + if (strpos($text,'[nobb]') !== false) { + $text = preg_replace_callback("/\[nobb\](.*?)\[\/nobb\]/ism", 'bb_spacefy',$text); } - if (strpos($Text,'[pre]') !== false) { - $Text = preg_replace_callback("/\[pre\](.*?)\[\/pre\]/ism", 'bb_spacefy',$Text); + if (strpos($text,'[pre]') !== false) { + $text = preg_replace_callback("/\[pre\](.*?)\[\/pre\]/ism", 'bb_spacefy',$text); } - if (strpos($Text,'[summary]') !== false) { - $Text = preg_replace_callback("/\[summary\](.*?)\[\/summary\]/ism", 'bb_spacefy',$Text); + if (strpos($text,'[summary]') !== false) { + $text = preg_replace_callback("/\[summary\](.*?)\[\/summary\]/ism", 'bb_spacefy',$text); } - if (strpos($Text,'[/img]') !== false) { - $Text = preg_replace_callback('/\[img(.*?)\[\/(img)\]/ism','\red_escape_codeblock',$Text); + if (strpos($text,'[/img]') !== false) { + $text = preg_replace_callback('/\[img(.*?)\[\/(img)\]/ism','\red_escape_codeblock',$text); } - if (strpos($Text,'[/zmg]') !== false) { - $Text = preg_replace_callback('/\[zmg(.*?)\[\/(zmg)\]/ism','\red_escape_codeblock',$Text); + if (strpos($text,'[/zmg]') !== false) { + $text = preg_replace_callback('/\[zmg(.*?)\[\/(zmg)\]/ism','\red_escape_codeblock',$text); } // Set up the parameters for a URL search string @@ -1247,19 +1307,19 @@ function bbcode($Text, $options = []) { $s2 = '</span>'; $obsBaseURL = $observer['xchan_connurl']; $obsBaseURL = preg_replace("/\/poco\/.*$/", '', $obsBaseURL); - $Text = str_replace('[observer.baseurl]', $obsBaseURL, $Text); - $Text = str_replace('[observer.url]',$observer['xchan_url'], $Text); - $Text = str_replace('[observer.name]',$s1 . $observer['xchan_name'] . $s2, $Text); - $Text = str_replace('[observer.address]',$s1 . $observer['xchan_addr'] . $s2, $Text); - $Text = str_replace('[observer.webname]', substr($observer['xchan_addr'],0,strpos($observer['xchan_addr'],'@')), $Text); - $Text = str_replace('[observer.photo]',$s1 . '[zmg]'.$observer['xchan_photo_l'].'[/zmg]' . $s2, $Text); + $text = str_replace('[observer.baseurl]', $obsBaseURL, $text); + $text = str_replace('[observer.url]',$observer['xchan_url'], $text); + $text = str_replace('[observer.name]',$s1 . $observer['xchan_name'] . $s2, $text); + $text = str_replace('[observer.address]',$s1 . $observer['xchan_addr'] . $s2, $text); + $text = str_replace('[observer.webname]', substr($observer['xchan_addr'],0,strpos($observer['xchan_addr'],'@')), $text); + $text = str_replace('[observer.photo]',$s1 . '[zmg]'.$observer['xchan_photo_l'].'[/zmg]' . $s2, $text); } else { - $Text = str_replace('[observer.baseurl]', '', $Text); - $Text = str_replace('[observer.url]','', $Text); - $Text = str_replace('[observer.name]','', $Text); - $Text = str_replace('[observer.address]','', $Text); - $Text = str_replace('[observer.webname]','',$Text); - $Text = str_replace('[observer.photo]','', $Text); + $text = str_replace('[observer.baseurl]', '', $text); + $text = str_replace('[observer.url]','', $text); + $text = str_replace('[observer.name]','', $text); + $text = str_replace('[observer.address]','', $text); + $text = str_replace('[observer.webname]','',$text); + $text = str_replace('[observer.photo]','', $text); } @@ -1268,257 +1328,283 @@ function bbcode($Text, $options = []) { $urlchars = '[a-zA-Z0-9\pL\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,\@\(\)]'; - if (strpos($Text,'http') !== false) { + if (strpos($text,'http') !== false) { if($tryoembed) { - $Text = preg_replace_callback("/([^\]\='".'"'."\;\/]|^|\#\^)(https?\:\/\/$urlchars+)/ismu", 'tryoembed', $Text); + $text = preg_replace_callback("/([^\]\='".'"'."\;\/]|^|\#\^)(https?\:\/\/$urlchars+)/ismu", 'tryoembed', $text); } // Is this still desired? // We already turn naked URLs into links during creation time cleanup_bbcode() - $Text = preg_replace("/([^\]\='".'"'."\;\/]|^|\#\^)(https?\:\/\/$urlchars+)/ismu", '$1<a href="$2" ' . $target . ' rel="nofollow noopener">$2</a>', $Text); + $text = preg_replace("/([^\]\='".'"'."\;\/]|^|\#\^)(https?\:\/\/$urlchars+)/ismu", '$1<a href="$2" ' . $target . ' rel="nofollow noopener">$2</a>', $text); } $count = 0; - while (strpos($Text,'[/share]') !== false && $count < 10) { - $Text = preg_replace_callback("/\[share(.*?)\](.*?)\[\/share\]/ism", 'bb_ShareAttributes', $Text); + while (strpos($text,'[/share]') !== false && $count < 10) { + $text = preg_replace_callback("/\[share(.*?)\](.*?)\[\/share\]/ism", 'bb_ShareAttributes', $text); $count ++; } if($tryoembed) { - if (strpos($Text,'[/url]') !== false) { - $Text = preg_replace_callback("/[^\^]\[url\]([$URLSearchString]*)\[\/url\]/ism", 'tryoembed', $Text); + if (strpos($text,'[/url]') !== false) { + $text = preg_replace_callback("/[^\^]\[url\]([$URLSearchString]*)\[\/url\]/ism", 'tryoembed', $text); } } - if (strpos($Text,'[/url]') !== false) { - $Text = preg_replace("/\#\^\[url\]([$URLSearchString]*)\[\/url\]/ism", '<span class="bookmark-identifier">#^</span><a class="bookmark" href="$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $Text); - $Text = preg_replace("/\#\^\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '<span class="bookmark-identifier">#^</span><a class="bookmark" href="$1" ' . $target . ' rel="nofollow noopener" >$2</a>', $Text); - $Text = preg_replace("/\[url\]([$URLSearchString]*)\[\/url\]/ism", '<a href="$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $Text); - $Text = preg_replace("/\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '<a href="$1" ' . $target . ' rel="nofollow noopener" >$2</a>', $Text); + if (strpos($text,'[/url]') !== false) { + $text = preg_replace("/\#\^\[url\]([$URLSearchString]*)\[\/url\]/ism", '<span class="bookmark-identifier">#^</span><a class="bookmark" href="$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $text); + $text = preg_replace("/\#\^\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '<span class="bookmark-identifier">#^</span><a class="bookmark" href="$1" ' . $target . ' rel="nofollow noopener" >$2</a>', $text); + $text = preg_replace("/\[url\]([$URLSearchString]*)\[\/url\]/ism", '<a href="$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $text); + $text = preg_replace("/\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '<a href="$1" ' . $target . ' rel="nofollow noopener" >$2</a>', $text); } - if (strpos($Text,'[/zrl]') !== false) { - $Text = preg_replace("/\#\^\[zrl\]([$URLSearchString]*)\[\/zrl\]/ism", '<span class="bookmark-identifier">#^</span><a class="zrl bookmark" href="$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $Text); - $Text = preg_replace("/\#\^\[zrl\=([$URLSearchString]*)\](.*?)\[\/zrl\]/ism", '<span class="bookmark-identifier">#^</span><a class="zrl bookmark" href="$1" ' . $target . ' rel="nofollow noopener" >$2</a>', $Text); - $Text = preg_replace("/\[zrl\]([$URLSearchString]*)\[\/zrl\]/ism", '<a class="zrl" href="$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $Text); - $Text = preg_replace("/\[zrl\=([$URLSearchString]*)\](.*?)\[\/zrl\]/ism", '<a class="zrl" href="$1" ' . $target . ' rel="nofollow noopener" >$2</a>', $Text); + if (strpos($text,'[/zrl]') !== false) { + $text = preg_replace("/\#\^\[zrl\]([$URLSearchString]*)\[\/zrl\]/ism", '<span class="bookmark-identifier">#^</span><a class="zrl bookmark" href="$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $text); + $text = preg_replace("/\#\^\[zrl\=([$URLSearchString]*)\](.*?)\[\/zrl\]/ism", '<span class="bookmark-identifier">#^</span><a class="zrl bookmark" href="$1" ' . $target . ' rel="nofollow noopener" >$2</a>', $text); + $text = preg_replace("/\[zrl\]([$URLSearchString]*)\[\/zrl\]/ism", '<a class="zrl" href="$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $text); + $text = preg_replace("/\[zrl\=([$URLSearchString]*)\](.*?)\[\/zrl\]/ism", '<a class="zrl" href="$1" ' . $target . ' rel="nofollow noopener" >$2</a>', $text); } // Perform MAIL Search - if (strpos($Text,'[/mail]') !== false) { - $Text = preg_replace("/\[mail\]([$MAILSearchString]*)\[\/mail\]/", '<a href="mailto:$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $Text); - $Text = preg_replace("/\[mail\=([$MAILSearchString]*)\](.*?)\[\/mail\]/", '<a href="mailto:$1" ' . $target . ' rel="nofollow noopener" >$2</a>', $Text); + if (strpos($text,'[/mail]') !== false) { + $text = preg_replace("/\[mail\]([$MAILSearchString]*)\[\/mail\]/", '<a href="mailto:$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $text); + $text = preg_replace("/\[mail\=([$MAILSearchString]*)\](.*?)\[\/mail\]/", '<a href="mailto:$1" ' . $target . ' rel="nofollow noopener" >$2</a>', $text); } // leave open the posibility of [map=something] // this is replaced in prepare_body() which has knowledge of the item location if ($cache) { - $Text = str_replace([ '[map]','[/map]' ], [ '','' ], $Text); - $Text = preg_replace('/\[map=(.*?)\]/ism','$1',$Text); + $text = str_replace([ '[map]','[/map]' ], [ '','' ], $text); + $text = preg_replace('/\[map=(.*?)\]/ism','$1',$text); } else { - if (strpos($Text,'[/map]') !== false) { - $Text = preg_replace_callback("/\[map\](.*?)\[\/map\]/ism", 'bb_map_location', $Text); + if (strpos($text,'[/map]') !== false) { + $text = preg_replace_callback("/\[map\](.*?)\[\/map\]/ism", 'bb_map_location', $text); } - if (strpos($Text,'[map=') !== false) { - $Text = preg_replace_callback("/\[map=(.*?)\]/ism", 'bb_map_coords', $Text); + if (strpos($text,'[map=') !== false) { + $text = preg_replace_callback("/\[map=(.*?)\]/ism", 'bb_map_coords', $text); } - if (strpos($Text,'[map]') !== false) { - $Text = preg_replace("/\[map\]/", '<div class="map"></div>', $Text); + if (strpos($text,'[map]') !== false) { + $text = preg_replace("/\[map\]/", '<div class="map"></div>', $text); } } // Check for bold text - if (strpos($Text,'[b]') !== false) { - $Text = preg_replace("(\[b\](.*?)\[\/b\])ism", '<strong>$1</strong>', $Text); + if (strpos($text,'[b]') !== false) { + $text = preg_replace("(\[b\](.*?)\[\/b\])ism", '<strong>$1</strong>', $text); } // Check for Italics text - if (strpos($Text,'[i]') !== false) { - $Text = preg_replace("(\[i\](.*?)\[\/i\])ism", '<em>$1</em>', $Text); + if (strpos($text,'[i]') !== false) { + $text = preg_replace("(\[i\](.*?)\[\/i\])ism", '<em>$1</em>', $text); } // Check for Underline text - if (strpos($Text,'[u]') !== false) { - $Text = preg_replace("(\[u\](.*?)\[\/u\])ism", '<u>$1</u>', $Text); + if (strpos($text,'[u]') !== false) { + $text = preg_replace("(\[u\](.*?)\[\/u\])ism", '<u>$1</u>', $text); } // Check for strike-through text - if (strpos($Text,'[s]') !== false) { - $Text = preg_replace("(\[s\](.*?)\[\/s\])ism", '<span style="text-decoration: line-through;">$1</span>', $Text); + if (strpos($text,'[s]') !== false) { + $text = preg_replace("(\[s\](.*?)\[\/s\])ism", '<span style="text-decoration: line-through;">$1</span>', $text); } // Check for over-line text - if (strpos($Text,'[o]') !== false) { - $Text = preg_replace("(\[o\](.*?)\[\/o\])ism", '<span style="text-decoration: overline;">$1</span>', $Text); + if (strpos($text,'[o]') !== false) { + $text = preg_replace("(\[o\](.*?)\[\/o\])ism", '<span style="text-decoration: overline;">$1</span>', $text); } - if (strpos($Text,'[sup]') !== false) { - $Text = preg_replace("(\[sup\](.*?)\[\/sup\])ism", '<sup>$1</sup>', $Text); + if (strpos($text,'[sup]') !== false) { + $text = preg_replace("(\[sup\](.*?)\[\/sup\])ism", '<sup>$1</sup>', $text); } - if (strpos($Text,'[sub]') !== false) { - $Text = preg_replace("(\[sub\](.*?)\[\/sub\])ism", '<sub>$1</sub>', $Text); + if (strpos($text,'[sub]') !== false) { + $text = preg_replace("(\[sub\](.*?)\[\/sub\])ism", '<sub>$1</sub>', $text); } // Check for colored text - if (strpos($Text,'[/color]') !== false) { - $Text = preg_replace("(\[color=(.*?)\](.*?)\[\/color\])ism", "<span style=\"color: $1;\">$2</span>", $Text); + if (strpos($text,'[/color]') !== false) { + $text = preg_replace("(\[color=(.*?)\](.*?)\[\/color\])ism", "<span style=\"color: $1;\">$2</span>", $text); } // @DEPRECATED: Check for colored text (deprecated in favor of mark which is a html5 standard) - if (strpos($Text,'[/hl]') !== false) { - $Text = preg_replace("(\[hl\](.*?)\[\/hl\])ism", "<span class=\"default-highlight\">$1</span>", $Text); - $Text = preg_replace("(\[hl=(.*?)\](.*?)\[\/hl\])ism", "<span style=\"background-color: $1;\">$2</span>", $Text); + if (strpos($text,'[/hl]') !== false) { + $text = preg_replace("(\[hl\](.*?)\[\/hl\])ism", "<span class=\"default-highlight\">$1</span>", $text); + $text = preg_replace("(\[hl=(.*?)\](.*?)\[\/hl\])ism", "<span style=\"background-color: $1;\">$2</span>", $text); } - if (strpos($Text,'[/mark]') !== false) { - $Text = preg_replace("(\[mark\](.*?)\[\/mark\])ism", "<mark class=\"mark\">$1</mark>", $Text); - $Text = preg_replace("(\[mark=(.*?)\](.*?)\[\/mark\])ism", "<mark style=\"background-color: $1;\">$2</mark>", $Text); + if (strpos($text,'[/mark]') !== false) { + $text = preg_replace("(\[mark\](.*?)\[\/mark\])ism", "<mark class=\"mark\">$1</mark>", $text); + $text = preg_replace("(\[mark=(.*?)\](.*?)\[\/mark\])ism", "<mark style=\"background-color: $1;\">$2</mark>", $text); } // Check for sized text // [size=50] --> font-size: 50px (with the unit). - if (strpos($Text,'[/size]') !== false) { - $Text = preg_replace("(\[size=(\d*?)\](.*?)\[\/size\])ism", "<span style=\"font-size: $1px;\">$2</span>", $Text); - $Text = preg_replace("(\[size=(.*?)\](.*?)\[\/size\])ism", "<span style=\"font-size: $1;\">$2</span>", $Text); + if (strpos($text,'[/size]') !== false) { + $text = preg_replace("(\[size=(\d*?)\](.*?)\[\/size\])ism", "<span style=\"font-size: $1px;\">$2</span>", $text); + $text = preg_replace("(\[size=(.*?)\](.*?)\[\/size\])ism", "<span style=\"font-size: $1;\">$2</span>", $text); } // Check for h1 - if (strpos($Text,'[h1]') !== false) { - $Text = preg_replace("(\[h1\](.*?)\[\/h1\])ism",'<h1>$1</h1>',$Text); - $Text = str_replace('</h1><br />', '</h1>', $Text); + if (strpos($text,'[h1]') !== false) { + $text = preg_replace("(\[h1\](.*?)\[\/h1\])ism",'<h1>$1</h1>',$text); + $text = str_replace(["</h1>\r", "</h1>\n"], '</h1>', $text); } // Check for h2 - if (strpos($Text,'[h2]') !== false) { - $Text = preg_replace("(\[h2\](.*?)\[\/h2\])ism",'<h2>$1</h2>',$Text); - $Text = str_replace('</h2><br />', '</h2>', $Text); + if (strpos($text,'[h2]') !== false) { + $text = preg_replace("(\[h2\](.*?)\[\/h2\])ism",'<h2>$1</h2>',$text); + $text = str_replace(["</h2>\r", "</h2>\n"], '</h2>', $text); } // Check for h3 - if (strpos($Text,'[h3]') !== false) { - $Text = preg_replace("(\[h3\](.*?)\[\/h3\])ism",'<h3>$1</h3>',$Text); - $Text = str_replace('</h3><br />', '</h3>', $Text); + if (strpos($text,'[h3]') !== false) { + $text = preg_replace("(\[h3\](.*?)\[\/h3\])ism",'<h3>$1</h3>',$text); + $text = str_replace(["</h3>\r", "</h3>\n"], '</h3>', $text); } // Check for h4 - if (strpos($Text,'[h4]') !== false) { - $Text = preg_replace("(\[h4\](.*?)\[\/h4\])ism",'<h4>$1</h4>',$Text); - $Text = str_replace('</h4><br />', '</h4>', $Text); + if (strpos($text,'[h4]') !== false) { + $text = preg_replace("(\[h4\](.*?)\[\/h4\])ism",'<h4>$1</h4>',$text); + $text = str_replace(["</h4>\r", "</h4>\n"], '</h4>', $text); } // Check for h5 - if (strpos($Text,'[h5]') !== false) { - $Text = preg_replace("(\[h5\](.*?)\[\/h5\])ism",'<h5>$1</h5>',$Text); - $Text = str_replace('</h5><br />', '</h5>', $Text); + if (strpos($text,'[h5]') !== false) { + $text = preg_replace("(\[h5\](.*?)\[\/h5\])ism",'<h5>$1</h5>',$text); + $text = str_replace(["</h5>\r", "</h5>\n"], '</h5>', $text); } // Check for h6 - if (strpos($Text,'[h6]') !== false) { - $Text = preg_replace("(\[h6\](.*?)\[\/h6\])ism",'<h6>$1</h6>',$Text); - $Text = str_replace('</h6><br />', '</h6>', $Text); + if (strpos($text,'[h6]') !== false) { + $text = preg_replace("(\[h6\](.*?)\[\/h6\])ism",'<h6>$1</h6>',$text); + $text = str_replace(["</h6>\r", "</h6>\n"], '</h6>', $text); } // Check for table of content without params - while(strpos($Text,'[toc]') !== false) { + while(strpos($text,'[toc]') !== false) { $toc_id = 'toc-' . random_string(10); - $Text = preg_replace("/\[toc\]/ism", '<ul id="' . $toc_id . '" class="toc"></ul><script>$(document).ready(function() { let toc_container = $("#' . $toc_id . '").parent().closest("div").attr("id") || ".section-content-wrapper"; $("#' . $toc_id . '").toc({content: "#" + toc_container, headings: "h1,h2,h3,h4"}); });</script>', $Text, 1); + $text = preg_replace("/\[toc\]/ism", '<ul id="' . $toc_id . '" class="toc"></ul><script>$(document).ready(function() { let toc_container = $("#' . $toc_id . '").parent().closest("div").attr("id") || ".section-content-wrapper"; $("#' . $toc_id . '").toc({content: "#" + toc_container, headings: "h1,h2,h3,h4"}); });</script>', $text, 1); } // Check for table of content with params - while(strpos($Text,'[toc') !== false) { + while(strpos($text,'[toc') !== false) { $toc_id = 'toc-' . random_string(10); - $Text = preg_replace("/\[toc([^\]]+?)\]/ism", '<ul id="' . $toc_id . '" class="toc" $1></ul><script>$("#' . $toc_id . '").toc();</script>', $Text, 1); + $text = preg_replace("/\[toc([^\]]+?)\]/ism", '<ul id="' . $toc_id . '" class="toc" $1></ul><script>$("#' . $toc_id . '").toc();</script>', $text, 1); } // Check for centered text - if (strpos($Text,'[/center]') !== false) { - $Text = preg_replace("(\[center\](.*?)\[\/center\])ism", "<div style=\"text-align:center;\">$1</div>", $Text); + if (strpos($text,'[/center]') !== false) { + $text = preg_replace("(\[center\](.*?)\[\/center\])ism", "<div style=\"text-align:center;\">$1</div>", $text); } // Check for footer - if (strpos($Text,'[/footer]') !== false) { - $Text = preg_replace("(\[footer\](.*?)\[\/footer\])ism", "<div class=\"wall-item-footer\">$1</div>", $Text); + if (strpos($text,'[/footer]') !== false) { + $text = preg_replace("(\[footer\](.*?)\[\/footer\])ism", "<div class=\"wall-item-footer\">$1</div>", $text); } // Check for bdi - if (strpos($Text,'[/bdi]') !== false) { - $Text = preg_replace("(\[bdi\](.*?)\[\/bdi\])ism", "<bdi>$1</bdi>", $Text); + if (strpos($text,'[/bdi]') !== false) { + $text = preg_replace("(\[bdi\](.*?)\[\/bdi\])ism", "<bdi>$1</bdi>", $text); } // Check for list text - $Text = preg_replace("/<br \/>\[\*\]/ism",'[*]',$Text); + $text = str_replace(["\r\n[*]", "\r[*]", "\n[*]"], "[*]", $text); + $text = str_replace("[*]", "<li>", $text); - $Text = str_replace("[*]", "<li>", $Text); // handle nested lists $endlessloop = 0; - while ((((strpos($Text, "[/list]") !== false) && (strpos($Text, "[list") !== false)) || - ((strpos($Text, "[/ol]") !== false) && (strpos($Text, "[ol]") !== false)) || - ((strpos($Text, "[/ul]") !== false) && (strpos($Text, "[ul]") !== false)) || - ((strpos($Text, "[/dl]") !== false) && (strpos($Text, "[dl") !== false)) || - ((strpos($Text, "[/li]") !== false) && (strpos($Text, "[li]") !== false))) && (++$endlessloop < 20)) { - $Text = preg_replace("/\[list\](.*?)\[\/list\]/ism", '<ul class="listbullet">$1</ul>', $Text); - $Text = preg_replace("/\[list=\](.*?)\[\/list\]/ism", '<ul class="listnone" style="list-style-type: none;">$1</ul>', $Text); - $Text = preg_replace("/\[list=1\](.*?)\[\/list\]/ism", '<ul class="listdecimal" style="list-style-type: decimal;">$1</ul>', $Text); - $Text = preg_replace("/\[list=((?-i)i)\](.*?)\[\/list\]/ism",'<ul class="listlowerroman" style="list-style-type: lower-roman;">$2</ul>', $Text); - $Text = preg_replace("/\[list=((?-i)I)\](.*?)\[\/list\]/ism", '<ul class="listupperroman" style="list-style-type: upper-roman;">$2</ul>', $Text); - $Text = preg_replace("/\[list=((?-i)a)\](.*?)\[\/list\]/ism", '<ul class="listloweralpha" style="list-style-type: lower-alpha;">$2</ul>', $Text); - $Text = preg_replace("/\[list=((?-i)A)\](.*?)\[\/list\]/ism", '<ul class="listupperalpha" style="list-style-type: upper-alpha;">$2</ul>', $Text); - $Text = preg_replace("/\[ul\](.*?)\[\/ul\]/ism", '<ul class="listbullet">$1</ul>', $Text); - $Text = preg_replace("/\[ol\](.*?)\[\/ol\]/ism", '<ul class="listdecimal" style="list-style-type: decimal;">$1</ul>', $Text); - $Text = preg_replace("/\[\/li\]<br \/>\[li\]/ism",'[/li][li]',$Text); - $Text = preg_replace("/\[li\](.*?)\[\/li\]/ism", '<li>$1</li>', $Text); + while ((((strpos($text, "[/list]") !== false) && (strpos($text, "[list") !== false)) || + ((strpos($text, "[/ol]") !== false) && (strpos($text, "[ol]") !== false)) || + ((strpos($text, "[/ul]") !== false) && (strpos($text, "[ul]") !== false)) || + ((strpos($text, "[/dl]") !== false) && (strpos($text, "[dl") !== false)) || + ((strpos($text, "[/li]") !== false) && (strpos($text, "[li]") !== false))) && (++$endlessloop < 20)) { + + $text = str_replace(["[/list]\r", "[/list]\n"], '[/list]', $text); + + $text = preg_replace_callback("/\[list\](.*?)\[\/list\]/ism",'bb_fix_lf', $text); + $text = preg_replace("/\[list\](.*?)\[\/list\]/ism", '<ul class="listbullet">$1</ul>', $text); + + $text = preg_replace_callback("/\[list=\](.*?)\[\/list\]/ism",'bb_fix_lf', $text); + $text = preg_replace("/\[list=\](.*?)\[\/list\]/ism", '<ul class="listnone" style="list-style-type: none;">$1</ul>', $text); + + $text = preg_replace_callback("/\[list=1\](.*?)\[\/list\]/ism",'bb_fix_lf', $text); + $text = preg_replace("/\[list=1\](.*?)\[\/list\]/ism", '<ol class="listdecimal" style="list-style-type: decimal;">$1</ol>', $text); + + $text = preg_replace_callback("/\[list=((?-i)i)\](.*?)\[\/list\]/ism",'bb_fix_lf', $text); + $text = preg_replace("/\[list=((?-i)i)\](.*?)\[\/list\]/ism",'<ol class="listlowerroman" style="list-style-type: lower-roman;">$2</ol>', $text); + + $text = preg_replace_callback("/\[list=((?-i)I)\](.*?)\[\/list\]/ism",'bb_fix_lf', $text); + $text = preg_replace("/\[list=((?-i)I)\](.*?)\[\/list\]/ism", '<ol class="listupperroman" style="list-style-type: upper-roman;">$2</ol>', $text); + + $text = preg_replace_callback("/\[list=((?-i)a)\](.*?)\[\/list\]/ism",'bb_fix_lf', $text); + $text = preg_replace("/\[list=((?-i)a)\](.*?)\[\/list\]/ism", '<ol class="listloweralpha" style="list-style-type: lower-alpha;">$2</ol>', $text); + + $text = preg_replace_callback("/\[list=((?-i)A)\](.*?)\[\/list\]/ism",'bb_fix_lf', $text); + $text = preg_replace("/\[list=((?-i)A)\](.*?)\[\/list\]/ism", '<ol class="listupperalpha" style="list-style-type: upper-alpha;">$2</ol>', $text); + + $text = preg_replace_callback("/\[ol\](.*?)\[\/ol\]/ism",'bb_fix_lf', $text); + $text = preg_replace("/\[ol\](.*?)\[\/ol\]/ism", '<ol class="listdecimal" style="list-style-type: decimal;">$1</ol>', $text); + + $text = preg_replace_callback("/\[ul\](.*?)\[\/ul\]/ism",'bb_fix_lf', $text); + $text = preg_replace("/\[ul\](.*?)\[\/ul\]/ism", '<ul class="listbullet">$1</ul>', $text); + + $text = preg_replace("/\[\/li\]<br \/>\[li\]/ism",'[/li][li]', $text); + $text = preg_replace("/\[li\](.*?)\[\/li\]/ism", '<li>$1</li>', $text); // [dl] tags have an optional [dl terms="bi"] form where bold/italic/underline/mono/large // etc. style may be specified for the "terms" in the definition list. The quotation marks // are also optional. The regex looks intimidating, but breaks down as: // "[dl" <optional-whitespace> <optional-termStyles> "]" <matchGroup2> "[/dl]" // where optional-termStyles are: "terms=" <optional-quote> <matchGroup1> <optional-quote> - $Text = preg_replace_callback('/\[dl[[:space:]]*(?:terms=(?:"|")?([a-zA-Z]+)(?:"|")?)?\](.*?)\[\/dl\]/ism', 'bb_definitionList', $Text); + $text = preg_replace_callback('/\[dl[[:space:]]*(?:terms=(?:"|")?([a-zA-Z]+)(?:"|")?)?\](.*?)\[\/dl\]/ism', 'bb_definitionList', $text); } - if (strpos($Text,'[checklist]') !== false) { - $Text = preg_replace_callback("/\[checklist\](.*?)\[\/checklist\]/ism", 'bb_checklist', $Text); + if (strpos($text,'[checklist]') !== false) { + $text = preg_replace_callback("/\[checklist\](.*?)\[\/checklist\]/ism",'bb_fix_lf', $text); + $text = preg_replace_callback("/\[checklist\](.*?)\[\/checklist\]/ism", 'bb_checklist', $text); } - if (strpos($Text,'[th]') !== false) { - $Text = preg_replace("/\[th\](.*?)\[\/th\]/sm", '<th>$1</th>', $Text); + if (strpos($text,'[th]') !== false) { + $text = preg_replace("/\[th\](.*?)\[\/th\]/sm", '<th>$1</th>', $text); } - if (strpos($Text,'[td]') !== false) { - $Text = preg_replace("/\[td\](.*?)\[\/td\]/sm", '<td>$1</td>', $Text); + + if (strpos($text,'[td]') !== false) { + $text = preg_replace("/\[td\](.*?)\[\/td\]/sm", '<td>$1</td>', $text); } - if (strpos($Text,'[tr]') !== false) { - $Text = preg_replace("/\[tr\](.*?)\[\/tr\]/sm", '<tr>$1</tr>', $Text); + + if (strpos($text,'[tr]') !== false) { + $text = preg_replace("/\[tr\](.*?)\[\/tr\]/sm", '<tr>$1</tr>', $text); } - if (strpos($Text,'[/table]') !== false) { - $Text = preg_replace("/\[table\](.*?)\[\/table\]/sm", '<table>$1</table>', $Text); - $Text = preg_replace("/\[table border=1\](.*?)\[\/table\]/sm", '<table class="table table-responsive table-bordered" >$1</table>', $Text); - $Text = preg_replace("/\[table border=0\](.*?)\[\/table\]/sm", '<table class="table table-responsive" >$1</table>', $Text); + + if (strpos($text,'[/table]') !== false) { + $text = preg_replace_callback("/\[table\](.*?)\[\/table\]/ism",'bb_fix_lf',$text); + $text = preg_replace("/\[table\](.*?)\[\/table\]/sm", '<table>$1</table>', $text); + $text = preg_replace("/\[table border=1\](.*?)\[\/table\]/sm", '<table class="table table-responsive table-bordered" >$1</table>', $text); + $text = preg_replace("/\[table border=0\](.*?)\[\/table\]/sm", '<table class="table table-responsive" >$1</table>', $text); } - $Text = str_replace('</tr><br /><tr>', "</tr>\n<tr>", $Text); - $Text = str_replace('[hr]', '<hr />', $Text); + + $text = str_replace('[hr]', '<hr />', $text); // This is actually executed in prepare_body() - $Text = str_replace('[nosmile]', '', $Text); + $text = str_replace('[nosmile]', '', $text); // Check for font change text - if (strpos($Text,'[/font]') !== false) { - $Text = preg_replace("/\[font=(.*?)\](.*?)\[\/font\]/sm", "<span style=\"font-family: $1;\">$2</span>", $Text); + if (strpos($text,'[/font]') !== false) { + $text = preg_replace("/\[font=(.*?)\](.*?)\[\/font\]/sm", "<span style=\"font-family: $1;\">$2</span>", $text); } // Check for [spoiler] text $endlessloop = 0; - while ((strpos($Text, "[/spoiler]")!== false) and (strpos($Text, "[spoiler]") !== false) and (++$endlessloop < 20)) { - $Text = preg_replace_callback("/\[spoiler\](.*?)\[\/spoiler\]/ism", 'bb_spoilertag', $Text); + while ((strpos($text, "[/spoiler]")!== false) and (strpos($text, "[spoiler]") !== false) and (++$endlessloop < 20)) { + $text = preg_replace_callback("/\[spoiler\](.*?)\[\/spoiler\]/ism", 'bb_spoilertag', $text); } // Check for [spoiler=Author] text $endlessloop = 0; - while ((strpos($Text, "[/spoiler]")!== false) and (strpos($Text, "[spoiler=") !== false) and (++$endlessloop < 20)) { - $Text = preg_replace_callback("/\[spoiler=(.*?)\](.*?)\[\/spoiler\]/ism", 'bb_spoilertag', $Text); + while ((strpos($text, "[/spoiler]")!== false) and (strpos($text, "[spoiler=") !== false) and (++$endlessloop < 20)) { + $text = preg_replace_callback("/\[spoiler=(.*?)\](.*?)\[\/spoiler\]/ism", 'bb_spoilertag', $text); } // Check for [open] text $endlessloop = 0; - while ((strpos($Text, "[/open]")!== false) and (strpos($Text, "[open]") !== false) and (++$endlessloop < 20)) { - $Text = preg_replace_callback("/\[open\](.*?)\[\/open\]/ism", 'bb_opentag', $Text); + while ((strpos($text, "[/open]")!== false) and (strpos($text, "[open]") !== false) and (++$endlessloop < 20)) { + $text = preg_replace_callback("/\[open\](.*?)\[\/open\]/ism", 'bb_opentag', $text); } // Check for [open=Title] text $endlessloop = 0; - while ((strpos($Text, "[/open]")!== false) and (strpos($Text, "[open=") !== false) and (++$endlessloop < 20)) { - $Text = preg_replace_callback("/\[open=(.*?)\](.*?)\[\/open\]/ism", 'bb_opentag', $Text); + while ((strpos($text, "[/open]")!== false) and (strpos($text, "[open=") !== false) and (++$endlessloop < 20)) { + $text = preg_replace_callback("/\[open=(.*?)\](.*?)\[\/open\]/ism", 'bb_opentag', $text); } @@ -1528,8 +1614,8 @@ function bbcode($Text, $options = []) { // Check for [quote] text // handle nested quotes $endlessloop = 0; - while ((strpos($Text, "[/quote]") !== false) and (strpos($Text, "[quote]") !== false) and (++$endlessloop < 20)) - $Text = preg_replace("/\[quote\](.*?)\[\/quote\]/ism", "$QuoteLayout", $Text); + while ((strpos($text, "[/quote]") !== false) and (strpos($text, "[quote]") !== false) and (++$endlessloop < 20)) + $text = preg_replace("/\[quote\](.*?)\[\/quote\]/ism", "$QuoteLayout", $text); // Check for [quote=Author] text @@ -1537,119 +1623,120 @@ function bbcode($Text, $options = []) { // handle nested quotes $endlessloop = 0; - while ((strpos($Text, "[/quote]")!== false) and (strpos($Text, "[quote=") !== false) and (++$endlessloop < 20)) - $Text = preg_replace("/\[quote=[\"\']*(.*?)[\"\']*\](.*?)\[\/quote\]/ism", + while ((strpos($text, "[/quote]")!== false) and (strpos($text, "[quote=") !== false) and (++$endlessloop < 20)) + $text = preg_replace("/\[quote=[\"\']*(.*?)[\"\']*\](.*?)\[\/quote\]/ism", "<span class=".'"bb-quote"'.">" . $t_wrote . "</span><blockquote>$2</blockquote>", - $Text); + $text); // Images - if (strpos($Text,'[/img]') !== false) { - $Text = preg_replace_callback('/\[\$b64img(.*?)\[\/(img)\]/ism','\red_unescape_codeblock',$Text); + if (strpos($text,'[/img]') !== false) { + $text = preg_replace_callback('/\[\$b64img(.*?)\[\/(img)\]/ism','\red_unescape_codeblock',$text); } - if (strpos($Text,'[/zmg]') !== false) { - $Text = preg_replace_callback('/\[\$b64zmg(.*?)\[\/(zmg)\]/ism','\red_unescape_codeblock',$Text); + if (strpos($text,'[/zmg]') !== false) { + $text = preg_replace_callback('/\[\$b64zmg(.*?)\[\/(zmg)\]/ism','\red_unescape_codeblock',$text); } // [img]pathtoimage[/img] - if (strpos($Text,'[/img]') !== false) { + if (strpos($text,'[/img]') !== false) { - $Text = preg_replace("/\[img\](.*?)\[\/img\]/ism", '<img style="max-width: 100%;" src="$1" alt="' . t('Image/photo') . '" loading="eager" />', $Text); + $text = preg_replace("/\[img\](.*?)\[\/img\]/ism", '<img style="max-width: 100%;" src="$1" alt="' . t('Image/photo') . '" loading="eager" />', $text); } // [img=pathtoimage]image description[/img] - if (strpos($Text,'[/img]') !== false) { - $Text = preg_replace("/\[img=http(.*?)\](.*?)\[\/img\]/ism", '<img style="max-width: 100%;" src="http$1" alt="$2" title="$2" loading="eager" />', $Text); + if (strpos($text,'[/img]') !== false) { + $text = preg_replace("/\[img=http(.*?)\](.*?)\[\/img\]/ism", '<img style="max-width: 100%;" src="http$1" alt="$2" title="$2" loading="eager" />', $text); } // [zmg]pathtoimage[/zmg] - if (strpos($Text,'[/zmg]') !== false) { - $Text = preg_replace("/\[zmg\](.*?)\[\/zmg\]/ism", '<img class="zrl" style="max-width: 100%;" src="$1" alt="' . t('Image/photo') . '" loading="eager" />', $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') . '" loading="eager" />', $text); } // [zmg=pathtoimage]image description[/zmg] - if (strpos($Text,'[/zmg]') !== false) { - $Text = preg_replace("/\[zmg=http(.*?)\](.*?)\[\/zmg\]/ism", '<img class="zrl" style="max-width: 100%;" src="http$1" alt="$2" title="$2" loading="eager" />', $Text); + if (strpos($text,'[/zmg]') !== false) { + $text = preg_replace("/\[zmg=http(.*?)\](.*?)\[\/zmg\]/ism", '<img class="zrl" style="max-width: 100%;" src="http$1" alt="$2" title="$2" loading="eager" />', $text); } - $Text = preg_replace_callback("/\[([zi])mg([ \=])(.*?)\](.*?)\[\/[zi]mg\]/ism",'bb_imgoptions',$Text); + $text = preg_replace_callback("/\[([zi])mg([ \=])(.*?)\](.*?)\[\/[zi]mg\]/ism",'bb_imgoptions',$text); // style (sanitized) - if (strpos($Text,'[/style]') !== false) { - $Text = preg_replace_callback("(\[style=(.*?)\](.*?)\[\/style\])ism", "bb_sanitize_style", $Text); + if (strpos($text,'[/style]') !== false) { + $text = preg_replace_callback("(\[style=(.*?)\](.*?)\[\/style\])ism", "bb_sanitize_style", $text); } // crypt - if (strpos($Text,'[/crypt]') !== false) { - $x = random_string(); - $Text = preg_replace("/\[crypt\](.*?)\[\/crypt\]/ism",'<br /><div id="' . $x . '"><img class="cursor-pointer" src="' .z_root() . '/images/lock_icon.svg" onclick="red_decrypt(\'rot13\',\'\',\'$1\',\'#' . $x . '\');" alt="' . t('Encrypted content') . '" title="' . t('Encrypted content') . '" /><br /></div>', $Text); - $Text = preg_replace_callback("/\[crypt (.*?)\](.*?)\[\/crypt\]/ism", 'bb_parse_crypt', $Text); + if (strpos($text,'[/crypt]') !== false) { + $text = preg_replace_callback("/\[crypt\](.*?)\[\/crypt\]/ism", 'bb_parse_crypt', $text); + $text = preg_replace_callback("/\[crypt (.*?)\](.*?)\[\/crypt\]/ism", 'bb_parse_crypt', $text); } - if(strpos($Text,'[/app]') !== false) { - $Text = preg_replace_callback("/\[app\](.*?)\[\/app\]/ism",'bb_parse_app', $Text); + if(strpos($text,'[/app]') !== false) { + $text = preg_replace_callback("/\[app\](.*?)\[\/app\]/ism",'bb_parse_app', $text); } - if(strpos($Text,'[/element]') !== false) { - $Text = preg_replace_callback("/\[element\](.*?)\[\/element\]/ism",'bb_parse_element', $Text); + if(strpos($text,'[/element]') !== false) { + $text = preg_replace_callback("/\[element\](.*?)\[\/element\]/ism",'bb_parse_element', $text); } // html5 video and audio - if (strpos($Text,'[/video]') !== false) { - $Text = preg_replace_callback("/\[video (.*?)\](.*?)\[\/video\]/ism", 'videowithopts', $Text); - $Text = preg_replace_callback("/\[video\](.*?)\[\/video\]/ism", 'tryzrlvideo', $Text); + if (strpos($text,'[/video]') !== false) { + $text = preg_replace_callback("/\[video (.*?)\](.*?)\[\/video\]/ism", 'videowithopts', $text); + $text = preg_replace_callback("/\[video\](.*?)\[\/video\]/ism", 'tryzrlvideo', $text); } - if (strpos($Text,'[/audio]') !== false) { - $Text = preg_replace_callback("/\[audio\](.*?)\[\/audio\]/ism", 'tryzrlaudio', $Text); + if (strpos($text,'[/audio]') !== false) { + $text = preg_replace_callback("/\[audio\](.*?)\[\/audio\]/ism", 'tryzrlaudio', $text); } - if (strpos($Text,'[/zvideo]') !== false) { - $Text = preg_replace_callback("/\[zvideo (.*?)\](.*?)\[\/zvideo\]/ism", 'videowithopts', $Text); - $Text = preg_replace_callback("/\[zvideo\](.*?)\[\/zvideo\]/ism", 'tryzrlvideo', $Text); + if (strpos($text,'[/zvideo]') !== false) { + $text = preg_replace_callback("/\[zvideo (.*?)\](.*?)\[\/zvideo\]/ism", 'videowithopts', $text); + $text = preg_replace_callback("/\[zvideo\](.*?)\[\/zvideo\]/ism", 'tryzrlvideo', $text); } - if (strpos($Text,'[/zaudio]') !== false) { - $Text = preg_replace_callback("/\[zaudio\](.*?)\[\/zaudio\]/ism", 'tryzrlaudio', $Text); + if (strpos($text,'[/zaudio]') !== false) { + $text = preg_replace_callback("/\[zaudio\](.*?)\[\/zaudio\]/ism", 'tryzrlaudio', $text); } // SVG stuff - $Text = preg_replace_callback("/\[svg(.*?)\](.*?)\[\/svg\]/ism", 'bb_svg', $Text); + $text = preg_replace_callback("/\[svg(.*?)\](.*?)\[\/svg\]/ism", 'bb_svg', $text); // Try to Oembed if ($tryoembed) { - if (strpos($Text,'[/video]') !== false) { - $Text = preg_replace_callback("/\[video\](.*?)\[\/video\]/ism", 'tryoembed', $Text); + if (strpos($text,'[/video]') !== false) { + $text = preg_replace_callback("/\[video\](.*?)\[\/video\]/ism", 'tryoembed', $text); } - if (strpos($Text,'[/audio]') !== false) { - $Text = preg_replace_callback("/\[audio\](.*?)\[\/audio\]/ism", 'tryoembed', $Text); + if (strpos($text,'[/audio]') !== false) { + $text = preg_replace_callback("/\[audio\](.*?)\[\/audio\]/ism", 'tryoembed', $text); } - if (strpos($Text,'[/zvideo]') !== false) { - $Text = preg_replace_callback("/\[zvideo\](.*?)\[\/zvideo\]/ism", 'tryoembed', $Text); + if (strpos($text,'[/zvideo]') !== false) { + $text = preg_replace_callback("/\[zvideo\](.*?)\[\/zvideo\]/ism", 'tryoembed', $text); } - if (strpos($Text,'[/zaudio]') !== false) { - $Text = preg_replace_callback("/\[zaudio\](.*?)\[\/zaudio\]/ism", 'tryoembed', $Text); + if (strpos($text,'[/zaudio]') !== false) { + $text = preg_replace_callback("/\[zaudio\](.*?)\[\/zaudio\]/ism", 'tryoembed', $text); } } // if video couldn't be embedded, link to it instead. - if (strpos($Text,'[/video]') !== false) { - $Text = preg_replace("/\[video\](.*?)\[\/video\]/", '<a href="$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $Text); + if (strpos($text,'[/video]') !== false) { + $text = preg_replace("/\[video\](.*?)\[\/video\]/", '<a href="$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $text); } - if (strpos($Text,'[/audio]') !== false) { - $Text = preg_replace("/\[audio\](.*?)\[\/audio\]/", '<a href="$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $Text); + if (strpos($text,'[/audio]') !== false) { + $text = preg_replace("/\[audio\](.*?)\[\/audio\]/", '<a href="$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $text); } - if (strpos($Text,'[/zvideo]') !== false) { - $Text = preg_replace("/\[zvideo\](.*?)\[\/zvideo\]/", '<a class="zid" href="$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $Text); + if (strpos($text,'[/zvideo]') !== false) { + $text = preg_replace("/\[zvideo\](.*?)\[\/zvideo\]/", '<a class="zid" href="$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $text); } - if (strpos($Text,'[/zaudio]') !== false) { - $Text = preg_replace("/\[zaudio\](.*?)\[\/zaudio\]/", '<a class="zid" href="$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $Text); + if (strpos($text,'[/zaudio]') !== false) { + $text = preg_replace("/\[zaudio\](.*?)\[\/zaudio\]/", '<a class="zid" href="$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $text); } // oembed tag - $Text = oembed_bbcode2html($Text); + if (strpos($text,'[/embed]') !== false) { + $text = oembed_bbcode2html($text); - // Avoid triple linefeeds through oembed - $Text = str_replace("<br style='clear:left'></span><br /><br />", "<br style='clear:left'></span><br />", $Text); + // Avoid triple linefeeds through oembed + $text = str_replace("<br style='clear:left'></span><br /><br />", "<br style='clear:left'></span>", $text); + } // If we found an event earlier, strip out all the event code and replace with a reformatted version. // Replace the event-start section with the entire formatted event. The other bbcode is stripped. @@ -1662,72 +1749,73 @@ function bbcode($Text, $options = []) { $sub = str_replace('$',"\0",$sub); - $Text = preg_replace("/\[event\-start\](.*?)\[\/event\-start\]/ism",$sub,$Text); + $text = preg_replace("/\[event\-start\](.*?)\[\/event\-start\]/ism",$sub,$text); - $Text = preg_replace("/\[event\](.*?)\[\/event\]/ism",'',$Text); - $Text = preg_replace("/\[event\-summary\](.*?)\[\/event\-summary\]/ism",'',$Text); - $Text = preg_replace("/\[event\-description\](.*?)\[\/event\-description\]/ism",'',$Text); - $Text = preg_replace("/\[event\-finish\](.*?)\[\/event\-finish\]/ism",'',$Text); - $Text = preg_replace("/\[event\-id\](.*?)\[\/event\-id\]/ism",'',$Text); - $Text = preg_replace("/\[event\-location\](.*?)\[\/event\-location\]/ism",'',$Text); - $Text = preg_replace("/\[event\-timezone\](.*?)\[\/event\-timezone\]/ism",'',$Text); - $Text = preg_replace("/\[event\-adjust\](.*?)\[\/event\-adjust\]/ism",'',$Text); + $text = preg_replace("/\[event\](.*?)\[\/event\]/ism",'',$text); + $text = preg_replace("/\[event\-summary\](.*?)\[\/event\-summary\]/ism",'',$text); + $text = preg_replace("/\[event\-description\](.*?)\[\/event\-description\]/ism",'',$text); + $text = preg_replace("/\[event\-finish\](.*?)\[\/event\-finish\]/ism",'',$text); + $text = preg_replace("/\[event\-id\](.*?)\[\/event\-id\]/ism",'',$text); + $text = preg_replace("/\[event\-location\](.*?)\[\/event\-location\]/ism",'',$text); + $text = preg_replace("/\[event\-timezone\](.*?)\[\/event\-timezone\]/ism",'',$text); + $text = preg_replace("/\[event\-adjust\](.*?)\[\/event\-adjust\]/ism",'',$text); - $Text = str_replace("\0",'$',$Text); + $text = str_replace("\0",'$',$text); } - if (strpos($Text,'[summary]') !== false) { - $Text = preg_replace_callback("/\[summary\](.*?)\[\/summary\]/ism", 'bb_unspacefy',$Text); + if (strpos($text,'[summary]') !== false) { + $text = preg_replace_callback("/\[summary\](.*?)\[\/summary\]/ism", 'bb_unspacefy',$text); } - if(strpos($Text,'[/summary]') !== false) { - $Text = preg_replace_callback("/^(.*?)\[summary\](.*?)\[\/summary\](.*?)$/is", 'bb_summary', $Text); + if(strpos($text,'[/summary]') !== false) { + $text = preg_replace_callback("/^(.*?)\[summary\](.*?)\[\/summary\](.*?)$/is", 'bb_summary', $text); } // Unhide all [noparse] contained bbtags unspacefying them // and triming the [noparse] tag. - if (strpos($Text,'[noparse]') !== false) { - $Text = preg_replace_callback("/\[noparse\](.*?)\[\/noparse\]/ism", 'bb_unspacefy_and_trim', $Text); + if (strpos($text,'[noparse]') !== false) { + $text = preg_replace_callback("/\[noparse\](.*?)\[\/noparse\]/ism", 'bb_unspacefy_and_trim', $text); } - if (strpos($Text,'[nobb]') !== false) { - $Text = preg_replace_callback("/\[nobb\](.*?)\[\/nobb\]/ism", 'bb_unspacefy_and_trim', $Text); + if (strpos($text,'[nobb]') !== false) { + $text = preg_replace_callback("/\[nobb\](.*?)\[\/nobb\]/ism", 'bb_unspacefy_and_trim', $text); } - if (strpos($Text,'[pre]') !== false) { - $Text = preg_replace_callback("/\[pre\](.*?)\[\/pre\]/ism", 'bb_unspacefy_and_trim', $Text); + if (strpos($text,'[pre]') !== false) { + $text = preg_replace_callback("/\[pre\](.*?)\[\/pre\]/ism", 'bb_unspacefy_and_trim', $text); } // replace escaped links in code= blocks - $Text = str_replace('%eY9-!','http', $Text); - $Text = bb_code_unprotect($Text); + $text = str_replace('%eY9-!','http', $text); + $text = bb_code_unprotect($text); - $Text = preg_replace('/\[\&\;([#a-z0-9]+)\;\]/', '&$1;', $Text); + $text = preg_replace('/\[\&\;([#a-z0-9]+)\;\]/', '&$1;', $text); // fix any escaped ampersands that may have been converted into links - if(strpos($Text,'&') !== false) - $Text = preg_replace("/\<(.*?)(src|href)=(.*?)\&\;(.*?)\>/ism", '<$1$2=$3&$4>', $Text); + if(strpos($text,'&') !== false) + $text = preg_replace("/\<(.*?)(src|href)=(.*?)\&\;(.*?)\>/ism", '<$1$2=$3&$4>', $text); // This is subtle - it's an XSS filter. It only accepts links with a protocol scheme and where // the scheme begins with z (zhttp), h (http(s)), f (ftp(s)), m (mailto), t (tel) and named anchors. - $Text = preg_replace("/\<(.*?)(src|href)=\"[^zhfmt#](.*?)\>/ism", '<$1$2="">', $Text); + $text = preg_replace("/\<(.*?)(src|href)=\"[^zhfmt#](.*?)\>/ism", '<$1$2="">', $text); - $Text = bb_replace_images($Text, $saved_images); + $text = bb_replace_images($text, $saved_images); // Convert new line chars to html <br /> tags // nlbr seems to be hopelessly messed up - // $Text = nl2br($Text); + // $text = nl2br($text); // We'll emulate it. - $Text = str_replace("\r\n", "\n", $Text); - $Text = str_replace(array("\r", "\n"), array('<br />', '<br />'), $Text); - - if ($preserve_nl) - $Text = str_replace(array("\n", "\r"), array('', ''), $Text); + $text = str_replace("\r\n", "\n", $text); + $text = str_replace(["\r", "\n"], '<br />', $text); - call_hooks('bbcode', $Text); + /** + * @hooks bbcode + * * _string_ **html** - The resulting HTML after converting from bbcode. + */ + call_hooks('bbcode', $text); - return $Text; + return $text; } diff --git a/include/channel.php b/include/channel.php index 640274348..a82794bfd 100644 --- a/include/channel.php +++ b/include/channel.php @@ -16,6 +16,7 @@ use Zotlabs\Lib\Libzot; use Zotlabs\Lib\Connect; use Zotlabs\Lib\Libsync; use Zotlabs\Lib\AccessList; +use Zotlabs\Lib\Multibase; require_once('include/crypto.php'); require_once('include/menu.php'); @@ -236,6 +237,10 @@ function create_identity($arr) { $guid = Libzot::new_uid($nick); $key = Crypto::new_keypair(4096); + $eckey = sodium_crypto_sign_keypair(); + $ekey['pubkey'] = sodium_bin2base64(sodium_crypto_sign_publickey($eckey), SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING); + $ekey['prvkey'] = sodium_bin2base64(sodium_crypto_sign_secretkey($eckey), SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING); + // zot6 $sig = Libzot::sign($guid,$key['prvkey']); $hash = Libzot::make_xchan_hash($guid,$key['pubkey']); @@ -275,6 +280,8 @@ function create_identity($arr) { 'channel_portable_id' => '', 'channel_prvkey' => $key['prvkey'], 'channel_pubkey' => $key['pubkey'], + 'channel_eprvkey' => $ekey['prvkey'], + 'channel_epubkey' => $ekey['pubkey'], 'channel_pageflags' => intval($pageflags), 'channel_system' => intval($system), 'channel_expire_days' => intval($expire), @@ -370,6 +377,7 @@ function create_identity($arr) { 'xchan_guid' => $guid, 'xchan_guid_sig' => $sig, 'xchan_pubkey' => $key['pubkey'], + 'xchan_epubkey' => (new Multibase())->publicKey($ekey['pubkey']), 'xchan_photo_mimetype' => (($photo_type) ? $photo_type : 'image/png'), 'xchan_photo_l' => z_root() . "/photo/profile/l/{$newuid}", 'xchan_photo_m' => z_root() . "/photo/profile/m/{$newuid}", @@ -1763,17 +1771,19 @@ function advanced_profile() { if(App::$profile['gender']) $profile['gender'] = array( t('Gender:'), App::$profile['gender'] ); $ob_hash = get_observer_hash(); +/* TODO: AS2 compatibility if($ob_hash && perm_is_allowed(App::$profile['profile_uid'],$ob_hash,'post_like')) { $profile['canlike'] = true; $profile['likethis'] = t('Like this channel'); $profile['profile_guid'] = App::$profile['profile_guid']; } - $likers = q("select liker, xchan.* from likes left join xchan on liker = xchan_hash where channel_id = %d and target_type = '%s' and verb = '%s'", + $likers = q("select liker, xchan.* from likes left join xchan on liker = xchan_hash where channel_id = %d and (target_type = 'Profile' OR target_type = '%s') and (verb = 'Like' OR verb = '%s')", intval(App::$profile['profile_uid']), dbesc(ACTIVITY_OBJ_PROFILE), dbesc(ACTIVITY_LIKE) ); + $profile['likers'] = array(); $profile['like_count'] = count($likers); $profile['like_button_label'] = tt('Like','Likes',$profile['like_count'],'noun'); @@ -1781,7 +1791,7 @@ function advanced_profile() { foreach($likers as $l) $profile['likers'][] = array('name' => $l['xchan_name'],'photo' => zid($l['xchan_photo_s']), 'url' => zid($l['xchan_url'])); } - +*/ if((App::$profile['dob']) && (App::$profile['dob'] != '0000-00-00')) { $val = ''; @@ -2642,6 +2652,8 @@ function channel_store_lowlevel($arr) { 'channel_startpage' => ((array_key_exists('channel_startpage',$arr)) ? $arr['channel_startpage'] : ''), 'channel_pubkey' => ((array_key_exists('channel_pubkey',$arr)) ? $arr['channel_pubkey'] : ''), 'channel_prvkey' => ((array_key_exists('channel_prvkey',$arr)) ? $arr['channel_prvkey'] : ''), + 'channel_epubkey' => ((array_key_exists('channel_epubkey',$arr)) ? $arr['channel_epubkey'] : ''), + 'channel_eprvkey' => ((array_key_exists('channel_eprvkey',$arr)) ? $arr['channel_eprvkey'] : ''), 'channel_notifyflags' => ((array_key_exists('channel_notifyflags',$arr)) ? $arr['channel_notifyflags'] : '65535'), 'channel_pageflags' => ((array_key_exists('channel_pageflags',$arr)) ? $arr['channel_pageflags'] : '0'), 'channel_dirdate' => ((array_key_exists('channel_dirdate',$arr)) ? $arr['channel_dirdate'] : NULL_DATE), diff --git a/include/contact_widgets.php b/include/contact_widgets.php index 182f674ca..c05ecaf7c 100644 --- a/include/contact_widgets.php +++ b/include/contact_widgets.php @@ -85,7 +85,7 @@ function categories_widget($baseurl,$selected = '') { AND term.otype = %d AND item.owner_xchan = '%s' AND item.item_wall = 1 - AND item.verb != '%s' + AND item.verb NOT IN ('Update', '%s') $item_normal $sql_extra ORDER BY term.term ASC", diff --git a/include/conversation.php b/include/conversation.php index faa53c639..79fe12d54 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -90,7 +90,7 @@ function item_redir_and_replace_images($body, $images, $cid) { function localize_item(&$item){ - if (activity_match($item['verb'],ACTIVITY_LIKE) || activity_match($item['verb'],ACTIVITY_DISLIKE)){ + if (activity_match($item['verb'], ['Like', 'Dislike', ACTIVITY_LIKE, ACTIVITY_DISLIKE, ACTIVITY_SHARE])){ if(! $item['obj']) return; @@ -143,7 +143,7 @@ function localize_item(&$item){ switch($obj['type']) { case ACTIVITY_OBJ_PHOTO: - case 'Photo': + case 'Image': $post_type = t('photo'); break; case ACTIVITY_OBJ_EVENT: @@ -151,6 +151,7 @@ function localize_item(&$item){ $post_type = t('event'); break; case ACTIVITY_OBJ_PERSON: + case 'Person': $post_type = t('channel'); $author_name = $obj['title']; if($obj['link']) { @@ -189,21 +190,19 @@ function localize_item(&$item){ $plink = '[zrl=' . zid($item_url) . ']' . $post_type . '[/zrl]'; - if(activity_match($item['verb'],ACTIVITY_LIKE)) { + if(activity_match($item['verb'], ['Like', ACTIVITY_LIKE])) { $bodyverb = t('%1$s likes %2$s\'s %3$s'); - } - elseif(activity_match($item['verb'],ACTIVITY_DISLIKE)) { - $bodyverb = t('%1$s doesn\'t like %2$s\'s %3$s'); - } - - // short version, in notification strings the author will be displayed separately - - if(activity_match($item['verb'],ACTIVITY_LIKE)) { + // short version, in notification strings the author will be displayed separately $shortbodyverb = t('likes %1$s\'s %2$s'); } - elseif(activity_match($item['verb'],ACTIVITY_DISLIKE)) { + elseif(activity_match($item['verb'], ['Dislike', ACTIVITY_DISLIKE])) { + $bodyverb = t('%1$s doesn\'t like %2$s\'s %3$s'); $shortbodyverb = t('doesn\'t like %1$s\'s %2$s'); } + elseif(activity_match($item['verb'], ACTIVITY_SHARE)) { + $bodyverb = t('%1$s repeated %2$s\'s %3$s'); + $shortbodyverb = t('repeated %1$s\'s %2$s'); + } $item['shortlocalize'] = sprintf($shortbodyverb, '[bdi]' . $author_name . '[/bdi]', $post_type); @@ -218,191 +217,6 @@ function localize_item(&$item){ } - if (activity_match($item['verb'],ACTIVITY_FRIEND)) { - - if ($item['obj_type'] == "" || $item['obj_type'] !== ACTIVITY_OBJ_PERSON) - return; - - $Aname = $item['author']['xchan_name']; - $Alink = $item['author']['xchan_url']; - - - $obj= json_decode($item['obj'],true); - - $Blink = $Bphoto = ''; - - if($obj['link']) { - $Blink = get_rel_link($obj['link'],'alternate'); - $Bphoto = get_rel_link($obj['link'],'photo'); - } - $Bname = $obj['title']; - - - $A = '[zrl=' . chanlink_url($Alink) . '][bdi]' . $Aname . '[/bdi][/zrl]'; - $B = '[zrl=' . chanlink_url($Blink) . '][bdi]' . $Bname . '[/bdi][/zrl]'; - if ($Bphoto!="") $Bphoto = '[zrl=' . chanlink_url($Blink) . '][zmg=80x80]' . $Bphoto . '[/zmg][/zrl]'; - - $item['shortlocalize'] = sprintf( t('%1$s is now connected with %2$s'), '[bdi]' . $Aname . '[/bdi]', '[bdi]' . $Bname . '[/bdi]'); - - $item['body'] = $item['localize'] = sprintf( t('%1$s is now connected with %2$s'), $A, $B); - $item['body'] .= "\n\n\n" . $Bphoto; - } - - if (stristr($item['verb'], ACTIVITY_POKE)) { - - /** @FIXME for obscured private posts, until then leave untranslated */ - return; - - $verb = urldecode(substr($item['verb'],strpos($item['verb'],'#')+1)); - if(! $verb) - return; - - if ($item['obj_type']=="" || $item['obj_type']!== ACTIVITY_OBJ_PERSON) return; - - $Aname = $item['author']['xchan_name']; - $Alink = $item['author']['xchan_url']; - - $obj= json_decode($item['obj'],true); - - $Blink = $Bphoto = ''; - - if($obj['link']) { - $Blink = get_rel_link($obj['link'],'alternate'); - $Bphoto = get_rel_link($obj['link'],'photo'); - } - $Bname = $obj['title']; - - $A = '[zrl=' . chanlink_url($Alink) . '][bdi]' . $Aname . '[/bdi][/zrl]'; - $B = '[zrl=' . chanlink_url($Blink) . '][bdi]' . $Bname . '[/bdi][/zrl]'; - if ($Bphoto!="") $Bphoto = '[zrl=' . chanlink_url($Blink) . '][zmg=80x80]' . $Bphoto . '[/zmg][/zrl]'; - - // we can't have a translation string with three positions but no distinguishable text - // So here is the translate string. - - $txt = t('%1$s poked %2$s'); - - // now translate the verb - - $txt = str_replace( t('poked'), t($verb), $txt); - - // then do the sprintf on the translation string - - $item['shortlocalize'] = sprintf($txt, '[bdi]' . $Aname . '[/bdi]', '[bdi]' . $Bname . '[/bdi]'); - - $item['body'] = $item['localize'] = sprintf($txt, $A, $B); - $item['body'] .= "\n\n\n" . $Bphoto; - } - if (stristr($item['verb'],ACTIVITY_MOOD)) { - $verb = urldecode(substr($item['verb'],strpos($item['verb'],'#')+1)); - if(! $verb) - return; - - $Aname = $item['author']['xchan_name']; - $Alink = $item['author']['xchan_url']; - - $A = '[zrl=' . chanlink_url($Alink) . '][bdi]' . $Aname . '[/bdi][/zrl]'; - - $txt = t('%1$s is %2$s','mood'); - - $item['body'] = sprintf($txt, $A, t($verb)); - } - - - -/* -// FIXME store parent item as object or target -// (and update to json storage) - - if (activity_match($item['verb'],ACTIVITY_TAG)) { - $r = q("SELECT * from item,contact WHERE - item.contact-id=contact.id AND item.mid='%s';", - dbesc($item['parent_mid'])); - if(count($r)==0) return; - $obj=$r[0]; - - $author = '[zrl=' . zid($item['author-link']) . ']' . $item['author-name'] . '[/zrl]'; - $objauthor = '[zrl=' . zid($obj['author-link']) . ']' . $obj['author-name'] . '[/zrl]'; - - switch($obj['verb']){ - case ACTIVITY_POST: - switch ($obj['obj_type']){ - case ACTIVITY_OBJ_EVENT: - $post_type = t('event'); - break; - default: - $post_type = t('status'); - } - break; - default: - if($obj['resource_id']){ - $post_type = t('photo'); - $m=array(); preg_match("/\[[zu]rl=([^]]*)\]/", $obj['body'], $m); - $rr['plink'] = $m[1]; - } else { - $post_type = t('status'); - } - } - $plink = '[zrl=' . $obj['plink'] . ']' . $post_type . '[/zrl]'; - -// $parsedobj = parse_xml_string($xmlhead.$item['obj']); - - $tag = sprintf('#[zrl=%s]%s[/zrl]', $parsedobj->id, $parsedobj->content); - $item['body'] = sprintf( t('%1$s tagged %2$s\'s %3$s with %4$s'), $author, $objauthor, $plink, $tag ); - - } - - if (activity_match($item['verb'],ACTIVITY_FAVORITE)){ - - if ($item['obj_type']== "") - return; - - $Aname = $item['author']['xchan_name']; - $Alink = $item['author']['xchan_url']; - - $xmlhead="<"."?xml version='1.0' encoding='UTF-8' ?".">"; - -// $obj = parse_xml_string($xmlhead.$item['obj']); - if(strlen($obj->id)) { - $r = q("select * from item where mid = '%s' and uid = %d limit 1", - dbesc($obj->id), - intval($item['uid']) - ); - if(count($r) && $r[0]['plink']) { - $target = $r[0]; - $Bname = $target['author-name']; - $Blink = $target['author-link']; - $A = '[zrl=' . zid($Alink) . ']' . $Aname . '[/zrl]'; - $B = '[zrl=' . zid($Blink) . ']' . $Bname . '[/zrl]'; - $P = '[zrl=' . $target['plink'] . ']' . t('post/item') . '[/zrl]'; - $item['body'] = sprintf( t('%1$s marked %2$s\'s %3$s as favorite'), $A, $B, $P)."\n"; - - } - } - } -*/ - -/* - $matches = null; - if(strpos($item['body'],'[zrl') !== false) { - if(preg_match_all('/@\[zrl=(.*?)\]/is',$item['body'],$matches,PREG_SET_ORDER)) { - foreach($matches as $mtch) { - if(! strpos($mtch[1],'zid=')) - $item['body'] = str_replace($mtch[0],'@[zrl=' . zid($mtch[1]). ']',$item['body']); - } - } - } - - if(strpos($item['body'],'[zmg') !== false) { - // add zid's to public images - if(preg_match_all('/\[zrl=(.*?)\/photos\/(.*?)\/image\/(.*?)\]\[zmg(.*?)\]h(.*?)\[\/zmg\]\[\/zrl\]/is',$item['body'],$matches,PREG_SET_ORDER)) { - foreach($matches as $mtch) { - $item['body'] = str_replace($mtch[0],'[zrl=' . zid( $mtch[1] . '/photos/' . $mtch[2] . '/image/' . $mtch[3]) . '][zmg' . $mtch[4] . ']h' . $mtch[5] . '[/zmg][/zrl]',$item['body']); - } - } - } -*/ - - } /** @@ -441,7 +255,7 @@ function count_descendants($item) { * @return boolean */ function visible_activity($item) { - $hidden_activities = [ ACTIVITY_LIKE, ACTIVITY_DISLIKE, ACTIVITY_AGREE, ACTIVITY_DISAGREE, ACTIVITY_ABSTAIN, ACTIVITY_ATTEND, ACTIVITY_ATTENDNO, ACTIVITY_ATTENDMAYBE, ACTIVITY_POLLRESPONSE ]; + $hidden_activities = ['Like', 'Dislike', 'Accept', 'Reject', 'TentativeAccept', ACTIVITY_LIKE, ACTIVITY_DISLIKE, ACTIVITY_SHARE, ACTIVITY_ATTEND, ACTIVITY_ATTENDNO, ACTIVITY_ATTENDMAYBE]; if(intval($item['item_notshown'])) return false; @@ -450,7 +264,7 @@ function visible_activity($item) { return false; } - if (in_array($item['verb'], ['Add', 'Remove', ACTIVITY_TAG])) { + if (in_array($item['verb'], ['Add', 'Remove'])) { return false; } @@ -476,27 +290,6 @@ function visible_activity($item) { return true; } -/** - * @brief Check if a given activity is an edit activity - * - * - * @param array $item - * @return boolean - */ - -function is_edit_activity($item) { - - $post_types = [ ACTIVITY_OBJ_NOTE, ACTIVITY_OBJ_COMMENT, basename(ACTIVITY_OBJ_NOTE), basename(ACTIVITY_OBJ_COMMENT)]; - - // In order to share edits with networks which have no concept of editing, we'll create - // separate activities to indicate the edit. Our network will not require them, since our - // edits are automatically applied and the activity indicated. - - if(($item['verb'] === ACTIVITY_UPDATE) && (in_array($item['obj_type'],$post_types))) - return true; - - return false; -} /** * @brief "Render" a conversation or list of items for HTML display. @@ -670,13 +463,11 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa $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' => [] + 'answer' => [], + 'announce' => ['title' => t('Repeats','title')], ]; @@ -718,9 +509,9 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa $is_new = false; if($mode === 'search' || $mode === 'community') { - if(((activity_match($item['verb'],ACTIVITY_LIKE)) || (activity_match($item['verb'],ACTIVITY_DISLIKE))) - && ($item['id'] != $item['parent'])) + if(activity_match($item['verb'], ['Like', 'Dislike', ACTIVITY_LIKE, ACTIVITY_DISLIKE]) && $item['id'] != $item['parent']) { continue; + } } $sp = false; @@ -785,16 +576,14 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa if(strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC','now - 12 hours')) > 0) $is_new = true; - $conv_link_mid = (($mode == 'moderate') ? gen_link_id($item['parent_mid']) : gen_link_id($item['mid'])); - - $conv_link = z_root() . '/display/' . $conv_link_mid; + $conv_link = z_root() . '/display/' . $item['uuid']; if(local_channel()) { - $conv_link = z_root() . '/hq/' . $conv_link_mid; + $conv_link = z_root() . '/hq/' . $item['uuid']; } if ($mode === 'pubstream-new') { - $conv_link = z_root() . '/pubstream?mid=' . $conv_link_mid; + $conv_link = z_root() . '/pubstream?mid=' . $item['uuid']; } $contact = []; @@ -812,8 +601,8 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa 'delete' => t('Delete'), 'preview_lbl' => $preview_lbl, 'id' => (($preview) ? 'P0' : $item['item_id']), - 'mid' => gen_link_id($item['mid']), - 'mids' => json_encode([gen_link_id($item['mid'])]), + 'mid' => $item['uuid'], + 'mids' => json_encode([$item['uuid']]), 'linktitle' => sprintf( t('View %s\'s profile @ %s'), $profile_name, $profile_link), 'author_id' => (($item['author']['xchan_addr']) ? $item['author']['xchan_addr'] : $item['author']['xchan_url']), 'profile_url' => $profile_link, @@ -914,7 +703,7 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa continue; } - + $mid_uuid_map[$item['mid']] = $item['uuid']; $item['pagedrop'] = $page_dropping; @@ -934,7 +723,7 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa } } - $threads = $conv->get_template_data($conv_responses); + $threads = $conv->get_template_data($conv_responses, $mid_uuid_map); if(!$threads) { logger('[ERROR] conversation : Failed to get template data.', LOGGER_DEBUG); $threads = array(); @@ -1091,13 +880,10 @@ function thread_author_menu($item, $mode = '') { } } - $contact_url = ''; $posts_link = ''; - $poke_link = ''; if($contact) { - $poke_link = ((Apps::system_app_installed($local_channel, 'Poke')) ? z_root() . '/poke/?f=&c=' . $contact['abook_id'] : ''); if (isset($contact['abook_self']) && !intval($contact['abook_self'])) $contact_url = z_root() . '/connections#' . $contact['abook_id']; $posts_link = z_root() . '/network/?cid=' . $contact['abook_id']; @@ -1151,18 +937,6 @@ function thread_author_menu($item, $mode = '') { ]; } - if($poke_link) { - $menu[] = [ - 'menu' => 'poke', - 'title' => t('Poke'), - 'icon' => 'fw', - 'action' => '', - 'href' => $poke_link, - 'data' => '', - 'class' => '' - ]; - } - $args = [ 'item' => $item, 'mode' => $mode, 'menu' => $menu ]; call_hooks('thread_author_menu', $args); @@ -1186,7 +960,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) && $item['obj_type'] !== 'Answer') + if(activity_match($item['verb'], ['Create', ACTIVITY_POST]) && $item['obj_type'] !== 'Answer') return; foreach($conv_responses as $mode => $v) { @@ -1195,31 +969,25 @@ function builtin_activity_puller($item, &$conv_responses) { switch($mode) { case 'like': - $verb = ACTIVITY_LIKE; + $verb = ['Like', ACTIVITY_LIKE]; break; case 'dislike': - $verb = ACTIVITY_DISLIKE; - break; - case 'agree': - $verb = ACTIVITY_AGREE; - break; - case 'disagree': - $verb = ACTIVITY_DISAGREE; - break; - case 'abstain': - $verb = ACTIVITY_ABSTAIN; + $verb = ['Dislike', ACTIVITY_DISLIKE]; break; case 'attendyes': - $verb = ACTIVITY_ATTEND; + $verb = ['Accept', ACTIVITY_ATTEND]; break; case 'attendno': - $verb = ACTIVITY_ATTENDNO; + $verb = ['Reject', ACTIVITY_ATTENDNO]; break; case 'attendmaybe': - $verb = ACTIVITY_ATTENDMAYBE; + $verb = ['TentativeAccept', ACTIVITY_ATTENDMAYBE]; break; case 'answer': - $verb = ACTIVITY_POST; + $verb = ['Create', ACTIVITY_POST]; + break; + case 'announce': + $verb = 'Announce'; break; default: return; @@ -1227,6 +995,7 @@ function builtin_activity_puller($item, &$conv_responses) { } if((activity_match($item['verb'], $verb)) && ($item['id'] != $item['parent'])) { + $name = (($item['author']['xchan_name']) ? $item['author']['xchan_name'] : t('Unknown')); $moderate = ((intval($item['item_blocked']) === ITEM_MODERATED) ? '<a href="moderate/' . $item['id'] . '/approve" onclick="moderate_approve(' . $item['id'] . '); return false;" class="text-success pe-2" title="' . t('Approve this item') . '"><i class="fa fa-check" ></i></a><a href="moderate/' . $item['id'] . '/drop" onclick="moderate_drop(' . $item['id'] . '); return false;" class="text-danger pe-2" title="' . t('Delete this item') . '"><i class="fa fa-trash-o" ></i></a>' : ''); @@ -1241,7 +1010,7 @@ function builtin_activity_puller($item, &$conv_responses) { if(! $item['thr_parent']) $item['thr_parent'] = $item['parent_mid']; - $conv_responses[$mode]['mids'][$item['thr_parent']][] = gen_link_id($item['mid']); + $conv_responses[$mode]['mids'][$item['thr_parent']][] = $item['uuid']; if($item['obj_type'] === 'Answer') continue; @@ -1778,28 +1547,22 @@ function get_responses($conv_responses,$response_verbs,$ob,$item) { function get_response_button_text($v,$count) { switch($v) { case 'like': - return tt('Like','Likes',$count,'noun'); + return ['label' => tt('Like','Likes',$count,'noun'), 'icon' => 'thumbs-o-up', 'class' => 'like']; + break; + case 'announce': + return ['label' => tt('Repeat','Repeats',$count,'noun'), 'icon' => 'retweet', 'class' => 'announce']; break; case 'dislike': - return tt('Dislike','Dislikes',$count,'noun'); + return ['label' => tt('Dislike','Dislikes',$count,'noun'), 'icon' => 'thumbs-o-down', 'class' => 'dislike']; break; case 'attendyes': - return tt('Attending','Attending',$count,'noun'); + return ['label' => tt('Attending','Attending',$count,'noun'), 'icon' => 'calendar-check-o', 'class' => 'attendyes']; break; case 'attendno': - return tt('Not Attending','Not Attending',$count,'noun'); + return ['label' => tt('Not Attending','Not Attending',$count,'noun'), 'icon' => 'calendar-times-o', 'class' => 'attendno']; break; case 'attendmaybe': - return tt('Undecided','Undecided',$count,'noun'); - break; - case 'agree': - return tt('Agree','Agrees',$count,'noun'); - break; - case 'disagree': - return tt('Disagree','Disagrees',$count,'noun'); - break; - case 'abstain': - return tt('Abstain','Abstains',$count,'noun'); + return ['label' => tt('Undecided','Undecided',$count,'noun'), 'icon' => 'calendar-o', 'class' => 'attendmaybe']; break; default: return ''; diff --git a/include/dba/dba_transaction.php b/include/dba/dba_transaction.php new file mode 100644 index 000000000..02e9945ca --- /dev/null +++ b/include/dba/dba_transaction.php @@ -0,0 +1,64 @@ +<?php +/** + * Class to represent a database transaction. + * + * A database transaction is initiated upon construction of an object of this + * class. The transaction will be automatically rolled back upon destruction + * unless it has been explicitly committed by calling the `commit` method. + * + * Wrapping multiple database operation within a transaction ensures that all + * (or none) of the operations are successfully completed at the same time. + * + * If a transaction is already active when constructing an object of this + * class, it will _not_ try to initiate a transaction, but constructs an object + * that will in practice be a stub. This prevents that "nested" transactions + * will cause problems with the existing active transaction. + * + * It also means that any rollbacks or commits perfomed on the "nested" + * transaction will be ignored, and postponed to the outer transaction is + * committed or rolled back. + * + * Also note that any modification to the database schema will implicitly + * commit active transactions in most cases, so be careful about relying on + * transactions in those cases. + * + * @Note This class assumes the actual underlying database driver is PDO. + */ +class DbaTransaction { + private bool $committed = false; + private bool $active = false; + + /** + * Creates a database transaction object. + * + * If a transaction is already active for this db connection, + * no transaction is initiated, and the constructed object will + * not perform any commit or rollback actions. + */ + public function __construct(private dba_driver $dba) { + if (! $this->dba->db->inTransaction()) { + $this->active = $this->dba->db->beginTransaction(); + } + } + + /** + * Roll back the transaction if it is active and not already committed. + */ + public function __destruct() { + if ($this->active && ! $this->committed) { + $this->dba->db->rollBack(); + } + } + + /** + * Commit the transaction if active. + * + * This will also mark the transaction as committed, preventing it from + * being attempted rolled back on destruction. + */ + public function commit(): void { + if ($this->active && ! $this->committed) { + $this->committed = $this->dba->db->commit(); + } + } +} diff --git a/include/event.php b/include/event.php index 745469064..701f3c330 100644 --- a/include/event.php +++ b/include/event.php @@ -58,7 +58,9 @@ function format_event_html($ev) { $ev['dtend'] , $bd_format ))) . '</span></div>' . "\r\n"; - $o .= '<div class="event-description">' . zidify_links(smilies(bbcode($ev['description']))) . '</div>' . "\r\n"; + if (!empty($ev['description'])) { + $o .= '<div class="event-description">' . zidify_links(smilies(bbcode($ev['description']))) . '</div>' . "\r\n"; + } if(isset($ev['location']) && $ev['location']) $o .= '<div class="event-location"><span class="event-label"> ' . t('Location:') . '</span> <span class="location">' @@ -117,6 +119,7 @@ function format_event_obj($jobject) { $dtdiff = $dtstart->diff($dtend_obj); + $oneday = false; if($allday && ($dtdiff->days < 2)) $oneday = true; @@ -993,7 +996,7 @@ function event_import_ical($ical, $uid) { $ev['timezone'] = 'UTC'; // Try to get an usable olson format timezone - if($ev['adjust']) { + if($ev['adjust'] && isset($ical->DTSTART['TZID'])) { //TODO: we should pass the vcalendar to getTimeZone() to be more accurate // we do not have it here since parse_ical_file() is passing the vevent only. $timezone_obj = \Sabre\VObject\TimeZoneUtil::getTimeZone($ical->DTSTART['TZID']); @@ -1308,7 +1311,7 @@ function event_store_item($arr, $event) { dbesc($sig), intval($r[0]['item_flags']), intval($private), - dbesc(ACTIVITY_OBJ_EVENT), + dbesc('Event'), intval($r[0]['id']), intval($arr['uid']) ); @@ -1405,7 +1408,7 @@ function event_store_item($arr, $event) { $item_arr['resource_type'] = 'event'; $item_arr['resource_id'] = $event['event_hash']; - $item_arr['obj_type'] = ACTIVITY_OBJ_EVENT; + $item_arr['obj_type'] = 'Event'; $item_arr['body'] = $prefix . format_event_bbcode($arr); // if it's local send the permalink to the channel page. @@ -1414,7 +1417,7 @@ function event_store_item($arr, $event) { if($wall) $item_arr['plink'] = $item_arr['mid']; else - $item_arr['plink'] = z_root() . '/display/' . gen_link_id($item_arr['mid']); + $item_arr['plink'] = z_root() . '/display/' . $item_arr['uuid']; set_iconfig($item_arr, 'event','timezone',$arr['timezone'],true); diff --git a/include/feedutils.php b/include/feedutils.php index a2d52c698..f05c15414 100644 --- a/include/feedutils.php +++ b/include/feedutils.php @@ -184,7 +184,7 @@ function construct_verb($item) { if ($item['verb']) return $item['verb']; - return ACTIVITY_POST; + return 'Create'; } function construct_activity_object($item) { @@ -305,7 +305,7 @@ function get_atom_author($feed, $item) { } $rawactor = $item->get_item_tags(NAMESPACE_ACTIVITY, 'actor'); - if($rawactor && activity_match($rawactor[0]['child'][NAMESPACE_ACTIVITY]['obj_type'][0]['data'], ACTIVITY_OBJ_PERSON)) { + if($rawactor && activity_match($rawactor[0]['child'][NAMESPACE_ACTIVITY]['obj_type'][0]['data'], ['Person', ACTIVITY_OBJ_PERSON])) { $base = $rawactor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']; if($base && count($base)) { foreach($base as $link) { @@ -350,7 +350,7 @@ function get_atom_author($feed, $item) { $rawactor = $feed->get_feed_tags(NAMESPACE_ACTIVITY, 'subject'); - if($rawactor && activity_match($rawactor[0]['child'][NAMESPACE_ACTIVITY]['obj_type'][0]['data'],ACTIVITY_OBJ_PERSON)) { + if($rawactor && activity_match($rawactor[0]['child'][NAMESPACE_ACTIVITY]['obj_type'][0]['data'], ['Person', ACTIVITY_OBJ_PERSON])) { $base = $rawactor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']; if($base && count($base)) { @@ -508,36 +508,17 @@ function get_atom_elements($feed, $item) { } } - $ostatus_protocol = ($ostatus_conversation || (x($res,'verb') && $res['verb']) ? true : false); - $mastodon = (($item->get_item_tags('http://mastodon.social/schema/1.0','scope')) ? true : false); if($mastodon) { - $ostatus_protocol = true; if(($mastodon[0]['data']) && ($mastodon[0]['data'] !== 'public')) $res['item_private'] = 1; } - logger('ostatus_protocol: ' . intval($ostatus_protocol), LOGGER_DEBUG); - $apps = $item->get_item_tags(NAMESPACE_STATUSNET, 'notice_info'); if($apps && $apps[0]['attribs']['']['source']) { $res['app'] = strip_tags(unxmlify($apps[0]['attribs']['']['source'])); } - if($ostatus_protocol) { - - // translate OStatus unfollow to activity streams if it happened to get selected - - if((x($res,'verb')) && ($res['verb'] === 'http://ostatus.org/schema/1.0/unfollow')) { - $res['verb'] = ACTIVITY_UNFOLLOW; - } - - // And OStatus 'favorite' is pretty much what we call 'like' on other networks - - if((x($res,'verb')) && ($res['verb'] === ACTIVITY_FAVORITE)) { - $res['verb'] = ACTIVITY_LIKE; - } - } /* * If there's a copy of the body content which is guaranteed to have survived mangling in transit, use it. @@ -600,10 +581,7 @@ function get_atom_elements($feed, $item) { $terms = []; - if($ostatus_protocol) { - $res['title'] = ''; - } - elseif($res['plink'] && $res['title']) { + if($res['plink'] && $res['title']) { $res['body'] = '#^[url=' . $res['plink'] . ']' . $res['title'] . '[/url]' . "\n\n" . $res['body']; $terms[] = array( 'otype' => TERM_OBJ_POST, @@ -814,7 +792,7 @@ function get_atom_elements($feed, $item) { if(array_key_exists('verb',$res) && $res['verb'] === ACTIVITY_SHARE - && array_key_exists('obj_type',$res) && in_array($res['obj_type'], [ ACTIVITY_OBJ_NOTE, ACTIVITY_OBJ_COMMENT, ACTIVITY_OBJ_ACTIVITY ] )) { + && array_key_exists('obj_type',$res) && in_array($res['obj_type'], ['Note', ACTIVITY_OBJ_NOTE, ACTIVITY_OBJ_COMMENT])) { feed_get_reshare($res,$item); } @@ -1197,7 +1175,7 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { // Update content if 'updated' changes if($r) { - if(activity_match($datarray['verb'],ACTIVITY_DELETE) + if(activity_match($datarray['verb'], ['Delete', ACTIVITY_DELETE]) && $datarray['author_xchan'] === $r[0]['author_xchan']) { if(! intval($r[0]['item_deleted'])) { logger('deleting item ' . $r[0]['id'] . ' mid=' . $datarray['mid'], LOGGER_DEBUG); @@ -1361,7 +1339,7 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { // allow likes of comments - if($item_parent_mid && activity_match($datarray['verb'],ACTIVITY_LIKE)) { + if($item_parent_mid && activity_match($datarray['verb'], ['Like', ACTIVITY_LIKE])) { $datarray['thr_parent'] = $item_parent_mid[0]['parent_mid']; } @@ -1465,7 +1443,7 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { // Update content if 'updated' changes if($r) { - if(isset($datarray['verb']) && activity_match($datarray['verb'], ACTIVITY_DELETE) + if(isset($datarray['verb']) && activity_match($datarray['verb'], ['Delete', ACTIVITY_DELETE]) && isset($datarray['author_xchan']) && $datarray['author_xchan'] === $r[0]['author_xchan']) { if(! intval($r[0]['item_deleted'])) { logger('deleting item ' . $r[0]['id'] . ' mid=' . $datarray['mid'], LOGGER_DEBUG); @@ -1955,7 +1933,7 @@ function atom_entry($item, $type, $author, $owner, $comment = false, $cid = 0, $ $o .= '<thr:in-reply-to ref="' . xmlify($parent_item) . '" type="text/html" href="' . xmlify($item['plink']) . '" />' . "\r\n"; } - if(activity_match($item['obj_type'],ACTIVITY_OBJ_EVENT) && activity_match($item['verb'],ACTIVITY_POST)) { + if((activity_match($item['obj_type'], ACTIVITY_OBJ_EVENT) || activity_match($item['obj_type'], 'Event')) && activity_match($item['verb'],['Create', ACTIVITY_POST])) { $obj = ((is_array($item['obj'])) ? $item['obj'] : json_decode($item['obj'],true)); $o .= '<title>' . xmlify($item['title']) . '</title>' . "\r\n"; diff --git a/include/html2bbcode.php b/include/html2bbcode.php index c928b91b1..03e09cd62 100644 --- a/include/html2bbcode.php +++ b/include/html2bbcode.php @@ -10,6 +10,10 @@ Originally made for the syncom project: http://wiki.piratenpartei.de/Syncom function node2bbcode(&$doc, $oldnode, $attributes, $startbb, $endbb) { do { + if (empty($startbb) && empty($endbb)) { + break; + } + $done = node2bbcodesub($doc, $oldnode, $attributes, $startbb, $endbb); } while ($done); } @@ -65,6 +69,23 @@ function node2bbcodesub(&$doc, $oldnode, $attributes, $startbb, $endbb) if ($oldNode->hasChildNodes()) { foreach ($oldNode->childNodes as $child) { $newNode = $child->cloneNode(true); + + // Newlines are insignificant in HTML, but not so in BBCode, so let's + // unwrap the child nodes of when converting them. Also we compress + // consecutive whitespace chars to one. + // + // The exception is `<pre>` and `<code>` elements which + // should keep both newlines and whitespace intact. + if ($oldNode->nodeName != 'pre' && $oldNode->nodeName != 'code') { + + $newNode->nodeValue = str_replace( + array("\n<", ">\n", "\r", "\n", "\xC3\x82\xC2\xA0"), + array("<", ">", "<br />", " ", ""), + htmlspecialchars($newNode->nodeValue, ENT_COMPAT, 'UTF-8', false)); + + $newNode->nodeValue = preg_replace('=[\s]{2,}=i', " ", htmlspecialchars($newNode->nodeValue, ENT_COMPAT, 'UTF-8', false)); + } + $oldNode->parentNode->insertBefore($newNode, $oldNode); } } @@ -104,7 +125,11 @@ function html2bbcode($message) $message = preg_replace('=<(\w+):(.+?)>=', '<removeme>', $message); $message = preg_replace('=</(\w+):(.+?)>=', '</removeme>', $message); - $message = mb_convert_encoding($message, 'HTML-ENTITIES', "UTF-8"); + // mb_convert_encoding() is deprecated + //$message = mb_convert_encoding($message, 'HTML-ENTITIES', "UTF-8"); + $message = mb_encode_numericentity($message, [0x80, 0x10FFFF, 0, ~0], 'UTF-8'); + + if(!$message) return; @@ -121,16 +146,6 @@ function html2bbcode($message) deletenode($doc, 'xml'); deletenode($doc, 'removeme'); - $xpath = new DomXPath($doc); - $list = $xpath->query("//pre"); - foreach ($list as $node) - $node->nodeValue = str_replace("\n", "\r", $node->nodeValue); - - $message = $doc->saveHTML(); - $message = str_replace(array("\n<", ">\n", "\r", "\n", "\xC3\x82\xC2\xA0"), array("<", ">", "<br />", " ", ""), $message); - $message = preg_replace('= [\s]*=i', " ", $message); - @$doc->loadHTML($message); - node2bbcode($doc, 'html', array(), "", ""); node2bbcode($doc, 'body', array(), "", ""); @@ -172,23 +187,24 @@ function html2bbcode($message) node2bbcode($doc, 'u', array(), '[u]', '[/u]'); node2bbcode($doc, 's', array(), '[s]', '[/s]'); node2bbcode($doc, 'mark', array(), '[mark]', '[/mark]'); + node2bbcode($doc, 'span', array(), "", ""); node2bbcode($doc, 'big', array(), "[size=large]", "[/size]"); node2bbcode($doc, 'small', array(), "[size=small]", "[/size]"); - node2bbcode($doc, 'blockquote', array(), '[quote]', '[/quote]'); + // Use a temporary tag to keep line breaks + node2bbcode($doc, 'br', array(), '[br]', ''); - node2bbcode($doc, 'br', array(), "\n", ''); + node2bbcode($doc, 'a', array('href'=>'/(.+)/'), '[url=$1]', '[/url]'); - node2bbcode($doc, 'p', array('class'=>'MsoNormal'), "\n", ""); - node2bbcode($doc, 'div', array('class'=>'MsoNormal'), "\r", ""); + node2bbcode($doc, 'img', array('src'=>'/(.+)/', 'width'=>'/(\d+)/', 'height'=>'/(\d+)/'), '[img=$2x$3]$1', '[/img]'); + node2bbcode($doc, 'img', array('src'=>'/(.+)/', 'alt'=>'/(.+)/'), '[img=$1]$2', '[/img]'); + node2bbcode($doc, 'img', array('src'=>'/(.+)/'), '[img]$1', '[/img]'); - node2bbcode($doc, 'span', array(), "", ""); + node2bbcode($doc, 'video', array('src'=>'/(.+)/'), '[video]$1', '[/video]'); + node2bbcode($doc, 'audio', array('src'=>'/(.+)/'), '[audio]$1', '[/audio]'); +// node2bbcode($doc, 'iframe', array('src'=>'/(.+)/'), '[iframe]$1', '[/iframe]'); - node2bbcode($doc, 'span', array(), "", ""); - node2bbcode($doc, 'pre', array(), "", ""); - node2bbcode($doc, 'div', array(), "\r", "\r"); - node2bbcode($doc, 'p', array(), "\n", "\n"); node2bbcode($doc, 'ul', array(), "[list]", "[/list]"); node2bbcode($doc, 'ol', array(), "[list=1]", "[/list]"); @@ -205,24 +221,25 @@ function html2bbcode($message) node2bbcode($doc, 'tr', array(), "[tr]", "[/tr]"); node2bbcode($doc, 'td', array(), "[td]", "[/td]"); - node2bbcode($doc, 'h1', array(), "\n\n[h1]", "[/h1]\n"); - node2bbcode($doc, 'h2', array(), "\n\n[h2]", "[/h2]\n"); - node2bbcode($doc, 'h3', array(), "\n\n[h3]", "[/h3]\n"); - node2bbcode($doc, 'h4', array(), "\n\n[h4]", "[/h4]\n"); - node2bbcode($doc, 'h5', array(), "\n\n[h5]", "[/h5]\n"); - node2bbcode($doc, 'h6', array(), "\n\n[h6]", "[/h6]\n"); + node2bbcode($doc, 'h1', array(), "[h1]", "[/h1]"); + node2bbcode($doc, 'h2', array(), "[h2]", "[/h2]"); + node2bbcode($doc, 'h3', array(), "[h3]", "[/h3]"); + node2bbcode($doc, 'h4', array(), "[h4]", "[/h4]"); + node2bbcode($doc, 'h5', array(), "[h5]", "[/h5]"); + node2bbcode($doc, 'h6', array(), "[h6]", "[/h6]"); - node2bbcode($doc, 'a', array('href'=>'/(.+)/'), '[url=$1]', '[/url]'); - node2bbcode($doc, 'img', array('src'=>'/(.+)/', 'width'=>'/(\d+)/', 'height'=>'/(\d+)/'), '[img=$2x$3]$1', '[/img]'); - node2bbcode($doc, 'img', array('src'=>'/(.+)/'), '[img]$1', '[/img]'); + node2bbcode($doc, 'blockquote', array(), '[quote]', '[/quote]'); + node2bbcode($doc, 'pre', array(), "", ""); + node2bbcode($doc, 'code', array('class'=>'/(.+)/'), '[code=$1]', '[/code]'); + node2bbcode($doc, 'code', array(), '[code]', '[/code]'); - node2bbcode($doc, 'video', array('src'=>'/(.+)/'), '[video]$1', '[/video]'); - node2bbcode($doc, 'audio', array('src'=>'/(.+)/'), '[audio]$1', '[/audio]'); -// node2bbcode($doc, 'iframe', array('src'=>'/(.+)/'), '[iframe]$1', '[/iframe]'); + node2bbcode($doc, 'p', array('class'=>'MsoNormal'), "\n", ""); + node2bbcode($doc, 'p', array(), "\n", "\n"); - node2bbcode($doc, 'code', array(), '[code]', '[/code]'); + node2bbcode($doc, 'div', array('class'=>'MsoNormal'), "\r", ""); + node2bbcode($doc, 'div', array(), "\r", "\r"); $message = $doc->saveHTML(); @@ -263,6 +280,8 @@ function html2bbcode($message) $oldmessage = $message; $message = str_replace(array( "[/size]\n\n", + "\n[br]", + "[br]\n", "\n[hr]", "[hr]\n", "\n[list", @@ -273,6 +292,8 @@ function html2bbcode($message) "\n[*]"), array( "[/size]\n", + "[br]", + "[br]", "[hr]", "[hr]", "[list", @@ -287,6 +308,9 @@ function html2bbcode($message) $message = str_replace(array('[b][b]', '[/b][/b]', '[i][i]', '[/i][/i]'), array('[b]', '[/b]', '[i]', '[/i]'), $message); + // Restore linebreaks from temp tag + $message = preg_replace('/\[br\]\s?/', "\n", $message); + // Handling Yahoo style of mails // $message = str_replace('[hr][b]From:[/b]', '[quote][b]From:[/b]', $message); diff --git a/include/html2plain.php b/include/html2plain.php index 48bbe3d9e..5cb7ee35d 100644 --- a/include/html2plain.php +++ b/include/html2plain.php @@ -121,7 +121,10 @@ function html2plain($html, $wraplength = 75, $compact = false) { $message = str_replace("\r", "", $html); - $message = mb_convert_encoding($message, 'HTML-ENTITIES', "UTF-8"); + + // mb_convert_encoding() is deprecated + //$message = mb_convert_encoding($message, 'HTML-ENTITIES', "UTF-8"); + $message = mb_encode_numericentity($message, [0x80, 0x10FFFF, 0, ~0], 'UTF-8'); if(!$message) return; diff --git a/include/import.php b/include/import.php index 291dd2638..7dac518f5 100644 --- a/include/import.php +++ b/include/import.php @@ -80,6 +80,12 @@ function import_channel($channel, $account_id, $seize, $newname = '') { } } + if (empty($channel['channel_epubkey']) && empty($channel['channel_eprvkey'])) { + $eckey = sodium_crypto_sign_keypair(); + $channel['channel_epubkey'] = sodium_bin2base64(sodium_crypto_sign_publickey($eckey), SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING); + $channel['channel_eprvkey'] = sodium_bin2base64(sodium_crypto_sign_secretkey($eckey), SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING); + } + unset($channel['channel_id']); $channel['channel_account_id'] = $account_id; $channel['channel_primary'] = (($seize) ? 1 : 0); diff --git a/include/items.php b/include/items.php index 6cef3d2cd..a5a23650e 100644 --- a/include/items.php +++ b/include/items.php @@ -353,7 +353,7 @@ function can_comment_on_post($observer_xchan, $item) { case 'specific': case 'contacts': case '': - if(local_channel() && get_abconfig(local_channel(), (($item['verb'] === ACTIVITY_SHARE) ? $item['author_xchan'] : $item['owner_xchan']), 'their_perms', 'post_comments')) { + if(local_channel() && get_abconfig(local_channel(), (($item['verb'] === ACTIVITY_SHARE) ? $item['source_xchan'] : $item['owner_xchan']), 'their_perms', 'post_comments')) { return true; } if(intval($item['item_wall']) && perm_is_allowed($item['uid'],$observer_xchan,'post_comments')) { @@ -459,7 +459,7 @@ function post_activity_item($arr, $allow_code = false, $deliver = true) { if(! $arr['mid']) { - $arr['uuid'] = ((x($arr,'uuid')) ? $arr['uuid'] : item_message_id()); + $arr['uuid'] = ((x($arr,'uuid')) ? $arr['uuid'] : new_uuid()); } $arr['mid'] = ((x($arr,'mid')) ? $arr['mid'] : z_root() . '/item/' . $arr['uuid']); $arr['parent_mid'] = ((x($arr,'parent_mid')) ? $arr['parent_mid'] : $arr['mid']); @@ -468,10 +468,8 @@ function post_activity_item($arr, $allow_code = false, $deliver = true) { $arr['owner_xchan'] = ((x($arr,'owner_xchan')) ? $arr['owner_xchan'] : $channel['channel_hash']); $arr['author_xchan'] = ((x($arr,'author_xchan')) ? $arr['author_xchan'] : $observer['xchan_hash']); - $arr['verb'] = ((x($arr,'verb')) ? $arr['verb'] : ACTIVITY_POST); - $arr['obj_type'] = ((x($arr,'obj_type')) ? $arr['obj_type'] : ACTIVITY_OBJ_NOTE); - if(($is_comment) && ($arr['obj_type'] === ACTIVITY_OBJ_NOTE)) - $arr['obj_type'] = ACTIVITY_OBJ_COMMENT; + $arr['verb'] = ((x($arr,'verb')) ? $arr['verb'] : 'Create'); + $arr['obj_type'] = ((x($arr,'obj_type')) ? $arr['obj_type'] : 'Note'); if(! ( array_key_exists('allow_cid',$arr) || array_key_exists('allow_gid',$arr) || array_key_exists('deny_cid',$arr) || array_key_exists('deny_gid',$arr))) { @@ -522,7 +520,7 @@ function post_activity_item($arr, $allow_code = false, $deliver = true) { return $ret; if($post_id && $deliver) { - Master::Summon([ 'Notifier','activity',$post_id ]); + Master::Summon(['Notifier','activity', $post_id]); } $ret['success'] = true; @@ -1751,8 +1749,8 @@ function item_store($arr, $allow_exec = false, $deliver = true) { $arr['coord'] = ((x($arr,'coord')) ? notags(trim($arr['coord'])) : ''); $arr['parent_mid'] = ((x($arr,'parent_mid')) ? notags(trim($arr['parent_mid'])) : ''); $arr['thr_parent'] = ((x($arr,'thr_parent')) ? notags(trim($arr['thr_parent'])) : $arr['parent_mid']); - $arr['verb'] = ((x($arr,'verb')) ? notags(trim($arr['verb'])) : ACTIVITY_POST); - $arr['obj_type'] = ((x($arr,'obj_type')) ? notags(trim($arr['obj_type'])) : ACTIVITY_OBJ_NOTE); + $arr['verb'] = ((x($arr,'verb')) ? notags(trim($arr['verb'])) : 'Create'); + $arr['obj_type'] = ((x($arr,'obj_type')) ? notags(trim($arr['obj_type'])) : 'Note'); $arr['obj'] = ((x($arr,'obj')) ? trim($arr['obj']) : ''); $arr['tgt_type'] = ((x($arr,'tgt_type')) ? notags(trim($arr['tgt_type'])) : ''); $arr['target'] = ((x($arr,'target')) ? trim($arr['target']) : ''); @@ -1775,13 +1773,18 @@ function item_store($arr, $allow_exec = false, $deliver = true) { $d1 = new DateTime('now +10 minutes', new DateTimeZone('UTC')); $d2 = new DateTime($arr['created'] . '+00:00'); - if($d2 > $d1) + + if($d2 > $d1) { $arr['item_delayed'] = 1; + } - $arr['llink'] = z_root() . '/display/' . gen_link_id($arr['mid']); + if(empty($arr['llink'])) { + $arr['llink'] = z_root() . '/display/' . $arr['uuid']; + } - if(! $arr['plink']) + if(empty($arr['plink'])) { $arr['plink'] = $arr['llink']; + } if($arr['parent_mid'] === $arr['mid']) { $parent_id = 0; @@ -1822,9 +1825,6 @@ function item_store($arr, $allow_exec = false, $deliver = true) { return $ret; } - if(($arr['obj_type'] == ACTIVITY_OBJ_NOTE) && (! $arr['obj'])) - $arr['obj_type'] = ACTIVITY_OBJ_COMMENT; - // is the new message multi-level threaded? // even though we don't support it now, preserve the info // and re-attach to the conversation parent. @@ -1837,7 +1837,7 @@ function item_store($arr, $allow_exec = false, $deliver = true) { dbesc($r[0]['parent_mid']), intval($arr['uid']) ); - if($z && count($z)) + if($z) $r = $z; } @@ -1999,14 +1999,15 @@ function item_store($arr, $allow_exec = false, $deliver = true) { if(($terms) && (is_array($terms))) { foreach($terms as $t) { - q("insert into term (uid,oid,otype,ttype,term,url) - values(%d,%d,%d,%d,'%s','%s') ", + q("insert into term (uid,oid,otype,ttype,term,url,imgurl) + values(%d,%d,%d,%d,'%s','%s','%s') ", intval($arr['uid']), intval($current_post), intval(TERM_OBJ_POST), intval($t['ttype']), dbesc($t['term']), - dbesc($t['url']) + dbesc($t['url']), + dbesc($t['imgurl'] ?? ''), ); } @@ -2443,7 +2444,7 @@ function send_status_notifications($post_id,$item) { $type = ((intval($item['item_private']) === 2) ? NOTIFY_MAIL : NOTIFY_COMMENT); - if(array_key_exists('verb',$item) && (activity_match($item['verb'], ACTIVITY_LIKE) || activity_match($item['verb'], ACTIVITY_DISLIKE))) { + if(array_key_exists('verb',$item) && activity_match($item['verb'], ['Like', 'Dislike', ACTIVITY_LIKE, ACTIVITY_DISLIKE])) { $type = NOTIFY_LIKE; @@ -2487,8 +2488,8 @@ function send_status_notifications($post_id,$item) { // check for an unfollow thread activity - we should probably decode the obj and check the id // but it will be extremely rare for this to be wrong. - if(($xx['verb'] === ACTIVITY_UNFOLLOW) - && ($xx['obj_type'] === ACTIVITY_OBJ_NOTE || $xx['obj_type'] === ACTIVITY_OBJ_PHOTO) + if((in_array($xx['verb'], ['Ignore', ACTIVITY_UNFOLLOW])) + && (in_array($xx['obj_type'], ['Note', 'Image', ACTIVITY_OBJ_NOTE, ACTIVITY_OBJ_PHOTO])) && ($xx['parent'] != $xx['id'])) $unfollowed = true; } @@ -2501,7 +2502,7 @@ function send_status_notifications($post_id,$item) { if($unfollowed) return; - $link = z_root() . '/display/' . gen_link_id($item['mid']); + $link = z_root() . '/display/' . $item['uuid']; $y = q("select id from notify where link = '%s' and uid = %d limit 1", dbesc($link), @@ -2514,7 +2515,6 @@ function send_status_notifications($post_id,$item) { if(! $notify) return; - Enotify::submit(array( 'type' => $type, 'from_xchan' => $item['author_xchan'], @@ -2607,7 +2607,6 @@ function tag_deliver($uid, $item_id) { return; } - if ($is_group && intval($item['item_thread_top']) && intval($item['item_wall']) && $item['author_xchan'] !== $item['owner_xchan']) { if($item['resource_type'] === 'group_item') { @@ -2625,39 +2624,6 @@ function tag_deliver($uid, $item_id) { } /* - * Seems like a good place to plug in a poke notification. - */ - - if (stristr($item['verb'],ACTIVITY_POKE)) { - $poke_notify = true; - - if(($item['obj_type'] == "") || ($item['obj_type'] !== ACTIVITY_OBJ_PERSON) || (! $item['obj'])) - $poke_notify = false; - - $obj = json_decode($item['obj'],true); - if($obj) { - if($obj['id'] !== $u[0]['channel_hash']) - $poke_notify = false; - } - if(intval($item['item_deleted'])) - $poke_notify = false; - - $verb = urldecode(substr($item['verb'],strpos($item['verb'],'#')+1)); - if($poke_notify) { - Enotify::submit(array( - 'to_xchan' => $u[0]['channel_hash'], - 'from_xchan' => $item['author_xchan'], - 'type' => NOTIFY_POKE, - 'item' => $item, - 'link' => $i[0]['llink'], - 'verb' => ACTIVITY_POKE, - 'activity' => $verb, - 'otype' => 'item' - )); - } - } - - /* * Do community tagging */ @@ -3174,7 +3140,7 @@ function start_delivery_chain($channel, $item, $item_id, $parent, $group = false $item['mid'] = z_root() . '/item/' . $item['uuid']; $item['parent_mid'] = $item['mid']; $item['thr_parent'] = $item['mid']; - $item['llink'] = z_root() . '/display/' . gen_link_id($item['mid']); + $item['llink'] = z_root() . '/display/' . $item['uuid']; } $r = q("UPDATE item SET author_xchan = '%s', mid = '%s', parent_mid = '%s', thr_parent = '%s', llink = '%s' WHERE id = %d", @@ -3297,7 +3263,7 @@ function start_delivery_chain($channel, $item, $item_id, $parent, $group = false $arr['owner_xchan'] = $channel['channel_hash']; $arr['obj_type'] = $item['obj_type']; - $arr['verb'] = ACTIVITY_POST; + $arr['verb'] = 'Create'; $arr['allow_cid'] = $channel['channel_allow_cid']; $arr['allow_gid'] = $channel['channel_allow_gid']; @@ -4411,7 +4377,7 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C $item_normal = item_normal(); if (! (isset($arr['include_follow']) && intval($arr['include_follow']))) { - $item_normal .= sprintf(" and not verb in ('%s', '%s') ", + $item_normal .= sprintf(" and not verb in ('Follow', 'Ignore', '%s', '%s') ", dbesc(ACTIVITY_FOLLOW), dbesc(ACTIVITY_UNFOLLOW) ); @@ -4824,54 +4790,7 @@ function comment_local_origin($item) { return false; } - - -function send_profile_photo_activity($channel,$photo,$profile) { - - // for now only create activities for the default profile - - if(! intval($profile['is_default'])) - return; - - $arr = array(); - $arr['item_thread_top'] = 1; - $arr['item_origin'] = 1; - $arr['item_wall'] = 1; - - if(stripos($profile['gender'],t('female')) !== false) - $t = t('%1$s updated her %2$s'); - elseif(stripos($profile['gender'],t('male')) !== false) - $t = t('%1$s updated his %2$s'); - else - $t = t('%1$s updated their %2$s'); - - $ptext = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $photo['resource_id'] . ']' . t('profile photo') . '[/zrl]'; - - $ltext = '[zrl=' . z_root() . '/profile/' . $channel['channel_address'] . ']' . '[zmg=150x150]' . z_root() . '/photo/' . $photo['resource_id'] . '-4[/zmg][/zrl]'; - - $arr['body'] = sprintf($t,$channel['channel_name'],$ptext) . "\n\n" . $ltext; - - $acl = new Zotlabs\Access\AccessList($channel); - $x = $acl->get(); - - $arr['allow_cid'] = $x['allow_cid']; - - $arr['allow_gid'] = $x['allow_gid']; - $arr['deny_cid'] = $x['deny_cid']; - $arr['deny_gid'] = $x['deny_gid']; - - $arr['uid'] = $channel['channel_id']; - $arr['aid'] = $channel['channel_account_id']; - - $arr['owner_xchan'] = $channel['channel_hash']; - $arr['author_xchan'] = $channel['channel_hash']; - - post_activity_item($arr); -} - - function sync_an_item($channel_id,$item_id) { - $r = q("select * from item where id = %d", intval($item_id) ); @@ -5151,83 +5070,13 @@ function fix_attached_permissions($uid, $body, $str_contact_allow, $str_group_al } } -function item_create_edit_activity($post) { - - if((! $post) || (! $post['item']) || ($post['item']['item_type'] != ITEM_TYPE_POST)) - return; - - $update_item = $post['item']; - - $new_item = $update_item; - - $author = q("select * from xchan where xchan_hash = '%s' limit 1", - dbesc($new_item['author_xchan']) - ); - if($author) - $item_author = $author[0]; - - - $new_item['id'] = 0; - $new_item['parent'] = 0; - $new_item['uuid'] = item_message_id(); - $new_item['mid'] = z_root() . '/item/' . $new_item['uuid']; - - $new_item['body'] = sprintf( t('[Edited %s]'), (($update_item['item_thread_top']) ? t('Post','edit_activity') : t('Comment','edit_activity'))); - - $new_item['body'] .= "\n\n"; - $new_item['body'] .= $update_item['body']; - - $new_item['sig'] = ''; - - $new_item['verb'] = ACTIVITY_UPDATE; - $new_item['item_thread_top'] = 0; - $new_item['created'] = $new_item['edited'] = datetime_convert(); - $new_item['obj_type'] = (($update_item['item_thread_top']) ? ACTIVITY_OBJ_NOTE : ACTIVITY_OBJ_COMMENT); - $new_item['obj'] = json_encode(array( - 'type' => $new_item['obj_type'], - 'id' => $update_item['mid'], - 'parent' => $update_item['parent_mid'], - 'link' => array(array('rel' => 'alternate','type' => 'text/html', 'href' => $update_item['plink'])), - 'title' => $update_item['title'], - 'content' => $update_item['body'], - 'created' => $update_item['created'], - 'edited' => $update_item['edited'], - 'author' => array( - 'name' => $item_author['xchan_name'], - 'address' => $item_author['xchan_addr'], - 'guid' => $item_author['xchan_guid'], - 'guid_sig' => $item_author['xchan_guid_sig'], - 'link' => array( - array('rel' => 'alternate', 'type' => 'text/html', 'href' => $item_author['xchan_url']), - array('rel' => 'photo', 'type' => $item_author['xchan_photo_mimetype'], 'href' => $item_author['xchan_photo_m'])), - ), - )); - - - $x = post_activity_item($new_item); - $post_id = $x['id']; - if($post_id) { - $r = q("select * from item where id = %d", - intval($post_id) - ); - if($r) { - xchan_query($r); - $sync_item = fetch_post_tags($r); - Libsync::build_sync_packet($new_item['uid'],array('item' => array(encode_item($sync_item[0],true)))); - } - } - - Master::Summon([ 'Notifier', 'edit_activity', $post_id ]); -} /** * @brief copies an entire conversation from the pubstream to this channel's stream * which will allow you to interact with it. */ - - function copy_of_pubitem($channel,$mid) { $result = null; diff --git a/include/markdown.php b/include/markdown.php index 7fba1259f..b2adcd0d5 100644 --- a/include/markdown.php +++ b/include/markdown.php @@ -221,6 +221,21 @@ function bb_to_markdown_transform_tags($match) { return '#'. str_replace(' ', '_', $match[3]); } +function bb_to_markdown_parse_b64_crypt($match) { + + if(empty($match[1])) { + return; + } + + $r = '```' . "\n"; + $r .= '-----BEGIN ENCRYPTED MESSAGE-----' . "\n"; + $r .= $match[1] . "\n"; + $r .= '-----END ENCRYPTED MESSAGE-----' . "\n"; + $r .= '```' . "\n"; + + return wordwrap($r, 75, "\n", true); + +} /** * @brief Convert bbcode to Markdown. @@ -244,6 +259,10 @@ function bb_to_markdown($Text, $options = []) { $Text = preg_replace_callback("/\[share(.*?)\](.*?)\[\/share\]/ism", 'bb_to_markdown_share', $Text); + if (str_contains($Text, '[/crypt]')) { + $Text = preg_replace_callback("/\[crypt\](.*?)\[\/crypt\]/ism", 'bb_to_markdown_parse_b64_crypt', $Text); + } + $x = [ 'bbcode' => $Text, 'options' => $options ]; /** @@ -264,8 +283,8 @@ function bb_to_markdown($Text, $options = []) { // Now convert HTML to Markdown $Text = html2markdown($Text); - //html2markdown adds backslashes infront of hashes after a new line. remove them - $Text = str_replace("\n\#", "\n#", $Text); + //html2markdown adds backslashes infront of hashes and dashes after a new line. remove them + $Text = str_replace(["\n\#", "\n\-----"], ["\n#", "\n-----"], $Text); // If the text going into bbcode() has a plain URL in it, i.e. // with no [url] tags around it, it will come out of parseString() diff --git a/include/network.php b/include/network.php index dee11e3e9..a5c14f9d1 100644 --- a/include/network.php +++ b/include/network.php @@ -1,6 +1,6 @@ <?php -use Zotlabs\Lib\LDSignatures; +use Zotlabs\Lib\Activity; use Zotlabs\Lib\Zotfinger; use Zotlabs\Lib\Libzot; use Zotlabs\Lib\Queue; @@ -423,25 +423,20 @@ function json_return_and_die($x, $content_type = 'application/json') { killme(); } -function as_return_and_die($obj,$channel) { +function as_return_and_die($obj, $channel = []) { - $x = array_merge(['@context' => [ - ACTIVITYSTREAMS_JSONLD_REV, - 'https://w3id.org/security/v1', - z_root() . ZOT_APSCHEMA_REV - ]], $obj ); + $ret = Activity::build_packet($obj, $channel); + logger('data: ' . jindent($ret), LOGGER_DATA); - $headers = []; $headers['Content-Type'] = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' ; - $x['signature'] = LDSignatures::sign($x,$channel); - $ret = json_encode($x, JSON_UNESCAPED_SLASHES); - logger('data: ' . jindent($ret), LOGGER_DATA); $headers['Date'] = datetime_convert('UTC','UTC', 'now', 'D, d M Y H:i:s \\G\\M\\T'); $headers['Digest'] = HTTPSig::generate_digest_header($ret); $headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI']; - $h = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel)); - HTTPSig::set_headers($h); + if ($channel) { + $h = HTTPSig::create_sig($headers, $channel['channel_prvkey'], channel_url($channel)); + HTTPSig::set_headers($h); + } echo $ret; killme(); @@ -598,23 +593,30 @@ function validate_url(&$url) { } /** - * @brief Checks that email is an actual resolvable internet address. + * @brief Checks that email is valid, and that the domain resolves. * - * @param string $addr - * @return boolean + * Note: This does not try to check that the actual email address will resolve, + * only the domain! + * + * @param string $addr The email address to validate. + * @return boolean True if email is valid, false otherwise. */ -function validate_email($addr) { +function validate_email(string $addr): bool { if(get_config('system', 'disable_email_validation')) return true; - if(! strpos($addr, '@')) - return false; - - $h = substr($addr, strpos($addr, '@') + 1); + $matches = array(); + $result = preg_match( + '/^[A-Z0-9._%-]+@([A-Z0-9.-]+\.[A-Z0-9-]{2,})$/i', + punify($addr), + $matches); - if(($h) && z_dns_check($h, true)) { - return true; + if($result) { + $domain = $matches[1]; + if(($domain) && z_dns_check($domain, true)) { + return true; + } } return false; @@ -2138,12 +2140,13 @@ function get_request_string($url) { } -/* +/** + * Builds a url from the result of `parse_url`. * - * Takes the output of parse_url and builds a URL from it + * @param array $parsed_url An associative array as produced by `parse_url`. * + * @return The reassembled URL as a string. */ - function unparse_url($parsed_url) { $scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : ''; $host = isset($parsed_url['host']) ? $parsed_url['host'] : ''; diff --git a/include/oembed.php b/include/oembed.php index fbe6a573b..a90c91641 100644 --- a/include/oembed.php +++ b/include/oembed.php @@ -395,7 +395,11 @@ function oembed_format_object($j){ $ret .= "<br /><a href='$embedurl' rel='oembed'>$embedurl</a>"; } $ret.="<br style='clear:left'></span>"; - return mb_convert_encoding($ret, 'HTML-ENTITIES', mb_detect_encoding($ret)); + + // mb_convert_encoding() is deprecated + // return mb_convert_encoding($ret, 'HTML-ENTITIES', mb_detect_encoding($ret)); + return mb_encode_numericentity($ret, [0x80, 0x10FFFF, 0, ~0], 'UTF-8'); + } function oembed_iframe($src,$width,$height) { @@ -456,7 +460,10 @@ function oembed_html2bbcode($text) { if (strpos($text, "oembed")){ // convert non ascii chars to html entities - $html_text = mb_convert_encoding($text, 'HTML-ENTITIES', mb_detect_encoding($text)); + + // mb_convert_encoding() is deprecated + // $html_text = mb_convert_encoding($text, 'HTML-ENTITIES', mb_detect_encoding($text)); + $html_text = mb_encode_numericentity($text, [0x80, 0x10FFFF, 0, ~0], 'UTF-8'); // If it doesn't parse at all, just return the text. diff --git a/include/photo/photo_driver.php b/include/photo/photo_driver.php index 522e638de..4394d3238 100644 --- a/include/photo/photo_driver.php +++ b/include/photo/photo_driver.php @@ -117,7 +117,14 @@ function guess_image_type($filename, $data = '') { $body = $data['body']; if ($body) { $image = new Imagick(); - $image->readImageBlob($body); + + try{ + $image->readImageBlob($body); + } catch (\Exception $e) { + logger('Imagick readImageBlob() exception:' . print_r($e, true)); + return $type; + } + $r = $image->identifyImage(); if ($r && is_array($r) && array_key_exists($r['mimetype'], $types)) $type = $r['mimetype']; diff --git a/include/photos.php b/include/photos.php index 8d83b8475..5e993e15f 100644 --- a/include/photos.php +++ b/include/photos.php @@ -455,7 +455,7 @@ function photo_upload($channel, $observer, $args) { $item['body'] = $summary; $item['mimetype'] = 'text/bbcode'; - $item['obj_type'] = ACTIVITY_OBJ_PHOTO; + $item['obj_type'] = 'Image'; $object['id'] = $item['mid']; $object['diaspora:guid'] = $item['uuid']; @@ -511,8 +511,8 @@ function photo_upload($channel, $observer, $args) { 'allow_gid' => $ac['allow_gid'], 'deny_cid' => $ac['deny_cid'], 'deny_gid' => $ac['deny_gid'], - 'verb' => ACTIVITY_POST, - 'obj_type' => ACTIVITY_OBJ_PHOTO, + 'verb' => 'Create', + 'obj_type' => 'Image', 'obj' => json_encode($object), 'tgt_type' => 'orderedCollection', 'target' => json_encode($target), diff --git a/include/plugin.php b/include/plugin.php index 2a35b72de..bbfeab988 100644 --- a/include/plugin.php +++ b/include/plugin.php @@ -213,7 +213,7 @@ function reload_plugins() { if(function_exists($pl . '_unload')) { $func = $pl . '_unload'; try { - $func(); + $func(); } catch (Exception $e) { handleerrors_plugin($pl, '', 'UNLOAD FAILED (uninstalling) : ' . $e->getMessage(),true); continue; @@ -222,7 +222,7 @@ function reload_plugins() { if(function_exists($pl . '_load')) { $func = $pl . '_load'; try { - $func(); + $func(); } catch (Exception $e) { handleerrors_plugin($pl, '', 'LOAD FAILED (uninstalling): ' . $e->getMessage(),true); continue; @@ -556,7 +556,11 @@ function get_plugin_info($plugin){ $ll = explode("\n", $m[0]); foreach( $ll as $l ) { $l = trim($l, "\t\n\r */"); - if ($l != ""){ + if ($l != "") { + if (strpos($l, ':') === false) { + continue; + } + list($k, $v) = array_map("trim", explode(":", $l, 2)); $k = strtolower($k); if ($k == 'author' || $k == 'maintainer'){ @@ -643,7 +647,11 @@ function get_widget_info($widget){ $ll = explode("\n", $m[0]); foreach( $ll as $l ) { $l = trim($l, "\t\n\r */"); - if ($l != ""){ + if ($l != "") { + if (strpos($l, ':') === false) { + continue; + } + list($k, $v) = array_map("trim", explode(":", $l, 2)); $k = strtolower($k); if ($k == 'author' || $k == 'maintainer'){ @@ -778,7 +786,11 @@ function get_theme_info($theme){ $ll = explode("\n", $m[0]); foreach( $ll as $l ) { $l = trim($l, "\t\n\r */"); - if ($l != ""){ + if ($l != "") { + if (strpos($l, ':') === false) { + continue; + } + list($k, $v) = array_map("trim", explode(":", $l, 2)); $k = strtolower($k); if ($k == 'author'){ @@ -860,7 +872,7 @@ function get_template_info($template){ $ll = explode("\n", $m[0]); foreach( $ll as $l ) { $l = trim($l, "\t\n\r */"); - if ($l != ""){ + if ($l != "") { if (strpos($l, ':') === false) { continue; } @@ -973,12 +985,12 @@ function format_css_if_exists($source) { } } else { // It's a file from the theme - $path = '/' . theme_include($script); + $path = theme_include($script); } if($path) { $qstring = ((parse_url($path, PHP_URL_QUERY)) ? '&' : '?') . 'v=' . STD_VERSION; - return '<link rel="stylesheet" href="' . $path_prefix . $path . $qstring . '" type="text/css" media="' . $source[1] . '">' . "\r\n"; + return '<link rel="stylesheet" href="' . $path_prefix . '/' . $path . $qstring . '" type="text/css" media="' . $source[1] . '">' . "\r\n"; } } @@ -1045,11 +1057,11 @@ function format_js_if_exists($source) { } else { // It's a file from the theme - $path = '/' . theme_include($source); + $path = theme_include($source); } if($path) { $qstring = ((parse_url($path, PHP_URL_QUERY)) ? '&' : '?') . 'v=' . STD_VERSION; - return '<script src="' . $path_prefix . $path . $qstring . '" ></script>' . "\r\n" ; + return '<script src="' . $path_prefix . '/' . $path . $qstring . '" ></script>' . "\r\n" ; } } diff --git a/include/sharedwithme.php b/include/sharedwithme.php deleted file mode 100644 index b342f51d5..000000000 --- a/include/sharedwithme.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php - -function apply_updates() { - - //check for updated items and remove them - $x = q("SELECT mid, max(obj) AS obj FROM item WHERE verb = '%s' AND obj_type = '%s' GROUP BY mid", - dbesc(ACTIVITY_UPDATE), - dbesc(ACTIVITY_OBJ_FILE) - ); - - if($x) { - - foreach($x as $xx) { - - $object = json_decode($xx['obj'],true); - - $d_mid = $object['d_mid']; - $u_mid = $xx['mid']; - - $y = q("DELETE FROM item WHERE obj_type = '%s' AND (verb = '%s' AND mid = '%s') OR (verb = '%s' AND mid = '%s')", - dbesc(ACTIVITY_OBJ_FILE), - dbesc(ACTIVITY_POST), - dbesc($d_mid), - dbesc(ACTIVITY_UPDATE), - dbesc($u_mid) - ); - - } - - } - -} diff --git a/include/taxonomy.php b/include/taxonomy.php index cfec8414a..90ccb6142 100644 --- a/include/taxonomy.php +++ b/include/taxonomy.php @@ -58,7 +58,7 @@ function term_item_parent_query($uid,$table,$s,$type = TERM_UNKNOWN, $type2 = '' $s = str_replace('*','%',$s); if($type2) { - $r = q("select parent from item left join term on term.oid = item.id where term.ttype in (%d, %d) and term.term like '%s' and term.uid = %d and term.otype = 1 and item.verb != '%s'", + $r = q("select parent from item left join term on term.oid = item.id where term.ttype in (%d, %d) and term.term like '%s' and term.uid = %d and term.otype = 1 and item.verb NOT IN ('Update', '%s')", intval($type), intval($type2), dbesc($s), @@ -67,7 +67,7 @@ function term_item_parent_query($uid,$table,$s,$type = TERM_UNKNOWN, $type2 = '' ); } else { - $r = q("select parent from item left join term on term.oid = item.id where term.ttype = %d and term.term like '%s' and term.uid = %d and term.otype = 1 and item.verb != '%s'", + $r = q("select parent from item left join term on term.oid = item.id where term.ttype = %d and term.term like '%s' and term.uid = %d and term.otype = 1 and item.verb NOT IN ('Update', '%s')", intval($type), dbesc($s), intval($uid), diff --git a/include/text.php b/include/text.php index fc30ed8aa..713911af2 100644 --- a/include/text.php +++ b/include/text.php @@ -13,6 +13,7 @@ use Zotlabs\Lib\Crypto; use Zotlabs\Lib\SvgSanitizer; use Zotlabs\Lib\Libzot; use Zotlabs\Lib\AccessList; +use Zotlabs\Lib\Text; require_once("include/bbcode.php"); @@ -108,10 +109,14 @@ function notags($string) { * @return string */ function escape_tags($string) { + return Text::escape_tags($string); +} + +function unescape_tags($string) { if (!$string) { return EMPTY_STR; } - return (htmlspecialchars($string, ENT_COMPAT, 'UTF-8', false)); + return (htmlspecialchars_decode($string, ENT_COMPAT)); } /** @@ -267,31 +272,41 @@ function purify_html($s, $allow_position = false) { //data- attributes used by the bootstrap library - $def->info_global_attr['data-dismiss'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-target'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-toggle'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-backdrop'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-keyboard'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-show'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-spy'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-offset'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-animation'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-container'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-delay'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-placement'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-title'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-trigger'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-content'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-trigger'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-parent'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-ride'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-slide-to'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-slide'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-interval'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-pause'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-wrap'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-offset-top'] = new HTMLPurifier_AttrDef_Text; - $def->info_global_attr['data-offset-bottom'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-bs-target'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-bs-ride'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-bs-slide'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-bs-toggle'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-bs-animation'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-bs-title'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-bs-delay'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-bs-parent'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-bs-offset'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-bs-dismiss'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-bs-backdrop'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-bs-keyboard'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-bs-content'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-bs-container'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-bs-trigger'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-bs-spy'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-bs-placement'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-bs-slide-to'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-bs-interval'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-bs-pause'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-bs-wrap'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-bs-display'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-bs-root-margin'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-bs-smooth-scroll'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-bs-dismiss'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-bs-delay'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-bs-autohide'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-bs-scroll'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-bs-reference'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-bs-config'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-bs-custom-class'] = new HTMLPurifier_AttrDef_Text; + //old + $def->info_global_attr['data-bs-show'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-bs-offset-top'] = new HTMLPurifier_AttrDef_Text; + $def->info_global_attr['data-bs-offset-bottom'] = new HTMLPurifier_AttrDef_Text; //some html5 elements //Block @@ -1253,76 +1268,32 @@ function sslify($s) { return $s; } -/** - * @brief Get an array of poke verbs. - * - * @return array - * * \e index is present tense verb - * * \e value is array containing past tense verb, translation of present, translation of past - */ -function get_poke_verbs() { - - $arr = [ - 'poke' => ['poked', t('poke'), t('poked')], - 'ping' => ['pinged', t('ping'), t('pinged')], - - // Those might be better suited for a nsfw poke addon - - // 'prod' => ['prodded', t('prod'), t('prodded')], - // 'slap' => ['slapped', t('slap'), t('slapped')], - // 'finger' => ['fingered', t('finger'), t('fingered')], - // 'rebuff' => ['rebuffed', t('rebuff'), t('rebuffed')] +function get_emojis() { + $emojis = [ + // Reactions (emojitwo emojis) + 'astonished_face' => ['shortname' => ':astonished_face:', 'filepath' => 'images/emoji/astonished_face.png'], + 'bottle_with_popping_cork' => ['shortname' => ':bottle_with_popping_cork:', 'filepath' => 'images/emoji/bottle_with_popping_cork.png'], + 'clapping_hands' => ['shortname' => ':clapping_hands:', 'filepath' => 'images/emoji/clapping_hands.png'], + 'disappointed_face' => ['shortname' => ':disappointed_face:', 'filepath' => 'images/emoji/disappointed_face.png'], + 'grinning_face' => ['shortname' => ':grinning_face:', 'filepath' => 'images/emoji/grinning_face.png'], + 'kiss_mark' => ['shortname' => ':kiss_mark:', 'filepath' => 'images/emoji/kiss_mark.png'], + 'red_heart' => ['shortname' => ':red_heart:', 'filepath' => 'images/emoji/red_heart.png'], + 'sleeping_face' => ['shortname' => ':sleeping_face:', 'filepath' => 'images/emoji/sleeping_face.png'], + 'slightly_smiling_face' => ['shortname' => ':slightly_smiling_face:', 'filepath' => 'images/emoji/slightly_smiling_face.png'], + 'smiling_face_with_halo' => ['shortname' => ':smiling_face_with_halo:', 'filepath' => 'images/emoji/smiling_face_with_halo.png'], + 'smiling_face_with_horns' => ['shortname' => ':smiling_face_with_horns:', 'filepath' => 'images/emoji/smiling_face_with_horns.png'], + 'winking_face_with_tongue' => ['shortname' => ':winking_face_with_tongue:', 'filepath' => 'images/emoji/winking_face_with_tongue.png'], + + 'facepalm' => ['shortname' => ':facepalm:', 'filepath' => 'images/emoticons/smiley-facepalm.gif'] ]; - /** - * @hooks poke_verbs - * * \e array associative array with another array as value - */ - call_hooks('poke_verbs', $arr); + call_hooks('get_emojis', $emojis); - return $arr; + return $emojis; } -/** - * @brief Get an array of mood verbs. - * - * @return array - * * \e index is the verb - * * \e value is the translated verb - */ -function get_mood_verbs() { - - $arr = [ - 'happy' => t('happy'), - 'sad' => t('sad'), - 'mellow' => t('mellow'), - 'tired' => t('tired'), - 'perky' => t('perky'), - 'angry' => t('angry'), - 'stupefied' => t('stupefied'), - 'puzzled' => t('puzzled'), - 'interested' => t('interested'), - 'bitter' => t('bitter'), - 'cheerful' => t('cheerful'), - 'alive' => t('alive'), - 'annoyed' => t('annoyed'), - 'anxious' => t('anxious'), - 'cranky' => t('cranky'), - 'disturbed' => t('disturbed'), - 'frustrated' => t('frustrated'), - 'depressed' => t('depressed'), - 'motivated' => t('motivated'), - 'relaxed' => t('relaxed'), - 'surprised' => t('surprised'), - ]; - - /** - * @hooks mood_verbs - * * \e array associative array with mood verbs - */ - call_hooks('mood_verbs', $arr); - - return $arr; +function is_solo_string(string $emoji, string $body) : bool { + return empty(trim(str_replace($emoji, '', $body, $count))) && $count === 1; } /** @@ -1371,38 +1342,37 @@ function list_smilies($default_only = false) { ); $icons = array( - '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-heart.gif" alt="<3" />', - '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-brokenheart.gif" alt="</3" />', - '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-smile.gif" alt=":-)" />', - '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-wink.gif" alt=";-)" />', - '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-frown.gif" alt=":-(" />', - '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-tongue-out.gif" alt=":-P" />', - '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-tongue-out.gif" alt=":-p" />', - '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-kiss.gif" alt=":-\"" />', - '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-kiss.gif" alt=":-\"" />', - '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-kiss.gif" alt=":-x" />', - '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-kiss.gif" alt=":-X" />', - '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-laughing.gif" alt=":-D" />', - '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-surprised.gif" alt="8-|" />', - '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-surprised.gif" alt="8-O" />', - '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-surprised.gif" alt=":-O" />', - '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-thumbsup.gif" alt="\\o/" />', - '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-Oo.gif" alt="o.O" />', - '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-Oo.gif" alt="O.o" />', - '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-Oo.gif" alt="o_O" />', - '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-Oo.gif" alt="O_o" />', - '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-cry.gif" alt=":\'(" />', - '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-foot-in-mouth.gif" alt=":-!" />', - '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-undecided.gif" alt=":-/" />', - '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-embarassed.gif" alt=":-[" />', - '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-cool.gif" alt="8-)" />', - '<img class="smiley" src="' . z_root() . '/images/emoticons/beer_mug.gif" alt=":beer" />', - '<img class="smiley" src="' . z_root() . '/images/emoticons/beer_mug.gif" alt=":homebrew" />', - '<img class="smiley" src="' . z_root() . '/images/emoticons/coffee.gif" alt=":coffee" />', - '<img class="smiley" src="' . z_root() . '/images/emoticons/smiley-facepalm.gif" alt=":facepalm" />', - '<img class="smiley" src="' . z_root() . '/images/emoticons/like.gif" alt=":like" />', - '<img class="smiley" src="' . z_root() . '/images/emoticons/dislike.gif" alt=":dislike" />' - + '<img class="smiley emoji" src="' . z_root() . '/images/emoticons/smiley-heart.gif" alt="<3" />', + '<img class="smiley emoji" src="' . z_root() . '/images/emoticons/smiley-brokenheart.gif" alt="</3" />', + '<img class="smiley emoji" src="' . z_root() . '/images/emoticons/smiley-smile.gif" alt=":-)" />', + '<img class="smiley emoji" src="' . z_root() . '/images/emoticons/smiley-wink.gif" alt=";-)" />', + '<img class="smiley emoji" src="' . z_root() . '/images/emoticons/smiley-frown.gif" alt=":-(" />', + '<img class="smiley emoji" src="' . z_root() . '/images/emoticons/smiley-tongue-out.gif" alt=":-P" />', + '<img class="smiley emoji" src="' . z_root() . '/images/emoticons/smiley-tongue-out.gif" alt=":-p" />', + '<img class="smiley emoji" src="' . z_root() . '/images/emoticons/smiley-kiss.gif" alt=":-\"" />', + '<img class="smiley emoji" src="' . z_root() . '/images/emoticons/smiley-kiss.gif" alt=":-\"" />', + '<img class="smiley emoji" src="' . z_root() . '/images/emoticons/smiley-kiss.gif" alt=":-x" />', + '<img class="smiley emoji" src="' . z_root() . '/images/emoticons/smiley-kiss.gif" alt=":-X" />', + '<img class="smiley emoji" src="' . z_root() . '/images/emoticons/smiley-laughing.gif" alt=":-D" />', + '<img class="smiley emoji" src="' . z_root() . '/images/emoticons/smiley-surprised.gif" alt="8-|" />', + '<img class="smiley emoji" src="' . z_root() . '/images/emoticons/smiley-surprised.gif" alt="8-O" />', + '<img class="smiley emoji" src="' . z_root() . '/images/emoticons/smiley-surprised.gif" alt=":-O" />', + '<img class="smiley emoji" src="' . z_root() . '/images/emoticons/smiley-thumbsup.gif" alt="\\o/" />', + '<img class="smiley emoji" src="' . z_root() . '/images/emoticons/smiley-Oo.gif" alt="o.O" />', + '<img class="smiley emoji" src="' . z_root() . '/images/emoticons/smiley-Oo.gif" alt="O.o" />', + '<img class="smiley emoji" src="' . z_root() . '/images/emoticons/smiley-Oo.gif" alt="o_O" />', + '<img class="smiley emoji" src="' . z_root() . '/images/emoticons/smiley-Oo.gif" alt="O_o" />', + '<img class="smiley emoji" src="' . z_root() . '/images/emoticons/smiley-cry.gif" alt=":\'(" />', + '<img class="smiley emoji" src="' . z_root() . '/images/emoticons/smiley-foot-in-mouth.gif" alt=":-!" />', + '<img class="smiley emoji" src="' . z_root() . '/images/emoticons/smiley-undecided.gif" alt=":-/" />', + '<img class="smiley emoji" src="' . z_root() . '/images/emoticons/smiley-embarassed.gif" alt=":-[" />', + '<img class="smiley emoji" src="' . z_root() . '/images/emoticons/smiley-cool.gif" alt="8-)" />', + '<img class="smiley emoji" src="' . z_root() . '/images/emoticons/beer_mug.gif" alt=":beer" />', + '<img class="smiley emoji" src="' . z_root() . '/images/emoticons/beer_mug.gif" alt=":homebrew" />', + '<img class="smiley emoji" src="' . z_root() . '/images/emoticons/coffee.gif" alt=":coffee" />', + '<img class="smiley emoji" src="' . z_root() . '/images/emoticons/smiley-facepalm.gif" alt=":facepalm" />', + '<img class="smiley emoji" src="' . z_root() . '/images/emoticons/like.gif" alt=":like" />', + '<img class="smiley emoji" src="' . z_root() . '/images/emoticons/dislike.gif" alt=":dislike" />' ); $params = array('texts' => $texts, 'icons' => $icons); @@ -1441,10 +1411,34 @@ function smilies($s, $sample = false) { || (local_channel() && intval(get_pconfig(local_channel(), 'system', 'no_smilies')))) return $s; + $s = preg_replace_callback('{<(pre|code)>.*?</\1>}ism', 'smile_shield', $s); $s = preg_replace_callback('/<[a-z]+ .*?>/ism', 'smile_shield', $s); + if (preg_match_all('/(\:(\w|\+|\-)+\:)(?=|[\!\.\?]|$)/', $s, $match)) { + // emoji shortcodes + $emojis = get_emojis(); + foreach ($match[0] as $mtch) { + $name = trim($mtch, ':'); + + if (!isset($emojis[$name])) { + continue; + } + + $emoji = $emojis[$name]; + + $class = 'emoji'; + if (is_solo_string($mtch, $s)) { + $class .= ' single-emoji'; + } + $img = '<img class="' . $class . '" src="' . $emoji['filepath'] . '" alt="' . trim($emoji['shortname'], ':') . '" title="' . trim($emoji['shortname'], ':') . '" />'; + + string_replace($emoji['shortname'], $img, $s); + } + } + +/* $params = list_smilies(); $params['string'] = $s; @@ -1455,9 +1449,9 @@ function smilies($s, $sample = false) { } } else { $params['string'] = preg_replace_callback('/<(3+)/','preg_heart',$params['string']); - $s = str_replace($params['texts'],$params['icons'],$params['string']); + $s = str_replace($params['texts'], $params['icons'], $params['string']); } - +*/ $s = preg_replace_callback('/<!--base64:(.*?)-->/ism', 'smile_unshield', $s); @@ -1595,15 +1589,16 @@ function theme_attachments(&$item) { $url = z_root() . '/magic?f=&owa=1&hash=' . $item['author_xchan'] . '&bdest=' . bin2hex($r['href'] . '/' . $revision); } - //$s .= '<a href="' . $url . '" title="' . $title . '" class="attachlink" >' . $icon . '</a>'; - if (isset($label) && isset($url) && isset($icon) && isset($title)) - $attaches[] = array('label' => $label, 'url' => $url, 'icon' => $icon, 'title' => $title); + if (isset($label) && isset($url) && isset($icon) && isset($title)) { + array_unshift($attaches, ['label' => $label, 'url' => $url, 'icon' => $icon, 'title' => $title]); + } } - if (count($attaches) > 0) + if ($attaches) { $s = replace_macros(get_markup_template('item_attach.tpl'), [ '$attaches' => $attaches ]); + } } return $s; @@ -1759,7 +1754,7 @@ function prepare_body(&$item,$attach = false,$opts = false) { $s = ''; $photo = ''; - $is_photo = ((($item['verb'] === ACTIVITY_POST) && ($item['obj_type'] === ACTIVITY_OBJ_PHOTO)) ? true : false); + $is_photo = (((in_array($item['verb'], ['Create', ACTIVITY_POST])) && (in_array($item['obj_type'], ['Image', ACTIVITY_OBJ_PHOTO]))) ? true : false); if ($is_photo) { $object = json_decode($item['obj'],true); @@ -1804,7 +1799,7 @@ 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); + $poll = (($item['obj_type'] === 'Question' && in_array($item['verb'],['Create', 'Update', ACTIVITY_POST, ACTIVITY_UPDATE, ACTIVITY_SHARE])) ? format_poll($item, $s, $opts) : false); if ($poll) { $s = $poll; } @@ -2103,7 +2098,7 @@ function prepare_text($text, $content_type = 'text/bbcode', $opts = false) { function create_export_photo_body(&$item) { - if(($item['verb'] === ACTIVITY_POST) && ($item['obj_type'] === ACTIVITY_OBJ_PHOTO)) { + if((in_array($item['verb'], ['Create', ACTIVITY_POST])) && (in_array($item['obj_type'], ['Image', ACTIVITY_OBJ_PHOTO]))) { $j = json_decode($item['obj'],true); if($j) { $item['body'] .= "\n\n" . (($j['body']) ? $j['body'] : $j['bbcode']); @@ -2616,7 +2611,7 @@ function trim_and_unpunify($s) { * @param number $effective_uid */ function xchan_query(&$items, $abook = true, $effective_uid = 0) { - $arr = array(); + $arr = []; if($items && count($items)) { if($effective_uid) { @@ -2631,6 +2626,8 @@ function xchan_query(&$items, $abook = true, $effective_uid = 0) { $arr[] = "'" . dbesc($item['owner_xchan']) . "'"; if($item['author_xchan'] && (! in_array("'" . dbesc($item['author_xchan']) . "'",$arr))) $arr[] = "'" . dbesc($item['author_xchan']) . "'"; + if($item['source_xchan'] && (! in_array("'" . dbesc($item['source_xchan']) . "'",$arr))) + $arr[] = "'" . dbesc($item['source_xchan']) . "'"; } } if(count($arr)) { @@ -2654,6 +2651,7 @@ function xchan_query(&$items, $abook = true, $effective_uid = 0) { for($x = 0; $x < count($items); $x ++) { $items[$x]['owner'] = find_xchan_in_array($items[$x]['owner_xchan'],$chans); $items[$x]['author'] = find_xchan_in_array($items[$x]['author_xchan'],$chans); + $items[$x]['source'] = find_xchan_in_array($items[$x]['source_xchan'],$chans); } } } @@ -2888,6 +2886,7 @@ function handle_tag(&$body, &$str_tags, $profile_uid, $tag, $in_network = true) $replaced = false; $r = null; $match = array(); + $newtag = ''; $termtype = ((strpos($tag,'#') === 0) ? TERM_HASHTAG : TERM_UNKNOWN); $termtype = ((strpos($tag,'@') === 0) ? TERM_MENTION : $termtype); @@ -2937,10 +2936,11 @@ function handle_tag(&$body, &$str_tags, $profile_uid, $tag, $in_network = true) } // is the link already in str_tags? - if(! stristr($str_tags,$newtag)) { + if (!stristr($str_tags, $newtag)) { // append or set str_tags - if(strlen($str_tags)) + if (strlen($str_tags)) { $str_tags .= ','; + } $str_tags .= $newtag; } diff --git a/include/xchan.php b/include/xchan.php index 4a2d389c0..b8677c8c4 100644 --- a/include/xchan.php +++ b/include/xchan.php @@ -17,6 +17,7 @@ function xchan_store_lowlevel($arr) { 'xchan_guid' => ((array_key_exists('xchan_guid',$arr)) ? $arr['xchan_guid'] : ''), 'xchan_guid_sig' => ((array_key_exists('xchan_guid_sig',$arr)) ? $arr['xchan_guid_sig'] : ''), 'xchan_pubkey' => ((array_key_exists('xchan_pubkey',$arr)) ? $arr['xchan_pubkey'] : ''), + 'xchan_epubkey' => ((array_key_exists('xchan_epubkey',$arr)) ? $arr['xchan_epubkey'] : ''), 'xchan_photo_mimetype' => ((array_key_exists('xchan_photo_mimetype',$arr)) ? $arr['xchan_photo_mimetype'] : ''), 'xchan_photo_l' => ((array_key_exists('xchan_photo_l',$arr)) ? $arr['xchan_photo_l'] : ''), 'xchan_photo_m' => ((array_key_exists('xchan_photo_m',$arr)) ? $arr['xchan_photo_m'] : ''), @@ -32,6 +33,7 @@ function xchan_store_lowlevel($arr) { 'xchan_flags' => ((array_key_exists('xchan_flags',$arr)) ? intval($arr['xchan_flags']) : 0), 'xchan_photo_date' => ((array_key_exists('xchan_photo_date',$arr)) ? datetime_convert('UTC','UTC',$arr['xchan_photo_date']) : NULL_DATE), 'xchan_name_date' => ((array_key_exists('xchan_name_date',$arr)) ? datetime_convert('UTC','UTC',$arr['xchan_name_date']) : NULL_DATE), + 'xchan_updated' => ((array_key_exists('xchan_updated',$arr)) ? datetime_convert('UTC','UTC',$arr['xchan_updated']) : NULL_DATE), 'xchan_hidden' => ((array_key_exists('xchan_hidden',$arr)) ? intval($arr['xchan_hidden']) : 0), 'xchan_orphan' => ((array_key_exists('xchan_orphan',$arr)) ? intval($arr['xchan_orphan']) : 0), 'xchan_censored' => ((array_key_exists('xchan_censored',$arr)) ? intval($arr['xchan_censored']) : 0), |