diff --git a/app/Console/Commands/ImportHtmlTemplates.php b/app/Console/Commands/ImportHtmlTemplates.php
index 59e2c44..9aeb840 100644
--- a/app/Console/Commands/ImportHtmlTemplates.php
+++ b/app/Console/Commands/ImportHtmlTemplates.php
@@ -9,32 +9,28 @@ use Illuminate\Console\Command;
use Illuminate\Support\Facades\File;
use DOMDocument;
use DOMXPath;
+use DOMElement;
class ImportHtmlTemplates extends Command
{
- /**
- * The name and signature of the console command.
- *
- * @var string
- */
protected $signature = 'theme:import-html {--force : Mevcut şablonları günceller}';
+ protected $description = 'HTML şablonlarını analiz eder, gelişmiş placeholderları oluşturur ve veritabanına aktarır.';
- /**
- * The console command description.
- *
- * @var string
- */
- protected $description = 'public/html klasöründeki HTML şablonlarını analiz eder ve veritabanına aktarır.';
-
- /**
- * Otomatik değiştirilecek metinler ve placeholder karşılıkları
- */
- protected $replacements = [
- 'Truncgil Technology' => '{text.company_name}',
- 'Trunçgil' => '{text.company_name}',
- 'tel:+' => 'tel:{setting.phone}',
- 'mailto:' => 'mailto:{setting.email}',
- '© 2025' => '© {date.year}',
+ // Etiket -> Kategori/Prefix eşleşmesi
+ protected $tagMappings = [
+ 'h1' => 'text.title',
+ 'h2' => 'text.title',
+ 'h3' => 'text.subtitle',
+ 'h4' => 'text.subtitle',
+ 'h5' => 'text.heading',
+ 'h6' => 'text.heading',
+ 'p' => 'text.description',
+ 'span' => 'text.content',
+ 'address' => 'text.address',
+ 'button' => 'text.button',
+ 'a' => 'url.link',
+ 'li' => 'text.list_item',
+ 'label' => 'text.label',
];
public function handle()
@@ -51,7 +47,6 @@ class ImportHtmlTemplates extends Command
foreach ($files as $file) {
if ($file->getExtension() !== 'html') continue;
-
$this->processFile($file);
}
@@ -62,121 +57,237 @@ class ImportHtmlTemplates extends Command
{
$filename = $file->getFilenameWithoutExtension();
$content = File::get($file->getPathname());
- $this->line("İşleniyor: $filename");
+
+ // UTF-8 ve HTML Entity fix
+ $content = mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8');
- // HTML Parse İşlemi
- // Hataları gizle (HTML5 etiketlerinde bazen uyarı verebilir)
libxml_use_internal_errors(true);
$dom = new DOMDocument();
- // UTF-8 karakter sorunu yaşamamak için hack
- // mb_convert_encoding ile içeriğin UTF-8 olduğundan emin oluyoruz
- $content = mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8');
+ // HTML5 tagleri için ayarlar
$dom->loadHTML($content, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
libxml_clear_errors();
$xpath = new DOMXPath($dom);
- // 1. HEADER ÇIKARTMA
+ // 1. HEADER
$headerNodes = $xpath->query('//header');
if ($headerNodes->length > 0) {
- $headerHtml = $this->getInnerHtml($headerNodes->item(0));
- $this->createHeaderTemplate($filename, $headerHtml);
+ $this->createTemplate($filename, 'header', $headerNodes->item(0));
}
- // 2. FOOTER ÇIKARTMA
+ // 2. FOOTER
$footerNodes = $xpath->query('//footer');
if ($footerNodes->length > 0) {
- $footerHtml = $this->getInnerHtml($footerNodes->item(0));
- $this->createFooterTemplate($filename, $footerHtml);
+ $this->createTemplate($filename, 'footer', $footerNodes->item(0));
}
- // 3. SECTIONS (BODY İÇİNDEKİLER)
- // Header ve Footer haricindeki sectionları bul
+ // 3. SECTIONS
$sections = $xpath->query('//section');
foreach ($sections as $index => $section) {
- $sectionHtml = $this->getInnerHtml($section);
+ $sectionId = $section->getAttribute('id') ?: 'section-' . ($index + 1);
+ if (strlen($section->textContent) < 10 && $section->getElementsByTagName('img')->length == 0) continue;
- // ID varsa al yoksa oluştur
- $sectionId = $section->getAttribute('id');
- if (empty($sectionId)) {
- $sectionId = 'section-' . ($index + 1);
- }
-
- // Eğer section çok kısaysa atla (boş sectionlar için)
- if (strlen(strip_tags($sectionHtml)) < 10) continue;
-
- $this->createSectionTemplate($filename, $sectionId, $sectionHtml);
+ $this->createTemplate($filename, 'section', $section, $sectionId);
}
}
- protected function createHeaderTemplate($source, $html)
+ protected function createTemplate($source, $type, DOMElement $node, $sectionId = null)
{
- $html = $this->processHtmlContent($html);
+ $result = $this->processNodeAndExtractData($node, $type);
+
+ $namePrefix = ucfirst($source);
- HeaderTemplate::updateOrCreate(
- ['title' => ucfirst($source) . ' Header'],
- [
- 'html_content' => $html,
- 'is_active' => true,
- 'default_data' => []
- ]
- );
+ if ($type === 'header') {
+ HeaderTemplate::updateOrCreate(
+ ['title' => "$namePrefix Header"],
+ [
+ 'html_content' => $result['html'],
+ 'default_data' => $result['data'],
+ 'is_active' => true
+ ]
+ );
+ $this->line("✓ Header eklendi: $source");
+ }
+ elseif ($type === 'footer') {
+ FooterTemplate::updateOrCreate(
+ ['title' => "$namePrefix Footer"],
+ [
+ 'html_content' => $result['html'],
+ 'default_data' => $result['data'],
+ 'is_active' => true
+ ]
+ );
+ $this->line("✓ Footer eklendi: $source");
+ }
+ elseif ($type === 'section') {
+ $title = "$namePrefix - " . ucfirst(str_replace(['-', '_'], ' ', $sectionId));
+ SectionTemplate::updateOrCreate(
+ ['title' => $title],
+ [
+ 'html_content' => $result['html'],
+ 'default_data' => $result['data'],
+ 'is_active' => true
+ ]
+ );
+ $this->line("✓ Section eklendi: $title");
+ }
}
- protected function createFooterTemplate($source, $html)
+ protected function processNodeAndExtractData(DOMElement $originalNode, $templateType)
{
- $html = $this->processHtmlContent($html);
+ $node = $originalNode->cloneNode(true);
+ $xpath = new DOMXPath($node->ownerDocument);
+
+ $data = [];
+ $counts = [];
- FooterTemplate::updateOrCreate(
- ['title' => ucfirst($source) . ' Footer'],
- [
- 'html_content' => $html,
- 'is_active' => true,
- 'default_data' => []
- ]
- );
+ // A. Header Özel Alanları
+ if ($templateType === 'header') {
+ // Logo
+ $brands = $xpath->query('.//*[contains(@class, "navbar-brand")]//img', $node);
+ foreach ($brands as $img) {
+ $src = $this->fixAssetPath($img->getAttribute('src'));
+ $class = $img->getAttribute('class');
+
+ if (str_contains($class, 'logo-dark')) {
+ $data['setting.logo_dark'] = $src;
+ $img->setAttribute('src', '{setting.logo_dark}');
+ } elseif (str_contains($class, 'logo-light')) {
+ $data['setting.logo_light'] = $src;
+ $img->setAttribute('src', '{setting.logo_light}');
+ } else {
+ $data['setting.logo'] = $src;
+ $img->setAttribute('src', '{setting.logo}');
+ }
+ }
+
+ // Menu
+ $navs = $xpath->query('.//*[contains(@class, "navbar-nav")]', $node);
+ foreach ($navs as $nav) {
+ if (!$nav->parentNode) continue;
+ // İçini temizle
+ while ($nav->hasChildNodes()) {
+ $nav->removeChild($nav->firstChild);
+ }
+ // Placeholder metni ekle - Token olarak
+ $nav->nodeValue = '___SPECIAL_MENU___';
+ }
+
+ // Dil Seçici
+ $langs = $xpath->query('.//*[contains(@class, "language")]', $node);
+ foreach ($langs as $lang) {
+ $lang->nodeValue = '___SETTING_LANGUAGES___';
+ }
+ }
+
+ // B. Genel İşlemler
+ // 1. Resimler
+ $images = $xpath->query('.//img', $node);
+ foreach ($images as $img) {
+ $src = $img->getAttribute('src');
+ if (str_starts_with($src, '{')) continue;
+ if (empty($src)) continue;
+
+ $fixedSrc = $this->fixAssetPath($src);
+ $key = $this->generateKey('image.content', $counts);
+
+ $data[$key] = $fixedSrc;
+ $img->setAttribute('src', "{" . $key . "}");
+
+ $alt = $img->getAttribute('alt');
+ if (!empty($alt) && !str_starts_with($alt, '{')) {
+ $altKey = str_replace('image.content', 'text.alt', $key);
+ $data[$altKey] = $alt;
+ $img->setAttribute('alt', "{" . $altKey . "}");
+ }
+ }
+
+ // 2. Linkler
+ $links = $xpath->query('.//a', $node);
+ foreach ($links as $link) {
+ $href = $link->getAttribute('href');
+ if (empty($href) || str_starts_with($href, '#') || str_starts_with($href, 'javascript:') || str_starts_with($href, 'mailto:') || str_starts_with($href, 'tel:') || str_starts_with($href, '{')) continue;
+
+ $fixedHref = $this->fixAssetPath($href);
+ if (str_ends_with($fixedHref, '.html')) {
+ $fixedHref = str_replace('.html', '', $fixedHref);
+ if($fixedHref == 'index') $fixedHref = '/';
+ }
+
+ $key = $this->generateKey('url.link', $counts);
+ $data[$key] = $fixedHref;
+ $link->setAttribute('href', "{" . $key . "}");
+ }
+
+ // 3. Metinler
+ $textNodes = $xpath->query('.//text()[normalize-space()]', $node);
+ foreach ($textNodes as $textNode) {
+ $parent = $textNode->parentNode;
+ if (in_array(strtolower($parent->nodeName), ['script', 'style', 'noscript', 'iframe'])) continue;
+
+ $textContent = trim($textNode->textContent);
+ // Özel tokenlarımızı atla
+ if (str_contains($textContent, '___SPECIAL_') || str_contains($textContent, '___SETTING_')) continue;
+
+ if (str_starts_with($textContent, '{') && str_ends_with($textContent, '}')) continue;
+ if (mb_strlen($textContent) < 2) continue;
+
+ $tagName = strtolower($parent->nodeName);
+ $prefix = $this->tagMappings[$tagName] ?? 'text.content';
+
+ $key = $this->generateKey($prefix, $counts);
+
+ $data[$key] = $textContent;
+ $textNode->nodeValue = "{" . $key . "}";
+ }
+
+ $html = $this->getInnerHtml($node);
+ $html = $this->fixAssetPathInString($html);
+
+ // ENCODE FIX: %7B -> {, %7D -> }
+ // DOMDocument attribute değerlerini encode ettiği için bunları geri çeviriyoruz
+ $html = str_replace(['%7B', '%7D'], ['{', '}'], $html);
+
+ // SPECIAL TOKENS FIX
+ if ($templateType === 'header') {
+ $html = str_replace('___SPECIAL_MENU___', '{special.menu}', $html);
+ $html = str_replace('___SETTING_LANGUAGES___', '{setting.available_languages}', $html);
+ }
+
+ return ['html' => $html, 'data' => $data];
}
- protected function createSectionTemplate($source, $sectionId, $html)
+ protected function generateKey($prefix, &$counts)
{
- $html = $this->processHtmlContent($html);
- $title = ucfirst($source) . ' - ' . ucfirst(str_replace(['-', '_'], ' ', $sectionId));
-
- SectionTemplate::updateOrCreate(
- ['title' => $title],
- [
- 'html_content' => $html,
- 'is_active' => true,
- 'default_data' => []
- ]
- );
+ if (!isset($counts[$prefix])) {
+ $counts[$prefix] = 1;
+ } else {
+ $counts[$prefix]++;
+ }
+ return $prefix . '_' . $counts[$prefix];
}
- /**
- * HTML İçeriğini temizler, asset yollarını düzeltir ve placeholderları yerleştirir
- */
- protected function processHtmlContent($html)
+ protected function fixAssetPath($path)
+ {
+ if (str_starts_with($path, 'assets/')) {
+ return '/html/' . $path;
+ }
+ return $path;
+ }
+
+ protected function fixAssetPathInString($html)
{
- // 1. Asset Yollarını Düzelt (assets/img -> /html/assets/img)
- // Public/html altında olduğu için absolute path veriyoruz
$html = str_replace('src="assets/', 'src="/html/assets/', $html);
$html = str_replace('href="assets/', 'href="/html/assets/', $html);
$html = str_replace("src='assets/", "src='/html/assets/", $html);
-
- // 2. Linkleri Düzelt (index.html -> /)
- $html = str_replace('href="index.html"', 'href="/"', $html);
+ $html = str_replace("href='assets/", "href='/html/assets/", $html);
+ $html = str_replace('url(assets/', 'url(/html/assets/', $html);
+ $html = str_replace('url("assets/', 'url("/html/assets/', $html);
+ $html = str_replace("url('assets/", "url('/html/assets/", $html);
- // 3. Placeholder Değişimi (Basit string replace)
- foreach ($this->replacements as $search => $replace) {
- $html = str_replace($search, $replace, $html);
- }
-
return $html;
}
- /**
- * DOMNode'un HTML içeriğini string olarak alır
- */
protected function getInnerHtml($node)
{
$innerHTML = '';
@@ -187,4 +298,3 @@ class ImportHtmlTemplates extends Command
return $innerHTML;
}
}
-
diff --git a/database/seeders/SettingSeeder.php b/database/seeders/SettingSeeder.php
index 6f15bea..d440d32 100644
--- a/database/seeders/SettingSeeder.php
+++ b/database/seeders/SettingSeeder.php
@@ -585,6 +585,26 @@ class SettingSeeder extends Seeder
'is_public' => true,
'is_active' => true,
],
+ [
+ 'key' => 'logo_light',
+ 'value' => 'assets/img/logo-light.png',
+ 'type' => 'file',
+ 'group' => 'layout',
+ 'label' => 'Light Logo',
+ 'description' => 'Koyu zeminler için açık renkli logo',
+ 'is_public' => true,
+ 'is_active' => true,
+ ],
+ [
+ 'key' => 'logo_dark',
+ 'value' => 'assets/img/logo-dark.png',
+ 'type' => 'file',
+ 'group' => 'layout',
+ 'label' => 'Dark Logo',
+ 'description' => 'Açık zeminler için koyu renkli logo',
+ 'is_public' => true,
+ 'is_active' => true,
+ ],
[
'key' => 'favicon_path',
'value' => 'assets/img/favicon.ico',
@@ -1298,10 +1318,26 @@ class SettingSeeder extends Seeder
];
foreach ($settings as $setting) {
- Setting::updateOrCreate(
- ['key' => $setting['key']],
- $setting
- );
+ $existing = Setting::where('key', $setting['key'])->first();
+ if ($existing) {
+ $updated = false;
+ // Sadece değişmişse güncelle
+ foreach ($setting as $field => $value) {
+ if ($existing->$field != $value) {
+ $updated = true;
+ break;
+ }
+ }
+ if ($updated) {
+ $existing->update($setting);
+ $this->command->info("Setting updated: " . $setting['key']);
+ } else {
+ $this->command->info("Setting unchanged: " . $setting['key']);
+ }
+ } else {
+ Setting::create($setting);
+ $this->command->info("Setting created: " . $setting['key']);
+ }
}
}
}