dynamic_image_generator-1.0.x-dev/src/Form/DynamicImageGeneratorSettingsForm.php
src/Form/DynamicImageGeneratorSettingsForm.php
<?php
// File: src/Form/DynamicImageGeneratorSettingsForm.php
namespace Drupal\dynamic_image_generator\Form;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use GuzzleHttp\ClientInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
/**
* Configure Dynamic Image Generator API settings.
*
* This form handles API configuration, different from ImageTemplateSettingsForm
* which handles template entity settings.
*/
class DynamicImageGeneratorSettingsForm extends ConfigFormBase {
/**
* The HTTP client.
*
* @var \GuzzleHttp\ClientInterface
*/
protected $httpClient;
/**
* The messenger service.
*
* @var \Drupal\Core\Messenger\MessengerInterface
*/
protected $messenger;
/**
* The logger service.
*
* @var \Psr\Log\LoggerInterface
*/
protected $logger;
/**
* Constructs a DynamicImageGeneratorSettingsForm object.
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The factory for configuration objects.
* @param \GuzzleHttp\ClientInterface $http_client
* The HTTP client.
* @param \Drupal\Core\Messenger\MessengerInterface $messenger
* The messenger service.
* @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
* The logger factory service.
*/
public function __construct(ConfigFactoryInterface $config_factory, ClientInterface $http_client, MessengerInterface $messenger, LoggerChannelFactoryInterface $logger_factory) {
parent::__construct($config_factory);
$this->httpClient = $http_client;
$this->messenger = $messenger;
$this->logger = $logger_factory->get('dynamic_image_generator');
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('config.factory'),
$container->get('http_client'),
$container->get('messenger'),
$container->get('logger.factory')
);
}
/**
* {@inheritdoc}
*/
protected function getEditableConfigNames() {
return ['dynamic_image_generator.settings'];
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'dynamic_image_generator_api_settings_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$config = $this->config('dynamic_image_generator.settings');
// Page header
$form['header'] = [
'#type' => 'markup',
'#markup' => '<div class="image-settings-header">
<h2>Dynamic Image Generator API Settings</h2>
<p>Configure your image generation API credentials and settings.</p>
</div>',
];
// API Configuration Section
$form['api_settings'] = [
'#type' => 'details',
'#title' => $this->t('API Configuration'),
'#open' => TRUE,
'#description' => $this->t('Configure your HTML/CSS to Image API credentials. Get your credentials from <a href="@url" target="_blank">htmlcsstoimage.com dashboard</a>.', [
'@url' => 'https://htmlcsstoimage.com/dashboard',
]),
];
// API provider selection - only show built-in if module is available
$api_provider_options = [
'htmlcsstoimage' => $this->t('External API (htmlcsstoimage.com)'),
];
// Only add built-in option if the image_creating_engine module is enabled
if (\Drupal::moduleHandler()->moduleExists('image_creating_engine')) {
$api_provider_options['inbuilt'] = $this->t('Built-in Image Engine (Local)');
}
$form['api_settings']['api_provider'] = [
'#type' => 'radios',
'#title' => $this->t('Image Generation Provider'),
'#description' => $this->t('Choose how images should be generated.'),
'#options' => $api_provider_options,
'#default_value' => $config->get('api_provider') ?: 'htmlcsstoimage',
'#required' => TRUE,
'#ajax' => [
'callback' => [$this, 'updateApiFieldsCallback'],
'wrapper' => 'api-credentials-wrapper',
'event' => 'change',
],
];
// Get the currently selected API provider
$selected_provider = $form_state->getValue('api_provider') ?: $config->get('api_provider') ?: 'htmlcsstoimage';
// Wrap API credential fields in a container for AJAX updating
$form['api_settings']['credentials_wrapper'] = [
'#type' => 'container',
'#attributes' => ['id' => 'api-credentials-wrapper'],
];
// Change required state based on selected provider
$api_fields_required = ($selected_provider === 'htmlcsstoimage');
$form['api_settings']['credentials_wrapper']['api_user_id'] = [
'#type' => 'textfield',
'#title' => $this->t('API User ID'),
'#description' => $this->t('Your API User ID from htmlcsstoimage.com dashboard.'),
'#default_value' => $config->get('api_user_id'),
'#required' => $api_fields_required,
'#size' => 60,
'#maxlength' => 255,
'#placeholder' => 'e.g., sadd-c7b0-4daf-a46b-sadasdade',
'#states' => [
'visible' => [
':input[name="api_provider"]' => ['value' => 'htmlcsstoimage'],
],
'required' => [
':input[name="api_provider"]' => ['value' => 'htmlcsstoimage'],
],
],
];
$form['api_settings']['credentials_wrapper']['api_key'] = [
'#type' => 'password',
'#title' => $this->t('API Key'),
'#description' => $this->t('Your API Key from htmlcsstoimage.com dashboard. Leave blank to keep current key.'),
'#size' => 60,
'#maxlength' => 255,
'#placeholder' => 'Enter your API key here...',
'#states' => [
'visible' => [
':input[name="api_provider"]' => ['value' => 'htmlcsstoimage'],
],
],
];
// Show current API key status
if ($config->get('api_key')) {
$form['api_settings']['credentials_wrapper']['current_key_status'] = [
'#type' => 'markup',
'#markup' => '<div class="messages messages--status">
<strong>API Key Status:</strong> Currently configured (last 8 characters: ••••••••' . substr($config->get('api_key'), -8) . ')
<br><small>Enter a new key above to update it, or leave blank to keep the current key.</small>
</div>',
'#states' => [
'visible' => [
':input[name="api_provider"]' => ['value' => 'htmlcsstoimage'],
],
],
];
}
$form['api_settings']['credentials_wrapper']['api_endpoint'] = [
'#type' => 'url',
'#title' => $this->t('API Endpoint'),
'#description' => $this->t('The API endpoint URL. Usually you don\'t need to change this.'),
'#default_value' => $config->get('api_endpoint') ?: 'https://hcti.io/v1/image',
'#required' => $api_fields_required,
'#size' => 80,
'#states' => [
'visible' => [
':input[name="api_provider"]' => ['value' => 'htmlcsstoimage'],
],
'required' => [
':input[name="api_provider"]' => ['value' => 'htmlcsstoimage'],
],
],
];
// Add a message explaining the built-in engine when selected
$form['api_settings']['credentials_wrapper']['inbuilt_info'] = [
'#type' => 'markup',
'#markup' => '<div class="messages messages--info">
<strong>Built-in Engine:</strong> This option uses wkhtmltoimage to generate images locally without requiring external API credentials.
<br><small>Note: The built-in engine provides fast, reliable HTML/CSS rendering directly on your server. Requires wkhtmltopdf package (sudo apt-get install wkhtmltopdf).</small>
</div>',
'#states' => [
'visible' => [
':input[name="api_provider"]' => ['value' => 'inbuilt'],
],
],
];
// Add a test button for each provider
$form['api_settings']['credentials_wrapper']['test_container'] = [
'#type' => 'container',
'#attributes' => ['class' => ['api-test-container']],
];
// For htmlcsstoimage provider
$form['api_settings']['credentials_wrapper']['test_container']['test_htmlcsstoimage'] = [
'#type' => 'button',
'#value' => $this->t('Test API Connection'),
'#ajax' => [
'callback' => [$this, 'testApiConnectionCallback'],
'wrapper' => 'api-test-result',
'progress' => ['type' => 'throbber', 'message' => 'Testing connection...'],
],
'#states' => [
'visible' => [
':input[name="api_provider"]' => ['value' => 'htmlcsstoimage'],
],
],
];
// For inbuilt provider - fix the button so it doesn't disappear
$form['api_settings']['credentials_wrapper']['test_container']['test_inbuilt'] = [
'#type' => 'button',
'#value' => $this->t('Test Built-in Generator'),
'#ajax' => [
'callback' => [$this, 'testInbuiltGeneratorCallback'],
'wrapper' => 'api-test-result',
'progress' => ['type' => 'throbber', 'message' => 'Testing built-in generator...'],
],
'#states' => [
'visible' => [
':input[name="api_provider"]' => ['value' => 'inbuilt'],
],
],
];
// Container for test results
$form['api_settings']['credentials_wrapper']['test_result'] = [
'#type' => 'container',
'#attributes' => ['id' => 'api-test-result'],
];
/*
// Image Generation Settings
$form['generation_settings'] = [
'#type' => 'details',
'#title' => $this->t('Image Generation Settings'),
'#open' => TRUE,
'#description' => $this->t('Configure default settings for generated images.'),
];
$form['generation_settings']['default_width'] = [
'#type' => 'number',
'#title' => $this->t('Default Width'),
'#description' => $this->t('Default width for generated images in pixels.'),
'#default_value' => $config->get('default_width') ?: 1200,
'#required' => TRUE,
'#min' => 100,
'#max' => 3000,
'#step' => 1,
];
$form['generation_settings']['default_height'] = [
'#type' => 'number',
'#title' => $this->t('Default Height'),
'#description' => $this->t('Default height for generated images in pixels.'),
'#default_value' => $config->get('default_height') ?: 630,
'#required' => TRUE,
'#min' => 100,
'#max' => 3000,
'#step' => 1,
];
$form['generation_settings']['image_format'] = [
'#type' => 'select',
'#title' => $this->t('Image Format'),
'#description' => $this->t('The default image format for generated images.'),
'#options' => [
'png' => $this->t('PNG (better quality, larger file size)'),
'jpeg' => $this->t('JPEG (smaller file size, lossy compression)'),
],
'#default_value' => $config->get('image_format') ?: 'png',
'#required' => TRUE,
];
$form['generation_settings']['quality'] = [
'#type' => 'number',
'#title' => $this->t('Image Quality'),
'#description' => $this->t('Quality setting for JPEG images (0-100). Higher values mean better quality but larger file size.'),
'#default_value' => $config->get('quality') ?: 90,
'#required' => TRUE,
'#min' => 10,
'#max' => 100,
'#step' => 1,
'#states' => [
'visible' => [
':input[name="image_format"]' => ['value' => 'jpeg'],
],
],
];
*/
// Advanced Settings
$form['advanced_settings'] = [
'#type' => 'details',
'#title' => $this->t('Advanced Settings'),
'#open' => FALSE,
];
$form['advanced_settings']['debug_mode'] = [
'#type' => 'checkbox',
'#title' => $this->t('Enable Debug Mode'),
'#description' => $this->t('When enabled, additional debug information will be logged during image generation.'),
'#default_value' => $config->get('debug_mode') ?: FALSE,
];
// Add submit button
$form['actions'] = [
'#type' => 'actions',
];
$form['actions']['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Save Settings'),
'#button_type' => 'primary',
];
return parent::buildForm($form, $form_state);
}
/**
* Ajax callback to update API credential fields.
*/
public function updateApiFieldsCallback(array &$form, FormStateInterface $form_state) {
// Get the selected provider from form state
$selected_provider = $form_state->getValue('api_provider');
// Manually set access based on provider selection instead of relying on #states
if ($selected_provider === 'htmlcsstoimage') {
$form['api_settings']['credentials_wrapper']['api_user_id']['#access'] = TRUE;
$form['api_settings']['credentials_wrapper']['api_key']['#access'] = TRUE;
$form['api_settings']['credentials_wrapper']['api_endpoint']['#access'] = TRUE;
if (isset($form['api_settings']['credentials_wrapper']['current_key_status'])) {
$form['api_settings']['credentials_wrapper']['current_key_status']['#access'] = TRUE;
}
$form['api_settings']['credentials_wrapper']['inbuilt_info']['#access'] = FALSE;
$form['api_settings']['credentials_wrapper']['test_container']['test_htmlcsstoimage']['#access'] = TRUE;
$form['api_settings']['credentials_wrapper']['test_container']['test_inbuilt']['#access'] = FALSE;
} else {
$form['api_settings']['credentials_wrapper']['api_user_id']['#access'] = FALSE;
$form['api_settings']['credentials_wrapper']['api_key']['#access'] = FALSE;
$form['api_settings']['credentials_wrapper']['api_endpoint']['#access'] = FALSE;
if (isset($form['api_settings']['credentials_wrapper']['current_key_status'])) {
$form['api_settings']['credentials_wrapper']['current_key_status']['#access'] = FALSE;
}
$form['api_settings']['credentials_wrapper']['inbuilt_info']['#access'] = TRUE;
$form['api_settings']['credentials_wrapper']['test_container']['test_htmlcsstoimage']['#access'] = FALSE;
$form['api_settings']['credentials_wrapper']['test_container']['test_inbuilt']['#access'] = TRUE;
}
return $form['api_settings']['credentials_wrapper'];
}
/**
* Ajax callback to test API connection.
*/
public function testApiConnectionCallback(array &$form, FormStateInterface $form_state) {
$user_id = $form_state->getValue('api_user_id');
$api_key = $form_state->getValue('api_key') ?: $this->config('dynamic_image_generator.settings')->get('api_key');
$endpoint = $form_state->getValue('api_endpoint');
$response = ['#markup' => ''];
if (empty($user_id) || empty($api_key) || empty($endpoint)) {
$response['#markup'] = '<div class="messages messages--error">Please provide API credentials before testing.</div>';
return $response;
}
try {
$test_data = [
'html' => '<div style="width: 300px; height: 150px; background: #007bff; color: white; display: flex; align-items: center; justify-content: center; font-family: Arial; font-size: 24px; font-weight: bold;">API Test Success</div>',
'css' => 'body { margin: 0; padding: 20px; }',
'width' => 340,
'height' => 190,
];
$api_response = $this->httpClient->request('POST', $endpoint, [
'form_params' => $test_data,
'auth' => [$user_id, $api_key],
'timeout' => 30,
]);
if ($api_response->getStatusCode() === 200) {
$result = json_decode($api_response->getBody(), TRUE);
if (!empty($result['url'])) {
$response['#markup'] = '<div class="messages messages--status">
<strong>API Connection Successful!</strong><br>
<img src="' . $result['url'] . '" alt="Test image" style="max-width: 100%; margin-top: 10px; border: 1px solid #ddd; border-radius: 4px;">
</div>';
} else {
$response['#markup'] = '<div class="messages messages--warning">
<strong>Partial Success:</strong> Connected to API but no image URL was returned.
</div>';
}
} else {
$response['#markup'] = '<div class="messages messages--error">
<strong>API Connection Failed:</strong> Unexpected response code: ' . $api_response->getStatusCode() . '
</div>';
}
} catch (\Exception $e) {
$response['#markup'] = '<div class="messages messages--error">
<strong>API Connection Failed:</strong> ' . $e->getMessage() . '
</div>';
}
return $response;
}
/**
* Ajax callback to test the built-in generator.
*/
public function testInbuiltGeneratorCallback(array &$form, FormStateInterface $form_state) {
$response = ['#markup' => ''];
try {
// Check if we have the inbuilt service
if (!\Drupal::hasService('image_creating_engine.generator')) {
// Change the error message to be more helpful
$response['#markup'] = '<div class="messages messages--error">
<strong>Built-in Generator Error:</strong> The image_creating_engine module is not enabled.
Please enable it first by going to Extend page and enabling "Image Creating Engine" module.
</div>';
return $response;
}
// Get the inbuilt generator service
$generator = \Drupal::service('image_creating_engine.generator');
// Simple test HTML and CSS
$test_html = '<div class="test-image">Built-in Generator Works!</div>';
$test_css = 'body { margin: 0; padding: 20px; background: #f5f5f5; }
.test-image {
width: 300px;
height: 150px;
background: #28a745;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-family: Arial, sans-serif;
font-size: 20px;
font-weight: bold;
border-radius: 8px;
}';
$options = [
'width' => 340,
'height' => 190,
'format' => 'png',
];
// Generate a test image
$result = $generator->generateImage($test_html, $test_css, $options);
$image_url = $result['url'];
if ($image_url) {
$response['#markup'] = '<div class="messages messages--status">
<strong>Built-in Generator is Working!</strong><br>
<img src="' . $image_url . '" alt="Test image" style="max-width: 100%; margin-top: 10px; border: 1px solid #ddd; border-radius: 4px;">
</div>';
} else {
$response['#markup'] = '<div class="messages messages--error">
<strong>Built-in Generator Failed:</strong> Could not generate test image. Check logs for details.
</div>';
}
} catch (\Exception $e) {
$response['#markup'] = '<div class="messages messages--error">
<strong>Built-in Generator Error:</strong> ' . $e->getMessage() . '
</div>';
}
return $response;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
$values = $form_state->getValues();
$api_provider = $values['api_provider'];
// Only validate API credentials if using external API
if ($api_provider === 'htmlcsstoimage') {
// Check if API User ID is provided
if (empty($values['api_user_id'])) {
$form_state->setErrorByName('api_user_id', $this->t('API User ID is required when using the external API.'));
}
// Check if we need a new API key
$config = $this->config('dynamic_image_generator.settings');
$current_api_key = $config->get('api_key');
// If API key field is empty and we don't have an existing key, show error
if (empty($values['api_key']) && empty($current_api_key)) {
$form_state->setErrorByName('api_key', $this->t('API Key is required when using the external API.'));
}
// Validate API endpoint URL
if (empty($values['api_endpoint'])) {
$form_state->setErrorByName('api_endpoint', $this->t('API Endpoint URL is required when using the external API.'));
}
}
// Validate other form fields regardless of API provider
parent::validateForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$config = $this->config('dynamic_image_generator.settings');
$values = $form_state->getValues();
// Save API provider selection
$config->set('api_provider', $values['api_provider']);
// Only save API credentials if using external API
if ($values['api_provider'] === 'htmlcsstoimage') {
$config->set('api_user_id', $values['api_user_id']);
// Only update API key if a new one was provided
if (!empty($values['api_key'])) {
$config->set('api_key', $values['api_key']);
}
$config->set('api_endpoint', $values['api_endpoint']);
}
// Remove the commented out generation settings from being saved
// since they're commented out in the form
// $config->set('default_width', $values['default_width']);
// $config->set('default_height', $values['default_height']);
// $config->set('image_format', $values['image_format']);
// $config->set('quality', $values['quality']);
$config->set('debug_mode', !empty($values['debug_mode']));
$config->save();
$this->messenger->addMessage($this->t('Dynamic Image Generator settings have been saved.'));
}
}