]> git.mxchange.org Git - quix0rs-gnu-social.git/blobdiff - classes/Notice.php
Added a lot more type-hints where they will happen. Please note that I found
[quix0rs-gnu-social.git] / classes / Notice.php
index 55af2d1b6ed90d8eb9477d11dd2bbceadbcdb2a1..6314ff534a0a23846e2a11f06e716826b618c299 100644 (file)
@@ -330,7 +330,7 @@ class Notice extends Managed_DataObject
      * Record the given set of hash tags in the db for this notice.
      * Given tag strings will be normalized and checked for dupes.
      */
-    function saveKnownTags($hashtags)
+    function saveKnownTags(array $hashtags)
     {
         //turn each into their canonical tag
         //this is needed to remove dupes before saving e.g. #hash.tag = #hashtag
@@ -414,23 +414,27 @@ class Notice extends Managed_DataObject
      * @return Notice
      * @throws ClientException
      */
-    static function saveNew($profile_id, $content, $source, array $options=null) {
+    static function saveNew($profile_id, $content, $source, array $options=array()) {
         $defaults = array('uri' => null,
                           'url' => null,
-                          'reply_to' => null,
-                          'repeat_of' => null,
+                          'conversation' => null,   // URI of conversation
+                          'reply_to' => null,       // This will override convo URI if the parent is known
+                          'repeat_of' => null,      // This will override convo URI if the repeated notice is known
                           'scope' => null,
                           'distribute' => true,
                           'object_type' => null,
                           'verb' => null);
 
-        if (!empty($options) && is_array($options)) {
+        /*
+         * Above type-hint is already array, so simply count it, this saves
+         * "some" CPU cycles.
+         */
+        if (count($options) > 0) {
             $options = array_merge($defaults, $options);
-            extract($options);
-        } else {
-            extract($defaults);
         }
 
+        extract($options);
+
         if (!isset($is_local)) {
             $is_local = Notice::LOCAL_PUBLIC;
         }
@@ -600,6 +604,26 @@ class Notice extends Managed_DataObject
 
                 // Scope set below
             }
+
+            // If we don't know the reply, we might know the conversation!
+            // This will happen if a known remote user replies to an
+            // unknown remote user - within a known conversation.
+            if (empty($notice->conversation) and !empty($options['conversation'])) {
+                $conv = Conversation::getKV('uri', $options['conversation']);
+                if ($conv instanceof Conversation) {
+                    common_debug('Conversation stitched together from (probably) reply to unknown remote user. Activity creation time ('.$notice->created.') should maybe be compared to conversation creation time ('.$conv->created.').');
+                    $notice->conversation = $conv->id;
+                } else {
+                    // Conversation URI was not found, so we must create it. But we can't create it
+                    // until we have a Notice ID because of the database layout...
+                    $notice->tmp_conv_uri = $options['conversation'];
+                }
+            } else {
+                // If we're not using the attached conversation URI let's remove it
+                // so we don't mistake ourselves later, when creating our own Conversation.
+                // This implies that the notice knows which conversation it belongs to.
+                $options['conversation'] = null;
+            }
         }
 
         if (!empty($lat) && !empty($lon)) {
@@ -649,6 +673,15 @@ class Notice extends Managed_DataObject
 
             try {
                 $notice->insert();  // throws exception on failure
+                // If it's not part of a conversation, it's
+                // the beginning of a new conversation.
+                if (empty($notice->conversation)) { 
+                    $orig = clone($notice);
+                    // $act->context->conversation will be null if it was not provided
+                    $conv = Conversation::create($notice, $options['conversation']);
+                    $notice->conversation = $conv->id;
+                    $notice->update($orig);
+                }
             } catch (Exception $e) {
                 // Let's test if we managed initial insert, which would imply
                 // failing on some update-part (check 'insert()'). Delete if
@@ -656,6 +689,7 @@ class Notice extends Managed_DataObject
                 if (!empty($notice->id)) {
                     $notice->delete();
                 }
+                throw $e;
             }
         }
 
@@ -712,24 +746,22 @@ class Notice extends Managed_DataObject
                                         'and post again in a few minutes.'));
         }
 
-/* This interferes with stuff like Favorites from old StatusNet installations (first object in objects is the favored notice)
         // Get ActivityObject properties
-        $actobj = count($act->objects)==1 ? $act->objects[0] : null;
-        if (!is_null($actobj) && $actobj->id) {
-            $options['uri'] = $actobj->id;
-            if ($actobj->link) {
-                $options['url'] = $actobj->link;
-            } elseif ($act->link) {
-                $options['url'] = $act->link;
-            } elseif (preg_match('!^https?://!', $actobj->id)) {
-                $options['url'] = $actobj->id;
-            }
-        } else {
+        if (!empty($act->id)) {
             // implied object
             $options['uri'] = $act->id;
             $options['url'] = $act->link;
+        } else {
+            $actobj = count($act->objects)==1 ? $act->objects[0] : null;
+            if (!is_null($actobj) && !empty($actobj->id)) {
+                $options['uri'] = $actobj->id;
+                if ($actobj->link) {
+                    $options['url'] = $actobj->link;
+                } elseif (preg_match('!^https?://!', $actobj->id)) {
+                    $options['url'] = $actobj->id;
+                }
+            }
         }
-*/
 
         $defaults = array(
                           'groups'   => array(),
@@ -768,6 +800,12 @@ class Notice extends Managed_DataObject
         $stored->url = $url;
         $stored->verb = $act->verb;
 
+        // Use the local user's shortening preferences, if applicable.
+        $stored->rendered = $actor->isLocal()
+                                ? $actor->shortenLinks($act->content)
+                                : $act->content;
+        $stored->content = common_strip_html($stored->rendered);
+
         $autosource = common_config('public', 'autosource');
 
         // Sandboxed are non-false, but not 1, either
@@ -853,13 +891,21 @@ class Notice extends Managed_DataObject
 
             try {
                 $stored->insert();    // throws exception on error
+                $orig = clone($stored); // for updating later in this try clause
+
+                // If it's not part of a conversation, it's
+                // the beginning of a new conversation.
+                if (empty($stored->conversation)) {
+                    // $act->context->conversation will be null if it was not provided
+                    $conv = Conversation::create($stored, $act->context->conversation);
+                    $stored->conversation = $conv->id;
+                }
 
                 $object = null;
                 Event::handle('StoreActivityObject', array($act, $stored, $options, &$object));
                 if (empty($object)) {
                     throw new ServerException('No object from StoreActivityObject '.$stored->uri . ': '.$act->asString());
                 }
-                $orig = clone($stored);
                 $stored->object_type = ActivityUtils::resolveUri($object->getObjectType(), true);
                 $stored->update($orig);
             } catch (Exception $e) {
@@ -1228,10 +1274,16 @@ class Notice extends Managed_DataObject
         }
 
         // If this isn't a reply to anything, then it's its own
-        // root.
+        // root if it's the earliest notice in the conversation:
 
         if (empty($this->reply_to)) {
-            return $this;
+            $root = new Notice;
+            $root->conversation = $this->conversation;
+            $root->orderBy('notice.created ASC');
+            $root->find();
+            $root->fetch();
+            $root->free();
+            return $root;
         }
         
         if (is_null($profile)) {
@@ -1676,6 +1728,8 @@ class Notice extends Managed_DataObject
 
         $recipientIds = $this->getReplies();
         if (Event::handle('StartNotifyMentioned', array($this, &$recipientIds))) {
+            require_once INSTALLDIR.'/lib/mail.php';
+
             foreach ($recipientIds as $recipientId) {
                 $user = User::getKV('id', $recipientId);
                 if ($user instanceof User) {
@@ -1731,12 +1785,12 @@ class Notice extends Managed_DataObject
     /**
      * Convert a notice into an activity for export.
      *
-     * @param User $cur Current user
+     * @param Profile $scoped   The currently logged in/scoped profile
      *
      * @return Activity activity object representing this Notice.
      */
 
-    function asActivity($cur=null)
+    function asActivity(Profile $scoped=null)
     {
         $act = self::cacheGet(Cache::codeKey('notice:as-activity:'.$this->id));
 
@@ -1745,7 +1799,7 @@ class Notice extends Managed_DataObject
         }
         $act = new Activity();
 
-        if (Event::handle('StartNoticeAsActivity', array($this, &$act))) {
+        if (Event::handle('StartNoticeAsActivity', array($this, $act, $scoped))) {
 
             $act->id      = $this->uri;
             $act->time    = strtotime($this->created);
@@ -1760,14 +1814,19 @@ class Notice extends Managed_DataObject
             $profile = $this->getProfile();
 
             $act->actor            = $profile->asActivityObject();
-            $act->actor->extra[]   = $profile->profileInfo($cur);
+            $act->actor->extra[]   = $profile->profileInfo($scoped);
 
             $act->verb = $this->verb;
 
             if ($this->repeat_of) {
                 $repeated = Notice::getKV('id', $this->repeat_of);
                 if ($repeated instanceof Notice) {
-                    $act->objects[] = $repeated->asActivity($cur);
+                    // TRANS: A repeat activity's title. %1$s is repeater's nickname
+                    //        and %2$s is the repeated user's nickname.
+                    $act->title = sprintf(_('%1$s repeated a notice by %2$s'),
+                                          $this->getProfile()->getNickname(),
+                                          $repeated->getProfile()->getNickname());
+                    $act->objects[] = $repeated->asActivity($scoped);
                 }
             } else {
                 $act->objects[] = $this->asActivityObject();
@@ -1892,7 +1951,7 @@ class Notice extends Managed_DataObject
                 $act->editLink = $act->selfLink;
             }
 
-            Event::handle('EndNoticeAsActivity', array($this, &$act));
+            Event::handle('EndNoticeAsActivity', array($this, $act, $scoped));
         }
 
         self::cacheSet(Cache::codeKey('notice:as-activity:'.$this->id), $act);
@@ -1906,10 +1965,10 @@ class Notice extends Managed_DataObject
     function asAtomEntry($namespace=false,
                          $source=false,
                          $author=true,
-                         $cur=null)
+                         Profile $scoped=null)
     {
-        $act = $this->asActivity($cur);
-        $act->extra[] = $this->noticeInfo($cur);
+        $act = $this->asActivity($scoped);
+        $act->extra[] = $this->noticeInfo($scoped);
         return $act->asString($namespace, $author, $source);
     }
 
@@ -1919,12 +1978,12 @@ class Notice extends Managed_DataObject
      * Clients use some extra notice info in the atom stream.
      * This gives it to them.
      *
-     * @param User $cur Current user
+     * @param Profile $scoped   The currently logged in/scoped profile
      *
      * @return array representation of <statusnet:notice_info> element
      */
 
-    function noticeInfo($cur)
+    function noticeInfo(Profile $scoped=null)
     {
         // local notice ID (useful to clients for ordering)
 
@@ -1950,9 +2009,7 @@ class Notice extends Managed_DataObject
 
         // favorite and repeated
 
-        $scoped = null;
-        if (!empty($cur)) {
-            $scoped = $cur->getProfile();
+        if ($scoped instanceof Profile) {
             $noticeInfoAttr['repeated'] = ($scoped->hasRepeated($this)) ? "true" : "false";
         }
 
@@ -2092,6 +2149,18 @@ class Notice extends Managed_DataObject
                            $author->getNickname(),
                            $this->content);
 
+        $maxlen = self::maxContent();
+        if ($maxlen > 0 && mb_strlen($content) > $maxlen) {
+            // Web interface and current Twitter API clients will
+            // pull the original notice's text, but some older
+            // clients and RSS/Atom feeds will see this trimmed text.
+            //
+            // Unfortunately this is likely to lose tags or URLs
+            // at the end of long notices.
+            $content = mb_substr($content, 0, $maxlen - 4) . ' ...';
+        }     
+
+
         // Scope is same as this one's
         return self::saveNew($repeater->id,
                              $content,
@@ -2359,14 +2428,6 @@ class Notice extends Managed_DataObject
             $changed = true;
         }
 
-        // If it's not part of a conversation, it's
-        // the beginning of a new conversation.
-        if (empty($this->conversation)) {
-            $conv = Conversation::create($this);
-            $this->conversation = $conv->id;
-            $changed = true;
-        }
-
         if ($changed && $this->update($orig) === false) {
             common_log_db_error($notice, 'UPDATE', __FILE__);
             // TRANS: Server exception thrown when a notice cannot be updated.
@@ -2386,31 +2447,34 @@ class Notice extends Managed_DataObject
      */
     function getSource()
     {
+        if (empty($this->source)) {
+            return false;
+        }
+
         $ns = new Notice_source();
-        if (!empty($this->source)) {
-            switch ($this->source) {
-            case 'web':
-            case 'xmpp':
-            case 'mail':
-            case 'omb':
-            case 'system':
-            case 'api':
+        switch ($this->source) {
+        case 'web':
+        case 'xmpp':
+        case 'mail':
+        case 'omb':
+        case 'system':
+        case 'api':
+            $ns->code = $this->source;
+            break;
+        default:
+            $ns = Notice_source::getKV($this->source);
+            if (!$ns) {
+                $ns = new Notice_source();
                 $ns->code = $this->source;
-                break;
-            default:
-                $ns = Notice_source::getKV($this->source);
-                if (!$ns) {
-                    $ns = new Notice_source();
-                    $ns->code = $this->source;
-                    $app = Oauth_application::getKV('name', $this->source);
-                    if ($app) {
-                        $ns->name = $app->name;
-                        $ns->url  = $app->source_url;
-                    }
+                $app = Oauth_application::getKV('name', $this->source);
+                if ($app) {
+                    $ns->name = $app->name;
+                    $ns->url  = $app->source_url;
                 }
-                break;
             }
+            break;
         }
+
         return $ns;
     }