<?php
namespace AppBundle\Controller;
use AppBundle\Entity\Instance;
use AppBundle\Entity\InstanceUser;
use AppBundle\Entity\Language;
use AppBundle\Entity\Notification;
use AppBundle\Entity\User;
use AppBundle\Form\InstanceForm;
use AppBundle\Form\InstanceType;
use AppBundle\Form\JiraAccountsType;
use AppBundle\Form\PermissionsOfUsersInInstanceType;
use AppBundle\Form\RegexpAndMentorInstanceType;
use AppBundle\Form\RegistrationType;
use AppBundle\Form\UserType;
use AppBundle\Service\ExerciseManager;
use AppBundle\Service\GitHubManager;
use AppBundle\Service\Instance as InstanceService;
use AppBundle\Service\Participation;
use AppBundle\Service\UserManager;
use AppBundle\Service\UserService;
use CodersLab\Lms\Modules\IdentityAccess\Application\ICourseInstanceContext;
use CodersLab\Lms\Modules\Materials\Domain\ICourses;
use CodersLab\Lms\SharedKernel\Application\Command\SendWelcomeEmail;
use CodersLab\Lms\SharedKernel\Application\Mailer\IRegistrationMailer;
use CodersLab\Lms\SharedKernel\Application\SecurityContext\PasswordEncoder;
use CodersLab\Lms\SharedKernel\Domain\Bus\CommandBus;
use CodersLab\Lms\SharedKernel\Domain\Bus\EventRecorder;
use CodersLab\Lms\SharedKernel\Domain\Courses\MemberType;
use CodersLab\Lms\SharedKernel\PublishedLanguage\Events\CourseInstanceAdded;
use CodersLab\Lms\SharedKernel\PublishedLanguage\Events\CourseInstanceMemberAdded;
use CodersLab\Lms\SharedKernel\PublishedLanguage\Events\SignatureUpdated;
use CodersLab\Lms\SharedKernel\PublishedLanguage\Events\UserGithubUpdated;
use Doctrine\ORM\EntityManagerInterface;
use Exception;
use GuzzleHttp\Exception\ClientException;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Form;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
final class AdminController extends AbstractController
{
public function __construct(
private EventRecorder $eventRecorder,
private CommandBus $commandBus,
private Participation $participationService,
private InstanceService $instanceService,
private UserService $userService
) {
}
/**
* @Route("/admin/user", name="users")
*/
public function usersAction(Request $request): Response
{
if ($this->isGranted("ROLE_CS") || $this->isGranted("ROLE_ADMIN")) {
$users = $this->userService->getAllUsers(
$request,
$request->query->getInt('page', 1),
10
);
} elseif ($this->isGranted("ROLE_MENTOR")) {
$instances = [];
foreach ($this->getUser()->getParticipations() as $participation) {
if ($participation instanceof InstanceUser) {
$instances[] = $participation->getInstance();
}
}
return $this->render('mentor/users.html.twig', ['instances' => $instances]);
} else {
throw new AccessDeniedException();
}
return $this->render('admin/users.html.twig', ['users' => $users]);
}
/**
* @Route("/admin/user/{id}", name="user")
* @param User $user
* @param Request $request
*
* @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function userAction(
User $user,
Request $request,
EntityManagerInterface $em,
PasswordEncoder $passwordEncoder
): Response {
$oldGhAccount = $user->getGithub();
$form = $this->createForm(UserType::class, $user, ['role' => $this->getUser()->getRoles()]);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
if ($user->getPlainPassword()) {
$password = $passwordEncoder->encodeUserPassword((string)$user->getId(), $user->getPlainPassword());
$user->setPassword($password);
}
if ($user->getGithub() !== $oldGhAccount) {
$this->eventRecorder->record(new UserGithubUpdated(
$user->getId(),
$user->getGithub(),
$oldGhAccount,
));
}
$em->persist($user);
$em->flush();
return $this->redirect($request->getUri());
}
$userCourseAudit = $em->getRepository(InstanceUser::class)->findBy(['user' => $user]);
return $this->render('admin/user.html.twig', [
'user' => $user,
'userCourseAudit' => $userCourseAudit,
'form' => $form->createView(),
]);
}
/**
* @Route("/admin/user_create", name="user_create")
*/
public function userCreateAction(
Request $request,
EntityManagerInterface $em,
PasswordEncoder $passwordEncoder
): Response {
$user = new User();
$form = $this->createForm(UserType::class, $user, ['role' => $this->getUser()->getRoles()]);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
if ($user->getPlainPassword()) {
$password = $passwordEncoder->encodePassword($user->getPlainPassword());
$user->setPassword($password);
} else {
$user->setPassword(UserManager::generatePassword());
$user->setConfirmationToken(UserManager::generatePassword());
}
$em->persist($user);
$em->flush();
$this->commandBus->dispatch(new SendWelcomeEmail((string)$user->getId()));
return $this->redirect($this->generateUrl('user', ['id' => $user->getId()]));
}
$userCourseAudit = $em->getRepository('AppBundle:InstanceUser')->findBy(['user' => $user]);
return $this->render('admin/user.html.twig', [
'user' => $user,
'userCourseAudit' => $userCourseAudit,
'form' => $form->createView(),
]);
}
/**
* @Route("/admin/report", name="admin_report")
*/
public function reportAction(): Response
{
return $this->render('admin/report.html.twig');
}
/**
* @Route("/admin/instance", name="instances")
*/
public function instancesAction(Request $request): Response
{
$this->denyAccessUnlessGranted(User::ROLE_MENTOR);
if ($this->isGranted(User::ROLE_CS)) {
$instances = $this->instanceService->getAllInstances(
$request,
$request->query->getInt('page', 1),
15,
[
'signature' => 'ASC',
'mode' => 'ASC',
'number' => 'ASC',
]
);
} else {
$instances = $this->instanceService->getAllInstancesByParticipation(
$request,
$this->getUser(),
$request->query->getInt('page', 1),
15,
[
'signature' => 'ASC',
'mode' => 'ASC',
'number' => 'ASC',
],
);
}
return $this->render('admin/instances.html.twig', [
'instances' => $instances,
]);
}
/**
* @Route("/admin/instance_create", name="instance_create")
*/
public function instanceCreateAction(Request $request, EntityManagerInterface $em)
{
$this->denyAccessUnlessGranted("ROLE_CS");
$instance = new Instance();
$form = $this->createForm(InstanceType::class, $instance, [
'participation_service' => $this->participationService,
'instance_service' => $this->instanceService,
'user' => $this->getUser(),
]);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em->beginTransaction();
$course = $instance->getCourse();
$instance->setVersion($course->getDetails()->getVersion()->value());
$instance->setLanguage($em->getRepository(Language::class)->find(
$course->getDetails()->getTechnologyId()->toString()
));
$em->persist($instance);
$em->flush();
/** @var ExerciseManager $exerciseManager */
$exerciseManager = $this->get('app.exercise.manager');
$exercises = $exerciseManager->createExercisesForInstance($instance);
foreach ($exercises as $exercise) {
$em->persist($exercise);
}
/** @var GitHubManager $gitHub */
$gitHub = $this->get('app.github.students');
$ghTeam = $gitHub->createNewTeam($instance->getSignature()) ?: $gitHub->findTeamByName($instance->getSignature());
if (!isset($ghTeam)) {
throw new \Exception("Cannot create or find GitHub team with name: " . $instance->getSignature());
}
$instance->setGhTeamId($ghTeam->id);
foreach ($instance->getParticipants() as $participant) {
if ($participant instanceof InstanceUser) {
if ((in_array($participant->getRole(), [InstanceUser::ROLE_MENTOR , InstanceUser::ROLE_USER])) &&
!empty($participant->getUser()->getGithub())) {
try {
$gitHub->addUserToTeam(
$instance->getGhTeamId(),
$participant->getUser()->getGithubId(),
$participant->getRole() === InstanceUser::ROLE_MENTOR
);
} catch (ClientException $e) {
throw new Exception('Cannot add user to github team.');
}
}
$this->eventRecorder->record(new CourseInstanceMemberAdded(
$instance->getId(),
$instance->getCourse()->id(),
$participant->getUser()->getId(),
new MemberType($participant->getRole())
));
$em->persist($participant);
}
}
$em->flush();
$em->commit();
$this->eventRecorder->record(new CourseInstanceAdded((string)$instance->getId()));
return $this->redirectToRoute('instances');
}
return $this->render('admin/instance_create.html.twig', [
'form' => ($form instanceof Form) ? $form->createView() : null,
]);
}
/**
* @Route("/admin/instance/{id}", name="instance")
* @param Instance $instance
* @param Request $request
*
* @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
* @throws \JiraRestApi\JiraException
* @throws \JsonMapper_Exception
*/
public function instanceAction(
Instance $instance,
Request $request,
ICourses $courses,
EventRecorder $eventRecorder,
IRegistrationMailer $registrationMailer,
ICourseInstanceContext $instanceContext
) {
if (!$this->getUser()->hasRole(User::ROLE_CS) &&
!$this->getUser()->hasRole(User::ROLE_SUPER_ADMIN)) {
if (!$instanceContext->isMentorOnCourseByEmail($this->getUser()->getUsername(), (string)$instance->getId())) {
throw new AccessDeniedException("You are not assigned to this course");
}
}
$instanceForm = $this->createForm(InstanceForm::class, $instance, [
'user' => $this->getUser(),
'id' => $instance->getCourse()->id()->toString(),
]);
$instanceForm->handleRequest($request);
$em = $this->getDoctrine()->getManager();
if ($instanceForm->isSubmitted() && $instanceForm->isValid()) {
$course = $courses->getById($instance->getCourse()->id());
$instance->setVersion($course->getDetails()->getVersion()->value());
$instance->setLanguage($em->getRepository(Language::class)->find(
$course->getDetails()->getTechnologyId()->toString()
));
$em->persist($instance);
$em->flush();
$eventRecorder->record(...$instance->getEventRecorder()->pullEvents());
if ($instance->isSignatureUpdated() === true) {
$this->eventRecorder->record(new SignatureUpdated((string)$instance->getId(), $instance->getSignature()));
}
return $this->redirectToRoute('instance', ['id' => $instance->getId()]);
}
$user = (new User())
->setPassword($this->get('app.user.manager')::generatePassword())
->setConfirmationToken($this->get('app.user.manager')::generatePassword());
$form = $this->createForm(RegexpAndMentorInstanceType::class, $instance, [
'participation_service' => $this->participationService,
'instance_service' => $this->instanceService,
]);
$form->setParent(null);
$registerForm = $this->createForm(RegistrationType::class, $user);
$participantsPermissionForm = $this->createForm(PermissionsOfUsersInInstanceType::class, $instance);
$participantsPermissionForm->handleRequest($request);
$registerForm->handleRequest($request);
if ($registerForm->isSubmitted() && $registerForm->isValid()) {
foreach ($instance->getParticipants(InstanceUser::ROLE_MENTOR) as $participant) {
$notification = (new Notification())
->setReceiver($participant->getUser())
->setCreator($user)
->setType('reg')
->setInstance($instance);
$em->persist($notification);
}
$em->persist($instance);
$em->persist($user);
$em->flush();
$eventRecorder->record(...$instance->getEventRecorder()->pullEvents());
$registrationMailer->send($user);
return $this->redirectToRoute('instance', ['id' => $instance->getId()]);
}
// THIS FORM IS HANDLED IT OTHER ROUTE
if ($participantsPermissionForm->isSubmitted() && $participantsPermissionForm->isValid()) {
foreach ($instance->getParticipants() as $participant) {
$em->persist($participant);
}
$em->flush();
$eventRecorder->record(...$instance->getEventRecorder()->pullEvents());
return $this->redirect($request->getUri());
}
$jiraAccountsTesterForm = $this->createForm(JiraAccountsType::class);
$jiraAccountsTesterForm->handleRequest($request);
$jiraAccountsCreationResult = [];
if ($jiraAccountsTesterForm->isSubmitted() && $jiraAccountsTesterForm->isValid()) {
$jiraAccountsCreationResult = $this->instanceService->createJiraAccounts($instance);
}
$inactiveParticipants = $this->participationService->getInactiveParticipants($instance);
$permisssionFormView = (isset($participantsPermissionForm) &&
$participantsPermissionForm instanceof Form
) ? $participantsPermissionForm->createView() : null;
return $this->render('admin/instance.html.twig', [
'instance' => $instance,
'form' => ($form instanceof Form) ? $form->createView() : null,
'register_form' => (isset($registerForm) && $registerForm instanceof Form) ? $registerForm->createView() : null,
'inactive_participants' => $inactiveParticipants,
'modules' => $em->getRepository('AppBundle:Module')->findBy([
'language' => $instance->getLanguage(),
'country' => $instance->getMode()->getCountry(),
]),
'participants_permission_form' => $permisssionFormView,
'instance_form' => $instanceForm->createView(),
'jira_accounts_tester' => $instance->getLanguage()->isJiraAccountCreation(),
'jira_accounts_tester_form' => $jiraAccountsTesterForm->createView(),
'jira_accounts_creation_result' => $jiraAccountsCreationResult,
]);
}
}