Commit c9967637 authored by martin's avatar martin
Browse files

fix password forgot

parent ce1faab0
<!DOCTYPE html>
<html lang="fr">
<body>
<h2 style="text-align: center;">Conditions générales d'utilisation de VIKAZIMUT</h2>
<p>Les présentes conditions générales d’utilisation ont pour objet de définir les modalités et conditions dans lesquelles
VIKAZIMUT met à disposition son application mobile au bénéfice de l’utilisateur.
En utilisant l’application mobile de VIKAZIMUT, l’utilisateur accepte sans réserve l’ensemble des présentes dispositions.</p>
<h2>Article 1&nbsp;: Description de l’application</h2>
<p>L’application mobile a pour objectif de faciliter la pratique de la course d’orientation.
Elle remplace la carte papier, la boussole et le poinçon de
validation des points de contrôle.</p>
<p>Un parcours d'orientation consiste en une suite de points de contrôle matérialisés sur le terrain par une balise type fédération internationale de
course d’orientation (IOF) contenant en plus un QR Code et tag NFC. L’utilisateur utilise l’application pour se repérer à partir de la carte et valider son passage aux points de contrôle avec le lecteur QR Code ou le lecteur NFC.</p>
<p>L’application affiche en fin du parcours des statistiques sur la réalisation du parcours : le temps total, le temps intermédiaire entre chaque balise
et le tracé du parcours réalisé sur la carte.</p>
<p>L’application se présente sous deux modes : un mode course où l’orienteur n’est pas aidé pour sa localisation et un mode promenade où l’orienteur est
positionné sur la carte en temps réel.</p>
<h2>Article 2&nbsp;: Protection des données</h2>
<p>Conformément à la loi Informatique et Libertés n° 78-17 du 6 janvier 1978, aucune information personnelle autre que la date et la localisation ne
seront utilisées par cette application.</p>
<p>En application de la loi Informatique et Libertés n° 78-17 du 6 janvier 1978 modifiée, l’utilisateur dispose d’un droit d’accès, de modification, de
rectification, d’opposition et de suppression sur les données le concernant collectées sur cette application. Ces droits sont strictement personnels et ne peuvent être exercés que par l’utilisateur pour les données le concernant, ou
concernant un autre utilisateur dont il est le représentant légal.</p>
<h2> Article 3&nbsp;: Responsabilité</h2>
<p>En utilisant l’application VIKAZIMUT, l’utilisateur déclare dégager de toute responsabilité l’application VIKAZIMUT en cas d’incident de toute nature
ayant lieu sur les parcours présents et à venir, qu’ils soient chronométrés ou non, proposés par l’application, et ce, à l’égard de lui-même ou d’un tiers.</p>
<h2> Article 4&nbsp;: Respect des normes existantes</h2>
<p>L’utilisateur s’engage à respecter le code de la route et toutes les autres réglementations en vigueur sur l’ensemble des parcours. </p>
<h2> Article 5&nbsp;: Non contre-indication médicale</h2>
<p>L’utilisateur déclare ne présenter aucune contre-indication médicale à la pratique de la course d’orientation.</p>
<h2> Article 6&nbsp;: Disposition spécifique aux mineurs</h2>
<p>L’utilisateur mineur (de moins de 18 ans révolus) certifie avoir l’autorisation de son responsable légal pour utiliser la présente application ainsi
qu’être accompagné d’une personne majeure lors de l’utilisation.</p>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<body>
<h2 style="text-align: center;">General Conditions of Use of VIKAZIMUT</h2>
<p>These terms and conditions are intended to define the terms and conditions under which VIKAZIMUT makes available its mobile application for the benefit of the user. By
using the mobile application VIKAZIMUT, the user accepts without limitation all these terms of use.</p>
<h2>Article 1 : Description of the application</h2>
<p>The mobile application aims to facilitate the practice of orienteering. It replaces the map, the compass and the control card.</p>
<p>An orienteering course is composed of a start point, a series of control points, and a finish point. Controls are marked with a white and orange flag that contains an NFC and QR Code. The orienteer
uses the application to find their way using the map and uses the QR Code or NFC readers to validate the control points.</p>
<p>The application displays at the end of the course some statistics about the completion of the course: the total time, the split time and the trace of the course on the map.</p>
<p>The application comes in two modes: a race mode where the orienteer is not helped for his position and a walk mode where the orienteer is positioned on the map in real time.</p>
<h2>Article 2 : Data protection</h2>
<p>In accordance with the french law “Informatique et Libertés” 78-17 of January 6, 1978, no personal information other than the date and the location will be used by this application.</p>
<p>In application of the french law “Informatique et Libertés” 78-17 of January 6th, 1978 modified, the user has a right of access, modification, rectification, opposition and suppression on the data
concerning him collected on this application. These rights are strictly personal and can only be exercised by the user for data concerning him, or concerning another user of which he is the legal representative.</p>
<h2>Article 3 : Liability</h2>
<p>By using the application VIKAZIMUT, the user declares to release of any responsibility the application VIKAZIMUT in the event of any incident taking place on the present and future courses, whether
timed or not, proposed by the application, and this, with respect to himself or a third party.</p>
<h2>Article 4 : Respect for existing standards</h2>
<p>The user agrees to respect the rules of the road and all other regulations in effect along the courses.</p>
<h2>Article 5 : Medical contraindications</h2>
<p>The user declares that he has no medical contraindications to the practice of orienteering.</p>
<h2>Article 6 : Specific provision for minors</h2>
<p>The minor users (under 18 years old) certify having the authorization from their parents or legal guardians to use the application as well as to be accompanied by a major person during the course.</p>
</body>
</html>
\ No newline at end of file
......@@ -45,7 +45,7 @@ class AdminController extends AbstractController
UserPasswordEncoderInterface $encoder, \Swift_Mailer $mailer){
$planner = new User();
$form = $this->createForm(PlannerType::class, $planner);
$form = $this->createForm(PlannerType::class, $planner, ['password' => false]);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()){
......
......@@ -13,6 +13,8 @@ use Symfony\Component\HttpFoundation\JsonResponse;
use App\Entity\Course;
use App\Modele\CreateTrack;
use App\Entity\Track;
use App\Modele\CreateMissingCheckpoint;
use App\Entity\MissingCheckpoint;
class DataController extends AbstractController
{
......@@ -132,4 +134,20 @@ class DataController extends AbstractController
$this->getDoctrine()->getRepository(Course::class))
]);
}
/**
* @Route("/data/missing-control-point")
*/
public function post_missing_controlpoint()
{
$request = Request::createFromGlobals();
$creator = new CreateMissingCheckpoint($request->getContent());
return new JsonResponse([
"reponse" =>
$creator->addCheckpoint($this->getDoctrine()->getManager(),
$this->getDoctrine()->getRepository(Course::class))
]);
}
}
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use App\Form\PlannerType;
use App\Entity\User;
use App\Modele\CreateUser;
class ProfileController extends AbstractController
{
private $security;
public function __construct(Security $security)
{
$this->security = $security;
}
/**
* @Route("/profile", name="profile")
*/
public function index()
{
$user = $this->security->getUser();
return $this->render('profile/index.html.twig', [
'user' => $user,
]);
}
/**
* @Route("/profile/modify", name="modify_info")
*/
public function modify(Request $request, TranslatorInterface $translator,
UserPasswordEncoderInterface $encoder){
$planner = $this->security->getUser();
$form = $this->createForm(PlannerType::class, $planner, ['password' => true]);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()){
$user = $form->getData();
$creator = new CreateUser($user, $translator);
$valid = $creator->addUser($encoder, $this->getDoctrine()->getManager());
if($valid === true){
return $this->redirectToRoute('profile');
}else{
return $this->render('profile/modify.html.twig', [
'form' => $form->createView(),
'error' => $valid,
]);
}
}
return $this->render('profile/modify.html.twig', [
'form' => $form->createView(),
]);
}
}
......@@ -6,6 +6,15 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use App\Form\PlannerType;
use App\Entity\User;
use App\Modele\CreateUser;
class SecurityController extends AbstractController
{
......@@ -33,4 +42,38 @@ class SecurityController extends AbstractController
{
throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
}
/**
* @Route("/password-forgot", name="password_forgot")
*/
public function password_forgot(Request $request, TranslatorInterface $translator,
UserPasswordEncoderInterface $encoder, \Swift_Mailer $mailer){
$form = $this->createFormBuilder()
->add('username', TextType::class, ['label' => 'your.username'])
->add('save', SubmitType::class, ['label' => 'submit'])
->getForm();
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()){
$username = $form->getData()["username"];
$user = $this->getDoctrine()->getRepository(User::class)->findOneBy(['username' => $username]);
if($user){
$creator = new CreateUser($user, $translator);
$creator->forgotPassword($encoder, $this->getDoctrine()->getManager());
$creator->sendEmail($mailer, false);
return $this->redirectToRoute('all_planners');
}else{
return $this->render('security/password_forgot.html.twig', [
'form' => $form->createView(),
'error' => $valid,
]);
}
}
return $this->render('security/password_forgot.html.twig', [
'form' => $form->createView(),
]);
}
}
......@@ -61,9 +61,15 @@ class Course
*/
private $orienteer;
/**
* @ORM\OneToMany(targetEntity="App\Entity\MissingCheckpoint", mappedBy="course", orphanRemoval=true)
*/
private $missingCheckpoints;
public function __construct()
{
$this->orienteer = new ArrayCollection();
$this->missingCheckpoints = new ArrayCollection();
}
public function isValid(): bool
......@@ -192,4 +198,35 @@ class Course
return $this;
}
/**
* @return Collection|MissingCheckpoint[]
*/
public function getMissingCheckpoints(): Collection
{
return $this->missingCheckpoints;
}
public function addMissingCheckpoint(MissingCheckpoint $missingCheckpoint): self
{
if (!$this->missingCheckpoints->contains($missingCheckpoint)) {
$this->missingCheckpoints[] = $missingCheckpoint;
$missingCheckpoint->setCourse($this);
}
return $this;
}
public function removeMissingCheckpoint(MissingCheckpoint $missingCheckpoint): self
{
if ($this->missingCheckpoints->contains($missingCheckpoint)) {
$this->missingCheckpoints->removeElement($missingCheckpoint);
// set the owning side to null (unless already changed)
if ($missingCheckpoint->getCourse() === $this) {
$missingCheckpoint->setCourse(null);
}
}
return $this;
}
}
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass="App\Repository\MissingCheckpointRepository")
*/
class MissingCheckpoint
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="integer")
*/
private $controlPointId;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Course", inversedBy="missingCheckpoints")
* @ORM\JoinColumn(nullable=false)
*/
private $course;
public function getId(): ?int
{
return $this->id;
}
public function getControlPointId(): ?int
{
return $this->controlPointId;
}
public function setControlPointId(int $controlPointId): self
{
$this->controlPointId = $controlPointId;
return $this;
}
public function getCourse(): ?Course
{
return $this->course;
}
public function setCourse(?Course $course): self
{
$this->course = $course;
return $this;
}
}
......@@ -6,6 +6,7 @@ use App\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
......@@ -13,8 +14,12 @@ class PlannerType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('username', TextType::class, ['label' => "name"]);
if($options["password"])
$builder->add('password', PasswordType::class, ['label' => 'new.password']);
$builder
->add('username', TextType::class, ['label' => "name"])
->add('email', TextType::class, ['label' => 'email'])
->add('phone', TextType::class, ['label' => 'phone', 'required' => false])
->add('submit', SubmitType::class)
......@@ -25,6 +30,7 @@ class PlannerType extends AbstractType
{
$resolver->setDefaults([
'data_class' => User::class,
'password' => bool::class,
]);
}
}
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20200515143120 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
$this->addSql('CREATE TABLE missing_checkpoint (id INT AUTO_INCREMENT NOT NULL, course_id INT NOT NULL, control_point_id INT NOT NULL, INDEX IDX_D3F3931591CC992 (course_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('ALTER TABLE missing_checkpoint ADD CONSTRAINT FK_D3F3931591CC992 FOREIGN KEY (course_id) REFERENCES course (id)');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
$this->addSql('DROP TABLE missing_checkpoint');
}
}
<?php
namespace App\Modele;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\DBAL\DBALException;
use Doctrine\ORM\ORMException;
use PDOException;
use App\Entity\MissingCheckpoint;
class CreateMissingCheckpoint{
private $checkpoint;
private $data;
public function __construct(string $data){
$this->checkpoint = new MissingCheckpoint();
$this->data = \json_decode($data);
}
public function addCheckpoint(EntityManagerInterface $em, $repositery){
$this->checkpoint->setControlPointId($this->data->controlPointId);
if(filter_var($this->data->courseId, FILTER_VALIDATE_INT)){
$course = $repositery->find(intval($this->data->courseId));
if($course == null){
return "wrong id";
}
}else{
return "wrong id";
}
try{
$em->persist($this->track);
$em->flush();
}catch (DBALException $e){
if(strpos($e->getMessage(), "Integrity constraint violation")){
return "Data does not fit expected value";
}else{
return $e->getMessage();
}
}catch (PDOException $e){
return $e->getMessage();
}catch (ORMException $e){
return $e->getMessage();
}
return true;
}
}
\ No newline at end of file
......@@ -58,10 +58,25 @@ class CreateUser{
}
}
public function sendEmail(\Swift_Mailer $mailer){
$body = "Bonjour,\nVotre inscription au site vikazimut en tant que traceur a étais accepté.\n".
"Votre nom d'utilisateur: ".$this->user->getUsername()."\n".
"Votre mot de passe provisoire: ".$this->plainPassword."\n".
public function forgotPassword(UserPasswordEncoderInterface $encoder, EntityManagerInterface $em){
$this->plainPassword = self::generatePassword();
$encoded = $encoder->encodePassword($this->user, $this->plainPassword);
$this->user->setPassword($encoded);
$em->persist($this->user);
$em->flush();
}
public function sendEmail(\Swift_Mailer $mailer, $new){
if($new){
$body = "Bonjour,\nVotre inscription au site vikazimut en tant que traceur a étais accepté.\n".
"Votre nom d'utilisateur: ".$this->user->getUsername()."\n";
}else{
$body = "Bonjour ".$this->user->getUsername()."\n".
"Votre mot de passe à étais réinitialiser\n";
}
$body .= "Votre mot de passe provisoire: ".$this->plainPassword."\n".
"Merci de ne pas répondre à ce mail";
$message = (new \Swift_Message("Vikazimut"))
->setFrom("noreply@vikazimut.vikazim.fr")
......
<?php
namespace App\Repository;
use App\Entity\MissingCheckpoint;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method MissingCheckpoint|null find($id, $lockMode = null, $lockVersion = null)
* @method MissingCheckpoint|null findOneBy(array $criteria, array $orderBy = null)
* @method MissingCheckpoint[] findAll()
* @method MissingCheckpoint[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class MissingCheckpointRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, MissingCheckpoint::class);
}
// /**
// * @return MissingCheckpoint[] Returns an array of MissingCheckpoint objects
// */
/*
public function findByExampleField($value)
{
return $this->createQueryBuilder('m')
->andWhere('m.exampleField = :val')
->setParameter('val', $value)
->orderBy('m.id', 'ASC')
->setMaxResults(10)
->getQuery()
->getResult()
;
}
*/
/*
public function findOneBySomeField($value): ?MissingCheckpoint
{
return $this->createQueryBuilder('m')
->andWhere('m.exampleField = :val')
->setParameter('val', $value)
->getQuery()
->getOneOrNullResult()
;
}
*/
}
{% extends 'base.html.twig' %}
{% block title %}Hello AdminController!{% endblock %}
{% block body %}
<style>
.example-wrapper { margin: 1em auto; max-width: 800px; width: 95%; font: 18px/1.5 sans-serif; }
.example-wrapper code { background: #F5F5F5; padding: 2px 6px; }
</style>
<div class="example-wrapper">
<h1>Hello {{ controller_name }}! ✅</h1>
This friendly message is coming from:
<ul>
<li>Your controller at <code><a href="{{ '/home/martin/site-vikazimute/src/Controller/AdminController.php'|file_link(0) }}">src/Controller/AdminController.php</a></code></li>
<li>Your template at <code><a href="{{ '/home/martin/site-vikazimute/templates/admin/index.html.twig'|file_link(0) }}">templates/admin/index.html.twig</a></code></li>
</ul>
</div>
{% endblock %}
......@@ -40,6 +40,11 @@
<li class="nav-item">
<a class="nav-link active" href="{{ path('your_courses') }}">{% trans %}your.courses{% endtrans %}</a>
</li>
{% if is_granted('ROLE_ADMIN') %}
<li class="nav-item">
<a class="nav-link active" href="{{ path('all_planners') }}">{% trans %}the.planners{% endtrans %}</a>
</li>
{%endif %}
{% endif %}
<li class="nav-item active">
<a class="nav-link" href="{{ path('home') }}#team ">{% trans %}us{% endtrans %}</a>
......@@ -49,6 +54,7 @@
</li>
</ul>
{% if app.user %}
<a class="btn btn-outline-light mr-2" href="{{ path('app_login') }}">{% trans %}your.info{% endtrans %}</a>
<a class="btn btn-light mr-2" href="{{ path('app_logout') }}">{% trans %}log.out{% endtrans %}</a>
{% else %}
<a class="btn btn-outline-light mr-2" href="{{ path('app_login') }}">{% trans %}log.in{% endtrans %}</a>
......
......@@ -7,29 +7,85 @@
#banner {
background-position: center center;
background-size: cover;
font-size: 20px;
height: 28em;
text-align: center;
position: relative;
}