<?php
namespace Products\NotificationsBundle\Form\Forms\Messages;
use App\Form\Type\ReactMediaManagerType;
use App\Form\Type\TextareaCounterType;
use App\Util\Json;
use Cms\CoreBundle\Util\Doctrine\EntityManager;
use Products\NotificationsBundle\Entity\AbstractList;
use Products\NotificationsBundle\Entity\Notifications\Channels\ChannelsInterface;
use Products\NotificationsBundle\Entity\Notifications\Message;
use Products\NotificationsBundle\Form\Type\FancyEntityType;
use Products\NotificationsBundle\Form\Type\ReactVoiceRecorderType;
use Products\NotificationsBundle\Form\Type\RichTextType;
use Products\NotificationsBundle\Form\Type\SourceTextType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
/**
* Class MessageDataForm
* @package Products\NotificationsBundle\Form\Forms\Messages
*/
final class MessageDataForm extends AbstractType
{
// TODO: this needs handled a different way...
private const SMS_LENGTH = 160;
private const URL_LENGTH = 21;
private const SPACING_LENGTH = 1;
private const HEADER_LENGTH = 0;
public const MAX_LENGTH = self::SMS_LENGTH - self::HEADER_LENGTH - self::SPACING_LENGTH - self::URL_LENGTH;
/**
* @var EntityManager
*/
protected EntityManager $em;
/**
* @var AuthorizationCheckerInterface
*/
private AuthorizationCheckerInterface $authorizationChecker;
/**
* @param EntityManager $em
* @param AuthorizationCheckerInterface $authorizationChecker
*/
public function __construct(
EntityManager $em,
AuthorizationCheckerInterface $authorizationChecker
)
{
$this->em = $em;
$this->authorizationChecker = $authorizationChecker;
}
/**
* {@inheritDoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options): void
{
/** @var Message $message */
$message = $builder->getData();
$descriptionType = RichTextType::class;
if ($message->isHtml()) {
$descriptionType = SourceTextType::class;
}
$prefillParams = $options['prefillParams'] ?? [];
$builder
->add('prefillParams', HiddenType::class, [
'data' => Json::encode($prefillParams),
'mapped' => false,
])
->add('lists', FancyEntityType::class, [
'class' => AbstractList::class,
'multiple' => true,
'choices' => array_filter(
$this->em->getRepository(AbstractList::class)->findAll(),
function($list) use ($message) {
return $this->authorizationChecker->isGranted(
sprintf(
'app.notifications.messaging.%s',
$message->isUrgent() ? 'urgent' : 'general',
),
$list,
);
},
),
'choice_label' => 'name',
'label_tooltip' => 'Select the list(s) you would like to send to.',
])
->add('title', TextareaCounterType::class, [
'label_tooltip' => 'Be concise but thorough. The content in this field will be sent as a text message. <a target="_blank" href="https://help.schoolnow.com/kb/send-a-new-broadcast">Learn more.</a>',
'limit' => self::MAX_LENGTH,
'constraints' => [
new NotBlank(),
new Length([
'max' => self::MAX_LENGTH,
]),
],
])
->add('description', $descriptionType, [
'label_tooltip' => 'The content entered here will be the body of the email and also set up as a web page text recipients can click to.',
'required' => true,
'constraints' => [
new NotBlank(
['message' => 'Details field should not be blank.']
),
],
])
->add('forceTranslation', HiddenType::class, [
'mapped' => false,
'required' => false,
'data' => '0',
'attr' => [
'class' => 'force-translation',
],
])
->add('media', ReactMediaManagerType::class, [
'label_tooltip' => 'Select a message header image. <a target="_blank" href="https://help.schoolnow.com/kb/what-are-the-ideal-image-header-sizes-to-use-for-outgoing-messages">View recommended sizes</a>',
'limit' => 1,
])
->addEventListener(FormEvents::PRE_SUBMIT, [$this, 'onPreSubmit'])
->addEventListener(FormEvents::POST_SUBMIT, [$this, 'onPostSubmit']);
if ($message->isUrgent()) {
$builder
->add('script', TextareaType::class, [
'attr' => ['rows' => 4],
'label_tooltip' => 'Voice is only available for urgent messages. Choose how you will create your voice message.',
'required' => false,
'constraints' => [
new NotBlank(
['message' => 'Please add a voice transcription for both voice message generation and translation services to be performed.']
),
],
])
->add('recording', ReactVoiceRecorderType::class, [
'required' => true,
'label_tooltip' => false,
'label' => false,
'constraints' => [
new NotBlank(
['message' => 'Recording is missing. Click "Convert text to speech" or add a recording.']
),
],
]);
$builder->addEventSubscriber(new ScriptChangeDetectionListener());
}
}
/**
* @param FormEvent $event
* @return void
*/
public function onPreSubmit(FormEvent $event): void
{
/** @var Message $message */
$message = $event->getData();
if ( ! $message) {
return;
}
if ((isset($message['html']) && (bool)$message['html'] === false) || empty($message['description_html'])) {
return;
}
$message['description'] = $message['description_html'];
$event->setData($message);
}
/**
* @param FormEvent $event
* @return void
*/
public function onPostSubmit(FormEvent $event): void
{
$message = $event->getData();
if ( ! $message instanceof Message) {
return;
}
if ($message->getMedia()->count() > 0) {
return;
}
$message->removeChannel(ChannelsInterface::CHANNELS__INSTAGRAM);
$event->setData($message);
}
public function configureOptions(OptionsResolver $resolver): void
{
parent::configureOptions($resolver);
$resolver->setDefaults([
'prefillParams' => null,
]);
}
}