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,&lt;div style=\'background:blue;color:white;width:300px;height:200px;text-align:center;line-height:200px;\'&gt;TEST&lt;/div&gt;" \\
  /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;
  }

}

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

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