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