]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Merge branch 'limitdist2' into 1.0.x
authorEvan Prodromou <evan@status.net>
Tue, 29 Mar 2011 21:32:17 +0000 (17:32 -0400)
committerEvan Prodromou <evan@status.net>
Tue, 29 Mar 2011 21:32:17 +0000 (17:32 -0400)
39 files changed:
README
actions/apistatusesretweet.php
actions/newnotice.php
actions/repeat.php
actions/showgroup.php
actions/shownotice.php
actions/showstream.php
classes/Notice.php
classes/Profile.php
classes/statusnet.ini
db/core.php
lib/action.php
lib/command.php
lib/conversationnoticestream.php
lib/default.php
lib/favenoticestream.php
lib/filenoticestream.php
lib/filteringnoticestream.php [new file with mode: 0644]
lib/groupnoticestream.php
lib/noticeform.php
lib/noticelistitem.php
lib/profilenoticestream.php
lib/publicnoticestream.php
lib/repeatedbymenoticestream.php
lib/repeatsofmenoticestream.php
lib/replynoticestream.php
lib/scopingnoticestream.php [new file with mode: 0644]
lib/taggedprofilenoticestream.php
lib/tagnoticestream.php
lib/toselector.php [new file with mode: 0644]
plugins/Bookmark/showbookmark.php
plugins/Event/showevent.php
plugins/Event/showrsvp.php
plugins/FacebookBridge/FacebookBridgePlugin.php
plugins/OStatus/OStatusPlugin.php
plugins/Poll/showpoll.php
plugins/TwitterBridge/TwitterBridgePlugin.php
scripts/createsim.php
theme/rebase/css/display.css

diff --git a/README b/README
index 0dcbeea23929b01a8b5f8a6ee7978f5c2f5693cd..ef032cad8423a341dcb6b90b3e545a8be3ea0084 100644 (file)
--- a/README
+++ b/README
@@ -1472,6 +1472,8 @@ Configuration options specific to notices.
 contentlimit: max length of the plain-text content of a notice.
     Default is null, meaning to use the site-wide text limit.
     0 means no limit.
+defaultscope: default scope for notices. Defaults to 0; set to
+             1 to keep notices private to this site by default.
 
 message
 -------
index ecc4a3f03301b92651dea1d5fe522f8792214f27..2bc9092ba6f42aa02313a03fbf904d52e5136019 100644 (file)
@@ -85,8 +85,27 @@ class ApiStatusesRetweetAction extends ApiAuthAction
             return false;
         }
 
+        // Is it OK to repeat that notice (general enough scope)?
+
+        if ($this->original->scope != Notice::SITE_SCOPE &&
+            $this->original->scope != Notice::PUBLIC_SCOPE) {
+            $this->clientError(_('You may not repeat a private notice.'),
+                               403,
+                               $this->format);
+            return false;
+        }
+
         $profile = $this->user->getProfile();
 
+        // Can the profile actually see that notice?
+
+        if (!$this->original->inScope($profile)) {
+            $this->clientError(_('No access to that notice.'),
+                               403,
+                               $this->format);
+            return false;
+        }
+
         if ($profile->hasRepeated($id)) {
             // TRANS: Client error displayed trying to re-repeat a notice through the API.
             $this->clientError(_('Already repeated that notice.'),
@@ -94,6 +113,7 @@ class ApiStatusesRetweetAction extends ApiAuthAction
             return false;
         }
 
+
         return true;
     }
 
index 7f697e23f397d8962792396a834d4954730558c9..f48134c3b21e5743d449858644ea6b6652a23ece 100644 (file)
@@ -209,6 +209,10 @@ class NewnoticeAction extends Action
         $author_id = $user->id;
         $text      = $content_shortened;
 
+        // Does the heavy-lifting for getting "To:" information
+
+        ToSelector::fillOptions($this, $options);
+
         if (Event::handle('StartNoticeSaveWeb', array($this, &$author_id, &$text, &$options))) {
 
             $notice = Notice::saveNew($user->id, $content_shortened, 'web', $options);
index 869c2ddd4ee1851c073586789020768b64eb56a0..4201a4ce955af96b69340d5e1e2e56e801979e59 100644 (file)
@@ -73,6 +73,14 @@ class RepeatAction extends Action
             return false;
         }
 
+        // Is it OK to repeat that notice (general enough scope)?
+
+        if ($this->notice->scope != Notice::SITE_SCOPE &&
+            $this->notice->scope != Notice::PUBLIC_SCOPE) {
+            $this->clientError(_('You may not repeat a private notice.'),
+                               403);
+        }
+
         if ($this->user->id == $this->notice->profile_id) {
             // TRANS: Client error displayed when trying to repeat an own notice.
             $this->clientError(_('You cannot repeat your own notice.'));
@@ -88,6 +96,13 @@ class RepeatAction extends Action
 
         $profile = $this->user->getProfile();
 
+        // Can the profile actually see that notice?
+
+        if (!$this->notice->inScope($profile)) {
+            $this->clientError(_('No access to that notice.'), 403);
+        }
+
+
         if ($profile->hasRepeated($id)) {
             // TRANS: Client error displayed when trying to repeat an already repeated notice.
             $this->clientError(_('You already repeated that notice.'));
index d77fbeed7173f353694ccb463da01142b89e5886..512ca6a0ee262d71d89d49c1899164d0ce84bf8a 100644 (file)
@@ -365,6 +365,18 @@ class ShowgroupAction extends GroupDesignAction
         $this->raw(common_markup_to_html($m));
         $this->elementEnd('div');
     }
+
+    function noticeFormOptions()
+    {
+        $options = parent::noticeFormOptions();
+        $cur = common_current_user();
+
+        if (!empty($cur) && $cur->isMember($this->group)) {
+            $options['to_group'] =  $this->group;
+        }
+
+        return $options;
+    }
 }
 
 class GroupAdminSection extends ProfileSection
index f6074faddc3d80437601a410ada1b26216f30b69..7127a60db4c216104223f51aabd5efb6e38698e3 100644 (file)
@@ -79,7 +79,7 @@ class ShownoticeAction extends OwnerDesignAction
 
         $id = $this->arg('notice');
 
-        $this->notice = Notice::staticGet($id);
+        $this->notice = Notice::staticGet('id', $id);
 
         if (empty($this->notice)) {
             // Did we used to have it, and it got deleted?
@@ -94,6 +94,18 @@ class ShownoticeAction extends OwnerDesignAction
             return false;
         }
 
+        $cur = common_current_user();
+
+        if (!empty($cur)) {
+            $curProfile = $cur->getProfile();
+        } else {
+            $curProfile = null;
+        }
+
+        if (!$this->notice->inScope($curProfile)) {
+            throw new ClientException(_('Not available.'), 403);
+        }
+
         $this->profile = $this->notice->getProfile();
 
         if (empty($this->profile)) {
index 1a01812ec5d6a1c604c0d1be2ebe002c8455ca17..fed9685b355ab5055c3ced4ba045ce115ebcd28c 100644 (file)
@@ -278,6 +278,18 @@ class ShowstreamAction extends ProfileAction
         $cloud = new PersonalTagCloudSection($this, $this->user);
         $cloud->show();
     }
+
+    function noticeFormOptions()
+    {
+        $options = parent::noticeFormOptions();
+        $cur = common_current_user();
+
+        if (empty($cur) || $cur->id != $this->profile->id) {
+            $options['to_profile'] =  $this->profile;
+        }
+
+        return $options;
+    }
 }
 
 // We don't show the author for a profile, since we already know who it is!
index 114119bfc9f7027f23d56b9877a58b94a4e48f7b..b5eafb0ffa94fdba006f4c947e277705895547ca 100644 (file)
@@ -73,6 +73,7 @@ class Notice extends Memcached_DataObject
     public $location_ns;                     // int(4)
     public $repeat_of;                       // int(4)
     public $object_type;                     // varchar(255)
+    public $scope;                           // int(4)
 
     /* Static get */
     function staticGet($k,$v=NULL)
@@ -89,6 +90,12 @@ class Notice extends Memcached_DataObject
     const LOCAL_NONPUBLIC = -1;
     const GATEWAY         = -2;
 
+    const PUBLIC_SCOPE    = 0; // Useful fake constant
+    const SITE_SCOPE      = 1;
+    const ADDRESSEE_SCOPE = 2;
+    const GROUP_SCOPE     = 4;
+    const FOLLOWER_SCOPE  = 8;
+
     function getProfile()
     {
         $profile = Profile::staticGet('id', $this->profile_id);
@@ -243,6 +250,7 @@ class Notice extends Memcached_DataObject
      *                           notice in place of extracting links from content
      *              boolean 'distribute' whether to distribute the notice, default true
      *              string 'object_type' URL of the associated object type (default ActivityObject::NOTE)
+     *              int 'scope' Scope bitmask; default to SITE_SCOPE on private sites, 0 otherwise
      *
      * @fixme tag override
      *
@@ -254,6 +262,7 @@ class Notice extends Memcached_DataObject
                           'url' => null,
                           'reply_to' => null,
                           'repeat_of' => null,
+                          'scope' => null,
                           'distribute' => true);
 
         if (!empty($options)) {
@@ -336,6 +345,19 @@ class Notice extends Memcached_DataObject
         // Handle repeat case
 
         if (isset($repeat_of)) {
+
+            // Check for a private one
+
+            $repeat = Notice::staticGet('id', $repeat_of);
+
+            if (!empty($repeat) &&
+                $repeat->scope != Notice::SITE_SCOPE &&
+                $repeat->scope != Notice::PUBLIC_SCOPE) {
+                throw new ClientException(_('Cannot repeat a private notice.'), 403);
+            }
+
+            // XXX: Check for access...?
+
             $notice->repeat_of = $repeat_of;
         } else {
             $notice->reply_to = self::getReplyTo($reply_to, $profile_id, $source, $final);
@@ -343,6 +365,10 @@ class Notice extends Memcached_DataObject
 
         if (!empty($notice->reply_to)) {
             $reply = Notice::staticGet('id', $notice->reply_to);
+            if (!$reply->inScope($profile)) {
+                throw new ClientException(sprintf(_("%s has no access to notice %d"),
+                                                  $profile->nickname, $reply->id), 403);
+            }
             $notice->conversation = $reply->conversation;
         }
 
@@ -368,6 +394,12 @@ class Notice extends Memcached_DataObject
             $notice->object_type = $object_type;
         }
 
+        if (is_null($scope)) { // 0 is a valid value
+            $notice->scope = common_config('notice', 'defaultscope');
+        } else {
+            $notice->scope = $scope;
+        }
+
         if (Event::handle('StartNoticeSave', array(&$notice))) {
 
             // XXX: some of these functions write to the DB
@@ -1556,8 +1588,13 @@ class Notice extends Memcached_DataObject
             $content = mb_substr($content, 0, $maxlen - 4) . ' ...';
         }
 
-        return self::saveNew($repeater_id, $content, $source,
-                             array('repeat_of' => $this->id));
+        // Scope is same as this one's
+
+        return self::saveNew($repeater_id,
+                             $content,
+                             $source,
+                             array('repeat_of' => $this->id,
+                                   'scope' => $this->scope));
     }
 
     // These are supposed to be in chron order!
@@ -2011,4 +2048,95 @@ class Notice extends Memcached_DataObject
                     ($this->is_local != Notice::GATEWAY));
         }
     }
+
+    /**
+     * Check that the given profile is allowed to read, respond to, or otherwise
+     * act on this notice.
+     * 
+     * The $scope member is a bitmask of scopes, representing a logical AND of the
+     * scope requirement. So, 0x03 (Notice::ADDRESSEE_SCOPE | Notice::SITE_SCOPE) means
+     * "only visible to people who are mentioned in the notice AND are users on this site."
+     * Users on the site who are not mentioned in the notice will not be able to see the
+     * notice.
+     *
+     * @param Profile $profile The profile to check
+     *
+     * @return boolean whether the profile is in the notice's scope
+     */
+
+    function inScope($profile)
+    {
+        // If there's no scope, anyone (even anon) is in scope.
+
+        if ($this->scope == 0) {
+            return true;
+        }
+
+        // If there's scope, anon cannot be in scope
+
+        if (empty($profile)) {
+            return false;
+        }
+
+        // Author is always in scope
+
+        if ($this->profile_id == $profile->id) {
+            return true;
+        }
+
+        // Only for users on this site
+
+        if ($this->scope & Notice::SITE_SCOPE) {
+            $user = $profile->getUser();
+            if (empty($user)) {
+                return false;
+            }
+        }
+
+        // Only for users mentioned in the notice
+
+        if ($this->scope & Notice::ADDRESSEE_SCOPE) {
+
+            // XXX: just query for the single reply
+
+            $replies = $this->getReplies();
+
+            if (!in_array($profile->id, $replies)) {
+                return false;
+            }
+        }
+
+        // Only for members of the given group
+
+        if ($this->scope & Notice::GROUP_SCOPE) {
+
+            // XXX: just query for the single membership
+
+            $groups = $this->getGroups();
+
+            $foundOne = false;
+
+            foreach ($groups as $group) {
+                if ($profile->isMember($group)) {
+                    $foundOne = true;
+                    break;
+                }
+            }
+
+            if (!$foundOne) {
+                return false;
+            }
+        }
+
+        // Only for followers of the author
+
+        if ($this->scope & Notice::FOLLOWER_SCOPE) {
+            $author = $this->getProfile();
+            if (!Subscription::exists($profile, $author)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
 }
index 032a370656735f86078d933fe0cf154274a237df..73a98d4ff51541e1b4b4e05c9d99e3c37af97098 100644 (file)
@@ -1087,4 +1087,44 @@ class Profile extends Memcached_DataObject
 
         return $profile;
     }
+
+    function canRead(Notice $notice)
+    {
+        if ($notice->scope & Notice::SITE_SCOPE) {
+            $user = $this->getUser();
+            if (empty($user)) {
+                return false;
+            }
+        }
+
+        if ($notice->scope & Notice::ADDRESSEE_SCOPE) {
+            $replies = $notice->getReplies();
+
+            if (!in_array($this->id, $replies)) {
+                $groups = $notice->getGroups();
+
+                $foundOne = false;
+
+                foreach ($groups as $group) {
+                    if ($this->isMember($group)) {
+                        $foundOne = true;
+                        break;
+                    }
+                }
+
+                if (!$foundOne) {
+                    return false;
+                }
+            }
+        }
+
+        if ($notice->scope & Notice::FOLLOWER_SCOPE) {
+            $author = $notice->getProfile();
+            if (!Subscription::exists($this, $author)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
 }
index ab2c3d02ef60144a68f62f67babfe7af4097e286..40d83f0a87e5364b05d487d2ceaafebaf7b881d0 100644 (file)
@@ -337,6 +337,7 @@ location_id = 1
 location_ns = 1
 repeat_of = 1
 object_type = 2
+scope = 1
 
 [notice__keys]
 id = N
index f44a85cad7efd2e16f3d8f45b8ec29db47d3fb2a..ab1f1597433fb02d54c2ccfc6ccea6715af9d8a8 100644 (file)
@@ -203,6 +203,9 @@ $schema['notice'] = array(
         'location_ns' => array('type' => 'int', 'description' => 'namespace for location'),
         'repeat_of' => array('type' => 'int', 'description' => 'notice this is a repeat of'),
         'object_type' => array('type' => 'varchar', 'length' => 255, 'description' => 'URI representing activity streams object type', 'default' => 'http://activitystrea.ms/schema/1.0/note'),
+        'scope' => array('type' => 'int',
+                         'default' => '1',
+                         'description' => 'bit map for distribution scope; 0 = everywhere; 1 = this server only; 2 = addressees; 4 = followers'),
     ),
     'primary key' => array('id'),
     'unique keys' => array(
index 654ec7aa43e533b6c94ca8ba8bfc580935c9357a..3ac95b46a6223a31fc39b63cd50ef00660d8abe4 100644 (file)
@@ -657,7 +657,8 @@ class Action extends HTMLOutputter // lawsuit
 
                 if (Event::handle('StartMakeEntryForm', array($tag, $this, &$form))) {
                     if ($tag == 'status') {
-                        $form = new NoticeForm($this);
+                        $options = $this->noticeFormOptions();
+                        $form = new NoticeForm($this, $options);
                     }
                     Event::handle('EndMakeEntryForm', array($tag, $this, $form));
                 }
@@ -673,6 +674,11 @@ class Action extends HTMLOutputter // lawsuit
         $this->elementEnd('div');
     }
 
+    function noticeFormOptions()
+    {
+        return array();
+    }
+
     /**
      * Show anonymous message.
      *
index 5b9964c5b1b0a2f878b0b81c39241cb0b8351019..35d070268414c57ab9ab3a776a409666f6dee4e8 100644 (file)
@@ -544,7 +544,22 @@ class RepeatCommand extends Command
             return;
         }
 
-        if ($this->user->getProfile()->hasRepeated($notice->id)) {
+        // Is it OK to repeat that notice (general enough scope)?
+
+        if ($notice->scope != Notice::SITE_SCOPE &&
+            $notice->scope != Notice::PUBLIC_SCOPE) {
+            $channel->error($this->user, _('You may not repeat a private notice.'));
+        }
+
+        $profile = $this->user->getProfile();
+
+        // Can the profile actually see that notice?
+
+        if (!$notice->inScope($profile)) {
+            $channel->error($this->user, _('You have no access to that notice.'));
+        }
+
+        if ($profile->hasRepeated($notice->id)) {
             // TRANS: Error text shown when trying to repeat an notice that was already repeated by the user.
             $channel->error($this->user, _('Already repeated that notice.'));
             return;
index dbba6cd6f0f225a97aeb7a9e888690e4f9497715..addf8768ceb57e0db3b84be1ca96d7947480c784 100644 (file)
@@ -1,14 +1,70 @@
 <?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Notice stream for a conversation
+ * 
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Cache
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
 
-class ConversationNoticeStream extends CachingNoticeStream
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Notice stream for a conversation
+ *
+ * @category  Stream
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+class ConversationNoticeStream extends ScopingNoticeStream
 {
     function __construct($id)
     {
-        parent::__construct(new RawConversationNoticeStream($id),
-                            'notice:conversation_ids:'.$id);
+        parent::__construct(new CachingNoticeStream(new RawConversationNoticeStream($id),
+                                                    'notice:conversation_ids:'.$id));
     }
 }
 
+/**
+ * Notice stream for a conversation
+ *
+ * @category  Stream
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
 class RawConversationNoticeStream extends NoticeStream
 {
     protected $id;
index e6caf0301a636df07ecc288477f227019ced1edf..9872d8ffd3b2ad7c6e145fddf4c9512ed064aac5 100644 (file)
@@ -288,7 +288,8 @@ $default =
         array('enabled' => true,
               'css' => ''),
         'notice' =>
-        array('contentlimit' => null),
+        array('contentlimit' => null,
+              'defaultscope' => 0), // set to 0 for default open
         'message' =>
         array('contentlimit' => null),
         'location' =>
index 5aaad5ce5b51f3c9a452342a5c42e1684b7e68c0..987805cf9d970246f9cd8192474f811fb28ac44d 100644 (file)
@@ -1,6 +1,51 @@
 <?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Notice stream for favorites
+ * 
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Stream
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
 
-class FaveNoticeStream extends CachingNoticeStream
+/**
+ * Notice stream for favorites
+ *
+ * @category  Stream
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+class FaveNoticeStream extends ScopingNoticeStream
 {
     function __construct($user_id, $own)
     {
@@ -10,10 +55,21 @@ class FaveNoticeStream extends CachingNoticeStream
         } else {
             $key = 'fave:ids_by_user:'.$user_id;
         }
-        parent::__construct($stream, $key);
+        parent::__construct(new CachingNoticeStream($stream, $key));
     }
 }
 
+/**
+ * Raw notice stream for favorites
+ *
+ * @category  Stream
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
 class RawFaveNoticeStream extends NoticeStream
 {
     protected $user_id;
index fddc5d33ce930cb72af4d7a7c431ae95273a8536..8c01893634d55fe030a3a6dced098c73ca8b6d1e 100644 (file)
@@ -1,22 +1,67 @@
 <?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Stream of notices that reference an URL
+ * 
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Stream
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
 
-class FileNoticeStream extends CachingNoticeStream
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+class FileNoticeStream extends ScopingNoticeStream
 {
     function __construct($file)
     {
-        parent::__construct(new RawFileNoticeStream($file),
-                            'file:notice-ids:'.$this->url);
+        parent::__construct(new CachingNoticeStream(new RawFileNoticeStream($file),
+                                                    'file:notice-ids:'.$this->url));
     }
 }
 
+/**
+ * Raw stream for a file
+ *
+ * @category  Stream
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
 class RawFileNoticeStream extends NoticeStream
 {
     protected $file = null;
 
     function __construct($file)
     {
-        $this->file = $file;
         parent::__construct();
+        $this->file = $file;
     }
 
     /**
diff --git a/lib/filteringnoticestream.php b/lib/filteringnoticestream.php
new file mode 100644 (file)
index 0000000..a3bdc08
--- /dev/null
@@ -0,0 +1,115 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * A notice stream that filters its upstream content
+ * 
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Stream
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * A class for presenting a filtered notice stream based on an upstream stream
+ *
+ * @category  Stream
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+abstract class FilteringNoticeStream extends NoticeStream
+{
+    protected $upstream;
+
+    function __construct($upstream)
+    {
+        $this->upstream = $upstream;
+    }
+
+    abstract function filter($notice);
+
+    function getNotices($offset, $limit, $sinceId, $maxId)
+    {
+        // "offset" is virtual; we have to get a lot
+        $total = $offset + $limit;
+
+        $filtered = array();
+
+        $startAt = 0;
+        $askFor  = $total;
+
+        // Keep going till we have $total notices in $notices array,
+        // or we get nothing from upstream.
+
+        $results = null;
+
+        do {
+
+            $raw = $this->upstream->getNotices($startAt, $askFor, $sinceId, $maxId);
+
+            $results = $raw->N;
+
+            if ($results == 0) {
+                break;
+            }
+
+            while ($raw->fetch()) {
+                if ($this->filter($raw)) {
+                    $filtered[] = clone($raw);
+                    if (count($filtered >= $total)) {
+                        break;
+                    }
+                }
+            }
+
+            // XXX: make these smarter; factor hit rate into $askFor
+
+            $startAt += $askFor;
+            $askFor   = max($total - count($filtered), NOTICES_PER_PAGE);
+
+        } while (count($filtered) < $total && $results !== 0);
+
+        return new ArrayWrapper(array_slice($filtered, $offset, $limit));
+    }
+
+    function getNoticeIds($offset, $limit, $sinceId, $maxId)
+    {
+        $notices = $this->getNotices($offset, $limit, $sinceId, $maxId);
+
+        $ids = array();
+
+        while ($notices->fetch()) {
+            $ids[] = $notice->id;
+        }
+
+        return $ids;
+    }
+}
index 4b4fb0022954f3b758e373069219dff9e4b9718b..22d94d0482fcc7f1ee54884dfc692473f41ec3e0 100644 (file)
@@ -1,14 +1,68 @@
 <?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Stream of notices for a group
+ * 
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Stream
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
 
-class GroupNoticeStream extends CachingNoticeStream
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Stream of notices for a group
+ *
+ * @category  Stream
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class GroupNoticeStream extends ScopingNoticeStream
 {
     function __construct($group)
     {
-        parent::__construct(new RawGroupNoticeStream($group),
-                            'user_group:notice_ids:' . $group->id);
+        parent::__construct(new CachingNoticeStream(new RawGroupNoticeStream($group),
+                                                    'user_group:notice_ids:' . $group->id));
     }
 }
 
+/**
+ * Stream of notices for a group
+ *
+ * @category  Stream
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
 class RawGroupNoticeStream extends NoticeStream
 {
     protected $group;
index 2cbacc9280ae6f20df06b7574e35a9a20541ddc2..ee4e2ca9670fe8dbf29b16f29dbe21641099641e 100644 (file)
@@ -79,6 +79,15 @@ class NoticeForm extends Form
     var $location_id;
     var $location_ns;
 
+    /** select this group from the drop-down by default. */
+    var $to_group;
+
+    /** select this user from the drop-down by default. */
+    var $to_profile;
+
+    /** Pre-click the private checkbox. */
+    var $private;
+
     /**
      * Constructor
      *
@@ -109,7 +118,8 @@ class NoticeForm extends Form
         $this->actionName  = $action->trimmed('action');
 
         $prefill = array('content', 'inreplyto', 'lat', 
-                         'lon', 'location_id', 'location_ns');
+                         'lon', 'location_id', 'location_ns',
+                         'to_group', 'to_profile', 'private');
 
         foreach ($prefill as $fieldName) {
             if (array_key_exists($fieldName, $options)) {
@@ -117,6 +127,16 @@ class NoticeForm extends Form
             }
         }
 
+        // Prefill the profile if we're replying
+
+        if (empty($this->to_profile) &&
+            !empty($this->inreplyto)) {
+            $notice = Notice::staticGet('id', $this->inreplyto);
+            if (!empty($notice)) {
+                $this->to_profile = $notice->getProfile();
+            }
+        }
+
         if (array_key_exists('user', $options)) {
             $this->user = $options['user'];
         } else {
@@ -218,6 +238,14 @@ class NoticeForm extends Form
             }
             $this->out->hidden('notice_in-reply-to', $this->inreplyto, 'inreplyto');
 
+            $this->out->elementStart('div', 'to-selector');
+            $toWidget = new ToSelector($this->out,
+                                       $this->user,
+                                       (!empty($this->to_group) ? $this->to_group : $this->to_profile));
+
+            $toWidget->show();
+            $this->out->elementEnd('div');
+
             if ($this->user->shareLocation()) {
                 $this->out->hidden('notice_data-lat', empty($this->lat) ? (empty($this->profile->lat) ? null : $this->profile->lat) : $this->lat, 'lat');
                 $this->out->hidden('notice_data-lon', empty($this->lon) ? (empty($this->profile->lon) ? null : $this->profile->lon) : $this->lon, 'lon');
index 17827d07ef6b97f9c3f5898d19c0039491a15e68..097a5d06c44352a7f01febf515133c7120726df1 100644 (file)
@@ -170,7 +170,11 @@ class NoticeListItem extends Widget
     {
         if (Event::handle('StartOpenNoticeListItemElement', array($this))) {
             $id = (empty($this->repeat)) ? $this->notice->id : $this->repeat->id;
-            $this->out->elementStart('li', array('class' => 'hentry notice',
+            $class = 'hentry notice';
+            if ($this->notice->scope != 0 && $this->notice->scope != 1) {
+                $class .= ' limited-scope';
+            }
+            $this->out->elementStart('li', array('class' => $class,
                                                  'id' => 'notice-' . $id));
             Event::handle('EndOpenNoticeListItemElement', array($this));
         }
@@ -592,17 +596,21 @@ class NoticeListItem extends Widget
 
     function showRepeatForm()
     {
-        $user = common_current_user();
-        if ($user && $user->id != $this->notice->profile_id) {
-            $this->out->text(' ');
-            $profile = $user->getProfile();
-            if ($profile->hasRepeated($this->notice->id)) {
-                $this->out->element('span', array('class' => 'repeated',
-                                                  'title' => _('Notice repeated')),
-                                            _('Repeated'));
-            } else {
-                $rf = new RepeatForm($this->out, $this->notice);
-                $rf->show();
+        if ($this->notice->scope == Notice::PUBLIC_SCOPE ||
+            $this->notice->scope == Notice::SITE_SCOPE) {
+            $user = common_current_user();
+            if (!empty($user) &&
+                $user->id != $this->notice->profile_id) {
+                $this->out->text(' ');
+                $profile = $user->getProfile();
+                if ($profile->hasRepeated($this->notice->id)) {
+                    $this->out->element('span', array('class' => 'repeated',
+                                                      'title' => _('Notice repeated')),
+                                        _('Repeated'));
+                } else {
+                    $rf = new RepeatForm($this->out, $this->notice);
+                    $rf->show();
+                }
             }
         }
     }
index f62b787b04d5109b8653c47266ac598d6d3b3d1d..7ea653f8c8c2a1bcc31bfabc6ad033c5f643c2d0 100644 (file)
@@ -45,12 +45,12 @@ if (!defined('STATUSNET')) {
  * @link      http://status.net/
  */
 
-class ProfileNoticeStream extends CachingNoticeStream
+class ProfileNoticeStream extends ScopingNoticeStream
 {
     function __construct($profile)
     {
-        parent::__construct(new RawProfileNoticeStream($profile),
-                            'profile:notice_ids:' . $profile->id);
+        parent::__construct(new CachingNoticeStream(new RawProfileNoticeStream($profile),
+                                                    'profile:notice_ids:' . $profile->id));
     }
 }
 
index 6a861ca26e68109921ca1acd4042a0c2602df88b..5c8d313d46591dd3650d80a6bd78352a9aceca48 100644 (file)
@@ -1,13 +1,70 @@
 <?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Public stream
+ * 
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Stream
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
 
-class PublicNoticeStream extends CachingNoticeStream
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Public stream
+ *
+ * @category  Stream
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+class PublicNoticeStream extends ScopingNoticeStream
 {
     function __construct()
     {
-        parent::__construct(new RawPublicNoticeStream(), 'public');
+        parent::__construct(new CachingNoticeStream(new RawPublicNoticeStream(),
+                                                    'public'));
     }
 }
 
+/**
+ * Raw public stream
+ *
+ * @category  Stream
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
 class RawPublicNoticeStream extends NoticeStream
 {
     function getNoticeIds($offset, $limit, $since_id, $max_id)
index 2c4c00ebf9f056fdab9a43103167cf156deb2f90..98c1583d6a596481a252a9ef7d1ad14b32358296 100644 (file)
@@ -1,14 +1,70 @@
 <?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Stream of notices repeated by me
+ * 
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Stream
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
 
-class RepeatedByMeNoticeStream extends CachingNoticeStream
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Stream of notices repeated by me
+ *
+ * @category  General
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+class RepeatedByMeNoticeStream extends ScopingNoticeStream
 {
     function __construct($user)
     {
-        parent::__construct(new RawRepeatedByMeNoticeStream($user),
-                            'user:repeated_by_me:'.$user->id);
+        parent::__construct(new CachingNoticeStream(new RawRepeatedByMeNoticeStream($user),
+                                                    'user:repeated_by_me:'.$user->id));
     }
 }
 
+/**
+ * Raw stream of notices repeated by me
+ *
+ * @category  General
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
 class RawRepeatedByMeNoticeStream extends NoticeStream
 {
     protected $user;
index 1441908e5aeb6e41b2ac9fe038217fa89c082987..f51fc9e4472d49b1d041f01cbeae762e60875497 100644 (file)
@@ -1,14 +1,69 @@
 <?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Stream of notices that are repeats of mine
+ * 
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Stream
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
 
-class RepeatsOfMeNoticeStream extends CachingNoticeStream
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Stream of notices that are repeats of mine
+ *
+ * @category  Stream
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+class RepeatsOfMeNoticeStream extends ScopingNoticeStream
 {
     function __construct($user)
     {
-        parent::__construct(new RawRepeatsOfMeNoticeStream($user),
-                            'user:repeats_of_me:'.$user->id);
+        parent::__construct(new CachingNoticeStream(new RawRepeatsOfMeNoticeStream($user),
+                                                    'user:repeats_of_me:'.$user->id));
     }
 }
 
+/**
+ * Raw stream of notices that are repeats of mine
+ *
+ * @category  Stream
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
 class RawRepeatsOfMeNoticeStream extends NoticeStream
 {
     protected $user;
index d0ae5fc4a7309b44746237543187263bef8d4876..9de8d4efaf9c49ca62b17e48e819537c2f7b4dba 100644 (file)
@@ -1,14 +1,70 @@
 <?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Stream of mentions of me
+ * 
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Stream
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
 
-class ReplyNoticeStream extends CachingNoticeStream
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Stream of mentions of me
+ *
+ * @category  Stream
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+class ReplyNoticeStream extends ScopingNoticeStream
 {
     function __construct($userId)
     {
-        parent::__construct(new RawReplyNoticeStream($userId),
-                            'reply:stream:' . $userId);
+        parent::__construct(new CachingNoticeStream(new RawReplyNoticeStream($userId),
+                                                    'reply:stream:' . $userId));
     }
 }
 
+/**
+ * Raw stream of mentions of me
+ *
+ * @category  Stream
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
 class RawReplyNoticeStream extends NoticeStream
 {
     protected $userId;
diff --git a/lib/scopingnoticestream.php b/lib/scopingnoticestream.php
new file mode 100644 (file)
index 0000000..a7ecbcd
--- /dev/null
@@ -0,0 +1,78 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Filtering notice stream that recognizes notice scope
+ * 
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Stream
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Class comment
+ *
+ * @category  Stream
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+class ScopingNoticeStream extends FilteringNoticeStream
+{
+    protected $profile;
+
+    function __construct($upstream, $profile = null)
+    {
+        parent::__construct($upstream);
+
+        if (empty($profile)) {
+            $user = common_current_user();
+            if (!empty($user)) {
+                $profile = $user->getProfile();
+            }
+        }
+        $this->profile = $profile;
+    }
+
+    /**
+     * Only return notices where the profile is in scope
+     *
+     * @param Notice $notice The notice to check
+     *
+     * @return boolean whether to include the notice
+     */
+
+    function filter($notice)
+    {
+        return $notice->inScope($this->profile);
+    }
+    
+}
index 291d3d6eb0de1d511f5ad3cb01b6bcdc27777bd8..83c304ed8ffedce35d2797e9f2b53014b56b35f1 100644 (file)
@@ -1,14 +1,70 @@
 <?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Stream of notices by a profile with a given tag
+ * 
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Stream
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
 
-class TaggedProfileNoticeStream extends CachingNoticeStream
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Stream of notices with a given profile and tag
+ *
+ * @category  Stream
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+class TaggedProfileNoticeStream extends ScopingNoticeStream
 {
     function __construct($profile, $tag)
     {
-        parent::__construct(new RawTaggedProfileNoticeStream($profile, $tag),
-                            'profile:notice_ids_tagged:'.$profile->id.':'.Cache::keyize($tag));
+        parent::__construct(new CachingNoticeStream(new RawTaggedProfileNoticeStream($profile, $tag),
+                                                    'profile:notice_ids_tagged:'.$profile->id.':'.Cache::keyize($tag)));
     }
 }
 
+/**
+ * Raw stream of notices with a given profile and tag
+ *
+ * @category  Stream
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
 class RawTaggedProfileNoticeStream extends NoticeStream
 {
     protected $profile;
index 0e287744dd0215b4c8bf6ec5b418266987dafaa8..1dcf9f14bb9b4f12e951daad8462783bfafc7f7b 100644 (file)
@@ -1,14 +1,70 @@
 <?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Stream of notices with a given tag
+ * 
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Stream
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
 
-class TagNoticeStream extends CachingNoticeStream
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Stream of notices with a given tag
+ *
+ * @category  Stream
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+class TagNoticeStream extends ScopingNoticeStream
 {
     function __construct($tag)
     {
-        parent::__construct(new RawTagNoticeStream($tag),
-                            'notice_tag:notice_ids:' . Cache::keyize($tag));
+        parent::__construct(new CachingNoticeStream(new RawTagNoticeStream($tag),
+                                                    'notice_tag:notice_ids:' . Cache::keyize($tag)));
     }
 }
 
+/**
+ * Raw stream of notices with a given tag
+ *
+ * @category  Stream
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
 class RawTagNoticeStream extends NoticeStream
 {
     protected $tag;
diff --git a/lib/toselector.php b/lib/toselector.php
new file mode 100644 (file)
index 0000000..6dd4b5c
--- /dev/null
@@ -0,0 +1,156 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Widget showing a drop-down of potential addressees
+ * 
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Widget
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Widget showing a drop-down of potential addressees
+ *
+ * @category  Widget
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+class ToSelector extends Widget
+{
+    protected $user;
+    protected $to;
+    protected $id;
+    protected $name;
+    protected $private;
+
+    /**
+     * Constructor
+     *
+     * @param HTMLOutputter $out  output context
+     * @param User          $user Current user
+     * @param mixed         $to   Default selection for addressee
+     */
+    function __construct($out, $user, $to, $private=false, $id='notice_to', $name='notice_to')
+    {
+        parent::__construct($out);
+
+        $this->user    = $user;
+        $this->to      = $to;
+        $this->private = $private;
+        $this->id      = $id;
+        $this->name    = $name;
+    }
+
+    /**
+     * Constructor
+     *
+     * @param HTMLOutputter $out  output context
+     * @param User          $user Current user
+     * @param mixed         $to   Default selection for addressee
+     */
+    function show()
+    {
+        $choices = array();
+        $default = 'public:site';
+
+        if (!common_config('site', 'private')) {
+            $choices['public:everyone'] = _('Everyone');
+            $default = 'public:everyone';
+        }
+        // XXX: better name...?
+        $choices['public:site'] = sprintf(_('My colleagues at %s'), common_config('site', 'name'));
+        
+        $groups = $this->user->getGroups();
+
+        while ($groups->fetch()) {
+            $value = 'group:'.$groups->id;
+            if (($this->to instanceof User_group) && $this->to->id == $groups->id) {
+                $default = $value;
+            }
+            $choices[$value] = $groups->getBestName();
+        }
+
+        // XXX: add users...?
+
+        if ($this->to instanceof Profile) {
+            $value = 'profile:'.$this->to->id;
+            $default = $value;
+            $choices[$value] = $this->to->getBestName();
+        }
+
+        $this->out->dropdown($this->id,
+                             _('To:'),
+                             $choices,
+                             null,
+                             false,
+                             $default);
+
+        $this->out->checkbox('notice_private',
+                             _('Private'),
+                             $this->private);
+    }
+
+    static function fillOptions($action, &$options)
+    {
+        // XXX: make arg name selectable
+        $toArg = $action->trimmed('notice_to');
+        $private = $action->boolean('notice_private');
+
+        list($prefix, $value) = explode(':', $toArg);
+        switch ($prefix) {
+        case 'group':
+            $options['groups'] = array($value);
+            if ($private) {
+                $options['scope'] = Notice::GROUP_SCOPE;
+            }
+            break;
+        case 'profile':
+            $profile = Profile::staticGet('id', $value);
+            $options['replies'] = $profile->getUri();
+            if ($private) {
+                $options['scope'] = Notice::ADDRESSEE_SCOPE;
+            }
+            break;
+        case 'public':
+            if ($value == 'everyone' && !common_config('site', 'private')) {
+                $options['scope'] = 0;
+            } else if ($value == 'site') {
+                $options['scope'] = Notice::SITE_SCOPE;
+            }
+            break;
+        default:
+            throw new ClientException('Unknown to value: ' . toArg);
+            break;
+        }
+    }
+}
index 6bebffb68e2e81710ed96ede63b9752acbae2073..435d85940a506434f6344313afaf071b8830d881 100644 (file)
@@ -76,6 +76,16 @@ class ShowbookmarkAction extends ShownoticeAction
             throw new ClientException(_('No such bookmark.'), 404);
         }
 
+        if (!empty($cur)) {
+            $curProfile = $cur->getProfile();
+        } else {
+            $curProfile = null;
+        }
+
+        if (!$this->notice->inScope($curProfile)) {
+            throw new ClientException(_('Not available.'), 403);
+        }
+
         $this->user = User::staticGet('id', $this->bookmark->profile_id);
 
         if (empty($this->user)) {
index 7fb702f9dbeea5a80dec9d4584fb3f5c8ab2426b..93ce6dd68bd08456fddd57476cc1dc779e82bed4 100644 (file)
@@ -77,6 +77,18 @@ class ShoweventAction extends ShownoticeAction
             throw new ClientException(_('No such event.'), 404);
         }
 
+        $cur = common_current_user();
+
+        if (!empty($cur)) {
+            $curProfile = $cur->getProfile();
+        } else {
+            $curProfile = null;
+        }
+
+        if (!$this->notice->inScope($curProfile)) {
+            throw new ClientException(_('Not available.'), 403);
+        }
+
         $this->user = User::staticGet('id', $this->event->profile_id);
 
         if (empty($this->user)) {
index fde1d48f0e773d0d85b7ebf8a60b15603ff46559..3b7db6788ad47d5df8715cd405ca15e1744744ff 100644 (file)
@@ -83,6 +83,18 @@ class ShowrsvpAction extends ShownoticeAction
             throw new ClientException(_('No such RSVP.'), 404);
         }
 
+        $cur = common_current_user();
+
+        if (!empty($cur)) {
+            $curProfile = $cur->getProfile();
+        } else {
+            $curProfile = null;
+        }
+
+        if (!$this->notice->inScope($curProfile)) {
+            throw new ClientException(_('Not available.'), 403);
+        }
+
         $this->user = User::staticGet('id', $this->rsvp->profile_id);
 
         if (empty($this->user)) {
index 9c2a4060900b09e957107c95d4fc0f5ac8f8fd44..d5a3f7c633f27a6650d8c183dbcd205775fd1420 100644 (file)
@@ -455,7 +455,7 @@ ENDOFSCRIPT;
      */
     function onStartEnqueueNotice($notice, &$transports)
     {
-        if (self::hasApplication() && $notice->isLocal()) {
+        if (self::hasApplication() && $notice->isLocal() && $notice->inScope(null)) {
             array_push($transports, 'facebook');
         }
         return true;
index e75130b9e981f0dfe06083851c3f3fc01c02ff27..26b7ade64dbf2203525f35880aaa4040b55e0025 100644 (file)
@@ -111,7 +111,9 @@ class OStatusPlugin extends Plugin
      */
     function onStartEnqueueNotice($notice, &$transports)
     {
-        if ($notice->isLocal()) {
+        // FIXME: we don't do privacy-controlled OStatus updates yet.
+        // once that happens, finer grain of control here.
+        if ($notice->isLocal() && $notice->inScope(null)) {
             // put our transport first, in case there's any conflict (like OMB)
             array_unshift($transports, 'ostatus');
         }
index d95b1c512ef34acfcae72858827ccd58d0869b71..d59d9e28f3489ec0f7295e879f5d5fb16ae8d7ef 100644 (file)
@@ -76,6 +76,18 @@ class ShowPollAction extends ShownoticeAction
             throw new ClientException(_m('No such poll notice.'), 404);
         }
 
+        $cur = common_current_user();
+
+        if (!empty($cur)) {
+            $curProfile = $cur->getProfile();
+        } else {
+            $curProfile = null;
+        }
+
+        if (!$this->notice->inScope($curProfile)) {
+            throw new ClientException(_('Not available.'), 403);
+        }
+
         $this->user = User::staticGet('id', $this->poll->profile_id);
 
         if (empty($this->user)) {
index d7c895d9cf4e8e7d401db4bd345e91bffa57ee31..b6658b13a6f2c15d9caabde1ccdc8bc0dd3fe225 100644 (file)
@@ -228,7 +228,7 @@ class TwitterBridgePlugin extends Plugin
      */
     function onStartEnqueueNotice($notice, &$transports)
     {
-        if (self::hasKeys() && $notice->isLocal()) {
+        if (self::hasKeys() && $notice->isLocal() && $notice->inScope(null)) {
             // Avoid a possible loop
             if ($notice->source != 'twitter') {
                 array_push($transports, 'twitter');
index 3244cda104708dae8e897e8c09425136ab624abb..b460be1dd22609c7e854adcd69a4b573fd5a6cc4 100644 (file)
@@ -71,7 +71,7 @@ function newNotice($i, $tagmax)
 {
     global $userprefix;
 
-    $options = array();
+    $options = array('scope' => common_config('notice', 'defaultscope'));
 
     $n = rand(0, $i - 1);
     $user = User::staticGet('nickname', sprintf('%s%d', $userprefix, $n));
@@ -95,6 +95,10 @@ function newNotice($i, $tagmax)
                 $rprofile = $notices->getProfile();
                 $content = "@".$rprofile->nickname." ".$content;
             }
+            $private_to_addressees = rand(0, 4);
+            if ($private_to_addressees == 0) {
+                $options['scope'] |= Notice::ADDRESSEE_SCOPE;
+            }
         }
     }
 
@@ -120,9 +124,19 @@ function newNotice($i, $tagmax)
             }
             $options['groups'] = array($groups->id);
             $content = "!".$groups->nickname." ".$content;
+            $private_to_group = rand(0, 2);
+            if ($private_to_group == 0) {
+                $options['scope'] |= Notice::GROUP_SCOPE;
+            }
         }
     }
 
+    $private_to_site = rand(0, 4);
+
+    if ($private_to_site == 0) {
+        $options['scope'] |= Notice::SITE_SCOPE;
+    }
+
     $notice = Notice::saveNew($user->id, $content, 'system', $options);
 }
 
index 6c3c9e62961835f09c6d6cc109c3e3d3195ecaf5..45553e973aaf11f00df77e27588e5a125f1efd3a 100644 (file)
@@ -361,6 +361,10 @@ address .poweredby {
     opacity:0;
 }
 
+.form_notice .to-selector { 
+    clear:left;
+}
+
 /* Local navigation */
 
 #site_nav_local_views { 
@@ -1156,6 +1160,11 @@ width:auto;
 margin-left:0;
 }
 
+.limited-scope .entry-content .timestamp:before {
+content:'☠';
+font-size:150%;
+}
+
 /* override OStatus plugin style */
 
 #form_ostatus_connect.form_settings.dialogbox, #form_ostatus_sub.dialogbox {