]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - actions/twitapistatuses.php
4ad2766c0a439ccdf5ac015fe80d4bcc68e943ba
[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                 $user = $apidata['user'];
378                 $status = $this->trimmed('status');
379                 $source = $this->trimmed('source');
380                 $in_reply_to_status_id = intval($this->trimmed('in_reply_to_status_id'));
381
382                 if (!$source) {
383                         $source = 'api';
384                 }
385
386                 if (!$status) {
387
388                         // XXX: Note: In this case, Twitter simply returns '200 OK'
389                         // No error is given, but the status is not posted to the
390                         // user's timeline.  Seems bad.  Shouldn't we throw an
391                         // errror? -- Zach
392                         exit();
393
394                 } else if (mb_strlen($status) > 140) {
395
396                         // XXX: Twitter truncates anything over 140, flags the status
397                     // as "truncated."  Sending this error may screw up some clients
398                     // that assume Twitter will truncate for them.  Should we just
399                     // truncate too? -- Zach
400                         $this->client_error(_('That\'s too long. Max notice size is 140 chars.'), $code = 406, $apidata['content-type']);
401                         exit();
402                 }
403
404                 $reply_to = NULL;
405
406                 if ($in_reply_to_status_id) {
407                                                 
408                         // check whether notice actually exists
409                         $reply = Notice::staticGet($in_reply_to_status_id);
410                         
411                         if ($reply) {
412                                 $reply_to = $in_reply_to_status_id;
413                         } else {
414                                 $this->client_error(_('Not found'), $code = 404, $apidata['content-type']);
415                                 exit();
416                         }
417                 }
418                         
419                 $notice = Notice::saveNew($user->id, $status, $source, 1, $reply_to);
420
421                 if (is_string($notice)) {
422                         $this->server_error($notice);
423                         exit();
424                 }
425
426                 common_broadcast_notice($notice);
427
428                 // FIXME: Bad Hack
429                 // I should be able to just sent this notice off for display,
430                 // but $notice->created does not contain a string at this
431                 // point and I don't know how to convert it to one here. So
432                 // I'm forced to have DBObject pull the notice back out of the
433                 // DB before printing. --Zach
434                 $apidata['api_arg'] = $notice->id;
435                 $this->show($args, $apidata);
436
437                 exit();
438         }
439
440         /*
441                 Returns the 20 most recent @replies (status updates prefixed with @username) for the authenticating user.
442                 URL: http://server/api/statuses/replies.format
443
444                 Formats: xml, json, rss, atom
445
446                 Parameters:
447
448                 * page. Optional. Retrieves the 20 next most recent replies. Ex: http://server/api/statuses/replies.xml?page=3
449                 * since. Optional. Narrows the returned results to just those replies created after the specified HTTP-formatted date. The
450         same behavior is available by setting an If-Modified-Since header in your HTTP request. Ex:
451         http://server/api/statuses/replies.xml?since=Tue%2C+27+Mar+2007+22%3A55%3A48+GMT
452                 * since_id. Optional. Returns only statuses with an ID greater than (that is, more recent than) the specified
453                 ID. Ex: http://server/api/statuses/replies.xml?since_id=12345
454         */
455         function replies($args, $apidata) {
456
457                 parent::handle($args);
458
459                 $since = $this->arg('since');
460
461                 $count = $this->arg('count');
462                 $page = $this->arg('page');
463
464                 $user = $apidata['user'];
465                 $profile = $user->getProfile();
466
467                 $sitename = common_config('site', 'name');
468                 $siteserver = common_config('site', 'server');
469
470                 $title = sprintf(_('%1$s / Updates replying to %2$s'), $sitename, $user->nickname);
471                 $id = "tag:$siteserver:replies:".$user->id;
472                 $link = common_local_url('replies', array('nickname' => $user->nickname));
473                 $subtitle = "gar";
474                 $subtitle = sprintf(_('%1$s updates that reply to updates from %2$s / %3$s.'), $sitename, $user->nickname, $profile->getBestName());
475
476                 if (!$page) {
477                         $page = 1;
478                 }
479
480                 if (!$count) {
481                         $count = 20;
482                 }
483
484                 $reply = new Reply();
485
486                 $reply->profile_id = $user->id;
487
488                 $reply->orderBy('modified DESC');
489
490                 $page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
491
492                 $reply->limit((($page-1)*20), $count);
493
494                 $cnt = $reply->find();
495
496                 $notices = array();
497
498                 if ($cnt) {
499                         while ($reply->fetch()) {
500                                 $notice = new Notice();
501                                 $notice->id = $reply->notice_id;
502                                 $result = $notice->find(true);
503                                 if (!$result) {
504                                         continue;
505                                 }
506                                 $notices[] = clone($notice);
507                         }
508                 }
509
510                 switch($apidata['content-type']) {
511                  case 'xml':
512                         $this->show_xml_timeline($notices);
513                         break;
514                  case 'rss':
515                         $this->show_rss_timeline($notices, $title, $id, $link, $subtitle);
516                         break;
517                  case 'atom':
518                         $this->show_atom_timeline($notices, $title, $id, $link, $subtitle);
519                         break;
520                  case 'json':
521                         $this->show_json_timeline($notices);
522                         break;
523                  default:
524                         common_user_error(_('API method not found!'), $code = 404);
525                 }
526
527
528                 exit();
529
530
531         }
532
533         function show($args, $apidata) {
534                 parent::handle($args);
535                 
536                 $notice_id = $apidata['api_arg'];               
537                 $notice = Notice::staticGet($notice_id);
538
539                 if ($notice) {
540                         if ($apidata['content-type'] == 'xml') { 
541                                 $this->show_single_xml_status($notice);
542                         } elseif ($apidata['content-type'] == 'json') {
543                                 $this->show_single_json_status($notice);
544                         }
545                 } else {
546                         // XXX: Twitter just sets a 404 header and doens't bother to return an err msg
547                         $this->client_error(_('No status with that ID found.'), 404, $apidata['content-type']);
548                 }
549                 
550                 exit();
551         }
552
553
554         /*
555                 Destroys the status specified by the required ID parameter. The authenticating user must be
556         the author of the specified status.
557
558                  URL: http://server/api/statuses/destroy/id.format
559
560                  Formats: xml, json
561
562                  Parameters:
563
564                  * id. Required. The ID of the status to destroy. Ex:
565                 http://server/api/statuses/destroy/12345.json or
566                 http://server/api/statuses/destroy/23456.xml
567
568         */
569         function destroy($args, $apidata) {
570         
571                 parent::handle($args);
572
573                 common_debug($_SERVER['REQUEST_METHOD']);
574                 
575                 // Check for RESTfulness  
576                 if (!in_array($_SERVER['REQUEST_METHOD'], array('POST', 'DELETE'))) {
577                         // XXX: Twitter just prints the err msg, no XML / JSON.
578                         $this->client_error(_('This method requires a POST or DELETE.'), 400, $apidata['content-type']);
579                         exit();
580                 } 
581                 
582                 $user = $apidata['user'];                               
583                 $notice_id = $apidata['api_arg'];               
584                 $notice = Notice::staticGet($notice_id);
585                 
586                 if (!$notice) {
587                         $this->client_error(_('No status found with that ID.'), 404, $apidata['content-type']);
588                         exit();
589                 }
590                                 
591                 if ($user->id == $notice->profile_id) {
592                         $replies = new Reply;
593                         $replies->get('notice_id', $notice_id);
594                         common_dequeue_notice($notice);
595                         $replies->delete();
596                         $notice->delete();
597                         
598                         if ($apidata['content-type'] == 'xml') { 
599                                 $this->show_single_xml_status($notice);
600                         } elseif ($apidata['content-type'] == 'json') {
601                                 $this->show_single_json_status($notice);
602                         }       
603                 } else {
604                         $this->client_error(_('You may not delete another user\'s status.'), 403, $apidata['content-type']);
605                 }
606                 
607                 exit();
608         }
609
610         # User Methods
611
612         /*
613                 Returns up to 100 of the authenticating user's friends who have most recently updated, each with current status inline.
614         It's also possible to request another user's recent friends list via the id parameter below.
615
616                  URL: http://server/api/statuses/friends.format
617
618                  Formats: xml, json
619
620                  Parameters:
621
622                  * id. Optional. The ID or screen name of the user for whom to request a list of friends. Ex:
623                 http://server/api/statuses/friends/12345.json
624                         or
625                         http://server/api/statuses/friends/bob.xml
626                  * page. Optional. Retrieves the next 100 friends. Ex: http://server/api/statuses/friends.xml?page=2
627                  * lite. Optional. Prevents the inline inclusion of current status. Must be set to a value of true. Ex:
628                 http://server/api/statuses/friends.xml?lite=true
629                  * since. Optional. Narrows the returned results to just those friendships created after the specified
630                         HTTP-formatted date. The same behavior is available by setting an If-Modified-Since header in your HTTP
631                         request. Ex: http://server/api/statuses/friends.xml?since=Tue%2C+27+Mar+2007+22%3A55%3A48+GMT
632         */
633         function friends($args, $apidata) {
634                 parent::handle($args);
635                 return $this->subscriptions($apidata, 'subscribed', 'subscriber');
636         }
637
638         /*
639                 Returns the authenticating user's followers, each with current status inline. They are ordered by the
640                 order in which they joined Twitter (this is going to be changed).
641
642                 URL: http://server/api/statuses/followers.format
643                 Formats: xml, json
644
645                 Parameters:
646
647                     * id. Optional. The ID or screen name of the user for whom to request a list of followers. Ex:
648                 http://server/api/statuses/followers/12345.json
649                                 or
650                                 http://server/api/statuses/followers/bob.xml
651                     * page. Optional. Retrieves the next 100 followers. Ex: http://server/api/statuses/followers.xml?page=2
652                     * lite. Optional. Prevents the inline inclusion of current status. Must be set to a value of true.
653                                 Ex: http://server/api/statuses/followers.xml?lite=true
654         */
655         function followers($args, $apidata) {
656                 parent::handle($args);
657
658                 return $this->subscriptions($apidata, 'subscriber', 'subscribed');
659         }
660
661         function subscriptions($apidata, $other_attr, $user_attr) {
662
663                 $user = $this->get_subs_user($apidata);
664
665                 # XXX: id
666                 # XXX: lite
667
668                 $page = $this->trimmed('page');
669
670                 if (!$page || !is_numeric($page)) {
671                         $page = 1;
672                 }
673
674                 $profile = $user->getProfile();
675
676                 if (!$profile) {
677                         common_server_error(_('User has no profile.'));
678                         return;
679                 }
680
681                 $sub = new Subscription();
682                 $sub->$user_attr = $profile->id;
683                 $sub->orderBy('created DESC');
684                 $sub->limit(($page-1)*100, 100);
685
686                 $others = array();
687
688                 if ($sub->find()) {
689                         while ($sub->fetch()) {
690                                 $others[] = Profile::staticGet($sub->$other_attr);
691                         }
692                 } else {
693                         // user has no followers
694                 }
695
696                 $type = $apidata['content-type'];
697
698                 $this->init_document($type);
699                 $this->show_profiles($others, $type);
700                 $this->end_document($type);
701                 exit();
702         }
703
704         function get_subs_user($apidata) {
705
706                 // function was called with an argument /statuses/user_timeline/api_arg.format
707                 if (isset($apidata['api_arg'])) {
708
709                         if (is_numeric($apidata['api_arg'])) {
710                                 $user = User::staticGet($apidata['api_arg']);
711                         } else {
712                                 $nickname = common_canonical_nickname($apidata['api_arg']);
713                                 $user = User::staticGet('nickname', $nickname);
714                         }
715                 } else {
716
717                         // if no user was specified, then we'll use the authenticated user
718                         $user = $apidata['user'];
719                 }
720
721                 if (!$user) {
722                         // Set the user to be the auth user if asked-for can't be found
723                         // honestly! This is what Twitter does, I swear --Zach
724                         $user = $apidata['user'];
725                 }
726
727                 return $user;
728         }
729
730         function show_profiles($profiles, $type) {
731                 switch ($type) {
732                  case 'xml':
733                         common_element_start('users', array('type' => 'array'));
734                         foreach ($profiles as $profile) {
735                                 $this->show_profile($profile);
736                         }
737                         common_element_end('users');
738                         break;
739                  case 'json':
740                         $arrays = array();
741                         foreach ($profiles as $profile) {
742                                 $arrays[] = $this->twitter_user_array($profile, true);
743                         }
744                         print json_encode($arrays);
745                         break;
746                  default:
747                         $this->client_error(_('unsupported file type'));
748                         exit();
749                 }
750         }
751
752         /*
753         Returns a list of the users currently featured on the site with their current statuses inline.
754         URL: http://server/api/statuses/featured.format
755
756         Formats: xml, json
757         */
758         function featured($args, $apidata) {
759                 parent::handle($args);
760                 common_server_error(_('API method under construction.'), $code=501);
761         }
762
763         function get_user($id, $apidata) {
764                 if (!$id) {
765                         return $apidata['user'];
766                 } else if (is_numeric($id)) {
767                         return User::staticGet($id);
768                 } else {
769                         return User::staticGet('nickname', $id);
770                 }
771         }
772 }
773