From 677a62bd79d5c4188bfbde0e79444247a635fff8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sedat=20=C3=96ZT=C3=9CRK?=
<76204082+iamsedatozturk@users.noreply.github.com>
Date: Tue, 29 Sep 2026 09:14:07 +0300
Subject: [PATCH] =?UTF-8?q?Md=20dosyalar=C4=B1=20d=C3=BCzenleme=20ve=20?=
=?UTF-8?q?=C3=B6l=C3=BC=20kodlar=20kald=C4=B1r=C4=B1ld=C4=B1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.github/instructions/ai.instructions.md | 10 +-
.github/instructions/dotnet.instructions.md | 2 +-
.github/instructions/lowcode.instructions.md | 36 +++++---
README.md | 58 +++++++-----
.../McpDevelopAppService.cs | 2 +
.../McpTimesheetTexts.cs | 2 +
.../Editors/EditorOptions.cs | 92 ++++++++-----------
.../Extensions/AbpPermissionsExtensions.cs | 16 ----
claude.md | 19 ++--
.../host/data/App.Languages.LanguageText.json | 28 ++++++
ui/src/views/shared/htmlEditorOptions.ts | 1 +
11 files changed, 148 insertions(+), 118 deletions(-)
diff --git a/.github/instructions/ai.instructions.md b/.github/instructions/ai.instructions.md
index 8162f546..331dd63e 100644
--- a/.github/instructions/ai.instructions.md
+++ b/.github/instructions/ai.instructions.md
@@ -362,8 +362,10 @@ MCP (`/mcp`, `Sozsoft.Mcp` module) — expose ListForm data/queries to an extern
(Claude, ChatGPT, or an OAuth-registered MCP client) without new code: configure a server row
on the **MCP Servers** screen (`App.DeveloperKit.McpServers`, §12.2 of
`lowcode-reference.instructions.md`) with its allowed tools and tenant scope; queries always run
-on the calling tenant's own database. Tenant users are read-only over MCP (schema tools,
-`query_list`, `run_select`); writing and development tools are host-only. A
+on the calling tenant's own database. Tenant users read and write data over MCP but cannot develop
+(`McpToolNames.TenantTools`: schema tools, `query_list`, `run_select`, `insert_row`/`update_row`/`delete_row`
+— row writes go through the screen's C/U/D contract and need `.Write`); development tools,
+`list_queries` and `run_query` are host-only. A
request to "let an agent query/update X" is a Decision Flow §1 (ListForm) request first — MCP
only gates which of the existing screens/queries an already-authorized client may reach; it
never grants access beyond the calling user's own ListForm/query permissions. Ayrıntı:
@@ -879,7 +881,7 @@ Default is `List`, so older seed files that lack it keep working.
| Kind | Produces | Menu URL | Child permissions |
| --- | --- | --- | --- |
-| `List` | ListForm + ListFormFields + Route + Menu + Permissions + language keys | `/admin/list/{ListFormCode}` | `.Create`, `.Update`, `.Delete`, `.Export`, `.Import`, `.Note` |
+| `List` | ListForm + ListFormFields + Menu + Permissions + language keys (no Route record; the generic `/admin/list/{code}` route serves it) | `/admin/list/{ListFormCode}` | `.Create`, `.Update`, `.Delete`, `.Export`, `.Import`, `.Note` |
| `Custom` | Menu + Permissions + language keys; the screen is the selected Custom Component | the component's `RoutePath` | `.Create`, `.Update`, `.Delete` only |
`Export` / `Import` / `Note` exist only in the ListForm ecosystem. Never propose them on the
@@ -891,7 +893,7 @@ embedded elsewhere as a SubGrid/part. Default is `true`.
### 32.4 Permission group selection
-The wizard's permission group comes from a select list backed by `GetWizardPermissionGroups`.
+The wizard's permission group comes from a select list backed by `ListFormWizardAppService.GetPermissionGroups` (`/api/app/list-form-wizard/permission-groups`).
Display names are read from the language key whose name equals the group name.
- Selecting an existing group fills `PermissionGroupEn/Tr` (seed file: `Language.PermissionGroupEn/Tr`);
diff --git a/.github/instructions/dotnet.instructions.md b/.github/instructions/dotnet.instructions.md
index 75a1103b..981f224e 100644
--- a/.github/instructions/dotnet.instructions.md
+++ b/.github/instructions/dotnet.instructions.md
@@ -31,7 +31,7 @@ Kural:
- Nullable, proje bazında ve bilinçli olarak açılır (`enable`).
Şu an açık olanlar: `Sozsoft.Platform.Application`, `Sozsoft.Notifications.*`,
- test projeleri.
+ `Sozsoft.Mcp.*`, test projeleri.
- **Entity taşıyan projelerde** (`*.Domain`) nullable açılacaksa, aynı PR'da
`dotnet ef migrations add` çıktısı gözden geçirilir; şema değişikliği isteniyorsa
kabul edilir, istenmiyorsa kolon zorunluluğu `PlatformDbContext` içinde
diff --git a/.github/instructions/lowcode.instructions.md b/.github/instructions/lowcode.instructions.md
index 5dbd9573..4f720448 100644
--- a/.github/instructions/lowcode.instructions.md
+++ b/.github/instructions/lowcode.instructions.md
@@ -482,7 +482,8 @@ ya da ziyaretçiye açık yeni bir fiziksel sayfa ürettiğinde.
**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
+sosyal hesaplar, varsayılan anahtar kelimeler (`DefaultKeywords`), sitemap anahtarı (`SitemapEnabled`) ve
+sitemap dışı yollar (`SitemapExcludePaths`) `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.
@@ -605,7 +606,8 @@ talebin metnini döndürür. Başlık birden fazla talebe uyuyorsa Id ister. `St
- `Timesheets` — **sunucu yazar, model hiçbir şey yapmaz.** `get_dev_request` / `start_dev_request`
talebi kullanıcının etkin talebi yapar; ardından yapılan her MCP yazma işlemi (`create_table`,
`deploy_wizard`, `delete_wizard`, `save_component`, `delete_component`, `insert_row` /
- `update_row` / `delete_row`, `save_dev_request_analysis`) o talebe bir satır ekler
+ `update_row` / `delete_row`, `save_dev_request_analysis`, `append_dev_request_note`,
+ `add_dev_request_subtasks`) o talebe bir satır ekler
(`IMcpDevRequestTimesheetRecorder`). Açıklama `App.Mcp.Timesheet.*` dil anahtarından **Türkçe**
üretilir ("Hr_T_Doctor tablosu oluşturuldu"), süre önceki satırdan ya da talebin açıldığı andan bu
yana ölçülür (en az 1 dakika, saat olarak), çalışan bağlanan kullanıcı, tarih bugündür.
@@ -901,7 +903,7 @@ hiçbir şey silmez**. Tek çıktısı seed dosyalarıdır:
| `wizard/{WizardName}.json` | Ekranın `ListForm`/`Fields` sözleşmesi, wizard cevapları, `InsertedRecords`, `Pending` |
| `data/App.Menus.Module.json`, `data/App.Menus.Menu.json` | Modül, üst menü, ekranın menüsü |
| `data/Abp.Identity.PermissionGroups.json`, `data/Abp.Identity.Permissions.json` | İzin grubu, okuma yetkisi ve alt yetkileri |
-| `data/App.Languages.LanguageText.json` | Menü, başlık, açıklama, grup ve alan başlıklarının EN + TR metinleri (`Id` iş anahtarından v5 GUID, `SeedRowIds.LanguageText`) |
+| `data/App.Languages.LanguageText.json` | Menü, başlık, açıklama, grup, alan başlıkları ve `Language.Texts` anahtarlarının (widget başlıkları) EN + TR metinleri (`Id` iş anahtarından v5 GUID, `SeedRowIds.LanguageText`) |
Satırları `WizardSeedRowStore` yazar; anahtarlar `WizardSeedRowKeys`'ten gelir (export aynı
listeyle toplar). **Sahiplik:** bir satır ne veritabanında ne dosyada varsa ya da önceki sürüm ona
@@ -942,7 +944,8 @@ sürümünü taşır.
"Menu": { "MenuCode": "…", "MenuOrder": 2, "MenuUrl": "…", "MenuIcon": "…",
"MenuParentPath": [ { "Code": "…", "Icon": "…", "En": "…", "Tr": "…" } ], // yalnızca derin yolda
"MenuParentCode": "…", "MenuParentIcon": "…" }, // "CreateMenu": false ise eklenir
- "Language": { "MenuEn": "…", /* Menu/Title/Desc/MenuParent/PermissionGroup · EN + TR */ },
+ "Language": { "MenuEn": "…", /* Menu/Title/Desc/MenuParent/PermissionGroup · EN + TR */
+ "Texts": [ { "Key": "…", "En": "…", "Tr": "…" } ] }, // Texts yalnızca doluysa
"Groups": [ { "Items": [ { "FieldName": "…", "CaptionName": "…",
"En": "…", "Tr": "…" } ] } ] // grup Caption/ColCount yok
},
@@ -993,7 +996,7 @@ düzenleme izinleri, görünüm option blokları, alt formlar, widget'lar, iş a
Dosyada wizard alanları üç başlık altında toplanır — `Menu` (menü kaydı), `Language` (dil
metinleri), `Groups` (alan başlıklarının dil karşılıkları); geri kalanı kök seviyededir.
-Gruplama ile `ListFormCode → WizardCode` ve `MenuParentModuleId → ModuleId` adlandırmaları
+Gruplama ile `ListFormCode → WizardCode`, `MenuParentModuleId → ModuleId` ve `LanguageTexts → Language.Texts` adlandırmaları
yalnızca **dosya biçimidir**; C#
sözleşmesi (`ListFormWizardDto`) düzdür ve dosya okunurken düzleştirilir
(`WizardSeedFileDto.FromSeedJson` ↔ `ToSeedJson`). Aşağıdaki tablolar C# alan adlarını kullanır.
@@ -1671,8 +1674,9 @@ Silme ucu (`DeleteFile`) her kaydın akıbetini (`Deleted` / `Kept` + kullananla
Düzenleme (`EditFileName`) bu sınıfı çağırmaz; önceki sürümün kayıtlarını migrate kaldırır (§4.0).
Wizard'ın yarattığı modül `InsertedRecords.ModuleIds` ile izlenir.
-Silme yolunda ekranın veri aynası (`ListForm.SeedFilePath` → `data/{ListFormCode}.json` ya da
-klasörü) ekranla birlikte silinir; ekransız uygulanamaz.
+Kurulu wizard silinirken ekranın veri aynası (`ListForm.SeedFilePath` → `data/{ListFormCode}.json` ya da
+klasörü) ekranla birlikte silinir; ekransız uygulanamaz. Migrate bekleyen wizard'da ayna yalnızca ekran
+hiç kurulmamışsa silinir (MCP demo satırları); kurulu ekranın aynası veritabanındaki satırların kaydıdır, kalır.
**Ekranın tablosu** wizard'ın ürettiği bir kayıt değildir (tasarımcı ya da `create_table` ile
ayrıca kurulur, `InsertedRecords`'ta yoktur) ve **kendiliğinden silinmez**. Silme onayı önce
@@ -2124,8 +2128,9 @@ opsiyonel `ApprovalDateFieldName`, `ApprovalDescriptionFieldName`, `ApprovalIsFi
dönsün). Onay düğümleri (`Criteria`) görsel akış tasarımcısında kurulur ve `ListFormWorkflow`
kayıtlarına gider (§4.7).
-**Adım 12 — Deploy.** Sol tarafta tüm adımların özeti, sağda üretim log'u. Üretim sırası —
-seed dosyası uygulanırken `WizardDataSeeder`'ın izlediği sıranın aynısıdır:
+**Adım 12 — Deploy.** Sol tarafta tüm adımların özeti, sağda üretim log'u. Log, deploy'un seed
+dosyalarına yazdığı bölümleri sırayla gösterir; migrate'te `WizardDataSeeder` ekrana özgü kayıtları,
+ardından `ListFormSeedDataApplier` menü/yetki/dil satırlarını kurar (§4.0):
1. Konfigürasyon doğrulama
2. Menü kaydı (`CreateMenu: false` ise atlanır)
@@ -2134,10 +2139,11 @@ seed dosyası uygulanırken `WizardDataSeeder`'ın izlediği sıranın aynısıd
5. List yolunda: veri kaynağı → ListForm → alan grupları · Custom yolunda: bileşen bağlama
6. Deploy + tamamlandı
-Başarıda sihirbaz `/admin/list/{ListFormCode}`'a yönlendirir ve seed dosyası
-`configs/seeds/{kapsam}/wizard/` altına yazılmış olur. **Düzenleme modu** Wizard Manager'dan
-`EditFileName` ile açılır: aynı adımlar dolu gelir, kaydetmede önce eski dosya ve
-`InsertedRecords` kayıtları silinir (§4.5).
+Başarıda sihirbaz "Migrate bekliyor" bildirimiyle Wizard Manager'a döner (ekran henüz kurulu
+değildir; DB Migrate ile kurulur) ve seed dosyası `configs/seeds/{kapsam}/wizard/` altına yazılmış
+olur. **Düzenleme modu** Wizard Manager'dan `EditFileName` ile açılır: aynı adımlar dolu gelir,
+kaydetmede dosyalar cevaptan yeniden yazılır; önceki sürümün kayıtları `Pending.Replaced`'a alınır,
+veritabanına dokunulmaz — kaldırma ve yeniden kurulum DB Migrate'tedir (§4.0).
---
@@ -2471,7 +2477,7 @@ Preset mevcut JSON ile **birleşir**, diğer ayarları silmez; aynı anahtar var
| TagBox anında seçim | `showSelectionControls`, `applyValueMode:instantly`, `searchEnabled` |
| GridBox tek/çok seçim | `columns`, `selectionMode`, `filterRowVisible`, `height`, `width` |
| Çoklu görsel | `width`, `height`, `multiple`, `accept:image/*` |
-| HTML tam araç çubuğu | `toolbar.items` (tam liste), `toolbar.multiline`, `mediaResizing`, `imageUpload` |
+| HTML tam araç çubuğu | `htmlEditorOptions.ts` varsayılanı: `toolbar.items` (tam liste), `toolbar.multiline`, `mediaResizing`, `imageUpload` (`tabs: file/url`, base64), `tableResizing`, `tableContextMenu` — boş `editorOptions` da aynı varsayılanı alır |
| Yükseklik 100 / 200 | `{"height":100}` · `{"height":200}` |
| Barkod okutma butonu | `buttons[]` — kamera ikonlu buton; `onClick` = `"@SCAN_BARCODE"` token'ı. Yalnız `dxTextBox`. Token'ı `editingForm.ts` kamera diyaloğuna bağlar, okunan kod alana yazılır. HTTPS şart. |
| Kamerayla foto çek | `{"accept":"image/*","capture":"environment"}` — yalnız `dxImageUpload`. Mobilde dosya seçici yerine **yalnızca** kamera açılır (`user` = ön kamera); masaüstünde yok sayılır. Ayarlanmazsa dokunmatik cihazda dosya seçicinin yanına ayrı bir kamera butonu gelir, yani ikisi birden kullanılabilir — bu kalıp kamerayı **zorunlu** kılmak içindir. |
@@ -2489,7 +2495,7 @@ Preset mevcut JSON ile **birleşir**, diğer ayarları silmez; aynı anahtar var
| Telefon | `EditorOptions.Phone()` | global format + maske + örnek |
| Kaydırıcı | `EditorOptions.Slider()` | `{"tooltip":{"enabled":true}}` |
| Görsel yükleme | `EditorOptions.ImageUpload(multiple: true, 80, 80)` | |
-| Zengin metin | `EditorOptions.Html(240)` | tam araç çubuğu + `encodeHtml` |
+| Zengin metin | `EditorOptions.Html(240)` | `htmlEditorOptions.ts` varsayılanıyla aynı bloklar (tam araç çubuğu, `imageUpload`, `mediaResizing`, `tableResizing`, `tableContextMenu`; biri değişirse diğeri de değişir) + `encodeHtml` + `elementAttr` `min-height:240px` (sabit yükseklik değil; içerikle büyür) |
Akıcı ekler: `.Placeholder()`, `.MaxLength()`, `.Height(int\|string)`, `.Width(int\|string)`,
`.Format()`, `.FixedPointFormat(p)`, `.DisplayFormat()`, `.DateSerializationFormat()`,
diff --git a/README.md b/README.md
index ca081d54..010c7756 100644
--- a/README.md
+++ b/README.md
@@ -533,7 +533,7 @@ eskisi gibi çalışır.
| `ComponentKind` | Üretilen | Menü URL'i | Yetkiler |
| --- | --- | --- | --- |
-| `List` | ListForm + ListFormField + Route + Menu + Permission + dil anahtarları | `/admin/list/{ListFormCode}` | `.Create`, `.Update`, `.Delete`, `.Export`, `.Import`, `.Note` |
+| `List` | ListForm + ListFormField (+ iş akışı) + Menu + Permission + dil anahtarları (rota üretilmez; genel `/admin/list/{ListFormCode}` rotası kullanılır) | `/admin/list/{ListFormCode}` | `.Create`, `.Update`, `.Delete`, `.Export`, `.Import`, `.Note` |
| `Custom` | Menü + Permission + dil anahtarları; ekran seçilen Custom Component'tir | Component'in `RoutePath` değeri | Yalnızca `.Create`, `.Update`, `.Delete` (Export/Import/Note ListForm'a özgüdür) |
**Menüsüz wizard (`CreateMenu = false`).** Menü (ve gerekiyorsa üst menü) kaydı hiç üretilmez;
@@ -542,7 +542,7 @@ ekranda SubGrid/parça olarak kullanılmak üzere tanımlanır. Alan varsayılan
alanı taşımayan eski seed dosyaları etkilenmez.
**İzin grubu seçimi.** Menü adımında izin grubu bir seçim listesinden gelir
-(`GetWizardPermissionGroups`). Görünen adlar, grup adıyla aynı olan dil anahtarının metinlerinden
+(`ListFormWizardAppService.GetPermissionGroups`, `/api/app/list-form-wizard/permission-groups`). Görünen adlar, grup adıyla aynı olan dil anahtarının metinlerinden
okunur; seçim yapıldığında `PermissionGroupEn/Tr` forma doldurulur. Yeni bir grup
tanımlanırsa grup adıyla aynı dil anahtarına bu metinler yazılır; mevcut grup seçilip alanlar boş
bırakılırsa sunucu veritabanındaki değeri geri yazar. Böylece seed dosyası her durumda iki dilli
@@ -556,12 +556,15 @@ Var olan bir ekranı düzenlemek için `/admin/listform/edit/{listFormCode}` —
yapılandırması sekmeli bir editörde yönetilir (Veritabanı, Sütunlar, Filtreler, Düzenleme,
Yetkiler, Alt Form, Pivot, Tree, Gantt, Scheduler, Todo, Widget, Workflow, Durum, Grafik sekmeleri).
-**Wizard File Manager** (`/admin/listform/wizard` liste ekranı) üretilmiş seed dosyalarını yönetir:
+**Wizard File Manager** (`/admin/listform/wizardManager`; sihirbazın kendisi `/admin/listform/wizard`) üretilmiş seed dosyalarını yönetir:
liste/kart görünümü arasında geçiş (tercih diğer listelerle aynı yerde, `admin.lists.states`
altında saklanır), arama, düzenleme (dosyanın yerine geçen yeni bir çalıştırma), silme, veritabanı
migrate tetikleme, dışa aktarma ve içe aktarma. Liste, wizard'ın ilk deploy damgasına
(`Wizard.SeededAt`) göre **tersine** sıralıdır — en son üretilen ekran en üstte durur; damgası
-okunamayan dosyalar sona düşer. Butonlar `App.Listforms.Wizard.*` yetkileriyle gizlenir; asıl
+okunamayan dosyalar sona düşer. Silme, bağlı wizard'ı olan dosyada reddedilir; onay diyaloğu önce
+`DeleteAnalysis` ile düşürülebilecek tabloyu sorar, sonuç her kaydın akıbetiyle (`Deleted` / `Kept` +
+kullananlar / `NotFound`) rapor olarak gösterilir (`WizardRecordCleaner`, `WizardTableCleaner`;
+`lowcode.instructions.md` §4.5). Butonlar `App.Listforms.Wizard.*` yetkileriyle gizlenir; asıl
kontrol `ListFormWizardAppService` üzerindedir.
### 7.7. İçe/dışa aktarma
@@ -869,8 +872,8 @@ tıklamak seçim yapar, sayfayı değiştirmez — tıklama yakalama evresinde d
**Diğer notlar.** `PlatformIcon` tasarımcı dokümanındaki ikon adını çözer; `selectComponents.ts`
`Select.componentAs` için saklanan adı (`ReactSelect`, `CreatableSelect`, `AsyncSelect`) gerçek
bileşene çevirir — kanvas ve üretilen bileşenin aynı adı aynı şekilde yorumlamasını sağlayan tek
-nokta budur. Yeni bileşen `configs/seeds/host/data/App.DeveloperKit.CustomComponents/{Id}.json`
-içindeki bir satır olarak başlar.
+nokta budur. Yeni bileşen `configs/seeds/host/data/App.DeveloperKit.CustomComponents/` klasöründe
+kendi `{Id}.json` satır dosyası olarak başlar.
**Dinamik servis yaşam döngüsü:** `DynamicServiceCompiler` kodu derler →
`DynamicAssemblyRegistrationService` assembly'yi tenant bağlamıyla kaydeder →
@@ -917,8 +920,9 @@ kendi aralarında 1'den. `PlatformPermissionAppService` sıralama anahtarını k
zincirinden kurar, böylece çocuk her zaman ebeveyninin altında kalır. Değerler seed dosyalarındaki
`Order` alanında tutulur ve Permissions / Permission Groups ekranlarından düzenlenebilir.
Wizard deploy'u yeni grubu ve grubun yeni kök yetkisini (seed satırı olarak) veritabanı ile seed
-dosyasındaki kardeşlerin en büyük sırasının bir fazlasıyla, alt yetkileri Create/Update/Delete/Export/Import/Note dizilimiyle
-1'den yazar (`NextPermissionOrder`, `AbpPermissionsExtensions`).
+dosyasındaki kardeşlerin en büyük sırasının bir fazlasıyla, alt yetkileri `WizardSeedRowKeys.ChildPermissions` dizilimiyle
+(List: Create/Update/Delete/Export/Import/Note, Custom: Create/Update/Delete) 1'den yazar
+(`ListFormWizardAppService.WritePermissionGroupAsync` / `WritePermissionRowsAsync`).
Aksiyon yetkisi olan alt gruplar (UI tarafındaki karşılıkları `constants/permission.constant.ts`
içindedir; oradaki kontroller yalnızca butonları gizler, asıl kontrol AppService'lerdedir):
@@ -948,7 +952,7 @@ içindedir; oradaki kontroller yalnızca butonları gizler, asıl kontrol AppSer
| **Sozsoft.MailQueue** | Şablon (Scriban) tabanlı gövde üretimi, ek dosya yaşam döngüsü, kuyruk çalıştırma ve günlükleme. |
| **Sozsoft.Sender** | Amazon SES (mail), Posta Güvercini (SMS), WhatsApp Cloud API, Rocket.Chat göndericileri. |
| **Sozsoft.SqlQueryManager** | SQL nesne yönetimi, sorgu çalıştırma, tablo/kolon metadata'sı. |
-| **Sozsoft.Mcp** | MCP (Model Context Protocol) sunucusu: `/mcp` Streamable HTTP ucu, kiracı bazlı araç seti, şema keşfi, sorgu, veri yazma ve (host) tablo/ekran/bileşen geliştirme. Kiracı kullanıcısı yalnızca veri okur. Bkz. §13.9. |
+| **Sozsoft.Mcp** | MCP (Model Context Protocol) sunucusu: `/mcp` Streamable HTTP ucu, kiracı bazlı araç seti, şema keşfi, sorgu, veri yazma ve (host) tablo/ekran/bileşen geliştirme. Kiracı kullanıcısı veri okur ve ekranın C/U/D sözleşmesinden geçerek satır yazar; geliştirme araçları yalnızca host'tadır. Bkz. §13.9. |
### 10.2. Uygulama servisleri (`api/src/Sozsoft.Platform.Application`)
@@ -1003,7 +1007,7 @@ DeveloperKit > Custom Components (Visual Designer) üzerinden değişir. Aynı y
ile paylaşan custom component kazanır; karşılığı olan dosyası bulunmayan rota kaydı ise hiç
yüklenmez (`hasPhysicalComponent`) — seeder yalnızca eklediği için eski kayıtlar veritabanında
kalsa bile ekran patlamaz. Seed: `configs/seeds/host/data/App.DeveloperKit.CustomComponents/{Id}.json`;
-bileşenin ayrı dosyası yoktur, her bileşen bu dosyada bir satırdır.
+ekran klasör modundadır, her bileşen kendi satır dosyasındadır (dosya adı satırın `Id`'si).
Runtime kapsamı sınırlıdır: bileşen kodundaki `import` satırları derlemede temizlenir,
kullanılabilenler `React`, `ui` kit, `PlatformViewHost`, `apiService`, `axios`, `DOMPurify`,
@@ -1089,7 +1093,7 @@ Veriden beslenen public sayfalar (`/products`, `/checkout`, `/payment`, `/succes
| 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ı |
+| 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, varsayılan anahtar kelimeler (`DefaultKeywords`), sitemap anahtarı (`SitemapEnabled`), sitemap dışı yollar (`SitemapExcludePaths`) |
| 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 |
@@ -1109,7 +1113,11 @@ Sayfanın kendi `SeoHead`'i, kabuğun (`PublicLayout`) `fallback` kaydını her
`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
+`SeoNoIndex` işaretli ya da `SeoIncludeInSitemap = false` olan yollar elenir.
+`App.SiteManagement.Seo.SitemapExcludePaths` (satır başına bir yol, sonu `*` ise önek) hem sitemap'ten
+düşer hem `robots.txt`'e `Disallow` olarak yazılır; `robots.txt` ayrıca `/admin/`, `/auth/`,
+`/checkout`, `/payment`, `/success` yollarını her zaman kapatır. `SitemapEnabled` kapalıyken
+`sitemap.xml` 404 döner ve robots'a `Sitemap:` satırı yazılmaz. 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.
@@ -1276,21 +1284,23 @@ Bildirimin tamamı Administration menüsünde `App.Administration.Notifications`
**Bildirim Türleri**, **Bildirim Kuralları**, **Bildirimler**, **Bildirim Şablonları**. Grup
Agenda'nın üstündedir; Saas altında bildirim ekranı **yoktur**.
-Tablolar kiracı kapsamındadır (`Adm_T_NotificationType`, `Adm_T_NotificationRule`,
-`Adm_T_Notification`; üçü de `IMultiTenant`). Her kiracı kendi türünü, kuralını ve kuyruğunu
+Tablolar kiracı kapsamındadır (`Adm_T_NotificationTemplate`, `Adm_T_NotificationType`,
+`Adm_T_NotificationRule`, `Adm_T_Notification`; dördü de `IMultiTenant`). Her kiracı kendi türünü, kuralını ve kuyruğunu
görür; host kaydı (`TenantId = null`) yalnızca host oturumunda görünür. Yeni bir kiracı
oluşturulduğunda varsayılan tür ve kural kümesi o kiracıya kopyalanır
(`NotificationTenantDefaultsSeedContributor`): kaynak host kapsamındaki
`data/App.Notifications.Notification{Types,Rules}.json` dosyalarıdır, satırlar **yeni
-kimliklerle** yazılır ve eşleşme ad üzerinden yapıldığı için ikinci çalıştırma bir şey eklemez.
+kimliklerle** yazılır ve eşleşme ad üzerinden yapıldığı için ikinci çalıştırma bir şey eklemez;
+şablonlar kopyalanmaz.
**Bildirimler** ekranı (`App.Notifications.Notification`) hem kuyruk günlüğü hem gönderim
ekranıdır: liste kural motorunun ürettiklerini de gösterir, ekleme formu ise elle gönderimdir.
Ayrı bir manuel gönderim kaydı yoktur — gönderimin kaydı bildirimin kendisidir. Ekleme anında
yazma işi `list-form-dynamic-api/notification-insert` ucuna gider (`InsertServiceAddress`); uç
seçilen alıcı × kanal açılımını `INotificationManager.SendManualNotificationAsync` ile kuyruğa
-yazar — yani Bildirim Kuralları ekranındaki elle gönderimle **aynı yol**, tek fark alıcı
-kümesinin kurallardan değil ekrandan gelmesi. Güncelleme kapalıdır, silme kaydı kaldırır.
+yazar. Bildirim Kuralları ekranındaki `CreateNotification` diyaloğu ise aynı yöneticinin
+`SendNotificationAsync` yolunu kullanır: türün kurallarını işletir ve kullanıcı tercihini dikkate
+alır; bu uç alıcıyı ekrandan alır ve tercihi okumaz. Güncelleme kapalıdır, silme kaydı kaldırır.
Formdaki `NotificationChannel` ve `UserId` alanları listede tek değerlidir (her satır bir
alıcı × kanal), formda `dxTagBox` ile çoklu seçilir; tipli DTO yolunda çoklu değeri
@@ -1414,12 +1424,14 @@ liste süzgeci ve çağrı kapısı aynı kuralı kullanır.
| `save_dev_request_analysis` | `Backlog` ya da `Analysis` durumundaki talebin analizini (HTML) `Analysis` kolonuna yazar — var olanı değiştirir — ve talebi `Analysis` durumuna alır; diğer durumları reddeder. `Description`'a dokunmaz. Analizin standardı `lowcode.instructions.md` §0.12. `.Write` + `App.DevRequests.Update` ister. |
| `add_dev_request_subtasks` | Talebin `SubTasks` listesine tamamlanmış satırlar ekler: model yalnızca başlığı verir, atanan kişi bağlanan kullanıcının adı (`userName`), bitiş tarihi bugün (`yyyy-MM-dd`) olarak sunucuda yazılır; var olan satırlar korunur. `.Write` + `App.DevRequests.Update` ister. |
| `append_dev_request_note` | Talebin `Analysis` kolonunun sonuna HTML ekler (üretim sonuç notu); `Description` talebin yazıldığı hâliyle kalır. İkisi de `update_row` ile yazılmaz — açıklama base64 görsel taşır. `.Write` + `App.DevRequests.Update` ister. |
+| `list_components` / `get_component` / `save_component` / `delete_component` | Custom component'ler `ICustomComponentAppService` üzerinden; seed senkronu ve `DataSources` çözümü aynen çalışır. `save_component` ada göre ekler/günceller, tasarımcı belgesi (`Props.visualDesigner`, `sourceMode ≠ code`) taşıyan bileşene kod yazmayı reddeder — kod belgeden üretildiği için tasarımcıdaki ilk kayıtta ezilirdi. |
**Timesheets.** Talebin `Timesheets` listesini sunucu doldurur (`IMcpDevRequestTimesheetRecorder`):
`get_dev_request` / `start_dev_request` talebi kullanıcının etkin talebi yapar (dağıtık önbellek,
`McpDevRequestActivityCacheItem`, 12 saat); sonraki her MCP yazma işlemi (`create_table`,
`deploy_wizard`, `delete_wizard`, `save_component`, `delete_component`, satır araçları,
-`save_dev_request_analysis`) o talebe bir satır ekler. Açıklama `App.Mcp.Timesheet.*` anahtarından
+`save_dev_request_analysis`, `append_dev_request_note`, `add_dev_request_subtasks`) o talebe bir satır
+ekler; talebi açan `start_dev_request` yazmaz. Açıklama `App.Mcp.Timesheet.*` anahtarından
Türkçe üretilir, süre önceki satırdan ya da talebin açıldığı andan ölçülür (en az 1 dakika, saat
olarak), çalışan bağlanan kullanıcı, tarih bugündür. Etkin talep yoksa satır yazılmaz; bozuk bir
`Timesheets` değeri işlemi geri almaz, yalnızca satır atlanır ve log'a düşer.
@@ -1454,7 +1466,6 @@ değilse sunucu reddeder ve hiçbir şey üretilmez.
başlıkta komut yerine Id ya da düz metin kullan. Başlık birden fazla talebe uyuyorsa sunucu Id ister.
- Düz metin de aynı yoldan geçer: sunucu talimatları modeli `start_dev_request`'e yönlendirir,
durum şartı yine sunucuda uygulanır.
-| `list_components` / `get_component` / `save_component` / `delete_component` | Custom component'ler `ICustomComponentAppService` üzerinden; seed senkronu ve `DataSources` çözümü aynen çalışır. `save_component` ada göre ekler/günceller, tasarımcı belgesi (`Props.visualDesigner`, `sourceMode ≠ code`) taşıyan bileşene kod yazmayı reddeder — kod belgeden üretildiği için tasarımcıdaki ilk kayıtta ezilirdi. |
**Yetki modeli: MCP yetki vermez.** Kullanıcı MCP'de uygulamadaki yetkisini taşır; MCP'nin iki
yetkisi yalnızca kapıdır. Bir çağrı üç katmandan geçer ve her katman ayrı reddeder:
@@ -1471,7 +1482,7 @@ yetkisi yalnızca kapıdır. Bir çağrı üç katmandan geçer ve her katman ay
| Yetki | Kim alabilir | Ne açar | Açmadığı / sınırı |
| --- | --- | --- | --- |
| `App.DeveloperKit.McpServers` | Host, kiracı | MCP Servers ekranını görme (`.Create/.Update/.Delete/.Export/.Import` ekran işlemleri) | `/mcp` ucuna erişim vermez |
-| `App.DeveloperKit.McpServers.Invoke` | Host, kiracı | `/mcp` bağlantısı ve bütün okuma araçları: `server_info`, `list_lists`, `describe_list`, `search_schema`, `query_list`; host'ta ayrıca `get_dev_guide`, `list_wizards`, `get_wizard`, `verify_wizard`, `list_components`, `get_component` | Hiçbir şey yazmaz. Okuma da ekran yetkisine bağlıdır: `R` yetkisi olmayan liste görünmez, sorgulanamaz. Ham SQL (`run_select`, `list_queries`, `run_query`) ayrıca `App.SqlQueryManager` ister |
+| `App.DeveloperKit.McpServers.Invoke` | Host, kiracı | `/mcp` bağlantısı ve bütün okuma araçları: `server_info`, `list_lists`, `describe_list`, `search_schema`, `query_list`; host'ta ayrıca `get_dev_guide`, `list_wizards`, `get_wizard`, `verify_wizard`, `get_dev_request`, `list_components`, `get_component` | Hiçbir şey yazmaz. Okuma da ekran yetkisine bağlıdır: `R` yetkisi olmayan liste görünmez, sorgulanamaz. Ham SQL (`run_select`, `list_queries`, `run_query`) ayrıca `App.SqlQueryManager` ister |
| `App.DeveloperKit.McpServers.Write` | Host, kiracı | MCP üzerinden her değişiklik. **Kiracıda:** `insert_row`, `update_row`, `delete_row`. **Host'ta:** bunlara ek olarak `create_table`, `deploy_wizard`, `delete_wizard`, `save_component`, `delete_component`, `start_dev_request`, `save_dev_request_analysis`, `append_dev_request_note`, `add_dev_request_subtasks` | Kendi başına bir şey yazdırmaz: satır araçları ekranın `C`/`U`/`D` yetkisini, geliştirme araçları Wizard / Custom Components / SQL Query Manager / `App.DevRequests.Update` yetkisini ayrıca ister. Kiracıda geliştirme araçlarını **açmaz** — kiracı bağlamında bu araçlar yetkiden bağımsız kapalıdır (`McpToolNames.TenantTools`). `Invoke` olmadan işe yaramaz |
**Araç → gereken yetkiler** (Invoke hepsinde aranır, tabloda tekrar yazılmadı)
@@ -1960,7 +1971,7 @@ configs/seeds/
│ │ # Abp.Identity.Permission{Groups,s}.json — yetki tanımları
│ │ # App.Definitions.{CountryGroup,Country,City,District}.json — adres zinciri
│ │ # App.Definitions.{Currency,ContactTitle,AiBot}.json — para birimi, hitap, AI bot
-│ │ # App.Notifications.Notification{Types,Rules}.json — bildirim tipi → kuralı
+│ │ # App.Notifications.Notification{Templates,Types,Rules}.json — bildirim şablonu, tipi → kuralı
│ │ # App.Languages.{Language,LanguageText}.json — dil → metin (eksik LanguageKey metinle üretilir)
│ │ # App.SettingDefinitions.json, App.BackgroundWorkers.RecurringJobs.json — ayar tanımları, zamanlanmış işler
│ ├── sql/{object,execute}/ # .sql (SQL Server)
@@ -1970,8 +1981,9 @@ configs/seeds/
```
> Bu dosyalar elle de düzenlenebilir; ilgili ekrandan tekrar kaydedildiğinde yeniden üretilirler.
-> Wizard dosyası `EditFileName` ile güncellenirse sunucu önce eski dosyayı ve ürettiği kayıtları
-> siler, sonra yenisini üretir. Wizard'ın ürettiği ekran **ListForm detayından** düzenlendiğinde
+> Wizard dosyası `EditFileName` ile güncellenirse deploy veritabanına dokunmaz: dosyalar cevaptan
+> yeniden yazılır (ad değiştiyse eski dosya silinir), önceki sürümün kayıtları `Pending.Replaced`'a
+> alınır ve DB Migrate onları kaldırıp ekranı yeniden kurar (`lowcode.instructions.md` §4.0). Wizard'ın ürettiği ekran **ListForm detayından** düzenlendiğinde
> de aynı dosya güncellenir (`WizardSeedSynchronizer`): `ListForm`/`Fields` bölümleri ve
> `Wizard` bölümündeki karşılıkları veritabanındaki güncel haliyle değiştirilir.
diff --git a/api/modules/Sozsoft.Mcp/Sozsoft.Mcp.Application/McpDevelopAppService.cs b/api/modules/Sozsoft.Mcp/Sozsoft.Mcp.Application/McpDevelopAppService.cs
index 338a8999..bf66953b 100644
--- a/api/modules/Sozsoft.Mcp/Sozsoft.Mcp.Application/McpDevelopAppService.cs
+++ b/api/modules/Sozsoft.Mcp/Sozsoft.Mcp.Application/McpDevelopAppService.cs
@@ -448,6 +448,7 @@ public class McpDevelopAppService(
var devRequest = await FindDevRequestAsync(request);
devRequest.Analysis = string.Concat(devRequest.Analysis, html.Trim());
await devRequestRepository.UpdateAsync(devRequest, autoSave: true);
+ await timesheetRecorder.RecordAsync(McpTimesheetTexts.AppendNote);
}
///
@@ -497,6 +498,7 @@ public class McpDevelopAppService(
devRequest.SubTasks = subTasks.ToJsonString();
await devRequestRepository.UpdateAsync(devRequest, autoSave: true);
+ await timesheetRecorder.RecordAsync(McpTimesheetTexts.AddSubTasks, items.Count);
}
private static McpDevRequestDto ToDto(DevRequest devRequest)
diff --git a/api/modules/Sozsoft.Mcp/Sozsoft.Mcp.Domain.Shared/McpTimesheetTexts.cs b/api/modules/Sozsoft.Mcp/Sozsoft.Mcp.Domain.Shared/McpTimesheetTexts.cs
index 8ebfc23d..7400b6d3 100644
--- a/api/modules/Sozsoft.Mcp/Sozsoft.Mcp.Domain.Shared/McpTimesheetTexts.cs
+++ b/api/modules/Sozsoft.Mcp/Sozsoft.Mcp.Domain.Shared/McpTimesheetTexts.cs
@@ -9,6 +9,8 @@ public static class McpTimesheetTexts
private const string Prefix = "App.Mcp.Timesheet.";
public const string Analysis = Prefix + "Analysis";
+ public const string AppendNote = Prefix + "AppendNote";
+ public const string AddSubTasks = Prefix + "AddSubTasks";
public const string CreateTable = Prefix + "CreateTable";
public const string DeployWizard = Prefix + "DeployWizard";
public const string DeleteWizard = Prefix + "DeleteWizard";
diff --git a/api/src/Sozsoft.Platform.Domain.Shared/Editors/EditorOptions.cs b/api/src/Sozsoft.Platform.Domain.Shared/Editors/EditorOptions.cs
index 63d04709..a9d741dd 100644
--- a/api/src/Sozsoft.Platform.Domain.Shared/Editors/EditorOptions.cs
+++ b/api/src/Sozsoft.Platform.Domain.Shared/Editors/EditorOptions.cs
@@ -80,71 +80,57 @@ public static class EditorOptions
///
/// height verilirse editor sabitlenir ve icerik kaydirilir; min-height ise
/// en az yuksekligi korurken yeni satirlarla buyumesine izin verir.
+ /// Bloklar UI'daki htmlEditorOptions.ts varsayilaniyla birebir aynidir: istemci ic ice
+ /// bloklari tek seviye birlestirdigi icin buradaki toolbar varsayilanin yerine gecer.
///
public static EditorOptionsBuilder Html(int minHeight = 200)
=> New()
- .Raw("mediaResizing", "{\"enabled\":true}")
- .Raw("imageUpload", "{\"tabs\":[\"file\",\"url\"],\"fileUploadMode\":\"base64\"}")
.Raw("toolbar", "{\"multiline\":true,\"items\":[" + string.Join(",", HtmlToolbarItems()) + "]}")
+ .Raw("imageUpload", "{\"tabs\":[\"file\",\"url\"],\"fileUploadMode\":\"base64\"}")
+ .Raw("mediaResizing", "{\"enabled\":true}")
+ .Raw("tableResizing", "{\"enabled\":true}")
+ .Raw("tableContextMenu", "{\"enabled\":true}")
.Flag("encodeHtml", true)
.Raw("elementAttr", "{\"style\":\"min-height:" + EditorJson.Number(minHeight) + "px\"}");
- private static IEnumerable HtmlToolbarItems()
- {
- var fontSizes = new[] { "8pt", "10pt", "12pt", "14pt", "18pt", "24pt", "36pt" };
- var fontFamilies = new[]
- {
- "Arial", "Courier New", "Georgia", "Impact",
- "Lucida Console", "Tahoma", "Times New Roman", "Verdana"
- };
+ private static readonly string[] HtmlToolbarNames =
+ [
+ "undo", "redo", "separator",
+ "header", "size", "font", "separator",
+ "bold", "italic", "underline", "strike", "superscript", "subscript", "separator",
+ "color", "background", "separator",
+ "alignLeft", "alignCenter", "alignRight", "alignJustify", "separator",
+ "orderedList", "bulletList", "decreaseIndent", "increaseIndent", "separator",
+ "blockquote", "codeBlock", "link", "image", "separator",
+ "insertTable", "insertHeaderRow", "insertRowAbove", "insertRowBelow", "deleteRow",
+ "insertColumnLeft", "insertColumnRight", "deleteColumn", "cellProperties", "tableProperties", "deleteTable",
+ "separator", "clear"
+ ];
- yield return Item("undo");
- yield return Item("redo");
- yield return Item("separator");
- yield return Picker("size", fontSizes, "Font size");
- yield return Picker("font", fontFamilies, "Font family");
- yield return Item("separator");
- yield return Item("bold");
- yield return Item("italic");
- yield return Item("strike");
- yield return Item("underline");
- yield return Item("separator");
- yield return Item("alignLeft");
- yield return Item("alignCenter");
- yield return Item("alignRight");
- yield return Item("alignJustify");
- yield return Item("separator");
- yield return Item("orderedList");
- yield return Item("bulletList");
- yield return Item("separator");
- yield return "{\"name\":\"header\",\"acceptedValues\":[false,1,2,3,4,5]}";
- yield return Item("separator");
- yield return Item("color");
- yield return Item("background");
- yield return Item("separator");
- yield return Item("link");
- yield return Item("image");
- yield return Item("separator");
- yield return Item("clear");
- yield return Item("codeBlock");
- yield return Item("blockquote");
- yield return Item("separator");
- yield return Item("insertTable");
- yield return Item("deleteTable");
- yield return Item("insertRowAbove");
- yield return Item("insertRowBelow");
- yield return Item("deleteRow");
- yield return Item("insertColumnLeft");
- yield return Item("insertColumnRight");
- yield return Item("deleteColumn");
- yield return Item("cellProperties");
- yield return Item("tableProperties");
- }
+ private static readonly string[] HtmlFontSizes = ["8pt", "10pt", "12pt", "14pt", "18pt", "24pt", "36pt"];
+
+ private static readonly string[] HtmlFontFamilies =
+ [
+ "Arial", "Courier New", "Georgia", "Impact",
+ "Lucida Console", "Segoe UI", "Tahoma", "Times New Roman", "Verdana"
+ ];
+
+ private static readonly string[] HtmlHeaderValues = ["false", "1", "2", "3", "4", "5", "6"];
+
+ private static IEnumerable HtmlToolbarItems()
+ => HtmlToolbarNames.Select(name => name switch
+ {
+ "size" => Picker(name, HtmlFontSizes.Select(EditorJson.Quote), "Font size"),
+ "font" => Picker(name, HtmlFontFamilies.Select(EditorJson.Quote), "Font family"),
+ "header" => Picker(name, HtmlHeaderValues, "Header"),
+ _ => Item(name)
+ });
private static string Item(string name) => "{\"name\":" + EditorJson.Quote(name) + "}";
+ /// JSON olarak yazilmis degerlerdir (metin tirnakli, sayi/bool ciplak).
private static string Picker(string name, IEnumerable values, string ariaLabel)
=> "{\"name\":" + EditorJson.Quote(name) +
- ",\"acceptedValues\":[" + string.Join(",", values.Select(EditorJson.Quote)) + "]" +
+ ",\"acceptedValues\":[" + string.Join(",", values) + "]" +
",\"options\":{\"inputAttr\":{\"aria-label\":" + EditorJson.Quote(ariaLabel) + "}}}";
}
diff --git a/api/src/Sozsoft.Platform.Domain/Extensions/AbpPermissionsExtensions.cs b/api/src/Sozsoft.Platform.Domain/Extensions/AbpPermissionsExtensions.cs
index 6964ca01..db05ca39 100644
--- a/api/src/Sozsoft.Platform.Domain/Extensions/AbpPermissionsExtensions.cs
+++ b/api/src/Sozsoft.Platform.Domain/Extensions/AbpPermissionsExtensions.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections.Generic;
using System.Linq;
using Volo.Abp.Data;
using Volo.Abp.PermissionManagement;
@@ -49,20 +48,5 @@ public static class AbpPermissionsExtensions
.Distinct()
.ToArray();
}
-
- ///
- /// Yetki ekranindaki sira () kardesler arasindadir;
- /// yeni kayit en buyuk kardes sirasinin bir fazlasini alir, kardes yoksa 1. Bos birakilan sira
- /// 0 olur ve kayit listenin en ustune cikar.
- ///
- public static int NextPermissionOrder(this IEnumerable siblings)
- {
- return siblings.Select(s => s.GetProperty(PlatformConsts.Permissions.Order)).DefaultIfEmpty(0).Max() + 1;
- }
-
- public static void SetPermissionOrder(this IHasExtraProperties record, int order)
- {
- record.SetProperty(PlatformConsts.Permissions.Order, order);
- }
}
diff --git a/claude.md b/claude.md
index fbd462aa..48224370 100644
--- a/claude.md
+++ b/claude.md
@@ -176,14 +176,19 @@ Bir çelişki varsa sıra: `ai.instructions.md` → `dotnet.instructions.md` →
- **`scriptRecipes.ts` (TS) ↔ `Domain.Shared/Editors/*.cs`** — biri değişirse diğeri de değişir.
İkisinin çıktısı **birebir aynı** olmak zorundadır; aksi hâlde seeder ile basılan script
dialogda "elle düzenlenmiş" sayılır ve kural editörü kapanır.
+- **`htmlEditorOptions.ts` ↔ `EditorOptions.Html`** — HtmlEditor varsayılanı iki yerde yazılır ve
+ bloklar birebir aynıdır. İstemci iç içe blokları tek seviye birleştirdiği için seed edilen
+ `toolbar` varsayılanın yerine geçer; biri değişip diğeri değişmezse seed'li ekranlar eski araç
+ çubuğunu gösterir.
- **Wizard seed dosyasında tekrar yok** → `Wizard` bölümü yalnızca ekrandan üretilemeyen bilgileri
taşır. `ListForm`/`Fields` içinde karşılığı olan her alan (veri kaynağı, select, anahtar alan,
kırılım/düzen/düzenleme bayrakları, görünüm option blokları, alt form, widget, iş akışı, alan
tanımları) dosyaya yazılmadan çıkarılır (`WizardSeedFileDto.ToSeedJson`) ve okurken geri
doldurulur (`FromSeedJson`). Wizard'a yeni bir alan eklerken karşılığı ListForm'da varsa listeye ekle.
Dosyada kalan wizard alanları üç başlık altında toplanır — `Menu`, `Language`, `Groups` — ve
- `ListFormCode` → `WizardCode`, `MenuParentModuleId` → `ModuleId` adıyla yazılır; varsayılanı olan
- alanlar (`CreateMenu: true`, boş `DataSourceConnectionString`) hiç yazılmaz. Bu yalnızca dosya biçimidir; C#
+ `ListFormCode` → `WizardCode`, `MenuParentModuleId` → `ModuleId`, `LanguageTexts` → `Language.Texts`
+ adıyla yazılır; varsayılanı olan alanlar (`CreateMenu: true`, boş `DataSourceConnectionString`, boş
+ `MenuParentPath` / `LanguageTexts`) hiç yazılmaz. Bu yalnızca dosya biçimidir; C#
sözleşmesi düzdür, dönüşüm `WizardSeedFileDto.ToSeedJson` / `FromSeedJson` içindedir. **Wizard
seed dosyası her zaman bu iki metotla okunup yazılır**; düz `JsonSerializer` çağrısı bloklu
bölümleri sessizce düşürür.
@@ -264,8 +269,10 @@ Bir çelişki varsa sıra: `ai.instructions.md` → `dotnet.instructions.md` →
`add_dev_request_subtasks` ile yazılır: atanan kişi bağlanan kullanıcı, bitiş tarihi bugün —
sunucu doldurur, modele bırakılmaz. `Timesheets`'i tamamen sunucu yazar
(`IMcpDevRequestTimesheetRecorder`): `get_dev_request`/`start_dev_request` talebi etkin talep yapar,
- sonraki her MCP yazma işlemi ona `App.Mcp.Timesheet.*` anahtarından Türkçe açıklama ve ölçülen
- süreyle bir satır ekler. Yeni bir MCP yazma aracı eklenirse kaydedici çağrısı ve dil anahtarı da eklenir.
+ sonraki her üretim/veri yazma işlemi (`create_table`, `deploy_wizard`, `delete_wizard`,
+ `save_component`, `delete_component`, satır araçları, `save_dev_request_analysis`,
+ `append_dev_request_note`, `add_dev_request_subtasks`) ona `App.Mcp.Timesheet.*` anahtarından Türkçe
+ açıklama ve ölçülen süreyle bir satır ekler; yalnızca talebi açan `start_dev_request` satır yazmaz. Yeni bir üretim/veri yazma aracı eklenirse kaydedici çağrısı ve dil anahtarı da eklenir.
- **Talep analizi** → `Description` talebin yazıldığı hâliyle kalır; MCP ona yazmaz. Analiz ve
üretim notları `Analysis` kolonundadır (Todo panosunda `AnalysisExpr`, Açıklama yanında sekme).
Durum sırası `Backlog → Analysis → Ready to Start → …`: "Analiz et" `save_dev_request_analysis`
@@ -289,8 +296,8 @@ Bir çelişki varsa sıra: `ai.instructions.md` → `dotnet.instructions.md` →
karşılık gelen yetkiyle eklenir. `create_table` bu yüzden `.Write`'a ek olarak `App.SqlQueryManager`
ister; tablo scriptini tasarımcının `generateCreateTableSql` çıktısıyla aynı biçimde üretir
(`McpTableCreator`) — tasarımcının şablon kolonları ya da script biçimi değişirse o da değişir.
-- **Bildirim kapsamı** → bildirim tipi, kuralı ve kuyruğu kiracıya aittir (`Adm_T_NotificationType`,
- `Adm_T_NotificationRule`, `Adm_T_Notification`; üçü de `IMultiTenant`) ve ekranların tamamı
+- **Bildirim kapsamı** → bildirim tipi, kuralı ve kuyruğu kiracıya aittir (`Adm_T_NotificationTemplate`,
+ `Adm_T_NotificationType`, `Adm_T_NotificationRule`, `Adm_T_Notification`; dördü de `IMultiTenant`) ve ekranların tamamı
Administration › Bildirimler altındadır. Ayrı bir manuel gönderim tablosu yoktur: **Bildirimler**
ekranının ekleme formu elle gönderimdir, uç alıcı × kanal açılımını kuyruğa yazar
(`list-form-dynamic-api/notification-insert`). Yeni kiracıya varsayılan tür/kural kümesi
diff --git a/configs/seeds/host/data/App.Languages.LanguageText.json b/configs/seeds/host/data/App.Languages.LanguageText.json
index 038d6dd6..5f5a0aaf 100644
--- a/configs/seeds/host/data/App.Languages.LanguageText.json
+++ b/configs/seeds/host/data/App.Languages.LanguageText.json
@@ -67428,6 +67428,34 @@
"Key": "App.Mcp.Timesheet.DeleteRow",
"Value": "{0} ekranından satır silindi"
},
+ {
+ "Id": "02d139fc-753d-567a-b672-8aa0a0212ad0",
+ "CultureName": "en",
+ "ResourceName": "Platform",
+ "Key": "App.Mcp.Timesheet.AppendNote",
+ "Value": "Production note added to the analysis"
+ },
+ {
+ "Id": "9e9cc3eb-557b-5a02-8730-29347f4f77b4",
+ "CultureName": "tr",
+ "ResourceName": "Platform",
+ "Key": "App.Mcp.Timesheet.AppendNote",
+ "Value": "Analize üretim notu eklendi"
+ },
+ {
+ "Id": "eb67cbd2-c6b3-57d6-ab44-ee7e79636553",
+ "CultureName": "en",
+ "ResourceName": "Platform",
+ "Key": "App.Mcp.Timesheet.AddSubTasks",
+ "Value": "{0} completed subtasks added"
+ },
+ {
+ "Id": "ec94c375-713b-51f8-8db8-5e58b9e4ce7b",
+ "CultureName": "tr",
+ "ResourceName": "Platform",
+ "Key": "App.Mcp.Timesheet.AddSubTasks",
+ "Value": "{0} tamamlanmış alt görev eklendi"
+ },
{
"Id": "a5e491f7-a85c-572f-bf66-4245cb84fa36",
"CultureName": "en",
diff --git a/ui/src/views/shared/htmlEditorOptions.ts b/ui/src/views/shared/htmlEditorOptions.ts
index 6ad2a383..95bac9da 100644
--- a/ui/src/views/shared/htmlEditorOptions.ts
+++ b/ui/src/views/shared/htmlEditorOptions.ts
@@ -3,6 +3,7 @@
* `` ile, dinamik formlar `mergeHtmlEditorOptions` ile
* aynı yetenek setini alır; ListForm'daki "HTML toolbar" kalıbı da buradan beslenir.
* Değerler JSON'a yazılabilir kalmalıdır — kalıp editorOptions kolonuna olduğu gibi kaydedilir.
+ * Seed'li ekranlar aynı blokları C# `EditorOptions.Html` ile yazar; biri değişirse diğeri de değişir.
*/
import type { Properties as HtmlEditorProperties } from 'devextreme/ui/html_editor'