PageRenderTime 59ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/dbtech/vbactivity/includes/class_core.php

https://gitlab.com/elasa/vb-elasa.ir
PHP | 1917 lines | 1187 code | 241 blank | 489 comment | 136 complexity | 92f662ca63ebf20b997c84792d3fdf08 MD5 | raw file
  1. <?php
  2. /*======================================================================*\
  3. || #################################################################### ||
  4. || # ---------------------------------------------------------------- # ||
  5. || # Copyright ©2013 Fillip Hannisdal AKA Revan/NeoRevan/Belazor # ||
  6. || # All Rights Reserved. # ||
  7. || # This file may not be redistributed in whole or significant part. # ||
  8. || # ---------------------------------------------------------------- # ||
  9. || # You are not allowed to use this on your server unless the files # ||
  10. || # you downloaded were done so with permission. # ||
  11. || # ---------------------------------------------------------------- # ||
  12. || #################################################################### ||
  13. \*======================================================================*/
  14. // #############################################################################
  15. // vBActivity functionality class
  16. /**
  17. * Handles everything to do with vBActivity.
  18. */
  19. class VBACTIVITY
  20. {
  21. /**
  22. * Version info
  23. *
  24. * @public mixed
  25. */
  26. public static $jQueryVersion = '1.7.2';
  27. public static $version = '3.1.9 Patch Level 1';
  28. public static $versionnumber = '319pl1';
  29. /**
  30. * The vBulletin registry object
  31. *
  32. * @private vB_Registry
  33. */
  34. protected static $vbulletin = NULL;
  35. /**
  36. * The database object
  37. *
  38. * @private Thanks_Database
  39. */
  40. public static $db = NULL;
  41. /**
  42. * The vBulletin registry object
  43. *
  44. * @private vB_Registry
  45. */
  46. protected static $prefix = 'dbtech_';
  47. /**
  48. * The vBulletin registry object
  49. *
  50. * @private vB_Registry
  51. */
  52. protected static $bitfieldgroup = array(
  53. 'vbactivitypermissions',
  54. 'vbactivitymodpermissions',
  55. );
  56. /**
  57. * Array of permissions to be returned
  58. *
  59. * @public array
  60. */
  61. public static $permissions = NULL;
  62. /**
  63. * Array of cached items
  64. *
  65. * @public array
  66. */
  67. public static $cache = array();
  68. /**
  69. * Whether we've called the DM fetcher
  70. *
  71. * @public boolean
  72. */
  73. protected static $called = false;
  74. /**
  75. * Whether we've called the translations
  76. *
  77. * @public boolean
  78. */
  79. protected static $called2 = false;
  80. /**
  81. * Array of cached items
  82. *
  83. * @public array
  84. */
  85. public static $unserialize = array(
  86. 'contest' => array(
  87. 'data',
  88. 'prizes',
  89. 'prizes2',
  90. 'winners',
  91. 'excludedcriteria',
  92. 'excludedforums',
  93. ),
  94. 'type' => array(
  95. 'pointsperforum'
  96. ),
  97. );
  98. /**
  99. * Array of translatable items
  100. *
  101. * @public array
  102. */
  103. public static $translations = array(
  104. 'achievement' => 'achievementid',
  105. 'category' => 'categoryid',
  106. 'contest' => 'contestid',
  107. 'contesttype' => 'varname',
  108. 'medal' => 'medalid',
  109. );
  110. /**
  111. * Whether we have the pro version or not
  112. *
  113. * @public boolean
  114. */
  115. public static $isPro = false;
  116. /**
  117. * Array of postbits
  118. *
  119. * @public array
  120. */
  121. public static $postbitcache = array();
  122. /**
  123. * Array of postbits
  124. *
  125. * @public array
  126. */
  127. public static $postbitcache2 = array();
  128. /**
  129. * Whether we've cached the points
  130. *
  131. * @private boolean
  132. */
  133. public static $points_cached = false;
  134. /**
  135. * Caches various values to ease resource consumption
  136. *
  137. * @private array
  138. */
  139. private static $levels = array();
  140. private static $tnlvalues = array();
  141. /**
  142. * Array of type objects
  143. *
  144. * @public array
  145. */
  146. public static $types = array();
  147. /**
  148. * Array of cached users
  149. *
  150. * @public array
  151. */
  152. public static $cachedUsers = array();
  153. /**
  154. * Does important checking before anything else should be going on
  155. *
  156. * @param vB_Registry Registry object
  157. */
  158. public static function init($vbulletin)
  159. {
  160. // Check if the vBulletin Registry is an object
  161. if (!is_object($vbulletin))
  162. {
  163. // Something went wrong here I think
  164. trigger_error("Registry object is not an object", E_USER_ERROR);
  165. }
  166. // Set registry
  167. self::$vbulletin =& $vbulletin;
  168. // Set database object
  169. self::$db = new vBActivity_Database($vbulletin->db);
  170. // Set permissions shorthand
  171. self::_getPermissions();
  172. // What permissions to override
  173. $override = array(
  174. 'canviewactivity',
  175. );
  176. foreach ($override as $permname)
  177. {
  178. // Override various permissions
  179. self::$permissions[$permname] = (self::$permissions['ismanager'] ? 1 : self::$permissions[$permname]);
  180. }
  181. if (VB_AREA == 'AdminCP')
  182. {
  183. // Easier than a billion if checks in the files
  184. $override = array(
  185. 'achievement',
  186. 'category',
  187. 'criteria',
  188. 'award',
  189. 'grantawards',
  190. 'maintenance',
  191. 'trophy',
  192. 'contest',
  193. 'promotion',
  194. 'backup',
  195. 'snapshot',
  196. 'impex',
  197. 'points',
  198. 'permissions',
  199. 'options',
  200. );
  201. foreach ($override as $permname)
  202. {
  203. // Override various permissions
  204. self::$permissions[$permname] = 1;
  205. }
  206. }
  207. foreach (self::$unserialize as $cachetype => $keys)
  208. {
  209. foreach (self::$cache[$cachetype] as $id => $arr)
  210. {
  211. foreach ($keys as $key)
  212. {
  213. // Do unserialize
  214. self::$cache[$cachetype][$id][$key] = @unserialize($arr[$key]);
  215. self::$cache[$cachetype][$id][$key] = (is_array(self::$cache[$cachetype][$id][$key]) ? self::$cache[$cachetype][$id][$key] : array());
  216. }
  217. }
  218. }
  219. // Set pro version
  220. /*DBTECH_PRO_START*/
  221. self::$isPro = true;
  222. /*DBTECH_PRO_END*/
  223. }
  224. /**
  225. * Handles translation of things
  226. */
  227. public static function loadTranslations()
  228. {
  229. global $vbphrase;
  230. if (self::$called2)
  231. {
  232. // We've already done this
  233. return;
  234. }
  235. foreach (self::$translations as $cachetype => $key)
  236. {
  237. foreach (self::$cache[$cachetype] as $id => $arr)
  238. {
  239. self::$cache[$cachetype][$id]['title_translated'] = isset($vbphrase["dbtech_vbactivity_{$cachetype}_{$arr[$key]}_title"]) ? $vbphrase["dbtech_vbactivity_{$cachetype}_{$arr[$key]}_title"] : $arr['title'];
  240. self::$cache[$cachetype][$id]['description_translated'] = isset($vbphrase["dbtech_vbactivity_{$cachetype}_{$arr[$key]}_description"]) ? $vbphrase["dbtech_vbactivity_{$cachetype}_{$arr[$key]}_description"] : $arr['description'];
  241. }
  242. }
  243. // Translations loaded
  244. self::$called2 = true;
  245. }
  246. /**
  247. * Check if we have permissions to perform an action
  248. *
  249. * @param array User info
  250. * @param array Permissions info
  251. */
  252. public static function checkPermissions(&$user, $permissions, $bitIndex)
  253. {
  254. if (!$user['usergroupid'] OR (!isset($user['membergroupids']) AND $user['userid']))
  255. {
  256. // Ensure we have this
  257. $user = fetch_userinfo($user['userid']);
  258. }
  259. if (!is_array($user['permissions']))
  260. {
  261. // Ensure we have the perms
  262. cache_permissions($user);
  263. }
  264. $ugs = fetch_membergroupids_array($user);
  265. if (!$ugs[0])
  266. {
  267. // Hardcode guests
  268. $ugs[0] = 1;
  269. }
  270. $bits = array(
  271. 'default' => 4
  272. );
  273. $bit = $bits[$bitIndex];
  274. //self::$vbulletin->usergroupcache
  275. foreach ($ugs as $usergroupid)
  276. {
  277. $value = $permissions[$usergroupid][$bit];
  278. $value = (isset($value) ? $value : 0);
  279. switch ($value)
  280. {
  281. case 1:
  282. // Allow
  283. return true;
  284. break;
  285. case -1:
  286. // Usergroup Default
  287. if (!($user[self::$prefix . self::$bitfieldgroup[0]] & $bit))
  288. {
  289. // Allow by default
  290. return true;
  291. }
  292. break;
  293. }
  294. }
  295. // We didn't make it
  296. return false;
  297. }
  298. /**
  299. * Class factory. This is used for instantiating the extended classes.
  300. *
  301. * @param string The type of the class to be called (user, forum etc.)
  302. * @param vB_Registry An instance of the vB_Registry object.
  303. * @param integer One of the ERRTYPE_x constants
  304. *
  305. * @return vB_DataManager An instance of the desired class
  306. */
  307. public static function &initDataManager($classtype, &$registry, $errtype = ERRTYPE_STANDARD)
  308. {
  309. if (empty(self::$called))
  310. {
  311. // include the abstract base class
  312. require_once(DIR . '/includes/class_dm.php');
  313. self::$called = true;
  314. }
  315. if (preg_match('#^\w+$#', $classtype))
  316. {
  317. if (file_exists(DIR . '/dbtech/vbactivity/includes/class_dm_' . strtolower($classtype) . '.php'))
  318. {
  319. // Lite
  320. require_once(DIR . '/dbtech/vbactivity/includes/class_dm_' . strtolower($classtype) . '.php');
  321. }
  322. else
  323. {
  324. // Pro
  325. require_once(DIR . '/dbtech/vbactivity_pro/includes/class_dm_' . strtolower($classtype) . '.php');
  326. }
  327. $classname = 'vBActivity_DataManager_' . $classtype;
  328. $object = new $classname($registry, $errtype);
  329. return $object;
  330. }
  331. }
  332. /**
  333. * (Legacy) Class factory. This is used for instantiating the extended classes.
  334. *
  335. * @param string The type of the class to be called (user, forum etc.)
  336. * @param vB_Registry An instance of the vB_Registry object.
  337. * @param integer One of the ERRTYPE_x constants
  338. *
  339. * @return vB_DataManager An instance of the desired class
  340. */
  341. public static function &datamanager_init($classtype, &$registry, $errtype = ERRTYPE_STANDARD)
  342. {
  343. return self::initDataManager($classtype, $registry, $errtype);
  344. }
  345. /**
  346. * JS class fetcher for AdminCP
  347. *
  348. * @param string The JS file name or the code
  349. * @param boolean Whether it's a file or actual JS code
  350. */
  351. public static function js($js = '', $file = true, $echo = true)
  352. {
  353. $output = '';
  354. if ($file)
  355. {
  356. $output = '<script type="text/javascript" src="' . self::$vbulletin->options['bburl'] . '/dbtech/vbactivity/clientscript/vbactivity' . $js . '.js?v=' . self::$versionnumber . '"></script>';
  357. }
  358. else
  359. {
  360. $output = "
  361. <script type=\"text/javascript\">
  362. <!--
  363. $js
  364. // -->
  365. </script>
  366. ";
  367. }
  368. if ($echo)
  369. {
  370. echo $output;
  371. }
  372. else
  373. {
  374. return $output;
  375. }
  376. }
  377. /**
  378. * Determines the path to jQuery based on browser settings
  379. */
  380. public static function jQueryPath()
  381. {
  382. // create the path to jQuery depending on the version
  383. if (self::$vbulletin->options['customjquery_path'])
  384. {
  385. $path = str_replace('{version}', self::$jQueryVersion, self::$vbulletin->options['customjquery_path']);
  386. if (!preg_match('#^https?://#si', self::$vbulletin->options['customjquery_path']))
  387. {
  388. $path = REQ_PROTOCOL . '://' . $path;
  389. }
  390. return $path;
  391. }
  392. else
  393. {
  394. switch (self::$vbulletin->options['remotejquery'])
  395. {
  396. case 1:
  397. default:
  398. // Google CDN
  399. return REQ_PROTOCOL . '://ajax.googleapis.com/ajax/libs/jquery/' . self::$jQueryVersion . '/jquery.min.js';
  400. break;
  401. case 2:
  402. // jQuery CDN
  403. return REQ_PROTOCOL . '://code.jquery.com/jquery-' . self::$jQueryVersion . '.min.js';
  404. break;
  405. case 3:
  406. // Microsoft CDN
  407. return REQ_PROTOCOL . '://ajax.aspnetcdn.com/ajax/jquery/jquery-' . self::$jQueryVersion . '.min.js';
  408. break;
  409. }
  410. }
  411. }
  412. /**
  413. * Returns a 'depth mark' for use in prefixing items that need to show depth in a hierarchy
  414. *
  415. * @param integer Depth of item (0 = no depth, 3 = third level depth)
  416. * @param string Character or string to repeat $depth times to build the depth mark
  417. * @param string Existing depth mark to append to
  418. *
  419. * @return string
  420. */
  421. function getDepthMark($depth, $depthchar, $depthmark = '')
  422. {
  423. for ($i = 0; $i < $depth; $i++)
  424. {
  425. $depthmark .= $depthchar;
  426. }
  427. return $depthmark;
  428. }
  429. /**
  430. * Breaks down a difference (in seconds) into its days / hours / minutes / seconds components.
  431. *
  432. * @param integer Difference (in seconds)
  433. *
  434. * @return array
  435. */
  436. function getTimeBreakdown($difference)
  437. {
  438. $breakdown = array();
  439. // Set days
  440. $breakdown['days'] = intval($difference / 86400);
  441. $difference -= ($breakdown['days'] * 86400);
  442. // Set hours
  443. $breakdown['hours'] = intval($difference / 3600);
  444. $difference -= ($breakdown['hours'] * 3600);
  445. // Set minutes
  446. $breakdown['minutes'] = intval($difference / 60);
  447. $difference -= ($breakdown['minutes'] * 60);
  448. // Set seconds
  449. $breakdown['seconds'] = intval($difference);
  450. return $breakdown;
  451. }
  452. /**
  453. * Quick Method of building the CPNav Template
  454. *
  455. * @param string The selected item in the CPNav
  456. */
  457. public static function setNavClass($selectedcell = 'main')
  458. {
  459. global $navclass;
  460. $cells = array(
  461. 'main',
  462. 'achievements',
  463. 'promotions',
  464. 'trophies',
  465. 'medals',
  466. 'activity',
  467. 'ranking',
  468. 'leaderboards',
  469. 'allachievements',
  470. 'alltrophies',
  471. 'allawards',
  472. 'contests',
  473. 'activitystats',
  474. );
  475. //($hook = vBulletinHook::fetch_hook('usercp_nav_start')) ? eval($hook) : false;
  476. // set the class for each cell/group
  477. $navclass = array();
  478. foreach ($cells AS $cellname)
  479. {
  480. $navclass[$cellname] = (intval(self::$vbulletin->versionnumber) == 3 ? 'alt2' : 'inactive');
  481. }
  482. $navclass[$selectedcell] = (intval(self::$vbulletin->versionnumber) == 3 ? 'alt1' : 'active');
  483. //($hook = vBulletinHook::fetch_hook('usercp_nav_complete')) ? eval($hook) : false;
  484. }
  485. /**
  486. * Escapes a string and makes it JavaScript-safe
  487. *
  488. * @param mixed The string or array to make JS-safe
  489. */
  490. public static function jsEscapeString(&$arr)
  491. {
  492. $find = array(
  493. "\r\n",
  494. "\n",
  495. "\t",
  496. '"'
  497. );
  498. $replace = array(
  499. '\r\n',
  500. '\n',
  501. '\t',
  502. '\"',
  503. );
  504. $arr = str_replace($find, $replace, $arr);
  505. }
  506. /**
  507. * Encodes a string as a JSON object (consistent behaviour instead of relying on PHP built-in functions)
  508. *
  509. * @param mixed The string or array to encode
  510. * @param boolean (Optional) Whether this is an associative array
  511. * @param boolean (Optional) Whether we should escape the string or if they have already been escaped
  512. */
  513. public static function encodeJSON($arr, $assoc = true, $doescape = true)
  514. {
  515. if ($doescape)
  516. {
  517. self::jsEscapeString($arr);
  518. }
  519. if (!$assoc)
  520. {
  521. // Not associative, simple return
  522. return '{"' . implode('","', $arr) . '"}';
  523. }
  524. $content = array();
  525. foreach ((array)$arr as $key => $val)
  526. {
  527. if (is_array($val))
  528. {
  529. // Recursion, definition: see recursion
  530. $val = self::encodeJSON($val);
  531. $content[] = '"' . $key . '":' . $val;
  532. }
  533. else
  534. {
  535. $content[] = '"' . $key . '":"' . $val . '"';
  536. }
  537. }
  538. return '{' . implode(',', $content) . '}';
  539. }
  540. /**
  541. * Outputs a JSON string to the browser
  542. *
  543. * @param mixed array to output
  544. */
  545. /**
  546. * Outputs a JSON string to the browser
  547. *
  548. * @param mixed array to output
  549. */
  550. public static function outputJSON($json, $full_shutdown = false)
  551. {
  552. if (headers_sent($file, $line))
  553. {
  554. die("Cannot send response, headers already sent. File: $file Line: $line");
  555. }
  556. // Store the charset
  557. $charset = strtoupper(self::getCharset());
  558. // We need to convert $json charset if we're not using UTF-8
  559. if ($charset != 'UTF-8')
  560. {
  561. $json = self::toCharset($json, $charset, 'UTF-8');
  562. }
  563. //If this is IE9, IE10, or IE11 -- we also need to work around the deliberate attempt to break "is IE" logic by the
  564. //IE dev team -- we need to send type "text/plain". Yes, we know that's not the standard.
  565. if (
  566. isset($_SERVER['HTTP_USER_AGENT']) && (
  567. (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== false) OR
  568. (strpos($_SERVER['HTTP_USER_AGENT'], 'Trident') !== false)
  569. )
  570. )
  571. {
  572. header('Content-type: text/plain; charset=UTF-8');
  573. }
  574. else
  575. {
  576. header('Content-type: application/json; charset=UTF-8');
  577. }
  578. // IE will cache ajax requests, and we need to prevent this - VBV-148
  579. header('Cache-Control: max-age=0,no-cache,no-store,post-check=0,pre-check=0');
  580. header('Expires: Sat, 1 Jan 2000 01:00:00 GMT');
  581. header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
  582. header("Pragma: no-cache");
  583. // Create JSON
  584. $json = self::encodeJSON($json);
  585. // Turn off debug output
  586. self::$vbulletin->debug = false;
  587. if (defined('VB_API') AND VB_API === true)
  588. {
  589. print_output($json);
  590. }
  591. //run any registered shutdown functions
  592. if (intval(self::$vbulletin->versionnumber) > 3)
  593. {
  594. $GLOBALS['vbulletin']->shutdown->shutdown();
  595. }
  596. exec_shut_down();
  597. self::$vbulletin->db->close();
  598. $sendHeader = false;
  599. switch(self::$vbulletin->options['ajaxheader'])
  600. {
  601. case 0 :
  602. $sendHeader = true;
  603. case 1 :
  604. $sendHeader = false;
  605. case 2 :
  606. default:
  607. $sendHeader = (strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') !== false);
  608. }
  609. if ($sendHeader)
  610. {
  611. // this line is causing problems with mod_gzip/deflate, but is needed for some IIS setups
  612. @header('Content-Length: ' . strlen($json));
  613. }
  614. // Finally spit out JSON
  615. echo $json;
  616. die();
  617. }
  618. /**
  619. * Converts a string from one character encoding to another.
  620. * If the target encoding is not specified then it will be resolved from the current
  621. * language settings.
  622. *
  623. * @param string|array The string/array to convert
  624. * @param string The source encoding
  625. * @return string The target encoding
  626. */
  627. public static function toCharset($in, $in_encoding, $target_encoding = false)
  628. {
  629. if (!$target_encoding) {
  630. if (!($target_encoding = self::getCharset())) {
  631. return $in;
  632. }
  633. }
  634. if (is_object($in))
  635. {
  636. foreach ($in as $key => $val)
  637. {
  638. $in->$key = self::toCharset($val, $in_encoding, $target_encoding);
  639. }
  640. return $in;
  641. }
  642. else if (is_array($in)) {
  643. foreach ($in as $key => $val)
  644. {
  645. $in["$key"] = self::toCharset($val, $in_encoding, $target_encoding);
  646. }
  647. return $in;
  648. }
  649. else if (is_string($in))
  650. {
  651. // ISO-8859-1 or other Western charset doesn't support Asian ones so that we need to NCR them
  652. // Iconv will ignore them
  653. if (preg_match("/^[ISO|Windows|IBM|MAC|CP]/i", $target_encoding)) {
  654. $in = self::ncrEncode($in, true, true);
  655. }
  656. // Try iconv
  657. if (function_exists('iconv')) {
  658. // Try iconv
  659. $out = @iconv($in_encoding, $target_encoding . '//IGNORE', $in);
  660. return $out;
  661. }
  662. // Try mbstring
  663. if (function_exists('mb_convert_encoding')) {
  664. return @mb_convert_encoding($in, $target_encoding, $in_encoding);
  665. }
  666. }
  667. else
  668. {
  669. // if it's not a string, array or object, don't modify it
  670. return $in;
  671. }
  672. }
  673. /**
  674. * Gets the current charset
  675. **/
  676. public static function getCharset()
  677. {
  678. static $lang_charset = '';
  679. if (!empty($lang_charset))
  680. {
  681. return $lang_charset;
  682. }
  683. if (intval(self::$vbulletin->versionnumber) > 3)
  684. {
  685. // vB4
  686. $lang_charset = vB_Template_Runtime::fetchStyleVar('charset');
  687. }
  688. else
  689. {
  690. // vB3
  691. $lang_charset = $GLOBALS['stylevar']['charset'];
  692. }
  693. if (!empty($lang_charset))
  694. {
  695. return $lang_charset;
  696. }
  697. $lang_charset = (!empty(self::$vbulletin->userinfo['lang_charset'])) ? self::$vbulletin->userinfo['lang_charset'] : 'utf-8';
  698. return $lang_charset;
  699. }
  700. /**
  701. * Converts a UTF-8 string into unicode NCR equivelants.
  702. *
  703. * @param string String to encode
  704. * @param bool Only ncrencode unicode bytes
  705. * @param bool If true and $skip_ascii is true, it will skip windows-1252 extended chars
  706. * @return string Encoded string
  707. */
  708. public static function ncrEncode($str, $skip_ascii = false, $skip_win = false)
  709. {
  710. if (!$str)
  711. {
  712. return $str;
  713. }
  714. if (function_exists('mb_encode_numericentity'))
  715. {
  716. if ($skip_ascii)
  717. {
  718. if ($skip_win)
  719. {
  720. $start = 0xFE;
  721. }
  722. else
  723. {
  724. $start = 0x80;
  725. }
  726. }
  727. else
  728. {
  729. $start = 0x0;
  730. }
  731. return mb_encode_numericentity($str, array($start, 0xffff, 0, 0xffff), 'UTF-8');
  732. }
  733. if (is_pcre_unicode())
  734. {
  735. return preg_replace_callback(
  736. '#\X#u',
  737. create_function('$matches', 'return ncrencode_matches($matches, ' . (int)$skip_ascii . ', ' . (int)$skip_win . ');'),
  738. $str
  739. );
  740. }
  741. return $str;
  742. }
  743. /**
  744. * Constructs some <option>s for use in the templates
  745. *
  746. * @param array The key:value data array
  747. * @param mixed (Optional) The selected id(s)
  748. * @param boolean (Optional) Whether we should HTMLise the values
  749. */
  750. public static function createSelectOptions($array, $selectedid = '', $htmlise = false)
  751. {
  752. if (!is_array($array))
  753. {
  754. return '';
  755. }
  756. $options = '';
  757. foreach ($array as $key => $val)
  758. {
  759. if (is_array($val))
  760. {
  761. // Create the template
  762. $templater = vB_Template::create('optgroup');
  763. $templater->register('optgroup_label', ($htmlise ? htmlspecialchars_uni($key) : $key));
  764. $templater->register('optgroup_options', self::createSelectOptions($val, $selectedid, $tabindex, $htmlise));
  765. $options .= $templater->render();
  766. }
  767. else
  768. {
  769. if (is_array($selectedid))
  770. {
  771. $selected = iif(in_array($key, $selectedid), ' selected="selected"', '');
  772. }
  773. else
  774. {
  775. $selected = iif($key == $selectedid, ' selected="selected"', '');
  776. }
  777. $templater = vB_Template::create('option');
  778. $templater->register('optionvalue', ($key !== 'no_value' ? $key : ''));
  779. $templater->register('optionselected', $selected);
  780. $templater->register('optiontitle', ($htmlise ? htmlspecialchars_uni($val) : $val));
  781. $options .= $templater->render();
  782. }
  783. }
  784. return $options;
  785. }
  786. /**
  787. * Constructs a time selector
  788. *
  789. * Resulting form element names: $name[day], $name[month], $name[year], $name[hour], $name[minute]
  790. *
  791. * @param string Title for row
  792. * @param string Base name for form elements - $name[day], $name[month], $name[year] etc.
  793. * @param mixed Unix timestamp to be represented by the form fields OR SQL date field (yyyy-mm-dd)
  794. * @param boolean Whether or not to show the time input components, or only the date
  795. * @param boolean If true, expect an SQL date field from the unix timestamp parameter instead (for birthdays)
  796. * @param string Vertical alignment for the row
  797. *
  798. * @return string The constructed time row
  799. */
  800. public static function timeRow($name = 'date', $unixtime = '', $showtime = true, $birthday = false, $valign = 'middle')
  801. {
  802. global $vbphrase;
  803. $output = '';
  804. $monthnames = array(
  805. 0 => '- - - -',
  806. 1 => $vbphrase['january'],
  807. 2 => $vbphrase['february'],
  808. 3 => $vbphrase['march'],
  809. 4 => $vbphrase['april'],
  810. 5 => $vbphrase['may'],
  811. 6 => $vbphrase['june'],
  812. 7 => $vbphrase['july'],
  813. 8 => $vbphrase['august'],
  814. 9 => $vbphrase['september'],
  815. 10 => $vbphrase['october'],
  816. 11 => $vbphrase['november'],
  817. 12 => $vbphrase['december'],
  818. );
  819. if (is_array($unixtime))
  820. {
  821. require_once(DIR . '/includes/functions_misc.php');
  822. $unixtime = vbmktime(0, 0, 0, $unixtime['month'], $unixtime['day'], $unixtime['year']);
  823. }
  824. if ($birthday)
  825. { // mktime() on win32 doesn't support dates before 1970 so we can't fool with a negative timestamp
  826. if ($unixtime == '')
  827. {
  828. $month = 0;
  829. $day = '';
  830. $year = '';
  831. }
  832. else
  833. {
  834. $temp = explode('-', $unixtime);
  835. $month = intval($temp[0]);
  836. $day = intval($temp[1]);
  837. if ($temp[2] == '0000')
  838. {
  839. $year = '';
  840. }
  841. else
  842. {
  843. $year = intval($temp[2]);
  844. }
  845. }
  846. }
  847. else
  848. {
  849. if ($unixtime)
  850. {
  851. $month = vbdate('n', $unixtime, false, false);
  852. $day = vbdate('j', $unixtime, false, false);
  853. $year = vbdate('Y', $unixtime, false, false);
  854. $hour = vbdate('G', $unixtime, false, false);
  855. $minute = vbdate('i', $unixtime, false, false);
  856. }
  857. }
  858. $cell = array();
  859. $cell[] = "<label for=\"{$name}_month\">$vbphrase[month]</label><br /><select name=\"{$name}[month]\" id=\"{$name}_month\" tabindex=\"1\" class=\"primary select\"" . iif(self::$vbulletin->debug, " title=\"name=&quot;$name" . "[month]&quot;\"") . ">\n" . self::createSelectOptions($monthnames, $month) . "\t\t</select>";
  860. $cell[] = "<label for=\"{$name}_date\">$vbphrase[day]</label><br /><input type=\"text\" class=\"primary textbox\" name=\"{$name}[day]\" id=\"{$name}_date\" value=\"$day\" size=\"4\" maxlength=\"2\" tabindex=\"1\"" . iif(self::$vbulletin->debug, " title=\"name=&quot;$name" . "[day]&quot;\"") . ' />';
  861. $cell[] = "<label for=\"{$name}_year\">$vbphrase[year]</label><br /><input type=\"text\" class=\"primary textbox\" name=\"{$name}[year]\" id=\"{$name}_year\" value=\"$year\" size=\"4\" maxlength=\"4\" tabindex=\"1\"" . iif(self::$vbulletin->debug, " title=\"name=&quot;$name" . "[year]&quot;\"") . ' />';
  862. if ($showtime)
  863. {
  864. $cell[] = "<label for=\"{$name}_hour\">$vbphrase[hours]</label><br /><input type=\"text\" class=\"primary textbox\" name=\"{$name}[hour]\" id=\"{$name}_year\" value=\"$hour\" size=\"4\" maxlength=\"4\" tabindex=\"1\"" . iif(self::$vbulletin->debug, " title=\"name=&quot;$name" . "[hour]&quot;\"") . ' />';
  865. $cell[] = "<label for=\"{$name}_minute\">$vbphrase[minute]</label><br /><input type=\"text\" class=\"primary textbox\" name=\"{$name}[minute]\" id=\"{$name}_minute\" value=\"$minute\" size=\"4\" maxlength=\"4\" tabindex=\"1\"" . iif(self::$vbulletin->debug, " title=\"name=&quot;$name" . "[minute]&quot;\"") . ' />';
  866. }
  867. $inputs = '';
  868. foreach($cell AS $html)
  869. {
  870. $inputs .= "\t\t<td style=\"padding-left:6px;\"><span class=\"smallfont\">$html</span></td>\n";
  871. }
  872. $output .= "<table cellpadding=\"0\" cellspacing=\"2\" border=\"0\"><tr>\n$inputs\t\n</tr></table>";
  873. return $output;
  874. }
  875. /**
  876. * Sends a PM to a specified user
  877. *
  878. * @param mixed The UserID or userinfo to send the PM to
  879. * @param string Title of the PM
  880. * @param string Body of the PM
  881. * @param mixed Userinfo or vBOption key to send the PM from
  882. */
  883. public static function sendPM($recipient, $title, $message, $sender = NULL)
  884. {
  885. global $vbphrase;
  886. if (!is_array($recipient))
  887. {
  888. // Who's the PM to
  889. $recipient = fetch_userinfo($recipient);
  890. }
  891. if (array_key_exists($sender, self::$vbulletin->options))
  892. {
  893. if (self::$vbulletin->options[$sender])
  894. {
  895. // Who's the PM from
  896. $sender = fetch_userinfo(self::$vbulletin->options[$sender]);
  897. }
  898. else
  899. {
  900. // Null this out since we had no defined sender
  901. $sender = NULL;
  902. }
  903. }
  904. if ($sender === NULL)
  905. {
  906. // We're using the recipient
  907. $sender = $recipient;
  908. }
  909. if (!isset($sender))
  910. {
  911. if (!$fromuserid)
  912. {
  913. // Invalid user
  914. return false;
  915. }
  916. // Who's the PM to
  917. $sender = fetch_userinfo($fromuserid);
  918. }
  919. // Send pm
  920. $pmdm =& datamanager_init('PM', self::$vbulletin, ERRTYPE_ARRAY);
  921. $pmdm->set_info('is_automated', true); // implies overridequota
  922. $pmdm->set('fromuserid', $sender['userid']);
  923. $pmdm->set('fromusername', unhtmlspecialchars($sender['username']));
  924. $pmdm->set_recipients(unhtmlspecialchars($recipient['username']), $sender['permissions'], 'cc');
  925. $pmdm->setr('title', $title);
  926. $pmdm->setr('message', $message);
  927. $pmdm->set('dateline', TIMENOW);
  928. $pmdm->set('showsignature', 1);
  929. $pmdm->set('allowsmilie', 1);
  930. if (!$pmdm->pre_save())
  931. {
  932. return $pmdm->errors;
  933. }
  934. else
  935. {
  936. return $pmdm->save();
  937. }
  938. }
  939. /**
  940. * Grabs what permissions we have got
  941. */
  942. protected static function _getPermissions()
  943. {
  944. if (!self::$vbulletin->userinfo['permissions'])
  945. {
  946. // For some reason, this is missing
  947. cache_permissions(self::$vbulletin->userinfo);
  948. }
  949. foreach (self::$bitfieldgroup as $bitfieldgroup)
  950. {
  951. // Override bitfieldgroup variable
  952. $bitfieldgroup = self::$prefix . $bitfieldgroup;
  953. if (!is_array(self::$vbulletin->bf_ugp[$bitfieldgroup]))
  954. {
  955. // Something went wrong here I think
  956. require_once(DIR . '/includes/class_bitfield_builder.php');
  957. if (vB_Bitfield_Builder::build(false) !== false)
  958. {
  959. $myobj =& vB_Bitfield_Builder::init();
  960. if (sizeof($myobj->data['ugp'][$bitfieldgroup]) != sizeof(self::$vbulletin->bf_ugp[$bitfieldgroup]))
  961. {
  962. require_once(DIR . '/includes/adminfunctions.php');
  963. $myobj->save(self::$vbulletin->db);
  964. build_forum_permissions();
  965. if (IN_CONTROL_PANEL === true)
  966. {
  967. define('CP_REDIRECT', self::$vbulletin->scriptpath);
  968. print_stop_message('rebuilt_bitfields_successfully');
  969. }
  970. else
  971. {
  972. self::$vbulletin->url = self::$vbulletin->scriptpath;
  973. if (version_compare(self::$vbulletin->versionnumber, '4.1.7') >= 0)
  974. {
  975. eval(print_standard_redirect(array('redirect_updatethanks', self::$vbulletin->userinfo['username']), true, true));
  976. }
  977. else
  978. {
  979. eval(print_standard_redirect('redirect_updatethanks', true, true));
  980. }
  981. }
  982. }
  983. }
  984. else
  985. {
  986. echo "<strong>error</strong>\n";
  987. print_r(vB_Bitfield_Builder::fetch_errors());
  988. die();
  989. }
  990. }
  991. foreach ((array)self::$vbulletin->bf_ugp[$bitfieldgroup] as $permname => $bit)
  992. {
  993. // Set the permission
  994. self::$permissions[$permname] = (!$bit ? self::$vbulletin->userinfo['permissions'][$bitfieldgroup][$permname] : (self::$vbulletin->userinfo['permissions'][$bitfieldgroup] & $bit ? 1 : 0));
  995. }
  996. }
  997. }
  998. /**
  999. * Fetches the type id of the specified type name
  1000. *
  1001. * @param string Type name
  1002. *
  1003. * @return integer Type ID
  1004. */
  1005. public static function fetch_type($typename)
  1006. {
  1007. $retval = 0;
  1008. foreach (self::$cache['type'] as $typeid => $type)
  1009. {
  1010. if ($type['typename'] == $typename)
  1011. {
  1012. // This is the correct type
  1013. $retval = $typeid;
  1014. break;
  1015. }
  1016. }
  1017. return $retval;
  1018. }
  1019. /**
  1020. * Initialises a type class.
  1021. *
  1022. * @param string Type name
  1023. */
  1024. public static function init_type($type)
  1025. {
  1026. if (!class_exists('vBActivity_Type_Core'))
  1027. {
  1028. // Include the needed class
  1029. require_once(DIR . '/dbtech/vbactivity/type/core.php');
  1030. }
  1031. if (self::$types[$type['typename']])
  1032. {
  1033. // We don't need to init this
  1034. return;
  1035. }
  1036. if (!$type['active'])
  1037. {
  1038. // We don't want to init this
  1039. self::$types[$type['typename']] = new vBActivity_Type_Core(self::$vbulletin, $type);
  1040. return;
  1041. }
  1042. $classname = 'vBActivity_Type_' . $type['typename'];
  1043. if (!class_exists($classname))
  1044. {
  1045. // Include the needed class
  1046. require_once(DIR . $type['filename']);
  1047. }
  1048. // Init the type
  1049. self::$types[$type['typename']] = new $classname(self::$vbulletin, $type);
  1050. }
  1051. /**
  1052. * Initialises a contest class.
  1053. *
  1054. * @param string Type name
  1055. */
  1056. public static function initContest($contest)
  1057. {
  1058. if (!is_file(DIR . self::$cache['contesttype'][$contest['contesttypeid']]['filename']) OR $contest == NULL)
  1059. {
  1060. // We don't want to init this
  1061. return false;
  1062. }
  1063. $classname = 'vBActivity_ContestType_' . ucfirst(self::$cache['contesttype'][$contest['contesttypeid']]['varname']);
  1064. if (!class_exists($classname))
  1065. {
  1066. // Include the needed class
  1067. require_once(DIR . self::$cache['contesttype'][$contest['contesttypeid']]['filename']);
  1068. }
  1069. // Return the object
  1070. return new $classname(self::$vbulletin, $contest);
  1071. }
  1072. /**
  1073. * Adds a winner to a contest and rewards them their points / medals if applicable
  1074. *
  1075. * @param integer The UserID that won
  1076. * @param integer The ContestID information
  1077. * @param double The number of points we had
  1078. */
  1079. public static function addContestWinner($userid, $contestid, $numpoints, $typename = 'contestswon', $multiplier = 1)
  1080. {
  1081. global $vbphrase;
  1082. if (!$contest = self::$cache['contest'][$contestid])
  1083. {
  1084. // Contest didn't exist
  1085. return false;
  1086. }
  1087. foreach ((array)$contest['winners'] as $place => $winnerInfo)
  1088. {
  1089. if ($winnerInfo['userid'] == $userid)
  1090. {
  1091. // Already won this contest
  1092. return false;
  1093. }
  1094. }
  1095. // Determine our place
  1096. $place = count($contest['winners']) + 1;
  1097. // Dupe this
  1098. $winners = $contest['winners'];
  1099. // Set winrar
  1100. $winners[$place] = array(
  1101. 'userid' => $userid,
  1102. 'points' => $numpoints
  1103. );
  1104. if ($contest['prizes'][$place] AND self::$cache['medal'][$contest['prizes'][$place]])
  1105. {
  1106. // We had a prize
  1107. $userinfo = array('userid' => $userid);
  1108. // Add the reward
  1109. self::add_reward('medal', $contest['prizes'][$place], $userinfo);
  1110. // Rebuild the cache
  1111. self::build_rewards_cache($userinfo);
  1112. }
  1113. // init data manager
  1114. $dm =& self::initDataManager('Contest', self::$vbulletin, ERRTYPE_SILENT);
  1115. $dm->set_existing($contest);
  1116. $dm->set('winners', $winners);
  1117. $dm->save();
  1118. // Insert points for contests won
  1119. self::insert_points($typename, $contestid, $userid, $multiplier);
  1120. if ($contest['prizes2'][$place])
  1121. {
  1122. // Insert points for contests won
  1123. self::insert_points('contestprize', $contestid, $userid, $contest['prizes2'][$place]);
  1124. }
  1125. if ($contest['admin_notifs'])
  1126. {
  1127. // Fetch winner user info
  1128. $winnerInfo = fetch_userinfo($userid);
  1129. // Grab title and message
  1130. $title = $vbphrase['dbtech_vbactivity_new_contest_winner_title'];
  1131. $message = construct_phrase($vbphrase['dbtech_vbactivity_new_contest_winner_body'],
  1132. self::$vbulletin->options['bburl'],
  1133. $userid,
  1134. $winnerInfo['username'],
  1135. $place,
  1136. $contest['title_translated'],
  1137. $numpoints,
  1138. ($contest['prizes'][$place] ? self::$cache['medal'][$contest['prizes'][$place]]['title_translated'] : $vbphrase['n_a']),
  1139. intval($contest['prizes2'][$place]),
  1140. $contest['contestid']
  1141. );
  1142. $notifs = explode(',', $contest['admin_notifs']);
  1143. foreach ($notifs as $recipient)
  1144. {
  1145. if (self::$vbulletin->options['dbtech_vbactivity_contestadminnotif_pm'])
  1146. {
  1147. // Who's the PM from
  1148. $sender = 'dbtech_vbactivity_contestadminnotif_pm';
  1149. }
  1150. else
  1151. {
  1152. // Null this out since we had no defined sender
  1153. $sender = $winnerInfo;
  1154. }
  1155. // Now send the PM
  1156. self::sendPM($recipient, $title, $message, $sender);
  1157. }
  1158. }
  1159. if ((int)$contest['winner_notifs'] & 1 OR (int)$contest['winner_notifs'] & 2)
  1160. {
  1161. // Fetch winner user info
  1162. $winnerInfo = fetch_userinfo($userid);
  1163. // Grab title and message
  1164. $title = $vbphrase['dbtech_vbactivity_you_are_contest_winner_title'];
  1165. $message = construct_phrase($vbphrase['dbtech_vbactivity_you_are_contest_winner_body'],
  1166. self::$vbulletin->options['bburl'],
  1167. $winnerInfo['username'],
  1168. $place,
  1169. $contest['title_translated'],
  1170. $numpoints,
  1171. ($contest['prizes'][$place] ? self::$cache['medal'][$contest['prizes'][$place]]['title_translated'] : $vbphrase['n_a']),
  1172. intval($contest['prizes2'][$place]),
  1173. $contest['contestid']
  1174. );
  1175. if (((int)$contest['winner_notifs'] & 1) AND ((int)$winnerInfo['options'] & self::$vbulletin->bf_misc_useroptions['adminemail']))
  1176. {
  1177. if (!function_exists('convert_url_to_bbcode'))
  1178. {
  1179. // Ensure we can convert URL to BBCode
  1180. require_once(DIR . '/includes/functions_newpost.php');
  1181. }
  1182. // Convert URL to BBCode
  1183. $message = convert_url_to_bbcode($message);
  1184. // Parse the BBCode that we generated
  1185. require_once(DIR . '/includes/class_bbcode.php');
  1186. $parser = new vB_BbCodeParser(self::$vbulletin, fetch_tag_list());
  1187. $message = $parser->parse($message, 'nonforum', false);
  1188. unset($parser);
  1189. // Send email to winner
  1190. vbmail($winnerInfo['email'], $title, $message, true);
  1191. }
  1192. if ((int)$contest['winner_notifs'] & 2)
  1193. {
  1194. // Send PM to winner
  1195. self::sendPM($userid, $title, $message, 'dbtech_vbactivity_contestwinnernotif_pm');
  1196. }
  1197. }
  1198. }
  1199. /**
  1200. * Gets information about the contest's (potential) winners.
  1201. *
  1202. * @param integer Contest ID
  1203. * @param boolean Whether we're returning raw winner data
  1204. */
  1205. public static function getContestStanding($contestid, $winnersOnly = false, $numUsers = 0)
  1206. {
  1207. global $vbphrase;
  1208. if (!$contest = self::$cache['contest'][$contestid])
  1209. {
  1210. // Invalid contest
  1211. return false;
  1212. }
  1213. // Init this array
  1214. $SQL = array();
  1215. $winnerTmp = array();
  1216. if ((($contest['show_progress'] AND $contest['end'] > TIMENOW) OR $winnersOnly) AND $contest['numwinners'])
  1217. {
  1218. // In case we have some winners in progress
  1219. $winnerTmp = (array)$contest['winners'];
  1220. $progressWinners = array(0);
  1221. foreach ($winnerTmp as $place => $winnerInfo)
  1222. {
  1223. // Add to the winner array
  1224. $progressWinners[] = $winnerInfo['userid'];
  1225. }
  1226. // In-progress contest that we're showing progress for
  1227. $numUsers = $numUsers ? $numUsers : ($contest['numwinners'] - count($winnerTmp));
  1228. // This contest is ongoing
  1229. $winnerList = self::$db->fetchAll('
  1230. SELECT points, userid
  1231. FROM $dbtech_vbactivity_contestprogress
  1232. WHERE contestid = ?
  1233. AND userid NOT :userList
  1234. ORDER BY points DESC
  1235. LIMIT :limit
  1236. ', array(
  1237. $contestid,
  1238. ':userList' => self::$db->queryList((array)$progressWinners),
  1239. ':limit' => $numUsers,
  1240. ));
  1241. $winnersToSort = array();
  1242. foreach ($winnerTmp as $place => $winnerInfo)
  1243. {
  1244. // Add to the winner array
  1245. $winnersToSort[$winnerInfo['userid']] = $winnerInfo['points'];
  1246. }
  1247. foreach ($winnerList as $winner)
  1248. {
  1249. // Add to the winner array
  1250. $winnersToSort[$winner['userid']] = $winner['points'];
  1251. }
  1252. arsort($winnersToSort, SORT_NUMERIC);
  1253. if ($winnersOnly)
  1254. {
  1255. // We're only returning the raw winner data
  1256. return $winnersToSort;
  1257. }
  1258. $winners = array();
  1259. $i = 0;
  1260. foreach ($winnersToSort as $winner => $points)
  1261. {
  1262. // Temporary winner display
  1263. $winnerTmp[++$i] = array(
  1264. 'userid' => $winner,
  1265. 'points' => $points
  1266. );
  1267. if (in_array($winner, $SQL))
  1268. {
  1269. // We don't need more of this thank you
  1270. continue;
  1271. }
  1272. if (is_array(self::$cachedUsers[$winner]))
  1273. {
  1274. // Already cached this user
  1275. continue;
  1276. }
  1277. // We're looking up this userid
  1278. $SQL[] = $winner;
  1279. }
  1280. }
  1281. $winnerDisplay = array();
  1282. if ($contest['end'] <= TIMENOW)
  1283. {
  1284. $winnerTmp = $contest['winners'];
  1285. foreach ((array)$contest['winners'] as $winner)
  1286. {
  1287. if (in_array($winner['userid'], $SQL))
  1288. {
  1289. // We don't need more of this thank you
  1290. continue;
  1291. }
  1292. if (is_array(self::$cachedUsers[$winner['userid']]))
  1293. {
  1294. // Already cached this user
  1295. continue;
  1296. }
  1297. // We're looking up this userid
  1298. $SQL[] = $winner['userid'];
  1299. }
  1300. }
  1301. if (count($SQL))
  1302. {
  1303. if (!function_exists('fetch_avatar_from_userinfo'))
  1304. {
  1305. // Get the avatar function
  1306. require_once(DIR . '/includes/functions_user.php');
  1307. }
  1308. $winners = self::$db->fetchAllKeyed('
  1309. SELECT *, user.userid AS realuserid
  1310. ' . (self::$vbulletin->options['avatarenabled'] ? ', avatar.avatarpath, NOT ISNULL(customavatar.userid) AS hascustomavatar, customavatar.dateline AS avatardateline, customavatar.width AS avwidth, customavatar.height AS avheight, customavatar.height_thumb AS avheight_thumb, customavatar.width_thumb AS avwidth_thumb, customavatar.filedata_thumb' : '') . '
  1311. FROM $user AS user
  1312. ' . (self::$vbulletin->options['avatarenabled'] ? '
  1313. LEFT JOIN $avatar AS avatar ON (avatar.avatarid = user.avatarid)
  1314. LEFT JOIN $customavatar AS customavatar ON (customavatar.userid = user.userid)
  1315. ' : '') . '
  1316. WHERE user.userid :userList
  1317. ', 'realuserid', array(
  1318. ':userList' => self::$db->queryList($SQL),
  1319. ));
  1320. foreach ($winners as $userid => $winner)
  1321. {
  1322. // No idea why this is needed
  1323. $winners[$userid]['userid'] = $userid;
  1324. // Grab markup username
  1325. fetch_musername($winners[$userid]);
  1326. // grab avatar from userinfo
  1327. fetch_avatar_from_userinfo($winners[$userid], true);
  1328. // Cache this user
  1329. self::$cachedUsers[$userid] = $winners[$userid];
  1330. }
  1331. }
  1332. foreach ((array)$winnerTmp as $place => $winner)
  1333. {
  1334. $winner['place'] = $place;
  1335. if ($contest['end'] <= TIMENOW)
  1336. {
  1337. switch ($place)
  1338. {
  1339. case 1:
  1340. $winner['trophy'] = 'gold';
  1341. break;
  1342. case 2:
  1343. $winner['trophy'] = 'silver';
  1344. break;
  1345. default:
  1346. $winner['trophy'] = 'bronze';
  1347. break;
  1348. }
  1349. }
  1350. //self::$cachedUsers[$winner['userid']]
  1351. //$winner
  1352. // We're looking up this userid
  1353. $templater = vB_Template::create('dbtech_vbactivity_contests_winner');
  1354. $templater->register('winner', array_merge($winner, (array)self::$cachedUsers[$winner['userid']]));
  1355. $winnerDisplay['winnerList'] .= $templater->render();
  1356. }
  1357. return $winnerDisplay;
  1358. }
  1359. /**
  1360. * Fetches the type id of the specified type name
  1361. *
  1362. * @param string Type name
  1363. * @param integer (Optional) ID field we are inserting
  1364. * @param integer (Optional) Userid we are granting points to
  1365. * @param integer (Optional) For the "per reputation point" etc settings
  1366. * @param integer (Optional) Dateline
  1367. * @param integer (Optional) Forum ID of the source
  1368. *
  1369. * @return none none
  1370. */
  1371. public static function insert_points($typename, $idfield = 0, $userid = 0, $multiplier = 1, $dateline = TIMENOW, $forumid = 0)
  1372. {
  1373. $typeid = self::fetch_type($typename);
  1374. $type = self::$cache['type'][$typeid];
  1375. // Store number of points for this action
  1376. $type['points'] = (($forumid AND $type['pointsperforum'][$forumid] !== NULL AND $type['pointsperforum'][$forumid] != -1) ? $type['pointsperforum'][$forumid] : $type['points']);
  1377. if ($type['points'] == 0 OR
  1378. $multiplier == 0 OR
  1379. !$type['active'])
  1380. {
  1381. // We aren't awarding/taking away any points, so ignore this
  1382. return;
  1383. }
  1384. // Shorthand
  1385. $userid = ($userid > 0 ? $userid : self::$vbulletin->userinfo['userid']);
  1386. $points = ($type['points'] * $multiplier);
  1387. // Round the points off to 2 decimals
  1388. $points = round($points, 2);
  1389. // Insert into pointslog
  1390. self::$db->insert('dbtech_vbactivity_pointslog', array(
  1391. 'userid' => $userid,
  1392. 'dateline' => ($dateline ? $dateline : TIMENOW),
  1393. 'points' => $points,
  1394. 'typeid' => $typeid,
  1395. 'idfield' => $idfield,
  1396. 'forumid' => $forumid,
  1397. ));
  1398. // Update users points
  1399. self::$vbulletin->db->query_write("
  1400. UPDATE " . TABLE_PREFIX . "dbtech_vbactivity_points
  1401. SET $typename = $typename + " . doubleval($points) . "
  1402. WHERE userid = '" . $userid . "'
  1403. ");
  1404. // Update users points
  1405. self::$vbulletin->db->query_write("
  1406. UPDATE " . TABLE_PREFIX . "user
  1407. SET
  1408. dbtech_vbactivity_points = dbtech_vbactivity_points + " . self::$vbulletin->db->sql_prepare($points) . ",
  1409. dbtech_vbactivity_pointscache = dbtech_vbactivity_pointscache + " . self::$vbulletin->db->sql_prepare($points) . ",
  1410. dbtech_vbactivity_pointscache_day = dbtech_vbactivity_pointscache_day + " . self::$vbulletin->db->sql_prepare($points) . ",
  1411. dbtech_vbactivity_pointscache_week = dbtech_vbactivity_pointscache_week + " . self::$vbulletin->db->sql_prepare($points) . ",
  1412. dbtech_vbactivity_pointscache_month = dbtech_vbactivity_pointscache_month + " . self::$vbulletin->db->sql_prepare($points) . "
  1413. WHERE userid = '" . $userid . "'
  1414. ");
  1415. ($hook = vBulletinHook::fetch_hook('dbtech_vbactivity_points_add')) ? eval($hook) : false;
  1416. if ($userid == self::$vbulletin->userinfo['userid'])
  1417. {
  1418. // To make AJAX shit work properly n such
  1419. self::$vbulletin->userinfo['dbtech_vbactivity_points'] += $points;
  1420. self::$vbulletin->userinfo['dbtech_vbactivity_pointscache'] += $points;
  1421. self::$vbulletin->userinfo['dbtech_vbactivity_pointscache_day'] += $points;
  1422. self::$vbulletin->userinfo['dbtech_vbactivity_pointscache_week'] += $points;
  1423. self::$vbulletin->userinfo['dbtech_vbactivity_pointscache_month'] += $points;
  1424. // Store userinfo
  1425. $userinfo = self::$vbulletin->userinfo;
  1426. }
  1427. else
  1428. {
  1429. // Fetch userinfo
  1430. $userinfo = fetch_userinfo($userid);
  1431. }
  1432. if (!$userinfo['permissions'])
  1433. {
  1434. // For some reason, this is missing
  1435. cache_permissions($userinfo);
  1436. }
  1437. if (($userinfo['permissions']['dbtech_vbactivitypermissions'] & self::$vbulletin->bf_ugp_dbtech_vbactivitypermissions['isexcluded_contests']) OR $userinfo['dbtech_vbactivity_excluded'])
  1438. {
  1439. // We're excluded
  1440. return true;
  1441. }
  1442. if ($typename == 'contestprize')
  1443. {
  1444. // We're not counting this
  1445. return true;
  1446. }
  1447. foreach (self::$cache['contest'] as $contestid => $contest)
  1448. {
  1449. if ($contest['start'] > $dateline)
  1450. {
  1451. // Contest hasn't started yet
  1452. continue;
  1453. }
  1454. if ($contest['end'] <= $dateline)
  1455. {
  1456. // Contest has ended
  1457. continue;
  1458. }
  1459. if (!is_array($contest['excludedcriteria']))
  1460. {
  1461. // For some reason
  1462. $contest['excludedcriteria'] = array();
  1463. }
  1464. if (in_array($typeid, $contest['excludedcriteria']))
  1465. {
  1466. // We're not dealing with this criteria
  1467. continue;
  1468. }
  1469. if (!is_array($contest['excludedforums']))
  1470. {
  1471. // For some reason
  1472. $contest['excludedforums'] = array();
  1473. }
  1474. if ($forumid AND in_array($forumid, $contest['excludedforums']))
  1475. {
  1476. // We're not dealing with this forum
  1477. continue;
  1478. }
  1479. // Update users points
  1480. self::$db->query('
  1481. INSERT INTO $dbtech_vbactivity_contestprogress
  1482. (contestid, userid, points)
  1483. VALUES (?, ?, :points)
  1484. ON DUPLICATE KEY UPDATE
  1485. points = points + :points
  1486. ', array(
  1487. $contestid,
  1488. $userid,
  1489. ':points' => doubleval($points)
  1490. ));
  1491. if (self::$cache['contesttype'][$contest['contesttypeid']]['varname'] != 'target')
  1492. {
  1493. // Wrong contest type
  1494. continue;
  1495. }
  1496. if (count($contest['winners']) >= $contest['numwinners'])
  1497. {
  1498. // We've already drawn all the winners we need
  1499. continue;
  1500. }
  1501. // Detect winners
  1502. $contest['winners'] = (is_array($contest['winners']) ? $contest['winners'] : array());
  1503. foreach ((array)$contest['winners'] as $winnerid => $winner)
  1504. {
  1505. if ($winner['userid'] == $userid)
  1506. {
  1507. // Already won
  1508. continue 2;
  1509. }
  1510. }
  1511. // This contest is ongoing
  1512. $numpoints = self::$db->fetchOne('
  1513. SELECT points
  1514. FROM $dbtech_vbactivity_contestprogress
  1515. WHERE contestid = ?
  1516. AND userid = ?
  1517. ', array(
  1518. $contestid,
  1519. $userid,
  1520. ));
  1521. if ($numpoints < $contest['target'])
  1522. {
  1523. // No winrar
  1524. continue;
  1525. }
  1526. // Add Contest winner
  1527. self::addContestWinner($userid, $contestid, $numpoints);
  1528. }
  1529. }
  1530. /**
  1531. * Grabs our activity level
  1532. *
  1533. * @param array Information regarding the user we're checking
  1534. */
  1535. public static function fetch_activity_level(&$userinfo)
  1536. {
  1537. // Ensure this is set
  1538. $userinfo['pointscache'] = round(($userinfo['pointscache'] ? $userinfo['pointscache'] : 0), 2);
  1539. // Begin at 0 points
  1540. $points = 0;
  1541. $userinfo['activitylevel'] = 0;
  1542. do
  1543. {
  1544. $userinfo['activitylevel']++;
  1545. $points = self::fetch_points_required($userinfo['activitylevel']);
  1546. }
  1547. while ($points < $userinfo['pointscache']);
  1548. // Set our actual activity level
  1549. $userinfo['activitylevel'] = ($userinfo['activitylevel'] > 1 ? ($userinfo['activitylevel'] - 1) : 1);
  1550. $currentlevel = self::fetch_points_required($userinfo['activitylevel']);
  1551. $nextlevel = self::fetch_points_required($userinfo['activitylevel'] + 1);
  1552. $difference = $nextlevel - $currentlevel;
  1553. // Set TNL
  1554. $userinfo['tonextlevel'] = $nextlevel - $userinfo['pointscache'];
  1555. $userinfo['tnlpercent'] = (round(($userinfo['tonextlevel'] / $difference), 4) * 100);
  1556. $userinfo['tnlpercent'] = round(($userinfo['tnlpercent'] > 100 ? 100 : $userinfo['tnlpercent']), 2);
  1557. $userinfo['levelpercent'] = round(abs($userinfo['tnlpercent'] - 100), 2);
  1558. }
  1559. /**
  1560. * Fetches how many points we need to reach a specified level
  1561. *
  1562. * @param integer The level we want to check
  1563. */
  1564. public static function fetch_points_required($level)
  1565. {
  1566. $retval = 0;
  1567. if (!self::$levels[$level])
  1568. {
  1569. switch ($level)
  1570. {
  1571. case 1:
  1572. $retval = 10;
  1573. break;
  1574. default:
  1575. // Fetch number of points needed based on previous levels
  1576. for ($i = $level; $i >= 1; $i--)
  1577. {
  1578. // Sum TNLs of all previous levels
  1579. $retval += self::fetch_points_tnl($i);
  1580. }
  1581. break;
  1582. }
  1583. self::$levels[$level] = $retval;
  1584. }
  1585. else
  1586. {
  1587. // Grab from cache
  1588. $retval = self::$levels[$level];
  1589. }
  1590. return $retval;
  1591. }
  1592. /**
  1593. * Fetches how many points we need to advance to the next level
  1594. *
  1595. * @param integer The level we currently are
  1596. */
  1597. private static function fetch_points_tnl($level)
  1598. {
  1599. $retval = 0;
  1600. if (!self::$tnlvalues[$level])
  1601. {
  1602. switch ($level)
  1603. {
  1604. case 1:
  1605. $retval = 10 + (10 * 0.025) + 10;
  1606. break;
  1607. default:
  1608. // Fetch number of points needed based on previous levels
  1609. $prevlevel = $level - 1;
  1610. $retval = self::$tnlvalues[$prevlevel] + (self::$tnlvalues[$prevlevel] * 0.025) + self::$tnlvalues[1];
  1611. break;
  1612. }
  1613. self::$tnlvalues[$level] = floor($retval);
  1614. }
  1615. else
  1616. {
  1617. // Grab from cache
  1618. $retval = self::$tnlvalues[$level];
  1619. }
  1620. return floor($retval);
  1621. }
  1622. /**
  1623. * Grabs our activity rating
  1624. *
  1625. * @param array Information regarding the user we're checking
  1626. */
  1627. public static function fetch_activity_rating(&$userinfo)
  1628. {
  1629. // Init this
  1630. $userinfo['target'] = array();
  1631. // Shorthand
  1632. $target = self::$vbulletin->options['dbtech_vbactivity_activity_target'];
  1633. $target = ($target >= 1 ? $target : 1); // Avoid division by zero
  1634. // Set targets
  1635. $userinfo['target']['weekly_target'] = $target;
  1636. $userinfo['target']['daily_target'] = round(($target / 7), 2);
  1637. $userinfo['target']['monthly_target'] = round((($target / 7) * date('t')), 2);
  1638. if ($userinfo['pointscache'] > 0)
  1639. {
  1640. // %age of weekly target
  1641. $userinfo['target']['weekly'] = self::calculate_target($userinfo['pointscache_week'], $userinfo['target']['weekly_target']);
  1642. $userinfo['target']['weekly_bar'] = ($userinfo['target']['weekly'] > 100 ? 100 : $userinfo['target']['weekly']);
  1643. // %age of daily target
  1644. $userinfo['target']['daily'] = self::calculate_target($userinfo['pointscache_day'], $userinfo['target']['daily_target']);
  1645. $userinfo['target']['daily_bar'] = ($userinfo['target']['daily'] > 100 ? 100 : $userinfo['target']['daily']);
  1646. // %age of monthly target
  1647. $userinfo['target']['monthly'] = self::calculate_target($userinfo['pointscache_month'], $userinfo['target']['monthly_target']);
  1648. $userinfo['target']['monthly_bar'] = ($userinfo['target']['monthly'] > 100 ? 100 : $userinfo['target']['monthly']);
  1649. }
  1650. else
  1651. {
  1652. // Default values
  1653. $userinfo['target']['weekly'] = $userinfo['target']['daily'] = $userinfo['target']['monthly'] =
  1654. $userinfo['target']['weekly_bar'] = $userinfo['target']['daily_bar'] = $userinfo['target']['monthly_bar'] = 0;
  1655. }
  1656. }
  1657. /**
  1658. * Calculates our relation to a target
  1659. *
  1660. * @param float The number of points we have
  1661. * @param float The target points
  1662. *
  1663. * @return float How close in % we are to achieving the current target
  1664. */
  1665. private static function calculate_target($points, $target)
  1666. {
  1667. $result = round($points / ($target / 100), 2);
  1668. return ($result >= 0 ? $result : 0);
  1669. }
  1670. /**
  1671. * Checks whether we meet a certain criteria
  1672. *
  1673. * @param integer The criteria ID we are checking
  1674. * @param array Information regarding the user we're checking
  1675. *
  1676. * @return boolean Whether this criteria has