Team's part #84

Merged
maxime.batista merged 28 commits from team/completing into master 1 year ago

@ -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 && (

@ -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

@ -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 {

@ -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
}

@ -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
}

@ -0,0 +1,6 @@
export interface User {
id: number
name: string
email: string
profilePicture: string
}

@ -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 {

@ -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

@ -1,5 +1,5 @@
import { Player } from "./Player"
import { CourtObject } from "./CourtObjects"
import { CourtObject } from "./Ball"
import { Action } from "./Action"
export interface Tactic {

@ -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;
}

@ -1,4 +0,0 @@
export enum Team {
Allies = "allies",
Opponents = "opponents",
}

@ -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<RackedCourtObject[]>(
@ -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 <BallPiece />
}
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) =>

@ -162,7 +162,7 @@ function TableData({ allTactics }: { allTactics: Tactic[] }) {
function BodyPersonalSpace({ allTactics }: { allTactics: Tactic[] }) {
let data
if (allTactics.length == 0) {
data = <p>Aucune tactique créé !</p>
data = <p>Aucune tactique créée !</p>
} else {
data = <TableData allTactics={allTactics} />
}

@ -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 (
<div id="main-div">
<header>
<h1>
<a href={BASE + "/"}>IQBall</a>
</h1>
</header>
<TeamDisplay team={team.info} />
{isCoach && <CoachOptions id={team.info.id} />}
<MembersDisplay
members={team.members}
isCoach={isCoach}
idTeam={team.info.id}
currentUserId={currentUserId}
/>
</div>
)
}
function TeamDisplay({ team }: { team: TeamInfo }) {
return (
<div id="team-info">
<div id="first-part">
<h1 id="team-name">{team.name}</h1>
<img id="logo" src={team.picture} alt="Logo d'équipe" />
</div>
<div id="colors">
<div id="colorsTitle">
<p>Couleur principale</p>
<p>Couleur secondaire</p>
</div>
<div id="actual-colors">
<ColorDisplay color={team.mainColor} />
<ColorDisplay color={team.secondColor} />
</div>
</div>
</div>
)
}
function ColorDisplay({ color }: { color: string }) {
return <div className="square" style={{ backgroundColor: color }} />
}
function CoachOptions({ id }: { id: number }) {
return (
<div>
<button
id="delete"
onClick={() =>
confirm("Êtes-vous sûr de supprimer cette équipe?")
? (window.location.href = `${BASE}/team/${id}/delete`)
: {}
}>
Supprimer
</button>
<button
id="edit"
onClick={() =>
(window.location.href = `${BASE}/team/${id}/edit`)
}>
Modifier
</button>
</div>
)
}
function MembersDisplay({
members,
isCoach,
idTeam,
currentUserId,
}: {
members: Member[]
isCoach: boolean
idTeam: number
currentUserId: number
}) {
const listMember = members.map((member) => (
<MemberDisplay
member={member}
isCoach={isCoach}
idTeam={idTeam}
currentUserId={currentUserId}
/>
))
return (
<div id="members">
<div id="head-members">
<h2>Membres :</h2>
{isCoach && (
<button
id="add-member"
onClick={() =>
(window.location.href = `${BASE}/team/${idTeam}/addMember`)
}>
+
</button>
)}
</div>
{listMember}
</div>
)
}
function MemberDisplay({
member,
isCoach,
idTeam,
currentUserId,
}: {
member: Member
isCoach: boolean
idTeam: number
currentUserId: number
}) {
return (
<div className="member">
<img
id="profile-picture"
src={member.user.profilePicture}
alt="Photo de profile"
/>
<p>{member.user.name}</p>
<p>{member.role}</p>
<p>{member.user.email}</p>
{isCoach && currentUserId !== member.user.id && (
<button
id="delete"
onClick={() =>
confirm(
"Êtes-vous sûr de retirer ce membre de l'équipe?",
)
? (window.location.href = `${BASE}/team/${idTeam}/remove/${member.user.id}`)
: {}
}>
Retirer
</button>
)}
{isCoach && currentUserId == member.user.id && (
<button
id="delete"
onClick={() =>
confirm("Êtes-vous sûr de quitter cette équipe?")
? (window.location.href = `${BASE}/team/${idTeam}/remove/${member.user.id}`)
: {}
}>
Quitter
</button>
)}
</div>
)
}

@ -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"

@ -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);

@ -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;
}

@ -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

@ -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.

@ -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);

@ -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<string, mixed> $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<string, mixed> $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<string,mixed> $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);
}
}

@ -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
*/

@ -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);

@ -67,28 +67,43 @@
display: flex;
justify-content: space-between;
}
.failed{
color: red;
}
</style>
</head>
<body>
<header>
<h1><a href="{{ path('/') }}">IQBall</a></h1>
</header>
<div class="container">
<h2>Ajouter un membre à votre équipe</h2>
<form action="{{ path('/team/members/add') }}" method="POST">
<form action="{{ path("/team/#{idTeam}/addMember") }}" method="POST">
<div class="form-group">
<label for="team">Team où ajouter le membre :</label>
<input type="text" id="team" name="team" required>
<label for="mail">Email du membre :</label>
<input type="text" id="mail" name="mail" required>
<label for="email">Email du membre :</label>
{% if badEmail %}
<p class="failed">Email invalide</p>
{% endif %}
{%if notFound %}
<p class="failed">Cette personne n'a pas été trouvé</p>
{% endif %}
{% if alreadyExisting %}
<p class="failed">Cette personne est déjà dans l'équipe</p>
{% endif %}
<input type="text" id="email" name="email" required>
<fieldset class="role">
<legend>Rôle du membre dans l'équipe :</legend>
<div class="radio">
<label for="P">Joueur</label>
<input type="radio" id="P" name="role" value="P" checked />
<input type="radio" id="P" name="role" value="PLAYER" checked />
</div>
<div class="radio">
<label for="C">Coach</label>
<input type="radio" id="C" name="role" value="C" />
<input type="radio" id="C" name="role" value="COACH" />
</div>
</fieldset>

@ -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;
}
</style>
</head>
<body>
@ -61,13 +65,18 @@
</header>
<section class="container">
<div class="team container">
{% if notDeleted %}
<popup>
<p>Cette équipe ne peut être supprimée.</p>
</popup>
{% endif %}
{% if team is defined %}
<div class="team">
<div>
<h1>{{ team.getInfo().getName() }}</h1>
<img src="{{ team.getInfo().getPicture() }}" alt="Logo d'équipe" class="logo">
</div>
<div>
<div id="colors">
<div class="color"><p>Couleur principale : </p>
<div class="square" id="main_color"></div>
</div>
@ -75,17 +84,26 @@
<div class="square" id="second_color"></div>
</div>
</div>
{% if isCoach %}
<button id="delete" onclick="confirm('Êtes-vous sûr de supprimer cette équipe?') ? window.location.href = '{{ path("/team/#{team.getInfo().getId()}/delete") }}' : {}">Supprimer</button>
<button></button>
{% endif %}
{% for m in team.listMembers() %}
<p> {{ m.getUserId() }} </p>
{% if m.getRole().isCoach() %}
<div class="player">
<p> {{ m.getUserId() }} </p>
{% if m.getRole().isCoach() %}
<p> : Coach</p>
{% else %}
{% else %}
<p> : Joueur</p>
{% endif %}
{% endif %}
</div>
{% endfor %}
</div>
{% else %}
<div>
<h3>Cette équipe ne peut être affichée</h3>
</div>
{% endif %}
</section>
</body>
</html>

@ -3,9 +3,37 @@
<head>
<meta charset="UTF-8">
<title>Twig view</title>
<style>
body {
display: flex;
flex-direction: column;
background-color: #f1f1f1;
align-items: center;
}
section{
flex-direction: row;
justify-content: space-around;
background-color: white;
width: 60%;
}
.team {
border-radius: 10px;
border-color: darkgrey;
}
.logo_team {
width: 15%;
aspect-ratio: 3/2;
object-fit: contain;
}
</style>
</head>
<body>
<header>
<h1><a href="{{ path('/') }}">IQBall</a></h1>
</header>
<section>
{% if teams is empty %}
<p>Aucune équipe n'a été trouvée</p>
<div class="container">
@ -22,12 +50,12 @@
</div>
{% else %}
{% for t in teams %}
<div class="team" onclick="window.location.href = '{{ path("/team/#{t.id}") }}'">
<p>Nom de l'équipe : {{ t.name }}</p>
<img src="{{ t.picture }}" alt="logo de l'équipe">
<div class="team" onclick="window.location.href = '{{ path("/team/#{t.getId()}") }}'">
<p>Nom de l'équipe : {{ t.getName() }}</p>
<img src="{{ t.getPicture() }}" alt="logo de l'équipe" class="logo_team">
</div>
{% endfor %}
{% endif %}
</section>
</body>
</html>

@ -0,0 +1,81 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Insertion view</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f1f1f1;
}
.container {
max-width: 400px;
margin: 5px auto;
padding: 20px;
background-color: #fff;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
h2 {
text-align: center;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
}
{% for item in bad_fields %}
#{{ item }}{
border-color: red;
}{% endfor %} input[type="text"], input[type="password"] {
width: 100%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
}
input[type="submit"] {
background-color: #007bff;
color: #fff;
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
}
input[type="submit"]:hover {
background-color: #0056b3;
}
</style>
</head>
<body>
<div class="container">
<h2>Modifier votre équipe</h2>
<form action="{{ path('/team/' ~ team.getInfo().getId() ~ '/edit') }}" method="post">
<div class="form-group">
<label for="name">Nom de l'équipe :</label>
<input type="text" id="name" name="name" value="{{ team.getInfo().getName() }}" required>
<label for="picture">Logo:</label>
<input type="text" id="picture" name="picture" value="{{ team.getInfo().getPicture() }}" required>
<label for="main_color">Couleur principale</label>
<input type="color" value="{{ team.getInfo().getMainColor() }}" id="main_color" name="main_color" required>
<label for="second_color">Couleur secondaire</label>
<input type="color" id="second_color" name="second_color" value="{{ team.getInfo().getSecondColor() }}" required>
</div>
<div class="form-group">
<input type="submit" value="Confirmer">
</div>
</form>
</div>
</body>
</html>

@ -74,7 +74,7 @@
</div>
{% endfor %}
{% else %}
<p>Aucune équipe créé !</p>
<p>Aucune équipe créée !</p>
{% endif %}
<h2> Mes strategies </h2>
@ -90,7 +90,7 @@
</div>
{% endfor %}
{% else %}
<p> Aucune tactique créé !</p>
<p> Aucune tactique créée !</p>
{% endif %}
</body>

@ -54,7 +54,6 @@
background-color: #0056b3;
}
</style>
</head>
<body>
@ -68,7 +67,7 @@
<label for="picture">Logo:</label>
<input type="text" id="picture" name="picture" required>
<label for="main_color">Couleur principale</label>
<input type="color" id="main_color" name="main_color" required>
<input type="color" value="#ffffff" id="main_color" name="main_color" required>
<label for="second_color">Couleur secondaire</label>
<input type="color" id="second_color" name="second_color" required>
</div>

@ -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 @@
</style>
</head>
<body>
<header>
<h1><a href="{{ path('/') }}">IQBall</a></h1>
</header>
<div class="container">
<h2>Chercher une équipe</h2>
<form action="{{ path('/team/search') }}" method="post">

@ -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;
}
}

@ -1,44 +0,0 @@
<?php
namespace IQBall\Core\Data;
use InvalidArgumentException;
class Color {
/**
* @var string that represents an hexadecimal color code
*/
private string $hex;
/**
* @param string $value 6 bytes unsigned int that represents an RGB color
* @throws InvalidArgumentException if the value is negative or greater than 0xFFFFFF
*/
private function __construct(string $value) {
$this->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);
}
}

@ -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);
}
}

@ -1,68 +0,0 @@
<?php
namespace IQBall\Core\Data;
use InvalidArgumentException;
/**
* Enumeration class workaround
* As there is no enumerations in php 7.4, this class
* encapsulates an integer value and use it as a variant discriminant
*/
final class MemberRole {
private const ROLE_PLAYER = 0;
private const ROLE_COACH = 1;
private const MIN = self::ROLE_PLAYER;
private const MAX = self::ROLE_COACH;
private int $value;
private function __construct(int $val) {
if (!$this->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);
}
}

@ -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);
}
}

@ -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);
}
}

@ -0,0 +1,72 @@
<?php
namespace IQBall\Core\Data;
use _PHPStan_4c4f22f13\Nette\Utils\Json;
class User implements \JsonSerializable {
/**
* @var string $email user's mail address
*/
private string $email;
/**
* @var string the user's username
*/
private string $name;
/**
* @var int the user's id
*/
private int $id;
/**
* @var string user's profile picture
*/
private string $profilePicture;
/**
* @param string $email
* @param string $name
* @param int $id
* @param string $profilePicture
*/
public function __construct(string $email, string $name, int $id, string $profilePicture) {
$this->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);
}
}

@ -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"]));
}

@ -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);
}
}

@ -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<array<string, mixed>>
*/

@ -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);
}
}

@ -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);
}
}

Loading…
Cancel
Save