PageRenderTime 48ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/webapp/_lib/controller/class.PostAPIController.php

https://github.com/unruthless/ThinkUp
PHP | 809 lines | 448 code | 56 blank | 305 comment | 118 complexity | 039a3c18549b42627fbda57ed1b149ec MD5 | raw file
Possible License(s): GPL-3.0, LGPL-2.1
  1. <?php
  2. /**
  3. *
  4. * ThinkUp/webapp/_lib/controller/class.PostAPIController.php
  5. *
  6. * Copyright (c) 2011 Sam Rose
  7. *
  8. * LICENSE:
  9. *
  10. * This file is part of ThinkUp (http://thinkupapp.com).
  11. *
  12. * ThinkUp is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
  13. * License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any
  14. * later version.
  15. *
  16. * ThinkUp is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
  17. * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  18. * details.
  19. *
  20. * You should have received a copy of the GNU General Public License along with ThinkUp. If not, see
  21. * <http://www.gnu.org/licenses/>.
  22. *
  23. *
  24. * Post API Controller
  25. *
  26. * @license http://www.gnu.org/licenses/gpl.html
  27. * @copyright 2011 Sam Rose
  28. * @author Sam Rose <samwho@lbak.co.uk>
  29. *
  30. */
  31. class PostAPIController extends ThinkUpController {
  32. /**
  33. * The network to query. Defaults to twitter.
  34. * @var str
  35. */
  36. public $network = 'twitter';
  37. /**
  38. * The user ID to use. No default value. In requests that require user data, either this or username must be set.
  39. * @var int
  40. */
  41. public $user_id;
  42. /**
  43. * The username to use. No default value. In requests that require user data, either this or user ID must be set.
  44. * @var str
  45. */
  46. public $username;
  47. /**
  48. * The post ID to use. No default value.
  49. * @var int
  50. */
  51. public $post_id;
  52. /**
  53. * The API call type to make. Defaults to "post".
  54. * @var str
  55. */
  56. public $type = 'post';
  57. /**
  58. * The number of results to return. Defaults to 20.
  59. * @var int
  60. */
  61. public $count = 20;
  62. /**
  63. * The page of results to return. Defaults to 1 (the first page).
  64. * @var int
  65. */
  66. public $page = 1;
  67. /**
  68. * What to order the results by. Does not work on all calls. Defaults to "default". Different calls handle this
  69. * value differently.
  70. * @var str
  71. */
  72. public $order_by = 'default';
  73. /**
  74. * The direction to order the results in. Defaults to DESC for descending order.
  75. * @var str DESC or ASC
  76. */
  77. public $direction = 'DESC';
  78. /**
  79. * In time range API calls, this is the starting date. Can be a Unix timestamp or a valid time string. Defaults to
  80. * 0 which represents midnight on Jan 1st 1970.
  81. * @var mixed
  82. */
  83. public $from = 0;
  84. /**
  85. * In time range API calls, this is the end date. Can be a Unix timestamp or a valid time string.
  86. * @var mixed
  87. */
  88. public $until;
  89. /**
  90. * In some API calls, the reply/retweet distance is returned as an integer. This variable defines whether that
  91. * variable is the distance in miles ("mi") or kilometers ("km"). Defaults to "km".
  92. * @var str
  93. */
  94. public $unit = 'km';
  95. /**
  96. * Whether or not to include replies to each tweet. Works on all API calls. Defaults to false.
  97. * @var bool
  98. */
  99. public $include_replies = false;
  100. /**
  101. * Whether or not to include tweet entities (links, mentions, hashtags). Defaults to false.
  102. * @var bool
  103. */
  104. public $include_entities = false;
  105. /**
  106. * Whether or not to trim the user to just the user ID. Defaults to false.
  107. * @var bool
  108. */
  109. public $trim_user = false;
  110. /**
  111. * Some API calls (user mentions) will, by default, not include retweets as mentions. Set this to true if you wish
  112. * to include retweets as mentions.
  113. * @var bool
  114. */
  115. public $include_rts = false;
  116. /**
  117. * A User object set when either the user_id or username variables are set. If you are using User data at any point
  118. * in this class, you should use this object.
  119. * @var User
  120. */
  121. private $user;
  122. /**
  123. *
  124. * @var PostDAO
  125. */
  126. private $post_dao;
  127. /**
  128. *
  129. * @var UserDAO
  130. */
  131. private $user_dao;
  132. /**
  133. * Constructor
  134. *
  135. * @param boolean $session_started
  136. */
  137. public function __construct($session_started=false) {
  138. parent::__construct($session_started);
  139. $this->setContentType('application/json');
  140. $this->view_mgr->cache_lifetime = 60;
  141. /*
  142. * START READ IN OF QUERY STRING VARS
  143. */
  144. if (isset($_GET['network'])) {
  145. $this->network = $_GET['network'];
  146. }
  147. if (isset($_GET['post_id'])) {
  148. if (is_numeric($_GET['post_id'])) {
  149. $this->post_id = $_GET['post_id'];
  150. }
  151. }
  152. if (isset($_GET['user_id'])) {
  153. if (is_numeric($_GET['user_id'])) {
  154. $this->user_id = $_GET['user_id'];
  155. }
  156. }
  157. if (isset($_GET['type'])) {
  158. $this->type = $_GET['type'];
  159. }
  160. if (isset($_GET['username'])) {
  161. $this->username = $_GET['username'];
  162. }
  163. if (isset($_GET['count'])) {
  164. if (is_numeric($_GET['count'])) {
  165. $this->count = (int) $_GET['count'] > 200 ? 200 : (int) $_GET['count'];
  166. }
  167. }
  168. if (isset($_GET['page'])) {
  169. if (is_numeric($_GET['page'])) {
  170. $this->page = (int) $_GET['page'];
  171. }
  172. }
  173. if (isset($_GET['order_by'])) {
  174. $this->order_by = $this->parseOrderBy($_GET['order_by']);
  175. }
  176. if (isset($_GET['direction'])) {
  177. $this->direction = $_GET['direction'] == 'DESC' ? 'DESC' : 'ASC';
  178. }
  179. if (isset($_GET['from'])) {
  180. $this->from = $_GET['from'];
  181. }
  182. if (isset($_GET['until'])) {
  183. $this->until = $_GET['until'];
  184. }
  185. if (isset($_GET['unit'])) {
  186. $this->unit = $_GET['unit'];
  187. }
  188. if (isset($_GET['include_replies'])) {
  189. $this->include_replies = $this->isTrue($_GET['include_replies']);
  190. }
  191. if (isset($_GET['include_entities'])) {
  192. $this->include_entities = $this->isTrue($_GET['include_entities']);
  193. }
  194. if (isset($_GET['trim_user'])) {
  195. $this->trim_user = $this->isTrue($_GET['trim_user']);
  196. }
  197. if (isset($_GET['include_rts'])) {
  198. $this->include_rts = $this->isTrue($_GET['include_rts']);
  199. }
  200. /*
  201. * END READ IN OF QUERY STRING VARS
  202. */
  203. // perhaps extend this in future to allow auth to see private posts
  204. $this->is_public = true;
  205. }
  206. /**
  207. * Convert the order_by option to database column.
  208. *
  209. * For example, 'date' gets converted into the appropriate database colum name: 'pub_date'.
  210. *
  211. * @param string $order_by The value from $_GET['order_by']
  212. * @return string A valid database column.
  213. */
  214. private function parseOrderBy($order_by) {
  215. switch ($order_by) {
  216. case 'date': $order_by = 'pub_date';
  217. break;
  218. case 'post_id': $order_by = 'p.post_id';
  219. break;
  220. case 'location': $order_by = 'location';
  221. break;
  222. case 'source': $order_by = 'source';
  223. break;
  224. case 'follower_count': $order_by = 'author_follower_count';
  225. break;
  226. case 'post_text': $order_by = 'post_text';
  227. break;
  228. case 'author_username': $order_by = 'author_username';
  229. break;
  230. default: $order_by = $this->order_by;
  231. break;
  232. }
  233. return $order_by;
  234. }
  235. /**
  236. * Determine whether the given value represents true or not. Used for the boolean $_GET values such as
  237. * trim_user and include_entities.
  238. *
  239. * @param string $var The value to determine.
  240. * @return bool True if $var is 't', 'true' or '1'.
  241. */
  242. private function isTrue($var) {
  243. if (isset($var) && !is_null($var)) {
  244. return $var == 'true' || $var == 't' || $var == '1';
  245. } else {
  246. return false;
  247. }
  248. }
  249. public function control() {
  250. if ($this->view_mgr->isViewCached()) {
  251. if ($this->view_mgr->is_cached('json.tpl', $this->getCacheKeyString())) {
  252. // set the json data to keep the ThinkUpController happy.
  253. $this->setJsonData(array());
  254. return $this->generateView();
  255. }
  256. }
  257. // fetch the correct PostDAO and UserDAO from the DAOFactory
  258. $this->post_dao = DAOFactory::getDAO('PostDAO');
  259. $this->user_dao = DAOFactory::getDAO('UserDAO');
  260. /*
  261. * Use the information gathered from the query string to retrieve a
  262. * User object. This will be the standard object with which to get
  263. * User information from in API calls.
  264. */
  265. if ($this->user_id != null) {
  266. $this->user = $this->user_dao->getDetails($this->user_id, $this->network);
  267. } else if ($this->username != null) {
  268. $this->user = $this->user_dao->getUserByName($this->username, $this->network);
  269. } else {
  270. $this->user = null;
  271. }
  272. /**
  273. * This switch statement is the main part of this function. It decides
  274. * what type of posts will be fetched depending on the "type" GET
  275. * variable and use the PostDAO to fetch the appropriate posts from
  276. * the database.
  277. *
  278. * If a required field is missing it will create an error field to
  279. * output in JSON.
  280. */
  281. switch ($this->type) {
  282. /**
  283. * Gets a post.
  284. *
  285. * Required arguments: post_id
  286. *
  287. * Optional arguments: network, include_entities, include_replies, trim_user
  288. */
  289. case 'post':
  290. if (is_null($this->post_id)) {
  291. $m = 'A request of type ' . $this->type . ' requires a post_id to be specified.';
  292. throw new RequiredArgumentMissingException($m);
  293. } else {
  294. $data = $this->post_dao->getPost($this->post_id, $this->network, $this->is_public);
  295. }
  296. break;
  297. /**
  298. * Gets all retweets to a post.
  299. *
  300. * Required arguments: post_id
  301. *
  302. * Optional arguments: network, order_by, unit, count, page, include_entities, include_replies, trim_user
  303. */
  304. case 'post_retweets':
  305. if (is_null($this->post_id)) {
  306. $m = 'A request of type ' . $this->type . ' requires a post_id to be specified.';
  307. throw new RequiredArgumentMissingException($m);
  308. } else {
  309. $data = $this->post_dao->getRetweetsOfPost($this->post_id, $this->network, $this->order_by,
  310. $this->unit, $this->is_public, $this->count, $this->page);
  311. }
  312. break;
  313. /**
  314. * Gets replies to a post.
  315. *
  316. * Required arguments: post_id
  317. *
  318. * Optional arguments: network, order_by, unit, count, page, include_entities, include_replies, trim_user
  319. *
  320. * Ordering can only be done by either location or follower count.
  321. */
  322. case 'post_replies':
  323. if (is_null($this->post_id)) {
  324. $m = 'A request of type ' . $this->type . ' requires a post_id to be specified.';
  325. throw new RequiredArgumentMissingException($m);
  326. } else {
  327. $data = $this->post_dao->getRepliesToPost($this->post_id, $this->network, $this->order_by,
  328. $this->unit, $this->is_public, $this->count, $this->page);
  329. }
  330. break;
  331. /*
  332. * Get posts related to a post (replies to it, retweets of it).
  333. *
  334. * Required arguments: post_id
  335. *
  336. * Optional arguments: network, count, page, geo_encoded_only, include_original_post, include_entities,
  337. * include_replies, trim_user
  338. */
  339. case 'related_posts':
  340. if (is_null($this->post_id)) {
  341. $m = 'A request of type ' . $this->type . ' requires a post_id to be specified.';
  342. throw new RequiredArgumentMissingException($m);
  343. } else {
  344. $data = $this->post_dao->getRelatedPosts($this->post_id, $this->network, $this->is_public,
  345. $this->count, $this->page, $geo_encoded_only = false, $include_original_post = false);
  346. }
  347. break;
  348. /*
  349. * Gets the user's most replied to posts.
  350. *
  351. * Required arguments: user_id or username
  352. *
  353. * Optional arguments: network, count, page, include_entities, include_replies, trim_user
  354. */
  355. case 'user_posts_most_replied_to':
  356. if (is_null($this->user)) {
  357. // Check why the User object is null. Could be missing required fields or not found.
  358. if (is_null($this->user_id) && is_null($this->username)) {
  359. $m = 'A request of type ' . $this->type . ' requires a user_id or username to be specified.';
  360. throw new RequiredArgumentMissingException($m);
  361. } else {
  362. throw new UserNotFoundException();
  363. }
  364. } else {
  365. $data = $this->post_dao->getMostRepliedToPosts($this->user->user_id, $this->network, $this->count,
  366. $this->page, $this->is_public);
  367. }
  368. break;
  369. /*
  370. * Gets the user's most retweeted posts.
  371. *
  372. * Required arguments: user_id or username
  373. *
  374. * Optional arguments: network, count, page, include_entities, include_replies, trim_user
  375. */
  376. case 'user_posts_most_retweeted':
  377. if (is_null($this->user)) {
  378. // Check why the User object is null. Could be missing required fields or not found.
  379. if (is_null($this->user_id) && is_null($this->username)) {
  380. $m = 'A request of type ' . $this->type . ' requires a user_id or username to be specified.';
  381. throw new RequiredArgumentMissingException($m);
  382. } else {
  383. throw new UserNotFoundException();
  384. }
  385. } else {
  386. $data = $this->post_dao->getMostRetweetedPosts($this->user->user_id, $this->network, $this->count,
  387. $this->page, $this->is_public);
  388. }
  389. break;
  390. /*
  391. * Gets posts a user has made.
  392. *
  393. * Required arguments: user_id or username
  394. *
  395. * Optional arguments: network, count, page, order_by, direction, include_entities, include_replies,
  396. * trim_user
  397. */
  398. case 'user_posts':
  399. if (is_null($this->user)) {
  400. // Check why the User object is null. Could be missing required fields or not found.
  401. if (is_null($this->user_id) && is_null($this->username)) {
  402. $m = 'A request of type ' . $this->type . ' requires a user_id or username to be specified.';
  403. throw new RequiredArgumentMissingException($m);
  404. } else {
  405. throw new UserNotFoundException();
  406. }
  407. } else {
  408. $data = $this->post_dao->getAllPosts($this->user->user_id, $this->network, $this->count,
  409. $this->page, true, $this->order_by, $this->direction, $this->is_public);
  410. }
  411. break;
  412. /*
  413. * Gets posts a user has made.
  414. *
  415. * Required arguments: user_id or username, from and until
  416. *
  417. * Optional arguments: network, order_by, direction, include_entities, include_replies,
  418. * trim_user
  419. */
  420. case 'user_posts_in_range':
  421. if (is_null($this->user) || is_null($this->from) || is_null($this->until)) {
  422. // Check why the User object is null. Could be missing required fields or not found.
  423. if (is_null($this->user_id) && is_null($this->username)) {
  424. $m = 'A request of type ' . $this->type . ' requires a user_id or username to be specified.';
  425. throw new RequiredArgumentMissingException($m);
  426. } else if (is_null($this->from) || is_null($this->until)) {
  427. $m = 'A request of type ' . $this->type . ' requires valid from and until parameters to be ';
  428. $m .= 'specified.';
  429. throw new RequiredArgumentMissingException($m);
  430. } else {
  431. throw new UserNotFoundException();
  432. }
  433. } else {
  434. $data = $this->post_dao->getPostsByUserInRange($this->user->user_id, $this->network, $this->from,
  435. $this->until, $this->order_by, $this->direction, $iterator=false, $this->is_public);
  436. }
  437. break;
  438. /*
  439. * Gets posts a user is mentioned in.
  440. *
  441. * Required arguments: user_id or username
  442. *
  443. * Optional arguments: network, count, page, include_rts, include_entities, include_replies, trim_user
  444. */
  445. case 'user_mentions':
  446. if (is_null($this->user)) {
  447. // Check why the User object is null. Could be missing required fields or not found.
  448. if (is_null($this->user_id) && is_null($this->username)) {
  449. $m = 'A request of type ' . $this->type . ' requires a user_id or username to be specified.';
  450. throw new RequiredArgumentMissingException($m);
  451. } else {
  452. throw new UserNotFoundException();
  453. }
  454. } else {
  455. $data = $this->post_dao->getAllMentions($this->user->username, $this->count, $this->network,
  456. $this->page, $this->is_public, $this->include_rts, $this->order_by, $this->direction);
  457. }
  458. break;
  459. /*
  460. * Gets question posts a user has made.
  461. *
  462. * Required arguments: user_id or username
  463. *
  464. * Optional arguments: network, count, page, order_by, direction, include_entities, include_replies,
  465. * trim_user
  466. */
  467. case 'user_questions':
  468. if (is_null($this->user)) {
  469. // Check why the User object is null. Could be missing required fields or not found.
  470. if (is_null($this->user_id) && is_null($this->username)) {
  471. $m = 'A request of type ' . $this->type . ' requires a user_id or username to be specified.';
  472. throw new RequiredArgumentMissingException($m);
  473. } else {
  474. throw new UserNotFoundException();
  475. }
  476. } else {
  477. $data = $this->post_dao->getAllQuestionPosts($this->user->user_id, $this->network, $this->count,
  478. $this->page, $this->order_by, $this->direction, $this->is_public);
  479. }
  480. break;
  481. /*
  482. * Gets replies to a user.
  483. *
  484. * Required arguments: user_id or username
  485. *
  486. * Optional arguments: network, count, page, order_by, direction, include_entities, include_replies,
  487. * trim_user
  488. */
  489. case 'user_replies':
  490. if (is_null($this->user)) {
  491. // Check why the User object is null. Could be missing required fields or not found.
  492. if (is_null($this->user_id) && is_null($this->username)) {
  493. $m = 'A request of type ' . $this->type . ' requires a user_id or username to be specified.';
  494. throw new RequiredArgumentMissingException($m);
  495. } else {
  496. throw new UserNotFoundException();
  497. }
  498. } else {
  499. $data = $this->post_dao->getAllReplies($this->user->user_id, $this->network, $this->count,
  500. $this->page, $this->order_by, $this->direction, $this->is_public);
  501. }
  502. break;
  503. /*
  504. * Generate an error because the API call type was not recognized.
  505. */
  506. default:
  507. throw new APICallTypeNotRecognizedException($this->type);
  508. break;
  509. }
  510. switch ($this->network) {
  511. case 'twitter':
  512. if (is_array($data)) {
  513. foreach ($data as $key => $post) {
  514. $data[$key] = $this->convertPostToTweet($post);
  515. }
  516. } else {
  517. $data = $this->convertPostToTweet($data);
  518. }
  519. break;
  520. case 'facebook':
  521. // write a function here to convert to Facebook API style
  522. break;
  523. default: break;
  524. }
  525. // if no posts were found, $data is null. Set it to an empty array.
  526. if (is_null($data)) {
  527. $data = array();
  528. }
  529. $this->setJsonData($data);
  530. return $this->generateView();
  531. }
  532. /**
  533. * Convert the post as it is returned from the database to how it looks when output by the Twitter API.
  534. * Also, add the replies into the post with the index "replies".
  535. *
  536. * If the $post parameter is not a Post object, the function returns null.
  537. *
  538. * @param Post $post The post object.
  539. * @return stdObject The post formatted to look like the Twitter API.
  540. */
  541. private function convertPostToTweet($post) {
  542. if (!is_a($post, 'Post')) {
  543. return null;
  544. }
  545. if ($this->include_replies) {
  546. /*
  547. * Get all replies to the post. The limit is set to 0 because if the count is not greater than 0,
  548. * the method returns all replies.
  549. */
  550. $replies = $this->post_dao->getRepliesToPost($post->post_id, $post->network,
  551. $this->order_by, $this->unit, $this->is_public, 0);
  552. // if replies exist for this post
  553. if ($replies) {
  554. // recursively scan through the post replies, converting them
  555. foreach ($replies as $reply) {
  556. $reply = $this->convertPostToTweet($reply);
  557. }
  558. // add the replies to the post
  559. $post->replies = $replies;
  560. }
  561. }
  562. /*
  563. * Chop and changing the data fetched from the database to look more like the official Twitter API.
  564. */
  565. $post->text = $post->post_text;
  566. $post->created_at = strftime('%a %b %d %T %z %Y', strtotime($post->pub_date));
  567. $post->id = $post->post_id;
  568. $post->favorited = $post->favorited ? true : false;
  569. $post->annotations = null; // to be implemented at some point
  570. $post->truncated = false; // always false
  571. $post->protected = $post->is_protected == 0 ? false : true;
  572. if ($post->geo != null) {
  573. $coordinates = preg_split('/(, |,| )/', $post->geo);
  574. $post->geo = new stdClass();
  575. $post->geo->coordinates = $coordinates;
  576. $post->coordinates->coordinates = $coordinates;
  577. }
  578. /*
  579. * SET THINKUP METADATA
  580. */
  581. $post->thinkup = new stdClass();
  582. $post->thinkup->retweet_count_cache = $post->retweet_count_cache;
  583. $post->thinkup->reply_count_cache = $post->reply_count_cache;
  584. $post->thinkup->old_retweet_count_cache = $post->old_retweet_count_cache;
  585. $post->thinkup->is_geo_encoded = $post->is_geo_encoded;
  586. $user = $this->user_dao->getUserByName($post->author_username, $post->network);
  587. /*
  588. * Occasionally you run into users you haven't fetched yet. Bypass this code if you find one of them.
  589. */
  590. if ($user != null) {
  591. if (!$this->trim_user) {
  592. $post->user = $this->convertUserToStdClass($user);
  593. $post->user->id = $post->user->user_id;
  594. $post->user->followers_count = $post->user->follower_count;
  595. $post->user->profile_image_url = $post->user->avatar;
  596. $post->user->name = $post->user->full_name;
  597. $post->user->screen_name = $post->user->username;
  598. $post->user->statuses_count = $post->user->post_count;
  599. $post->user->created_at = strftime('%a %b %d %T %z %Y', strtotime($post->user->joined));
  600. $post->user->favorites_count = $post->user->favorites_count;
  601. $post->user->utc_offset = Config::getInstance()->getGMTOffset() * 3600;
  602. if (isset($post->user->other)) {
  603. if (isset($post->user->other['avg_tweets_per_day'])) {
  604. $post->user->avg_tweets_per_day = $post->user->other['avg_tweets_per_day'];
  605. }
  606. if (isset($post->user->other['last_updated'])) {
  607. $post->user->last_updated = $post->user->other['last_updated'];
  608. }
  609. }
  610. $post->user->thinkup = new stdClass();
  611. $post->user->thinkup->last_post = $post->user->last_post;
  612. $post->user->thinkup->last_post_id = $post->user->last_post_id;
  613. $post->user->thinkup->found_in = $post->user->found_in;
  614. } else {
  615. $post->user->id = $user->user_id;
  616. }
  617. }
  618. if ($this->include_entities) {
  619. /*
  620. * Gather the links and format them into a Tweet entity.
  621. *
  622. * As part of this conditional, a search for the link in the post text
  623. * is made because it seemed occasionally that unrelated links were
  624. * finding their way into entities. I don't know why.
  625. */
  626. $post->entities->urls = array();
  627. if (!is_null($post->link)) {
  628. if (!is_null($post->link->url) && !empty($post->link->url)
  629. && stripos($post->link->url, $post->text) !== false) {
  630. $link = new stdClass();
  631. $link->url = stripslashes($post->link->url);
  632. $link->expanded_url = $post->link->expanded_url == "" ? null : $post->link->expanded_url;
  633. $link->indices = array();
  634. $link->indices[] = stripos($post->text, $link->url);
  635. $link->indices[] = strlen($link->url) + $link->indices[0];
  636. $post->entities->urls[] = $link;
  637. }
  638. }
  639. /*
  640. * Gather hashtags and format them into a Tweet entity.
  641. */
  642. $extracted_hashtags = Post::extractHashtags($post->text);
  643. $post->entities->hashtags = array();
  644. if (!empty($extracted_hashtags)) {
  645. foreach ($extracted_hashtags as $hashtag_text) {
  646. $hashtag = new stdClass();
  647. $hashtag->text = str_replace('#', '', $hashtag_text);
  648. $hashtag->indices[] = stripos($post->text, $hashtag_text);
  649. $hashtag->indices[] = strlen($hashtag_text) + $hashtag->indices[0];
  650. $post->entities->hashtags[] = $hashtag;
  651. }
  652. }
  653. /*
  654. * Gather mentions and format them into a Tweet entity.
  655. */
  656. $mentions = Post::extractMentions($post->text);
  657. $post->entities->user_mentions = array();
  658. if (!empty($mentions)) {
  659. foreach ($mentions as $username) {
  660. $mentioned_user = $this->user_dao->getUserByName(str_replace('@', '', $username),
  661. $user->network);
  662. $mention = new stdClass();
  663. if (is_null($mentioned_user)) {
  664. // skip this for now, probably not a good idea
  665. continue;
  666. /*
  667. * If the user is not in our own ThinkUp database, a Twitter API call needs to be
  668. * made to fill in the missing details.
  669. *
  670. * Not 100% sure if this is a good idea but it works.
  671. */
  672. $user_api_call = json_decode(Utils::getURLContents(
  673. 'https://api.twitter.com/1/users/show.json?screen_name=' . $username));
  674. $mention->name = $user_api_call->name;
  675. $mention->id = $user_api_call->id;
  676. $mention->screen_name = $user_api_call->screen_name;
  677. } else {
  678. $mention->name = $mentioned_user->full_name;
  679. $mention->id = $mentioned_user->user_id;
  680. $mention->screen_name = $mentioned_user->username;
  681. }
  682. $mention->indices = array();
  683. $mention->indices[] = stripos($post->text, $username);
  684. $mention->indices[] = strlen($username) + $mention->indices[0];
  685. $post->entities->user_mentions[] = $mention;
  686. }
  687. }
  688. }
  689. if ($post->in_retweet_of_post_id != null) {
  690. $post->retweeted_status = $this->post_dao->getPost($post->in_retweet_of_post_id, $user->network);
  691. $post->retweeted_status = $this->convertPostToTweet($post->retweeted_status);
  692. }
  693. /*
  694. * Unset no-longer-used variables in this post; mostly variables that have been moved to more
  695. * Twtter like locations / naming conventions.
  696. */
  697. unset(
  698. $post->post_id,
  699. $post->pub_date,
  700. $post->network,
  701. $post->post_text,
  702. $post->author,
  703. $post->author_fullname,
  704. $post->author_username,
  705. $post->author_user_id,
  706. $post->author_avatar,
  707. $post->adj_pub_date,
  708. $post->user->follower_count,
  709. $post->user->is_protected,
  710. $post->user->network,
  711. $post->user->avatar,
  712. $post->user->full_name,
  713. $post->user->username,
  714. $post->user->user_id,
  715. $post->user->post_count,
  716. $post->user->joined,
  717. $post->user->favorites_count,
  718. $post->user->other,
  719. $post->link,
  720. $post->in_retweet_of_post_id,
  721. $post->in_retweet_of_user_id,
  722. $post->retweet_count_cache,
  723. $post->reply_count_cache,
  724. $post->old_retweet_count_cache,
  725. $post->is_geo_encoded,
  726. $post->rt_threshold,
  727. $post->is_protected,
  728. $post->user->last_post,
  729. $post->user->last_post_id,
  730. $post->user->found_in
  731. );
  732. return $post;
  733. }
  734. /**
  735. * Convert a User object into a stdClass object. This was necessary because of the overloaded __set() method on
  736. * the User object.
  737. *
  738. * @param User $user A User object.
  739. * @return stdClass A stdClass object with all of the same vars as the User object that was passed in.
  740. */
  741. private function convertUserToStdClass(User $user) {
  742. if (is_object($user)) {
  743. $return = new stdClass();
  744. foreach (get_object_vars($user) as $key => $val) {
  745. $return->$key = $val;
  746. }
  747. return $return;
  748. }
  749. else {
  750. return null;
  751. }
  752. }
  753. }