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.
 
 
 
 
 
 

467 lines
15 KiB

<?php
// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project
//
// All Rights Reserved. See copyright.txt for details and a complete list of authors.
// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
// $Id$
// TODO: product_id is meant for storing some information about the product
// purchased that led to the credits being added.
/**
* CreditsLib
*
* @uses TikiLib
*/
class CreditsLib extends TikiLib
{
public function getRawCredits($userId)
{
$result = $this->query(
"SELECT `creditId`, `credit_type`, `creation_date`, `expiration_date`, `total_amount`, `used_amount`" .
" FROM `tiki_credits` WHERE `userId` = ? ORDER BY `credit_type`, `creation_date`, `expiration_date`",
[$userId]
);
$credits = [];
while ($row = $result->fetchRow()) {
$credits[$row['creditId']] = $row;
}
return $credits;
}
public function getRawCreditsByType($userId, $credit_type)
{
$result = $this->query(
"SELECT `creditId`, `creation_date`, `expiration_date`, `total_amount`, `used_amount`" .
" FROM `tiki_credits`" .
" WHERE `userId` = ? AND `credit_type` = ? ORDER BY `credit_type`, `creation_date`, `expiration_date`",
[$userId, $credit_type]
);
$credits = [];
while ($row = $result->fetchRow()) {
$credits[$row['creditId']] = $row;
}
return $credits;
}
public function updateCreditType($credit_type, $display_text, $unit_text, $is_static_level = 'n', $scaling_divisor = 1)
{
$bindvars = [$credit_type, $display_text, $unit_text, $is_static_level, $scaling_divisor];
$result = $this->query(
"REPLACE INTO `tiki_credits_types`" .
" (`credit_type`, `display_text`, `unit_text`, `is_static_level`, `scaling_divisor`)" .
" VALUES (?, ?, ?, ?, ?)",
$bindvars
);
return $result;
}
public function getCreditTypes($staticonly = false)
{
$result = $this->query("SELECT `credit_type`, `display_text`, `unit_text`, `is_static_level`, `scaling_divisor` FROM `tiki_credits_types`");
$creditTypes = [];
while ($row = $result->fetchRow()) {
if ($staticonly && $row['is_static_level'] == 'y') {
$creditTypes[ $row['credit_type'] ] = $row;
} elseif (! $staticonly) {
$creditTypes[ $row['credit_type'] ] = $row;
}
}
return $creditTypes;
}
public function getCredits($userId)
{
$result = $this->query(
"SELECT `credit_type`, SUM(`total_amount`) total_amount, SUM(`used_amount`) used_amount" .
" FROM `tiki_credits`" .
" WHERE" .
" `userId` = ?" .
" AND (`expiration_date` IS NULL OR `expiration_date` > NOW())" .
" AND `creation_date` <= NOW()" .
" GROUP BY `credit_type`",
[$userId]
);
$credits = [];
while ($row = $result->fetchRow()) {
$credits[$row['credit_type']] = [
'remain' => $row['total_amount'] - $row['used_amount'],
'used' => $row['used_amount'],
'total' => $row['total_amount'],
];
}
// Handle level-type credits in a different manner
// Level of used amount stored in user preferences
// Total used (flow) from credits table
$userlib = TikiLib::lib('user');
$tikilib = TikiLib::lib('tiki');
$info = $userlib->get_userid_info($userId);
$creditTypes = $this->getCreditTypes();
foreach ($credits as $type => $crVal) {
if ($creditTypes[$type]['is_static_level'] == 'y') {
$prefName = "credits_level_" . $type;
$credits[$type]['used'] = (float) $tikilib->get_user_preference($info['login'], $prefName);
$credits[$type]['remain'] = $credits[$type]['total'] - $credits['used'];
}
}
// set zero for creditTypes that user does not have
foreach ($creditTypes as $k => $c) {
if (! array_key_exists($k, $credits)) {
$credits[$k]['used'] = 0;
$credits[$k]['remain'] = 0;
$credits[$k]['total'] = 0;
}
}
return $credits;
}
public function getScaledCredits($userId)
{
$creditTypes = $this->getCreditTypes();
$credits = $this->getCredits($userId);
foreach ($credits as $type => &$data) {
$factor = 1;
if (isset($creditTypes[$type]) && $creditTypes[$type]['scaling_divisor']) {
$factor = $creditTypes[$type]['scaling_divisor'];
}
if (isset($creditTypes[$type]) && $display_text = $creditTypes[$type]['display_text']) {
$data['display_text'] = $display_text;
} else {
$data['display_text'] = $type;
}
if (isset($creditTypes[$type]) && $unit_text = $creditTypes[$type]['unit_text']) {
$data['unit_text'] = $unit_text;
} else {
$data['unit_text'] = '';
}
$data['discreet_total'] = $this->scale($data['total'], $factor);
$data['discreet_used'] = empty($data['discreet_total']) ? 0 : $data['used'] / ($data['total'] / $data['discreet_total']);
$data['discreet_remain'] = $data['discreet_total'] - $data['discreet_used'];
$data['empty'] = $data['remain'] <= 0;
$data['low'] = $data['remain'] <= $data['total'] * .15;
}
return $credits;
}
private function scale($value, $factor = 1)
{
// 1.5 log( (x/fac)^2 + 1 )
return floor(1.5 * log(($value / $factor) * ($value / $factor) + 1));
}
public function removeCreditBlock($creditId)
{
$this->query("DELETE FROM `tiki_credits` WHERE `creditId` = ?", [$creditId]);
}
public function replaceCredit($creditId, $type, $used, $total, $validFrom, $expirationDate)
{
if (! empty($expirationDate)) {
$expirationDate = date('Y-m-d H:i:s', $time = strtotime($expirationDate));
}
if ($time === false) {
return false;
}
$validFrom = date('Y-m-d H:i:s', $time = strtotime($validFrom));
if ($time === false) {
return false;
}
$this->query(
"UPDATE `tiki_credits`
SET `credit_type` = ?, `used_amount` = ?, `total_amount` = ?, `expiration_date` = if (?='',NULL,?), `creation_date` = ?
WHERE `creditId` = ?",
[$type, $used, $total, $expirationDate, $expirationDate, $validFrom, $creditId]
);
}
/**
* Adds a new credits entry for the user.
*/
public function addCredits($userId, $creditType, $amount, $expirationDate = null, $validFrom = null)
{
if (! $amount) {
return false;
}
if (! empty($expirationDate)) {
$expirationDate = date('Y-m-d H:i:s', $time = strtotime($expirationDate));
}
if ($time === false) {
return false;
}
if (! empty($validFrom)) {
$validFrom = date('Y-m-d H:i:s', $time = strtotime($validFrom));
}
if ($time === false) {
return false;
}
$this->query(
"INSERT INTO `tiki_credits`
(`userId`, `credit_type`, `total_amount`, `expiration_date`, `creation_date`)
VALUES(?,?,?,if (? = '', NULL, ?),if (?='', NULL, ?))",
[$userId, $creditType, $amount, $expirationDate, $expirationDate, $validFrom, $validFrom]
);
return true;
}
/**
* Use the user's credits of a certain type. If the user does not have
* enough credits, the function will return false. Credits may be used from
* different entries. Entries expiring soon will be used first.
*/
public function useCredits($userId, $creditType, $amount, $product_id = null)
{
if ($amount == 0) {
return true;
}
// Level-type credits
$creditTypes = $this->getCreditTypes();
if ($creditTypes[$creditType]['is_static_level'] == 'y') {
$credits = $this->getCredits($userId);
if (! array_key_exists($creditType, $credits)) {
return false;
}
if ($credits[$creditType]['remain'] > 0) {
$userlib = TikiLib::lib('user');
$tikilib = TikiLib::lib('tiki');
$info = $userlib->get_userid_info($userId);
// Expense all credits if not enough
$toUse = min($credits[$creditType]['used'] + $amount, $credits[$creditType]['remain']);
$prefName = "credits_level_" . $creditType;
$tikilib->set_user_preference(
$info['login'],
$prefName,
$toUse
);
if ($amount > 0) {
$this->_recCredits($userId, $creditType, $amount, $product_id);
}
return true;
}
return false;
}
// Expendable credits
$result = $this->query(
"SELECT `creditId`, `product_id`, `total_amount` - `used_amount` as available
FROM `tiki_credits`
WHERE
( `expiration_date` > NOW() OR `expiration_date` IS NULL )
AND `creation_date` <= NOW()
AND `userId` = ?
AND `credit_type` = ?
ORDER BY if (`expiration_date` IS NULL, 1000000, DATEDIFF(`expiration_date`, NOW())) ASC",
[$userId, $creditType]
);
$total = 0;
$list = [];
while ($row = $result->fetchRow()) {
$total += $row['available'];
$list[] = $row;
}
if ($total == 0) {
return false;
}
if ($amount > $total) {
$amount = $total;
}
if ($amount > 0) {
$this->_recCredits($userId, $creditType, $amount, $product_id);
}
foreach ($list as $row) {
$amount = $this->_useCredits($row['creditId'], $row['available'], $amount);
if ($amount <= 0) {
return true;
}
}
die("The verification failed in using credits.");
}
public function restoreCredits($userId, $creditType, $amount, $product_id = null)
{
// Only valid for level-type credits
if (! array_key_exists($creditType, $this->getCreditTypes(true))) {
return false;
}
$userlib = TikiLib::lib('user');
$tikilib = TikiLib::lib('tiki');
$info = $userlib->get_userid_info($userId);
$prefName = "credits_level_" . $creditType;
$used = (float) $tikilib->get_user_preference($info['login'], $prefName);
if ($used === 0) {
return false;
}
$used -= $amount;
if ($used < 0) {
$used = 0;
}
$tikilib->set_user_preference($info['login'], $prefName, $used);
if ($amount > 0) {
$this->_recCredits($userId, $creditType, -1 * $amount, $product_id);
}
return true;
}
/**
* Uses up a credit id and returns the amount of credits remaining to use.
*/
public function _useCredits($creditId, $available, $amount)
{
if ($available >= $amount) {
$this->query(
"UPDATE `tiki_credits` SET `used_amount` = `used_amount` + ? WHERE `creditId`= ?",
[$amount, $creditId]
);
return 0;
} else {
$this->query(
"UPDATE `tiki_credits` SET `used_amount` = `total_amount` WHERE `creditId`= ?",
[$creditId]
);
return $amount - $available;
}
}
/**
* Uses up a credit id and returns the amount of credits remaining to use.
*/
public function _recCredits($userId, $creditType, $amount, $product_id = null)
{
return $this->query(
"INSERT INTO `tiki_credits_usage` (`userId`, `usage_date`, `credit_type`, `used_amount`, `product_id`)" .
" VALUES (?, NOW(), ?, ?, ?)",
[$userId, $creditType, $amount, $product_id]
);
}
/**
* Delete expired credit entries and those completely used up.
*/
public function purgeCredits()
{
$this->query("DELETE FROM `tiki_credits` WHERE `expiration_date` IS NOT NULL AND `expiration_date` < NOW()");
$this->query("DELETE FROM `tiki_credits` WHERE `total_amount` = `used_amount`");
}
public function getPlanExpiry($userId, $creditType)
{
$result = $this->getOne(
"SELECT MAX(`expiration_date`) FROM `tiki_credits` WHERE `userId` = ? AND `expiration_date` IS NOT NULL AND `credit_type` = ?",
[$userId, $creditType]
);
if ($result) {
return $result;
} else {
return '';
}
}
public function getLatestPlanBegin($userId, $creditType)
{
$result = $this->getOne(
"SELECT MAX(`creation_date`) FROM `tiki_credits`" .
" WHERE `userId` = ? AND `expiration_date` IS NOT NULL AND `expiration_date` > NOW()" .
" AND `credit_type` = ? AND `creation_date` < NOW()",
[$userId, $creditType]
);
if ($result) {
return $result;
} else {
return '';
}
}
public function getNextPlanBegin($userId, $creditType)
{
$result = $this->getOne(
"SELECT MIN(`creation_date`)" .
" FROM `tiki_credits`" .
" WHERE `userId` = ? AND `expiration_date` IS NOT NULL AND `credit_type` = ? AND `creation_date` > NOW()",
[$userId, $creditType]
);
if ($result) {
return $result;
} else {
return '';
}
}
public function getCreditsUsage($target_user_id, $req_type, $start_date, $end_date)
{
if ($req_type) {
$results = $this->query(
"SELECT * FROM tiki_credits_usage" .
" WHERE userId = ? AND credit_type = ? AND usage_date > ? AND usage_date <= ?" .
" ORDER BY `usage_date` desc",
[$target_user_id, $req_type, $start_date, $end_date]
);
} else {
$results = $this->query(
"SELECT * FROM tiki_credits_usage WHERE userId = ? AND usage_date > ? AND usage_date <= ? ORDER BY `usage_date` desc",
[$target_user_id, $start_date, $end_date]
);
}
$consumption_data = [];
while ($row = $results->fetchRow()) {
$consumption_data[] = $row;
}
return $consumption_data;
}
}