]> git.mxchange.org Git - quix0rs-gnu-social.git/blobdiff - classes/Notice.php
Improve ShownoticeAction remote redirect code
[quix0rs-gnu-social.git] / classes / Notice.php
index df05ce465061dd0eacf2fe8f5bf9e2c0e9f04da3..e46ed227a179cbb4cd4f9119a2b2bcb4a7e13ffd 100644 (file)
  * @license  GNU Affero General Public License http://www.gnu.org/licenses/
  */
 
-if (!defined('STATUSNET') && !defined('LACONICA')) {
-    exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
 
 /**
  * Table Definition for notice
  */
-require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
 
 /* We keep 200 notices, the max number of notices available per API request,
  * in the memcached cache. */
@@ -145,24 +142,24 @@ class Notice extends Managed_DataObject
     const FOLLOWER_SCOPE  = 8;
 
     protected $_profile = -1;
-
+    
     public function getProfile()
     {
         if ($this->_profile === -1) {
             $this->_setProfile(Profile::getKV('id', $this->profile_id));
         }
-        if (!$this->_profile instanceof Profile) {
-            throw new NoProfileException($this->profile_id);
-        }
         return $this->_profile;
     }
     
-    function _setProfile(Profile $profile)
+    public function _setProfile(Profile $profile=null)
     {
+        if (!$profile instanceof Profile) {
+            throw new NoProfileException($this->profile_id);
+        }
         $this->_profile = $profile;
     }
 
-    function delete()
+    function delete($useWhere=false)
     {
         // For auditing purposes, save a record that the notice
         // was deleted.
@@ -171,11 +168,11 @@ class Notice extends Managed_DataObject
         // insert fails.
         $deleted = Deleted_notice::getKV('id', $this->id);
 
-        if (!$deleted) {
+        if (!$deleted instanceof Deleted_notice) {
             $deleted = Deleted_notice::getKV('uri', $this->uri);
         }
 
-        if (!$deleted) {
+        if (!$deleted instanceof Deleted_notice) {
             $deleted = new Deleted_notice();
 
             $deleted->id         = $this->id;
@@ -197,12 +194,12 @@ class Notice extends Managed_DataObject
             $this->clearTags();
             $this->clearGroupInboxes();
             $this->clearFiles();
+            $this->clearAttentions();
 
-            // NOTE: we don't clear inboxes
             // NOTE: we don't clear queue items
         }
 
-        $result = parent::delete();
+        $result = parent::delete($useWhere);
 
         $this->blowOnDelete();
         return $result;
@@ -216,7 +213,35 @@ class Notice extends Managed_DataObject
     public function getUrl()
     {
         // The risk is we start having empty urls and non-http uris...
-        return $this->url ?: $this->uri;
+        // and we can't really handle any other protocol right now.
+        switch (true) {
+        case common_valid_http_url($this->url): // should we allow non-http/https URLs?
+            return $this->url;
+        case $this->isLocal():
+            // let's generate a valid link to our locally available notice on demand
+            return common_local_url('shownotice', array('notice' => $this->id), null, null, false);
+        case common_valid_http_url($this->uri):
+            return $this->uri;
+        default:
+            common_debug('No URL available for notice: id='.$this->id);
+            throw new InvalidUrlException($this->url);
+        }
+    }
+
+    public function get_object_type($canonical=false) {
+        return $canonical
+                ? ActivityObject::canonicalType($this->object_type)
+                : $this->object_type;
+    }
+
+    public static function getByUri($uri)
+    {
+        $notice = new Notice();
+        $notice->uri = $uri;
+        if (!$notice->find(true)) {
+            throw new NoResultException($notice);
+        }
+        return $notice;
     }
 
     /**
@@ -301,7 +326,7 @@ class Notice extends Managed_DataObject
      *              int 'location_ns' geoname namespace to interpret location_id
      *              int 'reply_to'; notice ID this is a reply to
      *              int 'repeat_of'; notice ID this is a repeat of
-     *              string 'uri' unique ID for notice; defaults to local notice URL
+     *              string 'uri' unique ID for notice; a unique tag uri (can be url or anything too)
      *              string 'url' permalink to notice; defaults to local notice URL
      *              string 'rendered' rendered HTML version of content
      *              array 'replies' list of profile URIs for reply delivery in
@@ -403,6 +428,16 @@ class Notice extends Managed_DataObject
             $notice->created = common_sql_now();
         }
 
+        if (!$notice->isLocal()) {
+            // Only do these checks for non-local notices. Local notices will generate these values later.
+            if (!common_valid_http_url($url)) {
+                common_debug('Bad notice URL: ['.$url.'], URI: ['.$uri.']. Cannot link back to original! This is normal for shared notices etc.');
+            }
+            if (empty($uri)) {
+                throw new ServerException('No URI for remote notice. Cannot accept that.');
+            }
+        }
+
         $notice->content = $final;
 
         $notice->source = $source;
@@ -410,9 +445,8 @@ class Notice extends Managed_DataObject
         $notice->url = $url;
 
         // Get the groups here so we can figure out replies and such
-
         if (!isset($groups)) {
-            $groups = self::groupsFromText($notice->content, $profile);
+            $groups = User_group::idsFromText($notice->content, $profile);
         }
 
         $reply = null;
@@ -572,8 +606,13 @@ class Notice extends Managed_DataObject
 
             $changed = false;
 
-            if (empty($uri)) {
-                $notice->uri = common_notice_uri($notice);
+            // We can only get here if it's a local notice, since remote notices
+            // should've bailed out earlier due to lacking a URI.
+            if (empty($notice->uri)) {
+                $notice->uri = sprintf('%s%s=%d:%s=%s',
+                                    TagURI::mint(),
+                                    'noticeId', $notice->id,
+                                    'objectType', $notice->get_object_type(true));
                 $changed = true;
             }
 
@@ -581,13 +620,13 @@ class Notice extends Managed_DataObject
             // the beginning of a new conversation.
 
             if (empty($notice->conversation)) {
-                $conv = Conversation::create();
+                $conv = Conversation::create($notice);
                 $notice->conversation = $conv->id;
                 $changed = true;
             }
 
             if ($changed) {
-                if (!$notice->update($orig)) {
+                if ($notice->update($orig) === false) {
                     common_log_db_error($notice, 'UPDATE', __FILE__);
                     // TRANS: Server exception thrown when a notice cannot be updated.
                     throw new ServerException(_('Problem saving notice.'));
@@ -973,30 +1012,23 @@ class Notice extends Managed_DataObject
             }
         }
 
-        if (is_null($groups)) {
-            $groups = $this->getGroups();
-        }
-
         if (is_null($recipients)) {
             $recipients = $this->getReplies();
         }
 
-        $users = $this->getSubscribedUsers();
-        $ptags = $this->getProfileTags();
-
-        // FIXME: kind of ignoring 'transitional'...
-        // we'll probably stop supporting inboxless mode
-        // in 0.9.x
-
         $ni = array();
 
         // Give plugins a chance to add folks in at start...
         if (Event::handle('StartNoticeWhoGets', array($this, &$ni))) {
 
+            $users = $this->getSubscribedUsers();
             foreach ($users as $id) {
                 $ni[$id] = NOTICE_INBOX_SOURCE_SUB;
             }
 
+            if (is_null($groups)) {
+                $groups = $this->getGroups();
+            }
             foreach ($groups as $group) {
                 $users = $group->getUserMembers();
                 foreach ($users as $id) {
@@ -1006,12 +1038,10 @@ class Notice extends Managed_DataObject
                 }
             }
 
-            foreach ($ptags as $ptag) {
-                $users = $ptag->getUserSubscribers();
-                foreach ($users as $id) {
-                    if (!array_key_exists($id, $ni)) {
-                        $ni[$id] = NOTICE_INBOX_SOURCE_PROFILE_TAG;
-                    }
+            $ptAtts = $this->getAttentionsFromProfileTags();
+            foreach ($ptAtts as $key=>$val) {
+                if (!array_key_exists($key, $ni)) {
+                    $ni[$key] = $val;
                 }
             }
 
@@ -1058,43 +1088,6 @@ class Notice extends Managed_DataObject
         return $ni;
     }
 
-    /**
-     * Adds this notice to the inboxes of each local user who should receive
-     * it, based on author subscriptions, group memberships, and @-replies.
-     *
-     * Warning: running a second time currently will make items appear
-     * multiple times in users' inboxes.
-     *
-     * @fixme make more robust against errors
-     * @fixme break up massive deliveries to smaller background tasks
-     *
-     * @param array $groups optional list of Group objects;
-     *              if left empty, will be loaded from group_inbox records
-     * @param array $recipient optional list of reply profile ids
-     *              if left empty, will be loaded from reply records
-     */
-    function addToInboxes(array $groups=null, array $recipients=null)
-    {
-        $ni = $this->whoGets($groups, $recipients);
-
-        $ids = array_keys($ni);
-
-        // We remove the author (if they're a local user),
-        // since we'll have already done this in distribute()
-
-        $i = array_search($this->profile_id, $ids);
-
-        if ($i !== false) {
-            unset($ids[$i]);
-        }
-
-        // Bulk insert
-
-        Inbox::bulkInsert($this->id, $ids);
-
-        return;
-    }
-
     function getSubscribedUsers()
     {
         $user = new User();
@@ -1135,6 +1128,19 @@ class Notice extends Managed_DataObject
         return $ptags;
     }
 
+    public function getAttentionsFromProfileTags()
+    {
+        $ni = array();
+        $ptags = $this->getProfileTags();
+        foreach ($ptags as $ptag) {
+            $users = $ptag->getUserSubscribers();
+            foreach ($users as $id) {
+                $ni[$id] = NOTICE_INBOX_SOURCE_PROFILE_TAG;
+            }
+        }
+        return $ni;
+    }
+
     /**
      * Record this notice to the given group inboxes for delivery.
      * Overrides the regular parsing of !group markup.
@@ -1154,7 +1160,7 @@ class Notice extends Managed_DataObject
         $groups = array();
         foreach (array_unique($group_ids) as $id) {
             $group = User_group::getKV('id', $id);
-            if ($group) {
+            if ($group instanceof User_group) {
                 common_log(LOG_ERR, "Local delivery to group id $id, $group->nickname");
                 $result = $this->addToGroupInbox($group);
                 if (!$result) {
@@ -1181,48 +1187,7 @@ class Notice extends Managed_DataObject
         return $groups;
     }
 
-    /**
-     * Parse !group delivery and record targets into group_inbox.
-     * @return array of Group objects
-     */
-    function saveGroups()
-    {
-        // Don't save groups for repeats
-
-        if (!empty($this->repeat_of)) {
-            return array();
-        }
-
-        $profile = $this->getProfile();
-
-        $groups = self::groupsFromText($this->content, $profile);
-
-        /* Add them to the database */
-
-        foreach ($groups as $group) {
-            /* XXX: remote groups. */
-
-            if (empty($group)) {
-                continue;
-            }
-
-
-            if ($profile->isMember($group)) {
-
-                $result = $this->addToGroupInbox($group);
-
-                if (!$result) {
-                    common_log_db_error($gi, 'INSERT', __FILE__);
-                }
-
-                $groups[] = clone($group);
-            }
-        }
-
-        return $groups;
-    }
-
-    function addToGroupInbox($group)
+    function addToGroupInbox(User_group $group)
     {
         $gi = Group_inbox::pkeyGet(array('group_id' => $group->id,
                                          'notice_id' => $this->id));
@@ -1445,7 +1410,7 @@ class Notice extends Managed_DataObject
 
     /**
      * Pull list of groups this notice needs to be delivered to,
-     * as previously recorded by saveGroups() or saveKnownGroups().
+     * as previously recorded by saveKnownGroups().
      *
      * @return array of Group objects
      */
@@ -1982,6 +1947,20 @@ class Notice extends Managed_DataObject
         return $options;
     }
 
+    function clearAttentions()
+    {
+        $att = new Attention();
+        $att->notice_id = $this->getID();
+
+        if ($att->find()) {
+            while ($att->fetch()) {
+                // Can't do delete() on the object directly since it won't remove all of it
+                $other = clone($att);
+                $other->delete();
+            }
+        }
+    }
+
     function clearReplies()
     {
         $replyNotice = new Notice();
@@ -2101,33 +2080,23 @@ class Notice extends Managed_DataObject
         // have to wait
         Event::handle('StartNoticeDistribute', array($this));
 
-        $user = User::getKV('id', $this->profile_id);
-        if ($user instanceof User) {
-            Inbox::insertNotice($user->id, $this->id);
-        }
-
-        if (common_config('queue', 'inboxes')) {
-            // If there's a failure, we want to _force_
-            // distribution at this point.
+        // If there's a failure, we want to _force_
+        // distribution at this point.
+        try {
+            $qm = QueueManager::get();
+            $qm->enqueue($this, 'distrib');
+        } catch (Exception $e) {
+            // If the exception isn't transient, this
+            // may throw more exceptions as DQH does
+            // its own enqueueing. So, we ignore them!
             try {
-                $qm = QueueManager::get();
-                $qm->enqueue($this, 'distrib');
+                $handler = new DistribQueueHandler();
+                $handler->handle($this);
             } catch (Exception $e) {
-                // If the exception isn't transient, this
-                // may throw more exceptions as DQH does
-                // its own enqueueing. So, we ignore them!
-                try {
-                    $handler = new DistribQueueHandler();
-                    $handler->handle($this);
-                } catch (Exception $e) {
-                    common_log(LOG_ERR, "emergency redistribution resulted in " . $e->getMessage());
-                }
-                // Re-throw so somebody smarter can handle it.
-                throw $e;
+                common_log(LOG_ERR, "emergency redistribution resulted in " . $e->getMessage());
             }
-        } else {
-            $handler = new DistribQueueHandler();
-            $handler->handle($this);
+            // Re-throw so somebody smarter can handle it.
+            throw $e;
         }
     }
 
@@ -2500,29 +2469,6 @@ class Notice extends Managed_DataObject
         return false;
     }
 
-    static function groupsFromText($text, $profile)
-    {
-        $groups = array();
-
-        /* extract all !group */
-        $count = preg_match_all('/(?:^|\s)!(' . Nickname::DISPLAY_FMT . ')/',
-                                strtolower($text),
-                                $match);
-
-        if (!$count) {
-            return $groups;
-        }
-
-        foreach (array_unique($match[1]) as $nickname) {
-            $group = User_group::getForNickname($nickname, $profile);
-            if ($group instanceof User_group && $profile->isMember($group)) {
-                $groups[] = $group->id;
-            }
-        }
-
-        return $groups;
-    }
-
     public function getParent()
     {
         $parent = Notice::getKV('id', $this->reply_to);
@@ -2568,10 +2514,15 @@ class Notice extends Managed_DataObject
        {
                $map = self::getProfiles($notices);
                
-               foreach ($notices as $notice) {
-                       if (array_key_exists($notice->profile_id, $map)) {
-                               $notice->_setProfile($map[$notice->profile_id]);    
-                       }
+               foreach ($notices as $entry=>$notice) {
+            try {
+                       if (array_key_exists($notice->profile_id, $map)) {
+                               $notice->_setProfile($map[$notice->profile_id]);
+                       }
+            } catch (NoProfileException $e) {
+                common_log(LOG_WARNING, "Failed to fill profile in Notice with non-existing entry for profile_id: {$e->id}");
+                unset($notices[$entry]);
+            }
                }
                
                return array_values($map);