+
+ /**
+ * 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; pass null to check for public/unauthenticated users.
+ *
+ * @return boolean whether the profile is in the notice's scope
+ */
+ function inScope($profile)
+ {
+ if (is_null($profile)) {
+ $keypart = sprintf('notice:in-scope-for:%d:null', $this->id);
+ } else {
+ $keypart = sprintf('notice:in-scope-for:%d:%d', $this->id, $profile->id);
+ }
+
+ $result = self::cacheGet($keypart);
+
+ if ($result === false) {
+ $bResult = $this->_inScope($profile);
+ $result = ($bResult) ? 1 : 0;
+ self::cacheSet($keypart, $result, 0, 300);
+ }
+
+ return ($result == 1) ? true : false;
+ }
+
+ protected 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;
+ }
+
+ static function groupsFromText($text, $profile)
+ {
+ $groups = array();
+
+ /* extract all !group */
+ $count = preg_match_all('/(?:^|\s)!(' . Nickname::DISPLAY_FMT . ')/',
+ strtolower($text),
+ $match);
+
+ if (!$count) {
+ return $groups;
+ }
+
+ foreach (array_unique($match[1]) as $nickname) {
+ $group = User_group::getForNickname($nickname, $profile);
+ if (!empty($group) && $profile->isMember($group)) {
+ $groups[] = $group->id;
+ }
+ }
+
+ return $groups;
+ }
+
+ protected $_original = -1;
+
+ function getOriginal()
+ {
+ if (is_int($this->_original) && $this->_original == -1) {
+ if (empty($this->reply_to)) {
+ $this->_original = null;
+ } else {
+ $this->_original = Notice::staticGet('id', $this->reply_to);
+ }
+ }
+ return $this->_original;
+ }
+
+ /**
+ * Magic function called at serialize() time.
+ *
+ * We use this to drop a couple process-specific references
+ * from DB_DataObject which can cause trouble in future
+ * processes.
+ *
+ * @return array of variable names to include in serialization.
+ */
+
+ function __sleep()
+ {
+ $vars = parent::__sleep();
+ $skip = array('_original', '_profile');
+ return array_diff($vars, $skip);
+ }
+
+ static function defaultScope()
+ {
+ $scope = common_config('notice', 'defaultscope');
+ if (is_null($scope)) {
+ if (common_config('site', 'private')) {
+ $scope = 1;
+ } else {
+ $scope = 0;
+ }
+ }
+ return $scope;
+ }
+