]> git.mxchange.org Git - quix0rs-gnu-social.git/blobdiff - classes/Notice.php
say that it's OK to do OpenID login in private mode
[quix0rs-gnu-social.git] / classes / Notice.php
index e975cab93ce6b44e8bef11375c866ec1d3304382..9578d87b2b149925e8fefffc17c43abd3b3c75b3 100644 (file)
@@ -32,7 +32,6 @@ define('NOTICE_CACHE_WINDOW', 61);
 define('NOTICE_LOCAL_PUBLIC', 1);
 define('NOTICE_REMOTE_OMB', 0);
 define('NOTICE_LOCAL_NONPUBLIC', -1);
-define('NOTICE_GATEWAY', -2);
 
 define('MAX_BOXCARS', 128);
 
@@ -63,6 +62,8 @@ class Notice extends Memcached_DataObject
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
 
+    const GATEWAY = -2;
+
     function getProfile()
     {
         return Profile::staticGet('id', $this->profile_id);
@@ -74,7 +75,21 @@ class Notice extends Memcached_DataObject
         $this->blowFavesCache(true);
         $this->blowSubsCache(true);
 
+        // For auditing purposes, save a record that the notice
+        // was deleted.
+
+        $deleted = new Deleted_notice();
+
+        $deleted->id         = $this->id;
+        $deleted->profile_id = $this->profile_id;
+        $deleted->uri        = $this->uri;
+        $deleted->created    = $this->created;
+        $deleted->deleted    = common_sql_now();
+
         $this->query('BEGIN');
+
+        $deleted->insert();
+
         //Null any notices that are replies to this notice
         $this->query(sprintf("UPDATE notice set reply_to = null WHERE reply_to = %d", $this->id));
         $related = array('Reply',
@@ -97,13 +112,20 @@ class Notice extends Memcached_DataObject
     function saveTags()
     {
         /* extract all #hastags */
-        $count = preg_match_all('/(?:^|\s)#([A-Za-z0-9_\-\.]{1,64})/', strtolower($this->content), $match);
+        $count = preg_match_all('/(?:^|\s)#([\pL\pN_\-\.]{1,64})/', strtolower($this->content), $match);
         if (!$count) {
             return true;
         }
 
+        //turn each into their canonical tag
+        //this is needed to remove dupes before saving e.g. #hash.tag = #hashtag
+        $hashtags = array();
+        for($i=0; $i<count($match[1]); $i++) {
+            $hashtags[] = common_canonical_tag($match[1][$i]);
+        }
+
         /* Add them to the database */
-        foreach(array_unique($match[1]) as $hashtag) {
+        foreach(array_unique($hashtags) as $hashtag) {
             /* elide characters we don't want in the tag */
             $this->saveTag($hashtag);
         }
@@ -112,8 +134,6 @@ class Notice extends Memcached_DataObject
 
     function saveTag($hashtag)
     {
-        $hashtag = common_canonical_tag($hashtag);
-
         $tag = new Notice_tag();
         $tag->notice_id = $this->id;
         $tag->tag = $hashtag;
@@ -176,29 +196,30 @@ class Notice extends Memcached_DataObject
             $notice->is_local = $is_local;
         }
 
-               $notice->query('BEGIN');
-
-               $notice->reply_to = $reply_to;
         if (!empty($created)) {
             $notice->created = $created;
         } else {
             $notice->created = common_sql_now();
         }
+
                $notice->content = $final;
                $notice->rendered = common_render_content($final, $notice);
                $notice->source = $source;
                $notice->uri = $uri;
 
-        if (!empty($reply_to)) {
-            $reply_notice = Notice::staticGet('id', $reply_to);
-            if (!empty($reply_notice)) {
-                $notice->reply_to = $reply_to;
-                $notice->conversation = $reply_notice->conversation;
-            }
+               $notice->reply_to = self::getReplyTo($reply_to, $profile_id, $source, $final);
+
+        if (!empty($notice->reply_to)) {
+            $reply = Notice::staticGet('id', $notice->reply_to);
+            $notice->conversation = $reply->conversation;
         }
 
         if (Event::handle('StartNoticeSave', array(&$notice))) {
 
+            // XXX: some of these functions write to the DB
+
+            $notice->query('BEGIN');
+
             $id = $notice->insert();
 
             if (!$id) {
@@ -206,18 +227,33 @@ class Notice extends Memcached_DataObject
                 return _('Problem saving notice.');
             }
 
-            # Update the URI after the notice is in the database
-            if (!$uri) {
-                $orig = clone($notice);
+            // Update ID-dependent columns: URI, conversation
+
+            $orig = clone($notice);
+
+            $changed = false;
+
+            if (empty($uri)) {
                 $notice->uri = common_notice_uri($notice);
+                $changed = true;
+            }
 
+            // If it's not part of a conversation, it's
+            // the beginning of a new conversation.
+
+            if (empty($notice->conversation)) {
+                $notice->conversation = $notice->id;
+                $changed = true;
+            }
+
+            if ($changed) {
                 if (!$notice->update($orig)) {
                     common_log_db_error($notice, 'UPDATE', __FILE__);
                     return _('Problem saving notice.');
                 }
             }
 
-            # XXX: do we need to change this for remote users?
+            // XXX: do we need to change this for remote users?
 
             $notice->saveReplies();
             $notice->saveTags();
@@ -225,8 +261,13 @@ class Notice extends Memcached_DataObject
             $notice->addToInboxes();
 
             $notice->saveUrls();
+
+            // FIXME: why do we have to re-render the content?
+            // Remove this if it's not necessary.
+
             $orig2 = clone($notice);
-               $notice->rendered = common_render_content($final, $notice);
+
+            $notice->rendered = common_render_content($final, $notice);
             if (!$notice->update($orig2)) {
                 common_log_db_error($notice, 'UPDATE', __FILE__);
                 return _('Problem saving notice.');
@@ -283,9 +324,9 @@ class Notice extends Memcached_DataObject
         $notice->profile_id = $profile_id;
         $notice->content = $content;
         if (common_config('db','type') == 'pgsql')
-            $notice->whereAdd('extract(epoch from now() - created) < ' . common_config('site', 'dupelimit'));
+          $notice->whereAdd('extract(epoch from now() - created) < ' . common_config('site', 'dupelimit'));
         else
-            $notice->whereAdd('now() - created < ' . common_config('site', 'dupelimit'));
+          $notice->whereAdd('now() - created < ' . common_config('site', 'dupelimit'));
 
         $cnt = $notice->count();
         return ($cnt == 0);
@@ -356,6 +397,8 @@ class Notice extends Memcached_DataObject
         $this->blowTagCache($blowLast);
         $this->blowGroupCache($blowLast);
         $this->blowConversationCache($blowLast);
+        $profile = Profile::staticGet($this->profile_id);
+        $profile->blowNoticeCount();
     }
 
     function blowConversationCache($blowLast=false)
@@ -871,8 +914,11 @@ class Notice extends Memcached_DataObject
                 if ($cnt > 0) {
                     $qry .= ', ';
                 }
-                $qry .= '('.$id.', '.$this->id.', '.$source.', "'.$this->created.'") ';
+                $qry .= '('.$id.', '.$this->id.', '.$source.", '".$this->created. "') ";
                 $cnt++;
+                if (rand() % NOTICE_INBOX_SOFT_LIMIT == 0) {
+                    Notice_inbox::gc($id);
+                }
                 if ($cnt >= MAX_BOXCARS) {
                     $inbox = new Notice_inbox();
                     $inbox->query($qry);
@@ -894,10 +940,14 @@ class Notice extends Memcached_DataObject
     {
         $user = new User();
 
+        if(common_config('db','quote_identifiers'))
+          $user_table = '"user"';
+        else $user_table = 'user';
+
         $qry =
           'SELECT id ' .
-          'FROM user JOIN subscription '.
-          'ON user.id = subscription.subscriber ' .
+          'FROM '. $user_table .' JOIN subscription '.
+          'ON '. $user_table .'.id = subscription.subscriber ' .
           'WHERE subscription.subscribed = %d ';
 
         $user->query(sprintf($qry, $this->profile_id));
@@ -1015,16 +1065,6 @@ class Notice extends Memcached_DataObject
             if (!$recipient) {
                 continue;
             }
-            if ($i == 0 && ($recipient->id != $sender->id) && !$this->reply_to) { // Don't save reply to self
-                $reply_for = $recipient;
-                $recipient_notice = $reply_for->getCurrentNotice();
-                if ($recipient_notice) {
-                    $orig = clone($this);
-                    $this->reply_to = $recipient_notice->id;
-                    $this->conversation = $recipient_notice->conversation;
-                    $this->update($orig);
-                }
-            }
             // Don't save replies from blocked profile to local user
             $recipient_user = User::staticGet('id', $recipient->id);
             if ($recipient_user && $recipient_user->hasBlocked($sender)) {
@@ -1071,14 +1111,6 @@ class Notice extends Memcached_DataObject
             }
         }
 
-        // If it's not a reply, make it the root of a new conversation
-
-        if (empty($this->conversation)) {
-            $orig = clone($this);
-            $this->conversation = $this->id;
-            $this->update($orig);
-        }
-
         foreach (array_keys($replied) as $recipient) {
             $user = User::staticGet('id', $recipient);
             if ($user) {
@@ -1168,11 +1200,13 @@ class Notice extends Memcached_DataObject
         $attachments = $this->attachments();
         if($attachments){
             foreach($attachments as $attachment){
-                $attributes = array('rel'=>'enclosure','href'=>$attachment->url,'type'=>$attachment->mimetype,'length'=>$attachment->size);
-                if($attachment->title){
-                    $attributes['title']=$attachment->title;
+                if ($attachment->isEnclosure()) {
+                    $attributes = array('rel'=>'enclosure','href'=>$attachment->url,'type'=>$attachment->mimetype,'length'=>$attachment->size);
+                    if($attachment->title){
+                        $attributes['title']=$attachment->title;
+                    }
+                    $xs->element('link', $attributes, null);
                 }
-                $xs->element('link', $attributes, null);
             }
         }
 
@@ -1248,4 +1282,76 @@ class Notice extends Memcached_DataObject
 
         return $ids;
     }
+
+    /**
+     * Determine which notice, if any, a new notice is in reply to.
+     *
+     * For conversation tracking, we try to see where this notice fits
+     * in the tree. Rough algorithm is:
+     *
+     * if (reply_to is set and valid) {
+     *     return reply_to;
+     * } else if ((source not API or Web) and (content starts with "T NAME" or "@name ")) {
+     *     return ID of last notice by initial @name in content;
+     * }
+     *
+     * Note that all @nickname instances will still be used to save "reply" records,
+     * so the notice shows up in the mentioned users' "replies" tab.
+     *
+     * @param integer $reply_to   ID passed in by Web or API
+     * @param integer $profile_id ID of author
+     * @param string  $source     Source tag, like 'web' or 'gwibber'
+     * @param string  $content    Final notice content
+     *
+     * @return integer ID of replied-to notice, or null for not a reply.
+     */
+
+    static function getReplyTo($reply_to, $profile_id, $source, $content)
+    {
+        static $lb = array('xmpp', 'mail', 'sms', 'omb');
+
+        // If $reply_to is specified, we check that it exists, and then
+        // return it if it does
+
+        if (!empty($reply_to)) {
+            $reply_notice = Notice::staticGet('id', $reply_to);
+            if (!empty($reply_notice)) {
+                return $reply_to;
+            }
+        }
+
+        // If it's not a "low bandwidth" source (one where you can't set
+        // a reply_to argument), we return. This is mostly web and API
+        // clients.
+
+        if (!in_array($source, $lb)) {
+            return null;
+        }
+
+        // Is there an initial @ or T?
+
+        if (preg_match('/^T ([A-Z0-9]{1,64}) /', $content, $match) ||
+            preg_match('/^@([a-z0-9]{1,64})\s+/', $content, $match)) {
+            $nickname = common_canonical_nickname($match[1]);
+        } else {
+            return null;
+        }
+
+        // Figure out who that is.
+
+        $sender = Profile::staticGet('id', $profile_id);
+        $recipient = common_relative_profile($sender, $nickname, common_sql_now());
+
+        if (empty($recipient)) {
+            return null;
+        }
+
+        // Get their last notice
+
+        $last = $recipient->getCurrentNotice();
+
+        if (!empty($last)) {
+            return $last->id;
+        }
+    }
 }