]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - actions/twitapistatuses.php
local-only is optional on public timeline
[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 /* XXX: Please don't freak out about all the ugly comments in this file.
25  * They are mostly in here for reference while I work on the
26  * API. I'll fix things up later to make them look better later. -- Zach
27  */
28 class TwitapistatusesAction extends TwitterapiAction {
29
30         function is_readonly() {
31
32                 static $write_methods = array(  'update',
33                                                                                 'destroy');
34
35                 $cmdtext = explode('.', $this->arg('method'));
36
37                 if (in_array($cmdtext[0], $write_methods)) {
38                         return false;
39                 }
40
41                 return true;
42         }
43
44         function public_timeline($args, $apidata) {
45                 parent::handle($args);
46
47                 $sitename = common_config('site', 'name');
48                 $siteserver = common_config('site', 'server');
49                 $title = sprintf(_("%s public timeline"), $sitename);
50                 $id = "tag:$siteserver:Statuses";
51                 $link = common_root_url();
52                 $subtitle = sprintf(_("%s updates from everyone!"), $sitename);
53
54                 // Number of public statuses to return by default -- Twitter sends 20
55                 $MAX_PUBSTATUSES = 20;
56
57                 $notice = new Notice();
58
59                 // FIXME: To really live up to the spec we need to build a list
60                 // of notices by users who have custom avatars, so fix this SQL -- Zach
61
62                 # XXX: sub-optimal performance
63
64                 if (common_config('public', 'localonly')) {
65                         $notice->is_local = 1;
66                 }
67
68                 $notice->orderBy('created DESC, notice.id DESC');
69                 $notice->limit($MAX_PUBSTATUSES);
70                 $cnt = $notice->find();
71
72                 if ($cnt > 0) {
73
74                         switch($apidata['content-type']) {
75                                 case 'xml':
76                                         $this->show_xml_timeline($notice);
77                                         break;
78                                 case 'rss':
79                                         $this->show_rss_timeline($notice, $title, $id, $link, $subtitle);
80                                         break;
81                                 case 'atom':
82                                         $this->show_atom_timeline($notice, $title, $id, $link, $subtitle);
83                                         break;
84                                 case 'json':
85                                         $this->show_json_timeline($notice);
86                                         break;
87                                 default:
88                                         common_user_error(_('API method not found!'), $code = 404);
89                                         break;
90                         }
91
92                 } else {
93                         common_server_error(_('Couldn\'t find any statuses.'), $code = 503);
94                 }
95
96                 exit();
97         }
98
99         function show_xml_timeline($notice) {
100
101                 $this->init_document('xml');
102                 common_element_start('statuses', array('type' => 'array'));
103
104                 if (is_array($notice)) {
105                         foreach ($notice as $n) {
106                                 $twitter_status = $this->twitter_status_array($n);
107                                 $this->show_twitter_xml_status($twitter_status);
108                         }
109                 } else {
110                         while ($notice->fetch()) {
111                                 $twitter_status = $this->twitter_status_array($notice);
112                                 $this->show_twitter_xml_status($twitter_status);
113                         }
114                 }
115
116                 common_element_end('statuses');
117                 $this->end_document('xml');
118         }
119
120         function show_rss_timeline($notice, $title, $id, $link, $subtitle) {
121
122                 $this->init_document('rss');
123
124                 common_element_start('channel');
125                 common_element('title', NULL, $title);
126                 common_element('link', NULL, $link);
127                 common_element('description', NULL, $subtitle);
128                 common_element('language', NULL, 'en-us');
129                 common_element('ttl', NULL, '40');
130
131
132                 if (is_array($notice)) {
133                         foreach ($notice as $n) {
134                                 $entry = $this->twitter_rss_entry_array($n);
135                                 $this->show_twitter_rss_item($entry);
136                         }
137                 } else {
138                         while ($notice->fetch()) {
139                                 $entry = $this->twitter_rss_entry_array($notice);
140                                 $this->show_twitter_rss_item($entry);
141                         }
142                 }
143
144                 common_element_end('channel');
145                 $this->end_twitter_rss();
146         }
147
148         function show_atom_timeline($notice, $title, $id, $link, $subtitle=NULL) {
149
150                 $this->init_document('atom');
151
152                 common_element('title', NULL, $title);
153                 common_element('id', NULL, $id);
154                 common_element('link', array('href' => $link, 'rel' => 'alternate', 'type' => 'text/html'), NULL);
155                 common_element('subtitle', NULL, $subtitle);
156
157                 if (is_array($notice)) {
158                         foreach ($notice as $n) {
159                                 $entry = $this->twitter_rss_entry_array($n);
160                                 $this->show_twitter_atom_entry($entry);
161                         }
162                 } else {
163                         while ($notice->fetch()) {
164                                 $entry = $this->twitter_rss_entry_array($notice);
165                                 $this->show_twitter_atom_entry($entry);
166                         }
167                 }
168
169                 $this->end_document('atom');
170
171         }
172
173         function show_json_timeline($notice) {
174
175                 $this->init_document('json');
176
177                 $statuses = array();
178
179                 if (is_array($notice)) {
180                         foreach ($notice as $n) {
181                                 $twitter_status = $this->twitter_status_array($n);
182                                 array_push($statuses, $twitter_status);
183                         }
184                 } else {
185                         while ($notice->fetch()) {
186                                 $twitter_status = $this->twitter_status_array($notice);
187                                 array_push($statuses, $twitter_status);
188                         }
189                 }
190
191                 $this->show_twitter_json_statuses($statuses);
192
193                 $this->end_document('json');
194         }
195
196         /*
197         Returns the 20 most recent statuses posted by the authenticating user and that user's friends.
198         This is the equivalent of /home on the Web.
199
200         URL: http://server/api/statuses/friends_timeline.format
201
202         Parameters:
203
204             * since.  Optional.  Narrows the returned results to just those statuses created after the specified
205                         HTTP-formatted date.  The same behavior is available by setting an If-Modified-Since header in
206                         your HTTP request.
207                         Ex: http://server/api/statuses/friends_timeline.rss?since=Tue%2C+27+Mar+2007+22%3A55%3A48+GMT
208             * since_id.  Optional.  Returns only statuses with an ID greater than (that is, more recent than)
209                         the specified ID.  Ex: http://server/api/statuses/friends_timeline.xml?since_id=12345
210             * count.  Optional.  Specifies the number of statuses to retrieve. May not be greater than 200.
211                         Ex: http://server/api/statuses/friends_timeline.xml?count=5
212             * page. Optional. Ex: http://server/api/statuses/friends_timeline.rss?page=3
213
214         Formats: xml, json, rss, atom
215         */
216         function friends_timeline($args, $apidata) {
217                 parent::handle($args);
218
219                 $since = $this->arg('since');
220                 $since_id = $this->arg('since_id');
221                 $count = $this->arg('count');
222                 $page = $this->arg('page');
223
224                 if (!$page) {
225                         $page = 1;
226                 }
227
228                 if (!$count) {
229                         $count = 20;
230                 }
231
232                 $user = $this->get_user($id, $apidata);
233                 $profile = $user->getProfile();
234
235                 $sitename = common_config('site', 'name');
236                 $siteserver = common_config('site', 'server');
237
238                 $title = sprintf(_("%s and friends"), $user->nickname);
239                 $id = "tag:$siteserver:friends:".$user->id;
240                 $link = common_local_url('all', array('nickname' => $user->nickname));
241                 $subtitle = sprintf(_('Updates from %1$s and friends on %2$s!'), $user->nickname, $sitename);
242
243                 $notice = $user->noticesWithFriends(($page-1)*20, $count);
244
245                 switch($apidata['content-type']) {
246                  case 'xml':
247                         $this->show_xml_timeline($notice);
248                         break;
249                  case 'rss':
250                         $this->show_rss_timeline($notice, $title, $id, $link, $subtitle);
251                         break;
252                  case 'atom':
253                         $this->show_atom_timeline($notice, $title, $id, $link, $subtitle);
254                         break;
255                  case 'json':
256                         $this->show_json_timeline($notice);
257                         break;
258                  default:
259                         common_user_error(_('API method not found!'), $code = 404);
260                 }
261
262                 exit();
263         }
264
265         /*
266                 Returns the 20 most recent statuses posted from the authenticating user. It's also possible to
267         request another user's timeline via the id parameter below. This is the equivalent of the Web
268         /archive page for your own user, or the profile page for a third party.
269
270                 URL: http://server/api/statuses/user_timeline.format
271
272                 Formats: xml, json, rss, atom
273
274                 Parameters:
275
276                     * id. Optional. Specifies the ID or screen name of the user for whom to return the
277             friends_timeline. Ex: http://server/api/statuses/user_timeline/12345.xml or
278             http://server/api/statuses/user_timeline/bob.json.
279                         * count. Optional. Specifies the number of
280             statuses to retrieve. May not be greater than 200. Ex:
281             http://server/api/statuses/user_timeline.xml?count=5
282                         * since. Optional. Narrows the returned
283             results to just those statuses created after the specified HTTP-formatted date. The same
284             behavior is available by setting an If-Modified-Since header in your HTTP request. Ex:
285             http://server/api/statuses/user_timeline.rss?since=Tue%2C+27+Mar+2007+22%3A55%3A48+GMT
286                         * since_id. Optional. Returns only statuses with an ID greater than (that is, more recent than)
287             the specified ID. Ex: http://server/api/statuses/user_timeline.xml?since_id=12345 * page.
288             Optional. Ex: http://server/api/statuses/friends_timeline.rss?page=3
289         */
290         function user_timeline($args, $apidata) {
291                 parent::handle($args);
292
293                 $user = null;
294
295                 // function was called with an argument /statuses/user_timeline/api_arg.format
296                 if (isset($apidata['api_arg'])) {
297
298                         if (is_numeric($apidata['api_arg'])) {
299                                 $user = User::staticGet($apidata['api_arg']);
300                         } else {
301                                 $nickname = common_canonical_nickname($apidata['api_arg']);
302                                 $user = User::staticGet('nickname', $nickname);
303                         }
304                 } else {
305
306                         // if no user was specified, then we'll use the authenticated user
307                         $user = $apidata['user'];
308                 }
309
310                 if (!$user) {
311                         // Set the user to be the auth user if asked-for can't be found
312                         // honestly! This is what Twitter does, I swear --Zach
313                         $user = $apidata['user'];
314                 }
315
316                 $profile = $user->getProfile();
317
318                 if (!$profile) {
319                         common_server_error(_('User has no profile.'));
320                         exit();
321                 }
322
323                 $count = $this->arg('count');
324                 $since = $this->arg('since');
325                 $since_id = $this->arg('since_id');
326
327                 if (!$page) {
328                         $page = 1;
329                 }
330
331                 if (!$count) {
332                         $count = 20;
333                 }
334
335                 $sitename = common_config('site', 'name');
336                 $siteserver = common_config('site', 'server');
337
338                 $title = sprintf(_("%s timeline"), $user->nickname);
339                 $id = "tag:$siteserver:user:".$user->id;
340                 $link = common_local_url('showstream', array('nickname' => $user->nickname));
341                 $subtitle = sprintf(_('Updates from %1$s on %2$s!'), $user->nickname, $sitename);
342
343                 $notice = new Notice();
344
345                 $notice->profile_id = $user->id;
346
347                 # XXX: since
348                 # XXX: since_id
349
350                 $notice->orderBy('created DESC, notice.id DESC');
351
352                 $notice->limit((($page-1)*20), $count);
353
354                 $cnt = $notice->find();
355
356                 switch($apidata['content-type']) {
357                  case 'xml':
358                         $this->show_xml_timeline($notice);
359                         break;
360                  case 'rss':
361                         $this->show_rss_timeline($notice, $title, $id, $link, $subtitle);
362                         break;
363                  case 'atom':
364                         $this->show_atom_timeline($notice, $title, $id, $link, $subtitle);
365                         break;
366                  case 'json':
367                         $this->show_json_timeline($notice);
368                         break;
369                  default:
370                         common_user_error(_('API method not found!'), $code = 404);
371                 }
372
373                 exit();
374         }
375
376         function update($args, $apidata) {
377
378                 parent::handle($args);
379
380                 if ($_SERVER['REQUEST_METHOD'] != 'POST') {
381                         $this->client_error(_('This method requires a POST.'), 400, $apidata['content-type']);
382                         exit();
383                 }
384
385                 $user = $apidata['user'];
386                 $status = $this->trimmed('status');
387                 $source = $this->trimmed('source');
388                 $in_reply_to_status_id = intval($this->trimmed('in_reply_to_status_id'));
389
390                 if (!$source) {
391                         $source = 'api';
392                 }
393
394                 if (!$status) {
395
396                         // XXX: Note: In this case, Twitter simply returns '200 OK'
397                         // No error is given, but the status is not posted to the
398                         // user's timeline.  Seems bad.  Shouldn't we throw an
399                         // errror? -- Zach
400                         exit();
401
402                 } else if (mb_strlen($status) > 140) {
403
404                         // XXX: Twitter truncates anything over 140, flags the status
405                     // as "truncated."  Sending this error may screw up some clients
406                     // that assume Twitter will truncate for them.  Should we just
407                     // truncate too? -- Zach
408                         $this->client_error(_('That\'s too long. Max notice size is 140 chars.'), $code = 406, $apidata['content-type']);
409                         exit();
410                 }
411
412                 $reply_to = NULL;
413
414                 if ($in_reply_to_status_id) {
415                                                 
416                         // check whether notice actually exists
417                         $reply = Notice::staticGet($in_reply_to_status_id);
418                         
419                         if ($reply) {
420                                 $reply_to = $in_reply_to_status_id;
421                         } else {
422                                 $this->client_error(_('Not found'), $code = 404, $apidata['content-type']);
423                                 exit();
424                         }
425                 }
426                         
427                 $notice = Notice::saveNew($user->id, $status, $source, 1, $reply_to);
428
429                 if (is_string($notice)) {
430                         $this->server_error($notice);
431                         exit();
432                 }
433
434                 common_broadcast_notice($notice);
435
436                 // FIXME: Bad Hack
437                 // I should be able to just sent this notice off for display,
438                 // but $notice->created does not contain a string at this
439                 // point and I don't know how to convert it to one here. So
440                 // I'm forced to have DBObject pull the notice back out of the
441                 // DB before printing. --Zach
442                 $apidata['api_arg'] = $notice->id;
443                 $this->show($args, $apidata);
444
445                 exit();
446         }
447
448         /*
449                 Returns the 20 most recent @replies (status updates prefixed with @username) for the authenticating user.
450                 URL: http://server/api/statuses/replies.format
451
452                 Formats: xml, json, rss, atom
453
454                 Parameters:
455
456                 * page. Optional. Retrieves the 20 next most recent replies. Ex: http://server/api/statuses/replies.xml?page=3
457                 * since. Optional. Narrows the returned results to just those replies created after the specified HTTP-formatted date. The
458         same behavior is available by setting an If-Modified-Since header in your HTTP request. Ex:
459         http://server/api/statuses/replies.xml?since=Tue%2C+27+Mar+2007+22%3A55%3A48+GMT
460                 * since_id. Optional. Returns only statuses with an ID greater than (that is, more recent than) the specified
461                 ID. Ex: http://server/api/statuses/replies.xml?since_id=12345
462         */
463         function replies($args, $apidata) {
464
465                 parent::handle($args);
466
467                 $since = $this->arg('since');
468
469                 $count = $this->arg('count');
470                 $page = $this->arg('page');
471
472                 $user = $apidata['user'];
473                 $profile = $user->getProfile();
474
475                 $sitename = common_config('site', 'name');
476                 $siteserver = common_config('site', 'server');
477
478                 $title = sprintf(_('%1$s / Updates replying to %2$s'), $sitename, $user->nickname);
479                 $id = "tag:$siteserver:replies:".$user->id;
480                 $link = common_local_url('replies', array('nickname' => $user->nickname));
481                 $subtitle = "gar";
482                 $subtitle = sprintf(_('%1$s updates that reply to updates from %2$s / %3$s.'), $sitename, $user->nickname, $profile->getBestName());
483
484                 if (!$page) {
485                         $page = 1;
486                 }
487
488                 if (!$count) {
489                         $count = 20;
490                 }
491
492                 $reply = new Reply();
493
494                 $reply->profile_id = $user->id;
495
496                 $reply->orderBy('modified DESC');
497
498                 $page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
499
500                 $reply->limit((($page-1)*20), $count);
501
502                 $cnt = $reply->find();
503
504                 $notices = array();
505
506                 if ($cnt) {
507                         while ($reply->fetch()) {
508                                 $notice = new Notice();
509                                 $notice->id = $reply->notice_id;
510                                 $result = $notice->find(true);
511                                 if (!$result) {
512                                         continue;
513                                 }
514                                 $notices[] = clone($notice);
515                         }
516                 }
517
518                 switch($apidata['content-type']) {
519                  case 'xml':
520                         $this->show_xml_timeline($notices);
521                         break;
522                  case 'rss':
523                         $this->show_rss_timeline($notices, $title, $id, $link, $subtitle);
524                         break;
525                  case 'atom':
526                         $this->show_atom_timeline($notices, $title, $id, $link, $subtitle);
527                         break;
528                  case 'json':
529                         $this->show_json_timeline($notices);
530                         break;
531                  default:
532                         common_user_error(_('API method not found!'), $code = 404);
533                 }
534
535
536                 exit();
537
538
539         }
540
541         function show($args, $apidata) {
542                 parent::handle($args);
543                 
544                 $notice_id = $apidata['api_arg'];               
545                 $notice = Notice::staticGet($notice_id);
546
547                 if ($notice) {
548                         if ($apidata['content-type'] == 'xml') { 
549                                 $this->show_single_xml_status($notice);
550                         } elseif ($apidata['content-type'] == 'json') {
551                                 $this->show_single_json_status($notice);
552                         }
553                 } else {
554                         // XXX: Twitter just sets a 404 header and doens't bother to return an err msg
555                         $this->client_error(_('No status with that ID found.'), 404, $apidata['content-type']);
556                 }
557                 
558                 exit();
559         }
560
561
562         /*
563                 Destroys the status specified by the required ID parameter. The authenticating user must be
564         the author of the specified status.
565
566                  URL: http://server/api/statuses/destroy/id.format
567
568                  Formats: xml, json
569
570                  Parameters:
571
572                  * id. Required. The ID of the status to destroy. Ex:
573                 http://server/api/statuses/destroy/12345.json or
574                 http://server/api/statuses/destroy/23456.xml
575
576         */
577         function destroy($args, $apidata) {
578         
579                 parent::handle($args);
580
581                 common_debug($_SERVER['REQUEST_METHOD']);
582                 
583                 // Check for RESTfulness  
584                 if (!in_array($_SERVER['REQUEST_METHOD'], array('POST', 'DELETE'))) {
585                         // XXX: Twitter just prints the err msg, no XML / JSON.
586                         $this->client_error(_('This method requires a POST or DELETE.'), 400, $apidata['content-type']);
587                         exit();
588                 } 
589                 
590                 $user = $apidata['user'];                               
591                 $notice_id = $apidata['api_arg'];               
592                 $notice = Notice::staticGet($notice_id);
593                 
594                 if (!$notice) {
595                         $this->client_error(_('No status found with that ID.'), 404, $apidata['content-type']);
596                         exit();
597                 }
598                                 
599                 if ($user->id == $notice->profile_id) {
600                         $replies = new Reply;
601                         $replies->get('notice_id', $notice_id);
602                         common_dequeue_notice($notice);
603                         $replies->delete();
604                         $notice->delete();
605                         
606                         if ($apidata['content-type'] == 'xml') { 
607                                 $this->show_single_xml_status($notice);
608                         } elseif ($apidata['content-type'] == 'json') {
609                                 $this->show_single_json_status($notice);
610                         }       
611                 } else {
612                         $this->client_error(_('You may not delete another user\'s status.'), 403, $apidata['content-type']);
613                 }
614                 
615                 exit();
616         }
617
618         # User Methods
619
620         /*
621                 Returns up to 100 of the authenticating user's friends who have most recently updated, each with current status inline.
622         It's also possible to request another user's recent friends list via the id parameter below.
623
624                  URL: http://server/api/statuses/friends.format
625
626                  Formats: xml, json
627
628                  Parameters:
629
630                  * id. Optional. The ID or screen name of the user for whom to request a list of friends. Ex:
631                 http://server/api/statuses/friends/12345.json
632                         or
633                         http://server/api/statuses/friends/bob.xml
634                  * page. Optional. Retrieves the next 100 friends. Ex: http://server/api/statuses/friends.xml?page=2
635                  * lite. Optional. Prevents the inline inclusion of current status. Must be set to a value of true. Ex:
636                 http://server/api/statuses/friends.xml?lite=true
637                  * since. Optional. Narrows the returned results to just those friendships created after the specified
638                         HTTP-formatted date. The same behavior is available by setting an If-Modified-Since header in your HTTP
639                         request. Ex: http://server/api/statuses/friends.xml?since=Tue%2C+27+Mar+2007+22%3A55%3A48+GMT
640         */
641         function friends($args, $apidata) {
642                 parent::handle($args);
643                 return $this->subscriptions($apidata, 'subscribed', 'subscriber');
644         }
645
646         /*
647                 Returns the authenticating user's followers, each with current status inline. They are ordered by the
648                 order in which they joined Twitter (this is going to be changed).
649
650                 URL: http://server/api/statuses/followers.format
651                 Formats: xml, json
652
653                 Parameters:
654
655                     * id. Optional. The ID or screen name of the user for whom to request a list of followers. Ex:
656                 http://server/api/statuses/followers/12345.json
657                                 or
658                                 http://server/api/statuses/followers/bob.xml
659                     * page. Optional. Retrieves the next 100 followers. Ex: http://server/api/statuses/followers.xml?page=2
660                     * lite. Optional. Prevents the inline inclusion of current status. Must be set to a value of true.
661                                 Ex: http://server/api/statuses/followers.xml?lite=true
662         */
663         function followers($args, $apidata) {
664                 parent::handle($args);
665
666                 return $this->subscriptions($apidata, 'subscriber', 'subscribed');
667         }
668
669         function subscriptions($apidata, $other_attr, $user_attr) {
670
671                 $user = $this->get_subs_user($apidata);
672
673                 # XXX: id
674                 # XXX: lite
675
676                 $page = $this->trimmed('page');
677
678                 if (!$page || !is_numeric($page)) {
679                         $page = 1;
680                 }
681
682                 $profile = $user->getProfile();
683
684                 if (!$profile) {
685                         common_server_error(_('User has no profile.'));
686                         return;
687                 }
688
689                 $sub = new Subscription();
690                 $sub->$user_attr = $profile->id;
691                 $sub->orderBy('created DESC');
692                 $sub->limit(($page-1)*100, 100);
693
694                 $others = array();
695
696                 if ($sub->find()) {
697                         while ($sub->fetch()) {
698                                 $others[] = Profile::staticGet($sub->$other_attr);
699                         }
700                 } else {
701                         // user has no followers
702                 }
703
704                 $type = $apidata['content-type'];
705
706                 $this->init_document($type);
707                 $this->show_profiles($others, $type);
708                 $this->end_document($type);
709                 exit();
710         }
711
712         function get_subs_user($apidata) {
713
714                 // function was called with an argument /statuses/user_timeline/api_arg.format
715                 if (isset($apidata['api_arg'])) {
716
717                         if (is_numeric($apidata['api_arg'])) {
718                                 $user = User::staticGet($apidata['api_arg']);
719                         } else {
720                                 $nickname = common_canonical_nickname($apidata['api_arg']);
721                                 $user = User::staticGet('nickname', $nickname);
722                         }
723                 } else {
724
725                         // if no user was specified, then we'll use the authenticated user
726                         $user = $apidata['user'];
727                 }
728
729                 if (!$user) {
730                         // Set the user to be the auth user if asked-for can't be found
731                         // honestly! This is what Twitter does, I swear --Zach
732                         $user = $apidata['user'];
733                 }
734
735                 return $user;
736         }
737
738         function show_profiles($profiles, $type) {
739                 switch ($type) {
740                  case 'xml':
741                         common_element_start('users', array('type' => 'array'));
742                         foreach ($profiles as $profile) {
743                                 $this->show_profile($profile);
744                         }
745                         common_element_end('users');
746                         break;
747                  case 'json':
748                         $arrays = array();
749                         foreach ($profiles as $profile) {
750                                 $arrays[] = $this->twitter_user_array($profile, true);
751                         }
752                         print json_encode($arrays);
753                         break;
754                  default:
755                         $this->client_error(_('unsupported file type'));
756                         exit();
757                 }
758         }
759
760         /*
761         Returns a list of the users currently featured on the site with their current statuses inline.
762         URL: http://server/api/statuses/featured.format
763
764         Formats: xml, json
765         */
766         function featured($args, $apidata) {
767                 parent::handle($args);
768                 common_server_error(_('API method under construction.'), $code=501);
769         }
770
771         function get_user($id, $apidata) {
772                 if (!$id) {
773                         return $apidata['user'];
774                 } else if (is_numeric($id)) {
775                         return User::staticGet($id);
776                 } else {
777                         return User::staticGet('nickname', $id);
778                 }
779         }
780 }
781