userID = $row['UserID']; $this->privateCityAndState = (boolean) $row['flLocation']; if ($row['DOB'] !== '0000-00-00') { $birthdate = new \DateTime($row['DOB']); $today = new \DateTime(); $interval = $birthdate->diff($today, true); $this->userAge = $interval->y; } // The following code privileges the most specific location information // known about the user. The table data _should_ be consistent. That // is, the city should be in the state which is in turn within the // country. However, there may be inconsistencies. For example, if the // user specified their location as "Toronto, Ontario, Canada" through // one TIG-network site but then changed their province to "Quebec" // on another TIG site (which was built before the city field was // added), the row may erroneously read "Toronto, Quebec, Canada". if ($row['CityID']) { $this->loadCity($row['CityID']); } elseif ($row['StateID']) { $this->loadState($row['StateID']); } elseif ($row['CountryID']) { $this->loadCountry($row['CountryID']); } } /****************************************************************************** * CRUD Methods ******************************************************************************/ public static function readByUserID($userID, $readFromMaster = false) { if (!filter_var($userID, FILTER_VALIDATE_INT)) { throw new \InvalidArgumentException("Invalid user ID: $userID"); } $row = query("SELECT UserID, CityID, StateID, CountryID, flLocation, DOB FROM tig.Users WHERE UserID = $userID", '', $readFromMaster); if (!$row) { throw new \OutOfBoundsException("User ID not found: $userID"); } return new self($row); } public function update() { safe_dbwrite(" UPDATE tig.Users SET CityID = " . ($this->cityID ?: '0') . ", StateID = " . ($this->stateID ?: '0') . ", CountryID = " . ($this->countryID ?: '0') . ", flLocation = " . ($this->privateCityAndState ? '1' : '0') . " WHERE UserID = $this->userID"); } /****************************************************************************** * Getters \ Setters \ Error Checkers ******************************************************************************/ /** * Returns the name of the country the user is located in * * @return string */ public function getCountryName() { return $this->countryName; } /** * The full location of the user, irrespective of the user's privacy settings * * @return string The full location of the user, if known. False if unknown. */ public function getFullLocation() { $parts = array( $this->cityName, $this->stateName, $this->countryName ); $presentParts = array_filter($parts); if (count($presentParts) === 0) { return false; } return implode(', ', $presentParts); } /** * The city and country of the user's location * * While there are occassionally more than one city in a country sharing a * name, most of the time showing the city and country is enough. * * @param boolean $ignoreRestrictions If true, show the user's exact location regardless of privacy settings. * @return string The location the user wishes to share publically. False if unknown. */ public function getCityAndCountry($ignoreRestrictions = false) { if (!$ignoreRestrictions && $this->isCityAndStateRestricted()) { return $this->countryName; } if (!$this->cityName) { return $this->countryName; } return $this->cityName . ", " . $this->countryName; } /** * Returns a url to a small image of the flag of the country the user is located in * * @return string */ public function getFlagImageUrl() { if ($this->countryID) { return "http://cdn.tigurl.org/images/explore/flags/sm/" . $this->countryID . ".gif"; } } /** * Returns the latitude coordinate of the user's location * * @param boolean $ignoreRestrictions If true, show the user's exact location regardless of privacy settings. * @return string */ public function getLatitude($ignoreRestrictions = false) { if (!$ignoreRestrictions && $this->isCityAndStateRestricted()) { return $this->countryLatitude; } return $this->cityLatitude; } /** * Returns the longitude coordinate of the user's location * * @param boolean $ignoreRestrictions If true, show the user's exact location regardless of privacy settings. * @return string */ public function getLongitude($ignoreRestrictions = false) { if (!$ignoreRestrictions && $this->isCityAndStateRestricted()) { return $this->countryLongitude; } return $this->cityLongitude; } /** * Changes the user's city (and state and country) * * NOTE: This change must be saved by the update() method. * * @param int $cityID */ public function setByCityID($cityID) { $this->loadCity($cityID); } /** * Indicates whether the user has elected to hide their city and state * * @return boolean */ public function getPrivateCityAndState() { return $this->privateCityAndState; } /** * * @param boolean $isPrivate */ public function setPrivateCityAndState($isPrivate) { $this->privateCityAndState = (boolean) $isPrivate; } /************************************************************************** * Helpers **************************************************************************/ private function loadCity($cityID) { if (!filter_var($cityID, FILTER_VALIDATE_INT)) { throw new \InvalidArgumentException("Invalid city: $cityID"); } $city = query(" SELECT ci.ID, ci.CityNameML, ci.Lat, ci.Long, s.StateID, s.StateName, co.CountryID, co.CountryName FROM tig.Cities AS ci LEFT JOIN tig.States AS s ON ci.StateID = s.StateID INNER JOIN tig.Countries AS co ON ci.CountryID = co.CountryID WHERE ci.ID = $cityID "); if (!$city) { throw new \InvalidArgumentException("City not found: $cityID"); } $this->cityID = $city['ID']; $this->cityName = $city['CityNameML']; $this->cityLatitude = $city['Lat']; $this->cityLongitude = $city['Long']; $this->stateID = $city['StateID']; $this->stateName = $city['StateName']; $this->countryID = $city['CountryID']; $this->countryName = $city['CountryName']; $this->countryLatitude = $city['MiddleLat']; $this->countryLongitude = $city['MiddleLong']; } private function nullCity() { $this->cityID = null; $this->cityName = null; $this->cityLatitude = null; $this->cityLongitude = null; } private function loadState($stateID) { if (!filter_var($stateID, FILTER_VALIDATE_INT)) { throw new \InvalidArgumentException("Invalid state: $stateID"); } $state = query(" SELECT s.StateID, s.StateName, co.CountryID, co.CountryName, co.MiddleLat, co.MiddleLong FROM tig.States AS s LEFT JOIN tig.Countries AS co ON s.CountryID = co.CountryID WHERE s.StateID = $stateID "); if (!$state) { throw new \InvalidArgumentException("State not found: $stateID"); } $this->nullCity(); $this->stateID = $state['StateID']; $this->stateName = $state['StateName']; $this->countryID = $state['CountryID']; $this->countryName = $state['CountryName']; $this->countryLatitude = $state['MiddleLat']; $this->countryLongitude = $state['MiddleLong']; } private function nullState() { $this->stateID = null; $this->stateName = null; } private function loadCountry($countryID) { if (!filter_var($countryID, FILTER_VALIDATE_INT)) { throw new \InvalidArgumentException("Invalid country: $countryID"); } $country = query("SELECT CountryID, CountryName, MiddleLat, MiddleLong FROM tig.Countries WHERE CountryID = $countryID"); if (!$country) { throw new \InvalidArgumentException("Country not found: $countryID"); } $this->nullCity(); $this->nullState(); $this->countryID = $country['CountryID']; $this->countryName = $country['CountryName']; $this->countryLatitude = $country['MiddleLat']; $this->countryLongitude = $country['MiddleLong']; } private function isCityAndStateRestricted() { // We do not show the location of users under the age of 14 if ($this->userAge && $this->userAge < 14) { return true; } // Has the user elected not to display their city and state? if ($this->privateCityAndState) { return true; } return false; } }