diff --git a/profiles/dev-config-profile.php b/profiles/dev-config-profile.php index 0c06513..b8a50be 100644 --- a/profiles/dev-config-profile.php +++ b/profiles/dev-config-profile.php @@ -21,14 +21,21 @@ function _asset(string $assetURI): string { function _init_database(PDO $pdo): void { $accounts = new AccountGateway(new Connection($pdo)); + $teams = new \IQBall\Core\Gateway\TeamGateway((new Connection($pdo))); $defaultAccounts = ["maxime", "mael", "yanis", "vivien"]; + $defaultTeams = ["Lakers", "Celtics", "Bulls"]; + foreach ($defaultAccounts as $name) { $email = "$name@mail.com"; $id = $accounts->insertAccount($name, $email, AuthModel::generateToken(), password_hash("123456", PASSWORD_DEFAULT), "https://cdn.pixabay.com/photo/2015/10/05/22/37/blank-profile-picture-973460_960_720.png"); $accounts->setIsAdmin($id, true); } + + foreach ($defaultTeams as $name) { + $id = $teams->insert($name, "https://lebasketographe.fr/wp-content/uploads/2019/11/nom-equipes-nba.jpg", "#1a2b3c", "#FF00AA"); + } } function _get_base_path(): string { diff --git a/public/api/index.php b/public/api/index.php index db14194..be2e8a3 100644 --- a/public/api/index.php +++ b/public/api/index.php @@ -33,6 +33,7 @@ function getAccountController(): APIAccountsController { $con = new Connection(get_database()); $gw = new AccountGateway($con); return new APIAccountsController(new AuthModel($gw), $gw); + } function getServerController(): APIServerController { @@ -40,6 +41,13 @@ function getServerController(): APIServerController { return new APIServerController($basePath, get_database()); } +function getAPITeamController(): \IQBall\Api\Controller\APITeamController { + $con = new Connection(get_database()); + return new \IQBall\Api\Controller\APITeamController(new \IQBall\Core\Model\TeamModel(new \IQBall\Core\Gateway\TeamGateway($con), new \IQBall\Core\Gateway\MemberGateway($con), new AccountGateway($con))); +} + + + function getRoutes(): AltoRouter { global $basePath; $router = new AltoRouter(); @@ -48,7 +56,6 @@ function getRoutes(): AltoRouter { $router->map("POST", "/auth", Action::noAuth(fn() => getAuthController()->authorize())); $router->map("POST", "/tactic/[i:id]/edit/name", Action::auth(fn(int $id, Account $acc) => getTacticController()->updateName($id, $acc))); $router->map("POST", "/tactic/[i:id]/save", Action::auth(fn(int $id, Account $acc) => getTacticController()->saveContent($id, $acc))); - $router->map("GET", "/admin/list-users", Action::noAuth(fn() => getAccountController()->listUsers($_GET))); $router->map("GET", "/admin/user/[i:id]", Action::noAuth(fn(int $id) => getAccountController()->getUser($id))); $router->map("GET", "/admin/user/[i:id]/space", Action::noAuth(fn(int $id) => getTacticController()->getUserTactics($id))); @@ -56,6 +63,12 @@ function getRoutes(): AltoRouter { $router->map("POST", "/admin/user/remove-all", Action::noAuth(fn() => getAccountController()->removeUsers())); $router->map("POST", "/admin/user/[i:id]/update", Action::noAuth(fn(int $id) => getAccountController()->updateUser($id))); $router->map("GET", "/admin/server-info", Action::noAuth(fn() => getServerController()->getServerInfo())); + $router->map("GET", "/admin/list-team", Action::noAuth(fn() => getAPITeamController()->listTeams($_GET))); + $router->map("POST", "/admin/add-team", Action::noAuth(fn() => getAPITeamController()->addTeam())); + $router->map("POST", "/admin/delete-teams", Action::noAuth(fn() => getAPITeamController()->deleteTeamSelected())); + $router->map("POST", "/admin/team/[i:id]/update", Action::noAuth(fn(int $id) => getAPITeamController()->updateTeam($id))); + + return $router; } diff --git a/sql/database.php b/sql/database.php index 69b53e7..6d20c56 100644 --- a/sql/database.php +++ b/sql/database.php @@ -1,5 +1,9 @@ getHeaders() as $header => $value) { header("$header: $value"); } diff --git a/src/Api/Controller/APIAccountsController.php b/src/Api/Controller/APIAccountsController.php index 32fd956..6ebf0fc 100644 --- a/src/Api/Controller/APIAccountsController.php +++ b/src/Api/Controller/APIAccountsController.php @@ -3,7 +3,6 @@ namespace IQBall\Api\Controller; use IQBall\Api\APIControl; -use IQBall\App\Control; use IQBall\Core\Data\Account; use IQBall\Core\Gateway\AccountGateway; use IQBall\Core\Http\HttpCodes; @@ -25,6 +24,7 @@ class APIAccountsController { public function __construct(AuthModel $model, AccountGateway $accounts) { $this->accounts = $accounts; $this->authModel = $model; + } @@ -34,6 +34,7 @@ class APIAccountsController { */ public function listUsers(array $request): HttpResponse { return APIControl::runCheckedFrom($request, [ + 'start' => [DefaultValidators::isUnsignedInteger()], 'n' => [DefaultValidators::isIntInRange(0, 250)], 'search' => [DefaultValidators::lenBetween(0, 256)], diff --git a/src/Api/Controller/APITacticController.php b/src/Api/Controller/APITacticController.php index 3cfe02e..eb00ff5 100644 --- a/src/Api/Controller/APITacticController.php +++ b/src/Api/Controller/APITacticController.php @@ -77,6 +77,7 @@ class APITacticController { 'name' => $t->getName(), 'court' => $t->getCourtType(), 'creation_date' => $t->getCreationDate(), + ], $tactics); return new JsonHttpResponse($response); diff --git a/src/Api/Controller/APITeamController.php b/src/Api/Controller/APITeamController.php new file mode 100644 index 0000000..270468d --- /dev/null +++ b/src/Api/Controller/APITeamController.php @@ -0,0 +1,79 @@ +teamModel = $teamModel; + } + + /** + * @param array $req_params + * @return HttpResponse + */ + public function listTeams(array $req_params): HttpResponse { + return APIControl::runCheckedFrom($req_params, [ + 'start' => [DefaultValidators::isUnsignedInteger()], + 'n' => [DefaultValidators::isUnsignedInteger()], + ], function (HttpRequest $req) { + $teams = $this->teamModel->listAll(intval($req['start']), intval($req['n'])); + return new JsonHttpResponse([ + "totalCount" => $this->teamModel->countTeam(), + "teams" => $teams, + ]); + }); + } + + public function addTeam(): HttpResponse { + return APIControl::runChecked([ + 'name' => [DefaultValidators::name()], + 'picture' => [DefaultValidators::isURL()], + 'mainColor' => [DefaultValidators::hexColor()], + 'secondaryColor' => [DefaultValidators::hexColor()], + + ], function (HttpRequest $req) { + $this->teamModel->createTeam($req['name'], $req['picture'], $req['mainColor'], $req['secondaryColor']); + return HttpResponse::fromCode(HttpCodes::OK); + }); + } + + public function deleteTeamSelected(): HttpResponse { + return APIControl::runChecked([ + 'teams' => [], + ], function (HttpRequest $req) { + $this->teamModel->deleteTeamSelected($req['teams']); + return HttpResponse::fromCode(HttpCodes::OK); + }); + } + + public function updateTeam(int $id): HttpResponse { + return APIControl::runChecked([ + 'name' => [DefaultValidators::name()], + 'picture' => [DefaultValidators::isURL()], + 'mainColor' => [DefaultValidators::hexColor()], + 'secondaryColor' => [DefaultValidators::hexColor()], + ], function (HttpRequest $req) { + $this->teamModel->editTeam($req['id'], $req['name'], $req['picture'], $req['mainColor'], $req['secondaryColor']); + return HttpResponse::fromCode(HttpCodes::OK); + }); + } + + +} diff --git a/src/App/App.php b/src/App/App.php index 1cfe6d7..557ad13 100644 --- a/src/App/App.php +++ b/src/App/App.php @@ -12,7 +12,6 @@ use Twig\Environment; use Twig\Error\LoaderError; use Twig\Error\RuntimeError; use Twig\Error\SyntaxError; -use Twig\Loader\FilesystemLoader; class App { /** @@ -90,6 +89,7 @@ class App { if ($action->getAuthType() == Action::AUTH_ADMIN && !$account->getUser()->isAdmin()) { return new JsonHttpResponse([ValidationFail::unauthorized()], HttpCodes::UNAUTHORIZED); } + } return $action->run($params, $session); diff --git a/src/Core/Control.php b/src/Core/Control.php index 106052d..51d6622 100644 --- a/src/Core/Control.php +++ b/src/Core/Control.php @@ -24,6 +24,7 @@ class Control { if (!$payload_obj instanceof \stdClass) { $fail = new ValidationFail("bad-payload", "request body is not a valid json object"); return $errorFactory->apply([$fail]); + } $payload = get_object_vars($payload_obj); return self::runCheckedFrom($payload, $schema, $run, $errorFactory); @@ -44,6 +45,7 @@ class Control { if (!empty($fails)) { return $errorFactory->apply($fails); + } return call_user_func_array($run, [$request]); diff --git a/src/Core/Data/User.php b/src/Core/Data/User.php index 02a44c0..8471929 100644 --- a/src/Core/Data/User.php +++ b/src/Core/Data/User.php @@ -24,7 +24,7 @@ class User implements \JsonSerializable { private string $profilePicture; /** - * @var bool isAdmin + * @var bool true if the user is an administrator */ private bool $isAdmin; diff --git a/src/Core/Gateway/AccountGateway.php b/src/Core/Gateway/AccountGateway.php index 6752b01..1a0c689 100644 --- a/src/Core/Gateway/AccountGateway.php +++ b/src/Core/Gateway/AccountGateway.php @@ -47,7 +47,6 @@ class AccountGateway { return !empty($result); } - /** * promote or demote a user to server administrator * @param int $id @@ -60,6 +59,7 @@ class AccountGateway { } else { $stmnt = $this->con->prepare("DELETE FROM Admins WHERE id = :id"); } + $stmnt->bindValue(':id', $id); $stmnt->execute(); @@ -155,6 +155,7 @@ class AccountGateway { ] ); return array_map(fn(array $acc) => new Account($acc["token"], new User($acc["email"], $acc["username"], $acc["id"], $acc["profile_picture"], $this->isAdmin($acc["id"]))), $res); + } /** diff --git a/src/Core/Gateway/MemberGateway.php b/src/Core/Gateway/MemberGateway.php index 98d2d41..f79ff60 100644 --- a/src/Core/Gateway/MemberGateway.php +++ b/src/Core/Gateway/MemberGateway.php @@ -47,6 +47,7 @@ class MemberGateway { ] ); return array_map(fn($row) => new Member(new User($row['email'], $row['username'], $row['id'], $row['profile_picture'], $row['is_admin']), $teamId, $row['role']), $rows); + } /** diff --git a/src/Core/Gateway/TeamGateway.php b/src/Core/Gateway/TeamGateway.php index a817687..4309a49 100644 --- a/src/Core/Gateway/TeamGateway.php +++ b/src/Core/Gateway/TeamGateway.php @@ -3,6 +3,8 @@ namespace IQBall\Core\Gateway; use IQBall\Core\Connection; +use IQBall\Core\Data\Color; +use IQBall\Core\Data\Team; use IQBall\Core\Data\TeamInfo; use PDO; @@ -46,6 +48,7 @@ class TeamGateway { "id" => [$id, PDO::PARAM_INT], ] ); + return array_map(fn($row) => new TeamInfo($row['id'], $row['name'], $row['picture'], $row['main_color'], $row['second_color']), $result); } @@ -137,5 +140,47 @@ class TeamGateway { ); } + /** + * @param int $start + * @param int $n + * @return TeamInfo[] + */ + public function listAll(int $start, int $n): array { + $rows = $this->con->fetch( + "SELECT * FROM Team LIMIT :start, :n", + [ + ":start" => [$start, PDO::PARAM_INT], + ":n" => [$n, PDO::PARAM_INT], + ] + ); + return array_map(fn($row) => new TeamInfo($row['id'], $row['name'], $row['picture'], $row['main_color'], $row['second_color']), $rows); + } + + public function countTeam(): int { + $result = $this->con->fetch( + "SELECT count(*) as count FROM Team", + [] + ); + if (empty($result) || !isset($result[0]['count'])) { + return 0; + } + return $result[0]['count']; + } + + /** + * @param array $selectedTeams + * @return void + */ + public function deleteTeamSelected(array $selectedTeams): void { + foreach ($selectedTeams as $team) { + $this->con->exec( + "DELETE FROM TEAM WHERE id=:team", + [ + "team" => [$team, PDO::PARAM_INT], + ] + ); + } + } + } diff --git a/src/Core/Model/TeamModel.php b/src/Core/Model/TeamModel.php index 2bfe36e..b6b7bdd 100644 --- a/src/Core/Model/TeamModel.php +++ b/src/Core/Model/TeamModel.php @@ -45,10 +45,10 @@ class TeamModel { */ public function addMember(string $mail, int $teamId, string $role): int { $user = $this->users->getAccountFromMail($mail); - if($user == null) { + if ($user == null) { return -1; } - if(!$this->members->isMemberOfTeam($teamId, $user->getUser()->getId())) { + if (!$this->members->isMemberOfTeam($teamId, $user->getUser()->getId())) { $this->members->insert($teamId, $user->getUser()->getId(), $role); return 1; } @@ -70,7 +70,7 @@ class TeamModel { * @return Team|null */ public function getTeam(int $idTeam, int $idCurrentUser): ?Team { - if(!$this->members->isMemberOfTeam($idTeam, $idCurrentUser)) { + if (!$this->members->isMemberOfTeam($idTeam, $idCurrentUser)) { return null; } $teamInfo = $this->teams->getTeamById($idTeam); @@ -86,7 +86,7 @@ class TeamModel { */ public function deleteMember(int $idMember, int $teamId): int { $this->members->remove($teamId, $idMember); - if(empty($this->members->getMembersOfTeam($teamId))) { + if (empty($this->members->getMembersOfTeam($teamId))) { $this->teams->deleteTeam($teamId); return -1; } @@ -100,7 +100,7 @@ class TeamModel { * @return int */ public function deleteTeam(string $email, int $idTeam): int { - if($this->members->isCoach($email, $idTeam)) { + if ($this->members->isCoach($email, $idTeam)) { $this->teams->deleteTeam($idTeam); return 0; } @@ -139,4 +139,26 @@ class TeamModel { public function getAll(int $user): array { return $this->teams->getAll($user); } + + /** + * @param int $start + * @param int $n + * @return TeamInfo[] + */ + public function listAll(int $start, int $n) { + return $this->teams->listAll($start, $n); + } + + public function countTeam(): int { + return $this->teams->countTeam(); + } + + /** + * @param array $selectedTeams + * @return void + */ + public function deleteTeamSelected(array $selectedTeams) { + $this->teams->deleteTeamSelected($selectedTeams); + } + }