Seo çalışmaları
5
.github/instructions/ai.instructions.md
vendored
|
|
@ -60,7 +60,8 @@ System nature:
|
|||
Anything beyond that — a new React component or page, a new toolbox family, core engine changes (ListForm
|
||||
runtime, seeders, code generator, auth, DataSourceManager, dispatcher/compiler), new ABP
|
||||
entity/module/migration, platform table schema changes, new NuGet/npm dependencies, the
|
||||
`App.Menus.Routes.json` seed data, new external integrations — is **out of frame**: finish everything that
|
||||
`App.Menus.Routes.json` seed data (except the `Seo*` columns of an existing row, which are part
|
||||
of ordinary production — lowcode §0.9), new external integrations — is **out of frame**: finish everything that
|
||||
fits inside the frame first, then ask the user with a single question (why configuration is
|
||||
insufficient, the smallest change that works, files touched, rollback) and proceed **only with
|
||||
explicit approval**. Approval is per request and does not carry over. Full lists:
|
||||
|
|
@ -723,6 +724,8 @@ From the `configs/seeds/host/data/App.Menus.*.json` patterns, AI must define bot
|
|||
|
||||
- `App.Menus.Routes.json` row:
|
||||
- `Id`, `Key`, `Path`, `ComponentPath`, `RouteType`, `Authority`
|
||||
- for a visitor-facing route, also its `Seo*` columns — there is no separate SEO table
|
||||
(lowcode §0.9)
|
||||
- `App.Menus.Menu.json` row:
|
||||
- `ParentCode`, `Code`, `DisplayName`, `Url`, `Icon`, `RequiredPermissionName`, `Order`
|
||||
|
||||
|
|
|
|||
2
.github/instructions/list.instructions.md
vendored
|
|
@ -28,7 +28,7 @@ This document summarizes the rules, standards, and step-by-step instructions for
|
|||
## Special File Handling Rules
|
||||
|
||||
- **configs/seeds/host/data/App.Menus.*.json:**
|
||||
- Never modify `App.Menus.Routes.json` directly. Only `App.Menus.Module.json` and `App.Menus.Menu.json` can be changed for menu additions or updates. All menu additions must use the platform's configuration mechanisms or be added to those two files as required by platform design.
|
||||
- Never add, remove or re-path a route in `App.Menus.Routes.json`. The only field set you may edit on an existing row is its `Seo*` columns (lowcode §0.9). Only `App.Menus.Module.json` and `App.Menus.Menu.json` can be changed for menu additions or updates. All menu additions must use the platform's configuration mechanisms or be added to those two files as required by platform design.
|
||||
|
||||
- **LanguagesData.json:**
|
||||
- When adding a new key, always check if the key already exists. Only add the key if it does not exist to prevent duplicates.
|
||||
|
|
|
|||
|
|
@ -1304,6 +1304,10 @@ yazma, oradan yeni bir Custom Component üret. `Services` sayfası yoktur.
|
|||
| `/demo` | `views/public/Demo` | `Sas_H_Demo` |
|
||||
| `/access-denied` | `views/AccessDenied` | — |
|
||||
|
||||
Ziyaretçiye açık her yolun SEO alanları ayrı bir tabloda değil, yolu tanımlayan kaydın
|
||||
üzerindedir: `Route` satırının ya da Custom Component'in `Seo*` kolonları. Listeye yeni bir public
|
||||
yol eklediğinde bu alanları da doldur (lowcode §0.9).
|
||||
|
||||
**Kimlik akışı** (`routeType: authenticated`): `/login`, `/register`, `/forgot-password`,
|
||||
`/reset-password`, `/confirm`, `/confirm/:userId/:token`, `/extend-login` →
|
||||
`views/auth/*`. Bunlara dokunma; parola ve doğrulama akışları ABP tarafındadır.
|
||||
|
|
|
|||
78
.github/instructions/lowcode.instructions.md
vendored
|
|
@ -53,9 +53,9 @@ configs/seeds/host/
|
|||
├── postgres/object|execute/… ← aynısının PostgreSQL diyalekti (hedef PG ise)
|
||||
├── crud/{EntityName}.json ← tablonun REST uçları
|
||||
├── wizard/{WizardName}.json ← ekran + menü + yetki + dil anahtarları
|
||||
└── data/{ListFormCode}.json ← ekrandan girilen liste verisinin aynası (opsiyonel)
|
||||
Custom Component'ler de burada yaşar:
|
||||
data/App.DeveloperKit.CustomComponents/{Id}.json
|
||||
├── data/{ListFormCode}.json ← ekrandan girilen liste verisinin aynası (opsiyonel)
|
||||
│ Custom Component'ler de burada yaşar:
|
||||
│ data/App.DeveloperKit.CustomComponents/{Id}.json
|
||||
```
|
||||
|
||||
Tenant kapsamı isteniyorsa `host/` yerine `tenants/{tenantId}/`; iç düzen aynıdır.
|
||||
|
|
@ -70,6 +70,7 @@ Tenant kapsamı isteniyorsa `host/` yerine `tenants/{tenantId}/`; iç düzen ayn
|
|||
| 4 | Wizard | `wizard/{Ad}.json` | Menü, yetki, dil anahtarı ve ListForm'u üretir |
|
||||
| 5 | Alt ekranlar (varsa) | ek `wizard/…` dosyaları | Ana wizard'dan **önceki** `SeededAt` damgasını alır |
|
||||
| 6 | Liste verisi (isteniyorsa) | `data/{ListFormCode}.json` | Ekran ve tablo hazır olmalı; `ListForm.SeedFilePath` bu yolu gösterir |
|
||||
| 7 | SEO alanları (public yol ürettiysen) | Yolu tanımlayan satırın kendisi: `data/App.Menus.Routes.json` ya da `data/App.DeveloperKit.CustomComponents/{Id}.json` | Yolu üreten dosya yazıldıktan sonra; ziyaretçiye açık hiçbir yol metasız kalmaz (§0.9) |
|
||||
|
||||
**`Wizard.SeededAt` bir bağımlılık aracıdır.** Dosya adı tarih taşımaz (`{WizardName}.json`);
|
||||
`WizardDataSeeder` `wizard/` klasörünü **`Wizard.SeededAt` damgasına göre sıralı** işler (eşitlikte
|
||||
|
|
@ -394,7 +395,7 @@ kullanıcıya sorulur ve yalnızca onayla yapılır.** "Yapılamaz" cevabı yokt
|
|||
|
||||
| Alan | Örnek |
|
||||
| --- | --- |
|
||||
| Seed artefaktları | Wizard, custom component (visual/code modu), `crud/`, SQL nesneleri (tablo/view/SP/fonksiyon), Custom Endpoint, Dynamic Service, BackgroundWorker, bildirim türü/kuralı, dil anahtarı, yetki kaydı |
|
||||
| Seed artefaktları | Wizard, custom component (visual/code modu), `crud/`, SQL nesneleri (tablo/view/SP/fonksiyon), Custom Endpoint, Dynamic Service, BackgroundWorker, bildirim türü/kuralı, dil anahtarı, yetki kaydı, **SEO kaydı** (§0.9) |
|
||||
| Mevcut bileşeni genişletme | Kullanılan bir `ui` bileşenine **eksik olay / prop / küçük yetenek** ekleme (§7.3 notu) — komponentin %70'i hazır araçlarla kuruluyorsa kalanı bu eklemelerle tamamlanır; `componentProps.json` girdisini tamamlama |
|
||||
| Mevcut sözlüğü genişletme | Yeni EditorScript tarifi (`scriptRecipes.ts` ↔ `Domain.Shared/Editors` birlikte), yeni `EditorOptions` anahtarı, yeni `DefaultValue` token'ı — **geriye dönük uyumlu** olmak şartıyla |
|
||||
| Tasarımcı sözleşmesi | Var olan düğüme prop/varsayılan ekleme, yeni Script Builder tarifi |
|
||||
|
|
@ -407,7 +408,7 @@ kullanıcıya sorulur ve yalnızca onayla yapılır.** "Yapılamaz" cevabı yokt
|
|||
| Çekirdek motor | ListForm runtime, seeder'lar, `codeGenerator.ts`, yetki/kimlik altyapısı, `DataSourceManager`, dispatcher/derleyici (`CustomEndpointAppService`, `DynamicServiceCompiler`) |
|
||||
| Backend şeması | Yeni ABP modülü/entity/migration, platform tablolarında (`{Önek}_{H\|T\|B}_{Ad}` — reference §12.1) kolon değişikliği, `TableNameEnum`/`TableNameResolver` değişikliği, DTO/JSON kolon sözleşmesini **bozan** değişiklik |
|
||||
| Bağımlılık | Yeni NuGet/npm paketi, sürüm yükseltme |
|
||||
| Sabit yapılar | `data/App.Menus.Routes.json`, `PlatformConsts` içindeki mevcut değerlerin değişmesi |
|
||||
| Sabit yapılar | `data/App.Menus.Routes.json` satırlarının eklenmesi/silinmesi/yolunun değişmesi (`Seo*` alanları hariç, §0.9), `PlatformConsts` içindeki mevcut değerlerin değişmesi |
|
||||
| Dış dünya | Yeni harici entegrasyon, yeni bağlantı tipi, üçüncü taraf servis |
|
||||
|
||||
**Sorma biçimi:** çerçeve içinde yapılabilen her şeyi **önce bitir**, sonra tek soruda
|
||||
|
|
@ -416,6 +417,73 @@ hangi değişiklik yeter, hangi dosyalara dokunacak, geri alma nasıl olacak. Se
|
|||
daima "çerçeve içinde daraltılmış çözüm" alternatifi de bulunsun. Onay gelmeden `api/` ya da
|
||||
`ui/` altında çerçeve dışı bir satır yazılmaz; onay bir talebe özeldir, sonraki talebe taşınmaz.
|
||||
|
||||
---
|
||||
|
||||
### 0.9 SEO alanları — ziyaretçiye açık her yol için zorunlu
|
||||
|
||||
Ziyaretçiye açık bir yol üreten her iş, o yolun **SEO alanlarını da** doldurur. Bu kullanıcıdan
|
||||
istenen bir bilgi değildir, üretimin parçasıdır: kullanıcı SEO bilgisi girmez, sen yazarsın.
|
||||
Metasız yayınlanan bir sayfa arama sonucunda başlıksız ve açıklamasız görünür; paylaşıldığında
|
||||
da boş bir kart çıkar.
|
||||
|
||||
**Ayrı bir SEO tablosu yoktur.** Meta, yolu tanımlayan kaydın üzerinde yaşar. Bu platformda rota
|
||||
iki yerden doğar, ikisinde de aynı alan kümesi vardır:
|
||||
|
||||
| Yolu tanımlayan | Seed dosyası | Örnek yollar |
|
||||
| --- | --- | --- |
|
||||
| `Route` satırı (fiziksel React sayfası) | `data/App.Menus.Routes.json` | `/products`, `/blog`, `/demo`, `/checkout` |
|
||||
| Custom Component `RoutePath` | `data/App.DeveloperKit.CustomComponents/{Id}.json` | `/home`, `/about`, `/contact` |
|
||||
|
||||
Aynı yolu ikisi de tanımlıyorsa **bileşen kazanır** — çalışma zamanında da bileşen fiziksel
|
||||
rotayı gölgeler (`SeoAppService.CollectSeoRoutesAsync`). `/admin/` ile başlayan hiçbir yol için
|
||||
alan doldurulmaz; yönetim yüzeyi zaten `noindex` gelir.
|
||||
|
||||
**Ne zaman:** public bir `RoutePath` taşıyan Custom Component, `Pub` modülüne eklenen bir menü,
|
||||
ya da ziyaretçiye açık yeni bir fiziksel sayfa ürettiğinde.
|
||||
|
||||
```jsonc
|
||||
// data/App.Menus.Routes.json ya da data/App.DeveloperKit.CustomComponents/{Id}.json
|
||||
// içindeki satırın üzerine, var olan alanların yanına:
|
||||
{
|
||||
"Path": "/kurslar", // Custom Component'te: "RoutePath"
|
||||
"SeoTitle": "::App.PublicSeo.Courses.Title", // dil anahtarı ya da düz metin
|
||||
"SeoDescription": "::App.PublicSeo.Courses.Description",
|
||||
"SeoKeywords": "::App.PublicSeo.Courses.Keywords",
|
||||
"SeoOgImage": null, // boşsa ayarlardaki varsayılan paylaşım görseli
|
||||
"SeoCanonicalUrl": null, // boşsa site adresi + yol
|
||||
"SeoNoIndex": false,
|
||||
"SeoIncludeInSitemap": true,
|
||||
"SeoChangeFrequency": "weekly",
|
||||
"SeoPriority": 0.7,
|
||||
"SeoStructuredData": null // ham JSON-LD; sayfaya olduğu gibi eklenir
|
||||
}
|
||||
```
|
||||
|
||||
**Kurallar:**
|
||||
|
||||
| Kural | Neden |
|
||||
| --- | --- |
|
||||
| Metin alanları **dil anahtarı** taşır (`::App.PublicSeo.{Sayfa}.{Alan}`), anahtarlar `LanguagesData.json`'a EN + TR yazılır | Kayıt kültür başına çoğalmaz; çeviri dil dosyasında, tek yerde durur (standing default 4) |
|
||||
| `Title` 60, `Description` 160 karakteri aşmaz | Arama sonucunda kırpılır |
|
||||
| Metni uydurma — sayfanın kendi içeriğinden türet | Sayfada yazmayan bir vaat meta'da da yazmamalı |
|
||||
| Kişiye özel / tek kullanımlık akış (`/checkout`, `/payment`, `/success`) → `SeoNoIndex: true`, `SeoIncludeInSitemap: false` | İçeriği olmayan, tekrar ziyaret edilemeyen adres |
|
||||
| Parametreli rota (`/blog/:id`) alan taşımaz | Bir adres değil kalıptır; tek tek adresler sitemap'e veriden üretilir (`SeoAppService`) |
|
||||
| Yönlendiren adres → `SeoCanonicalUrl` hedefi gösterir, `SeoIncludeInSitemap: false` | Aynı içerik iki adreste görünmesin |
|
||||
| **Her satır** `SeoNoIndex` ve `SeoIncludeInSitemap` taşır — ziyaretçiye açık olmayanlar da (`true`/`false`) | Alanlar ekranın alan listesinde; `NOT NULL` bit kolonu satırda yoksa insert düşer. Diğer non-nullable alanlarla (`IsActive`, `IsDisabled`) aynı kural |
|
||||
|
||||
**Genel varsayılanlar kayıtta değil ayardadır:** site adresi, site adı, varsayılan başlık ve
|
||||
açıklama, paylaşım görseli, Twitter hesabı, diller, robots yönergesi, Google doğrulaması,
|
||||
sosyal hesaplar ve sitemap anahtarı `App.SiteManagement.Seo.*` ayarlarındadır
|
||||
(`configs/seeds/host/data/App.SettingDefinitions.json`). Bir değer bütün site için geçerliyse
|
||||
oraya yazılır, her yola tekrarlanmaz. Site kökü (`/`) hiçbir kayıt taşımaz; doğrudan bu
|
||||
varsayılanlardan beslenir.
|
||||
|
||||
**Akış:** ayarlar + iki kaynaktan toplanan yol kayıtları uygulama konfigürasyonunun `seo`
|
||||
alanıyla ziyaretçiye taşınır (`PlatformApplicationConfigurationContributor`), istemcide
|
||||
`SeoHead` sayfanın başlığını, açıklamasını, kanonik adresini, `og:`/`twitter:` kartını ve
|
||||
JSON-LD belgelerini kurar; `::` ile başlayan değerler orada çevrilir (`resolveSeoText`).
|
||||
`sitemap.xml` ve `robots.txt` aynı kaynaktan `SeoController` tarafından üretilir.
|
||||
|
||||
## 1. Hangi istek → hangi artefakt
|
||||
|
||||
| İstek | Üretilecek artefakt | Bölüm |
|
||||
|
|
|
|||
42
README.md
|
|
@ -1067,11 +1067,53 @@ hâlde ikinci rotasız bileşen birincisiyle çakışırdı.
|
|||
`/home`, `/about`, `/contact` bu yolla çalışır (`PublicHomePage`, `PublicAboutPage`,
|
||||
`PublicContactPage`); üst ve alt bilgi rotasızdır (`PublicHeader`, `PublicFooter`).
|
||||
|
||||
Yalnızca ikon taşıyan bir bağlantı ya da düğmenin ekran okuyucuda adı olmaz; `a` ve `button`
|
||||
düğümleri bunun için `aria-label` özelliği taşır (`componentDefinitions.ts`). Görsel düğümüne
|
||||
`width`/`height` yazmak da tarayıcının yeri önceden ayırmasını sağlar ve yerleşim kaymasını
|
||||
(CLS) önler — gösterim ölçüsü `className`'den gelmeye devam eder.
|
||||
|
||||
Veriden beslenen public sayfalar (`/products`, `/checkout`, `/payment`, `/success`, `/blog`,
|
||||
`/blog/:id`, `/demo`) fiziksel kalır; içerik sayfaları Custom Component'tir. Blog listesi ve detayı
|
||||
`PublicAppService`'in `post-list` / `post-by-slug` uçlarından beslenir, yönetimi
|
||||
`App.BlogManagement.*` ekranlarındadır.
|
||||
|
||||
**SEO.** Ziyaretçiye açık yüzeyin arama motoru sözleşmesi üç parçadır:
|
||||
|
||||
| Parça | Nerede | Ne taşır |
|
||||
| --- | --- | --- |
|
||||
| Genel varsayılanlar | `App.SiteManagement.Seo.*` ayarları (Ayarlar ekranı, yetki `App.SiteManagement.Seo`) | Site adresi, site adı, varsayılan başlık/açıklama/paylaşım görseli, Twitter hesabı, diller, robots yönergesi, Google doğrulaması, sosyal hesaplar, sitemap anahtarı |
|
||||
| Yol bazlı alanlar | Yolu tanımlayan kaydın `Seo*` kolonları: `Route` (`Sas_H_Route`, ekran **Menü Yönetimi › Route**) ve `CustomComponent` (`Sas_H_CustomComponent`, ekran **DeveloperKit › Custom Components**) | Yolun başlığı, açıklaması, anahtar kelimeleri, paylaşım görseli, kanonik adresi, `SeoNoIndex`, sitemap davranışı ve ek JSON-LD |
|
||||
| İstemci | `components/shared/SeoHead.tsx` | `title`, `description`, `robots`, `canonical`, `og:`/`twitter:` kartı, `og:locale:alternate` ve JSON-LD |
|
||||
|
||||
Ayrı bir SEO tablosu yoktur: meta, yolu tanımlayan kaydın üzerinde yaşar. `SeoAppService`
|
||||
iki kaynağı tek sözleşmede birleştirir — `RouteType = public` olan `Route` satırları ve rota
|
||||
taşıyan aktif Custom Component'ler. Aynı yolu ikisi de tanımlıyorsa bileşen kazanır; çalışma
|
||||
zamanında da bileşen fiziksel rotayı gölgeler.
|
||||
|
||||
Alanlar ve ayarlar uygulama konfigürasyonunun `seo` alanıyla ziyaretçiye taşınır
|
||||
(`PlatformApplicationConfigurationContributor` → `SeoAppService.GetConfigAsync`); sayfa ayrı bir
|
||||
istek atmaz, meta ilk boyamada hazırdır. Yol çözümü daraldıkça öncelik kazanır: birebir yol, sonra
|
||||
en uzun joker (`/blog/*`). Metin alanları kültür başına çoğalmaz: `::Anahtar` biçimindeki değer
|
||||
istemcide çevrilir (`resolveSeoText`), düz metin olduğu gibi kullanılır.
|
||||
Sayfanın kendi `SeoHead`'i, kabuğun (`PublicLayout`) `fallback` kaydını her zaman yener —
|
||||
`PageTitle` ile aynı çözümleme.
|
||||
|
||||
`sitemap.xml` ve `robots.txt` aynı servisten üretilir (`SeoController`, anonim). Sitemap'in
|
||||
kaynakları: `Pub` menüleri, aktif custom component rotaları, yayınlanmış blog yazıları ve
|
||||
yolu tanımlayan kayıtların `Seo*` alanları; `/admin` yolları, parametreli rotalar (`/blog/:id`) ve
|
||||
`SeoNoIndex` işaretli yollar elenir. Arama motorları bu iki dosyayı **sitenin** kök dizininde aradığı için UI sunucusu her iki
|
||||
yolu da API'ye geçirir (`vite.config.ts` > `seoProxy`, hem `server` hem `preview`); ayrı bir ters
|
||||
vekil kuralı gerekmez. `VITE_API_URL` tanımlı değilse proxy kurulmaz ve `ui/public/robots.txt`
|
||||
içindeki statik varsayılan geçerli kalır.
|
||||
|
||||
Yönetim ve oturum yüzeyi `noindex` gelir (`Layouts.tsx`), satın alma akışı (`/checkout`,
|
||||
`/payment`, `/success`) kendi sayfasında `noIndex` bildirir.
|
||||
|
||||
**Ziyaretçiye açık yeni bir yol üreten her iş SEO alanlarını da doldurur**; iki ekranın da
|
||||
`SeedSync*` bayrakları açık olduğu için ekrandan girilen değer `data/App.Menus.Routes.json` ya da
|
||||
`data/App.DeveloperKit.CustomComponents/{Id}.json` dosyasına düşer.
|
||||
Ayrıntı: `.github/instructions/lowcode.instructions.md` §0.9.
|
||||
|
||||
Eski statik içerik sayfaları ve sayfa içi tasarım modu (`?design=1`) kaldırılmıştır:
|
||||
`views/public/Home|About|Contact|Services`, `views/public/designer/`, bunlara özel istemci
|
||||
servisleri; sunucuda `Home`/`About`/`Contact`/`Service` entity'leri, `PublicAppService` sayfa
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
using System.Threading.Tasks;
|
||||
using Volo.Abp.Application.Services;
|
||||
|
||||
namespace Sozsoft.Platform.Public;
|
||||
|
||||
/// <summary>
|
||||
/// Public sitenin arama motoru yuzeyi. Tamami anonim erisime aciktir: sozlesme uygulama
|
||||
/// konfigurasyonuyla ziyaretciye tasinir, sitemap ve robots metni ise <c>SeoController</c>
|
||||
/// uzerinden ham metin olarak yayinlanir.
|
||||
/// </summary>
|
||||
public interface ISeoAppService : IApplicationService
|
||||
{
|
||||
Task<SeoConfigDto> GetConfigAsync();
|
||||
|
||||
/// <param name="baseUrl">Ayarda site adresi bos birakildiginda kullanilan istek adresi.</param>
|
||||
Task<string> GetSitemapAsync(string baseUrl);
|
||||
|
||||
Task<string> GetRobotsAsync(string baseUrl);
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Sozsoft.Platform.Public;
|
||||
|
||||
/// <summary>
|
||||
/// Ziyaretciye acik SEO sozlesmesi. Istemci her sayfada bu sozlesmeden kendi metasini kurar:
|
||||
/// once yola karsilik gelen <see cref="Routes"/> kaydi, yoksa buradaki varsayilanlar.
|
||||
/// </summary>
|
||||
public class SeoConfigDto
|
||||
{
|
||||
/// <summary>Kanonik kok adres; bos ise istemci tarayicidaki adresi kullanir.</summary>
|
||||
public string SiteUrl { get; set; } = string.Empty;
|
||||
|
||||
public string SiteName { get; set; } = string.Empty;
|
||||
public string DefaultTitle { get; set; } = string.Empty;
|
||||
public string DefaultDescription { get; set; } = string.Empty;
|
||||
public string DefaultKeywords { get; set; } = string.Empty;
|
||||
public string DefaultImage { get; set; } = string.Empty;
|
||||
public string TwitterSite { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>hreflang baglantilarinin uretildigi kultur listesi.</summary>
|
||||
public List<string> Locales { get; set; } = [];
|
||||
|
||||
/// <summary>Varsayilan robots yonergesi (<c>index, follow</c>).</summary>
|
||||
public string Robots { get; set; } = string.Empty;
|
||||
|
||||
public string GoogleSiteVerification { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>Organization yapisal verisinin <c>sameAs</c> alani.</summary>
|
||||
public List<string> SameAs { get; set; } = [];
|
||||
|
||||
public List<SeoRouteDto> Routes { get; set; } = [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tek bir yolun SEO sozlesmesi. Ayri bir SEO tablosu yoktur: kayit ya <c>Route</c>
|
||||
/// satirindan ya da rota tasiyan bir Custom Component'ten gelir.
|
||||
/// </summary>
|
||||
public class SeoRouteDto
|
||||
{
|
||||
/// <summary>Uygulama yolu; <c>/blog/*</c> bicimi alt agacin tamamini kapsar.</summary>
|
||||
public string RoutePath { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>Dogrudan metin ya da <c>::Anahtar</c> bicimiyle dil anahtari; cozumlemeyi istemci yapar.</summary>
|
||||
public string? Title { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public string? Keywords { get; set; }
|
||||
public string? OgImage { get; set; }
|
||||
public string? CanonicalUrl { get; set; }
|
||||
public bool NoIndex { get; set; }
|
||||
public string? StructuredData { get; set; }
|
||||
}
|
||||
403
api/src/Sozsoft.Platform.Application/Public/SeoAppService.cs
Normal file
|
|
@ -0,0 +1,403 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Sozsoft.Platform.Entities;
|
||||
using Volo.Abp.Domain.Repositories;
|
||||
using Volo.Abp.Settings;
|
||||
using SeoSettings = Sozsoft.Platform.PlatformConsts.AbpSettings.SiteManagement.Seo;
|
||||
|
||||
namespace Sozsoft.Platform.Public;
|
||||
|
||||
/// <summary>
|
||||
/// Public sitenin arama motoru yuzeyi: sayfa metasinin sozlesmesi, sitemap ve robots metni.
|
||||
/// Tamami anonim erisime aciktir.
|
||||
/// </summary>
|
||||
[AllowAnonymous]
|
||||
public class SeoAppService : PlatformAppService, ISeoAppService
|
||||
{
|
||||
/// <summary>Sitemap'e yalnizca ziyaretciye acik yollar girer; yonetim yuzeyi disaridadir.</summary>
|
||||
private const string AdminPathPrefix = "/admin";
|
||||
|
||||
private const string DefaultChangeFrequency = "weekly";
|
||||
|
||||
/// <summary>Route tablosunda ziyaretciye acik satirlarin tipi.</summary>
|
||||
private const string PublicRouteType = "public";
|
||||
|
||||
private readonly ISettingProvider _settingProvider;
|
||||
private readonly IRepository<Menu, Guid> _menuRepository;
|
||||
private readonly IRepository<CustomComponent, Guid> _customComponentRepository;
|
||||
private readonly IRepository<BlogPost, Guid> _postRepository;
|
||||
private readonly IRepository<Route, Guid> _routeRepository;
|
||||
|
||||
public SeoAppService(
|
||||
ISettingProvider settingProvider,
|
||||
IRepository<Menu, Guid> menuRepository,
|
||||
IRepository<CustomComponent, Guid> customComponentRepository,
|
||||
IRepository<BlogPost, Guid> postRepository,
|
||||
IRepository<Route, Guid> routeRepository)
|
||||
{
|
||||
_settingProvider = settingProvider;
|
||||
_menuRepository = menuRepository;
|
||||
_customComponentRepository = customComponentRepository;
|
||||
_postRepository = postRepository;
|
||||
_routeRepository = routeRepository;
|
||||
}
|
||||
|
||||
public async Task<SeoConfigDto> GetConfigAsync()
|
||||
{
|
||||
var config = new SeoConfigDto
|
||||
{
|
||||
SiteUrl = NormalizeBaseUrl(await GetSettingAsync(SeoSettings.SiteUrl)),
|
||||
SiteName = await GetSettingAsync(SeoSettings.SiteName),
|
||||
DefaultTitle = await GetSettingAsync(SeoSettings.DefaultTitle),
|
||||
DefaultDescription = await GetSettingAsync(SeoSettings.DefaultDescription),
|
||||
DefaultKeywords = await GetSettingAsync(SeoSettings.DefaultKeywords),
|
||||
DefaultImage = await GetSettingAsync(SeoSettings.DefaultImage),
|
||||
TwitterSite = await GetSettingAsync(SeoSettings.TwitterSite),
|
||||
Robots = await GetSettingAsync(SeoSettings.Robots),
|
||||
GoogleSiteVerification = await GetSettingAsync(SeoSettings.GoogleSiteVerification),
|
||||
Locales = SplitList(await GetSettingAsync(SeoSettings.Locales)),
|
||||
SameAs = SplitList(await GetSettingAsync(SeoSettings.OrganizationSameAs)),
|
||||
};
|
||||
|
||||
config.Routes = [.. (await CollectSeoRoutesAsync())
|
||||
.Select(entry => entry.ToDto())
|
||||
.OrderBy(route => route.RoutePath, StringComparer.Ordinal)];
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
public async Task<string> GetSitemapAsync(string baseUrl)
|
||||
{
|
||||
if (!await GetBoolSettingAsync(SeoSettings.SitemapEnabled))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var siteUrl = await ResolveSiteUrlAsync(baseUrl);
|
||||
var excluded = SplitLines(await GetSettingAsync(SeoSettings.SitemapExcludePaths));
|
||||
var entries = await CollectSitemapEntriesAsync(excluded);
|
||||
|
||||
var builder = new StringBuilder();
|
||||
builder.AppendLine("""<?xml version="1.0" encoding="UTF-8"?>""");
|
||||
builder.AppendLine("""<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">""");
|
||||
|
||||
foreach (var entry in entries)
|
||||
{
|
||||
builder.AppendLine(" <url>");
|
||||
builder.AppendLine($" <loc>{Escape(siteUrl + entry.Path)}</loc>");
|
||||
|
||||
if (entry.LastModified.HasValue)
|
||||
{
|
||||
builder.AppendLine($" <lastmod>{entry.LastModified.Value.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)}</lastmod>");
|
||||
}
|
||||
|
||||
builder.AppendLine($" <changefreq>{Escape(entry.ChangeFrequency)}</changefreq>");
|
||||
builder.AppendLine($" <priority>{entry.Priority.ToString("0.0", CultureInfo.InvariantCulture)}</priority>");
|
||||
builder.AppendLine(" </url>");
|
||||
}
|
||||
|
||||
builder.AppendLine("</urlset>");
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
public async Task<string> GetRobotsAsync(string baseUrl)
|
||||
{
|
||||
var robots = await GetSettingAsync(SeoSettings.Robots);
|
||||
var siteUrl = await ResolveSiteUrlAsync(baseUrl);
|
||||
|
||||
var builder = new StringBuilder();
|
||||
builder.AppendLine("User-agent: *");
|
||||
|
||||
// Ayar "dizine ekleme" derse butun site kapatilir; aksi halde yalnizca ziyaretciye
|
||||
// acik olmayan yuzeyler disarida birakilir.
|
||||
if (robots.Contains("noindex", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
builder.AppendLine("Disallow: /");
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
builder.AppendLine("Disallow: /admin/");
|
||||
builder.AppendLine("Disallow: /auth/");
|
||||
builder.AppendLine("Disallow: /checkout");
|
||||
builder.AppendLine("Disallow: /payment");
|
||||
builder.AppendLine("Disallow: /success");
|
||||
|
||||
foreach (var path in SplitLines(await GetSettingAsync(SeoSettings.SitemapExcludePaths)))
|
||||
{
|
||||
builder.AppendLine($"Disallow: {path.TrimEnd('*')}");
|
||||
}
|
||||
|
||||
if (await GetBoolSettingAsync(SeoSettings.SitemapEnabled))
|
||||
{
|
||||
builder.AppendLine();
|
||||
builder.AppendLine($"Sitemap: {siteUrl}/sitemap.xml");
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private async Task<List<SitemapEntry>> CollectSitemapEntriesAsync(List<string> excluded)
|
||||
{
|
||||
var entries = new Dictionary<string, SitemapEntry>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
void Add(string? path, DateTime? lastModified, string changeFrequency, decimal priority)
|
||||
{
|
||||
var normalized = NormalizePath(path);
|
||||
if (normalized == null || IsExcluded(normalized, excluded) || entries.ContainsKey(normalized))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
entries[normalized] = new SitemapEntry(normalized, lastModified, changeFrequency, priority);
|
||||
}
|
||||
|
||||
// Ana sayfa menude bir kayit olmayabilir; site kokunun her zaman listede olmasi gerekir.
|
||||
Add("/", null, "daily", 1.0m);
|
||||
|
||||
var menus = await _menuRepository.GetListAsync(
|
||||
menu => menu.ModuleId == PlatformConsts.PublicSite.ModuleId && !menu.IsDisabled);
|
||||
|
||||
foreach (var menu in menus.OrderBy(menu => menu.Order))
|
||||
{
|
||||
Add(menu.Url, null, DefaultChangeFrequency, 0.8m);
|
||||
}
|
||||
|
||||
var components = await _customComponentRepository.GetListAsync(component => component.IsActive);
|
||||
|
||||
foreach (var component in components)
|
||||
{
|
||||
Add(component.RoutePath, component.LastModificationTime ?? component.CreationTime, DefaultChangeFrequency, 0.7m);
|
||||
}
|
||||
|
||||
var posts = await _postRepository.GetListAsync(post => post.IsPublished);
|
||||
|
||||
foreach (var post in posts)
|
||||
{
|
||||
Add($"/blog/{post.Slug}", post.LastModificationTime ?? post.PublishedAt, "monthly", 0.6m);
|
||||
}
|
||||
|
||||
// Yolu tanimlayan kayitlarin kendi SEO alanlari hem yeni yol ekler hem de
|
||||
// yukaridakilerin sirasini ve sikligini ezer.
|
||||
foreach (var seo in await CollectSeoRoutesAsync())
|
||||
{
|
||||
if (seo.RoutePath.Contains('*'))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (seo.NoIndex || !seo.IncludeInSitemap)
|
||||
{
|
||||
entries.Remove(seo.RoutePath);
|
||||
continue;
|
||||
}
|
||||
|
||||
entries[seo.RoutePath] = new SitemapEntry(
|
||||
seo.RoutePath,
|
||||
seo.LastModified,
|
||||
seo.ChangeFrequency.IsNullOrWhiteSpace() ? DefaultChangeFrequency : seo.ChangeFrequency!,
|
||||
seo.Priority ?? 0.5m);
|
||||
}
|
||||
|
||||
return [.. entries.Values.OrderBy(entry => entry.Path, StringComparer.Ordinal)];
|
||||
}
|
||||
|
||||
/// <summary>Uygulama ici yollari tek bicime indirger; disa donuk ve yonetim adresleri elenir.</summary>
|
||||
private static string? NormalizePath(string? path)
|
||||
{
|
||||
if (path.IsNullOrWhiteSpace())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var value = path!.Trim();
|
||||
|
||||
if (value.Contains("://", StringComparison.Ordinal))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!value.StartsWith('/'))
|
||||
{
|
||||
value = "/" + value;
|
||||
}
|
||||
|
||||
if (value.StartsWith(AdminPathPrefix + "/", StringComparison.OrdinalIgnoreCase)
|
||||
|| value.Equals(AdminPathPrefix, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Parametreli rota (/blog/:id) bir adres degil, bir kaliptir.
|
||||
if (value.Contains(':', StringComparison.Ordinal))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return value.Length > 1 ? value.TrimEnd('/') : value;
|
||||
}
|
||||
|
||||
private static bool IsExcluded(string path, List<string> excluded)
|
||||
{
|
||||
return excluded.Exists(rule => rule.EndsWith('*')
|
||||
? path.StartsWith(rule.TrimEnd('*'), StringComparison.OrdinalIgnoreCase)
|
||||
: path.Equals(rule, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
private async Task<string> ResolveSiteUrlAsync(string baseUrl)
|
||||
{
|
||||
var configured = NormalizeBaseUrl(await GetSettingAsync(SeoSettings.SiteUrl));
|
||||
|
||||
return configured.IsNullOrWhiteSpace() ? NormalizeBaseUrl(baseUrl) : configured;
|
||||
}
|
||||
|
||||
private static string NormalizeBaseUrl(string value)
|
||||
{
|
||||
return value.IsNullOrWhiteSpace() ? string.Empty : value.Trim().TrimEnd('/');
|
||||
}
|
||||
|
||||
private async Task<string> GetSettingAsync(string name)
|
||||
{
|
||||
return await _settingProvider.GetOrNullAsync(name) ?? string.Empty;
|
||||
}
|
||||
|
||||
private async Task<bool> GetBoolSettingAsync(string name)
|
||||
{
|
||||
var value = await GetSettingAsync(name);
|
||||
|
||||
return value.IsNullOrWhiteSpace() || value.Equals("true", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static List<string> SplitList(string value)
|
||||
{
|
||||
return [.. value
|
||||
.Split([',', ';', '\n', '\r'], StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)];
|
||||
}
|
||||
|
||||
private static List<string> SplitLines(string value)
|
||||
{
|
||||
return [.. value
|
||||
.Split(['\n', '\r'], StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)];
|
||||
}
|
||||
|
||||
private static string Escape(string value)
|
||||
{
|
||||
return value
|
||||
.Replace("&", "&", StringComparison.Ordinal)
|
||||
.Replace("<", "<", StringComparison.Ordinal)
|
||||
.Replace(">", ">", StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SEO kayitlarini yolu tanimlayan iki kaynaktan toplar: fiziksel sayfalarin <c>Route</c>
|
||||
/// satirlari ve rota tasiyan Custom Component kayitlari. Ayni yolu ikisi de tanimliyorsa
|
||||
/// bilesen kazanir; calisma zamaninda da bilesen fiziksel rotayi golgeler.
|
||||
/// </summary>
|
||||
private async Task<List<SeoEntry>> CollectSeoRoutesAsync()
|
||||
{
|
||||
var entries = new Dictionary<string, SeoEntry>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
void Add(SeoEntry? entry)
|
||||
{
|
||||
if (entry != null)
|
||||
{
|
||||
entries[entry.RoutePath] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
var routes = await _routeRepository.GetListAsync(route => route.RouteType == PublicRouteType);
|
||||
|
||||
foreach (var route in routes)
|
||||
{
|
||||
Add(SeoEntry.From(
|
||||
route.Path,
|
||||
route.SeoTitle, route.SeoDescription, route.SeoKeywords, route.SeoOgImage,
|
||||
route.SeoCanonicalUrl, route.SeoNoIndex, route.SeoIncludeInSitemap,
|
||||
route.SeoChangeFrequency, route.SeoPriority, route.SeoStructuredData,
|
||||
route.LastModificationTime ?? route.CreationTime));
|
||||
}
|
||||
|
||||
var components = await _customComponentRepository.GetListAsync(component => component.IsActive);
|
||||
|
||||
foreach (var component in components)
|
||||
{
|
||||
Add(SeoEntry.From(
|
||||
component.RoutePath,
|
||||
component.SeoTitle, component.SeoDescription, component.SeoKeywords, component.SeoOgImage,
|
||||
component.SeoCanonicalUrl, component.SeoNoIndex, component.SeoIncludeInSitemap,
|
||||
component.SeoChangeFrequency, component.SeoPriority, component.SeoStructuredData,
|
||||
component.LastModificationTime ?? component.CreationTime));
|
||||
}
|
||||
|
||||
return [.. entries.Values];
|
||||
}
|
||||
|
||||
/// <summary>Iki kaynagin SEO alanlarini tek bicime indirger.</summary>
|
||||
private sealed record SeoEntry(
|
||||
string RoutePath,
|
||||
string? Title,
|
||||
string? Description,
|
||||
string? Keywords,
|
||||
string? OgImage,
|
||||
string? CanonicalUrl,
|
||||
bool NoIndex,
|
||||
bool IncludeInSitemap,
|
||||
string? ChangeFrequency,
|
||||
decimal? Priority,
|
||||
string? StructuredData,
|
||||
DateTime? LastModified)
|
||||
{
|
||||
/// <summary>Hicbir SEO alani doldurulmamis kayit sozlesmeye girmez; genel varsayilanlar zaten uygulanir.</summary>
|
||||
public static SeoEntry? From(
|
||||
string? routePath,
|
||||
string? title, string? description, string? keywords, string? ogImage,
|
||||
string? canonicalUrl, bool noIndex, bool includeInSitemap,
|
||||
string? changeFrequency, decimal? priority, string? structuredData,
|
||||
DateTime? lastModified)
|
||||
{
|
||||
var normalized = NormalizePath(routePath);
|
||||
if (normalized == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var hasValue = !title.IsNullOrWhiteSpace()
|
||||
|| !description.IsNullOrWhiteSpace()
|
||||
|| !keywords.IsNullOrWhiteSpace()
|
||||
|| !ogImage.IsNullOrWhiteSpace()
|
||||
|| !canonicalUrl.IsNullOrWhiteSpace()
|
||||
|| !structuredData.IsNullOrWhiteSpace()
|
||||
|| !changeFrequency.IsNullOrWhiteSpace()
|
||||
|| priority.HasValue
|
||||
|| noIndex
|
||||
|| !includeInSitemap;
|
||||
|
||||
return hasValue
|
||||
? new SeoEntry(normalized, title, description, keywords, ogImage, canonicalUrl,
|
||||
noIndex, includeInSitemap, changeFrequency, priority, structuredData, lastModified)
|
||||
: null;
|
||||
}
|
||||
|
||||
public SeoRouteDto ToDto()
|
||||
{
|
||||
return new SeoRouteDto
|
||||
{
|
||||
RoutePath = RoutePath,
|
||||
Title = Title,
|
||||
Description = Description,
|
||||
Keywords = Keywords,
|
||||
OgImage = OgImage,
|
||||
CanonicalUrl = CanonicalUrl,
|
||||
NoIndex = NoIndex,
|
||||
StructuredData = StructuredData,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private sealed record SitemapEntry(string Path, DateTime? LastModified, string ChangeFrequency, decimal Priority);
|
||||
}
|
||||
|
|
@ -1,7 +1,10 @@
|
|||
using System;
|
||||
using System.Data;
|
||||
using System.Text.Json;
|
||||
using Sozsoft.Platform.Entities;
|
||||
using Sozsoft.Platform.Enums;
|
||||
using Sozsoft.Languages.Languages;
|
||||
using Sozsoft.Platform.Editors;
|
||||
using Sozsoft.Platform.ListForms;
|
||||
using Sozsoft.Platform.Queries;
|
||||
using static Sozsoft.Platform.PlatformConsts;
|
||||
|
|
@ -277,4 +280,60 @@ public static class ListFormSeeder_DefaultJsons
|
|||
Deny = d
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bir yolun arama motoru sozlesmesi. Ayri bir SEO tablosu yoktur: alanlar yolu tanimlayan
|
||||
/// kaydin (Route, Custom Component) ekranina eklenir, bu yuzden kume tek yerde durur.
|
||||
/// Metin alanlari dogrudan yazilabilir ya da <c>::Anahtar</c> ile dil anahtari tasiyabilir.
|
||||
/// </summary>
|
||||
/// <param name="listForm">Alanlarin eklenecegi ekran.</param>
|
||||
/// <param name="editGroupOrderNo">Duzenleme formunda SEO alanlarinin toplandigi grup.</param>
|
||||
/// <param name="editOrderStart">Grup icindeki ilk siranin numarasi.</param>
|
||||
/// <param name="listOrderStart">Liste gorunumundeki ilk sutun numarasi.</param>
|
||||
public static ListFormField[] SeoFields(ListForm listForm, int editGroupOrderNo, int editOrderStart, int listOrderStart)
|
||||
{
|
||||
var editOrder = editOrderStart;
|
||||
var listOrder = listOrderStart;
|
||||
|
||||
ListFormField Field(string fieldName, DbType dbType, string editorType, string caption, string? editorOptions = null)
|
||||
{
|
||||
return new ListFormField
|
||||
{
|
||||
ListFormCode = listForm.ListFormCode,
|
||||
CultureName = LanguageCodes.En,
|
||||
SourceDbType = dbType,
|
||||
FieldName = fieldName,
|
||||
EditGroupOrderNo = editGroupOrderNo,
|
||||
EditOrderNo = editOrder++,
|
||||
EditorType2 = editorType,
|
||||
ColSpan = 1,
|
||||
EditorOptions = editorOptions,
|
||||
CaptionName = caption,
|
||||
Width = 0,
|
||||
ListOrderNo = listOrder++,
|
||||
// SEO alanlari liste gorunumunu sisirir; ekranda varsayilan olarak gizlidir,
|
||||
// duzenleme formundaki SEO grubundan yonetilir.
|
||||
Visible = false,
|
||||
IsActive = true,
|
||||
AllowSearch = false,
|
||||
ColumnCustomizationJson = DefaultColumnCustomizationJson,
|
||||
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
|
||||
PivotSettingsJson = DefaultPivotSettingsJson,
|
||||
};
|
||||
}
|
||||
|
||||
return
|
||||
[
|
||||
Field("SeoTitle", DbType.String, EditorTypes.dxTextBox, "App.Seo.Title"),
|
||||
Field("SeoDescription", DbType.String, EditorTypes.dxTextArea, "App.Seo.Description", EditorOptions.Multiline(80)),
|
||||
Field("SeoKeywords", DbType.String, EditorTypes.dxTextBox, "App.Seo.Keywords"),
|
||||
Field("SeoOgImage", DbType.String, EditorTypes.dxTextBox, "App.Seo.OgImage"),
|
||||
Field("SeoCanonicalUrl", DbType.String, EditorTypes.dxTextBox, "App.Seo.CanonicalUrl"),
|
||||
Field("SeoNoIndex", DbType.Boolean, EditorTypes.dxCheckBox, "App.Seo.NoIndex"),
|
||||
Field("SeoIncludeInSitemap", DbType.Boolean, EditorTypes.dxCheckBox, "App.Seo.IncludeInSitemap"),
|
||||
Field("SeoChangeFrequency", DbType.String, EditorTypes.dxTextBox, "App.Seo.ChangeFrequency"),
|
||||
Field("SeoPriority", DbType.Decimal, EditorTypes.dxNumberBox, "App.Seo.Priority"),
|
||||
Field("SeoStructuredData", DbType.String, EditorTypes.dxTextArea, "App.Seo.StructuredData", EditorOptions.Multiline(160)),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5709,10 +5709,16 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
{
|
||||
new() {
|
||||
Order = 1, ColCount = 1, ColSpan = 1, ItemType = "group" },
|
||||
// SEO alanlari kendi grubunda toplanir; yolun metasi bu kaydin uzerinde yasar.
|
||||
new() {
|
||||
Order = 2, ColCount = 2, ColSpan = 1, ItemType = "group", Caption = "App.Seo" },
|
||||
}),
|
||||
InsertFieldsDefaultValueJson = DefaultInsertFieldsDefaultValueJson(),
|
||||
FormFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[] {
|
||||
new() { FieldName = "RouteType", FieldDbType = DbType.String, Value = "public", CustomValueType = FieldCustomValueTypeEnum.Value }
|
||||
new() { FieldName = "RouteType", FieldDbType = DbType.String, Value = "public", CustomValueType = FieldCustomValueTypeEnum.Value },
|
||||
// NOT NULL bit kolonlar; form bunlari yazmazsa insert NULL gonderir.
|
||||
new() { FieldName = "SeoNoIndex", FieldDbType = DbType.Boolean, Value = "false", CustomValueType = FieldCustomValueTypeEnum.Value },
|
||||
new() { FieldName = "SeoIncludeInSitemap", FieldDbType = DbType.Boolean, Value = "true", CustomValueType = FieldCustomValueTypeEnum.Value },
|
||||
})
|
||||
});
|
||||
|
||||
|
|
@ -5887,7 +5893,8 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
}),
|
||||
ColumnCustomizationJson = DefaultColumnCustomizationJson,
|
||||
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
|
||||
}
|
||||
},
|
||||
.. SeoFields(listForm, editGroupOrderNo: 2, editOrderStart: 1, listOrderStart: 8),
|
||||
]);
|
||||
#endregion
|
||||
}
|
||||
|
|
@ -7074,6 +7081,8 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>()
|
||||
{
|
||||
new() { Order = 1, ColCount = 2, ColSpan = 1, ItemType = "group" },
|
||||
// SEO alanlari kendi grubunda toplanir; yolun metasi bu kaydin uzerinde yasar.
|
||||
new() { Order = 2, ColCount = 2, ColSpan = 1, ItemType = "group", Caption = "App.Seo" },
|
||||
}),
|
||||
InsertFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[] {
|
||||
new() { FieldName = "CreationTime", FieldDbType = DbType.DateTimeOffset, Value = "@NOW", CustomValueType = FieldCustomValueTypeEnum.CustomKey },
|
||||
|
|
@ -7084,6 +7093,9 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
}),
|
||||
FormFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[] {
|
||||
new() { FieldName = "IsActive", FieldDbType = DbType.Boolean, Value = "true", CustomValueType = FieldCustomValueTypeEnum.Value },
|
||||
// NOT NULL bit kolonlar; form bunlari yazmazsa insert NULL gonderir.
|
||||
new() { FieldName = "SeoNoIndex", FieldDbType = DbType.Boolean, Value = "false", CustomValueType = FieldCustomValueTypeEnum.Value },
|
||||
new() { FieldName = "SeoIncludeInSitemap", FieldDbType = DbType.Boolean, Value = "true", CustomValueType = FieldCustomValueTypeEnum.Value },
|
||||
}),
|
||||
DeleteCommand = DefaultDeleteCommand(nameof(TableNameEnum.CustomComponent)),
|
||||
DeleteFieldsDefaultValueJson = DefaultDeleteFieldsDefaultValueJson(),
|
||||
|
|
@ -7329,6 +7341,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
|
||||
PivotSettingsJson = DefaultPivotSettingsJson
|
||||
},
|
||||
.. SeoFields(listForm, editGroupOrderNo: 2, editOrderStart: 1, listOrderStart: 10),
|
||||
]);
|
||||
#endregion
|
||||
}
|
||||
|
|
@ -12296,5 +12309,6 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
#endregion
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Sozsoft.Languages.Languages;
|
||||
using Sozsoft.Platform.Enums;
|
||||
|
|
@ -258,6 +258,26 @@ public static class PlatformConsts
|
|||
public const string TimedLoginEmails = Default + ".TimedLoginEmails";
|
||||
}
|
||||
|
||||
/// <summary>Public sitenin arama motoru varsayilanlari; tamami istemciye tasinir.</summary>
|
||||
public static class Seo
|
||||
{
|
||||
public const string Default = GroupName + ".Seo";
|
||||
|
||||
public const string SiteUrl = Default + ".SiteUrl";
|
||||
public const string SiteName = Default + ".SiteName";
|
||||
public const string DefaultTitle = Default + ".DefaultTitle";
|
||||
public const string DefaultDescription = Default + ".DefaultDescription";
|
||||
public const string DefaultKeywords = Default + ".DefaultKeywords";
|
||||
public const string DefaultImage = Default + ".DefaultImage";
|
||||
public const string TwitterSite = Default + ".TwitterSite";
|
||||
public const string Locales = Default + ".Locales";
|
||||
public const string Robots = Default + ".Robots";
|
||||
public const string GoogleSiteVerification = Default + ".GoogleSiteVerification";
|
||||
public const string OrganizationSameAs = Default + ".OrganizationSameAs";
|
||||
public const string SitemapEnabled = Default + ".SitemapEnabled";
|
||||
public const string SitemapExcludePaths = Default + ".SitemapExcludePaths";
|
||||
}
|
||||
|
||||
public static class Theme
|
||||
{
|
||||
public const string Default = GroupName + ".Theme";
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Sozsoft.Platform.Enums;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,29 @@ public class CustomComponent : FullAuditedEntity<Guid>
|
|||
/// </summary>
|
||||
public string? DataSources { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Arama motoru yuzeyi. Ayri bir SEO tablosu yoktur: meta, yolu tanimlayan kaydin
|
||||
/// uzerinde yasar. Metin alanlari dogrudan yazilabilir ya da <c>::Anahtar</c> bicimiyle
|
||||
/// dil anahtari tasiyabilir; kulture gore cozumlemeyi istemci yapar.
|
||||
/// Rotasiz bilesende (ust bilgi, alt bilgi) bos kalir.
|
||||
/// </summary>
|
||||
public string? SeoTitle { get; set; }
|
||||
|
||||
public string? SeoDescription { get; set; }
|
||||
public string? SeoKeywords { get; set; }
|
||||
public string? SeoOgImage { get; set; }
|
||||
public string? SeoCanonicalUrl { get; set; }
|
||||
|
||||
/// <summary>Dizine eklenmesin; sitemap disinda kalir ve <c>noindex</c> yazilir.</summary>
|
||||
public bool SeoNoIndex { get; set; }
|
||||
|
||||
public bool SeoIncludeInSitemap { get; set; } = true;
|
||||
public string? SeoChangeFrequency { get; set; }
|
||||
public decimal? SeoPriority { get; set; }
|
||||
|
||||
/// <summary>Sayfaya gomulen JSON-LD; bos ise yalnizca genel Organization verisi yazilir.</summary>
|
||||
public string? SeoStructuredData { get; set; }
|
||||
|
||||
public CustomComponent(string name, string? routePath, string code, string? props, string? description, bool isActive, string? dependencies, string? dataSources = null)
|
||||
{
|
||||
Name = name;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,28 @@ public class Route : FullAuditedEntity<Guid>
|
|||
public string RouteType { get; set; }
|
||||
public string[] Authority { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Arama motoru yuzeyi. Ayri bir SEO tablosu yoktur: meta, yolu tanimlayan kaydin
|
||||
/// uzerinde yasar. Metin alanlari dogrudan yazilabilir ya da <c>::Anahtar</c> bicimiyle
|
||||
/// dil anahtari tasiyabilir; kulture gore cozumlemeyi istemci yapar.
|
||||
/// </summary>
|
||||
public string? SeoTitle { get; set; }
|
||||
|
||||
public string? SeoDescription { get; set; }
|
||||
public string? SeoKeywords { get; set; }
|
||||
public string? SeoOgImage { get; set; }
|
||||
public string? SeoCanonicalUrl { get; set; }
|
||||
|
||||
/// <summary>Dizine eklenmesin; sitemap disinda kalir ve <c>noindex</c> yazilir.</summary>
|
||||
public bool SeoNoIndex { get; set; }
|
||||
|
||||
public bool SeoIncludeInSitemap { get; set; } = true;
|
||||
public string? SeoChangeFrequency { get; set; }
|
||||
public decimal? SeoPriority { get; set; }
|
||||
|
||||
/// <summary>Sayfaya gomulen JSON-LD; bos ise yalnizca genel Organization verisi yazilir.</summary>
|
||||
public string? SeoStructuredData { get; set; }
|
||||
|
||||
public Route(string key, string path, string componentType, string componentPath, string routeType, string[] authority)
|
||||
{
|
||||
Key = key;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
|
@ -280,6 +280,17 @@ public class PlatformDbContext :
|
|||
b.Property(x => x.ComponentPath).IsRequired().HasMaxLength(256);
|
||||
b.Property(x => x.RouteType).HasMaxLength(64);
|
||||
|
||||
// SEO metni ya dogrudan yazilir ya da `::Anahtar` bicimiyle dil anahtari tasir.
|
||||
b.Property(x => x.SeoTitle).HasMaxLength(200);
|
||||
b.Property(x => x.SeoDescription).HasMaxLength(500);
|
||||
b.Property(x => x.SeoKeywords).HasMaxLength(300);
|
||||
b.Property(x => x.SeoOgImage).HasMaxLength(500);
|
||||
b.Property(x => x.SeoCanonicalUrl).HasMaxLength(500);
|
||||
b.Property(x => x.SeoChangeFrequency).HasMaxLength(20);
|
||||
b.Property(x => x.SeoPriority).HasPrecision(2, 1);
|
||||
// Sayfaya eklenen JSON-LD belgesinin uzunlugu onceden bilinemez.
|
||||
b.Property(x => x.SeoStructuredData).HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property(x => x.Authority).HasConversion(
|
||||
v => string.Join(",", v),
|
||||
v => v.Split(',', StringSplitOptions.RemoveEmptyEntries)
|
||||
|
|
@ -693,6 +704,17 @@ public class PlatformDbContext :
|
|||
// Data sekmesindeki endpoint listesi; adet ve url uzunlugu onceden bilinemez.
|
||||
b.Property(x => x.DataSources).HasColumnType("nvarchar(max)");
|
||||
|
||||
// SEO metni ya dogrudan yazilir ya da `::Anahtar` bicimiyle dil anahtari tasir.
|
||||
b.Property(x => x.SeoTitle).HasMaxLength(200);
|
||||
b.Property(x => x.SeoDescription).HasMaxLength(500);
|
||||
b.Property(x => x.SeoKeywords).HasMaxLength(300);
|
||||
b.Property(x => x.SeoOgImage).HasMaxLength(500);
|
||||
b.Property(x => x.SeoCanonicalUrl).HasMaxLength(500);
|
||||
b.Property(x => x.SeoChangeFrequency).HasMaxLength(20);
|
||||
b.Property(x => x.SeoPriority).HasPrecision(2, 1);
|
||||
// Sayfaya eklenen JSON-LD belgesinin uzunlugu onceden bilinemez.
|
||||
b.Property(x => x.SeoStructuredData).HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasIndex(x => new { x.Name }).IsUnique().HasFilter("[IsDeleted] = 0");
|
||||
// Rotasi olmayan bilesen (alt bilgi, ust bilgi) `RoutePath` tasimaz; tekil indeks
|
||||
// yalnizca dolu degerleri kapsar, aksi halde her rotasiz bilesen digeriyle carpisir.
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
|
|||
namespace Sozsoft.Platform.Migrations
|
||||
{
|
||||
[DbContext(typeof(PlatformDbContext))]
|
||||
[Migration("20260922094111_Initial")]
|
||||
[Migration("20260922201238_Initial")]
|
||||
partial class Initial
|
||||
{
|
||||
/// <inheritdoc />
|
||||
|
|
@ -1956,6 +1956,43 @@ namespace Sozsoft.Platform.Migrations
|
|||
.HasMaxLength(512)
|
||||
.HasColumnType("nvarchar(512)");
|
||||
|
||||
b.Property<string>("SeoCanonicalUrl")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<string>("SeoChangeFrequency")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<string>("SeoDescription")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<bool>("SeoIncludeInSitemap")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("SeoKeywords")
|
||||
.HasMaxLength(300)
|
||||
.HasColumnType("nvarchar(300)");
|
||||
|
||||
b.Property<bool>("SeoNoIndex")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("SeoOgImage")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<decimal?>("SeoPriority")
|
||||
.HasPrecision(2, 1)
|
||||
.HasColumnType("decimal(2,1)");
|
||||
|
||||
b.Property<string>("SeoStructuredData")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("SeoTitle")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("nvarchar(200)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Name")
|
||||
|
|
@ -4998,6 +5035,43 @@ namespace Sozsoft.Platform.Migrations
|
|||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<string>("SeoCanonicalUrl")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<string>("SeoChangeFrequency")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<string>("SeoDescription")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<bool>("SeoIncludeInSitemap")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("SeoKeywords")
|
||||
.HasMaxLength(300)
|
||||
.HasColumnType("nvarchar(300)");
|
||||
|
||||
b.Property<bool>("SeoNoIndex")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("SeoOgImage")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<decimal?>("SeoPriority")
|
||||
.HasPrecision(2, 1)
|
||||
.HasColumnType("decimal(2,1)");
|
||||
|
||||
b.Property<string>("SeoStructuredData")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("SeoTitle")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("nvarchar(200)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Key")
|
||||
|
|
@ -1187,6 +1187,16 @@ namespace Sozsoft.Platform.Migrations
|
|||
IsActive = table.Column<bool>(type: "bit", nullable: false),
|
||||
Dependencies = table.Column<string>(type: "nvarchar(2048)", maxLength: 2048, nullable: true),
|
||||
DataSources = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
SeoTitle = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true),
|
||||
SeoDescription = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
|
||||
SeoKeywords = table.Column<string>(type: "nvarchar(300)", maxLength: 300, nullable: true),
|
||||
SeoOgImage = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
|
||||
SeoCanonicalUrl = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
|
||||
SeoNoIndex = table.Column<bool>(type: "bit", nullable: false),
|
||||
SeoIncludeInSitemap = table.Column<bool>(type: "bit", nullable: false),
|
||||
SeoChangeFrequency = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: true),
|
||||
SeoPriority = table.Column<decimal>(type: "decimal(2,1)", precision: 2, scale: 1, nullable: true),
|
||||
SeoStructuredData = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
|
|
@ -1689,6 +1699,16 @@ namespace Sozsoft.Platform.Migrations
|
|||
ComponentPath = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
|
||||
RouteType = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
|
||||
Authority = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
SeoTitle = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true),
|
||||
SeoDescription = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
|
||||
SeoKeywords = table.Column<string>(type: "nvarchar(300)", maxLength: 300, nullable: true),
|
||||
SeoOgImage = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
|
||||
SeoCanonicalUrl = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
|
||||
SeoNoIndex = table.Column<bool>(type: "bit", nullable: false),
|
||||
SeoIncludeInSitemap = table.Column<bool>(type: "bit", nullable: false),
|
||||
SeoChangeFrequency = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: true),
|
||||
SeoPriority = table.Column<decimal>(type: "decimal(2,1)", precision: 2, scale: 1, nullable: true),
|
||||
SeoStructuredData = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
|
|
@ -1953,6 +1953,43 @@ namespace Sozsoft.Platform.Migrations
|
|||
.HasMaxLength(512)
|
||||
.HasColumnType("nvarchar(512)");
|
||||
|
||||
b.Property<string>("SeoCanonicalUrl")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<string>("SeoChangeFrequency")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<string>("SeoDescription")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<bool>("SeoIncludeInSitemap")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("SeoKeywords")
|
||||
.HasMaxLength(300)
|
||||
.HasColumnType("nvarchar(300)");
|
||||
|
||||
b.Property<bool>("SeoNoIndex")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("SeoOgImage")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<decimal?>("SeoPriority")
|
||||
.HasPrecision(2, 1)
|
||||
.HasColumnType("decimal(2,1)");
|
||||
|
||||
b.Property<string>("SeoStructuredData")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("SeoTitle")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("nvarchar(200)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Name")
|
||||
|
|
@ -4995,6 +5032,43 @@ namespace Sozsoft.Platform.Migrations
|
|||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<string>("SeoCanonicalUrl")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<string>("SeoChangeFrequency")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<string>("SeoDescription")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<bool>("SeoIncludeInSitemap")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("SeoKeywords")
|
||||
.HasMaxLength(300)
|
||||
.HasColumnType("nvarchar(300)");
|
||||
|
||||
b.Property<bool>("SeoNoIndex")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("SeoOgImage")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<decimal?>("SeoPriority")
|
||||
.HasPrecision(2, 1)
|
||||
.HasColumnType("decimal(2,1)");
|
||||
|
||||
b.Property<string>("SeoStructuredData")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("SeoTitle")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("nvarchar(200)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Key")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Sozsoft.Platform.Public;
|
||||
using Volo.Abp.AspNetCore.Mvc;
|
||||
|
||||
namespace Sozsoft.Platform.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Arama motorlarinin kok dizinde bekledigi iki metin dosyasi. Adresler API alan adinda
|
||||
/// yayinlanir; site alan adindan servis edilmesi icin ters vekil <c>/sitemap.xml</c> ve
|
||||
/// <c>/robots.txt</c> yollarini buraya yonlendirir.
|
||||
/// </summary>
|
||||
[AllowAnonymous]
|
||||
public class SeoController : AbpController
|
||||
{
|
||||
private readonly ISeoAppService _seoAppService;
|
||||
|
||||
public SeoController(ISeoAppService seoAppService)
|
||||
{
|
||||
_seoAppService = seoAppService;
|
||||
}
|
||||
|
||||
[HttpGet("sitemap.xml")]
|
||||
public async Task<IActionResult> SitemapAsync()
|
||||
{
|
||||
var sitemap = await _seoAppService.GetSitemapAsync(GetRequestBaseUrl());
|
||||
|
||||
// Ayar kapaliyken bos govde yerine 404 donulur; bos bir sitemap arama motoruna
|
||||
// "bu sitede indekslenecek sayfa yok" demektir.
|
||||
return string.IsNullOrWhiteSpace(sitemap)
|
||||
? NotFound()
|
||||
: Content(sitemap, "application/xml");
|
||||
}
|
||||
|
||||
[HttpGet("robots.txt")]
|
||||
public async Task<IActionResult> RobotsAsync()
|
||||
{
|
||||
return Content(await _seoAppService.GetRobotsAsync(GetRequestBaseUrl()), "text/plain");
|
||||
}
|
||||
|
||||
private string GetRequestBaseUrl()
|
||||
{
|
||||
return $"{Request.Scheme}://{Request.Host}";
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ using Microsoft.Extensions.DependencyInjection;
|
|||
using Microsoft.Extensions.Hosting;
|
||||
using Sozsoft.Platform.Entities;
|
||||
using Sozsoft.Platform.Identity;
|
||||
using Sozsoft.Platform.Public;
|
||||
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
|
||||
using Volo.Abp.Data;
|
||||
using Volo.Abp.DependencyInjection;
|
||||
|
|
@ -75,6 +76,11 @@ public class PlatformApplicationConfigurationContributor : IApplicationConfigura
|
|||
})
|
||||
.ToList());
|
||||
|
||||
// Public sayfanin metasi ilk boyamada hazir olmali; ayri bir istek beklenirse
|
||||
// tarayici basligi ve paylasim karti bir kare gec dolar.
|
||||
var seoAppService = context.ServiceProvider.GetRequiredService<ISeoAppService>();
|
||||
context.ApplicationConfiguration.SetProperty("seo", await seoAppService.GetConfigAsync());
|
||||
|
||||
var currentUser = context.ServiceProvider.GetRequiredService<ICurrentUser>();
|
||||
if (currentUser.Id.HasValue)
|
||||
{
|
||||
|
|
|
|||
32
claude.md
|
|
@ -48,7 +48,7 @@ options you rejected.
|
|||
3. Every implementation proposal must include tenant and permission design.
|
||||
4. Never bypass platform authorization patterns.
|
||||
5. Never hardcode secrets, tenant IDs, or connection strings.
|
||||
6. Never step outside the framework without explicit approval (standing default 10).
|
||||
6. Never step outside the framework without explicit approval (standing default 11).
|
||||
|
||||
Exception:
|
||||
|
||||
|
|
@ -138,11 +138,17 @@ Bir çelişki varsa sıra: `ai.instructions.md` → `dotnet.instructions.md` →
|
|||
görsel Tur 2'nin çoğunu cevaplar, yalnızca görselden okunamayanlar sorulur.
|
||||
Bu kural yalnızca **yeni üretim** içindir; düzeltme/alan ekleme/debug doğrudan yapılır.
|
||||
Ayrıntı: `lowcode.instructions.md` §0.6–§0.7.
|
||||
7. **Ekran içi hesap/koşul ihtiyacı** → önce `EditorScript` (Script Builder tarifleri), sonra
|
||||
7. **Ziyaretçiye açık yeni yol** (public `RoutePath` taşıyan custom component, `Pub` menüsü,
|
||||
yeni public sayfa) → SEO alanları da doldurulur. Ayrı bir SEO tablosu yoktur: meta, yolu
|
||||
tanımlayan kaydın `Seo*` kolonlarındadır — `data/App.Menus.Routes.json` ya da
|
||||
`data/App.DeveloperKit.CustomComponents/{Id}.json`. Metin `::Anahtar` ile dil anahtarı taşır.
|
||||
Kullanıcı SEO bilgisi girmez; üreten yazar. Genel varsayılanlar kayıtta değil
|
||||
`App.SiteManagement.Seo.*` ayarlarındadır. Ayrıntı: `lowcode.instructions.md` §0.9.
|
||||
8. **Ekran içi hesap/koşul ihtiyacı** → önce `EditorScript` (Script Builder tarifleri), sonra
|
||||
`EditorOptions`; kod yazmak son çare.
|
||||
8. **SQL** → her zaman parametreli. String birleştirme ile sorgu kurma.
|
||||
9. **Silme** → `DeleteCommand = DefaultDeleteCommand("{Tablo}")`; ham SQL string'i yazma.
|
||||
10. **Çerçeve sınırı** → uygulama kendini kendi mekanizmalarıyla geliştirir (seed artefaktları;
|
||||
9. **SQL** → her zaman parametreli. String birleştirme ile sorgu kurma.
|
||||
10. **Silme** → `DeleteCommand = DefaultDeleteCommand("{Tablo}")`; ham SQL string'i yazma.
|
||||
11. **Çerçeve sınırı** → uygulama kendini kendi mekanizmalarıyla geliştirir (seed artefaktları;
|
||||
var olan `ui` bileşenine eksik olay/prop/küçük yetenek ekleme — komponentin %70'i hazır
|
||||
araçlarla kuruluyorsa kalanı bu eklemelerle tamamlanır, yeni bileşen yazılmaz; geriye dönük
|
||||
uyumlu tarif/seçenek/token ekleme).
|
||||
|
|
@ -234,6 +240,18 @@ Bir çelişki varsa sıra: `ai.instructions.md` → `dotnet.instructions.md` →
|
|||
duyurulur (`INotificationRealtimeNotifier`; sözleşme bildirim modülünün Domain'inde, SignalR
|
||||
gerçeklemesi barındıran uygulamada — modül katmanı ASP.NET Core'a bağlanmaz) — duyuru en iyi çabadır, istemcideki yedek yoklama kaldırılmaz.
|
||||
Ayrıntı: `README.md` §13.3–§13.5.
|
||||
- **SEO** → **ayrı bir SEO tablosu yoktur**; meta, yolu tanımlayan kaydın üzerinde yaşar. Üç
|
||||
parça birlikte çalışır: genel varsayılanlar `App.SiteManagement.Seo.*` ayarları, yol bazlı
|
||||
alanlar `Route.Seo*` ve `CustomComponent.Seo*` (ikisinin de `SeedSync*` bayrakları açık),
|
||||
istemci tarafı `SeoHead`. Üçü de uygulama konfigürasyonunun `seo` alanından beslenir
|
||||
(`PlatformApplicationConfigurationContributor` → `SeoAppService.GetConfigAsync`); `sitemap.xml`
|
||||
ve `robots.txt` aynı servisten `SeoController` ile yayınlanır. Aynı yolu iki kaynak da
|
||||
tanımlıyorsa bileşen kazanır — çalışma zamanında da bileşen fiziksel rotayı gölgeler.
|
||||
**Ziyaretçiye açık yeni bir yol üreten her iş SEO alanlarını da doldurur** (standing default 7).
|
||||
Sitemap'e giren kaynaklar (`Pub` menüleri, aktif custom component rotaları, yayınlanmış blog
|
||||
yazıları) `SeoAppService` içindedir; yeni bir public kaynak türü eklenirse toplayıcıya da
|
||||
eklenir, yoksa sayfa sitemap'te görünmez. Yeni bir meta alanı = iki entity + `SeoRouteDto` +
|
||||
`SeoFields` (seeder) + `SeoHead` birlikte.
|
||||
- **Yeni bir dil anahtarı** → `LanguagesData.json`; eklemeden önce anahtarın zaten var olup
|
||||
olmadığını kontrol et.
|
||||
- **Custom component sınıfları** → bileşen kodu veritabanında yaşar, Tailwind derlemede yalnızca
|
||||
|
|
@ -308,7 +326,9 @@ Bir çelişki varsa sıra: `ai.instructions.md` → `dotnet.instructions.md` →
|
|||
ve zamanlanmış işler (`App.BackgroundWorkers.RecurringJobs.json`) da buradadır; `HostData.json`
|
||||
kaldırıldı, `HostDataSeeder` yalnızca bağlantı cümlesi appsettings'ten gelen varsayılan veri kaynağını kurar. Dosyalar ekranın
|
||||
anahtarını (`KeyFieldName`) kullanır; `Guid` anahtarlı satırın `Id`'si iş anahtarından türetilmiş
|
||||
sabit bir GUID'dir, satır eklerken aynı kuralla üret. `App.Menus.Routes.json` dosyasına dokunma.
|
||||
sabit bir GUID'dir, satır eklerken aynı kuralla üret. `App.Menus.Routes.json` dosyasına
|
||||
yalnızca var olan satırın `Seo*` alanları için dokunulur (standing default 7); rota eklemek,
|
||||
silmek ya da yolunu değiştirmek çerçeve dışıdır.
|
||||
- **Public site** → menü `Menu` tablosunda `ModuleId = "Pub"` altında (`PlatformConsts.PublicSite`);
|
||||
`App.Public.Header` hem üst menüyü hem alt bilgideki hızlı bağlantıları besler (ikisi de
|
||||
`PublicNav` düğümü) ve ziyaretçiye uygulama konfigürasyonunun `publicMenus` alanıyla taşınır
|
||||
|
|
|
|||
|
|
@ -3774,6 +3774,16 @@
|
|||
"MultiTenancySide": 2,
|
||||
"Modules": "Sas|Adm"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.SiteManagement.Seo",
|
||||
"ParentName": "App.SiteManagement",
|
||||
"DisplayName": "App.SiteManagement.Seo",
|
||||
"Order": 4,
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 2,
|
||||
"Modules": "Sas|Adm"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Sender",
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
"Title": "App.PostsAi.AiTitle",
|
||||
"Slug": "ai-ve-gelecegi",
|
||||
"Summary": "App.PostsAi.Excerpt",
|
||||
"CoverImage": "https://images.pexels.com/photos/8386434/pexels-photo-8386434.jpeg?auto=compress&cs=tinysrgb&w=1920",
|
||||
"CoverImage": "/img/blog/ai-ve-gelecegi.webp",
|
||||
"ReadTime": "5 dk",
|
||||
"CategoryId": "20294789-3472-5326-843d-4c80a9a0b740",
|
||||
"Author": "system@sozsoft.com",
|
||||
|
|
@ -25,7 +25,7 @@
|
|||
"Title": "App.PostsWeb.WebTitle",
|
||||
"Slug": "web-gelistirmede-son-trendler",
|
||||
"Summary": "App.PostsWeb.Excerpt",
|
||||
"CoverImage": "https://images.pexels.com/photos/11035471/pexels-photo-11035471.jpeg?auto=compress&cs=tinysrgb&w=1920",
|
||||
"CoverImage": "/img/blog/web-gelistirmede-son-trendler.webp",
|
||||
"ReadTime": "7 dk",
|
||||
"CategoryId": "29997b64-a21d-5e04-a392-d6246c6cf766",
|
||||
"Author": "system@sozsoft.com",
|
||||
|
|
@ -42,7 +42,7 @@
|
|||
"Title": "App.PostsSecurity.SecurityTitle",
|
||||
"Slug": "siber-guvenlik-tehditleri-ve-korunma-yollari",
|
||||
"Summary": "App.PostsSecurity.Excerpt",
|
||||
"CoverImage": "https://images.pexels.com/photos/5380642/pexels-photo-5380642.jpeg?auto=compress&cs=tinysrgb&w=1920",
|
||||
"CoverImage": "/img/blog/siber-guvenlik-tehditleri-ve-korunma-yollari.webp",
|
||||
"ReadTime": "6 dk",
|
||||
"CategoryId": "748a96c1-eba8-53a7-9c3d-7317f12ec627",
|
||||
"Author": "system@sozsoft.com",
|
||||
|
|
@ -59,7 +59,7 @@
|
|||
"Title": "App.PostsMobile.MobileTitle",
|
||||
"Slug": "mobil-uygulama-gelistirmede-cross-platform-cozumler",
|
||||
"Summary": "App.PostsMobile.Excerpt",
|
||||
"CoverImage": "https://images.pexels.com/photos/13017583/pexels-photo-13017583.jpeg?auto=compress&cs=tinysrgb&w=1920",
|
||||
"CoverImage": "/img/blog/mobil-uygulama-gelistirmede-cross-platform-cozumler.webp",
|
||||
"ReadTime": "4 dk",
|
||||
"CategoryId": "f282699d-8dda-5d0a-bd11-9e9b40a10161",
|
||||
"Author": "system@sozsoft.com",
|
||||
|
|
@ -76,7 +76,7 @@
|
|||
"Title": "App.PostsDatabase.DatabaseTitle",
|
||||
"Slug": "veritabani-yonetim-sistemleri-karsilastirmasi",
|
||||
"Summary": "App.PostsDatabase.Excerpt",
|
||||
"CoverImage": "https://images.pexels.com/photos/325229/pexels-photo-325229.jpeg?auto=compress&cs=tinysrgb&w=1920",
|
||||
"CoverImage": "/img/blog/veritabani-yonetim-sistemleri-karsilastirmasi.webp",
|
||||
"ReadTime": "8 dk",
|
||||
"CategoryId": "63dcbcec-e47f-5859-9de0-5bd46106ffb5",
|
||||
"Author": "system@sozsoft.com",
|
||||
|
|
@ -93,7 +93,7 @@
|
|||
"Title": "App.PostsDigital.DigitalTitle",
|
||||
"Slug": "dijital-pazarlamada-veri-analizi",
|
||||
"Summary": "App.PostsDigital.Excerpt",
|
||||
"CoverImage": "https://images.pexels.com/photos/7681091/pexels-photo-7681091.jpeg?auto=compress&cs=tinysrgb&w=1920",
|
||||
"CoverImage": "/img/blog/dijital-pazarlamada-veri-analizi.webp",
|
||||
"ReadTime": "6 dk",
|
||||
"CategoryId": "89a1ed95-dbf9-54f1-a5a8-41dddae7a9d2",
|
||||
"Author": "system@sozsoft.com",
|
||||
|
|
|
|||
|
|
@ -13,7 +13,17 @@
|
|||
"Dependencies": [],
|
||||
"Code": "/*__SOZSOFT_VISUAL_DESIGNER__%7B%22version%22%3A1%2C%22sourceMode%22%3A%22visual%22%2C%22nodes%22%3A%5B%5D%2C%22canvas%22%3A%7B%22width%22%3A%22responsive%22%7D%2C%22lifecycle%22%3A%7B%22onMount%22%3A%22%22%7D%2C%22dataSources%22%3A%5B%5D%7D__*/\nconst NewComponent = () => {\n\n\n return (\n <>\n\n </>\n )\n}\n\nexport default NewComponent",
|
||||
"Props": "{\"visualDesigner\":{\"version\":1,\"sourceMode\":\"visual\",\"nodes\":[],\"canvas\":{\"width\":\"responsive\"},\"lifecycle\":{\"onMount\":\"\"},\"dataSources\":[]}}",
|
||||
"DataSources": []
|
||||
"DataSources": [],
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -15,7 +15,17 @@
|
|||
],
|
||||
"Code": "const RoleListComponent = ({\n title = \"AbpRoles\"\n}) => {\n return (\n <DynamicEntityComponent id=\"c_mdljvvmq_fno52v\" title={title} />\n );\n};\n\nexport default RoleListComponent;",
|
||||
"Props": null,
|
||||
"DataSources": []
|
||||
"DataSources": [],
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -15,7 +15,17 @@
|
|||
"Props": null,
|
||||
"DataSources": "",
|
||||
"LastModificationTime": null,
|
||||
"CreationTime": "2026-09-05T17:22:11"
|
||||
"CreationTime": "2026-09-05T17:22:11",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -11,7 +11,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/public/Products",
|
||||
"RouteType": "public",
|
||||
"Authority": ""
|
||||
"Authority": "",
|
||||
"SeoTitle": "::App.PublicSeo.Products.Title",
|
||||
"SeoDescription": "::App.PublicSeo.Products.Description",
|
||||
"SeoKeywords": "::App.PublicSeo.Products.Keywords",
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": false,
|
||||
"SeoIncludeInSitemap": true,
|
||||
"SeoChangeFrequency": "weekly",
|
||||
"SeoPriority": 0.9,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "5c071295-f626-5e41-93fc-b4462a7a0528",
|
||||
|
|
@ -20,7 +30,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/public/Checkout",
|
||||
"RouteType": "public",
|
||||
"Authority": ""
|
||||
"Authority": "",
|
||||
"SeoTitle": "::App.PublicSeo.Checkout.Title",
|
||||
"SeoDescription": "::App.PublicSeo.Checkout.Description",
|
||||
"SeoKeywords": "::App.PublicSeo.Checkout.Keywords",
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "f88f7222-42e6-5bef-b2aa-a8db81d11383",
|
||||
|
|
@ -29,7 +49,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/public/Payment",
|
||||
"RouteType": "public",
|
||||
"Authority": ""
|
||||
"Authority": "",
|
||||
"SeoTitle": "::App.PublicSeo.Payment.Title",
|
||||
"SeoDescription": "::App.PublicSeo.Payment.Description",
|
||||
"SeoKeywords": "::App.PublicSeo.Payment.Keywords",
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "c4c17ec7-8423-513a-863a-6065816efe0a",
|
||||
|
|
@ -38,7 +68,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/public/Success",
|
||||
"RouteType": "public",
|
||||
"Authority": ""
|
||||
"Authority": "",
|
||||
"SeoTitle": "::App.PublicSeo.Success.Title",
|
||||
"SeoDescription": "::App.PublicSeo.Success.Description",
|
||||
"SeoKeywords": "::App.PublicSeo.Success.Keywords",
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "d3529321-2f70-5965-8e2d-7e4dc9e56dca",
|
||||
|
|
@ -47,7 +87,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/public/Blog",
|
||||
"RouteType": "public",
|
||||
"Authority": ""
|
||||
"Authority": "",
|
||||
"SeoTitle": "::App.PublicSeo.Blog.Title",
|
||||
"SeoDescription": "::App.PublicSeo.Blog.Description",
|
||||
"SeoKeywords": "::App.PublicSeo.Blog.Keywords",
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": false,
|
||||
"SeoIncludeInSitemap": true,
|
||||
"SeoChangeFrequency": "daily",
|
||||
"SeoPriority": 0.8,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "037dc714-77fc-5b0c-840c-0cdeca22df19",
|
||||
|
|
@ -56,7 +106,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/public/BlogDetail",
|
||||
"RouteType": "public",
|
||||
"Authority": ""
|
||||
"Authority": "",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "d170f3e3-b0bd-5ed0-8394-601b51f5666f",
|
||||
|
|
@ -65,7 +125,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/public/Demo",
|
||||
"RouteType": "public",
|
||||
"Authority": ""
|
||||
"Authority": "",
|
||||
"SeoTitle": "::App.PublicSeo.Demo.Title",
|
||||
"SeoDescription": "::App.PublicSeo.Demo.Description",
|
||||
"SeoKeywords": "::App.PublicSeo.Demo.Keywords",
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": false,
|
||||
"SeoIncludeInSitemap": true,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": 0.6,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "e4d6f040-ad8a-523f-bd4f-211329c651cf",
|
||||
|
|
@ -74,7 +144,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/AccessDenied",
|
||||
"RouteType": "public",
|
||||
"Authority": ""
|
||||
"Authority": "",
|
||||
"SeoTitle": "::App.PublicSeo.AccessDenied.Title",
|
||||
"SeoDescription": "::App.PublicSeo.AccessDenied.Description",
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "bf6d30a3-03cf-5475-9daa-730023459bb0",
|
||||
|
|
@ -83,7 +163,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/auth/Login",
|
||||
"RouteType": "authenticated",
|
||||
"Authority": ""
|
||||
"Authority": "",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "df9b57fd-37f7-5377-b84c-70257295261a",
|
||||
|
|
@ -92,7 +182,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/auth/Register",
|
||||
"RouteType": "authenticated",
|
||||
"Authority": ""
|
||||
"Authority": "",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "bb282d0d-062b-51b7-87d7-22545c6f0c4d",
|
||||
|
|
@ -101,7 +201,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/auth/ForgotPassword",
|
||||
"RouteType": "authenticated",
|
||||
"Authority": ""
|
||||
"Authority": "",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "7be57172-a75d-5f03-8a7a-566523dc5acc",
|
||||
|
|
@ -110,7 +220,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/auth/ResetPassword",
|
||||
"RouteType": "authenticated",
|
||||
"Authority": ""
|
||||
"Authority": "",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "2e78c334-4e75-586b-9c14-f5b5169bd895",
|
||||
|
|
@ -119,7 +239,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/auth/SendConfirmationCode",
|
||||
"RouteType": "authenticated",
|
||||
"Authority": ""
|
||||
"Authority": "",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "d5f0773b-dece-5b55-beea-db2547ff9293",
|
||||
|
|
@ -128,7 +258,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/auth/ExtendLogin",
|
||||
"RouteType": "authenticated",
|
||||
"Authority": ""
|
||||
"Authority": "",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "f639b8fd-a462-5af5-b230-d2e5bd8a8eaf",
|
||||
|
|
@ -137,7 +277,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/auth/VerifyConfirmationCode",
|
||||
"RouteType": "authenticated",
|
||||
"Authority": ""
|
||||
"Authority": "",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "56ecf681-cf6b-5968-9acc-754e3026231c",
|
||||
|
|
@ -146,7 +296,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/Dashboard",
|
||||
"RouteType": "protected",
|
||||
"Authority": ""
|
||||
"Authority": "",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "f5d4a187-ef76-5010-bb2f-14844722875d",
|
||||
|
|
@ -155,7 +315,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/menu/MenuManager",
|
||||
"RouteType": "protected",
|
||||
"Authority": "App.Menus.Manager"
|
||||
"Authority": "App.Menus.Manager",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "43285584-09c7-52cb-83ae-a35cb69bfced",
|
||||
|
|
@ -164,7 +334,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/admin/listForm/wizard/Wizard",
|
||||
"RouteType": "protected",
|
||||
"Authority": "App.Listforms.Wizard"
|
||||
"Authority": "App.Listforms.Wizard",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "e87db52d-25cd-5c0f-9aa8-01dc2dc5b032",
|
||||
|
|
@ -173,7 +353,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/admin/listForm/wizard/WizardFileManager",
|
||||
"RouteType": "protected",
|
||||
"Authority": "App.Listforms.Wizard"
|
||||
"Authority": "App.Listforms.Wizard",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "8ce5dfc6-8795-5d5e-b815-447f08292900",
|
||||
|
|
@ -182,7 +372,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/admin/listForm/edit/FormEdit",
|
||||
"RouteType": "protected",
|
||||
"Authority": ""
|
||||
"Authority": "",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "4e380430-9661-55ff-9c6b-270823b14401",
|
||||
|
|
@ -191,7 +391,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/ai/Assistant",
|
||||
"RouteType": "protected",
|
||||
"Authority": "App.Definitions.AiBot.Asistant"
|
||||
"Authority": "App.Definitions.AiBot.Asistant",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "ad21c21d-84c2-5f73-9eff-5bdd4d6754eb",
|
||||
|
|
@ -200,7 +410,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/admin/profile/Profile",
|
||||
"RouteType": "protected",
|
||||
"Authority": ""
|
||||
"Authority": "",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "43df2816-075a-5fb0-8d75-07041acedf60",
|
||||
|
|
@ -209,7 +429,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/admin/profile/Profile",
|
||||
"RouteType": "protected",
|
||||
"Authority": ""
|
||||
"Authority": "",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "5bf4ec7d-09fa-57bc-a832-915f0bd9a424",
|
||||
|
|
@ -218,7 +448,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/admin/profile/Profile",
|
||||
"RouteType": "protected",
|
||||
"Authority": ""
|
||||
"Authority": "",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "c64d7bd0-e884-55a6-915a-7a43e0c5e684",
|
||||
|
|
@ -227,7 +467,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/admin/activityLog/ActivityLog",
|
||||
"RouteType": "protected",
|
||||
"Authority": ""
|
||||
"Authority": "",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "012f714f-fb86-5292-a2f1-c11c4bf88a06",
|
||||
|
|
@ -236,7 +486,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/version/ChangeLog",
|
||||
"RouteType": "protected",
|
||||
"Authority": ""
|
||||
"Authority": "",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "507adc62-0ffd-51f0-a9d2-165eccbfc225",
|
||||
|
|
@ -245,7 +505,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/settings/Settings",
|
||||
"RouteType": "protected",
|
||||
"Authority": "App.Setting"
|
||||
"Authority": "App.Setting",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "9cae5316-b620-5100-9726-bd638cd71517",
|
||||
|
|
@ -254,7 +524,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/admin/user-management/Details",
|
||||
"RouteType": "protected",
|
||||
"Authority": "AbpIdentity.Users.Update"
|
||||
"Authority": "AbpIdentity.Users.Update",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "f44286f9-e9d8-5478-80c1-3643eb848078",
|
||||
|
|
@ -263,7 +543,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/admin/organization-unit/OrganizationUnits",
|
||||
"RouteType": "protected",
|
||||
"Authority": "Abp.Identity.OrganizationUnits"
|
||||
"Authority": "Abp.Identity.OrganizationUnits",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "dc18c096-b996-51f6-b774-14a90e74f137",
|
||||
|
|
@ -272,7 +562,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/admin/hr/OrgChart",
|
||||
"RouteType": "protected",
|
||||
"Authority": "App.Definitions.Department"
|
||||
"Authority": "App.Definitions.Department",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "53d4baee-8360-523a-8470-26eaaaa54371",
|
||||
|
|
@ -281,7 +581,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/forum/Forum",
|
||||
"RouteType": "protected",
|
||||
"Authority": "App.ForumManagement.Publish"
|
||||
"Authority": "App.ForumManagement.Publish",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "094842eb-fc95-51ea-bd57-6284f377c199",
|
||||
|
|
@ -290,7 +600,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/list/List",
|
||||
"RouteType": "protected",
|
||||
"Authority": ""
|
||||
"Authority": "",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "c2230e57-183d-56b5-8e7b-e6fde917b335",
|
||||
|
|
@ -299,7 +619,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/form/FormNew",
|
||||
"RouteType": "protected",
|
||||
"Authority": ""
|
||||
"Authority": "",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "62e585e8-7393-5749-8281-ba94f7fdd43e",
|
||||
|
|
@ -308,7 +638,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/form/FormView",
|
||||
"RouteType": "protected",
|
||||
"Authority": ""
|
||||
"Authority": "",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "fddafca0-a6ad-51fb-85b0-ccdf1cfed1d6",
|
||||
|
|
@ -317,7 +657,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/form/FormEdit",
|
||||
"RouteType": "protected",
|
||||
"Authority": ""
|
||||
"Authority": "",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "35d0b346-fe04-5a5a-896f-5e2b87e90bc3",
|
||||
|
|
@ -326,7 +676,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/list/Chart",
|
||||
"RouteType": "protected",
|
||||
"Authority": ""
|
||||
"Authority": "",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "76a3f75e-11db-560a-b749-989f2ca579d7",
|
||||
|
|
@ -335,7 +695,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/developerKit/SqlQueryManager",
|
||||
"RouteType": "protected",
|
||||
"Authority": "App.SqlQueryManager"
|
||||
"Authority": "App.SqlQueryManager",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "08891b2a-5d2e-54c5-b12a-e0e8c2547b55",
|
||||
|
|
@ -344,7 +714,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/developerKit/ComponentCodeLayout",
|
||||
"RouteType": "protected",
|
||||
"Authority": "App.DeveloperKit.CustomComponents"
|
||||
"Authority": "App.DeveloperKit.CustomComponents",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "cdbfdea5-ea04-5537-bb5c-1dd964c675fe",
|
||||
|
|
@ -353,7 +733,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/intranet/documents/IntranetDocuments",
|
||||
"RouteType": "protected",
|
||||
"Authority": "App.Intranet.Document"
|
||||
"Authority": "App.Intranet.Document",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "ea5acaee-a479-5daf-b4e6-2999578fd78d",
|
||||
|
|
@ -362,7 +752,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/admin/files/FileManager",
|
||||
"RouteType": "protected",
|
||||
"Authority": "App.Files"
|
||||
"Authority": "App.Files",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "c074e5cc-ab9c-51e9-a89c-d9f662316ab5",
|
||||
|
|
@ -371,7 +771,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/report/DevexpressReportViewer",
|
||||
"RouteType": "protected",
|
||||
"Authority": ""
|
||||
"Authority": "",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "57520752-8f21-5d81-aaf7-fa8089b83e1c",
|
||||
|
|
@ -380,7 +790,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/report/DevexpressReportDesigner",
|
||||
"RouteType": "protected",
|
||||
"Authority": ""
|
||||
"Authority": "",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "16917407-a912-53ab-ab96-d14e7ef6abd2",
|
||||
|
|
@ -389,7 +809,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/admin/videoroom/Dashboard",
|
||||
"RouteType": "protected",
|
||||
"Authority": "App.Videoroom"
|
||||
"Authority": "App.Videoroom",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "7cfef810-4738-577e-88aa-ca32f9d15ccc",
|
||||
|
|
@ -398,7 +828,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/admin/videoroom/RoomList",
|
||||
"RouteType": "protected",
|
||||
"Authority": "App.Videoroom.List"
|
||||
"Authority": "App.Videoroom.List",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
},
|
||||
{
|
||||
"Id": "b6af134c-95b8-5453-915a-4cf5d84cb600",
|
||||
|
|
@ -407,7 +847,17 @@
|
|||
"ComponentType": "normal",
|
||||
"ComponentPath": "@/views/admin/videoroom/RoomDetail",
|
||||
"RouteType": "protected",
|
||||
"Authority": "App.Videoroom.Detail"
|
||||
"Authority": "App.Videoroom.Detail",
|
||||
"SeoTitle": null,
|
||||
"SeoDescription": null,
|
||||
"SeoKeywords": null,
|
||||
"SeoOgImage": null,
|
||||
"SeoCanonicalUrl": null,
|
||||
"SeoNoIndex": true,
|
||||
"SeoIncludeInSitemap": false,
|
||||
"SeoChangeFrequency": null,
|
||||
"SeoPriority": null,
|
||||
"SeoStructuredData": null
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
"MonthlyPrice": 850,
|
||||
"YearlyPrice": 3400,
|
||||
"IsQuantityBased": true,
|
||||
"ImageUrl": "https://images.pexels.com/photos/1181673/pexels-photo-1181673.jpeg?auto=compress&cs=tinysrgb&w=300"
|
||||
"ImageUrl": "/img/products/branch-hosting.webp"
|
||||
},
|
||||
{
|
||||
"Id": "3ced7419-b72a-5bd5-b587-b76cc9778460",
|
||||
|
|
@ -23,7 +23,7 @@
|
|||
"MonthlyPrice": 2100,
|
||||
"YearlyPrice": 2100,
|
||||
"IsQuantityBased": false,
|
||||
"ImageUrl": "https://images.pexels.com/photos/442150/pexels-photo-442150.jpeg?auto=compress&cs=tinysrgb&w=300"
|
||||
"ImageUrl": "/img/products/backup-service.webp"
|
||||
},
|
||||
{
|
||||
"Id": "22d113c5-94ad-5002-8e1f-79a622ca1ac6",
|
||||
|
|
@ -34,7 +34,7 @@
|
|||
"MonthlyPrice": 5000,
|
||||
"YearlyPrice": 5000,
|
||||
"IsQuantityBased": false,
|
||||
"ImageUrl": "https://images.pexels.com/photos/3184465/pexels-photo-3184465.jpeg?auto=compress&cs=tinysrgb&w=300"
|
||||
"ImageUrl": "/img/products/remote-support-contract.webp"
|
||||
},
|
||||
{
|
||||
"Id": "39999ed6-7115-5816-bd32-5a786d68e356",
|
||||
|
|
@ -45,7 +45,7 @@
|
|||
"MonthlyPrice": 900,
|
||||
"YearlyPrice": 3500,
|
||||
"IsQuantityBased": true,
|
||||
"ImageUrl": "https://images.pexels.com/photos/3184360/pexels-photo-3184360.jpeg?auto=compress&cs=tinysrgb&w=300"
|
||||
"ImageUrl": "/img/products/user-license.webp"
|
||||
},
|
||||
{
|
||||
"Id": "0c927e71-0c51-5a2d-899c-c22302b563a0",
|
||||
|
|
@ -56,7 +56,7 @@
|
|||
"MonthlyPrice": 3700,
|
||||
"YearlyPrice": 3700,
|
||||
"IsQuantityBased": true,
|
||||
"ImageUrl": "https://images.pexels.com/photos/3184339/pexels-photo-3184339.jpeg?auto=compress&cs=tinysrgb&w=300"
|
||||
"ImageUrl": "/img/products/remote-branch-training.webp"
|
||||
},
|
||||
{
|
||||
"Id": "37d1aefa-cbfe-5251-9525-ca17becb596f",
|
||||
|
|
@ -67,7 +67,7 @@
|
|||
"MonthlyPrice": 2000,
|
||||
"YearlyPrice": 2000,
|
||||
"IsQuantityBased": true,
|
||||
"ImageUrl": "https://images.pexels.com/photos/3184291/pexels-photo-3184291.jpeg?auto=compress&cs=tinysrgb&w=300"
|
||||
"ImageUrl": "/img/products/extra-hourly-service.webp"
|
||||
},
|
||||
{
|
||||
"Id": "591e9307-c825-521e-bf06-d6f647708ca4",
|
||||
|
|
@ -78,7 +78,7 @@
|
|||
"MonthlyPrice": 750,
|
||||
"YearlyPrice": 750,
|
||||
"IsQuantityBased": true,
|
||||
"ImageUrl": "https://images.pexels.com/photos/404280/pexels-photo-404280.jpeg?auto=compress&cs=tinysrgb&w=300"
|
||||
"ImageUrl": "/img/products/sms5k.webp"
|
||||
},
|
||||
{
|
||||
"Id": "8ff856d2-30d5-5f5e-b38e-f6cebee76421",
|
||||
|
|
@ -89,7 +89,7 @@
|
|||
"MonthlyPrice": 1350,
|
||||
"YearlyPrice": 1350,
|
||||
"IsQuantityBased": true,
|
||||
"ImageUrl": "https://images.pexels.com/photos/404280/pexels-photo-404280.jpeg?auto=compress&cs=tinysrgb&w=300"
|
||||
"ImageUrl": "/img/products/sms5k.webp"
|
||||
},
|
||||
{
|
||||
"Id": "bfa0964b-e8cb-5739-8b4a-d44773553f0a",
|
||||
|
|
@ -100,7 +100,7 @@
|
|||
"MonthlyPrice": 2900,
|
||||
"YearlyPrice": 2900,
|
||||
"IsQuantityBased": true,
|
||||
"ImageUrl": "https://images.pexels.com/photos/404280/pexels-photo-404280.jpeg?auto=compress&cs=tinysrgb&w=300"
|
||||
"ImageUrl": "/img/products/sms5k.webp"
|
||||
},
|
||||
{
|
||||
"Id": "2b675f5f-320f-5424-8393-3dc0363ad27e",
|
||||
|
|
@ -111,7 +111,7 @@
|
|||
"MonthlyPrice": 5100,
|
||||
"YearlyPrice": 5100,
|
||||
"IsQuantityBased": true,
|
||||
"ImageUrl": "https://images.pexels.com/photos/404280/pexels-photo-404280.jpeg?auto=compress&cs=tinysrgb&w=300"
|
||||
"ImageUrl": "/img/products/sms5k.webp"
|
||||
},
|
||||
{
|
||||
"Id": "730c2dbf-c700-506d-b216-a169cbd85a15",
|
||||
|
|
@ -122,7 +122,7 @@
|
|||
"MonthlyPrice": 8800,
|
||||
"YearlyPrice": 8800,
|
||||
"IsQuantityBased": true,
|
||||
"ImageUrl": "https://images.pexels.com/photos/404280/pexels-photo-404280.jpeg?auto=compress&cs=tinysrgb&w=300"
|
||||
"ImageUrl": "/img/products/sms5k.webp"
|
||||
},
|
||||
{
|
||||
"Id": "53ae0678-3c01-5887-89e9-12a45bcc188f",
|
||||
|
|
@ -133,7 +133,7 @@
|
|||
"MonthlyPrice": 880,
|
||||
"YearlyPrice": 880,
|
||||
"IsQuantityBased": true,
|
||||
"ImageUrl": "https://images.pexels.com/photos/1591061/pexels-photo-1591061.jpeg?auto=compress&cs=tinysrgb&w=300"
|
||||
"ImageUrl": "/img/products/sms-blocking.webp"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1005,6 +1005,227 @@
|
|||
"IsVisibleToClients": true,
|
||||
"IsInherited": true,
|
||||
"IsEncrypted": false
|
||||
},
|
||||
{
|
||||
"Id": "e5b8222e-664c-564b-b853-d360e9a64f4c",
|
||||
"Code": "App.SiteManagement.Seo.SiteUrl",
|
||||
"MainGroupKey": "App.SiteManagement",
|
||||
"SubGroupKey": "App.SiteManagement.Seo",
|
||||
"Order": 1,
|
||||
"NameKey": "App.SiteManagement.Seo.SiteUrl",
|
||||
"DescriptionKey": "App.SiteManagement.Seo.SiteUrl.Description",
|
||||
"RequiredPermissionName": "App.SiteManagement.Seo",
|
||||
"Providers": "G|D",
|
||||
"DefaultValue": "",
|
||||
"DataType": "Text",
|
||||
"SelectOptions": "{}",
|
||||
"IsVisibleToClients": true,
|
||||
"IsInherited": false,
|
||||
"IsEncrypted": false
|
||||
},
|
||||
{
|
||||
"Id": "8001a080-ff59-5bf6-9da9-5aebc2abd41b",
|
||||
"Code": "App.SiteManagement.Seo.SiteName",
|
||||
"MainGroupKey": "App.SiteManagement",
|
||||
"SubGroupKey": "App.SiteManagement.Seo",
|
||||
"Order": 2,
|
||||
"NameKey": "App.SiteManagement.Seo.SiteName",
|
||||
"DescriptionKey": "App.SiteManagement.Seo.SiteName.Description",
|
||||
"RequiredPermissionName": "App.SiteManagement.Seo",
|
||||
"Providers": "G|D",
|
||||
"DefaultValue": "Sözsoft",
|
||||
"DataType": "Text",
|
||||
"SelectOptions": "{}",
|
||||
"IsVisibleToClients": true,
|
||||
"IsInherited": false,
|
||||
"IsEncrypted": false
|
||||
},
|
||||
{
|
||||
"Id": "5e336aa9-0a1a-56d2-8f9a-5a899ee343d2",
|
||||
"Code": "App.SiteManagement.Seo.DefaultTitle",
|
||||
"MainGroupKey": "App.SiteManagement",
|
||||
"SubGroupKey": "App.SiteManagement.Seo",
|
||||
"Order": 3,
|
||||
"NameKey": "App.SiteManagement.Seo.DefaultTitle",
|
||||
"DescriptionKey": "App.SiteManagement.Seo.DefaultTitle.Description",
|
||||
"RequiredPermissionName": "App.SiteManagement.Seo",
|
||||
"Providers": "G|D",
|
||||
"DefaultValue": "Sözsoft",
|
||||
"DataType": "Text",
|
||||
"SelectOptions": "{}",
|
||||
"IsVisibleToClients": true,
|
||||
"IsInherited": false,
|
||||
"IsEncrypted": false
|
||||
},
|
||||
{
|
||||
"Id": "a80126cc-116e-5eaa-b31a-819738b6272d",
|
||||
"Code": "App.SiteManagement.Seo.DefaultDescription",
|
||||
"MainGroupKey": "App.SiteManagement",
|
||||
"SubGroupKey": "App.SiteManagement.Seo",
|
||||
"Order": 4,
|
||||
"NameKey": "App.SiteManagement.Seo.DefaultDescription",
|
||||
"DescriptionKey": "App.SiteManagement.Seo.DefaultDescription.Description",
|
||||
"RequiredPermissionName": "App.SiteManagement.Seo",
|
||||
"Providers": "G|D",
|
||||
"DefaultValue": "",
|
||||
"DataType": "Memo",
|
||||
"SelectOptions": "{}",
|
||||
"IsVisibleToClients": true,
|
||||
"IsInherited": false,
|
||||
"IsEncrypted": false
|
||||
},
|
||||
{
|
||||
"Id": "929037b4-29dd-5e4f-8004-180dae435c3c",
|
||||
"Code": "App.SiteManagement.Seo.DefaultKeywords",
|
||||
"MainGroupKey": "App.SiteManagement",
|
||||
"SubGroupKey": "App.SiteManagement.Seo",
|
||||
"Order": 5,
|
||||
"NameKey": "App.SiteManagement.Seo.DefaultKeywords",
|
||||
"DescriptionKey": "App.SiteManagement.Seo.DefaultKeywords.Description",
|
||||
"RequiredPermissionName": "App.SiteManagement.Seo",
|
||||
"Providers": "G|D",
|
||||
"DefaultValue": "",
|
||||
"DataType": "Text",
|
||||
"SelectOptions": "{}",
|
||||
"IsVisibleToClients": true,
|
||||
"IsInherited": false,
|
||||
"IsEncrypted": false
|
||||
},
|
||||
{
|
||||
"Id": "39a4b2a8-9809-5cb8-999c-738fe94a44c7",
|
||||
"Code": "App.SiteManagement.Seo.DefaultImage",
|
||||
"MainGroupKey": "App.SiteManagement",
|
||||
"SubGroupKey": "App.SiteManagement.Seo",
|
||||
"Order": 6,
|
||||
"NameKey": "App.SiteManagement.Seo.DefaultImage",
|
||||
"DescriptionKey": "App.SiteManagement.Seo.DefaultImage.Description",
|
||||
"RequiredPermissionName": "App.SiteManagement.Seo",
|
||||
"Providers": "G|D",
|
||||
"DefaultValue": "/img/logo/logo-512.png",
|
||||
"DataType": "Text",
|
||||
"SelectOptions": "{}",
|
||||
"IsVisibleToClients": true,
|
||||
"IsInherited": false,
|
||||
"IsEncrypted": false
|
||||
},
|
||||
{
|
||||
"Id": "7ce5fce9-deee-5170-9ac5-b4e1f4aefa1c",
|
||||
"Code": "App.SiteManagement.Seo.TwitterSite",
|
||||
"MainGroupKey": "App.SiteManagement",
|
||||
"SubGroupKey": "App.SiteManagement.Seo",
|
||||
"Order": 7,
|
||||
"NameKey": "App.SiteManagement.Seo.TwitterSite",
|
||||
"DescriptionKey": "App.SiteManagement.Seo.TwitterSite.Description",
|
||||
"RequiredPermissionName": "App.SiteManagement.Seo",
|
||||
"Providers": "G|D",
|
||||
"DefaultValue": "",
|
||||
"DataType": "Text",
|
||||
"SelectOptions": "{}",
|
||||
"IsVisibleToClients": true,
|
||||
"IsInherited": false,
|
||||
"IsEncrypted": false
|
||||
},
|
||||
{
|
||||
"Id": "787c036d-e549-5207-b6c3-f4a34b7fdf6e",
|
||||
"Code": "App.SiteManagement.Seo.Locales",
|
||||
"MainGroupKey": "App.SiteManagement",
|
||||
"SubGroupKey": "App.SiteManagement.Seo",
|
||||
"Order": 8,
|
||||
"NameKey": "App.SiteManagement.Seo.Locales",
|
||||
"DescriptionKey": "App.SiteManagement.Seo.Locales.Description",
|
||||
"RequiredPermissionName": "App.SiteManagement.Seo",
|
||||
"Providers": "G|D",
|
||||
"DefaultValue": "tr,en",
|
||||
"DataType": "Text",
|
||||
"SelectOptions": "{}",
|
||||
"IsVisibleToClients": true,
|
||||
"IsInherited": false,
|
||||
"IsEncrypted": false
|
||||
},
|
||||
{
|
||||
"Id": "4e2fd424-301c-55e6-b494-b9190759fe76",
|
||||
"Code": "App.SiteManagement.Seo.Robots",
|
||||
"MainGroupKey": "App.SiteManagement",
|
||||
"SubGroupKey": "App.SiteManagement.Seo",
|
||||
"Order": 9,
|
||||
"NameKey": "App.SiteManagement.Seo.Robots",
|
||||
"DescriptionKey": "App.SiteManagement.Seo.Robots.Description",
|
||||
"RequiredPermissionName": "App.SiteManagement.Seo",
|
||||
"Providers": "G|D",
|
||||
"DefaultValue": "index, follow",
|
||||
"DataType": "List",
|
||||
"SelectOptions": "{\"index, follow\":\"App.SiteManagement.Seo.Robots.IndexFollow\",\"noindex, nofollow\":\"App.SiteManagement.Seo.Robots.NoIndexNoFollow\"}",
|
||||
"IsVisibleToClients": true,
|
||||
"IsInherited": false,
|
||||
"IsEncrypted": false
|
||||
},
|
||||
{
|
||||
"Id": "5859839b-32aa-579e-8d2a-290800bbb865",
|
||||
"Code": "App.SiteManagement.Seo.GoogleSiteVerification",
|
||||
"MainGroupKey": "App.SiteManagement",
|
||||
"SubGroupKey": "App.SiteManagement.Seo",
|
||||
"Order": 10,
|
||||
"NameKey": "App.SiteManagement.Seo.GoogleSiteVerification",
|
||||
"DescriptionKey": "App.SiteManagement.Seo.GoogleSiteVerification.Description",
|
||||
"RequiredPermissionName": "App.SiteManagement.Seo",
|
||||
"Providers": "G|D",
|
||||
"DefaultValue": "",
|
||||
"DataType": "Text",
|
||||
"SelectOptions": "{}",
|
||||
"IsVisibleToClients": true,
|
||||
"IsInherited": false,
|
||||
"IsEncrypted": false
|
||||
},
|
||||
{
|
||||
"Id": "c20e0c12-10bf-5cb0-99f5-eb3825276694",
|
||||
"Code": "App.SiteManagement.Seo.OrganizationSameAs",
|
||||
"MainGroupKey": "App.SiteManagement",
|
||||
"SubGroupKey": "App.SiteManagement.Seo",
|
||||
"Order": 11,
|
||||
"NameKey": "App.SiteManagement.Seo.OrganizationSameAs",
|
||||
"DescriptionKey": "App.SiteManagement.Seo.OrganizationSameAs.Description",
|
||||
"RequiredPermissionName": "App.SiteManagement.Seo",
|
||||
"Providers": "G|D",
|
||||
"DefaultValue": "",
|
||||
"DataType": "Memo",
|
||||
"SelectOptions": "{}",
|
||||
"IsVisibleToClients": true,
|
||||
"IsInherited": false,
|
||||
"IsEncrypted": false
|
||||
},
|
||||
{
|
||||
"Id": "2b871f1c-bd20-58eb-85d3-012e5dd835be",
|
||||
"Code": "App.SiteManagement.Seo.SitemapEnabled",
|
||||
"MainGroupKey": "App.SiteManagement",
|
||||
"SubGroupKey": "App.SiteManagement.Seo",
|
||||
"Order": 12,
|
||||
"NameKey": "App.SiteManagement.Seo.SitemapEnabled",
|
||||
"DescriptionKey": "App.SiteManagement.Seo.SitemapEnabled.Description",
|
||||
"RequiredPermissionName": "App.SiteManagement.Seo",
|
||||
"Providers": "G|D",
|
||||
"DefaultValue": "true",
|
||||
"DataType": "Bool",
|
||||
"SelectOptions": "{}",
|
||||
"IsVisibleToClients": true,
|
||||
"IsInherited": false,
|
||||
"IsEncrypted": false
|
||||
},
|
||||
{
|
||||
"Id": "43c3ab04-da10-5880-86c5-e1b32099228a",
|
||||
"Code": "App.SiteManagement.Seo.SitemapExcludePaths",
|
||||
"MainGroupKey": "App.SiteManagement",
|
||||
"SubGroupKey": "App.SiteManagement.Seo",
|
||||
"Order": 13,
|
||||
"NameKey": "App.SiteManagement.Seo.SitemapExcludePaths",
|
||||
"DescriptionKey": "App.SiteManagement.Seo.SitemapExcludePaths.Description",
|
||||
"RequiredPermissionName": "App.SiteManagement.Seo",
|
||||
"Providers": "G|D",
|
||||
"DefaultValue": "",
|
||||
"DataType": "Memo",
|
||||
"SelectOptions": "{}",
|
||||
"IsVisibleToClients": false,
|
||||
"IsInherited": false,
|
||||
"IsEncrypted": false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,9 @@ export default [
|
|||
'import/resolver': {
|
||||
typescript: { project: './tsconfig.eslint.json', alwaysTryTypes: true },
|
||||
},
|
||||
// Vite'in sanal modulleri diskte yoktur; cozucu onlari bulamaz, tipleri
|
||||
// `vite-env.d.ts` uzerinden gelir.
|
||||
'import/core-modules': ['virtual:pwa-register'],
|
||||
},
|
||||
rules: {
|
||||
...reactPlugin.configs.recommended.rules,
|
||||
|
|
@ -83,4 +86,17 @@ export default [
|
|||
},
|
||||
},
|
||||
prettier,
|
||||
// Derleme araclari Node'da CommonJS olarak calisir: tarayici globalleri yerine Node
|
||||
// globalleri gecerlidir ve Tailwind eklentisi `require` ile yuklenir (tailwind.config.ts
|
||||
// Node tarafindan okunur, bundle'a girmez).
|
||||
{
|
||||
files: ['tailwind.config.ts', 'postcss.config.cjs', 'twSafelistGenerator/**/*.js'],
|
||||
languageOptions: {
|
||||
globals: globals.node,
|
||||
sourceType: 'commonjs',
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/no-require-imports': 'off',
|
||||
},
|
||||
},
|
||||
]
|
||||
|
|
|
|||
|
|
@ -4,9 +4,27 @@
|
|||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="description" content="Sözsoft" />
|
||||
<!--
|
||||
Tema rengi sistem temasina gore ayrilir; tek deger koyu modda tarayici cubugunu
|
||||
acik birakip sayfanin ustunde beyaz bir serit gosteriyordu.
|
||||
-->
|
||||
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)" />
|
||||
<meta name="theme-color" content="#111827" media="(prefers-color-scheme: dark)" />
|
||||
|
||||
<!--
|
||||
Buradaki meta yalnizca ilk boyamanin ve JavaScript calistirmayan istemcilerin
|
||||
gordugu varsayilandir. Gercek deger uygulama konfigurasyonundaki SEO sozlesmesinden
|
||||
gelir ve `SeoHead` tarafindan yazilir (bkz. components/shared/SeoHead.tsx).
|
||||
-->
|
||||
<title>Sözsoft</title>
|
||||
<meta name="description" content="Sözsoft" />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:site_name" content="Sözsoft" />
|
||||
<meta property="og:title" content="Sözsoft" />
|
||||
<meta property="og:description" content="Sözsoft" />
|
||||
<meta property="og:image" content="/img/logo/logo-512.png" />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
|
|
|||
BIN
ui/public/img/blog/ai-ve-gelecegi.webp
Normal file
|
After Width: | Height: | Size: 6 KiB |
BIN
ui/public/img/blog/dijital-pazarlamada-veri-analizi.webp
Normal file
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 8.3 KiB |
BIN
ui/public/img/blog/web-gelistirmede-son-trendler.webp
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
BIN
ui/public/img/hero/about.webp
Normal file
|
After Width: | Height: | Size: 101 KiB |
BIN
ui/public/img/hero/blog.webp
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
ui/public/img/hero/contact.webp
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
ui/public/img/hero/home.webp
Normal file
|
After Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 8.1 KiB |
|
Before Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 8.1 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 8.2 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 8.2 KiB |
|
Before Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 8.1 KiB |
|
Before Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 9.9 KiB |
BIN
ui/public/img/products/backup-service.webp
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
ui/public/img/products/branch-hosting.webp
Normal file
|
After Width: | Height: | Size: 28 KiB |