dynamic_image_generator-1.0.x-dev/src/Controller/DynamicImageGeneratorController.php
src/Controller/DynamicImageGeneratorController.php
<?php
namespace Drupal\dynamic_image_generator\Controller;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\HtmlCommand;
use Drupal\Core\Ajax\OpenModalDialogCommand;
use Drupal\node\Entity\Node;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
/**
* Controller for Dynamic Image Generator operations.
*/
class DynamicImageGeneratorController extends ControllerBase {
/**
* Generate preview with current form data (real-time values).
*/
public function previewWithCurrentData($poster_entity, $node, Request $request) {
$response = new AjaxResponse();
try {
// Load the poster entity properly
if (is_numeric($poster_entity)) {
$poster_entity = \Drupal::entityTypeManager()->getStorage('poster_entity')->load($poster_entity);
}
if (!$poster_entity) {
throw new \Exception('Image template not found');
}
$service = \Drupal::service('dynamic_image_generator.dynamic_image_generator_service');
// Get form data from request - CHECK BOTH GET AND POST
$form_data_json = $request->query->get('form_data') ?: $request->request->get('form_data');
$form_data = $form_data_json ? json_decode($form_data_json, TRUE) : [];
// Check if this is a live preview with template changes
$is_live_preview = isset($form_data['preview_mode']) && $form_data['preview_mode'] === 'live';
$is_live_trigger = isset($form_data['trigger_type']) && $form_data['trigger_type'] === 'live_preview_button';
$is_node_form_preview = isset($form_data['preview_mode']) && $form_data['preview_mode'] === 'node_form';
$is_node_form_trigger = isset($form_data['trigger_type']) && $form_data['trigger_type'] === 'node_form_preview';
// Only process live preview requests or node form preview requests
if (!($is_live_preview && $is_live_trigger) && !($is_node_form_preview && $is_node_form_trigger)) {
// Return empty response to prevent unwanted popups
return $response;
}
// For node form previews, use the current node ID from form data or URL parameter
if ($is_node_form_preview && isset($form_data['current_node_id'])) {
$selected_node_id = $form_data['current_node_id'];
$detection_method = 'node_form_current';
} elseif ($is_node_form_preview && $node !== 'new' && is_numeric($node)) {
$selected_node_id = $node;
$detection_method = 'node_form_url';
} else {
// Check for selected_node in form data (live preview)
if (isset($form_data['selected_node']) && !empty($form_data['selected_node'])) {
$autocomplete_value = $form_data['selected_node'];
// Extract node ID from autocomplete value (format: "Title (123)")
if (preg_match('/\((\d+)\)$/', $autocomplete_value, $matches)) {
$selected_node_id = $matches[1];
$detection_method = 'form_data_selected_node';
}
}
}
// Load the node entity if we found an ID
if ($selected_node_id) {
$entity_id = $selected_node_id;
$node_entity = \Drupal::entityTypeManager()->getStorage('node')->load($entity_id);
}
// Prepare custom tokens
$custom_tokens = [];
if ($node_entity) {
// Use the actual node's data for tokens
$custom_tokens = $this->getNodeTokens($node_entity);
} else {
// No specific node selected, use sample data
$custom_tokens = [
'title' => 'Sample Title',
'node:title' => 'Sample Title',
'body' => 'This is sample body content for the preview.',
'node:body' => 'This is sample body content for the preview.',
];
}
// Always add these default tokens
$custom_tokens['site_name'] = \Drupal::config('system.site')->get('name');
$custom_tokens['site:name'] = \Drupal::config('system.site')->get('name');
$custom_tokens['date'] = \Drupal::service('date.formatter')->format(time(), 'medium');
// If this is a live preview, add custom HTML/CSS from form
if ($is_live_preview && isset($form_data['template_html']) && isset($form_data['template_css'])) {
$custom_tokens['_custom_html'] = $form_data['template_html'];
$custom_tokens['_custom_css'] = $form_data['template_css'];
}
// Generate preview image
if ($entity_id && $node_entity) {
$image_url = $service->generatePosterImage($poster_entity->id(), $custom_tokens, $entity_id);
$preview_type = 'with selected content (' . $node_entity->getTitle() . ')';
} else {
$image_url = $service->generatePreviewImage($poster_entity->id(), $custom_tokens);
$preview_type = 'with sample data';
}
if ($is_live_preview) {
$preview_type .= ' + current template changes';
} elseif ($is_node_form_preview) {
$preview_type .= ' + current form data';
}
if ($image_url) {
$content = [
'#theme' => 'container',
'#attributes' => ['class' => ['dynamic-image-preview']],
'#children' => [
'header' => [
'#markup' => '<div style="background: #f8f9fa; padding: 15px; margin-bottom: 20px; border-radius: 4px; border-left: 4px solid #007bff;">
<h4 style="margin: 0 0 5px 0; color: #2c3e50;">Preview: ' . $poster_entity->label() . '</h4>
<p style="margin: 0; color: #6c757d; font-size: 0.9em;">Generated ' . $preview_type . '. No image is saved during preview.</p>
</div>',
],
'image' => [
'#theme' => 'image',
'#uri' => $image_url,
'#alt' => $this->t('Preview of @name', ['@name' => $poster_entity->label()]),
'#attributes' => [
'style' => 'max-width: 600px; max-height: 400px; width: auto; height: auto; border: 1px solid #ddd; border-radius: 4px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); display: block; margin: 0 auto;',
'class' => ['generated-preview-image'],
],
],
'image_info' => [
'#markup' => '<div style="text-align: center; margin-top: 15px; padding: 10px; background: #f0f8ff; border-radius: 4px;">
<p style="margin: 0; color: #666; font-size: 0.85em;">
<strong>Full Size:</strong> <a href="' . $image_url . '" target="_blank" style="color: #007bff; text-decoration: none;">View Original Size</a> |
<a href="' . $image_url . '" download style="color: #007bff; text-decoration: none;">Download Image</a>
</p>
</div>',
],
],
];
$title = $entity_id ?
$this->t('LIVE: @name (using selected content)', ['@name' => $poster_entity->label()]) :
$this->t('LIVE: @name (sample data)', ['@name' => $poster_entity->label()]);
// Render the content properly to HTML string
$renderer = \Drupal::service('renderer');
$rendered_content = $renderer->render($content);
} else {
$rendered_content = '<div class="messages messages--error" style="margin: 20px 0;">' .
$this->t('Failed to generate preview. Check logs for details.') .
'</div>';
$title = $this->t('Preview Error');
}
// Generate the single modal response with proper content - reduced modal size
$response->addCommand(new OpenModalDialogCommand($title, $rendered_content, [
'width' => 700,
'height' => 600,
'resizable' => true,
'draggable' => true,
]));
} catch (\Exception $e) {
// Handle errors for both live preview and node form preview
if ((isset($form_data['trigger_type']) && $form_data['trigger_type'] === 'live_preview_button') ||
(isset($form_data['trigger_type']) && $form_data['trigger_type'] === 'node_form_preview')) {
$content = [
'#markup' => '<div class="messages messages--error">' .
$this->t('Error generating preview: @error', ['@error' => $e->getMessage()]) .
'</div>',
];
$response->addCommand(new OpenModalDialogCommand($this->t('Preview Error'), $content));
}
// For other requests, just return empty response
}
return $response;
}
/**
* Extract tokens from form data.
*/
protected function extractTokensFromFormData(array $form_data, $content_type) {
$tokens = [];
foreach ($form_data as $field_name => $value) {
// Clean field name (remove array notation)
$clean_name = preg_replace('/\[.*?\]/', '', $field_name);
// Skip system fields
if (in_array($clean_name, ['form_build_id', 'form_token', 'form_id', 'op', 'preview_mode'])) {
continue;
}
// Handle HTML and CSS content from live preview
if ($clean_name === 'template_html') {
$tokens['_custom_html'] = $value;
continue;
}
if ($clean_name === 'template_css') {
$tokens['_custom_css'] = $value;
continue;
}
// Convert field names to token format
if (strpos($clean_name, 'field_') === 0 || in_array($clean_name, ['title', 'body'])) {
$token_name = str_replace('field_', '', $clean_name);
$tokens[$token_name] = is_array($value) ? implode(', ', $value) : $value;
// Also add with node: prefix for compatibility
$tokens['node:' . $clean_name] = $tokens[$token_name];
}
}
return $tokens;
}
/**
* Get tokens from a node entity.
*/
protected function getNodeTokens($node) {
$tokens = [];
// Basic node info
$tokens['title'] = $node->getTitle();
$tokens['node:title'] = $node->getTitle();
$tokens['nid'] = $node->id();
$tokens['node:nid'] = $node->id();
// Get body field if it exists
if ($node->hasField('body') && !$node->get('body')->isEmpty()) {
$body_value = $node->get('body')->first();
if ($body_value) {
$body_text = strip_tags($body_value->value);
$tokens['body'] = $body_text;
$tokens['node:body'] = $body_text;
$tokens['node:body:value'] = $body_text;
// Add summary if available
if (!empty($body_value->summary)) {
$tokens['body:summary'] = strip_tags($body_value->summary);
$tokens['node:body:summary'] = strip_tags($body_value->summary);
}
}
}
// Get all custom field values
$field_count = 0;
// Define known base fields to skip
$base_fields = [
'nid', 'uuid', 'vid', 'langcode', 'type', 'revision_timestamp',
'revision_uid', 'revision_log', 'status', 'uid', 'title', 'created',
'changed', 'promote', 'sticky', 'default_langcode', 'revision_default',
'revision_translation_affected', 'path', 'body', 'comment'
];
foreach ($node->getFields() as $field_name => $field) {
// Skip if field is empty
if ($field->isEmpty()) {
continue;
}
// Skip known base fields
if (in_array($field_name, $base_fields)) {
continue;
}
// Skip fields that don't start with 'field_' (likely base fields)
if (strpos($field_name, 'field_') !== 0) {
continue;
}
$field_value = $field->first();
if ($field_value) {
$clean_field_name = str_replace('field_', '', $field_name);
$field_type = $field->getFieldDefinition()->getType();
// Handle different field types
switch ($field_type) {
case 'string':
case 'string_long':
case 'text':
case 'text_long':
case 'text_with_summary':
$value = is_object($field_value) && isset($field_value->value) ? strip_tags($field_value->value) : (string) $field_value;
$tokens[$clean_field_name] = $value;
$tokens['node:' . $field_name] = $value;
$tokens['node:' . $field_name . ':value'] = $value;
$field_count++;
break;
case 'integer':
case 'decimal':
case 'float':
$value = is_object($field_value) && isset($field_value->value) ? $field_value->value : (string) $field_value;
$tokens[$clean_field_name] = $value;
$tokens['node:' . $field_name] = $value;
$tokens['node:' . $field_name . ':value'] = $value;
$field_count++;
break;
case 'boolean':
$value = is_object($field_value) && isset($field_value->value) ? ($field_value->value ? 'Yes' : 'No') : 'No';
$tokens[$clean_field_name] = $value;
$tokens['node:' . $field_name] = $value;
$field_count++;
break;
case 'entity_reference':
case 'entity_reference_revisions':
// For entity references, get the label
if (is_object($field_value) && isset($field_value->entity) && $field_value->entity) {
$referenced_entity = $field_value->entity;
$label = $referenced_entity->label();
$tokens[$clean_field_name] = $label;
$tokens['node:' . $field_name] = $label;
$tokens['node:' . $field_name . ':entity:label'] = $label;
$field_count++;
// For taxonomy terms, also add the ID
if ($referenced_entity->getEntityTypeId() === 'taxonomy_term') {
$tokens[$clean_field_name . ':tid'] = $referenced_entity->id();
$tokens['node:' . $field_name . ':target_id'] = $referenced_entity->id();
}
}
break;
case 'image':
case 'file':
// For file/image fields, provide the URL
if (is_object($field_value) && isset($field_value->entity) && $field_value->entity) {
$file = $field_value->entity;
try {
$file_url = \Drupal::service('file_url_generator')->generateAbsoluteString($file->getFileUri());
$tokens[$clean_field_name] = $file_url;
$tokens['node:' . $field_name] = $file_url;
$tokens['node:' . $field_name . ':url'] = $file_url;
$tokens['node:' . $field_name . ':entity:url'] = $file_url;
$field_count++;
// Add alt text for images
if ($field_type === 'image' && !empty($field_value->alt)) {
$tokens[$clean_field_name . ':alt'] = $field_value->alt;
$tokens['node:' . $field_name . ':alt'] = $field_value->alt;
}
} catch (\Exception $e) {
// Log error but continue processing
}
}
break;
default:
// Fallback for other field types
if (is_object($field_value)) {
if (isset($field_value->value)) {
$tokens[$clean_field_name] = $field_value->value;
$tokens['node:' . $field_name] = $field_value->value;
$field_count++;
} elseif (method_exists($field_value, '__toString')) {
$tokens[$clean_field_name] = (string) $field_value;
$tokens['node:' . $field_name] = (string) $field_value;
$field_count++;
}
} else {
$tokens[$clean_field_name] = (string) $field_value;
$tokens['node:' . $field_name] = (string) $field_value;
$field_count++;
}
}
}
}
// Add node metadata
$tokens['node:created'] = \Drupal::service('date.formatter')->format($node->getCreatedTime(), 'medium');
$tokens['node:created:short'] = \Drupal::service('date.formatter')->format($node->getCreatedTime(), 'short');
$tokens['node:created:long'] = \Drupal::service('date.formatter')->format($node->getCreatedTime(), 'long');
$tokens['node:changed'] = \Drupal::service('date.formatter')->format($node->getChangedTime(), 'medium');
$tokens['node:author:name'] = $node->getOwner() ? $node->getOwner()->getDisplayName() : 'Anonymous';
$tokens['node:author:uid'] = $node->getOwnerId();
$tokens['node:url'] = $node->toUrl('canonical', ['absolute' => TRUE])->toString();
$tokens['node:type'] = $node->bundle();
$tokens['node:type:name'] = $node->type->entity->label();
// Add some variations for common use cases
$tokens['author'] = $tokens['node:author:name'];
$tokens['created'] = $tokens['node:created'];
$tokens['type'] = $tokens['node:type:name'];
return $tokens;
}
/**
* Generate a preview image.
*/
public function generatePreview($poster_entity, Request $request) {
// ...existing code...
}
/**
* Debug endpoint to check form state and node selection.
*/
public function debugNodeSelection(Request $request) {
// ...existing code...
}
/**
* Select content for generation.
*/
public function selectContent($poster_entity, Request $request) {
return new JsonResponse(['status' => 'not implemented']);
}
/**
* Generate image for a specific node.
*/
public function generateForNode($poster_entity, Node $node, Request $request) {
return new JsonResponse(['status' => 'not implemented']);
}
/**
* Search content API endpoint.
*/
public function searchContent(Request $request) {
return new JsonResponse([]);
}
/**
* Generate poster via API.
*/
public function generatePoster($poster_entity_id, Request $request) {
return new JsonResponse(['status' => 'not implemented']);
}
/**
* Get entity title for page titles.
*/
public function entityTitle($poster_entity = NULL) {
if (!$poster_entity) {
return $this->t('Dynamic Image Generator');
}
if (is_numeric($poster_entity)) {
$poster_entity = \Drupal::entityTypeManager()->getStorage('poster_entity')->load($poster_entity);
}
if (!$poster_entity) {
return $this->t('Dynamic Image Generator');
}
return $poster_entity->label();
}
/**
* Get content type fields for AJAX callback.
*/
public function getContentTypeFields($content_type, Request $request) {
return new JsonResponse([]);
}
/**
* Extract form data from node form submission.
*/
protected function extractNodeFormData(array $form_data) {
$tokens = [];
foreach ($form_data as $field_name => $value) {
// Skip system fields
if (in_array($field_name, ['form_build_id', 'form_token', 'form_id', 'op', 'preview_mode', 'current_node_id', 'trigger_type'])) {
continue;
}
// Handle title field
if (strpos($field_name, 'title[') === 0 && is_array($value)) {
$title_value = reset($value);
if (!empty($title_value)) {
$tokens['title'] = $title_value;
$tokens['node:title'] = $title_value;
}
}
// Handle body field
if (strpos($field_name, 'body[') === 0 && is_array($value)) {
$body_data = reset($value);
if (is_array($body_data) && isset($body_data['value'])) {
$tokens['body'] = strip_tags($body_data['value']);
$tokens['node:body'] = strip_tags($body_data['value']);
$tokens['node:body:value'] = strip_tags($body_data['value']);
}
}
// Handle custom fields
if (strpos($field_name, 'field_') === 0) {
// Extract field name
preg_match('/^(field_[^[]+)/', $field_name, $matches);
if (isset($matches[1])) {
$clean_field_name = $matches[1];
$token_name = str_replace('field_', '', $clean_field_name);
if (is_array($value)) {
$field_value = reset($value);
if (is_array($field_value)) {
// Handle different field structures
if (isset($field_value['value'])) {
$tokens[$token_name] = $field_value['value'];
$tokens['node:' . $clean_field_name] = $field_value['value'];
} elseif (isset($field_value['target_id'])) {
// Entity reference field - load the referenced entity
try {
$referenced_entity = \Drupal::entityTypeManager()->getStorage('node')->load($field_value['target_id']);
if ($referenced_entity) {
$tokens[$token_name] = $referenced_entity->label();
$tokens['node:' . $clean_field_name] = $referenced_entity->label();
}
} catch (\Exception $e) {
// Continue if entity can't be loaded
}
}
} else {
$tokens[$token_name] = $field_value;
$tokens['node:' . $clean_field_name] = $field_value;
}
}
}
}
}
return $tokens;
}
/**
* Test Chrome directly without Browsershot.
*
* @return array
* Render array with test results.
*/
public function testChromeDirect() {
$results = [];
// Test 1: Basic Chrome version
$chrome_path = '/usr/bin/chromium';
$version_cmd = escapeshellarg($chrome_path) . ' --version 2>&1';
$version_output = shell_exec($version_cmd);
$results['version_test'] = [
'command' => $version_cmd,
'output' => $version_output,
'success' => strpos($version_output, 'Chromium') !== FALSE,
];
// Test 2: Chrome with minimal args
$minimal_cmd = escapeshellarg($chrome_path) . ' --headless --no-sandbox --disable-gpu --disable-setuid-sandbox --dump-dom "data:text/html,<h1>Test</h1>" 2>&1';
$minimal_output = shell_exec($minimal_cmd);
$results['minimal_test'] = [
'command' => $minimal_cmd,
'output' => $minimal_output,
'success' => strpos($minimal_output, '<h1>Test</h1>') !== FALSE,
];
// Test 3: Try to generate a screenshot directly with Chrome
$temp_file = '/tmp/chrome_test_' . uniqid() . '.png';
$screenshot_cmd = escapeshellarg($chrome_path) . ' --headless --no-sandbox --disable-gpu --disable-setuid-sandbox --window-size=400,300 --screenshot=' . escapeshellarg($temp_file) . ' "data:text/html,<div style=\"width:300px;height:200px;background:red;color:white;text-align:center;line-height:200px;\">DIRECT TEST</div>" 2>&1';
$screenshot_output = shell_exec($screenshot_cmd);
$screenshot_success = file_exists($temp_file) && filesize($temp_file) > 0;
$results['screenshot_test'] = [
'command' => $screenshot_cmd,
'output' => $screenshot_output,
'file_exists' => file_exists($temp_file),
'file_size' => file_exists($temp_file) ? filesize($temp_file) : 0,
'success' => $screenshot_success,
];
// Clean up test file
if (file_exists($temp_file)) {
unlink($temp_file);
}
// Test 4: Try Browsershot with single argument at a time
if (class_exists('Spatie\Browsershot\Browsershot')) {
try {
$temp_browsershot = '/tmp/browsershot_test_' . uniqid() . '.png';
// Create minimal Browsershot test
$browsershot = \Spatie\Browsershot\Browsershot::html('<div style="width:200px;height:100px;background:blue;color:white;text-align:center;line-height:100px;">BROWSERSHOT</div>')
->windowSize(200, 100)
->setChromePath($chrome_path)
->timeout(30);
// Add arguments one by one to see which fails
$args_to_test = [
'--headless',
'--no-sandbox',
'--disable-gpu',
'--disable-setuid-sandbox'
];
foreach ($args_to_test as $arg) {
$browsershot->addChromiumArguments([$arg]);
}
$browsershot->save($temp_browsershot);
$browsershot_success = file_exists($temp_browsershot) && filesize($temp_browsershot) > 0;
$results['browsershot_test'] = [
'args_used' => $args_to_test,
'file_exists' => file_exists($temp_browsershot),
'file_size' => file_exists($temp_browsershot) ? filesize($temp_browsershot) : 0,
'success' => $browsershot_success,
];
if (file_exists($temp_browsershot)) {
unlink($temp_browsershot);
}
} catch (\Exception $e) {
$results['browsershot_test'] = [
'error' => $e->getMessage(),
'success' => FALSE,
];
}
}
// Build output
$build = [];
$build['#attached']['library'][] = 'system/admin';
$build['title'] = [
'#markup' => '<h2>Direct Chrome Testing Results</h2>',
];
foreach ($results as $test_name => $result) {
$status = $result['success'] ? '✓ SUCCESS' : '✗ FAILED';
$status_class = $result['success'] ? 'messages--status' : 'messages--error';
$build[$test_name] = [
'#type' => 'details',
'#title' => ucfirst(str_replace('_', ' ', $test_name)) . ' - ' . $status,
'#open' => !$result['success'], // Open failed tests
];
$build[$test_name]['status'] = [
'#markup' => '<div class="messages ' . $status_class . '">' . $status . '</div>',
];
if (isset($result['command'])) {
$build[$test_name]['command'] = [
'#markup' => '<p><strong>Command:</strong><br><code>' . htmlspecialchars($result['command']) . '</code></p>',
];
}
if (isset($result['output'])) {
$build[$test_name]['output'] = [
'#markup' => '<p><strong>Output:</strong><br><pre>' . htmlspecialchars($result['output']) . '</pre></p>',
];
}
if (isset($result['args_used'])) {
$build[$test_name]['args'] = [
'#markup' => '<p><strong>Args used:</strong> ' . implode(' ', $result['args_used']) . '</p>',
];
}
if (isset($result['file_size'])) {
$build[$test_name]['file_info'] = [
'#markup' => '<p><strong>File created:</strong> ' . ($result['file_exists'] ? 'Yes' : 'No') .
($result['file_exists'] ? ' (' . $result['file_size'] . ' bytes)' : '') . '</p>',
];
}
if (isset($result['error'])) {
$build[$test_name]['error'] = [
'#markup' => '<p><strong>Error:</strong><br><code>' . htmlspecialchars($result['error']) . '</code></p>',
];
}
}
return $build;
}
/**
* Test Chrome installation and provide debugging information.
*
* @return array
* Render array with debugging information.
*/
public function testChromeInstallation() {
// Check if image_creating_engine is available
if (!\Drupal::hasService('image_creating_engine.generator')) {
return [
'#markup' => '<div class="messages messages--error">Image Creating Engine service not available. Make sure the image_creating_engine module is enabled.</div>',
];
}
$generator = \Drupal::service('image_creating_engine.generator');
$debug_info = $generator->getDebugInfo();
$debug_script = $generator->generateDebugScript();
$build = [];
// Add CSS for better formatting
$build['#attached']['library'][] = 'system/admin';
// System Information
$build['system'] = [
'#type' => 'details',
'#title' => 'System Information',
'#open' => TRUE,
];
$system_rows = [];
foreach ($debug_info['system'] as $key => $value) {
$system_rows[] = [ucfirst(str_replace('_', ' ', $key)), $value];
}
$build['system']['table'] = [
'#type' => 'table',
'#header' => ['Property', 'Value'],
'#rows' => $system_rows,
];
// Dependencies
$build['dependencies'] = [
'#type' => 'details',
'#title' => 'Dependencies Status',
'#open' => TRUE,
];
$dep_rows = [];
foreach ($debug_info['dependencies'] as $key => $value) {
$status = is_bool($value) ? ($value ? '✓ Available' : '✗ Missing') : $value;
$dep_rows[] = [ucfirst(str_replace('_', ' ', $key)), $status];
}
$build['dependencies']['table'] = [
'#type' => 'table',
'#header' => ['Dependency', 'Status'],
'#rows' => $dep_rows,
];
// Chrome Detection
$build['chrome'] = [
'#type' => 'details',
'#title' => 'Chrome/Chromium Detection',
'#open' => TRUE,
];
$chrome_rows = [];
foreach ($debug_info['chrome_detection']['paths'] as $path => $info) {
$status = [];
if ($info['exists']) $status[] = 'Exists';
if ($info['executable']) $status[] = 'Executable';
if ($info['can_launch']) $status[] = 'Can Launch';
$chrome_rows[] = [
$path,
implode(', ', $status) ?: 'Not Available',
$info['version'] ?: 'N/A',
];
}
$build['chrome']['table'] = [
'#type' => 'table',
'#header' => ['Path', 'Status', 'Version'],
'#rows' => $chrome_rows,
];
if ($debug_info['chrome_detection']['working_chrome']) {
$build['chrome']['working'] = [
'#markup' => '<div class="messages messages--status">Working Chrome found: ' . $debug_info['chrome_detection']['working_chrome'] . '</div>',
];
} else {
$build['chrome']['not_working'] = [
'#markup' => '<div class="messages messages--error">No working Chrome installation found!</div>',
];
}
// Terminal Commands
$build['commands'] = [
'#type' => 'details',
'#title' => 'Terminal Debug Commands',
'#open' => TRUE,
'#description' => 'Run these commands in your terminal to debug the issue:',
];
$commands = $generator->getTerminalDebugCommands();
foreach ($commands as $category => $info) {
$build['commands'][$category] = [
'#type' => 'details',
'#title' => ucfirst(str_replace('_', ' ', $category)),
'#description' => $info['description'],
];
$command_list = [];
foreach ($info['commands'] as $description => $command) {
$command_list[] = "<strong>$description:</strong><br><code>$command</code>";
}
$build['commands'][$category]['list'] = [
'#markup' => '<div style="margin: 10px 0;">' . implode('<br><br>', $command_list) . '</div>',
];
}
// Debug Script Download
$build['script'] = [
'#type' => 'details',
'#title' => 'Download Debug Script',
'#open' => FALSE,
];
$build['script']['description'] = [
'#markup' => '<p>Download and run this script to automatically test all debug commands:</p>',
];
$build['script']['code'] = [
'#type' => 'textarea',
'#title' => 'Debug Script',
'#value' => $debug_script,
'#rows' => 20,
'#attributes' => ['readonly' => 'readonly'],
];
$build['script']['instructions'] = [
'#markup' => '<div class="messages messages--info">
<strong>To use this script:</strong><br>
1. Copy the script above<br>
2. Save it as <code>debug_chrome.sh</code><br>
3. Run: <code>chmod +x debug_chrome.sh</code><br>
4. Run: <code>./debug_chrome.sh</code><br>
5. Send the output to your administrator
</div>',
];
// Quick Test
$build['test'] = [
'#type' => 'details',
'#title' => 'Quick Test',
'#open' => FALSE,
];
$build['test']['description'] = [
'#markup' => '<p>Test image generation with a simple example:</p>',
];
try {
$result = $generator->generateImage(
'<div style="padding: 20px; background: #f0f0f0;"><h1 style="color: #333;">Test Image</h1><p>Generated at ' . date('Y-m-d H:i:s') . '</p></div>',
'body { font-family: Arial, sans-serif; margin: 0; }',
['width' => 400, 'height' => 200]
);
if ($result) {
$build['test']['success'] = [
'#markup' => '<div class="messages messages--status">✓ Test image generated successfully!<br>URL: <a href="' . $result['url'] . '" target="_blank">' . $result['url'] . '</a></div>',
];
} else {
$build['test']['failed'] = [
'#markup' => '<div class="messages messages--error">✗ Test image generation failed. Check the logs and run the debug commands above.</div>',
];
}
} catch (\Exception $e) {
$build['test']['error'] = [
'#markup' => '<div class="messages messages--error">✗ Test failed with error: ' . $e->getMessage() . '</div>',
];
}
return $build;
}
/**
* Test wkhtmltoimage installation.
*
* @return array
* Render array with test results.
*/
public function testWkhtmlInstallation() {
$build = [];
$build['#attached']['library'][] = 'system/admin';
// Check if wkhtmltoimage service is available
if (!\Drupal::hasService('image_creating_engine.wkhtml_generator')) {
$build['error'] = [
'#markup' => '<div class="messages messages--error">wkhtmltoimage service not available. Make sure the image_creating_engine module is enabled and cache is cleared.</div>',
];
return $build;
}
$generator = \Drupal::service('image_creating_engine.wkhtml_generator');
$test_results = $generator->testInstallation();
$build['title'] = [
'#markup' => '<h2>wkhtmltoimage Installation Test</h2>',
];
// Installation status
$status = $test_results['wkhtml_found'] ? '✓ FOUND' : '✗ NOT FOUND';
$status_class = $test_results['wkhtml_found'] ? 'messages--status' : 'messages--error';
$build['status'] = [
'#markup' => '<div class="messages ' . $status_class . '">wkhtmltoimage: ' . $status . '</div>',
];
if ($test_results['wkhtml_found']) {
$build['details'] = [
'#type' => 'details',
'#title' => 'Installation Details',
'#open' => TRUE,
];
$build['details']['path'] = [
'#markup' => '<p><strong>Path:</strong> ' . htmlspecialchars($test_results['wkhtml_path']) . '</p>',
];
$build['details']['version'] = [
'#markup' => '<p><strong>Version:</strong><br><pre>' . htmlspecialchars($test_results['version']) . '</pre></p>',
];
// Generation test
$gen_status = $test_results['can_generate'] ? '✓ SUCCESS' : '✗ FAILED';
$gen_class = $test_results['can_generate'] ? 'messages--status' : 'messages--error';
$build['generation'] = [
'#markup' => '<div class="messages ' . $gen_class . '">Image Generation Test: ' . $gen_status . '</div>',
];
if ($test_results['can_generate']) {
$build['generation_details'] = [
'#markup' => '<p>Successfully generated test image (' . $test_results['test_file_size'] . ' bytes)</p>',
];
// Live test
$build['live_test'] = [
'#type' => 'details',
'#title' => 'Live Generation Test',
'#open' => TRUE,
];
try {
$test_html = '<div style="background: linear-gradient(45deg, #ff6b6b, #4ecdc4); color: white; padding: 30px; text-align: center; border-radius: 10px;"><h2>wkhtmltoimage Success!</h2><p>Generated at ' . date('Y-m-d H:i:s') . '</p></div>';
$test_css = 'body { margin: 0; padding: 20px; font-family: Arial, sans-serif; background: #f0f8ff; }';
$result = $generator->generateImage($test_html, $test_css, [
'width' => 500,
'height' => 200,
'format' => 'png'
]);
if ($result) {
$build['live_test']['success'] = [
'#markup' => '<div class="messages messages--status">✓ Live test successful!</div>',
];
$build['live_test']['image'] = [
'#markup' => '<p><strong>Generated Image:</strong><br><img src="' . $result['url'] . '" alt="Test Image" style="border: 1px solid #ccc; max-width: 100%;"></p>',
];
$build['live_test']['details'] = [
'#markup' => '<p><strong>Details:</strong><br>' .
'URL: <a href="' . $result['url'] . '" target="_blank">' . $result['url'] . '</a><br>' .
'Dimensions: ' . $result['width'] . 'x' . $result['height'] . '<br>' .
'Format: ' . $result['format'] . '<br>' .
'Generator: ' . $result['generator'] . '</p>',
];
} else {
$build['live_test']['failed'] = [
'#markup' => '<div class="messages messages--error">✗ Live test failed</div>',
];
}
} catch (\Exception $e) {
$build['live_test']['error'] = [
'#markup' => '<div class="messages messages--error">✗ Live test error: ' . htmlspecialchars($e->getMessage()) . '</div>',
];
}
}
} else {
// Installation instructions
$build['install'] = [
'#type' => 'details',
'#title' => 'Installation Instructions',
'#open' => TRUE,
];
$build['install']['commands'] = [
'#markup' => '<h3>Install wkhtmltopdf on Debian/Ubuntu:</h3>
<pre>
# Update package list
sudo apt-get update
# Install wkhtmltopdf (includes wkhtmltoimage)
sudo apt-get install wkhtmltopdf
# Verify installation
wkhtmltoimage --version
which wkhtmltoimage
# Test manually
wkhtmltoimage --width 400 --height 300 --format png \\
"data:text/html,<div style=\'background:blue;color:white;width:300px;height:200px;text-align:center;line-height:200px;\'>TEST</div>" \\
/tmp/wkhtml_test.png
# Check generated file
ls -la /tmp/wkhtml_test.png
# Clear Drupal cache after installation
cd /var/www/bigone/dev/zi-egovreview/docroot
drush cache:rebuild
</pre>',
];
}
// Error messages
if (!empty($test_results['error_messages'])) {
$build['errors'] = [
'#type' => 'details',
'#title' => 'Error Messages',
'#open' => TRUE,
];
foreach ($test_results['error_messages'] as $error) {
$build['errors'][] = [
'#markup' => '<div class="messages messages--error">' . htmlspecialchars($error) . '</div>',
];
}
}
return $build;
}
}