X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=lib%2Fapiaction.php;h=d8249055a492d30c980e4aad3ed7106b6d2c79db;hb=99194e03fa50b61f99164674afc949b4bbefd44a;hp=27df558928f003e5bd1457fec0aec05c18db7ee4;hpb=9684cbe3c6d61be0b1cc2094bc4278cd8a21b33e;p=quix0rs-gnu-social.git diff --git a/lib/apiaction.php b/lib/apiaction.php index 27df558928..d8249055a4 100644 --- a/lib/apiaction.php +++ b/lib/apiaction.php @@ -27,7 +27,7 @@ * @author Jeffery To * @author Toby Inkster * @author Zach Copley - * @copyright 2009 StatusNet, Inc. + * @copyright 2009-2010 StatusNet, Inc. * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ @@ -98,6 +98,8 @@ if (!defined('STATUSNET')) { exit(1); } +class ApiValidationException extends Exception { } + /** * Contains most of the Twitter-compatible API output functions. * @@ -112,7 +114,6 @@ if (!defined('STATUSNET')) { * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ - class ApiAction extends Action { const READ_ONLY = 1; @@ -126,6 +127,7 @@ class ApiAction extends Action var $max_id = null; var $since_id = null; var $source = null; + var $callback = null; var $access = self::READ_ONLY; // read (default) or read-write @@ -138,13 +140,13 @@ class ApiAction extends Action * * @return boolean false if user doesn't exist */ - function prepare($args) { StatusNet::setApi(true); // reduce exception reports to aid in debugging parent::prepare($args); $this->format = $this->arg('format'); + $this->callback = $this->arg('callback'); $this->page = (int)$this->arg('page', 1); $this->count = (int)$this->arg('count', 20); $this->max_id = (int)$this->arg('max_id', 0); @@ -170,7 +172,6 @@ class ApiAction extends Action * * @return void */ - function handle($args) { header('Access-Control-Allow-Origin: *'); @@ -271,11 +272,13 @@ class ApiAction extends Action // Is the requesting user following this user? $twitter_user['following'] = false; + $twitter_user['statusnet:blocking'] = false; $twitter_user['notifications'] = false; if (isset($this->auth_user)) { $twitter_user['following'] = $this->auth_user->isSubscribed($profile); + $twitter_user['statusnet:blocking'] = $this->auth_user->hasBlocked($profile); // Notifications on? $sub = Subscription::pkeyGet(array('subscriber' => @@ -297,7 +300,7 @@ class ApiAction extends Action // StatusNet-specific - $twitter_user['statusnet:profile_url'] = $profile->profileurl; + $twitter_user['statusnet_profile_url'] = $profile->profileurl; return $twitter_user; } @@ -402,27 +405,39 @@ class ApiAction extends Action // StatusNet-specific - $twitter_status['statusnet:html'] = $notice->rendered; + $twitter_status['statusnet_html'] = $notice->rendered; return $twitter_status; } function twitterGroupArray($group) { - $twitter_group=array(); - $twitter_group['id']=$group->id; - $twitter_group['url']=$group->permalink(); - $twitter_group['nickname']=$group->nickname; - $twitter_group['fullname']=$group->fullname; - $twitter_group['original_logo']=$group->original_logo; - $twitter_group['homepage_logo']=$group->homepage_logo; - $twitter_group['stream_logo']=$group->stream_logo; - $twitter_group['mini_logo']=$group->mini_logo; - $twitter_group['homepage']=$group->homepage; - $twitter_group['description']=$group->description; - $twitter_group['location']=$group->location; - $twitter_group['created']=$this->dateTwitter($group->created); - $twitter_group['modified']=$this->dateTwitter($group->modified); + $twitter_group = array(); + + $twitter_group['id'] = $group->id; + $twitter_group['url'] = $group->permalink(); + $twitter_group['nickname'] = $group->nickname; + $twitter_group['fullname'] = $group->fullname; + + if (isset($this->auth_user)) { + $twitter_group['member'] = $this->auth_user->isMember($group); + $twitter_group['blocked'] = Group_block::isBlocked( + $group, + $this->auth_user->getProfile() + ); + } + + $twitter_group['member_count'] = $group->getMemberCount(); + $twitter_group['original_logo'] = $group->original_logo; + $twitter_group['homepage_logo'] = $group->homepage_logo; + $twitter_group['stream_logo'] = $group->stream_logo; + $twitter_group['mini_logo'] = $group->mini_logo; + $twitter_group['homepage'] = $group->homepage; + $twitter_group['description'] = $group->description; + $twitter_group['location'] = $group->location; + $twitter_group['created'] = $this->dateTwitter($group->created); + $twitter_group['modified'] = $this->dateTwitter($group->modified); + return $twitter_group; } @@ -446,65 +461,70 @@ class ApiAction extends Action function twitterRssEntryArray($notice) { - $profile = $notice->getProfile(); $entry = array(); - // We trim() to avoid extraneous whitespace in the output + if (Event::handle('StartRssEntryArray', array($notice, &$entry))) { + $profile = $notice->getProfile(); - $entry['content'] = common_xml_safe_str(trim($notice->rendered)); - $entry['title'] = $profile->nickname . ': ' . common_xml_safe_str(trim($notice->content)); - $entry['link'] = common_local_url('shownotice', array('notice' => $notice->id)); - $entry['published'] = common_date_iso8601($notice->created); + // We trim() to avoid extraneous whitespace in the output - $taguribase = TagURI::base(); - $entry['id'] = "tag:$taguribase:$entry[link]"; + $entry['content'] = common_xml_safe_str(trim($notice->rendered)); + $entry['title'] = $profile->nickname . ': ' . common_xml_safe_str(trim($notice->content)); + $entry['link'] = common_local_url('shownotice', array('notice' => $notice->id)); + $entry['published'] = common_date_iso8601($notice->created); - $entry['updated'] = $entry['published']; - $entry['author'] = $profile->getBestName(); + $taguribase = TagURI::base(); + $entry['id'] = "tag:$taguribase:$entry[link]"; - // Enclosures - $attachments = $notice->attachments(); - $enclosures = array(); - - foreach ($attachments as $attachment) { - $enclosure_o=$attachment->getEnclosure(); - if ($enclosure_o) { - $enclosure = array(); - $enclosure['url'] = $enclosure_o->url; - $enclosure['mimetype'] = $enclosure_o->mimetype; - $enclosure['size'] = $enclosure_o->size; - $enclosures[] = $enclosure; - } - } + $entry['updated'] = $entry['published']; + $entry['author'] = $profile->getBestName(); - if (!empty($enclosures)) { - $entry['enclosures'] = $enclosures; - } + // Enclosures + $attachments = $notice->attachments(); + $enclosures = array(); + + foreach ($attachments as $attachment) { + $enclosure_o=$attachment->getEnclosure(); + if ($enclosure_o) { + $enclosure = array(); + $enclosure['url'] = $enclosure_o->url; + $enclosure['mimetype'] = $enclosure_o->mimetype; + $enclosure['size'] = $enclosure_o->size; + $enclosures[] = $enclosure; + } + } - // Tags/Categories - $tag = new Notice_tag(); - $tag->notice_id = $notice->id; - if ($tag->find()) { - $entry['tags']=array(); - while ($tag->fetch()) { - $entry['tags'][]=$tag->tag; + if (!empty($enclosures)) { + $entry['enclosures'] = $enclosures; } - } - $tag->free(); - // RSS Item specific - $entry['description'] = $entry['content']; - $entry['pubDate'] = common_date_rfc2822($notice->created); - $entry['guid'] = $entry['link']; + // Tags/Categories + $tag = new Notice_tag(); + $tag->notice_id = $notice->id; + if ($tag->find()) { + $entry['tags']=array(); + while ($tag->fetch()) { + $entry['tags'][]=$tag->tag; + } + } + $tag->free(); + + // RSS Item specific + $entry['description'] = $entry['content']; + $entry['pubDate'] = common_date_rfc2822($notice->created); + $entry['guid'] = $entry['link']; + + if (isset($notice->lat) && isset($notice->lon)) { + // This is the format that GeoJSON expects stuff to be in. + // showGeoRSS() below uses it for XML output, so we reuse it + $entry['geo'] = array('type' => 'Point', + 'coordinates' => array((float) $notice->lat, + (float) $notice->lon)); + } else { + $entry['geo'] = null; + } - if (isset($notice->lat) && isset($notice->lon)) { - // This is the format that GeoJSON expects stuff to be in. - // showGeoRSS() below uses it for XML output, so we reuse it - $entry['geo'] = array('type' => 'Point', - 'coordinates' => array((float) $notice->lat, - (float) $notice->lon)); - } else { - $entry['geo'] = null; + Event::handle('EndRssEntryArray', array($notice, &$entry)); } return $entry; @@ -533,7 +553,6 @@ class ApiAction extends Action $notifications = false; if ($source->isSubscribed($target)) { - $sub = Subscription::pkeyGet(array('subscriber' => $source->id, 'subscribed' => $target->id)); @@ -596,7 +615,11 @@ class ApiAction extends Action $this->showTwitterXmlStatus($value, 'retweeted_status'); break; default: - $this->element($element, null, $value); + if (strncmp($element, 'statusnet_', 10) == 0) { + $this->element('statusnet:'.substr($element, 10), null, $value); + } else { + $this->element($element, null, $value); + } } } $this->elementEnd($tag); @@ -621,6 +644,8 @@ class ApiAction extends Action foreach($twitter_user as $element => $value) { if ($element == 'status') { $this->showTwitterXmlStatus($twitter_user['status']); + } else if (strncmp($element, 'statusnet_', 10) == 0) { + $this->element('statusnet:'.substr($element, 10), null, $value); } else { $this->element($element, null, $value); } @@ -713,20 +738,21 @@ class ApiAction extends Action function showXmlTimeline($notice) { - $this->initDocument('xml'); $this->elementStart('statuses', array('type' => 'array', 'xmlns:statusnet' => 'http://status.net/schema/api/1/')); if (is_array($notice)) { - foreach ($notice as $n) { - $twitter_status = $this->twitterStatusArray($n); - $this->showTwitterXmlStatus($twitter_status); - } - } else { - while ($notice->fetch()) { + $notice = new ArrayWrapper($notice); + } + + while ($notice->fetch()) { + try { $twitter_status = $this->twitterStatusArray($notice); $this->showTwitterXmlStatus($twitter_status); + } catch (Exception $e) { + common_log(LOG_ERR, $e->getMessage()); + continue; } } @@ -736,7 +762,6 @@ class ApiAction extends Action function showRssTimeline($notice, $title, $link, $subtitle, $suplink = null, $logo = null, $self = null) { - $this->initDocument('rss'); $this->element('title', null, $title); @@ -774,14 +799,16 @@ class ApiAction extends Action $this->element('ttl', null, '40'); if (is_array($notice)) { - foreach ($notice as $n) { - $entry = $this->twitterRssEntryArray($n); - $this->showTwitterRssItem($entry); - } - } else { - while ($notice->fetch()) { + $notice = new ArrayWrapper($notice); + } + + while ($notice->fetch()) { + try { $entry = $this->twitterRssEntryArray($notice); $this->showTwitterRssItem($entry); + } catch (Exception $e) { + common_log(LOG_ERR, $e->getMessage()); + // continue on exceptions } } @@ -790,7 +817,6 @@ class ApiAction extends Action function showAtomTimeline($notice, $title, $id, $link, $subtitle=null, $suplink=null, $selfuri=null, $logo=null) { - $this->initDocument('atom'); $this->element('title', null, $title); @@ -817,22 +843,23 @@ class ApiAction extends Action $this->element('subtitle', null, $subtitle); if (is_array($notice)) { - foreach ($notice as $n) { - $this->raw($n->asAtomEntry()); - } - } else { - while ($notice->fetch()) { + $notice = new ArrayWrapper($notice); + } + + while ($notice->fetch()) { + try { $this->raw($notice->asAtomEntry()); + } catch (Exception $e) { + common_log(LOG_ERR, $e->getMessage()); + continue; } } $this->endDocument('atom'); - } function showRssGroups($group, $title, $link, $subtitle) { - $this->initDocument('rss'); $this->element('title', null, $title); @@ -980,7 +1007,6 @@ class ApiAction extends Action function showAtomGroups($group, $title, $id, $link, $subtitle=null, $selfuri=null) { - $this->initDocument('atom'); $this->element('title', null, common_xml_safe_str($title)); @@ -1011,20 +1037,21 @@ class ApiAction extends Action function showJsonTimeline($notice) { - $this->initDocument('json'); $statuses = array(); if (is_array($notice)) { - foreach ($notice as $n) { - $twitter_status = $this->twitterStatusArray($n); - array_push($statuses, $twitter_status); - } - } else { - while ($notice->fetch()) { + $notice = new ArrayWrapper($notice); + } + + while ($notice->fetch()) { + try { $twitter_status = $this->twitterStatusArray($notice); array_push($statuses, $twitter_status); + } catch (Exception $e) { + common_log(LOG_ERR, $e->getMessage()); + continue; } } @@ -1035,7 +1062,6 @@ class ApiAction extends Action function showJsonGroups($group) { - $this->initDocument('json'); $groups = array(); @@ -1081,7 +1107,6 @@ class ApiAction extends Action function showTwitterXmlUsers($user) { - $this->initDocument('xml'); $this->elementStart('users', array('type' => 'array', 'xmlns:statusnet' => 'http://status.net/schema/api/1/')); @@ -1104,7 +1129,6 @@ class ApiAction extends Action function showJsonUsers($user) { - $this->initDocument('json'); $users = array(); @@ -1161,9 +1185,8 @@ class ApiAction extends Action header('Content-Type: application/json; charset=utf-8'); // Check for JSONP callback - $callback = $this->arg('callback'); - if ($callback) { - print $callback . '('; + if (isset($this->callback)) { + print $this->callback . '('; } break; case 'rss': @@ -1190,10 +1213,8 @@ class ApiAction extends Action $this->endXML(); break; case 'json': - // Check for JSONP callback - $callback = $this->arg('callback'); - if ($callback) { + if (isset($this->callback)) { print ')'; } break; @@ -1223,7 +1244,10 @@ class ApiAction extends Action $status_string = ClientErrorAction::$status[$code]; - header('HTTP/1.1 '.$code.' '.$status_string); + // Do not emit error header for JSONP + if (!isset($this->callback)) { + header('HTTP/1.1 '.$code.' '.$status_string); + } if ($format == 'xml') { $this->initDocument('xml'); @@ -1256,7 +1280,10 @@ class ApiAction extends Action $status_string = ServerErrorAction::$status[$code]; - header('HTTP/1.1 '.$code.' '.$status_string); + // Do not emit error header for JSONP + if (!isset($this->callback)) { + header('HTTP/1.1 '.$code.' '.$status_string); + } if ($content_type == 'xml') { $this->initDocument('xml'); @@ -1331,7 +1358,6 @@ class ApiAction extends Action function getTargetUser($id) { if (empty($id)) { - // Twitter supports these other ways of passing the user ID if (is_numeric($this->arg('id'))) { return User::staticGet($this->arg('id')); @@ -1360,6 +1386,34 @@ class ApiAction extends Action } } + function getTargetProfile($id) + { + if (empty($id)) { + + // Twitter supports these other ways of passing the user ID + if (is_numeric($this->arg('id'))) { + return Profile::staticGet($this->arg('id')); + } else if ($this->arg('id')) { + $nickname = common_canonical_nickname($this->arg('id')); + return Profile::staticGet('nickname', $nickname); + } else if ($this->arg('user_id')) { + // This is to ensure that a non-numeric user_id still + // overrides screen_name even if it doesn't get used + if (is_numeric($this->arg('user_id'))) { + return Profile::staticGet('id', $this->arg('user_id')); + } + } else if ($this->arg('screen_name')) { + $nickname = common_canonical_nickname($this->arg('screen_name')); + return Profile::staticGet('nickname', $nickname); + } + } else if (is_numeric($id)) { + return Profile::staticGet($id); + } else { + $nickname = common_canonical_nickname($id); + return Profile::staticGet('nickname', $nickname); + } + } + function getTargetGroup($id) { if (empty($id)) { @@ -1414,7 +1468,6 @@ class ApiAction extends Action */ function arg($key, $def=null) { - // XXX: Do even more input validation/scrubbing? if (array_key_exists($key, $this->args)) { @@ -1481,5 +1534,4 @@ class ApiAction extends Action return $uri; } - }