Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VAGOV-TEAM: 93059, 93060, 94023 #19541

Merged
merged 22 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
f3deb3f
VAGOV-TEAM-90124:
ryguyk Sep 16, 2024
cc6face
Adds namespaces to test files.
ryguyk Sep 17, 2024
04b9ce5
Removes now-unused services file.
ryguyk Sep 26, 2024
e93e3ab
Removes custom service originally built to handle validation.
ryguyk Sep 27, 2024
13c87d1
Adds a routing.yml file for va_gov_form_builder module.
ryguyk Sep 27, 2024
5a906fa
Adds routing and controller for va_gov_form_builder.
ryguyk Oct 3, 2024
fd186f9
Adds forms for Intro and StartConversion form-builder steps.
ryguyk Oct 3, 2024
fadd2b4
Creates a Trait for some shared testing constants.
ryguyk Oct 3, 2024
3b36632
Uses new Trait in existing UniqueFieldValidatorTest
ryguyk Oct 3, 2024
fbfe7cf
Adds functional tests for controller.
ryguyk Oct 3, 2024
08f1621
Adds functional test for Intro form page.
ryguyk Oct 3, 2024
718dff9
Adds functional test for StartConversion form page.
ryguyk Oct 3, 2024
06a253b
Removes login from controller test.
ryguyk Oct 3, 2024
8dbf964
- Updates controller to remove constructor and call parent::create().
ryguyk Oct 3, 2024
9733a76
Corrects incorrect namespace in controller test.
ryguyk Oct 3, 2024
25b1c71
Updates functional test to properly indicate `@group functional`
ryguyk Oct 3, 2024
893fa1d
Adds module dependency to functional tests.
ryguyk Oct 3, 2024
2e9d8db
- Adds Form Builder "Intro" page.
ryguyk Oct 18, 2024
0b740f2
Qualifies ReflectionClass
ryguyk Oct 18, 2024
555ff14
Ignore PHPStan on call to drupalFormBuilder->getForm, as passing extr…
ryguyk Oct 18, 2024
7fc0db9
Adds controller test for nameAndDob.
ryguyk Oct 18, 2024
701ba90
Merge branch 'main' into VAGOV-TEAM-93059-93060-94023
ryguyk Oct 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

namespace Drupal\va_gov_form_builder\Controller;

use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
* Controller for the VA Form Builder experience.
*
* Note:
*
* Drupal also has a something called "form builder"
* that is utilized in this controller (`drupalFormBuilder`).
* This `FormBuilder` service is responsible for building and
* processing forms in Drupal, and is called here so the controller can
* return the form that should be rendered for the given route.
*
* The naming can be confusing, so it's important to remember that
* `VaGovFormBuilder` is the tool built by this module (`va_gov_form_builder`),
* and used for building Veteran-facing VA forms, while Drupal's
* `FormBuilder` (service) is responsible for building and processing
* forms _within_ Drupal.
ryguyk marked this conversation as resolved.
Show resolved Hide resolved
*/
class VaGovFormBuilderController extends ControllerBase {

/**
* The Drupal form builder.
*
* @var \Drupal\Core\Form\FormBuilderInterface
*/
private $drupalFormBuilder;

/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
$instance = parent::create($container);
$instance->drupalFormBuilder = $container->get('form_builder');

return $instance;
}

/**
* Entry point for the VA Form Builder. Redirects to the intro page.
*/
public function entry() {
return $this->redirect('va_gov_form_builder.intro');
}

/**
* Intro page.
*/
public function intro() {
return $this->drupalFormBuilder->getForm('Drupal\va_gov_form_builder\Form\Intro');
}

/**
* Start-conversion page.
*/
public function startConversion() {
return $this->drupalFormBuilder->getForm('Drupal\va_gov_form_builder\Form\StartConversion');
}

/**
* Name-and-date-of-birth page.
*/
public function nameAndDob($nid) {
// @phpstan-ignore-next-line
return $this->drupalFormBuilder->getForm('Drupal\va_gov_form_builder\Form\NameAndDob', $nid);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace Drupal\va_gov_form_builder\Form\Base;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;

/**
* Abstract base class for Form Builder form steps.
*/
abstract class FormBuilderBase extends FormBase {

/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form['#title'] = $this->t('Form Builder');
derekhouck marked this conversation as resolved.
Show resolved Hide resolved

return $form;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

namespace Drupal\va_gov_form_builder\Form\Base;

use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

/**
* Abstract base class for Form Builder form steps that edit an existing node.
*/
abstract class FormBuilderEditBase extends FormBuilderNodeBase {

/**
* Flag indicating if the node has been changed.
*
* Indicates if the node has been changed
* since the form was first instantiated.
*
* @var bool
*/
protected $digitalFormNodeIsChanged;

/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, $nid = NULL) {
// When form is first built, initialize flag to false.
$this->digitalFormNodeIsChanged = FALSE;

// Load the node indicated by the passed-in node id.
$digitalFormNode = $this->entityTypeManager->getStorage('node')->load($nid);
if (!$digitalFormNode) {
throw new NotFoundHttpException();
}
$this->digitalFormNode = $digitalFormNode;

$form = parent::buildForm($form, $form_state);
return $form;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?php

namespace Drupal\va_gov_form_builder\Form\Base;

use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
* Abstract base class for Form Builder form steps that access a node.
*/
abstract class FormBuilderNodeBase extends FormBuilderBase {

/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;

/**
* The Digital Form node created or loaded by this form step.
*
* @var \Drupal\node\Entity\Node
*/
protected $digitalFormNode;

/**
* {@inheritDoc}
*/
public function __construct(EntityTypeManagerInterface $entityTypeManager) {
$this->entityTypeManager = $entityTypeManager;
}

/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity_type.manager')
);
}

/**
* Returns the Digital Form fields accessed by this form step.
*/
abstract protected function getFields();

/**
* Sets (creates or updates) a Digital Form node from the form-state data.
*/
abstract protected function setDigitalFormNodeFromFormState(array &$form, FormStateInterface $form_state);

/**
* Determines if digitalFormNode has a chapter (paragraph) of a given type.
*/
protected function digitalFormNodeHasChapterOfType($type) {
$chapters = $this->digitalFormNode->get('field_chapters')->getValue();

foreach ($chapters as $chapter) {
if (isset($chapter['target_id'])) {
$paragraph = $this->entityTypeManager->getStorage('paragraph')->load($chapter['target_id']);
if ($paragraph) {
if ($paragraph->bundle() === $type) {
return TRUE;
}
}
}
}

return FALSE;
}

/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
$this->setDigitalFormNodeFromFormState($form, $form_state);

// Validate the node entity.
/** @var \Symfony\Component\Validator\ConstraintViolationListInterface $violations */
$violations = $this->digitalFormNode->validate();
ryguyk marked this conversation as resolved.
Show resolved Hide resolved

// Loop through each violation and set errors on the form.
if ($violations->count() > 0) {
foreach ($violations as $violation) {
$fieldName = $violation->getPropertyPath();

// Only concern ourselves with validation of fields used on this form.
if (in_array($fieldName, $this->getFields())) {
$message = $violation->getMessage();
$form_state->setErrorByName($fieldName, $message);
}
}
}
}

/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
// Save the previously validated node.
$this->digitalFormNode->save();
}

}
103 changes: 103 additions & 0 deletions docroot/modules/custom/va_gov_form_builder/src/Form/Intro.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?php

namespace Drupal\va_gov_form_builder\Form;

use Drupal\Core\Form\FormStateInterface;
use Drupal\va_gov_form_builder\Form\Base\FormBuilderBase;

/**
* Form step for creating a new form (new conversion).
*/
class Intro extends FormBuilderBase {

/**
* {@inheritdoc}
*/
public function getFormId() {
return 'form_builder__intro';
}

/**
* {@inheritdoc}
*/
protected function getFields() {
return [];
}

/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form = parent::buildForm($form, $form_state);

$form['working_with_form_builder_header'] = [
'#type' => 'html_tag',
'#tag' => 'h2',
'#children' => $this->t('Working with the Form Builder'),
];

$form['paragraph_1'] = [
'#type' => 'html_tag',
'#tag' => 'p',
'#children' => $this->t('This is where the conversion of an existing form, or the continued editing
of a converted form, takes place.'),
];

$form['before_beginning_header'] = [
'#type' => 'html_tag',
'#tag' => 'h3',
'#children' => $this->t('Before beginning'),
];

$form['paragraph_2'] = [
'#type' => 'html_tag',
'#tag' => 'p',
'#children' => $this->t('Make sure you have a copy of the form you are intending to convert.
This will make it easier to reference the information
that will be needed, and the order in which it appears for your users.'),
];

$form['paragraph_3'] = [
'#type' => 'html_tag',
'#tag' => 'p',
'#children' => $this->t('When returning to update an existing conversion, your past projects
are listed under <b>Projects</b> at right.'),
];

$form['paragraph_4'] = [
'#type' => 'html_tag',
'#tag' => 'p',
'#children' => $this->t('If you need to access a conversion that you did not create, you can use the
<b>content search</b> to find that project.'),
];

$form['paragraph_5'] = [
'#type' => 'html_tag',
'#tag' => 'p',
'#children' => $this->t('To begin a new project select <b>Start conversion</b>.'),
];

$form['actions']['start_conversion'] = [
'#type' => 'submit',
'#value' => $this->t('Start conversion'),
'#attributes' => [
'class' => [
'button',
'button--primary',
'js-form-submit',
'form-submit',
],
],
];

return $form;
}

/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$form_state->setRedirect('va_gov_form_builder.start_conversion');
}

}
Loading
Loading