diff --git a/public/css/rankster.css b/public/css/rankster.css index 703ed26..ec89f97 100644 --- a/public/css/rankster.css +++ b/public/css/rankster.css @@ -33,4 +33,10 @@ color: #fff; padding: 2px 8px; font-size: 10px; +} + + +.reject-button, .confirm-button { + width: 80px; + margin: 1px; } \ No newline at end of file diff --git a/public/js/rankster.js b/public/js/rankster.js index 7954ea3..51916c7 100644 --- a/public/js/rankster.js +++ b/public/js/rankster.js @@ -59,6 +59,11 @@ $("#game-replay").modal(); } + function newGameDialog() { + $("#game-req").modal(); + } + + function eventHandler () { $('#game').select2({ ajax: { @@ -141,6 +146,7 @@ formatParticipant: formatParticipant, formatParticipantSelection: formatParticipantSelection, gameReplayDialog: gameReplayDialog, + newGameDialog: newGameDialog, eventHandler: eventHandler, setUserGameInfo: function setUserGameInfo(u, g) { users = u; diff --git a/src/Api/V1.php b/src/Api/V1.php index f307ee4..681241c 100755 --- a/src/Api/V1.php +++ b/src/Api/V1.php @@ -7,6 +7,7 @@ use Rankster\Api\V1\DeleteUser; use Rankster\Api\V1\Games; use Rankster\Api\V1\Login; +use Rankster\Api\V1\Oauth\Oauth; use Rankster\Api\V1\RecalculateRank; use Rankster\Api\V1\ReloadFbAvatars; use Rankster\Api\V1\SeedMatches; @@ -47,6 +48,7 @@ static function setUpDefinition(Definition $definition, $options) ->addToEnum(SeedMatches::definition(), 'seed-matches') ->addToEnum(DeleteUser::definition(), 'delete-user') ->addToEnum(ReloadFbAvatars::definition()) + ->addToEnum(Oauth::definition()) ->setIsUnnamed(); } diff --git a/src/Api/V1/Oauth/Google.php b/src/Api/V1/Oauth/Google.php new file mode 100644 index 0000000..59c4749 --- /dev/null +++ b/src/Api/V1/Oauth/Google.php @@ -0,0 +1,61 @@ +state = Command\Option::create()->setType() + ->setDescription('State information (contains initial url)'); + $options->code = Command\Option::create()->setType()->setIsRequired() + ->setDescription('Auth security code'); + } + + public function performAction() + { + $google = \Rankster\Service\Google::getInstance(); + $token = $google->getToken($this->code); + $info = $google->getUserInfo(); + + if (empty($info['id'])) { + throw new \Exception('Bad response'); + } + + $user = new User(); + $user->email = $info['email']; + if ($saved = $user->findSaved()) { + $user = $saved; + //$user->downloadImage($info['picture'], $user->googleId); + //$user->save(); + if (empty($user->googleId)) { + $user->googleId = $info['id']; + $user->save(); + } + } else { + $user->name = $info['name']; + $user->googleId = $info['id']; + if (!empty($info['picture'])) { + $user->downloadImage($info['picture'], $user->googleId); + } + $user->save(); + } + AuthSession::set($user->id); + header("Location: $this->state"); + exit(); + } + + +} \ No newline at end of file diff --git a/src/Api/V1/Oauth/Oauth.php b/src/Api/V1/Oauth/Oauth.php new file mode 100644 index 0000000..8124b2b --- /dev/null +++ b/src/Api/V1/Oauth/Oauth.php @@ -0,0 +1,33 @@ +action = Command\Option::create() + ->addToEnum(Google::definition()) + ->setIsUnnamed() + ->setIsRequired(); + } + + + public function performAction() + { + $this->action->performAction(); + } + +} \ No newline at end of file diff --git a/src/Entity/Game.php b/src/Entity/Game.php index 63998a2..1417bb4 100755 --- a/src/Entity/Game.php +++ b/src/Entity/Game.php @@ -60,4 +60,16 @@ public function countPlayers() return $r['c']; } + + public static function findByPrimaryKey($id) + { + static $cache = array(); + if (isset($cache[$id])) { + return $cache[$id]; + } + + $game = parent::findByPrimaryKey($id); + $cache[$id] = $game; + return $game; + } } diff --git a/src/Entity/MatchRequest.php b/src/Entity/MatchRequest.php index cfbe3ee..c2810e2 100644 --- a/src/Entity/MatchRequest.php +++ b/src/Entity/MatchRequest.php @@ -20,7 +20,9 @@ class MatchRequest extends Entity public $user2Id; public $winnerId; public $status; + /** @var \DateTime */ public $eventTime; + /** @var \DateTime */ public $actionTime; /** @@ -84,8 +86,37 @@ public static function getCountNew($userId) return $cnt['cnt']; } + public static function getCountOpponentNew($userId) + { + $query = MatchRequest::statement()->select('count(*) as cnt') + ->where('? = ?', MatchRequest::columns()->user1Id, $userId) + ->where('? = ?', MatchRequest::columns()->status, MatchRequest::STATUS_NEW) + ->query(); + $query->bindResultClass(); + $cnt = $query->fetchRow(); + + return $cnt['cnt']; + } + + public static function getCountRejected($userId) + { + $query = MatchRequest::statement()->select('count(*) as cnt') + ->where('? = ?', MatchRequest::columns()->user1Id, $userId) + ->where('? = ?', MatchRequest::columns()->status, MatchRequest::STATUS_REJECTED) + ->query(); + $query->bindResultClass(); + $cnt = $query->fetchRow(); + + return $cnt['cnt']; + } + + public static function getMatchRequestNewList($userId, $perPage = 20, $page = 1) { + if (empty($page)) { + $page = 1; + } + return MatchRequest::statement() ->where('? = ?', MatchRequest::columns()->user2Id, $userId) ->where('? = ?', MatchRequest::columns()->status, MatchRequest::STATUS_NEW) @@ -95,8 +126,43 @@ public static function getMatchRequestNewList($userId, $perPage = 20, $page = 1) ->fetchAll(); } + public static function getMatchRequestNewOpponentList($userId, $perPage = 20, $page = 1) + { + if (empty($page)) { + $page = 1; + } + + return MatchRequest::statement() + ->where('? = ?', MatchRequest::columns()->user1Id, $userId) + ->where('? = ?', MatchRequest::columns()->status, MatchRequest::STATUS_NEW) + ->order('? DESC', MatchRequest::columns()->eventTime) + ->limit($perPage, $perPage * ($page - 1)) + ->query() + ->fetchAll(); + } + + public static function getMatchRequestRejectedList($userId, $perPage = 20, $page = 1) + { + if (empty($page)) { + $page = 1; + } + + return MatchRequest::statement() + ->where('? = ?', MatchRequest::columns()->user1Id, $userId) + ->where('? = ?', MatchRequest::columns()->status, MatchRequest::STATUS_REJECTED) + ->order('? DESC', MatchRequest::columns()->eventTime) + ->limit($perPage, $perPage * ($page - 1)) + ->query() + ->fetchAll(); + } + + public static function getMatchRequestNewListForUserAndGame($userId, $userOpponent, $gameId, $perPage = 20, $page = 1) { + if (empty($page)) { + $page = 1; + } + return MatchRequest::statement() ->where('? = ?', MatchRequest::columns()->user1Id, $userOpponent) ->where('? = ?', MatchRequest::columns()->user2Id, $userId) diff --git a/src/Entity/User.php b/src/Entity/User.php index a7945ad..5d91703 100755 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -13,6 +13,7 @@ class User extends Entity public $id; public $facebookId; + public $googleId; public $name; public $login; public $email; @@ -23,12 +24,13 @@ class User extends Entity static function setUpColumns($columns) { $columns->id = Column::AUTO_ID; - $columns->facebookId = Column::create(Column::STRING + Column::NOT_NULL) + $columns->facebookId = Column::create(Column::STRING) ->setStringLength(50) ->setUnique(); + $columns->googleId = Column::create(Column::STRING)->setUnique(); $columns->login = Column::create(Column::STRING)->setUnique(); - $columns->name = Column::create(Column::STRING + Column::NOT_NULL)->setDefault(""); - $columns->email = Column::create(Column::STRING + Column::NOT_NULL)->setDefault(""); + $columns->name = Column::create(Column::STRING + Column::NOT_NULL)->setDefault(''); + $columns->email = Column::create(Column::STRING + Column::NOT_NULL)->setDefault('')->setUnique(); $columns->picturePath = Column::STRING; } @@ -37,14 +39,17 @@ static function setUpTable(\Yaoi\Database\Definition\Table $table, $columns) $table->setSchemaName('user'); } - public function downloadImage($url) + public function downloadImage($url, $id = null) { $response = file_get_contents($url); if (!$response) { return; } - $md5 = md5($this->facebookId . 'FacebookHackathon2016'); + if (null === $id) { + $id = $this->facebookId; + } + $md5 = md5($id . 'FacebookHackathon2016'); $dir = substr($md5, 0, 3); $imagesDirectory = DOC_ROOT . '/' . self::IMAGE_FOLDER . '/'; @@ -143,4 +148,16 @@ public function getMatchRequestNewCount() { return MatchRequest::getCountNew($this->id); } + + public static function findByPrimaryKey($id) + { + static $cache = array(); + if (isset($cache[$id])) { + return $cache[$id]; + } + + $entity = parent::findByPrimaryKey($id); + $cache[$id] = $entity; + return $entity; + } } diff --git a/src/Service/Google.php b/src/Service/Google.php new file mode 100644 index 0000000..f8c2fbb --- /dev/null +++ b/src/Service/Google.php @@ -0,0 +1,62 @@ + 'code', + 'client_id' => $this->getSettings()->clientId, + 'redirect_uri' => $this->getSettings()->redirectUrl, + 'scope' => 'email profile', + 'state' => $_SERVER['REQUEST_URI'], + 'access_type' => 'online', + 'prompt' => 'select_account', + ); + + return 'https://accounts.google.com/o/oauth2/v2/auth?' . http_build_query($params); + } + + + private $accessToken; + + public function getToken($code) { + $params = array( + 'code' => $code, + 'client_id' => $this->getSettings()->clientId, + 'client_secret' => $this->getSettings()->secret, + 'redirect_uri' => $this->getSettings()->redirectUrl, + 'grant_type' => 'authorization_code', + ); + + $client = new Client(); + $client->post = $params; + $tokenResponse = json_decode($client->fetch('https://www.googleapis.com/oauth2/v4/token'),true); + $this->accessToken = $tokenResponse['access_token']; + return $this->accessToken; + } + + public function getUserInfo() { + $url = 'https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token=' . $this->accessToken; + $response = file_get_contents($url); + return json_decode($response, true); + } + +} \ No newline at end of file diff --git a/src/Service/Google/GoogleSettings.php b/src/Service/Google/GoogleSettings.php new file mode 100644 index 0000000..7c77da9 --- /dev/null +++ b/src/Service/Google/GoogleSettings.php @@ -0,0 +1,12 @@ +

name ?>

-

New match requests getMatchRequestNewCount() ?>

+

Pending your confirmation + getMatchRequestNewCount() ?> +

Rookie Logout @@ -47,30 +50,31 @@ public function render()
-
- - - - New Match - - +
+
-
-
- findLastMatch(); - if ($rematch) { - /** @var \Rankster\Entity\User $user */ - $user = $rematch['user']; - /** @var \Rankster\Entity\Game $game */ - $game = $rematch['game']; + findLastMatch(); + if ($rematch) { + /** @var \Rankster\Entity\User $user */ + $user = $rematch['user']; + /** @var \Rankster\Entity\Game $game */ + $game = $rematch['game']; - Data::getInstance()->addUserInfo($user); - Data::getInstance()->addGameInfo($game); + Data::getInstance()->addUserInfo($user); + Data::getInstance()->addGameInfo($game); - ?> + ?> +
+
- - +
-
+ +
render(); -RequestForm::create()->render(); - } else { ?> -
+ ReplayForm::create()->render(); + RequestForm::create()->render(); + } else { + + $google = Google::getInstance(); + echo <<
- renderItem($match); } + if (!$rows) { + $rows = "No data"; + } + echo <<
diff --git a/src/View/MatchRequestList.php b/src/View/MatchRequestList.php index 9ffa013..37dae8a 100644 --- a/src/View/MatchRequestList.php +++ b/src/View/MatchRequestList.php @@ -25,6 +25,8 @@ public function setPagination(Pagination $pagination) public $hideGameColumn = false; + public $isOpponent = false; + /** * History constructor. * @param MatchRequest[] $rows @@ -45,8 +47,8 @@ public function renderItem(MatchRequestEntity $matchRequest) $w = ' class="winner"'; $status = ' wins '; } else { - $w = ' class="looser"'; - $status = ' loses '; + $w = ' class="loser"'; + $status = ' loses to '; } $result = << {$user1->name} {$status} {$user2->name} + +HTML; + if (!$this->isOpponent) { + $result .= <<
@@ -63,13 +69,18 @@ public function renderItem(MatchRequestEntity $matchRequest)

-

+ +HTML; + } + $result .= << + HTML; return $result; @@ -88,8 +99,12 @@ public function render() $rows .= $this->renderItem($match); } - echo << + if (!$rows) { + $rows = 'No data'; + } + + $result = <<

{$this->title}

@@ -98,12 +113,26 @@ public function render() {$rows} + +HTML; + + if (!$this->isOpponent && $this->rows) { + $result .= <<"Auto confirm" is a sign of trust, if checked all matches (including pending confirmation) + from this user and this game will be confirmed automatically
+HTML; + } + + $result .= << {$this->pagination}
HTML; + echo $result; + } diff --git a/src/View/UserRankTable.php b/src/View/UserRankTable.php index 7f20060..95dd610 100644 --- a/src/View/UserRankTable.php +++ b/src/View/UserRankTable.php @@ -123,6 +123,11 @@ public function render() $rows .= $this->renderItem($i, $rank); } + if (!$rows) { + $rows = "No data"; + } + + $secondaryLink = ''; if ($this->name && $this->image) { $captionLink = $this->name; @@ -137,11 +142,18 @@ public function render() } } + if ($this->game) { + $secondaryLink = 'Other games'; + } + echo <<
-

{$captionLink}

+
+

{$captionLink}

+ {$secondaryLink} +

diff --git a/src/Web/MainPage.php b/src/Web/MainPage.php index 5f42ccd..f367f55 100755 --- a/src/Web/MainPage.php +++ b/src/Web/MainPage.php @@ -31,13 +31,10 @@ public function performAction() } $limit = 2; - if ($user = AuthSession::getUser()) { - $games = $user->getMostPlayedGames($limit); - } else { + if ((!$user = AuthSession::getUser()) || !$games = $user->getMostPlayedGames($limit)) { $games = Game::statement() ->order('? DESC', Game::columns()->playersCount) ->limit($limit)->query()->fetchAll(); - } $this->response->addContent('
'); diff --git a/src/Web/MatchRequest/MatchRequest.php b/src/Web/MatchRequest/MatchRequest.php index d0310fc..d6113c6 100644 --- a/src/Web/MatchRequest/MatchRequest.php +++ b/src/Web/MatchRequest/MatchRequest.php @@ -16,11 +16,14 @@ class MatchRequest extends Command { public $page; + public $pageRejected; + public $pageOpponent; public $whiteListed; public $accept; public $reject; public $action; public $mrId; + /** * Required setup option types in provided options object * @param $definition Definition @@ -29,6 +32,8 @@ class MatchRequest extends Command static function setUpDefinition(Definition $definition, $options) { $options->page = Command\Option::create()->setType(); + $options->pageOpponent = Command\Option::create()->setType(); + $options->pageRejected = Command\Option::create()->setType(); $options->action = Command\Option::create()->setType(); $options->whiteListed = Command\Option::create()->setType(); $options->mrId = Command\Option::create()->setType(); @@ -41,7 +46,7 @@ public function performAction() $user = AuthSession::getUser(); if (!$user) { header('Location: /'); - return ; + return; } if ($this->action == 'process') { @@ -54,18 +59,45 @@ public function performAction() } $commandState = self::createState($this->io); - $pages = ceil($user->getMatchRequestNewCount() / $perPage); - - $list = MatchRequestEntity::getMatchRequestNewList($user->id, $perPage, $this->page); - $mrList = new MatchRequestList($list); - $mrList->title = 'Match Request List'; - $mrList->setPagination(new Pagination($commandState, self::options()->page, $pages)); - $this->response->addContent(new Heading('Match Request List')); + $confirmationList = new MatchRequestList( + MatchRequestEntity::getMatchRequestNewList($user->id, $perPage, $this->page) + ); + $confirmationList->title = 'Pending your confirmation'; + $confirmationList->setPagination(new Pagination( + $commandState, + self::options()->page, + ceil($user->getMatchRequestNewCount() / $perPage) + )); + + $opponentList = new MatchRequestList( + MatchRequestEntity::getMatchRequestNewOpponentList($user->id, $perPage, $this->pageOpponent) + ); + $opponentList->title = 'Pending opponent confirmation'; + $opponentList->isOpponent = true; + $opponentList->setPagination(new Pagination( + $commandState, + self::options()->pageOpponent, + ceil(MatchRequestEntity::getCountOpponentNew($user->id) / $perPage) + )); + + $rejectedList = new MatchRequestList( + MatchRequestEntity::getMatchRequestRejectedList($user->id, $perPage, $this->pageRejected) + ); + $rejectedList->title = 'Rejected'; + $rejectedList->isOpponent = true; + $rejectedList->setPagination(new Pagination( + $commandState, + self::options()->pageRejected, + ceil(MatchRequestEntity::getCountRejected($user->id) / $perPage) + )); + + $this->response->addContent(new Heading('Matches')); $this->response->addContent('
'); - $this->response->addContent($mrList); + $this->response->addContent($confirmationList); + $this->response->addContent($opponentList); + $this->response->addContent($rejectedList); $this->response->addContent('
'); - } protected function processAction() @@ -100,7 +132,7 @@ protected function addToWhiteListAndProcessAll($userId, $gameId) $page = 1; $perPage = 50; - + do { $list = MatchRequestEntity::getMatchRequestNewListForUserAndGame($user->id, $userId, $gameId, $perPage, $page); /** @var MatchRequestEntity $mr */ diff --git a/src/Web/User/MatchHistory.php b/src/Web/User/MatchHistory.php index 999577a..959d990 100644 --- a/src/Web/User/MatchHistory.php +++ b/src/Web/User/MatchHistory.php @@ -59,7 +59,7 @@ public function performAction() $commandState = self::createState($this->io); $perPage = 12; - $pages = ceil($game->matchesCount / $perPage); + $pages = ceil($rank->matches / $perPage); if ($this->historyPage > $pages) { $this->historyPage = $pages; @@ -71,6 +71,7 @@ public function performAction() $matches = Match::statement() ->where('? = ?', Match::columns()->gameId, $this->gameId) + ->where('? IN (?, ?)', $this->userId, Match::columns()->user1Id, Match::columns()->user2Id) ->order('? DESC', Match::columns()->eventTime) ->limit($perPage, $perPage * ($this->historyPage - 1)) ->query()