bootstrap_five_layouts-1.0.x-dev/src/Form/BootstrapLayoutForm.php

src/Form/BootstrapLayoutForm.php
<?php

namespace Drupal\bootstrap_five_layouts\Form;

use Drupal\bootstrap_five_layouts\Traits\BytesFormatTrait;
use Drupal\bootstrap_five_layouts\BootstrapFiveLayoutsManager;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Extension\ModuleExtensionList;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Config\TypedConfigManagerInterface;
use Drupal\Component\Utility\Bytes;
use Drupal\Component\Utility\DiffArray;
use Drupal\Component\Serialization\Yaml;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Link;
use Drupal\Core\Url;

/**
 * Provides the layout builder modal configuration form.
 */
class BootstrapLayoutForm extends ConfigFormBase {
  use BytesFormatTrait;

  /**
   * The module handler service.
   *
   * @var \Drupal\Core\Extension\ModuleHandlerInterface
   */
  protected $moduleHandler;

  /**
   * The module extension list service.
   *
   * @var \Drupal\Core\Extension\ModuleExtensionList
   */
  protected $moduleExtensionList;

  /**
   * The Bootstrap Five Layouts manager.
   *
   * @var \Drupal\bootstrap_five_layouts\BootstrapFiveLayoutsManager
   */
  protected $bootstrapFiveLayoutsManager;

  /**
   * Constructs a BootstrapLayoutForm object.
   */
  public function __construct(ConfigFactoryInterface $config_factory, TypedConfigManagerInterface $typed_config_manager, ModuleHandlerInterface $module_handler, ModuleExtensionList $module_extension_list, BootstrapFiveLayoutsManager $bootstrap_five_layouts_manager) {
    parent::__construct($config_factory, $typed_config_manager);
    $this->moduleHandler = $module_handler;
    $this->moduleExtensionList = $module_extension_list;
    $this->bootstrapFiveLayoutsManager = $bootstrap_five_layouts_manager;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('config.factory'),
      $container->get('config.typed'),
      $container->get('module_handler'),
      $container->get('extension.list.module'),
      $container->get('plugin.manager.bootstrap_five_layouts')
    );
  }

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

  /**
   * {@inheritdoc}
   */
  public function getEditableConfigNames() {
    return ['bootstrap_five_layouts.settings'];
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    $config = $this->configFactory->get('bootstrap_five_layouts.settings');

    $form['key_options'] = [
      '#type' => 'fieldset',
      '#title' => $this->t('General Options'),
    ];

    $form['key_options']['default_container'] = [
      '#type' => 'select',
      '#title' => $this->t('Default Container'),
      '#options' => $this->bootstrapFiveLayoutsManager->getContainerOptions(),
      '#default_value' => $config->get('default_container'),
    ];

    // context:  'base' is a config translitible item.
    $form['key_options']['xs_label'] = [
      '#type' => 'textfield',
      '#title' => $this->t('X-Small Breakpoint Optgroup Label'),
      '#default_value' => $config->get('xs_label'),
      '#description' => $this->t('<q>@base</q> is the default value.', ['@base' => 'Base']),
      '#size' => 12,
      '#required' => TRUE,
      '#maxlength' => 56,
    ];

    // Get current config values and ensure they are properly typed.
    $container_appearence = $config->get('container_appearence');
    $row_appearence = $config->get('row_appearence');
    $column_appearence = $config->get('column_appearence');
    $spacing_appearence = $config->get('spacing_appearence');

    // Ensure boolean values are properly set.
    $container_appearence = $container_appearence !== NULL ? (bool) $container_appearence : TRUE;
    $row_appearence = $row_appearence !== NULL ? (bool) $row_appearence : TRUE;
    $column_appearence = $column_appearence !== NULL ? (bool) $column_appearence : TRUE;
    $spacing_appearence = $spacing_appearence !== NULL ? (bool) $spacing_appearence : FALSE;

    $form['key_options']['container_appearence'] = [
      '#type' => 'radios',
      '#options' => [
        1 => $this->t('Open'),
        0 => $this->t('Closed'),
      ],
      '#title' => $this->t('Container Details Appearence'),
      '#default_value' => $container_appearence ? 1 : 0,
    ];
    $form['key_options']['row_appearence '] = [
      '#type' => 'radios',
      '#options' => [
        1 => $this->t('Open'),
        0 => $this->t('Closed'),
      ],
      '#title' => $this->t('Row Details Appearence'),
      '#default_value' => $row_appearence  ? 1 : 0,
    ];
    $form['key_options']['column_appearence'] = [
      '#type' => 'radios',
      '#options' => [
        1 => $this->t('Open'),
        0 => $this->t('Closed'),
      ],
      '#title' => $this->t('Column(s) Details Appearence'),
      '#default_value' => $column_appearence ? 1 : 0,
    ];
    $form['key_options']['spacing_appearence'] = [
      '#type' => 'radios',
      '#options' => [
        1 => $this->t('Open'),
        0 => $this->t('Closed'),
      ],
      '#title' => $this->t('Spacing Utilites Details Appearence'),
      '#description' => $this->t("Boostrap's Padding & Margin Classes.  Within Container, Row and Columns the full set of Spacing Utilites may be too distacting for users and reduce screen clutter while setting up a layout."),
      '#description_display' =>'before',
      '#default_value' => $spacing_appearence ? 1 : 0,
    ];

    $form['options'] = [
      '#type' => 'fieldset',
      '#title' => $this->t('Themers Integration'),
    ];
    $form['options']['theme_classes'] = [
      '#type' => 'textarea',
      '#title' => $this->t('Theme Sets'),
      '#default_value' => $config->get('theme_classes'),
      '#description_display' =>'before',
      '#description' =>
      '<ul>' .
      '<li>' . $this->t('Basic Usage:  Use a pipe (|) charater to seperate the <b>class name</b> and <b>title</b> of the item.  <br>Example:  <code>theme--green|Green</code>') . '</li>' .
      '<li>' . $this->t('Enter one set item per Line.') . '</li>' .
      '<li>' . $this->t('Avoid extra white-space on each line.') . '</li>' .
      '<li>' . $this->t('Do not add a period(.) to class name.') . '</li>' .
      '</ul>',
      '#rows' => 12,
    ];

    $form['options']['pillbox_classes'] = [
      '#type' => 'textarea',
      '#title' => $this->t('Custom Clases for UI Pillbox.'),
      '#default_value' => $config->get('pillbox_classes'),
      '#rows' => 12,
    ];
    $description = '<ul>';
    $pillbox_raw = (string) $config->get('pillbox_classes');
    $lines = preg_split('/\r\n|\r|\n/', $pillbox_raw);
    $non_empty_lines = array_filter(array_map('trim', $lines), static function ($line) {
      return $line !== '';
    });
    $total = count($non_empty_lines);
    if ($total > 0) {
      $description .= '<li><b>' . $this->formatPlural(
        $total,
        '@count item currently stored.',
        '@count items currently stored.'
      ) . '</b></li>';
    }
    $description .= '<li>' . $this->t('Items will be filtered for duplicates.') .
        '<li>' . $this->t('Multiples per line will be split into multiple lines.') . '</li>' .
        '<li>' . $this->t('Do not add a period(.) to class name.') . '</li>' .
        '</ul>';
    $form['options']['pillbox_classes']['#description'] = $description;
    $form['options']['pillbox_classes']['#description_display'] = 'before';
    // Attach the pillbox CSS library
    $form['#attached']['library'][] = 'bootstrap_five_layouts/pillbox';

    $form['options']['key_options'] = [
      '#type' => 'details',
      '#title' => $this->t('UI Pillbox Legend'),
      '#open' => FALSE
    ];

    $modPath = $this->getModulePath();
    $image = Url::fromUri('base:' . $modPath . '/images/ui-pillbox-key.png')->toString();
    $form['options']['key_options']['legend'] =[
      '#type' => 'markup',
      '#markup' => '<figure><img src="'. $image .'" alt="' . $this->t('UI Pillbox color samples.') . '" width="205" height="12" align="right"></figure><figcaption>'.
      '<dl class="bootstrap-layout-legend">
        <dt>' . $this->t('Soft Green') . '</dt>
        <dd>' . $this->t('Custom UI Pillbox (from above input).') . '</dd>
        <dt>' . $this->t('Deep Blue') . '</dt>
        <dd>' . $this->t('Special use Bootstrap (example: row).') . '</dd>
        <dt>' . $this->t('Pale Blue') . '</dt>
        <dd>' . $this->t('General Bootstrap classes for layout and submodule <b>Bootstrap5 Layout CSS Loader</b> supported items.  Otherwise;  by a full Bootstrap based theme.') . '</dd>
        <dt>' . $this->t('Red <small>(text with border)</small>') . '</dt>
        <dd>' . $this->t('Restricted Bootstrap classes (example: any container in other uses).') . '</dd>
        <dt>' . $this->t('Light Grey') . '</dt>
        <dd>' . $this->t('Any other unknown, yet valid classname entered.  Could be some class already provided by theme/module, communicate by project team.') . '</dd>
      </dl>'.
      '</figcaption>',
    ];


    // Preflight: Load original config.
    $default_config = $this->getOriginalConfig();

    // Check if current config differs from default config.
    $current_utility_options = $config->get('utility_options');
    $current_base_options = $config->get('base_options');
    $default_utility_options = $default_config['utility_options'] ?? NULL;
    $default_base_options = $default_config['base_options'] ?? NULL;

    $config_modified = FALSE;
    if ($default_config !== NULL) {
      // Use Drupal's DiffArray utility for recursive array comparison.
      $utility_differs = !empty(DiffArray::diffAssocRecursive($current_utility_options ?? [], $default_utility_options ?? []));
      $base_differs = !empty(DiffArray::diffAssocRecursive($current_base_options ?? [], $default_base_options ?? []));
      $config_modified = $utility_differs || $base_differs;
    }

    $form['options']['base-setup'] = [
      '#type' => 'details',
      '#access' => \Drupal::currentUser()->hasPermission('reset bootstrap5 layouts bare options'),
      '#open' => FALSE,
      '#title' => $this->t('Utility & Base Factory Reset'),
      '#description' => $this->t('This does not reset Theme Sets, Custom Classes nor any other General Options.  These are advanced setups for this module, no UI is provided.  Verify your site Configuration Synchronize status if unsure of this tool.'),
    ];
    $message = '<div class="messages messages--status">' .
        $this->t('The active configuration for Utility & Base Options appears to match.') .
        '</div>';
    if ($config_modified) {
      $message = '<div class="messages messages--warning">' .
          $this->t('Notice: The active configuration for Utility & Base Options differs from the factory settings.') .
          '</div>';
      }
    $form['options']['base-setup']['warning'] = [
      '#type' => 'markup',
      '#markup' => $message,
      '#weight' => -10,
    ];

    $form['options']['base-setup']['reset'] = [
      '#type' => 'submit',
      '#weight' => '10',
      '#value' => t('Reset Options'),
      '#submit' => ['::resetClassOptions'],
      '#button_type' => 'danger',
    ];

    $form['description'] = [
      '#type' => 'fieldset',
      '#title' => $this->t('Internal Options'),
    ];
    $form['description']['description_helper_selector'] = [
      '#type' => 'textfield',
      '#required' => TRUE,
      '#title' => $this->t('Description Enhancer Selector'),
      '#default_value' => $config->get('description_helper_selector') ?: '.form-item__description',
      '#field_prefix' => '.bootstrap-five-layouts-settings-tray-admin',
      '#description' =>'<ul><li>'. $this->t("This CSS selector is used for the javascript enhancement for description elements on the Bootstrap Five Layout Settings Tray fields to reduce screen cluttering.").'</li>'.
      '<li>'. $this->t("Different themes may need different selectors or adjustments to the themes templates/preprocessing to ensure a base class of some type.").'</li>'.
      '<li>'. $this->t("The default of <code>.form-item__description</code> is based from the theme Olivero (Drupal/9+ default theme).").'</li></ul>',
      '#size' => 60,
      '#maxlength' => 255,
    ];


    $form['file'] = [
      '#type' => 'fieldset',
      '#title' => $this->t('Layout Background Integration'),
      // Early dev @scope..
      '#access' => FALSE,
    ];
    $form['file']['enable_background'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Enable Background Feature'),
      '#default_value' => $config->get('enable_background') ?? FALSE,
    ];
    $form['file']['background'] = [
      '#type' => 'container',
      '#states' => [
        'visible' => [
          ':input[name="enable_background"]' => ['checked' => TRUE],
        ],
      ],
    ];
    $form['file']['background']['allow_media'] = [
      '#type' => 'radios',
      '#options' => ['media' => $this->t('Media'), 'file' => $this->t('Image/File')],
      '#title' => $this->t('File Type Preference'),
      '#default_value' => $config->get('allow_media'),
      '#field_prefix' => $this->t('Warning:  Avoid changes while in a production enviorment.'),
    ];
    if ($this->moduleHandler->moduleExists('file')) {
      // Get PHP upload_max_filesize and convert to bytes.
      $php_upload_max = ini_get('upload_max_filesize');
      $php_upload_max_bytes = $this->getPhpUploadMaxBytes($php_upload_max);
      $form['file']['background']['max_file_size'] = [
        '#type' => 'textfield',
        '#maxlength' => '56',
        '#size' => '19',
        '#title' => $this->t('Maximum File Size'),
        '#field_suffix' => '<small>' . $this->t('Bytes (@current)', [
          '@current' => $this->formatBytes(($config->get('max_file_size') ?: $php_upload_max_bytes)),
        ]) . '</small>',
        '#min' => 1,
        '#step' => 1,
        '#default_value' => $config->get('max_file_size') ?: $php_upload_max_bytes,
        '#description_display' =>'before',
        '#description' => '<ul>' .
        '<li>' . $this->t('Set the maximum file size (in bytes) for uploads.') . '</li>' .
        '<li>' . $this->t('Auto Convert Helper:  Input will convert formatted size to bytes.') . '</li>' .
        '<li>' . $this->t('Current PHP max: @php_max (@php_max_bytes bytes).', [
          '@php_max' => $php_upload_max,
          '@php_max_bytes' => $php_upload_max_bytes,
        ]) . '</li>' .
        '<li>' . $this->t('Minimum allowed file size is 20 bytes.') . '</li>' .
        '</ul>',
        '#states' => [
          'visible' => [
            ':input[name="allow_media"]' => ['value' => 'file'],
          ],
        ],
      ];
    }
    else {
      $form['file']['background']['allow_media']['#access'] = FALSE;
    }
    $form['file']['background']['allowed_file_types'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Allowed Image File Types'),
      '#maxlength' => '256',
      '#size' => '19',
      '#default_value' => $config->get('allowed_file_types'),
      '#states' => [
        'visible' => [
          ':input[name="allow_media"]' => ['value' => 'file'],
        ],
      ],
    ];
    // in-case media is offline, just carry the values..
    $form['file']['background']['allowed_media_types'] = [
      '#title' => $this->t('Allowed Media Types'),
      '#type' => 'checkboxes',
      '#options' => $config->get('allowed_media_types') ?? ['file'],
      '#default_value' => $config->get('allowed_media_types') ?? ['file'],
      '#states' => [
        'visible' => [
          ':input[name="allow_media"]' => ['value' => 'media'],
        ],
      ],
    ];
    if ($this->moduleHandler->moduleExists('media_library_form_element')) {
      // Load the currently active media types into a checkbox input.
      $media_type_storage = \Drupal::entityTypeManager()->getStorage('media_type');
      $media_types = $media_type_storage->loadMultiple();
      $media_type_options = [];
      foreach ($media_types as $media_type) {
        $media_type_options[$media_type->id()] = $media_type->label();
      }
      $form['file']['background']['allowed_media_types']['#options'] = $media_type_options;
    }
    else {
      unset($form['file']['background']['allow_media']['#options']['media']);
      $link = Link::fromTextAndUrl('Media Library Form Element', Url::fromUri('https://www.drupal.org/project/media_library_form_element', ['attributes' => ['target' => '_blank']]));
      $form['file']['background']['allow_media']['#description'] = $this->t('Module @link is not enabled at this time.', ['@link' => $link->toString()]);
    }

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

public function resetClassOptions($element, &$form_state)  {
  // Preflight: Load original config.
  $default_config = $this->getOriginalConfig();

  if ($default_config === NULL) {
    return;
  }

  if (isset($default_config['utility_options']) && isset($default_config['base_options'])) {
    $config = $this->config('bootstrap_five_layouts.settings');
    $config->set('utility_options', $default_config['utility_options']);
    $config->set('base_options', $default_config['base_options']);
    $config->save();
    $this->messenger()->addStatus($this->t('Utility & Base Option Customization has been reset to original values.'));
  }
  else {
    $this->messenger()->addError($this->t('Could not find utility_options or base_options in default configuration.'));
  }
}

  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state) {
    $description_helper_selector = $form_state->getValue('description_helper_selector');
    //  Cannot contain ID selectors (#).
    if (strpos($description_helper_selector, '#') !== FALSE) {
      $form_state->setErrorByName('description_helper_selector', $this->t('Description helper selector cannot contain ID selectors (#).'));
      return;
    }

    $theme_classes = $form_state->getValue('theme_classes');
    $lines = preg_split('/\r\n|\r|\n/', $theme_classes);
    foreach ($lines as $index => $line) {
      $line = trim($line);
      if ($line === '') {
        // Skip empty lines.
        continue;
      }
      $parts = explode('|', $line);
      if (count($parts) !== 2) {
        $form_state->setErrorByName('theme_classes', $this->t('Error on line @line: <q>@content</q>. Each line must contain exactly one pipe (|).', ['@line' => $index + 1, '@content' => $line]));
        return;
      }
      $left = $parts[0];
      $right = $parts[1];
      if (strlen($left) < 2 || strlen(trim($right)) < 2) {
        $form_state->setErrorByName('theme_classes', $this->t('Error on line @line: <q>@content</q>.  Each segment must be at least 2 characters.', ['@line' => $index + 1, '@content' => $line]));
        return;
      }
      if (preg_match('/\s/', $left)) {
        $form_state->setErrorByName('theme_classes', $this->t('Error on line @line: <q>@content</q>.  No whitespace is allowed in the class name segment.', ['@line' => $index + 1, '@content' => $line]));
        return;
      }
      if (strpos($left, '.') === 0) {
        $form_state->setErrorByName('theme_classes', $this->t('Error on line @line: <q>@content</q>.  The class name segment must not start with a (.) period.', ['@line' => $index + 1, '@content' => $line]));
        return;
      }
      if ($right !== trim($right)) {
        $form_state->setErrorByName('theme_classes', $this->t('Error on line @line: <q>@content</q>.  The title segment cannot start or end with whitespace.', ['@line' => $index + 1, '@content' => $line]));
        return;
      }
    }

    // Validate and normalize pillbox_classes: split multiple classes per line,
    // remove duplicates, trim whitespace, and ensure valid CSS class tokens.
    $pillbox_raw = (string) $form_state->getValue('pillbox_classes');
    if ($pillbox_raw !== '') {
      $pillbox_lines = preg_split('/\r\n|\r|\n/', $pillbox_raw);
      $normalized_tokens = [];
      $seen = [];
      foreach ($pillbox_lines as $line_index => $raw_line) {
        $raw_line = trim($raw_line);
        if ($raw_line === '') {
          continue;
        }
        // Split on any whitespace to allow multiple classes per line.
        $tokens = preg_split('/\s+/', $raw_line);
        foreach ($tokens as $token) {
          $token = trim($token);
          if ($token === '') {
            continue;
          }
          // Reject tokens starting with '.' and any invalid CSS class name chars.
          if (strpos($token, '.') === 0) {
            $form_state->setErrorByName('pillbox_classes', $this->t('Invalid token <q><code>@token</code></q> on line @line: do not prefix class names with a period.', ['@token' => $token, '@line' => $line_index + 1]));
            return;
          }
          // CSS class name pattern (simplified): start with letter/_/-, then letters/digits/_/-.
          if (!preg_match('/^[A-Za-z_-][A-Za-z0-9_-]*$/', $token)) {
            $form_state->setErrorByName('pillbox_classes', $this->t('Invalid class name <q><code>@token</code></q> on line @line.', ['@token' => $token, '@line' => $line_index + 1]));
            return;
          }
          $key = mb_strtolower($token);
          if (!isset($seen[$key])) {
            $seen[$key] = TRUE;
            $normalized_tokens[] = $token;
          }
        }
      }
      // Set normalized value back: one class per line.
      $form_state->setValue('pillbox_classes', implode("\n", $normalized_tokens));
    }

    // Validate max_file_size.
    $max_file_size = $this->convertToBytes($form_state->getValue('max_file_size'));
    if ($max_file_size === FALSE || (!is_numeric($max_file_size) && $max_file_size > 0)) {
      $form_state->setErrorByName('max_file_size', $this->t('Can not convert input'));
    }

    // @convert helper.  Set the converted site back to the input.
    if ($max_file_size !== NULL && is_numeric($max_file_size) && $max_file_size > 0) {
      $form_state->setValue('max_file_size', $max_file_size);
    }

    if ($max_file_size !== NULL && (!is_numeric($max_file_size) || $max_file_size < 1)) {
      $form_state->setErrorByName('max_file_size', $this->t('Maximum file size must be a positive number.'));
    }
    if ($max_file_size !== NULL && (!is_numeric($max_file_size) || $max_file_size < 20)) {
      $form_state->setErrorByName('max_file_size', $this->t('Sorry, the file’s header size would be too small.  The minimum expected file size is 20 bytes&hellip;'));
    }
    elseif ($max_file_size !== NULL) {
      $php_upload_max = $this->convertToBytes(ini_get('upload_max_filesize'));
      if ($max_file_size > $php_upload_max) {
        $form_state->setErrorByName('max_file_size', $this->t('Maximum file size cannot exceed the PHP <code>upload_max_filesize</code> setting (@php_max_bytes bytes).', ['@php_max_bytes' => $php_upload_max]));
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $this->config('bootstrap_five_layouts.settings')
      ->set('default_container', $form_state->getValue('default_container'))
      ->set('container_appearence', (bool) $form_state->getValue('container_appearence'))
      ->set('row_appearence', (bool) $form_state->getValue('row_appearence'))
      ->set('column_appearence', (bool) $form_state->getValue('column_appearence'))
      ->set('spacing_appearence', (bool) $form_state->getValue('spacing_appearence'))
      ->set('theme_classes', $form_state->getValue('theme_classes'))
      ->set('pillbox_classes', $form_state->getValue('pillbox_classes'))
      ->set('xs_label', $form_state->getValue('xs_label'))
      ->set('description_helper_selector', $form_state->getValue('description_helper_selector'))
      ->set('allowed_media_types', array_filter($form_state->getValue('allowed_media_types', [])))
      ->set('max_file_size', $form_state->getValue('max_file_size'))
      ->set('allowed_file_types', $form_state->getValue('allowed_file_types'))
      ->set('allow_media', $form_state->getValue('allow_media'))
      ->set('enable_background', $form_state->getValue('enable_background'))
      ->save();
    parent::submitForm($form, $form_state);
  }

  /**
   * Gets the PHP upload_max_filesize value in bytes.
   *
   * @param string $php_upload_max
   *   The PHP upload_max_filesize value.
   *
   * @return int
   *   The maximum upload file size in bytes.
   */
  private function getPhpUploadMaxBytes($php_upload_max = 0) {
    return Bytes::toNumber($php_upload_max);
  }

  /**
   * Gets the module path using proper dependency injection.
   *
   * @return string
   *   The module path.
   */
  private function getModulePath() {
    return $this->moduleExtensionList->getPath('bootstrap_five_layouts');
  }
  /**
   * Gets the original/default configuration from the install directory.
   *
   * @return array|null
   *   The decoded original configuration array, or NULL if the file cannot be loaded.
   */
  private function getOriginalConfig() {
    $module_path = $this->getModulePath();
    $install_config_file = $module_path . '/config/install/bootstrap_five_layouts.settings.yml';

    if (!file_exists($install_config_file)) {
      $this->messenger()->addError($this->t('Could not load default configuration file from @path.', ['@path' => $install_config_file]));
      return NULL;
    }

    $yaml_content = file_get_contents($install_config_file);
    if ($yaml_content === FALSE) {
      $this->messenger()->addError($this->t('Could not read default configuration file from @path.', ['@path' => $install_config_file]));
      return NULL;
    }

    $default_config = Yaml::decode($yaml_content);
    if ($default_config === NULL) {
      $this->messenger()->addError($this->t('Could not decode default configuration file from @path.', ['@path' => $install_config_file]));
      return NULL;
    }

    return $default_config;
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc