BxDolVoting.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. <?php defined('BX_DOL') or die('hack attempt');
  2. /**
  3. * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
  4. * CC-BY License - http://creativecommons.org/licenses/by/3.0/
  5. *
  6. * @defgroup DolphinCore Dolphin Core
  7. * @{
  8. */
  9. bx_import('BxDolVotingQuery');
  10. define( 'BX_PERIOD_PER_VOTE', 7 * 86400 ); ///< user can vote again for the same content after this number of seconds
  11. define( 'BX_OLD_VOTES', 365*86400 ); ///< votes older than this number of seconds will be deleted automatically
  12. /**
  13. * @page objects
  14. * @section votings Votings
  15. * @ref BxDolVoting
  16. */
  17. /**
  18. * Votings for any content
  19. *
  20. * Related classes:
  21. * - BxDolVotingQuery - votings database queries
  22. * - BxBaseVotingView - votings base representation
  23. * - BxTemplVotingView - custom template representation
  24. *
  25. * AJAX votings for any content. Big and small votings stars are supported.
  26. *
  27. * To add votings section to your site you need to add a record to 'sys_objects_vote' table:
  28. *
  29. * - ID - autoincremented id for internal usage
  30. * - ObjectName - your unique module name, with vendor prefix, lowercase and spaces are underscored
  31. * - TableRating - table name where sumarry votigs are stored
  32. * - TableTrack - table name where each vote is stored
  33. * - RowPrefix - row prefix for TableRating
  34. * - MaxVotes - max vote number, 5 by default
  35. * - PostName - post variable name with rating
  36. * - IsDuplicate - number of seconds to not allow duplicate vote (for some bad reason it is define here)
  37. * - IsOn - is this vote object enabled
  38. * - ClassName - custom class name for HotOrNot @ref BxDolRate
  39. * - ClassFile - custom class path for HotOrNot
  40. * - TriggerTable - table to be updated upon each vote
  41. * - TriggerFieldRate - TriggerTable table field with average rate
  42. * - TriggerFieldRateCount - TriggerTable table field with votes count
  43. * - TriggerFieldId - TriggerTable table field with unique record id, primary key
  44. * - OverrideClassName - your custom class name, if you overrride default class
  45. * - OverrideClassFile - your custom class path
  46. *
  47. * You can refer to BoonEx modules for sample record in this table.
  48. *
  49. *
  50. *
  51. * @section example Example of usage:
  52. * After filling in the table you can show big votings in any place, using the following code:
  53. * @code
  54. * bx_import('BxTemplVotingView');
  55. * $o = new BxTemplVotingView ('value of ObjectName field', $iYourEntryId);
  56. * if (!$o->isEnabled()) return '';
  57. * echo $o->getBigVoting (1); // 1 - rate is allowed
  58. * @endcode
  59. *
  60. * And small votings, using the following code:
  61. * @code
  62. * $o = new BxTemplVotingView ('value of ObjectName field', $iYourEntryId);
  63. * if (!$o->isEnabled()) return '';
  64. * echo $o->getSmallVoting (0); // 0 - rate is not allowed, like readonly votings
  65. * @endcode
  66. *
  67. * In some cases votes are already in database and there is no need to execute additional query to get ratings,
  68. * so you can use the following code:
  69. * @code
  70. * $o = new BxTemplVotingView ('value of ObjectName field', 0);
  71. * foreach ($aRecords as $aData)
  72. * echo $o->getJustVotingElement(0, $aData['ID'], $aData['voting_rate']);
  73. * @endcode
  74. *
  75. * Please note that you never need to use BxDolVoting class directly, use BxTemplVotingView instead.
  76. * Also if you override votings class with your own then make it child of BxTemplVotingView class.
  77. *
  78. *
  79. *
  80. * @section acl Memberships/ACL:
  81. * vote - ACTION_ID_VOTE
  82. *
  83. *
  84. *
  85. * @section alerts Alerts:
  86. * Alerts type/unit - every module has own type/unit, it equals to ObjectName.
  87. * The following alerts are rised:
  88. *
  89. * - rate - comment was posted
  90. * - $iObjectId - entry id
  91. * - $iSenderId - rater user id
  92. * - $aExtra['rate'] - rate
  93. *
  94. */
  95. class BxDolVoting extends BxDol
  96. {
  97. var $_iId = 0; ///< item id to be rated
  98. var $_iCount = 0; ///< number of votes
  99. var $_fRate = 0; ///< average rate
  100. var $_sSystem = 'profile'; ///< current rating system name
  101. var $_aSystem = array (); ///< current rating system array
  102. var $_oQuery = null;
  103. /**
  104. * Constructor
  105. */
  106. function BxDolVoting( $sSystem, $iId, $iInit = 1)
  107. {
  108. parent::BxDol();
  109. $this->_aSystems =& $this->getSystems();
  110. $this->_sSystem = $sSystem;
  111. if (isset($this->_aSystems[$sSystem]))
  112. $this->_aSystem = $this->_aSystems[$sSystem];
  113. else
  114. return;
  115. $this->_oQuery = new BxDolVotingQuery($this->_aSystem);
  116. if ($iInit)
  117. $this->init($iId);
  118. if (!$this->isEnabled()) return;
  119. $iVoteResult = $this->_getVoteResult ();
  120. if ($iVoteResult)
  121. {
  122. if (!$this->makeVote ($iVoteResult))
  123. {
  124. exit;
  125. }
  126. $this->initVotes ();
  127. echo $this->getVoteRate() . ',' . $this->getVoteCount();
  128. exit;
  129. }
  130. }
  131. function & getSystems ()
  132. {
  133. if (isset($GLOBALS['bx_dol_voting_systems'])) {
  134. return $GLOBALS['bx_dol_voting_systems'];
  135. }
  136. $oDb = BxDolDb::getInstance();
  137. $oCache = $oDb->getDbCacheObject();
  138. $GLOBALS['bx_dol_voting_systems'] = $oCache->getData($oDb->genDbCacheKey('sys_objects_vote'));
  139. if (null === $GLOBALS['bx_dol_voting_systems']) {
  140. // cache is empty | load data from DB
  141. $sQuery = "SELECT * FROM `sys_objects_vote`";
  142. $sRows = $oDb->getAll($sQuery);
  143. $GLOBALS['bx_dol_voting_systems'] = array();
  144. foreach ($sRows as $aRow)
  145. {
  146. $GLOBALS['bx_dol_voting_systems'][$aRow['ObjectName']] = array
  147. (
  148. 'table_rating' => $aRow['TableRating'],
  149. 'table_track' => $aRow['TableTrack'],
  150. 'row_prefix' => $aRow['RowPrefix'],
  151. 'max_votes' => $aRow['MaxVotes'],
  152. 'post_name' => $aRow['PostName'],
  153. 'is_duplicate' => is_int($aRow['IsDuplicate']) ? $aRow['IsDuplicate'] : constant($aRow['IsDuplicate']),
  154. 'is_on' => $aRow['IsOn'],
  155. 'className' => $aRow['className'],
  156. 'classFile' => $aRow['classFile'],
  157. 'trigger_table' => $aRow['TriggerTable'], // table with field to update on every rating change
  158. 'trigger_field_rate' => $aRow['TriggerFieldRate'], // table field name with rating
  159. 'trigger_field_rate_count' => $aRow['TriggerFieldRateCount'], // table field name with rating count
  160. 'trigger_field_id' => $aRow['TriggerFieldId'], // table field name with object id
  161. 'override_class_name' => $aRow['OverrideClassName'], // new class to override
  162. 'override_class_file' => $aRow['OverrideClassFile'], // class file path
  163. );
  164. }
  165. // write data into cache file
  166. $oCache = $oDb->getDbCacheObject();
  167. $oCache->setData ($oDb->genDbCacheKey('sys_objects_vote'), $GLOBALS['bx_dol_voting_systems']);
  168. }
  169. return $GLOBALS['bx_dol_voting_systems'];
  170. }
  171. function init ($iId)
  172. {
  173. if (!$iId)
  174. $iId = $this->_iId;
  175. if (!$this->isEnabled()) return;
  176. if (!$this->_iId && $iId)
  177. {
  178. $this->setId($iId);
  179. }
  180. }
  181. function initVotes ()
  182. {
  183. if (!$this->isEnabled()) return;
  184. if (!$this->_oQuery) return;
  185. $a = $this->_oQuery->getVote ($this->getId());
  186. if (!$a) return;
  187. $this->_iCount = $a['count'];
  188. $this->_fRate = $a['rate'];
  189. }
  190. function makeVote ($iVote)
  191. {
  192. if (!$this->isEnabled()) return false;
  193. if ($this->isDublicateVote()) return false;
  194. if (!$this->checkAction()) return false;
  195. if ($this->_sSystem == 'profile' && $this->getId() == getLoggedId())
  196. return false;
  197. if ($this->_oQuery->putVote ($this->getId(), getVisitorIP(), $iVote)) {
  198. $this->_triggerVote();
  199. $oZ = new BxDolAlerts($this->_sSystem, 'rate', $this->getId(), getLoggedId(), array ('rate' => $iVote));
  200. $oZ->alert();
  201. return true;
  202. }
  203. return false;
  204. }
  205. function checkAction ()
  206. {
  207. bx_import('BxDolAcl');
  208. if (isset($this->_checkActionResult))
  209. return $this->_checkActionResult;
  210. $iId = getLoggedId();
  211. $check_res = checkAction( $iId, ACTION_ID_VOTE );
  212. return ($this->_checkActionResult = ($check_res[CHECK_ACTION_RESULT] == CHECK_ACTION_RESULT_ALLOWED));
  213. }
  214. function isDublicateVote ()
  215. {
  216. if (!$this->isEnabled()) return false;
  217. return $this->_oQuery->isDublicateVote ($this->getId(), getVisitorIP());
  218. }
  219. function getId ()
  220. {
  221. return $this->_iId;
  222. }
  223. function isEnabled ()
  224. {
  225. return $this->_aSystem['is_on'];
  226. }
  227. function getMaxVote()
  228. {
  229. return $this->_aSystem['max_votes'];
  230. }
  231. function getVoteCount()
  232. {
  233. return $this->_iCount;
  234. }
  235. function getVoteRate()
  236. {
  237. return $this->_fRate;
  238. }
  239. function getSystemName()
  240. {
  241. return $this->_sSystem;
  242. }
  243. /**
  244. * set id to operate with votes
  245. */
  246. function setId ($iId)
  247. {
  248. if ($iId == $this->getId()) return;
  249. $this->_iId = $iId;
  250. $this->initVotes();
  251. }
  252. function getSqlParts ($sMailTable, $sMailField)
  253. {
  254. if ($this->isEnabled())
  255. return $this->_oQuery->getSqlParts ($sMailTable, $sMailField);
  256. else
  257. return array();
  258. }
  259. function isValidSystem ($sSystem)
  260. {
  261. return isset($this->_aSystems[$sSystem]);
  262. }
  263. function deleteVotings ($iId)
  264. {
  265. if (!(int)$iId || !$this->_oQuery)
  266. return false;
  267. $this->_oQuery->deleteVotings ($iId);
  268. return true;
  269. }
  270. function getTopVotedItem ($iDays, $sJoinTable = '', $sJoinField = '', $sWhere = '')
  271. {
  272. return $this->_oQuery->getTopVotedItem ($iDays, $sJoinTable, $sJoinField, $sWhere);
  273. }
  274. function getVotedItems ($sIp)
  275. {
  276. return $this->_oQuery->getVotedItems ($sIp);
  277. }
  278. /**
  279. * it is called on cron every day or similar period to clean old votes
  280. */
  281. function maintenance () {
  282. $iDeletedRecords = 0;
  283. $oDb = BxDolDb::getInstance();
  284. foreach ($this->_aSystems as $aSystem) {
  285. if (!$aSystem['is_on'])
  286. continue;
  287. $sPre = $aSystem['row_prefix'];
  288. $sQuery = $oDb->prepare("DELETE FROM `{$aSystem['table_track']}` WHERE `{$sPre}date` < DATE_SUB(NOW(), INTERVAL ? SECOND)", BX_OLD_VOTES);
  289. $iDeletedRecords += $oDb->query($sQuery);
  290. $oDb->query("OPTIMIZE TABLE `{$aSystem['table_track']}`");
  291. }
  292. return $iDeletedRecords;
  293. }
  294. /** private functions
  295. *********************************************/
  296. function _getVoteResult ()
  297. {
  298. if (empty($_GET[$this->_aSystem['post_name']]) || 0 != strcasecmp($_SERVER['REQUEST_METHOD'], 'POST'))
  299. return 0;
  300. $iVote = (int)$_GET[$this->_aSystem['post_name']];
  301. if (!$iVote)
  302. return 0;
  303. if ($iVote > $this->getMaxVote())
  304. $iVote = $this->getMaxVote();
  305. if ($iVote < 1)
  306. $iVote = 1;
  307. return $iVote;
  308. }
  309. function _triggerVote()
  310. {
  311. if (!$this->_aSystem['trigger_table'])
  312. return false;
  313. $iId = $this->getId();
  314. if (!$iId)
  315. return false;
  316. $this->initVotes ();
  317. $iCount = $this->getVoteCount();
  318. $fRate = $this->getVoteRate();
  319. return $this->_oQuery->updateTriggerTable($iId, $fRate, $iCount);
  320. }
  321. }
  322. /** @} */