3 * StatusNet, the distributed open-source microblogging tool
5 * Show the public timeline
9 * LICENCE: This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU Affero General Public License as published by
11 * the Free Software Foundation, either version 3 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Affero General Public License for more details.
19 * You should have received a copy of the GNU Affero General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 * @author Craig Andrews <candrews@integralblue.com>
25 * @author Evan Prodromou <evan@status.net>
26 * @author Jeffery To <jeffery.to@gmail.com>
27 * @author mac65 <mac65@mac65.com>
28 * @author Mike Cochrane <mikec@mikenz.geek.nz>
29 * @author Robin Millette <robin@millette.info>
30 * @author Zach Copley <zach@status.net>
31 * @copyright 2009 StatusNet, Inc.
32 * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
33 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
34 * @link http://status.net/
37 if (!defined('STATUSNET')) {
42 * Returns the most recent notices (default 20) posted by everybody
46 * @author Craig Andrews <candrews@integralblue.com>
47 * @author Evan Prodromou <evan@status.net>
48 * @author Jeffery To <jeffery.to@gmail.com>
49 * @author mac65 <mac65@mac65.com>
50 * @author Mike Cochrane <mikec@mikenz.geek.nz>
51 * @author Robin Millette <robin@millette.info>
52 * @author Zach Copley <zach@status.net>
53 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
54 * @link http://status.net/
57 /* External API usage documentation. Please update when you change how this method works. */
59 /*! @page publictimeline statuses/public_timeline
62 Returns the 20 most recent notices from users throughout the system who have
63 uploaded their own avatars. Depending on configuration, it may or may not
64 not include notices from automatic posting services.
67 @li /api/statuses/public_timeline.:format
69 @par Formats (:format)
75 @par Requires Authentication
78 @param since_id (Optional) Returns only statuses with an ID greater
79 than (that is, more recent than) the specified ID.
80 @param max_id (Optional) Returns only statuses with an ID less than
81 (that is, older than) or equal to the specified ID.
82 @param count (Optional) Specifies the number of statuses to retrieve.
83 @param page (Optional) Specifies the page of results to retrieve.
87 @subsection usagenotes Usage notes
88 @li The URL pattern is relative to the @ref apiroot.
89 @li The XML response uses <a href="http://georss.org/Main_Page">GeoRSS</a>
90 to encode the latitude and longitude (see example response below <georss:point>).
92 @subsection exampleusage Example usage
95 curl http://identi.ca/api/statuses/friends_timeline/evan.xml?count=1&page=2
98 @subsection exampleresponse Example response
101 <?xml version="1.0" encoding="UTF-8"?>
102 <statuses type="array">
104 <text>@skwashd oh, commbank reenabled me super quick both times. but disconcerting when you don't expect it though</text>
105 <truncated>false</truncated>
106 <created_at>Sat Apr 17 00:49:12 +0000 2010</created_at>
107 <in_reply_to_status_id>28838393</in_reply_to_status_id>
108 <source>xmpp</source>
110 <in_reply_to_user_id>39303</in_reply_to_user_id>
111 <in_reply_to_screen_name>skwashd</in_reply_to_screen_name>
113 <favorited>false</favorited>
116 <name>joshua may</name>
117 <screen_name>notjosh</screen_name>
118 <location></location>
119 <description></description>
120 <profile_image_url>http://avatar.identi.ca/44517-48-20090321004106.jpeg</profile_image_url>
122 <protected>false</protected>
123 <followers_count>17</followers_count>
124 <profile_background_color></profile_background_color>
125 <profile_text_color></profile_text_color>
126 <profile_link_color></profile_link_color>
127 <profile_sidebar_fill_color></profile_sidebar_fill_color>
128 <profile_sidebar_border_color></profile_sidebar_border_color>
129 <friends_count>20</friends_count>
130 <created_at>Sat Mar 21 00:40:25 +0000 2009</created_at>
131 <favourites_count>0</favourites_count>
132 <utc_offset>0</utc_offset>
133 <time_zone>UTC</time_zone>
134 <profile_background_image_url></profile_background_image_url>
135 <profile_background_tile>false</profile_background_tile>
136 <statuses_count>100</statuses_count>
137 <following>false</following>
138 <notifications>false</notifications>
145 class ApiTimelinePublicAction extends ApiPrivateAuthAction
150 * Take arguments for running
152 * @param array $args $_REQUEST args
154 * @return boolean success flag
157 protected function prepare(array $args=array())
159 parent::prepare($args);
161 $this->notices = $this->getNotices();
169 * Just show the notices
173 protected function handle()
176 $this->showTimeline();
181 // TRANS: Title for site timeline. %s is the GNU social sitename.
182 return sprintf(_("%s public timeline"), common_config('site', 'name'));
186 * Show the timeline of notices
190 function showTimeline()
192 $nonapi_action = substr($this->action, strlen('apitimeline')); // Just so we don't need to set this explicitly
194 $sitelogo = (common_config('site', 'logo')) ? common_config('site', 'logo') : Theme::path('logo.png');
195 $title = $this->title();
196 $taguribase = TagURI::base();
197 $id = "tag:$taguribase:" . ucfirst($nonapi_action) . 'Timeline'; // Public or Networkpublic probably
198 $link = common_local_url($nonapi_action);
199 $self = $this->getSelfUri();
200 // TRANS: Subtitle for site timeline. %s is the GNU social sitename.
201 $subtitle = sprintf(_("%s updates from everyone!"), common_config('site', 'name'));
203 switch($this->format) {
205 $this->showXmlTimeline($this->notices);
208 $this->showRssTimeline(
220 header('Content-Type: application/atom+xml; charset=utf-8');
222 $atom = new AtomNoticeFeed($this->auth_user);
225 $atom->setTitle($title);
226 $atom->setSubtitle($subtitle);
227 $atom->setLogo($sitelogo);
228 $atom->setUpdated('now');
229 $atom->addLink(common_local_url($nonapi_action));
230 $atom->setSelfLink($self);
231 $atom->addEntryFromNotices($this->notices);
233 $this->raw($atom->getString());
237 $this->showJsonTimeline($this->notices);
240 header('Content-Type: ' . ActivityStreamJSONDocument::CONTENT_TYPE);
241 $doc = new ActivityStreamJSONDocument($this->auth_user);
242 $doc->setTitle($title);
243 $doc->addLink($link, 'alternate', 'text/html');
244 $doc->addItemsFromNotices($this->notices);
245 $this->raw($doc->asString());
248 // TRANS: Client error displayed when coming across a non-supported API method.
249 $this->clientError(_('API method not found.'), $code = 404);
257 * @return array notices
259 function getNotices()
263 $stream = $this->getStream();
265 $notice = $stream->getNotices(($this->page - 1) * $this->count,
270 $notices = $notice->fetchAll();
272 NoticeList::prefill($notices);
277 protected function getStream()
279 return new PublicNoticeStream($this->scoped);
283 * Is this action read only?
285 * @param array $args other arguments
287 * @return boolean true
289 function isReadOnly(array $args=array())
295 * When was this feed last modified?
297 * @return string datestamp of the latest notice in the stream
299 function lastModified()
301 if (!empty($this->notices) && (count($this->notices) > 0)) {
302 return strtotime($this->notices[0]->created);
309 * An entity tag for this stream
311 * Returns an Etag based on the action name, language, and
312 * timestamps of the first and last notice in the timeline
314 * @return string etag
318 if (!empty($this->notices) && (count($this->notices) > 0)) {
320 $last = count($this->notices) - 1;
322 return '"' . implode(
324 array($this->arg('action'),
325 common_user_cache_hash($this->auth_user),
327 strtotime($this->notices[0]->created),
328 strtotime($this->notices[$last]->created))