Your IP : 216.73.217.79


Current Path : /var/www/v3.cesa.co.za/src/ContentBundle/Services/
Upload File :
Current File : /var/www/v3.cesa.co.za/src/ContentBundle/Services/FormManager.php

<?php

namespace App\ContentBundle\Services;

use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Monolog\Logger;
use App\ContentBundle\Entity\Form;
use App\ContentBundle\Form\Type\FormCompletedType;

/**
 * Forms manager
 *
 * @author Otto Saayman <otto@igotafrica.com>
 * @package ContentBundle
 * @subpackage Forms
 * @version 0.0.1
 */
final class FormManager
{
    private function formatExternalFormStartDate(?\DateTimeInterface $startDate): string
    {
        if (!($startDate instanceof \DateTimeInterface)) {
            return '';
        }

        return $startDate->format('Y-m-d');
    }

    private function buildExternalFormTitleWithStartDate(Form $form): string
    {
        $title = trim((string) $form->getTitle());
        $startDateLabel = $this->formatExternalFormStartDate($form->getStartDate());

        if ($startDateLabel === '') {
            return $title;
        }

        return $title . ' (' . $startDateLabel . ')';
    }

    private function applyAssignmentCopyDateWindow(Form $form): void
    {
        // Assignment feedback copies must never be treated as survey forms.
        $form->setSurvey(false);

        $now = new \DateTime();
        $form->setStartDate($now);

        $endDate = (clone $now);
        $endDate->modify('+2 months');
        $form->setEndDate($endDate);
    }

    private function inferTargetYearForAssignment(Form $formTemplate): int
    {
        // Assignment copies must align to the active/current cycle year,
        // not the template's original creation/start year.
        return intval((new \DateTime())->format('Y'));
    }

    private function inferYearForForm(Form $form): ?int
    {
        $startDate = $form->getStartDate();
        if ($startDate instanceof \DateTimeInterface) {
            return intval($startDate->format('Y'));
        }

        $createdAt = $form->getCreatedAt();
        if ($createdAt instanceof \DateTimeInterface) {
            return intval($createdAt->format('Y'));
        }

        return null;
    }

    private function ensureYearInFormTitle(string $title, int $year): string
    {
        $title = trim($title);
        if ($title === '') {
            return (string) $year;
        }

        // If the title already has a year token, force it to the target year.
        if (preg_match('/\b(19|20)\d{2}\b/', $title) === 1) {
            return preg_replace('/\b(19|20)\d{2}\b/', (string) $year, $title, 1);
        }

        return $title . ' - ' . $year;
    }

    /**
     * Service Container
     * @var object
     */
    private $container = null;

    /**
     * Entity manager
     * @var object
     */
    private $em;

    /**
     * Security Context
     * @var object
     */
    private $securityContext = null;

    /**
     * Form Factory
     * @var object
     */
    protected $formFactory;

    /**
     * Class construct
     *
     * @param  ContainerInterface $container
     * @param  Logger             $logger
     * @return void
     */
    public function __construct(
        ContainerInterface $container,
        TokenStorageInterface $securityContext,
        FormFactoryInterface $formFactory
    ) {
        $this->setContainer($container);
        $this->setSecurityContext($securityContext);
        $this->setFormFactory($formFactory);
        $this->setEm($container->get('doctrine')->getManager('default'));

        return;
    }

    /**
     * Get Container
     *
     * @return ContainerInterface $container
     */
    public function getContainer()
    {
        return $this->container;
    }

    /**
     * Set Container
     *
     * @param  ContainerInterface $container
     * @return void
     */
    public function setContainer($container)
    {
        $this->container = $container;
    }

    /**
     * Get Entity Manager
     *
     * @return string
     */
    public function getEm()
    {
        return $this->em;
    }

    /**
     * Set Container
     *
     * @param  string $em
     * @return void
     */
    public function setEm($em)
    {
        $this->em = $em;
    }

    public function getSecurityContext()
    {
        return $this->securityContext;
    }

    public function setSecurityContext(TokenStorageInterface $securityContext)
    {
        $this->securityContext = $securityContext;
    }

    /**
     * Get FormFactory
     *
     * @return FormFactoryInterface $formFactory
     */
    public function getFormFactory()
    {
        return $this->formFactory;
    }

    /**
     * Set FormFactory
     *
     * @param  FormFactoryInterface $formFactory
     * @return void
     */
    public function setFormFactory($formFactory)
    {
        $this->formFactory = $formFactory;
    }

    /**
     * Get a form
     *
     * @param string $id
     * @return Form
     */
    public function find($id)
    {
        return $this->getEm()->getRepository(Form::class)->find($id);
    }

    /**
     * Get all forms
     *
     * @param array $params
     * @param array $order
     * @return array
     */
    public function findBy($params, $order)
    {
        return $this->getEm()->getRepository(Form::class)->findBy($params, $order);
    }

    /**
     * Check form object default values
     *
     * @param Form $form
     * @return void
     */
    public function checkDefaults(Form &$form)
    {

        if (is_null($form->getCreatedBy())) {
            $token = $this->getSecurityContext()->getToken();
            if ($token !== null) {
                $user = $token->getUser();
                if ($user instanceof UserInterface) {
                    $form->setCreatedBy($user->getUserIdentifier());
                } elseif (is_string($user)) {
                    $form->setCreatedBy($user);
                }
            }
        }

        if (is_null($form->getStartDate())) {
            $form->setStartDate(new \DateTime());
        }

        if (is_null($form->getMethod())) {
            $form->setMethod('POST');
        }

        if (is_null($form->isIsPublic())) {
            $form->setIsPublic(false);
        }

        if (is_null($form->isIsActive())) {
            $form->setIsActive(true);
        }

        if (is_null($form->isIsDeleted())) {
            $form->setIsDeleted(false);
        }

        if (is_null($form->getMethod())) {
            $form->setMethod('POST');
        }
    }

    /**
     * Save form object
     *
     * @param Form $event
     * @return $form
     */
    public function save(Form &$form)
    {

        $this->checkDefaults($form);

        $this->getEm()->persist($form);
        $this->getEm()->flush();
    }

    /**
     * Copy form object
     *
     * @param Form $event
     * @return Form
     */
    public function copy(Form &$form, $title = null): Form
    {

        $newForm = clone $form;

        if (is_string($title) && strlen(trim($title)) > 0) {
            $newForm->setTitle(trim($title));
        } else {
            $newForm->setTitle('Copy of ' . $form->getTitle());
        }

        // When copying, ensure the copied form is active within the assignment window
        // unless the template already provides both dates.
        $hasStartDate = ($newForm->getStartDate() !== null);
        $hasEndDate = ($newForm->getEndDate() !== null);
        if (!($hasStartDate && $hasEndDate)) {
            $now = new \DateTime();
            $newForm->setStartDate($now);
            $endDate = (clone $now);
            $endDate->modify('+2 months');
            $newForm->setEndDate($endDate);
        }

        $this->getEm()->persist($newForm);
        $this->getEm()->flush();

        foreach ($form->getFormItems() as $formItemForm) {

            $formItemFormNew = clone $formItemForm;

            $formItemNew = clone $formItemForm->getFormItem();

            $this->getContainer()->get('form_item.manager')->save($formItemNew);

            $formItemFormNew->setForm($newForm);
            $formItemFormNew->setFormItem($formItemNew);

            $this->getContainer()->get('form_item_form.manager')->save($formItemFormNew);

            foreach ($formItemForm->getFormItem()->getFormItemList() as $itemList) {
                $itemListNew = clone $itemList;
                $itemListNew->setFormItem($formItemNew);
                $this->getContainer()->get('form_item_list.manager')->save($itemListNew);
            }
        }

        return $newForm;
    }

    /**
     * Copy a template form for an assignment, but do not duplicate the form if one already exists.
     *
     * Note: V3 does not store assignment_id on the Form entity; CESA-main generates a deterministic
     * title per assignment, so this existence check uses the title.
     *
     * @return array{form: Form, already_copied: bool}
     */
    public function copyOrGetForAssignment(Form &$formTemplate, $title = null, ?int $assignmentId = null): array
    {
        $requestedTitle = is_string($title) ? trim($title) : '';

        if ($assignmentId !== null && $assignmentId > 0 && $requestedTitle !== '') {
            $targetYear = $this->inferTargetYearForAssignment($formTemplate);
            $titleWithYear = $this->ensureYearInFormTitle($requestedTitle, $targetYear);

            $existingForms = $this->findBy(
                array('title' => $titleWithYear),
                array('title' => 'ASC')
            );

            if (is_array($existingForms) && count($existingForms) > 0 && is_object($existingForms[0])) {
                return array(
                    'form' => $existingForms[0],
                    'already_copied' => true,
                );
            }

            $legacyExistingForms = $this->findBy(
                array('title' => $requestedTitle),
                array('title' => 'ASC')
            );

            if (is_array($legacyExistingForms) && count($legacyExistingForms) > 0 && is_object($legacyExistingForms[0])) {
                $legacyYear = $this->inferYearForForm($legacyExistingForms[0]);
                if ($legacyYear !== null && $legacyYear >= $targetYear) {
                    return array(
                        'form' => $legacyExistingForms[0],
                        'already_copied' => true,
                    );
                }
            }

            $newForm = $this->copy($formTemplate, $titleWithYear);
            // Safety: enforce canonical title/year on the copied form.
            $newForm->setTitle($titleWithYear);
            $this->applyAssignmentCopyDateWindow($newForm);
            $this->save($newForm);
            return array(
                'form' => $newForm,
                'already_copied' => false,
            );
        }

        $newFormTitle = $requestedTitle;
        if ($requestedTitle !== '') {
            $targetYear = $this->inferTargetYearForAssignment($formTemplate);
            $newFormTitle = $this->ensureYearInFormTitle($requestedTitle, $targetYear);
        }

        $newForm = $this->copy($formTemplate, $newFormTitle);

        return array(
            'form' => $newForm,
            'already_copied' => false,
        );
    }

    /**
     * Save form object
     *
     * @return array
     */
    public function getSiteListQuery()
    {
        return $this->getEm()->getRepository(Form::class)->getSiteListQuery();
    }

    /**
     * Build external forms list sorted by start date descending.
     *
     * @return array<int, array<string, mixed>>
     */
    public function getExternalReportsFormsList(): array
    {
        $forms = $this->getSiteListQuery()->getResult();
        $data = array();

        foreach ($forms as $form) {
            if (!($form instanceof Form)) {
                continue;
            }

            $startDate = $form->getStartDate();
            $data[] = array(
                'id' => $form->getId(),
                'title' => $this->buildExternalFormTitleWithStartDate($form),
                'base_title' => (string) $form->getTitle(),
                'start_date' => ($startDate instanceof \DateTimeInterface) ? $startDate->format('Y-m-d H:i:s') : null,
                'start_date_display' => $this->formatExternalFormStartDate($startDate),
                'start_timestamp' => ($startDate instanceof \DateTimeInterface) ? $startDate->getTimestamp() : 0,
            );
        }

        usort($data, function (array $left, array $right): int {
            $leftTimestamp = isset($left['start_timestamp']) ? intval($left['start_timestamp']) : 0;
            $rightTimestamp = isset($right['start_timestamp']) ? intval($right['start_timestamp']) : 0;

            if ($leftTimestamp === $rightTimestamp) {
                return strcmp((string) ($left['base_title'] ?? ''), (string) ($right['base_title'] ?? ''));
            }

            return ($leftTimestamp > $rightTimestamp) ? -1 : 1;
        });

        foreach ($data as &$entry) {
            unset($entry['start_timestamp']);
        }
        unset($entry);

        return $data;
    }

    /**
     * Save form details from request array
     *
     * @param array $formValues
     * @param Form $form
     * @return string
     */
    public function saveFromRequest($formValues, Form &$form = null)
    {

        $return = '';

        if (!is_object($form)) {
            $form = new Form();
        }

        if (!isset($formValues['form_type'])) {
            $formValues['form_type'] = '0';
        }

        if (!is_object($formValues['form_type'])) {
            $formValues['form_type'] = $this->getContainer()->get('form_type.manager')->find($formValues['form_type']);
        }

        if (is_object($formValues['form_type'])) {
            $form->setFormType($formValues['form_type']);
        } else {
            return $this->getContainer()->get('language_phrases.manager')->translate('Invalid form type');
        }

        if (!isset($formValues['to_name'])) {
            $formValues['to_name'] = $form->getToName();
        }

        if (!isset($formValues['to_e_mail'])) {
            $formValues['to_e_mail'] = $form->getToEMail();
        }

        if (!isset($formValues['title'])) {
            $formValues['title'] = $form->getTitle();
        }

        if (!isset($formValues['description'])) {
            $formValues['description'] = $form->getDescription();
        }

        $form->setToName($formValues['to_name']);
        $form->setToEMail($formValues['to_e_mail']);
        $form->setTitle($formValues['title']);
        $form->setDescription($formValues['description']);

        $this->checkDefaults($form);

        $this->save($form);

        return $return;
    }

    /**
     * Render the forms
     *
     * @param array $forms
     * @return string
     */
    public function renderForms($forms)
    {

        $return = array();

        foreach ($forms as $form) {
            $return[] = $this->renderForm($form);
        }

        return $return;
    }

    /**
     * Render the forms
     *
     * @param array $forms
     * @return string
     */
    public function renderForm($formId)
    {

        $formCompletedManager = $this->getContainer()->get('form_completed.manager');
        $formItemManager = $this->getContainer()->get('form_item.manager');
        $session = $this->getContainer()->get('sessions.manager')->getSession();
        $loggedInUser = $this->getContainer()->get('user_list.manager')->getLoggedInUser();

        if (!is_object($formId)) {
            $form = $this->find($formId);
        } else {
            $form = $formId;
        }

        if (!is_object($form)) {
            return [
                'formBody' => 'Form not found',
                'formTitle' => 'Form not found',
                'id' => '0',
                'formObject' => null,
            ];
        }

        if (is_object($form->getEndDate()) && $form->getEndDate() < new \DateTime()) {
            return [
                'formBody' => 'Form is no longer active',
                'formTitle' => 'Form is no longer active',
                'id' => $form->getId(),
                'formObject' => $form,
            ];
        }

        $formCompleted = $formCompletedManager->getObject($form, $loggedInUser, $session);
        $formItemObjects = $formItemManager->findByForm($form);
        $formItems = array();

        foreach ($formItemObjects as $formItemObject) {

            if ($formItemObject->isIsActive() == false) {
                continue;
            }

            $formItems[] = $formItemObject->getAsArray($form);
        }

        $formCompletedForm = $this->getFormFactory()->create(
            FormCompletedType::class,
            $formCompleted,
            array('form_items' => $formItems)
        );

        $twig = $this->getContainer()->get('service_getter.manager')->getTwig();

        $formCompletedId = $formCompleted->getId();

        if (strlen($formCompletedId) <= 0) {
            $formCompletedId = '0';
        }

        $return = array(
            'formBody' => $twig->render(
                '@ContentBundle/Form/form.html.twig',
                array(
                    'form' => $formCompletedForm->createView(),
                    'formItems' => $formItems,
                    'formObject' => $form,
                    'formCompletedId' => $formCompletedId,
                )
            ),
            'formTitle' => $form->getTitle(),
            'id' => $form->getId(),
            'formObject' => $form,
        );

        return $return;
    }

    /**
     * Link content to a form
     *
     * @param string $formId
     * @param string $contentId
     * @return void
     */
    public function linkContentToForm($formId, $contentId)
    {

        if (strlen($formId) <= 0 || strlen($contentId) <= 0) {
            return;
        }

        $form = $this->find($formId);
        $content = $this->getContainer()->get('content.manager')->find($contentId);

        if (!is_object($content) || !is_object($form)) {
            return;
        }

        $mustAdd = true;
        $linkedForms = $content->getForm();

        foreach ($linkedForms as $linkedForm) {
            if ($form->getId() == $linkedForm->getId()) {
                //                echo 'found it! ';
                //                exit;
                $mustAdd = false;
            }
        }

        if ($mustAdd) {
            //            $content->addForm($form);
            //            $this->getContainer()->get('content.manager')->save($content);

            $form->addContent($content);
            $this->save($form);
            //            echo 'Added form';
            //            exit;
        }

        //        echo 'added?';
        //        exit;
    }

    /**
     * Get form select array.
     *
     * @return array
     */
    public function getSelectArray()
    {
        $return = array();

        $forms = $this->findBy(array(), array('title' => 'ASC'));

        if (!is_countable($forms) || count($forms) <= 0) {
            return $return;
        }

        foreach ($forms as $object) {
            $return[$object->getTitle()] = $object->getId();
        }

        return $return;
    }
}