yamlform.module in YAML Form 8
Enables the creation of forms and questionnaires.
File
yamlform.moduleView source
<?php
/**
* @file
* Enables the creation of forms and questionnaires.
*/
use Drupal\Component\Utility\Unicode;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Asset\AttachedAssetsInterface;
use Drupal\Core\Breadcrumb\Breadcrumb;
use Drupal\Core\Database\Query\AlterableInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Url;
use Drupal\yamlform\Entity\YamlForm;
use Drupal\yamlform\Plugin\YamlFormElement\ManagedFile;
use Drupal\yamlform\Utility\YamlFormArrayHelper;
use Drupal\yamlform\YamlFormInterface;
use Drupal\yamlform\YamlFormSubmissionForm;
module_load_include('inc', 'yamlform', 'includes/yamlform.libraries');
module_load_include('inc', 'yamlform', 'includes/yamlform.options');
module_load_include('inc', 'yamlform', 'includes/yamlform.translation');
/**
* Return status for saving which deleted an existing item.
*/
const YAMLFORM_SAVED_DRAFT = 4;
/**
* Implements hook_help().
*/
function yamlform_help($route_name, RouteMatchInterface $route_match) {
// Get path from route match.
$path = preg_replace('/^' . preg_quote(base_path(), '/') . '/', '/', Url::fromRouteMatch($route_match)
->setAbsolute(FALSE)
->toString());
if (!in_array($route_name, [
'system.modules_list',
]) && strpos($route_name, 'yamlform') === FALSE && strpos($path, '/yamlform') === FALSE) {
return NULL;
}
/** @var \Drupal\yamlform\YamlFormHelpManagerInterface $help_manager */
$help_manager = \Drupal::service('yamlform.help_manager');
if ($route_name == 'help.page.yamlform') {
$build = $help_manager
->buildIndex();
}
else {
$build = $help_manager
->buildHelp($route_name, $route_match);
}
if ($build) {
$renderer = \Drupal::service('renderer');
$config = \Drupal::config('yamlform.settings');
$renderer
->addCacheableDependency($build, $config);
return $build;
}
else {
return NULL;
}
}
/**
* Implements hook_modules_installed().
*/
function yamlform_modules_installed($modules) {
// Add form paths when the path.module is being installed.
if (in_array('path', $modules)) {
/** @var \Drupal\yamlform\YamlFormInterface[] $yamlforms */
$yamlforms = YamlForm::loadMultiple();
foreach ($yamlforms as $yamlform) {
$yamlform
->updatePaths();
}
}
// Check HTML email provider support as modules are installed.
/** @var \Drupal\yamlform\YamlFormEmailProviderInterface $email_provider */
$email_provider = \Drupal::service('yamlform.email_provider');
$email_provider
->check();
}
/**
* Implements hook_modules_uninstalled().
*/
function yamlform_modules_uninstalled($modules) {
// Remove uninstalled module's third party settings from admin settings.
$config = \Drupal::configFactory()
->getEditable('yamlform.settings');
$third_party_settings = $config
->get('third_party_settings');
foreach ($modules as $module) {
unset($third_party_settings[$module]);
}
$config
->set('third_party_settings', $third_party_settings);
$config
->save();
// Check HTML email provider support as modules are ininstalled.
/** @var \Drupal\yamlform\YamlFormEmailProviderInterface $email_provider */
$email_provider = \Drupal::service('yamlform.email_provider');
$email_provider
->check();
}
/**
* Implements hook_local_tasks_alter().
*/
function yamlform_local_tasks_alter(&$local_tasks) {
if (isset($local_tasks['config_translation.local_tasks:entity.yamlform.config_translation_overview'])) {
// Change 'Translate' base route from 'entity.yamlform.edit_form'
// to 'entity.yamlform.canonical' because by default config entities don't
// have canonical views but the form entity does.
$local_tasks['config_translation.local_tasks:entity.yamlform.config_translation_overview']['title'] = 'Translate';
$local_tasks['config_translation.local_tasks:entity.yamlform.config_translation_overview']['base_route'] = 'entity.yamlform.canonical';
}
}
/**
* Implements hook_menu_local_tasks_alter().
*/
function yamlform_menu_local_tasks_alter(&$data, $route_name) {
if (strpos($route_name, 'entity.yamlform.') !== 0) {
return;
}
// Change 'Translate yamlform' tab to be just label 'Translate'.
if (isset($data['tabs'][0]['config_translation.local_tasks:entity.yamlform.config_translation_overview']['#link']['title'])) {
$data['tabs'][0]['config_translation.local_tasks:entity.yamlform.config_translation_overview']['#link']['title'] = t('Translate');
}
}
/**
* Implements hook_form_alter().
*/
function yamlform_form_alter(&$form, FormStateInterface $form_state, $form_id) {
if (strpos($form_id, 'yamlform_') === FALSE || strpos($form_id, 'node_') === 0) {
return;
}
$is_submission_form = $form_state
->getFormObject() instanceof YamlFormSubmissionForm;
// Don't include details toggle all for submission forms.
if (!$is_submission_form) {
$form['#attributes']['class'][] = 'js-yamlform-details-toggle';
$form['#attributes']['class'][] = 'yamlform-details-toggle';
$form['#attached']['library'][] = 'yamlform/yamlform.element.details.toggle';
}
if ($is_submission_form) {
$form['#after_build'][] = '_yamlform_form_after_build';
}
}
/**
* Alter form after build.
*/
function _yamlform_form_after_build($form, FormStateInterface $form_state) {
$form_object = $form_state
->getFormObject();
// Add contextual links and change theme wrapper to yamlform.html.twig
// which includes 'title_prefix' and 'title_suffix' variables needed for
// contextual links to appear.
$form['#contextual_links']['yamlform'] = [
'route_parameters' => [
'yamlform' => $form_object
->getEntity()
->getYamlForm()
->id(),
],
];
$form['#theme_wrappers'] = [
'yamlform',
];
return $form;
}
/**
* Implements hook_system_breadcrumb_alter().
*/
function yamlform_system_breadcrumb_alter(Breadcrumb &$breadcrumb, RouteMatchInterface $route_match, array $context) {
// Remove 'Forms' prefix from breadcrumb links generated path breadcrumbs.
// @see \Drupal\system\PathBasedBreadcrumbBuilder
$path = Url::fromRouteMatch($route_match)
->toString();
if (strpos($path, '/admin/structure/yamlform/settings/') !== FALSE) {
$links = $breadcrumb
->getLinks();
foreach ($links as $link) {
$text = $link
->getText();
if (strpos($text, (string) t('Forms') . ' ') == 0) {
$text = str_replace((string) t('Forms') . ': ', '', $text);
$link
->setText(Unicode::ucfirst($text));
}
}
}
// Fix 'Help' breadcrumb text.
if ($route_match
->getRouteName() == 'yamlform.help') {
$links = $breadcrumb
->getLinks();
$link = end($links);
$link
->setText(t('Forms'));
}
}
/**
* Implements hook_entity_delete().
*/
function yamlform_entity_delete(EntityInterface $entity) {
// Delete saved export settings for a form or source entity with the
// yamlform field.
if ($entity instanceof YamlFormInterface || method_exists($entity, 'hasField') && $entity
->hasField('yamlform')) {
$name = 'yamlform.export.' . $entity
->getEntityTypeId() . '.' . $entity
->id();
\Drupal::state()
->delete($name);
}
}
/**
* Implements hook_mail().
*/
function yamlform_mail($key, &$message, $params) {
// Never send emails when using devel generate to create 1000's of
// submissions.
if (\Drupal::moduleHandler()
->moduleExists('devel_generate') && \Drupal\yamlform\Plugin\DevelGenerate\YamlFormSubmissionDevelGenerate::isGeneratingSubmissions()) {
$message['send'] = FALSE;
}
$message['subject'] = $params['subject'];
$message['body'][] = $params['body'];
// Set the header's 'From' to the 'from_mail' so that the form's email from
// value is used instead of site's email address.
// See: \Drupal\Core\Mail\MailManager::mail.
if (!empty($params['from_mail'])) {
$message['from'] = $params['from_mail'];
$message['headers']['From'] = $params['from_mail'];
$message['headers']['Reply-to'] = $params['from_mail'];
$message['headers']['Return-Path'] = $params['from_mail'];
}
if (!empty($params['cc_mail'])) {
$message['headers']['Cc'] = $params['cc_mail'];
}
if (!empty($params['bcc_mail'])) {
$message['headers']['Bcc'] = $params['bcc_mail'];
}
}
/**
* Implements hook_mail_alter().
*/
function yamlform_mail_alter(&$message) {
// Drupal hardcodes all mail header as 'text/plain' so we need to set the
// header's 'Content-type' to HTML if the EmailYamlFormHandler's
// 'html' flag has been set.
// @see \Drupal\Core\Mail\MailManager::mail()
// @see \Drupal\yamlform\Plugin\YamlFormHandler\EmailYamlFormHandler::getMessage().
if (strpos($message['id'], 'yamlform') === 0) {
if ($message['params']['html']) {
$message['headers']['Content-Type'] = 'text/html; charset=UTF-8; format=flowed';
}
}
}
/**
* Implements hook_page_attachments().
*/
function yamlform_page_attachments(&$page) {
$route_name = Drupal::routeMatch()
->getRouteName();
$url = Url::fromRoute('<current>')
->toString();
// Attach global libraries.
if (preg_match('/^(yamlform\\.|^entity\\.([^.]+\\.)?yamlform)/', $route_name) || preg_match('#(/node/add/yamlform|/admin/help/yamlform)#', $url)) {
// Attach theme specific library to yamlform routers so that we can tweak
// the seven.theme.
$theme = \Drupal::theme()
->getActiveTheme()
->getName();
if (file_exists(drupal_get_path('module', 'yamlform') . "/css/yamlform.theme.{$theme}.css")) {
$page['#attached']['library'][] = "yamlform/yamlform.theme.{$theme}";
}
// Attach details element save open/close library.
if (\Drupal::config('yamlform.settings')
->get('ui.details_save')) {
$page['#attached']['library'][] = 'yamlform/yamlform.element.details.save';
}
}
// Attach codemirror library to block admin to ensure that the library
// is loaded by the form block is placed using AJAX.
if (\Drupal::routeMatch()
->getRouteName() == 'block.admin_display') {
$page['#attached']['library'][] = 'yamlform/yamlform.codemirror.yaml';
}
}
/**
* Implements hook_css_alter().
*
* @see \Drupal\yamlform\YamlFormSubmissionForm::form
*/
function yamlform_css_alter(&$css, AttachedAssetsInterface $assets) {
_yamlform_asset_alter($css, $assets, 'css', 'css');
}
/**
* Implements hook_js_alter().
*
* @see \Drupal\yamlform\YamlFormSubmissionForm::form
*/
function yamlform_js_alter(&$javascript, AttachedAssetsInterface $assets) {
_yamlform_asset_alter($javascript, $assets, 'javascript', 'js');
}
/**
* Alter CSS or JavaScript assets to include custom form assets.
*
* Note: CSS and JavaScript are not aggregated or minified to make it easier
* for themers to debug and custom their code. We could write the CSS and JS
* to the 'files/css' and 'files/js' using the hash key and aggregrate them.
*
* @param array $items
* An array of all CSS or JavaScript being presented on the page.
* @param \Drupal\Core\Asset\AttachedAssetsInterface $assets
* The assets attached to the current response.
* @param string $type
* The type of asset being attached.
* @param string $extension
* The asset file extension being attached.
*/
function _yamlform_asset_alter(array &$items, AttachedAssetsInterface $assets, $type, $extension) {
$settings = $assets
->getSettings();
if (empty($settings['yamlform']['assets'][$type])) {
return;
}
$path = drupal_get_path('module', 'yamlform');
foreach ($settings['yamlform']['assets'][$type] as $id => $hash) {
$key = "{$path}/{$extension}/yamlform.assets.{$extension}";
if (isset($items[$key])) {
$items[$key] = [
'data' => base_path() . "yamlform/{$id}/assets/{$type}?v={$hash}",
'group' => 1000,
'weight' => 1000,
] + $items[$key];
}
}
}
/**
* Implements hook_file_download().
*/
function yamlform_file_download($uri) {
return ManagedFile::accessFileDownload($uri);
}
/**
* Implements hook_theme().
*/
function yamlform_theme() {
$info = [
'yamlform_help' => [
'variables' => [
'info' => [],
],
],
'yamlform_help_video_youtube' => [
'variables' => [
'youtube_id' => NULL,
],
],
'yamlform' => [
'render element' => 'element',
],
'yamlform_actions' => [
'render element' => 'element',
],
'yamlform_handler_email_summary' => [
'variables' => [
'settings' => NULL,
'handler' => [],
],
],
'yamlform_handler_remote_post_summary' => [
'variables' => [
'settings' => NULL,
'handler' => [],
],
],
'yamlform_confirmation' => [
'variables' => [
'yamlform' => NULL,
'source_entity' => NULL,
'yamlform_submission' => NULL,
],
],
'yamlform_submission_navigation' => [
'variables' => [
'yamlform_submission' => NULL,
],
],
'yamlform_submission_information' => [
'variables' => [
'yamlform_submission' => NULL,
'source_entity' => NULL,
'open' => FALSE,
],
],
'yamlform_submission_html' => [
'variables' => [
'yamlform_submission' => NULL,
'source_entity' => NULL,
],
],
'yamlform_submission_table' => [
'variables' => [
'yamlform_submission' => NULL,
'source_entity' => NULL,
],
],
'yamlform_submission_text' => [
'variables' => [
'yamlform_submission' => NULL,
'source_entity' => NULL,
],
],
'yamlform_submission_yaml' => [
'variables' => [
'yamlform_submission' => NULL,
'source_entity' => NULL,
],
],
'yamlform_element_base_html' => [
'variables' => [
'element' => [],
'value' => NULL,
'options' => [],
],
],
'yamlform_element_base_text' => [
'variables' => [
'element' => [],
'value' => NULL,
'options' => [],
],
],
'yamlform_container_base_html' => [
'variables' => [
'element' => [],
'value' => NULL,
'options' => [],
],
],
'yamlform_container_base_text' => [
'variables' => [
'element' => [],
'value' => NULL,
'options' => [],
],
],
'yamlform_element_color_value_swatch' => [
'variables' => [
'element' => NULL,
'value' => NULL,
'options' => [],
],
],
'yamlform_element_managed_file' => [
'variables' => [
'element' => NULL,
'value' => NULL,
'options' => [],
'file' => NULL,
],
],
'yamlform_element_audio_file' => [
'variables' => [
'element' => NULL,
'value' => NULL,
'options' => [],
'file' => NULL,
],
],
'yamlform_element_document_file' => [
'variables' => [
'element' => NULL,
'value' => NULL,
'options' => [],
'file' => NULL,
],
],
'yamlform_element_image_file' => [
'variables' => [
'element' => NULL,
'value' => NULL,
'options' => [],
'file' => NULL,
],
],
'yamlform_element_video_file' => [
'variables' => [
'element' => NULL,
'value' => NULL,
'options' => [],
'file' => NULL,
],
],
'yamlform_message' => [
'render element' => 'element',
],
'yamlform_composite_address' => [
'render element' => 'element',
],
'yamlform_composite_contact' => [
'render element' => 'element',
],
'yamlform_composite_creditcard' => [
'render element' => 'element',
],
'yamlform_composite_location' => [
'render element' => 'element',
],
'yamlform_composite_name' => [
'render element' => 'element',
],
'yamlform_codemirror' => [
'variables' => [
'code' => NULL,
'type' => 'text',
],
],
'yamlform_progress' => [
'variables' => [
'yamlform' => NULL,
'current_page' => NULL,
],
],
'yamlform_progress_bar' => [
'variables' => [
'yamlform' => NULL,
'current_page' => NULL,
'max_pages' => 10,
],
],
];
// Since any rendering of a form is going to require 'yamlform.theme.inc'
// we are going to just add it to every template.
foreach ($info as &$template) {
$template['file'] = 'includes/yamlform.theme.inc';
}
return $info;
}
/**
* Implements hook_theme_registry_alter().
*/
function yamlform_theme_registry_alter(&$theme_registry) {
// Allow attributes to be defined for status messages so that #states
// can be added to messages.
// @see \Drupal\yamlform\Element\YamlFormMessage
if (!isset($theme_registry['status_messages']['variables']['attributes'])) {
$theme_registry['status_messages']['variables']['attributes'] = [];
}
}
/**
* Implements hook_theme_suggestions_alter().
*/
function yamlform_theme_suggestions_alter(array &$suggestions, array $variables, $hook) {
if (strpos($hook, 'yamlform_') !== 0) {
return;
}
if (strpos($hook, 'yamlform_element_base_') === 0 || strpos($hook, 'yamlform_container_base_') === 0) {
$element = $variables['element'];
if (empty($element['#type'])) {
return;
}
$type = $element['#type'];
$name = $element['#yamlform_key'];
$suggestions[] = $hook . '__' . $type;
$suggestions[] = $hook . '__' . $type . '__' . $name;
/** @var \Drupal\yamlform\YamlFormElementManagerInterface $element_manager */
$element_manager = \Drupal::service('plugin.manager.yamlform.element');
$element_handler = $element_manager
->createInstance($type);
if ($format = $element_handler
->getFormat($element)) {
$suggestions[] = $hook . '__' . $type . '__' . $format;
$suggestions[] = $hook . '__' . $type . '__' . $name . '__' . $format;
}
}
elseif (isset($variables['yamlform_submission'])) {
/** @var \Drupal\yamlform\YamlFormSubmissionInterface $yamlform_submission */
$yamlform_submission = $variables['yamlform_submission'];
$yamlform = $yamlform_submission
->getYamlForm();
$suggestions[] = $hook . '__' . $yamlform
->id();
}
elseif (isset($variables['yamlform'])) {
/** @var \Drupal\yamlform\YamlFormInterface $yamlform */
$yamlform = $variables['yamlform'];
$suggestions[] = $hook . '__' . $yamlform
->id();
}
}
/**
* Prepares variables for checkboxes templates.
*
* @see \Drupal\yamlform\Plugin\YamlFormElement\OptionsBase
*/
function yamlform_preprocess_checkboxes(&$variables) {
$element = $variables['element'];
$options_display = !empty($element['#options_display']) ? $element['#options_display'] : 'one_column';
$variables['attributes']['class'][] = 'yamlform-options-display-' . str_replace('_', '-', $options_display);
$variables['#attached']['library'][] = 'yamlform/yamlform.options';
}
/**
* Prepares variables for radios templates.
*
* @see \Drupal\yamlform\Plugin\YamlFormElement\OptionsBase
*/
function yamlform_preprocess_radios(&$variables) {
yamlform_preprocess_checkboxes($variables);
}
/**
* Adds JavaScript to change the state of an element based on another element.
*
* @param array $elements
* A renderable array element having a #states property as described above.
* @param string $key
* The element property to add the states attribute to.
*
* @see drupal_process_states()
*/
function yamlform_process_states(&$elements, $key = '#attributes') {
if (empty($elements['#states'])) {
return;
}
$elements['#attached']['library'][] = 'core/drupal.states';
$elements[$key]['data-drupal-states'] = Json::encode($elements['#states']);
// Make sure to include target class for this container.
if (empty($elements[$key]['class']) || !YamlFormArrayHelper::inArray([
'js-form-item',
'js-form-submit',
'js-form-wrapper',
], $elements[$key]['class'])) {
$elements[$key]['class'][] = 'js-form-item';
}
}
/******************************************************************************/
// Private functions.
/******************************************************************************/
/**
* Provides custom PHP error handling when form rendering is validated.
*
* Converts E_RECOVERABLE_ERROR to WARNING so that an exceptions can be thrown
* and caught by
* \Drupal\yamlform\YamlFormEntityElementsValidator::validateRendering().
*
* @param int $error_level
* The level of the error raised.
* @param string $message
* The error message.
* @param string $filename
* The filename that the error was raised in.
* @param int $line
* The line number the error was raised at.
* @param array $context
* An array that points to the active symbol table at the point the error
* occurred.
*
* @throws \ErrorException
* Throw ErrorException for E_RECOVERABLE_ERROR errors.
*
* @see \Drupal\yamlform\YamlFormEntityElementsValidator::validateRendering()
*/
function _yamlform_entity_element_validate_rendering_error_handler($error_level, $message, $filename, $line, array $context) {
// From: http://stackoverflow.com/questions/15461611/php-try-catch-not-catching-all-exceptions
if (E_RECOVERABLE_ERROR === $error_level) {
// Allow Drupal to still log the error but convert it to a warning.
_drupal_error_handler(E_WARNING, $message, $filename, $line, $context);
throw new ErrorException($message, $error_level, 0, $filename, $line);
}
else {
_drupal_error_handler($message, $message, $filename, $line, $context);
}
}
/**
* Implements hook_query_alter().
*
* Append EAV sort to yamlform_submission entity query.
*
* @see http://stackoverflow.com/questions/12893314/sorting-eav-database
* @see \Drupal\yamlform\YamlFormSubmissionListBuilder::getEntityIds
*/
function yamlform_query_alter(AlterableInterface $query) {
/** @var \Drupal\Core\Database\Query\SelectInterface $query */
$name = $query
->getMetaData('yamlform_submission_element_name');
if (!$name) {
return;
}
$direction = $query
->getMetaData('yamlform_submission_element_direction');
$property_name = $query
->getMetaData('yamlform_submission_element_property_name');
$query
->distinct();
$query
->addJoin('INNER', 'yamlform_submission_data', NULL, 'base_table.sid = yamlform_submission_data.sid');
$query
->addField('yamlform_submission_data', 'value', 'value');
$query
->condition('name', $name);
if ($property_name) {
$query
->condition('property', $property_name);
}
$query
->orderBy('value', $direction);
}
Functions
Constants
Name | Description |
---|---|
YAMLFORM_SAVED_DRAFT | Return status for saving which deleted an existing item. |