]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Move the functionality to a plugin
authorStephen Paul Weber <singpolyma@singpolyma.net>
Wed, 28 Oct 2015 00:11:54 +0000 (00:11 +0000)
committerStephen Paul Weber <singpolyma@singpolyma.net>
Wed, 28 Oct 2015 00:11:54 +0000 (00:11 +0000)
Use an associated model to prevent race conditions on creating the
profile object.

classes/Profile.php
lib/util.php
plugins/MentionURL/MentionURLPlugin.php [new file with mode: 0644]
plugins/MentionURL/classes/Mention_url_profile.php [new file with mode: 0644]
plugins/MentionURL/lib/util.php [new file with mode: 0644]

index f3252f2f6067b82f9e09e64ee875a5f9f1dfc54e..5ef77a9506fb5c48b63ba966f2bd69b3c05be11b 100644 (file)
@@ -1520,48 +1520,6 @@ 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 b842bbaa93e75d38f84b7af25bcf756109116f09..f31812349b2d9814410f956d01fa7e6d611bc86a 100644 (file)
@@ -730,33 +730,24 @@ function common_find_mentions($text, Notice $notice)
         $matches = common_find_mentions_raw($text);
 
         foreach ($matches as $match) {
-            // 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 {
+                $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);
-                }
-                $text = $match[0];
+            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 ($mentioned instanceof Profile) {
@@ -770,7 +761,7 @@ function common_find_mentions($text, Notice $notice)
 
                 $mention = array('mentioned' => array($mentioned),
                                  'type' => 'mention',
-                                 'text' => $text,
+                                 'text' => $match[0],
                                  'position' => $match[1],
                                  'length' => mb_strlen($match[0]),
                                  'url' => $url);
@@ -848,7 +839,7 @@ function common_find_mentions_raw($text)
                    PREG_OFFSET_CAPTURE);
 
     $atmatches = array();
-    preg_match_all('/(?:^|\s+)@((?:[A-Za-z0-9_:\-\.\/%]+)|(?:' . Nickname::DISPLAY_FMT . '))\b/',
+    preg_match_all('/(?:^|\s+)@(' . Nickname::DISPLAY_FMT . ')\b/',
                    $text,
                    $atmatches,
                    PREG_OFFSET_CAPTURE);
@@ -2440,62 +2431,6 @@ 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();
diff --git a/plugins/MentionURL/MentionURLPlugin.php b/plugins/MentionURL/MentionURLPlugin.php
new file mode 100644 (file)
index 0000000..86135d8
--- /dev/null
@@ -0,0 +1,70 @@
+<?php
+
+if (!defined('GNUSOCIAL')) { exit(1); }
+
+require_once __DIR__ . '/lib/util.php';
+
+/*
+ * This plugin lets you type @twitter.com/singpolyma
+ * so that you can be specific instead of relying on heuristics.
+ */
+class MentionURLPlugin extends Plugin
+{
+    public function onStartFindMentions($sender, $text, &$mentions)
+    {
+        preg_match_all('/(?:^|\s+)@([A-Za-z0-9_:\-\.\/%]+)\b/',
+                       $text,
+                       $atmatches,
+                       PREG_OFFSET_CAPTURE);
+
+        foreach ($atmatches[1] as $match) {
+            $url = $match[0];
+            if(!common_valid_http_url($url)) { $url = 'http://' . $url; }
+            if(common_valid_http_url($url)) {
+                $mentioned = Mention_url_profile::fromUrl($url);
+                $text = mb_strlen($mentioned->nickname) <= mb_strlen($match[0]) ? $mentioned->nickname : $match[0];
+            }
+
+            if($mentioned instanceof Profile) {
+                $mentions[] = array('mentioned' => array($mentioned),
+                                   'type' => 'mention',
+                                   'text' => $text,
+                                   'position' => $match[1],
+                                   'length' => mb_strlen($match[0]),
+                                   'url' => $mentioned->profileurl);
+            }
+        }
+
+        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 (file)
index 0000000..7f247c1
--- /dev/null
@@ -0,0 +1,118 @@
+<?php
+/*
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.     If not, see <http://www.gnu.org/licenses/>.
+ */
+
+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 (file)
index 0000000..b266780
--- /dev/null
@@ -0,0 +1,57 @@
+<?php
+
+function mention_url_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;
+}