]> git.mxchange.org Git - quix0rs-gnu-social.git/blobdiff - classes/Notice.php
Merge branch 'master' into 0.9.x
[quix0rs-gnu-social.git] / classes / Notice.php
index a067cd3741fa4fb4b8e6fafff99b0f407cc44ad1..141ae8fd44883c3cb012214fd1a35b239d33801e 100644 (file)
@@ -109,6 +109,11 @@ class Notice extends Memcached_DataObject
         // @fixme we have some cases where things get re-run and so the
         // insert fails.
         $deleted = Deleted_notice::staticGet('id', $this->id);
+
+        if (!$deleted) {
+            $deleted = Deleted_notice::staticGet('uri', $this->uri);
+        }
+
         if (!$deleted) {
             $deleted = new Deleted_notice();
 
@@ -130,6 +135,7 @@ class Notice extends Memcached_DataObject
             $this->clearFaves();
             $this->clearTags();
             $this->clearGroupInboxes();
+            $this->clearFiles();
 
             // NOTE: we don't clear inboxes
             // NOTE: we don't clear queue items
@@ -147,7 +153,7 @@ class Notice extends Memcached_DataObject
     function saveTags()
     {
         /* extract all #hastags */
-        $count = preg_match_all('/(?:^|\s)#([\pL\pN_\-\.]{1,64})/', strtolower($this->content), $match);
+        $count = preg_match_all('/(?:^|\s)#([\pL\pN_\-\.]{1,64})/u', strtolower($this->content), $match);
         if (!$count) {
             return true;
         }
@@ -234,6 +240,8 @@ class Notice extends Memcached_DataObject
      *                           in place of extracting # tags from content
      *              array 'urls' list of attached/referred URLs to save with the
      *                           notice in place of extracting links from content
+     *              boolean 'distribute' whether to distribute the notice, default true
+     *
      * @fixme tag override
      *
      * @return Notice
@@ -243,7 +251,8 @@ class Notice extends Memcached_DataObject
         $defaults = array('uri' => null,
                           'url' => null,
                           'reply_to' => null,
-                          'repeat_of' => null);
+                          'repeat_of' => null,
+                          'distribute' => true);
 
         if (!empty($options)) {
             $options = $options + $defaults;
@@ -426,8 +435,10 @@ class Notice extends Memcached_DataObject
             $notice->saveUrls();
         }
 
-        // Prepare inbox delivery, may be queued to background.
-        $notice->distribute();
+        if ($distribute) {
+            // Prepare inbox delivery, may be queued to background.
+            $notice->distribute();
+        }
 
         return $notice;
     }
@@ -435,7 +446,10 @@ class Notice extends Memcached_DataObject
     function blowOnInsert($conversation = false)
     {
         self::blow('profile:notice_ids:%d', $this->profile_id);
-        self::blow('public');
+
+        if ($this->isPublic()) {
+            self::blow('public');
+        }
 
         // XXX: Before we were blowing the casche only if the notice id
         // was not the root of the conversation.  What to do now?
@@ -470,7 +484,10 @@ class Notice extends Memcached_DataObject
         $this->blowOnInsert();
 
         self::blow('profile:notice_ids:%d;last', $this->profile_id);
-        self::blow('public;last');
+
+        if ($this->isPublic()) {
+            self::blow('public;last');
+        }
     }
 
     /** save all urls in the notice to the db
@@ -654,7 +671,7 @@ class Notice extends Memcached_DataObject
         $notice->selectAdd(); // clears it
         $notice->selectAdd('id');
 
-        $notice->orderBy('id DESC');
+        $notice->orderBy('created DESC, id DESC');
 
         if (!is_null($offset)) {
             $notice->limit($offset, $limit);
@@ -668,13 +685,8 @@ class Notice extends Memcached_DataObject
             $notice->whereAdd('is_local !='. Notice::GATEWAY);
         }
 
-        if ($since_id != 0) {
-            $notice->whereAdd('id > ' . $since_id);
-        }
-
-        if ($max_id != 0) {
-            $notice->whereAdd('id <= ' . $max_id);
-        }
+        Notice::addWhereSinceId($notice, $since_id);
+        Notice::addWhereMaxId($notice, $max_id);
 
         $ids = array();
 
@@ -709,19 +721,14 @@ class Notice extends Memcached_DataObject
 
         $notice->conversation = $id;
 
-        $notice->orderBy('id DESC');
+        $notice->orderBy('created DESC, id DESC');
 
         if (!is_null($offset)) {
             $notice->limit($offset, $limit);
         }
 
-        if ($since_id != 0) {
-            $notice->whereAdd('id > ' . $since_id);
-        }
-
-        if ($max_id != 0) {
-            $notice->whereAdd('id <= ' . $max_id);
-        }
+        Notice::addWhereSinceId($notice, $since_id);
+        Notice::addWhereMaxId($notice, $max_id);
 
         $ids = array();
 
@@ -818,9 +825,18 @@ class Notice extends Memcached_DataObject
 
         // Exclude any deleted, non-local, or blocking recipients.
         $profile = $this->getProfile();
+        $originalProfile = null;
+        if ($this->repeat_of) {
+            // Check blocks against the original notice's poster as well.
+            $original = Notice::staticGet('id', $this->repeat_of);
+            if ($original) {
+                $originalProfile = $original->getProfile();
+            }
+        }
         foreach ($ni as $id => $source) {
             $user = User::staticGet('id', $id);
-            if (empty($user) || $user->hasBlocked($profile)) {
+            if (empty($user) || $user->hasBlocked($profile) ||
+                ($originalProfile && $user->hasBlocked($originalProfile))) {
                 unset($ni[$id]);
             }
         }
@@ -948,7 +964,7 @@ class Notice extends Memcached_DataObject
         $groups = array();
 
         /* extract all !group */
-        $count = preg_match_all('/(?:^|\s)!([A-Za-z0-9]{1,64})/',
+        $count = preg_match_all('/(?:^|\s)!(' . Nickname::DISPLAY_FMT . ')/',
                                 strtolower($this->content),
                                 $match);
         if (!$count) {
@@ -1056,6 +1072,7 @@ class Notice extends Memcached_DataObject
 
             $reply->notice_id  = $this->id;
             $reply->profile_id = $profile->id;
+            $reply->modified   = $this->created;
 
             common_log(LOG_INFO, __METHOD__ . ": saving reply: notice $this->id to profile $profile->id");
 
@@ -1116,6 +1133,7 @@ class Notice extends Memcached_DataObject
 
                 $reply->notice_id  = $this->id;
                 $reply->profile_id = $mentioned->id;
+                $reply->modified   = $this->created;
 
                 $id = $reply->insert();
 
@@ -1230,33 +1248,33 @@ class Notice extends Memcached_DataObject
      * Convert a notice into an activity for export.
      *
      * @param User $cur Current user
-     * 
+     *
      * @return Activity activity object representing this Notice.
      */
 
-    function asActivity()
+    function asActivity($cur)
     {
         $act = self::cacheGet(Cache::codeKey('notice:as-activity:'.$this->id));
 
         if (!empty($act)) {
             return $act;
         }
-
         $act = new Activity();
-       
+
         if (Event::handle('StartNoticeAsActivity', array($this, &$act))) {
 
             $profile = $this->getProfile();
-           
-            $act->actor     = ActivityObject::fromProfile($profile);
-            $act->verb      = ActivityVerb::POST;
-            $act->objects[] = ActivityObject::fromNotice($this);
+
+            $act->actor            = ActivityObject::fromProfile($profile);
+            $act->actor->extra[]   = $profile->profileInfo($cur);
+            $act->verb             = ActivityVerb::POST;
+            $act->objects[]        = ActivityObject::fromNotice($this);
 
             // XXX: should this be handled by default processing for object entry?
 
             $act->time    = strtotime($this->created);
             $act->link    = $this->bestUrl();
-           
+
             $act->content = common_xml_safe_str($this->rendered);
             $act->id      = $this->uri;
             $act->title   = common_xml_safe_str($this->content);
@@ -1283,9 +1301,9 @@ class Notice extends Memcached_DataObject
                     $act->enclosures[] = $enclosure;
                 }
             }
-            
+
             $ctx = new ActivityContext();
-           
+
             if (!empty($this->reply_to)) {
                 $reply = Notice::staticGet('id', $this->reply_to);
                 if (!empty($reply)) {
@@ -1293,31 +1311,31 @@ class Notice extends Memcached_DataObject
                     $ctx->replyToUrl = $reply->bestUrl();
                 }
             }
-           
+
             $ctx->location = $this->getLocation();
-           
+
             $conv = null;
-           
+
             if (!empty($this->conversation)) {
                 $conv = Conversation::staticGet('id', $this->conversation);
                 if (!empty($conv)) {
                     $ctx->conversation = $conv->uri;
                 }
             }
-           
+
             $reply_ids = $this->getReplies();
-           
+
             foreach ($reply_ids as $id) {
                 $profile = Profile::staticGet('id', $id);
                 if (!empty($profile)) {
                     $ctx->attention[] = $profile->getUri();
                 }
             }
-           
+
             $groups = $this->getGroups();
-           
+
             foreach ($groups as $group) {
-                $ctx->attention[] = $group->uri;
+                $ctx->attention[] = $group->getUri();
             }
 
             // XXX: deprecated; use ActivityVerb::SHARE instead
@@ -1329,7 +1347,7 @@ class Notice extends Memcached_DataObject
                 $ctx->forwardID  = $repeat->uri;
                 $ctx->forwardUrl = $repeat->bestUrl();
             }
-           
+
             $act->context = $ctx;
 
             // Source
@@ -1339,7 +1357,7 @@ class Notice extends Memcached_DataObject
             if (!empty($atom_feed)) {
 
                 $act->source = new ActivitySource();
-                   
+
                 // XXX: we should store the actual feed ID
 
                 $act->source->id = $atom_feed;
@@ -1352,7 +1370,7 @@ class Notice extends Memcached_DataObject
                 $act->source->links['self']      = $atom_feed;
 
                 $act->source->icon = $profile->avatarUrl(AVATAR_PROFILE_SIZE);
-                   
+
                 $notice = $profile->getCurrentNotice();
 
                 if (!empty($notice)) {
@@ -1374,7 +1392,7 @@ class Notice extends Memcached_DataObject
 
             Event::handle('EndNoticeAsActivity', array($this, &$act));
         }
-       
+
         self::cacheSet(Cache::codeKey('notice:as-activity:'.$this->id), $act);
 
         return $act;
@@ -1385,17 +1403,17 @@ class Notice extends Memcached_DataObject
 
     function asAtomEntry($namespace=false,
                          $source=false,
-                         $author=true, 
+                         $author=true,
                          $cur=null)
     {
-        $act = $this->asActivity();
+        $act = $this->asActivity($cur);
         $act->extra[] = $this->noticeInfo($cur);
         return $act->asString($namespace, $author, $source);
     }
 
     /**
      * Extra notice info for atom entries
-     * 
+     *
      * Clients use some extra notice info in the atom stream.
      * This gives it to them.
      *
@@ -1695,10 +1713,10 @@ class Notice extends Memcached_DataObject
 
         $notice->repeat_of = $this->id;
 
-        $notice->orderBy('created'); // NB: asc!
+        $notice->orderBy('created, id'); // NB: asc!
 
-        if (!is_null($offset)) {
-            $notice->limit($offset, $limit);
+        if (!is_null($limit)) {
+            $notice->limit(0, $limit);
         }
 
         $ids = array();
@@ -1785,6 +1803,21 @@ class Notice extends Memcached_DataObject
         $reply->free();
     }
 
+    function clearFiles()
+    {
+        $f2p = new File_to_post();
+
+        $f2p->post_id = $this->id;
+
+        if ($f2p->find()) {
+            while ($f2p->fetch()) {
+                $f2p->delete();
+            }
+        }
+        // FIXME: decide whether to delete File objects
+        // ...and related (actual) files
+    }
+
     function clearRepeats()
     {
         $repeatNotice = new Notice();
@@ -1978,4 +2011,118 @@ class Notice extends Memcached_DataObject
         $d = new DateTime($dateStr, new DateTimeZone('UTC'));
         return $d->format(DATE_W3C);
     }
+
+    /**
+     * Look up the creation timestamp for a given notice ID, even
+     * if it's been deleted.
+     *
+     * @param int $id
+     * @return mixed string recorded creation timestamp, or false if can't be found
+     */
+    public static function getAsTimestamp($id)
+    {
+        if (!$id) {
+            return false;
+        }
+
+        $notice = Notice::staticGet('id', $id);
+        if ($notice) {
+            return $notice->created;
+        }
+
+        $deleted = Deleted_notice::staticGet('id', $id);
+        if ($deleted) {
+            return $deleted->created;
+        }
+
+        return false;
+    }
+
+    /**
+     * Build an SQL 'where' fragment for timestamp-based sorting from a since_id
+     * parameter, matching notices posted after the given one (exclusive).
+     *
+     * If the referenced notice can't be found, will return false.
+     *
+     * @param int $id
+     * @param string $idField
+     * @param string $createdField
+     * @return mixed string or false if no match
+     */
+    public static function whereSinceId($id, $idField='id', $createdField='created')
+    {
+        $since = Notice::getAsTimestamp($id);
+        if ($since) {
+            return sprintf("($createdField = '%s' and $idField > %d) or ($createdField > '%s')", $since, $id, $since);
+        }
+        return false;
+    }
+
+    /**
+     * Build an SQL 'where' fragment for timestamp-based sorting from a since_id
+     * parameter, matching notices posted after the given one (exclusive), and
+     * if necessary add it to the data object's query.
+     *
+     * @param DB_DataObject $obj
+     * @param int $id
+     * @param string $idField
+     * @param string $createdField
+     * @return mixed string or false if no match
+     */
+    public static function addWhereSinceId(DB_DataObject $obj, $id, $idField='id', $createdField='created')
+    {
+        $since = self::whereSinceId($id, $idField, $createdField);
+        if ($since) {
+            $obj->whereAdd($since);
+        }
+    }
+
+    /**
+     * Build an SQL 'where' fragment for timestamp-based sorting from a max_id
+     * parameter, matching notices posted before the given one (inclusive).
+     *
+     * If the referenced notice can't be found, will return false.
+     *
+     * @param int $id
+     * @param string $idField
+     * @param string $createdField
+     * @return mixed string or false if no match
+     */
+    public static function whereMaxId($id, $idField='id', $createdField='created')
+    {
+        $max = Notice::getAsTimestamp($id);
+        if ($max) {
+            return sprintf("($createdField < '%s') or ($createdField = '%s' and $idField <= %d)", $max, $max, $id);
+        }
+        return false;
+    }
+
+    /**
+     * Build an SQL 'where' fragment for timestamp-based sorting from a max_id
+     * parameter, matching notices posted before the given one (inclusive), and
+     * if necessary add it to the data object's query.
+     *
+     * @param DB_DataObject $obj
+     * @param int $id
+     * @param string $idField
+     * @param string $createdField
+     * @return mixed string or false if no match
+     */
+    public static function addWhereMaxId(DB_DataObject $obj, $id, $idField='id', $createdField='created')
+    {
+        $max = self::whereMaxId($id, $idField, $createdField);
+        if ($max) {
+            $obj->whereAdd($max);
+        }
+    }
+
+    function isPublic()
+    {
+        if (common_config('public', 'localonly')) {
+            return ($this->is_local == Notice::LOCAL_PUBLIC);
+        } else {
+            return (($this->is_local != Notice::LOCAL_NONPUBLIC) &&
+                    ($this->is_local != Notice::GATEWAY));
+        }
+    }
 }