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