Last active
August 29, 2015 14:14
-
-
Save Wolfsblvt/5a496d9955d67507a68e to your computer and use it in GitHub Desktop.
Parsing function for mentions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Replace all mentions inside of a text with full username_string and mention name | |
* | |
* @param string $text the original text | |
* @return string the text with replaced mentions | |
*/ | |
public function replace_mentions_for_display($text) | |
{ | |
$start_time = microtime(true); | |
// Get mentioned users ordered by position descending, so that we can safely replace without conflict | |
$mentions = $this->mentions->get_mentioned_users($text, true); | |
foreach ($mentions as $id => $mention_data) | |
{ | |
$username_full = $mention_data['username_full']; | |
// If we used a custom text for the mention, we take this as username | |
if ($mention_data['type'] == mentions::MENTION_BBCODE_TEXT) | |
{ | |
$username_full = str_replace($mention_data['name'], substr($text, $mention_data['start'], $mention_data['length']), $username_full); | |
} | |
// We should cut the bbcodes as well, so we have to look now how long they are | |
$len_before = $len_after = 0; | |
switch ($mention_data['type']) | |
{ | |
case mentions::MENTION_AT: | |
break; | |
case mentions::MENTION_BBCODE: | |
$len_before = strlen('[mention]'); | |
$len_after = strlen('[/mention]'); | |
break; | |
case mentions::MENTION_BBCODE_TEXT: | |
$len_before = strlen("[mention="{$mention_data['name']}"]"); | |
$len_after = strlen('[/mention]'); | |
} | |
// We add the username as title, so that we can see that someone is mentioned and who is mentioned on hover | |
$a_title = $this->user->lang['MENTIONS_MENTION'] . $this->user->lang['COLON'] . ' ' . $mention_data['name']; | |
$username_full = preg_replace('#<a#', "<a title=\"$a_title\"", $username_full, 1); | |
$text = substr_replace($text, $username_full, $mention_data['start'] - $len_before, $mention_data['length'] + $len_before + $len_after); | |
} | |
// At last, we should remove the mention tags that still exist. | |
// We don't go the way of BBCodes, cause we don't have a "real" BBCode with it here and we have | |
// our own settings wich should matter. | |
//$text = preg_replace('#\[/?mention(\="(.*?)")?\]#i', '', $text); | |
echo "Runtime of function: " . (microtime(true) - $start_time)/1000 . "<br />"; | |
return $text; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Searches post text for all mentions and returns them as an array | |
* | |
* Values of the array are the following: | |
* 'name' Username | |
* 'user_id' Users id | |
* 'posts' Post count | |
* 'colour' Color of username | |
* 'avatar' User avatar as full html img element | |
* 'username_full' Username as html element with color and link | |
* 'type' Type of the mention | |
* 'start' Startposition of the username inside the text | |
* 'end' Endposition of the username inside the text | |
* | |
* @param string $post_text The post text | |
* @param bool $sort_by_position_desc If the returned array should be sorted by position, descending | |
* @return array Array of all mentioned users and the start position of their name in text | |
*/ | |
public function get_mentioned_users($post_text, $sort_by_position_desc = false) | |
{ | |
$mentioned = array(); | |
$user_list = $this->get_userlist(); | |
// At first, the easy part. Let's get the one posted in [mention="{username}"]{text}[/mention] | |
if (true) // ACP config will come later | |
{ | |
$regular_expression_match = '#\[mention="(.+?)"\](.*?)\[/mention\]#'; | |
$matches = false; | |
preg_match_all($regular_expression_match, $post_text, $matches, PREG_OFFSET_CAPTURE); | |
for ($i = 0, $len = count($matches[1]); $i < $len; $i++) | |
{ | |
$username_clean = utf8_clean_string($matches[1][$i][0]); | |
if (array_key_exists($username_clean, $user_list)) | |
{ | |
$startpos = $matches[2][$i][1]; | |
$length = strlen($matches[2][$i][0]); | |
$user_data = $user_list[$username_clean]; | |
$mentioned[] = array_merge($user_data, array( | |
'type' => self::MENTION_BBCODE_TEXT, | |
'start' => $startpos, | |
'length' => $length, | |
)); | |
} | |
} | |
} | |
// Second we get the one posted in [mention]{username}[/mention] | |
if (true) // ACP config will come later | |
{ | |
$regular_expression_match = '#\[mention\](.*?)\[/mention\]#'; | |
$matches = false; | |
preg_match_all($regular_expression_match, $post_text, $matches, PREG_OFFSET_CAPTURE); | |
for ($i = 0, $len = count($matches[1]); $i < $len; $i++) | |
{ | |
$username_clean = utf8_clean_string($matches[1][$i][0]); | |
if (array_key_exists($username_clean, $user_list)) | |
{ | |
$startpos = $matches[1][$i][1]; | |
$length = strlen($matches[1][$i][0]); | |
$user_data = $user_list[$username_clean]; | |
$mentioned[] = array_merge($user_data, array( | |
'type' => self::MENTION_BBCODE, | |
'start' => $startpos, | |
'length' => $length, | |
)); | |
} | |
} | |
} | |
// Now the difficult part. Let's see if we can get the correct usernames for the @{username} mentions | |
if (false) // ACP config will come later | |
{ | |
$regular_expression_match = '#(?:^|\\s)@(.+?)(?:\n|$)#'; | |
$matches = false; | |
$offset = 0; | |
$maximum_at_mentions = $this->config['wolfsblvt.mentions.maximum_at_mentions_per_post']; | |
$at_mentions = 0; | |
while ($at_mentions < $maximum_at_mentions && preg_match($regular_expression_match, $post_text, $matches, PREG_OFFSET_CAPTURE, $offset)) | |
{ | |
$at_mentions += 1; | |
$line = $matches[1][0]; | |
$search_string = substr($line, 0, 1); | |
$filtered_usernames = array_keys($user_list); | |
$matched_username = false; | |
// Loop, make the search string one by one char longer and see if we have still usernames matching | |
while (count($filtered_usernames) > 1) | |
{ | |
$filtered_usernames = array_filter($filtered_usernames, function ($username_clean) use ($search_string, &$matched_username) { | |
$search_string = utf8_clean_string($search_string); | |
if (strlen($username_clean) == strlen($search_string)) | |
{ | |
if ($username_clean == $search_string) | |
{ | |
$matched_username = $username_clean; | |
} | |
return false; | |
} | |
return (substr($username_clean, 0, strlen($search_string)) == $search_string); | |
}); | |
if ($search_string == $line) | |
{ | |
// We have reached the end of the line, so stop | |
break; | |
} | |
$search_string = substr($line, 0, strlen($search_string) + 1); | |
} | |
// If there is still one in filter, we check if it is matching | |
$first_username = reset($filtered_usernames); | |
if (count($filtered_usernames) == 1 && utf8_clean_string(substr($line, 0, strlen($first_username))) == $first_username) | |
{ | |
$matched_username = $first_username; | |
} | |
// We can assume that $matched_username is the longest matching username we have found due to iteration with growing search_string | |
// So we use it now as the only match (Even if there are maybe shorts usernames matching too. But this is nothing we can solve here, | |
// This needs to be handled by the user, honestly. There is a autocomplete popup which tells the other, longer fitting name if the user is still typing, | |
// and if he continues to enter the full name, I think it is okay to choose the longer name as the chosen one. | |
if ($matched_username) | |
{ | |
$startpos = $matches[1][1]; | |
// We need to get the endpos, cause the username is cleaned and the real string might be longer | |
$full_username = substr($post_text, $startpos, strlen($matched_username)); | |
while (utf8_clean_string($full_username) != $matched_username) | |
{ | |
$full_username = substr($post_text, $startpos, strlen($full_username) + 1); | |
} | |
$length = strlen($full_username); | |
$user_data = $user_list[$matched_username]; | |
$mentioned[] = array_merge($user_data, array( | |
'type' => self::MENTION_AT, | |
'start' => $startpos, | |
'length' => $length, | |
)); | |
} | |
$offset = $matches[0][1] + strlen($search_string); | |
} | |
} | |
// Sort the array by descending position so that replacing can be done with correct position values | |
if ($sort_by_position_desc) | |
{ | |
usort($mentioned, function ($a, $b) { | |
return $b['start'] - $a['start']; | |
}); | |
} | |
return $mentioned; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment