BioTorrents.de’s version of Gazelle
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

donations.class.php 38KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029
  1. <?php
  2. define('BTC_API_URL', 'https://api.bitcoinaverage.com/ticker/global/EUR/');
  3. define('USD_API_URL', 'http://www.google.com/ig/calculator?hl=en&q=1USD=?EUR');
  4. class Donations
  5. {
  6. private static $ForumDescriptions = array(
  7. "I want only two houses, rather than seven... I feel like letting go of things",
  8. "A billion here, a billion there, sooner or later it adds up to real money.",
  9. "I've cut back, because I'm buying a house in the West Village.",
  10. "Some girls are just born with glitter in their veins.",
  11. "I get half a million just to show up at parties. My life is, like, really, really fun.",
  12. "Some people change when they think they're a star or something",
  13. "I'd rather not talk about money. It’s kind of gross.",
  14. "I have not been to my house in Bermuda for two or three years, and the same goes for my house in Portofino. How long do I have to keep leading this life of sacrifice?",
  15. "When I see someone who is making anywhere from $300,000 to $750,000 a year, that's middle class.",
  16. "Money doesn't make you happy. I now have $50 million but I was just as happy when I had $48 million.",
  17. "I'd rather smoke crack than eat cheese from a tin.",
  18. "I am who I am. I can’t pretend to be somebody who makes $25,000 a year.",
  19. "A girl never knows when she might need a couple of diamonds at ten 'o' clock in the morning.",
  20. "I wouldn't run for president. I wouldn't want to move to a smaller house.",
  21. "I have the stardom glow.",
  22. "What's Walmart? Do they like, sell wall stuff?",
  23. "Whenever I watch TV and see those poor starving kids all over the world, I can't help but cry. I mean I'd love to be skinny like that, but not with all those flies and death and stuff.",
  24. "Too much money ain't enough money.",
  25. "What's a soup kitchen?",
  26. "I work very hard and I’m worth every cent!",
  27. "To all my Barbies out there who date Benjamin Franklin, George Washington, Abraham Lincoln, you'll be better off in life. Get that money."
  28. );
  29. private static $IsSchedule = false;
  30. public static function regular_donate($UserID, $DonationAmount, $Source, $Reason, $Currency = "EUR")
  31. {
  32. self::donate(
  33. $UserID,
  34. array(
  35. "Source" => $Source,
  36. "Price" => $DonationAmount,
  37. "Currency" => $Currency,
  38. "Source" => $Source,
  39. "Reason" => $Reason,
  40. "SendPM" => true)
  41. );
  42. }
  43. public static function donate($UserID, $Args)
  44. {
  45. $UserID = (int)$UserID;
  46. $QueryID = G::$DB->get_query_id();
  47. G::$DB->query("
  48. SELECT 1
  49. FROM users_main
  50. WHERE ID = '$UserID'
  51. LIMIT 1");
  52. if (G::$DB->has_results()) {
  53. G::$Cache->InternalCache = false;
  54. foreach ($Args as &$Arg) {
  55. $Arg = db_string($Arg);
  56. }
  57. extract($Args);
  58. // We don't always get a date passed in.
  59. if (empty($Date)) {
  60. $Date = sqltime();
  61. }
  62. // Get the ID of the staff member making the edit
  63. $AddedBy = 0;
  64. if (!self::$IsSchedule) {
  65. $AddedBy = G::$LoggedUser['ID'];
  66. }
  67. // Legacy donor, should remove at some point
  68. G::$DB->query("
  69. UPDATE users_info
  70. SET Donor = '1'
  71. WHERE UserID = '$UserID'");
  72. // Give them the extra invite
  73. $ExtraInvite = G::$DB->affected_rows();
  74. // A staff member is directly manipulating donor points
  75. if (isset($Manipulation) && $Manipulation === "Direct") {
  76. $DonorPoints = $Rank;
  77. $AdjustedRank = $Rank >= MAX_EXTRA_RANK ? MAX_EXTRA_RANK : $Rank;
  78. G::$DB->query("
  79. INSERT INTO users_donor_ranks
  80. (UserID, Rank, TotalRank, DonationTime, RankExpirationTime)
  81. VALUES
  82. ('$UserID', '$AdjustedRank', '$TotalRank', '$Date', NOW())
  83. ON DUPLICATE KEY UPDATE
  84. Rank = '$AdjustedRank',
  85. TotalRank = '$TotalRank',
  86. DonationTime = '$Date',
  87. RankExpirationTime = NOW()");
  88. } else {
  89. // Donations from the store get donor points directly, no need to calculate them
  90. if ($Source == "Store Parser") {
  91. $ConvertedPrice = self::currency_exchange($Amount * $Price, $Currency);
  92. } else {
  93. $ConvertedPrice = self::currency_exchange($Price, $Currency);
  94. $DonorPoints = self::calculate_rank($ConvertedPrice);
  95. }
  96. $IncreaseRank = $DonorPoints;
  97. // Rank is the same thing as DonorPoints
  98. $CurrentRank = self::get_rank($UserID);
  99. // A user's donor rank can never exceed MAX_EXTRA_RANK
  100. // If the amount they donated causes it to overflow, chnage it to MAX_EXTRA_RANK
  101. // The total rank isn't affected by this, so their original donor point value is added to it
  102. if (($CurrentRank + $DonorPoints) >= MAX_EXTRA_RANK) {
  103. $AdjustedRank = MAX_EXTRA_RANK;
  104. } else {
  105. $AdjustedRank = $CurrentRank + $DonorPoints;
  106. }
  107. G::$DB->query("
  108. INSERT INTO users_donor_ranks
  109. (UserID, Rank, TotalRank, DonationTime, RankExpirationTime)
  110. VALUES
  111. ('$UserID', '$AdjustedRank', '$DonorPoints', '$Date', NOW())
  112. ON DUPLICATE KEY UPDATE
  113. Rank = '$AdjustedRank',
  114. TotalRank = TotalRank + '$DonorPoints',
  115. DonationTime = '$Date',
  116. RankExpirationTime = NOW()");
  117. }
  118. // Donor cache key is outdated
  119. G::$Cache->delete_value("donor_info_$UserID");
  120. // Get their rank
  121. $Rank = self::get_rank($UserID);
  122. $TotalRank = self::get_total_rank($UserID);
  123. // Now that their rank and total rank has been set, we can calculate their special rank
  124. self::calculate_special_rank($UserID);
  125. // Hand out invites
  126. G::$DB->query("
  127. SELECT InvitesRecievedRank
  128. FROM users_donor_ranks
  129. WHERE UserID = '$UserID'");
  130. list($InvitesRecievedRank) = G::$DB->next_record();
  131. $AdjustedRank = $Rank >= MAX_RANK ? (MAX_RANK - 1) : $Rank;
  132. $InviteRank = $AdjustedRank - $InvitesRecievedRank;
  133. if ($InviteRank > 0) {
  134. $Invites = $ExtraInvite ? ($InviteRank + 1) : $InviteRank;
  135. G::$DB->query("
  136. UPDATE users_main
  137. SET Invites = Invites + '$Invites'
  138. WHERE ID = $UserID");
  139. G::$DB->query("
  140. UPDATE users_donor_ranks
  141. SET InvitesRecievedRank = '$AdjustedRank'
  142. WHERE UserID = '$UserID'");
  143. }
  144. // Send them a thank you PM
  145. if ($SendPM) {
  146. $Subject = "Your contribution has been received and credited. Thank you!";
  147. $Body = self::get_pm_body($Source, $Currency, $Price, $IncreaseRank, $Rank);
  148. Misc::send_pm($UserID, 0, $Subject, $Body);
  149. }
  150. // Lastly, add this donation to our history
  151. G::$DB->query("
  152. INSERT INTO donations
  153. (UserID, Amount, Source, Reason, Currency, Email, Time, AddedBy, Rank, TotalRank)
  154. VALUES
  155. ('$UserID', '$ConvertedPrice', '$Source', '$Reason', '$Currency', '', '$Date', '$AddedBy', '$DonorPoints', '$TotalRank')");
  156. // Clear their user cache keys because the users_info values has been modified
  157. G::$Cache->delete_value("user_info_$UserID");
  158. G::$Cache->delete_value("user_info_heavy_$UserID");
  159. G::$Cache->delete_value("donor_info_$UserID");
  160. }
  161. G::$DB->set_query_id($QueryID);
  162. }
  163. private static function calculate_special_rank($UserID)
  164. {
  165. $UserID = (int)$UserID;
  166. $QueryID = G::$DB->get_query_id();
  167. // Are they are special?
  168. G::$DB->query("
  169. SELECT TotalRank, SpecialRank
  170. FROM users_donor_ranks
  171. WHERE UserID = '$UserID'");
  172. if (G::$DB->has_results()) {
  173. // Adjust their special rank depending on the total rank.
  174. list($TotalRank, $SpecialRank) = G::$DB->next_record();
  175. if ($TotalRank < 10) {
  176. $SpecialRank = 0;
  177. }
  178. if ($SpecialRank < 1 && $TotalRank >= 10) {
  179. Misc::send_pm($UserID, 0, "You've Reached Special Donor Rank #1! You've Earned: One User Pick. Details Inside.", self::get_special_rank_one_pm());
  180. $SpecialRank = 1;
  181. }
  182. if ($SpecialRank < 2 && $TotalRank >= 20) {
  183. Misc::send_pm($UserID, 0, "You've Reached Special Donor Rank #2! You've Earned: The Double-Avatar. Details Inside.", self::get_special_rank_two_pm());
  184. $SpecialRank = 2;
  185. }
  186. if ($SpecialRank < 3 && $TotalRank >= 50) {
  187. Misc::send_pm($UserID, 0, "You've Reached Special Donor Rank #3! You've Earned: Diamond Rank. Details Inside.", self::get_special_rank_three_pm());
  188. $SpecialRank = 3;
  189. }
  190. // Make them special
  191. G::$DB->query("
  192. UPDATE users_donor_ranks
  193. SET SpecialRank = '$SpecialRank'
  194. WHERE UserID = '$UserID'");
  195. G::$Cache->delete_value("donor_info_$UserID");
  196. }
  197. G::$DB->set_query_id($QueryID);
  198. }
  199. public static function schedule()
  200. {
  201. self::$IsSchedule = true;
  202. DonationsBitcoin::find_new_donations();
  203. self::expire_ranks();
  204. self::get_new_conversion_rates();
  205. }
  206. public static function expire_ranks()
  207. {
  208. $QueryID = G::$DB->get_query_id();
  209. G::$DB->query("
  210. SELECT UserID, Rank
  211. FROM users_donor_ranks
  212. WHERE Rank > 1
  213. AND SpecialRank != 3
  214. AND RankExpirationTime < NOW() - INTERVAL 766 HOUR");
  215. // 2 hours less than 32 days to account for schedule run times
  216. if (G::$DB->record_count() > 0) {
  217. $UserIDs = [];
  218. while (list($UserID, $Rank) = G::$DB->next_record()) {
  219. G::$Cache->delete_value("donor_info_$UserID");
  220. G::$Cache->delete_value("donor_title_$UserID");
  221. G::$Cache->delete_value("donor_profile_rewards_$UserID");
  222. $UserIDs[] = $UserID;
  223. }
  224. $In = implode(',', $UserIDs);
  225. G::$DB->query("
  226. UPDATE users_donor_ranks
  227. SET Rank = Rank - IF(Rank = " . MAX_RANK . ", 2, 1), RankExpirationTime = NOW()
  228. WHERE UserID IN ($In)");
  229. }
  230. G::$DB->set_query_id($QueryID);
  231. }
  232. private static function calculate_rank($Amount)
  233. {
  234. return floor($Amount / 5);
  235. }
  236. public static function update_rank($UserID, $Rank, $TotalRank, $Reason)
  237. {
  238. $Rank = (int)$Rank;
  239. $TotalRank = (int)$TotalRank;
  240. self::donate(
  241. $UserID,
  242. array(
  243. "Manipulation" => "Direct",
  244. "Rank" => $Rank,
  245. "TotalRank" => $TotalRank,
  246. "Reason" => $Reason,
  247. "Source" => "Modify Values",
  248. "Currency" => "EUR")
  249. );
  250. }
  251. public static function hide_stats($UserID)
  252. {
  253. $QueryID = G::$DB->get_query_id();
  254. G::$DB->query("
  255. INSERT INTO users_donor_ranks
  256. (UserID, Hidden)
  257. VALUES
  258. ('$UserID', '1')
  259. ON DUPLICATE KEY UPDATE
  260. Hidden = '1'");
  261. G::$DB->set_query_id($QueryID);
  262. }
  263. public static function show_stats($UserID)
  264. {
  265. $QueryID = G::$DB->get_query_id();
  266. G::$DB->query("
  267. INSERT INTO users_donor_ranks
  268. (UserID, Hidden)
  269. VALUES
  270. ('$UserID', '0')
  271. ON DUPLICATE KEY UPDATE
  272. Hidden = '0'");
  273. G::$DB->set_query_id($QueryID);
  274. }
  275. public static function is_visible($UserID)
  276. {
  277. $QueryID = G::$DB->get_query_id();
  278. G::$DB->query("
  279. SELECT Hidden
  280. FROM users_donor_ranks
  281. WHERE Hidden = '0'
  282. AND UserID = '$UserID'");
  283. $HasResults = G::$DB->has_results();
  284. G::$DB->set_query_id($QueryID);
  285. return $HasResults;
  286. }
  287. public static function has_donor_forum($UserID)
  288. {
  289. return self::get_rank($UserID) >= DONOR_FORUM_RANK || self::get_special_rank($UserID) >= MAX_SPECIAL_RANK;
  290. }
  291. /**
  292. * Put all the common donor info in the same cache key to save some cache calls
  293. */
  294. public static function get_donor_info($UserID)
  295. {
  296. // Our cache class should prevent identical memcached requests
  297. $DonorInfo = G::$Cache->get_value("donor_info_$UserID");
  298. if ($DonorInfo === false) {
  299. $QueryID = G::$DB->get_query_id();
  300. G::$DB->query("
  301. SELECT
  302. Rank,
  303. SpecialRank,
  304. TotalRank,
  305. DonationTime,
  306. RankExpirationTime + INTERVAL 766 HOUR
  307. FROM users_donor_ranks
  308. WHERE UserID = '$UserID'");
  309. // 2 hours less than 32 days to account for schedule run times
  310. if (G::$DB->has_results()) {
  311. list($Rank, $SpecialRank, $TotalRank, $DonationTime, $ExpireTime) = G::$DB->next_record(MYSQLI_NUM, false);
  312. if ($DonationTime === null) {
  313. $DonationTime = 0;
  314. }
  315. if ($ExpireTime === null) {
  316. $ExpireTime = 0;
  317. }
  318. } else {
  319. $Rank = $SpecialRank = $TotalRank = $DonationTime = $ExpireTime = 0;
  320. }
  321. if (Permissions::is_mod($UserID)) {
  322. $Rank = MAX_EXTRA_RANK;
  323. $SpecialRank = MAX_SPECIAL_RANK;
  324. }
  325. G::$DB->query("
  326. SELECT
  327. IconMouseOverText,
  328. AvatarMouseOverText,
  329. CustomIcon,
  330. CustomIconLink,
  331. SecondAvatar
  332. FROM donor_rewards
  333. WHERE UserID = '$UserID'");
  334. $Rewards = G::$DB->next_record(MYSQLI_ASSOC);
  335. G::$DB->set_query_id($QueryID);
  336. $DonorInfo = array(
  337. 'Rank' => (int)$Rank,
  338. 'SRank' => (int)$SpecialRank,
  339. 'TotRank' => (int)$TotalRank,
  340. 'Time' => $DonationTime,
  341. 'ExpireTime' => $ExpireTime,
  342. 'Rewards' => $Rewards
  343. );
  344. G::$Cache->cache_value("donor_info_$UserID", $DonorInfo, 0);
  345. }
  346. return $DonorInfo;
  347. }
  348. public static function get_rank($UserID)
  349. {
  350. return self::get_donor_info($UserID)['Rank'];
  351. }
  352. public static function get_special_rank($UserID)
  353. {
  354. return self::get_donor_info($UserID)['SRank'];
  355. }
  356. public static function get_total_rank($UserID)
  357. {
  358. return self::get_donor_info($UserID)['TotRank'];
  359. }
  360. public static function get_donation_time($UserID)
  361. {
  362. return self::get_donor_info($UserID)['Time'];
  363. }
  364. public static function get_personal_collages($UserID)
  365. {
  366. $DonorInfo = self::get_donor_info($UserID);
  367. if ($DonorInfo['SRank'] == MAX_SPECIAL_RANK) {
  368. $Collages = 5;
  369. } else {
  370. $Collages = min($DonorInfo['Rank'], 5); // One extra collage per donor rank up to 5
  371. }
  372. return $Collages;
  373. }
  374. public static function get_titles($UserID)
  375. {
  376. $Results = G::$Cache->get_value("donor_title_$UserID");
  377. if ($Results === false) {
  378. $QueryID = G::$DB->get_query_id();
  379. G::$DB->query("
  380. SELECT Prefix, Suffix, UseComma
  381. FROM donor_forum_usernames
  382. WHERE UserID = '$UserID'");
  383. $Results = G::$DB->next_record();
  384. G::$DB->set_query_id($QueryID);
  385. G::$Cache->cache_value("donor_title_$UserID", $Results, 0);
  386. }
  387. return $Results;
  388. }
  389. public static function get_enabled_rewards($UserID)
  390. {
  391. $Rewards = [];
  392. $Rank = self::get_rank($UserID);
  393. $SpecialRank = self::get_special_rank($UserID);
  394. $HasAll = $SpecialRank == 3;
  395. $Rewards = array(
  396. 'HasAvatarMouseOverText' => false,
  397. 'HasCustomDonorIcon' => false,
  398. 'HasDonorForum' => false,
  399. 'HasDonorIconLink' => false,
  400. 'HasDonorIconMouseOverText' => false,
  401. 'HasProfileInfo1' => false,
  402. 'HasProfileInfo2' => false,
  403. 'HasProfileInfo3' => false,
  404. 'HasProfileInfo4' => false,
  405. 'HasSecondAvatar' => false);
  406. // if ($Rank >= 1 || $HasAll) {
  407. //
  408. // }
  409. if ($Rank >= 2 || $HasAll) {
  410. $Rewards["HasDonorIconMouseOverText"] = true;
  411. $Rewards["HasProfileInfo1"] = true;
  412. }
  413. if ($Rank >= 3 || $HasAll) {
  414. $Rewards["HasAvatarMouseOverText"] = true;
  415. $Rewards["HasProfileInfo2"] = true;
  416. }
  417. if ($Rank >= 4 || $HasAll) {
  418. $Rewards["HasDonorIconLink"] = true;
  419. $Rewards["HasProfileInfo3"] = true;
  420. }
  421. if ($Rank >= MAX_RANK || $HasAll) {
  422. $Rewards["HasCustomDonorIcon"] = true;
  423. $Rewards["HasDonorForum"] = true;
  424. $Rewards["HasProfileInfo4"] = true;
  425. }
  426. if ($SpecialRank >= 2) {
  427. $Rewards["HasSecondAvatar"] = true;
  428. }
  429. return $Rewards;
  430. }
  431. public static function get_rewards($UserID)
  432. {
  433. return self::get_donor_info($UserID)['Rewards'];
  434. }
  435. public static function get_profile_rewards($UserID)
  436. {
  437. $Results = G::$Cache->get_value("donor_profile_rewards_$UserID");
  438. if ($Results === false) {
  439. $QueryID = G::$DB->get_query_id();
  440. G::$DB->query("
  441. SELECT
  442. ProfileInfo1,
  443. ProfileInfoTitle1,
  444. ProfileInfo2,
  445. ProfileInfoTitle2,
  446. ProfileInfo3,
  447. ProfileInfoTitle3,
  448. ProfileInfo4,
  449. ProfileInfoTitle4
  450. FROM donor_rewards
  451. WHERE UserID = '$UserID'");
  452. $Results = G::$DB->next_record();
  453. G::$DB->set_query_id($QueryID);
  454. G::$Cache->cache_value("donor_profile_rewards_$UserID", $Results, 0);
  455. }
  456. return $Results;
  457. }
  458. private static function add_profile_info_reward($Counter, &$Insert, &$Values, &$Update)
  459. {
  460. if (isset($_POST["profile_title_" . $Counter]) && isset($_POST["profile_info_" . $Counter])) {
  461. $ProfileTitle = db_string($_POST["profile_title_" . $Counter]);
  462. $ProfileInfo = db_string($_POST["profile_info_" . $Counter]);
  463. $ProfileInfoTitleSQL = "ProfileInfoTitle" . $Counter;
  464. $ProfileInfoSQL = "ProfileInfo" . $Counter;
  465. $Insert[] = "$ProfileInfoTitleSQL";
  466. $Values[] = "'$ProfileInfoTitle'";
  467. $Update[] = "$ProfileInfoTitleSQL = '$ProfileTitle'";
  468. $Insert[] = "$ProfileInfoSQL";
  469. $Values[] = "'$ProfileInfo'";
  470. $Update[] = "$ProfileInfoSQL = '$ProfileInfo'";
  471. }
  472. }
  473. public static function update_rewards($UserID)
  474. {
  475. $Rank = self::get_rank($UserID);
  476. $SpecialRank = self::get_special_rank($UserID);
  477. $HasAll = $SpecialRank == 3;
  478. $Counter = 0;
  479. $Insert = [];
  480. $Values = [];
  481. $Update = [];
  482. $Insert[] = "UserID";
  483. $Values[] = "'$UserID'";
  484. if ($Rank >= 1 || $HasAll) {
  485. }
  486. if ($Rank >= 2 || $HasAll) {
  487. if (isset($_POST['donor_icon_mouse_over_text'])) {
  488. $IconMouseOverText = db_string($_POST['donor_icon_mouse_over_text']);
  489. $Insert[] = "IconMouseOverText";
  490. $Values[] = "'$IconMouseOverText'";
  491. $Update[] = "IconMouseOverText = '$IconMouseOverText'";
  492. }
  493. $Counter++;
  494. }
  495. if ($Rank >= 3 || $HasAll) {
  496. if (isset($_POST['avatar_mouse_over_text'])) {
  497. $AvatarMouseOverText = db_string($_POST['avatar_mouse_over_text']);
  498. $Insert[] = "AvatarMouseOverText";
  499. $Values[] = "'$AvatarMouseOverText'";
  500. $Update[] = "AvatarMouseOverText = '$AvatarMouseOverText'";
  501. }
  502. $Counter++;
  503. }
  504. if ($Rank >= 4 || $HasAll) {
  505. if (isset($_POST['donor_icon_link'])) {
  506. $CustomIconLink = db_string($_POST['donor_icon_link']);
  507. if (!Misc::is_valid_url($CustomIconLink)) {
  508. $CustomIconLink = '';
  509. }
  510. $Insert[] = "CustomIconLink";
  511. $Values[] = "'$CustomIconLink'";
  512. $Update[] = "CustomIconLink = '$CustomIconLink'";
  513. }
  514. $Counter++;
  515. }
  516. if ($Rank >= MAX_RANK || $HasAll) {
  517. if (isset($_POST['donor_icon_custom_url'])) {
  518. $CustomIcon = db_string($_POST['donor_icon_custom_url']);
  519. if (!Misc::is_valid_url($CustomIcon)) {
  520. $CustomIcon = '';
  521. }
  522. $Insert[] = "CustomIcon";
  523. $Values[] = "'$CustomIcon'";
  524. $Update[] = "CustomIcon = '$CustomIcon'";
  525. }
  526. self::update_titles($UserID, $_POST['donor_title_prefix'], $_POST['donor_title_suffix'], $_POST['donor_title_comma']);
  527. $Counter++;
  528. }
  529. for ($i = 1; $i <= $Counter; $i++) {
  530. self::add_profile_info_reward($i, $Insert, $Values, $Update);
  531. }
  532. if ($SpecialRank >= 2) {
  533. if (isset($_POST['second_avatar'])) {
  534. $SecondAvatar = db_string($_POST['second_avatar']);
  535. if (!Misc::is_valid_url($SecondAvatar)) {
  536. $SecondAvatar = '';
  537. }
  538. $Insert[] = "SecondAvatar";
  539. $Values[] = "'$SecondAvatar'";
  540. $Update[] = "SecondAvatar = '$SecondAvatar'";
  541. }
  542. }
  543. $Insert = implode(', ', $Insert);
  544. $Values = implode(', ', $Values);
  545. $Update = implode(', ', $Update);
  546. if ($Counter > 0) {
  547. $QueryID = G::$DB->get_query_id();
  548. G::$DB->query("
  549. INSERT INTO donor_rewards
  550. ($Insert)
  551. VALUES
  552. ($Values)
  553. ON DUPLICATE KEY UPDATE
  554. $Update");
  555. G::$DB->set_query_id($QueryID);
  556. }
  557. G::$Cache->delete_value("donor_profile_rewards_$UserID");
  558. G::$Cache->delete_value("donor_info_$UserID");
  559. }
  560. public static function update_titles($UserID, $Prefix, $Suffix, $UseComma)
  561. {
  562. $QueryID = G::$DB->get_query_id();
  563. $Prefix = trim(db_string($Prefix));
  564. $Suffix = trim(db_string($Suffix));
  565. $UseComma = empty($UseComma);
  566. G::$DB->query("
  567. INSERT INTO donor_forum_usernames
  568. (UserID, Prefix, Suffix, UseComma)
  569. VALUES
  570. ('$UserID', '$Prefix', '$Suffix', '$UseComma')
  571. ON DUPLICATE KEY UPDATE
  572. Prefix = '$Prefix',
  573. Suffix = '$Suffix',
  574. UseComma = '$UseComma'");
  575. G::$Cache->delete_value("donor_title_$UserID");
  576. G::$DB->set_query_id($QueryID);
  577. }
  578. public static function get_donation_history($UserID)
  579. {
  580. $UserID = (int)$UserID;
  581. if (empty($UserID)) {
  582. error(404);
  583. }
  584. $QueryID = G::$DB->get_query_id();
  585. G::$DB->query("
  586. SELECT Amount, Email, Time, Currency, Reason, Source, AddedBy, Rank, TotalRank
  587. FROM donations
  588. WHERE UserID = '$UserID'
  589. ORDER BY Time DESC");
  590. $DonationHistory = G::$DB->to_array(false, MYSQLI_ASSOC, false);
  591. G::$DB->set_query_id($QueryID);
  592. return $DonationHistory;
  593. }
  594. public static function get_rank_expiration($UserID)
  595. {
  596. $DonorInfo = self::get_donor_info($UserID);
  597. if ($DonorInfo['SRank'] == MAX_SPECIAL_RANK || $DonorInfo['Rank'] == 1) {
  598. $Return = 'Never';
  599. } elseif ($DonorInfo['ExpireTime']) {
  600. $ExpireTime = strtotime($DonorInfo['ExpireTime']);
  601. if ($ExpireTime - time() < 60) {
  602. $Return = 'Soon';
  603. } else {
  604. $Expiration = time_diff($ExpireTime); // 32 days
  605. $Return = "in $Expiration";
  606. }
  607. } else {
  608. $Return = '';
  609. }
  610. return $Return;
  611. }
  612. public static function get_leaderboard_position($UserID)
  613. {
  614. $UserID = (int)$UserID;
  615. $QueryID = G::$DB->get_query_id();
  616. G::$DB->query("SET @RowNum := 0");
  617. G::$DB->query("
  618. SELECT Position
  619. FROM (
  620. SELECT d.UserID, @RowNum := @RowNum + 1 AS Position
  621. FROM users_donor_ranks AS d
  622. ORDER BY TotalRank DESC
  623. ) l
  624. WHERE UserID = '$UserID'");
  625. if (G::$DB->has_results()) {
  626. list($Position) = G::$DB->next_record();
  627. } else {
  628. $Position = 0;
  629. }
  630. G::$DB->set_query_id($QueryID);
  631. return $Position;
  632. }
  633. public static function is_donor($UserID)
  634. {
  635. return self::get_rank($UserID) > 0;
  636. }
  637. public static function currency_exchange($Amount, $Currency)
  638. {
  639. if (!self::is_valid_currency($Currency)) {
  640. error("$Currency is not valid currency");
  641. }
  642. switch ($Currency) {
  643. case 'USD':
  644. $Amount = self::usd_to_euro($Amount);
  645. break;
  646. case 'BTC':
  647. $Amount = self::btc_to_euro($Amount);
  648. break;
  649. default:
  650. break;
  651. }
  652. return round($Amount, 2);
  653. }
  654. public static function is_valid_currency($Currency)
  655. {
  656. return $Currency === 'EUR' || $Currency === 'BTC' || $Currency === 'USD';
  657. }
  658. public static function btc_to_euro($Amount)
  659. {
  660. $Rate = G::$Cache->get_value('btc_rate');
  661. if (empty($Rate)) {
  662. $Rate = self::get_stored_conversion_rate('BTC');
  663. G::$Cache->cache_value('btc_rate', $Rate, 86400);
  664. }
  665. return $Rate * $Amount;
  666. }
  667. public static function usd_to_euro($Amount)
  668. {
  669. $Rate = G::$Cache->get_value('usd_rate');
  670. if (empty($Rate)) {
  671. $Rate = self::get_stored_conversion_rate('USD');
  672. G::$Cache->cache_value('usd_rate', $Rate, 86400);
  673. }
  674. return $Rate * $Amount;
  675. }
  676. public static function get_stored_conversion_rate($Currency)
  677. {
  678. $QueryID = G::$DB->get_query_id();
  679. G::$DB->query("
  680. SELECT Rate
  681. FROM currency_conversion_rates
  682. WHERE Currency = '$Currency'");
  683. list($Rate) = G::$DB->next_record(MYSQLI_NUM, false);
  684. G::$DB->set_query_id($QueryID);
  685. return $Rate;
  686. }
  687. private static function set_stored_conversion_rate($Currency, $Rate)
  688. {
  689. $QueryID = G::$DB->get_query_id();
  690. G::$DB->query("
  691. REPLACE INTO currency_conversion_rates
  692. (Currency, Rate, Time)
  693. VALUES
  694. ('$Currency', $Rate, NOW())");
  695. if ($Currency === 'USD') {
  696. $KeyName = 'usd_rate';
  697. } elseif ($Currency === 'BTC') {
  698. $KeyName = 'btc_rate';
  699. }
  700. G::$Cache->cache_value($KeyName, $Rate, 86400);
  701. G::$DB->set_query_id($QueryID);
  702. }
  703. private static function get_new_conversion_rates()
  704. {
  705. if ($BTC = file_get_contents(BTC_API_URL)) {
  706. $BTC = json_decode($BTC, true);
  707. if (isset($BTC['24h_avg'])) {
  708. if ($Rate = round($BTC['24h_avg'], 4)) { // We don't need good precision
  709. self::set_stored_conversion_rate('BTC', $Rate);
  710. }
  711. }
  712. }
  713. if ($USD = file_get_contents(USD_API_URL)) {
  714. // Valid JSON isn't returned so we make it valid.
  715. $Replace = array(
  716. 'lhs' => '"lhs"',
  717. 'rhs' => '"rhs"',
  718. 'error' => '"error"',
  719. 'icc' => '"icc"'
  720. );
  721. $USD = str_replace(array_keys($Replace), array_values($Replace), $USD);
  722. $USD = json_decode($USD, true);
  723. if (isset($USD['rhs'])) {
  724. // The response is in format "# Euroes", extracts the numbers.
  725. $Rate = preg_split("/[\s,]+/", $USD['rhs']);
  726. if ($Rate = round($Rate[0], 4)) { // We don't need good precision
  727. self::set_stored_conversion_rate('USD', $Rate);
  728. }
  729. }
  730. }
  731. }
  732. public static function get_forum_description()
  733. {
  734. return self::$ForumDescriptions[rand(0, count(self::$ForumDescriptions) - 1)];
  735. }
  736. # todo: Figure out why this doesn't use the template system
  737. # What the fuck is this shit?
  738. private static function get_pm_body($Source, $Currency, $DonationAmount, $ReceivedRank, $CurrentRank)
  739. {
  740. if ($Currency != 'BTC') {
  741. $DonationAmount = number_format($DonationAmount, 2);
  742. }
  743. if ($CurrentRank >= MAX_RANK) {
  744. $CurrentRank = MAX_RANK - 1;
  745. } elseif ($CurrentRank == 5) {
  746. $CurrentRank = 4;
  747. }
  748. return "Thank you for your generosity and support. It's users like you who make all of this possible. What follows is a brief description of your transaction:
  749. [*][b]You Contributed:[/b] $DonationAmount $Currency
  750. [*][b]You Received:[/b] $ReceivedRank Donor Point".($ReceivedRank == 1 ? '' : 's')."
  751. [*][b]Your Donor Rank:[/b] Donor Rank # $CurrentRank
  752. Once again, thank you for your continued support of the site.
  753. Sincerely,
  754. ".SITE_NAME.' Staff
  755. [align=center][If you have any questions or concerns, please [url='.site_url().'staffpm.php]send a Staff PM[/url].]';
  756. }
  757. private static function get_special_rank_one_pm()
  758. {
  759. return 'Congratulations on reaching [url='.site_url().'forums.php?action=viewthread&threadid=178640&postid=4839790#post4839790]Special Rank #1[/url]! You\'ve been awarded [b]one user pick[/b]! This user pick will be featured on the '.SITE_NAME.' front page during an upcoming event. After you submit your pick, there is no guarantee as to how long it will take before your pick is featured. Picks will be featured on a first-submitted, first-served basis. Please abide by the following guidelines when making your selection:
  760. [*]Pick something that hasn\'t been chosen. You can tell if a pick has been used previously by looking at the collages it\'s in.
  761. [*]Complete the enclosed form carefully and completely.
  762. [*]Send a [url='.site_url().'staffpm.php]Staff PM[/url] with the completed form. Title this PM "Special Rank User Pick".
  763. [important][align=center]**The following form must be used. Do not edit the BBCode. Reply to this message with the completed form.**[/align][/important]
  764. [quote][align=center][size=10]SUBMISSION FORM[/size]
  765. [size=4]COPY & PASTE THE BBCODE INTO YOUR REPLY. FILL FORM WITH YOUR INFO. SUBMIT.[/size][/align]
  766. [code][align=center][size=10][u]FRONT PAGE[/u][/size][/align]
  767. [b][size=5][user]<NAME>[/user]\'s Special Donor Pick[/size][/b]
  768. [b]Artist Name - Album Title[/b]
  769. [b]Genre:[/b] Alternative, Rock, Grunge, ...
  770. [b]Torrents:[/b] Torrent Group Link Here
  771. [b]Review:[/b] [quote]Put your front page review here. The review should be a maximum of two small(ish) paragraphs or one medium-sized paragraph. Do not include a huge review for the front page portion of your post, as it will be truncated per the wishes of the Staff Team.[/quote]
  772. [align=center][size=10][u]FORUM POST[/u][/size][/align]
  773. [b][size=5][user]<NAME>[/user]\'s Special Donor Pick[/size][/b]
  774. [img]Album Cover URL[/img]
  775. [b]Artist Name - Album Title[/b]
  776. [b]Genre:[/b] Alternative, Rock, Grunge, ...
  777. [b]Torrents:[/b] Torrent Group Link Here
  778. [b]Release Info:[/b]
  779. [*][u]Release Date[/u]: Release Date Here
  780. [*][u]Tracks[/u]: Track Count Here
  781. [*][u]Length[/u]: Release Length Here
  782. [*][u]Label[/u]: Release Label Here
  783. [b]Credits:[/b]
  784. [*][b]Member Name 1:[/b] Role / Instruments Played
  785. [*][b]Member Name 2:[/b] Role / Instruments Played
  786. [*][b]Member Name 3:[/b] Role / Instruments Played
  787. [b]Track Listing:[/b]
  788. [#]Remove or add to this list...
  789. [#]...as is necessary.
  790. [#]Track 03
  791. [#]Track 04
  792. [#]Track 05
  793. [#]Track 06
  794. [#]Track 07
  795. [#]Track 08
  796. [#]Track 09
  797. [#]Track 10
  798. [b]Web site:[/b] Artist Web Site / Info Page / Fan Site Link Here
  799. [b]Review:[/b] [quote]Put your forum post review here. This review can be any length, but please be reasonable. You can write it yourself or source it from the Internet. Please try to include a source if you don\'t write your own review. If your review is too long, it will be included but put into "hide" tags by the person formatting the thread.[/quote][/code][/quote]
  800. [hide=Completed Submission Form (Example)][align=center][size=10][u]FRONT PAGE[/u][/size][/align]
  801. [b][size=5][user]DixieFlatline[/user]\'s Special Donor Pick[/size][/b]
  802. [b]The Door and The Window - Detailed Twang[/b]
  803. [b]Genre:[/b] Post-Punk, Rock, Experimental
  804. [b]Torrents:[/b] https://what.cd/torrents.php?id=649629
  805. [b]Review:[/b] [quote=Mutant Sounds]Stumblebum idiot savant songform fragmentation that vacillates between moments of delirious jerry-rigged inspiration and passages that border on the insufferably sophomoric, brought to you by this batch of DIY rabble rousers whose stance and attitude (complete with DIY manifestos on the back cover) aligned them strongly at the time with the likes of The Desperate Bicycles. Loosely yoked to a whole network of willfully rinky dink art damage, Nag and Bendle (2/3 of The Door And The Window) also participated in The 49 Americans, a group with a similar propensity for inspired faux naif art brut whimsy and Mark Perry (the other 1/3) was of course the leader of Alternative TV, whose NWW list included group The Good Missionaries also traffic in much the same sort of abstruse fuckery.[/quote]
  806. [align=center][size=10][u]FORUM POST[/u][/size][/align]
  807. [b][size=5][user]DixieFlatline[/user]\'s Special Donor Pick[/size][/b]
  808. [img]https://i.imgur.com/wXdQd.jpg[/img]
  809. [b]The Door and The Window - Detailed Twang[/b]
  810. [b]Genre:[/b] Post-Punk, Rock, Experimental
  811. [b]Torrents:[/b] https://what.cd/torrents.php?id=649629
  812. [b]Release Info:[/b]
  813. [*][u]Release Date[/u]: 1980 (Reissue: 2003)
  814. [*][u]Tracks[/u]: 12 (Reissue: 23)
  815. [*][u]Length[/u]: 1:09:11
  816. [*][u]Label[/u]: Overground Records
  817. [b]Credits:[/b]
  818. [*][b]Bendle:[/b] Guitar, Percussion, Vocals
  819. [*][b]Nag:[/b] Percussion, Synthesizer, Vocals
  820. [*][b]Mark Perry:[/b] Drums, Saxophone, Vocals (tracks: 1 to 12, 22, 23)
  821. [b]Track Listing:[/b]
  822. [#]Dads (4:41)
  823. [#]Habits (2:14)
  824. [#]We Do Scare Each Other (2:23)
  825. [#]Order And Obey (3:20)
  826. [#]He Feels Like A Doris (4:47)
  827. [#]Part-Time Punks (3:50)
  828. [#]In The Car (0:43)
  829. [#]Subculture Fashion Slaves (3:51)
  830. [#]Sticks And Stones (3:56)
  831. [#]Positive (4:24)
  832. [#]Why Must You Build Walls Around Us? (2:17)
  833. [#]Detailed Twang (1:50)
  834. [#]Subculture Fashion Slaves (Early Version) (3:26)
  835. [#]Nostradomus (1:38)
  836. [#]Don\'t Kill Colin (3:33)
  837. [#]Wurst Band (1:38)
  838. [#]Dig (2:10)
  839. [#]Production Line (4:05)
  840. [#]He Feels Like A Doris (Early Version) (3:25)
  841. [#]I Like Sound (1:25)
  842. [#]Innocent (1:38)
  843. [#]The Number One Entertainer (3:38)
  844. [#]C.C.H. (4:19)
  845. [b]Web site:[/b] https://www.wikipedia.org
  846. [b]Review:[/b] [quote=Mutant Sounds]Stumblebum idiot savant songform fragmentation that vacillates between moments of delirious jerry-rigged inspiration and passages that border on the insufferably sophomoric, brought to you by this batch of DIY rabble rousers whose stance and attitude (complete with DIY manifestos on the back cover) aligned them strongly at the time with the likes of The Desperate Bicycles. Loosely yoked to a whole network of willfully rinky dink art damage, Nag and Bendle (2/3 of The Door And The Window) also participated in The 49 Americans, a group with a similar propensity for inspired faux naif art brut whimsy and Mark Perry (the other 1/3) was of course the leader of Alternative TV, whose NWW list included group The Good Missionaries also traffic in much the same sort of abstruse fuckery.[/quote][/hide]
  847. At this time, we\'d like to thank you for your continued support of the site. The fact that you\'ve reached this milestone is testament to your belief in '.SITE_NAME.' as a project. It\'s dedicated users like you that keep us alive. We look forward to featuring your pick in an upcoming announcement.
  848. Sincerely,
  849. '.SITE_NAME.' Staff';
  850. }
  851. private static function get_special_rank_two_pm()
  852. {
  853. return 'Congratulations on reaching [url='.site_url().'forums.php?action=viewthread&threadid=178640&postid=4839790#post4839790]Special Rank #2[/url]! You\'ve been awarded [b]double avatar functionality[/b]! To set a second avatar, please enter a URL leading to a valid image in the new field which has been unlocked in your [b]Personal Settings[/b]. Any avatar you choose must abide by normal avatar rules. When running your cursor over your avatar, it will flip to the alternate choice you\'ve established. Other users will also be able to view both of your avatars using this method.
  854. At this time, we\'d like to thank you for your continued support of the site. The fact that you\'ve reached this milestone is testament to your belief in '.SITE_NAME.' as a project. It\'s dedicated users like you that keep us alive. Have fun with the new toy.
  855. Sincerely,
  856. '.SITE_NAME.' Staff';
  857. }
  858. private static function get_special_rank_three_pm()
  859. {
  860. return 'Congratulations on reaching [url='.site_url().'forums.php?action=viewthread&threadid=178640&postid=4839790#post4839790]Special Rank #3[/url]! You\'ve been awarded [b]Diamond Rank[/b]! Diamond Rank grants you the benefits associated with every Donor Rank up to and including Gold ([url='.site_url().'forums.php?action=viewthread&threadid=178640&postid=4839789#post4839789]Donor Rank #5[/url]). But unlike Donor Rank #5 - because Diamond Rank is a Special Rank - it will never expire.
  861. At this time, we\'d like to thank you for your continued support of the site. The fact that you\'ve reached this milestone is testament to your belief in '.SITE_NAME.' as a project. It\'s dedicated users like you that keep us alive. Consider yourself one of our top supporters!
  862. Sincerely,
  863. '.SITE_NAME.' Staff';
  864. }
  865. }