| Current Path : /var/www/v3.cesa.co.za/src/ContentBundle/Services/ |
| 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;
}
}