]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Allow users to @mention URLs
authorStephen Paul Weber <singpolyma@singpolyma.net>
Fri, 23 Oct 2015 21:15:40 +0000 (21:15 +0000)
committerStephen Paul Weber <singpolyma@singpolyma.net>
Fri, 23 Oct 2015 21:15:40 +0000 (21:15 +0000)
Because inferring who you mean (especially in the presence of remotes) can suck

classes/Profile.php
lib/util.php

index 5ef77a9506fb5c48b63ba966f2bd69b3c05be11b..f3252f2f6067b82f9e09e64ee875a5f9f1dfc54e 100644 (file)
@@ -1520,6 +1520,48 @@ class Profile extends Managed_DataObject
         return $profile;
     }
 
+    /*
+     * Get or create a profile given a possible URL
+     */
+    static function ensureFromUrl($url, $mf2=null) {
+        common_debug('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 = Profile::getKV('profileurl', $url);
+        }
+
+        $url = str_replace('https://', 'http://', $url);
+        if(!($profile instanceof Profile)) {
+            try {
+                $profile = Profile::fromUri($url);
+            } catch(UnknownUriException $ex) {}
+        }
+
+        if(!($profile instanceof Profile)) {
+            $profile = Profile::getKV('profileurl', $url);
+        }
+
+        if(!($profile instanceof Profile)) {
+            $hcard = common_representative_hcard($url, null, $mf2);
+            if(!$hcard) return null;
+
+            $profile = new Profile();
+            $profile->profileurl = $hcard['url'][0];
+            $profile->fullname = $hcard['name'][0];
+            preg_match_all('/'.Nickname::DISPLAY_FMT.'/', $profile->fullname, $altnick);
+            $profile->nickname = $hcard['nickname'] ? $hcard['nickname'][0] : implode($altnick[0]);
+            $profile->created = common_sql_now();
+            $profile->insert();
+        }
+
+        return $profile;
+    }
+
     function canRead(Notice $notice)
     {
         if ($notice->scope & Notice::SITE_SCOPE) {
index 66847a435004815c1569deb0ee3fdc137ecf0eaf..b842bbaa93e75d38f84b7af25bcf756109116f09 100644 (file)
@@ -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;
@@ -730,24 +730,33 @@ function common_find_mentions($text, Notice $notice)
         $matches = common_find_mentions_raw($text);
 
         foreach ($matches as $match) {
-            try {
-                $nickname = Nickname::normalize($match[0]);
-            } catch (NicknameException $e) {
-                // Bogus match? Drop it.
-                continue;
-            }
+            // Try to process it as @URL
+            $url = $match[0];
+            if(!common_valid_http_url($url)) { $url = 'http://' . $url; }
+            if(common_valid_http_url($url)) {
+                $mentioned = Profile::ensureFromUrl($url);
+                $text = mb_strlen($mentioned->nickname) <= mb_strlen($match[0]) ? $mentioned->nickname : $match[0];
+            } else {
+                try {
+                    $nickname = Nickname::normalize($match[0]);
+                } catch (NicknameException $e) {
+                    // Bogus match? Drop it.
+                    continue;
+                }
 
-            // Try to get a profile for this nickname.
-            // Start with conversation context, then go to
-            // sender context.
+                // Try to get a profile for this nickname.
+                // Start with conversation context, then go to
+                // sender context.
 
-            if ($origAuthor instanceof Profile && $origAuthor->nickname == $nickname) {
-                $mentioned = $origAuthor;
-            } else if (!empty($origMentions) &&
-                       array_key_exists($nickname, $origMentions)) {
-                $mentioned = $origMentions[$nickname];
-            } else {
-                $mentioned = common_relative_profile($sender, $nickname);
+                if ($origAuthor instanceof Profile && $origAuthor->nickname == $nickname) {
+                    $mentioned = $origAuthor;
+                } else if (!empty($origMentions) &&
+                           array_key_exists($nickname, $origMentions)) {
+                    $mentioned = $origMentions[$nickname];
+                } else {
+                    $mentioned = common_relative_profile($sender, $nickname);
+                }
+                $text = $match[0];
             }
 
             if ($mentioned instanceof Profile) {
@@ -761,8 +770,9 @@ function common_find_mentions($text, Notice $notice)
 
                 $mention = array('mentioned' => array($mentioned),
                                  'type' => 'mention',
-                                 'text' => $match[0],
+                                 'text' => $text,
                                  'position' => $match[1],
+                                 'length' => mb_strlen($match[0]),
                                  'url' => $url);
 
                 if (!empty($mentioned->fullname)) {
@@ -838,7 +848,7 @@ function common_find_mentions_raw($text)
                    PREG_OFFSET_CAPTURE);
 
     $atmatches = array();
-    preg_match_all('/(?:^|\s+)@(' . Nickname::DISPLAY_FMT . ')\b/',
+    preg_match_all('/(?:^|\s+)@((?:[A-Za-z0-9_:\-\.\/%]+)|(?:' . Nickname::DISPLAY_FMT . '))\b/',
                    $text,
                    $atmatches,
                    PREG_OFFSET_CAPTURE);
@@ -2430,6 +2440,62 @@ function common_strip_html($html, $trim=true, $save_whitespace=false)
     return $trim ? trim($text) : $text;
 }
 
+function common_representative_hcard($url, $fn=null, $mf2=null) {
+   if(!$mf2) {
+       $request = HTTPClient::start();
+
+        try {
+            $response = $request->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('/<title>([^<]+)/', $response->getBody(), $match);
+        $hcard = array('name' => array($match[1]));
+    }
+
+    if($hcard && !$hcard['url']) {
+        $hcard['url'] = array($url);
+    }
+
+    return $hcard;
+}
+
 function html_sprintf()
 {
     $args = func_get_args();