diff --git a/front/components/editor/BasketCourt.tsx b/front/components/editor/BasketCourt.tsx index e59bdb7..1e3d916 100644 --- a/front/components/editor/BasketCourt.tsx +++ b/front/components/editor/BasketCourt.tsx @@ -9,12 +9,13 @@ import { } from "react" import CourtPlayer from "./CourtPlayer" -import { Player } from "../../tactic/Player" -import { Action, ActionKind } from "../../tactic/Action" + +import { Player } from "../../model/tactic/Player" +import { Action, ActionKind } from "../../model/tactic/Action" import ArrowAction from "../actions/ArrowAction" import { middlePos, ratioWithinBase } from "../arrows/Pos" import BallAction from "../actions/BallAction" -import { CourtObject } from "../../tactic/CourtObjects" +import { CourtObject } from "../../model/tactic/Ball" import { contains } from "../arrows/Box" import { CourtAction } from "../../views/editor/CourtAction" @@ -255,7 +256,7 @@ export function BasketCourt({ /> ) } - throw new Error("unknown court object", object.type) + throw new Error("unknown court object" + object.type) })} {previewAction && ( diff --git a/front/components/editor/CourtBall.tsx b/front/components/editor/CourtBall.tsx index 9ba5ae5..b1fa1d0 100644 --- a/front/components/editor/CourtBall.tsx +++ b/front/components/editor/CourtBall.tsx @@ -1,7 +1,7 @@ import React, { useRef } from "react" import Draggable from "react-draggable" import { BallPiece } from "./BallPiece" -import { Ball } from "../../tactic/CourtObjects" +import { Ball } from "../../model/tactic/Ball" export interface CourtBallProps { onMoved: (rect: DOMRect) => void diff --git a/front/components/editor/CourtPlayer.tsx b/front/components/editor/CourtPlayer.tsx index 3d5ffde..c0c94d5 100644 --- a/front/components/editor/CourtPlayer.tsx +++ b/front/components/editor/CourtPlayer.tsx @@ -2,7 +2,7 @@ import { ReactNode, RefObject, useRef } from "react" import "../../style/player.css" import Draggable from "react-draggable" import { PlayerPiece } from "./PlayerPiece" -import { Player } from "../../tactic/Player" +import { Player } from "../../model/tactic/Player" import { NULL_POS, ratioWithinBase } from "../arrows/Pos" export interface PlayerProps { diff --git a/front/components/editor/PlayerPiece.tsx b/front/components/editor/PlayerPiece.tsx index a1b5e74..1b52ff8 100644 --- a/front/components/editor/PlayerPiece.tsx +++ b/front/components/editor/PlayerPiece.tsx @@ -1,8 +1,8 @@ import "../../style/player.css" -import { Team } from "../../tactic/Team" +import { PlayerTeam } from "../../model/tactic/Player" export interface PlayerPieceProps { - team: Team + team: PlayerTeam text: string hasBall: boolean } diff --git a/front/model/Team.ts b/front/model/Team.ts new file mode 100644 index 0000000..99d530b --- /dev/null +++ b/front/model/Team.ts @@ -0,0 +1,19 @@ +import {User} from "./User"; + +export interface TeamInfo { + id: number + name: string + picture: string + mainColor: string + secondColor: string +} + +export interface Team { + info: TeamInfo + members: Member[] +} + +export interface Member { + user: User + role: string +} diff --git a/front/model/User.ts b/front/model/User.ts new file mode 100644 index 0000000..36bbb67 --- /dev/null +++ b/front/model/User.ts @@ -0,0 +1,6 @@ +export interface User { + id: number + name: string + email: string + profilePicture: string +} diff --git a/front/tactic/Action.ts b/front/model/tactic/Action.ts similarity index 75% rename from front/tactic/Action.ts rename to front/model/tactic/Action.ts index d66f375..0b5aee5 100644 --- a/front/tactic/Action.ts +++ b/front/model/tactic/Action.ts @@ -1,5 +1,5 @@ -import { Pos } from "../components/arrows/Pos" -import { Segment } from "../components/arrows/BendableArrow" +import { Pos } from "../../components/arrows/Pos" +import { Segment } from "../../components/arrows/BendableArrow" import { PlayerId } from "./Player" export enum ActionKind { diff --git a/front/tactic/CourtObjects.ts b/front/model/tactic/Ball.ts similarity index 100% rename from front/tactic/CourtObjects.ts rename to front/model/tactic/Ball.ts diff --git a/front/tactic/Player.ts b/front/model/tactic/Player.ts similarity index 84% rename from front/tactic/Player.ts rename to front/model/tactic/Player.ts index 1d71d8a..f94d6bf 100644 --- a/front/tactic/Player.ts +++ b/front/model/tactic/Player.ts @@ -1,14 +1,17 @@ -import { Team } from "./Team" - export type PlayerId = string +export enum PlayerTeam { + Allies = "allies", + Opponents = "opponents", +} + export interface Player { readonly id: PlayerId /** * the player's team * */ - readonly team: Team + readonly team: PlayerTeam /** * player's role diff --git a/front/tactic/Tactic.ts b/front/model/tactic/Tactic.ts similarity index 85% rename from front/tactic/Tactic.ts rename to front/model/tactic/Tactic.ts index 296c339..2eab85b 100644 --- a/front/tactic/Tactic.ts +++ b/front/model/tactic/Tactic.ts @@ -1,5 +1,5 @@ import { Player } from "./Player" -import { CourtObject } from "./CourtObjects" +import { CourtObject } from "./Ball" import { Action } from "./Action" export interface Tactic { diff --git a/front/style/team_panel.css b/front/style/team_panel.css new file mode 100644 index 0000000..6e48795 --- /dev/null +++ b/front/style/team_panel.css @@ -0,0 +1,135 @@ +#main-div { + display: flex; + flex-direction: column; + align-items: center; + height: 100%; +} + +#main-div header { + display: flex; + justify-content: center; + background-color: #525252; + width: 100%; + margin-bottom: 5px; +} + +header h1 a { + color: orange; + text-decoration: none; + font-size: 1.5em; +} + +.square { + width: 50px; + height: 50px; + border: 2px white solid; +} + +#team-info { + display: flex; + flex-direction: column; + align-items: center; + width: 60%; + background-color: #8f8f8f; + padding-bottom: 10px; + border-radius: 10px; +} + +#first-part { + display: flex; + flex-direction: column; + align-items: center; +} + +#team-name { + font-size: 2.8em; +} + +#colors { + display: flex; + flex-direction: column; +} +.color { + flex-direction: row; + justify-content: space-between; +} + +#colorsTitle { + width: 110%; + display: flex; + flex-direction: row; + justify-content: space-between; + font-size: 1.3em; + color: white; +} + +#actual-colors { + display: flex; + flex-direction: row; + justify-content: space-around; +} + +#logo { + aspect-ratio: 3/2; + object-fit: contain; + max-width: 70%; + max-height: 70%; +} + +#delete { + border-radius: 10px; + background-color: red; + color: white; + margin-top: 10px; + margin-bottom: 10px; + margin-right: 5px; +} + +#edit { + border-radius: 10px; + background-color: orange; + color: white; + margin-top: 10px; + margin-bottom: 10px; +} + +#head-members { + width: 33%; + display: flex; + flex-direction: row; + justify-content: space-evenly; +} + +#add-member { + height: 30px; + aspect-ratio: 1/1; + border-radius: 100%; + align-self: center; +} + +#members { + display: flex; + flex-direction: column; + background-color: #bcbcbc; + width: 60%; + align-items: center; + justify-content: space-around; + border-radius: 10px; +} + +.member { + width: 60%; + background-color: white; + display: flex; + flex-direction: row; + justify-content: space-evenly; + align-items: center; + border-radius: 10px; + margin-top: 5px; + margin-bottom: 5px; +} + +#profile-picture { + height: 40px; + width: 40px; +} diff --git a/front/tactic/Team.ts b/front/tactic/Team.ts deleted file mode 100644 index 5b35943..0000000 --- a/front/tactic/Team.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum Team { - Allies = "allies", - Opponents = "opponents", -} diff --git a/front/views/Editor.tsx b/front/views/Editor.tsx index 69c1eca..e1f0edd 100644 --- a/front/views/Editor.tsx +++ b/front/views/Editor.tsx @@ -16,22 +16,22 @@ import { BallPiece } from "../components/editor/BallPiece" import { Rack } from "../components/Rack" import { PlayerPiece } from "../components/editor/PlayerPiece" -import { Player } from "../tactic/Player" +import { Player } from "../model/tactic/Player" -import { Tactic, TacticContent } from "../tactic/Tactic" +import { Tactic, TacticContent } from "../model/tactic/Tactic" import { fetchAPI } from "../Fetcher" -import { Team } from "../tactic/Team" +import { PlayerTeam } from "../model/tactic/Player" import SavingState, { SaveState, SaveStates, } from "../components/editor/SavingState" -import { CourtObject } from "../tactic/CourtObjects" +import { CourtObject } from "../model/tactic/Ball" import { CourtAction } from "./editor/CourtAction" import { BasketCourt } from "../components/editor/BasketCourt" import { ratioWithinBase } from "../components/arrows/Pos" -import { Action, ActionKind } from "../tactic/Action" +import { Action, ActionKind } from "../model/tactic/Action" const ERROR_STYLE: CSSProperties = { borderColor: "red", @@ -58,7 +58,7 @@ export interface EditorProps { * information about a player that is into a rack */ interface RackedPlayer { - team: Team + team: PlayerTeam key: string } @@ -134,10 +134,10 @@ function EditorView({ ) const [allies, setAllies] = useState( - getRackPlayers(Team.Allies, content.players), + getRackPlayers(PlayerTeam.Allies, content.players), ) const [opponents, setOpponents] = useState( - getRackPlayers(Team.Opponents, content.players), + getRackPlayers(PlayerTeam.Opponents, content.players), ) const [objects, setObjects] = useState( @@ -222,7 +222,7 @@ function EditorView({ break default: - throw new Error("unknown court object ", rackedObject.key) + throw new Error("unknown court object " + rackedObject.key) } setContent((content) => { @@ -357,10 +357,10 @@ function EditorView({ })) let setter switch (player.team) { - case Team.Opponents: + case PlayerTeam.Opponents: setter = setOpponents break - case Team.Allies: + case PlayerTeam.Allies: setter = setAllies } if (player.hasBall) { @@ -539,7 +539,7 @@ function renderCourtObject(courtObject: RackedCourtObject) { if (courtObject.key == "ball") { return } - throw new Error("unknown racked court object ", courtObject.key) + throw new Error("unknown racked court object " + courtObject.key) } function Court({ courtType }: { courtType: string }) { @@ -554,7 +554,7 @@ function Court({ courtType }: { courtType: string }) { ) } -function getRackPlayers(team: Team, players: Player[]): RackedPlayer[] { +function getRackPlayers(team: PlayerTeam, players: Player[]): RackedPlayer[] { return ["1", "2", "3", "4", "5"] .filter( (role) => diff --git a/front/views/Home.tsx b/front/views/Home.tsx index 44803a3..8be885d 100644 --- a/front/views/Home.tsx +++ b/front/views/Home.tsx @@ -162,7 +162,7 @@ function TableData({ allTactics }: { allTactics: Tactic[] }) { function BodyPersonalSpace({ allTactics }: { allTactics: Tactic[] }) { let data if (allTactics.length == 0) { - data =

Aucune tactique créé !

+ data =

Aucune tactique créée !

} else { data = } diff --git a/front/views/TeamPanel.tsx b/front/views/TeamPanel.tsx new file mode 100644 index 0000000..709d7f2 --- /dev/null +++ b/front/views/TeamPanel.tsx @@ -0,0 +1,169 @@ +import "../style/team_panel.css" +import { BASE } from "../Constants" +import { Team, TeamInfo, Member } from "../model/Team" +import { User } from "../model/User" + +export default function TeamPanel({ + isCoach, + team, + currentUserId, +}: { + isCoach: boolean + team: Team + currentUserId: number +}) { + return ( +
+
+

+ IQBall +

+
+ + + {isCoach && } + + +
+ ) +} + +function TeamDisplay({ team }: { team: TeamInfo }) { + return ( +
+
+

{team.name}

+ +
+
+
+

Couleur principale

+

Couleur secondaire

+
+
+ + +
+
+
+ ) +} + +function ColorDisplay({ color }: { color: string }) { + return
+} + +function CoachOptions({ id }: { id: number }) { + return ( +
+ + +
+ ) +} + +function MembersDisplay({ + members, + isCoach, + idTeam, + currentUserId, +}: { + members: Member[] + isCoach: boolean + idTeam: number + currentUserId: number +}) { + const listMember = members.map((member) => ( + + )) + return ( +
+
+

Membres :

+ {isCoach && ( + + )} +
+ {listMember} +
+ ) +} + +function MemberDisplay({ + member, + isCoach, + idTeam, + currentUserId, +}: { + member: Member + isCoach: boolean + idTeam: number + currentUserId: number +}) { + return ( +
+ Photo de profile +

{member.user.name}

+

{member.role}

+

{member.user.email}

+ {isCoach && currentUserId !== member.user.id && ( + + )} + {isCoach && currentUserId == member.user.id && ( + + )} +
+ ) +} diff --git a/front/views/editor/CourtAction.tsx b/front/views/editor/CourtAction.tsx index b4028ff..de33224 100644 --- a/front/views/editor/CourtAction.tsx +++ b/front/views/editor/CourtAction.tsx @@ -1,4 +1,4 @@ -import { Action, ActionKind } from "../../tactic/Action" +import { Action, ActionKind } from "../../model/tactic/Action" import BendableArrow from "../../components/arrows/BendableArrow" import { RefObject } from "react" import { MoveToHead, ScreenHead } from "../../components/actions/ArrowAction" diff --git a/public/api/index.php b/public/api/index.php index 5734571..da25013 100644 --- a/public/api/index.php +++ b/public/api/index.php @@ -51,7 +51,6 @@ function tryGetAuthorization(): ?Account { $session = PhpSessionHandle::init(); return $session->getAccount(); } - $token = $headers['Authorization']; $gateway = new AccountGateway(new Connection(get_database())); return $gateway->getAccountFromToken($token); diff --git a/public/index.php b/public/index.php index 935e2f2..82dd37f 100644 --- a/public/index.php +++ b/public/index.php @@ -102,10 +102,13 @@ function getRoutes(): AltoRouter { $ar->map("GET", "/team/search", Action::auth(fn(SessionHandle $s) => getTeamController()->displayListTeamByName($s))); $ar->map("POST", "/team/search", Action::auth(fn(SessionHandle $s) => getTeamController()->listTeamByName($_POST, $s))); $ar->map("GET", "/team/[i:id]", Action::auth(fn(int $id, SessionHandle $s) => getTeamController()->displayTeam($id, $s))); - $ar->map("GET", "/team/members/add", Action::auth(fn(SessionHandle $s) => getTeamController()->displayAddMember($s))); - $ar->map("POST", "/team/members/add", Action::auth(fn(SessionHandle $s) => getTeamController()->addMember($_POST, $s))); - $ar->map("GET", "/team/members/remove", Action::auth(fn(SessionHandle $s) => getTeamController()->displayDeleteMember($s))); - $ar->map("POST", "/team/members/remove", Action::auth(fn(SessionHandle $s) => getTeamController()->deleteMember($_POST, $s))); + $ar->map("GET", "/team/[i:id]/delete", Action::auth(fn(int $id, SessionHandle $s) => getTeamController()->deleteTeamById($id, $s))); + $ar->map("GET", "/team/[i:id]/addMember", Action::auth(fn(int $id, SessionHandle $s) => getTeamController()->displayAddMember($id, $s))); + $ar->map("POST", "/team/[i:id]/addMember", Action::auth(fn(int $id, SessionHandle $s) => getTeamController()->addMember($id, $_POST, $s))); + $ar->map("GET", "/team/[i:idTeam]/remove/[i:idMember]", Action::auth(fn(int $idTeam, int $idMember, SessionHandle $s) => getTeamController()->deleteMember($idTeam, $idMember, $s))); + $ar->map("GET", "/team/[i:id]/edit", Action::auth(fn(int $idTeam, SessionHandle $s) => getTeamController()->displayEditTeam($idTeam, $s))); + $ar->map("POST", "/team/[i:id]/edit", Action::auth(fn(int $idTeam, SessionHandle $s) => getTeamController()->editTeam($idTeam, $_POST, $s))); + return $ar; } diff --git a/sql/setup-tables.sql b/sql/setup-tables.sql index 633081f..0d157d9 100644 --- a/sql/setup-tables.sql +++ b/sql/setup-tables.sql @@ -10,7 +10,8 @@ CREATE TABLE Account email varchar UNIQUE NOT NULL, username varchar NOT NULL, token varchar UNIQUE NOT NULL, - hash varchar NOT NULL + hash varchar NOT NULL, + profilePicture varchar NOT NULL ); CREATE TABLE Tactic diff --git a/src/Api/Controller/APITacticController.php b/src/Api/Controller/APITacticController.php index 79e766c..a116add 100644 --- a/src/Api/Controller/APITacticController.php +++ b/src/Api/Controller/APITacticController.php @@ -36,7 +36,7 @@ class APITacticController { "name" => [Validators::lenBetween(1, 50), Validators::nameWithSpaces()], ], function (HttpRequest $request) use ($tactic_id, $account) { - $failures = $this->model->updateName($tactic_id, $request["name"], $account->getId()); + $failures = $this->model->updateName($tactic_id, $request["name"], $account->getUser()->getId()); if (!empty($failures)) { //TODO find a system to handle Unauthorized error codes more easily from failures. diff --git a/src/App/Controller/EditorController.php b/src/App/Controller/EditorController.php index 5561590..4bdcfae 100644 --- a/src/App/Controller/EditorController.php +++ b/src/App/Controller/EditorController.php @@ -63,7 +63,7 @@ class EditorController { return $this->openTestEditor($type); } - $tactic = $this->model->makeNewDefault($session->getAccount()->getId(), $type); + $tactic = $this->model->makeNewDefault($session->getAccount()->getUser()->getId(), $type); return $this->openEditorFor($tactic); } @@ -76,7 +76,7 @@ class EditorController { public function openEditor(int $id, SessionHandle $session): ViewHttpResponse { $tactic = $this->model->get($id); - $failure = TacticValidator::validateAccess($id, $tactic, $session->getAccount()->getId()); + $failure = TacticValidator::validateAccess($id, $tactic, $session->getAccount()->getUser()->getId()); if ($failure != null) { return ViewHttpResponse::twig('error.html.twig', ['failures' => [$failure]], HttpCodes::NOT_FOUND); diff --git a/src/App/Controller/TeamController.php b/src/App/Controller/TeamController.php index b2c0ea9..4ab3fd7 100644 --- a/src/App/Controller/TeamController.php +++ b/src/App/Controller/TeamController.php @@ -4,10 +4,13 @@ namespace IQBall\App\Controller; use IQBall\App\Session\SessionHandle; use IQBall\App\ViewHttpResponse; +use IQBall\Core\Data\Account; +use IQBall\Core\Http\HttpCodes; use IQBall\Core\Http\HttpRequest; use IQBall\Core\Http\HttpResponse; use IQBall\Core\Model\TeamModel; use IQBall\Core\Validation\FieldValidationFail; +use IQBall\Core\Validation\ValidationFail; use IQBall\Core\Validation\Validators; class TeamController { @@ -28,16 +31,6 @@ class TeamController { return ViewHttpResponse::twig("insert_team.html.twig", []); } - - /** - * @param SessionHandle $session - * @return ViewHttpResponse the team panel to add a member - */ - public function displayAddMember(SessionHandle $session): ViewHttpResponse { - return ViewHttpResponse::twig("add_member.html.twig", []); - } - - /** * @param SessionHandle $session * @return ViewHttpResponse the team panel to delete a member @@ -70,7 +63,8 @@ class TeamController { return ViewHttpResponse::twig('insert_team.html.twig', ['bad_fields' => $badFields]); } $teamId = $this->model->createTeam($request['name'], $request['picture'], $request['main_color'], $request['second_color']); - return $this->displayTeam($teamId, $session); + $this->model->addMember($session->getAccount()->getUser()->getEmail(), $teamId, 'COACH'); + return HttpResponse::redirect('/team/' . $teamId); } /** @@ -98,58 +92,155 @@ class TeamController { return ViewHttpResponse::twig('list_team_by_name.html.twig', ['bad_field' => $badField]); } - $teams = $this->model->listByName($request['name']); + $teams = $this->model->listByName($request['name'], $session->getAccount()->getUser()->getId()); if (empty($teams)) { return ViewHttpResponse::twig('display_teams.html.twig', []); } - return ViewHttpResponse::twig('display_teams.html.twig', ['teams' => $teams]); } /** + * Delete a team with its id + * @param int $id + * @param SessionHandle $session + * @return HttpResponse + */ + public function deleteTeamById(int $id, SessionHandle $session): HttpResponse { + $a = $session->getAccount(); + $ret = $this->model->deleteTeam($a->getUser()->getEmail(), $id); + if($ret != 0) { + return ViewHttpResponse::twig('display_team.html.twig', ['notDeleted' => true]); + } + return HttpResponse::redirect('/'); + } + + /** + * Display a team with its id * @param int $id * @param SessionHandle $session * @return ViewHttpResponse a view that displays given team information */ public function displayTeam(int $id, SessionHandle $session): ViewHttpResponse { - $result = $this->model->getTeam($id); - return ViewHttpResponse::twig('display_team.html.twig', ['team' => $result]); + $result = $this->model->getTeam($id, $session->getAccount()->getUser()->getId()); + if($result == null) { + return ViewHttpResponse::twig('error.html.twig', [ + 'failures' => [ValidationFail::unauthorized("Vous n'avez pas accès à cette équipe.")], + ], HttpCodes::FORBIDDEN); + } + $role = $this->model->isCoach($id, $session->getAccount()->getUser()->getEmail()); + + return ViewHttpResponse::react( + 'views/TeamPanel.tsx', + [ + 'team' => [ + "info" => $result->getInfo(), + "members" => $result->listMembers(), + ], + 'isCoach' => $role, + 'currentUserId' => $session->getAccount()->getUser()->getId()] + ); + } + + /** + * @param int $idTeam + * @param SessionHandle $session + * @return ViewHttpResponse the team panel to add a member + */ + public function displayAddMember(int $idTeam, SessionHandle $session): ViewHttpResponse { + return ViewHttpResponse::twig("add_member.html.twig", ['idTeam' => $idTeam]); } /** * add a member to a team + * @param int $idTeam * @param array $request * @param SessionHandle $session * @return HttpResponse */ - public function addMember(array $request, SessionHandle $session): HttpResponse { + public function addMember(int $idTeam, array $request, SessionHandle $session): HttpResponse { $errors = []; - + if(!$this->model->isCoach($idTeam, $session->getAccount()->getUser()->getEmail())) { + return ViewHttpResponse::twig('error.html.twig', [ + 'failures' => [ValidationFail::unauthorized("Vous n'avez pas accès à cette action pour cette équipe.")], + ], HttpCodes::FORBIDDEN); + } $request = HttpRequest::from($request, $errors, [ - "team" => [Validators::isInteger()], "email" => [Validators::email(), Validators::lenBetween(5, 256)], ]); + if(!empty($errors)) { + return ViewHttpResponse::twig('add_member.html.twig', ['badEmail' => true,'idTeam' => $idTeam]); + } + $ret = $this->model->addMember($request['email'], $idTeam, $request['role']); + + switch($ret) { + case -1: + return ViewHttpResponse::twig('add_member.html.twig', ['notFound' => true,'idTeam' => $idTeam]); + case -2: + return ViewHttpResponse::twig('add_member.html.twig', ['alreadyExisting' => true,'idTeam' => $idTeam]); + default: + return HttpResponse::redirect('/team/' . $idTeam); + } + } - $teamId = intval($request['team']); - $this->model->addMember($request['email'], $teamId, $request['role']); + /** + * remove a member from a team with their ids + * @param int $idTeam + * @param int $idMember + * @param SessionHandle $session + * @return HttpResponse + */ + public function deleteMember(int $idTeam, int $idMember, SessionHandle $session): HttpResponse { + if(!$this->model->isCoach($idTeam, $session->getAccount()->getUser()->getEmail())) { + return ViewHttpResponse::twig('error.html.twig', [ + 'failures' => [ValidationFail::unauthorized("Vous n'avez pas accès à cette action pour cette équipe.")], + ], HttpCodes::FORBIDDEN); + } + $teamId = $this->model->deleteMember($idMember, $idTeam); + if($teamId == -1 || $session->getAccount()->getUser()->getId() == $idMember) { + return HttpResponse::redirect('/'); + } return $this->displayTeam($teamId, $session); } /** - * remove a member from a team - * @param array $request + * @param int $idTeam * @param SessionHandle $session - * @return HttpResponse + * @return ViewHttpResponse */ - public function deleteMember(array $request, SessionHandle $session): HttpResponse { - $errors = []; + public function displayEditTeam(int $idTeam, SessionHandle $session): ViewHttpResponse { + return ViewHttpResponse::twig("edit_team.html.twig", ['team' => $this->model->getTeam($idTeam, $session->getAccount()->getUser()->getId())]); + } - $request = HttpRequest::from($request, $errors, [ - "team" => [Validators::isInteger()], - "email" => [Validators::email(), Validators::lenBetween(5, 256)], + /** + * @param int $idTeam + * @param array $request + * @param SessionHandle $session + * @return HttpResponse + */ + public function editTeam(int $idTeam, array $request, SessionHandle $session): HttpResponse { + if(!$this->model->isCoach($idTeam, $session->getAccount()->getUser()->getEmail())) { + return ViewHttpResponse::twig('error.html.twig', [ + 'failures' => [ValidationFail::unauthorized("Vous n'avez pas accès à cette action pour cette équipe.")], + ], HttpCodes::FORBIDDEN); + } + $failures = []; + $request = HttpRequest::from($request, $failures, [ + "name" => [Validators::lenBetween(1, 32), Validators::nameWithSpaces()], + "main_color" => [Validators::hexColor()], + "second_color" => [Validators::hexColor()], + "picture" => [Validators::isURL()], ]); - - return $this->displayTeam($this->model->deleteMember($request['email'], intval($request['team'])), $session); + if (!empty($failures)) { + $badFields = []; + foreach ($failures as $e) { + if ($e instanceof FieldValidationFail) { + $badFields[] = $e->getFieldName(); + } + } + return ViewHttpResponse::twig('edit_team.html.twig', ['bad_fields' => $badFields]); + } + $this->model->editTeam($idTeam, $request['name'], $request['picture'], $request['main_color'], $request['second_color']); + return HttpResponse::redirect('/team/' . $idTeam); } } diff --git a/src/App/Controller/UserController.php b/src/App/Controller/UserController.php index e33ee61..2718843 100644 --- a/src/App/Controller/UserController.php +++ b/src/App/Controller/UserController.php @@ -16,6 +16,7 @@ class UserController { /** * @param TacticModel $tactics + * @param TeamModel|null $teams */ public function __construct(TacticModel $tactics, ?TeamModel $teams = null) { $this->tactics = $tactics; @@ -28,12 +29,15 @@ class UserController { */ public function home(SessionHandle $session): ViewHttpResponse { $limitNbTactics = 5; - $lastTactics = $this->tactics->getLast($limitNbTactics, $session->getAccount()->getId()); - $allTactics = $this->tactics->getAll($session->getAccount()->getId()); - $name = $session->getAccount()->getName(); + + $user = $session->getAccount()->getUser(); + + $lastTactics = $this->tactics->getLast($limitNbTactics, $user->getId()); + $allTactics = $this->tactics->getAll($user->getId()); + $name = $user->getName(); if ($this->teams != null) { - $teams = $this->teams->getAll($session->getAccount()->getId()); + $teams = $this->teams->getAll($user->getId()); } else { $teams = []; } @@ -45,11 +49,7 @@ class UserController { "username" => $name, ]); } - - public function homeTwig(SessionHandle $session): ViewHttpResponse { - return ViewHttpResponse::twig("home.twig", []); - } - + /** * @return ViewHttpResponse account settings page */ diff --git a/src/App/Controller/VisualizerController.php b/src/App/Controller/VisualizerController.php index 631468e..946f6d0 100644 --- a/src/App/Controller/VisualizerController.php +++ b/src/App/Controller/VisualizerController.php @@ -28,7 +28,7 @@ class VisualizerController { public function openVisualizer(int $id, SessionHandle $session): HttpResponse { $tactic = $this->tacticModel->get($id); - $failure = TacticValidator::validateAccess($id, $tactic, $session->getAccount()->getId()); + $failure = TacticValidator::validateAccess($id, $tactic, $session->getAccount()->getUser()->getId()); if ($failure != null) { return ViewHttpResponse::twig('error.html.twig', ['failures' => [$failure]], HttpCodes::NOT_FOUND); diff --git a/src/App/Views/add_member.html.twig b/src/App/Views/add_member.html.twig index c6bae0e..cfee16d 100644 --- a/src/App/Views/add_member.html.twig +++ b/src/App/Views/add_member.html.twig @@ -67,28 +67,43 @@ display: flex; justify-content: space-between; } + + .failed{ + color: red; + } +
+

IQBall

+

Ajouter un membre à votre équipe

-
+
- - - - + + + {% if badEmail %} +

Email invalide

+ {% endif %} + {%if notFound %} +

Cette personne n'a pas été trouvé

+ {% endif %} + {% if alreadyExisting %} +

Cette personne est déjà dans l'équipe

+ {% endif %} +
Rôle du membre dans l'équipe :
- +
- +
diff --git a/src/App/Views/display_team.html.twig b/src/App/Views/display_team.html.twig index 7f23b8b..8928e84 100644 --- a/src/App/Views/display_team.html.twig +++ b/src/App/Views/display_team.html.twig @@ -11,10 +11,6 @@ align-items: center; } - section { - width: 60%; - } - .square { width: 50px; height: 50px; @@ -30,19 +26,17 @@ border: solid; } - .container { + section { background-color: #fff; display: flex; flex-direction: column; align-items: center; + width: 60%; } - .team { - border-color: darkgrey; - border-radius: 20px; - + #colors{ + flex-direction: row; } - .color { flex-direction: row; justify-content: space-between; @@ -53,6 +47,16 @@ width: 80px; } + #delete{ + border-radius:10px ; + background-color: red; + color: white; + } + + .player{ + flex-direction: row; + justify-content: space-evenly; + } @@ -61,13 +65,18 @@
- -
+ {% if notDeleted %} + +

Cette équipe ne peut être supprimée.

+
+ {% endif %} +{% if team is defined %} +

{{ team.getInfo().getName() }}

-
+

Couleur principale :

@@ -75,17 +84,26 @@
- + {% if isCoach %} + + + {% endif %} {% for m in team.listMembers() %} -

{{ m.getUserId() }}

- {% if m.getRole().isCoach() %} +
+

{{ m.getUserId() }}

+ {% if m.getRole().isCoach() %}

: Coach

- {% else %} + {% else %}

: Joueur

- {% endif %} + {% endif %} +
{% endfor %}
- +{% else %} +
+

Cette équipe ne peut être affichée

+
+{% endif %}
\ No newline at end of file diff --git a/src/App/Views/display_teams.html.twig b/src/App/Views/display_teams.html.twig index 1e1420a..3e3ab12 100644 --- a/src/App/Views/display_teams.html.twig +++ b/src/App/Views/display_teams.html.twig @@ -3,9 +3,37 @@ Twig view + - +
+

IQBall

+
+
{% if teams is empty %}

Aucune équipe n'a été trouvée

@@ -22,12 +50,12 @@
{% else %} {% for t in teams %} -
-

Nom de l'équipe : {{ t.name }}

- logo de l'équipe +
+

Nom de l'équipe : {{ t.getName() }}

+ logo de l'équipe
{% endfor %} {% endif %} - +
\ No newline at end of file diff --git a/src/App/Views/edit_team.html.twig b/src/App/Views/edit_team.html.twig new file mode 100644 index 0000000..409d71a --- /dev/null +++ b/src/App/Views/edit_team.html.twig @@ -0,0 +1,81 @@ + + + + + Insertion view + + + + +
+

Modifier votre équipe

+ +
+ + + + + + + + +
+
+ +
+ +
+ + + \ No newline at end of file diff --git a/src/App/Views/home.twig b/src/App/Views/home.twig index 7d8430e..0fc426a 100644 --- a/src/App/Views/home.twig +++ b/src/App/Views/home.twig @@ -74,7 +74,7 @@
{% endfor %} {% else %} -

Aucune équipe créé !

+

Aucune équipe créée !

{% endif %}

Mes strategies

@@ -90,7 +90,7 @@
{% endfor %} {% else %} -

Aucune tactique créé !

+

Aucune tactique créée !

{% endif %} diff --git a/src/App/Views/insert_team.html.twig b/src/App/Views/insert_team.html.twig index 65cd096..0c10114 100644 --- a/src/App/Views/insert_team.html.twig +++ b/src/App/Views/insert_team.html.twig @@ -54,7 +54,6 @@ background-color: #0056b3; } - @@ -68,7 +67,7 @@ - +
diff --git a/src/App/Views/list_team_by_name.html.twig b/src/App/Views/list_team_by_name.html.twig index eca5e19..092a149 100644 --- a/src/App/Views/list_team_by_name.html.twig +++ b/src/App/Views/list_team_by_name.html.twig @@ -7,6 +7,9 @@ body { font-family: Arial, sans-serif; background-color: #f1f1f1; + display: flex; + flex-direction: column; + align-items: center; } .container { @@ -56,7 +59,9 @@ - +
+

IQBall

+

Chercher une équipe

diff --git a/src/Core/Data/Account.php b/src/Core/Data/Account.php index 48b3e69..01f5406 100755 --- a/src/Core/Data/Account.php +++ b/src/Core/Data/Account.php @@ -8,54 +8,34 @@ namespace IQBall\Core\Data; * to share to other users, or non-needed public information */ class Account { - /** - * @var string $email account's mail address - */ - private string $email; - /** * @var string string token */ private string $token; /** - * @var string the account's username + * @var User contains all the account's "public" information */ - private string $name; - - /** - * @var int - */ - private int $id; - + private User $user; /** - * @param string $email - * @param string $name * @param string $token - * @param int $id + * @param User $user */ - public function __construct(string $email, string $name, string $token, int $id) { - $this->email = $email; - $this->name = $name; + public function __construct(string $token, User $user) { $this->token = $token; - $this->id = $id; - } - - public function getId(): int { - return $this->id; - } - - public function getEmail(): string { - return $this->email; + $this->user = $user; } public function getToken(): string { return $this->token; } - public function getName(): string { - return $this->name; + /** + * @return User + */ + public function getUser(): User { + return $this->user; } } diff --git a/src/Core/Data/Color.php b/src/Core/Data/Color.php deleted file mode 100755 index e0cd27c..0000000 --- a/src/Core/Data/Color.php +++ /dev/null @@ -1,44 +0,0 @@ -hex = $value; - } - - /** - * @return string - */ - public function getValue(): string { - return $this->hex; - } - - public static function from(string $value): Color { - $color = self::tryFrom($value); - if ($color == null) { - var_dump($value); - throw new InvalidArgumentException("The string is not an hexadecimal code"); - } - return $color; - } - - public static function tryFrom(string $value): ?Color { - if (!preg_match('/#(?:[0-9a-fA-F]{6})/', $value)) { - return null; - } - return new Color($value); - } -} diff --git a/src/Core/Data/Member.php b/src/Core/Data/Member.php index d68140c..30e4202 100755 --- a/src/Core/Data/Member.php +++ b/src/Core/Data/Member.php @@ -5,11 +5,8 @@ namespace IQBall\Core\Data; /** * information about a team member */ -class Member { - /** - * @var int The member's user account - */ - private int $userId; +class Member implements \JsonSerializable { + private User $user; /** * @var int The member's team id @@ -17,32 +14,25 @@ class Member { private int $teamId; /** - * @var MemberRole the member's role + * @var string the member's role */ - private MemberRole $role; + private string $role; /** - * @param int $userId - * @param MemberRole $role + * @param User $user + * @param int $teamId + * @param string $role */ - public function __construct(int $userId, int $teamId, MemberRole $role) { - $this->userId = $userId; + public function __construct(User $user, int $teamId, string $role) { + $this->user = $user; $this->teamId = $teamId; $this->role = $role; } - - /** - * @return int - */ - public function getUserId(): int { - return $this->userId; - } - /** - * @return MemberRole + * @return string */ - public function getRole(): MemberRole { + public function getRole(): string { return $this->role; } @@ -52,4 +42,16 @@ class Member { public function getTeamId(): int { return $this->teamId; } + + /** + * @return User + */ + public function getUser(): User { + return $this->user; + } + + + public function jsonSerialize() { + return get_object_vars($this); + } } diff --git a/src/Core/Data/MemberRole.php b/src/Core/Data/MemberRole.php deleted file mode 100755 index 9606c0b..0000000 --- a/src/Core/Data/MemberRole.php +++ /dev/null @@ -1,68 +0,0 @@ -isValid($val)) { - throw new InvalidArgumentException("Valeur du rôle invalide"); - } - $this->value = $val; - } - - public static function player(): MemberRole { - return new MemberRole(MemberRole::ROLE_PLAYER); - } - - public static function coach(): MemberRole { - return new MemberRole(MemberRole::ROLE_COACH); - } - - public function name(): string { - switch ($this->value) { - case self::ROLE_COACH: - return "COACH"; - case self::ROLE_PLAYER: - return "PLAYER"; - } - die("unreachable"); - } - - public static function fromName(string $name): ?MemberRole { - switch ($name) { - case "COACH": - return MemberRole::coach(); - case "PLAYER": - return MemberRole::player(); - default: - return null; - } - } - - private function isValid(int $val): bool { - return ($val <= self::MAX and $val >= self::MIN); - } - - public function isPlayer(): bool { - return ($this->value == self::ROLE_PLAYER); - } - - public function isCoach(): bool { - return ($this->value == self::ROLE_COACH); - } - -} diff --git a/src/Core/Data/Team.php b/src/Core/Data/Team.php index b8e7834..7adeb49 100755 --- a/src/Core/Data/Team.php +++ b/src/Core/Data/Team.php @@ -2,7 +2,7 @@ namespace IQBall\Core\Data; -class Team { +class Team implements \JsonSerializable { private TeamInfo $info; /** @@ -29,4 +29,10 @@ class Team { public function listMembers(): array { return $this->members; } + + public function jsonSerialize() { + return get_object_vars($this); + } + + } diff --git a/src/Core/Data/TeamInfo.php b/src/Core/Data/TeamInfo.php index 7affcea..0f741fe 100644 --- a/src/Core/Data/TeamInfo.php +++ b/src/Core/Data/TeamInfo.php @@ -2,21 +2,21 @@ namespace IQBall\Core\Data; -class TeamInfo { +class TeamInfo implements \JsonSerializable { private int $id; private string $name; private string $picture; - private Color $mainColor; - private Color $secondColor; + private string $mainColor; + private string $secondColor; /** * @param int $id * @param string $name * @param string $picture - * @param Color $mainColor - * @param Color $secondColor + * @param string $mainColor + * @param string $secondColor */ - public function __construct(int $id, string $name, string $picture, Color $mainColor, Color $secondColor) { + public function __construct(int $id, string $name, string $picture, string $mainColor, string $secondColor) { $this->id = $id; $this->name = $name; $this->picture = $picture; @@ -37,13 +37,17 @@ class TeamInfo { return $this->picture; } - public function getMainColor(): Color { + public function getMainColor(): string { return $this->mainColor; } - public function getSecondColor(): Color { + public function getSecondColor(): string { return $this->secondColor; } + public function jsonSerialize() { + return get_object_vars($this); + } + } diff --git a/src/Core/Data/User.php b/src/Core/Data/User.php new file mode 100644 index 0000000..71e0dd1 --- /dev/null +++ b/src/Core/Data/User.php @@ -0,0 +1,72 @@ +email = $email; + $this->name = $name; + $this->id = $id; + $this->profilePicture = $profilePicture; + } + + /** + * @return string + */ + public function getEmail(): string { + return $this->email; + } + + /** + * @return string + */ + public function getName(): string { + return $this->name; + } + + /** + * @return int + */ + public function getId(): int { + return $this->id; + } + + /** + * @return string + */ + public function getProfilePicture(): string { + return $this->profilePicture; + } + + public function jsonSerialize() { + return get_object_vars($this); + } +} diff --git a/src/Core/Gateway/AccountGateway.php b/src/Core/Gateway/AccountGateway.php index 7740b57..a9c3e18 100644 --- a/src/Core/Gateway/AccountGateway.php +++ b/src/Core/Gateway/AccountGateway.php @@ -4,6 +4,7 @@ namespace IQBall\Core\Gateway; use IQBall\Core\Connection; use IQBall\Core\Data\Account; +use IQBall\Core\Data\User; use PDO; class AccountGateway { @@ -16,13 +17,13 @@ class AccountGateway { $this->con = $con; } - - public function insertAccount(string $name, string $email, string $token, string $hash): int { - $this->con->exec("INSERT INTO Account(username, hash, email, token) VALUES (:username,:hash,:email,:token)", [ + public function insertAccount(string $name, string $email, string $token, string $hash, string $profilePicture): int { + $this->con->exec("INSERT INTO Account(username, hash, email, token,profilePicture) VALUES (:username,:hash,:email,:token,:profilePic)", [ ':username' => [$name, PDO::PARAM_STR], ':hash' => [$hash, PDO::PARAM_STR], ':email' => [$email, PDO::PARAM_STR], ':token' => [$token, PDO::PARAM_STR], + ':profilePic' => [$profilePicture, PDO::PARAM_STR], ]); return intval($this->con->lastInsertId()); } @@ -65,7 +66,7 @@ class AccountGateway { return null; } - return new Account($email, $acc["username"], $acc["token"], $acc["id"]); + return new Account($acc["token"], new User($email, $acc["username"], $acc["id"], $acc["profilePicture"])); } /** @@ -78,7 +79,7 @@ class AccountGateway { return null; } - return new Account($acc["email"], $acc["username"], $acc["token"], $acc["id"]); + return new Account($acc["token"], new User($acc["email"], $acc["username"], $acc["id"], $acc["profilePicture"])); } diff --git a/src/Core/Gateway/MemberGateway.php b/src/Core/Gateway/MemberGateway.php index 999bf10..a5116e8 100644 --- a/src/Core/Gateway/MemberGateway.php +++ b/src/Core/Gateway/MemberGateway.php @@ -4,7 +4,7 @@ namespace IQBall\Core\Gateway; use IQBall\Core\Connection; use IQBall\Core\Data\Member; -use IQBall\Core\Data\MemberRole; +use IQBall\Core\Data\User; use PDO; class MemberGateway { @@ -41,13 +41,12 @@ class MemberGateway { */ public function getMembersOfTeam(int $teamId): array { $rows = $this->con->fetch( - "SELECT a.id,m.role,a.email,a.username FROM Account a,Team t,Member m WHERE t.id = :id AND m.id_team = t.id AND m.id_user = a.id", + "SELECT a.id,a.email,a.username,a.profilePicture,m.role FROM Account a,team t,Member m WHERE t.id = :id AND m.id_team = t.id AND m.id_user = a.id", [ ":id" => [$teamId, PDO::PARAM_INT], ] ); - - return array_map(fn($row) => new Member($row['id_user'], $row['id_team'], MemberRole::fromName($row['role'])), $rows); + return array_map(fn($row) => new Member(new User($row['email'], $row['username'], $row['id'], $row['profilePicture']), $teamId, $row['role']), $rows); } /** @@ -66,4 +65,36 @@ class MemberGateway { ); } + /** + * @param string $email + * @param int $idTeam + * @return bool + */ + public function isCoach(string $email, int $idTeam): bool { + $result = $this->con->fetch( + "SELECT role FROM Member WHERE id_team=:team AND id_user = (SELECT id FROM Account WHERE email=:email)", + [ + "team" => [$idTeam, PDO::PARAM_INT], + "email" => [$email, PDO::PARAM_STR], + ] + )[0]['role']; + + return $result == 'COACH'; + } + + /** + * @param int $idTeam + * @param int $idCurrentUser + * @return bool + */ + public function isMemberOfTeam(int $idTeam, int $idCurrentUser): bool { + $result = $this->con->fetch( + "SELECT id_user FROM Member WHERE id_team = :team AND id_user = :user", + [ + "team" => [$idTeam, PDO::PARAM_INT], + "user" => [$idCurrentUser, PDO::PARAM_INT], + ] + ); + return !empty($result); + } } diff --git a/src/Core/Gateway/TeamGateway.php b/src/Core/Gateway/TeamGateway.php index bc1da94..fe0b2bb 100644 --- a/src/Core/Gateway/TeamGateway.php +++ b/src/Core/Gateway/TeamGateway.php @@ -3,7 +3,6 @@ namespace IQBall\Core\Gateway; use IQBall\Core\Connection; -use IQBall\Core\Data\Color; use IQBall\Core\Data\TeamInfo; use PDO; @@ -23,7 +22,7 @@ class TeamGateway { */ public function insert(string $name, string $picture, string $mainColor, string $secondColor): int { $this->con->exec( - "INSERT INTO Team(name, picture, main_color, second_color) VALUES (:team_name , :picture, :main_color, :second_color)", + "INSERT INTO team(name, picture, main_color, second_color) VALUES (:team_name , :picture, :main_color, :second_color)", [ ":team_name" => [$name, PDO::PARAM_STR], ":picture" => [$picture, PDO::PARAM_STR], @@ -34,38 +33,37 @@ class TeamGateway { return intval($this->con->lastInsertId()); } - /** * @param string $name + * @param int $id * @return TeamInfo[] */ - public function listByName(string $name): array { + public function listByName(string $name, int $id): array { $result = $this->con->fetch( - "SELECT * FROM Team WHERE name LIKE '%' || :name || '%'", + "SELECT t.* FROM team t, Member m WHERE t.name LIKE '%' || :name || '%' AND t.id=m.id_team AND m.id_user=:id", [ ":name" => [$name, PDO::PARAM_STR], + "id" => [$id, PDO::PARAM_INT], ] ); - - return array_map(fn($row) => new TeamInfo($row['id'], $row['name'], $row['picture'], Color::from($row['main_color']), Color::from($row['second_color'])), $result); + return array_map(fn($row) => new TeamInfo($row['id'], $row['name'], $row['picture'], $row['main_color'], $row['second_color']), $result); } /** * @param int $id - * @return TeamInfo + * @return TeamInfo|null */ public function getTeamById(int $id): ?TeamInfo { $row = $this->con->fetch( - "SELECT * FROM Team WHERE id = :id", + "SELECT * FROM team WHERE id = :id", [ - ":id" => [$id, PDO::PARAM_INT], - ] + ":id" => [$id, PDO::PARAM_INT], + ] )[0] ?? null; if ($row == null) { return null; } - - return new TeamInfo($row['id'], $row['name'], $row['picture'], Color::from($row['main_color']), Color::from($row['second_color'])); + return new TeamInfo($row['id'], $row['name'], $row['picture'], $row['main_color'], $row['second_color']); } /** @@ -74,16 +72,60 @@ class TeamGateway { */ public function getTeamIdByName(string $name): ?int { return $this->con->fetch( - "SELECT id FROM Team WHERE name = :name", + "SELECT id FROM team WHERE name = :name", [ - ":name" => [$name, PDO::PARAM_INT], - ] + ":name" => [$name, PDO::PARAM_INT], + ] )[0]['id'] ?? null; } + /** + * @param int $idTeam + */ + public function deleteTeam(int $idTeam): void { + $this->con->exec( + "DELETE FROM Member WHERE id_team=:team", + [ + "team" => [$idTeam, PDO::PARAM_INT], + ] + ); + $this->con->exec( + "DELETE FROM TEAM WHERE id=:team", + [ + "team" => [$idTeam, PDO::PARAM_INT], + ] + ); + } + + /** + * @param int $idTeam + * @param string $newName + * @param string $newPicture + * @param string $newMainColor + * @param string $newSecondColor + * @return void + */ + public function editTeam(int $idTeam, string $newName, string $newPicture, string $newMainColor, string $newSecondColor) + { + $this->con->exec( + "UPDATE team + SET name = :newName, + picture = :newPicture, + main_color = :newMainColor, + second_color = :newSecondColor + WHERE id = :team", + [ + "team" => [$idTeam, PDO::PARAM_INT], + "newName" => [$newName, PDO::PARAM_STR], + "newPicture" => [$newPicture, PDO::PARAM_STR], + "newMainColor" => [$newMainColor, PDO::PARAM_STR], + "newSecondColor" => [$newSecondColor, PDO::PARAM_STR], + ] + ); + + } /** * Get all the user's teams - * * @param integer $user * @return array> */ diff --git a/src/Core/Model/AuthModel.php b/src/Core/Model/AuthModel.php index 929eb99..bc29248 100644 --- a/src/Core/Model/AuthModel.php +++ b/src/Core/Model/AuthModel.php @@ -2,13 +2,16 @@ namespace IQBall\Core\Model; +use Exception; use IQBall\Core\Data\Account; +use IQBall\Core\Data\User; use IQBall\Core\Gateway\AccountGateway; use IQBall\Core\Validation\FieldValidationFail; use IQBall\Core\Validation\ValidationFail; class AuthModel { private AccountGateway $gateway; + private const DEFAULT_PROFILE_PICTURE = "https://cdn.pixabay.com/photo/2015/10/05/22/37/blank-profile-picture-973460_960_720.png"; /** * @param AccountGateway $gateway @@ -17,7 +20,6 @@ class AuthModel { $this->gateway = $gateway; } - /** * @param string $username * @param string $password @@ -25,6 +27,7 @@ class AuthModel { * @param string $email * @param ValidationFail[] $failures * @return Account|null the registered account or null if failures occurred + * @throws Exception */ public function register(string $username, string $password, string $confirmPassword, string $email, array &$failures): ?Account { @@ -43,13 +46,14 @@ class AuthModel { $hash = password_hash($password, PASSWORD_DEFAULT); $token = $this->generateToken(); - $accountId = $this->gateway->insertAccount($username, $email, $token, $hash); - return new Account($email, $username, $token, $accountId); + $accountId = $this->gateway->insertAccount($username, $email, $token, $hash, self::DEFAULT_PROFILE_PICTURE); + return new Account($token, new User($email, $username, $accountId, self::DEFAULT_PROFILE_PICTURE)); } /** * Generate a random base 64 string * @return string + * @throws Exception */ private function generateToken(): string { return base64_encode(random_bytes(64)); @@ -70,5 +74,4 @@ class AuthModel { return $this->gateway->getAccountFromMail($email); } - } diff --git a/src/Core/Model/TeamModel.php b/src/Core/Model/TeamModel.php index a5ba84b..d6c97ce 100644 --- a/src/Core/Model/TeamModel.php +++ b/src/Core/Model/TeamModel.php @@ -2,7 +2,6 @@ namespace IQBall\Core\Model; -use IQBall\Core\Data\Color; use IQBall\Core\Data\Team; use IQBall\Core\Data\TeamInfo; use IQBall\Core\Gateway\AccountGateway; @@ -26,6 +25,7 @@ class TeamModel { } /** + * Create a team * @param string $name * @param string $picture * @param string $mainColor @@ -37,48 +37,100 @@ class TeamModel { } /** - * adds a member to a team + * add a member to a team * @param string $mail * @param int $teamId * @param string $role - * @return void + * @return int */ - public function addMember(string $mail, int $teamId, string $role): void { - $userId = $this->users->getAccountFromMail($mail)->getId(); - $this->members->insert($teamId, $userId, $role); + public function addMember(string $mail, int $teamId, string $role): int { + $user = $this->users->getAccountFromMail($mail); + if($user == null) { + return -1; + } + if(!$this->members->isMemberOfTeam($teamId, $user->getUser()->getId())) { + $this->members->insert($teamId, $user->getUser()->getId(), $role); + return 1; + } + return -2; } /** * @param string $name + * @param int $id * @return TeamInfo[] */ - public function listByName(string $name): array { - return $this->teams->listByName($name); + public function listByName(string $name, int $id): array { + return $this->teams->listByName($name, $id); } /** - * @param int $id - * @return Team + * @param int $idTeam + * @param int $idCurrentUser + * @return Team|null */ - public function getTeam(int $id): Team { - $teamInfo = $this->teams->getTeamById($id); - $members = $this->members->getMembersOfTeam($id); + public function getTeam(int $idTeam, int $idCurrentUser): ?Team { + if(!$this->members->isMemberOfTeam($idTeam, $idCurrentUser)) { + return null; + } + $teamInfo = $this->teams->getTeamById($idTeam); + $members = $this->members->getMembersOfTeam($idTeam); return new Team($teamInfo, $members); } - /** * delete a member from given team identifier - * @param string $mail + * @param int $idMember * @param int $teamId * @return int */ - public function deleteMember(string $mail, int $teamId): int { - $userId = $this->users->getAccountFromMail($mail)->getId(); - $this->members->remove($teamId, $userId); + public function deleteMember(int $idMember, int $teamId): int { + $this->members->remove($teamId, $idMember); + if(empty($this->members->getMembersOfTeam($teamId))) { + $this->teams->deleteTeam($teamId); + return -1; + } return $teamId; } + /** + * Delete a team + * @param string $email + * @param int $idTeam + * @return int + */ + public function deleteTeam(string $email, int $idTeam): int { + if($this->members->isCoach($email, $idTeam)) { + $this->teams->deleteTeam($idTeam); + return 0; + } + return -1; + } + + /** + * Verify if the account associated to an email is in a specific team indicated with its id + * @param int $idTeam + * @param string $email + * @return bool + */ + public function isCoach(int $idTeam, string $email): bool { + return $this->members->isCoach($email, $idTeam); + } + + /** + * Edit a team with its id, and replace the current attributes with the new ones + * @param int $idTeam + * @param string $newName + * @param string $newPicture + * @param string $newMainColor + * @param string $newSecondColor + * @return void + */ + public function editTeam(int $idTeam, string $newName, string $newPicture, string $newMainColor, string $newSecondColor) + { + $this->teams->editTeam($idTeam, $newName, $newPicture, $newMainColor, $newSecondColor); + } + /** * Get all user's teams * @@ -88,5 +140,4 @@ class TeamModel { public function getAll(int $user): array { return $this->teams->getAll($user); } - }