<?php
/**
* Contains the implementation of the {@link OFFL_Trade} class
*
* @package offl
* @author Stephen Rochelle <hide@address.com>
* @see OFFL_tradeplayer.php
* @see OFFL_tradeprotest.php
*/
if (strtr(__FILE__, "\\", "/") == $_SERVER["SCRIPT_FILENAME"])
{ die ("Cannot access file directly!"); }
require_once($DOC_ROOT . "/lib/classes/offl_dbobject.php");
/**
* Defines interfaces for the trade process.
*
* @see OFFL_TradeProtest
* @see OFFL_TradePlayer
* @package offl
*/
class OFFL_Trade extends OFFL_DBObject
{
/**
* @var integer
*/
var $_trade_id = NULL;
/**
* @var integer
*/
var $_league_id = NULL;
/**
* @var integer
*/
var $_proposed_fflteam_id = NULL;
/**
* @var integer
*/
var $_target_fflteam_id = NULL;
/**
* @var array Array of integers (player ids)
*/
var $_proposed_player_id_array = array();
/**
* @var array Array of integers (player ids)
*/
var $_target_player_id_array = array();
/**
* @var string
* I need to list allowed values
*/
var $_status = "proposed";
/**
* @var integer
*/
var $_protest_votes = NULL;
/**
* @var integer
*/
var $_protest_votes_needed = NULL;
/**
* @var string SQL DATETIME format, GMT
*/
var $_deadline = NULL;
/**
* Constructor
*
* @param integer $trade_id Optional: if set, loads info from database
* @see populate()
*/
function OFFL_Trade ($trade_id = NULL)
{
OFFL_DBObject::OFFL_DBObject();
if(isset($trade_id))
{
$this->_trade_id = $trade_id;
$this->populate();
}
}
/**
* Pulls info from database. {@link $_trade_id} must be set.
*
* @return boolean TRUE if successful, FALSE otherwise.
*/
function populate ()
{
if(is_null($this->_trade_id))
{
$this->_emsg = "\$_trade_id is not set. Cannot populate Trade object.";
error_log ($this->_emsg);
return FALSE;
}
$sql = "SELECT * FROM trades WHERE trade_id = $this->_trade_id";
$result = mysql_query($sql,$this->_conn) or die (mysql_error() . ": $sql");
if(mysql_num_rows($result) == 0)
{
$this->_emsg = "No trade records found for trade_id " . $this->_trade_id . ".";
error_log ($this->_emsg);
mysql_free_result($result);
return FALSE;
}
$this->_league_id = mysql_result($result,0,"league_id");
$this->_proposed_fflteam_id = mysql_result($result,0,"proposed_fflteam_id");
$this->_target_fflteam_id = mysql_result($result,0,"target_fflteam_id");
$this->_status = htmlspecialchars(stripslashes(mysql_result($result,0,"status")));
$this->_deadline = mysql_result($result,0,"deadline");
$this->_proposed_player_id_array = array_diff(explode(",", mysql_result($result,0,"proposed_player_id_array")), array(""));
$this->_target_player_id_array = array_diff(explode(",", mysql_result($result,0,"target_player_id_array")), array(""));
mysql_free_result($result);
$x = new OFFL_TradeProtest();
$x->setTradeID($this->_trade_id);
$this->_protest_votes = $x->getNumberOfTradeProtests();
$x = new OFFL_Control($this->_league_id);
$league = new OFFL_League($this->_league_id);
$this->_protest_votes_needed = ceil($x->getValue("CONFIG_TRADE_PROTEST_PCT") * $league->getNumberOfFFLTeams() / 100);
return TRUE;
}
/**
* Saves the transaction to the database
*
* Determines INSERT vs UPDATE automagically
*/
function save ()
{
if (is_null($this->_trade_id)) // insert a new trade
{
// status is set by a default value in the database
$sql = "INSERT INTO trades (league_id, proposed_fflteam_id, target_fflteam_id, proposed_player_id_array, target_player_id_array) VALUES ('" . $this->_league_id . "', '" . $this->_proposed_fflteam_id . "', '" . $this->_target_fflteam_id . "', '" . implode(",", $this->_proposed_player_id_array) ."', '" . implode(",", $this->_target_player_id_array) . "')";
}
else
{
$sql = "UPDATE trades SET league_id='" . $this->_league_id . "', proposed_fflteam_id='" . $this->_proposed_fflteam_id . "', target_fflteam_id='" . $this->_target_fflteam_id . "', proposed_player_id_array='" . implode(",", $this->_proposed_player_id_array) ."', target_player_id_array='" . implode(",", $this->_target_player_id_array) ."', status='" . mysql_escape_string($this->_status) . "', deadline='" . mysql_escape_string($this->_deadline) . "' WHERE trade_id='" . $this->_trade_id . "'";
}
$result = mysql_query($sql,$this->_conn) or die (mysql_error() . ": $sql");
return $result;
}
/**
* @return integer
*/
function getTradeID()
{
return $this->_trade_id;
}
function setLeagueID($league_id)
{
$this->_league_id = $league_id;
}
/**
* @return integer
*/
function getLeagueID()
{
return $this->_league_id;
}
function setProposedFFLTeamID($proposed_fflteam_id)
{
$this->_proposed_fflteam_id = $proposed_fflteam_id;
}
/**
* @return integer
*/
function getProposedFFLTeamID()
{
return $this->_proposed_fflteam_id;
}
function setTargetFFLTeamID($target_fflteam_id)
{
$this->_target_fflteam_id = $target_fflteam_id;
}
/**
* @return integer
*/
function getTargetFFLTeamID()
{
return $this->_target_fflteam_id;
}
function setProposedPlayerIDArray($proposed_player_id_array)
{
$this->_proposed_player_id_array = $proposed_player_id_array;
}
/**
* @return array Array of integers
*/
function getProposedPlayerIDArray()
{
return $this->_proposed_player_id_array;
}
function setTargetPlayerIDArray($target_player_id_array)
{
$this->_target_player_id_array = $target_player_id_array;
}
/**
* @return array Array of integers
*/
function getTargetPlayerIDArray()
{
return $this->_target_player_id_array;
}
function setStatus($status)
{
$this->_status = $status;
}
/**
* @return string Need to document allowed values
*/
function getStatus()
{
return $this->_status;
}
/**
* @param boolean $refresh Optional: If set TRUE, updates from database
* @return integer
*/
function getProtestVotes($refresh=FALSE)
{
if ($refresh)
{
$this->_protest_votes = $this->getNumberOfTradeProtests();
}
return $this->_protest_votes;
}
/**
* @return integer
*/
function getProtestVotesNeeded()
{
return $this->_protest_votes_needed;
}
// ****************************************************
// The Deadline functions must be fleshed out and then
// inserted properly into populate, save, etc.
// v1.0 Note: what does this mean?
/**
*
* @param integer $deadline Timestamp, GMT, or NULL to unset
* @param string $time Optional: If set "trade" (default), uses CONFIG_TRADE_HOURS for timestamp offset, otherwise uses CONFIG_ROSTERVIOLATION_HOURS
*/
function setDeadline($deadline,$time="trade")
{
if (is_null($deadline))
{
$this->_deadline = NULL;
}
else
{
$x = new OFFL_Control($this->_league_id);
if ($time == "trade")
{ $trade_delay = $x->getValue("CONFIG_TRADE_HOURS"); }
else
{ $trade_delay = $x->getValue("CONFIG_ROSTERVIOLATION_HOURS"); }
$deadline += $trade_delay * 3600; // convert hours to seconds
$this->_deadline = gmdate("Y-m-d H:i:s", $deadline);
}
}
/**
* @return integer Timestamp, GMT (output from {@link gmmktime()})
*/
function getDeadline()
{
if (is_null($this->_deadline))
return $this->_deadline;
// assemble DATETIME format into UNIX timestamp
list($date, $time) = explode(" ", $this->_deadline);
list($Y, $M, $D) = explode("-", $date);
list($h, $i, $s) = explode(":", $time);
$time = gmmktime($h, $i, $s, $M, $D, $Y);
return $time;
}
/**
* Returns trade protests lodged against this trade. {@link $_trade_id} must be set.
*
* @return array Array of {@link OFFL_TradeProtest} objects, FALSE if {@link $_trade_id} not set
*/
function getTradeProtests()
{
$retArr = array();
if(is_null($this->_trade_id))
{
$this->_emsg = "\$_trade_id is not set. Cannot retrieve TradeProtest objects.";
error_log ($this->_emsg);
return FALSE;
}
$sql = "SELECT protest_id FROM tradeprotests WHERE trade_id=" . $this->_trade_id;
$result = mysql_query($sql,$this->_conn) or die (mysql_error() . ": $sql");
for($i=0; $i<mysql_num_rows($result); $i++)
{
$tp = new OFFL_TradeProtest(mysql_result($result,$i,"protest_id"));
array_push($retArr, $tp);
}
mysql_free_result($result);
return $retArr;
}
/**
* Returns trades linked to a given {@link OFFL_FFLTeam}, optionally restricted
*
* @param integer $fflteam_id OFFL_FFLTeam UID used for trade lookup
* @param string $target Optional: default "all" returns both proposed and target trades, may also be set "proposed" or "target" to return only the relevant trades. Other values return nothing.
* @return array Array of OFFL_Trades
*/
function getAllTeamTrades($fflteam_id, $target="all")
{
$retArr = array();
$sql = "SELECT trade_id FROM trades WHERE ";
switch ($target)
{
case "all":
$sql .= "proposed_fflteam_id=" . $fflteam_id . " OR target_fflteam_id=" . $fflteam_id;
break;
case "proposed":
$sql .= "proposed_fflteam_id=" . $fflteam_id;
break;
case "target":
$sql .= "target_fflteam_id=" . $fflteam_id;
break;
default:
$sql .= "1=0";
break;
}
$result = mysql_query($sql,$this->_conn) or die (mysql_error() . ": $sql");
for($i=0; $i<mysql_num_rows($result); $i++)
{
$t = new OFFL_Trade(mysql_result($result,$i,"trade_id"));
array_push($retArr, $t);
}
mysql_free_result($result);
return $retArr;
}
/**
* Pulls trades from an {@link OFFL_League} <i>not</i> related to a given {@link OFFL_FFLTeam}
*
* Normally used to display pending trades for possible protest and the like. It pulls all non-team
* trades within a league that are accepted / vetoed / protested, with optional refinement.
*
* @param integer $league_id League to reference for trades
* @param integer $fflteam_id Team to exclude from returned trades
* @param string $status Optional: specific trade status to return (default is "all")
* @return array Array of {@link OFFL_Trade} objects
*/
function getOtherViewableTrades($league_id, $fflteam_id, $status="all")
{
$retArr = array();
$sql = "SELECT trade_id FROM trades WHERE league_id=" . $league_id . " AND proposed_fflteam_id<>" . $fflteam_id . " AND target_fflteam_id<>" . $fflteam_id . " AND ";
switch($status)
{
case "all":
$sql .= "status<>'proposed' AND status<>'declined'";
break;
default:
$sql .= "status='" . mysql_escape_string($status) . "'";
}
$result = mysql_query($sql,$this->_conn) or die (mysql_error() . ": $sql");
for($i=0; $i<mysql_num_rows($result); $i++)
{
$t = new OFFL_Trade(mysql_result($result,$i,"trade_id"));
array_push($retArr, $t);
}
mysql_free_result($result);
return $retArr;
}
/**
* Returns trade objects with optional conditions
*
* Intended for admin and resolution, refinements can be made to the pending status and {@link OFFL_League} association.
*
* @param boolean $pending_only Optional: set true to return only trades with some pending deadline (usually for trade processing purposes)
* @param integer $league_id Optional: restrict returned trades to a given league
* @return array Array of {@link OFFL_Trade} objects
*/
function getAllTrades($pending_only=FALSE, $league_id=NULL)
// if pending_only is set, only trades with deadlines (i.e. those eligible for processing) are returned
{
$retArr = array();
$sql = "SELECT trade_id FROM trades";
if ($pending_only)
{
$sql .= " WHERE deadline IS NOT NULL";
if (isset($league_id))
{ $sql .= " AND league_id=" . $league_id; }
}
$sql .= " ORDER BY deadline";
$result = mysql_query($sql, $this->_conn) or die (mysql_error() . ": $sql");
for($i=0; $i<mysql_num_rows($result); $i++)
{
$t = new OFFL_Trade(mysql_result($result,$i,"trade_id"));
array_push($retArr, $t);
}
mysql_free_result($result);
return $retArr;
}
/**
* Sets the "trade lock" status on players involved in an approved trade
*
* "Trade lock" is the process by which players involved in an approved, but not yet processed, trade are
* restricted from drops or other trades that would exempt this transaction from completion. Trade lock is
* a timestamp concept rather than a simple boolean so that, if a trade streches on without completion due to roster
* violations, a team's roster cannot be held hostage indefinitely. However, the timestamp itself is contained by
* the {@link OFFL_Trade} class, so the tradelock variable is boolean
*
* @param integer $tradelock Optional: Defaults to setting trade lock, set 0 to unset tradeock
*/
function setTradelock($tradelock = 1)
{
$player_ids = array_merge($this->_proposed_player_id_array, $this->_target_player_id_array);
if ($tradelock)
{
foreach ($player_ids as $player_id)
{
$player = new OFFL_TradePlayer();
$player->setPlayerID($player_id);
$player->setLeagueID($this->_league_id);
$player->setTradelock(1);
$player->save();
}
}
else
{
foreach ($player_ids as $player_id)
{
$player = new OFFL_TradePlayer($player_id, $this->_league_id);
$player->deleteTradePlayer();
}
}
}
/**
* Checks for tradelock status on players involved in trade
*
* Used to test for pre-existing trade lock before allowing submission of new trades.
* <ul>
* <li><b>0:</b> No trade lock</li>
* <li><b>-1:</b> Trade lock on proposing team only</li>
* <li><b>-2:</b> Trade lock on target team only</li>
* <li><b>-3:</b> Trade lock on both teams</li>
* </ul>
*
* @return integer
*/
function checkTradelock()
{
$returnVal = 0;
foreach ($this->_proposed_player_id_array as $player_id)
{
$player = new OFFL_TradePlayer($player_id, $this->_league_id);
if ($player->getTradelock())
{
$returnVal = -1;
break;
}
}
foreach ($this->_target_player_id_array as $player_id)
{
$player = new OFFL_TradePlayer($player_id, $this->_league_id);
if ($player->getTradelock())
{
$returnVal -= 2;
break;
}
}
return $returnVal;
}
/**
* Tentatively executes this trade and tests resulting rosters for correctness
*
* @param string $team Optional: which team is checked. Default "both", also allowed are "proposed" and "target"
* @return array Array of strings (error messages). If array is empty, no errors found.
*/
function checkTradeRosters($team="both")
{
$errors = array();
$thisTeam = new OFFL_FFLTeam($this->_proposed_fflteam_id);
$thatTeam = new OFFL_FFLTeam($this->_target_fflteam_id);
$thisRosterOld = $thisTeam->getRoster();
$thatRosterOld = $thatTeam->getRoster();
$thisRoster = array();
$thatRoster = array();
foreach ($thisRosterOld as $player)
{
if (in_array($player->getPlayerID(), $this->_proposed_player_id_array)) // being traded
{
$player->setStarter(0);
$thatRoster[] = $player;
}
else
{ $thisRoster[] = $player; }
}
foreach ($thatRosterOld as $player)
{
if (in_array($player->getPlayerID(), $this->_target_player_id_array)) // being traded
{
$player->setStarter(0);
$thisRoster[] = $player;
}
else
{ $thatRoster[] = $player; }
}
usort($thisRoster, "cmp_players");
usort($thatRoster, "cmp_players");
$theseErrors = $thisTeam->validateRosterFlex($thisRoster);
$thoseErrors = $thatTeam->validateRosterFlex($thatRoster);
switch ($team)
{
case "both":
$errors = array_merge($theseErrors, $thoseErrors);
break;
case "proposed";
$errors = $theseErrors;
if (sizeof($thoseErrors) > 0)
$errors[] = $thatTeam->getFFLTeamFullName() . " has remaining roster violations.";
break;
case "target";
$errors = $thoseErrors;
if (sizeof($theseErrors) > 0)
$errors[] = $thisTeam->getFFLTeamFullName() . " has remaining roster violations.";
break;
}
return $errors;
}
/**
* Actual execution of trade, plus {@link OFFL_TradePlayer trade lock} cleanup if needed
*
* Several key assumptions are made by this function, most notably that the deadline is appropriate and that resulting
* rosters will be valid. The only internal check is that traded players are on the correct teams.
*
* No database cleanup of the trade or {@link OFFL_TradeProtest protests} is done by this function (reserved for {@link deleteTrade()}).
*
* @return array Array of strings. All array elements are error messages. If the array returns 0 elements, trade successfully processed.
*/
function processTrade()
{
$errors = array();
$TradeOK = TRUE;
foreach($this->_proposed_player_id_array as $player_id)
{
$player = new OFFL_Player($player_id, $this->_league_id);
if ($player->getFFLTeamID() != $this->_proposed_fflteam_id)
{
$TradeOK = FALSE;
array_push($errors, $player->getName() . " is not on expected team.");
}
}
foreach($this->_target_player_id_array as $player_id)
{
$player = new OFFL_Player($player_id, $this->_league_id);
if ($player->getFFLTeamID() != $this->_target_fflteam_id)
{
$TradeOK = FALSE;
array_push($errors, $player->getName() . " is not on expected team.");
}
}
if ($TradeOK)
{
$transaction = new OFFL_Transaction();
$proposedTeam = new OFFL_FFLTeam($this->_proposed_fflteam_id);
$targetTeam = new OFFL_FFLTeam($this->_target_fflteam_id);
$transaction->setTransType("trade");
$transaction->setLeagueID($this->_league_id);
$transaction->save();
foreach($this->_proposed_player_id_array as $player_id)
{
$tradeLock = new OFFL_TradePlayer($player_id, $this->_league_id);
$tradeLock->deleteTradePlayer();
unset ($tradeLock);
$transItem = new OFFL_TransactionItem();
$transItem->setTransID($transaction->getTransID());
$transItem->setTransType("trade");
$transItem->setPlayerID($player_id);
$transItem->setFromFFLTeamID($this->_proposed_fflteam_id);
$transItem->setToFFLTeamID($this->_target_fflteam_id);
$transItem->save();
$proposedTeam->dropPlayer($player_id);
$targetTeam->addPlayer($player_id);
}
foreach($this->_target_player_id_array as $player_id)
{
$tradeLock = new OFFL_TradePlayer($player_id, $this->_league_id);
$tradeLock->deleteTradePlayer();
unset ($tradeLock);
$transItem = new OFFL_TransactionItem();
$transItem->setTransID($transaction->getTransID());
$transItem->setTransType("trade");
$transItem->setPlayerID($player->getPlayerID());
$transItem->setFromFFLTeamID($this->_target_fflteam_id);
$transItem->setToFFLTeamID($this->_proposed_fflteam_id);
$transItem->save();
$targetTeam->dropPlayer($player_id);
$proposedTeam->addPlayer($player_id);
}
$proposedTeam->save();
$targetTeam->save();
}
return $errors;
}
/**
* Deletes this trade from the database as well as associated {@link OFFL_TradeProtest} objects
* @return boolean TRUE on success, FALSE if {@link $_trade_id} not set
*/
function deleteTrade()
{
if(isset($this->_trade_id))
{
$protests = $this->getTradeProtests();
foreach ($protests as $protest)
{
$protest->deleteTradeProtest();
}
$sql = "DELETE FROM trades WHERE trade_id=" . $this->_trade_id;
$result = mysql_query($sql,$this->_conn) or die (mysql_error() . ": $sql");
return TRUE;
}
$this->_emsg = "Unable to delete trade: trade_id not defined.";
error_log ($this->_emsg);
return FALSE;
}
} // end OFFL_Trade class
?>