Compare commits
114 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6742393271 | ||
|
|
ad00e75911 | ||
|
|
9b9ef9c100 | ||
|
|
59ec8950a3 | ||
|
|
54e3390cbf | ||
|
|
76091c0518 | ||
|
|
a20d47cab8 | ||
|
|
3ec9a94860 | ||
|
|
ed143c01d0 | ||
|
|
1423865a8c | ||
|
|
c33926d6e5 | ||
|
|
8be8fef972 | ||
|
|
4bf6b4c257 | ||
|
|
fd8d2ef73a | ||
|
|
6f756f33d0 | ||
|
|
63653dd974 | ||
|
|
43e3aeebd0 | ||
|
|
2e4e089e4a | ||
|
|
e80fc2337c | ||
|
|
3b93cd3895 | ||
|
|
a0491f9db8 | ||
|
|
a1a76b1a95 | ||
|
|
3db82df964 | ||
|
|
7c9934f56e | ||
|
|
9e99475a3e | ||
|
|
e237cacc2a | ||
|
|
47d9a20998 | ||
|
|
7a0839a3f6 | ||
|
|
0941b9143b | ||
|
|
3622764923 | ||
|
|
4d7aaef901 | ||
|
|
78bc20b645 | ||
|
|
c4de55d459 | ||
|
|
7ec77c551f | ||
|
|
462dd20edb | ||
|
|
1fc7111da2 | ||
|
|
a3667539d6 | ||
|
|
a670d2e966 | ||
|
|
91de623a85 | ||
|
|
ae7ddf6c7f | ||
|
|
9981d5b19a | ||
|
|
422e32489f | ||
|
|
e4cecc8a62 | ||
|
|
9de6b1d05c | ||
|
|
ec6b35d6d5 | ||
|
|
b1dac82d56 | ||
|
|
0d0834efaf | ||
|
|
9e362f6671 | ||
|
|
f744d18f83 | ||
|
|
b13f0b6a57 | ||
|
|
baef565dd4 | ||
|
|
90a66562e7 | ||
|
|
4c09de9ad0 | ||
|
|
7186cb0ab0 | ||
|
|
870c98690c | ||
|
|
4003dc1845 | ||
|
|
61234dec3f | ||
|
|
690da79b87 | ||
|
|
6da6654ec4 | ||
|
|
7659dbdaa1 | ||
|
|
003e74101f | ||
|
|
ece4ee9783 | ||
|
|
eb77285e59 | ||
|
|
010594ac27 | ||
|
|
3aa74318c2 | ||
|
|
3152cd681b | ||
|
|
7870ebb7bc | ||
|
|
a0121f8e11 | ||
|
|
f5ba73f875 | ||
|
|
9a2d88b93b | ||
|
|
c993b77b5d | ||
|
|
39ad5cab12 | ||
|
|
b517b7346e | ||
|
|
853ad0240e | ||
|
|
abece2b33e | ||
|
|
c2f1b203ab | ||
|
|
b4c8fc3a2b | ||
|
|
cedf788787 | ||
|
|
c1a74bc245 | ||
|
|
f372c17249 | ||
|
|
b5cba69e28 | ||
|
|
cced417ede | ||
|
|
ee121db521 | ||
|
|
f949d0ae78 | ||
|
|
0abf181a9c | ||
|
|
534dcce6da | ||
|
|
f292b6b6a5 | ||
|
|
783c1fe1d3 | ||
|
|
a5ee6de819 | ||
|
|
de7e6c1e21 | ||
|
|
5217887bb8 | ||
|
|
1fe4abaeb6 | ||
|
|
db24baed91 | ||
|
|
5626c27a90 | ||
|
|
3ecb4e2102 | ||
|
|
42d502f812 | ||
|
|
bbfa1cee0d | ||
|
|
a42776e9c9 | ||
|
|
2368ee3825 | ||
|
|
b9550acda2 | ||
|
|
a01ce1533a | ||
|
|
56b74b85a5 | ||
|
|
0d27742f8c | ||
|
|
a1f5e5e539 | ||
|
|
38e3ef5797 | ||
|
|
93e337b9b2 | ||
|
|
81960df31b | ||
|
|
0224f4b3e1 | ||
|
|
ac4ccd76d6 | ||
|
|
24eb249f9f | ||
|
|
9a76afae24 | ||
|
|
25ae424f42 | ||
|
|
9cc77fb3a1 | ||
|
|
f39a3e5764 |
1130 changed files with 125896 additions and 70267 deletions
309
.github/instructions/ai.instructions.md
vendored
309
.github/instructions/ai.instructions.md
vendored
|
|
@ -14,14 +14,25 @@ Primary principle:
|
||||||
|
|
||||||
- Configuration first, code last.
|
- Configuration first, code last.
|
||||||
|
|
||||||
|
Companion documents (this file wins on conflict):
|
||||||
|
|
||||||
|
| Document | Scope |
|
||||||
|
| --- | --- |
|
||||||
|
| `lowcode.instructions.md` | **Authoring reference**: exact schemas, enum values and worked examples for producing wizard screens, editor scripts, custom components, SQL objects and endpoints. Read it before writing any artifact. |
|
||||||
|
| `lowcode-reference.instructions.md` | **Capability inventory**: field-by-field contract of every ListForm/ListFormField JSON column, chart and pivot options, SQL command hooks, Visual Designer toolbox, the scheduled-mail module and (§12) every ready-made screen under the Saas and Administration menus. Sweep its §0 checklist before producing a screen or component; a request whose answer already exists as a platform screen (§12.5) is configured there, not rebuilt. |
|
||||||
|
| `dotnet.instructions.md` | Binding code standard for everything under `api/` |
|
||||||
|
| `list.instructions.md` | Step-by-step procedure for adding a module / list through seeders |
|
||||||
|
| `../../README.md` | Technical map: what exists, where it lives, how it is wired |
|
||||||
|
| `../../CLAUDE.md` | Operating rules and standing defaults for AI agents in this repo |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. Platform Scope
|
## 2. Platform Scope
|
||||||
|
|
||||||
Core stack:
|
Core stack:
|
||||||
|
|
||||||
- Backend: C# .NET 9 + ABP 9.x
|
- Backend: C# .NET 10 (C# 14) + ABP 10.x — kod standardı: `dotnet.instructions.md`
|
||||||
- Frontend: React 18 + DevExtreme
|
- Frontend: React 19 + DevExtreme
|
||||||
- Database: SQL Server (dynamic datasource support)
|
- Database: SQL Server (dynamic datasource support)
|
||||||
- Cache: Redis
|
- Cache: Redis
|
||||||
- Background jobs: Hangfire + ABP Background Workers
|
- Background jobs: Hangfire + ABP Background Workers
|
||||||
|
|
@ -41,11 +52,25 @@ System nature:
|
||||||
3. Every proposal must include tenant and permission design.
|
3. Every proposal must include tenant and permission design.
|
||||||
4. Never bypass platform authorization patterns.
|
4. Never bypass platform authorization patterns.
|
||||||
5. Never hardcode secrets, tenant ids, or connection strings.
|
5. Never hardcode secrets, tenant ids, or connection strings.
|
||||||
|
6. **Stay inside the framework; ask before stepping out.** The platform may extend itself through
|
||||||
|
its own mechanisms (seed artifacts; adding a missing event, prop, or small backward-compatible
|
||||||
|
capability to an existing UI component — when Developer Kit tooling builds ~70% of the
|
||||||
|
requested component, the rest is completed with such small additions, never with a new
|
||||||
|
component; backward-compatible additions to script recipes / editor options / tokens).
|
||||||
|
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, `Routes`
|
||||||
|
in `MenusData.json`, 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:
|
||||||
|
`lowcode.instructions.md` §0.8.
|
||||||
|
|
||||||
Exception policy:
|
Exception policy:
|
||||||
|
|
||||||
- Code-level React or backend development can be considered only if user explicitly requests code implementation and configuration path is insufficient.
|
- Code-level React or backend development can be considered only if user explicitly requests code implementation and configuration path is insufficient.
|
||||||
- If exception is required, AI must explain why configuration-based options are not enough.
|
- If exception is required, AI must explain why configuration-based options are not enough.
|
||||||
|
- Never say "cannot be done": the answer is either an in-frame build or an approval question.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -116,7 +141,7 @@ Capabilities:
|
||||||
- Query
|
- Query
|
||||||
- WebService
|
- WebService
|
||||||
- Cascading lookup parent-child behavior
|
- Cascading lookup parent-child behavior
|
||||||
- Dynamic validation, editor options, editor scripts
|
- Dynamic validation, editor options, editor scripts (see 7.1)
|
||||||
- Conditional formatting and style injection
|
- Conditional formatting and style injection
|
||||||
- Grid state save/load/reset
|
- Grid state save/load/reset
|
||||||
- User filter save/apply/delete flows
|
- User filter save/apply/delete flows
|
||||||
|
|
@ -146,6 +171,34 @@ Supported editor types:
|
||||||
- dxTextArea
|
- dxTextArea
|
||||||
- dxTextBox
|
- dxTextBox
|
||||||
|
|
||||||
|
### 7.1 Editor Script and Editor Options (builder-backed)
|
||||||
|
|
||||||
|
`EditorScript` and `EditorOptions` are produced from a shared vocabulary, in two places that
|
||||||
|
must stay identical:
|
||||||
|
|
||||||
|
| Surface | Location |
|
||||||
|
| --- | --- |
|
||||||
|
| Visual builder (UI) | `ui/src/views/admin/listForm/edit/json-row-operations/editor-script/scriptRecipes.ts` and `.../editor-options/optionSpecs.ts` + `presets.ts` |
|
||||||
|
| Seeder API (C#) | `api/src/Sozsoft.Platform.Domain.Shared/Editors/` — `EditorScript`, `EditorScriptRule`, `EditorScriptCondition`, `EditorOptions`, `EditorOptionsBuilder` |
|
||||||
|
|
||||||
|
Rules AI must follow:
|
||||||
|
|
||||||
|
- Prefer a recipe over hand-written JS. Recipes cover arithmetic (`Multiply`, `Subtract`,
|
||||||
|
`Percent`, `Sum`, `Formula`), dates (`Today`, `Days`, `Hours`), data movement (`Copy`,
|
||||||
|
`SetValue`, `Clear`, `ApiToField`), and view/notify actions (`ReadOnly`, `Notify`, `Ask`,
|
||||||
|
`OpenUrl`). Free-form JS is `Custom` and is the last option.
|
||||||
|
- Generated script carries a `// @builder {...}` header on the first line that stores the rules,
|
||||||
|
plus `// @runOnOpen` when any rule runs on form open. **Never edit the header by hand and never
|
||||||
|
emit a script that does not match what the builder would emit** — the dialog then treats the
|
||||||
|
script as manually edited and closes the rule editor.
|
||||||
|
- Rule triggers: `change` (default), `open` (default-value generation), `both`. Conditions join
|
||||||
|
with `and` / `or`.
|
||||||
|
- When a recipe changes on one side, change its port on the other side in the same task.
|
||||||
|
- In seeders write `EditorOptions = EditorOptions.Number(2).ShowClearButton()` style expressions
|
||||||
|
instead of raw JSON strings. Options marked `platform: true` in `optionSpecs.ts` deserialize
|
||||||
|
into typed DTOs (`GridBoxOptionsDto`, `TagBoxOptionsDto`, `ImageUploadOptionsDto`); wrong types
|
||||||
|
are silently ignored.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 8. Dynamic Form System
|
## 8. Dynamic Form System
|
||||||
|
|
@ -172,6 +225,8 @@ Supported component families:
|
||||||
- TreeList / Tree view
|
- TreeList / Tree view
|
||||||
- SubForm tabs (List, Tree, Gantt, Scheduler, Form, Chart)
|
- SubForm tabs (List, Tree, Gantt, Scheduler, Form, Chart)
|
||||||
- Widget Group (dashboard KPI cards)
|
- Widget Group (dashboard KPI cards)
|
||||||
|
- TodoBoard — a Kanban board: columns come from `statusExpr` values, cards change status by drag
|
||||||
|
and drop, new columns (statuses) can be added from the board
|
||||||
|
|
||||||
Runtime UI capabilities:
|
Runtime UI capabilities:
|
||||||
|
|
||||||
|
|
@ -212,6 +267,54 @@ If user requests custom code explicitly:
|
||||||
- Provide a warning that low-code path is preferred.
|
- Provide a warning that low-code path is preferred.
|
||||||
- Offer configuration-first alternative first.
|
- Offer configuration-first alternative first.
|
||||||
|
|
||||||
|
### 10.1 Custom Component path (still not hand-written React)
|
||||||
|
|
||||||
|
When a screen genuinely cannot be expressed as a ListForm, the next step is **not** a physical
|
||||||
|
React page. It is a Custom Component built in the Visual Designer and attached to a menu through
|
||||||
|
the Wizard's `Custom` path.
|
||||||
|
|
||||||
|
- The component is stored in the database (`Name`, `RoutePath`, `Code`, `Props`, `Dependencies`,
|
||||||
|
`DataSources`) and compiled in the browser with `@babel/standalone`.
|
||||||
|
- The designer document lives in `Props.visualDesigner` **and** in the
|
||||||
|
`/*__SOZSOFT_VISUAL_DESIGNER__…__*/` header of the generated code. `sourceMode` is `visual` or
|
||||||
|
`code`; switching to `code` is one-way — the canvas cannot be restored afterwards, so do not
|
||||||
|
propose it casually.
|
||||||
|
- Toolbox families: `layout` (`PageContainer`, `FlexRow`, `Spacer`), `data` (`Form` — owns the
|
||||||
|
four CRUD endpoints and acts as a container), `platform` (`ListView`, `DataGridView`,
|
||||||
|
`TreeView`, `GanttView`, `TodoBoard`, `CardView`, `SchedulerView`, `PivotView`, `ChartView` —
|
||||||
|
each embeds a ListForm screen through `listFormCode`), `html`/`ui`, and other custom
|
||||||
|
components as `custom`.
|
||||||
|
- **Reuse before drawing:** if the requirement is a list/tree/chart that a ListForm already
|
||||||
|
serves, drop the matching `platform` node with its `listFormCode` instead of rebuilding it out
|
||||||
|
of primitives.
|
||||||
|
- Data binding goes through CRUD endpoints. Filter operators map to the CrudEndpoint `GetList`
|
||||||
|
contract: `eq` is a bare query parameter, every other operator carries its name as a suffix
|
||||||
|
(`?Name.contains=…`).
|
||||||
|
- Permissions are two-layered and mandatory: node-level `designerPermission` for visibility, and
|
||||||
|
per-command Form permissions (`selectPermission`, `insertPermission`, `updatePermission`,
|
||||||
|
`deletePermission`). In `Otomatik` mode the base is the read permission the Wizard created for
|
||||||
|
the component and the commands get `''` / `.Create` / `.Update` / `.Delete` suffixes.
|
||||||
|
- List screen permissions: `App.DeveloperKit.CustomComponents{,.Create,.Update,.Delete,.Export,.Import}`.
|
||||||
|
- Every save/delete is mirrored into `configs/seeds/{scope}/data/App.DeveloperKit.CustomComponents.json`; the endpoints it uses are
|
||||||
|
referenced through `crud/{Entity}.json`. A component that is not seed-backed is not done.
|
||||||
|
|
||||||
|
### 10.2 SQL View Designer
|
||||||
|
|
||||||
|
For shaping data before a screen binds to it, prefer a designed view over an inline query:
|
||||||
|
`SqlViewDesignerDialog` produces T-SQL from a diagram (sources, joins, `CROSS/OUTER APPLY`,
|
||||||
|
derived subqueries) plus an SSMS-style criteria grid (Column / Alias / Output / Group By / Sort /
|
||||||
|
Filter / Or…). It is **one-way** — model to T-SQL. A view that does not parse back into the
|
||||||
|
canonical shape drops the dialog to raw SQL mode, so hand-editing a designed view costs the
|
||||||
|
designer. Aggregations available in the Group By column: `GroupBy`, `Where`, `SUM`, `COUNT`,
|
||||||
|
`COUNT_DISTINCT`, `AVG`, `MIN`, `MAX`.
|
||||||
|
|
||||||
|
Persistence: only the **table designer's** deploy writes a seed file automatically
|
||||||
|
(`{sql|postgres}/object/{TableName}.sql`, full CREATE script, via `save-table-script`). View
|
||||||
|
Designer deploys and any DDL run from the SQL editor (views, procedures, functions) reach the
|
||||||
|
database only — their seed files under `{sql|postgres}/object/` must be written explicitly.
|
||||||
|
Full mechanics and the SP/function production patterns: `lowcode.instructions.md` §8.1–§8.2.1
|
||||||
|
and §9.5.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 11. Integrations
|
## 11. Integrations
|
||||||
|
|
@ -342,7 +445,8 @@ When configuration cannot satisfy requirement:
|
||||||
1. Try SQL-based design
|
1. Try SQL-based design
|
||||||
2. Try Custom Endpoint
|
2. Try Custom Endpoint
|
||||||
3. Try Dynamic Service
|
3. Try Dynamic Service
|
||||||
4. Propose minimal code change with explicit justification
|
4. Propose minimal code change with explicit justification — this step is out of frame:
|
||||||
|
**ask and wait for approval before touching code** (rule 3.6).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -540,13 +644,39 @@ When in doubt, AI must choose the path that preserves:
|
||||||
|
|
||||||
## 31. Seeder-Driven Low-Code Development Guide (Authoritative)
|
## 31. Seeder-Driven Low-Code Development Guide (Authoritative)
|
||||||
|
|
||||||
AI must learn and teach implementation flow primarily from these seed assets:
|
There are **two seed surfaces**. AI must not mix them.
|
||||||
|
|
||||||
- api/src/Sozsoft.Platform.DbMigrator/Seeds/ListFormSeeder_Saas.cs
|
**A. Repository seeds (shipped with code)** — `api/src/Sozsoft.Platform.DbMigrator/Migrations/`.
|
||||||
- api/src/Sozsoft.Platform.DbMigrator/Seeds/MenusData.json
|
Platform-owned screens, menus, permissions and language texts. Changing them requires a build and
|
||||||
- api/src/Sozsoft.Platform.DbMigrator/Seeds/PermissionsData.json
|
a deploy. AI must learn and teach implementation flow primarily from these assets:
|
||||||
- api/src/Sozsoft.Platform.DbMigrator/Seeds/HostData.json
|
|
||||||
- api/src/Sozsoft.Platform.DbMigrator/Seeds/LanguagesData.json
|
- ListFormSeeder_Saas.cs / ListFormSeeder_Administration.cs / ListFormSeeder_{Modul}.cs
|
||||||
|
- MenusData.json
|
||||||
|
- PermissionsData.json
|
||||||
|
- HostData.json
|
||||||
|
- LanguagesData.json
|
||||||
|
- WizardDataSeeder.cs / CrudDataSeeder.cs — the seeders that read
|
||||||
|
surface B below
|
||||||
|
- ListFormSeedDataApplier, CrudEndpointSeedApplier (Domain/Data/) — the single home of the apply
|
||||||
|
rules; both the migrate/seed flow and the wizard import's completion call the same services.
|
||||||
|
`data/` rows are inserted-only on migrate (a possibly stale file must not overwrite live data);
|
||||||
|
`crud/` entries are timestamp-guarded — an existing record is updated or deleted only when the
|
||||||
|
file's `GeneratedAt` is newer than the record's `LastModificationTime`/`CreationTime`. **Migrate
|
||||||
|
adds; it does not overwrite.** Follow this rule in any new seeder.
|
||||||
|
|
||||||
|
**B. Runtime seeds (written by the running application)** — `configs/seeds/`, resolved through
|
||||||
|
`SeedPathResolver`. The Wizard, the Custom Components screen / Visual Designer, the CRUD
|
||||||
|
Endpoint Manager and every ListForm screen with a `SeedFilePath` write here; the
|
||||||
|
seeders above restore them when the database is recreated. Scope folders mirror the CDN layout:
|
||||||
|
|
||||||
|
```
|
||||||
|
configs/seeds/host/{wizard,data,crud,sql/{object,execute},postgres/{object,execute}}
|
||||||
|
configs/seeds/tenants/{tenantId}/… same layout, tenant scope
|
||||||
|
configs/seeds/.imports/{importId}/… wizard import staging + backup (not scanned)
|
||||||
|
```
|
||||||
|
|
||||||
|
Rule: anything produced at runtime must land in surface B. **A feature that disappears when the
|
||||||
|
database is recreated is not delivered.**
|
||||||
|
|
||||||
If user asks "how to add a new module/screen", AI must answer with this exact operational sequence.
|
If user asks "how to add a new module/screen", AI must answer with this exact operational sequence.
|
||||||
|
|
||||||
|
|
@ -659,6 +789,39 @@ When user asks for a screen/module, AI must answer in this order:
|
||||||
|
|
||||||
AI should produce practical, copy-adaptable artifact definitions and avoid abstract-only explanations.
|
AI should produce practical, copy-adaptable artifact definitions and avoid abstract-only explanations.
|
||||||
|
|
||||||
|
### 31.9 Screen brief, discovery questions and image-based requests
|
||||||
|
|
||||||
|
A request arrives either as prose or as a **screenshot / mockup / photo** of the desired screen.
|
||||||
|
Both enter the same pipeline — and for a **new screen/component the pipeline starts with
|
||||||
|
questions, not files**:
|
||||||
|
|
||||||
|
1. **Run the discovery round** (`lowcode.instructions.md` §0.6). Ask in batches (max 4 questions
|
||||||
|
per round, each with 2–4 options and a recommended default), max three rounds. Round 1 always
|
||||||
|
covers: screen kind (List/Wizard vs Custom Component vs single-record form), data source,
|
||||||
|
owning module, and tenancy scope. Round 2 uses the path-specific question set (§0.6.3 for
|
||||||
|
List, §0.6.4 for Custom). Never ask what the repo already answers — search first, confirm
|
||||||
|
findings instead of re-asking. If the user says "you decide / don't ask", skip the round and
|
||||||
|
build on defaults.
|
||||||
|
2. Turn the answers into a single screen brief — purpose, data source, columns and editors,
|
||||||
|
layouts, actions, sub-screens, widgets, approval flow, menu, permissions, tenancy, language
|
||||||
|
texts. For an image, read it first (page regions → main region type → toolbar → columns →
|
||||||
|
filters → form controls → row actions); the image answers most of round 2, so ask only what
|
||||||
|
an image cannot show (§0.7.4).
|
||||||
|
3. **Get the brief confirmed before writing any seed file.** Mark defaulted lines `(varsayılan)`
|
||||||
|
so the user sees what they are approving.
|
||||||
|
4. Pick the tool: grid-shaped work → SQL Query Manager + Wizard; free layout / dashboard /
|
||||||
|
single-record custom form → Custom Component. **When in doubt, recommend the Wizard.**
|
||||||
|
5. Produce the seed files in dependency order, then list them and restate the defaulted choices.
|
||||||
|
|
||||||
|
An image is a reference, not a pixel contract: build the closest thing the platform's own
|
||||||
|
components give, and state at the end what had no equivalent. Three things are never assumed,
|
||||||
|
always asked: which table is written to, who approves (when there is an approval flow), and the
|
||||||
|
formula of any calculation/business rule. This question-first rule applies only to producing a
|
||||||
|
**new** screen/component; fixes, field additions, debugging and explanations proceed directly.
|
||||||
|
|
||||||
|
The concrete question sets, brief template, the image-element → artifact mapping and the
|
||||||
|
visual-control → `EditorType` mapping are in `lowcode.instructions.md` §0.6–§0.7.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 32. Mandatory Default Behaviors (Do Not Ask Repeatedly)
|
## 32. Mandatory Default Behaviors (Do Not Ask Repeatedly)
|
||||||
|
|
@ -701,7 +864,131 @@ Rules:
|
||||||
- Permission contract must be created/bound for both parent and child menu items.
|
- Permission contract must be created/bound for both parent and child menu items.
|
||||||
- Route/menu consistency remains mandatory (`Url` and screen contract must match).
|
- Route/menu consistency remains mandatory (`Url` and screen contract must match).
|
||||||
|
|
||||||
### 32.3 AI enforcement requirement
|
### 32.3 Wizard component kind and menu creation
|
||||||
|
|
||||||
|
`ListFormWizardDto.ComponentKind` is the wizard's fork and the first field in the seed file.
|
||||||
|
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` |
|
||||||
|
| `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
|
||||||
|
`Custom` path.
|
||||||
|
|
||||||
|
`CreateMenu = false` (menu-less wizard): no menu record — and no parent menu record — is created;
|
||||||
|
ListForm, permissions and language keys are still produced. Use it when the screen is meant to be
|
||||||
|
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`.
|
||||||
|
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`);
|
||||||
|
if left blank the server writes the database values back, so the seed file always carries both
|
||||||
|
languages.
|
||||||
|
- Defining a new group writes those texts to the language key with the same name as the group.
|
||||||
|
- AI must never propose a permission group without EN + TR display names.
|
||||||
|
|
||||||
|
### 32.5 Wizard export/import is a package, not a file
|
||||||
|
|
||||||
|
Exporting a wizard produces a zip whose paths are relative to the scope folder
|
||||||
|
(`host` / `tenants/{tenantId}`) and which carries every dependency:
|
||||||
|
|
||||||
|
The closure is **recursive** (`WizardExportCollector`): a sub-form's wizard file is queued and its
|
||||||
|
own dependencies are collected under the same rules, so the archive stands on its own.
|
||||||
|
|
||||||
|
- `wizard/{file}.json` — the screen itself plus the wizard files of the sub-forms it references
|
||||||
|
through `SubFormsDto[].Code`, recursively
|
||||||
|
- `data/App.DeveloperKit.CustomComponents.json` — `Custom` path; the file is filtered down to the
|
||||||
|
exported component and its dependency chain, never exported whole
|
||||||
|
- `data/{ListFormCode}.json` — the screen's own data mirror when `SeedFilePath` is set
|
||||||
|
- `crud/{entity}.json` — endpoints those components use
|
||||||
|
- `{sql|postgres}/{object|execute}/{object}.sql` — the object behind `SelectCommand`, the objects
|
||||||
|
a lookup query (`LookupDto.LookupQuery`) references through `FROM`/`JOIN`, and on the `Custom`
|
||||||
|
path the tables the CRUD endpoints sit on
|
||||||
|
|
||||||
|
Script files are never guessed from a fixed provider/folder combination: the scope's script folders
|
||||||
|
are scanned from disk, and every file matching the object name is added. A missing script is **not**
|
||||||
|
a warning — not every table is seed-managed (ABP's own tables come from EF migrations); whether the
|
||||||
|
target has it is decided by the import analysis against the real database, never assumed here.
|
||||||
|
|
||||||
|
An unresolved **recorded reference** (a sub-form, a `SeedFilePath` mirror, a `DataSources[].SeedFile`,
|
||||||
|
a component row named in a seed file but with no counterpart) does not block the download but is
|
||||||
|
never silent: `ExportAnalysis` returns the full archive contents plus what is missing, and the File
|
||||||
|
Manager shows that list before downloading.
|
||||||
|
|
||||||
|
The import screen is a four-step wizard (`Steps`): **Files** (resolve conflicts) → **Checks**
|
||||||
|
(findings; a schema finding opens the existing `SqlTableDesignerDialog`, and the analysis re-runs
|
||||||
|
after a deploy) → **Plan** (the full list of what confirming will do, in the server's completion
|
||||||
|
order) → **Apply**. Going back is always allowed; going forward is gated — unresolved conflicts
|
||||||
|
block Checks, a blocking finding blocks Plan.
|
||||||
|
|
||||||
|
Server-side it is two phases: `AnalyzeImport` (unzip to `.imports/{id}/staged`, classify each entry
|
||||||
|
as `New` / `Identical` / `Conflict`) then `ApplyImport` (file-by-file write, previous content backed
|
||||||
|
up so `RollbackImport` can undo the whole batch), closed by `CompleteImport`.
|
||||||
|
|
||||||
|
`AnalyzeImport` also **validates** before anything is written (`WizardImportValidator`). With at
|
||||||
|
least one `Error` finding the session is marked `blocked` on the server and `ApplyImport` refuses,
|
||||||
|
so the check cannot be bypassed by calling the endpoint directly. Errors: malformed JSON; a wizard
|
||||||
|
file with no `Wizard`/`ListForm` section; a screen's or a `crud/{Entity}.json`'s table missing in
|
||||||
|
the target **and** absent from the archive's scripts; a `data/{code}.json` whose ListForm exists
|
||||||
|
neither in the archive nor in the target; an unresolvable custom-component `Dependencies` /
|
||||||
|
`DataSources[].SeedFile`; an empty component name or route on the `Custom` path; a sub-form
|
||||||
|
reference with no counterpart. Warnings: object existence could not be queried, provider mismatch
|
||||||
|
(`sql` ↔ `postgres`), a missing `SeedFilePath` data file, an empty file.
|
||||||
|
|
||||||
|
The analysis also **compares the table schema**. Generated table scripts are wrapped in
|
||||||
|
`IF OBJECT_ID(...) IS NULL BEGIN CREATE TABLE ... END`, so when the target already has the table the
|
||||||
|
`CREATE` block is skipped entirely and a new column never arrives from the script itself — the screen
|
||||||
|
would then fail with an invalid-column error. The incoming `CREATE TABLE` declaration (`SqlCreateTableParser`) is
|
||||||
|
compared against the live `INFORMATION_SCHEMA.COLUMNS` shape. The split follows the risk of loss:
|
||||||
|
**a missing column is added** — adding is lossless, so it is reported as a Warning with the statement
|
||||||
|
it will run (`WizardImportIssueDto.Statement`) and applied at completion; the one exception is a
|
||||||
|
`NOT NULL` column with no default on a non-empty table, which the database itself would reject and
|
||||||
|
where the value to write is a business decision — that blocks. **An existing column is never
|
||||||
|
changed**: a length / precision / scale / nullability difference is an Error and stops the import,
|
||||||
|
because narrowing truncates and a type conversion can fail. An extra column in the target is a
|
||||||
|
Warning and is never dropped; no path emits `DROP TABLE`, `DROP COLUMN` or `ALTER COLUMN`. The
|
||||||
|
comparison lives in one place (`SqlTableSchemaComparer`) and both the analysis and the completion
|
||||||
|
use it. Type **names** are deliberately not compared: the provider catalog reports its own
|
||||||
|
canonical name (`VARCHAR` → `character varying` on PostgreSQL), so a textual comparison would raise
|
||||||
|
a false difference on every `VARCHAR` column.
|
||||||
|
|
||||||
|
A `data/*.json` entry is compared and merged **by row, not by file**: the diff shows only the
|
||||||
|
rows whose keys the archive carries (file-level `GeneratedAt`/`Order` are excluded), so unrelated
|
||||||
|
records never surface as a difference — no matching row means `New` (insert), a matching identical
|
||||||
|
row means `Identical` (nothing is transferred), a matching different row means `Conflict` (update).
|
||||||
|
The file itself is never overwritten: rows are merged into the target by key (incoming row wins,
|
||||||
|
target-only rows are kept). `CompleteImport` then applies exactly the
|
||||||
|
`data/` files it wrote to the database through `IListFormSeedDataApplier` with
|
||||||
|
`overwriteExisting: true` — a missing row is inserted, an existing one is updated from the file
|
||||||
|
(unlike migrate/seed, where a possibly stale file must not overwrite live data), and a row that was
|
||||||
|
soft deleted is revived by clearing the columns the screen's `DeleteCommand` sets
|
||||||
|
(`IsDeleted`/`DeletionTime`/`DeleterId`), otherwise the default filter would keep hiding it — and
|
||||||
|
reports the inserted/updated/skipped counts. Data files that were already identical to the target
|
||||||
|
are applied too: there was nothing to write, but the rows may be missing or soft deleted in the
|
||||||
|
database. A file the user chose to keep (different, not written) is left alone. This step is not
|
||||||
|
covered by `RollbackImport`, which only restores files and must be run before it.
|
||||||
|
|
||||||
|
`CompleteImport` runs in **dependency order and the order is fixed**: (a) the `.sql` scripts under
|
||||||
|
`{sql|postgres}/{object,execute}` (`object` first) — a missing table is created here, otherwise the
|
||||||
|
later steps would fail on it; (b) columns missing from existing tables are added; (c) `crud/*.json`
|
||||||
|
through `ICrudEndpointSeedApplier`; (d) the `data/*.json` rows; (e) `wizard/*.json` — the screens
|
||||||
|
are installed through the same path the wizard screen uses (`DeployWizardAsync`), reusing the file's
|
||||||
|
`ListForm`/`Fields` contract as-is instead of rebuilding it from the wizard answers. A screen that
|
||||||
|
already exists is left untouched, and each screen is installed in its own unit of work. No migrate
|
||||||
|
run is needed: the screen works as soon as the import finishes.
|
||||||
|
|
||||||
|
Limits: allowed root folders `wizard`, `crud`, `data`, `sql`, `postgres` (and only `object` /
|
||||||
|
`execute` under the SQL provider folders); 5 MB per entry, 50 MB per archive, 500 entries.
|
||||||
|
Permissions: `App.Listforms.Wizard.Export` / `.Import`.
|
||||||
|
|
||||||
|
AI must not propose moving a screen between environments by copying a single json file.
|
||||||
|
|
||||||
|
### 32.6 AI enforcement requirement
|
||||||
|
|
||||||
AI must proactively apply these defaults in:
|
AI must proactively apply these defaults in:
|
||||||
|
|
||||||
|
|
|
||||||
150
.github/instructions/dotnet.instructions.md
vendored
Normal file
150
.github/instructions/dotnet.instructions.md
vendored
Normal file
|
|
@ -0,0 +1,150 @@
|
||||||
|
# .NET / ABP Kod Standardı
|
||||||
|
|
||||||
|
Bu doküman `api/` altındaki tüm projeler için bağlayıcıdır. Platform davranışına dair
|
||||||
|
kurallar için `ai.instructions.md` esastır; çelişki olursa o dosya kazanır.
|
||||||
|
|
||||||
|
Standart derlemede zorlanır: `api/Directory.Build.props` analizörleri açar,
|
||||||
|
`api/.editorconfig` kural şiddetlerini tanımlar. Bu iki dosya standardın tek
|
||||||
|
kaynağıdır; proje dosyalarında tekrar edilmez.
|
||||||
|
|
||||||
|
## 1. Hedef sürümler
|
||||||
|
|
||||||
|
| Katman | Sürüm | Nerede tanımlı |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| SDK | .NET 10 (`10.0.300`) | `api/global.json` |
|
||||||
|
| TargetFramework | `net10.0` | proje dosyaları |
|
||||||
|
| Dil | C# 14 (`LangVersion=latest`) | `api/common.props` |
|
||||||
|
| ABP | 10.0.0 | `Volo.Abp.*` paket referansları |
|
||||||
|
|
||||||
|
Sürüm yükseltmesi tek noktadan yapılır: `global.json` + `common.props` + paket sürümleri.
|
||||||
|
Proje dosyalarına TFM/LangVersion tekrar yazılmaz.
|
||||||
|
|
||||||
|
## 2. Nullable
|
||||||
|
|
||||||
|
`Directory.Build.props` içinde **çözüm geneli `Nullable` ayarı yapılmaz.** Sebebi
|
||||||
|
EF Core: kolonun zorunlu olup olmadığını, entity assembly'sinin nullable annotation
|
||||||
|
bağlamından türetir. Global bir `enable`/`annotations` anahtarı, bir sonraki
|
||||||
|
`dotnet ef migrations add` çağrısında tüm `string` özelliklerini sessizce NOT NULL
|
||||||
|
kolona çevirir — seeder'ın doldurmadığı her alan da o anda kırılır.
|
||||||
|
|
||||||
|
Kural:
|
||||||
|
|
||||||
|
- Nullable, proje bazında ve bilinçli olarak açılır (`<Nullable>enable</Nullable>`).
|
||||||
|
Şu an açık olanlar: `Sozsoft.Platform.Application`, `Sozsoft.Notifications.*`,
|
||||||
|
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
|
||||||
|
`.IsRequired(false)` ile açıkça sabitlenir.
|
||||||
|
- Kolon zorunluluğu tercihen NRT'ye değil, `PlatformDbContext` yapılandırmasına
|
||||||
|
yazılır; böylece derleyici anahtarından bağımsız olur.
|
||||||
|
|
||||||
|
## 3. Ölü kod politikası
|
||||||
|
|
||||||
|
Aşağıdaki kurallar ihlal edilmiş kod merge edilmez:
|
||||||
|
|
||||||
|
| Kural | Anlamı |
|
||||||
|
| --- | --- |
|
||||||
|
| IDE0005 | Kullanılmayan `using` |
|
||||||
|
| IDE0051 | Çağrılmayan private üye |
|
||||||
|
| IDE0052 | Yazılıp hiç okunmayan private alan (kullanılmayan enjekte bağımlılık) |
|
||||||
|
| IDE0059 | Gereksiz değer ataması |
|
||||||
|
| IDE0161 | `namespace X;` (file-scoped) |
|
||||||
|
|
||||||
|
Tarama ve otomatik düzeltme (kalıcı yapılandırma gerektirmez):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Rapor
|
||||||
|
dotnet build api/Sozsoft.Platform.sln --no-incremental \
|
||||||
|
-p:EnforceCodeStyleInBuild=true -p:GenerateDocumentationFile=true
|
||||||
|
|
||||||
|
# Düzeltme
|
||||||
|
dotnet format api/Sozsoft.Platform.sln --diagnostics IDE0005,IDE0161 --severity warn
|
||||||
|
```
|
||||||
|
|
||||||
|
> Kurallar `.editorconfig`'te `warning` seviyesindedir ve `EnforceCodeStyleInBuild`
|
||||||
|
> açık olduğu için normal `dotnet build` de bunları raporlar.
|
||||||
|
|
||||||
|
IDE0052 düzeltilirken alan ile birlikte **constructor parametresi de** silinir; yoksa
|
||||||
|
DI hâlâ gereksiz servisi çözer.
|
||||||
|
|
||||||
|
`CS0162` (erişilemez kod) `Directory.Build.props` içinde gerekçesiyle susturulmuştur:
|
||||||
|
veritabanı sağlayıcısı derleme zamanı sabiti olduğu için diğer sağlayıcının dalları
|
||||||
|
erişilemez görünür. Bu durum dışında erişilemez kod bırakılmaz.
|
||||||
|
|
||||||
|
Ek kurallar:
|
||||||
|
|
||||||
|
- Bir tip/dosya silinmeden önce **repo genelinde** (api + ui + configs) referans
|
||||||
|
taraması yapılır. DI ile çözülen tipler (AppService, Manager, Provider, Repository,
|
||||||
|
Seeder, Localizer, Notification sağlayıcıları) düz metin aramasında "referanssız"
|
||||||
|
görünür — bunlar yalnızca modül/DI kaydı incelendikten sonra silinir.
|
||||||
|
- Yorum satırına alınmış kod bloğu bırakılmaz; ihtiyaç varsa git geçmişinden alınır.
|
||||||
|
- "İleride lazım olur" gerekçesiyle kullanılmayan DTO/Input/Consts tutulmaz.
|
||||||
|
- `Migrations/` ve `*.g.cs` üretilmiş koddur, bu kuralların dışındadır.
|
||||||
|
|
||||||
|
## 4. Modern C# kullanımı (yeni kod)
|
||||||
|
|
||||||
|
- File-scoped namespace: `namespace Sozsoft.Platform.ListForms;`
|
||||||
|
- Primary constructor: `public class XAppService(IRepository<X, Guid> repo) : PlatformAppService`
|
||||||
|
— parametre doğrudan kullanılır, `private readonly ... = param;` şeklinde
|
||||||
|
tekrar tanımlanmaz.
|
||||||
|
- Koleksiyon ifadeleri (`[]`, `[.. items]`), `is not null`, `switch` ifadesi,
|
||||||
|
desen eşleme, `Index/Range`, hedefi belli `new()`.
|
||||||
|
- `out var`, tuple deconstruction; kullanılmayan çıktı için `_`.
|
||||||
|
- `async` metotlar `Async` ile biter ve `Task`/`Task<T>` döner; `async void` yasak.
|
||||||
|
ABP'de `ConfigureAwait` kullanılmaz.
|
||||||
|
- Biçim: `api/.editorconfig` geçerlidir (4 boşluk girinti, `using` blokları namespace
|
||||||
|
dışında ve System önce, dosya sonunda satır sonu, UTF-8 BOM).
|
||||||
|
|
||||||
|
## 5. ABP kuralları
|
||||||
|
|
||||||
|
- **Katman sınırı**: Domain.Shared → Domain → Application.Contracts → Application →
|
||||||
|
HttpApi → HttpApi.Host. Ters yönde referans verilmez; `EntityFrameworkCore` yalnızca
|
||||||
|
Domain'e bakar. `DbMigrator` yalnızca `Application.Contracts` + `EntityFrameworkCore`
|
||||||
|
referansı taşır; **Application'a referans verilmez** — seeder'ın uygulama servislerine
|
||||||
|
ihtiyaç duyması, kodun yanlış katmanda olduğunun işaretidir.
|
||||||
|
- **Hem seeder'ın hem AppService'in ihtiyaç duyduğu dönüşüm**: ortak kod
|
||||||
|
`Application.Contracts`'a konur (DTO'nun kendi metodu olarak; yeni yardımcı sınıf
|
||||||
|
üretilmez). Dönüşümün entity'ye yazması gerekiyorsa entity'nin **yazılabilir kolon
|
||||||
|
sözleşmesi** Domain.Shared'de bir arayüz olarak tanımlanır (`IListFormArtifact`,
|
||||||
|
`IListFormFieldArtifact`), entity bu arayüzü uygular ve dönüşüm arayüz üzerinden yazar.
|
||||||
|
Böylece Contracts, Domain'i tanımadan entity doldurabilir ve aynı dönüşüm iki yerde
|
||||||
|
kopyalanmaz.
|
||||||
|
- **AppService**: `PlatformAppService`'ten türer, arayüzü Application.Contracts'ta
|
||||||
|
tanımlanır. Controller yazılmaz — ABP otomatik API üretir. Controller sadece ABP
|
||||||
|
konvansiyonunun karşılamadığı durumda (dosya indirme, harici webhook) eklenir.
|
||||||
|
- **Yetki**: her AppService/metot `[Authorize(PlatformConsts.AppCodes....)]` ile
|
||||||
|
korunur; izin adları `PermissionDefinitionProvider` içinde tanımlanır. Yetki
|
||||||
|
kontrolü koda gömülmez, izin adı sabitten okunur.
|
||||||
|
- **Tenant izolasyonu**: sorgular `ICurrentTenant` bağlamında çalışır; `IMultiTenant`
|
||||||
|
entity'lerde manuel `TenantId` filtresi yazılmaz, veri filtreleri devre dışı
|
||||||
|
bırakılacaksa (`ICurrentTenant.Change`, `IDataFilter`) gerekçe yorumla yazılır.
|
||||||
|
- **Repository**: özel sorgu gerekiyorsa Domain'de arayüz + EntityFrameworkCore'da
|
||||||
|
implementasyon. AppService içinde `DbContext` doğrudan kullanılmaz.
|
||||||
|
- **Dinamik SQL**: parametreli çalıştırılır; string birleştirme ile sorgu kurulmaz.
|
||||||
|
- **Mapping**: Mapperly (`[Mapper]` partial class) kullanılır; AutoMapper eklenmez.
|
||||||
|
RMG012 uyarısı "hedefte karşılığı olmayan alan" demektir; yeni mapper eklerken bu
|
||||||
|
uyarı bırakılmaz.
|
||||||
|
- **Localization**: kullanıcıya görünen metin koda gömülmez, `PlatformResource`
|
||||||
|
anahtarı üzerinden verilir.
|
||||||
|
- **Çok değerli alan taşıyan girdi DTO'su**: ekranda `dxTagBox` (ya da çoklu seçimli
|
||||||
|
`dxGridBox`) ile doldurulan bir alan tipli bir DTO'ya bağlanıyorsa property `string`
|
||||||
|
kalır ve `[JsonConverter(typeof(MultiValueStringJsonConverter))]` ile işaretlenir —
|
||||||
|
UI diziyi olduğu gibi yollar, converter `PlatformConsts.MultiValueDelimiter` ile tek
|
||||||
|
string üretir. Varsayılan `list-form-data/*` ucundan geçen ekranlarda bu iş
|
||||||
|
`QueryHelper` tarafından yapılır, DTO yoktur; okuma DTO'larına öznitelik eklenmez.
|
||||||
|
- **Sabitler**: bağlantı dizesi, tenant id, anahtar/secret koda yazılmaz; konfigürasyon
|
||||||
|
veya `SettingDefinition` üzerinden okunur.
|
||||||
|
|
||||||
|
## 6. Değişiklik öncesi kontrol listesi
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet build api/Sozsoft.Platform.sln --no-incremental \
|
||||||
|
-p:EnforceCodeStyleInBuild=true -p:GenerateDocumentationFile=true # 0 hata, yeni IDE00xx yok
|
||||||
|
dotnet format api/Sozsoft.Platform.sln --verify-no-changes --diagnostics IDE0005,IDE0161 --severity warn
|
||||||
|
dotnet test api/test/Sozsoft.Platform.EntityFrameworkCore.Tests
|
||||||
|
```
|
||||||
|
|
||||||
|
> Not: EntityFrameworkCore testleri şu an SQLite şema oluşturmada
|
||||||
|
> (`nvarchar(max)` → `SQLite Error 1`) kırık; bu kırıklık bu standarttan önce de vardı
|
||||||
|
> ve ayrı bir iş olarak ele alınmalıdır.
|
||||||
8
.github/instructions/list.instructions.md
vendored
8
.github/instructions/list.instructions.md
vendored
|
|
@ -7,6 +7,10 @@ This document summarizes the rules, standards, and step-by-step instructions for
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
|
> **Bu doküman depo içi seeder'larla (DbMigrator/Migrations) modül eklemeyi anlatır.**
|
||||||
|
> Çalışma zamanında Wizard / Custom Components / CRUD Endpoint Manager ile üretim yapacaksan
|
||||||
|
> `lowcode.instructions.md` esastır; oradaki seed dosyaları `configs/seeds` altına düşer.
|
||||||
|
|
||||||
## General Principles
|
## General Principles
|
||||||
- **Configuration First:** Always prefer platform configuration (menus, permissions, forms, localization) over custom code.
|
- **Configuration First:** Always prefer platform configuration (menus, permissions, forms, localization) over custom code.
|
||||||
- **Modularization:** Each module (e.g., {Modul}) must have its own seeder, permission group, and localization entries.
|
- **Modularization:** Each module (e.g., {Modul}) must have its own seeder, permission group, and localization entries.
|
||||||
|
|
@ -24,7 +28,7 @@ This document summarizes the rules, standards, and step-by-step instructions for
|
||||||
## Special File Handling Rules
|
## Special File Handling Rules
|
||||||
|
|
||||||
- **MenusData.json:**
|
- **MenusData.json:**
|
||||||
- Never modify the `Routes` section directly. Only `MenuGroups` and `Menus` sections can be changed for menu additions or updates. All menu additions must use the platform's configuration mechanisms or be added to `MenuGroups` and `Menus` as required by platform design.
|
- Never modify the `Routes` section directly. Only `Modules` and `Menus` sections can be changed for menu additions or updates. All menu additions must use the platform's configuration mechanisms or be added to `Modules` and `Menus` as required by platform design.
|
||||||
|
|
||||||
- **LanguagesData.json:**
|
- **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.
|
- When adding a new key, always check if the key already exists. Only add the key if it does not exist to prevent duplicates.
|
||||||
|
|
@ -35,7 +39,7 @@ This document summarizes the rules, standards, and step-by-step instructions for
|
||||||
|
|
||||||
|
|
||||||
### 1. MenusData.json
|
### 1. MenusData.json
|
||||||
- **{Modul} menu** added as a top-level menu with `ShortName: {Modul}`.
|
- **{Modul} menu** added as a top-level menu with `ModuleId: {Modul}`.
|
||||||
- **{Liste}** added as a child menu under {Modul}.
|
- **{Liste}** added as a child menu under {Modul}.
|
||||||
|
|
||||||
### 2. PermissionsData.json
|
### 2. PermissionsData.json
|
||||||
|
|
|
||||||
1393
.github/instructions/lowcode-reference.instructions.md
vendored
Normal file
1393
.github/instructions/lowcode-reference.instructions.md
vendored
Normal file
File diff suppressed because it is too large
Load diff
3746
.github/instructions/lowcode.instructions.md
vendored
Normal file
3746
.github/instructions/lowcode.instructions.md
vendored
Normal file
File diff suppressed because it is too large
Load diff
11
.gitignore
vendored
11
.gitignore
vendored
|
|
@ -1,5 +1,14 @@
|
||||||
|
# Konteyner veri klasorleri (mssql/postgres/redis vb.); seed veri dosyalari haric tutulur.
|
||||||
configs/**/data/**
|
configs/**/data/**
|
||||||
|
!configs/seeds/**/data/**
|
||||||
*.bak
|
*.bak
|
||||||
**/node_modules
|
**/node_modules
|
||||||
**/.DS_Store
|
**/.DS_Store
|
||||||
logs/
|
logs/
|
||||||
|
|
||||||
|
# Deploy sırasında git tag'lerinden üretilir (configs/change-logs/generate-change-logs.sh)
|
||||||
|
# ve API konteynerine mount edilir. Takip edilirse deploy'daki `git pull` yerel
|
||||||
|
# değişiklikle çakışır ve dosya eskir.
|
||||||
|
configs/change-logs/change-logs.json
|
||||||
|
# Wizard import staging klasoru (gecici; oturum bitince silinir)
|
||||||
|
configs/seeds/.imports/
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<NoWarn>$(NoWarn);CS0162;CS8321;CS8618;CS8632</NoWarn>
|
<NoWarn>$(NoWarn);CS0162;CS8321;CS8618;CS8632;CS1591;CS1573;CS1572;CS1570;CS1574</NoWarn>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<AnalysisLevel>latest</AnalysisLevel>
|
||||||
|
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
|
||||||
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
using Volo.Abp.Application;
|
using Volo.Abp.Application;
|
||||||
using Volo.Abp.Modularity;
|
|
||||||
using Volo.Abp.Authorization;
|
using Volo.Abp.Authorization;
|
||||||
|
using Volo.Abp.Modularity;
|
||||||
|
|
||||||
namespace Sozsoft.Languages;
|
namespace Sozsoft.Languages;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
namespace Sozsoft.Languages;
|
|
||||||
|
|
||||||
public class LanguagesRemoteServiceConsts
|
|
||||||
{
|
|
||||||
public const string RemoteServiceName = "Languages";
|
|
||||||
|
|
||||||
public const string ModuleName = "languages";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Volo.Abp.Authorization.Permissions;
|
using Volo.Abp.Authorization.Permissions;
|
||||||
|
|
||||||
namespace Sozsoft.Languages.Permissions;
|
namespace Sozsoft.Languages.Permissions;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
using Sozsoft.Languages.Entities;
|
using System;
|
||||||
using Sozsoft.Languages.Localization;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Sozsoft.Languages.Entities;
|
||||||
|
using Sozsoft.Languages.Localization;
|
||||||
using Volo.Abp.Application.Dtos;
|
using Volo.Abp.Application.Dtos;
|
||||||
using Volo.Abp.Application.Services;
|
using Volo.Abp.Application.Services;
|
||||||
using Volo.Abp.Domain.Entities;
|
using Volo.Abp.Domain.Entities;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
using Sozsoft.Languages.Entities;
|
using System;
|
||||||
using Sozsoft.Languages.Localization;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Sozsoft.Languages.Entities;
|
||||||
|
using Sozsoft.Languages.Localization;
|
||||||
using Volo.Abp.Application.Dtos;
|
using Volo.Abp.Application.Dtos;
|
||||||
using Volo.Abp.Application.Services;
|
using Volo.Abp.Application.Services;
|
||||||
using Volo.Abp.Domain.Repositories;
|
using Volo.Abp.Domain.Repositories;
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
using Sozsoft.Languages.Entities;
|
using System;
|
||||||
using Sozsoft.Languages.Localization;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using StackExchange.Redis;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Dynamic.Core;
|
using System.Linq.Dynamic.Core;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Sozsoft.Languages.Entities;
|
||||||
|
using Sozsoft.Languages.Localization;
|
||||||
|
using StackExchange.Redis;
|
||||||
using Volo.Abp;
|
using Volo.Abp;
|
||||||
using Volo.Abp.Application.Dtos;
|
using Volo.Abp.Application.Dtos;
|
||||||
using Volo.Abp.Application.Services;
|
using Volo.Abp.Application.Services;
|
||||||
|
|
@ -136,7 +136,7 @@ public class LanguageTextAppService : CrudAppService<
|
||||||
var db = redis.GetDatabase();
|
var db = redis.GetDatabase();
|
||||||
await db.ExecuteAsync("FLUSHALL");
|
await db.ExecuteAsync("FLUSHALL");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual IQueryable<LanguageKey> ApplySorting(IQueryable<LanguageKey> query, LanguageTextTranslatedRequestDto input)
|
protected virtual IQueryable<LanguageKey> ApplySorting(IQueryable<LanguageKey> query, LanguageTextTranslatedRequestDto input)
|
||||||
{
|
{
|
||||||
//Try to sort query if available
|
//Try to sort query if available
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
|
using Riok.Mapperly.Abstractions;
|
||||||
using Sozsoft.Languages.Entities;
|
using Sozsoft.Languages.Entities;
|
||||||
using Volo.Abp.Localization;
|
using Volo.Abp.Localization;
|
||||||
using Riok.Mapperly.Abstractions;
|
|
||||||
using Volo.Abp.Mapperly;
|
using Volo.Abp.Mapperly;
|
||||||
|
|
||||||
namespace Sozsoft.Languages;
|
namespace Sozsoft.Languages;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
namespace Sozsoft.Languages;
|
|
||||||
|
|
||||||
public static class LanguagesErrorCodes
|
|
||||||
{
|
|
||||||
//Add your business exception error codes here...
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,45 +1,23 @@
|
||||||
namespace Sozsoft.Languages.Languages
|
namespace Sozsoft.Languages.Languages;
|
||||||
{
|
|
||||||
public static class LanguageCodes
|
|
||||||
{
|
|
||||||
public const string Ar = "ar";
|
|
||||||
public const string Cs = "cs";
|
|
||||||
public const string De = "de-DE";
|
|
||||||
public const string En = "en";
|
|
||||||
public const string Es = "es";
|
|
||||||
public const string Fi = "fi";
|
|
||||||
public const string Fr = "fr";
|
|
||||||
public const string Hi = "hi";
|
|
||||||
public const string Hr = "hr";
|
|
||||||
public const string Hu = "hu";
|
|
||||||
public const string It = "it";
|
|
||||||
public const string Pt = "pt-BR";
|
|
||||||
public const string Ru = "ru";
|
|
||||||
public const string Sk = "sk";
|
|
||||||
public const string Sl = "sl";
|
|
||||||
public const string Tr = "tr";
|
|
||||||
public const string Zh = "zh-Hans";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class LanguageNames
|
public static class LanguageCodes
|
||||||
{
|
{
|
||||||
public const string Ar = "العربية";
|
public const string Ar = "ar";
|
||||||
public const string Cs = "Čeština";
|
public const string Cs = "cs";
|
||||||
public const string De = "Deutsch";
|
public const string De = "de-DE";
|
||||||
public const string En = "English";
|
public const string En = "en";
|
||||||
public const string Es = "Español";
|
public const string Es = "es";
|
||||||
public const string Fi = "Finnish";
|
public const string Fi = "fi";
|
||||||
public const string Fr = "Français";
|
public const string Fr = "fr";
|
||||||
public const string Hi = "Hindi";
|
public const string Hi = "hi";
|
||||||
public const string Hr = "Croatian";
|
public const string Hr = "hr";
|
||||||
public const string Hu = "Magyar";
|
public const string Hu = "hu";
|
||||||
public const string It = "Italiano";
|
public const string It = "it";
|
||||||
public const string Pt = "Português";
|
public const string Pt = "pt-BR";
|
||||||
public const string Ru = "Русский";
|
public const string Ru = "ru";
|
||||||
public const string Sk = "Slovak";
|
public const string Sk = "sk";
|
||||||
public const string Sl = "Slovenščina";
|
public const string Sl = "sl";
|
||||||
public const string Tr = "Türkçe";
|
public const string Tr = "tr";
|
||||||
public const string Zh = "繁體中文";
|
public const string Zh = "zh-Hans";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,7 @@
|
||||||
using Sozsoft.Languages.Entities;
|
using System.Threading.Tasks;
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Volo.Abp.Caching;
|
using Volo.Abp.Caching;
|
||||||
using Volo.Abp.DependencyInjection;
|
using Volo.Abp.DependencyInjection;
|
||||||
using Volo.Abp.Domain.Entities.Events.Distributed;
|
using Volo.Abp.Domain.Entities.Events.Distributed;
|
||||||
using Volo.Abp.Domain.Repositories;
|
|
||||||
using Volo.Abp.EventBus.Distributed;
|
using Volo.Abp.EventBus.Distributed;
|
||||||
|
|
||||||
namespace Sozsoft.Languages;
|
namespace Sozsoft.Languages;
|
||||||
|
|
@ -15,14 +12,11 @@ public class LanguageTextCacheItemInvalidatorDistributed :
|
||||||
IDistributedEventHandler<EntityDeletedEto<LanguageTextEto>>,
|
IDistributedEventHandler<EntityDeletedEto<LanguageTextEto>>,
|
||||||
ITransientDependency
|
ITransientDependency
|
||||||
{
|
{
|
||||||
private readonly IRepository<LanguageText, Guid> languageTextRepository;
|
|
||||||
private readonly IDistributedCache<LanguageTextCacheItem> cache;
|
private readonly IDistributedCache<LanguageTextCacheItem> cache;
|
||||||
|
|
||||||
public LanguageTextCacheItemInvalidatorDistributed(
|
public LanguageTextCacheItemInvalidatorDistributed(
|
||||||
IRepository<LanguageText, Guid> languageTextRepository,
|
|
||||||
IDistributedCache<LanguageTextCacheItem> cache)
|
IDistributedCache<LanguageTextCacheItem> cache)
|
||||||
{
|
{
|
||||||
this.languageTextRepository = languageTextRepository;
|
|
||||||
this.cache = cache;
|
this.cache = cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
using Sozsoft.Languages.Entities;
|
using System;
|
||||||
using Microsoft.Extensions.Localization;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.Localization;
|
||||||
|
using Sozsoft.Languages.Entities;
|
||||||
using Volo.Abp.Caching;
|
using Volo.Abp.Caching;
|
||||||
using Volo.Abp.DependencyInjection;
|
using Volo.Abp.DependencyInjection;
|
||||||
using Volo.Abp.Domain.Repositories;
|
using Volo.Abp.Domain.Repositories;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
using Microsoft.Extensions.Localization;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.Localization;
|
||||||
using Volo.Abp.Localization;
|
using Volo.Abp.Localization;
|
||||||
|
|
||||||
namespace Sozsoft.Languages.Localization;
|
namespace Sozsoft.Languages.Localization;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
using Sozsoft.Languages.Entities;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Sozsoft.Languages.Entities;
|
||||||
using Volo.Abp.Data;
|
using Volo.Abp.Data;
|
||||||
using Volo.Abp.EntityFrameworkCore;
|
using Volo.Abp.EntityFrameworkCore;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
using Sozsoft.Languages.Entities;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Sozsoft.Languages.Entities;
|
||||||
using Volo.Abp;
|
using Volo.Abp;
|
||||||
using Volo.Abp.EntityFrameworkCore.Modeling;
|
using Volo.Abp.EntityFrameworkCore.Modeling;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
namespace Sozsoft.MailQueue.Application;
|
namespace Sozsoft.MailQueue.Application;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
using Sozsoft.MailQueue.FileGeneration;
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Sozsoft.MailQueue.FileGeneration;
|
||||||
using Sozsoft.MailQueue.MailGeneration.Models;
|
using Sozsoft.MailQueue.MailGeneration.Models;
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Volo.Abp.DependencyInjection;
|
using Volo.Abp.DependencyInjection;
|
||||||
using Volo.Abp.Guids;
|
using Volo.Abp.Guids;
|
||||||
using Volo.Abp.TextTemplating;
|
using Volo.Abp.TextTemplating;
|
||||||
|
|
@ -141,53 +141,53 @@ public class AttachmentGenerator : IAttachmentGenerator, ITransientDependency
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
// if (model.Filtreler.ContainsKey("QUERY") && model.Filtreler.ContainsKey("QUERYSQL"))
|
// if (model.Filtreler.ContainsKey("QUERY") && model.Filtreler.ContainsKey("QUERYSQL"))
|
||||||
// {
|
// {
|
||||||
// /*
|
// /*
|
||||||
//Parametrelerden asagidaki sekilde gelen queryleri en alttaki formata donusturuyoruz
|
//Parametrelerden asagidaki sekilde gelen queryleri en alttaki formata donusturuyoruz
|
||||||
//params
|
//params
|
||||||
//0 =>
|
//0 =>
|
||||||
// QUERY -> qryDepo_Cikis_CekiListesi_Detayli
|
// QUERY -> qryDepo_Cikis_CekiListesi_Detayli
|
||||||
// QUERYSQL -> Ksp_Depo_Cikis_CekiListesi_Detayli 252993
|
// QUERYSQL -> Ksp_Depo_Cikis_CekiListesi_Detayli 252993
|
||||||
// QUERY2 -> qryDepo_Cikis_CekiListesi_Sub
|
// QUERY2 -> qryDepo_Cikis_CekiListesi_Sub
|
||||||
// QUERY2SQL -> KSp_Depo_Cikis_CekiListesi_Sub 252993
|
// QUERY2SQL -> KSp_Depo_Cikis_CekiListesi_Sub 252993
|
||||||
|
|
||||||
//SQL olan keyvalue'lari cikar ve sadece querylerde foreach yap
|
|
||||||
// QUERY -> qryDepo_Cikis_CekiListesi_Detayli
|
|
||||||
// dict.add(item.Value, params[item.Key + "SQL"].Value)
|
|
||||||
// QUERY2 -> qryDepo_Cikis_CekiListesi_Sub
|
|
||||||
// dict.add(item.Value, params[item.Key + "SQL"].Value)
|
|
||||||
|
|
||||||
//Bunlari yeni dict'e ekle
|
//SQL olan keyvalue'lari cikar ve sadece querylerde foreach yap
|
||||||
//dict
|
// QUERY -> qryDepo_Cikis_CekiListesi_Detayli
|
||||||
//qryDepo_Cikis_CekiListesi_Detayli -> Ksp_Depo_Cikis_CekiListesi_Detayli 252993
|
// dict.add(item.Value, params[item.Key + "SQL"].Value)
|
||||||
//qryDepo_Cikis_CekiListesi_Sub -> KSp_Depo_Cikis_CekiListesi_Sub 252993
|
// QUERY2 -> qryDepo_Cikis_CekiListesi_Sub
|
||||||
// */
|
// dict.add(item.Value, params[item.Key + "SQL"].Value)
|
||||||
// //dict -> key (name), value (sql)
|
|
||||||
// var queries = model.Filtreler
|
|
||||||
// .Where(a => a.Key.StartsWith("QUERY"))
|
|
||||||
// .Where(a => !a.Key.EndsWith("SQL"))
|
|
||||||
// .Where(a => !a.Value.IsNullOrWhiteSpace())
|
|
||||||
// .ToDictionary(x => x.Value, x => model.Filtreler.GetValueOrDefault(x.Key + "SQL"));
|
|
||||||
|
|
||||||
// return MsAccessReportManager.GenerateAccessReport(
|
//Bunlari yeni dict'e ekle
|
||||||
// model.DbAdi,
|
//dict
|
||||||
// model.Index,
|
//qryDepo_Cikis_CekiListesi_Detayli -> Ksp_Depo_Cikis_CekiListesi_Detayli 252993
|
||||||
// model.RaporAdi,
|
//qryDepo_Cikis_CekiListesi_Sub -> KSp_Depo_Cikis_CekiListesi_Sub 252993
|
||||||
// model.ExportDosyaTipi,
|
// */
|
||||||
// queries,
|
// //dict -> key (name), value (sql)
|
||||||
// attachmentFolder);
|
// var queries = model.Filtreler
|
||||||
// }
|
// .Where(a => a.Key.StartsWith("QUERY"))
|
||||||
// else
|
// .Where(a => !a.Key.EndsWith("SQL"))
|
||||||
// {
|
// .Where(a => !a.Value.IsNullOrWhiteSpace())
|
||||||
// return MsAccessReportManager.GenerateAccessReport(
|
// .ToDictionary(x => x.Value, x => model.Filtreler.GetValueOrDefault(x.Key + "SQL"));
|
||||||
// model.DbAdi,
|
|
||||||
// model.Index,
|
// return MsAccessReportManager.GenerateAccessReport(
|
||||||
// model.RaporAdi,
|
// model.DbAdi,
|
||||||
// string.Join(" AND ", model.Filtreler.Select(a => $"{a.Key}={a.Value}")),
|
// model.Index,
|
||||||
// model.ExportDosyaTipi,
|
// model.RaporAdi,
|
||||||
// attachmentFolder);
|
// model.ExportDosyaTipi,
|
||||||
// }
|
// queries,
|
||||||
|
// attachmentFolder);
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// return MsAccessReportManager.GenerateAccessReport(
|
||||||
|
// model.DbAdi,
|
||||||
|
// model.Index,
|
||||||
|
// model.RaporAdi,
|
||||||
|
// string.Join(" AND ", model.Filtreler.Select(a => $"{a.Key}={a.Value}")),
|
||||||
|
// model.ExportDosyaTipi,
|
||||||
|
// attachmentFolder);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<string> ConvertFileToBase64Async(string filePath)
|
private async Task<string> ConvertFileToBase64Async(string filePath)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
using Sozsoft.MailQueue.MailGeneration.Models;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Sozsoft.MailQueue.MailGeneration.Models;
|
||||||
using Volo.Abp.DependencyInjection;
|
using Volo.Abp.DependencyInjection;
|
||||||
using Volo.Abp.TextTemplating;
|
using Volo.Abp.TextTemplating;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
using System.Data;
|
using System.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Sozsoft.MailQueue.Domain.Entities;
|
using Sozsoft.MailQueue.Domain.Entities;
|
||||||
using Sozsoft.MailQueue.EntityFrameworkCore;
|
using Sozsoft.MailQueue.EntityFrameworkCore;
|
||||||
using Sozsoft.MailQueue.MailGeneration.Models;
|
using Sozsoft.MailQueue.MailGeneration.Models;
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Volo.Abp.DependencyInjection;
|
using Volo.Abp.DependencyInjection;
|
||||||
using Volo.Abp.Domain.Repositories;
|
using Volo.Abp.Domain.Repositories;
|
||||||
using Volo.Abp.TextTemplating;
|
using Volo.Abp.TextTemplating;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
using Sozsoft.Sender.Mail;
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Sozsoft.MailQueue.Domain.MailGeneration;
|
using Sozsoft.MailQueue.Domain.MailGeneration;
|
||||||
using Sozsoft.MailQueue.Domain.Shared;
|
using Sozsoft.MailQueue.Domain.Shared;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Sozsoft.Sender.Mail;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Volo.Abp.BackgroundWorkers;
|
using Volo.Abp.BackgroundWorkers;
|
||||||
using Volo.Abp.Domain.Repositories;
|
using Volo.Abp.Domain.Repositories;
|
||||||
using Volo.Abp.Guids;
|
using Volo.Abp.Guids;
|
||||||
|
|
@ -182,7 +182,7 @@ public class MailQueueWorker : BackgroundWorkerBase
|
||||||
}
|
}
|
||||||
queue.SendStatus = result.Success;
|
queue.SendStatus = result.Success;
|
||||||
queue.SendTime = Clock.Now;
|
queue.SendTime = Clock.Now;
|
||||||
await Repository.UpdateAsync(queue);
|
await Repository.UpdateAsync(queue, cancellationToken: cancellationToken);
|
||||||
|
|
||||||
// Attachmentlari silebiliriz
|
// Attachmentlari silebiliriz
|
||||||
try
|
try
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
using Amazon;
|
using Amazon;
|
||||||
using Amazon.SQS;
|
using Amazon.SQS;
|
||||||
using Amazon.SQS.Model;
|
using Amazon.SQS.Model;
|
||||||
using Sozsoft.Sender.Mail.AmazonSes;
|
|
||||||
using Sozsoft.MailQueue.Domain.Entities;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Sozsoft.MailQueue.Domain.Entities;
|
||||||
|
using Sozsoft.Sender.Mail.AmazonSes;
|
||||||
using Volo.Abp.Domain.Repositories;
|
using Volo.Abp.Domain.Repositories;
|
||||||
using Volo.Abp.Domain.Services;
|
using Volo.Abp.Domain.Services;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
using Sozsoft.MailQueue.Domain;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Sozsoft.MailQueue.Domain;
|
||||||
using Volo.Abp.Data;
|
using Volo.Abp.Data;
|
||||||
using Volo.Abp.EntityFrameworkCore;
|
using Volo.Abp.EntityFrameworkCore;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
using Sozsoft.MailQueue.Domain;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Sozsoft.MailQueue.Domain;
|
||||||
using Sozsoft.MailQueue.Domain.Entities;
|
using Sozsoft.MailQueue.Domain.Entities;
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Volo.Abp;
|
using Volo.Abp;
|
||||||
using Volo.Abp.EntityFrameworkCore.Modeling;
|
using Volo.Abp.EntityFrameworkCore.Modeling;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using Sozsoft.MailQueue.Domain.Entities;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Sozsoft.MailQueue.Domain.Entities;
|
||||||
using Volo.Abp.DependencyInjection;
|
using Volo.Abp.DependencyInjection;
|
||||||
using Volo.Abp.Domain.Repositories;
|
using Volo.Abp.Domain.Repositories;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
using ClosedXML.Excel;
|
using ClosedXML.Excel;
|
||||||
using DocumentFormat.OpenXml.Spreadsheet;
|
|
||||||
using Sozsoft.MailQueue.MailGeneration.Models;
|
using Sozsoft.MailQueue.MailGeneration.Models;
|
||||||
using Volo.Abp.DependencyInjection;
|
using Volo.Abp.DependencyInjection;
|
||||||
|
|
||||||
|
|
@ -26,7 +25,7 @@ public class XLSFileGenerator : IFileGenerator, ITransientDependency
|
||||||
using var stream = new MemoryStream();
|
using var stream = new MemoryStream();
|
||||||
workbook.SaveAs(stream);
|
workbook.SaveAs(stream);
|
||||||
stream.Close();
|
stream.Close();
|
||||||
|
|
||||||
return await Task.FromResult(stream.ToArray());
|
return await Task.FromResult(stream.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -80,8 +79,11 @@ public class XLSFileGenerator : IFileGenerator, ITransientDependency
|
||||||
var col = table.TabloSutunlari[j];
|
var col = table.TabloSutunlari[j];
|
||||||
var cell = sheet.Cell(i + 2, j + 1);
|
var cell = sheet.Cell(i + 2, j + 1);
|
||||||
var value = row.GetValueOrDefault(col.SutunAdi);
|
var value = row.GetValueOrDefault(col.SutunAdi);
|
||||||
var dataType = XLDataType.Text;
|
if (!Enum.TryParse(col.VeriTipi, out XLDataType dataType))
|
||||||
Enum.TryParse(col.VeriTipi, out dataType);
|
{
|
||||||
|
dataType = XLDataType.Text;
|
||||||
|
}
|
||||||
|
|
||||||
switch (dataType)
|
switch (dataType)
|
||||||
{
|
{
|
||||||
case XLDataType.Number:
|
case XLDataType.Number:
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,16 @@
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Sozsoft.Sender;
|
|
||||||
using Sozsoft.MailQueue.Domain.Shared;
|
|
||||||
using Sozsoft.MailQueue.EntityFrameworkCore;
|
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Sozsoft.MailQueue.Domain.Shared;
|
||||||
|
using Sozsoft.MailQueue.EntityFrameworkCore;
|
||||||
|
using Sozsoft.Sender;
|
||||||
using Volo.Abp.Application;
|
using Volo.Abp.Application;
|
||||||
using Volo.Abp.Authorization;
|
using Volo.Abp.Authorization;
|
||||||
using Volo.Abp.Mapperly;
|
|
||||||
using Volo.Abp.Domain;
|
using Volo.Abp.Domain;
|
||||||
using Volo.Abp.EntityFrameworkCore;
|
using Volo.Abp.EntityFrameworkCore;
|
||||||
using Volo.Abp.Localization;
|
using Volo.Abp.Localization;
|
||||||
using Volo.Abp.Localization.ExceptionHandling;
|
using Volo.Abp.Localization.ExceptionHandling;
|
||||||
|
using Volo.Abp.Mapperly;
|
||||||
using Volo.Abp.Modularity;
|
using Volo.Abp.Modularity;
|
||||||
using Volo.Abp.TextTemplating.Razor;
|
using Volo.Abp.TextTemplating.Razor;
|
||||||
using Volo.Abp.VirtualFileSystem;
|
using Volo.Abp.VirtualFileSystem;
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,6 @@ public class NotificationFilterRequestDto : PagedAndSortedResultRequestDto
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// İstek; aktivite sayfasındaki liste ekranından mı geliyor yoksa bildirimleri kontrol etmek için mi
|
/// İstek; aktivite sayfasındaki liste ekranından mı geliyor yoksa bildirimleri kontrol etmek için mi
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsListRequest { get; set; } = false;
|
public bool IsListRequest { get; set; } = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
namespace Sozsoft.Notifications.Application;
|
|
||||||
|
|
||||||
public class NotificationRemoteServiceConsts
|
|
||||||
{
|
|
||||||
public const string RemoteServiceName = "Notifications";
|
|
||||||
|
|
||||||
public const string ModuleName = "notifications";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Volo.Abp.Authorization.Permissions;
|
using Volo.Abp.Authorization.Permissions;
|
||||||
|
|
||||||
namespace Sozsoft.Notifications.Permissions;
|
namespace Sozsoft.Notifications.Permissions;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,11 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Sozsoft.Notifications.Domain;
|
using Sozsoft.Notifications.Domain;
|
||||||
using Sozsoft.Notifications.Entities;
|
using Sozsoft.Notifications.Entities;
|
||||||
using Sozsoft.Notifications.Enums;
|
using Sozsoft.Notifications.Enums;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Volo.Abp;
|
using Volo.Abp;
|
||||||
using Volo.Abp.Application.Services;
|
using Volo.Abp.Application.Services;
|
||||||
using Volo.Abp.Domain.Entities;
|
using Volo.Abp.Domain.Entities;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
using Sozsoft.Notifications.Entities;
|
using Riok.Mapperly.Abstractions;
|
||||||
using Sozsoft.Notifications.Domain;
|
using Sozsoft.Notifications.Domain;
|
||||||
|
using Sozsoft.Notifications.Entities;
|
||||||
using Sozsoft.Notifications.NotificationRules;
|
using Sozsoft.Notifications.NotificationRules;
|
||||||
using Riok.Mapperly.Abstractions;
|
|
||||||
using Volo.Abp.Mapperly;
|
using Volo.Abp.Mapperly;
|
||||||
|
|
||||||
namespace Sozsoft.Notifications.Application;
|
namespace Sozsoft.Notifications.Application;
|
||||||
|
|
@ -9,7 +9,7 @@ namespace Sozsoft.Notifications.Application;
|
||||||
[Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)]
|
[Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)]
|
||||||
public partial class NotificationTypeToNotificationTypeDtoMapper : MapperBase<NotificationType, NotificationTypeDto>
|
public partial class NotificationTypeToNotificationTypeDtoMapper : MapperBase<NotificationType, NotificationTypeDto>
|
||||||
{
|
{
|
||||||
public override partial NotificationTypeDto Map(NotificationType source);
|
public override partial NotificationTypeDto Map(NotificationType source);
|
||||||
|
|
||||||
public override partial void Map(NotificationType source, NotificationTypeDto destination);
|
public override partial void Map(NotificationType source, NotificationTypeDto destination);
|
||||||
|
|
||||||
|
|
@ -25,7 +25,7 @@ public override partial NotificationTypeDto Map(NotificationType source);
|
||||||
[Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)]
|
[Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)]
|
||||||
public partial class NotificationRuleToNotificationRuleDtoMapper : MapperBase<NotificationRule, NotificationRuleDto>
|
public partial class NotificationRuleToNotificationRuleDtoMapper : MapperBase<NotificationRule, NotificationRuleDto>
|
||||||
{
|
{
|
||||||
public override partial NotificationRuleDto Map(NotificationRule source);
|
public override partial NotificationRuleDto Map(NotificationRule source);
|
||||||
|
|
||||||
public override partial void Map(NotificationRule source, NotificationRuleDto destination);
|
public override partial void Map(NotificationRule source, NotificationRuleDto destination);
|
||||||
|
|
||||||
|
|
@ -41,10 +41,15 @@ public override partial NotificationRuleDto Map(NotificationRule source);
|
||||||
[Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)]
|
[Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)]
|
||||||
public partial class NotificationToNotificationDtoMapper : MapperBase<Notification, NotificationDto>
|
public partial class NotificationToNotificationDtoMapper : MapperBase<Notification, NotificationDto>
|
||||||
{
|
{
|
||||||
[MapperIgnoreTarget(nameof(NotificationDto.CreatorFullname))]
|
// NotificationType hedefte string. Kaynaktaki navigation entity'si doğrudan
|
||||||
|
// eşlenirse ToString() devreye girip "[ENTITY: NotificationType] Id = ..."
|
||||||
|
// yazısı UI'a düşüyor; bu yüzden açıkça Name alanından beslenir.
|
||||||
|
[MapProperty("NotificationType.Name", nameof(NotificationDto.NotificationType))]
|
||||||
|
[MapperIgnoreTarget(nameof(NotificationDto.CreatorFullname))]
|
||||||
[MapperIgnoreTarget(nameof(NotificationDto.TenantId))]
|
[MapperIgnoreTarget(nameof(NotificationDto.TenantId))]
|
||||||
public override partial NotificationDto Map(Notification source);
|
public override partial NotificationDto Map(Notification source);
|
||||||
|
|
||||||
|
[MapProperty("NotificationType.Name", nameof(NotificationDto.NotificationType))]
|
||||||
[MapperIgnoreTarget(nameof(NotificationDto.CreatorFullname))]
|
[MapperIgnoreTarget(nameof(NotificationDto.CreatorFullname))]
|
||||||
[MapperIgnoreTarget(nameof(NotificationDto.TenantId))]
|
[MapperIgnoreTarget(nameof(NotificationDto.TenantId))]
|
||||||
public override partial void Map(Notification source, NotificationDto destination);
|
public override partial void Map(Notification source, NotificationDto destination);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
using Sozsoft.Notifications.Domain;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Sozsoft.Notifications.Domain;
|
||||||
using Volo.Abp.Application;
|
using Volo.Abp.Application;
|
||||||
using Volo.Abp.Mapperly;
|
using Volo.Abp.Mapperly;
|
||||||
using Volo.Abp.Modularity;
|
using Volo.Abp.Modularity;
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,10 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Sozsoft.Notifications.Domain;
|
|
||||||
using Sozsoft.Notifications.Entities;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Sozsoft.Notifications.Domain;
|
||||||
|
using Sozsoft.Notifications.Entities;
|
||||||
using Volo.Abp.Application.Dtos;
|
using Volo.Abp.Application.Dtos;
|
||||||
using Volo.Abp.Application.Services;
|
using Volo.Abp.Application.Services;
|
||||||
using Volo.Abp.Domain.Repositories;
|
using Volo.Abp.Domain.Repositories;
|
||||||
|
|
@ -23,22 +23,16 @@ public class NotificationRuleAppService : CrudAppService<
|
||||||
INotificationRuleAppService
|
INotificationRuleAppService
|
||||||
{
|
{
|
||||||
private readonly IRepository<NotificationType, Guid> repositoryType;
|
private readonly IRepository<NotificationType, Guid> repositoryType;
|
||||||
private readonly IOrganizationUnitRepository repositoryOrganizationUnit;
|
|
||||||
private readonly IdentityUserManager userManager;
|
|
||||||
private readonly IIdentityUserRepository repositoryUser;
|
private readonly IIdentityUserRepository repositoryUser;
|
||||||
|
|
||||||
public NotificationRuleAppService(
|
public NotificationRuleAppService(
|
||||||
IRepository<NotificationType, Guid> repositoryType,
|
IRepository<NotificationType, Guid> repositoryType,
|
||||||
IRepository<NotificationRule, Guid> repository,
|
IRepository<NotificationRule, Guid> repository,
|
||||||
IdentityUserManager userManager,
|
IIdentityUserRepository repositoryUser
|
||||||
IIdentityUserRepository repositoryUser,
|
|
||||||
IOrganizationUnitRepository repositoryOrganizationUnit
|
|
||||||
) : base(repository)
|
) : base(repository)
|
||||||
{
|
{
|
||||||
this.repositoryType = repositoryType;
|
this.repositoryType = repositoryType;
|
||||||
this.userManager = userManager;
|
|
||||||
this.repositoryUser = repositoryUser;
|
this.repositoryUser = repositoryUser;
|
||||||
this.repositoryOrganizationUnit = repositoryOrganizationUnit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string[]> GetNotificationTypes()
|
public async Task<string[]> GetNotificationTypes()
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,9 @@ public static class NotificationChannels
|
||||||
public const string Sms = "Sms"; //SMS (ABP Sms + Posta Guvercini)
|
public const string Sms = "Sms"; //SMS (ABP Sms + Posta Guvercini)
|
||||||
public const string Mail = "Mail"; //Email (ABP Emailing + Amazon SES)
|
public const string Mail = "Mail"; //Email (ABP Emailing + Amazon SES)
|
||||||
public const string Rocket = "Rocket"; //Rocket.Chat (HTTP API)
|
public const string Rocket = "Rocket"; //Rocket.Chat (HTTP API)
|
||||||
public const string Desktop = "Desktop";
|
public const string Desktop = "Desktop";
|
||||||
public const string UiActivity = "UiActivity"; //UI Activity (ABP UI Activity)
|
public const string UiActivity = "UiActivity"; //UI Activity (ABP UI Activity)
|
||||||
public const string UiToast = "UiToast"; //UI Toast (ABP UI Toast) Ayarlar/Sistem/Bildirimler/Chrome açık olması gerekiyor.
|
public const string UiToast = "UiToast"; //UI Toast (ABP UI Toast) Ayarlar/Sistem/Bildirimler/Chrome açık olması gerekiyor.
|
||||||
public const string WhatsApp = "WhatsApp"; //WhatsApp (HTTP API, template-based)
|
public const string WhatsApp = "WhatsApp"; //WhatsApp (HTTP API, template-based)
|
||||||
// public const string Telegram = "Telegram";
|
public const string Telegram = "Telegram"; //Telegram (Bot API, chat based)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
namespace Sozsoft.Notifications.Domain;
|
|
||||||
|
|
||||||
public static class NotificationErrorCodes
|
|
||||||
{
|
|
||||||
//Add your business exception error codes here...
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using Volo.Abp.Domain.Entities.Auditing;
|
using Volo.Abp.Domain.Entities.Auditing;
|
||||||
|
|
||||||
namespace Sozsoft.Notifications.Entities;
|
namespace Sozsoft.Notifications.Entities;
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ namespace Sozsoft.Notifications.Entities;
|
||||||
public class NotificationRule : FullAuditedEntity<Guid>
|
public class NotificationRule : FullAuditedEntity<Guid>
|
||||||
{
|
{
|
||||||
public required Guid NotificationTypeId { get; set; }
|
public required Guid NotificationTypeId { get; set; }
|
||||||
public NotificationType NotificationType { get; set; }
|
public NotificationType NotificationType { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Alıcı Tipi: User, Role, Organization, All, Custom
|
/// Alıcı Tipi: User, Role, Organization, All, Custom
|
||||||
/// User ise RecipientId UserId olacak
|
/// User ise RecipientId UserId olacak
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ public class NotificationIdentifierProvider : INotificationIdentifierProvider
|
||||||
NotificationChannels.UiActivity => user.Id.ToString(),
|
NotificationChannels.UiActivity => user.Id.ToString(),
|
||||||
NotificationChannels.UiToast => user.Id.ToString(),
|
NotificationChannels.UiToast => user.Id.ToString(),
|
||||||
NotificationChannels.WhatsApp => user.PhoneNumber,
|
NotificationChannels.WhatsApp => user.PhoneNumber,
|
||||||
// NotificationChannels.Telegram => user.PhoneNumber,
|
NotificationChannels.Telegram => user.GetProperty<string?>("TelegramChatId"),
|
||||||
_ => null,
|
_ => null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Sozsoft.Notifications.Entities;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Sozsoft.Notifications.Entities;
|
||||||
using Volo.Abp.DependencyInjection;
|
using Volo.Abp.DependencyInjection;
|
||||||
using Volo.Abp.Domain.Repositories;
|
using Volo.Abp.Domain.Repositories;
|
||||||
using Volo.Abp.Identity;
|
using Volo.Abp.Identity;
|
||||||
|
|
@ -192,9 +192,9 @@ public class NotificationManager : INotificationManager
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var notificationType = await repositoryType.FirstOrDefaultAsync(a => a.Name == NotificationType)
|
var notificationType = await repositoryType.FirstOrDefaultAsync(a => a.Name == NotificationType)
|
||||||
?? throw new Exception($"NotificationType '{NotificationType}' not found.");
|
?? throw new Exception($"NotificationType '{NotificationType}' not found.");
|
||||||
|
|
||||||
await repositoryNotification.InsertAsync(new Notification
|
await repositoryNotification.InsertAsync(new Notification
|
||||||
{
|
{
|
||||||
NotificationTypeId = notificationType.Id,
|
NotificationTypeId = notificationType.Id,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
using Sozsoft.Notifications.Domain;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Sozsoft.Notifications.Domain;
|
||||||
using Sozsoft.Notifications.Entities;
|
using Sozsoft.Notifications.Entities;
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Volo.Abp.Data;
|
using Volo.Abp.Data;
|
||||||
using Volo.Abp.EntityFrameworkCore;
|
using Volo.Abp.EntityFrameworkCore;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
using Sozsoft.Notifications.Domain;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Sozsoft.Notifications.Domain;
|
||||||
using Volo.Abp.EntityFrameworkCore;
|
using Volo.Abp.EntityFrameworkCore;
|
||||||
using Volo.Abp.Modularity;
|
using Volo.Abp.Modularity;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
using Volo.Abp.Localization;
|
using Volo.Abp.Localization;
|
||||||
|
|
||||||
namespace Sozsoft.Sender.Localization
|
namespace Sozsoft.Sender.Localization;
|
||||||
|
|
||||||
|
[LocalizationResourceName("SozsoftSender")]
|
||||||
|
public class ErpSenderResource
|
||||||
{
|
{
|
||||||
[LocalizationResourceName("SozsoftSender")]
|
|
||||||
public class ErpSenderResource
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Volo.Abp.BackgroundJobs;
|
using Volo.Abp.BackgroundJobs;
|
||||||
using Volo.Abp.DependencyInjection;
|
using Volo.Abp.DependencyInjection;
|
||||||
using Volo.Abp.MultiTenancy;
|
using Volo.Abp.MultiTenancy;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Volo.Abp.MultiTenancy;
|
using Volo.Abp.MultiTenancy;
|
||||||
|
|
||||||
namespace Sozsoft.Sender.Mail;
|
namespace Sozsoft.Sender.Mail;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Volo.Abp.Settings;
|
using Volo.Abp.Settings;
|
||||||
|
|
||||||
namespace Sozsoft.Sender.Rocket;
|
namespace Sozsoft.Sender.Rocket;
|
||||||
|
|
@ -12,16 +11,13 @@ public interface IRocketHttpClient
|
||||||
public sealed class RocketHttpClient : IRocketHttpClient, IDisposable
|
public sealed class RocketHttpClient : IRocketHttpClient, IDisposable
|
||||||
{
|
{
|
||||||
private readonly HttpClient httpClient;
|
private readonly HttpClient httpClient;
|
||||||
private readonly ILogger<RocketHttpClient> logger;
|
|
||||||
private readonly ISettingProvider settingProvider;
|
private readonly ISettingProvider settingProvider;
|
||||||
|
|
||||||
public RocketHttpClient(
|
public RocketHttpClient(
|
||||||
HttpClient httpClient,
|
HttpClient httpClient,
|
||||||
ILogger<RocketHttpClient> logger,
|
|
||||||
ISettingProvider settingProvider)
|
ISettingProvider settingProvider)
|
||||||
{
|
{
|
||||||
this.httpClient = httpClient;
|
this.httpClient = httpClient;
|
||||||
this.logger = logger;
|
|
||||||
this.settingProvider = settingProvider;
|
this.settingProvider = settingProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
using Microsoft.Extensions.Logging;
|
using Volo.Abp.Settings;
|
||||||
using Volo.Abp.Settings;
|
|
||||||
|
|
||||||
namespace Sozsoft.Sender.Sms.PostaGuvercini;
|
namespace Sozsoft.Sender.Sms.PostaGuvercini;
|
||||||
|
|
||||||
|
|
@ -11,16 +10,13 @@ public interface IPostaGuverciniHttpClient
|
||||||
public sealed class PostaGuverciniHttpClient : IPostaGuverciniHttpClient, IDisposable
|
public sealed class PostaGuverciniHttpClient : IPostaGuverciniHttpClient, IDisposable
|
||||||
{
|
{
|
||||||
private readonly HttpClient httpClient;
|
private readonly HttpClient httpClient;
|
||||||
private readonly ILogger<PostaGuverciniHttpClient> logger;
|
|
||||||
private readonly ISettingProvider settingProvider;
|
private readonly ISettingProvider settingProvider;
|
||||||
|
|
||||||
public PostaGuverciniHttpClient(
|
public PostaGuverciniHttpClient(
|
||||||
HttpClient httpClient,
|
HttpClient httpClient,
|
||||||
ILogger<PostaGuverciniHttpClient> logger,
|
|
||||||
ISettingProvider settingProvider)
|
ISettingProvider settingProvider)
|
||||||
{
|
{
|
||||||
this.httpClient = httpClient;
|
this.httpClient = httpClient;
|
||||||
this.logger = logger;
|
|
||||||
this.settingProvider = settingProvider;
|
this.settingProvider = settingProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
using Sozsoft.Sender.Localization;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Sozsoft.Sender.Localization;
|
||||||
using Sozsoft.Sender.Mail;
|
using Sozsoft.Sender.Mail;
|
||||||
using Sozsoft.Sender.Mail.AmazonSes;
|
using Sozsoft.Sender.Mail.AmazonSes;
|
||||||
using Sozsoft.Sender.Rocket;
|
using Sozsoft.Sender.Rocket;
|
||||||
using Sozsoft.Sender.Sms;
|
using Sozsoft.Sender.Sms;
|
||||||
using Sozsoft.Sender.Sms.PostaGuvercini;
|
using Sozsoft.Sender.Sms.PostaGuvercini;
|
||||||
|
using Sozsoft.Sender.Telegram;
|
||||||
using Sozsoft.Sender.WhatsApp;
|
using Sozsoft.Sender.WhatsApp;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Volo.Abp.BackgroundJobs;
|
using Volo.Abp.BackgroundJobs;
|
||||||
using Volo.Abp.Emailing;
|
using Volo.Abp.Emailing;
|
||||||
using Volo.Abp.Localization;
|
using Volo.Abp.Localization;
|
||||||
|
|
@ -36,6 +37,7 @@ public class ErpSenderModule : AbpModule
|
||||||
context.Services.AddScoped<ISozsoftSmsSender>(sp => sp.GetRequiredService<PostaGuverciniSmsSender>());
|
context.Services.AddScoped<ISozsoftSmsSender>(sp => sp.GetRequiredService<PostaGuverciniSmsSender>());
|
||||||
context.Services.AddScoped<ISozsoftRocketSender>(sp => sp.GetRequiredService<RocketSender>());
|
context.Services.AddScoped<ISozsoftRocketSender>(sp => sp.GetRequiredService<RocketSender>());
|
||||||
context.Services.AddScoped<ISozsoftWhatsAppSender>(sp => sp.GetRequiredService<SozsoftWhatsAppSender>());
|
context.Services.AddScoped<ISozsoftWhatsAppSender>(sp => sp.GetRequiredService<SozsoftWhatsAppSender>());
|
||||||
|
context.Services.AddScoped<ISozsoftTelegramSender>(sp => sp.GetRequiredService<SozsoftTelegramSender>());
|
||||||
|
|
||||||
context.Services.AddHttpClient<PostaGuverciniHttpClient>();
|
context.Services.AddHttpClient<PostaGuverciniHttpClient>();
|
||||||
context.Services.AddScoped<IPostaGuverciniHttpClient>(sp => sp.GetRequiredService<PostaGuverciniHttpClient>());
|
context.Services.AddScoped<IPostaGuverciniHttpClient>(sp => sp.GetRequiredService<PostaGuverciniHttpClient>());
|
||||||
|
|
@ -46,6 +48,9 @@ public class ErpSenderModule : AbpModule
|
||||||
context.Services.AddHttpClient<WhatsAppHttpClient>();
|
context.Services.AddHttpClient<WhatsAppHttpClient>();
|
||||||
context.Services.AddScoped<IWhatsAppHttpClient>(sp => sp.GetRequiredService<WhatsAppHttpClient>());
|
context.Services.AddScoped<IWhatsAppHttpClient>(sp => sp.GetRequiredService<WhatsAppHttpClient>());
|
||||||
|
|
||||||
|
context.Services.AddHttpClient<TelegramHttpClient>();
|
||||||
|
context.Services.AddScoped<ITelegramHttpClient>(sp => sp.GetRequiredService<TelegramHttpClient>());
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
44
api/modules/Sozsoft.Sender/Telegram/SozsoftTelegramSender.cs
Normal file
44
api/modules/Sozsoft.Sender/Telegram/SozsoftTelegramSender.cs
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Volo.Abp.DependencyInjection;
|
||||||
|
|
||||||
|
namespace Sozsoft.Sender.Telegram;
|
||||||
|
|
||||||
|
public interface ISozsoftTelegramSender : ITransientDependency
|
||||||
|
{
|
||||||
|
Task<TelegramSendResult> SendAsync(string ChatId, string Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SozsoftTelegramSender : ISozsoftTelegramSender
|
||||||
|
{
|
||||||
|
private readonly ITelegramHttpClient httpClient;
|
||||||
|
private readonly ILogger<SozsoftTelegramSender> logger;
|
||||||
|
|
||||||
|
public SozsoftTelegramSender(
|
||||||
|
ITelegramHttpClient httpClient,
|
||||||
|
ILogger<SozsoftTelegramSender> logger)
|
||||||
|
{
|
||||||
|
this.httpClient = httpClient;
|
||||||
|
this.logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<TelegramSendResult> SendAsync(string ChatId, string Message)
|
||||||
|
{
|
||||||
|
logger.LogInformation("Sending Telegram message. {ChatId}, {Message}", ChatId, Message);
|
||||||
|
var result = await httpClient.SendMessageAsync(ChatId, Message);
|
||||||
|
var success = result?.ok == true;
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
logger.LogInformation("Telegram sent successfully. {ChatId}, {Message}", ChatId, Message);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.LogWarning("Failed to send Telegram message. {ChatId}, {Message}, {ErrorCode}, {Description}",
|
||||||
|
ChatId, Message, result?.error_code, result?.description);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TelegramSendResult
|
||||||
|
{
|
||||||
|
Success = success,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
44
api/modules/Sozsoft.Sender/Telegram/TelegramHttpClient.cs
Normal file
44
api/modules/Sozsoft.Sender/Telegram/TelegramHttpClient.cs
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
using System.Net.Http.Json;
|
||||||
|
using Volo.Abp.Settings;
|
||||||
|
|
||||||
|
namespace Sozsoft.Sender.Telegram;
|
||||||
|
|
||||||
|
public interface ITelegramHttpClient
|
||||||
|
{
|
||||||
|
Task<TelegramSendResultDto> SendMessageAsync(string ChatId, string Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TelegramHttpClient : ITelegramHttpClient, IDisposable
|
||||||
|
{
|
||||||
|
private readonly HttpClient httpClient;
|
||||||
|
private readonly ISettingProvider settingProvider;
|
||||||
|
|
||||||
|
public TelegramHttpClient(
|
||||||
|
HttpClient httpClient,
|
||||||
|
ISettingProvider settingProvider)
|
||||||
|
{
|
||||||
|
this.httpClient = httpClient;
|
||||||
|
this.settingProvider = settingProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<TelegramSendResultDto> SendMessageAsync(string ChatId, string Message)
|
||||||
|
{
|
||||||
|
var url = await settingProvider.GetOrNullAsync(TelegramSettingNames.Url);
|
||||||
|
var botToken = await settingProvider.GetOrNullAsync(TelegramSettingNames.BotToken);
|
||||||
|
var parseMode = await settingProvider.GetOrNullAsync(TelegramSettingNames.ParseMode);
|
||||||
|
|
||||||
|
var response = await httpClient.PostAsJsonAsync($"{url.TrimEnd('/')}/bot{botToken}/sendMessage",
|
||||||
|
new TelegramPostMessageInput
|
||||||
|
{
|
||||||
|
chat_id = ChatId,
|
||||||
|
text = Message,
|
||||||
|
parse_mode = parseMode.IsNullOrWhiteSpace() ? null : parseMode,
|
||||||
|
disable_web_page_preview = true
|
||||||
|
});
|
||||||
|
|
||||||
|
// Bot API hatayı 4xx gövdesinde açıklar; durum kodunu fırlatmak açıklamayı kaybettirir.
|
||||||
|
return await response.Content.ReadFromJsonAsync<TelegramSendResultDto>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() => httpClient?.Dispose();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
namespace Sozsoft.Sender.Telegram;
|
||||||
|
|
||||||
|
public class TelegramPostMessageInput
|
||||||
|
{
|
||||||
|
public string chat_id { get; set; }
|
||||||
|
public string text { get; set; }
|
||||||
|
public string parse_mode { get; set; }
|
||||||
|
public bool disable_web_page_preview { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
namespace Sozsoft.Sender.Telegram;
|
||||||
|
|
||||||
|
public class TelegramSendResult
|
||||||
|
{
|
||||||
|
public bool Success { get; set; }
|
||||||
|
}
|
||||||
23
api/modules/Sozsoft.Sender/Telegram/TelegramSendResultDto.cs
Normal file
23
api/modules/Sozsoft.Sender/Telegram/TelegramSendResultDto.cs
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
namespace Sozsoft.Sender.Telegram;
|
||||||
|
|
||||||
|
public class TelegramSendResultDto
|
||||||
|
{
|
||||||
|
public bool ok { get; set; }
|
||||||
|
public int? error_code { get; set; }
|
||||||
|
public string description { get; set; }
|
||||||
|
public Message result { get; set; }
|
||||||
|
|
||||||
|
public class Message
|
||||||
|
{
|
||||||
|
public long message_id { get; set; }
|
||||||
|
public int date { get; set; }
|
||||||
|
public Chat chat { get; set; }
|
||||||
|
|
||||||
|
public class Chat
|
||||||
|
{
|
||||||
|
public long id { get; set; }
|
||||||
|
public string type { get; set; }
|
||||||
|
public string username { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
namespace Sozsoft.Sender.Telegram;
|
||||||
|
|
||||||
|
public static class TelegramSettingNames
|
||||||
|
{
|
||||||
|
public const string Default = "App.Sender.Telegram.";
|
||||||
|
public const string Url = Default + "Url"; // https://api.telegram.org
|
||||||
|
public const string BotToken = Default + "BotToken"; // BotFather token: 123456:ABC-DEF...
|
||||||
|
public const string ParseMode = Default + "ParseMode"; // HTML | Markdown | MarkdownV2 | (bos)
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Volo.Abp.Settings;
|
using Volo.Abp.Settings;
|
||||||
|
|
||||||
namespace Sozsoft.Sender.WhatsApp;
|
namespace Sozsoft.Sender.WhatsApp;
|
||||||
|
|
@ -13,16 +12,13 @@ public interface IWhatsAppHttpClient
|
||||||
public sealed class WhatsAppHttpClient : IWhatsAppHttpClient, IDisposable
|
public sealed class WhatsAppHttpClient : IWhatsAppHttpClient, IDisposable
|
||||||
{
|
{
|
||||||
private readonly HttpClient httpClient;
|
private readonly HttpClient httpClient;
|
||||||
private readonly ILogger<WhatsAppHttpClient> logger;
|
|
||||||
private readonly ISettingProvider settingProvider;
|
private readonly ISettingProvider settingProvider;
|
||||||
|
|
||||||
public WhatsAppHttpClient(
|
public WhatsAppHttpClient(
|
||||||
HttpClient httpClient,
|
HttpClient httpClient,
|
||||||
ILogger<WhatsAppHttpClient> logger,
|
|
||||||
ISettingProvider settingProvider)
|
ISettingProvider settingProvider)
|
||||||
{
|
{
|
||||||
this.httpClient = httpClient;
|
this.httpClient = httpClient;
|
||||||
this.logger = logger;
|
|
||||||
this.settingProvider = settingProvider;
|
this.settingProvider = settingProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Volo.Abp.Authorization.Permissions;
|
using Volo.Abp.Authorization.Permissions;
|
||||||
|
|
||||||
namespace Sozsoft.Settings.Permissions;
|
namespace Sozsoft.Settings.Permissions;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
namespace Sozsoft.Settings;
|
|
||||||
|
|
||||||
public class SettingsRemoteServiceConsts
|
|
||||||
{
|
|
||||||
public const string RemoteServiceName = "Settings";
|
|
||||||
|
|
||||||
public const string ModuleName = "settings";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Sozsoft.Languages;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Volo.Abp.Application.Services;
|
using Volo.Abp.Application.Services;
|
||||||
using Volo.Abp.MultiTenancy;
|
using Volo.Abp.MultiTenancy;
|
||||||
|
|
@ -15,20 +14,17 @@ namespace Sozsoft.Settings;
|
||||||
public class SettingUiAppService : ApplicationService, ISettingUiAppService
|
public class SettingUiAppService : ApplicationService, ISettingUiAppService
|
||||||
{
|
{
|
||||||
//private readonly IRepository<SettingDefinition, Guid> repositorySettingDefinition;
|
//private readonly IRepository<SettingDefinition, Guid> repositorySettingDefinition;
|
||||||
private readonly ILanguageKeyIntegrationService languageKeyIntegrationService;
|
|
||||||
private readonly ISettingDefinitionManager settingDefinitionManager;
|
private readonly ISettingDefinitionManager settingDefinitionManager;
|
||||||
private readonly ISettingManager settingManager;
|
private readonly ISettingManager settingManager;
|
||||||
private readonly ErpSettingDefinitionManager ErpSettingDefinitionManager;
|
private readonly ErpSettingDefinitionManager ErpSettingDefinitionManager;
|
||||||
private readonly ICurrentTenant currentTenant;
|
private readonly ICurrentTenant currentTenant;
|
||||||
|
|
||||||
public SettingUiAppService(
|
public SettingUiAppService(
|
||||||
ILanguageKeyIntegrationService languageKeyIntegrationService,
|
|
||||||
ISettingDefinitionManager settingDefinitionManager,
|
ISettingDefinitionManager settingDefinitionManager,
|
||||||
ISettingManager settingManager,
|
ISettingManager settingManager,
|
||||||
ErpSettingDefinitionManager ErpSettingDefinitionManager,
|
ErpSettingDefinitionManager ErpSettingDefinitionManager,
|
||||||
ICurrentTenant currentTenant)
|
ICurrentTenant currentTenant)
|
||||||
{
|
{
|
||||||
this.languageKeyIntegrationService = languageKeyIntegrationService;
|
|
||||||
this.settingDefinitionManager = settingDefinitionManager;
|
this.settingDefinitionManager = settingDefinitionManager;
|
||||||
this.settingManager = settingManager;
|
this.settingManager = settingManager;
|
||||||
this.ErpSettingDefinitionManager = ErpSettingDefinitionManager;
|
this.ErpSettingDefinitionManager = ErpSettingDefinitionManager;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
|
using Riok.Mapperly.Abstractions;
|
||||||
using Sozsoft.Settings.Entities;
|
using Sozsoft.Settings.Entities;
|
||||||
using Riok.Mapperly.Abstractions;
|
|
||||||
using Volo.Abp.Mapperly;
|
using Volo.Abp.Mapperly;
|
||||||
|
|
||||||
namespace Sozsoft.Settings;
|
namespace Sozsoft.Settings;
|
||||||
|
|
@ -7,7 +7,7 @@ namespace Sozsoft.Settings;
|
||||||
[Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)]
|
[Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)]
|
||||||
public partial class SettingDefinitionToSettingDefinitionDtoMapper : TwoWayMapperBase<SettingDefinition, SettingDefinitionDto>
|
public partial class SettingDefinitionToSettingDefinitionDtoMapper : TwoWayMapperBase<SettingDefinition, SettingDefinitionDto>
|
||||||
{
|
{
|
||||||
public override partial SettingDefinitionDto Map(SettingDefinition source);
|
public override partial SettingDefinitionDto Map(SettingDefinition source);
|
||||||
|
|
||||||
public override partial void Map(SettingDefinition source, SettingDefinitionDto destination);
|
public override partial void Map(SettingDefinition source, SettingDefinitionDto destination);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
namespace Sozsoft.Settings;
|
|
||||||
|
|
||||||
public static class SettingsErrorCodes
|
|
||||||
{
|
|
||||||
//Add your business exception error codes here...
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Sozsoft.Settings.Localization;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
|
using Sozsoft.Settings.Localization;
|
||||||
using Volo.Abp.Domain.Repositories;
|
using Volo.Abp.Domain.Repositories;
|
||||||
using Volo.Abp.Domain.Services;
|
using Volo.Abp.Domain.Services;
|
||||||
using Volo.Abp.Settings;
|
using Volo.Abp.Settings;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Volo.Abp.Modularity;
|
using Volo.Abp.Modularity;
|
||||||
using Volo.Abp.SettingManagement;
|
using Volo.Abp.SettingManagement;
|
||||||
using Volo.Abp.Settings;
|
using Volo.Abp.Settings;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
using Sozsoft.Settings.Entities;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Sozsoft.Settings.Entities;
|
||||||
using Volo.Abp.Data;
|
using Volo.Abp.Data;
|
||||||
using Volo.Abp.EntityFrameworkCore;
|
using Volo.Abp.EntityFrameworkCore;
|
||||||
using Volo.Abp.SettingManagement.EntityFrameworkCore;
|
using Volo.Abp.SettingManagement.EntityFrameworkCore;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Sozsoft.Settings.Entities;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||||||
|
using Sozsoft.Settings.Entities;
|
||||||
using Volo.Abp;
|
using Volo.Abp;
|
||||||
using Volo.Abp.EntityFrameworkCore.Modeling;
|
using Volo.Abp.EntityFrameworkCore.Modeling;
|
||||||
using Volo.Abp.SettingManagement.EntityFrameworkCore;
|
using Volo.Abp.SettingManagement.EntityFrameworkCore;
|
||||||
|
|
@ -15,7 +15,7 @@ public static class SettingsDbContextModelCreatingExtensions
|
||||||
public static void ConfigureSettings(this ModelBuilder builder)
|
public static void ConfigureSettings(this ModelBuilder builder)
|
||||||
{
|
{
|
||||||
Check.NotNull(builder, nameof(builder));
|
Check.NotNull(builder, nameof(builder));
|
||||||
|
|
||||||
builder.ConfigureSettingManagement();
|
builder.ConfigureSettingManagement();
|
||||||
|
|
||||||
builder.Entity<SettingDefinition>(b =>
|
builder.Entity<SettingDefinition>(b =>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Sozsoft.SqlQueryManager.Application.Contracts;
|
namespace Sozsoft.SqlQueryManager.Application.Contracts;
|
||||||
|
|
||||||
public class DatabaseTableDto
|
public class DatabaseTableDto
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Volo.Abp.Application.Services;
|
using Volo.Abp.Application.Services;
|
||||||
|
|
||||||
|
|
@ -30,29 +30,16 @@ public interface ISqlObjectManagerAppService : IApplicationService
|
||||||
Task<string> GetNativeObjectDefinitionAsync(string dataSourceCode, string schemaName, string objectName);
|
Task<string> GetNativeObjectDefinitionAsync(string dataSourceCode, string schemaName, string objectName);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Saves the T-SQL script to Seeds/SqlData/{fileName}.sql in the DbMigrator project.
|
/// Saves the T-SQL script to {SeedsRoot}/{host|tenants/{tenantId}}/{sql|postgres}/object/{fileName}.sql
|
||||||
|
/// (repo: configs/seeds). The scope folder follows the current tenant: host context writes to host/.
|
||||||
/// Called automatically after a successful SqlTableDesigner deploy so the script can be re-seeded.
|
/// Called automatically after a successful SqlTableDesigner deploy so the script can be re-seeded.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task SaveTableScriptAsync(SaveTableScriptDto input);
|
Task SaveTableScriptAsync(SaveTableScriptDto input);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Deletes matching SQL seed files from Seeds/SqlData when objects are dropped from the UI.
|
/// Deletes matching SQL seed files from the current scope's object folder when objects are
|
||||||
|
/// dropped from the UI.
|
||||||
/// Non-existing files are ignored.
|
/// Non-existing files are ignored.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task DeleteSqlDataFilesAsync(DeleteSqlDataFilesDto input);
|
Task DeleteSqlDataFilesAsync(DeleteSqlDataFilesDto input);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Lists .sql files currently available under DbMigrator Seeds/SqlData.
|
|
||||||
/// </summary>
|
|
||||||
Task<List<SqlDataFileDto>> GetSqlDataFilesAsync(string dataDirectoryName = "SqlData", string relativePath = "");
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Reads a .sql seed file content from the selected data directory.
|
|
||||||
/// </summary>
|
|
||||||
Task<string> GetSqlDataFileContentAsync(string dataDirectoryName = "SqlData", string relativePath = "");
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Moves a SQL seed file between the selected data directory root and HostData.
|
|
||||||
/// </summary>
|
|
||||||
Task MoveSqlDataFileAsync(MoveSqlDataFileDto input);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Sozsoft.SqlQueryManager.Application.Contracts;
|
|
||||||
|
|
||||||
public class SqlDataFileDto
|
|
||||||
{
|
|
||||||
public string FileName { get; set; } = string.Empty;
|
|
||||||
public string Name { get; set; } = string.Empty;
|
|
||||||
public string RelativePath { get; set; } = string.Empty;
|
|
||||||
public bool IsDirectory { get; set; }
|
|
||||||
public DateTime CreatedAt { get; set; }
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Sozsoft.SqlQueryManager.Application.Contracts;
|
namespace Sozsoft.SqlQueryManager.Application.Contracts;
|
||||||
|
|
||||||
|
|
@ -20,7 +20,8 @@ public class SqlQueryExecutionResultDto
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Input for saving a T-SQL script file to DbMigrator Seeds/SqlData.
|
/// Input for saving a T-SQL script file to the seeds folder
|
||||||
|
/// ({App:SeedsPath}/{host|tenants/{tenantId}}/{sql|postgres}/object).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class SaveTableScriptDto
|
public class SaveTableScriptDto
|
||||||
{
|
{
|
||||||
|
|
@ -36,7 +37,8 @@ public class SaveTableScriptDto
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Input for deleting seed files from DbMigrator Seeds/SqlData.
|
/// Input for deleting seed files from the seeds folder
|
||||||
|
/// ({App:SeedsPath}/{host|tenants/{tenantId}}/{sql|postgres}/object).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class DeleteSqlDataFilesDto
|
public class DeleteSqlDataFilesDto
|
||||||
{
|
{
|
||||||
|
|
@ -46,10 +48,3 @@ public class DeleteSqlDataFilesDto
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<string> FileNames { get; set; } = new();
|
public List<string> FileNames { get; set; } = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MoveSqlDataFileDto
|
|
||||||
{
|
|
||||||
public string DataDirectoryName { get; set; } = string.Empty;
|
|
||||||
public string SourceRelativePath { get; set; } = string.Empty;
|
|
||||||
public string TargetRelativePath { get; set; } = string.Empty;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Sozsoft.SqlQueryManager.Application.Contracts;
|
namespace Sozsoft.SqlQueryManager.Application.Contracts;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Volo.Abp.Application;
|
using Volo.Abp.Application;
|
||||||
using Volo.Abp.Authorization;
|
using Volo.Abp.Authorization;
|
||||||
using Volo.Abp.Modularity;
|
using Volo.Abp.Modularity;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
using System.Collections.Generic;
|
namespace Sozsoft.SqlQueryManager.Application.Contracts;
|
||||||
|
|
||||||
namespace Sozsoft.SqlQueryManager.Application.Contracts;
|
|
||||||
|
|
||||||
public class SqlTemplateDto
|
public class SqlTemplateDto
|
||||||
{
|
{
|
||||||
|
|
@ -9,10 +7,3 @@ public class SqlTemplateDto
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
public string Template { get; set; }
|
public string Template { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class GetTemplateInput
|
|
||||||
{
|
|
||||||
public string TemplateType { get; set; }
|
|
||||||
public string ObjectName { get; set; }
|
|
||||||
public string SchemaName { get; set; }
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -2,19 +2,21 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Sozsoft.SqlQueryManager.Application.Contracts;
|
|
||||||
using Sozsoft.SqlQueryManager.Domain.Services;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Volo.Abp.Application.Services;
|
using Sozsoft.Platform.Data.Seeds;
|
||||||
using Volo.Abp.MultiTenancy;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using Sozsoft.Platform.Enums;
|
using Sozsoft.Platform.Enums;
|
||||||
using Sozsoft.Platform.Queries;
|
using Sozsoft.Platform.Queries;
|
||||||
|
using Sozsoft.Settings;
|
||||||
|
using Sozsoft.SqlQueryManager.Application.Contracts;
|
||||||
|
using Sozsoft.SqlQueryManager.Domain.Services;
|
||||||
|
using Volo.Abp.Application.Services;
|
||||||
|
using Volo.Abp.MultiTenancy;
|
||||||
|
|
||||||
namespace Sozsoft.SqlQueryManager.Application;
|
namespace Sozsoft.SqlQueryManager.Application;
|
||||||
|
|
||||||
|
|
@ -48,7 +50,7 @@ public class SqlObjectManagerAppService : ApplicationService, ISqlObjectManagerA
|
||||||
private readonly IDataSourceManager _dataSourceManager;
|
private readonly IDataSourceManager _dataSourceManager;
|
||||||
private readonly ICurrentTenant _currentTenant;
|
private readonly ICurrentTenant _currentTenant;
|
||||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||||
private readonly IHostEnvironment _hostEnvironment;
|
private readonly IConfiguration _configuration;
|
||||||
private readonly ILogger<SqlObjectManagerAppService> _logger;
|
private readonly ILogger<SqlObjectManagerAppService> _logger;
|
||||||
|
|
||||||
public SqlObjectManagerAppService(
|
public SqlObjectManagerAppService(
|
||||||
|
|
@ -57,7 +59,7 @@ public class SqlObjectManagerAppService : ApplicationService, ISqlObjectManagerA
|
||||||
IDataSourceManager dataSourceManager,
|
IDataSourceManager dataSourceManager,
|
||||||
ICurrentTenant currentTenant,
|
ICurrentTenant currentTenant,
|
||||||
IHttpContextAccessor httpContextAccessor,
|
IHttpContextAccessor httpContextAccessor,
|
||||||
IHostEnvironment hostEnvironment,
|
IConfiguration configuration,
|
||||||
ILogger<SqlObjectManagerAppService> logger)
|
ILogger<SqlObjectManagerAppService> logger)
|
||||||
{
|
{
|
||||||
_sqlExecutorService = sqlExecutorService;
|
_sqlExecutorService = sqlExecutorService;
|
||||||
|
|
@ -65,7 +67,7 @@ public class SqlObjectManagerAppService : ApplicationService, ISqlObjectManagerA
|
||||||
_dataSourceManager = dataSourceManager;
|
_dataSourceManager = dataSourceManager;
|
||||||
_currentTenant = currentTenant;
|
_currentTenant = currentTenant;
|
||||||
_httpContextAccessor = httpContextAccessor;
|
_httpContextAccessor = httpContextAccessor;
|
||||||
_hostEnvironment = hostEnvironment;
|
_configuration = configuration;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -954,191 +956,27 @@ FROM (
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("api/app/sql-object-manager/sql-data-files")]
|
|
||||||
public Task<List<SqlDataFileDto>> GetSqlDataFilesAsync(
|
|
||||||
[FromQuery] string dataDirectoryName = "SqlData",
|
|
||||||
[FromQuery] string relativePath = "")
|
|
||||||
{
|
|
||||||
ValidateTenantAccess();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var rootPath = ResolveSqlDataOutputPath(dataDirectoryName);
|
|
||||||
var outputPath = ResolveSqlDataChildPath(rootPath, relativePath);
|
|
||||||
if (!Directory.Exists(outputPath))
|
|
||||||
return Task.FromResult(new List<SqlDataFileDto>());
|
|
||||||
|
|
||||||
var directories = Directory.GetDirectories(outputPath, "*", SearchOption.TopDirectoryOnly)
|
|
||||||
.Where(d => string.Equals(Path.GetFileName(d), "HostData", StringComparison.OrdinalIgnoreCase))
|
|
||||||
.Select(d => new SqlDataFileDto
|
|
||||||
{
|
|
||||||
FileName = Path.GetFileName(d)!,
|
|
||||||
Name = Path.GetFileName(d)!,
|
|
||||||
RelativePath = BuildSqlDataRelativePath(relativePath, Path.GetFileName(d)!),
|
|
||||||
IsDirectory = true,
|
|
||||||
CreatedAt = Directory.GetCreationTime(d)
|
|
||||||
});
|
|
||||||
|
|
||||||
var files = Directory.GetFiles(outputPath, "*.sql", SearchOption.TopDirectoryOnly)
|
|
||||||
.Select(f => new SqlDataFileDto
|
|
||||||
{
|
|
||||||
FileName = Path.GetFileName(f)!,
|
|
||||||
Name = Path.GetFileName(f)!,
|
|
||||||
RelativePath = BuildSqlDataRelativePath(relativePath, Path.GetFileName(f)!),
|
|
||||||
IsDirectory = false,
|
|
||||||
CreatedAt = File.GetCreationTime(f)
|
|
||||||
})
|
|
||||||
.Where(x => !string.IsNullOrWhiteSpace(x.Name));
|
|
||||||
|
|
||||||
var entries = directories
|
|
||||||
.Concat(files)
|
|
||||||
.OrderByDescending(x => x.IsDirectory)
|
|
||||||
.ThenBy(x => x.Name, StringComparer.OrdinalIgnoreCase)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
return Task.FromResult(entries);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Failed to list SQL seed files: {Message}", ex.Message);
|
|
||||||
return Task.FromResult(new List<SqlDataFileDto>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("api/app/sql-object-manager/sql-data-file-content")]
|
|
||||||
public async Task<string> GetSqlDataFileContentAsync(
|
|
||||||
[FromQuery] string dataDirectoryName = "SqlData",
|
|
||||||
[FromQuery] string relativePath = "")
|
|
||||||
{
|
|
||||||
ValidateTenantAccess();
|
|
||||||
|
|
||||||
var rootPath = ResolveSqlDataOutputPath(dataDirectoryName);
|
|
||||||
var filePath = ResolveSqlDataChildPath(rootPath, relativePath);
|
|
||||||
|
|
||||||
if (!File.Exists(filePath))
|
|
||||||
throw new Volo.Abp.UserFriendlyException("SQL seed file was not found.");
|
|
||||||
|
|
||||||
if (!string.Equals(Path.GetExtension(filePath), ".sql", StringComparison.OrdinalIgnoreCase))
|
|
||||||
throw new Volo.Abp.UserFriendlyException("Only .sql files can be previewed.");
|
|
||||||
|
|
||||||
return await File.ReadAllTextAsync(filePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("api/app/sql-object-manager/move-sql-data-file")]
|
|
||||||
public Task MoveSqlDataFileAsync(MoveSqlDataFileDto input)
|
|
||||||
{
|
|
||||||
ValidateTenantAccess();
|
|
||||||
|
|
||||||
if (input == null)
|
|
||||||
throw new Volo.Abp.UserFriendlyException("Invalid move request.");
|
|
||||||
|
|
||||||
var rootPath = ResolveSqlDataOutputPath(input.DataDirectoryName);
|
|
||||||
var sourcePath = ResolveSqlDataChildPath(rootPath, input.SourceRelativePath);
|
|
||||||
var targetPath = ResolveSqlDataChildPath(rootPath, input.TargetRelativePath);
|
|
||||||
|
|
||||||
if (!File.Exists(sourcePath))
|
|
||||||
throw new Volo.Abp.UserFriendlyException("Source file was not found.");
|
|
||||||
|
|
||||||
if (!string.Equals(Path.GetExtension(sourcePath), ".sql", StringComparison.OrdinalIgnoreCase) ||
|
|
||||||
!string.Equals(Path.GetExtension(targetPath), ".sql", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
throw new Volo.Abp.UserFriendlyException("Only .sql files can be moved.");
|
|
||||||
}
|
|
||||||
|
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(targetPath)!);
|
|
||||||
|
|
||||||
if (File.Exists(targetPath))
|
|
||||||
throw new Volo.Abp.UserFriendlyException("A file with the same name already exists in the target folder.");
|
|
||||||
|
|
||||||
File.Move(sourcePath, targetPath);
|
|
||||||
_logger.LogInformation("SQL seed file moved from {SourcePath} to {TargetPath}", sourcePath, targetPath);
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Script dosyalarinin yazilacagi klasor. SqlDataSeeder ile ayni cozumleyiciyi kullanir
|
||||||
|
/// (App:SeedsPath -> configs/seeds), aksi halde deploy edilen nesne seed olarak geri okunmaz.
|
||||||
|
/// <para>
|
||||||
|
/// Yol, islemin yapildigi veritabani kapsamini izler: host (varsayilan) baglaminda
|
||||||
|
/// <c>host/{sql|postgres}/object</c>, bir tenant secilmisken
|
||||||
|
/// <c>tenants/{tenantId}/{sql|postgres}/object</c>. Boylece nesne, seed sirasinda
|
||||||
|
/// olusturuldugu veritabaninda yeniden olusturulur.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
private string ResolveSqlDataOutputPath()
|
private string ResolveSqlDataOutputPath()
|
||||||
{
|
{
|
||||||
return ResolveSqlDataOutputPath("SqlData");
|
var providerDirectoryName = SeedPathResolver.GetProviderFolderName(
|
||||||
|
SettingsConsts.DefaultDatabaseProvider == SettingsConsts.DatabaseProvider.PostgreSql);
|
||||||
|
|
||||||
|
return SeedPathResolver.GetScopePath(
|
||||||
|
_configuration,
|
||||||
|
_currentTenant.Id,
|
||||||
|
providerDirectoryName,
|
||||||
|
SeedPathResolver.ObjectFolder);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ResolveSqlDataOutputPath(string dataDirectoryName)
|
|
||||||
{
|
|
||||||
const string dbMigratorName = "Sozsoft.Platform.DbMigrator";
|
|
||||||
var safeDirectoryName = NormalizeSqlDataDirectoryName(dataDirectoryName);
|
|
||||||
var dir = new DirectoryInfo(_hostEnvironment.ContentRootPath);
|
|
||||||
|
|
||||||
while (dir != null)
|
|
||||||
{
|
|
||||||
var candidate = Path.Combine(dir.FullName, "src", dbMigratorName, "Seeds");
|
|
||||||
if (Directory.Exists(candidate))
|
|
||||||
return Path.Combine(candidate, safeDirectoryName);
|
|
||||||
|
|
||||||
candidate = Path.Combine(dir.FullName, dbMigratorName, "Seeds");
|
|
||||||
if (Directory.Exists(candidate))
|
|
||||||
return Path.Combine(candidate, safeDirectoryName);
|
|
||||||
|
|
||||||
dir = dir.Parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Path.Combine(_hostEnvironment.ContentRootPath, "Seeds", safeDirectoryName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string NormalizeSqlDataDirectoryName(string dataDirectoryName)
|
|
||||||
{
|
|
||||||
return string.Equals(dataDirectoryName, "PostgresData", StringComparison.OrdinalIgnoreCase)
|
|
||||||
? "PostgresData"
|
|
||||||
: "SqlData";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string ResolveSqlDataChildPath(string rootPath, string relativePath)
|
|
||||||
{
|
|
||||||
var normalized = NormalizeSqlDataRelativePath(relativePath);
|
|
||||||
var fullPath = Path.GetFullPath(Path.Combine(rootPath, normalized));
|
|
||||||
var fullRoot = Path.GetFullPath(rootPath);
|
|
||||||
var fullRootWithSeparator = fullRoot.EndsWith(Path.DirectorySeparatorChar)
|
|
||||||
? fullRoot
|
|
||||||
: fullRoot + Path.DirectorySeparatorChar;
|
|
||||||
|
|
||||||
if (!string.Equals(fullPath, fullRoot, StringComparison.OrdinalIgnoreCase) &&
|
|
||||||
!fullPath.StartsWith(fullRootWithSeparator, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
throw new Volo.Abp.UserFriendlyException("Invalid path.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return fullPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string NormalizeSqlDataRelativePath(string relativePath)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(relativePath))
|
|
||||||
return string.Empty;
|
|
||||||
|
|
||||||
var normalized = relativePath.Replace('\\', '/').Trim('/');
|
|
||||||
var parts = normalized.Split('/', StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
|
|
||||||
if (parts.Length == 0)
|
|
||||||
return string.Empty;
|
|
||||||
|
|
||||||
if (parts.Any(p => p == "." || p == ".." || p.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0))
|
|
||||||
throw new Volo.Abp.UserFriendlyException("Invalid path.");
|
|
||||||
|
|
||||||
var isRootFile = parts.Length == 1 && parts[0].EndsWith(".sql", StringComparison.OrdinalIgnoreCase);
|
|
||||||
var isHostDataFolder = parts.Length == 1 && string.Equals(parts[0], "HostData", StringComparison.OrdinalIgnoreCase);
|
|
||||||
var isHostDataFile = parts.Length == 2 &&
|
|
||||||
string.Equals(parts[0], "HostData", StringComparison.OrdinalIgnoreCase) &&
|
|
||||||
parts[1].EndsWith(".sql", StringComparison.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
if (!isRootFile && !isHostDataFolder && !isHostDataFile)
|
|
||||||
throw new Volo.Abp.UserFriendlyException("Invalid path.");
|
|
||||||
|
|
||||||
return Path.Combine(parts);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string BuildSqlDataRelativePath(string parentRelativePath, string name)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(parentRelativePath))
|
|
||||||
return name;
|
|
||||||
|
|
||||||
return $"{parentRelativePath.Trim('/', '\\')}/{name}";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
namespace Sozsoft.SqlQueryManager.Application;
|
namespace Sozsoft.SqlQueryManager.Application;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Volo.Abp.Localization;
|
using Volo.Abp.Localization;
|
||||||
|
|
||||||
namespace Sozsoft.SqlQueryManager.Domain.Shared;
|
namespace Sozsoft.SqlQueryManager.Domain.Shared;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Sozsoft.SqlQueryManager.Domain.Shared;
|
namespace Sozsoft.SqlQueryManager.Domain.Shared;
|
||||||
|
|
||||||
// SqlObjectType, SqlFunctionType and SqlQueryStatus enums removed:
|
// SqlObjectType, SqlFunctionType and SqlQueryStatus enums removed:
|
||||||
// The module no longer persists SQL objects and therefore does not need these enums.
|
// The module no longer persists SQL objects and therefore does not need these enums.
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Sozsoft.SqlQueryManager.Domain.Shared;
|
using Sozsoft.SqlQueryManager.Domain.Shared;
|
||||||
using Volo.Abp.Domain;
|
using Volo.Abp.Domain;
|
||||||
using Volo.Abp.Localization;
|
using Volo.Abp.Localization;
|
||||||
using Volo.Abp.Localization.ExceptionHandling;
|
using Volo.Abp.Localization.ExceptionHandling;
|
||||||
|
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
namespace Sozsoft.SqlQueryManager.Domain.Shared;
|
|
||||||
|
|
||||||
public static class SqlQueryManagerErrorCodes
|
|
||||||
{
|
|
||||||
public const string InvalidSqlQuery = "SqlQueryManager:InvalidSqlQuery";
|
|
||||||
public const string DataSourceNotFound = "SqlQueryManager:DataSourceNotFound";
|
|
||||||
public const string ExecutionFailed = "SqlQueryManager:ExecutionFailed";
|
|
||||||
public const string PermissionDenied = "SqlQueryManager:PermissionDenied";
|
|
||||||
public const string InvalidObjectName = "SqlQueryManager:InvalidObjectName";
|
|
||||||
public const string ObjectAlreadyExists = "SqlQueryManager:ObjectAlreadyExists";
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Volo.Abp.Reflection;
|
using Volo.Abp.Reflection;
|
||||||
|
|
||||||
namespace Sozsoft.SqlQueryManager.Domain.Shared;
|
namespace Sozsoft.SqlQueryManager.Domain.Shared;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Sozsoft.SqlQueryManager.Domain.Services;
|
namespace Sozsoft.SqlQueryManager.Domain.Services;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Sozsoft.Platform.DynamicData;
|
using Sozsoft.Platform.DynamicData;
|
||||||
using Sozsoft.Platform.Enums;
|
using Sozsoft.Platform.Enums;
|
||||||
using Sozsoft.Platform.Queries;
|
using Sozsoft.Platform.Queries;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Volo.Abp;
|
using Volo.Abp;
|
||||||
using Volo.Abp.Domain.Services;
|
using Volo.Abp.Domain.Services;
|
||||||
|
|
||||||
|
|
@ -30,7 +30,7 @@ public class SqlExecutorService : DomainService, ISqlExecutorService
|
||||||
{
|
{
|
||||||
// Get DataSource to determine database type
|
// Get DataSource to determine database type
|
||||||
var dataSource = await _dataSourceManager.GetDataSourceAsync(
|
var dataSource = await _dataSourceManager.GetDataSourceAsync(
|
||||||
CurrentTenant.IsAvailable,
|
CurrentTenant.IsAvailable,
|
||||||
dataSourceCode);
|
dataSourceCode);
|
||||||
|
|
||||||
if (dataSource == null)
|
if (dataSource == null)
|
||||||
|
|
@ -53,7 +53,7 @@ public class SqlExecutorService : DomainService, ISqlExecutorService
|
||||||
}
|
}
|
||||||
|
|
||||||
var repository = _serviceProvider.GetKeyedService<IDynamicDataRepository>(repositoryKey);
|
var repository = _serviceProvider.GetKeyedService<IDynamicDataRepository>(repositoryKey);
|
||||||
|
|
||||||
if (repository == null)
|
if (repository == null)
|
||||||
{
|
{
|
||||||
throw new BusinessException("SqlQueryManager:RepositoryNotFound")
|
throw new BusinessException("SqlQueryManager:RepositoryNotFound")
|
||||||
|
|
@ -64,8 +64,8 @@ public class SqlExecutorService : DomainService, ISqlExecutorService
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<SqlExecutionResult> ExecuteQueryAsync(
|
public async Task<SqlExecutionResult> ExecuteQueryAsync(
|
||||||
string sql,
|
string sql,
|
||||||
string dataSourceCode,
|
string dataSourceCode,
|
||||||
Dictionary<string, object> parameters = null)
|
Dictionary<string, object> parameters = null)
|
||||||
{
|
{
|
||||||
var stopwatch = Stopwatch.StartNew();
|
var stopwatch = Stopwatch.StartNew();
|
||||||
|
|
@ -74,7 +74,7 @@ public class SqlExecutorService : DomainService, ISqlExecutorService
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var connectionString = await _dataSourceManager.GetConnectionStringAsync(
|
var connectionString = await _dataSourceManager.GetConnectionStringAsync(
|
||||||
CurrentTenant.IsAvailable,
|
CurrentTenant.IsAvailable,
|
||||||
dataSourceCode);
|
dataSourceCode);
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(connectionString))
|
if (string.IsNullOrWhiteSpace(connectionString))
|
||||||
|
|
@ -107,8 +107,8 @@ public class SqlExecutorService : DomainService, ISqlExecutorService
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<SqlExecutionResult> ExecuteNonQueryAsync(
|
public async Task<SqlExecutionResult> ExecuteNonQueryAsync(
|
||||||
string sql,
|
string sql,
|
||||||
string dataSourceCode,
|
string dataSourceCode,
|
||||||
Dictionary<string, object> parameters = null)
|
Dictionary<string, object> parameters = null)
|
||||||
{
|
{
|
||||||
var stopwatch = Stopwatch.StartNew();
|
var stopwatch = Stopwatch.StartNew();
|
||||||
|
|
@ -117,7 +117,7 @@ public class SqlExecutorService : DomainService, ISqlExecutorService
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var connectionString = await _dataSourceManager.GetConnectionStringAsync(
|
var connectionString = await _dataSourceManager.GetConnectionStringAsync(
|
||||||
CurrentTenant.IsAvailable,
|
CurrentTenant.IsAvailable,
|
||||||
dataSourceCode);
|
dataSourceCode);
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(connectionString))
|
if (string.IsNullOrWhiteSpace(connectionString))
|
||||||
|
|
@ -149,8 +149,8 @@ public class SqlExecutorService : DomainService, ISqlExecutorService
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<SqlExecutionResult> ExecuteScalarAsync<T>(
|
public async Task<SqlExecutionResult> ExecuteScalarAsync<T>(
|
||||||
string sql,
|
string sql,
|
||||||
string dataSourceCode,
|
string dataSourceCode,
|
||||||
Dictionary<string, object> parameters = null)
|
Dictionary<string, object> parameters = null)
|
||||||
{
|
{
|
||||||
var stopwatch = Stopwatch.StartNew();
|
var stopwatch = Stopwatch.StartNew();
|
||||||
|
|
@ -159,7 +159,7 @@ public class SqlExecutorService : DomainService, ISqlExecutorService
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var connectionString = await _dataSourceManager.GetConnectionStringAsync(
|
var connectionString = await _dataSourceManager.GetConnectionStringAsync(
|
||||||
CurrentTenant.IsAvailable,
|
CurrentTenant.IsAvailable,
|
||||||
dataSourceCode);
|
dataSourceCode);
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(connectionString))
|
if (string.IsNullOrWhiteSpace(connectionString))
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
|
||||||
using Volo.Abp.DependencyInjection;
|
using Volo.Abp.DependencyInjection;
|
||||||
|
|
||||||
namespace Sozsoft.SqlQueryManager.Domain.Services;
|
namespace Sozsoft.SqlQueryManager.Domain.Services;
|
||||||
|
|
@ -48,7 +47,7 @@ GO";
|
||||||
public string GetViewTemplate(string viewName, string schemaName = "dbo", bool withSchemaBinding = false)
|
public string GetViewTemplate(string viewName, string schemaName = "dbo", bool withSchemaBinding = false)
|
||||||
{
|
{
|
||||||
var schemaBindingClause = withSchemaBinding ? " WITH SCHEMABINDING" : "";
|
var schemaBindingClause = withSchemaBinding ? " WITH SCHEMABINDING" : "";
|
||||||
|
|
||||||
return $@"-- =============================================
|
return $@"-- =============================================
|
||||||
-- Author: <Author Name>
|
-- Author: <Author Name>
|
||||||
-- Create date: <Create Date>
|
-- Create date: <Create Date>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Volo.Abp.Domain;
|
||||||
using Volo.Abp.Domain;
|
|
||||||
using Volo.Abp.Modularity;
|
using Volo.Abp.Modularity;
|
||||||
|
|
||||||
namespace Sozsoft.SqlQueryManager;
|
namespace Sozsoft.SqlQueryManager;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Volo.Abp.Data;
|
using Volo.Abp.Data;
|
||||||
using Volo.Abp.EntityFrameworkCore;
|
using Volo.Abp.EntityFrameworkCore;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Volo.Abp.EntityFrameworkCore;
|
using Volo.Abp.EntityFrameworkCore;
|
||||||
using Volo.Abp.Modularity;
|
using Volo.Abp.Modularity;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Volo.Abp.Application.Dtos;
|
using Volo.Abp.Application.Dtos;
|
||||||
|
|
||||||
namespace Sozsoft.Platform.AuditLogs;
|
namespace Sozsoft.Platform.AuditLogs;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
using System;
|
||||||
|
using Volo.Abp.Application.Dtos;
|
||||||
|
|
||||||
|
namespace Sozsoft.Platform.ChangeLogs;
|
||||||
|
|
||||||
|
public class ChangeLogDto : EntityDto<Guid>
|
||||||
|
{
|
||||||
|
public string Version { get; set; }
|
||||||
|
public DateTime ReleaseDate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Sürüm notu maddeleri (entity'de satır satır saklanır).</summary>
|
||||||
|
public string[] ChangeLog { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -11,7 +11,7 @@ public class CrudEndpointDto : FullAuditedEntityDto<Guid>
|
||||||
public string OperationType { get; set; } = string.Empty;
|
public string OperationType { get; set; } = string.Empty;
|
||||||
public string CsharpCode { get; set; } = string.Empty;
|
public string CsharpCode { get; set; } = string.Empty;
|
||||||
public bool IsActive { get; set; } = true;
|
public bool IsActive { get; set; } = true;
|
||||||
|
|
||||||
// Navigation property for display purposes
|
// Navigation property for display purposes
|
||||||
public string? EntityDisplayName { get; set; }
|
public string? EntityDisplayName { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,21 +7,31 @@ namespace Sozsoft.Platform.DeveloperKit;
|
||||||
public class CustomComponentDto : FullAuditedEntityDto<Guid>
|
public class CustomComponentDto : FullAuditedEntityDto<Guid>
|
||||||
{
|
{
|
||||||
public string Name { get; set; } = string.Empty;
|
public string Name { get; set; } = string.Empty;
|
||||||
public string RoutePath { get; set; } = string.Empty;
|
/// <summary>Rotasi olmayan bilesende (ust bilgi, alt bilgi) bostur.</summary>
|
||||||
|
public string? RoutePath { get; set; }
|
||||||
public string Code { get; set; } = string.Empty;
|
public string Code { get; set; } = string.Empty;
|
||||||
public string? Props { get; set; }
|
public string? Props { get; set; }
|
||||||
public string? Description { get; set; }
|
public string? Description { get; set; }
|
||||||
public bool IsActive { get; set; } = true;
|
public bool IsActive { get; set; } = true;
|
||||||
public string? Dependencies { get; set; } // JSON string of component names
|
public string? Dependencies { get; set; } // JSON string of component names
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Data sekmesinde kullanilan API'ler; JSON dizisi. Props icindeki designer dokumanindan
|
||||||
|
/// kaydetme sirasinda sunucu tarafinda uretilir, istemciden gonderilmez.
|
||||||
|
/// </summary>
|
||||||
|
public string? DataSources { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CreateUpdateCustomComponentDto
|
public class CreateUpdateCustomComponentDto
|
||||||
{
|
{
|
||||||
public string Name { get; set; } = string.Empty;
|
public string Name { get; set; } = string.Empty;
|
||||||
[Required]
|
/// <summary>
|
||||||
|
/// Bilesenin kendi rotasi. Bos birakilabilir: rotasi olmayan bir bilesen (ornegin
|
||||||
|
/// baska bir ekranin icinde cizilen alt bilgi) hicbir adrese baglanmaz.
|
||||||
|
/// </summary>
|
||||||
[StringLength(512)]
|
[StringLength(512)]
|
||||||
[RegularExpression(@"^/.*", ErrorMessage = "RoutePath must start with '/'.")]
|
[RegularExpression(@"^(/.*)?$", ErrorMessage = "RoutePath must start with '/'.")]
|
||||||
public string RoutePath { get; set; } = string.Empty;
|
public string? RoutePath { get; set; }
|
||||||
public string Code { get; set; } = string.Empty;
|
public string Code { get; set; } = string.Empty;
|
||||||
public string? Props { get; set; }
|
public string? Props { get; set; }
|
||||||
public string? Description { get; set; }
|
public string? Description { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,17 @@ public interface ICrudEndpointAppService : ICrudAppService<
|
||||||
PagedAndSortedResultRequestDto,
|
PagedAndSortedResultRequestDto,
|
||||||
CreateUpdateCrudEndpointDto>
|
CreateUpdateCrudEndpointDto>
|
||||||
{
|
{
|
||||||
Task<List<CrudEndpointDto>> GetActiveEndpointsAsync();
|
|
||||||
Task<List<CrudEndpointDto>> GetEndpointsByEntityAsync(string entityName);
|
Task<List<CrudEndpointDto>> GetEndpointsByEntityAsync(string entityName);
|
||||||
Task<CrudEndpointDto> ToggleAsync(Guid id);
|
Task<CrudEndpointDto> ToggleAsync(Guid id);
|
||||||
Task<PagedResultDto<CrudEndpointDto>> GenerateCrudEndpointsAsync(string entityName);
|
/// <param name="operationTypes">
|
||||||
|
/// Aktif baslatilacak operasyonlarin virgulle ayrilmis listesi
|
||||||
|
/// (GetList, GetById, Create, Update, Delete). Bos birakilirsa hepsi aktif olur.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="objectType">
|
||||||
|
/// Veritabani nesnesinin turu (table, view, procedure, function). Tablo disindaki
|
||||||
|
/// nesneler icin yalnizca okuma endpoint'leri (GetList, GetById) uretilir.
|
||||||
|
/// </param>
|
||||||
|
Task<PagedResultDto<CrudEndpointDto>> GenerateCrudEndpointsAsync(string entityName, string operationTypes = null, string objectType = null);
|
||||||
|
Task DeleteByEntityAsync(string entityName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Volo.Abp.Application.Dtos;
|
using Volo.Abp.Application.Dtos;
|
||||||
using Volo.Abp.Application.Services;
|
using Volo.Abp.Application.Services;
|
||||||
|
|
||||||
|
|
@ -12,7 +10,4 @@ public interface ICustomComponentAppService : ICrudAppService<
|
||||||
PagedAndSortedResultRequestDto,
|
PagedAndSortedResultRequestDto,
|
||||||
CreateUpdateCustomComponentDto>
|
CreateUpdateCustomComponentDto>
|
||||||
{
|
{
|
||||||
Task<List<CustomComponentDto>> GetActiveComponentsAsync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
using Volo.Abp.Content;
|
using Volo.Abp.Content;
|
||||||
|
|
||||||
namespace Sozsoft.Platform.FileManagement;
|
namespace Sozsoft.Platform.FileManagement;
|
||||||
|
|
@ -11,7 +11,7 @@ public class CreateFolderDto
|
||||||
[Required]
|
[Required]
|
||||||
[StringLength(255)]
|
[StringLength(255)]
|
||||||
public string Name { get; set; } = string.Empty;
|
public string Name { get; set; } = string.Empty;
|
||||||
|
|
||||||
public string? ParentId { get; set; }
|
public string? ParentId { get; set; }
|
||||||
|
|
||||||
public string? TenantId { get; set; }
|
public string? TenantId { get; set; }
|
||||||
|
|
@ -37,11 +37,11 @@ public class UploadFileDto
|
||||||
{
|
{
|
||||||
[Required]
|
[Required]
|
||||||
public string FileName { get; set; } = string.Empty;
|
public string FileName { get; set; } = string.Empty;
|
||||||
|
|
||||||
public string? ParentId { get; set; }
|
public string? ParentId { get; set; }
|
||||||
|
|
||||||
public string? TenantId { get; set; }
|
public string? TenantId { get; set; }
|
||||||
|
|
||||||
// NoteModal pattern - Files array
|
// NoteModal pattern - Files array
|
||||||
public IRemoteStreamContent[]? Files { get; set; }
|
public IRemoteStreamContent[]? Files { get; set; }
|
||||||
}
|
}
|
||||||
|
|
@ -50,7 +50,7 @@ public class SearchFilesDto
|
||||||
{
|
{
|
||||||
[Required]
|
[Required]
|
||||||
public string Query { get; set; } = string.Empty;
|
public string Query { get; set; } = string.Empty;
|
||||||
|
|
||||||
public string? ParentId { get; set; }
|
public string? ParentId { get; set; }
|
||||||
|
|
||||||
public string? TenantId { get; set; }
|
public string? TenantId { get; set; }
|
||||||
|
|
@ -64,11 +64,11 @@ public class BulkDeleteDto
|
||||||
public string? TenantId { get; set; }
|
public string? TenantId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CopyItemsDto
|
public class CopyItemsDto
|
||||||
{
|
{
|
||||||
[Required]
|
[Required]
|
||||||
public List<string> ItemIds { get; set; } = new();
|
public List<string> ItemIds { get; set; } = new();
|
||||||
|
|
||||||
public string? TargetFolderId { get; set; }
|
public string? TargetFolderId { get; set; }
|
||||||
|
|
||||||
public string? TenantId { get; set; }
|
public string? TenantId { get; set; }
|
||||||
|
|
@ -78,7 +78,7 @@ public class MoveItemsDto
|
||||||
{
|
{
|
||||||
[Required]
|
[Required]
|
||||||
public List<string> ItemIds { get; set; } = new();
|
public List<string> ItemIds { get; set; } = new();
|
||||||
|
|
||||||
public string? TargetFolderId { get; set; }
|
public string? TargetFolderId { get; set; }
|
||||||
|
|
||||||
public string? TenantId { get; set; }
|
public string? TenantId { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -44,34 +44,6 @@ public class ForumCategoryDto : FullAuditedEntityDto<Guid>
|
||||||
public Guid? LastPostUserId { get; set; }
|
public Guid? LastPostUserId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CreateForumCategoryDto
|
|
||||||
{
|
|
||||||
public Guid? TenantId { get; set; }
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
[StringLength(100)]
|
|
||||||
public string Name { get; set; }
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
[StringLength(100)]
|
|
||||||
public string Slug { get; set; }
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
[StringLength(500)]
|
|
||||||
public string Description { get; set; }
|
|
||||||
|
|
||||||
[StringLength(10)]
|
|
||||||
public string Icon { get; set; }
|
|
||||||
|
|
||||||
public int DisplayOrder { get; set; }
|
|
||||||
public bool IsActive { get; set; } = true;
|
|
||||||
public bool IsLocked { get; set; } = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class UpdateForumCategoryDto : CreateForumCategoryDto
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public class GetCategoriesInput : PagedAndSortedResultRequestDto
|
public class GetCategoriesInput : PagedAndSortedResultRequestDto
|
||||||
{
|
{
|
||||||
public bool? IsActive { get; set; }
|
public bool? IsActive { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,6 @@ public interface IForumAppService : IApplicationService
|
||||||
Task<PagedResultDto<ForumCategoryDto>> GetCategoriesAsync(GetCategoriesInput input);
|
Task<PagedResultDto<ForumCategoryDto>> GetCategoriesAsync(GetCategoriesInput input);
|
||||||
Task<ForumCategoryDto> GetCategoryAsync(Guid id);
|
Task<ForumCategoryDto> GetCategoryAsync(Guid id);
|
||||||
Task<ForumCategoryDto> GetCategoryBySlugAsync(string slug);
|
Task<ForumCategoryDto> GetCategoryBySlugAsync(string slug);
|
||||||
Task<ForumCategoryDto> CreateCategoryAsync(CreateForumCategoryDto input);
|
|
||||||
Task<ForumCategoryDto> UpdateCategoryAsync(Guid id, UpdateForumCategoryDto input);
|
|
||||||
Task DeleteCategoryAsync(Guid id);
|
|
||||||
|
|
||||||
// Topics
|
// Topics
|
||||||
Task<PagedResultDto<ForumTopicDto>> GetTopicsAsync(GetTopicsInput input);
|
Task<PagedResultDto<ForumTopicDto>> GetTopicsAsync(GetTopicsInput input);
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ namespace Sozsoft.Platform.Identity.Dto;
|
||||||
|
|
||||||
public class AssignedBranchViewModel
|
public class AssignedBranchViewModel
|
||||||
{
|
{
|
||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
|
||||||
public bool IsAssigned { get; set; }
|
public bool IsAssigned { get; set; }
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue