X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=classes%2FNotice.php;h=90e3e76ef33c471fd999a5cff5b54ae819bf8cd8;hb=df2390a90f43e7493eef492960526df112efa9a4;hp=bcd7947bd17fa848f5f5535f50e74d22f6e9d27e;hpb=72c82a2e293549c10b60ca9e295f2f430224e814;p=quix0rs-gnu-social.git diff --git a/classes/Notice.php b/classes/Notice.php index bcd7947bd1..90e3e76ef3 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -55,24 +55,26 @@ class Notice extends Memcached_DataObject public $__table = 'notice'; // table name public $id; // int(4) primary_key not_null - public $profile_id; // int(4) not_null + public $profile_id; // int(4) multiple_key not_null public $uri; // varchar(255) unique_key - public $content; // text() - public $rendered; // text() + public $content; // text + public $rendered; // text public $url; // varchar(255) - public $created; // datetime() not_null - public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP + public $created; // datetime multiple_key not_null default_0000-00-00%2000%3A00%3A00 + public $modified; // timestamp not_null default_CURRENT_TIMESTAMP public $reply_to; // int(4) - public $is_local; // tinyint(1) + public $is_local; // int(4) public $source; // varchar(32) public $conversation; // int(4) public $lat; // decimal(10,7) public $lon; // decimal(10,7) public $location_id; // int(4) public $location_ns; // int(4) + public $repeat_of; // int(4) /* Static get */ - function staticGet($k,$v=NULL) { + function staticGet($k,$v=NULL) + { return Memcached_DataObject::staticGet('Notice',$k,$v); } @@ -92,10 +94,6 @@ class Notice extends Memcached_DataObject function delete() { - $this->blowCaches(true); - $this->blowFavesCache(true); - $this->blowSubsCache(true); - // For auditing purposes, save a record that the notice // was deleted. @@ -107,26 +105,20 @@ class Notice extends Memcached_DataObject $deleted->created = $this->created; $deleted->deleted = common_sql_now(); - $this->query('BEGIN'); - $deleted->insert(); - //Null any notices that are replies to this notice - $this->query(sprintf("UPDATE notice set reply_to = null WHERE reply_to = %d", $this->id)); - $related = array('Reply', - 'Fave', - 'Notice_tag', - 'Group_inbox', - 'Queue_item', - 'Notice_inbox'); - - foreach ($related as $cls) { - $inst = new $cls(); - $inst->notice_id = $this->id; - $inst->delete(); - } + // Clear related records + + $this->clearReplies(); + $this->clearRepeats(); + $this->clearFaves(); + $this->clearTags(); + $this->clearGroupInboxes(); + + // NOTE: we don't clear inboxes + // NOTE: we don't clear queue items + $result = parent::delete(); - $this->query('COMMIT'); } function saveTags() @@ -148,6 +140,7 @@ class Notice extends Memcached_DataObject foreach(array_unique($hashtags) as $hashtag) { /* elide characters we don't want in the tag */ $this->saveTag($hashtag); + self::blow('profile:notice_ids_tagged:%d:%s', $this->profile_id, $tag->tag); } return true; } @@ -165,11 +158,53 @@ class Notice extends Memcached_DataObject $last_error->message)); return; } + + // if it's saved, blow its cache + $tag->blowCache(false); } - static function saveNew($profile_id, $content, $source=null, - $is_local=Notice::LOCAL_PUBLIC, $reply_to=null, $uri=null, $created=null, - $lat=null, $lon=null, $location_id=null, $location_ns=null) { + /** + * Save a new notice and push it out to subscribers' inboxes. + * Poster's permissions are checked before sending. + * + * @param int $profile_id Profile ID of the poster + * @param string $content source message text; links may be shortened + * per current user's preference + * @param string $source source key ('web', 'api', etc) + * @param array $options Associative array of optional properties: + * string 'created' timestamp of notice; defaults to now + * int 'is_local' source/gateway ID, one of: + * Notice::LOCAL_PUBLIC - Local, ok to appear in public timeline + * Notice::REMOTE_OMB - Sent from a remote OMB service; + * hide from public timeline but show in + * local "and friends" timelines + * Notice::LOCAL_NONPUBLIC - Local, but hide from public timeline + * Notice::GATEWAY - From another non-OMB service; + * will not appear in public views + * float 'lat' decimal latitude for geolocation + * float 'lon' decimal longitude for geolocation + * int 'location_id' geoname identifier + * int 'location_ns' geoname namespace to interpret location_id + * int 'reply_to'; notice ID this is a reply to + * int 'repeat_of'; notice ID this is a repeat of + * string 'uri' permalink to notice; defaults to local notice URL + * + * @return Notice + * @throws ClientException + */ + static function saveNew($profile_id, $content, $source, $options=null) { + $defaults = array('uri' => null, + 'reply_to' => null, + 'repeat_of' => null); + + if (!empty($options)) { + $options = $options + $defaults; + extract($options); + } + + if (!isset($is_local)) { + $is_local = Notice::LOCAL_PUBLIC; + } $profile = Profile::staticGet($profile_id); @@ -225,7 +260,13 @@ class Notice extends Memcached_DataObject $notice->source = $source; $notice->uri = $uri; - $notice->reply_to = self::getReplyTo($reply_to, $profile_id, $source, $final); + // Handle repeat case + + if (isset($repeat_of)) { + $notice->repeat_of = $repeat_of; + } else { + $notice->reply_to = self::getReplyTo($reply_to, $profile_id, $source, $final); + } if (!empty($notice->reply_to)) { $reply = Notice::staticGet('id', $notice->reply_to); @@ -235,29 +276,17 @@ class Notice extends Memcached_DataObject if (!empty($lat) && !empty($lon)) { $notice->lat = $lat; $notice->lon = $lon; + } + + if (!empty($location_ns) && !empty($location_id)) { $notice->location_id = $location_id; $notice->location_ns = $location_ns; - } else if (!empty($location_ns) && !empty($location_id)) { - $location = Location::fromId($location_id, $location_ns); - if (!empty($location)) { - $notice->lat = $location->lat; - $notice->lon = $location->lon; - $notice->location_id = $location_id; - $notice->location_ns = $location_ns; - } - } else { - $notice->lat = $profile->lat; - $notice->lon = $profile->lon; - $notice->location_id = $profile->location_id; - $notice->location_ns = $profile->location_ns; } if (Event::handle('StartNoticeSave', array(&$notice))) { // XXX: some of these functions write to the DB - $notice->query('BEGIN'); - $id = $notice->insert(); if (!$id) { @@ -291,25 +320,47 @@ class Notice extends Memcached_DataObject } } - // XXX: do we need to change this for remote users? + } - $notice->saveTags(); + # Clear the cache for subscribed users, so they'll update at next request + # XXX: someone clever could prepend instead of clearing the cache + $notice->blowOnInsert(); - $notice->addToInboxes(); + if (common_config('queue', 'inboxes')) { + $qm = QueueManager::get(); + $qm->enqueue($notice, 'distrib'); + } else { + $handler = new DistribQueueHandler(); + $handler->handle($notice); + } - $notice->saveUrls(); + return $notice; + } - $notice->query('COMMIT'); + function blowOnInsert() + { + self::blow('profile:notice_ids:%d', $this->profile_id); + self::blow('public'); - Event::handle('EndNoticeSave', array($notice)); + if ($this->conversation != $this->id) { + self::blow('notice:conversation_ids:%d', $this->conversation); } - # Clear the cache for subscribed users, so they'll update at next request - # XXX: someone clever could prepend instead of clearing the cache + if (!empty($this->repeat_of)) { + self::blow('notice:repeats:%d', $this->repeat_of); + } - $notice->blowCaches(); + $original = Notice::staticGet('id', $this->repeat_of); - return $notice; + if (!empty($original)) { + $originalUser = User::staticGet('id', $original->profile_id); + if (!empty($originalUser)) { + self::blow('user:repeats_of_me:%d', $originalUser->id); + } + } + + $profile = Profile::staticGet($this->profile_id); + $profile->blowNoticeCount(); } /** save all urls in the notice to the db @@ -414,360 +465,6 @@ class Notice extends Memcached_DataObject return $att; } - function blowCaches($blowLast=false) - { - $this->blowSubsCache($blowLast); - $this->blowNoticeCache($blowLast); - $this->blowRepliesCache($blowLast); - $this->blowPublicCache($blowLast); - $this->blowTagCache($blowLast); - $this->blowGroupCache($blowLast); - $this->blowConversationCache($blowLast); - $profile = Profile::staticGet($this->profile_id); - $profile->blowNoticeCount(); - } - - function blowConversationCache($blowLast=false) - { - $cache = common_memcache(); - if ($cache) { - $ck = common_cache_key('notice:conversation_ids:'.$this->conversation); - $cache->delete($ck); - if ($blowLast) { - $cache->delete($ck.';last'); - } - } - } - - function blowGroupCache($blowLast=false) - { - $cache = common_memcache(); - if ($cache) { - $group_inbox = new Group_inbox(); - $group_inbox->notice_id = $this->id; - if ($group_inbox->find()) { - while ($group_inbox->fetch()) { - $cache->delete(common_cache_key('user_group:notice_ids:' . $group_inbox->group_id)); - if ($blowLast) { - $cache->delete(common_cache_key('user_group:notice_ids:' . $group_inbox->group_id.';last')); - } - $member = new Group_member(); - $member->group_id = $group_inbox->group_id; - if ($member->find()) { - while ($member->fetch()) { - $cache->delete(common_cache_key('notice_inbox:by_user:' . $member->profile_id)); - if ($blowLast) { - $cache->delete(common_cache_key('notice_inbox:by_user:' . $member->profile_id . ';last')); - } - } - } - } - } - $group_inbox->free(); - unset($group_inbox); - } - } - - function blowTagCache($blowLast=false) - { - $cache = common_memcache(); - if ($cache) { - $tag = new Notice_tag(); - $tag->notice_id = $this->id; - if ($tag->find()) { - while ($tag->fetch()) { - $tag->blowCache($blowLast); - $ck = 'profile:notice_ids_tagged:' . $this->profile_id . ':' . $tag->tag; - - $cache->delete($ck); - if ($blowLast) { - $cache->delete($ck . ';last'); - } - } - } - $tag->free(); - unset($tag); - } - } - - function blowSubsCache($blowLast=false) - { - $cache = common_memcache(); - if ($cache) { - $user = new User(); - - $UT = common_config('db','type')=='pgsql'?'"user"':'user'; - $user->query('SELECT id ' . - - "FROM $UT JOIN subscription ON $UT.id = subscription.subscriber " . - 'WHERE subscription.subscribed = ' . $this->profile_id); - - while ($user->fetch()) { - $cache->delete(common_cache_key('notice_inbox:by_user:'.$user->id)); - $cache->delete(common_cache_key('notice_inbox:by_user_own:'.$user->id)); - if ($blowLast) { - $cache->delete(common_cache_key('notice_inbox:by_user:'.$user->id.';last')); - $cache->delete(common_cache_key('notice_inbox:by_user_own:'.$user->id.';last')); - } - } - $user->free(); - unset($user); - } - } - - function blowNoticeCache($blowLast=false) - { - if ($this->is_local) { - $cache = common_memcache(); - if (!empty($cache)) { - $cache->delete(common_cache_key('profile:notice_ids:'.$this->profile_id)); - if ($blowLast) { - $cache->delete(common_cache_key('profile:notice_ids:'.$this->profile_id.';last')); - } - } - } - } - - function blowRepliesCache($blowLast=false) - { - $cache = common_memcache(); - if ($cache) { - $reply = new Reply(); - $reply->notice_id = $this->id; - if ($reply->find()) { - while ($reply->fetch()) { - $cache->delete(common_cache_key('reply:stream:'.$reply->profile_id)); - if ($blowLast) { - $cache->delete(common_cache_key('reply:stream:'.$reply->profile_id.';last')); - } - } - } - $reply->free(); - unset($reply); - } - } - - function blowPublicCache($blowLast=false) - { - if ($this->is_local == Notice::LOCAL_PUBLIC) { - $cache = common_memcache(); - if ($cache) { - $cache->delete(common_cache_key('public')); - if ($blowLast) { - $cache->delete(common_cache_key('public').';last'); - } - } - } - } - - function blowFavesCache($blowLast=false) - { - $cache = common_memcache(); - if ($cache) { - $fave = new Fave(); - $fave->notice_id = $this->id; - if ($fave->find()) { - while ($fave->fetch()) { - $cache->delete(common_cache_key('fave:ids_by_user:'.$fave->user_id)); - $cache->delete(common_cache_key('fave:by_user_own:'.$fave->user_id)); - if ($blowLast) { - $cache->delete(common_cache_key('fave:ids_by_user:'.$fave->user_id.';last')); - $cache->delete(common_cache_key('fave:by_user_own:'.$fave->user_id.';last')); - } - } - } - $fave->free(); - unset($fave); - } - } - - # XXX: too many args; we need to move to named params or even a separate - # class for notice streams - - static function getStream($qry, $cachekey, $offset=0, $limit=20, $since_id=0, $max_id=0, $order=null, $since=null) { - - if (common_config('memcached', 'enabled')) { - - # Skip the cache if this is a since, since_id or max_id qry - if ($since_id > 0 || $max_id > 0 || $since) { - return Notice::getStreamDirect($qry, $offset, $limit, $since_id, $max_id, $order, $since); - } else { - return Notice::getCachedStream($qry, $cachekey, $offset, $limit, $order); - } - } - - return Notice::getStreamDirect($qry, $offset, $limit, $since_id, $max_id, $order, $since); - } - - static function getStreamDirect($qry, $offset, $limit, $since_id, $max_id, $order, $since) { - - $needAnd = false; - $needWhere = true; - - if (preg_match('/\bWHERE\b/i', $qry)) { - $needWhere = false; - $needAnd = true; - } - - if ($since_id > 0) { - - if ($needWhere) { - $qry .= ' WHERE '; - $needWhere = false; - } else { - $qry .= ' AND '; - } - - $qry .= ' notice.id > ' . $since_id; - } - - if ($max_id > 0) { - - if ($needWhere) { - $qry .= ' WHERE '; - $needWhere = false; - } else { - $qry .= ' AND '; - } - - $qry .= ' notice.id <= ' . $max_id; - } - - if ($since) { - - if ($needWhere) { - $qry .= ' WHERE '; - $needWhere = false; - } else { - $qry .= ' AND '; - } - - $qry .= ' notice.created > \'' . date('Y-m-d H:i:s', $since) . '\''; - } - - # Allow ORDER override - - if ($order) { - $qry .= $order; - } else { - $qry .= ' ORDER BY notice.created DESC, notice.id DESC '; - } - - if (common_config('db','type') == 'pgsql') { - $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; - } else { - $qry .= ' LIMIT ' . $offset . ', ' . $limit; - } - - $notice = new Notice(); - - $notice->query($qry); - - return $notice; - } - - # XXX: this is pretty long and should probably be broken up into - # some helper functions - - static function getCachedStream($qry, $cachekey, $offset, $limit, $order) { - - # If outside our cache window, just go to the DB - - if ($offset + $limit > NOTICE_CACHE_WINDOW) { - return Notice::getStreamDirect($qry, $offset, $limit, null, null, $order, null); - } - - # Get the cache; if we can't, just go to the DB - - $cache = common_memcache(); - - if (empty($cache)) { - return Notice::getStreamDirect($qry, $offset, $limit, null, null, $order, null); - } - - # Get the notices out of the cache - - $notices = $cache->get(common_cache_key($cachekey)); - - # On a cache hit, return a DB-object-like wrapper - - if ($notices !== false) { - $wrapper = new ArrayWrapper(array_slice($notices, $offset, $limit)); - return $wrapper; - } - - # If the cache was invalidated because of new data being - # added, we can try and just get the new stuff. We keep an additional - # copy of the data at the key + ';last' - - # No cache hit. Try to get the *last* cached version - - $last_notices = $cache->get(common_cache_key($cachekey) . ';last'); - - if ($last_notices) { - - # Reverse-chron order, so last ID is last. - - $last_id = $last_notices[0]->id; - - # XXX: this assumes monotonically increasing IDs; a fair - # bet with our DB. - - $new_notice = Notice::getStreamDirect($qry, 0, NOTICE_CACHE_WINDOW, - $last_id, null, $order, null); - - if ($new_notice) { - $new_notices = array(); - while ($new_notice->fetch()) { - $new_notices[] = clone($new_notice); - } - $new_notice->free(); - $notices = array_slice(array_merge($new_notices, $last_notices), - 0, NOTICE_CACHE_WINDOW); - - # Store the array in the cache for next time - - $result = $cache->set(common_cache_key($cachekey), $notices); - $result = $cache->set(common_cache_key($cachekey) . ';last', $notices); - - # return a wrapper of the array for use now - - return new ArrayWrapper(array_slice($notices, $offset, $limit)); - } - } - - # Otherwise, get the full cache window out of the DB - - $notice = Notice::getStreamDirect($qry, 0, NOTICE_CACHE_WINDOW, null, null, $order, null); - - # If there are no hits, just return the value - - if (empty($notice)) { - return $notice; - } - - # Pack results into an array - - $notices = array(); - - while ($notice->fetch()) { - $notices[] = clone($notice); - } - - $notice->free(); - - # Store the array in the cache for next time - - $result = $cache->set(common_cache_key($cachekey), $notices); - $result = $cache->set(common_cache_key($cachekey) . ';last', $notices); - - # return a wrapper of the array for use now - - $wrapper = new ArrayWrapper(array_slice($notices, $offset, $limit)); - - return $wrapper; - } - function getStreamByIds($ids) { $cache = common_memcache(); @@ -788,10 +485,24 @@ class Notice extends Memcached_DataObject return $notice; } $notice->whereAdd('id in (' . implode(', ', $ids) . ')'); - $notice->orderBy('id DESC'); $notice->find(); - return $notice; + + $temp = array(); + + while ($notice->fetch()) { + $temp[$notice->id] = clone($notice); + } + + $wrapped = array(); + + foreach ($ids as $id) { + if (array_key_exists($id, $temp)) { + $wrapped[] = $temp[$id]; + } + } + + return new ArrayWrapper($wrapped); } } @@ -903,11 +614,28 @@ class Notice extends Memcached_DataObject return $ids; } - function addToInboxes() + /** + * @param $groups array of Group *objects* + * @param $recipients array of profile *ids* + */ + function whoGets($groups=null, $recipients=null) { - // XXX: loads constants + $c = self::memcache(); - $inbox = new Notice_inbox(); + if (!empty($c)) { + $ni = $c->get(common_cache_key('notice:who_gets:'.$this->id)); + if ($ni !== false) { + return $ni; + } + } + + if (is_null($groups)) { + $groups = $this->getGroups(); + } + + if (is_null($recipients)) { + $recipients = $this->getReplies(); + } $users = $this->getSubscribedUsers(); @@ -921,7 +649,6 @@ class Notice extends Memcached_DataObject $ni[$id] = NOTICE_INBOX_SOURCE_SUB; } - $groups = $this->saveGroups(); $profile = $this->getProfile(); foreach ($groups as $group) { @@ -936,8 +663,6 @@ class Notice extends Memcached_DataObject } } - $recipients = $this->saveReplies(); - foreach ($recipients as $recipient) { if (!array_key_exists($recipient, $ni)) { @@ -948,7 +673,19 @@ class Notice extends Memcached_DataObject } } - Notice_inbox::bulkInsert($this->id, $this->created, $ni); + if (!empty($c)) { + // XXX: pack this data better + $c->set(common_cache_key('notice:who_gets:'.$this->id), $ni); + } + + return $ni; + } + + function addToInboxes($groups, $recipients) + { + $ni = $this->whoGets($groups, $recipients); + + Inbox::bulkInsert($this->id, array_keys($ni)); return; } @@ -980,8 +717,17 @@ class Notice extends Memcached_DataObject return $ids; } + /** + * @return array of Group objects + */ function saveGroups() { + // Don't save groups for repeats + + if (!empty($this->repeat_of)) { + return array(); + } + $groups = array(); /* extract all !group */ @@ -1041,14 +787,30 @@ class Notice extends Memcached_DataObject $gi->notice_id = $this->id; $gi->created = $this->created; - return $gi->insert(); + $result = $gi->insert(); + + if (!result) { + common_log_db_error($gi, 'INSERT', __FILE__); + throw new ServerException(_('Problem saving group inbox.')); + } + + self::blow('user_group:notice_ids:%d', $gi->group_id); } return true; } + /** + * @return array of integer profile IDs + */ function saveReplies() { + // Don't save reply data for repeats + + if (!empty($this->repeat_of)) { + return array(); + } + // Alternative reply format $tname = false; if (preg_match('/^T ([A-Z0-9]{1,64}) /', $this->content, $match)) { @@ -1125,9 +887,10 @@ class Notice extends Memcached_DataObject $recipientIds = array_keys($replied); - foreach ($recipientIds as $recipient) { - $user = User::staticGet('id', $recipient); - if ($user) { + foreach ($recipientIds as $recipientId) { + $user = User::staticGet('id', $recipientId); + if (!empty($user)) { + self::blow('reply:stream:%d', $reply->profile_id); mail_notify_attn($user, $this); } } @@ -1135,6 +898,63 @@ class Notice extends Memcached_DataObject return $recipientIds; } + function getReplies() + { + // XXX: cache me + + $ids = array(); + + $reply = new Reply(); + $reply->selectAdd(); + $reply->selectAdd('profile_id'); + $reply->notice_id = $this->id; + + if ($reply->find()) { + while($reply->fetch()) { + $ids[] = $reply->profile_id; + } + } + + $reply->free(); + + return $ids; + } + + /** + * Same calculation as saveGroups but without the saving + * @fixme merge the functions + * @return array of Group objects + */ + function getGroups() + { + // Don't save groups for repeats + + if (!empty($this->repeat_of)) { + return array(); + } + + // XXX: cache me + + $groups = array(); + + $gi = new Group_inbox(); + + $gi->selectAdd(); + $gi->selectAdd('group_id'); + + $gi->notice_id = $this->id; + + if ($gi->find()) { + while ($gi->fetch()) { + $groups[] = clone($gi); + } + } + + $gi->free(); + + return $groups; + } + function asAtomEntry($namespace=false, $source=false) { $profile = $this->getProfile(); @@ -1187,7 +1007,7 @@ class Notice extends Memcached_DataObject $xs->element('id', null, $this->uri); $xs->element('published', null, common_date_w3dtf($this->created)); - $xs->element('updated', null, common_date_w3dtf($this->modified)); + $xs->element('updated', null, common_date_w3dtf($this->created)); if ($this->reply_to) { $reply_notice = Notice::staticGet('id', $this->reply_to); @@ -1265,7 +1085,7 @@ class Notice extends Memcached_DataObject $idstr = $cache->get($idkey); - if (!empty($idstr)) { + if ($idstr !== false) { // Cache hit! Woohoo! $window = explode(',', $idstr); $ids = array_slice($window, $offset, $limit); @@ -1274,7 +1094,7 @@ class Notice extends Memcached_DataObject $laststr = $cache->get($idkey.';last'); - if (!empty($laststr)) { + if ($laststr !== false) { $window = explode(',', $laststr); $last_id = $window[0]; $new_ids = call_user_func_array($fn, array_merge($args, array(0, NOTICE_CACHE_WINDOW, @@ -1409,4 +1229,222 @@ class Notice extends Memcached_DataObject return $location; } + + function repeat($repeater_id, $source) + { + $author = Profile::staticGet('id', $this->profile_id); + + $content = sprintf(_('RT @%1$s %2$s'), + $author->nickname, + $this->content); + + $maxlen = common_config('site', 'textlimit'); + if ($maxlen > 0 && mb_strlen($content) > $maxlen) { + // Web interface and current Twitter API clients will + // pull the original notice's text, but some older + // clients and RSS/Atom feeds will see this trimmed text. + // + // Unfortunately this is likely to lose tags or URLs + // at the end of long notices. + $content = mb_substr($content, 0, $maxlen - 4) . ' ...'; + } + + return self::saveNew($repeater_id, $content, $source, + array('repeat_of' => $this->id)); + } + + // These are supposed to be in chron order! + + function repeatStream($limit=100) + { + $cache = common_memcache(); + + if (empty($cache)) { + $ids = $this->_repeatStreamDirect($limit); + } else { + $idstr = $cache->get(common_cache_key('notice:repeats:'.$this->id)); + if ($idstr !== false) { + $ids = explode(',', $idstr); + } else { + $ids = $this->_repeatStreamDirect(100); + $cache->set(common_cache_key('notice:repeats:'.$this->id), implode(',', $ids)); + } + if ($limit < 100) { + // We do a max of 100, so slice down to limit + $ids = array_slice($ids, 0, $limit); + } + } + + return Notice::getStreamByIds($ids); + } + + function _repeatStreamDirect($limit) + { + $notice = new Notice(); + + $notice->selectAdd(); // clears it + $notice->selectAdd('id'); + + $notice->repeat_of = $this->id; + + $notice->orderBy('created'); // NB: asc! + + if (!is_null($offset)) { + $notice->limit($offset, $limit); + } + + $ids = array(); + + if ($notice->find()) { + while ($notice->fetch()) { + $ids[] = $notice->id; + } + } + + $notice->free(); + $notice = NULL; + + return $ids; + } + + function locationOptions($lat, $lon, $location_id, $location_ns, $profile = null) + { + $options = array(); + + if (!empty($location_id) && !empty($location_ns)) { + + $options['location_id'] = $location_id; + $options['location_ns'] = $location_ns; + + $location = Location::fromId($location_id, $location_ns); + + if (!empty($location)) { + $options['lat'] = $location->lat; + $options['lon'] = $location->lon; + } + + } else if (!empty($lat) && !empty($lon)) { + + $options['lat'] = $lat; + $options['lon'] = $lon; + + $location = Location::fromLatLon($lat, $lon); + + if (!empty($location)) { + $options['location_id'] = $location->location_id; + $options['location_ns'] = $location->location_ns; + } + } else if (!empty($profile)) { + + if (isset($profile->lat) && isset($profile->lon)) { + $options['lat'] = $profile->lat; + $options['lon'] = $profile->lon; + } + + if (isset($profile->location_id) && isset($profile->location_ns)) { + $options['location_id'] = $profile->location_id; + $options['location_ns'] = $profile->location_ns; + } + } + + return $options; + } + + function clearReplies() + { + $replyNotice = new Notice(); + $replyNotice->reply_to = $this->id; + + //Null any notices that are replies to this notice + + if ($replyNotice->find()) { + while ($replyNotice->fetch()) { + $orig = clone($replyNotice); + $replyNotice->reply_to = null; + $replyNotice->update($orig); + } + } + + // Reply records + + $reply = new Reply(); + $reply->notice_id = $this->id; + + if ($reply->find()) { + while($reply->fetch()) { + self::blow('reply:stream:%d', $reply->profile_id); + $reply->delete(); + } + } + + $reply->free(); + } + + function clearRepeats() + { + $repeatNotice = new Notice(); + $repeatNotice->repeat_of = $this->id; + + //Null any notices that are repeats of this notice + + if ($repeatNotice->find()) { + while ($repeatNotice->fetch()) { + $orig = clone($repeatNotice); + $repeatNotice->repeat_of = null; + $repeatNotice->update($orig); + } + } + } + + function clearFaves() + { + $fave = new Fave(); + $fave->notice_id = $this->id; + + if ($fave->find()) { + while ($fave->fetch()) { + self::blow('fave:ids_by_user_own:%d', $fave->user_id); + self::blow('fave:ids_by_user_own:%d;last', $fave->user_id); + self::blow('fave:ids_by_user:%d', $fave->user_id); + self::blow('fave:ids_by_user:%d;last', $fave->user_id); + $fave->delete(); + } + } + + $fave->free(); + } + + function clearTags() + { + $tag = new Notice_tag(); + $tag->notice_id = $this->id; + + if ($tag->find()) { + while ($tag->fetch()) { + self::blow('profile:notice_ids_tagged:%d:%s', $this->profile_id, common_keyize($tag->tag)); + self::blow('profile:notice_ids_tagged:%d:%s;last', $this->profile_id, common_keyize($tag->tag)); + self::blow('notice_tag:notice_ids:%s', common_keyize($tag->tag)); + self::blow('notice_tag:notice_ids:%s;last', common_keyize($tag->tag)); + $tag->delete(); + } + } + + $tag->free(); + } + + function clearGroupInboxes() + { + $gi = new Group_inbox(); + + $gi->notice_id = $this->id; + + if ($gi->find()) { + while ($gi->fetch()) { + self::blow('user_group:notice_ids:%d', $gi->group_id); + $gi->delete(); + } + } + + $gi->free(); + } }