From afc86a86d36dc8101c81d5d009546e4629db34a3 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 11 Dec 2009 11:51:09 -0500 Subject: save repeats from the form --- lib/router.php | 1 + 1 file changed, 1 insertion(+) (limited to 'lib/router.php') diff --git a/lib/router.php b/lib/router.php index 37525319f..142206c16 100644 --- a/lib/router.php +++ b/lib/router.php @@ -99,6 +99,7 @@ class Router 'groupblock', 'groupunblock', 'sandbox', 'unsandbox', 'silence', 'unsilence', + 'repeat', 'deleteuser'); foreach ($main as $a) { -- cgit v1.2.3-54-g00ecf From 683edfd199ae150afa74993521c4c65d76b1e19d Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 11 Dec 2009 12:40:05 -0500 Subject: show repeated notices correctly in API output --- actions/apistatusesretweet.php | 136 +++++++++++++++++++++++++++++++++++++++++ lib/router.php | 5 ++ 2 files changed, 141 insertions(+) create mode 100644 actions/apistatusesretweet.php (limited to 'lib/router.php') diff --git a/actions/apistatusesretweet.php b/actions/apistatusesretweet.php new file mode 100644 index 000000000..fc71d2274 --- /dev/null +++ b/actions/apistatusesretweet.php @@ -0,0 +1,136 @@ +. + * + * @category API + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once INSTALLDIR . '/lib/apiauth.php'; +require_once INSTALLDIR . '/lib/mediafile.php'; + +/** + * Repeat a notice through the API + * + * @category API + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class ApiStatusesRetweetAction extends ApiAuthAction +{ + var $original = null; + + /** + * Take arguments for running + * + * @param array $args $_REQUEST args + * + * @return boolean success flag + * + */ + + function prepare($args) + { + parent::prepare($args); + + if ($_SERVER['REQUEST_METHOD'] != 'POST') { + $this->clientError(_('This method requires a POST.'), + 400, $this->format); + return false; + } + + $id = $this->trimmed('id'); + + $this->original = Notice::staticGet('id', $id); + + if (empty($this->original)) { + $this->clientError(_('No such notice'), + 400, $this->format); + return false; + } + + $this->user = $this->auth_user; + + if ($this->user->id == $notice->profile_id) { + $this->clientError(_('Cannot repeat your own notice')); + 400, $this->format); + return false; + } + + $profile = $this->user->getProfile(); + + if ($profile->hasRepeated($id)) { + $this->clientError(_('Already repeated that notice'), + 400, $this->format); + return false; + } + + return true; + } + + /** + * Handle the request + * + * Make a new notice for the update, save it, and show it + * + * @param array $args $_REQUEST data (unused) + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + + $repeat = $this->original->repeat($this->user->id, $this->source); + + common_broadcast_notice($repeat); + + $this->showNotice($repeat); + } + + /** + * Show the resulting notice + * + * @return void + */ + + function showNotice($notice) + { + if (!empty($notice)) { + if ($this->format == 'xml') { + $this->showSingleXmlStatus($notice); + } elseif ($this->format == 'json') { + $this->show_single_json_status($notice); + } + } + } +} diff --git a/lib/router.php b/lib/router.php index 142206c16..835339720 100644 --- a/lib/router.php +++ b/lib/router.php @@ -359,6 +359,11 @@ class Router 'id' => '[0-9]+', 'format' => '(xml|json)')); + $m->connect('api/statuses/retweet/:id.:format', + array('action' => 'ApiStatusesRetweet', + 'id' => '[0-9]+', + 'format' => '(xml|json)')); + // users $m->connect('api/users/show.:format', -- cgit v1.2.3-54-g00ecf From c622d144400026dac65c39303994d11f114c0f5b Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sat, 12 Dec 2009 14:46:24 -0500 Subject: add statuses/retweets to API --- actions/apistatusesretweets.php | 116 ++++++++++++++++++++++++++++++++++++++++ classes/Notice.php | 67 +++++++++++++++++++++++ lib/router.php | 5 ++ 3 files changed, 188 insertions(+) create mode 100644 actions/apistatusesretweets.php (limited to 'lib/router.php') diff --git a/actions/apistatusesretweets.php b/actions/apistatusesretweets.php new file mode 100644 index 000000000..c54a374e2 --- /dev/null +++ b/actions/apistatusesretweets.php @@ -0,0 +1,116 @@ +. + * + * @category API + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once INSTALLDIR . '/lib/apiauth.php'; +require_once INSTALLDIR . '/lib/mediafile.php'; + +/** + * Show up to 100 repeats of a notice + * + * @category API + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class ApiStatusesRetweetsAction extends ApiAuthAction +{ + const MAXCOUNT = 100; + + var $original = null; + var $cnt = self::MAXCOUNT; + + /** + * Take arguments for running + * + * @param array $args $_REQUEST args + * + * @return boolean success flag + * + */ + + function prepare($args) + { + parent::prepare($args); + + $id = $this->trimmed('id'); + + $this->original = Notice::staticGet('id', $id); + + if (empty($this->original)) { + $this->clientError(_('No such notice'), + 400, $this->format); + return false; + } + + $cnt = $this->trimmed('count'); + + if (empty($cnt) || !is_integer($cnt)) { + $cnt = 100; + } else { + $this->cnt = min((int)$cnt, self::MAXCOUNT); + } + + return true; + } + + /** + * Handle the request + * + * Make a new notice for the update, save it, and show it + * + * @param array $args $_REQUEST data (unused) + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + + $strm = $this->original->repeatStream($this->cnt); + + switch ($this->format) { + case 'xml': + $this->showXmlTimeline($strm); + break; + case 'json': + $this->showJsonTimeline($strm); + break; + default: + $this->clientError(_('API method not found!'), $code = 404); + break; + } + } +} diff --git a/classes/Notice.php b/classes/Notice.php index 82753fbdd..ec80f763f 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -441,10 +441,23 @@ class Notice extends Memcached_DataObject $this->blowTagCache($blowLast); $this->blowGroupCache($blowLast); $this->blowConversationCache($blowLast); + $this->blowRepeatCache(); $profile = Profile::staticGet($this->profile_id); $profile->blowNoticeCount(); } + function blowRepeatCache() + { + if (!empty($this->repeat_of)) { + $cache = common_memcache(); + if (!empty($cache)) { + // XXX: only blow if <100 in cache + $ck = common_cache_key('notice:repeats:'.$this->repeat_of); + $result = $cache->delete($ck); + } + } + } + function blowConversationCache($blowLast=false) { $cache = common_memcache(); @@ -1455,4 +1468,58 @@ class Notice extends Memcached_DataObject 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 (!empty($idstr)) { + $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; + } } diff --git a/lib/router.php b/lib/router.php index 835339720..b0b95b080 100644 --- a/lib/router.php +++ b/lib/router.php @@ -364,6 +364,11 @@ class Router 'id' => '[0-9]+', 'format' => '(xml|json)')); + $m->connect('api/statuses/retweets/:id.:format', + array('action' => 'ApiStatusesRetweets', + 'id' => '[0-9]+', + 'format' => '(xml|json)')); + // users $m->connect('api/users/show.:format', -- cgit v1.2.3-54-g00ecf From 138ce0cd05e2e59c79b29f5eeea5c11d1e56e931 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sat, 12 Dec 2009 15:35:05 -0500 Subject: add statuses/retweeted_by_me api action --- actions/apitimelineretweetedbyme.php | 126 +++++++++++++++++++++++++++++++++++ classes/Notice.php | 9 +++ classes/User.php | 52 +++++++++++++++ lib/router.php | 4 ++ 4 files changed, 191 insertions(+) create mode 100644 actions/apitimelineretweetedbyme.php (limited to 'lib/router.php') diff --git a/actions/apitimelineretweetedbyme.php b/actions/apitimelineretweetedbyme.php new file mode 100644 index 000000000..1e65678ad --- /dev/null +++ b/actions/apitimelineretweetedbyme.php @@ -0,0 +1,126 @@ +. + * + * @category API + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once INSTALLDIR . '/lib/apiauth.php'; +require_once INSTALLDIR . '/lib/mediafile.php'; + +/** + * Show authenticating user's most recent repeats + * + * @category API + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class ApiTimelineRetweetedByMeAction extends ApiAuthAction +{ + const DEFAULTCOUNT = 20; + const MAXCOUNT = 200; + const MAXNOTICES = 3200; + + var $repeats = null; + var $cnt = self::DEFAULTCOUNT; + var $page = 1; + var $since_id = null; + var $max_id = null; + + /** + * Take arguments for running + * + * @param array $args $_REQUEST args + * + * @return boolean success flag + * + */ + + function prepare($args) + { + parent::prepare($args); + + $cnt = $this->int('count', self::DEFAULTCOUNT, self::MAXCOUNT, 1); + + $page = $this->int('page', 1, (self::MAXNOTICES/$this->cnt)); + + $since_id = $this->int('since_id'); + + $max_id = $this->int('max_id'); + + return true; + } + + /** + * Handle the request + * + * show a timeline of the user's repeated notices + * + * @param array $args $_REQUEST data (unused) + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + + $offset = ($this->page-1) * $this->cnt; + $limit = $this->cnt; + + $strm = $this->auth_user->repeatedByMe($offset, $limit, $this->since_id, $this->max_id); + + switch ($this->format) { + case 'xml': + $this->showXmlTimeline($strm); + break; + case 'json': + $this->showJsonTimeline($strm); + break; + case 'atom': + $profile = $this->auth_user->getProfile(); + + $title = sprintf(_("Repeated by %s"), $this->auth_user->nickname); + $taguribase = common_config('integration', 'taguri'); + $id = "tag:$taguribase:RepeatedByMe:" . $this->auth_user->id; + $link = common_local_url('showstream', + array('nickname' => $this->auth_user->nickname)); + + $this->showAtomTimeline($strm, $title, $id, $link); + break; + + default: + $this->clientError(_('API method not found!'), $code = 404); + break; + } + } +} diff --git a/classes/Notice.php b/classes/Notice.php index ec80f763f..6a701ae0c 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -454,6 +454,15 @@ class Notice extends Memcached_DataObject // XXX: only blow if <100 in cache $ck = common_cache_key('notice:repeats:'.$this->repeat_of); $result = $cache->delete($ck); + + $user = User::staticGet('id', $this->profile_id); + + if (!empty($user)) { + $uk = common_cache_key('user:repeated_by_me:'.$user->id); + $cache->delete($uk); + $user->free(); + unset($user); + } } } } diff --git a/classes/User.php b/classes/User.php index 2a4fab7d4..4b7365fc7 100644 --- a/classes/User.php +++ b/classes/User.php @@ -741,4 +741,56 @@ class User extends Memcached_DataObject $profile = $this->getProfile(); return $profile->isSilenced(); } + + function repeatedByMe($offset=0, $limit=20, $since_id=null, $max_id=null) + { + $ids = Notice::stream(array($this, '_repeatedByMeDirect'), + array(), + 'user:repeated_by_me:'.$this->id, + $offset, $limit, $since_id, $max_id, null); + + return Notice::getStreamByIds($ids); + } + + function _repeatedByMeDirect($offset, $limit, $since_id, $max_id, $since) + { + $notice = new Notice(); + + $notice->selectAdd(); // clears it + $notice->selectAdd('id'); + + $notice->profile_id = $this->id; + $notice->whereAdd('repeat_of IS NOT NULL'); + + $notice->orderBy('id DESC'); + + if (!is_null($offset)) { + $notice->limit($offset, $limit); + } + + if ($since_id != 0) { + $notice->whereAdd('id > ' . $since_id); + } + + if ($max_id != 0) { + $notice->whereAdd('id <= ' . $max_id); + } + + if (!is_null($since)) { + $notice->whereAdd('created > \'' . date('Y-m-d H:i:s', $since) . '\''); + } + + $ids = array(); + + if ($notice->find()) { + while ($notice->fetch()) { + $ids[] = $notice->id; + } + } + + $notice->free(); + $notice = NULL; + + return $ids; + } } diff --git a/lib/router.php b/lib/router.php index b0b95b080..5c1bd3c4f 100644 --- a/lib/router.php +++ b/lib/router.php @@ -319,6 +319,10 @@ class Router 'id' => '[a-zA-Z0-9]+', 'format' => '(xml|json|rss|atom)')); + $m->connect('api/statuses/retweeted_by_me.:format', + array('action' => 'ApiTimelineRetweetedByMe', + 'format' => '(xml|json|atom)')); + $m->connect('api/statuses/friends.:format', array('action' => 'ApiUserFriends', 'format' => '(xml|json)')); -- cgit v1.2.3-54-g00ecf From cfe67a9c0192129448ea3f0b98aadf49e962fd4d Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sat, 12 Dec 2009 16:00:27 -0500 Subject: add statuses/retweets_of_me to API --- actions/apitimelineretweetsofme.php | 126 ++++++++++++++++++++++++++++++++++++ classes/Notice.php | 14 ++++ classes/User.php | 53 +++++++++++++++ lib/router.php | 4 ++ 4 files changed, 197 insertions(+) create mode 100644 actions/apitimelineretweetsofme.php (limited to 'lib/router.php') diff --git a/actions/apitimelineretweetsofme.php b/actions/apitimelineretweetsofme.php new file mode 100644 index 000000000..479bff431 --- /dev/null +++ b/actions/apitimelineretweetsofme.php @@ -0,0 +1,126 @@ +. + * + * @category API + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once INSTALLDIR . '/lib/apiauth.php'; +require_once INSTALLDIR . '/lib/mediafile.php'; + +/** + * Show authenticating user's most recent notices that have been repeated + * + * @category API + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class ApiTimelineRetweetsOfMeAction extends ApiAuthAction +{ + const DEFAULTCOUNT = 20; + const MAXCOUNT = 200; + const MAXNOTICES = 3200; + + var $repeats = null; + var $cnt = self::DEFAULTCOUNT; + var $page = 1; + var $since_id = null; + var $max_id = null; + + /** + * Take arguments for running + * + * @param array $args $_REQUEST args + * + * @return boolean success flag + * + */ + + function prepare($args) + { + parent::prepare($args); + + $cnt = $this->int('count', self::DEFAULTCOUNT, self::MAXCOUNT, 1); + + $page = $this->int('page', 1, (self::MAXNOTICES/$this->cnt)); + + $since_id = $this->int('since_id'); + + $max_id = $this->int('max_id'); + + return true; + } + + /** + * Handle the request + * + * show a timeline of the user's repeated notices + * + * @param array $args $_REQUEST data (unused) + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + + $offset = ($this->page-1) * $this->cnt; + $limit = $this->cnt; + + $strm = $this->auth_user->repeatsOfMe($offset, $limit, $this->since_id, $this->max_id); + + switch ($this->format) { + case 'xml': + $this->showXmlTimeline($strm); + break; + case 'json': + $this->showJsonTimeline($strm); + break; + case 'atom': + $profile = $this->auth_user->getProfile(); + + $title = sprintf(_("Repeats of %s"), $this->auth_user->nickname); + $taguribase = common_config('integration', 'taguri'); + $id = "tag:$taguribase:RepeatsOfMe:" . $this->auth_user->id; + $link = common_local_url('showstream', + array('nickname' => $this->auth_user->nickname)); + + $this->showAtomTimeline($strm, $title, $id, $link); + break; + + default: + $this->clientError(_('API method not found!'), $code = 404); + break; + } + } +} diff --git a/classes/Notice.php b/classes/Notice.php index 6a701ae0c..eb611f314 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -463,6 +463,20 @@ class Notice extends Memcached_DataObject $user->free(); unset($user); } + + $original = Notice::staticGet('id', $this->repeat_of); + + if (!empty($original)) { + $originalUser = User::staticGet('id', $original->profile_id); + if (!empty($originalUser)) { + $ouk = common_cache_key('user:repeats_of_me:'.$originalUser->id); + $cache->delete($ouk); + $originalUser->free(); + unset($originalUser); + } + $original->free(); + unset($original); + } } } } diff --git a/classes/User.php b/classes/User.php index 4b7365fc7..b3da448a6 100644 --- a/classes/User.php +++ b/classes/User.php @@ -793,4 +793,57 @@ class User extends Memcached_DataObject return $ids; } + + function repeatsOfMe($offset=0, $limit=20, $since_id=null, $max_id=null) + { + $ids = Notice::stream(array($this, '_repeatsOfMeDirect'), + array(), + 'user:repeats_of_me:'.$this->id, + $offset, $limit, $since_id, $max_id, null); + + return Notice::getStreamByIds($ids); + } + + function _repeatsOfMeDirect($offset, $limit, $since_id, $max_id, $since) + { + $qry = + 'SELECT DISTINCT original.id AS id ' . + 'FROM notice original JOIN notice rept ON original.id = rept.repeat_of ' . + 'WHERE original.profile_id = ' . $this->id . ' '; + + if ($since_id != 0) { + $qry .= 'AND original.id > ' . $since_id . ' '; + } + + if ($max_id != 0) { + $qry .= 'AND original.id <= ' . $max_id . ' '; + } + + if (!is_null($since)) { + $qry .= 'AND original.modified > \'' . date('Y-m-d H:i:s', $since) . '\' '; + } + + // NOTE: we sort by fave time, not by notice time! + + $qry .= 'ORDER BY original.id DESC '; + + if (!is_null($offset)) { + $qry .= "LIMIT $limit OFFSET $offset"; + } + + $ids = array(); + + $notice = new Notice(); + + $notice->query($qry); + + while ($notice->fetch()) { + $ids[] = $notice->id; + } + + $notice->free(); + $notice = NULL; + + return $ids; + } } diff --git a/lib/router.php b/lib/router.php index 5c1bd3c4f..732caa1c7 100644 --- a/lib/router.php +++ b/lib/router.php @@ -323,6 +323,10 @@ class Router array('action' => 'ApiTimelineRetweetedByMe', 'format' => '(xml|json|atom)')); + $m->connect('api/statuses/retweets_of_me.:format', + array('action' => 'ApiTimelineRetweetsOfMe', + 'format' => '(xml|json|atom)')); + $m->connect('api/statuses/friends.:format', array('action' => 'ApiUserFriends', 'format' => '(xml|json)')); -- cgit v1.2.3-54-g00ecf From 1ec54d3433a79f56c14c0a99114bb1e607348899 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sat, 12 Dec 2009 16:15:23 -0500 Subject: add statuses/retweeted_to_me to API --- actions/apitimelineretweetedtome.php | 125 +++++++++++++++++++++++++++++++++++ classes/Notice.php | 14 ++++ classes/User.php | 54 +++++++++++++++ lib/router.php | 4 ++ 4 files changed, 197 insertions(+) create mode 100644 actions/apitimelineretweetedtome.php (limited to 'lib/router.php') diff --git a/actions/apitimelineretweetedtome.php b/actions/apitimelineretweetedtome.php new file mode 100644 index 000000000..681b0b9e9 --- /dev/null +++ b/actions/apitimelineretweetedtome.php @@ -0,0 +1,125 @@ +. + * + * @category API + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once INSTALLDIR . '/lib/apiauth.php'; + +/** + * Show most recent notices that are repeats in user's inbox + * + * @category API + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class ApiTimelineRetweetedToMeAction extends ApiAuthAction +{ + const DEFAULTCOUNT = 20; + const MAXCOUNT = 200; + const MAXNOTICES = 3200; + + var $repeats = null; + var $cnt = self::DEFAULTCOUNT; + var $page = 1; + var $since_id = null; + var $max_id = null; + + /** + * Take arguments for running + * + * @param array $args $_REQUEST args + * + * @return boolean success flag + * + */ + + function prepare($args) + { + parent::prepare($args); + + $cnt = $this->int('count', self::DEFAULTCOUNT, self::MAXCOUNT, 1); + + $page = $this->int('page', 1, (self::MAXNOTICES/$this->cnt)); + + $since_id = $this->int('since_id'); + + $max_id = $this->int('max_id'); + + return true; + } + + /** + * Handle the request + * + * show a timeline of the user's repeated notices + * + * @param array $args $_REQUEST data (unused) + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + + $offset = ($this->page-1) * $this->cnt; + $limit = $this->cnt; + + $strm = $this->auth_user->repeatedToMe($offset, $limit, $this->since_id, $this->max_id); + + switch ($this->format) { + case 'xml': + $this->showXmlTimeline($strm); + break; + case 'json': + $this->showJsonTimeline($strm); + break; + case 'atom': + $profile = $this->auth_user->getProfile(); + + $title = sprintf(_("Repeated to %s"), $this->auth_user->nickname); + $taguribase = common_config('integration', 'taguri'); + $id = "tag:$taguribase:RepeatedToMe:" . $this->auth_user->id; + $link = common_local_url('all', + array('nickname' => $this->auth_user->nickname)); + + $this->showAtomTimeline($strm, $title, $id, $link); + break; + + default: + $this->clientError(_('API method not found!'), $code = 404); + break; + } + } +} diff --git a/classes/Notice.php b/classes/Notice.php index a7b0f8cdb..7d2b898d2 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -483,6 +483,20 @@ class Notice extends Memcached_DataObject $original->free(); unset($original); } + + $ni = new Notice_inbox(); + + $ni->notice_id = $this->id; + + if ($ni->find()) { + while ($ni->fetch()) { + $tmk = common_cache_key('user:repeated_to_me:'.$ni->user_id); + $cache->delete($tmk); + } + } + + $ni->free(); + unset($ni); } } } diff --git a/classes/User.php b/classes/User.php index b3da448a6..9c071e06b 100644 --- a/classes/User.php +++ b/classes/User.php @@ -846,4 +846,58 @@ class User extends Memcached_DataObject return $ids; } + + function repeatedToMe($offset=0, $limit=20, $since_id=null, $max_id=null) + { + $ids = Notice::stream(array($this, '_repeatedToMeDirect'), + array(), + 'user:repeated_to_me:'.$this->id, + $offset, $limit, $since_id, $max_id, null); + + return Notice::getStreamByIds($ids); + } + + function _repeatedToMeDirect($offset, $limit, $since_id, $max_id, $since) + { + $qry = + 'SELECT notice.id AS id ' . + 'FROM notice JOIN notice_inbox ON notice.id = notice_inbox.notice_id ' . + 'WHERE notice_inbox.user_id = ' . $this->id . ' ' . + 'AND notice.repeat_of IS NOT NULL '; + + if ($since_id != 0) { + $qry .= 'AND notice.id > ' . $since_id . ' '; + } + + if ($max_id != 0) { + $qry .= 'AND notice.id <= ' . $max_id . ' '; + } + + if (!is_null($since)) { + $qry .= 'AND notice.modified > \'' . date('Y-m-d H:i:s', $since) . '\' '; + } + + // NOTE: we sort by fave time, not by notice time! + + $qry .= 'ORDER BY notice.id DESC '; + + if (!is_null($offset)) { + $qry .= "LIMIT $limit OFFSET $offset"; + } + + $ids = array(); + + $notice = new Notice(); + + $notice->query($qry); + + while ($notice->fetch()) { + $ids[] = $notice->id; + } + + $notice->free(); + $notice = NULL; + + return $ids; + } } diff --git a/lib/router.php b/lib/router.php index 732caa1c7..8f68f86ac 100644 --- a/lib/router.php +++ b/lib/router.php @@ -323,6 +323,10 @@ class Router array('action' => 'ApiTimelineRetweetedByMe', 'format' => '(xml|json|atom)')); + $m->connect('api/statuses/retweeted_to_me.:format', + array('action' => 'ApiTimelineRetweetedToMe', + 'format' => '(xml|json|atom)')); + $m->connect('api/statuses/retweets_of_me.:format', array('action' => 'ApiTimelineRetweetsOfMe', 'format' => '(xml|json|atom)')); -- cgit v1.2.3-54-g00ecf From 6ff13d6828d01a142b60b4254c07f6f9ad3f16ba Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 14 Dec 2009 16:10:16 -0500 Subject: move full-featured timeline to apitimelinehome.php --- actions/apitimelinefriends.php | 249 ----------------------------------------- actions/apitimelinehome.php | 249 +++++++++++++++++++++++++++++++++++++++++ lib/router.php | 5 +- 3 files changed, 252 insertions(+), 251 deletions(-) delete mode 100644 actions/apitimelinefriends.php create mode 100644 actions/apitimelinehome.php (limited to 'lib/router.php') diff --git a/actions/apitimelinefriends.php b/actions/apitimelinefriends.php deleted file mode 100644 index 09ba7a969..000000000 --- a/actions/apitimelinefriends.php +++ /dev/null @@ -1,249 +0,0 @@ -. - * - * @category API - * @package StatusNet - * @author Craig Andrews - * @author Evan Prodromou - * @author Jeffery To - * @author mac65 - * @author Mike Cochrane - * @author Robin Millette - * @author Zach Copley - * @copyright 2009 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -if (!defined('STATUSNET')) { - exit(1); -} - -require_once INSTALLDIR . '/lib/apibareauth.php'; - -/** - * Returns the most recent notices (default 20) posted by the target user. - * This is the equivalent of 'You and friends' page accessed via Web. - * - * @category API - * @package StatusNet - * @author Craig Andrews - * @author Evan Prodromou - * @author Jeffery To - * @author mac65 - * @author Mike Cochrane - * @author Robin Millette - * @author Zach Copley - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -class ApiTimelineFriendsAction extends ApiBareAuthAction -{ - var $notices = null; - - /** - * Take arguments for running - * - * @param array $args $_REQUEST args - * - * @return boolean success flag - * - */ - - function prepare($args) - { - parent::prepare($args); - common_debug("api friends_timeline"); - $this->user = $this->getTargetUser($this->arg('id')); - - if (empty($this->user)) { - $this->clientError(_('No such user.'), 404, $this->format); - return; - } - - $this->notices = $this->getNotices(); - - return true; - } - - /** - * Handle the request - * - * Just show the notices - * - * @param array $args $_REQUEST data (unused) - * - * @return void - */ - - function handle($args) - { - parent::handle($args); - $this->showTimeline(); - } - - /** - * Show the timeline of notices - * - * @return void - */ - - function showTimeline() - { - $profile = $this->user->getProfile(); - $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); - $sitename = common_config('site', 'name'); - $title = sprintf(_("%s and friends"), $this->user->nickname); - $taguribase = common_config('integration', 'taguri'); - $id = "tag:$taguribase:FriendsTimeline:" . $this->user->id; - $link = common_local_url( - 'all', array('nickname' => $this->user->nickname) - ); - $subtitle = sprintf( - _('Updates from %1$s and friends on %2$s!'), - $this->user->nickname, $sitename - ); - $logo = ($avatar) ? $avatar->displayUrl() : Avatar::defaultImage(AVATAR_PROFILE_SIZE); - - switch($this->format) { - case 'xml': - $this->showXmlTimeline($this->notices); - break; - case 'rss': - $this->showRssTimeline($this->notices, $title, $link, $subtitle, null, $logo); - break; - case 'atom': - - $target_id = $this->arg('id'); - - if (isset($target_id)) { - $selfuri = common_root_url() . - 'api/statuses/friends_timeline/' . - $target_id . '.atom'; - } else { - $selfuri = common_root_url() . - 'api/statuses/friends_timeline.atom'; - } - - $this->showAtomTimeline( - $this->notices, $title, $id, $link, - $subtitle, null, $selfuri, $logo - ); - break; - case 'json': - $this->showJsonTimeline($this->notices); - break; - default: - $this->clientError(_('API method not found!'), $code = 404); - break; - } - } - - /** - * Get notices - * - * @return array notices - */ - - function getNotices() - { - $notices = array(); - - if (!empty($this->auth_user) && $this->auth_user->id == $this->user->id) { - $notice = $this->user->noticeInbox( - ($this->page-1) * $this->count, - $this->count, $this->since_id, - $this->max_id, $this->since - ); - } else { - $notice = $this->user->noticesWithFriends( - ($this->page-1) * $this->count, - $this->count, $this->since_id, - $this->max_id, $this->since - ); - } - - while ($notice->fetch()) { - $notices[] = clone($notice); - } - - return $notices; - } - - /** - * Is this action read only? - * - * @param array $args other arguments - * - * @return boolean true - */ - - function isReadOnly($args) - { - return true; - } - - /** - * When was this feed last modified? - * - * @return string datestamp of the latest notice in the stream - */ - - function lastModified() - { - if (!empty($this->notices) && (count($this->notices) > 0)) { - return strtotime($this->notices[0]->created); - } - - return null; - } - - /** - * An entity tag for this stream - * - * Returns an Etag based on the action name, language, user ID, and - * timestamps of the first and last notice in the timeline - * - * @return string etag - */ - - function etag() - { - if (!empty($this->notices) && (count($this->notices) > 0)) { - - $last = count($this->notices) - 1; - - return '"' . implode( - ':', - array($this->arg('action'), - common_language(), - $this->user->id, - strtotime($this->notices[0]->created), - strtotime($this->notices[$last]->created)) - ) - . '"'; - } - - return null; - } - -} diff --git a/actions/apitimelinehome.php b/actions/apitimelinehome.php new file mode 100644 index 000000000..5f5ea37b1 --- /dev/null +++ b/actions/apitimelinehome.php @@ -0,0 +1,249 @@ +. + * + * @category API + * @package StatusNet + * @author Craig Andrews + * @author Evan Prodromou + * @author Jeffery To + * @author mac65 + * @author Mike Cochrane + * @author Robin Millette + * @author Zach Copley + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once INSTALLDIR . '/lib/apibareauth.php'; + +/** + * Returns the most recent notices (default 20) posted by the target user. + * This is the equivalent of 'You and friends' page accessed via Web. + * + * @category API + * @package StatusNet + * @author Craig Andrews + * @author Evan Prodromou + * @author Jeffery To + * @author mac65 + * @author Mike Cochrane + * @author Robin Millette + * @author Zach Copley + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class ApiTimelineHomeAction extends ApiBareAuthAction +{ + var $notices = null; + + /** + * Take arguments for running + * + * @param array $args $_REQUEST args + * + * @return boolean success flag + * + */ + + function prepare($args) + { + parent::prepare($args); + common_debug("api home_timeline"); + $this->user = $this->getTargetUser($this->arg('id')); + + if (empty($this->user)) { + $this->clientError(_('No such user.'), 404, $this->format); + return; + } + + $this->notices = $this->getNotices(); + + return true; + } + + /** + * Handle the request + * + * Just show the notices + * + * @param array $args $_REQUEST data (unused) + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + $this->showTimeline(); + } + + /** + * Show the timeline of notices + * + * @return void + */ + + function showTimeline() + { + $profile = $this->user->getProfile(); + $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); + $sitename = common_config('site', 'name'); + $title = sprintf(_("%s and friends"), $this->user->nickname); + $taguribase = common_config('integration', 'taguri'); + $id = "tag:$taguribase:HomeTimeline:" . $this->user->id; + $link = common_local_url( + 'all', array('nickname' => $this->user->nickname) + ); + $subtitle = sprintf( + _('Updates from %1$s and friends on %2$s!'), + $this->user->nickname, $sitename + ); + $logo = ($avatar) ? $avatar->displayUrl() : Avatar::defaultImage(AVATAR_PROFILE_SIZE); + + switch($this->format) { + case 'xml': + $this->showXmlTimeline($this->notices); + break; + case 'rss': + $this->showRssTimeline($this->notices, $title, $link, $subtitle, null, $logo); + break; + case 'atom': + + $target_id = $this->arg('id'); + + if (isset($target_id)) { + $selfuri = common_root_url() . + 'api/statuses/home_timeline/' . + $target_id . '.atom'; + } else { + $selfuri = common_root_url() . + 'api/statuses/home_timeline.atom'; + } + + $this->showAtomTimeline( + $this->notices, $title, $id, $link, + $subtitle, null, $selfuri, $logo + ); + break; + case 'json': + $this->showJsonTimeline($this->notices); + break; + default: + $this->clientError(_('API method not found!'), $code = 404); + break; + } + } + + /** + * Get notices + * + * @return array notices + */ + + function getNotices() + { + $notices = array(); + + if (!empty($this->auth_user) && $this->auth_user->id == $this->user->id) { + $notice = $this->user->noticeInbox( + ($this->page-1) * $this->count, + $this->count, $this->since_id, + $this->max_id, $this->since + ); + } else { + $notice = $this->user->noticesWithFriends( + ($this->page-1) * $this->count, + $this->count, $this->since_id, + $this->max_id, $this->since + ); + } + + while ($notice->fetch()) { + $notices[] = clone($notice); + } + + return $notices; + } + + /** + * Is this action read only? + * + * @param array $args other arguments + * + * @return boolean true + */ + + function isReadOnly($args) + { + return true; + } + + /** + * When was this feed last modified? + * + * @return string datestamp of the latest notice in the stream + */ + + function lastModified() + { + if (!empty($this->notices) && (count($this->notices) > 0)) { + return strtotime($this->notices[0]->created); + } + + return null; + } + + /** + * An entity tag for this stream + * + * Returns an Etag based on the action name, language, user ID, and + * timestamps of the first and last notice in the timeline + * + * @return string etag + */ + + function etag() + { + if (!empty($this->notices) && (count($this->notices) > 0)) { + + $last = count($this->notices) - 1; + + return '"' . implode( + ':', + array($this->arg('action'), + common_language(), + $this->user->id, + strtotime($this->notices[0]->created), + strtotime($this->notices[$last]->created)) + ) + . '"'; + } + + return null; + } + +} diff --git a/lib/router.php b/lib/router.php index 8f68f86ac..474e05996 100644 --- a/lib/router.php +++ b/lib/router.php @@ -283,12 +283,13 @@ class Router array('action' => 'ApiTimelineFriends', 'id' => '[a-zA-Z0-9]+', 'format' => '(xml|json|rss|atom)')); + $m->connect('api/statuses/home_timeline.:format', - array('action' => 'ApiTimelineFriends', + array('action' => 'ApiTimelineHome', 'format' => '(xml|json|rss|atom)')); $m->connect('api/statuses/home_timeline/:id.:format', - array('action' => 'ApiTimelineFriends', + array('action' => 'ApiTimelineHome', 'id' => '[a-zA-Z0-9]+', 'format' => '(xml|json|rss|atom)')); -- cgit v1.2.3-54-g00ecf