]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - _darcs/pristine/actions/twitapistatuses.php
move opening brace of class declaration to next line
[quix0rs-gnu-social.git] / _darcs / pristine / actions / twitapistatuses.php
1 <?php
2 /*
3  * Laconica - a distributed open-source microblogging tool
4  * Copyright (C) 2008, Controlez-Vous, Inc.
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 if (!defined('LACONICA')) { exit(1); }
21
22 require_once(INSTALLDIR.'/lib/twitterapi.php');
23
24 class TwitapistatusesAction extends TwitterapiAction
25 {
26
27     function public_timeline($args, $apidata)
28     {
29         parent::handle($args);
30
31         $sitename = common_config('site', 'name');
32         $siteserver = common_config('site', 'server');
33         $title = sprintf(_("%s public timeline"), $sitename);
34         $id = "tag:$siteserver:Statuses";
35         $link = common_root_url();
36         $subtitle = sprintf(_("%s updates from everyone!"), $sitename);
37
38         // Number of public statuses to return by default -- Twitter sends 20
39         $MAX_PUBSTATUSES = 20;
40
41         // FIXME: To really live up to the spec we need to build a list
42         // of notices by users who have custom avatars, so fix this SQL -- Zach
43
44         $page = $this->arg('page');
45         $since_id = $this->arg('since_id');
46         $before_id = $this->arg('before_id');
47
48         // NOTE: page, since_id, and before_id are extensions to Twitter API -- TB
49         if (!$page) {
50             $page = 1;
51         }
52         if (!$since_id) {
53             $since_id = 0;
54         }
55         if (!$before_id) {
56             $before_id = 0;
57         }
58
59         $since = strtotime($this->arg('since'));
60
61         $notice = Notice::publicStream((($page-1)*$MAX_PUBSTATUSES), $MAX_PUBSTATUSES, $since_id, $before_id, $since);
62
63         if ($notice) {
64
65             switch($apidata['content-type']) {
66                 case 'xml':
67                     $this->show_xml_timeline($notice);
68                     break;
69                 case 'rss':
70                     $this->show_rss_timeline($notice, $title, $link, $subtitle);
71                     break;
72                 case 'atom':
73                     $this->show_atom_timeline($notice, $title, $id, $link, $subtitle);
74                     break;
75                 case 'json':
76                     $this->show_json_timeline($notice);
77                     break;
78                 default:
79                     common_user_error(_('API method not found!'), $code = 404);
80                     break;
81             }
82
83         } else {
84             common_server_error(_('Couldn\'t find any statuses.'), $code = 503);
85         }
86
87     }
88
89     function friends_timeline($args, $apidata)
90     {
91         parent::handle($args);
92
93         $since = $this->arg('since');
94         $since_id = $this->arg('since_id');
95         $count = $this->arg('count');
96         $page = $this->arg('page');
97         $before_id = $this->arg('before_id');
98
99         if (!$page) {
100             $page = 1;
101         }
102
103         if (!$count) {
104             $count = 20;
105         }
106
107         if (!$since_id) {
108             $since_id = 0;
109         }
110
111         // NOTE: before_id is an extension to Twitter API -- TB
112         if (!$before_id) {
113             $before_id = 0;
114         }
115
116         $since = strtotime($this->arg('since'));
117
118         $user = $this->get_user(null, $apidata);
119         $this->auth_user = $user;
120
121         $profile = $user->getProfile();
122
123         $sitename = common_config('site', 'name');
124         $siteserver = common_config('site', 'server');
125
126         $title = sprintf(_("%s and friends"), $user->nickname);
127         $id = "tag:$siteserver:friends:" . $user->id;
128         $link = common_local_url('all', array('nickname' => $user->nickname));
129         $subtitle = sprintf(_('Updates from %1$s and friends on %2$s!'), $user->nickname, $sitename);
130
131         $notice = $user->noticesWithFriends(($page-1)*20, $count, $since_id, $before_id, $since);
132
133         switch($apidata['content-type']) {
134          case 'xml':
135             $this->show_xml_timeline($notice);
136             break;
137          case 'rss':
138             $this->show_rss_timeline($notice, $title, $link, $subtitle);
139             break;
140          case 'atom':
141             $this->show_atom_timeline($notice, $title, $id, $link, $subtitle);
142             break;
143          case 'json':
144             $this->show_json_timeline($notice);
145             break;
146          default:
147             common_user_error(_('API method not found!'), $code = 404);
148         }
149
150     }
151
152     function user_timeline($args, $apidata)
153     {
154         parent::handle($args);
155
156         $this->auth_user = $apidata['user'];
157         $user = $this->get_user($apidata['api_arg'], $apidata);
158
159         if (!$user) {
160             $this->client_error('Not Found', 404, $apidata['content-type']);
161             return;
162         }
163
164         $profile = $user->getProfile();
165
166         if (!$profile) {
167             common_server_error(_('User has no profile.'));
168             return;
169         }
170
171         $count = $this->arg('count');
172         $since = $this->arg('since');
173         $since_id = $this->arg('since_id');
174         $page = $this->arg('page');
175         $before_id = $this->arg('before_id');
176
177         if (!$page) {
178             $page = 1;
179         }
180
181         if (!$count) {
182             $count = 20;
183         }
184
185         if (!$since_id) {
186             $since_id = 0;
187         }
188
189         // NOTE: before_id is an extensions to Twitter API -- TB
190         if (!$before_id) {
191             $before_id = 0;
192         }
193
194         $since = strtotime($this->arg('since'));
195
196         $sitename = common_config('site', 'name');
197         $siteserver = common_config('site', 'server');
198
199         $title = sprintf(_("%s timeline"), $user->nickname);
200         $id = "tag:$siteserver:user:".$user->id;
201         $link = common_local_url('showstream', array('nickname' => $user->nickname));
202         $subtitle = sprintf(_('Updates from %1$s on %2$s!'), $user->nickname, $sitename);
203
204         # FriendFeed's SUP protocol
205         # Also added RSS and Atom feeds
206
207         $suplink = common_local_url('sup', null, $user->id);
208         header('X-SUP-ID: '.$suplink);
209
210         # XXX: since
211
212         $notice = $user->getNotices((($page-1)*20), $count, $since_id, $before_id, $since);
213
214         switch($apidata['content-type']) {
215          case 'xml':
216             $this->show_xml_timeline($notice);
217             break;
218          case 'rss':
219             $this->show_rss_timeline($notice, $title, $link, $subtitle, $suplink);
220             break;
221          case 'atom':
222             $this->show_atom_timeline($notice, $title, $id, $link, $subtitle, $suplink);
223             break;
224          case 'json':
225             $this->show_json_timeline($notice);
226             break;
227          default:
228             common_user_error(_('API method not found!'), $code = 404);
229         }
230
231     }
232
233     function update($args, $apidata)
234     {
235
236         parent::handle($args);
237
238         if (!in_array($apidata['content-type'], array('xml', 'json'))) {
239             common_user_error(_('API method not found!'), $code = 404);
240             return;
241         }
242
243         if ($_SERVER['REQUEST_METHOD'] != 'POST') {
244             $this->client_error(_('This method requires a POST.'), 400, $apidata['content-type']);
245             return;
246         }
247
248         $this->auth_user = $apidata['user'];
249         $user = $this->auth_user;
250         $status = $this->trimmed('status');
251         $source = $this->trimmed('source');
252         $in_reply_to_status_id = intval($this->trimmed('in_reply_to_status_id'));
253         $reserved_sources = array('web', 'omb', 'mail', 'xmpp', 'api');
254         if (!$source || in_array($source, $reserved_sources)) {
255             $source = 'api';
256         }
257
258         if (!$status) {
259
260             // XXX: Note: In this case, Twitter simply returns '200 OK'
261             // No error is given, but the status is not posted to the
262             // user's timeline.     Seems bad.     Shouldn't we throw an
263             // errror? -- Zach
264             return;
265
266         } else {
267
268             $status_shortened = common_shorten_links($status);
269
270             if (mb_strlen($status_shortened) > 140) {
271
272                 // XXX: Twitter truncates anything over 140, flags the status
273                 // as "truncated." Sending this error may screw up some clients
274                 // that assume Twitter will truncate for them.    Should we just
275                 // truncate too? -- Zach
276                 $this->client_error(_('That\'s too long. Max notice size is 140 chars.'), $code = 406, $apidata['content-type']);
277                 return;
278
279             }
280         }
281
282         // Check for commands
283         $inter = new CommandInterpreter();
284         $cmd = $inter->handle_command($user, $status_shortened);
285
286         if ($cmd) {
287
288             if ($this->supported($cmd)) {
289                 $cmd->execute(new Channel());
290             }
291
292             // cmd not supported?  Twitter just returns your latest status.
293             // And, it returns your last status whether the cmd was successful
294             // or not!
295             $n = $user->getCurrentNotice();
296             $apidata['api_arg'] = $n->id;
297         } else {
298
299             $reply_to = null;
300
301             if ($in_reply_to_status_id) {
302
303                 // check whether notice actually exists
304                 $reply = Notice::staticGet($in_reply_to_status_id);
305
306                 if ($reply) {
307                     $reply_to = $in_reply_to_status_id;
308                 } else {
309                     $this->client_error(_('Not found'), $code = 404, $apidata['content-type']);
310                     return;
311                 }
312             }
313
314             $notice = Notice::saveNew($user->id, html_entity_decode($status, ENT_NOQUOTES, 'UTF-8'),
315                 $source, 1, $reply_to);
316
317             if (is_string($notice)) {
318                 $this->server_error($notice);
319                 return;
320             }
321
322             common_broadcast_notice($notice);
323             $apidata['api_arg'] = $notice->id;
324         }
325
326         $this->show($args, $apidata);
327     }
328
329     function replies($args, $apidata)
330     {
331
332         parent::handle($args);
333
334         $since = $this->arg('since');
335         $count = $this->arg('count');
336         $page = $this->arg('page');
337         $since_id = $this->arg('since_id');
338         $before_id = $this->arg('before_id');
339
340         $this->auth_user = $apidata['user'];
341         $user = $this->auth_user;
342         $profile = $user->getProfile();
343
344         $sitename = common_config('site', 'name');
345         $siteserver = common_config('site', 'server');
346
347         $title = sprintf(_('%1$s / Updates replying to %2$s'), $sitename, $user->nickname);
348         $id = "tag:$siteserver:replies:".$user->id;
349         $link = common_local_url('replies', array('nickname' => $user->nickname));
350         $subtitle = sprintf(_('%1$s updates that reply to updates from %2$s / %3$s.'), $sitename, $user->nickname, $profile->getBestName());
351
352         if (!$page) {
353             $page = 1;
354         }
355
356         if (!$count) {
357             $count = 20;
358         }
359
360         if (!$since_id) {
361             $since_id = 0;
362         }
363
364         // NOTE: before_id is an extension to Twitter API -- TB
365         if (!$before_id) {
366             $before_id = 0;
367         }
368
369         $since = strtotime($this->arg('since'));
370
371         $notice = $user->getReplies((($page-1)*20), $count, $since_id, $before_id, $since);
372         $notices = array();
373
374         while ($notice->fetch()) {
375             $notices[] = clone($notice);
376         }
377
378         switch($apidata['content-type']) {
379          case 'xml':
380             $this->show_xml_timeline($notices);
381             break;
382          case 'rss':
383             $this->show_rss_timeline($notices, $title, $link, $subtitle);
384             break;
385          case 'atom':
386             $this->show_atom_timeline($notices, $title, $id, $link, $subtitle);
387             break;
388          case 'json':
389             $this->show_json_timeline($notices);
390             break;
391          default:
392             common_user_error(_('API method not found!'), $code = 404);
393         }
394
395     }
396
397     function show($args, $apidata)
398     {
399         parent::handle($args);
400
401         if (!in_array($apidata['content-type'], array('xml', 'json'))) {
402             common_user_error(_('API method not found!'), $code = 404);
403             return;
404         }
405
406         $this->auth_user = $apidata['user'];
407         $notice_id = $apidata['api_arg'];
408         $notice = Notice::staticGet($notice_id);
409
410         if ($notice) {
411             if ($apidata['content-type'] == 'xml') {
412                 $this->show_single_xml_status($notice);
413             } elseif ($apidata['content-type'] == 'json') {
414                 $this->show_single_json_status($notice);
415             }
416         } else {
417             // XXX: Twitter just sets a 404 header and doens't bother to return an err msg
418             $this->client_error(_('No status with that ID found.'), 404, $apidata['content-type']);
419         }
420
421     }
422
423     function destroy($args, $apidata)
424     {
425
426         parent::handle($args);
427
428         if (!in_array($apidata['content-type'], array('xml', 'json'))) {
429             common_user_error(_('API method not found!'), $code = 404);
430             return;
431         }
432
433         // Check for RESTfulness
434         if (!in_array($_SERVER['REQUEST_METHOD'], array('POST', 'DELETE'))) {
435             // XXX: Twitter just prints the err msg, no XML / JSON.
436             $this->client_error(_('This method requires a POST or DELETE.'), 400, $apidata['content-type']);
437             return;
438         }
439
440         $this->auth_user = $apidata['user'];
441         $user = $this->auth_user;
442         $notice_id = $apidata['api_arg'];
443         $notice = Notice::staticGet($notice_id);
444
445         if (!$notice) {
446             $this->client_error(_('No status found with that ID.'), 404, $apidata['content-type']);
447             return;
448         }
449
450         if ($user->id == $notice->profile_id) {
451             $replies = new Reply;
452             $replies->get('notice_id', $notice_id);
453             common_dequeue_notice($notice);
454             $replies->delete();
455             $notice->delete();
456
457             if ($apidata['content-type'] == 'xml') {
458                 $this->show_single_xml_status($notice);
459             } elseif ($apidata['content-type'] == 'json') {
460                 $this->show_single_json_status($notice);
461             }
462         } else {
463             $this->client_error(_('You may not delete another user\'s status.'), 403, $apidata['content-type']);
464         }
465
466     }
467
468     function friends($args, $apidata)
469     {
470         parent::handle($args);
471         return $this->subscriptions($apidata, 'subscribed', 'subscriber');
472     }
473
474     function followers($args, $apidata)
475     {
476         parent::handle($args);
477
478         return $this->subscriptions($apidata, 'subscriber', 'subscribed');
479     }
480
481     function subscriptions($apidata, $other_attr, $user_attr)
482     {
483
484         # XXX: lite
485
486         $this->auth_user = $apidate['user'];
487         $user = $this->get_user($apidata['api_arg'], $apidata);
488
489         if (!$user) {
490             $this->client_error('Not Found', 404, $apidata['content-type']);
491             return;
492         }
493
494         $page = $this->trimmed('page');
495
496         if (!$page || !is_numeric($page)) {
497             $page = 1;
498         }
499
500         $profile = $user->getProfile();
501
502         if (!$profile) {
503             common_server_error(_('User has no profile.'));
504             return;
505         }
506
507         $sub = new Subscription();
508         $sub->$user_attr = $profile->id;
509
510         $since = strtotime($this->trimmed('since'));
511
512         if ($since) {
513             $d = date('Y-m-d H:i:s', $since);
514             $sub->whereAdd("created > '$d'");
515         }
516
517         $sub->orderBy('created DESC');
518         $sub->limit(($page-1)*100, 100);
519
520         $others = array();
521
522         if ($sub->find()) {
523             while ($sub->fetch()) {
524                 $others[] = Profile::staticGet($sub->$other_attr);
525             }
526         } else {
527             // user has no followers
528         }
529
530         $type = $apidata['content-type'];
531
532         $this->init_document($type);
533         $this->show_profiles($others, $type);
534         $this->end_document($type);
535     }
536
537     function show_profiles($profiles, $type)
538     {
539         switch ($type) {
540          case 'xml':
541             common_element_start('users', array('type' => 'array'));
542             foreach ($profiles as $profile) {
543                 $this->show_profile($profile);
544             }
545             common_element_end('users');
546             break;
547          case 'json':
548             $arrays = array();
549             foreach ($profiles as $profile) {
550                 $arrays[] = $this->twitter_user_array($profile, true);
551             }
552             print json_encode($arrays);
553             break;
554          default:
555             $this->client_error(_('unsupported file type'));
556         }
557     }
558
559     function featured($args, $apidata)
560     {
561         parent::handle($args);
562         common_server_error(_('API method under construction.'), $code=501);
563     }
564
565     function supported($cmd)
566     {
567
568         $cmdlist = array('MessageCommand', 'SubCommand', 'UnsubCommand', 'FavCommand', 'OnCommand', 'OffCommand');
569
570         if (in_array(get_class($cmd), $cmdlist)) {
571             return true;
572         }
573
574         return false;
575     }
576
577 }