Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8f6d084b4d | |||
| 47abff2550 | |||
| ffe661b4d2 | |||
| 73ff0ca5b6 | |||
| 7949e9b6a3 |
47
.claude/commands/koniec-pracy.md
Normal file
47
.claude/commands/koniec-pracy.md
Normal file
@@ -0,0 +1,47 @@
|
||||
Wykonaj procedurę zakończenia pracy w projekcie cmsPRO. Wszystkie kroki wykonuj kolejno:
|
||||
|
||||
## 1. Testy
|
||||
Uruchom `php vendor/bin/phpunit`. Jeśli testy nie przechodzą — napraw błędy przed kontynuowaniem.
|
||||
|
||||
## 2. Dokumentacja
|
||||
Sprawdź czy zmiany wymagają aktualizacji:
|
||||
- `docs/PROJECT_STRUCTURE.md` — struktura projektu, moduły, fazy refaktoryzacji
|
||||
- `docs/FORM_EDIT_SYSTEM.md` — system formularzy (tylko jeśli zmiany dotyczyły formularzy)
|
||||
|
||||
Zaktualizuj tylko jeśli zmiany tego wymagają. Nie aktualizuj na siłę.
|
||||
|
||||
## 3. Migracje SQL
|
||||
Jeśli były zmiany w bazie danych:
|
||||
- Utwórz plik `migrations/{version}.sql` (np. `migrations/1.694.sql`)
|
||||
- NIE w `updates/` — build script sam wczyta z `migrations/`
|
||||
|
||||
## 4. Commit
|
||||
Wykonaj git commit ze zmianami. Użyj konwencji z tego repo (patrz `git log --oneline -5`).
|
||||
|
||||
## 5. Paczka aktualizacji
|
||||
Procedura budowania paczki:
|
||||
|
||||
a) Znajdź aktualną wersję w `updates/versions.php` (`$current_ver = XXXX`)
|
||||
b) Oblicz nową wersję: `current_ver + 1`
|
||||
c) Zaktualizuj `$current_ver` w `updates/versions.php` na nową wartość
|
||||
d) Utwórz commit: `build(update): paczka {wersja} — {krótki opis zmian}`
|
||||
e) Utwórz git tag: `git tag v{wersja}` (format: v1.694, v1.695, ...)
|
||||
f) Uruchom build script:
|
||||
```
|
||||
powershell -ExecutionPolicy Bypass -File ./build-update.ps1 -FromTag v{poprzednia_wersja} -ToTag v{nowa_wersja} -ChangelogEntry "NEW - {opis zmian}"
|
||||
```
|
||||
g) Dodaj pliki paczki do ostatniego commita: `git add updates/*/ver_{wersja}.* && git commit --amend`
|
||||
|
||||
## 6. Push
|
||||
Wykonaj `git push && git push --tags`. Jeśli auth fail (próbuj 3 razy, czasem jest błąd za pierwszym razem) — poinformuj użytkownika żeby uruchomił `! git push && git push --tags`.
|
||||
|
||||
## Podsumowanie
|
||||
Na koniec wyświetl tabelkę:
|
||||
| Krok | Status |
|
||||
|------|--------|
|
||||
| Testy | OK/FAIL |
|
||||
| Dokumentacja | Zaktualizowana / Bez zmian |
|
||||
| Migracje SQL | Utworzone / Nie dotyczy |
|
||||
| Commit | hash |
|
||||
| Paczka | ver_X.XXX.zip |
|
||||
| Push | OK / Wymaga auth |
|
||||
@@ -48,7 +48,8 @@
|
||||
"Bash(python3:*)",
|
||||
"Bash(python:*)",
|
||||
"Bash(grep:*)",
|
||||
"Bash(grep ^<b>ver:*)"
|
||||
"Bash(grep ^<b>ver:*)",
|
||||
"Skill(paul:plan)"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,10 +7,11 @@ Autorski system CMS z panelem administracyjnym (17 modułów admin, 13 modułów
|
||||
Autorski system CMS umożliwiający zarządzanie treściami i stronami internetowymi.
|
||||
|
||||
## Already Completed
|
||||
- Domain (6 repos): Articles, Languages, Layouts, Pages, Settings, User
|
||||
- Shared (5 modules): Cache, Helpers, Html, Image, Tpl
|
||||
- Domain (10 repos): Articles, Languages, Layouts, Pages, Settings, User, Scontainers, Banners, Authors, Newsletter
|
||||
- Shared (7 modules): Cache, Helpers, Html, Image, Tpl, Email, Security
|
||||
- Form Edit System: FormEditViewModel, multi-tab, validation, persistence
|
||||
- PHPUnit base: Bootstrap, 3 test files
|
||||
- Wrapper delegation pattern: legacy factories delegate to Domain repositories
|
||||
|
||||
## Requirements
|
||||
|
||||
@@ -45,3 +46,4 @@ Autorski system CMS umożliwiający zarządzanie treściami i stronami interneto
|
||||
|
||||
---
|
||||
*Created: 2026-04-04*
|
||||
*Last updated: 2026-04-04 after Phase 4*
|
||||
|
||||
@@ -6,7 +6,7 @@ Pełna refaktoryzacja cmsPRO do architektury DDD wzorowanej na shopPRO. Wzorzec:
|
||||
## Current Milestone
|
||||
**v0.1 Refaktoryzacja** (v0.1.0)
|
||||
Status: In progress
|
||||
Phases: 2 of 19 complete
|
||||
Phases: 4 of 19 complete
|
||||
|
||||
## Already Completed (before PAUL)
|
||||
- **Domain (6 repos):** Articles, Languages, Layouts, Pages, Settings, User
|
||||
@@ -20,8 +20,8 @@ Phases: 2 of 19 complete
|
||||
|-------|------|-------|--------|-----------|
|
||||
| 1 | Infrastructure & Autoloader | 1 | Complete | 2026-04-04 |
|
||||
| 2 | Shared: Email + Security | 1 | Complete | 2026-04-04 |
|
||||
| 3 | Domain: Scontainers + Banners | 1 | Not started | - |
|
||||
| 4 | Domain: Authors + Newsletter | 1 | Not started | - |
|
||||
| 3 | Domain: Scontainers + Banners | 1 | Complete | 2026-04-04 |
|
||||
| 4 | Domain: Authors + Newsletter | 1 | Complete | 2026-04-04 |
|
||||
| 5 | Domain: SeoAdditional + Cron + Releases | 1 | Not started | - |
|
||||
| 6 | Admin: Base Infrastructure | 1 | Not started | - |
|
||||
| 7 | Admin: Articles + ArticlesArchive | 1 | Not started | - |
|
||||
|
||||
@@ -5,18 +5,18 @@
|
||||
See: .paul/PROJECT.md (updated 2026-04-04)
|
||||
|
||||
**Core value:** Autorski system CMS umożliwiający zarządzanie treściami i stronami internetowymi.
|
||||
**Current focus:** Phase 2 complete — ready for Phase 3
|
||||
**Current focus:** Phase 4 complete — ready for Phase 5
|
||||
|
||||
## Current Position
|
||||
|
||||
Milestone: v0.1 Refaktoryzacja
|
||||
Phase: 2 of 19 (Shared: Email + Security) — Complete
|
||||
Plan: 02-01 complete
|
||||
Phase: 4 of 19 (Domain: Authors + Newsletter) — Complete
|
||||
Plan: 04-01 complete
|
||||
Status: Loop closed, ready for next PLAN
|
||||
Last activity: 2026-04-04 — Phase 2 complete, UNIFY done
|
||||
Last activity: 2026-04-04 — Phase 4 complete, UNIFY done
|
||||
|
||||
Progress:
|
||||
- Milestone: [▓░░░░░░░░░] 10%
|
||||
- Milestone: [▓▓░░░░░░░░] 20%
|
||||
|
||||
## Loop Position
|
||||
|
||||
@@ -29,8 +29,8 @@ PLAN ──▶ APPLY ──▶ UNIFY
|
||||
## Performance Metrics
|
||||
|
||||
**Velocity:**
|
||||
- Total plans completed: 2
|
||||
- Total execution time: ~18min
|
||||
- Total plans completed: 4
|
||||
- Total execution time: ~22min
|
||||
|
||||
**By Phase:**
|
||||
|
||||
@@ -38,6 +38,8 @@ PLAN ──▶ APPLY ──▶ UNIFY
|
||||
|-------|-------|------------|----------|
|
||||
| 01-infrastructure | 1/1 | ~10min | ~10min |
|
||||
| 02-shared-email-security | 1/1 | ~8min | ~8min |
|
||||
| 03-domain-scontainers-banners | 1/1 | ~2min | ~2min |
|
||||
| 04-domain-authors-newsletter | 1/1 | ~2min | ~2min |
|
||||
|
||||
## Accumulated Context
|
||||
|
||||
@@ -46,6 +48,10 @@ PLAN ──▶ APPLY ──▶ UNIFY
|
||||
- CsrfToken: single token per session (shopPRO pattern)
|
||||
- Email: PHPMailer require via __DIR__ absolute paths
|
||||
- Shared layer kompletny: Cache, Helpers, Html, Image, Tpl, Email, Security
|
||||
- Wrapper delegation: factory creates new repo per call (no singleton)
|
||||
- Front repos: $lang[0] passed explicitly, repos don't use globals
|
||||
- Front caching: migrated from \Cache:: to \Shared\Cache\CacheHandler::
|
||||
- Newsletter: globals ($settings, $lang) passed as explicit params to repo methods
|
||||
|
||||
### Deferred Issues
|
||||
None.
|
||||
@@ -56,9 +62,9 @@ None.
|
||||
## Session Continuity
|
||||
|
||||
Last session: 2026-04-04
|
||||
Stopped at: Phase 2 complete, loop closed
|
||||
Next action: Run /paul:plan for Phase 3 (Domain: Scontainers + Banners)
|
||||
Resume file: .paul/phases/02-shared-email-security/02-01-SUMMARY.md
|
||||
Stopped at: Phase 4 complete, loop closed
|
||||
Next action: Run /paul:plan for Phase 5 (Domain: SeoAdditional + Cron + Releases)
|
||||
Resume file: .paul/phases/04-domain-authors-newsletter/04-01-SUMMARY.md
|
||||
|
||||
---
|
||||
*STATE.md — Updated after every significant action*
|
||||
|
||||
198
.paul/phases/03-domain-scontainers-banners/03-01-PLAN.md
Normal file
198
.paul/phases/03-domain-scontainers-banners/03-01-PLAN.md
Normal file
@@ -0,0 +1,198 @@
|
||||
---
|
||||
phase: 03-domain-scontainers-banners
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- autoload/Domain/Scontainers/ScontainersRepository.php
|
||||
- autoload/Domain/Banners/BannersRepository.php
|
||||
- autoload/admin/factory/class.Scontainers.php
|
||||
- autoload/admin/factory/class.Banners.php
|
||||
- autoload/front/factory/class.Scontainers.php
|
||||
- autoload/front/factory/class.Banners.php
|
||||
autonomous: true
|
||||
delegation: auto
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
Create Domain\Scontainers\ScontainersRepository and Domain\Banners\BannersRepository, then convert legacy factory classes to wrapper delegation.
|
||||
|
||||
## Purpose
|
||||
Continue DDD refactoring — migrate Scontainers and Banners data access from static factory methods (global $mdb) to injected-dependency Domain repositories. Establishes wrapper delegation pattern for the first time in the project.
|
||||
|
||||
## Output
|
||||
- 2 new Domain repository files
|
||||
- 4 legacy factory files converted to wrappers
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
## Project Context
|
||||
@.paul/PROJECT.md
|
||||
@.paul/ROADMAP.md
|
||||
@.paul/STATE.md
|
||||
|
||||
## Source Files
|
||||
@autoload/admin/factory/class.Scontainers.php
|
||||
@autoload/admin/factory/class.Banners.php
|
||||
@autoload/front/factory/class.Scontainers.php
|
||||
@autoload/front/factory/class.Banners.php
|
||||
@autoload/Domain/Languages/LanguagesRepository.php (pattern reference)
|
||||
</context>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: ScontainersRepository exists with all methods
|
||||
```gherkin
|
||||
Given the autoloader is configured for Domain\ namespace
|
||||
When ScontainersRepository is instantiated with $db (Medoo)
|
||||
Then it provides containerDetails(), containerSave(), containerDelete(), scontainerByLang() methods
|
||||
And all methods use $this->db instead of global $mdb
|
||||
```
|
||||
|
||||
## AC-2: BannersRepository exists with all methods
|
||||
```gherkin
|
||||
Given the autoloader is configured for Domain\ namespace
|
||||
When BannersRepository is instantiated with $db (Medoo)
|
||||
Then it provides bannerDetails(), bannerSave(), bannerDelete(), activeBanners(), mainBanner() methods
|
||||
And all methods use $this->db instead of global $mdb
|
||||
```
|
||||
|
||||
## AC-3: Legacy admin factories delegate to repositories
|
||||
```gherkin
|
||||
Given admin\factory\Scontainers and admin\factory\Banners exist
|
||||
When their static methods are called (e.g. container_save(), banner_delete())
|
||||
Then they instantiate the Domain repository with global $mdb
|
||||
And delegate the call to the corresponding repository method
|
||||
And return the same result as before
|
||||
```
|
||||
|
||||
## AC-4: Legacy front factories delegate to repositories
|
||||
```gherkin
|
||||
Given front\factory\Scontainers and front\factory\Banners exist
|
||||
When their static methods are called (e.g. scontainer_details(), banners())
|
||||
Then they delegate to the Domain repository
|
||||
And caching behavior is preserved (Cache::fetch/store in repository)
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Create ScontainersRepository and BannersRepository</name>
|
||||
<files>autoload/Domain/Scontainers/ScontainersRepository.php, autoload/Domain/Banners/BannersRepository.php</files>
|
||||
<action>
|
||||
Create Domain\Scontainers\ScontainersRepository following LanguagesRepository pattern:
|
||||
- namespace Domain\Scontainers
|
||||
- Constructor: __construct($db) storing Medoo instance
|
||||
- containerDetails($containerId): get from pp_scontainers + pp_scontainers_langs (all langs)
|
||||
- containerSave($containerId, $title, $text, $status, $showTitle, $src, $html): insert/update pp_scontainers + pp_scontainers_langs with multi-language support. Handle single-lang vs multi-lang arrays exactly as current factory does. Call \S::delete_cache() after.
|
||||
- containerDelete($containerId): delete from pp_scontainers, call \S::delete_cache()
|
||||
- scontainerByLang($scontainerId, $langId): get container + single lang translation, use \Shared\Cache\CacheHandler::fetch/store (migrate from \Cache:: to \Shared\Cache\CacheHandler::)
|
||||
|
||||
Create Domain\Banners\BannersRepository following same pattern:
|
||||
- namespace Domain\Banners
|
||||
- Constructor: __construct($db)
|
||||
- bannerDetails($bannerId): get from pp_banners + pp_banners_langs (all langs)
|
||||
- bannerSave($bannerId, $name, $status, $dateStart, $dateEnd, $homePage, $src, $url, $html, $text): insert/update pp_banners + pp_banners_langs. Handle single/multi lang arrays. Call \S::delete_cache().
|
||||
- bannerDelete($bannerId): delete from pp_banners, call \S::delete_cache()
|
||||
- activeBanners($langId): active non-homepage banners with date filtering, use \Shared\Cache\CacheHandler for caching
|
||||
- mainBanner($langId): single active homepage banner with date filtering, cached
|
||||
|
||||
IMPORTANT:
|
||||
- PHP < 8.0 compatible (no match, no named args, no union types, no str_contains)
|
||||
- Use $this->db->query() for complex SQL (date filtering in Banners) — keep raw SQL identical to current factory
|
||||
- Multi-language save pattern: query pp_langs for active languages, loop and insert translations
|
||||
- Status/checkbox conversion ('on' → 1, else 0) stays in repository methods
|
||||
</action>
|
||||
<verify>php -l autoload/Domain/Scontainers/ScontainersRepository.php && php -l autoload/Domain/Banners/BannersRepository.php</verify>
|
||||
<done>AC-1 and AC-2 satisfied: Both repositories exist with all methods, use injected $db</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Convert legacy factories to wrapper delegation</name>
|
||||
<files>autoload/admin/factory/class.Scontainers.php, autoload/admin/factory/class.Banners.php, autoload/front/factory/class.Scontainers.php, autoload/front/factory/class.Banners.php</files>
|
||||
<action>
|
||||
Convert all 4 factory files to thin wrappers that delegate to Domain repositories.
|
||||
|
||||
Pattern for each static method:
|
||||
```php
|
||||
public static function method_name($args)
|
||||
{
|
||||
global $mdb;
|
||||
$repo = new \Domain\Scontainers\ScontainersRepository($mdb);
|
||||
return $repo->methodName($args);
|
||||
}
|
||||
```
|
||||
|
||||
admin\factory\Scontainers:
|
||||
- container_delete($id) → $repo->containerDelete($id)
|
||||
- container_save(...) → $repo->containerSave(...)
|
||||
- container_details($id) → $repo->containerDetails($id)
|
||||
|
||||
admin\factory\Banners:
|
||||
- banner_delete($id) → $repo->bannerDelete($id)
|
||||
- banner_save(...) → $repo->bannerSave(...)
|
||||
- banner_details($id) → $repo->bannerDetails($id)
|
||||
|
||||
front\factory\Scontainers:
|
||||
- scontainer_details($id) → $repo->scontainerByLang($id, $lang[0]) — note: use global $lang
|
||||
|
||||
front\factory\Banners:
|
||||
- banners() → $repo->activeBanners($lang[0])
|
||||
- main_banner() → $repo->mainBanner($lang[0])
|
||||
|
||||
IMPORTANT:
|
||||
- Keep namespace declarations unchanged (admin\factory, front\factory)
|
||||
- Keep method signatures identical (same parameter names and order)
|
||||
- For front factories: pass $lang[0] explicitly to repository (repo does NOT use global $lang)
|
||||
</action>
|
||||
<verify>php -l autoload/admin/factory/class.Scontainers.php && php -l autoload/admin/factory/class.Banners.php && php -l autoload/front/factory/class.Scontainers.php && php -l autoload/front/factory/class.Banners.php</verify>
|
||||
<done>AC-3 and AC-4 satisfied: All legacy factories delegate to Domain repositories, signatures unchanged</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- autoload/autoloader.php (autoloader stable)
|
||||
- composer.json (PSR-4 mapping already includes Domain\)
|
||||
- autoload/admin/controls/class.Scontainers.php (admin controllers — Phase 10)
|
||||
- autoload/admin/controls/class.Banners.php (admin controllers — Phase 10)
|
||||
- autoload/admin/view/ (admin views — later phases)
|
||||
- autoload/front/view/ (front views — later phases)
|
||||
- autoload/class.Scontainer.php (legacy ArrayAccess entity — separate concern)
|
||||
- Any existing Domain\ repositories (Articles, Languages, Layouts, Pages, Settings, User)
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Only factory → repository migration, NOT admin controllers or views
|
||||
- No new Composer dependencies
|
||||
- No database schema changes
|
||||
- Do not refactor the multi-language save pattern (keep it working as-is)
|
||||
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
Before declaring plan complete:
|
||||
- [ ] php -l passes for all 6 files (2 new + 4 modified)
|
||||
- [ ] ScontainersRepository has: containerDetails, containerSave, containerDelete, scontainerByLang
|
||||
- [ ] BannersRepository has: bannerDetails, bannerSave, bannerDelete, activeBanners, mainBanner
|
||||
- [ ] All 4 factory files are thin wrappers (no direct $mdb usage, only delegation)
|
||||
- [ ] No PHP 8.0+ syntax used
|
||||
- [ ] \S::delete_cache() calls preserved in repository methods
|
||||
- [ ] Caching (\Shared\Cache\CacheHandler) used in front-facing repository methods
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- All tasks completed
|
||||
- All verification checks pass
|
||||
- Zero regression — factory method signatures unchanged
|
||||
- Domain repositories follow established pattern (constructor DI, $this->db)
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.paul/phases/03-domain-scontainers-banners/03-01-SUMMARY.md`
|
||||
</output>
|
||||
115
.paul/phases/03-domain-scontainers-banners/03-01-SUMMARY.md
Normal file
115
.paul/phases/03-domain-scontainers-banners/03-01-SUMMARY.md
Normal file
@@ -0,0 +1,115 @@
|
||||
---
|
||||
phase: 03-domain-scontainers-banners
|
||||
plan: 01
|
||||
subsystem: domain
|
||||
tags: [medoo, repository, scontainers, banners, wrapper-delegation]
|
||||
|
||||
requires:
|
||||
- phase: 01-infrastructure
|
||||
provides: PSR-4 autoloader for Domain\ namespace
|
||||
|
||||
provides:
|
||||
- Domain\Scontainers\ScontainersRepository
|
||||
- Domain\Banners\BannersRepository
|
||||
- Wrapper delegation pattern (first usage in project)
|
||||
|
||||
affects: [phase-10-admin-banners-authors-scontainers, phase-15-front-pages-menu-banners-scontainers]
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns: [wrapper-delegation, domain-repository-with-cache]
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- autoload/Domain/Scontainers/ScontainersRepository.php
|
||||
- autoload/Domain/Banners/BannersRepository.php
|
||||
modified:
|
||||
- autoload/admin/factory/class.Scontainers.php
|
||||
- autoload/admin/factory/class.Banners.php
|
||||
- autoload/front/factory/class.Scontainers.php
|
||||
- autoload/front/factory/class.Banners.php
|
||||
|
||||
key-decisions:
|
||||
- "Wrapper delegation pattern: factory static methods delegate to repo instances via global $mdb"
|
||||
- "Front factories pass $lang[0] explicitly — repositories do not use global $lang"
|
||||
- "Caching migrated from \\Cache:: to \\Shared\\Cache\\CacheHandler:: in repository layer"
|
||||
|
||||
patterns-established:
|
||||
- "Wrapper delegation: global $mdb; $repo = new \\Domain\\X\\XRepository($mdb); return $repo->method()"
|
||||
- "Front factory passes language ID explicitly to repository"
|
||||
|
||||
duration: ~2min
|
||||
started: 2026-04-04T00:00:00Z
|
||||
completed: 2026-04-04T00:00:00Z
|
||||
---
|
||||
|
||||
# Phase 3 Plan 01: Scontainers + Banners Repositories Summary
|
||||
|
||||
**Domain repositories for Scontainers and Banners with wrapper delegation in all 4 legacy factories.**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~2min |
|
||||
| Tasks | 2 completed (delegated) |
|
||||
| Files created | 2 |
|
||||
| Files modified | 4 |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: ScontainersRepository exists with all methods | Pass | 5 methods (incl. constructor), 110 lines |
|
||||
| AC-2: BannersRepository exists with all methods | Pass | 6 methods (incl. constructor), 148 lines |
|
||||
| AC-3: Legacy admin factories delegate to repositories | Pass | 6 static methods → thin wrappers |
|
||||
| AC-4: Legacy front factories delegate to repositories | Pass | 3 static methods → thin wrappers, $lang[0] passed explicitly |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- Created ScontainersRepository with containerDetails, containerSave, containerDelete, scontainerByLang
|
||||
- Created BannersRepository with bannerDetails, bannerSave, bannerDelete, activeBanners, mainBanner
|
||||
- Established wrapper delegation pattern — first usage in the project, template for all future phases
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `autoload/Domain/Scontainers/ScontainersRepository.php` | Created | Domain repository for scontainers CRUD + cached front read |
|
||||
| `autoload/Domain/Banners/BannersRepository.php` | Created | Domain repository for banners CRUD + cached active/main banner |
|
||||
| `autoload/admin/factory/class.Scontainers.php` | Modified | Wrapper: 3 methods delegate to ScontainersRepository |
|
||||
| `autoload/admin/factory/class.Banners.php` | Modified | Wrapper: 3 methods delegate to BannersRepository |
|
||||
| `autoload/front/factory/class.Scontainers.php` | Modified | Wrapper: 1 method delegates with $lang[0] |
|
||||
| `autoload/front/factory/class.Banners.php` | Modified | Wrapper: 2 methods delegate with $lang[0] |
|
||||
|
||||
## Decisions Made
|
||||
|
||||
| Decision | Rationale | Impact |
|
||||
|----------|-----------|--------|
|
||||
| Wrapper creates new repo instance per call | Matches static factory pattern, no singleton needed | Simple, no state leaks between calls |
|
||||
| Front repos use CacheHandler, not \Cache | Aligns with Shared layer conventions | Consistent caching across Domain layer |
|
||||
| $lang[0] passed as parameter, not global in repo | Repositories should not depend on globals | Cleaner, testable API |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
None — plan executed exactly as written.
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
None.
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:**
|
||||
- Domain\Scontainers and Domain\Banners available for Admin controllers (Phase 10)
|
||||
- Wrapper delegation pattern established for future Domain phases (4, 5)
|
||||
|
||||
**Concerns:**
|
||||
- None
|
||||
|
||||
**Blockers:**
|
||||
- None
|
||||
|
||||
---
|
||||
*Phase: 03-domain-scontainers-banners, Plan: 01*
|
||||
*Completed: 2026-04-04*
|
||||
216
.paul/phases/04-domain-authors-newsletter/04-01-PLAN.md
Normal file
216
.paul/phases/04-domain-authors-newsletter/04-01-PLAN.md
Normal file
@@ -0,0 +1,216 @@
|
||||
---
|
||||
phase: 04-domain-authors-newsletter
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- autoload/Domain/Authors/AuthorsRepository.php
|
||||
- autoload/Domain/Newsletter/NewsletterRepository.php
|
||||
- autoload/admin/factory/class.Authors.php
|
||||
- autoload/admin/factory/class.Newsletter.php
|
||||
- autoload/front/factory/class.Authors.php
|
||||
- autoload/front/factory/class.Newsletter.php
|
||||
autonomous: true
|
||||
delegation: auto
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
Create Domain\Authors\AuthorsRepository and Domain\Newsletter\NewsletterRepository, then convert legacy factory classes to wrapper delegation.
|
||||
|
||||
## Purpose
|
||||
Continue DDD refactoring — migrate Authors and Newsletter data access to Domain repositories using established wrapper delegation pattern from Phase 3.
|
||||
|
||||
## Output
|
||||
- 2 new Domain repository files
|
||||
- 4 legacy factory files converted to wrappers
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
## Project Context
|
||||
@.paul/PROJECT.md
|
||||
@.paul/ROADMAP.md
|
||||
@.paul/STATE.md
|
||||
|
||||
## Prior Work
|
||||
@.paul/phases/03-domain-scontainers-banners/03-01-SUMMARY.md (wrapper delegation pattern reference)
|
||||
|
||||
## Source Files
|
||||
@autoload/admin/factory/class.Authors.php
|
||||
@autoload/admin/factory/class.Newsletter.php
|
||||
@autoload/front/factory/class.Authors.php
|
||||
@autoload/front/factory/class.Newsletter.php
|
||||
@autoload/Domain/Languages/LanguagesRepository.php (pattern reference)
|
||||
</context>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: AuthorsRepository exists with all methods
|
||||
```gherkin
|
||||
Given the autoloader is configured for Domain\ namespace
|
||||
When AuthorsRepository is instantiated with $db (Medoo)
|
||||
Then it provides simpleList(), authorDetails(), authorSave(), authorDelete(), authorByLang() methods
|
||||
And all methods use $this->db instead of global $mdb
|
||||
```
|
||||
|
||||
## AC-2: NewsletterRepository exists with all methods
|
||||
```gherkin
|
||||
Given the autoloader is configured for Domain\ namespace
|
||||
When NewsletterRepository is instantiated with $db (Medoo)
|
||||
Then it provides emailsImport(), isAdminTemplate(), templateDelete(), send(), templateDetails(), templateSave(), templatesList(), unsubscribe(), confirm(), newsletterSend(), getHash(), signin(), getTemplate(), signout() methods
|
||||
And all methods use $this->db instead of global $mdb
|
||||
```
|
||||
|
||||
## AC-3: Legacy admin factories delegate to repositories
|
||||
```gherkin
|
||||
Given admin\factory\Authors and admin\factory\Newsletter exist
|
||||
When their static methods are called
|
||||
Then they instantiate the Domain repository with global $mdb
|
||||
And delegate the call to the corresponding repository method
|
||||
And return the same result as before
|
||||
```
|
||||
|
||||
## AC-4: Legacy front factories delegate to repositories
|
||||
```gherkin
|
||||
Given front\factory\Authors and front\factory\Newsletter exist
|
||||
When their static methods are called
|
||||
Then they delegate to the Domain repository
|
||||
And caching behavior is preserved (in repository for Authors)
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Create AuthorsRepository and NewsletterRepository</name>
|
||||
<files>autoload/Domain/Authors/AuthorsRepository.php, autoload/Domain/Newsletter/NewsletterRepository.php</files>
|
||||
<action>
|
||||
Create Domain\Authors\AuthorsRepository following established pattern:
|
||||
- namespace Domain\Authors
|
||||
- Constructor: __construct($db) storing Medoo instance
|
||||
- simpleList(): select from pp_authors, return array (from admin get_simple_list)
|
||||
- authorDetails($authorId): get from pp_authors + select pp_authors_langs, return with ['languages'][$lang_id] sub-array
|
||||
- authorSave($authorId, $author, $image, $description): insert/update pp_authors + pp_authors_langs with multi-language support. Same pattern as ScontainersRepository: query pp_langs for active languages, handle single vs multi lang arrays. Call \S::delete_cache() after.
|
||||
- authorDelete($authorId): delete from pp_authors, call \S::delete_cache(), return result
|
||||
- authorByLang($authorId, $langId): cached read using \Shared\Cache\CacheHandler::fetch("get_single_author:$authorId"). Get from pp_authors + pp_authors_langs for specific lang. Cache and return. Note: cache key does NOT include langId (matching original front factory).
|
||||
|
||||
Create Domain\Newsletter\NewsletterRepository following same pattern:
|
||||
- namespace Domain\Newsletter
|
||||
- Constructor: __construct($db)
|
||||
- emailsImport($emails): parse comma/newline separated emails, validate with filter_var, insert unique into pp_newsletter. Return count of imported.
|
||||
- isAdminTemplate($templateId): check if template exists in pp_newsletter_templates where id and admin=1. Return boolean.
|
||||
- templateDelete($templateId): delete from pp_newsletter_templates where id. Return result.
|
||||
- send($dates, $template, $onlyOnce): insert into pp_newsletter_send for each subscriber email from pp_newsletter. If $onlyOnce, check pp_newsletter_send for existing entries. Complex logic — replicate exactly from admin factory.
|
||||
- templateDetails($templateId): get single template from pp_newsletter_templates.
|
||||
- templateSave($id, $name, $text): insert/update pp_newsletter_templates. Call \S::delete_cache().
|
||||
- templatesList(): select all from pp_newsletter_templates ordered.
|
||||
- unsubscribe($hash): update pp_newsletter set status=0 where hash=$hash. Return result.
|
||||
- confirm($hash): update pp_newsletter set status=1 where hash=$hash. Return result.
|
||||
- newsletterSend($limit): select from pp_newsletter_send with limit, send emails via loop, delete sent entries. Replicate exactly from front factory.
|
||||
- getHash($email): select hash from pp_newsletter where email. Return hash or false.
|
||||
- signin($email): insert into pp_newsletter with email, hash (md5), status=0. Return result or hash.
|
||||
- getTemplate($templateName): get template from pp_newsletter_templates where name=$templateName. Return template.
|
||||
- signout($email): delete from pp_newsletter where email=$email. Return result.
|
||||
|
||||
IMPORTANT:
|
||||
- PHP < 8.0 compatible
|
||||
- Replicate logic EXACTLY from factory files — read them first
|
||||
- Multi-language save pattern same as Phase 3 repos
|
||||
- Keep all \S::delete_cache() calls where they exist in originals
|
||||
- Newsletter send() and newsletterSend() are complex — read carefully and replicate precisely
|
||||
</action>
|
||||
<verify>php -l autoload/Domain/Authors/AuthorsRepository.php && php -l autoload/Domain/Newsletter/NewsletterRepository.php</verify>
|
||||
<done>AC-1 and AC-2 satisfied: Both repositories exist with all methods, use injected $db</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Convert legacy factories to wrapper delegation</name>
|
||||
<files>autoload/admin/factory/class.Authors.php, autoload/admin/factory/class.Newsletter.php, autoload/front/factory/class.Authors.php, autoload/front/factory/class.Newsletter.php</files>
|
||||
<action>
|
||||
Convert all 4 factory files to thin wrappers using Phase 3 pattern:
|
||||
```php
|
||||
public static function method_name($args)
|
||||
{
|
||||
global $mdb;
|
||||
$repo = new \Domain\Authors\AuthorsRepository($mdb);
|
||||
return $repo->methodName($args);
|
||||
}
|
||||
```
|
||||
|
||||
admin\factory\Authors:
|
||||
- get_simple_list() → $repo->simpleList()
|
||||
- delete_author($id_author) → $repo->authorDelete($id_author)
|
||||
- save_author($id_author, $author, $image, $description) → $repo->authorSave($id_author, $author, $image, $description)
|
||||
|
||||
admin\factory\Newsletter:
|
||||
- emails_import($emails) → $repo->emailsImport($emails)
|
||||
- is_admin_template($template_id) → $repo->isAdminTemplate($template_id)
|
||||
- newsletter_template_delete($template_id) → $repo->templateDelete($template_id)
|
||||
- send($dates, $template, $only_once) → $repo->send($dates, $template, $only_once)
|
||||
- email_template_detalis($id_template) → $repo->templateDetails($id_template)
|
||||
- template_save($id, $name, $text) → $repo->templateSave($id, $name, $text)
|
||||
- templates_list() → $repo->templatesList()
|
||||
|
||||
front\factory\Authors:
|
||||
- get_single_author($id_author) → global $mdb; $repo = new \Domain\Authors\AuthorsRepository($mdb); return $repo->authorByLang($id_author, null);
|
||||
Note: front factory uses global $lang but the cache key doesn't include lang — pass null or handle in repo. Check original carefully.
|
||||
|
||||
front\factory\Newsletter:
|
||||
- newsletter_unsubscribe($hash) → $repo->unsubscribe($hash)
|
||||
- newsletter_confirm($hash) → $repo->confirm($hash)
|
||||
- newsletter_send($limit = 5) → $repo->newsletterSend($limit)
|
||||
- get_hash($email) → $repo->getHash($email)
|
||||
- newsletter_signin($email) → $repo->signin($email)
|
||||
- get_template($template_name) → $repo->getTemplate($template_name)
|
||||
- newsletter_signout($email) → $repo->signout($email)
|
||||
|
||||
IMPORTANT:
|
||||
- Keep namespaces and method signatures IDENTICAL
|
||||
- Read each file first before editing
|
||||
- Each method = thin 3-line wrapper
|
||||
</action>
|
||||
<verify>php -l autoload/admin/factory/class.Authors.php && php -l autoload/admin/factory/class.Newsletter.php && php -l autoload/front/factory/class.Authors.php && php -l autoload/front/factory/class.Newsletter.php</verify>
|
||||
<done>AC-3 and AC-4 satisfied: All legacy factories delegate to Domain repositories</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- autoload/autoloader.php
|
||||
- composer.json
|
||||
- autoload/admin/controls/ (admin controllers — later phases)
|
||||
- autoload/admin/view/ (admin views — later phases)
|
||||
- autoload/front/view/ (front views — later phases)
|
||||
- Any existing Domain\ repositories (Articles, Languages, Layouts, Pages, Settings, User, Scontainers, Banners)
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Only factory → repository migration
|
||||
- No new Composer dependencies
|
||||
- No database schema changes
|
||||
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
Before declaring plan complete:
|
||||
- [ ] php -l passes for all 6 files (2 new + 4 modified)
|
||||
- [ ] AuthorsRepository has: simpleList, authorDetails, authorSave, authorDelete, authorByLang
|
||||
- [ ] NewsletterRepository has: emailsImport, isAdminTemplate, templateDelete, send, templateDetails, templateSave, templatesList, unsubscribe, confirm, newsletterSend, getHash, signin, getTemplate, signout
|
||||
- [ ] All 4 factory files are thin wrappers (no direct $mdb usage)
|
||||
- [ ] No PHP 8.0+ syntax used
|
||||
- [ ] \S::delete_cache() calls preserved where originals had them
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- All tasks completed
|
||||
- All verification checks pass
|
||||
- Zero regression — factory method signatures unchanged
|
||||
- Domain repositories follow established pattern
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.paul/phases/04-domain-authors-newsletter/04-01-SUMMARY.md`
|
||||
</output>
|
||||
111
.paul/phases/04-domain-authors-newsletter/04-01-SUMMARY.md
Normal file
111
.paul/phases/04-domain-authors-newsletter/04-01-SUMMARY.md
Normal file
@@ -0,0 +1,111 @@
|
||||
---
|
||||
phase: 04-domain-authors-newsletter
|
||||
plan: 01
|
||||
subsystem: domain
|
||||
tags: [medoo, repository, authors, newsletter, wrapper-delegation]
|
||||
|
||||
requires:
|
||||
- phase: 01-infrastructure
|
||||
provides: PSR-4 autoloader for Domain\ namespace
|
||||
|
||||
provides:
|
||||
- Domain\Authors\AuthorsRepository
|
||||
- Domain\Newsletter\NewsletterRepository
|
||||
|
||||
affects: [phase-10-admin-banners-authors-scontainers, phase-11-admin-newsletter-emails-seoadditional]
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns: [wrapper-delegation, globals-to-parameters]
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- autoload/Domain/Authors/AuthorsRepository.php
|
||||
- autoload/Domain/Newsletter/NewsletterRepository.php
|
||||
modified:
|
||||
- autoload/admin/factory/class.Authors.php
|
||||
- autoload/admin/factory/class.Newsletter.php
|
||||
- autoload/front/factory/class.Authors.php
|
||||
- autoload/front/factory/class.Newsletter.php
|
||||
|
||||
key-decisions:
|
||||
- "Newsletter methods using global $settings/$lang now take them as explicit parameters"
|
||||
- "authorByLang cache key preserved from original (no langId in key)"
|
||||
|
||||
patterns-established:
|
||||
- "Globals-to-parameters: when repo method needs $settings or $lang, wrapper passes them explicitly"
|
||||
|
||||
duration: ~2min
|
||||
started: 2026-04-04T00:00:00Z
|
||||
completed: 2026-04-04T00:00:00Z
|
||||
---
|
||||
|
||||
# Phase 4 Plan 01: Authors + Newsletter Repositories Summary
|
||||
|
||||
**Domain repositories for Authors (5 methods) and Newsletter (14 methods) with wrapper delegation and globals-to-parameters pattern.**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~2min |
|
||||
| Tasks | 2 completed (delegated) |
|
||||
| Files created | 2 |
|
||||
| Files modified | 4 |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: AuthorsRepository exists with all methods | Pass | 5 methods, 156 lines |
|
||||
| AC-2: NewsletterRepository exists with all methods | Pass | 14 methods, 281 lines |
|
||||
| AC-3: Legacy admin factories delegate to repositories | Pass | 11 static methods → wrappers |
|
||||
| AC-4: Legacy front factories delegate to repositories | Pass | 8 static methods → wrappers, globals passed as params |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- Created AuthorsRepository with simpleList, authorDetails, authorSave, authorDelete, authorByLang
|
||||
- Created NewsletterRepository with full subscriber lifecycle + template CRUD + sending
|
||||
- Established globals-to-parameters pattern for methods needing $settings/$lang
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `autoload/Domain/Authors/AuthorsRepository.php` | Created | Domain repository for authors CRUD + cached front read |
|
||||
| `autoload/Domain/Newsletter/NewsletterRepository.php` | Created | Domain repository for newsletter subscriber lifecycle, templates, sending |
|
||||
| `autoload/admin/factory/class.Authors.php` | Modified | Wrapper: 4 methods delegate to AuthorsRepository |
|
||||
| `autoload/admin/factory/class.Newsletter.php` | Modified | Wrapper: 7 methods delegate to NewsletterRepository |
|
||||
| `autoload/front/factory/class.Authors.php` | Modified | Wrapper: 1 method delegates |
|
||||
| `autoload/front/factory/class.Newsletter.php` | Modified | Wrapper: 7 methods delegate, passing $settings/$lang |
|
||||
|
||||
## Decisions Made
|
||||
|
||||
| Decision | Rationale | Impact |
|
||||
|----------|-----------|--------|
|
||||
| Globals as parameters for newsletterSend/signin | Repos should not depend on globals | Front wrappers pass $settings, $lang explicitly |
|
||||
| Preserve original cache key for authorByLang | Backward compatibility with existing cache | Cache key "get_single_author:$id" without langId |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
None — plan executed exactly as written.
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
None.
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:**
|
||||
- Domain\Authors and Domain\Newsletter available for Admin controllers (Phases 10, 11)
|
||||
- All Domain repos for phases 3-4 complete
|
||||
|
||||
**Concerns:**
|
||||
- None
|
||||
|
||||
**Blockers:**
|
||||
- None
|
||||
|
||||
---
|
||||
*Phase: 04-domain-authors-newsletter, Plan: 01*
|
||||
*Completed: 2026-04-04*
|
||||
@@ -45,7 +45,9 @@ ignored_paths: []
|
||||
# Added on 2025-04-18
|
||||
read_only: false
|
||||
|
||||
# list of tool names to exclude. We recommend not excluding any tools, see the readme for more details.
|
||||
# list of tool names to exclude.
|
||||
# This extends the existing exclusions (e.g. from the global configuration)
|
||||
#
|
||||
# Below is the complete list of tools for convenience.
|
||||
# To make sure you have the latest list of tools, and to view their descriptions,
|
||||
# execute `uv run scripts/print_tool_overview.py`.
|
||||
@@ -86,7 +88,8 @@ read_only: false
|
||||
# * `write_memory`: Writes a named memory (for future reference) to Serena's project-specific memory store.
|
||||
excluded_tools: []
|
||||
|
||||
# list of tools to include that would otherwise be disabled (particularly optional tools that are disabled by default)
|
||||
# list of tools to include that would otherwise be disabled (particularly optional tools that are disabled by default).
|
||||
# This extends the existing inclusions (e.g. from the global configuration).
|
||||
included_optional_tools: []
|
||||
|
||||
# fixed set of tools to use as the base tool set (if non-empty), replacing Serena's default set of tools.
|
||||
@@ -112,8 +115,10 @@ default_modes:
|
||||
# (contrary to the memories, which are loaded on demand).
|
||||
initial_prompt: ""
|
||||
|
||||
# override of the corresponding setting in serena_config.yml, see the documentation there.
|
||||
# If null or missing, the value from the global config is used.
|
||||
# time budget (seconds) per tool call for the retrieval of additional symbol information
|
||||
# such as docstrings or parameter information.
|
||||
# This overrides the corresponding setting in the global configuration; see the documentation there.
|
||||
# If null or missing, use the setting from the global configuration.
|
||||
symbol_info_budget:
|
||||
|
||||
# The language backend to use for this project.
|
||||
@@ -122,3 +127,26 @@ symbol_info_budget:
|
||||
# Note: the backend is fixed at startup. If a project with a different backend
|
||||
# is activated post-init, an error will be returned.
|
||||
language_backend:
|
||||
|
||||
# line ending convention to use when writing source files.
|
||||
# Possible values: unset (use global setting), "lf", "crlf", or "native" (platform default)
|
||||
# This does not affect Serena's own files (e.g. memories and configuration files), which always use native line endings.
|
||||
line_ending:
|
||||
|
||||
# list of regex patterns which, when matched, mark a memory entry as read‑only.
|
||||
# Extends the list from the global configuration, merging the two lists.
|
||||
read_only_memory_patterns: []
|
||||
|
||||
# list of regex patterns for memories to completely ignore.
|
||||
# Matching memories will not appear in list_memories or activate_project output
|
||||
# and cannot be accessed via read_memory or write_memory.
|
||||
# To access ignored memory files, use the read_file tool on the raw file path.
|
||||
# Extends the list from the global configuration, merging the two lists.
|
||||
# Example: ["_archive/.*", "_episodes/.*"]
|
||||
ignored_memory_patterns: []
|
||||
|
||||
# advanced configuration option allowing to configure language server-specific options.
|
||||
# Maps the language key to the options.
|
||||
# Have a look at the docstring of the constructors of the LS implementations within solidlsp (e.g., for C# or PHP) to see which options are available.
|
||||
# No documentation on options means no options are available.
|
||||
ls_specific_settings: {}
|
||||
|
||||
3
.vscode/ftp-kr.json
vendored
3
.vscode/ftp-kr.json
vendored
@@ -17,6 +17,7 @@
|
||||
"/.serena",
|
||||
"/docs",
|
||||
"AGENTS.md",
|
||||
"CLAUDE.md"
|
||||
"CLAUDE.md",
|
||||
"/.paul"
|
||||
]
|
||||
}
|
||||
13
CLAUDE.md
13
CLAUDE.md
@@ -2,15 +2,4 @@
|
||||
|
||||
## KONIEC PRACY
|
||||
|
||||
Gdy użytkownik napisze `KONIEC PRACY`, wykonaj kolejno:
|
||||
|
||||
1. Przeprowadzenie testów.
|
||||
2. Aktualizacja dokumentacji technicznej, jeśli zmiany tego wymagają:
|
||||
- `docs/PROJECT_STRUCTURE.md`
|
||||
- `docs/FORM_EDIT_SYSTEM.md`
|
||||
3. Migracje SQL (jeśli były zmiany w bazie danych):
|
||||
- Plik: `migrations/{version}.sql` (np. `migrations/0.304.sql`)
|
||||
- **NIE** w `updates/` — build script sam wczyta z `migrations/`
|
||||
- Sprawdź czy plik istnieje i jest poprawnie nazwany przed commitem
|
||||
4. Commit.
|
||||
5. Push.
|
||||
Gdy użytkownik napisze `KONIEC PRACY`, uruchom komendę `/koniec-pracy`.
|
||||
|
||||
156
autoload/Domain/Authors/AuthorsRepository.php
Normal file
156
autoload/Domain/Authors/AuthorsRepository.php
Normal file
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
namespace Domain\Authors;
|
||||
|
||||
class AuthorsRepository
|
||||
{
|
||||
private $db;
|
||||
|
||||
public function __construct($db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prosta lista autorow
|
||||
* @return array|bool
|
||||
*/
|
||||
public function simpleList()
|
||||
{
|
||||
return $this->db->select('pp_authors', '*', ['ORDER' => ['author' => 'ASC']]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Szczegoly autora z jezykami
|
||||
* @param int $authorId
|
||||
* @return array|bool
|
||||
*/
|
||||
public function authorDetails($authorId)
|
||||
{
|
||||
$author = $this->db->get('pp_authors', '*', ['id' => (int)$authorId]);
|
||||
|
||||
$results = $this->db->select('pp_authors_langs', '*', ['id_author' => (int)$authorId]);
|
||||
if (is_array($results)) foreach ($results as $row)
|
||||
$author['languages'][$row['id_lang']] = $row;
|
||||
|
||||
return $author;
|
||||
}
|
||||
|
||||
/**
|
||||
* Zapis autora (insert lub update)
|
||||
* @param int $authorId
|
||||
* @param string $author
|
||||
* @param string $image
|
||||
* @param string|array $description
|
||||
* @return int|bool
|
||||
*/
|
||||
public function authorSave($authorId, $author, $image, $description)
|
||||
{
|
||||
if (!$authorId)
|
||||
{
|
||||
$this->db->insert('pp_authors', [
|
||||
'author' => $author,
|
||||
'image' => $image
|
||||
]);
|
||||
|
||||
$id = $this->db->id();
|
||||
|
||||
if ($id)
|
||||
{
|
||||
$i = 0;
|
||||
|
||||
$results = $this->db->select('pp_langs', ['id'], ['status' => 1, 'ORDER' => ['o' => 'ASC']]);
|
||||
if (is_array($results) and count($results) > 1) foreach ($results as $row)
|
||||
{
|
||||
$this->db->insert('pp_authors_langs', [
|
||||
'id_author' => (int)$id,
|
||||
'id_lang' => $row['id'],
|
||||
'description' => $description[$i]
|
||||
]);
|
||||
$i++;
|
||||
}
|
||||
else if (is_array($results) and count($results) == 1) foreach ($results as $row)
|
||||
{
|
||||
$this->db->insert('pp_authors_langs', [
|
||||
'id_author' => (int)$id,
|
||||
'id_lang' => $row['id'],
|
||||
'description' => $description
|
||||
]);
|
||||
}
|
||||
|
||||
\S::delete_cache();
|
||||
|
||||
return $id;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->db->update('pp_authors', [
|
||||
'author' => $author,
|
||||
'image' => $image
|
||||
], [
|
||||
'id' => (int)$authorId
|
||||
]);
|
||||
|
||||
$this->db->delete('pp_authors_langs', ['id_author' => (int)$authorId]);
|
||||
|
||||
$i = 0;
|
||||
|
||||
$results = $this->db->select('pp_langs', ['id'], ['status' => 1, 'ORDER' => ['o' => 'ASC']]);
|
||||
if (is_array($results) and count($results) > 1) foreach ($results as $row)
|
||||
{
|
||||
$this->db->insert('pp_authors_langs', [
|
||||
'id_author' => (int)$authorId,
|
||||
'id_lang' => $row['id'],
|
||||
'description' => $description[$i]
|
||||
]);
|
||||
$i++;
|
||||
}
|
||||
else if (is_array($results) and count($results) == 1) foreach ($results as $row)
|
||||
{
|
||||
$this->db->insert('pp_authors_langs', [
|
||||
'id_author' => (int)$authorId,
|
||||
'id_lang' => $row['id'],
|
||||
'description' => $description
|
||||
]);
|
||||
}
|
||||
|
||||
\S::delete_cache();
|
||||
|
||||
return $authorId;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Usuniecie autora
|
||||
* @param int $authorId
|
||||
* @return object|bool
|
||||
*/
|
||||
public function authorDelete($authorId)
|
||||
{
|
||||
$result = $this->db->delete('pp_authors', ['id' => (int)$authorId]);
|
||||
\S::delete_cache();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Szczegoly autora z cache (front)
|
||||
* @param int $authorId
|
||||
* @return array|bool
|
||||
*/
|
||||
public function authorByLang($authorId)
|
||||
{
|
||||
if (!$author = \Shared\Cache\CacheHandler::fetch("get_single_author:$authorId"))
|
||||
{
|
||||
$author = $this->db->get('pp_authors', '*', ['id' => (int)$authorId]);
|
||||
|
||||
$results = $this->db->select('pp_authors_langs', '*', ['id_author' => (int)$authorId]);
|
||||
if (is_array($results)) foreach ($results as $row)
|
||||
$author['languages'][$row['id_lang']] = $row;
|
||||
|
||||
\Shared\Cache\CacheHandler::store("get_single_author:$authorId", $author);
|
||||
}
|
||||
return $author;
|
||||
}
|
||||
}
|
||||
148
autoload/Domain/Banners/BannersRepository.php
Normal file
148
autoload/Domain/Banners/BannersRepository.php
Normal file
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
namespace Domain\Banners;
|
||||
|
||||
class BannersRepository
|
||||
{
|
||||
private $db;
|
||||
|
||||
public function __construct( $db )
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Odczyt
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public function bannerDetails( $bannerId )
|
||||
{
|
||||
$banner = $this->db->get( 'pp_banners', '*', [ 'id' => $bannerId ] );
|
||||
if ( !$banner ) return null;
|
||||
|
||||
$langs = $this->db->select( 'pp_banners_langs', '*', [ 'id_banner' => $bannerId ] );
|
||||
$banner['languages'] = [];
|
||||
if ( is_array( $langs ) )
|
||||
foreach ( $langs as $lang )
|
||||
$banner['languages'][ $lang['id_lang'] ] = $lang;
|
||||
|
||||
return $banner;
|
||||
}
|
||||
|
||||
public function activeBanners( $langId )
|
||||
{
|
||||
if ( $banners = \Shared\Cache\CacheHandler::fetch( 'banners' ) )
|
||||
return $banners;
|
||||
|
||||
$results = $this->db->query(
|
||||
'SELECT id, name FROM pp_banners WHERE status = 1 AND ( date_start <= \'' . date( 'Y-m-d' ) . '\' OR date_start IS NULL ) AND ( date_end >= \'' . date( 'Y-m-d' ) . '\' OR date_end IS NULL ) AND home_page = 0'
|
||||
)->fetchAll( \PDO::FETCH_ASSOC );
|
||||
|
||||
$banners = [];
|
||||
if ( is_array( $results ) )
|
||||
{
|
||||
foreach ( $results as $row )
|
||||
{
|
||||
$langData = $this->db->get( 'pp_banners_langs', '*', [
|
||||
'AND' => [ 'id_banner' => $row['id'], 'id_lang' => $langId ]
|
||||
] );
|
||||
$row['languages'] = $langData ?: [];
|
||||
$banners[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
\Shared\Cache\CacheHandler::store( 'banners', $banners );
|
||||
return $banners;
|
||||
}
|
||||
|
||||
public function mainBanner( $langId )
|
||||
{
|
||||
$cacheKey = "main_banner:$langId";
|
||||
if ( $banner = \Shared\Cache\CacheHandler::fetch( $cacheKey ) )
|
||||
return $banner;
|
||||
|
||||
$results = $this->db->query(
|
||||
'SELECT id, name FROM pp_banners WHERE status = 1 AND ( date_start <= \'' . date( 'Y-m-d' ) . '\' OR date_start IS NULL ) AND ( date_end >= \'' . date( 'Y-m-d' ) . '\' OR date_end IS NULL ) AND home_page = 1 ORDER BY date_end ASC LIMIT 1'
|
||||
)->fetchAll( \PDO::FETCH_ASSOC );
|
||||
|
||||
if ( !is_array( $results ) || empty( $results ) ) return null;
|
||||
|
||||
$banner = $results[0];
|
||||
$langData = $this->db->get( 'pp_banners_langs', '*', [
|
||||
'AND' => [ 'id_banner' => $banner['id'], 'id_lang' => $langId ]
|
||||
] );
|
||||
$banner['languages'] = $langData ?: [];
|
||||
|
||||
\Shared\Cache\CacheHandler::store( $cacheKey, $banner );
|
||||
return $banner;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Zapis / usuwanie
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public function bannerSave( $bannerId, $name, $status, $dateStart, $dateEnd, $homePage, $src, $url, $html, $text )
|
||||
{
|
||||
$languages = $this->db->select( 'pp_langs', '*', [ 'status' => 1, 'ORDER' => [ 'o' => 'ASC' ] ] );
|
||||
if ( !is_array( $languages ) ) $languages = [];
|
||||
$langCount = count( $languages );
|
||||
|
||||
if ( !$bannerId )
|
||||
{
|
||||
$this->db->insert( 'pp_banners', [
|
||||
'name' => $name,
|
||||
'status' => $status == 'on' ? 1 : 0,
|
||||
'date_start' => $dateStart ? $dateStart : null,
|
||||
'date_end' => $dateEnd ? $dateEnd : null,
|
||||
'home_page' => $homePage == 'on' ? 1 : 0,
|
||||
] );
|
||||
$bannerId = $this->db->id();
|
||||
if ( !$bannerId ) return false;
|
||||
|
||||
foreach ( $languages as $i => $lang )
|
||||
{
|
||||
$this->db->insert( 'pp_banners_langs', [
|
||||
'id_banner' => $bannerId,
|
||||
'id_lang' => $lang['id'],
|
||||
'src' => $langCount > 1 ? $src[ $i ] : $src,
|
||||
'url' => $langCount > 1 ? $url[ $i ] : $url,
|
||||
'html' => $langCount > 1 ? $html[ $i ] : $html,
|
||||
'text' => $langCount > 1 ? $text[ $i ] : $text,
|
||||
] );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->db->update( 'pp_banners', [
|
||||
'name' => $name,
|
||||
'status' => $status == 'on' ? 1 : 0,
|
||||
'date_start' => $dateStart ? $dateStart : null,
|
||||
'date_end' => $dateEnd ? $dateEnd : null,
|
||||
'home_page' => $homePage == 'on' ? 1 : 0,
|
||||
], [ 'id' => $bannerId ] );
|
||||
|
||||
$this->db->delete( 'pp_banners_langs', [ 'id_banner' => $bannerId ] );
|
||||
|
||||
foreach ( $languages as $i => $lang )
|
||||
{
|
||||
$this->db->insert( 'pp_banners_langs', [
|
||||
'id_banner' => $bannerId,
|
||||
'id_lang' => $lang['id'],
|
||||
'src' => $langCount > 1 ? $src[ $i ] : $src,
|
||||
'url' => $langCount > 1 ? $url[ $i ] : $url,
|
||||
'html' => $langCount > 1 ? $html[ $i ] : $html,
|
||||
'text' => $langCount > 1 ? $text[ $i ] : $text,
|
||||
] );
|
||||
}
|
||||
}
|
||||
|
||||
\S::delete_cache();
|
||||
return $bannerId;
|
||||
}
|
||||
|
||||
public function bannerDelete( $bannerId )
|
||||
{
|
||||
$result = $this->db->delete( 'pp_banners', [ 'id' => $bannerId ] );
|
||||
\S::delete_cache();
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
281
autoload/Domain/Newsletter/NewsletterRepository.php
Normal file
281
autoload/Domain/Newsletter/NewsletterRepository.php
Normal file
@@ -0,0 +1,281 @@
|
||||
<?php
|
||||
namespace Domain\Newsletter;
|
||||
|
||||
class NewsletterRepository
|
||||
{
|
||||
private $db;
|
||||
|
||||
public function __construct($db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Import emaili do newslettera
|
||||
* @param string $emails
|
||||
* @return bool
|
||||
*/
|
||||
public function emailsImport($emails)
|
||||
{
|
||||
$emails = explode(PHP_EOL, $emails);
|
||||
if (is_array($emails)) foreach ($emails as $email)
|
||||
{
|
||||
if (trim($email) and !$this->db->count('pp_newsletter', ['email' => trim($email)]))
|
||||
$this->db->insert('pp_newsletter', [
|
||||
'email' => trim($email),
|
||||
'hash' => md5($email . time()),
|
||||
'status' => 1
|
||||
]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sprawdza czy szablon jest adminski
|
||||
* @param int $templateId
|
||||
* @return string|bool
|
||||
*/
|
||||
public function isAdminTemplate($templateId)
|
||||
{
|
||||
return $this->db->get('pp_newsletter_templates', 'is_admin', ['id' => (int)$templateId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Usuniecie szablonu newslettera
|
||||
* @param int $templateId
|
||||
* @return object|bool
|
||||
*/
|
||||
public function templateDelete($templateId)
|
||||
{
|
||||
return $this->db->delete('pp_newsletter_templates', ['id' => (int)$templateId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wysylka newslettera - kolejkowanie
|
||||
* @param string $dates
|
||||
* @param int $template
|
||||
* @param string $onlyOnce
|
||||
* @return bool
|
||||
*/
|
||||
public function send($dates, $template, $onlyOnce)
|
||||
{
|
||||
$results = $this->db->select('pp_newsletter', 'email', ['status' => 1]);
|
||||
if (is_array($results) and !empty($results)) foreach ($results as $row)
|
||||
{
|
||||
if ($template and $onlyOnce)
|
||||
{
|
||||
if (!$this->db->count('pp_newsletter_send', ['AND' => ['id_template' => $template, 'email' => $row]]))
|
||||
$this->db->insert('pp_newsletter_send', [
|
||||
'email' => $row,
|
||||
'dates' => $dates,
|
||||
'id_template' => $template ? $template : null,
|
||||
'only_once' => ($onlyOnce == 'on' and $template) ? 1 : 0
|
||||
]);
|
||||
}
|
||||
else
|
||||
$this->db->insert('pp_newsletter_send', [
|
||||
'email' => $row,
|
||||
'dates' => $dates,
|
||||
'id_template' => $template ? $template : null,
|
||||
'only_once' => ($onlyOnce == 'on' and $template) ? 1 : 0
|
||||
]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Szczegoly szablonu email
|
||||
* @param int $templateId
|
||||
* @return array|bool
|
||||
*/
|
||||
public function templateDetails($templateId)
|
||||
{
|
||||
$result = $this->db->get('pp_newsletter_templates', '*', ['id' => (int)$templateId]);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Zapis szablonu (insert lub update)
|
||||
* @param int $id
|
||||
* @param string $name
|
||||
* @param string $text
|
||||
* @return int|bool
|
||||
*/
|
||||
public function templateSave($id, $name, $text)
|
||||
{
|
||||
if (!$id)
|
||||
{
|
||||
if ($this->db->insert('pp_newsletter_templates', [
|
||||
'name' => $name,
|
||||
'text' => $text
|
||||
]))
|
||||
{
|
||||
\S::delete_cache();
|
||||
return $this->db->id();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->db->update('pp_newsletter_templates', [
|
||||
'name' => $name,
|
||||
'text' => $text
|
||||
], [
|
||||
'id' => (int)$id
|
||||
]);
|
||||
|
||||
\S::delete_cache();
|
||||
return $id;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lista szablonow (nie-adminskich)
|
||||
* @return array|bool
|
||||
*/
|
||||
public function templatesList()
|
||||
{
|
||||
return $this->db->select('pp_newsletter_templates', '*', ['is_admin' => 0, 'ORDER' => ['name' => 'ASC']]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wypisanie z newslettera po hashu
|
||||
* @param string $hash
|
||||
* @return object|bool
|
||||
*/
|
||||
public function unsubscribe($hash)
|
||||
{
|
||||
return $this->db->update('pp_newsletter', ['status' => 0], ['hash' => $hash]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Potwierdzenie zapisu po hashu
|
||||
* @param string $hash
|
||||
* @return bool
|
||||
*/
|
||||
public function confirm($hash)
|
||||
{
|
||||
if (!$id = $this->db->get('pp_newsletter', 'id', ['AND' => ['hash' => $hash, 'status' => 0]]))
|
||||
return false;
|
||||
else
|
||||
$this->db->update('pp_newsletter', ['status' => 1], ['id' => $id]);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wysylka zakolejkowanych newsletterow (cron/front)
|
||||
* @param int $limit
|
||||
* @param array $settings
|
||||
* @param array $lang
|
||||
* @return bool
|
||||
*/
|
||||
public function newsletterSend($limit, $settings, $lang)
|
||||
{
|
||||
$results = $this->db->query('SELECT * FROM pp_newsletter_send WHERE mailed = 0 ORDER BY id ASC LIMIT ' . (int)$limit)->fetchAll();
|
||||
if (is_array($results) and !empty($results))
|
||||
{
|
||||
foreach ($results as $row)
|
||||
{
|
||||
$dates = explode(' - ', $row['dates']);
|
||||
|
||||
$text = \admin\view\Newsletter::preview(
|
||||
\admin\factory\Articles::articles_by_date_add($dates[0], $dates[1]),
|
||||
\admin\factory\Settings::settings_details(),
|
||||
\admin\factory\Newsletter::email_template_detalis($row['id_template'])
|
||||
);
|
||||
|
||||
if ($settings['ssl']) $base = 'https'; else $base = 'http';
|
||||
|
||||
$link = $base . "://" . $_SERVER['SERVER_NAME'] . '/newsletter/unsubscribe/hash=' . $this->getHash($row['email']);
|
||||
$text = str_replace('[WYPISZ_SIE]', $link, $text);
|
||||
|
||||
$regex = "-(<img[^>]+src\s*=\s*['\"])(((?!'|\"|http(|s)://).)*)(['\"][^>]*>)-i";
|
||||
$text = preg_replace($regex, "$1" . $base . "://" . $_SERVER['SERVER_NAME'] . "$2$4", $text);
|
||||
|
||||
$regex = "-(<a[^>]+href\s*=\s*['\"])(((?!'|\"|http(|s)://).)*)(['\"][^>]*>)-i";
|
||||
$text = preg_replace($regex, "$1" . $base . "://" . $_SERVER['SERVER_NAME'] . "$2$4", $text);
|
||||
|
||||
\S::send_email($row['email'], 'Newsletter ze strony: ' . $_SERVER['SERVER_NAME'], $text);
|
||||
|
||||
if ($row['only_once'])
|
||||
$this->db->update('pp_newsletter_send', ['mailed' => 1], ['id' => $row['id']]);
|
||||
else
|
||||
$this->db->delete('pp_newsletter_send', ['id' => $row['id']]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pobranie hasha dla emaila
|
||||
* @param string $email
|
||||
* @return string|bool
|
||||
*/
|
||||
public function getHash($email)
|
||||
{
|
||||
return $this->db->get('pp_newsletter', 'hash', ['email' => $email]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Zapis do newslettera z wysylka potwierdzenia
|
||||
* @param string $email
|
||||
* @param array $settings
|
||||
* @param array $lang
|
||||
* @return bool
|
||||
*/
|
||||
public function signin($email, $settings, $lang)
|
||||
{
|
||||
if (!\S::email_check($email))
|
||||
return false;
|
||||
|
||||
if (!$this->db->get('pp_newsletter', 'id', ['email' => $email]))
|
||||
{
|
||||
$hash = md5(time() . $email);
|
||||
|
||||
$text = $settings['newsletter_header'];
|
||||
$text .= $this->getTemplate('#potwierdzenie-zapisu-do-newslettera');
|
||||
$text .= $settings['newsletter_footer_1'];
|
||||
|
||||
$settings['ssl'] ? $base = 'https' : $base = 'http';
|
||||
|
||||
$regex = "-(<img[^>]+src\s*=\s*['\"])(((?!'|\"|http://).)*)(['\"][^>]*>)-i";
|
||||
$text = preg_replace($regex, "$1" . $base . "://" . $_SERVER['SERVER_NAME'] . "$2$4", $text);
|
||||
|
||||
$regex = "-(<a[^>]+href\s*=\s*['\"])(((?!'|\"|http://).)*)(['\"][^>]*>)-i";
|
||||
$text = preg_replace($regex, "$1" . $base . "://" . $_SERVER['SERVER_NAME'] . "$2$4", $text);
|
||||
|
||||
$link = '/newsletter/confirm/hash=' . $hash;
|
||||
|
||||
$text = str_replace('[LINK]', $link, $text);
|
||||
|
||||
$send = \S::send_email($email, $lang['potwierdz-zapisanie-sie-do-newslettera'], $text);
|
||||
|
||||
$this->db->insert('pp_newsletter', ['email' => $email, 'hash' => $hash, 'status' => 0]);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pobranie szablonu po nazwie
|
||||
* @param string $templateName
|
||||
* @return string|bool
|
||||
*/
|
||||
public function getTemplate($templateName)
|
||||
{
|
||||
return $this->db->get('pp_newsletter_templates', 'text', ['name' => $templateName]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wypisanie z newslettera po emailu
|
||||
* @param string $email
|
||||
* @return object|bool
|
||||
*/
|
||||
public function signout($email)
|
||||
{
|
||||
if ($this->db->get('pp_newsletter', 'id', ['email' => $email]))
|
||||
return $this->db->delete('pp_newsletter', ['email' => $email]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
110
autoload/Domain/Scontainers/ScontainersRepository.php
Normal file
110
autoload/Domain/Scontainers/ScontainersRepository.php
Normal file
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
namespace Domain\Scontainers;
|
||||
|
||||
class ScontainersRepository
|
||||
{
|
||||
private $db;
|
||||
|
||||
public function __construct( $db )
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Odczyt
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public function containerDetails( $containerId )
|
||||
{
|
||||
$container = $this->db->get( 'pp_scontainers', '*', [ 'id' => $containerId ] );
|
||||
if ( !$container ) return null;
|
||||
|
||||
$langs = $this->db->select( 'pp_scontainers_langs', '*', [ 'container_id' => $containerId ] );
|
||||
$container['languages'] = [];
|
||||
if ( is_array( $langs ) )
|
||||
foreach ( $langs as $lang )
|
||||
$container['languages'][ $lang['lang_id'] ] = $lang;
|
||||
|
||||
return $container;
|
||||
}
|
||||
|
||||
public function scontainerByLang( $scontainerId, $langId )
|
||||
{
|
||||
$cacheKey = "scontainer_details:$scontainerId:$langId";
|
||||
if ( $scontainer = \Shared\Cache\CacheHandler::fetch( $cacheKey ) )
|
||||
return $scontainer;
|
||||
|
||||
$scontainer = $this->db->get( 'pp_scontainers', '*', [ 'id' => $scontainerId ] );
|
||||
if ( !$scontainer ) return null;
|
||||
|
||||
$langData = $this->db->select( 'pp_scontainers_langs', '*', [
|
||||
'AND' => [ 'container_id' => $scontainerId, 'lang_id' => $langId ]
|
||||
] );
|
||||
$scontainer['languages'] = is_array( $langData ) ? $langData : [];
|
||||
|
||||
\Shared\Cache\CacheHandler::store( $cacheKey, $scontainer );
|
||||
return $scontainer;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Zapis / usuwanie
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public function containerSave( $containerId, $title, $text, $status, $showTitle, $src, $html )
|
||||
{
|
||||
$languages = $this->db->select( 'pp_langs', '*', [ 'status' => 1, 'ORDER' => [ 'o' => 'ASC' ] ] );
|
||||
if ( !is_array( $languages ) ) $languages = [];
|
||||
$langCount = count( $languages );
|
||||
|
||||
if ( !$containerId )
|
||||
{
|
||||
$this->db->insert( 'pp_scontainers', [
|
||||
'status' => $status == 'on' ? 1 : 0,
|
||||
'show_title' => $showTitle == 'on' ? 1 : 0,
|
||||
'src' => $src,
|
||||
] );
|
||||
$containerId = $this->db->id();
|
||||
if ( !$containerId ) return false;
|
||||
|
||||
foreach ( $languages as $i => $lang )
|
||||
{
|
||||
$this->db->insert( 'pp_scontainers_langs', [
|
||||
'container_id' => $containerId,
|
||||
'lang_id' => $lang['id'],
|
||||
'title' => $langCount > 1 ? $title[ $i ] : $title,
|
||||
'text' => $langCount > 1 ? $text[ $i ] : $text,
|
||||
'html' => $langCount > 1 ? $html[ $i ] : $html,
|
||||
] );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->db->update( 'pp_scontainers', [
|
||||
'status' => $status == 'on' ? 1 : 0,
|
||||
'show_title' => $showTitle == 'on' ? 1 : 0,
|
||||
'src' => $src,
|
||||
], [ 'id' => $containerId ] );
|
||||
|
||||
$this->db->delete( 'pp_scontainers_langs', [ 'container_id' => $containerId ] );
|
||||
|
||||
foreach ( $languages as $i => $lang )
|
||||
{
|
||||
$this->db->insert( 'pp_scontainers_langs', [
|
||||
'container_id' => $containerId,
|
||||
'lang_id' => $lang['id'],
|
||||
'title' => $langCount > 1 ? $title[ $i ] : $title,
|
||||
'text' => $langCount > 1 ? $text[ $i ] : $text,
|
||||
'html' => $langCount > 1 ? $html[ $i ] : $html,
|
||||
] );
|
||||
}
|
||||
}
|
||||
|
||||
\S::delete_cache();
|
||||
return $containerId;
|
||||
}
|
||||
|
||||
public function containerDelete( $containerId )
|
||||
{
|
||||
return $this->db->delete( 'pp_scontainers', [ 'id' => $containerId ] );
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
<?
|
||||
<?php
|
||||
namespace admin\factory;
|
||||
class Authors
|
||||
{
|
||||
@@ -6,112 +6,31 @@ class Authors
|
||||
static public function get_simple_list()
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb -> select( 'pp_authors', '*', [ 'ORDER' => [ 'author' => 'ASC' ] ] );
|
||||
$repo = new \Domain\Authors\AuthorsRepository($mdb);
|
||||
return $repo->simpleList();
|
||||
}
|
||||
|
||||
// usunięcie autora
|
||||
static public function delete_author( $id_author )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$result = $mdb -> delete( 'pp_authors', [ 'id' => (int)$id_author ] );
|
||||
\S::delete_cache();
|
||||
|
||||
return $result;
|
||||
$repo = new \Domain\Authors\AuthorsRepository($mdb);
|
||||
return $repo->authorDelete($id_author);
|
||||
}
|
||||
|
||||
// zapis autora
|
||||
static public function save_author( $id_author, $author, $image, $description )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
if ( !$id_author )
|
||||
{
|
||||
$mdb -> insert( 'pp_authors', [
|
||||
'author' => $author,
|
||||
'image' => $image
|
||||
] );
|
||||
|
||||
$id = $mdb -> id();
|
||||
|
||||
if ( $id )
|
||||
{
|
||||
$i = 0;
|
||||
|
||||
$results = $mdb -> select( 'pp_langs', [ 'id' ], [ 'status' => 1, 'ORDER' => [ 'o' => 'ASC' ] ] );
|
||||
if ( is_array( $results ) and count( $results ) > 1 ) foreach ( $results as $row )
|
||||
{
|
||||
$mdb -> insert( 'pp_authors_langs', [
|
||||
'id_author' => (int)$id,
|
||||
'id_lang' => $row['id'],
|
||||
'description' => $description[ $i ]
|
||||
] );
|
||||
$i++;
|
||||
}
|
||||
else if ( is_array( $results ) and count( $results ) == 1 ) foreach ( $results as $row )
|
||||
{
|
||||
$mdb -> insert( 'pp_authors_langs', [
|
||||
'id_author' => (int)$id,
|
||||
'id_lang' => $row['id'],
|
||||
'description' => $description
|
||||
] );
|
||||
}
|
||||
|
||||
\S::delete_cache();
|
||||
|
||||
return $id;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$mdb -> update( 'pp_authors', [
|
||||
'author' => $author,
|
||||
'image' => $image
|
||||
], [
|
||||
'id' => (int)$id_author
|
||||
] );
|
||||
|
||||
$mdb -> delete( 'pp_authors_langs', [ 'id_author' => (int)$id_author ] );
|
||||
|
||||
$i = 0;
|
||||
|
||||
$results = $mdb -> select( 'pp_langs', [ 'id' ], [ 'status' => 1, 'ORDER' => [ 'o' => 'ASC' ] ] );
|
||||
if ( is_array( $results ) and count( $results ) > 1 ) foreach ( $results as $row )
|
||||
{
|
||||
$mdb -> insert( 'pp_authors_langs', [
|
||||
'id_author' => (int)$id_author,
|
||||
'id_lang' => $row['id'],
|
||||
'description' => $description[ $i ]
|
||||
] );
|
||||
$i++;
|
||||
}
|
||||
else if ( is_array( $results ) and count( $results ) == 1 ) foreach ( $results as $row )
|
||||
{
|
||||
$mdb -> insert( 'pp_authors_langs', [
|
||||
'id_author' => (int)$id_author,
|
||||
'id_lang' => $row['id'],
|
||||
'description' => $description
|
||||
] );
|
||||
}
|
||||
|
||||
\S::delete_cache();
|
||||
|
||||
return $id_author;
|
||||
}
|
||||
return false;
|
||||
$repo = new \Domain\Authors\AuthorsRepository($mdb);
|
||||
return $repo->authorSave($id_author, $author, $image, $description);
|
||||
}
|
||||
|
||||
// szczególy autora
|
||||
static public function get_single_author( $id_author )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$author = $mdb -> get( 'pp_authors', '*', [ 'id' => (int)$id_author ] );
|
||||
|
||||
$results = $mdb -> select( 'pp_authors_langs', '*', [ 'id_author' => (int)$id_author ] );
|
||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
||||
$author['languages'][$row['id_lang']] = $row;
|
||||
|
||||
return $author;
|
||||
$repo = new \Domain\Authors\AuthorsRepository($mdb);
|
||||
return $repo->authorDetails($id_author);
|
||||
}
|
||||
}
|
||||
@@ -7,123 +7,21 @@ class Banners
|
||||
public static function banner_delete( $banner_id )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$result = $mdb -> delete( 'pp_banners', [ 'id' => (int) $banner_id ] );
|
||||
\S::delete_cache();
|
||||
|
||||
return $result;
|
||||
$repo = new \Domain\Banners\BannersRepository($mdb);
|
||||
return $repo->bannerDelete($banner_id);
|
||||
}
|
||||
|
||||
public static function banner_save( $banner_id, $name, $status, $date_start, $date_end, $home_page, $src, $url, $html, $text )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
if ( !$banner_id )
|
||||
{
|
||||
$mdb -> insert( 'pp_banners', [
|
||||
'name' => $name,
|
||||
'status' => $status == 'on' ? 1 : 0,
|
||||
'date_start' => $date_start != '' ? $date_start : null,
|
||||
'date_end' => $date_end != '' ? $date_end : null,
|
||||
'home_page' => $home_page == 'on' ? 1 : 0
|
||||
] );
|
||||
|
||||
$id = $mdb -> id();
|
||||
|
||||
if ( $id )
|
||||
{
|
||||
$i = 0;
|
||||
|
||||
$results = $mdb -> select( 'pp_langs', [ 'id' ], [ 'status' => 1, 'ORDER' => [ 'o' => 'ASC' ] ] );
|
||||
if ( is_array( $results ) and count( $results ) > 1 ) foreach ( $results as $row )
|
||||
{
|
||||
$mdb -> insert( 'pp_banners_langs', [
|
||||
'id_banner' => (int)$id,
|
||||
'id_lang' => $row['id'],
|
||||
'src' => $src[ $i ],
|
||||
'url' => $url[ $i ],
|
||||
'html' => $html[ $i ],
|
||||
'text' => $text[ $i ]
|
||||
] );
|
||||
$i++;
|
||||
}
|
||||
else if ( is_array( $results ) and count( $results ) == 1 ) foreach ( $results as $row )
|
||||
{
|
||||
$mdb -> insert( 'pp_banners_langs', [
|
||||
'id_banner' => (int)$id,
|
||||
'id_lang' => $row['id'],
|
||||
'src' => $src,
|
||||
'url' => $url,
|
||||
'html' => $html,
|
||||
'text' => $text
|
||||
] );
|
||||
}
|
||||
|
||||
\S::delete_cache();
|
||||
|
||||
return $id;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$mdb -> update( 'pp_banners',
|
||||
[
|
||||
'name' => $name,
|
||||
'status' => $status == 'on' ? 1 : 0,
|
||||
'date_start' => $date_start != '' ? $date_start : null,
|
||||
'date_end' => $date_end != '' ? $date_end : null,
|
||||
'home_page' => $home_page == 'on' ? 1 : 0
|
||||
], [
|
||||
'id' => (int) $banner_id
|
||||
] );
|
||||
|
||||
$mdb -> delete( 'pp_banners_langs', [ 'id_banner' => (int)$banner_id ] );
|
||||
|
||||
$i = 0;
|
||||
|
||||
$results = $mdb -> select( 'pp_langs', [ 'id' ], [ 'status' => 1, 'ORDER' => [ 'o' => 'ASC' ] ] );
|
||||
if ( is_array( $results ) and count( $results ) > 1 ) foreach ( $results as $row )
|
||||
{
|
||||
$mdb -> insert( 'pp_banners_langs', [
|
||||
'id_banner' => (int)$banner_id,
|
||||
'id_lang' => $row['id'],
|
||||
'src' => $src[ $i ],
|
||||
'url' => $url[ $i ],
|
||||
'html' => $html[ $i ],
|
||||
'text' => $text[ $i ]
|
||||
] );
|
||||
$i++;
|
||||
}
|
||||
else if ( is_array( $results ) and count( $results ) == 1 ) foreach ( $results as $row )
|
||||
{
|
||||
$mdb -> insert( 'pp_banners_langs', [
|
||||
'id_banner' => (int)$banner_id,
|
||||
'id_lang' => $row['id'],
|
||||
'src' => $src,
|
||||
'url' => $url,
|
||||
'html' => $html,
|
||||
'text' => $text
|
||||
] );
|
||||
}
|
||||
|
||||
\S::delete_cache();
|
||||
return $banner_id;
|
||||
}
|
||||
return false;
|
||||
$repo = new \Domain\Banners\BannersRepository($mdb);
|
||||
return $repo->bannerSave($banner_id, $name, $status, $date_start, $date_end, $home_page, $src, $url, $html, $text);
|
||||
}
|
||||
|
||||
public static function banner_details( $id_banner )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$banner = $mdb -> get( 'pp_banners', '*', [ 'id' => (int)$id_banner ] );
|
||||
|
||||
$results = $mdb -> select( 'pp_banners_langs', '*', [ 'id_banner' => (int)$id_banner ] );
|
||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
||||
$banner['languages'][$row['id_lang']] = $row;
|
||||
|
||||
return $banner;
|
||||
$repo = new \Domain\Banners\BannersRepository($mdb);
|
||||
return $repo->bannerDetails($id_banner);
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
}
|
||||
@@ -6,100 +6,49 @@ class Newsletter
|
||||
public static function emails_import( $emails )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$emails = explode( PHP_EOL, $emails );
|
||||
if ( is_array( $emails ) ) foreach ( $emails as $email )
|
||||
{
|
||||
if ( trim( $email ) and !$mdb -> count( 'pp_newsletter', [ 'email' => trim( $email ) ] ) )
|
||||
$mdb -> insert( 'pp_newsletter', [
|
||||
'email' => trim( $email ),
|
||||
'hash' => md5( $email . time() ),
|
||||
'status' => 1
|
||||
] );
|
||||
}
|
||||
return true;
|
||||
$repo = new \Domain\Newsletter\NewsletterRepository($mdb);
|
||||
return $repo->emailsImport($emails);
|
||||
}
|
||||
|
||||
|
||||
public static function is_admin_template( $template_id )
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb -> get( 'pp_newsletter_templates', 'is_admin', [ 'id' => (int)$template_id ] );
|
||||
$repo = new \Domain\Newsletter\NewsletterRepository($mdb);
|
||||
return $repo->isAdminTemplate($template_id);
|
||||
}
|
||||
|
||||
|
||||
public static function newsletter_template_delete( $template_id )
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb -> delete( 'pp_newsletter_templates', [ 'id' => (int)$template_id ] );
|
||||
$repo = new \Domain\Newsletter\NewsletterRepository($mdb);
|
||||
return $repo->templateDelete($template_id);
|
||||
}
|
||||
|
||||
|
||||
public static function send( $dates, $template, $only_once )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$results = $mdb -> select( 'pp_newsletter', 'email', [ 'status' => 1 ] );
|
||||
if ( is_array( $results ) and !empty( $results ) ) foreach ( $results as $row )
|
||||
{
|
||||
if ( $template and $only_once )
|
||||
{
|
||||
if ( !$mdb -> count( 'pp_newsletter_send', [ 'AND' => [ 'id_template' => $template, 'email' => $row ] ] ) )
|
||||
$mdb -> insert( 'pp_newsletter_send', [
|
||||
'email' => $row,
|
||||
'dates' => $dates,
|
||||
'id_template' => $template ? $template : null,
|
||||
'only_once' => ( $only_once == 'on' and $template ) ? 1 : 0
|
||||
] );
|
||||
}
|
||||
else
|
||||
$mdb -> insert( 'pp_newsletter_send', [
|
||||
'email' => $row,
|
||||
'dates' => $dates,
|
||||
'id_template' => $template ? $template : null,
|
||||
'only_once' => ( $only_once == 'on' and $template ) ? 1 : 0
|
||||
] );
|
||||
}
|
||||
return true;
|
||||
$repo = new \Domain\Newsletter\NewsletterRepository($mdb);
|
||||
return $repo->send($dates, $template, $only_once);
|
||||
}
|
||||
|
||||
|
||||
public static function email_template_detalis ($id_template)
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$result = $mdb -> get ('pp_newsletter_templates', '*', [ 'id' => (int)$id_template ] );
|
||||
return $result;
|
||||
$repo = new \Domain\Newsletter\NewsletterRepository($mdb);
|
||||
return $repo->templateDetails($id_template);
|
||||
}
|
||||
|
||||
|
||||
public static function template_save($id, $name, $text)
|
||||
{
|
||||
global $mdb;
|
||||
if ( !$id )
|
||||
{
|
||||
if ( $mdb -> insert( 'pp_newsletter_templates', [
|
||||
'name' => $name,
|
||||
'text' => $text
|
||||
] ) )
|
||||
{
|
||||
\S::delete_cache();
|
||||
return $mdb -> id();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$mdb -> update( 'pp_newsletter_templates', [
|
||||
'name' => $name,
|
||||
'text' => $text
|
||||
$repo = new \Domain\Newsletter\NewsletterRepository($mdb);
|
||||
return $repo->templateSave($id, $name, $text);
|
||||
}
|
||||
|
||||
], [
|
||||
'id' => (int)$id
|
||||
] );
|
||||
|
||||
\S::delete_cache();
|
||||
return $id;
|
||||
}
|
||||
}
|
||||
|
||||
public static function templates_list()
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb -> select( 'pp_newsletter_templates', '*', [ 'is_admin' => 0, 'ORDER' => [ 'name' => 'ASC' ] ] );
|
||||
$repo = new \Domain\Newsletter\NewsletterRepository($mdb);
|
||||
return $repo->templatesList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,115 +7,21 @@ class Scontainers
|
||||
public static function container_delete( $container_id )
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb -> delete( 'pp_scontainers', [ 'id' => (int) $container_id ] );
|
||||
$repo = new \Domain\Scontainers\ScontainersRepository($mdb);
|
||||
return $repo->containerDelete($container_id);
|
||||
}
|
||||
|
||||
public static function container_save( $container_id, $title, $text, $status, $show_title, $src, $html )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
if ( !$container_id )
|
||||
{
|
||||
$mdb -> insert( 'pp_scontainers',
|
||||
[
|
||||
'status' => $status == 'on' ? 1 : 0,
|
||||
'show_title' => $show_title == 'on' ? 1 : 0,
|
||||
'src' => $src
|
||||
] );
|
||||
|
||||
$id = $mdb -> id();
|
||||
|
||||
if ( $id )
|
||||
{
|
||||
$i = 0;
|
||||
|
||||
$results = $mdb -> select( 'pp_langs', [ 'id' ], [ 'status' => 1, 'ORDER' => [ 'o' => 'ASC' ] ] );
|
||||
if ( is_array( $results ) and count( $results ) > 1 ) foreach ( $results as $row )
|
||||
{
|
||||
$mdb -> insert( 'pp_scontainers_langs',
|
||||
[
|
||||
'container_id' => (int) $id,
|
||||
'lang_id' => $row['id'],
|
||||
'title' => $title[$i],
|
||||
'text' => $text[$i],
|
||||
'html' => $html[$i]
|
||||
] );
|
||||
$i++;
|
||||
}
|
||||
else if ( is_array( $results ) and count( $results ) == 1 ) foreach ( $results as $row )
|
||||
{
|
||||
$mdb -> insert( 'pp_scontainers_langs', [
|
||||
'container_id' => (int) $id,
|
||||
'lang_id' => $row['id'],
|
||||
'title' => $title,
|
||||
'text' => $text,
|
||||
'html' => $html
|
||||
] );
|
||||
}
|
||||
|
||||
\S::delete_cache();
|
||||
|
||||
return $id;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$mdb -> update( 'pp_scontainers',
|
||||
[
|
||||
'status' => $status == 'on' ? 1 : 0,
|
||||
'show_title' => $show_title == 'on' ? 1 : 0,
|
||||
'src' => $src
|
||||
],
|
||||
[
|
||||
'id' => (int) $container_id
|
||||
] );
|
||||
|
||||
$mdb -> delete( 'pp_scontainers_langs',
|
||||
[ 'container_id' => (int) $container_id ] );
|
||||
|
||||
$i = 0;
|
||||
|
||||
$results = $mdb -> select( 'pp_langs', [ 'id' ], [ 'status' => 1, 'ORDER' => [ 'o' => 'ASC' ] ] );
|
||||
if ( is_array( $results ) and count( $results ) > 1 ) foreach ( $results as $row )
|
||||
{
|
||||
$mdb -> insert( 'pp_scontainers_langs',
|
||||
[
|
||||
'container_id' => (int) $container_id,
|
||||
'lang_id' => $row['id'],
|
||||
'title' => $title[$i],
|
||||
'text' => $text[$i],
|
||||
'html' => $html[$i]
|
||||
] );
|
||||
$i++;
|
||||
}
|
||||
else if ( is_array( $results ) and count( $results ) == 1 ) foreach ( $results as $row )
|
||||
{
|
||||
$mdb -> insert( 'pp_scontainers_langs',
|
||||
[
|
||||
'container_id' => (int) $container_id,
|
||||
'lang_id' => $row['id'],
|
||||
'title' => $title,
|
||||
'text' => $text,
|
||||
'html' => $html
|
||||
] );
|
||||
}
|
||||
|
||||
\S::delete_cache();
|
||||
|
||||
return $container_id;
|
||||
}
|
||||
$repo = new \Domain\Scontainers\ScontainersRepository($mdb);
|
||||
return $repo->containerSave($container_id, $title, $text, $status, $show_title, $src, $html);
|
||||
}
|
||||
|
||||
public static function container_details( $container_id )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$container = $mdb -> get( 'pp_scontainers', '*', [ 'id' => (int) $container_id ] );
|
||||
|
||||
$results = $mdb -> select( 'pp_scontainers_langs', '*', [ 'container_id' => (int) $container_id ] );
|
||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
||||
$container['languages'][$row['lang_id']] = $row;
|
||||
|
||||
return $container;
|
||||
$repo = new \Domain\Scontainers\ScontainersRepository($mdb);
|
||||
return $repo->containerDetails($container_id);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
<?
|
||||
<?php
|
||||
namespace front\factory;
|
||||
class Authors
|
||||
{
|
||||
@@ -6,17 +6,7 @@ class Authors
|
||||
static public function get_single_author( $id_author )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
if ( !$author = \Cache::fetch( "get_single_author:$id_author" ) )
|
||||
{
|
||||
$author = $mdb -> get( 'pp_authors', '*', [ 'id' => (int)$id_author ] );
|
||||
|
||||
$results = $mdb -> select( 'pp_authors_langs', '*', [ 'id_author' => (int)$id_author ] );
|
||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
||||
$author['languages'][$row['id_lang']] = $row;
|
||||
|
||||
\Cache::store( "get_single_author:$id_author", $author );
|
||||
}
|
||||
return $author;
|
||||
$repo = new \Domain\Authors\AuthorsRepository($mdb);
|
||||
return $repo->authorByLang($id_author);
|
||||
}
|
||||
}
|
||||
@@ -6,58 +6,14 @@ class Banners
|
||||
public static function banners()
|
||||
{
|
||||
global $mdb, $lang;
|
||||
|
||||
if ( !$banners = \Cache::fetch( 'banners' ) )
|
||||
{
|
||||
$results = $mdb -> query( 'SELECT '
|
||||
. 'id, name '
|
||||
. 'FROM '
|
||||
. 'pp_banners '
|
||||
. 'WHERE '
|
||||
. 'status = 1 '
|
||||
. 'AND '
|
||||
. '( date_start <= \'' . date( 'Y-m-d' ) . '\' OR date_start IS NULL ) '
|
||||
. 'AND '
|
||||
. '( date_end >= \'' . date( 'Y-m-d' ) . '\' OR date_end IS NULL ) '
|
||||
. 'AND '
|
||||
. 'home_page = 0' ) -> fetchAll();
|
||||
if ( is_array( $results ) and !empty( $results ) ) foreach ( $results as $row )
|
||||
{
|
||||
$row['languages'] = $mdb -> get( 'pp_banners_langs', '*', [ 'AND' => [ 'id_banner' => (int)$row['id'], 'id_lang' => $lang[0] ] ] );
|
||||
$banners[] = $row;
|
||||
}
|
||||
\Cache::store( 'banners', $banners );
|
||||
}
|
||||
return $banners;
|
||||
$repo = new \Domain\Banners\BannersRepository($mdb);
|
||||
return $repo->activeBanners($lang[0]);
|
||||
}
|
||||
|
||||
|
||||
public static function main_banner()
|
||||
{
|
||||
global $mdb, $lang;
|
||||
|
||||
if ( !$banner = \Cache::fetch( "main_banner:" . $lang[0] ) )
|
||||
{
|
||||
$banner = $mdb -> query( 'SELECT '
|
||||
. '* '
|
||||
. 'FROM '
|
||||
. 'pp_banners '
|
||||
. 'WHERE '
|
||||
. 'status = 1 '
|
||||
. 'AND '
|
||||
. '( date_start <= \'' . date( 'Y-m-d' ) . '\' OR date_start IS NULL ) '
|
||||
. 'AND '
|
||||
. '( date_end >= \'' . date( 'Y-m-d' ) . '\' OR date_end IS NULL ) '
|
||||
. 'AND '
|
||||
. 'home_page = 1 '
|
||||
. 'ORDER BY '
|
||||
. 'date_end ASC '
|
||||
. 'LIMIT 1' ) -> fetchAll();
|
||||
$banner = $banner[0];
|
||||
if ( $banner )
|
||||
$banner['languages'] = $mdb -> get( 'pp_banners_langs', '*', [ 'AND' => [ 'id_banner' => (int)$banner['id'], 'id_lang' => $lang[0] ] ] );
|
||||
|
||||
\Cache::store( "main_banner:" . $lang[0], $banner );
|
||||
}
|
||||
return $banner;
|
||||
$repo = new \Domain\Banners\BannersRepository($mdb);
|
||||
return $repo->mainBanner($lang[0]);
|
||||
}
|
||||
}
|
||||
@@ -6,113 +6,49 @@ class Newsletter
|
||||
public static function newsletter_unsubscribe( $hash )
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb -> update( 'pp_newsletter', [ 'status' => 0 ], [ 'hash' => $hash ] );
|
||||
$repo = new \Domain\Newsletter\NewsletterRepository($mdb);
|
||||
return $repo->unsubscribe($hash);
|
||||
}
|
||||
|
||||
|
||||
public static function newsletter_confirm( $hash )
|
||||
{
|
||||
global $mdb;
|
||||
if ( !$id = $mdb -> get( 'pp_newsletter', 'id', [ 'AND' => [ 'hash' => $hash, 'status' => 0 ] ] ) )
|
||||
return false;
|
||||
else
|
||||
$mdb -> update( 'pp_newsletter', [ 'status' => 1 ], [ 'id' => $id ] );
|
||||
return true;
|
||||
$repo = new \Domain\Newsletter\NewsletterRepository($mdb);
|
||||
return $repo->confirm($hash);
|
||||
}
|
||||
|
||||
|
||||
public static function newsletter_send( $limit = 5 )
|
||||
{
|
||||
global $mdb, $settings, $lang;
|
||||
|
||||
$results = $mdb -> query( 'SELECT * FROM pp_newsletter_send WHERE mailed = 0 ORDER BY id ASC LIMIT ' . $limit ) -> fetchAll();
|
||||
if ( is_array( $results ) and !empty( $results ) )
|
||||
{
|
||||
foreach ( $results as $row )
|
||||
{
|
||||
$dates = explode( ' - ', $row['dates'] );
|
||||
|
||||
$text = \admin\view\Newsletter::preview(
|
||||
\admin\factory\Articles::articles_by_date_add( $dates[0], $dates[1] ),
|
||||
\admin\factory\Settings::settings_details(),
|
||||
\admin\factory\Newsletter::email_template_detalis($row['id_template'])
|
||||
);
|
||||
|
||||
if ( $settings['ssl'] ) $base = 'https'; else $base = 'http';
|
||||
|
||||
$link = $base . "://" . $_SERVER['SERVER_NAME'] . '/newsletter/unsubscribe/hash=' . \front\factory\Newsletter::get_hash( $row['email'] );
|
||||
$text = str_replace( '[WYPISZ_SIE]', $link, $text );
|
||||
|
||||
$regex = "-(<img[^>]+src\s*=\s*['\"])(((?!'|\"|http(|s)://).)*)(['\"][^>]*>)-i";
|
||||
$text = preg_replace( $regex, "$1" . $base . "://" . $_SERVER['SERVER_NAME'] . "$2$4", $text );
|
||||
|
||||
$regex = "-(<a[^>]+href\s*=\s*['\"])(((?!'|\"|http(|s)://).)*)(['\"][^>]*>)-i";
|
||||
$text = preg_replace( $regex, "$1" . $base . "://" . $_SERVER['SERVER_NAME'] . "$2$4", $text );
|
||||
|
||||
\S::send_email( $row['email'], 'Newsletter ze strony: ' . $_SERVER['SERVER_NAME'], $text );
|
||||
|
||||
if ( $row['only_once'] )
|
||||
$mdb -> update( 'pp_newsletter_send', [ 'mailed' => 1 ], [ 'id' => $row['id'] ] );
|
||||
else
|
||||
$mdb -> delete( 'pp_newsletter_send', [ 'id' => $row['id'] ] );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
$repo = new \Domain\Newsletter\NewsletterRepository($mdb);
|
||||
return $repo->newsletterSend($limit, $settings, $lang);
|
||||
}
|
||||
|
||||
|
||||
public static function get_hash( $email )
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb -> get( 'pp_newsletter', 'hash', [ 'email' => $email ] );
|
||||
$repo = new \Domain\Newsletter\NewsletterRepository($mdb);
|
||||
return $repo->getHash($email);
|
||||
}
|
||||
|
||||
|
||||
public static function newsletter_signin( $email )
|
||||
{
|
||||
global $mdb, $lang, $settings;
|
||||
|
||||
if ( !\S::email_check( $email ) )
|
||||
return false;
|
||||
|
||||
if ( !$mdb -> get( 'pp_newsletter', 'id', [ 'email' => $email ] ) )
|
||||
{
|
||||
$hash = md5( time() . $email );
|
||||
|
||||
$text = $settings['newsletter_header'];
|
||||
$text .= \front\factory\Newsletter::get_template( '#potwierdzenie-zapisu-do-newslettera' );
|
||||
$text .= $settings['newsletter_footer_1'];
|
||||
|
||||
$settings['ssl'] ? $base = 'https' : $base = 'http';
|
||||
|
||||
$regex = "-(<img[^>]+src\s*=\s*['\"])(((?!'|\"|http://).)*)(['\"][^>]*>)-i";
|
||||
$text = preg_replace( $regex, "$1" . $base . "://" . $_SERVER['SERVER_NAME'] . "$2$4", $text );
|
||||
|
||||
$regex = "-(<a[^>]+href\s*=\s*['\"])(((?!'|\"|http://).)*)(['\"][^>]*>)-i";
|
||||
$text = preg_replace( $regex, "$1" . $base . "://" . $_SERVER['SERVER_NAME'] . "$2$4", $text );
|
||||
|
||||
$link = '/newsletter/confirm/hash=' . $hash;
|
||||
|
||||
$text = str_replace( '[LINK]', $link, $text );
|
||||
|
||||
$send = \S::send_email( $email, $lang['potwierdz-zapisanie-sie-do-newslettera'], $text );
|
||||
|
||||
$mdb -> insert( 'pp_newsletter', [ 'email' => $email, 'hash' => $hash, 'status' => 0 ] );
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
$repo = new \Domain\Newsletter\NewsletterRepository($mdb);
|
||||
return $repo->signin($email, $settings, $lang);
|
||||
}
|
||||
|
||||
|
||||
public static function get_template( $template_name )
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb -> get( 'pp_newsletter_templates', 'text', [ 'name' => $template_name ] );
|
||||
$repo = new \Domain\Newsletter\NewsletterRepository($mdb);
|
||||
return $repo->getTemplate($template_name);
|
||||
}
|
||||
|
||||
|
||||
public static function newsletter_signout( $email )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
if ( $mdb -> get( 'pp_newsletter', 'id', [ 'email' => $email ] ) )
|
||||
return $mdb -> delete( 'pp_newsletter', [ 'email' => $email ] );
|
||||
return false;
|
||||
$repo = new \Domain\Newsletter\NewsletterRepository($mdb);
|
||||
return $repo->signout($email);
|
||||
}
|
||||
}
|
||||
@@ -6,18 +6,7 @@ class Scontainers
|
||||
public static function scontainer_details( $scontainer_id )
|
||||
{
|
||||
global $mdb, $lang;
|
||||
|
||||
if ( !$scontainer = \Cache::fetch( "scontainer_details:$scontainer_id:" . $lang[0] ) )
|
||||
{
|
||||
$scontainer = $mdb -> get( 'pp_scontainers', '*', [ 'id' => (int)$scontainer_id ] );
|
||||
|
||||
$results = $mdb -> select( 'pp_scontainers_langs', '*', [ 'AND' => [ 'container_id' => (int)$scontainer_id, 'lang_id' => $lang[0] ] ] );
|
||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
||||
$scontainer['languages'] = $row;
|
||||
|
||||
\Cache::store( "scontainer_details:$scontainer_id:" . $lang[0], $scontainer );
|
||||
}
|
||||
|
||||
return $scontainer;
|
||||
$repo = new \Domain\Scontainers\ScontainersRepository($mdb);
|
||||
return $repo->scontainerByLang($scontainer_id, $lang[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
updates/1.60/ver_1.694.zip
Normal file
BIN
updates/1.60/ver_1.694.zip
Normal file
Binary file not shown.
33
updates/1.60/ver_1.694_manifest.json
Normal file
33
updates/1.60/ver_1.694_manifest.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"changelog": "NEW - centralny autoloader, Shared\\Email, Shared\\Security\\CsrfToken",
|
||||
"version": "1.694",
|
||||
"files": {
|
||||
"added": [
|
||||
".mcp.json",
|
||||
"autoload/Shared/Email/Email.php",
|
||||
"autoload/Shared/Security/CsrfToken.php",
|
||||
"autoload/autoloader.php"
|
||||
],
|
||||
"deleted": [
|
||||
|
||||
],
|
||||
"modified": [
|
||||
"admin/ajax.php",
|
||||
"admin/index.php",
|
||||
"ajax.php",
|
||||
"api.php",
|
||||
"autoload/Shared/Helpers/Helpers.php",
|
||||
"cron.php",
|
||||
"download.php",
|
||||
"index.php"
|
||||
]
|
||||
},
|
||||
"checksum_zip": "sha256:a21dc4a768bc7c9e71b8a319ff0e6a16bdd894330a5145d0278b220a3ccc4027",
|
||||
"sql": [
|
||||
|
||||
],
|
||||
"date": "2026-04-04",
|
||||
"directories_deleted": [
|
||||
|
||||
]
|
||||
}
|
||||
BIN
updates/1.60/ver_1.695.zip
Normal file
BIN
updates/1.60/ver_1.695.zip
Normal file
Binary file not shown.
@@ -11,9 +11,9 @@ $mdb = new medoo( [
|
||||
'charset' => 'utf8'
|
||||
] );
|
||||
|
||||
$current_ver = 1693; // aktualizowane automatycznie przez build-update.ps1
|
||||
$current_ver = 1695; // aktualizowane automatycznie przez build-update.ps1
|
||||
|
||||
// 1. Skan filesystem — lista istniejących ZIPów
|
||||
// 1. Skan filesystem Ä‚ËÂĂ˂¬ĂË€ť lista istniejĂ„ĂË€¦cych ZIPĂłw
|
||||
$versions = [];
|
||||
for ( $i = 1; $i <= $current_ver; $i++ )
|
||||
{
|
||||
@@ -34,7 +34,7 @@ $license = $mdb->get( 'pp_update_licenses', '*', [ 'key' => ( $_GET['key'] ?? ''
|
||||
if ( !$license )
|
||||
die();
|
||||
|
||||
// 3. Sprawdź ważność daty
|
||||
// 3. SprawdĹş waĹĽnoÄąĂË€şĂ„ĂË€ˇ daty
|
||||
if ( $license['valid_to_date'] && $license['valid_to_date'] < date( 'Y-m-d' ) )
|
||||
die();
|
||||
|
||||
@@ -53,11 +53,11 @@ foreach ( $versions as $ver )
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Filtruj wersje wg kanału (beta widzi beta+stable, reszta tylko stable)
|
||||
// 5. Filtruj wersje wg kanaÄąĂË€šu (beta widzi beta+stable, reszta tylko stable)
|
||||
$channels = $license['beta'] ? [ 'beta', 'stable' ] : [ 'stable' ];
|
||||
$allowed = array_flip( $mdb->select( 'pp_update_versions', 'version', [ 'channel' => $channels ] ) ?: [] );
|
||||
|
||||
// 6. Wypisz dostępne wersje
|
||||
// 6. Wypisz dostĂ„ĂË„ËÂpne wersje
|
||||
$valid_to_version = $license['valid_to_version'];
|
||||
foreach ( $versions as $ver )
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user