diff options
-rw-r--r-- | Zotlabs/Module/Item.php | 2 | ||||
-rw-r--r-- | include/text.php | 117 |
2 files changed, 54 insertions, 65 deletions
diff --git a/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php index 640b4fa5c..ef1eb3700 100644 --- a/Zotlabs/Module/Item.php +++ b/Zotlabs/Module/Item.php @@ -533,7 +533,7 @@ class Item extends \Zotlabs\Web\Controller { // Look for tags and linkify them $results = linkify_tags($a, $body, ($uid) ? $uid : $profile_uid); - +logger('linkify: ' . print_r($results,true)); if($results) { // Set permissions based on tag replacements diff --git a/include/text.php b/include/text.php index 681103389..1c4f5e2ec 100644 --- a/include/text.php +++ b/include/text.php @@ -824,7 +824,7 @@ function get_tags($s) { // match bracket mentions - if(preg_match_all('/([@!]\{.*?\})/',$s,$match)) { + if(preg_match_all('/([@!]\!?\{.*?\})/',$s,$match)) { foreach($match[1] as $mtch) { $ret[] = $mtch; } @@ -833,12 +833,19 @@ function get_tags($s) { // Pull out single word tags. These can be @nickname, @first_last // and #hash tags. - if(preg_match_all('/(?<![a-zA-Z0-9=\pL\/\?\;])([@#\!][^ \x0D\x0A,;:\?\[\{\&]+)/u',$s,$match)) { + if(preg_match_all('/(?<![a-zA-Z0-9=\pL\/\?\;])([@#\!]\!?[^ \x0D\x0A,;:\?\[\{\&]+)/u',$s,$match)) { foreach($match[1] as $mtch) { + + // Cleanup/ignore false positives + + // Just ignore these rather than try and adjust the regex to deal with them + if(in_array($mtch,[ '@!', '!!' ])) + continue; + // likewise for trailing period. Strip it off rather than complicate the regex further. if(substr($mtch,-1,1) === '.') $mtch = substr($mtch,0,-1); // ignore strictly numeric tags like #1 or #^ bookmarks or ## double hash - if((strpos($mtch,'#') === 0) && ( ctype_digit(substr($mtch,1)) || substr($mtch,1,1) === '^') || substr($mtch,1,1) === '#') + if((strpos($mtch,'#') === 0) && ( ctype_digit(substr($mtch,1)) || in_array(substr($mtch,1,1), [ '^', '#' ]))) continue; // or quote remnants from the quoted strings we already picked out earlier if(strpos($mtch,'"')) @@ -863,7 +870,7 @@ function get_tags($s) { usort($ret,'tag_sort_length'); -// logger('get_tags: ' . print_r($ret,true)); + // logger('get_tags: ' . print_r($ret,true)); return $ret; } @@ -2537,9 +2544,10 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $i $termtype = ((strpos($tag,'!') === 0) ? TERM_FORUM : $termtype); $termtype = ((strpos($tag,'#^[') === 0) ? TERM_BOOKMARK : $termtype); - //is it a hash tag? - if(strpos($tag,'#') === 0) { - if(strpos($tag,'#^[') === 0) { + // Is it a hashtag of some kind? + + if ( in_array($termtype, [ TERM_HASHTAG, TERM_BOOKMARK ] )) { + if($termtype === TERM_BOOKMARK) { if(preg_match('/#\^\[(url|zrl)(.*?)\](.*?)\[\/(url|zrl)\]/',$tag,$match)) { $basetag = $match[3]; $url = ((substr($match[2],0,1) === '=') ? substr($match[2],1) : $match[3]); @@ -2548,12 +2556,13 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $i } // if the tag is already replaced... elseif((strpos($tag,'[zrl=')) || (strpos($tag,'[url='))) { - //...do nothing + // ...do nothing return $replaced; } + if(! $replaced) { - // base tag has the tags name only + // double-quoted hashtags: base tag has the htmlentity name only if((substr($tag,0,7) === '#"') && (substr($tag,-6,6) === '"')) { $basetag = substr($tag,7); @@ -2562,21 +2571,20 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $i else $basetag = substr($tag,1); - //create text for link + // create text for link $url = z_root() . '/search?tag=' . rawurlencode($basetag); $newtag = '#[zrl=' . z_root() . '/search?tag=' . rawurlencode($basetag) . ']' . $basetag . '[/zrl]'; - //replace tag by the link. Make sure to not replace something in the middle of a word - // The '=' is needed to not replace color codes if the code is also used as a tag - // Much better would be to somehow completely avoiding things in e.g. [color]-tags. - // This would allow writing things like "my favourite tag=#foobar". + // replace tag by the link. Make sure to not replace something in the middle of a word + $body = preg_replace('/(?<![a-zA-Z0-9=])'.preg_quote($tag,'/').'/', $newtag, $body); $replaced = true; } - //is the link already in str_tags? + + // is the link already in str_tags? if(! stristr($str_tags,$newtag)) { - //append or set str_tags + // append or set str_tags if(strlen($str_tags)) $str_tags .= ','; @@ -2587,43 +2595,38 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $i 'termtype' => $termtype, 'term' => $basetag, 'url' => $url, - 'contact' => $r[0] + 'contact' => [] ]; } - //is it a person tag? + // END hashtags - $grouptag = false; + // BEGIN mentions - if(strpos($tag,'!') === 0) { - $grouptag = true; - } + if ( in_array($termtype, [ TERM_MENTION, TERM_FORUM ] )) { - if(strpos($tag,'@') === 0 || $grouptag) { + // The @! tag and !! tag will alter permissions - // The @! tag will alter permissions - $exclusive = (((! $grouptag) && (strpos($tag,'!') === 1) && $in_network) ? true : false); - if(($grouptag) && (strpos($tag,'!!') === 0)) { - $exclusive = true; - } + // $in_network is set to false to avoid false positives on posts originating + // on a network which does not implement privacy tags or implements them differently. + + $exclusive = (((strpos($tag,'!') === 1) && $in_network) ? true : false); //is it already replaced? if(strpos($tag,'[zrl=') || strpos($tag,'[url=')) return $replaced; - //get the person's name + // get the channel name + // First extract the name or name fragment we are going to replace - $name = substr($tag,(($exclusive) ? 2 : 1)); // The name or name fragment we are going to replace - $newname = $name; // a copy that we can mess with + $name = substr($tag,(($exclusive) ? 2 : 1)); + $newname = $name; // make a copy that we can mess with $tagcid = 0; $r = null; - // is it some generated name? - - $forum = false; - $trailing_plus_name = false; + // is it some generated (autocompleted) name? if(substr($name,0,1) === '{' && substr($name,-1,1) === '}') { $newname = substr($name,1); @@ -2639,15 +2642,17 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $i // look for matching names in the address book - // Two ways to deal with spaces - double quote the name or use underscores - // we see this after input filtering so quotes have been html entity encoded + // Double quote the entire mentioned term to include special characters + // such as spaces and some punctuation. + + // We see this after input filtering so quotes have been html entity encoded if((substr($name,0,6) === '"') && (substr($name,-6,6) === '"')) { $newname = substr($name,6); $newname = substr($newname,0,-6); } - //select someone from this user's contacts by name + // select someone from this user's contacts by name $r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash WHERE xchan_name = '%s' AND abook_channel = %d LIMIT 1", @@ -2664,7 +2669,7 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $i ); } - //select someone by attag or nick and the name passed in + // select someone by attag or nick and the name passed in if(! $r) { $r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash @@ -2679,7 +2684,7 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $i // $r is set if we found something $channel = App::get_channel(); - + if($r) { $profile = $r[0]['xchan_url']; $newname = $r[0]['xchan_name']; @@ -2717,27 +2722,24 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $i } } - if(($exclusive) && (! $access_tag)) { - $access_tag .= 'cid:' . $channel['channel_hash']; - } - - // if there is an url for this channel + // if there is a url for this channel if(isset($profile)) { $replaced = true; //create profile link $profile = str_replace(',','%2c',$profile); $url = $profile; - if($grouptag) { + if($termtype === TERM_FORUM) { $newtag = '!' . (($exclusive) ? '!' : '') . '[zrl=' . $profile . ']' . $newname . '[/zrl]'; $body = str_replace('!' . (($exclusive) ? '!' : '') . $name, $newtag, $body); } else { + // ( $termtype === TERM_MENTION ) $newtag = '@' . (($exclusive) ? '!' : '') . '[zrl=' . $profile . ']' . $newname . '[/zrl]'; $body = str_replace('@' . (($exclusive) ? '!' : '') . $name, $newtag, $body); } - //append tag to str_tags + // append tag to str_tags if(! stristr($str_tags,$newtag)) { if(strlen($str_tags)) $str_tags .= ','; @@ -2751,14 +2753,14 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $i 'termtype' => $termtype, 'term' => $newname, 'url' => $url, - 'contact' => $r[0] + 'contact' => (($r) ? $r[0] : []) ]; } function linkify_tags($a, &$body, $uid, $in_network = true) { - $str_tags = ''; - $tagged = array(); - $results = array(); + $str_tags = EMPTY_STR; + $tagged = []; + $results = []; $tags = get_tags($body); @@ -2766,19 +2768,6 @@ function linkify_tags($a, &$body, $uid, $in_network = true) { foreach($tags as $tag) { $access_tag = ''; - // If we already tagged 'Robert Johnson', don't try and tag 'Robert'. - // Robert Johnson should be first in the $tags array - - $fullnametagged = false; - for($x = 0; $x < count($tagged); $x ++) { - if(stristr($tagged[$x],$tag . ' ')) { - $fullnametagged = true; - break; - } - } - if($fullnametagged) - continue; - $success = handle_tag($a, $body, $access_tag, $str_tags, ($uid) ? $uid : App::$profile_uid , $tag, $in_network); $results[] = array('success' => $success, 'access_tag' => $access_tag); |