/library/XenForo/Model/Poll.php

https://github.com/daohoangson/DTUI_201105 · PHP · 398 lines · 262 code · 45 blank · 91 comment · 22 complexity · d89ddffb39a759f2fd1941c26bcead25 MD5 · raw file

  1. <?php
  2. /**
  3. * Model for polls.
  4. *
  5. * @package XenForo_Poll
  6. */
  7. class XenForo_Model_Poll extends XenForo_Model
  8. {
  9. /**
  10. * Gets the specified poll.
  11. *
  12. * @param integer $id
  13. *
  14. * @return array|false
  15. */
  16. public function getPollById($id)
  17. {
  18. return $this->_getDb()->fetchRow('
  19. SELECT *
  20. FROM xf_poll
  21. WHERE poll_id = ?
  22. ', $id);
  23. }
  24. /**
  25. * Gets the specified poll by the content it belongs to.
  26. *
  27. * @param string $contentType
  28. * @param integer $contentId
  29. *
  30. * @return array|false
  31. */
  32. public function getPollByContent($contentType, $contentId)
  33. {
  34. return $this->_getDb()->fetchRow('
  35. SELECT *
  36. FROM xf_poll
  37. WHERE content_type = ?
  38. AND content_id = ?
  39. ', array($contentType, $contentId));
  40. }
  41. /**
  42. * Gets poll IDs starting from after the specified start, up to the given limit
  43. *
  44. * @param integer $start
  45. * @param integer $limit
  46. */
  47. public function getPollIdsInRange($start, $limit)
  48. {
  49. $db = $this->_getDb();
  50. return $db->fetchCol($db->limit('
  51. SELECT poll_id
  52. FROM xf_poll
  53. WHERE poll_id > ?
  54. ORDER BY poll_id
  55. ', $limit), $start);
  56. }
  57. /**
  58. * Gets poll response.
  59. *
  60. * @param integer $id
  61. *
  62. * @return array|false
  63. */
  64. public function getPollResponseById($id)
  65. {
  66. return $this->_getDb()->fetchRow('
  67. SELECT *
  68. FROM xf_poll_response
  69. WHERE poll_response_id = ?
  70. ', $id);
  71. }
  72. /**
  73. * Gets all poll responses that belong to the specified poll.
  74. *
  75. * @param $pollId
  76. *
  77. * @return array [poll response id] => info
  78. */
  79. public function getPollResponsesInPoll($pollId)
  80. {
  81. return $this->fetchAllKeyed('
  82. SELECT *
  83. FROM xf_poll_response
  84. WHERE poll_id = ?
  85. ORDER BY poll_response_id
  86. ', 'poll_response_id', $pollId);
  87. }
  88. /**
  89. * Gets poll response cache for use in the poll table.
  90. *
  91. * @param integer $pollId
  92. *
  93. * @return array [poll response id] => [response, response_vote_count, voters]
  94. */
  95. public function getPollResponseCache($pollId)
  96. {
  97. $responses = $this->getPollResponsesInPoll($pollId);
  98. $output = array();
  99. foreach ($responses AS $response)
  100. {
  101. $output[$response['poll_response_id']] = array(
  102. 'response' => $response['response'],
  103. 'response_vote_count' => $response['response_vote_count'],
  104. 'voters' => unserialize($response['voters'])
  105. );
  106. }
  107. return $output;
  108. }
  109. /**
  110. * Rebuilds the poll response cache in the specified poll.
  111. *
  112. * @param integer $pollId
  113. *
  114. * @return array The response cache
  115. */
  116. public function rebuildPollResponseCache($pollId)
  117. {
  118. $cache = $this->getPollResponseCache($pollId);
  119. $db = $this->_getDb();
  120. $db->update('xf_poll',
  121. array('responses' => serialize($cache)),
  122. 'poll_id = ' . $db->quote($pollId)
  123. );
  124. return $cache;
  125. }
  126. /**
  127. * Prepares the poll responses for viewing from the poll record's response cache.
  128. *
  129. * @param array|string $responses Serialized array or array itself
  130. * @param array|null $viewingUser
  131. *
  132. * @return array|false Responses prepared; false if responses can't be prepared
  133. */
  134. public function preparePollResponsesFromCache($responses, array $viewingUser = null)
  135. {
  136. $this->standardizeViewingUserReference($viewingUser);
  137. if (!is_array($responses))
  138. {
  139. $responses = unserialize($responses);
  140. }
  141. if (!is_array($responses))
  142. {
  143. return false;
  144. }
  145. foreach ($responses AS &$response)
  146. {
  147. $response['response'] = XenForo_Helper_String::censorString($response['response']);
  148. $response['hasVoted'] = isset($response['voters'][$viewingUser['user_id']]);
  149. }
  150. return $responses;
  151. }
  152. /**
  153. * Prepares the poll for viewing.
  154. *
  155. * @param array $poll
  156. * @param boolean $canVote If user can vote based on content-specified permissions
  157. * @param array|null $viewingUser
  158. *
  159. * @return array
  160. */
  161. public function preparePoll(array $poll, $canVote, array $viewingUser = null)
  162. {
  163. if (!is_array($poll['responses']))
  164. {
  165. $poll['responses'] = $this->preparePollResponsesFromCache($poll['responses'], $viewingUser);
  166. }
  167. if (!is_array($poll['responses']))
  168. {
  169. $poll['responses'] = $this->preparePollResponsesFromCache(
  170. $this->rebuildPollResponseCache($poll['poll_id']),
  171. $viewingUser
  172. );
  173. }
  174. $poll['hasVoted'] = false;
  175. foreach ($poll['responses'] AS $response)
  176. {
  177. if (!empty($response['hasVoted']))
  178. {
  179. $poll['hasVoted'] = true;
  180. break;
  181. }
  182. }
  183. $poll['open'] = (!$poll['close_date'] || $poll['close_date'] > XenForo_Application::$time);
  184. if (!$canVote || $poll['hasVoted'] || !$poll['open'])
  185. {
  186. $poll['canVote'] = false;
  187. }
  188. else
  189. {
  190. $poll['canVote'] = true;
  191. }
  192. $poll['question'] = XenForo_Helper_String::censorString($poll['question']);
  193. return $poll;
  194. }
  195. /**
  196. * Determines if the viewing user can voe on the poll. This does not take into account
  197. * content-specific permissions.
  198. *
  199. * @param array $poll
  200. * @param string $errorPhraseKey
  201. * @param array|null $viewingUser
  202. *
  203. * @return boolean
  204. */
  205. public function canVoteOnPoll(array $poll, &$errorPhraseKey = '', array $viewingUser = null)
  206. {
  207. if ($poll['close_date'] && $poll['close_date'] < XenForo_Application::$time)
  208. {
  209. return false;
  210. }
  211. $this->standardizeViewingUserReference($viewingUser);
  212. if (!$viewingUser['user_id'])
  213. {
  214. return false;
  215. }
  216. $voted = $this->_getDb()->fetchRow('
  217. SELECT poll_response_id
  218. FROM xf_poll_vote
  219. WHERE poll_id = ?
  220. AND user_id = ?
  221. ', array($poll['poll_id'], $viewingUser['user_id']));
  222. return ($voted ? false : true);
  223. }
  224. /**
  225. * Votes on the specified poll.
  226. *
  227. * @param integer $pollId
  228. * @param integer|array $votes One or more poll response IDs to vote on. This does not check if the poll allows multiple votes.
  229. * @param integer|null $userId
  230. * @param integer|null $voteDate
  231. *
  232. * @return boolean
  233. */
  234. public function voteOnPoll($pollId, $votes, $userId = null, $voteDate = null)
  235. {
  236. if (!is_array($votes))
  237. {
  238. if (!$votes)
  239. {
  240. return false;
  241. }
  242. $votes = array($votes);
  243. }
  244. if (!$votes)
  245. {
  246. return false;
  247. }
  248. if ($userId === null)
  249. {
  250. $userId = XenForo_Visitor::getUserId();
  251. }
  252. if (!$userId)
  253. {
  254. return false;
  255. }
  256. if ($voteDate === null)
  257. {
  258. $voteDate = XenForo_Application::$time;
  259. }
  260. $db = $this->_getDb();
  261. XenForo_Db::beginTransaction($db);
  262. $responses = $this->getPollResponsesInPoll($pollId);
  263. foreach ($votes AS $voteResponseId)
  264. {
  265. if (isset($responses[$voteResponseId]))
  266. {
  267. $db->insert('xf_poll_vote', array(
  268. 'user_id' => $userId,
  269. 'poll_response_id' => $voteResponseId,
  270. 'poll_id' => $pollId,
  271. 'vote_date' => $voteDate
  272. ));
  273. $voterCache = $this->getPollResponseVoterCache($voteResponseId);
  274. $db->query('
  275. UPDATE xf_poll_response SET
  276. response_vote_count = response_vote_count + 1,
  277. voters = ?
  278. WHERE poll_response_id = ?
  279. ', array(serialize($voterCache), $voteResponseId));
  280. }
  281. }
  282. $pollDw = XenForo_DataWriter::create('XenForo_DataWriter_Poll');
  283. $pollDw->setExistingData($pollId);
  284. $pollDw->set('voter_count', $pollDw->get('voter_count') + 1);
  285. $pollDw->save();
  286. XenForo_Db::commit($db);
  287. return true;
  288. }
  289. public function getPollResponseVoterCache($pollResponseId)
  290. {
  291. return $this->fetchAllKeyed('
  292. SELECT poll_vote.user_id, user.username
  293. FROM xf_poll_vote AS poll_vote
  294. LEFT JOIN xf_user AS user ON (poll_vote.user_id = user.user_id)
  295. WHERE poll_vote.poll_response_id = ?
  296. ', 'user_id', $pollResponseId);
  297. }
  298. public function getPollVoterCount($pollId)
  299. {
  300. return $this->_getDb()->fetchOne('
  301. SELECT COUNT(DISTINCT user_id)
  302. FROM xf_poll_vote
  303. WHERE poll_id = ?
  304. ', $pollId);
  305. }
  306. public function rebuildPollData($pollId)
  307. {
  308. $db = $this->_getDb();
  309. $votes = array();
  310. $voters = array();
  311. $results = $db->query('
  312. SELECT poll_vote.poll_response_id, poll_vote.user_id, user.username
  313. FROM xf_poll_vote AS poll_vote
  314. LEFT JOIN xf_user AS user ON (poll_vote.user_id = user.user_id)
  315. WHERE poll_vote.poll_id = ?
  316. ', $pollId);
  317. while ($vote = $results->fetch())
  318. {
  319. $votes[$vote['poll_response_id']][$vote['user_id']] = array(
  320. 'user_id' => $vote['user_id'],
  321. 'username' => $vote['username']
  322. );
  323. $voters[$vote['user_id']] = true;
  324. }
  325. $responses = $this->getPollResponsesInPoll($pollId);
  326. XenForo_Db::beginTransaction($db);
  327. foreach ($responses AS $responseId => $response)
  328. {
  329. if (!isset($votes[$responseId]))
  330. {
  331. $db->update('xf_poll_response', array(
  332. 'response_vote_count' => 0,
  333. 'voters' => ''
  334. ), 'poll_response_id = ' . $db->quote($responseId));
  335. }
  336. else
  337. {
  338. $db->update('xf_poll_response', array(
  339. 'response_vote_count' => count($votes[$responseId]),
  340. 'voters' => serialize($votes[$responseId])
  341. ), 'poll_response_id = ' . $db->quote($responseId));
  342. }
  343. }
  344. $dw = XenForo_DataWriter::create('XenForo_DataWriter_Poll', XenForo_DataWriter::ERROR_SILENT);
  345. if ($dw->setExistingData($pollId))
  346. {
  347. $dw->set('voter_count', count($voters));
  348. $dw->set('responses', serialize($this->getPollResponseCache($pollId)));
  349. $dw->save();
  350. }
  351. XenForo_Db::commit($db);
  352. }
  353. }