TurnstileSettingsForm.php (7778B)
1 <?php 2 3 namespace Drupal\taler_turnstile\Form; 4 5 use Drupal\Core\Form\ConfigFormBase; 6 use Drupal\Core\Form\FormStateInterface; 7 use Drupal\Core\Config\ConfigFactoryInterface; 8 use Drupal\Core\Entity\EntityTypeManagerInterface; 9 use Drupal\taler_turnstile\TalerBackendErrorKind; 10 use Drupal\taler_turnstile\TurnstileFieldManager; 11 use Drupal\taler_turnstile\TalerMerchantApiService; 12 use Symfony\Component\DependencyInjection\ContainerInterface; 13 14 /** 15 * Configure GNU Taler Turnstile settings. 16 */ 17 class TurnstileSettingsForm extends ConfigFormBase { 18 19 /** 20 * The entity type manager. 21 * 22 * @var \Drupal\Core\Entity\EntityTypeManagerInterface 23 */ 24 protected $entityTypeManager; 25 26 /** 27 * The Turnstile field manager. 28 * 29 * @var \Drupal\taler_turnstile\TurnstileFieldManager 30 */ 31 protected $fieldManager; 32 33 /** 34 * The Taler Merchant API service. 35 * 36 * @var \Drupal\taler_turnstile\TalerMerchantApiService 37 */ 38 protected $apiService; 39 40 /** 41 * Constructs a TurnstileSettingsForm object. 42 * 43 * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory 44 * The factory for configuration objects. 45 * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager 46 * The entity type manager. 47 * @param \Drupal\taler_turnstile\TurnstileFieldManager $field_manager 48 * The field manager. 49 * @param \Drupal\taler_turnstile\TalerMerchantApiService $api_service 50 * The API service. 51 */ 52 public function __construct(ConfigFactoryInterface $config_factory, EntityTypeManagerInterface $entity_type_manager, TurnstileFieldManager $field_manager, TalerMerchantApiService $api_service) { 53 parent::__construct($config_factory); 54 $this->entityTypeManager = $entity_type_manager; 55 $this->fieldManager = $field_manager; 56 $this->apiService = $api_service; 57 } 58 59 /** 60 * {@inheritdoc} 61 */ 62 public static function create(ContainerInterface $container) { 63 return new static( 64 $container->get('config.factory'), 65 $container->get('entity_type.manager'), 66 $container->get('taler_turnstile.field_manager'), 67 $container->get('taler_turnstile.api_service') 68 ); 69 } 70 71 /** 72 * {@inheritdoc} 73 */ 74 protected function getEditableConfigNames() { 75 return ['taler_turnstile.settings']; 76 } 77 78 /** 79 * {@inheritdoc} 80 */ 81 public function getFormId() { 82 return 'taler_turnstile_settings_form'; 83 } 84 85 /** 86 * {@inheritdoc} 87 */ 88 public function buildForm(array $form, FormStateInterface $form_state) { 89 $config = $this->config('taler_turnstile.settings'); 90 91 // Get all available content types. 92 $content_types = $this->entityTypeManager->getStorage('node_type')->loadMultiple(); 93 $type_options = []; 94 foreach ($content_types as $type) { 95 $type_options[$type->id()] = $type->label(); 96 } 97 98 $form['enabled_content_types'] = [ 99 '#type' => 'checkboxes', 100 '#title' => $this->t('Enabled content types'), 101 '#description' => $this->t('Select which content types should have the price field and be subject to paywall transformation.'), 102 '#options' => $type_options, 103 '#default_value' => $config->get('enabled_content_types') ?: [], 104 ]; 105 106 $form['payment_backend_url'] = [ 107 '#type' => 'url', 108 '#title' => $this->t('Payment backend URL'), 109 '#description' => $this->t('HTTP URL for the payment backend service.'), 110 '#default_value' => $config->get('payment_backend_url') ?: '', 111 '#maxlength' => 255, 112 ]; 113 114 $form['access_token'] = [ 115 '#type' => 'textfield', 116 '#title' => $this->t('Access token'), 117 '#description' => $this->t('Access token for authenticating with the payment backend.'), 118 '#default_value' => $config->get('access_token') ?: '', 119 '#maxlength' => 255, 120 ]; 121 122 $form['grant_access_on_error'] = [ 123 '#type' => 'checkbox', 124 '#title' => $this->t('Disable Turnstile when payment backend is unavailable'), 125 '#description' => $this->t('Allows users gratis access when Turnstile is unable to communicate with the GNU Taler merchant backend. Use this setting to avoid exposing users to configuration errors.'), 126 '#default_value' => $config->get('grant_access_on_error') ?: '', 127 ]; 128 129 return parent::buildForm($form, $form_state); 130 } 131 132 133 /** 134 * {@inheritdoc} 135 */ 136 public function validateForm(array &$form, FormStateInterface $form_state) { 137 parent::validateForm($form, $form_state); 138 $payment_backend_url = $form_state->getValue('payment_backend_url'); 139 $access_token = $form_state->getValue('access_token'); 140 141 // Validate the URL only if one was actually supplied; leaving 142 // the backend unconfigured is an intentional path (covered by 143 // the "warn but don't block" branch at the bottom). 144 if (!empty($payment_backend_url)) { 145 if (!str_ends_with($payment_backend_url, '/')) { 146 $form_state->setErrorByName('payment_backend_url', 147 $this->t('Payment backend URL must end with a "/".')); 148 return; 149 } 150 $cfg_result = $this->apiService->checkConfig($payment_backend_url); 151 if (!$cfg_result->isOk()) { 152 $form_state->setErrorByName( 153 'payment_backend_url', 154 $cfg_result->toUserMessage($payment_backend_url) 155 ); 156 return; 157 } 158 } 159 160 if ( (!empty($access_token)) && 161 (!str_starts_with($access_token, 'secret-token:')) ) { 162 $form_state->setErrorByName('access_token', 163 $this->t('Access token must begin with a "secret-token:".')); 164 return; 165 } 166 167 // Only verify the access token when both fields are present; 168 // otherwise we hit the warn-but-don't-block branch below. 169 // AUTH_FAILED is attached to the access_token field; every other 170 // failure (unreachable, 5xx, unknown instance, ...) is attached 171 // to the URL field. 172 if (!empty($payment_backend_url) && !empty($access_token)) { 173 $access_result = $this->apiService->checkAccess($payment_backend_url, $access_token); 174 if (!$access_result->isOk()) { 175 $field = ($access_result->kind === TalerBackendErrorKind::AUTH_FAILED) 176 ? 'access_token' 177 : 'payment_backend_url'; 178 $form_state->setErrorByName( 179 $field, 180 $access_result->toUserMessage($payment_backend_url) 181 ); 182 return; 183 } 184 } 185 186 // If the merchant backend is not configured at all, we allow the user 187 // to save the settings. But, we warn them if they did not set 188 // grant_access_on_error. 189 $grant_access_on_error = $form_state->getValue('grant_access_on_error'); 190 if ( (! $grant_access_on_error) && 191 (empty($payment_backend_url) || 192 empty($access_token)) ) { 193 $this->messenger()->addWarning( 194 $this->t('Warning: Merchant backend is not configured correctly. To keep the site working, you probably should set the "Disable Turnstile when payment backend is unavailable" option!')); 195 return; 196 } 197 } 198 199 /** 200 * {@inheritdoc} 201 */ 202 public function submitForm(array &$form, FormStateInterface $form_state) { 203 $config = $this->config('taler_turnstile.settings'); 204 205 $payment_backend_url = $form_state->getValue('payment_backend_url'); 206 $access_token = $form_state->getValue('access_token'); 207 $new_enabled_types = array_values(array_filter($form_state->getValue('enabled_content_types'))); 208 $grant_access_on_error = $form_state->getValue('grant_access_on_error'); 209 210 // Save configuration. 211 $config->set('enabled_content_types', $new_enabled_types); 212 $config->set('payment_backend_url', $payment_backend_url); 213 $config->set('access_token', $access_token); 214 $config->set('grant_access_on_error', $grant_access_on_error); 215 $config->save(); 216 217 parent::submitForm($form, $form_state); 218 } 219 }