diff --git a/lib/util.php b/lib/util.php index f58e8cd112..1844240f45 100644 --- a/lib/util.php +++ b/lib/util.php @@ -648,7 +648,7 @@ function common_linkify_mentions($text, Notice $notice) $linkText = common_linkify_mention($mention); - $text = substr_replace($text, $linkText, $position, mb_strlen($mention['text'])); + $text = substr_replace($text, $linkText, $position, $mention['length']); } return $text; @@ -765,6 +765,7 @@ function common_find_mentions($text, Notice $notice) 'type' => 'mention', 'text' => $match[0], 'position' => $match[1], + 'length' => mb_strlen($match[0]), 'url' => $url); if (!empty($mentioned->fullname)) { @@ -795,6 +796,7 @@ function common_find_mentions($text, Notice $notice) 'type' => 'list', 'text' => $hmatch[0], 'position' => $hmatch[1], + 'length' => mb_strlen($hmatch[0]), 'url' => $url); } @@ -814,6 +816,7 @@ function common_find_mentions($text, Notice $notice) 'type' => 'group', 'text' => $hmatch[0], 'position' => $hmatch[1], + 'length' => mb_strlen($hmatch[0]), 'url' => $group->permalink(), 'title' => $group->getFancyName()); } diff --git a/plugins/MentionURL/MentionURLPlugin.php b/plugins/MentionURL/MentionURLPlugin.php new file mode 100644 index 0000000000..f76aa8b28d --- /dev/null +++ b/plugins/MentionURL/MentionURLPlugin.php @@ -0,0 +1,84 @@ +nickname) <= mb_strlen($match[0]) ? $mentioned->nickname : $match[0]; + } + + if($mentioned instanceof Profile) { + $matches[$match[1]] = array('mentioned' => array($mentioned), + 'type' => 'mention', + 'text' => $text, + 'position' => $match[1], + 'length' => mb_strlen($match[0]), + 'url' => $mentioned->profileurl); + } + } + + foreach ($mentions as $i => $other) { + // If we share a common prefix with a local user, override it! + $pos = $other['position']; + if (isset($matches[$pos])) { + $mentions[$i] = $matches[$pos]; + unset($matches[$pos]); + } + } + foreach ($matches as $mention) { + $mentions[] = $mention; + } + + return true; + } + + public function onStartGetProfileFromURI($uri, &$profile) + { + $mention_profile = Mention_url_profile::getKV('profileurl', $uri); + if($mention_profile instanceof Mention_url_profile) { + $profile = $mention_profile->getProfile(); + return !($profile instanceof Profile); + } + + return true; + } + + public function onCheckSchema() + { + $schema = Schema::get(); + $schema->ensureTable('mention_url_profile', Mention_url_profile::schemaDef()); + return true; + } + + public function onPluginVersion(array &$versions) + { + $versions[] = array('name' => 'MentionURL', + 'version' => GNUSOCIAL_VERSION, + 'author' => 'Stephen Paul Weber', + 'homepage' => 'http://gnu.io/', + 'description' => + // TRANS: Plugin description. + _m('Plugin to allow mentioning arbitrary URLs.')); + return true; + } +} diff --git a/plugins/MentionURL/classes/Mention_url_profile.php b/plugins/MentionURL/classes/Mention_url_profile.php new file mode 100644 index 0000000000..7f247c1966 --- /dev/null +++ b/plugins/MentionURL/classes/Mention_url_profile.php @@ -0,0 +1,118 @@ +. + */ + +if (!defined('GNUSOCIAL')) { exit(1); } + +/** + * Table Definition for mention_url_profile + */ + +class Mention_url_profile extends Managed_DataObject +{ + public $__table = 'mention_url_profile'; // table name + public $profile_id; // int(4) not_null + public $profileurl; // varchar(191) primary_key not_null not 255 because utf8mb4 takes more space + + public static function schemaDef() + { + return array( + 'fields' => array( + 'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'matches exactly one profile id'), + 'profileurl' => array('type' => 'varchar', 'not null' => true, 'length' => 191, 'description' => 'URL of the profile'), + ), + 'primary key' => array('profileurl'), + 'foreign keys' => array( + 'mention_url_profile_profile_id_fkey' => array('profile', array('profile_id' => 'id')), + ), + ); + } + + public static function fromUrl($url, $depth=0) { + common_debug('MentionURL: trying to find a profile for ' . $url); + + $url = preg_replace('#https?://#', 'https://', $url); + try { + $profile = Profile::fromUri($url); + } catch(UnknownUriException $ex) {} + + if(!($profile instanceof Profile)) { + $profile = self::findProfileByProfileURL($url); + } + + $url = str_replace('https://', 'http://', $url); + if(!($profile instanceof Profile)) { + try { + $profile = Profile::fromUri($url); + } catch(UnknownUriException $ex) {} + } + + if(!($profile instanceof Profile)) { + $profile = self::findProfileByProfileURL($url); + } + + if(!($profile instanceof Profile)) { + $hcard = mention_url_representative_hcard($url); + if(!$hcard) return null; + + $mention_profile = new Mention_url_profile(); + $mention_profile->query('BEGIN'); + + $profile = new Profile(); + $profile->profileurl = $hcard['url'][0]; + $profile->fullname = $hcard['name'][0]; + preg_match('/\/([^\/]+)\/*$/', $profile->profileurl, $matches); + if(!$hcard['nickname']) $hcard['nickname'] = array($matches[1]); + $profile->nickname = $hcard['nickname'][0]; + $profile->created = common_sql_now(); + + $mention_profile->profile_id = $profile->insert(); + if(!$mention_profile->profile_id) { + $mention_profile->query('ROLLBACK'); + return null; + } + + $mention_profile->profileurl = $profile->profileurl; + if(!$mention_profile->insert()) { + $mention_profile->query('ROLLBACK'); + if($depth > 0) { + return null; + } else { + return self::fromUrl($url, $depth+1); + } + } else { + $mention_profile->query('COMMIT'); + } + } + + return $profile; + } + + protected static function findProfileByProfileURL($url) { + $profile = Profile::getKV('profileurl', $url); + if($profile instanceof Profile) { + $mention_profile = new Mention_url_profile(); + $mention_profile->profile_id = $profile->id; + $mention_profile->profileurl = $profile->profileurl; + $mention_profile->insert(); + } + + return $profile; + } + + public function getProfile() { + return Profile::getKV('id', $this->profile_id); + } +} diff --git a/plugins/MentionURL/lib/util.php b/plugins/MentionURL/lib/util.php new file mode 100644 index 0000000000..b2667806d1 --- /dev/null +++ b/plugins/MentionURL/lib/util.php @@ -0,0 +1,57 @@ +get($url); + } catch(Exception $ex) { + return null; + } + + $url = $response->getEffectiveUrl(); + $mf2 = new Mf2\Parser($response->getBody(), $url); + $mf2 = $mf2->parse(); + } + + $hcard = null; + + if(!empty($mf2['items'])) { + $hcards = array(); + foreach($mf2['items'] as $item) { + if(!in_array('h-card', $item['type'])) { + continue; + } + + // We found a match, return it immediately + if(isset($item['properties']['url']) && in_array($url, $item['properties']['url'])) { + $hcard = $item['properties']; + break; + } + + // Let's keep all the hcards for later, to return one of them at least + $hcards[] = $item['properties']; + } + + // No match immediately for the url we expected, but there were h-cards found + if (count($hcards) > 0) { + $hcard = $hcards[0]; + } + } + + if(!$hcard && $fn) { + $hcard = array('name' => array($fn)); + } + + if(!$hcard && $response) { + preg_match('/([^<]+)/', $response->getBody(), $match); + $hcard = array('name' => array($match[1])); + } + + if($hcard && !$hcard['url']) { + $hcard['url'] = array($url); + } + + return $hcard; +} diff --git a/plugins/OStatus/OStatusPlugin.php b/plugins/OStatus/OStatusPlugin.php index ef9a275ea4..7db385b1a9 100644 --- a/plugins/OStatus/OStatusPlugin.php +++ b/plugins/OStatus/OStatusPlugin.php @@ -297,10 +297,13 @@ class OStatusPlugin extends Plugin $oprofile = Ostatus_profile::ensureWebfinger($target); if ($oprofile instanceof Ostatus_profile && !$oprofile->isGroup()) { $profile = $oprofile->localProfile(); + $text = !empty($profile->nickname) && mb_strlen($profile->nickname) < mb_strlen($target) ? + $profile->nickname : $target; $matches[$pos] = array('mentioned' => array($profile), 'type' => 'mention', - 'text' => $target, + 'text' => $text, 'position' => $pos, + 'length' => mb_strlen($target), 'url' => $profile->getUrl()); } } catch (Exception $e) { @@ -310,7 +313,7 @@ class OStatusPlugin extends Plugin } // Profile matches: @example.com/mublog/user - if (preg_match_all('!(?:^|\s+)@((?:\w+\.)*\w+(?:\w+\-\w+)*\.\w+(?:/\w+)+)!', + if (preg_match_all('!(?:^|\s+)@((?:\w+\.)*\w+(?:\w+\-\w+)*\.\w+(?:/\w+)*)!', $text, $wmatches, PREG_OFFSET_CAPTURE)) { @@ -324,10 +327,13 @@ class OStatusPlugin extends Plugin $oprofile = Ostatus_profile::ensureProfileURL($url); if ($oprofile instanceof Ostatus_profile && !$oprofile->isGroup()) { $profile = $oprofile->localProfile(); + $text = !empty($profile->nickname) && mb_strlen($profile->nickname) < mb_strlen($target) ? + $profile->nickname : $target; $matches[$pos] = array('mentioned' => array($profile), 'type' => 'mention', - 'text' => $target, + 'text' => $text, 'position' => $pos, + 'length' => mb_strlen($target), 'url' => $profile->getUrl()); break; }