Combine code that finds mentions into one place and add hook points
Combined the code that finds mentions of other profiles into one place. common_find_mentions() finds mentions and calls hooks to allow supplemental syntax for mentions (like OStatus). common_linkify_mentions() links mentions. common_linkify_mention() links a mention. Notice::saveReplies() now uses common_find_mentions() instead of trying to parse everything again.
This commit is contained in:
parent
4209082677
commit
ab3db8c899
15
EVENTS.txt
15
EVENTS.txt
@ -748,3 +748,18 @@ EndDisfavorNotice: After saving a notice as a favorite
|
|||||||
- $profile: profile of the person faving (can be remote!)
|
- $profile: profile of the person faving (can be remote!)
|
||||||
- $notice: notice being faved
|
- $notice: notice being faved
|
||||||
|
|
||||||
|
StartFindMentions: start finding mentions in a block of text
|
||||||
|
- $sender: sender profile
|
||||||
|
- $text: plain text version of the notice
|
||||||
|
- &$mentions: mentions found so far. Array of arrays; each array
|
||||||
|
has 'mentioned' (array of mentioned profiles), 'url' (url to link as),
|
||||||
|
'title' (title of the link), 'position' (position of the text to
|
||||||
|
replace), 'text' (text to replace)
|
||||||
|
|
||||||
|
EndFindMentions: end finding mentions in a block of text
|
||||||
|
- $sender: sender profile
|
||||||
|
- $text: plain text version of the notice
|
||||||
|
- &$mentions: mentions found so far. Array of arrays; each array
|
||||||
|
has 'mentioned' (array of mentioned profiles), 'url' (url to link as),
|
||||||
|
'title' (title of the link), 'position' (position of the text to
|
||||||
|
replace), 'text' (text to replace)
|
||||||
|
@ -820,6 +820,7 @@ class Notice extends Memcached_DataObject
|
|||||||
/**
|
/**
|
||||||
* @return array of integer profile IDs
|
* @return array of integer profile IDs
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function saveReplies()
|
function saveReplies()
|
||||||
{
|
{
|
||||||
// Don't save reply data for repeats
|
// Don't save reply data for repeats
|
||||||
@ -828,76 +829,44 @@ class Notice extends Memcached_DataObject
|
|||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Alternative reply format
|
|
||||||
$tname = false;
|
|
||||||
if (preg_match('/^T ([A-Z0-9]{1,64}) /', $this->content, $match)) {
|
|
||||||
$tname = $match[1];
|
|
||||||
}
|
|
||||||
// extract all @messages
|
|
||||||
$cnt = preg_match_all('/(?:^|\s)@([a-z0-9]{1,64})/', $this->content, $match);
|
|
||||||
|
|
||||||
$names = array();
|
|
||||||
|
|
||||||
if ($cnt || $tname) {
|
|
||||||
// XXX: is there another way to make an array copy?
|
|
||||||
$names = ($tname) ? array_unique(array_merge(array(strtolower($tname)), $match[1])) : array_unique($match[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$sender = Profile::staticGet($this->profile_id);
|
$sender = Profile::staticGet($this->profile_id);
|
||||||
|
|
||||||
|
$mentions = common_find_mentions($this->profile_id, $this->content);
|
||||||
|
|
||||||
$replied = array();
|
$replied = array();
|
||||||
|
|
||||||
// store replied only for first @ (what user/notice what the reply directed,
|
// store replied only for first @ (what user/notice what the reply directed,
|
||||||
// we assume first @ is it)
|
// we assume first @ is it)
|
||||||
|
|
||||||
for ($i=0; $i<count($names); $i++) {
|
foreach ($mentions as $mention) {
|
||||||
$nickname = $names[$i];
|
|
||||||
$recipient = common_relative_profile($sender, $nickname, $this->created);
|
|
||||||
if (empty($recipient)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Don't save replies from blocked profile to local user
|
|
||||||
$recipient_user = User::staticGet('id', $recipient->id);
|
|
||||||
if (!empty($recipient_user) && $recipient_user->hasBlocked($sender)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$reply = new Reply();
|
|
||||||
$reply->notice_id = $this->id;
|
|
||||||
$reply->profile_id = $recipient->id;
|
|
||||||
$id = $reply->insert();
|
|
||||||
if (!$id) {
|
|
||||||
$last_error = &PEAR::getStaticProperty('DB_DataObject','lastError');
|
|
||||||
common_log(LOG_ERR, 'DB error inserting reply: ' . $last_error->message);
|
|
||||||
common_server_error(sprintf(_('DB error inserting reply: %s'), $last_error->message));
|
|
||||||
return array();
|
|
||||||
} else {
|
|
||||||
$replied[$recipient->id] = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hash format replies, too
|
foreach ($mention['mentioned'] as $mentioned) {
|
||||||
$cnt = preg_match_all('/(?:^|\s)@#([a-z0-9]{1,64})/', $this->content, $match);
|
|
||||||
if ($cnt) {
|
// skip if they're already covered
|
||||||
foreach ($match[1] as $tag) {
|
|
||||||
$tagged = Profile_tag::getTagged($sender->id, $tag);
|
if (!empty($replied[$mentioned->id])) {
|
||||||
foreach ($tagged as $t) {
|
continue;
|
||||||
if (!$replied[$t->id]) {
|
}
|
||||||
// Don't save replies from blocked profile to local user
|
|
||||||
$t_user = User::staticGet('id', $t->id);
|
// Don't save replies from blocked profile to local user
|
||||||
if ($t_user && $t_user->hasBlocked($sender)) {
|
|
||||||
continue;
|
$mentioned_user = User::staticGet('id', $mentioned->id);
|
||||||
}
|
if (!empty($mentioned_user) && $mentioned_user->hasBlocked($sender)) {
|
||||||
$reply = new Reply();
|
continue;
|
||||||
$reply->notice_id = $this->id;
|
}
|
||||||
$reply->profile_id = $t->id;
|
|
||||||
$id = $reply->insert();
|
$reply = new Reply();
|
||||||
if (!$id) {
|
|
||||||
common_log_db_error($reply, 'INSERT', __FILE__);
|
$reply->notice_id = $this->id;
|
||||||
return array();
|
$reply->profile_id = $mentioned->id;
|
||||||
} else {
|
|
||||||
$replied[$recipient->id] = 1;
|
$id = $reply->insert();
|
||||||
}
|
|
||||||
}
|
if (!$id) {
|
||||||
|
common_log_db_error($reply, 'INSERT', __FILE__);
|
||||||
|
throw new ServerException("Couldn't save reply for {$this->id}, {$mentioned->id}");
|
||||||
|
} else {
|
||||||
|
$replied[$mentioned->id] = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
195
lib/util.php
195
lib/util.php
@ -426,13 +426,148 @@ function common_render_content($text, $notice)
|
|||||||
{
|
{
|
||||||
$r = common_render_text($text);
|
$r = common_render_text($text);
|
||||||
$id = $notice->profile_id;
|
$id = $notice->profile_id;
|
||||||
$r = preg_replace('/(^|\s+)@(['.NICKNAME_FMT.']{1,64})/e', "'\\1@'.common_at_link($id, '\\2')", $r);
|
$r = common_linkify_mentions($id, $r);
|
||||||
$r = preg_replace('/^T ([A-Z0-9]{1,64}) /e', "'T '.common_at_link($id, '\\1').' '", $r);
|
|
||||||
$r = preg_replace('/(^|[\s\.\,\:\;]+)@#([A-Za-z0-9]{1,64})/e', "'\\1@#'.common_at_hash_link($id, '\\2')", $r);
|
|
||||||
$r = preg_replace('/(^|[\s\.\,\:\;]+)!([A-Za-z0-9]{1,64})/e', "'\\1!'.common_group_link($id, '\\2')", $r);
|
$r = preg_replace('/(^|[\s\.\,\:\;]+)!([A-Za-z0-9]{1,64})/e', "'\\1!'.common_group_link($id, '\\2')", $r);
|
||||||
return $r;
|
return $r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function common_linkify_mentions($profile_id, $text)
|
||||||
|
{
|
||||||
|
$mentions = common_find_mentions($profile_id, $text);
|
||||||
|
|
||||||
|
// We need to go through in reverse order by position,
|
||||||
|
// so our positions stay valid despite our fudging with the
|
||||||
|
// string!
|
||||||
|
|
||||||
|
$points = array();
|
||||||
|
|
||||||
|
foreach ($mentions as $mention)
|
||||||
|
{
|
||||||
|
$points[$mention['position']] = $mention;
|
||||||
|
}
|
||||||
|
|
||||||
|
krsort($points);
|
||||||
|
|
||||||
|
foreach ($points as $position => $mention) {
|
||||||
|
|
||||||
|
$linkText = common_linkify_mention($mention);
|
||||||
|
|
||||||
|
$text = substr_replace($text, $linkText, $position, mb_strlen($mention['text']));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $text;
|
||||||
|
}
|
||||||
|
|
||||||
|
function common_linkify_mention($mention)
|
||||||
|
{
|
||||||
|
$output = null;
|
||||||
|
|
||||||
|
if (Event::handle('StartLinkifyMention', array($mention, &$output))) {
|
||||||
|
|
||||||
|
$xs = new XMLStringer(false);
|
||||||
|
|
||||||
|
$attrs = array('href' => $mention['url'],
|
||||||
|
'class' => 'url');
|
||||||
|
|
||||||
|
if (!empty($mention['title'])) {
|
||||||
|
$attrs['title'] = $mention['title'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$xs->elementStart('span', 'vcard');
|
||||||
|
$xs->elementStart('a', $attrs);
|
||||||
|
$xs->element('span', 'fn nickname', $mention['text']);
|
||||||
|
$xs->elementEnd('a');
|
||||||
|
$xs->elementEnd('span');
|
||||||
|
|
||||||
|
$output = $xs->getString();
|
||||||
|
|
||||||
|
Event::handle('EndLinkifyMention', array($mention, &$output));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
function common_find_mentions($profile_id, $text)
|
||||||
|
{
|
||||||
|
$mentions = array();
|
||||||
|
|
||||||
|
$sender = Profile::staticGet('id', $profile_id);
|
||||||
|
|
||||||
|
if (empty($sender)) {
|
||||||
|
return $mentions;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Event::handle('StartFindMentions', array($sender, $text, &$mentions))) {
|
||||||
|
|
||||||
|
preg_match_all('/^T ([A-Z0-9]{1,64}) /',
|
||||||
|
$text,
|
||||||
|
$tmatches,
|
||||||
|
PREG_OFFSET_CAPTURE);
|
||||||
|
|
||||||
|
preg_match_all('/(?:^|\s+)@(['.NICKNAME_FMT.']{1,64})/',
|
||||||
|
$text,
|
||||||
|
$atmatches,
|
||||||
|
PREG_OFFSET_CAPTURE);
|
||||||
|
|
||||||
|
$matches = array_merge($tmatches[1], $atmatches[1]);
|
||||||
|
|
||||||
|
foreach ($matches as $match) {
|
||||||
|
|
||||||
|
$nickname = common_canonical_nickname($match[0]);
|
||||||
|
$mentioned = common_relative_profile($sender, $nickname);
|
||||||
|
|
||||||
|
if (!empty($mentioned)) {
|
||||||
|
|
||||||
|
$user = User::staticGet('id', $mentioned->id);
|
||||||
|
|
||||||
|
if ($user) {
|
||||||
|
$url = common_local_url('userbyid', array('id' => $user->id));
|
||||||
|
} else {
|
||||||
|
$url = $mentioned->profileurl;
|
||||||
|
}
|
||||||
|
|
||||||
|
$mention = array('mentioned' => array($mentioned),
|
||||||
|
'text' => $match[0],
|
||||||
|
'position' => $match[1],
|
||||||
|
'url' => $url);
|
||||||
|
|
||||||
|
if (!empty($mentioned->fullname)) {
|
||||||
|
$mention['title'] = $mentioned->fullname;
|
||||||
|
}
|
||||||
|
|
||||||
|
$mentions[] = $mention;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @#tag => mention of all subscriptions tagged 'tag'
|
||||||
|
|
||||||
|
preg_match_all('/(?:^|[\s\.\,\:\;]+)@#([\pL\pN_\-\.]{1,64})/',
|
||||||
|
$text,
|
||||||
|
$hmatches,
|
||||||
|
PREG_OFFSET_CAPTURE);
|
||||||
|
|
||||||
|
foreach ($hmatches[1] as $hmatch) {
|
||||||
|
|
||||||
|
$tag = common_canonical_tag($hmatch[0]);
|
||||||
|
|
||||||
|
$tagged = Profile_tag::getTagged($sender->id, $tag);
|
||||||
|
|
||||||
|
$url = common_local_url('subscriptions',
|
||||||
|
array('nickname' => $sender->nickname,
|
||||||
|
'tag' => $tag));
|
||||||
|
|
||||||
|
$mentions[] = array('mentioned' => $tagged,
|
||||||
|
'text' => $hmatch[0],
|
||||||
|
'position' => $hmatch[1],
|
||||||
|
'url' => $url);
|
||||||
|
}
|
||||||
|
|
||||||
|
Event::handle('EndFindMentions', array($sender, $text, &$mentions));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $mentions;
|
||||||
|
}
|
||||||
|
|
||||||
function common_render_text($text)
|
function common_render_text($text)
|
||||||
{
|
{
|
||||||
$r = htmlspecialchars($text);
|
$r = htmlspecialchars($text);
|
||||||
@ -663,37 +798,6 @@ function common_valid_profile_tag($str)
|
|||||||
return preg_match('/^[A-Za-z0-9_\-\.]{1,64}$/', $str);
|
return preg_match('/^[A-Za-z0-9_\-\.]{1,64}$/', $str);
|
||||||
}
|
}
|
||||||
|
|
||||||
function common_at_link($sender_id, $nickname)
|
|
||||||
{
|
|
||||||
$sender = Profile::staticGet($sender_id);
|
|
||||||
if (!$sender) {
|
|
||||||
return $nickname;
|
|
||||||
}
|
|
||||||
$recipient = common_relative_profile($sender, common_canonical_nickname($nickname));
|
|
||||||
if ($recipient) {
|
|
||||||
$user = User::staticGet('id', $recipient->id);
|
|
||||||
if ($user) {
|
|
||||||
$url = common_local_url('userbyid', array('id' => $user->id));
|
|
||||||
} else {
|
|
||||||
$url = $recipient->profileurl;
|
|
||||||
}
|
|
||||||
$xs = new XMLStringer(false);
|
|
||||||
$attrs = array('href' => $url,
|
|
||||||
'class' => 'url');
|
|
||||||
if (!empty($recipient->fullname)) {
|
|
||||||
$attrs['title'] = $recipient->fullname . ' (' . $recipient->nickname . ')';
|
|
||||||
}
|
|
||||||
$xs->elementStart('span', 'vcard');
|
|
||||||
$xs->elementStart('a', $attrs);
|
|
||||||
$xs->element('span', 'fn nickname', $nickname);
|
|
||||||
$xs->elementEnd('a');
|
|
||||||
$xs->elementEnd('span');
|
|
||||||
return $xs->getString();
|
|
||||||
} else {
|
|
||||||
return $nickname;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function common_group_link($sender_id, $nickname)
|
function common_group_link($sender_id, $nickname)
|
||||||
{
|
{
|
||||||
$sender = Profile::staticGet($sender_id);
|
$sender = Profile::staticGet($sender_id);
|
||||||
@ -716,29 +820,6 @@ function common_group_link($sender_id, $nickname)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function common_at_hash_link($sender_id, $tag)
|
|
||||||
{
|
|
||||||
$user = User::staticGet($sender_id);
|
|
||||||
if (!$user) {
|
|
||||||
return $tag;
|
|
||||||
}
|
|
||||||
$tagged = Profile_tag::getTagged($user->id, common_canonical_tag($tag));
|
|
||||||
if ($tagged) {
|
|
||||||
$url = common_local_url('subscriptions',
|
|
||||||
array('nickname' => $user->nickname,
|
|
||||||
'tag' => $tag));
|
|
||||||
$xs = new XMLStringer();
|
|
||||||
$xs->elementStart('span', 'tag');
|
|
||||||
$xs->element('a', array('href' => $url,
|
|
||||||
'rel' => $tag),
|
|
||||||
$tag);
|
|
||||||
$xs->elementEnd('span');
|
|
||||||
return $xs->getString();
|
|
||||||
} else {
|
|
||||||
return $tag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function common_relative_profile($sender, $nickname, $dt=null)
|
function common_relative_profile($sender, $nickname, $dt=null)
|
||||||
{
|
{
|
||||||
// Try to find profiles this profile is subscribed to that have this nickname
|
// Try to find profiles this profile is subscribed to that have this nickname
|
||||||
|
Loading…
Reference in New Issue
Block a user