HEX
Server: LiteSpeed
System: Linux s1049.use1.mysecurecloudhost.com 4.18.0-477.27.2.lve.el8.x86_64 #1 SMP Wed Oct 11 12:32:56 UTC 2023 x86_64
User: xedaptot (3356)
PHP: 8.3.31
Disabled: NONE
Upload Files
File: /home/xedaptot/hi.naniguide.com/app/Http/Controllers/Admin/TemplateImportController.php
<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\BuiltInTemplateSetting;
use App\Models\PublicTemplate;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\File;

class TemplateImportController extends Controller
{
    public function importGallery(Request $request)
    {
        $tab = (string) $request->query('tab', 'templates');
        if ($tab !== 'templates') {
            $tab = 'templates';
        }

        $builder = (string) $request->query('builder', 'unlayer');
        if (!in_array($builder, ['grapesjs', 'unlayer'], true)) {
            $builder = 'unlayer';
        }

        $fileItems = $this->fileGalleryItems($builder, $tab);

        $publicTemplates = PublicTemplate::query()
            ->where('is_active', true)
            ->where('builder', $builder)
            ->orderBy('name')
            ->get()
            ->map(function (PublicTemplate $template) {
                $settings = is_array($template->settings) ? $template->settings : [];
                $category = is_string($settings['category'] ?? null) && trim((string) $settings['category']) !== ''
                    ? trim((string) $settings['category'])
                    : 'other';

                return [
                    'id' => 'public-' . $template->id,
                    'name' => $template->name,
                    'description' => $template->description,
                    'thumbnail' => $template->thumbnail,
                    'category' => $category,
                    'builder' => $template->builder,
                    'source' => 'public',
                    'is_ai' => false,
                    'content_url' => route('admin.templates.import.public.content', $template),
                ];
            })
            ->values()
            ->all();

        $merged = array_values(array_merge($publicTemplates, $fileItems));

        usort($merged, function ($a, $b) {
            return strcmp((string) ($a['name'] ?? ''), (string) ($b['name'] ?? ''));
        });

        $categories = [
            ['id' => 'all', 'name' => 'All Templates', 'icon' => 'grid'],
            ['id' => 'marketing', 'name' => 'Marketing & Promotional', 'icon' => 'megaphone'],
            ['id' => 'transactional', 'name' => 'Transactional', 'icon' => 'receipt'],
            ['id' => 'automation', 'name' => 'Automation & Drip', 'icon' => 'zap'],
            ['id' => 'cold', 'name' => 'Cold & Outreach', 'icon' => 'send'],
            ['id' => 'relationship', 'name' => 'Relationship & Engagement', 'icon' => 'heart'],
            ['id' => 'support', 'name' => 'Support & System', 'icon' => 'headphones'],
            ['id' => 'ecommerce', 'name' => 'E-commerce', 'icon' => 'shopping'],
            ['id' => 'ai', 'name' => 'AI Generated', 'icon' => 'sparkles'],
            ['id' => 'other', 'name' => 'Other', 'icon' => 'folder'],
        ];

        return response()->json([
            'tab' => $tab,
            'templates' => $merged,
            'categories' => $categories,
        ]);
    }

    public function importFileContent(Request $request, string $key)
    {
        $payload = $this->fileGalleryPayloadByKey('unlayer', $key);
        if ($payload === null) {
            abort(404);
        }

        return response()->json($payload);
    }

    public function importPublicContent(PublicTemplate $publicTemplate)
    {
        if (!$publicTemplate->is_active) {
            abort(404);
        }

        return response()->json([
            'id' => $publicTemplate->id,
            'name' => $publicTemplate->name,
            'description' => $publicTemplate->description,
            'html_content' => $publicTemplate->html_content,
            'plain_text_content' => $publicTemplate->plain_text_content,
            'builder' => $publicTemplate->builder,
            'builder_data' => $publicTemplate->builder_data,
        ]);
    }

    private function fileGalleryRoot(string $builder): string
    {
        $builder = trim($builder);
        if ($builder === '') {
            $builder = 'unlayer';
        }

        return resource_path('template-gallery/' . $builder);
    }

    private function fileGalleryItems(string $builder, string $tab): array
    {
        if ($builder !== 'unlayer') {
            return [];
        }

        $root = $this->fileGalleryRoot($builder);
        if (!is_dir($root)) {
            return [];
        }

        $dir = $tab === 'pro' ? ($root . DIRECTORY_SEPARATOR . 'pro') : $root;
        if (!is_dir($dir)) {
            return [];
        }

        $files = File::glob($dir . DIRECTORY_SEPARATOR . '*.json');
        if (!is_array($files)) {
            return [];
        }

        $items = [];
        foreach ($files as $path) {
            if (!is_string($path) || $path === '' || !is_file($path)) {
                continue;
            }

            $relative = ltrim(str_replace($root, '', $path), DIRECTORY_SEPARATOR);
            $key = sha1($builder . ':' . $relative);

            $name = pathinfo($path, PATHINFO_FILENAME);
            $description = null;
            $thumbnail = null;
            $category = 'other';

            try {
                $raw = File::get($path);
                if (is_string($raw) && $raw !== '') {
                    $decoded = json_decode($raw, true);
                    if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
                        if (is_string($decoded['name'] ?? null) && trim((string) $decoded['name']) !== '') {
                            $name = trim((string) $decoded['name']);
                        }
                        if (is_string($decoded['description'] ?? null) && trim((string) $decoded['description']) !== '') {
                            $description = trim((string) $decoded['description']);
                        }
                        if (is_string($decoded['thumbnail'] ?? null) && trim((string) $decoded['thumbnail']) !== '') {
                            $thumbnail = trim((string) $decoded['thumbnail']);
                        }
                        if (is_string($decoded['category'] ?? null) && trim((string) $decoded['category']) !== '') {
                            $category = trim((string) $decoded['category']);
                        }
                    }
                }
            } catch (\Throwable $e) {
            }

            $setting = BuiltInTemplateSetting::firstOrCreate(
                [
                    'builder' => $builder,
                    'template_key' => $key,
                ],
                [
                    'relative_path' => $relative,
                    'name' => $name,
                    'is_active' => true,
                ]
            );

            if (($setting->name ?? null) !== $name || ($setting->relative_path ?? null) !== $relative) {
                $setting->forceFill([
                    'name' => $name,
                    'relative_path' => $relative,
                ])->save();
            }

            if (!$setting->is_active) {
                continue;
            }

            $items[] = [
                'id' => $key,
                'name' => $name,
                'description' => $description,
                'thumbnail' => $thumbnail,
                'category' => $category,
                'builder' => 'unlayer',
                'source' => 'file',
                'is_ai' => false,
                'content_url' => route('admin.templates.import.file.content', ['key' => $key]),
            ];
        }

        usort($items, function ($a, $b) {
            return strcmp((string) ($a['name'] ?? ''), (string) ($b['name'] ?? ''));
        });

        return $items;
    }

    private function fileGalleryPayloadByKey(string $builder, string $key): ?array
    {
        if ($builder !== 'unlayer') {
            return null;
        }

        $root = $this->fileGalleryRoot($builder);
        if (!is_dir($root)) {
            return null;
        }

        $files = File::allFiles($root);
        foreach ($files as $file) {
            $path = $file->getPathname();
            if (!is_string($path) || !str_ends_with(strtolower($path), '.json')) {
                continue;
            }

            $relative = ltrim(str_replace($root, '', $path), DIRECTORY_SEPARATOR);
            $computed = sha1($builder . ':' . $relative);
            if (!hash_equals($computed, $key)) {
                continue;
            }

            $setting = BuiltInTemplateSetting::firstOrCreate(
                [
                    'builder' => $builder,
                    'template_key' => $key,
                ],
                [
                    'relative_path' => $relative,
                    'name' => pathinfo($path, PATHINFO_FILENAME),
                    'is_active' => true,
                ]
            );

            if (!$setting->is_active) {
                return null;
            }

            $raw = File::get($path);
            $decoded = json_decode((string) $raw, true);
            if (json_last_error() !== JSON_ERROR_NONE || !is_array($decoded)) {
                return null;
            }

            $design = null;
            if (is_array($decoded['design'] ?? null)) {
                $design = $decoded['design'];
            } elseif (!isset($decoded['design'])) {
                $design = $decoded;
            }

            if (!is_array($design)) {
                return null;
            }

            $name = is_string($decoded['name'] ?? null) && trim((string) $decoded['name']) !== ''
                ? trim((string) $decoded['name'])
                : pathinfo($path, PATHINFO_FILENAME);

            if (($setting->name ?? null) !== $name || ($setting->relative_path ?? null) !== $relative) {
                $setting->forceFill([
                    'name' => $name,
                    'relative_path' => $relative,
                ])->save();
            }

            $description = is_string($decoded['description'] ?? null) && trim((string) $decoded['description']) !== ''
                ? trim((string) $decoded['description'])
                : null;

            $html = $this->unlayerPreviewHtmlFromDesign($design);

            $plain = is_string($decoded['plain_text_content'] ?? null) ? (string) $decoded['plain_text_content'] : null;
            if ($plain === null && is_string($decoded['plain'] ?? null)) {
                $plain = (string) $decoded['plain'];
            }

            return [
                'id' => $key,
                'name' => $name,
                'description' => $description,
                'html_content' => $html,
                'plain_text_content' => $plain,
                'builder' => 'unlayer',
                'builder_data' => $design,
            ];
        }

        return null;
    }

    private function unlayerPreviewHtmlFromDesign(array $design): string
    {
        $projectId = config('services.unlayer.project_id');
        $projectId = is_numeric($projectId) ? (int) $projectId : null;

        $designJson = json_encode(
            $design,
            JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT
        );
        if (!is_string($designJson) || trim($designJson) === '') {
            $designJson = 'null';
        }

        $projectIdJson = $projectId !== null ? json_encode($projectId) : 'null';

        return <<<HTML
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <style>
    html, body { margin: 0; padding: 0; background: #f9fafb; height: 100%; }
    #status {
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      height: 100%;
      min-height: 400px;
      font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial;
      font-size: 14px;
      font-weight: 500;
      color: #6b7280;
    }
    #status .spinner {
      width: 40px;
      height: 40px;
      border: 3px solid #e5e7eb;
      border-top-color: #4f46e5;
      border-radius: 50%;
      animation: spin 0.8s linear infinite;
      margin-bottom: 16px;
    }
    @keyframes spin {
      to { transform: rotate(360deg); }
    }
    #output { width: 100%; background: #ffffff; }
    #editor { width: 1px; height: 1px; overflow: hidden; position: absolute; left: -9999px; top: -9999px; }
  </style>
</head>
<body>
  <div id="status"><div class="spinner"></div>Rendering preview…</div>
  <div id="output"></div>
  <div id="editor"></div>

  <script src="https://editor.unlayer.com/embed.js"></script>
  <script>
    (function () {
      try {
        var design = $designJson;
        var projectId = $projectIdJson;

        if (!design || typeof design !== 'object') {
          document.getElementById('status').textContent = 'Preview not available: invalid Unlayer design JSON.';
          return;
        }

        var initOptions = { id: 'editor', displayMode: 'email' };
        if (projectId) {
          initOptions.projectId = Number(projectId);
        }

        unlayer.init(initOptions);

        unlayer.addEventListener('editor:ready', function () {
          unlayer.loadDesign(design);
          unlayer.exportHtml(function (data) {
            var html = (data && data.html) ? String(data.html) : '';
            if (!html.trim()) {
              document.getElementById('status').textContent = 'Preview not available.';
              return;
            }
            document.getElementById('status').remove();
            document.getElementById('output').innerHTML = html;
          });
        });
      } catch (e) {
        document.getElementById('status').textContent = 'Preview not available.';
      }
    })();
  </script>
</body>
</html>
HTML;
    }
}