PageRenderTime 64ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/apiaction.php

https://gitlab.com/windigo-gs/windigos-gnu-social
PHP | 1620 lines | 1186 code | 242 blank | 192 comment | 175 complexity | 3f8849c1cbbadce633e04235dfd3ba79 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, GPL-2.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * StatusNet, the distributed open-source microblogging tool
  4. *
  5. * Base API action
  6. *
  7. * PHP version 5
  8. *
  9. * LICENCE: This program is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU Affero General Public License as published by
  11. * the Free Software Foundation, either version 3 of the License, or
  12. * (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU Affero General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Affero General Public License
  20. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  21. *
  22. * @category API
  23. * @package StatusNet
  24. * @author Craig Andrews <candrews@integralblue.com>
  25. * @author Dan Moore <dan@moore.cx>
  26. * @author Evan Prodromou <evan@status.net>
  27. * @author Jeffery To <jeffery.to@gmail.com>
  28. * @author Toby Inkster <mail@tobyinkster.co.uk>
  29. * @author Zach Copley <zach@status.net>
  30. * @copyright 2009-2010 StatusNet, Inc.
  31. * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
  32. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  33. * @link http://status.net/
  34. */
  35. /* External API usage documentation. Please update when you change how the API works. */
  36. /*! @mainpage StatusNet REST API
  37. @section Introduction
  38. Some explanatory text about the API would be nice.
  39. @section API Methods
  40. @subsection timelinesmethods_sec Timeline Methods
  41. @li @ref publictimeline
  42. @li @ref friendstimeline
  43. @subsection statusmethods_sec Status Methods
  44. @li @ref statusesupdate
  45. @subsection usermethods_sec User Methods
  46. @subsection directmessagemethods_sec Direct Message Methods
  47. @subsection friendshipmethods_sec Friendship Methods
  48. @subsection socialgraphmethods_sec Social Graph Methods
  49. @subsection accountmethods_sec Account Methods
  50. @subsection favoritesmethods_sec Favorites Methods
  51. @subsection blockmethods_sec Block Methods
  52. @subsection oauthmethods_sec OAuth Methods
  53. @subsection helpmethods_sec Help Methods
  54. @subsection groupmethods_sec Group Methods
  55. @page apiroot API Root
  56. The URLs for methods referred to in this API documentation are
  57. relative to the StatusNet API root. The API root is determined by the
  58. site's @b server and @b path variables, which are generally specified
  59. in config.php. For example:
  60. @code
  61. $config['site']['server'] = 'example.org';
  62. $config['site']['path'] = 'statusnet'
  63. @endcode
  64. The pattern for a site's API root is: @c protocol://server/path/api E.g:
  65. @c http://example.org/statusnet/api
  66. The @b path can be empty. In that case the API root would simply be:
  67. @c http://example.org/api
  68. */
  69. if (!defined('STATUSNET')) {
  70. exit(1);
  71. }
  72. class ApiValidationException extends Exception { }
  73. /**
  74. * Contains most of the Twitter-compatible API output functions.
  75. *
  76. * @category API
  77. * @package StatusNet
  78. * @author Craig Andrews <candrews@integralblue.com>
  79. * @author Dan Moore <dan@moore.cx>
  80. * @author Evan Prodromou <evan@status.net>
  81. * @author Jeffery To <jeffery.to@gmail.com>
  82. * @author Toby Inkster <mail@tobyinkster.co.uk>
  83. * @author Zach Copley <zach@status.net>
  84. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  85. * @link http://status.net/
  86. */
  87. class ApiAction extends Action
  88. {
  89. const READ_ONLY = 1;
  90. const READ_WRITE = 2;
  91. var $user = null;
  92. var $auth_user = null;
  93. var $page = null;
  94. var $count = null;
  95. var $max_id = null;
  96. var $since_id = null;
  97. var $source = null;
  98. var $callback = null;
  99. var $access = self::READ_ONLY; // read (default) or read-write
  100. static $reserved_sources = array('web', 'omb', 'ostatus', 'mail', 'xmpp', 'api');
  101. /**
  102. * Initialization.
  103. *
  104. * @param array $args Web and URL arguments
  105. *
  106. * @return boolean false if user doesn't exist
  107. */
  108. protected function prepare(array $args=array())
  109. {
  110. StatusNet::setApi(true); // reduce exception reports to aid in debugging
  111. parent::prepare($args);
  112. $this->format = $this->arg('format');
  113. $this->callback = $this->arg('callback');
  114. $this->page = (int)$this->arg('page', 1);
  115. $this->count = (int)$this->arg('count', 20);
  116. $this->max_id = (int)$this->arg('max_id', 0);
  117. $this->since_id = (int)$this->arg('since_id', 0);
  118. if ($this->arg('since')) {
  119. header('X-StatusNet-Warning: since parameter is disabled; use since_id');
  120. }
  121. $this->source = $this->trimmed('source');
  122. if (empty($this->source) || in_array($this->source, self::$reserved_sources)) {
  123. $this->source = 'api';
  124. }
  125. return true;
  126. }
  127. /**
  128. * Handle a request
  129. *
  130. * @param array $args Arguments from $_REQUEST
  131. *
  132. * @return void
  133. */
  134. protected function handle()
  135. {
  136. header('Access-Control-Allow-Origin: *');
  137. parent::handle();
  138. }
  139. /**
  140. * Overrides XMLOutputter::element to write booleans as strings (true|false).
  141. * See that method's documentation for more info.
  142. *
  143. * @param string $tag Element type or tagname
  144. * @param array $attrs Array of element attributes, as
  145. * key-value pairs
  146. * @param string $content string content of the element
  147. *
  148. * @return void
  149. */
  150. function element($tag, $attrs=null, $content=null)
  151. {
  152. if (is_bool($content)) {
  153. $content = ($content ? 'true' : 'false');
  154. }
  155. return parent::element($tag, $attrs, $content);
  156. }
  157. function twitterUserArray($profile, $get_notice=false)
  158. {
  159. $twitter_user = array();
  160. try {
  161. $user = $profile->getUser();
  162. } catch (NoSuchUserException $e) {
  163. $user = null;
  164. }
  165. $twitter_user['id'] = intval($profile->id);
  166. $twitter_user['name'] = $profile->getBestName();
  167. $twitter_user['screen_name'] = $profile->nickname;
  168. $twitter_user['location'] = ($profile->location) ? $profile->location : null;
  169. $twitter_user['description'] = ($profile->bio) ? $profile->bio : null;
  170. // TODO: avatar url template (example.com/user/avatar?size={x}x{y})
  171. $twitter_user['profile_image_url'] = Avatar::urlByProfile($profile, AVATAR_STREAM_SIZE);
  172. $twitter_user['profile_image_url_https'] = $twitter_user['profile_image_url'];
  173. // START introduced by qvitter API, not necessary for StatusNet API
  174. $twitter_user['profile_image_url_profile_size'] = Avatar::urlByProfile($profile, AVATAR_PROFILE_SIZE);
  175. try {
  176. $avatar = Avatar::getUploaded($profile);
  177. $origurl = $avatar->displayUrl();
  178. } catch (Exception $e) {
  179. $origurl = $twitter_user['profile_image_url_profile_size'];
  180. }
  181. $twitter_user['profile_image_url_original'] = $origurl;
  182. $twitter_user['groups_count'] = $profile->getGroupCount();
  183. foreach (array('linkcolor', 'backgroundcolor') as $key) {
  184. $twitter_user[$key] = Profile_prefs::getConfigData($profile, 'theme', $key);
  185. }
  186. // END introduced by qvitter API, not necessary for StatusNet API
  187. $twitter_user['url'] = ($profile->homepage) ? $profile->homepage : null;
  188. $twitter_user['protected'] = (!empty($user) && $user->private_stream) ? true : false;
  189. $twitter_user['followers_count'] = $profile->subscriberCount();
  190. // Note: some profiles don't have an associated user
  191. $twitter_user['friends_count'] = $profile->subscriptionCount();
  192. $twitter_user['created_at'] = $this->dateTwitter($profile->created);
  193. $twitter_user['favourites_count'] = $profile->faveCount(); // British spelling!
  194. $timezone = 'UTC';
  195. if (!empty($user) && $user->timezone) {
  196. $timezone = $user->timezone;
  197. }
  198. $t = new DateTime;
  199. $t->setTimezone(new DateTimeZone($timezone));
  200. $twitter_user['utc_offset'] = $t->format('Z');
  201. $twitter_user['time_zone'] = $timezone;
  202. $twitter_user['statuses_count'] = $profile->noticeCount();
  203. // Is the requesting user following this user?
  204. $twitter_user['following'] = false;
  205. $twitter_user['statusnet_blocking'] = false;
  206. $twitter_user['notifications'] = false;
  207. if (isset($this->auth_user)) {
  208. $twitter_user['following'] = $this->auth_user->isSubscribed($profile);
  209. $twitter_user['statusnet_blocking'] = $this->auth_user->hasBlocked($profile);
  210. // Notifications on?
  211. $sub = Subscription::pkeyGet(array('subscriber' =>
  212. $this->auth_user->id,
  213. 'subscribed' => $profile->id));
  214. if ($sub) {
  215. $twitter_user['notifications'] = ($sub->jabber || $sub->sms);
  216. }
  217. }
  218. if ($get_notice) {
  219. $notice = $profile->getCurrentNotice();
  220. if ($notice instanceof Notice) {
  221. // don't get user!
  222. $twitter_user['status'] = $this->twitterStatusArray($notice, false);
  223. }
  224. }
  225. // StatusNet-specific
  226. $twitter_user['statusnet_profile_url'] = $profile->profileurl;
  227. return $twitter_user;
  228. }
  229. function twitterStatusArray($notice, $include_user=true)
  230. {
  231. $base = $this->twitterSimpleStatusArray($notice, $include_user);
  232. if (!empty($notice->repeat_of)) {
  233. $original = Notice::getKV('id', $notice->repeat_of);
  234. if (!empty($original)) {
  235. $original_array = $this->twitterSimpleStatusArray($original, $include_user);
  236. $base['retweeted_status'] = $original_array;
  237. }
  238. }
  239. return $base;
  240. }
  241. function twitterSimpleStatusArray($notice, $include_user=true)
  242. {
  243. $profile = $notice->getProfile();
  244. $twitter_status = array();
  245. $twitter_status['text'] = $notice->content;
  246. $twitter_status['truncated'] = false; # Not possible on StatusNet
  247. $twitter_status['created_at'] = $this->dateTwitter($notice->created);
  248. try {
  249. // We could just do $notice->reply_to but maybe the future holds a
  250. // different story for parenting.
  251. $parent = $notice->getParent();
  252. $in_reply_to = $parent->id;
  253. } catch (Exception $e) {
  254. $in_reply_to = null;
  255. }
  256. $twitter_status['in_reply_to_status_id'] = $in_reply_to;
  257. $source = null;
  258. $ns = $notice->getSource();
  259. if ($ns) {
  260. if (!empty($ns->name) && !empty($ns->url)) {
  261. $source = '<a href="'
  262. . htmlspecialchars($ns->url)
  263. . '" rel="nofollow">'
  264. . htmlspecialchars($ns->name)
  265. . '</a>';
  266. } else {
  267. $source = $ns->code;
  268. }
  269. }
  270. $twitter_status['uri'] = $notice->getUri();
  271. $twitter_status['source'] = $source;
  272. $twitter_status['id'] = intval($notice->id);
  273. $replier_profile = null;
  274. if ($notice->reply_to) {
  275. $reply = Notice::getKV(intval($notice->reply_to));
  276. if ($reply) {
  277. $replier_profile = $reply->getProfile();
  278. }
  279. }
  280. $twitter_status['in_reply_to_user_id'] =
  281. ($replier_profile) ? intval($replier_profile->id) : null;
  282. $twitter_status['in_reply_to_screen_name'] =
  283. ($replier_profile) ? $replier_profile->nickname : null;
  284. if (isset($notice->lat) && isset($notice->lon)) {
  285. // This is the format that GeoJSON expects stuff to be in
  286. $twitter_status['geo'] = array('type' => 'Point',
  287. 'coordinates' => array((float) $notice->lat,
  288. (float) $notice->lon));
  289. } else {
  290. $twitter_status['geo'] = null;
  291. }
  292. if (!is_null($this->scoped)) {
  293. $twitter_status['favorited'] = $this->scoped->hasFave($notice);
  294. $twitter_status['repeated'] = $this->scoped->hasRepeated($notice);
  295. } else {
  296. $twitter_status['favorited'] = false;
  297. $twitter_status['repeated'] = false;
  298. }
  299. // Enclosures
  300. $attachments = $notice->attachments();
  301. if (!empty($attachments)) {
  302. $twitter_status['attachments'] = array();
  303. foreach ($attachments as $attachment) {
  304. $enclosure_o=$attachment->getEnclosure();
  305. if ($enclosure_o) {
  306. $enclosure = array();
  307. $enclosure['url'] = $enclosure_o->url;
  308. $enclosure['mimetype'] = $enclosure_o->mimetype;
  309. $enclosure['size'] = $enclosure_o->size;
  310. $twitter_status['attachments'][] = $enclosure;
  311. }
  312. }
  313. }
  314. if ($include_user && $profile) {
  315. // Don't get notice (recursive!)
  316. $twitter_user = $this->twitterUserArray($profile, false);
  317. $twitter_status['user'] = $twitter_user;
  318. }
  319. // StatusNet-specific
  320. $twitter_status['statusnet_html'] = $notice->rendered;
  321. $twitter_status['statusnet_conversation_id'] = intval($notice->conversation);
  322. return $twitter_status;
  323. }
  324. function twitterGroupArray($group)
  325. {
  326. $twitter_group = array();
  327. $twitter_group['id'] = intval($group->id);
  328. $twitter_group['url'] = $group->permalink();
  329. $twitter_group['nickname'] = $group->nickname;
  330. $twitter_group['fullname'] = $group->fullname;
  331. if (isset($this->auth_user)) {
  332. $twitter_group['member'] = $this->auth_user->isMember($group);
  333. $twitter_group['blocked'] = Group_block::isBlocked(
  334. $group,
  335. $this->auth_user->getProfile()
  336. );
  337. }
  338. $twitter_group['admin_count'] = $group->getAdminCount();
  339. $twitter_group['member_count'] = $group->getMemberCount();
  340. $twitter_group['original_logo'] = $group->original_logo;
  341. $twitter_group['homepage_logo'] = $group->homepage_logo;
  342. $twitter_group['stream_logo'] = $group->stream_logo;
  343. $twitter_group['mini_logo'] = $group->mini_logo;
  344. $twitter_group['homepage'] = $group->homepage;
  345. $twitter_group['description'] = $group->description;
  346. $twitter_group['location'] = $group->location;
  347. $twitter_group['created'] = $this->dateTwitter($group->created);
  348. $twitter_group['modified'] = $this->dateTwitter($group->modified);
  349. return $twitter_group;
  350. }
  351. function twitterRssGroupArray($group)
  352. {
  353. $entry = array();
  354. $entry['content']=$group->description;
  355. $entry['title']=$group->nickname;
  356. $entry['link']=$group->permalink();
  357. $entry['published']=common_date_iso8601($group->created);
  358. $entry['updated']==common_date_iso8601($group->modified);
  359. $taguribase = common_config('integration', 'groupuri');
  360. $entry['id'] = "group:$groupuribase:$entry[link]";
  361. $entry['description'] = $entry['content'];
  362. $entry['pubDate'] = common_date_rfc2822($group->created);
  363. $entry['guid'] = $entry['link'];
  364. return $entry;
  365. }
  366. function twitterListArray($list)
  367. {
  368. $profile = Profile::getKV('id', $list->tagger);
  369. $twitter_list = array();
  370. $twitter_list['id'] = $list->id;
  371. $twitter_list['name'] = $list->tag;
  372. $twitter_list['full_name'] = '@'.$profile->nickname.'/'.$list->tag;;
  373. $twitter_list['slug'] = $list->tag;
  374. $twitter_list['description'] = $list->description;
  375. $twitter_list['subscriber_count'] = $list->subscriberCount();
  376. $twitter_list['member_count'] = $list->taggedCount();
  377. $twitter_list['uri'] = $list->getUri();
  378. if (isset($this->auth_user)) {
  379. $twitter_list['following'] = $list->hasSubscriber($this->auth_user);
  380. } else {
  381. $twitter_list['following'] = false;
  382. }
  383. $twitter_list['mode'] = ($list->private) ? 'private' : 'public';
  384. $twitter_list['user'] = $this->twitterUserArray($profile, false);
  385. return $twitter_list;
  386. }
  387. function twitterRssEntryArray($notice)
  388. {
  389. $entry = array();
  390. if (Event::handle('StartRssEntryArray', array($notice, &$entry))) {
  391. $profile = $notice->getProfile();
  392. // We trim() to avoid extraneous whitespace in the output
  393. $entry['content'] = common_xml_safe_str(trim($notice->rendered));
  394. $entry['title'] = $profile->nickname . ': ' . common_xml_safe_str(trim($notice->content));
  395. $entry['link'] = common_local_url('shownotice', array('notice' => $notice->id));
  396. $entry['published'] = common_date_iso8601($notice->created);
  397. $taguribase = TagURI::base();
  398. $entry['id'] = "tag:$taguribase:$entry[link]";
  399. $entry['updated'] = $entry['published'];
  400. $entry['author'] = $profile->getBestName();
  401. // Enclosures
  402. $attachments = $notice->attachments();
  403. $enclosures = array();
  404. foreach ($attachments as $attachment) {
  405. $enclosure_o=$attachment->getEnclosure();
  406. if ($enclosure_o) {
  407. $enclosure = array();
  408. $enclosure['url'] = $enclosure_o->url;
  409. $enclosure['mimetype'] = $enclosure_o->mimetype;
  410. $enclosure['size'] = $enclosure_o->size;
  411. $enclosures[] = $enclosure;
  412. }
  413. }
  414. if (!empty($enclosures)) {
  415. $entry['enclosures'] = $enclosures;
  416. }
  417. // Tags/Categories
  418. $tag = new Notice_tag();
  419. $tag->notice_id = $notice->id;
  420. if ($tag->find()) {
  421. $entry['tags']=array();
  422. while ($tag->fetch()) {
  423. $entry['tags'][]=$tag->tag;
  424. }
  425. }
  426. $tag->free();
  427. // RSS Item specific
  428. $entry['description'] = $entry['content'];
  429. $entry['pubDate'] = common_date_rfc2822($notice->created);
  430. $entry['guid'] = $entry['link'];
  431. if (isset($notice->lat) && isset($notice->lon)) {
  432. // This is the format that GeoJSON expects stuff to be in.
  433. // showGeoRSS() below uses it for XML output, so we reuse it
  434. $entry['geo'] = array('type' => 'Point',
  435. 'coordinates' => array((float) $notice->lat,
  436. (float) $notice->lon));
  437. } else {
  438. $entry['geo'] = null;
  439. }
  440. Event::handle('EndRssEntryArray', array($notice, &$entry));
  441. }
  442. return $entry;
  443. }
  444. function twitterRelationshipArray($source, $target)
  445. {
  446. $relationship = array();
  447. $relationship['source'] =
  448. $this->relationshipDetailsArray($source, $target);
  449. $relationship['target'] =
  450. $this->relationshipDetailsArray($target, $source);
  451. return array('relationship' => $relationship);
  452. }
  453. function relationshipDetailsArray($source, $target)
  454. {
  455. $details = array();
  456. $details['screen_name'] = $source->nickname;
  457. $details['followed_by'] = $target->isSubscribed($source);
  458. $details['following'] = $source->isSubscribed($target);
  459. $notifications = false;
  460. if ($source->isSubscribed($target)) {
  461. $sub = Subscription::pkeyGet(array('subscriber' =>
  462. $source->id, 'subscribed' => $target->id));
  463. if (!empty($sub)) {
  464. $notifications = ($sub->jabber || $sub->sms);
  465. }
  466. }
  467. $details['notifications_enabled'] = $notifications;
  468. $details['blocking'] = $source->hasBlocked($target);
  469. $details['id'] = intval($source->id);
  470. return $details;
  471. }
  472. function showTwitterXmlRelationship($relationship)
  473. {
  474. $this->elementStart('relationship');
  475. foreach($relationship as $element => $value) {
  476. if ($element == 'source' || $element == 'target') {
  477. $this->elementStart($element);
  478. $this->showXmlRelationshipDetails($value);
  479. $this->elementEnd($element);
  480. }
  481. }
  482. $this->elementEnd('relationship');
  483. }
  484. function showXmlRelationshipDetails($details)
  485. {
  486. foreach($details as $element => $value) {
  487. $this->element($element, null, $value);
  488. }
  489. }
  490. function showTwitterXmlStatus($twitter_status, $tag='status', $namespaces=false)
  491. {
  492. $attrs = array();
  493. if ($namespaces) {
  494. $attrs['xmlns:statusnet'] = 'http://status.net/schema/api/1/';
  495. }
  496. $this->elementStart($tag, $attrs);
  497. foreach($twitter_status as $element => $value) {
  498. switch ($element) {
  499. case 'user':
  500. $this->showTwitterXmlUser($twitter_status['user']);
  501. break;
  502. case 'text':
  503. $this->element($element, null, common_xml_safe_str($value));
  504. break;
  505. case 'attachments':
  506. $this->showXmlAttachments($twitter_status['attachments']);
  507. break;
  508. case 'geo':
  509. $this->showGeoXML($value);
  510. break;
  511. case 'retweeted_status':
  512. $this->showTwitterXmlStatus($value, 'retweeted_status');
  513. break;
  514. default:
  515. if (strncmp($element, 'statusnet_', 10) == 0) {
  516. $this->element('statusnet:'.substr($element, 10), null, $value);
  517. } else {
  518. $this->element($element, null, $value);
  519. }
  520. }
  521. }
  522. $this->elementEnd($tag);
  523. }
  524. function showTwitterXmlGroup($twitter_group)
  525. {
  526. $this->elementStart('group');
  527. foreach($twitter_group as $element => $value) {
  528. $this->element($element, null, $value);
  529. }
  530. $this->elementEnd('group');
  531. }
  532. function showTwitterXmlList($twitter_list)
  533. {
  534. $this->elementStart('list');
  535. foreach($twitter_list as $element => $value) {
  536. if($element == 'user') {
  537. $this->showTwitterXmlUser($value, 'user');
  538. }
  539. else {
  540. $this->element($element, null, $value);
  541. }
  542. }
  543. $this->elementEnd('list');
  544. }
  545. function showTwitterXmlUser($twitter_user, $role='user', $namespaces=false)
  546. {
  547. $attrs = array();
  548. if ($namespaces) {
  549. $attrs['xmlns:statusnet'] = 'http://status.net/schema/api/1/';
  550. }
  551. $this->elementStart($role, $attrs);
  552. foreach($twitter_user as $element => $value) {
  553. if ($element == 'status') {
  554. $this->showTwitterXmlStatus($twitter_user['status']);
  555. } else if (strncmp($element, 'statusnet_', 10) == 0) {
  556. $this->element('statusnet:'.substr($element, 10), null, $value);
  557. } else {
  558. $this->element($element, null, $value);
  559. }
  560. }
  561. $this->elementEnd($role);
  562. }
  563. function showXmlAttachments($attachments) {
  564. if (!empty($attachments)) {
  565. $this->elementStart('attachments', array('type' => 'array'));
  566. foreach ($attachments as $attachment) {
  567. $attrs = array();
  568. $attrs['url'] = $attachment['url'];
  569. $attrs['mimetype'] = $attachment['mimetype'];
  570. $attrs['size'] = $attachment['size'];
  571. $this->element('enclosure', $attrs, '');
  572. }
  573. $this->elementEnd('attachments');
  574. }
  575. }
  576. function showGeoXML($geo)
  577. {
  578. if (empty($geo)) {
  579. // empty geo element
  580. $this->element('geo');
  581. } else {
  582. $this->elementStart('geo', array('xmlns:georss' => 'http://www.georss.org/georss'));
  583. $this->element('georss:point', null, $geo['coordinates'][0] . ' ' . $geo['coordinates'][1]);
  584. $this->elementEnd('geo');
  585. }
  586. }
  587. function showGeoRSS($geo)
  588. {
  589. if (!empty($geo)) {
  590. $this->element(
  591. 'georss:point',
  592. null,
  593. $geo['coordinates'][0] . ' ' . $geo['coordinates'][1]
  594. );
  595. }
  596. }
  597. function showTwitterRssItem($entry)
  598. {
  599. $this->elementStart('item');
  600. $this->element('title', null, $entry['title']);
  601. $this->element('description', null, $entry['description']);
  602. $this->element('pubDate', null, $entry['pubDate']);
  603. $this->element('guid', null, $entry['guid']);
  604. $this->element('link', null, $entry['link']);
  605. // RSS only supports 1 enclosure per item
  606. if(array_key_exists('enclosures', $entry) and !empty($entry['enclosures'])){
  607. $enclosure = $entry['enclosures'][0];
  608. $this->element('enclosure', array('url'=>$enclosure['url'],'type'=>$enclosure['mimetype'],'length'=>$enclosure['size']), null);
  609. }
  610. if(array_key_exists('tags', $entry)){
  611. foreach($entry['tags'] as $tag){
  612. $this->element('category', null,$tag);
  613. }
  614. }
  615. $this->showGeoRSS($entry['geo']);
  616. $this->elementEnd('item');
  617. }
  618. function showJsonObjects($objects)
  619. {
  620. print(json_encode($objects));
  621. }
  622. function showSingleXmlStatus($notice)
  623. {
  624. $this->initDocument('xml');
  625. $twitter_status = $this->twitterStatusArray($notice);
  626. $this->showTwitterXmlStatus($twitter_status, 'status', true);
  627. $this->endDocument('xml');
  628. }
  629. function showSingleAtomStatus($notice)
  630. {
  631. header('Content-Type: application/atom+xml; charset=utf-8');
  632. print $notice->asAtomEntry(true, true, true, $this->auth_user);
  633. }
  634. function show_single_json_status($notice)
  635. {
  636. $this->initDocument('json');
  637. $status = $this->twitterStatusArray($notice);
  638. $this->showJsonObjects($status);
  639. $this->endDocument('json');
  640. }
  641. function showXmlTimeline($notice)
  642. {
  643. $this->initDocument('xml');
  644. $this->elementStart('statuses', array('type' => 'array',
  645. 'xmlns:statusnet' => 'http://status.net/schema/api/1/'));
  646. if (is_array($notice)) {
  647. $notice = new ArrayWrapper($notice);
  648. }
  649. while ($notice->fetch()) {
  650. try {
  651. $twitter_status = $this->twitterStatusArray($notice);
  652. $this->showTwitterXmlStatus($twitter_status);
  653. } catch (Exception $e) {
  654. common_log(LOG_ERR, $e->getMessage());
  655. continue;
  656. }
  657. }
  658. $this->elementEnd('statuses');
  659. $this->endDocument('xml');
  660. }
  661. function showRssTimeline($notice, $title, $link, $subtitle, $suplink = null, $logo = null, $self = null)
  662. {
  663. $this->initDocument('rss');
  664. $this->element('title', null, $title);
  665. $this->element('link', null, $link);
  666. if (!is_null($self)) {
  667. $this->element(
  668. 'atom:link',
  669. array(
  670. 'type' => 'application/rss+xml',
  671. 'href' => $self,
  672. 'rel' => 'self'
  673. )
  674. );
  675. }
  676. if (!is_null($suplink)) {
  677. // For FriendFeed's SUP protocol
  678. $this->element('link', array('xmlns' => 'http://www.w3.org/2005/Atom',
  679. 'rel' => 'http://api.friendfeed.com/2008/03#sup',
  680. 'href' => $suplink,
  681. 'type' => 'application/json'));
  682. }
  683. if (!is_null($logo)) {
  684. $this->elementStart('image');
  685. $this->element('link', null, $link);
  686. $this->element('title', null, $title);
  687. $this->element('url', null, $logo);
  688. $this->elementEnd('image');
  689. }
  690. $this->element('description', null, $subtitle);
  691. $this->element('language', null, 'en-us');
  692. $this->element('ttl', null, '40');
  693. if (is_array($notice)) {
  694. $notice = new ArrayWrapper($notice);
  695. }
  696. while ($notice->fetch()) {
  697. try {
  698. $entry = $this->twitterRssEntryArray($notice);
  699. $this->showTwitterRssItem($entry);
  700. } catch (Exception $e) {
  701. common_log(LOG_ERR, $e->getMessage());
  702. // continue on exceptions
  703. }
  704. }
  705. $this->endTwitterRss();
  706. }
  707. function showAtomTimeline($notice, $title, $id, $link, $subtitle=null, $suplink=null, $selfuri=null, $logo=null)
  708. {
  709. $this->initDocument('atom');
  710. $this->element('title', null, $title);
  711. $this->element('id', null, $id);
  712. $this->element('link', array('href' => $link, 'rel' => 'alternate', 'type' => 'text/html'), null);
  713. if (!is_null($logo)) {
  714. $this->element('logo',null,$logo);
  715. }
  716. if (!is_null($suplink)) {
  717. // For FriendFeed's SUP protocol
  718. $this->element('link', array('rel' => 'http://api.friendfeed.com/2008/03#sup',
  719. 'href' => $suplink,
  720. 'type' => 'application/json'));
  721. }
  722. if (!is_null($selfuri)) {
  723. $this->element('link', array('href' => $selfuri,
  724. 'rel' => 'self', 'type' => 'application/atom+xml'), null);
  725. }
  726. $this->element('updated', null, common_date_iso8601('now'));
  727. $this->element('subtitle', null, $subtitle);
  728. if (is_array($notice)) {
  729. $notice = new ArrayWrapper($notice);
  730. }
  731. while ($notice->fetch()) {
  732. try {
  733. $this->raw($notice->asAtomEntry());
  734. } catch (Exception $e) {
  735. common_log(LOG_ERR, $e->getMessage());
  736. continue;
  737. }
  738. }
  739. $this->endDocument('atom');
  740. }
  741. function showRssGroups($group, $title, $link, $subtitle)
  742. {
  743. $this->initDocument('rss');
  744. $this->element('title', null, $title);
  745. $this->element('link', null, $link);
  746. $this->element('description', null, $subtitle);
  747. $this->element('language', null, 'en-us');
  748. $this->element('ttl', null, '40');
  749. if (is_array($group)) {
  750. foreach ($group as $g) {
  751. $twitter_group = $this->twitterRssGroupArray($g);
  752. $this->showTwitterRssItem($twitter_group);
  753. }
  754. } else {
  755. while ($group->fetch()) {
  756. $twitter_group = $this->twitterRssGroupArray($group);
  757. $this->showTwitterRssItem($twitter_group);
  758. }
  759. }
  760. $this->endTwitterRss();
  761. }
  762. function showTwitterAtomEntry($entry)
  763. {
  764. $this->elementStart('entry');
  765. $this->element('title', null, common_xml_safe_str($entry['title']));
  766. $this->element(
  767. 'content',
  768. array('type' => 'html'),
  769. common_xml_safe_str($entry['content'])
  770. );
  771. $this->element('id', null, $entry['id']);
  772. $this->element('published', null, $entry['published']);
  773. $this->element('updated', null, $entry['updated']);
  774. $this->element('link', array('type' => 'text/html',
  775. 'href' => $entry['link'],
  776. 'rel' => 'alternate'));
  777. $this->element('link', array('type' => $entry['avatar-type'],
  778. 'href' => $entry['avatar'],
  779. 'rel' => 'image'));
  780. $this->elementStart('author');
  781. $this->element('name', null, $entry['author-name']);
  782. $this->element('uri', null, $entry['author-uri']);
  783. $this->elementEnd('author');
  784. $this->elementEnd('entry');
  785. }
  786. function showXmlDirectMessage($dm, $namespaces=false)
  787. {
  788. $attrs = array();
  789. if ($namespaces) {
  790. $attrs['xmlns:statusnet'] = 'http://status.net/schema/api/1/';
  791. }
  792. $this->elementStart('direct_message', $attrs);
  793. foreach($dm as $element => $value) {
  794. switch ($element) {
  795. case 'sender':
  796. case 'recipient':
  797. $this->showTwitterXmlUser($value, $element);
  798. break;
  799. case 'text':
  800. $this->element($element, null, common_xml_safe_str($value));
  801. break;
  802. default:
  803. $this->element($element, null, $value);
  804. break;
  805. }
  806. }
  807. $this->elementEnd('direct_message');
  808. }
  809. function directMessageArray($message)
  810. {
  811. $dmsg = array();
  812. $from_profile = $message->getFrom();
  813. $to_profile = $message->getTo();
  814. $dmsg['id'] = intval($message->id);
  815. $dmsg['sender_id'] = intval($from_profile->id);
  816. $dmsg['text'] = trim($message->content);
  817. $dmsg['recipient_id'] = intval($to_profile->id);
  818. $dmsg['created_at'] = $this->dateTwitter($message->created);
  819. $dmsg['sender_screen_name'] = $from_profile->nickname;
  820. $dmsg['recipient_screen_name'] = $to_profile->nickname;
  821. $dmsg['sender'] = $this->twitterUserArray($from_profile, false);
  822. $dmsg['recipient'] = $this->twitterUserArray($to_profile, false);
  823. return $dmsg;
  824. }
  825. function rssDirectMessageArray($message)
  826. {
  827. $entry = array();
  828. $from = $message->getFrom();
  829. $entry['title'] = sprintf('Message from %1$s to %2$s',
  830. $from->nickname, $message->getTo()->nickname);
  831. $entry['content'] = common_xml_safe_str($message->rendered);
  832. $entry['link'] = common_local_url('showmessage', array('message' => $message->id));
  833. $entry['published'] = common_date_iso8601($message->created);
  834. $taguribase = TagURI::base();
  835. $entry['id'] = "tag:$taguribase:$entry[link]";
  836. $entry['updated'] = $entry['published'];
  837. $entry['author-name'] = $from->getBestName();
  838. $entry['author-uri'] = $from->homepage;
  839. $entry['avatar'] = $from->avatarUrl(AVATAR_STREAM_SIZE);
  840. try {
  841. $avatar = $from->getAvatar(AVATAR_STREAM_SIZE);
  842. $entry['avatar-type'] = $avatar->mediatype;
  843. } catch (Exception $e) {
  844. $entry['avatar-type'] = 'image/png';
  845. }
  846. // RSS item specific
  847. $entry['description'] = $entry['content'];
  848. $entry['pubDate'] = common_date_rfc2822($message->created);
  849. $entry['guid'] = $entry['link'];
  850. return $entry;
  851. }
  852. function showSingleXmlDirectMessage($message)
  853. {
  854. $this->initDocument('xml');
  855. $dmsg = $this->directMessageArray($message);
  856. $this->showXmlDirectMessage($dmsg, true);
  857. $this->endDocument('xml');
  858. }
  859. function showSingleJsonDirectMessage($message)
  860. {
  861. $this->initDocument('json');
  862. $dmsg = $this->directMessageArray($message);
  863. $this->showJsonObjects($dmsg);
  864. $this->endDocument('json');
  865. }
  866. function showAtomGroups($group, $title, $id, $link, $subtitle=null, $selfuri=null)
  867. {
  868. $this->initDocument('atom');
  869. $this->element('title', null, common_xml_safe_str($title));
  870. $this->element('id', null, $id);
  871. $this->element('link', array('href' => $link, 'rel' => 'alternate', 'type' => 'text/html'), null);
  872. if (!is_null($selfuri)) {
  873. $this->element('link', array('href' => $selfuri,
  874. 'rel' => 'self', 'type' => 'application/atom+xml'), null);
  875. }
  876. $this->element('updated', null, common_date_iso8601('now'));
  877. $this->element('subtitle', null, common_xml_safe_str($subtitle));
  878. if (is_array($group)) {
  879. foreach ($group as $g) {
  880. $this->raw($g->asAtomEntry());
  881. }
  882. } else {
  883. while ($group->fetch()) {
  884. $this->raw($group->asAtomEntry());
  885. }
  886. }
  887. $this->endDocument('atom');
  888. }
  889. function showJsonTimeline($notice)
  890. {
  891. $this->initDocument('json');
  892. $statuses = array();
  893. if (is_array($notice)) {
  894. $notice = new ArrayWrapper($notice);
  895. }
  896. while ($notice->fetch()) {
  897. try {
  898. $twitter_status = $this->twitterStatusArray($notice);
  899. array_push($statuses, $twitter_status);
  900. } catch (Exception $e) {
  901. common_log(LOG_ERR, $e->getMessage());
  902. continue;
  903. }
  904. }
  905. $this->showJsonObjects($statuses);
  906. $this->endDocument('json');
  907. }
  908. function showJsonGroups($group)
  909. {
  910. $this->initDocument('json');
  911. $groups = array();
  912. if (is_array($group)) {
  913. foreach ($group as $g) {
  914. $twitter_group = $this->twitterGroupArray($g);
  915. array_push($groups, $twitter_group);
  916. }
  917. } else {
  918. while ($group->fetch()) {
  919. $twitter_group = $this->twitterGroupArray($group);
  920. array_push($groups, $twitter_group);
  921. }
  922. }
  923. $this->showJsonObjects($groups);
  924. $this->endDocument('json');
  925. }
  926. function showXmlGroups($group)
  927. {
  928. $this->initDocument('xml');
  929. $this->elementStart('groups', array('type' => 'array'));
  930. if (is_array($group)) {
  931. foreach ($group as $g) {
  932. $twitter_group = $this->twitterGroupArray($g);
  933. $this->showTwitterXmlGroup($twitter_group);
  934. }
  935. } else {
  936. while ($group->fetch()) {
  937. $twitter_group = $this->twitterGroupArray($group);
  938. $this->showTwitterXmlGroup($twitter_group);
  939. }
  940. }
  941. $this->elementEnd('groups');
  942. $this->endDocument('xml');
  943. }
  944. function showXmlLists($list, $next_cursor=0, $prev_cursor=0)
  945. {
  946. $this->initDocument('xml');
  947. $this->elementStart('lists_list');
  948. $this->elementStart('lists', array('type' => 'array'));
  949. if (is_array($list)) {
  950. foreach ($list as $l) {
  951. $twitter_list = $this->twitterListArray($l);
  952. $this->showTwitterXmlList($twitter_list);
  953. }
  954. } else {
  955. while ($list->fetch()) {
  956. $twitter_list = $this->twitterListArray($list);
  957. $this->showTwitterXmlList($twitter_list);
  958. }
  959. }
  960. $this->elementEnd('lists');
  961. $this->element('next_cursor', null, $next_cursor);
  962. $this->element('previous_cursor', null, $prev_cursor);
  963. $this->elementEnd('lists_list');
  964. $this->endDocument('xml');
  965. }
  966. function showJsonLists($list, $next_cursor=0, $prev_cursor=0)
  967. {
  968. $this->initDocument('json');
  969. $lists = array();
  970. if (is_array($list)) {
  971. foreach ($list as $l) {
  972. $twitter_list = $this->twitterListArray($l);
  973. array_push($lists, $twitter_list);
  974. }
  975. } else {
  976. while ($list->fetch()) {
  977. $twitter_list = $this->twitterListArray($list);
  978. array_push($lists, $twitter_list);
  979. }
  980. }
  981. $lists_list = array(
  982. 'lists' => $lists,
  983. 'next_cursor' => $next_cursor,
  984. 'next_cursor_str' => strval($next_cursor),
  985. 'previous_cursor' => $prev_cursor,
  986. 'previous_cursor_str' => strval($prev_cursor)
  987. );
  988. $this->showJsonObjects($lists_list);
  989. $this->endDocument('json');
  990. }
  991. function showTwitterXmlUsers($user)
  992. {
  993. $this->initDocument('xml');
  994. $this->elementStart('users', array('type' => 'array',
  995. 'xmlns:statusnet' => 'http://status.net/schema/api/1/'));
  996. if (is_array($user)) {
  997. foreach ($user as $u) {
  998. $twitter_user = $this->twitterUserArray($u);
  999. $this->showTwitterXmlUser($twitter_user);
  1000. }
  1001. } else {
  1002. while ($user->fetch()) {
  1003. $twitter_user = $this->twitterUserArray($user);
  1004. $this->showTwitterXmlUser($twitter_user);
  1005. }
  1006. }
  1007. $this->elementEnd('users');
  1008. $this->endDocument('xml');
  1009. }
  1010. function showJsonUsers($user)
  1011. {
  1012. $this->initDocument('json');
  1013. $users = array();
  1014. if (is_array($user)) {
  1015. foreach ($user as $u) {
  1016. $twitter_user = $this->twitterUserArray($u);
  1017. array_push($users, $twitter_user);
  1018. }
  1019. } else {
  1020. while ($user->fetch()) {
  1021. $twitter_user = $this->twitterUserArray($user);
  1022. array_push($users, $twitter_user);
  1023. }
  1024. }
  1025. $this->showJsonObjects($users);
  1026. $this->endDocument('json');
  1027. }
  1028. function showSingleJsonGroup($group)
  1029. {
  1030. $this->initDocument('json');
  1031. $twitter_group = $this->twitterGroupArray($group);
  1032. $this->showJsonObjects($twitter_group);
  1033. $this->endDocument('json');
  1034. }
  1035. function showSingleXmlGroup($group)
  1036. {
  1037. $this->initDocument('xml');
  1038. $twitter_group = $this->twitterGroupArray($group);
  1039. $this->showTwitterXmlGroup($twitter_group);
  1040. $this->endDocument('xml');
  1041. }
  1042. function showSingleJsonList($list)
  1043. {
  1044. $this->initDocument('json');
  1045. $twitter_list = $this->twitterListArray($list);
  1046. $this->showJsonObjects($twitter_list);
  1047. $this->endDocument('json');
  1048. }
  1049. function showSingleXmlList($list)
  1050. {
  1051. $this->initDocument('xml');
  1052. $twitter_list = $this->twitterListArray($list);
  1053. $this->showTwitterXmlList($twitter_list);
  1054. $this->endDocument('xml');
  1055. }
  1056. function dateTwitter($dt)
  1057. {
  1058. $dateStr = date('d F Y H:i:s', strtotime($dt));
  1059. $d = new DateTime($dateStr, new DateTimeZone('UTC'));
  1060. $d->setTimezone(new DateTimeZone(common_timezone()));
  1061. return $d->format('D M d H:i:s O Y');
  1062. }
  1063. function initDocument($type='xml')
  1064. {
  1065. switch ($type) {
  1066. case 'xml':
  1067. header('Content-Type: application/xml; charset=utf-8');
  1068. $this->startXML();
  1069. break;
  1070. case 'json':
  1071. header('Content-Type: application/json; charset=utf-8');
  1072. // Check for JSONP callback
  1073. if (isset($this->callback)) {
  1074. print $this->callback . '(';
  1075. }
  1076. break;
  1077. case 'rss':
  1078. header("Content-Type: application/rss+xml; charset=utf-8");
  1079. $this->initTwitterRss();
  1080. break;
  1081. case 'atom':
  1082. header('Content-Type: application/atom+xml; charset=utf-8');
  1083. $this->initTwitterAtom();
  1084. break;
  1085. default:
  1086. // TRANS: Client error on an API request with an unsupported data format.
  1087. $this->clientError(_('Not a supported data format.'));
  1088. }
  1089. return;
  1090. }
  1091. function endDocument($type='xml')
  1092. {
  1093. switch ($type) {
  1094. case 'xml':
  1095. $this->endXML();
  1096. break;
  1097. case 'json':
  1098. // Check for JSONP callback
  1099. if (isset($this->callback)) {
  1100. print ')';
  1101. }
  1102. break;
  1103. case 'rss':
  1104. $this->endTwitterRss();
  1105. break;
  1106. case 'atom':
  1107. $this->endTwitterRss();
  1108. break;
  1109. default:
  1110. // TRANS: Client error on an API request with an unsupported data format.
  1111. $this->clientError(_('Not a supported data format.'));
  1112. }
  1113. return;
  1114. }
  1115. function initTwitterRss()
  1116. {
  1117. $this->startXML();
  1118. $this->elementStart(
  1119. 'rss',
  1120. array(
  1121. 'version' => '2.0',
  1122. 'xmlns:atom' => 'http://www.w3.org/2005/Atom',
  1123. 'xmlns:georss' => 'http://www.georss.org/georss'
  1124. )
  1125. );
  1126. $this->elementStart('channel');
  1127. Event::handle('StartApiRss', array($this));
  1128. }
  1129. function endTwitterRss()
  1130. {
  1131. $this->elementEnd('channel');
  1132. $this->elementEnd('rss');
  1133. $this->endXML();
  1134. }
  1135. function initTwitterAtom()
  1136. {
  1137. $this->startXML();
  1138. // FIXME: don't hardcode the language here!
  1139. $this->elementStart('feed', array('xmlns' => 'http://www.w3.org/2005/Atom',
  1140. 'xml:lang' => 'en-US',
  1141. 'xmlns:thr' => 'http://purl.org/syndication/thread/1.0'));
  1142. }
  1143. function endTwitterAtom()
  1144. {
  1145. $this->elementEnd('feed');
  1146. $this->endXML();
  1147. }
  1148. function showProfile($profile, $content_type='xml', $notice=null, $includeStatuses=true)
  1149. {
  1150. $profile_array = $this->twitterUserArray($profile, $includeStatuses);
  1151. switch ($content_type) {
  1152. case 'xml':
  1153. $this->showTwitterXmlUser($profile_array);
  1154. break;
  1155. case 'json':
  1156. $this->showJsonObjects($profile_array);
  1157. break;
  1158. default:
  1159. // TRANS: Client error on an API request with an unsupported data format.
  1160. $this->clientError(_('Not a supported data format.'));
  1161. }
  1162. return;
  1163. }
  1164. private static function is_decimal($str)
  1165. {
  1166. return preg_match('/^[0-9]+$/', $str);
  1167. }
  1168. function getTargetUser($id)
  1169. {
  1170. if (empty($id)) {
  1171. // Twitter supports these other ways of passing the user ID
  1172. if (self::is_decimal($this->arg('id'))) {
  1173. return User::getKV($this->arg('id'));
  1174. } else if ($this->arg('id')) {
  1175. $nickname = common_canonical_nickname($this->arg('id'));
  1176. return User::getKV('nickname', $nickname);
  1177. } else if ($this->arg('user_id')) {
  1178. // This is to ensure that a non-numeric user_id still
  1179. // overrides screen_name even if it doesn't get used
  1180. if (self::is_decimal($this->arg('user_id'))) {
  1181. return User::getKV('id', $this->arg('user_id'));
  1182. }
  1183. } else if ($this->arg('screen_name')) {
  1184. $nickname = common_canonical_nickname($this->arg('screen_name'));
  1185. return User::getKV('nickname', $nickname);
  1186. } else {
  1187. // Fall back to trying the currently authenticated user
  1188. return $this->auth_user;
  1189. }
  1190. } else if (self::is_decimal($id)) {
  1191. return User::getKV($id);
  1192. } else {
  1193. $nickname = common_canonical_nickname($id);
  1194. return User::getKV('nickname', $nickname);
  1195. }
  1196. }
  1197. function getTargetProfile($id)
  1198. {
  1199. if (empty($id)) {
  1200. // Twitter supports these other ways of passing the user ID
  1201. if (self::is_decimal($this->arg('id'))) {
  1202. return Profile::getKV($this->arg('id'));
  1203. } else if ($this->arg('id')) {
  1204. // Screen names currently can only uniquely identify a local user.
  1205. $nickname = common_canonical_nickname($this->arg('id'));
  1206. $user = User::getKV('nickname', $nickname);
  1207. return $user ? $user->getProfile() : null;
  1208. } else if ($this->arg('user_id')) {
  1209. // This is to ensure that a non-numeric user_id still
  1210. // overrides screen_name even if it doesn't get used
  1211. if (self::is_decimal($this->arg('user_id'))) {
  1212. return Profile::getKV('id', $this->arg('user_id'));
  1213. }
  1214. } else if ($this->arg('screen_name')) {
  1215. $nickname = common_canonical_nickname($this->arg('screen_name'));
  1216. $user = User::getKV('nickname', $nickname);
  1217. return $user instanceof User ? $user->getProfile() : null;
  1218. } else {
  1219. // Fall back to trying the currently authenticated user
  1220. return $this->scoped;
  1221. }
  1222. } else if (self::is_decimal($id)) {
  1223. return Profile::getKV($id);
  1224. } else {
  1225. $nickname = common_canonical_nickname($id);
  1226. $user = User::getKV('nickname', $nickname);
  1227. return $user ? $user->getProfile() : null;
  1228. }
  1229. }
  1230. function getTargetGroup($id)
  1231. {
  1232. if (empty($id)) {
  1233. if (self::is_decimal($this->arg('id'))) {
  1234. return User_group::getKV('id', $this->arg('id'));
  1235. } else if ($this->arg('id')) {
  1236. return User_group::getForNickname($this->arg('id'));
  1237. } else if ($this->arg('group_id')) {
  1238. // This is to ensure that a non-numeric group_id still
  1239. // overrides group_name even if it doesn't get used
  1240. if (self::is_decimal($this->arg('group_id'))) {
  1241. return User_group::getKV('id', $this->arg('group_id'));
  1242. }
  1243. } else if ($this->arg('group_name')) {
  1244. return User_group::getForNickname($this->arg('group_name'));
  1245. }
  1246. } else if (self::is_decimal($id)) {

Large files files are truncated, but you can click here to view the full file