Skip to content

Commit

Permalink
FORSLAG-67: Handled survey webforms
Browse files Browse the repository at this point in the history
  • Loading branch information
rimi-itk committed Aug 17, 2023
1 parent 1e92f4d commit 3b34f3b
Show file tree
Hide file tree
Showing 7 changed files with 342 additions and 1 deletion.
15 changes: 15 additions & 0 deletions web/modules/custom/hoeringsportal_citizen_proposal/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,18 @@ something like
docker compose exec phpfpm vendor/bin/drush sql:query "SELECT nid, title FROM node_field_data WHERE type = 'citizen_proposal'"
docker compose exec phpfpm vendor/bin/drush hoeringsportal-citizen-proposal:test-mail:send 87 create test@example.com
```

## Surveys

We use the [Webform module](https://www.drupal.org/project/webform) to render
surveys when creating a citizen proposal, and create webform submission to store
the survey responses.

To keep things simple we should allow only very few element types in webforms
(cf. `/admin/structure/webform/config/elements#edit-types`).

When rendering a webform survey, we skip rendering “Entity autocomplete”
elements and all actions (e.g. “Submit”). However, if a survey webform contains
an “Entity autocomplete” element allowing references to “Citizen proposal”
nodes, we set a reference to the proposal on the survey response when saving the
response (creating a submission).
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,26 @@ services:
- '@logger.channel.hoeringsportal_citizen_proposal'
tags:
- {name: event_subscriber}

# @see https://www.drupal.org/project/drupal/issues/2376347
# @see https://www.drupal.org/docs/drupal-apis/services-and-dependency-injection/structure-of-a-service-file#s-properties-of-a-service
hoeringsportal_citizen_proposal.storage.webform:
class: Drupal\webform\WebformEntityStorageInterface
factory: ['@entity_type.manager', 'getStorage']
arguments: ['webform']

hoeringsportal_citizen_proposal.storage.webform_survey_temp_store:
class: Drupal\Core\TempStore\PrivateTempStore
factory: ['@tempstore.private', 'get']
arguments: ['hoeringsportal_citizen_proposal_survey']

hoeringsportal_citizen_proposal.storage.webform_config:
class: Drupal\Core\Config\ImmutableConfig
factory: ['@config.factory', 'get']
arguments: ['webform.settings']

Drupal\hoeringsportal_citizen_proposal\Helper\WebformHelper:
arguments:
- '@hoeringsportal_citizen_proposal.storage.webform'
- '@hoeringsportal_citizen_proposal.storage.webform_config'
- '@hoeringsportal_citizen_proposal.storage.webform_survey_temp_store'
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
use Drupal\Core\Form\FormStateInterface;
use Drupal\hoeringsportal_citizen_proposal\Helper\Helper;
use Drupal\hoeringsportal_citizen_proposal\Helper\MailHelper;
use Drupal\hoeringsportal_citizen_proposal\Helper\WebformHelper;
use Drupal\webform\WebformInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
Expand All @@ -17,7 +19,8 @@ final class ProposalAdminForm extends FormBase {
* Constructor for the proposal add form.
*/
public function __construct(
readonly private Helper $helper
readonly private Helper $helper,
readonly private WebformHelper $webformHelper
) {
}

Expand All @@ -27,6 +30,7 @@ public function __construct(
public static function create(ContainerInterface $container) {
return new static(
$container->get(Helper::class),
$container->get(WebformHelper::class),
);
}

Expand Down Expand Up @@ -237,6 +241,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
'#default_value' => $adminFormStateValues['sidebar_text']['value'] ?? '',
];

$this->buildSurveyForm($form, $adminFormStateValues ?? []);
$this->buildEmailsForm($form, $adminFormStateValues ?? []);

$form['actions']['#type'] = 'actions';
Expand All @@ -249,6 +254,57 @@ public function buildForm(array $form, FormStateInterface $form_state) {
return $form;
}

/**
* Build survey form.
*
* @param array $form
* The form.
* @param array $adminFormStateValues
* The admin form state values.
*
* @return array
* The form.
*/
private function buildSurveyForm(array &$form, array $adminFormStateValues): array {
$form['survey'] = [
'#type' => 'details',
'#tree' => TRUE,
'#open' => TRUE,
'#title' => $this
->t('Survey'),
];

$form['survey']['webform'] = [
'#type' => 'select',
'#title' => $this->t('Survey webform'),
'#options' => array_map(
static fn (WebformInterface $webform) => $webform->label(),
$this->webformHelper->loadSurveyWebforms()
),
'#empty_option' => $this->t('Select survey webform'),
'#default_value' => $adminFormStateValues['survey']['webform'] ?? '',
'#description' => $this->t('Select a survey to show as part of the citizen proposal creation form.'),
];

$form['survey']['description'] = [
'#type' => 'text_format',
'#title' => $this->t('Survey description'),
'#format' => $adminFormStateValues['survey']['description']['format'] ?? 'filtered_html',
'#default_value' => $adminFormStateValues['survey']['description']['value'] ?? '',
'#description' => $this->t('Tell a little about why the survey is shown.'),
'#states' => [
'visible' => [
':input[name="survey[webform]"]' => ['filled' => TRUE],
],
'required' => [
':input[name="survey[webform]"]' => ['filled' => TRUE],
],
],
];

return $form;
}

/**
* Build emails form.
*
Expand Down Expand Up @@ -315,4 +371,14 @@ public function submitForm(array &$form, FormStateInterface $formState): void {
$this->helper->setAdminValues($formState->getValues());
}

/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $formState) {
if (!empty($formState->getValue(['survey', 'webform']))
&& empty($formState->getValue(['survey', 'description', 'value']))) {
$formState->setError($form['survey']['description']['value'], $this->t('Please enter a survey description.'));
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ public function buildProposalForm(array $form, FormStateInterface $formState): a
],
];

$this->buildSurveyForm($form);

$form['consent'] = [
'#type' => 'checkbox',
'#title' => $this
Expand All @@ -167,6 +169,45 @@ public function buildProposalForm(array $form, FormStateInterface $formState): a
return $form;
}

/**
* Build survey form.
*
* @param array $form
* The form.
*
* @return array
* The form.
*/
private function buildSurveyForm(array &$form): array {
$form['survey'] = [
'#type' => 'container',
'#attributes' => [
'class' => ['survey', 'citizen-proposal-survey'],
],
'#tree' => TRUE,
];

try {
$description = $this->getAdminFormStateValue(['survey', 'description']);
if (($webform = $this->loadSurvey()) && isset($description['value'])) {
// We use a numeric index (implicit 0) here to prevent webform fields
// accidentally overwriting the description element.
$form['survey'][] = [
'#type' => 'processed_text',
'#text' => $description['value'],
'#format' => $description['format'] ?? 'filtered_html',
];

$this->webformHelper->renderWebformElements($webform, $form['survey']);
}
}
catch (\Exception $exception) {
throw $exception;
}

return $form;
}

/**
* {@inheritdoc}
*/
Expand Down Expand Up @@ -208,6 +249,16 @@ public function submitForm(array &$form, FormStateInterface $formState): void {
$this->helper->setDraftProposal($entity);
$formState
->setRedirect('hoeringsportal_citizen_proposal.citizen_proposal.proposal_approve');

// Handle survey.
try {
if ($webform = $this->loadSurvey()) {
$surveyData = (array) $formState->getValue('survey');
$this->webformHelper->setSurveyResponse($webform, $surveyData);
}
}
catch (\Exception) {
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,18 @@ public function submitForm(array &$form, FormStateInterface $formState) {

$this->messenger()->addStatus($this->getAdminFormStateValue('approve_submission_text', $this->t('Thank you for your submission.')));
$entity->save();

$this->helper->deleteDraftProposal();

// Handle survey.
try {
if ($webform = $this->loadSurvey()) {
$this->webformHelper->saveSurveyResponse($webform, $entity);
}
}
catch (\Exception) {
}

$formState->setRedirectUrl(
$this->deAuthenticateUser(
$this->getAdminFormStateValueUrl('approve_goto_url', '/')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
use Drupal\Core\Url;
use Drupal\hoeringsportal_citizen_proposal\Exception\RuntimeException;
use Drupal\hoeringsportal_citizen_proposal\Helper\Helper;
use Drupal\hoeringsportal_citizen_proposal\Helper\WebformHelper;
use Drupal\hoeringsportal_openid_connect\Controller\OpenIDConnectController;
use Drupal\hoeringsportal_openid_connect\Helper as AuthenticationHelper;
use Drupal\webform\WebformInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;

Expand All @@ -25,6 +27,7 @@ abstract class ProposalFormBase extends FormBase {
*/
final public function __construct(
readonly protected Helper $helper,
readonly protected WebformHelper $webformHelper,
readonly private AuthenticationHelper $authenticationHelper,
readonly private ImmutableConfig $config
) {
Expand All @@ -36,6 +39,7 @@ final public function __construct(
public static function create(ContainerInterface $container) {
return new static(
$container->get(Helper::class),
$container->get(WebformHelper::class),
$container->get(AuthenticationHelper::class),
$container->get('config.factory')->get('hoeringsportal_citizen_proposal.settings')
);
Expand Down Expand Up @@ -234,4 +238,17 @@ protected function getUserUuid($allowEditor = TRUE): string {
return sha1($userId);
}

/**
* Load survey webform.
*
* @return \Drupal\webform\WebformInterface|null
* The webform if any.
*/
protected function loadSurvey(): ?WebformInterface {
return $this->webformHelper->loadWebform((string) $this->getAdminFormStateValue([
'survey',
'webform',
]));
}

}
Loading

0 comments on commit 3b34f3b

Please sign in to comment.