]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Added saveActivity method to Notice class
authorMikael Nordfeldth <mmn@hethane.se>
Tue, 1 Jul 2014 09:42:08 +0000 (11:42 +0200)
committerMikael Nordfeldth <mmn@hethane.se>
Wed, 2 Jul 2014 09:38:37 +0000 (11:38 +0200)
saveActivity will accept an Activity which gets parsed and saved through
plugins. So when an ActivityHandlerPlugin (such as Favorite will be soon)
gets a feed to save, this will be the function called instead of saveNew.

classes/Notice.php
plugins/OStatus/OStatusPlugin.php
plugins/OStatus/classes/Ostatus_profile.php

index 3f138e1cfe7cfbb241015602edc5b6c53e128c8a..f11c670603dc202f8deb87c00a3ca2dc23c20a82 100644 (file)
@@ -627,41 +627,13 @@ class Notice extends Managed_DataObject
             $notice->object_type = $object_type;
         }
 
-        if (is_null($scope)) { // 0 is a valid value
-            if (!empty($reply)) {
-                $notice->scope = $reply->scope;
-            } else {
-                $notice->scope = self::defaultScope();
-            }
+        if (is_null($scope) && $reply instanceof Notice) {
+            $notice->scope = $reply->scope;
         } else {
             $notice->scope = $scope;
         }
 
-        // For private streams
-
-        try {
-            $user = $profile->getUser();
-
-            if ($user->private_stream &&
-                ($notice->scope == Notice::PUBLIC_SCOPE ||
-                 $notice->scope == Notice::SITE_SCOPE)) {
-                $notice->scope |= Notice::FOLLOWER_SCOPE;
-            }
-        } catch (NoSuchUserException $e) {
-            // Cannot handle private streams for remote profiles
-        }
-
-        // Force the scope for private groups
-
-        foreach ($groups as $groupId) {
-            $group = User_group::getKV('id', $groupId);
-            if ($group instanceof User_group) {
-                if ($group->force_scope) {
-                    $notice->scope |= Notice::GROUP_SCOPE;
-                    break;
-                }
-            }
-        }
+        $notice->scope = self::figureOutScope($profile, $groups, $notice->scope);
 
         if (Event::handle('StartNoticeSave', array(&$notice))) {
 
@@ -749,6 +721,252 @@ class Notice extends Managed_DataObject
         return $notice;
     }
 
+    static function saveActivity(Activity $act, Profile $actor, array $options=array()) {
+
+        // First check if we're going to let this Activity through from the specific actor
+        if (!$actor->hasRight(Right::NEWNOTICE)) {
+            common_log(LOG_WARNING, "Attempted post from user disallowed to post: " . $actor->getNickname());
+
+            // TRANS: Client exception thrown when a user tries to post while being banned.
+            throw new ClientException(_m('You are banned from posting notices on this site.'), 403);
+        }
+        if (common_config('throttle', 'enabled') && !self::checkEditThrottle($actor->id)) {
+            common_log(LOG_WARNING, 'Excessive posting by profile #' . $actor->id . '; throttled.');
+            // TRANS: Client exception thrown when a user tries to post too many notices in a given time frame.
+            throw new ClientException(_m('Too many notices too fast; take a breather '.
+                                        'and post again in a few minutes.'));
+        }
+
+        // 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 {
+            // implied object
+            $options['uri'] = $act->id;
+            $options['url'] = $act->link;
+        }
+
+        $defaults = array(
+                          'groups'   => array(),
+                          'is_local' => self::LOCAL_PUBLIC,
+                          'mentions' => array(),
+                          'reply_to' => null,
+                          'repeat_of' => null,
+                          'scope' => null,
+                          'source' => 'unknown',
+                          'tags' => array(),
+                          'uri' => null,
+                          'url' => null,
+                          'urls' => array(),
+                          'distribute' => true);
+
+        // options will have default values when nothing has been supplied
+        $options = array_merge($defaults, $options); 
+        foreach (array_keys($defaults) as $key) {
+            // Only convert the keynames we specify ourselves from 'defaults' array into variables
+            $$key = $options[$key];
+        }
+        extract($options, EXTR_SKIP);
+
+        $stored = new Notice();
+        if (!empty($uri)) {
+            $stored->uri = $uri;
+            if ($stored->find()) {
+                common_debug('cannot create duplicate Notice URI: '.$stored->uri);
+                throw new Exception('Notice URI already exists');
+            }
+        }
+
+        $stored->profile_id = $actor->id;
+        $stored->source = $source;
+        $stored->uri = $uri;
+        $stored->url = $url;
+        $stored->verb = $act->verb;
+
+        $autosource = common_config('public', 'autosource');
+
+        // Sandboxed are non-false, but not 1, either
+        if (!$actor->hasRight(Right::PUBLICNOTICE) ||
+            ($source && $autosource && in_array($source, $autosource))) {
+            $stored->is_local = Notice::LOCAL_NONPUBLIC;
+        }
+
+        // Maybe a missing act-time should be fatal if the actor is not local?
+        if (!empty($act->time)) {
+            $stored->created = common_sql_date($act->time);
+        } else {
+            $stored->created = common_sql_now();
+        }
+
+        $reply = null;
+        if ($act->context instanceof ActivityContext && !empty($act->context->replyToID)) {
+            $reply = self::getKV('uri', $act->context->replyToID);
+        }
+        if (!$reply instanceof Notice && $act->target instanceof ActivityObject) {
+            $reply = self::getKV('uri', $act->target->id);
+        }
+
+        if ($reply instanceof Notice) {
+            if (!$reply->inScope($actor)) {
+                // TRANS: Client error displayed when trying to reply to a notice a the target has no access to.
+                // TRANS: %1$s is a user nickname, %2$d is a notice ID (number).
+                throw new ClientException(sprintf(_m('%1$s has no right to reply to notice %2$d.'), $actor->getNickname(), $reply->id), 403);
+            }
+
+            $stored->reply_to     = $reply->id;
+            $stored->conversation = $reply->conversation;
+
+            // If the original is private to a group, and notice has no group specified,
+            // make it to the same group(s)
+            if (empty($groups) && ($reply->scope & Notice::GROUP_SCOPE)) {
+                $groups = array();
+                $replyGroups = $reply->getGroups();
+                foreach ($replyGroups as $group) {
+                    if ($actor->isMember($group)) {
+                        $groups[] = $group->id;
+                    }
+                }
+            }
+
+            if (is_null($scope)) {
+                $scope = $reply->scope;
+            }
+        }
+
+        if ($act->context instanceof ActivityContext) {
+            $location = $act->context->location;
+            if ($location) {
+                $stored->lat = $location->lat;
+                $stored->lon = $location->lon;
+                if ($location->location_id) {
+                    $stored->location_ns = $location->location_ns;
+                    $stored->location_id = $location->location_id;
+                }
+            }
+        } else {
+            $act->context = new ActivityContext();
+        }
+
+        $stored->scope = self::figureOutScope($actor, $groups, $scope);
+
+        foreach ($act->categories as $cat) {
+            if ($cat->term) {
+                $term = common_canonical_tag($cat->term);
+                if (!empty($term)) {
+                    $tags[] = $term;
+                }
+            }
+        }
+
+        foreach ($act->enclosures as $href) {
+            // @todo FIXME: Save these locally or....?
+            $urls[] = $href;
+        }
+
+        if (Event::handle('StartNoticeSave', array(&$stored))) {
+            // XXX: some of these functions write to the DB
+
+            try {
+                $stored->insert();    // throws exception on error
+
+                $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->type, true);
+                $stored->update($orig);
+            } catch (Exception $e) {
+                if (empty($stored->id)) {
+                    common_debug('Failed to save stored object entry in database ('.$e->getMessage().')');
+                } else {
+                    common_debug('Failed to store activity object in database ('.$e->getMessage().'), deleting notice id '.$stored->id);
+                    $stored->delete();
+                }
+                throw $e;
+            }
+        }
+
+
+        // Save per-notice metadata...
+        $mentions = array();
+        $groups   = array();
+
+        // This event lets plugins filter out non-local recipients (attentions we don't care about)
+        // Used primarily for OStatus (and if we don't federate, all attentions would be local anyway)
+        Event::handle('GetLocalAttentions', array($actor, $act->context->attention, &$mentions, &$groups));
+
+        if (!empty($mentions)) {
+            $stored->saveKnownReplies($mentions);
+        } else {
+            $stored->saveReplies();
+        }
+
+        if (!empty($tags)) {
+            $stored->saveKnownTags($tags);
+        } else {
+            $stored->saveTags();
+        }
+
+        // Note: groups may save tags, so must be run after tags are saved
+        // to avoid errors on duplicates.
+        // Note: groups should always be set.
+
+        $stored->saveKnownGroups($groups);
+
+        if (!empty($urls)) {
+            $stored->saveKnownUrls($urls);
+        } else {
+            $stored->saveUrls();
+        }
+
+        if ($distribute) {
+            // Prepare inbox delivery, may be queued to background.
+            $stored->distribute();
+        }
+
+        return $stored;
+    }
+
+    static public function figureOutScope(Profile $actor, array $groups, $scope=null) {
+        if (is_null($scope)) {
+            $scope = self::defaultScope();
+        }
+
+        // For private streams
+        try {
+            $user = $actor->getUser();
+            // FIXME: We can't do bit comparison with == (Legacy StatusNet thing. Let's keep it for now.)
+            if ($user->private_stream && ($scope == Notice::PUBLIC_SCOPE || $scope == Notice::SITE_SCOPE)) {
+                $scope |= Notice::FOLLOWER_SCOPE;
+            }
+        } catch (NoSuchUserException $e) {
+            // TODO: Not a local user, so we don't know about scope preferences... yet!
+        }
+
+        // Force the scope for private groups
+        foreach ($groups as $group_id) {
+            $group = User_group::staticGet('id', $group_id);
+            if ($group instanceof User_group) {
+                if ($group->force_scope) {
+                    $scope |= Notice::GROUP_SCOPE;
+                    break;
+                }
+            }
+        }
+
+        return $scope;
+    }
+
     function blowOnInsert($conversation = false)
     {
         $this->blowStream('profile:notice_ids:%d', $this->profile_id);
index 42ee9a43efb86a5299adfff1dfd5249d1e0f4be8..f799b4033d9829aff70c85d0ff898d5fe99d20da 100644 (file)
@@ -1360,4 +1360,9 @@ class OStatusPlugin extends Plugin
 
         return true;
     }
+
+    public function onGetLocalAttentions(Profile $actor, array $attention_uris, array &$mentions, array &$groups)
+    {
+        list($mentions, $groups) = Ostatus_profile::filterAttention($actor, $attention_uris);
+    }
 }
index 2bcb9b4071495e5d4e95b82bcccce4836b141aa4..3d550872d625e8874594bc39b5ea84887320886b 100644 (file)
@@ -670,7 +670,7 @@ class Ostatus_profile extends Managed_DataObject
         if ($activity->context) {
             // TODO: context->attention
             list($options['groups'], $options['replies'])
-                = $this->filterAttention($oprofile, $activity->context->attention);
+                = self::filterAttention($oprofile->localProfile(), $activity->context->attention);
 
             // Maintain direct reply associations
             // @todo FIXME: What about conversation ID?
@@ -839,7 +839,7 @@ class Ostatus_profile extends Managed_DataObject
         if ($activity->context) {
             // TODO: context->attention
             list($options['groups'], $options['replies'])
-                = $this->filterAttention($oprofile, $activity->context->attention);
+                = self::filterAttention($oprofile->localProfile(), $activity->context->attention);
 
             // Maintain direct reply associations
             // @todo FIXME: What about conversation ID?
@@ -913,11 +913,11 @@ class Ostatus_profile extends Managed_DataObject
 
     /**
      * Filters a list of recipient ID URIs to just those for local delivery.
-     * @param Ostatus_profile local profile of sender
+     * @param Profile local profile of sender
      * @param array in/out &$attention_uris set of URIs, will be pruned on output
      * @return array of group IDs
      */
-    protected function filterAttention($sender, array $attention)
+    static public function filterAttention(Profile $sender, array $attention)
     {
         common_log(LOG_DEBUG, "Original reply recipients: " . implode(', ', array_keys($attention)));
         $groups = array();
@@ -937,16 +937,11 @@ class Ostatus_profile extends Managed_DataObject
             if ($id) {
                 $group = User_group::getKV('id', $id);
                 if ($group instanceof User_group) {
-                    try {
-                        // Deliver to all members of this local group if allowed.
-                        $profile = $sender->localProfile();
-                        if ($profile->isMember($group)) {
-                            $groups[] = $group->id;
-                        } else {
-                            common_log(LOG_DEBUG, "Skipping reply to local group $group->nickname as sender $profile->id is not a member");
-                        }
-                    } catch (NoProfileException $e) {
-                        // Sender has no profile! Do some garbage collection, please.
+                    // Deliver to all members of this local group if allowed.
+                    if ($sender->isMember($group)) {
+                        $groups[] = $group->id;
+                    } else {
+                        common_log(LOG_DEBUG, sprintf('Skipping reply to local group %s as sender %d is not a member', $group->getNickname(), $sender->id));
                     }
                     continue;
                 } else {