Platform: Add contact form with categories - refs BT#20920

pull/4989/head
christian 2 years ago
parent be0b36b390
commit 0548a7c39a
  1. 4
      assets/vue/components/layout/TopbarNotLoggedIn.vue
  2. 6
      src/CoreBundle/Controller/Admin/IndexBlocksController.php
  3. 89
      src/CoreBundle/Controller/ContactCategoryController.php
  4. 61
      src/CoreBundle/Controller/ContactController.php
  5. 55
      src/CoreBundle/Entity/ContactCategory.php
  6. 26
      src/CoreBundle/Form/ContactCategoryType.php
  7. 109
      src/CoreBundle/Form/ContactType.php
  8. 56
      src/CoreBundle/Resources/views/Contact/index.html.twig
  9. 7
      src/CoreBundle/Resources/views/ContactCategory/_delete_form.html.twig
  10. 20
      src/CoreBundle/Resources/views/ContactCategory/_form.html.twig
  11. 16
      src/CoreBundle/Resources/views/ContactCategory/edit.html.twig
  12. 41
      src/CoreBundle/Resources/views/ContactCategory/index.html.twig
  13. 13
      src/CoreBundle/Resources/views/ContactCategory/new.html.twig
  14. 28
      src/CoreBundle/Resources/views/ContactCategory/show.html.twig

@ -54,6 +54,10 @@ const menuItems = ref([
label: t("Demo"),
to: { name: "Demo" },
},
{
label: t("Contact"),
url: "/contact",
},
{
key: "language_selector",
label: currentLanguage ? currentLanguage.originalName : "English",

@ -446,6 +446,12 @@ class IndexBlocksController extends BaseController
'label' => $this->translator->trans('External tools'),
];
$items[] = [
'class' => 'item-contact-category-admin',
'url' => $this->generateUrl('chamilo_contact_category_index'),
'label' => $this->translator->trans('Contact categories'),
];
return $items;
}

@ -0,0 +1,89 @@
<?php
declare(strict_types=1);
/* For licensing terms, see /license.txt */
namespace Chamilo\CoreBundle\Controller;
use Chamilo\CoreBundle\Entity\ContactCategory;
use Chamilo\CoreBundle\Form\ContactCategoryType;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
#[Route('/contact/category')]
class ContactCategoryController extends AbstractController
{
#[Route('/', name: 'chamilo_contact_category_index', methods: ['GET'])]
public function index(EntityManagerInterface $entityManager): Response
{
$this->denyAccessUnlessGranted('ROLE_ADMIN');
$contactCategories = $entityManager
->getRepository(ContactCategory::class)
->findAll();
return $this->render('@ChamiloCore/ContactCategory/index.html.twig', [
'contact_categories' => $contactCategories,
]);
}
#[Route('/new', name: 'chamilo_contact_category_new', methods: ['GET', 'POST'])]
public function new(Request $request, EntityManagerInterface $entityManager): Response
{
$contactCategory = new ContactCategory();
$form = $this->createForm(ContactCategoryType::class, $contactCategory);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$entityManager->persist($contactCategory);
$entityManager->flush();
return $this->redirectToRoute('chamilo_contact_category_index', [], Response::HTTP_SEE_OTHER);
}
return $this->renderForm('@ChamiloCore/ContactCategory/new.html.twig', [
'contact_category' => $contactCategory,
'form' => $form,
]);
}
#[Route('/{id}', name: 'chamilo_contact_category_show', methods: ['GET'])]
public function show(ContactCategory $contactCategory): Response
{
return $this->render('@ChamiloCore/ContactCategory/show.html.twig', [
'contact_category' => $contactCategory,
]);
}
#[Route('/{id}/edit', name: 'chamilo_contact_category_edit', methods: ['GET', 'POST'])]
public function edit(Request $request, ContactCategory $contactCategory, EntityManagerInterface $entityManager): Response
{
$form = $this->createForm(ContactCategoryType::class, $contactCategory);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$entityManager->flush();
return $this->redirectToRoute('chamilo_contact_category_index', [], Response::HTTP_SEE_OTHER);
}
return $this->renderForm('@ChamiloCore/ContactCategory/edit.html.twig', [
'contact_category' => $contactCategory,
'form' => $form,
]);
}
#[Route('/{id}', name: 'chamilo_contact_category_delete', methods: ['POST'])]
public function delete(Request $request, ContactCategory $contactCategory, EntityManagerInterface $entityManager): Response
{
if ($this->isCsrfTokenValid('delete'.$contactCategory->getId(), $request->request->get('_token'))) {
$entityManager->remove($contactCategory);
$entityManager->flush();
}
return $this->redirectToRoute('chamilo_contact_category_index', [], Response::HTTP_SEE_OTHER);
}
}

@ -0,0 +1,61 @@
<?php
declare(strict_types=1);
/* For licensing terms, see /license.txt */
namespace Chamilo\CoreBundle\Controller;
use Chamilo\CoreBundle\Form\ContactType;
use Chamilo\CoreBundle\Settings\SettingsManager;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Email;
use Symfony\Component\Routing\Annotation\Route;
class ContactController extends AbstractController
{
/**
* @Route("/contact", name="contact")
*/
public function index(Request $request, MailerInterface $mailer, SettingsManager $settingsManager)
{
$form = $this->createForm(ContactType::class);
$form->handleRequest($request);
// Check if the form is submitted and valid
if ($form->isSubmitted() && $form->isValid()) {
// Get the data from the form
$contactData = $form->getData();
$category = $contactData['category'];
$toEmail = $category->getEmail();
// Create and send the email
$email = (new Email())
->from($contactData['email'])
->to($toEmail)
->subject($contactData['subject'])
->text(
"Sender: {$contactData['email']}\n".
"Message: {$contactData['message']}"
);
// Send the email
$mailer->send($email);
// Add a flash message for user feedback
$this->addFlash('success', 'Your message has been sent successfully!');
// Redirect the user to the 'contact' route
return $this->redirectToRoute('contact');
}
// Render the form view
return $this->render(
'@ChamiloCore/Contact/index.html.twig',
[
'form' => $form->createView(),
]
);
}
}

@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
/* For licensing terms, see /license.txt */
namespace Chamilo\CoreBundle\Entity;
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\Table;
use Doctrine\ORM\Mapping\Id;
use Doctrine\ORM\Mapping\GeneratedValue;
use Doctrine\ORM\Mapping\Column;
#[Entity]
#[Table(name: "contact_form_contact_category")]
class ContactCategory
{
#[Id]
#[GeneratedValue]
#[Column(type: "integer")]
private ?int $id = null;
#[Column(type: "string", length: 255)]
private string $name;
#[Column(type: "string", length: 255)]
private string $email;
public function getId(): ?int
{
return $this->id;
}
public function getName(): string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
public function getEmail(): string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
}

@ -0,0 +1,26 @@
<?php
namespace Chamilo\CoreBundle\Form;
use Chamilo\CoreBundle\Entity\ContactCategory;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ContactCategoryType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('name')
->add('email')
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => ContactCategory::class,
]);
}
}

@ -0,0 +1,109 @@
<?php
declare(strict_types=1);
/* For licensing terms, see /license.txt */
namespace Chamilo\CoreBundle\Form;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\NotBlank;
use Chamilo\CoreBundle\Entity\ContactCategory;
class ContactType extends AbstractType
{
private $translator;
public function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
}
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('category', EntityType::class, [
'class' => ContactCategory::class,
'choice_label' => 'name',
'label' => $this->translator->trans('Category'),
'constraints' => [
new NotBlank(),
],
])
->add('firstName', TextType::class, [
'label' => $this->translator->trans('First name'),
'constraints' => [
new NotBlank(),
],
])
->add('lastName', TextType::class, [
'label' => $this->translator->trans('Last name'),
'constraints' => [
new NotBlank(),
],
])
->add('email', EmailType::class, [
'label' => $this->translator->trans('E-mail'),
'constraints' => [
new NotBlank(),
],
])
->add('subject', TextType::class, [
'label' => $this->translator->trans('Subject'),
'constraints' => [
new NotBlank(),
],
])
->add('message', TextareaType::class, [
'label' => $this->translator->trans('Message'),
'constraints' => [
new NotBlank(),
],
])
->add('termsAccepted', CheckboxType::class, [
'mapped' => false,
'label' => $this->translator->trans('By checking this box, I confirm that I accept the data processing by OFAJ'),
'constraints' => [
new NotBlank(),
],
])
->add('terms', TextareaType::class, [
'mapped' => false,
'label' => '',
'data' => $options['terms_content'],
'attr' => [
'disabled' => true,
'class' => 'text-gray-500',
'style' => 'width: 100%;height: 180px;',
],
])
->add('submit', SubmitType::class, [
'label' => $this->translator->trans('Send'),
'attr' => [
'class' => 'btn btn--primary hover:bg-blue-700 text-white font-bold py-2 px-4 rounded cursor-pointer',
'style' => 'border: none;',
],
]);
}
public function configureOptions(OptionsResolver $resolver): void
{
// You can define options here if needed
$resolver->setDefaults([
'terms_content' => $this->translator->trans('OFAJ, responsable du traitement, met en œuvre un traitement de données à caractère personnel pour répondre à votre demande de création d’un compte utilisateur pour accéder à la plateforme de formation'),
]);
}
public function getName(): string
{
return 'contact';
}
}

@ -0,0 +1,56 @@
{# templates/contact/index.html.twig #}
{% extends "@ChamiloCore/Layout/layout_one_col.html.twig" %}
{% block content %}
<section id="contact-us" class="py-8">
<div class="max-w-md mx-auto">
<h2 class="text-2xl font-semibold text-center mb-6">Contact Us</h2>
{{ form_start(form, {'attr': {'class': 'bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4'}}) }}
{% for message in app.flashes('success') %}
<div class="mb-4 bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded relative" role="alert">
{{ message }}
</div>
{% endfor %}
<div class="mb-4">
{{ form_label(form.category) }}
{{ form_widget(form.category, {'attr': {'class': 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline'}}) }}
</div>
<div class="mb-4">
{{ form_label(form.firstName) }}
{{ form_widget(form.firstName, {'attr': {'class': 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline'}}) }}
</div>
<div class="mb-4">
{{ form_label(form.lastName) }}
{{ form_widget(form.lastName, {'attr': {'class': 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline'}}) }}
</div>
<div class="mb-4">
{{ form_label(form.email) }}
{{ form_widget(form.email, {'attr': {'class': 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline'}}) }}
</div>
<div class="mb-4">
{{ form_label(form.subject) }}
{{ form_widget(form.subject, {'attr': {'class': 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline'}}) }}
</div>
<div class="mb-6">
{{ form_label(form.message) }}
{{ form_widget(form.message, {'attr': {'class': 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline'}}) }}
</div>
<div class="flex items-center justify-center mb-6">
{{ form_widget(form.termsAccepted, {'attr': {'class': 'mr-2 leading-tight'}}) }}
{{ form_label(form.termsAccepted, 'By checking this box, I confirm that I accept the data processing by OFAJ') }}
</div>
{{ form_end(form) }}
</div>
</section>
{% endblock %}

@ -0,0 +1,7 @@
<form method="post" action="{{ path('chamilo_contact_category_delete', {'id': contact_category.id}) }}" onsubmit="return confirm({{ 'Are you sure you want to delete this item?'|trans|json_encode }});">
<input type="hidden" name="_token" value="{{ csrf_token('delete' ~ contact_category.id) }}">
<button class="btn btn--danger">
<em class="mdi mdi-trash-can"></em>
{{ "Delete"|trans }}
</button>
</form>

@ -0,0 +1,20 @@
{{ form_start(form, {'attr': {'class': 'space-y-4'}}) }}
<div class="mb-4">
{{ form_label(form.name, null, {'label_attr': {'class': 'block text-gray-700 text-sm font-bold mb-2'}}) }}
{{ form_widget(form.name, {'attr': {'class': 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline'}}) }}
{{ form_errors(form.name, {'attr': {'class': 'text-red-500 text-xs italic'}}) }}
</div>
<div class="mb-4">
{{ form_label(form.email, null, {'label_attr': {'class': 'block text-gray-700 text-sm font-bold mb-2'}}) }}
{{ form_widget(form.email, {'attr': {'class': 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline'}}) }}
{{ form_errors(form.email, {'attr': {'class': 'text-red-500 text-xs italic'}}) }}
</div>
<button class=" btn btn--primary " name="submit" type="submit" id="update_course_submit">
<em class="mdi mdi-content-save"></em>
{{ button_label|default('Save') }}
</button>
{{ form_end(form) }}

@ -0,0 +1,16 @@
{% extends "@ChamiloCore/Layout/layout_one_col.html.twig" %}
{% block content %}
<h1>{{ "Edit contact category"|trans }}</h1>
<div class="flex items-center justify-start space-x-2 mb-4">
<a href="{{ path('chamilo_contact_category_index') }}" class="btn btn--secondary inline-flex items-center">
<i class="mdi mdi-arrow-left-bold-circle-outline mr-2"></i> {{ "Back to list"|trans }}
</a>
{{ include('@ChamiloCore/ContactCategory/_delete_form.html.twig') }}
</div>
{{ include('@ChamiloCore/ContactCategory/_form.html.twig', {'button_label': 'Update'}) }}
{% endblock %}

@ -0,0 +1,41 @@
{% extends "@ChamiloCore/Layout/layout_one_col.html.twig" %}
{% block content %}
<h1>{{ "Contact categories"|trans }}</h1>
<div class="flex justify-between items-center mb-4">
<a href="{{ path('chamilo_contact_category_new') }}" class="btn btn--primary inline-flex items-center">
<i class="mdi mdi-plus-box mr-2"></i> {{ "Create new"|trans }}
</a>
<a href="/admin" class="btn btn--secondary inline-flex items-center">
<i class="mdi mdi-arrow-left-bold-circle-outline mr-2"></i> {{ "Back to Admin"|trans }}
</a>
</div>
<table class="table">
<thead>
<tr>
<th>{{ "Name"|trans }}</th>
<th>{{ "Email"|trans }}</th>
<th>{{ "Actions"|trans }}</th>
</tr>
</thead>
<tbody>
{% for contact_category in contact_categories %}
<tr>
<td>{{ contact_category.name }}</td>
<td>{{ contact_category.email }}</td>
<td class="flex items-center">
<a href="{{ path('chamilo_contact_category_edit', {'id': contact_category.id}) }}" class="btn btn--info inline-flex items-center mr-2">
<i class="mdi mdi-pencil mr-2"></i> {{ "Edit"|trans }}
</a>
{{ include('@ChamiloCore/ContactCategory/_delete_form.html.twig') }}
</td>
</tr>
{% else %}
<tr>
<td colspan="3">{{ "No records found"|trans }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

@ -0,0 +1,13 @@
{% extends "@ChamiloCore/Layout/layout_one_col.html.twig" %}
{% block content %}
<h1>{{ "Create new category"|trans }}</h1>
<div class="flex justify-between items-center mb-4">
<a href="{{ path('chamilo_contact_category_index') }}" class="btn btn--secondary inline-flex items-center">
<i class="mdi mdi-arrow-left-bold-circle-outline mr-2"></i> {{ "Back to list"|trans }}
</a>
</div>
{{ include('@ChamiloCore/ContactCategory/_form.html.twig') }}
{% endblock %}

@ -0,0 +1,28 @@
{% extends "@ChamiloCore/Layout/layout_one_col.html.twig" %}
{% block content %}
<h1>{{ "Contact category"|trans }}</h1>
<table class="table">
<tbody>
<tr>
<th>Id</th>
<td>{{ contact_category.id }}</td>
</tr>
<tr>
<th>Name</th>
<td>{{ contact_category.name }}</td>
</tr>
<tr>
<th>Email</th>
<td>{{ contact_category.email }}</td>
</tr>
</tbody>
</table>
<a href="{{ path('chamilo_contact_category_index') }}">{{ "Back to list"|trans }}</a>
<a href="{{ path('chamilo_contact_category_edit', {'id': contact_category.id}) }}">{{ "edit"|trans }}</a>
{{ include('@ChamiloCore/ContactCategory/_delete_form.html.twig') }}
{% endblock %}
Loading…
Cancel
Save