HaveIMPlugin: is there an IM plugin loaded?
- &$haveIMPlugin: set me to true if you're loaded!
+StartShowNoticeOptionItems: Before showing first controls in a notice list item; inside the div
+- $nli: NoticeListItem being shown
+
+EndShowNoticeOptionItems: After showing last controls in a notice list item; inside the div
+- $nli: NoticeListItem being shown
+
+StartNoticeInScope: Before checking if a notice should be visible to a user
+- $notice: The notice to check
+- $profile: The profile to check for scope
+- &$bResult: The boolean result; fill this in if you want to skip
+
+EndNoticeInScope: After checking if a notice should be visible to a user
+- $notice: The notice to check
+- $profile: The profile to check for scope
+- &$bResult: The boolean result; overwrite this if you so desire
+
+StartNoticeListPrefill: Before pre-filling a list of notices with extra data
+- &$notices: Notices to be pre-filled
+- $avatarSize: The avatar size for the list
+
+EndNoticeListPrefill: After pre-filling a list of notices with extra data
+- &$notices: Notices that were pre-filled
+- &$profiles: Profiles that were pre-filled
+- $avatarSize: The avatar size for the list
+
$result = self::cacheGet($keypart);
if ($result === false) {
- $bResult = $this->_inScope($profile);
+ $bResult = false;
+ if (Event::handle('StartNoticeInScope', array($this, $profile, &$bResult))) {
+ $bResult = $this->_inScope($profile);
+ Event::handle('EndNoticeInScope', array($this, $profile, &$bResult));
+ }
$result = ($bResult) ? 1 : 0;
self::cacheSet($keypart, $result, 0, 300);
}
// If there's no scope, anyone (even anon) is in scope.
- if ($scope == 0) {
- return true;
- }
+ if ($scope == 0) { // Not private
- // If there's scope, anon cannot be in scope
+ return !$this->isHiddenSpam($profile);
- if (empty($profile)) {
- return false;
- }
+ } else { // Private, somehow
- // Author is always in scope
+ // If there's scope, anon cannot be in scope
- if ($this->profile_id == $profile->id) {
- return true;
- }
+ if (empty($profile)) {
+ return false;
+ }
- // Only for users on this site
+ // Author is always in scope
- if ($scope & Notice::SITE_SCOPE) {
- $user = $profile->getUser();
- if (empty($user)) {
- return false;
+ if ($this->profile_id == $profile->id) {
+ return true;
+ }
+
+ // Only for users on this site
+
+ if ($scope & Notice::SITE_SCOPE) {
+ $user = $profile->getUser();
+ if (empty($user)) {
+ return false;
+ }
}
- }
- // Only for users mentioned in the notice
+ // Only for users mentioned in the notice
- if ($scope & Notice::ADDRESSEE_SCOPE) {
+ if ($scope & Notice::ADDRESSEE_SCOPE) {
- $repl = Reply::pkeyGet(array('notice_id' => $this->id,
- 'profile_id' => $profile->id));
+ $repl = Reply::pkeyGet(array('notice_id' => $this->id,
+ 'profile_id' => $profile->id));
- if (empty($repl)) {
- return false;
+ if (empty($repl)) {
+ return false;
+ }
}
- }
- // Only for members of the given group
+ // Only for members of the given group
- if ($scope & Notice::GROUP_SCOPE) {
+ if ($scope & Notice::GROUP_SCOPE) {
- // XXX: just query for the single membership
+ // XXX: just query for the single membership
- $groups = $this->getGroups();
+ $groups = $this->getGroups();
- $foundOne = false;
+ $foundOne = false;
- foreach ($groups as $group) {
- if ($profile->isMember($group)) {
- $foundOne = true;
- break;
+ foreach ($groups as $group) {
+ if ($profile->isMember($group)) {
+ $foundOne = true;
+ break;
+ }
+ }
+
+ if (!$foundOne) {
+ return false;
}
}
- if (!$foundOne) {
- return false;
+ // Only for followers of the author
+
+ $author = null;
+
+ if ($scope & Notice::FOLLOWER_SCOPE) {
+
+ $author = $this->getProfile();
+
+ if (!Subscription::exists($profile, $author)) {
+ return false;
+ }
}
+
+ return !$this->isHiddenSpam($profile);
}
+ }
- // Only for followers of the author
+ function isHiddenSpam($profile) {
+
+ // Hide posts by silenced users from everyone but moderators.
+
+ if (common_config('notice', 'hidespam')) {
- if ($scope & Notice::FOLLOWER_SCOPE) {
$author = $this->getProfile();
- if (!Subscription::exists($profile, $author)) {
- return false;
+
+ if ($author->hasRole(Profile_role::SILENCED)) {
+ if (empty($profile) || !$profile->hasRole(Profile_role::MODERATOR)) {
+ return true;
+ }
}
}
- return true;
+ return false;
}
static function groupsFromText($text, $profile)
'gc_limit' => 1000), // max sessions to expire at a time
'notice' =>
array('contentlimit' => null,
- 'defaultscope' => null), // null means 1 if site/private, 0 otherwise
+ 'defaultscope' => null, // null means 1 if site/private, 0 otherwise
+ 'hidespam' => false), // Whether to hide silenced users from timelines
'message' =>
array('contentlimit' => null),
'location' =>
// or we get nothing from upstream.
$results = null;
+ $round = 0;
do {
+ common_debug(get_class($this) . ": ($offset, $limit) Round $round: fetching $askFor notices starting at $startAt");
+
$raw = $this->upstream->getNotices($startAt, $askFor, $sinceId, $maxId);
$results = $raw->N;
break;
}
- $notices = $raw->fetchAll();
-
- // XXX: this should probably only be in the scoping one.
-
- Notice::fillGroups($notices);
- Notice::fillReplies($notices);
-
- foreach ($notices as $notice) {
+ $notices = $raw->fetchAll();
+
+ $this->prefill($notices);
+
+ foreach ($notices as $notice) {
if ($this->filter($notice)) {
$filtered[] = $notice;
if (count($filtered) >= $total) {
// XXX: make these smarter; factor hit rate into $askFor
$startAt += $askFor;
- $askFor = max($total - count($filtered), NOTICES_PER_PAGE);
+
+ $hits = count($filtered);
+
+ $lastAsk = $askFor;
+
+ if ($hits === 0) {
+ $askFor = max(min(2 * $askFor, NOTICES_PER_PAGE * 50), NOTICES_PER_PAGE);
+ } else {
+ $askFor = max(min((($total - $hits)*$startAt)/$hits, NOTICES_PER_PAGE * 50), NOTICES_PER_PAGE);
+ }
- } while (count($filtered) < $total && $results !== 0);
+ common_debug(get_class($this) . ": ($offset, $limit) Round $round hits is $hits, results = $results.");
+
+ $round++;
+
+ } while (count($filtered) < $total && $results >= $lastAsk);
return new ArrayWrapper(array_slice($filtered, $offset, $limit));
}
return $ids;
}
+
+ function prefill($notices)
+ {
+ return;
+ }
}
*/
class GroupNoticeStream extends ScopingNoticeStream
{
+ var $group;
+ var $userProfile;
+
function __construct($group, $profile = -1)
{
if (is_int($profile) && $profile == -1) {
$profile = Profile::current();
}
+ $this->group = $group;
+ $this->userProfile = $profile;
+
parent::__construct(new CachingNoticeStream(new RawGroupNoticeStream($group),
'user_group:notice_ids:' . $group->id),
$profile);
}
+
+ function getNoticeIds($offset, $limit, $since_id, $max_id)
+ {
+ if ($this->impossibleStream()) {
+ return array();
+ } else {
+ return parent::getNoticeIds($offset, $limit, $since_id, $max_id);
+ }
+ }
+
+ function getNotices($offset, $limit, $sinceId = null, $maxId = null)
+ {
+ if ($this->impossibleStream()) {
+ return new ArrayWrapper(array());
+ } else {
+ return parent::getNotices($offset, $limit, $sinceId, $maxId);
+ }
+ }
+
+ function impossibleStream()
+ {
+ if ($this->group->force_scope &&
+ (empty($this->userProfile) || !$this->userProfile->isMember($this->group))) {
+ return true;
+ }
+
+ return false;
+ }
}
/**
static function prefill(&$notices, $avatarSize=AVATAR_STREAM_SIZE)
{
- // Prefill attachments
- Notice::fillAttachments($notices);
- // Prefill attachments
- Notice::fillFaves($notices);
- // Prefill repeat data
- Notice::fillRepeats($notices);
- // Prefill the profiles
- $profiles = Notice::fillProfiles($notices);
- // Prefill the avatars
- Profile::fillAvatars($profiles, $avatarSize);
+ if (Event::handle('StartNoticeListPrefill', array(&$notices, $avatarSize))) {
+
+ // Prefill attachments
+ Notice::fillAttachments($notices);
+ // Prefill attachments
+ Notice::fillFaves($notices);
+ // Prefill repeat data
+ Notice::fillRepeats($notices);
+ // Prefill the profiles
+ $profiles = Notice::fillProfiles($notices);
+ // Prefill the avatars
+ Profile::fillAvatars($profiles, $avatarSize);
- $p = Profile::current();
+ $p = Profile::current();
- if (!empty($p)) {
+ if (!empty($p)) {
- $ids = array();
+ $ids = array();
- foreach ($notices as $notice) {
- $ids[] = $notice->id;
- }
+ foreach ($notices as $notice) {
+ $ids[] = $notice->id;
+ }
- Memcached_DataObject::pivotGet('Fave', 'notice_id', $ids, array('user_id' => $p->id));
- Memcached_DataObject::pivotGet('Notice', 'repeat_of', $ids, array('profile_id' => $p->id));
- }
+ Memcached_DataObject::pivotGet('Fave', 'notice_id', $ids, array('user_id' => $p->id));
+ Memcached_DataObject::pivotGet('Notice', 'repeat_of', $ids, array('profile_id' => $p->id));
+ }
+
+ Event::handle('EndNoticeListPrefill', array(&$notices, &$profiles, $avatarSize));
+ }
}
}
$user = common_current_user();
if ($user) {
$this->out->elementStart('div', 'notice-options');
- $this->showFaveForm();
- $this->showReplyLink();
- $this->showRepeatForm();
- $this->showDeleteLink();
+ if (Event::handle('StartShowNoticeOptionItems', array($this))) {
+ $this->showFaveForm();
+ $this->showReplyLink();
+ $this->showRepeatForm();
+ $this->showDeleteLink();
+ Event::handle('EndShowNoticeOptionItems', array($this));
+ }
$this->out->elementEnd('div');
}
Event::handle('EndShowNoticeOptions', array($this));
return false;
}
+ $user = common_current_user();
+
+ if ($this->profile->hasRole(Profile_role::SILENCED) &&
+ (empty($user) || !$user->hasRight(Right::SILENCEUSER))) {
+ throw new ClientException(_('This profile has been silenced by site moderators'), 403);
+ }
+
$this->tag = $this->trimmed('tag');
$this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
common_set_returnto($this->selfUrl());
class ProfileNoticeStream extends ScopingNoticeStream
{
+ var $streamProfile;
+ var $userProfile;
+
function __construct($profile, $userProfile = -1)
{
if (is_int($userProfile) && $userProfile == -1) {
$userProfile = Profile::current();
}
+ $this->streamProfile = $profile;
+ $this->userProfile = $userProfile;
parent::__construct(new CachingNoticeStream(new RawProfileNoticeStream($profile),
'profile:notice_ids:' . $profile->id),
$userProfile);
}
+
+ function getNoticeIds($offset, $limit, $since_id, $max_id)
+ {
+ if ($this->impossibleStream()) {
+ return array();
+ } else {
+ return parent::getNoticeIds($offset, $limit, $since_id, $max_id);
+ }
+ }
+
+ function getNotices($offset, $limit, $sinceId = null, $maxId = null)
+ {
+ if ($this->impossibleStream()) {
+ return new ArrayWrapper(array());
+ } else {
+ return parent::getNotices($offset, $limit, $sinceId, $maxId);
+ }
+ }
+
+ function impossibleStream()
+ {
+ $user = User::staticGet('id', $this->streamProfile->id);
+
+ // If it's a private stream, and no user or not a subscriber
+
+ if (!empty($user) && $user->private_stream &&
+ (empty($this->userProfile) || !$this->userProfile->isSubscribed($this->streamProfile))) {
+ return true;
+ }
+
+ // If it's a spammy stream, and no user or not a moderator
+
+ if (common_config('notice', 'hidespam')) {
+ if ($this->streamProfile->hasRole(Profile_role::SILENCED) &&
+ (empty($this->userProfile) || !$this->userProfile->hasRole(Profile_role::MODERATOR))) {
+ return true;
+ }
+ }
+
+ return false;
+ }
}
/**
{
return $notice->inScope($this->profile);
}
+
+ function prefill($notices)
+ {
+ // XXX: this should probably only be in the scoping one.
+
+ Notice::fillGroups($notices);
+ Notice::fillReplies($notices);
+
+ if (common_config('notice', 'hidespam')) {
+
+ $profiles = Notice::getProfiles($notices);
+
+ foreach ($profiles as $profile) {
+ $pids[] = $profile->id;
+ }
+
+ Memcached_DataObject::pivotGet('Profile_role',
+ 'profile_id',
+ $pids,
+ array('role' => Profile_role::SILENCED));
+ }
+ }
}