/*
* Get the original representation URL of this notice.
+ *
+ * @param boolean $fallback Whether to fall back to generate a local URL or throw InvalidUrlException
*/
- public function getUrl()
+ public function getUrl($fallback=false)
{
// The risk is we start having empty urls and non-http uris...
// and we can't really handle any other protocol right now.
switch (true) {
case common_valid_http_url($this->url): // should we allow non-http/https URLs?
return $this->url;
- case $this->isLocal():
+ case !$this->isLocal() && common_valid_http_url($this->uri): // Sometimes we only have the URI for remote posts.
+ return $this->uri;
+ case $this->isLocal() || $fallback:
// let's generate a valid link to our locally available notice on demand
return common_local_url('shownotice', array('notice' => $this->id), null, null, false);
- case common_valid_http_url($this->uri):
- return $this->uri;
default:
common_debug('No URL available for notice: id='.$this->id);
throw new InvalidUrlException($this->url);
* @return Notice
* @throws ClientException
*/
- static function saveNew($profile_id, $content, $source, array $options=null) {
+ static function saveNew($profile_id, $content, $source, array $options=array()) {
$defaults = array('uri' => null,
'url' => null,
'conversation' => null, // URI of conversation
'object_type' => null,
'verb' => null);
- if (!empty($options) && is_array($options)) {
+ /*
+ * Above type-hint is already array, so simply count it, this saves
+ * "some" CPU cycles.
+ */
+ if (count($options) > 0) {
$options = array_merge($defaults, $options);
- extract($options);
- } else {
- extract($defaults);
}
+ extract($options);
+
if (!isset($is_local)) {
$is_local = Notice::LOCAL_PUBLIC;
}
throw new ClientException(_('You cannot repeat your own notice.'));
}
- if ($repeat->scope != Notice::SITE_SCOPE &&
- $repeat->scope != Notice::PUBLIC_SCOPE) {
+ if ($repeat->isPrivateScope()) {
// TRANS: Client error displayed when trying to repeat a non-public notice.
throw new ClientException(_('Cannot repeat a private notice.'), 403);
}
if ($this->isPublic()) {
$this->blowStream('public');
+ $this->blowStream('networkpublic');
}
self::blow('notice:list-ids:conversation:%s', $this->conversation);
if ($this->isPublic()) {
self::blow('public;last');
+ self::blow('networkpublic;last');
}
self::blow('fave:by_notice', $this->id);
*
* @return void
*/
- function saveKnownUrls($urls)
+ function saveKnownUrls(array $urls)
{
if (common_config('attachments', 'process_links')) {
// @fixme validation?
foreach (array_unique($group_ids) as $id) {
$group = User_group::getKV('id', $id);
if ($group instanceof User_group) {
- common_log(LOG_ERR, "Local delivery to group id $id, $group->nickname");
+ common_log(LOG_DEBUG, "Local delivery to group id $id, $group->nickname");
$result = $this->addToGroupInbox($group);
if (!$result) {
common_log_db_error($gi, 'INSERT', __FILE__);
$ids[] = $reply->profile_id;
}
- $this->_replies[$this->id] = $ids;
+ $this->_setReplies($ids);
return $ids;
}
- function _setReplies($replies)
+ function _setReplies(array $replies)
{
$this->_replies[$this->id] = $replies;
}
}
$groups = User_group::multiGet('id', $ids);
-
- $this->_groups[$this->id] = $groups->fetchAll();
-
+ $this->_setGroups($groups->fetchAll());
return $this->_groups[$this->id];
}
-
- function _setGroups($groups)
+
+ function _setGroups(array $groups)
{
$this->_groups[$this->id] = $groups;
}
*/
public function getTags()
{
+ // Check default scope (non-private notices)
+ $inScope = (!$this->isPrivateScope());
+
+ // Get current profile
+ $profile = Profile::current();
+
+ // Is the general scope check okay and the user in logged in?
+ //* NOISY-DEBUG: */ common_debug('[' . __METHOD__ . ':' . __LINE__ . ']: inScope=' . intval($inScope) . ',profile[]=' . gettype($profile));
+ if (($inScope === TRUE) && ($profile instanceof Profile)) {
+ /*
+ * Check scope, else a privacy leaks happens this way:
+ *
+ * 1) Bob and Alice follow each other and write private notices
+ * (this->scope=2) to each other.
+ * 2) Bob uses tags in his private notice to alice (which she can
+ * read from him).
+ * 3) Alice adds that notice (with tags) to her favorites
+ * ("faving") it.
+ * 4) The tags from Bob's private notice becomes visible in Alice's
+ * profile.
+ *
+ * This has the simple background that the scope is not being
+ * re-checked. This has to be done here at this point because given
+ * above scenario is a privacy leak as the tags may be *really*
+ * private (nobody else shall see them) such as initmate words or
+ * very political words.
+ */
+ $inScope = $this->inScope($profile);
+ //* NOISY-DEBUG: */ common_debug('[' . __METHOD__ . ':' . __LINE__ . ']: inScope=' . intval($inScope) . ' - After inScope() has been called.');
+ }
+
$tags = array();
$keypart = sprintf('notice:tags:%d', $this->id);
} else {
$tag = new Notice_tag();
$tag->notice_id = $this->id;
- if ($tag->find()) {
+
+ // Check scope for privacy-leak protection (see some lines above why)
+ if (($inScope === TRUE) && ($tag->find())) {
while ($tag->fetch()) {
$tags[] = $tag->tag;
}
function isPublic()
{
- if (common_config('public', 'localonly')) {
- return ($this->is_local == Notice::LOCAL_PUBLIC);
- } else {
- return (($this->is_local != Notice::LOCAL_NONPUBLIC) &&
- ($this->is_local != Notice::GATEWAY));
- }
+ return (($this->is_local != Notice::LOCAL_NONPUBLIC) &&
+ ($this->is_local != Notice::GATEWAY));
}
/**
*
* @return boolean whether the profile is in the notice's scope
*/
- function inScope($profile)
+ function inScope(Profile $profile=null)
{
if (is_null($profile)) {
$keypart = sprintf('notice:in-scope-for:%d:null', $this->id);
return ($result == 1) ? true : false;
}
- protected function _inScope($profile)
+ protected function _inScope(Profile $profile=null)
{
if (!is_null($this->scope)) {
$scope = $this->scope;
$reply = Reply::pkeyGet(array('notice_id' => $this->id,
'profile_id' => $profile->id));
-
+
if (!$reply instanceof Reply) {
return false;
}
}
}
- function isHiddenSpam($profile) {
-
+ function isHiddenSpam(Profile $profile=null) {
+
// Hide posts by silenced users from everyone but moderators.
if (common_config('notice', 'hidespam')) {
return $scope;
}
- static function fillProfiles($notices)
+ static function fillProfiles(array $notices)
{
$map = self::getProfiles($notices);
return array_values($map);
}
-
- static function getProfiles(&$notices)
+
+ static function getProfiles(array &$notices)
{
$ids = array();
foreach ($notices as $notice) {
return Profile::pivotGet('id', $ids);
}
-
- static function fillGroups(&$notices)
+
+ static function fillGroups(array &$notices)
{
$ids = self::_idsOf($notices);
return array_keys($ids);
}
- static function fillAttachments(&$notices)
+ static function fillAttachments(array &$notices)
{
$ids = self::_idsOf($notices);
}
}
- static function fillReplies(&$notices)
+ static function fillReplies(array &$notices)
{
$ids = self::_idsOf($notices);
$replyMap = Reply::listGet('notice_id', $ids);
return $this->_repeats[$this->id];
}
$repeatMap = Notice::listGet('repeat_of', array($this->id));
- $this->_repeats[$this->id] = $repeatMap[$this->id];
+ $this->_setRepeats($repeatMap[$this->id]);
return $this->_repeats[$this->id];
}
- function _setRepeats($repeats)
+ function _setRepeats(array $repeats)
{
$this->_repeats[$this->id] = $repeats;
}
- static function fillRepeats(&$notices)
+ static function fillRepeats(array &$notices)
{
$ids = self::_idsOf($notices);
$repeatMap = Notice::listGet('repeat_of', $ids);
foreach ($notices as $notice) {
- $repeats = $repeatMap[$notice->id];
+ $repeats = $repeatMap[$notice->id];
$notice->_setRepeats($repeats);
}
}
+
+ /**
+ * Checks whether this notice is in "private scope" (non-public notice)
+ *
+ * @return $isPrivate Whether this notice is private
+ */
+ public function isPrivateScope ()
+ {
+ return ($this->scope != Notice::SITE_SCOPE &&
+ $this->scope != Notice::PUBLIC_SCOPE);
+ }
}