aws_s3_stream_wrapper-1.0.x-dev/src/Form/EditWrapperConfigurationForm.php
src/Form/EditWrapperConfigurationForm.php
<?php
namespace Drupal\aws_s3_stream_wrapper\Form;
use Drupal\Core\DrupalKernel;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Configure the dynamic S3 wrappers.
*/
class EditWrapperConfigurationForm extends ConfigFormBase {
/**
* Prefix when naming config for a particular scheme.
*
* @var string
*/
const CONFIG_KEY_PREFIX = 'aws_s3_stream_wrapper.stream_wrapper';
/**
* Drupal kernel service.
*
* @var \Drupal\Core\DrupalKernel
*/
protected $kernel;
/**
* Constructor.
*
* @param \Drupal\Core\DrupalKernel $kernel
* Drupal kernel.
*/
public function __construct(DrupalKernel $kernel) {
$this->kernel = $kernel;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('kernel')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'aws_s3_stream_wrapper.edit_wrapper_configuration';
}
/**
* {@inheritdoc}
*/
protected function getEditableConfigNames() {
return [];
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, $scheme = NULL) {
if ($scheme) {
$configKey = self::CONFIG_KEY_PREFIX . '.' . $scheme;
$config = $this->config($configKey);
if (empty($config->get())) {
throw new NotFoundHttpException();
}
}
else {
// Generate an empty config property.
$config = $this->config(self::CONFIG_KEY_PREFIX);
}
$form['scheme'] = [
'#type' => 'textfield',
'#title' => $this->t('Scheme'),
'#required' => TRUE,
'#default_value' => $config->get('scheme'),
// Read-only when editing an existing dynamic stream wrapper.
'#disabled' => $config->get('scheme'),
];
if (empty($config->get('scheme'))) {
$form['scheme']['#element_validate'] = ['::schemeValidate'];
}
$form['name'] = [
'#type' => 'textfield',
'#title' => $this->t('Name'),
'#default_value' => $config->get('name'),
];
$form['description'] = [
'#type' => 'textarea',
'#title' => $this->t('Description'),
'#default_value' => $config->get('description'),
];
$form['bucket_name'] = [
'#type' => 'textfield',
'#title' => $this->t('Bucket'),
'#default_value' => $config->get('bucket_name'),
'#element_validate' => ['::bucketNameValidate'],
// S3 bucket names must be between 3 and 63 characters long.
'#max_length' => 63,
];
$form['bucket_path_prefix'] = [
'#type' => 'textfield',
'#title' => $this->t('Bucket path prefix'),
'#default_value' => $config->get('bucket_path_prefix'),
];
$form['visible'] = [
'#type' => 'radios',
'#title' => $this->t('Visibility'),
'#options' => [
1 => $this->t('Visible'),
0 => $this->t('Hidden'),
],
'#description' => $this->t('Select whether this stream wrapper should appear in the Drupal UI.'),
'#default_value' => ($config->get('visible')) ?? 0,
];
$form = parent::buildForm($form, $form_state);
$form['actions']['submit']['#value'] = ($config->get('scheme'))
? $this->t('Save configuration')
: $this->t('Add scheme');
return $form;
}
/**
* Validate that the name of the bucket meets the S3 requirements.
*
* @see https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html
*/
public function bucketNameValidate(array $element, FormStateInterface $form_state) {
$bucketName = $form_state->getValue($element['#name']);
// Bucket name is optional – if it's not defined, no validation needed.
if (empty($bucketName)) {
return;
}
// Rules for this pattern:
// - Begin and end in a letter or number.
// - Length between 3 and 63 characters.
// - Only consist of lowercase letters, numbers, dots (.), and hyphens (-).
$pattern = '/^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/';
if (!preg_match($pattern, $bucketName)) {
$rules = [];
if (strlen($bucketName) < 3 || strlen($bucketName) > 63) {
$rules[] = $this->t('Bucket names must be between 3 and 63 characters long.');
}
if (!preg_match('/^[a-z0-9].*[a-z0-9]$/', $bucketName)) {
$rules[] = $this->t('Bucket names must begin and end in a letter or number.');
}
if (preg_match('/[^a-z0-9.-]/', $bucketName)) {
$rules[] = $this->t('Bucket names must consist only of lowercase letters, numbers, dots (.), and hyphens (-).');
}
$form_state->setError($element, implode("\n", $rules));
}
if (filter_var($bucketName, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4)) {
$form_state->setError($element, $this->t('Bucket names must not be formatted as an IP address (for example, 192.168.5.4).'));
}
if (strpos($bucketName, 'xn--') === 0) {
$form_state->setError($element, $this->t('Bucket name must not begin with "xn--".'));
}
if (preg_match('/-s3alias$/', $bucketName)) {
$form_state->setError($element, $this->t('Bucket name must not end with "-s3alias".'));
}
}
/**
* Validate the scheme is not already defined, or uses a protected value.
*/
public function schemeValidate(array $element, FormStateInterface $form_state) {
$value = $form_state->getValue($element['#name']);
$defined_wrappers = stream_get_wrappers();
$defined_wrappers += [
'temporary',
'public',
'private',
];
if (in_array($value, $defined_wrappers)) {
$form_state->setError($element, $this->t('The selected scheme name is already in use.'));
}
// Validate the stream wrapper naming rules.
// @see https://www.php.net/manual/en/function.stream-wrapper-register.php
// Valid protocol names must contain alphanumerics, dots (.), plusses (+),
// or hyphens (-) only.
$pattern = '/^[a-zA-Z0-9.+=]+$/';
if (!preg_match($pattern, $value)) {
$form_state->setError($element, $this->t('Valid protocol names must contain alphanumerics, dots (.), plusses (+), or hyphens (-) only.'));
}
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$form_state->cleanValues();
$configKey = self::CONFIG_KEY_PREFIX . '.' . $form_state->getValue('scheme');
$config = $this->configFactory->getEditable($configKey);
foreach ($form_state->getValues() as $key => $value) {
if ($value) {
$config->set($key, $value);
}
else {
$config->clear($key);
}
}
$config->save();
// Mark the container for rebuild, to apply the changes in a new compiler
// pass.
$this->kernel->invalidateContainer();
$this->messenger()->addMessage('The stream wrapper configuration has been saved.');
$form_state->setRedirect('aws_s3_stream_wrapper.admin_overview');
}
}
