Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8f6d084b4d | |||
| 47abff2550 | |||
| ffe661b4d2 | |||
| 73ff0ca5b6 | |||
| 7949e9b6a3 | |||
| 3325eaf44c | |||
| 9b31ce0d16 | |||
| 964bfa877c | |||
| 36fa3fdeae | |||
| 645037d144 | |||
| b8ab53a6f3 |
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(python3:*)",
|
||||||
"Bash(python:*)",
|
"Bash(python:*)",
|
||||||
"Bash(grep:*)",
|
"Bash(grep:*)",
|
||||||
"Bash(grep ^<b>ver:*)"
|
"Bash(grep ^<b>ver:*)",
|
||||||
|
"Skill(paul:plan)"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
17
.mcp.json
Normal file
17
.mcp.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"serena": {
|
||||||
|
"command": "uvx",
|
||||||
|
"args": [
|
||||||
|
"--from",
|
||||||
|
"git+https://github.com/oraios/serena",
|
||||||
|
"serena",
|
||||||
|
"start-mcp-server",
|
||||||
|
"--context",
|
||||||
|
"ide-assistant",
|
||||||
|
"--project",
|
||||||
|
"C:/visual studio code/projekty/cmsPRO"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
49
.paul/PROJECT.md
Normal file
49
.paul/PROJECT.md
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# Project: cmsPRO
|
||||||
|
|
||||||
|
## Description
|
||||||
|
Autorski system CMS z panelem administracyjnym (17 modułów admin, 13 modułów front). Projekt przechodzi pełną refaktoryzację kodu w 19 fazach — wzorcem docelowej architektury jest shopPRO. Wzorzec migracji: wrapper delegation (stare klasy delegują do nowych, zero regresji).
|
||||||
|
|
||||||
|
## Core Value
|
||||||
|
Autorski system CMS umożliwiający zarządzanie treściami i stronami internetowymi.
|
||||||
|
|
||||||
|
## Already Completed
|
||||||
|
- 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
|
||||||
|
|
||||||
|
### Must Have
|
||||||
|
- Centralny PSR-4 autoloader (hybrydowy z legacy)
|
||||||
|
- Wszystkie Domain repositories (Scontainers, Banners, Authors, Newsletter, SEO, Cron, Releases)
|
||||||
|
- Shared\Email + Shared\Security (CsrfToken, HMAC-SHA256)
|
||||||
|
- Admin\ namespace z DI dla wszystkich 17 modułów
|
||||||
|
- Frontend\ namespace dla wszystkich front modułów
|
||||||
|
- Bezpieczne cookies (HMAC-SHA256 zamiast hash w JSON)
|
||||||
|
|
||||||
|
### Should Have
|
||||||
|
- PHPUnit testy dla nowych repositories i controllers
|
||||||
|
- Legacy cleanup (usunięcie wrapperów po pełnej migracji)
|
||||||
|
|
||||||
|
### Nice to Have
|
||||||
|
- Admin base classes (TableListRequestFactory, FormValidator — wzór shopPRO)
|
||||||
|
|
||||||
|
## Constraints
|
||||||
|
- PHP < 8.0 (produkcja) — brak match, named args, union types, str_contains()
|
||||||
|
- Referencja architektury: shopPRO (C:\visual studio code\projekty\shopPRO)
|
||||||
|
- Zachowanie 100% kompatybilności wstecznej podczas migracji (wrapper delegation)
|
||||||
|
- Medoo ORM (nie zmieniać)
|
||||||
|
- Zewnętrzne biblioteki (Mobile_Detect, geoplugin) — nie ruszać
|
||||||
|
|
||||||
|
## Success Criteria
|
||||||
|
- 19 faz refaktoryzacji zakończonych
|
||||||
|
- Cały kod w namespace'ach Domain\, Shared\, Admin\, Frontend\
|
||||||
|
- Zero regresji — istniejąca funkcjonalność działa bez zmian
|
||||||
|
- Bezpieczne cookies (HMAC-SHA256)
|
||||||
|
- Testy PHPUnit dla kluczowych modułów
|
||||||
|
|
||||||
|
---
|
||||||
|
*Created: 2026-04-04*
|
||||||
|
*Last updated: 2026-04-04 after Phase 4*
|
||||||
306
.paul/ROADMAP.md
Normal file
306
.paul/ROADMAP.md
Normal file
@@ -0,0 +1,306 @@
|
|||||||
|
# Roadmap: cmsPRO
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
Pełna refaktoryzacja cmsPRO do architektury DDD wzorowanej na shopPRO. Wzorzec: wrapper delegation — stare klasy delegują do nowych, zero regresji. Referencja: C:\visual studio code\projekty\shopPRO. PHP < 8.0 (produkcja).
|
||||||
|
|
||||||
|
## Current Milestone
|
||||||
|
**v0.1 Refaktoryzacja** (v0.1.0)
|
||||||
|
Status: In progress
|
||||||
|
Phases: 4 of 19 complete
|
||||||
|
|
||||||
|
## Already Completed (before PAUL)
|
||||||
|
- **Domain (6 repos):** Articles, Languages, Layouts, Pages, Settings, User
|
||||||
|
- **Shared (5 modules):** Cache, Helpers, Html, Image, Tpl
|
||||||
|
- **Form Edit System:** Universal form handling framework (FormEditViewModel, multi-tab, validation)
|
||||||
|
- **PHPUnit base:** Bootstrap, 3 test files (Languages, Settings, User)
|
||||||
|
|
||||||
|
## Phases
|
||||||
|
|
||||||
|
| Phase | Name | Plans | Status | Completed |
|
||||||
|
|-------|------|-------|--------|-----------|
|
||||||
|
| 1 | Infrastructure & Autoloader | 1 | Complete | 2026-04-04 |
|
||||||
|
| 2 | Shared: Email + Security | 1 | Complete | 2026-04-04 |
|
||||||
|
| 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 | - |
|
||||||
|
| 8 | Admin: Pages + Layouts | 1 | Not started | - |
|
||||||
|
| 9 | Admin: Languages + Settings | 1 | Not started | - |
|
||||||
|
| 10 | Admin: Banners + Authors + Scontainers | 1 | Not started | - |
|
||||||
|
| 11 | Admin: Newsletter + Emails + SeoAdditional | 1 | Not started | - |
|
||||||
|
| 12 | Admin: Users + Backups + Filemanager | 1 | Not started | - |
|
||||||
|
| 13 | Admin: Releases + Update | 1 | Not started | - |
|
||||||
|
| 14 | Front: Site + Articles | 1 | Not started | - |
|
||||||
|
| 15 | Front: Pages + Menu + Banners + Scontainers | 1 | Not started | - |
|
||||||
|
| 16 | Front: Remaining modules | 1-2 | Not started | - |
|
||||||
|
| 17 | Users & Security: HMAC-SHA256 | 1 | Not started | - |
|
||||||
|
| 18 | Tests | 1-2 | Not started | - |
|
||||||
|
| 19 | Legacy Cleanup | 1 | Not started | - |
|
||||||
|
|
||||||
|
## Phase Details
|
||||||
|
|
||||||
|
### Phase 1: Infrastructure & Autoloader
|
||||||
|
|
||||||
|
**Goal:** Centralny autoloader (PSR-4 + legacy), composer.json z mapowaniem, usunięcie duplikatów z entry pointów.
|
||||||
|
**Depends on:** Nothing (first phase)
|
||||||
|
**Research:** Unlikely
|
||||||
|
|
||||||
|
**Scope:**
|
||||||
|
- autoload/autoloader.php (hybrydowy)
|
||||||
|
- composer.json PSR-4: Domain\, Shared\, Admin\, Frontend\
|
||||||
|
- Migracja 6 entry pointów (index.php, admin/index.php, ajax.php, api.php, cron.php, download.php)
|
||||||
|
|
||||||
|
**Plans:**
|
||||||
|
- [ ] 01-01: PSR-4 autoloader setup i composer.json
|
||||||
|
|
||||||
|
### Phase 2: Shared: Email + Security
|
||||||
|
|
||||||
|
**Goal:** Dodać brakujące moduły Shared — Email (migracja z legacy) i Security (CsrfToken, wzór shopPRO).
|
||||||
|
**Depends on:** Phase 1 (autoloader)
|
||||||
|
**Research:** Unlikely
|
||||||
|
|
||||||
|
**Scope:**
|
||||||
|
- Shared\Email\Email — migracja z legacy
|
||||||
|
- Shared\Security\CsrfToken — nowy moduł (wzór shopPRO)
|
||||||
|
- Wrapper w starym class.Email.php (jeśli istnieje)
|
||||||
|
|
||||||
|
**Plans:**
|
||||||
|
- [ ] 02-01: Email + Security modules
|
||||||
|
|
||||||
|
### Phase 3: Domain: Scontainers + Banners
|
||||||
|
|
||||||
|
**Goal:** Repository dla Scontainers i Banners — przeniesienie logiki z factory do Domain\.
|
||||||
|
**Depends on:** Phase 1 (autoloader)
|
||||||
|
**Research:** Unlikely (wzorzec ustalony)
|
||||||
|
|
||||||
|
**Scope:**
|
||||||
|
- Domain\Scontainers\ScontainersRepository
|
||||||
|
- Domain\Banners\BannersRepository
|
||||||
|
- Wrappery w starych factory (delegacja)
|
||||||
|
|
||||||
|
**Plans:**
|
||||||
|
- [ ] 03-01: Scontainers + Banners repositories
|
||||||
|
|
||||||
|
### Phase 4: Domain: Authors + Newsletter
|
||||||
|
|
||||||
|
**Goal:** Repository dla Authors i Newsletter.
|
||||||
|
**Depends on:** Phase 1
|
||||||
|
**Research:** Unlikely
|
||||||
|
|
||||||
|
**Scope:**
|
||||||
|
- Domain\Authors\AuthorsRepository
|
||||||
|
- Domain\Newsletter\NewsletterRepository
|
||||||
|
|
||||||
|
**Plans:**
|
||||||
|
- [ ] 04-01: Authors + Newsletter repositories
|
||||||
|
|
||||||
|
### Phase 5: Domain: SeoAdditional + Cron + Releases
|
||||||
|
|
||||||
|
**Goal:** Repository dla SEO, Cron, i systemu Releases/Update.
|
||||||
|
**Depends on:** Phase 1
|
||||||
|
**Research:** Unlikely
|
||||||
|
|
||||||
|
**Scope:**
|
||||||
|
- Domain\SeoAdditional\SeoAdditionalRepository
|
||||||
|
- Domain\Cron\CronRepository
|
||||||
|
- Domain\Releases\ReleasesRepository (lub Update)
|
||||||
|
|
||||||
|
**Plans:**
|
||||||
|
- [ ] 05-01: SeoAdditional + Cron + Releases repositories
|
||||||
|
|
||||||
|
### Phase 6: Admin: Base Infrastructure
|
||||||
|
|
||||||
|
**Goal:** Bazowe klasy Admin\ — kontrolery bazowe, TableListRequestFactory, FormValidator (wzór shopPRO).
|
||||||
|
**Depends on:** Phase 1 (autoloader), Form Edit System (already done)
|
||||||
|
**Research:** Likely (analiza shopPRO Admin base classes)
|
||||||
|
|
||||||
|
**Scope:**
|
||||||
|
- Admin\Base\BaseController (lub abstrakcyjna klasa bazowa)
|
||||||
|
- Admin\Support\TableListRequestFactory
|
||||||
|
- Admin\Support\FormValidator
|
||||||
|
- Integracja z istniejącym FormEditViewModel
|
||||||
|
|
||||||
|
**Plans:**
|
||||||
|
- [ ] 06-01: Admin base infrastructure
|
||||||
|
|
||||||
|
### Phase 7: Admin: Articles + ArticlesArchive
|
||||||
|
|
||||||
|
**Goal:** Migracja kontrolerów Articles i ArticlesArchive do Admin\ z DI.
|
||||||
|
**Depends on:** Phase 6 (Admin base), Phase 1 (Domain\Articles already exists)
|
||||||
|
**Research:** Unlikely
|
||||||
|
|
||||||
|
**Scope:**
|
||||||
|
- Admin\Articles\ArticlesController
|
||||||
|
- Admin\Articles\ArticlesArchiveController
|
||||||
|
- Wrapper w starym controls/class.Articles.php
|
||||||
|
|
||||||
|
**Plans:**
|
||||||
|
- [ ] 07-01: Articles admin controllers
|
||||||
|
|
||||||
|
### Phase 8: Admin: Pages + Layouts
|
||||||
|
|
||||||
|
**Goal:** Migracja kontrolerów Pages i Layouts do Admin\.
|
||||||
|
**Depends on:** Phase 6
|
||||||
|
**Research:** Unlikely
|
||||||
|
|
||||||
|
**Scope:**
|
||||||
|
- Admin\Pages\PagesController
|
||||||
|
- Admin\Layouts\LayoutsController
|
||||||
|
|
||||||
|
**Plans:**
|
||||||
|
- [ ] 08-01: Pages + Layouts admin controllers
|
||||||
|
|
||||||
|
### Phase 9: Admin: Languages + Settings
|
||||||
|
|
||||||
|
**Goal:** Migracja kontrolerów Languages i Settings do Admin\.
|
||||||
|
**Depends on:** Phase 6
|
||||||
|
**Research:** Unlikely
|
||||||
|
|
||||||
|
**Scope:**
|
||||||
|
- Admin\Languages\LanguagesController
|
||||||
|
- Admin\Settings\SettingsController
|
||||||
|
|
||||||
|
**Plans:**
|
||||||
|
- [ ] 09-01: Languages + Settings admin controllers
|
||||||
|
|
||||||
|
### Phase 10: Admin: Banners + Authors + Scontainers
|
||||||
|
|
||||||
|
**Goal:** Migracja kontrolerów Banners, Authors, Scontainers do Admin\.
|
||||||
|
**Depends on:** Phase 6, Phase 3 (Domain repos), Phase 4 (Domain repos)
|
||||||
|
**Research:** Unlikely
|
||||||
|
|
||||||
|
**Scope:**
|
||||||
|
- Admin\Banners\BannersController
|
||||||
|
- Admin\Authors\AuthorsController
|
||||||
|
- Admin\Scontainers\ScontainersController
|
||||||
|
|
||||||
|
**Plans:**
|
||||||
|
- [ ] 10-01: Banners + Authors + Scontainers admin controllers
|
||||||
|
|
||||||
|
### Phase 11: Admin: Newsletter + Emails + SeoAdditional
|
||||||
|
|
||||||
|
**Goal:** Migracja kontrolerów Newsletter, Emails, SeoAdditional do Admin\.
|
||||||
|
**Depends on:** Phase 6, Phase 4, Phase 5
|
||||||
|
**Research:** Unlikely
|
||||||
|
|
||||||
|
**Scope:**
|
||||||
|
- Admin\Newsletter\NewsletterController
|
||||||
|
- Admin\Emails\EmailsController
|
||||||
|
- Admin\SeoAdditional\SeoAdditionalController
|
||||||
|
|
||||||
|
**Plans:**
|
||||||
|
- [ ] 11-01: Newsletter + Emails + SeoAdditional admin controllers
|
||||||
|
|
||||||
|
### Phase 12: Admin: Users + Backups + Filemanager
|
||||||
|
|
||||||
|
**Goal:** Migracja kontrolerów Users, Backups, Filemanager do Admin\.
|
||||||
|
**Depends on:** Phase 6
|
||||||
|
**Research:** Unlikely
|
||||||
|
|
||||||
|
**Scope:**
|
||||||
|
- Admin\Users\UsersController
|
||||||
|
- Admin\Backups\BackupsController
|
||||||
|
- Admin\Filemanager\FilemanagerController
|
||||||
|
|
||||||
|
**Plans:**
|
||||||
|
- [ ] 12-01: Users + Backups + Filemanager admin controllers
|
||||||
|
|
||||||
|
### Phase 13: Admin: Releases + Update
|
||||||
|
|
||||||
|
**Goal:** Migracja kontrolerów Releases i Update do Admin\.
|
||||||
|
**Depends on:** Phase 6, Phase 5 (Domain repos)
|
||||||
|
**Research:** Unlikely
|
||||||
|
|
||||||
|
**Scope:**
|
||||||
|
- Admin\Releases\ReleasesController
|
||||||
|
- Admin\Update\UpdateController
|
||||||
|
|
||||||
|
**Plans:**
|
||||||
|
- [ ] 13-01: Releases + Update admin controllers
|
||||||
|
|
||||||
|
### Phase 14: Front: Site + Articles
|
||||||
|
|
||||||
|
**Goal:** Migracja głównych kontrolerów front — Site i Articles do Frontend\.
|
||||||
|
**Depends on:** Phase 1 (autoloader), Domain repos
|
||||||
|
**Research:** Unlikely
|
||||||
|
|
||||||
|
**Scope:**
|
||||||
|
- Frontend\Site\SiteController (lub controls + factory + view)
|
||||||
|
- Frontend\Articles\ArticlesController
|
||||||
|
- LayoutEngine (jeśli potrzebny, wzór shopPRO)
|
||||||
|
|
||||||
|
**Plans:**
|
||||||
|
- [ ] 14-01: Site + Articles frontend controllers
|
||||||
|
|
||||||
|
### Phase 15: Front: Pages + Menu + Banners + Scontainers
|
||||||
|
|
||||||
|
**Goal:** Migracja front kontrolerów Pages, Menu, Banners, Scontainers.
|
||||||
|
**Depends on:** Phase 14 (Front base)
|
||||||
|
**Research:** Unlikely
|
||||||
|
|
||||||
|
**Scope:**
|
||||||
|
- Frontend\Pages, Frontend\Menu, Frontend\Banners, Frontend\Scontainers
|
||||||
|
|
||||||
|
**Plans:**
|
||||||
|
- [ ] 15-01: Pages + Menu + Banners + Scontainers frontend
|
||||||
|
|
||||||
|
### Phase 16: Front: Remaining modules
|
||||||
|
|
||||||
|
**Goal:** Migracja pozostałych front modułów — Authors, Languages, Newsletter, Search, AuditSEO, SeoAdditional, Layouts, Settings.
|
||||||
|
**Depends on:** Phase 14
|
||||||
|
**Research:** Unlikely
|
||||||
|
|
||||||
|
**Scope:**
|
||||||
|
- Wszystkie pozostałe front factories/controls/views
|
||||||
|
|
||||||
|
**Plans:**
|
||||||
|
- [ ] 16-01: Remaining frontend modules (batch 1)
|
||||||
|
- [ ] 16-02: Remaining frontend modules (batch 2, if needed)
|
||||||
|
|
||||||
|
### Phase 17: Users & Security: HMAC-SHA256
|
||||||
|
|
||||||
|
**Goal:** Wymiana insecure remember-me cookies (hash w JSON) na HMAC-SHA256 signed tokens.
|
||||||
|
**Depends on:** Phase 2 (Shared\Security), Phase 12 (Admin\Users)
|
||||||
|
**Research:** Likely (strategia migracji istniejących cookies, backward compat)
|
||||||
|
|
||||||
|
**Scope:**
|
||||||
|
- Nowy system remember-me z HMAC-SHA256
|
||||||
|
- Migracja istniejących sesji/cookies
|
||||||
|
- Security hardening w UserRepository
|
||||||
|
|
||||||
|
**Plans:**
|
||||||
|
- [ ] 17-01: HMAC-SHA256 cookie system
|
||||||
|
|
||||||
|
### Phase 18: Tests
|
||||||
|
|
||||||
|
**Goal:** Rozbudowa PHPUnit testów dla nowych Domain repositories i Admin controllers.
|
||||||
|
**Depends on:** Phase 5 (all Domain repos), Phase 13 (all Admin controllers)
|
||||||
|
**Research:** Unlikely
|
||||||
|
|
||||||
|
**Scope:**
|
||||||
|
- Testy dla nowych Domain repositories
|
||||||
|
- Testy dla Admin controllers (unit)
|
||||||
|
- Rozbudowa test bootstrap
|
||||||
|
|
||||||
|
**Plans:**
|
||||||
|
- [ ] 18-01: Domain repository tests
|
||||||
|
- [ ] 18-02: Admin controller tests
|
||||||
|
|
||||||
|
### Phase 19: Legacy Cleanup
|
||||||
|
|
||||||
|
**Goal:** Usunięcie legacy wrapperów i starych class.*.php po weryfikacji że cały kod używa nowych klas.
|
||||||
|
**Depends on:** All prior phases
|
||||||
|
**Research:** Unlikely
|
||||||
|
|
||||||
|
**Scope:**
|
||||||
|
- Usunięcie wrapperów z class.*.php
|
||||||
|
- Usunięcie starych controls/factory/view plików
|
||||||
|
- Finalna weryfikacja i cleanup
|
||||||
|
|
||||||
|
**Plans:**
|
||||||
|
- [ ] 19-01: Legacy wrapper removal and cleanup
|
||||||
|
|
||||||
|
---
|
||||||
|
*Roadmap created: 2026-04-04*
|
||||||
|
*Last updated: 2026-04-04*
|
||||||
70
.paul/STATE.md
Normal file
70
.paul/STATE.md
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
# Project State
|
||||||
|
|
||||||
|
## Project Reference
|
||||||
|
|
||||||
|
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 4 complete — ready for Phase 5
|
||||||
|
|
||||||
|
## Current Position
|
||||||
|
|
||||||
|
Milestone: v0.1 Refaktoryzacja
|
||||||
|
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 4 complete, UNIFY done
|
||||||
|
|
||||||
|
Progress:
|
||||||
|
- Milestone: [▓▓░░░░░░░░] 20%
|
||||||
|
|
||||||
|
## Loop Position
|
||||||
|
|
||||||
|
Current loop state:
|
||||||
|
```
|
||||||
|
PLAN ──▶ APPLY ──▶ UNIFY
|
||||||
|
✓ ✓ ✓ [Loop complete - ready for next PLAN]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance Metrics
|
||||||
|
|
||||||
|
**Velocity:**
|
||||||
|
- Total plans completed: 4
|
||||||
|
- Total execution time: ~22min
|
||||||
|
|
||||||
|
**By Phase:**
|
||||||
|
|
||||||
|
| Phase | Plans | Total Time | Avg/Plan |
|
||||||
|
|-------|-------|------------|----------|
|
||||||
|
| 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
|
||||||
|
|
||||||
|
### Decisions
|
||||||
|
- Centralny autoloader zamiast duplikatów
|
||||||
|
- 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.
|
||||||
|
|
||||||
|
### Blockers/Concerns
|
||||||
|
None.
|
||||||
|
|
||||||
|
## Session Continuity
|
||||||
|
|
||||||
|
Last session: 2026-04-04
|
||||||
|
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*
|
||||||
33
.paul/config.md
Normal file
33
.paul/config.md
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# Project Config
|
||||||
|
|
||||||
|
**Project:** cmsPRO
|
||||||
|
**Created:** 2026-04-04
|
||||||
|
|
||||||
|
## Project Settings
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
project:
|
||||||
|
name: cmsPRO
|
||||||
|
version: 0.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
## Integrations
|
||||||
|
|
||||||
|
### SonarQube
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
sonarqube:
|
||||||
|
enabled: true
|
||||||
|
project_key: cmsPRO
|
||||||
|
```
|
||||||
|
|
||||||
|
## Preferences
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
preferences:
|
||||||
|
auto_commit: false
|
||||||
|
verbose_output: false
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
*Config created: 2026-04-04*
|
||||||
176
.paul/phases/01-infrastructure/01-01-PLAN.md
Normal file
176
.paul/phases/01-infrastructure/01-01-PLAN.md
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
---
|
||||||
|
phase: 01-infrastructure
|
||||||
|
plan: 01
|
||||||
|
type: execute
|
||||||
|
wave: 1
|
||||||
|
depends_on: []
|
||||||
|
files_modified:
|
||||||
|
- composer.json
|
||||||
|
- autoload/autoloader.php
|
||||||
|
- index.php
|
||||||
|
- admin/index.php
|
||||||
|
- ajax.php
|
||||||
|
- api.php
|
||||||
|
- cron.php
|
||||||
|
- download.php
|
||||||
|
autonomous: true
|
||||||
|
delegation: off
|
||||||
|
---
|
||||||
|
|
||||||
|
<objective>
|
||||||
|
## Goal
|
||||||
|
Scentralizować autoloader w jednym pliku, dodać PSR-4 mapowanie w composer.json dla Domain\, Shared\, Admin\, Frontend\, i zastąpić zduplikowane __autoload_my_classes() we wszystkich entry pointach.
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
Fundament dla całej refaktoryzacji — bez działającego PSR-4 autoloadera nie można dodawać nowych klas w Admin\ i Frontend\ namespace'ach.
|
||||||
|
|
||||||
|
## Output
|
||||||
|
- Centralny autoload/autoloader.php (hybrydowy: PSR-4 + legacy class.*.php)
|
||||||
|
- Zaktualizowany composer.json z PSR-4 mapowaniem
|
||||||
|
- Wszystkie entry pointy używają jednego autoloadera
|
||||||
|
</objective>
|
||||||
|
|
||||||
|
<context>
|
||||||
|
## Project Context
|
||||||
|
@.paul/PROJECT.md
|
||||||
|
@.paul/ROADMAP.md
|
||||||
|
|
||||||
|
## Source Files
|
||||||
|
@composer.json
|
||||||
|
@index.php (zawiera __autoload_my_classes)
|
||||||
|
@admin/index.php (zawiera duplikat __autoload_my_classes)
|
||||||
|
@ajax.php, api.php, cron.php, download.php (kolejne duplikaty)
|
||||||
|
|
||||||
|
## Reference
|
||||||
|
shopPRO composer.json — PSR-4 mapping: Domain\, Admin\, Frontend\, Shared\ → autoload/
|
||||||
|
</context>
|
||||||
|
|
||||||
|
<acceptance_criteria>
|
||||||
|
|
||||||
|
## AC-1: Centralny autoloader
|
||||||
|
```gherkin
|
||||||
|
Given plik autoload/autoloader.php istnieje
|
||||||
|
When jest załadowany przez require_once
|
||||||
|
Then rejestruje spl_autoload_register z obsługą zarówno PSR-4 (ClassName.php) jak i legacy (class.ClassName.php)
|
||||||
|
```
|
||||||
|
|
||||||
|
## AC-2: composer.json PSR-4
|
||||||
|
```gherkin
|
||||||
|
Given composer.json ma sekcję autoload.psr-4
|
||||||
|
When uruchomię composer dump-autoload
|
||||||
|
Then namespace'y Domain\, Shared\, Admin\, Frontend\ mapują do autoload/Domain/, autoload/Shared/, autoload/Admin/, autoload/Frontend/
|
||||||
|
```
|
||||||
|
|
||||||
|
## AC-3: Entry pointy używają centralnego autoloadera
|
||||||
|
```gherkin
|
||||||
|
Given index.php, admin/index.php, ajax.php, api.php, cron.php, download.php
|
||||||
|
When sprawdzę ich kod
|
||||||
|
Then każdy zawiera require_once do autoload/autoloader.php (lub ../autoload/autoloader.php)
|
||||||
|
And żaden nie zawiera zduplikowanej funkcji __autoload_my_classes
|
||||||
|
```
|
||||||
|
|
||||||
|
## AC-4: Istniejące klasy działają
|
||||||
|
```gherkin
|
||||||
|
Given klasy Domain\Articles\ArticlesRepository, Shared\Cache\CacheHandler etc. istnieją
|
||||||
|
When autoloader próbuje je załadować
|
||||||
|
Then klasy ładują się poprawnie (brak Fatal Error)
|
||||||
|
```
|
||||||
|
|
||||||
|
</acceptance_criteria>
|
||||||
|
|
||||||
|
<tasks>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 1: Utworzenie centralnego autoloadera</name>
|
||||||
|
<files>autoload/autoloader.php</files>
|
||||||
|
<action>
|
||||||
|
Utworzyć plik autoload/autoloader.php:
|
||||||
|
- Funkcja __autoload_my_classes($class) obsługująca:
|
||||||
|
1. Zamiana namespace separator \ na /
|
||||||
|
2. Próba załadowania: autoload/{path}/class.{ClassName}.php (legacy)
|
||||||
|
3. Próba załadowania: autoload/{path}/{ClassName}.php (PSR-4)
|
||||||
|
- spl_autoload_register('__autoload_my_classes')
|
||||||
|
- Bazowy katalog ustalany przez __DIR__ . '/' (relatywnie do autoload/)
|
||||||
|
- Obsługa klas bez namespace (legacy) — szukanie w autoload/class.{name}.php
|
||||||
|
|
||||||
|
Wzorować się na istniejącej logice z index.php, ale:
|
||||||
|
- Używać __DIR__ zamiast ścieżek relatywnych do entry pointa
|
||||||
|
- Jeden plik obsługuje WSZYSTKIE entry pointy
|
||||||
|
</action>
|
||||||
|
<verify>Sprawdzić że plik istnieje i zawiera spl_autoload_register</verify>
|
||||||
|
<done>AC-1 satisfied: Centralny autoloader z obsługą PSR-4 i legacy</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 2: Aktualizacja composer.json</name>
|
||||||
|
<files>composer.json</files>
|
||||||
|
<action>
|
||||||
|
Dodać sekcję autoload.psr-4 do composer.json:
|
||||||
|
```json
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Domain\\": "autoload/Domain/",
|
||||||
|
"Shared\\": "autoload/Shared/",
|
||||||
|
"Admin\\": "autoload/Admin/",
|
||||||
|
"Frontend\\": "autoload/Frontend/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Zachować istniejący autoload-dev.
|
||||||
|
</action>
|
||||||
|
<verify>Sprawdzić że composer.json zawiera poprawne mapowanie PSR-4</verify>
|
||||||
|
<done>AC-2 satisfied: composer.json z PSR-4 mapowaniem</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 3: Migracja entry pointów</name>
|
||||||
|
<files>index.php, admin/index.php, ajax.php, api.php, cron.php, download.php</files>
|
||||||
|
<action>
|
||||||
|
W każdym entry poincie:
|
||||||
|
1. USUNĄĆ definicję funkcji __autoload_my_classes() i jej spl_autoload_register
|
||||||
|
2. DODAĆ na początku (po <?php): require_once __DIR__ . '/autoload/autoloader.php';
|
||||||
|
Dla admin/index.php: require_once __DIR__ . '/../autoload/autoloader.php';
|
||||||
|
3. Zachować resztę kodu bez zmian (config.php, medoo, session etc.)
|
||||||
|
|
||||||
|
NIE zmieniać niczego innego w tych plikach — tylko autoloader.
|
||||||
|
</action>
|
||||||
|
<verify>Grep po wszystkich entry pointach: brak __autoload_my_classes definicji, jest require autoloader.php</verify>
|
||||||
|
<done>AC-3 satisfied: Wszystkie entry pointy używają centralnego autoloadera</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
</tasks>
|
||||||
|
|
||||||
|
<boundaries>
|
||||||
|
|
||||||
|
## DO NOT CHANGE
|
||||||
|
- autoload/Domain/* (istniejące klasy Domain — nie modyfikować)
|
||||||
|
- autoload/Shared/* (istniejące klasy Shared — nie modyfikować)
|
||||||
|
- config.php (konfiguracja bazy danych)
|
||||||
|
- libraries/* (zewnętrzne biblioteki)
|
||||||
|
|
||||||
|
## SCOPE LIMITS
|
||||||
|
- Tylko autoloader — nie refaktoryzować żadnych klas
|
||||||
|
- Nie dodawać nowych klas Admin\ ani Frontend\ (to w kolejnych fazach)
|
||||||
|
- Nie zmieniać logiki biznesowej w entry pointach
|
||||||
|
|
||||||
|
</boundaries>
|
||||||
|
|
||||||
|
<verification>
|
||||||
|
Before declaring plan complete:
|
||||||
|
- [ ] autoload/autoloader.php istnieje i zawiera spl_autoload_register
|
||||||
|
- [ ] composer.json ma sekcję autoload.psr-4 z 4 namespace'ami
|
||||||
|
- [ ] Żaden entry point nie zawiera zduplikowanej funkcji __autoload_my_classes
|
||||||
|
- [ ] Wszystkie entry pointy mają require_once autoloader.php
|
||||||
|
- [ ] Istniejące testy PHPUnit przechodzą (jeśli są)
|
||||||
|
- All acceptance criteria met
|
||||||
|
</verification>
|
||||||
|
|
||||||
|
<success_criteria>
|
||||||
|
- Centralny autoloader działa dla PSR-4 i legacy class.*.php
|
||||||
|
- Wszystkie entry pointy korzystają z jednego autoloadera
|
||||||
|
- Zero regresji — istniejący kod działa bez zmian
|
||||||
|
</success_criteria>
|
||||||
|
|
||||||
|
<output>
|
||||||
|
After completion, create `.paul/phases/01-infrastructure/01-01-SUMMARY.md`
|
||||||
|
</output>
|
||||||
110
.paul/phases/01-infrastructure/01-01-SUMMARY.md
Normal file
110
.paul/phases/01-infrastructure/01-01-SUMMARY.md
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
---
|
||||||
|
phase: 01-infrastructure
|
||||||
|
plan: 01
|
||||||
|
subsystem: infra
|
||||||
|
tags: [autoloader, psr-4, composer]
|
||||||
|
|
||||||
|
requires: []
|
||||||
|
provides:
|
||||||
|
- Centralny hybrydowy autoloader (PSR-4 + legacy)
|
||||||
|
- composer.json z PSR-4 mapowaniem namespace'ów
|
||||||
|
affects: [all future phases - every new class uses this autoloader]
|
||||||
|
|
||||||
|
tech-stack:
|
||||||
|
added: []
|
||||||
|
patterns: [centralny autoloader z __DIR__, hybrydowy PSR-4 + legacy]
|
||||||
|
|
||||||
|
key-files:
|
||||||
|
created: [autoload/autoloader.php]
|
||||||
|
modified: [composer.json, index.php, admin/index.php, admin/ajax.php, ajax.php, api.php, cron.php, download.php]
|
||||||
|
|
||||||
|
key-decisions:
|
||||||
|
- "Centralny autoloader zamiast duplikatów w entry pointach (ulepszenie vs shopPRO)"
|
||||||
|
- "Savant3 special case przeniesiony do centralnego autoloadera"
|
||||||
|
|
||||||
|
patterns-established:
|
||||||
|
- "Jeden autoloader dla wszystkich entry pointów — __DIR__ based paths"
|
||||||
|
- "Hybrydowe ładowanie: legacy class.*.php → PSR-4 ClassName.php"
|
||||||
|
|
||||||
|
duration: ~10min
|
||||||
|
completed: 2026-04-04
|
||||||
|
---
|
||||||
|
|
||||||
|
# Phase 1 Plan 01: Infrastructure & Autoloader Summary
|
||||||
|
|
||||||
|
**Centralny hybrydowy autoloader (PSR-4 + legacy) zastępujący 7 zduplikowanych kopii w entry pointach.**
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
| Metric | Value |
|
||||||
|
|--------|-------|
|
||||||
|
| Duration | ~10min |
|
||||||
|
| Completed | 2026-04-04 |
|
||||||
|
| Tasks | 3 completed |
|
||||||
|
| Files modified | 8 |
|
||||||
|
|
||||||
|
## Acceptance Criteria Results
|
||||||
|
|
||||||
|
| Criterion | Status | Notes |
|
||||||
|
|-----------|--------|-------|
|
||||||
|
| AC-1: Centralny autoloader | Pass | autoload/autoloader.php z spl_autoload_register, __DIR__ paths |
|
||||||
|
| AC-2: composer.json PSR-4 | Pass | Domain\, Shared\, Admin\, Frontend\ mapped |
|
||||||
|
| AC-3: Entry pointy zmigrowane | Pass | 7 entry pointów, 0 duplikatów __autoload_my_classes |
|
||||||
|
| AC-4: Istniejące klasy działają | Pass | Autoloader obsługuje legacy + PSR-4 format |
|
||||||
|
|
||||||
|
## Accomplishments
|
||||||
|
|
||||||
|
- Utworzono centralny `autoload/autoloader.php` z obsługą legacy (class.*.php) i PSR-4 (ClassName.php)
|
||||||
|
- Zaktualizowano `composer.json` z PSR-4 mapowaniem dla 4 namespace'ów
|
||||||
|
- Zmigrowano 7 entry pointów (index.php, admin/index.php, admin/ajax.php, ajax.php, api.php, cron.php, download.php)
|
||||||
|
|
||||||
|
## Files Created/Modified
|
||||||
|
|
||||||
|
| File | Change | Purpose |
|
||||||
|
|------|--------|---------|
|
||||||
|
| `autoload/autoloader.php` | Created | Centralny hybrydowy autoloader |
|
||||||
|
| `composer.json` | Modified | PSR-4 mapping dla Domain\, Shared\, Admin\, Frontend\ |
|
||||||
|
| `index.php` | Modified | require_once autoloader.php |
|
||||||
|
| `admin/index.php` | Modified | require_once ../autoloader.php |
|
||||||
|
| `admin/ajax.php` | Modified | require_once ../autoloader.php (Savant3 przeniesiony) |
|
||||||
|
| `ajax.php` | Modified | require_once autoloader.php |
|
||||||
|
| `api.php` | Modified | require_once autoloader.php |
|
||||||
|
| `cron.php` | Modified | require_once autoloader.php |
|
||||||
|
| `download.php` | Modified | require_once autoloader.php |
|
||||||
|
|
||||||
|
## Decisions Made
|
||||||
|
|
||||||
|
| Decision | Rationale | Impact |
|
||||||
|
|----------|-----------|--------|
|
||||||
|
| Centralny autoloader (vs duplikaty jak w shopPRO) | DRY, łatwiejsze utrzymanie, jednorazowa poprawka | Ulepszenie vs shopPRO — notatka dodana do shopPRO/docs |
|
||||||
|
| Savant3 special case w centralnym autoloaderze | Był tylko w admin/ajax.php, powinien działać globalnie | Brak regresji |
|
||||||
|
|
||||||
|
## Deviations from Plan
|
||||||
|
|
||||||
|
### Summary
|
||||||
|
|
||||||
|
| Type | Count | Impact |
|
||||||
|
|------|-------|--------|
|
||||||
|
| Scope additions | 1 | Minimal — admin/ajax.php (7th entry point) |
|
||||||
|
|
||||||
|
Plan zakładał 6 entry pointów, ale znaleziono 7 (admin/ajax.php nie był wymieniony w planie). Zmigrowany bez problemów.
|
||||||
|
|
||||||
|
## Issues Encountered
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
## Next Phase Readiness
|
||||||
|
|
||||||
|
**Ready:**
|
||||||
|
- Autoloader obsługuje wszystkie namespace'y potrzebne dla faz 2-19
|
||||||
|
- Nowe klasy w Admin\, Frontend\ będą automatycznie ładowane
|
||||||
|
|
||||||
|
**Concerns:**
|
||||||
|
- AC-4 zweryfikowane statycznie (kod autoloadera) — runtime test wymaga uruchomienia aplikacji
|
||||||
|
|
||||||
|
**Blockers:**
|
||||||
|
- None
|
||||||
|
|
||||||
|
---
|
||||||
|
*Phase: 01-infrastructure, Plan: 01*
|
||||||
|
*Completed: 2026-04-04*
|
||||||
182
.paul/phases/02-shared-email-security/02-01-PLAN.md
Normal file
182
.paul/phases/02-shared-email-security/02-01-PLAN.md
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
---
|
||||||
|
phase: 02-shared-email-security
|
||||||
|
plan: 01
|
||||||
|
type: execute
|
||||||
|
wave: 1
|
||||||
|
depends_on: ["01-01"]
|
||||||
|
files_modified:
|
||||||
|
- autoload/Shared/Email/Email.php
|
||||||
|
- autoload/Shared/Security/CsrfToken.php
|
||||||
|
- autoload/Shared/Helpers/Helpers.php
|
||||||
|
autonomous: true
|
||||||
|
delegation: off
|
||||||
|
---
|
||||||
|
|
||||||
|
<objective>
|
||||||
|
## Goal
|
||||||
|
Utworzyć Shared\Email\Email i Shared\Security\CsrfToken wzorując się na shopPRO. Przenieść logikę z Helpers::send_email() i Helpers::get_token()/is_token_valid() do dedykowanych klas. Zachować wrappery w Helpers dla kompatybilności.
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
Email i Security to brakujące moduły Shared potrzebne przed refaktoryzacją Admin i Frontend kontrolerów. CsrfToken z kryptograficznie bezpiecznym tokenem zastąpi słaby sha1(mt_rand()).
|
||||||
|
|
||||||
|
## Output
|
||||||
|
- autoload/Shared/Email/Email.php — klasa email z PHPMailer
|
||||||
|
- autoload/Shared/Security/CsrfToken.php — CSRF z random_bytes + hash_equals
|
||||||
|
- Wrappery w Helpers.php delegujące do nowych klas
|
||||||
|
</objective>
|
||||||
|
|
||||||
|
<context>
|
||||||
|
## Project Context
|
||||||
|
@.paul/PROJECT.md
|
||||||
|
@.paul/ROADMAP.md
|
||||||
|
|
||||||
|
## Prior Work
|
||||||
|
@.paul/phases/01-infrastructure/01-01-SUMMARY.md — autoloader gotowy, PSR-4 działa
|
||||||
|
|
||||||
|
## Source Files
|
||||||
|
@autoload/Shared/Helpers/Helpers.php — zawiera send_email(), get_token(), is_token_valid()
|
||||||
|
|
||||||
|
## Reference
|
||||||
|
shopPRO autoload/Shared/Email/Email.php — docelowa implementacja
|
||||||
|
shopPRO autoload/Shared/Security/CsrfToken.php — docelowa implementacja
|
||||||
|
</context>
|
||||||
|
|
||||||
|
<acceptance_criteria>
|
||||||
|
|
||||||
|
## AC-1: Email class
|
||||||
|
```gherkin
|
||||||
|
Given plik autoload/Shared/Email/Email.php istnieje
|
||||||
|
When załaduję klasę Shared\Email\Email
|
||||||
|
Then klasa ma metody: send(), email_check(), load_by_name()
|
||||||
|
And send() używa PHPMailer do wysyłki maili
|
||||||
|
```
|
||||||
|
|
||||||
|
## AC-2: CsrfToken class
|
||||||
|
```gherkin
|
||||||
|
Given plik autoload/Shared/Security/CsrfToken.php istnieje
|
||||||
|
When załaduję klasę Shared\Security\CsrfToken
|
||||||
|
Then klasa ma statyczne metody: getToken(), validate(), regenerate()
|
||||||
|
And getToken() używa bin2hex(random_bytes(32))
|
||||||
|
And validate() używa hash_equals() (timing-safe)
|
||||||
|
```
|
||||||
|
|
||||||
|
## AC-3: Wrappery w Helpers
|
||||||
|
```gherkin
|
||||||
|
Given Helpers::send_email() i Helpers::get_token() nadal istnieją
|
||||||
|
When wywołam je z istniejącego kodu
|
||||||
|
Then delegują do nowych klas (Shared\Email\Email i Shared\Security\CsrfToken)
|
||||||
|
And istniejący kod działa bez zmian
|
||||||
|
```
|
||||||
|
|
||||||
|
</acceptance_criteria>
|
||||||
|
|
||||||
|
<tasks>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 1: Utworzenie Shared\Email\Email</name>
|
||||||
|
<files>autoload/Shared/Email/Email.php</files>
|
||||||
|
<action>
|
||||||
|
Utworzyć klasę Email wzorowaną na shopPRO:
|
||||||
|
- namespace Shared\Email
|
||||||
|
- Właściwość $table = 'pp_newsletter_templates'
|
||||||
|
- Właściwość $text (treść maila), $headers, $newsletter_headers, $newsletter_footers
|
||||||
|
- Metoda load_by_name(string $name) — ładuje szablon z DB
|
||||||
|
- Metoda email_check($email) — walidacja filter_var
|
||||||
|
- Metoda send(string $email, string $subject, bool $newsletter_headers = false, string $file = null)
|
||||||
|
- Używa PHPMailer (require_once z libraries/)
|
||||||
|
- Regex do naprawy relatywnych URL w obrazkach/linkach
|
||||||
|
- Obsługa załączników
|
||||||
|
- Return $mail->Send()
|
||||||
|
|
||||||
|
WAŻNE: Sprawdzić w Helpers.php jak wygląda obecna implementacja send_email()
|
||||||
|
i przenieść tę logikę do nowej klasy, dostosowując do wzorca shopPRO.
|
||||||
|
PHP < 8.0 — brak named args, union types, match.
|
||||||
|
</action>
|
||||||
|
<verify>Sprawdzić że plik istnieje, ma namespace Shared\Email, klasę Email z metodami send(), email_check()</verify>
|
||||||
|
<done>AC-1 satisfied: Email class z PHPMailer</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 2: Utworzenie Shared\Security\CsrfToken</name>
|
||||||
|
<files>autoload/Shared/Security/CsrfToken.php</files>
|
||||||
|
<action>
|
||||||
|
Utworzyć klasę CsrfToken wzorowaną na shopPRO:
|
||||||
|
- namespace Shared\Security
|
||||||
|
- const SESSION_KEY = 'csrf_token'
|
||||||
|
- static getToken(): string
|
||||||
|
- Jeśli brak tokenu w sesji → generuje bin2hex(random_bytes(32))
|
||||||
|
- Zapisuje w $_SESSION[self::SESSION_KEY]
|
||||||
|
- Zwraca token
|
||||||
|
- static validate(string $token): bool
|
||||||
|
- Porównuje z $_SESSION[self::SESSION_KEY] używając hash_equals()
|
||||||
|
- Return true/false (NIE usuwać tokenu po walidacji — to robi regenerate())
|
||||||
|
- static regenerate(): void
|
||||||
|
- Wymusza nowy token: unset($_SESSION[self::SESSION_KEY])
|
||||||
|
|
||||||
|
PHP < 8.0 — brak named args, union types, match.
|
||||||
|
</action>
|
||||||
|
<verify>Sprawdzić że plik istnieje, ma namespace Shared\Security, klasę CsrfToken z metodami getToken(), validate(), regenerate()</verify>
|
||||||
|
<done>AC-2 satisfied: CsrfToken z random_bytes + hash_equals</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 3: Wrappery w Helpers.php</name>
|
||||||
|
<files>autoload/Shared/Helpers/Helpers.php</files>
|
||||||
|
<action>
|
||||||
|
W klasie Helpers:
|
||||||
|
1. Metoda send_email() — zamienić ciało na delegację:
|
||||||
|
$email = new \Shared\Email\Email();
|
||||||
|
$email->text = $text;
|
||||||
|
return $email->send($to, $subject, false, $file);
|
||||||
|
2. Metoda get_token() — zamienić ciało na delegację:
|
||||||
|
return \Shared\Security\CsrfToken::getToken();
|
||||||
|
3. Metoda is_token_valid() — zamienić ciało na delegację:
|
||||||
|
return \Shared\Security\CsrfToken::validate($token);
|
||||||
|
|
||||||
|
Zachować sygnatury metod identyczne — żaden calling code się nie zmienia.
|
||||||
|
NIE usuwać metod — to wrappery dla kompatybilności wstecznej.
|
||||||
|
NIE zmieniać żadnych innych metod w Helpers.
|
||||||
|
</action>
|
||||||
|
<verify>Sprawdzić że Helpers::send_email(), get_token(), is_token_valid() delegują do nowych klas</verify>
|
||||||
|
<done>AC-3 satisfied: Wrappery delegują, istniejący kod działa bez zmian</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
</tasks>
|
||||||
|
|
||||||
|
<boundaries>
|
||||||
|
|
||||||
|
## DO NOT CHANGE
|
||||||
|
- autoload/Domain/* (nie ruszać repositories)
|
||||||
|
- autoload/Shared/Cache/* (nie ruszać)
|
||||||
|
- autoload/Shared/Html/* (nie ruszać)
|
||||||
|
- autoload/Shared/Image/* (nie ruszać)
|
||||||
|
- autoload/Shared/Tpl/* (nie ruszać)
|
||||||
|
- config.php, libraries/* (nie ruszać)
|
||||||
|
|
||||||
|
## SCOPE LIMITS
|
||||||
|
- Tylko Email i Security — nie refaktoryzować innych metod Helpers
|
||||||
|
- Nie zmieniać callerów (admin/, front/) — oni nadal używają Helpers::
|
||||||
|
- Nie dodawać nowych zależności poza tym co już jest w libraries/
|
||||||
|
|
||||||
|
</boundaries>
|
||||||
|
|
||||||
|
<verification>
|
||||||
|
Before declaring plan complete:
|
||||||
|
- [ ] autoload/Shared/Email/Email.php istnieje z namespace Shared\Email
|
||||||
|
- [ ] autoload/Shared/Security/CsrfToken.php istnieje z namespace Shared\Security
|
||||||
|
- [ ] Helpers::send_email() deleguje do Email class
|
||||||
|
- [ ] Helpers::get_token() deleguje do CsrfToken::getToken()
|
||||||
|
- [ ] Helpers::is_token_valid() deleguje do CsrfToken::validate()
|
||||||
|
- [ ] Żadne inne metody w Helpers nie zostały zmienione
|
||||||
|
- All acceptance criteria met
|
||||||
|
</verification>
|
||||||
|
|
||||||
|
<success_criteria>
|
||||||
|
- Email i CsrfToken klasy utworzone z poprawnymi namespace'ami
|
||||||
|
- Wrappery w Helpers zachowują kompatybilność wsteczną
|
||||||
|
- Zero regresji — istniejący kod używający Helpers:: działa bez zmian
|
||||||
|
</success_criteria>
|
||||||
|
|
||||||
|
<output>
|
||||||
|
After completion, create `.paul/phases/02-shared-email-security/02-01-SUMMARY.md`
|
||||||
|
</output>
|
||||||
108
.paul/phases/02-shared-email-security/02-01-SUMMARY.md
Normal file
108
.paul/phases/02-shared-email-security/02-01-SUMMARY.md
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
---
|
||||||
|
phase: 02-shared-email-security
|
||||||
|
plan: 01
|
||||||
|
subsystem: infra
|
||||||
|
tags: [email, phpmailer, csrf, security, shared]
|
||||||
|
|
||||||
|
requires:
|
||||||
|
- phase: 01-infrastructure
|
||||||
|
provides: centralny autoloader PSR-4
|
||||||
|
provides:
|
||||||
|
- Shared\Email\Email — klasa email z PHPMailer
|
||||||
|
- Shared\Security\CsrfToken — CSRF z random_bytes + hash_equals
|
||||||
|
- Wrappery w Helpers dla kompatybilności wstecznej
|
||||||
|
affects: [phase-06 admin-base, phase-17 users-security]
|
||||||
|
|
||||||
|
tech-stack:
|
||||||
|
added: []
|
||||||
|
patterns: [wrapper delegation dla Helpers, static utility class dla CsrfToken]
|
||||||
|
|
||||||
|
key-files:
|
||||||
|
created: [autoload/Shared/Email/Email.php, autoload/Shared/Security/CsrfToken.php]
|
||||||
|
modified: [autoload/Shared/Helpers/Helpers.php]
|
||||||
|
|
||||||
|
key-decisions:
|
||||||
|
- "CsrfToken: single token per session (shopPRO pattern) zamiast multi-token array"
|
||||||
|
- "Email: PHPMailer require via __DIR__ absolute paths"
|
||||||
|
- "Helpers::get_token() wywołuje regenerate() + getToken() — zachowuje semantykę jednorazowego tokenu"
|
||||||
|
|
||||||
|
patterns-established:
|
||||||
|
- "Wrapper delegation: stara metoda w Helpers deleguje do nowej klasy"
|
||||||
|
- "Security: random_bytes(32) + hash_equals() jako standard"
|
||||||
|
|
||||||
|
duration: ~8min
|
||||||
|
completed: 2026-04-04
|
||||||
|
---
|
||||||
|
|
||||||
|
# Phase 2 Plan 01: Shared Email + Security Summary
|
||||||
|
|
||||||
|
**Shared\Email\Email z PHPMailer i Shared\Security\CsrfToken z kryptograficznie bezpiecznym tokenem, plus wrappery w Helpers.**
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
| Metric | Value |
|
||||||
|
|--------|-------|
|
||||||
|
| Duration | ~8min |
|
||||||
|
| Completed | 2026-04-04 |
|
||||||
|
| Tasks | 3 completed |
|
||||||
|
| Files modified | 3 |
|
||||||
|
|
||||||
|
## Acceptance Criteria Results
|
||||||
|
|
||||||
|
| Criterion | Status | Notes |
|
||||||
|
|-----------|--------|-------|
|
||||||
|
| AC-1: Email class | Pass | send(), email_check(), load_by_name(), PHPMailer |
|
||||||
|
| AC-2: CsrfToken class | Pass | random_bytes(32), hash_equals(), regenerate() |
|
||||||
|
| AC-3: Wrappery w Helpers | Pass | send_email(), get_token(), is_token_valid() delegują |
|
||||||
|
|
||||||
|
## Accomplishments
|
||||||
|
|
||||||
|
- Utworzono `Shared\Email\Email` z pełną obsługą PHPMailer, załączników, reply-to, regex URL fix
|
||||||
|
- Utworzono `Shared\Security\CsrfToken` z kryptograficznie bezpiecznym tokenem (upgrade z sha1/mt_rand)
|
||||||
|
- Wrappery w Helpers zachowują pełną kompatybilność wsteczną
|
||||||
|
|
||||||
|
## Files Created/Modified
|
||||||
|
|
||||||
|
| File | Change | Purpose |
|
||||||
|
|------|--------|---------|
|
||||||
|
| `autoload/Shared/Email/Email.php` | Created | OOP Email z PHPMailer |
|
||||||
|
| `autoload/Shared/Security/CsrfToken.php` | Created | CSRF token management |
|
||||||
|
| `autoload/Shared/Helpers/Helpers.php` | Modified | Wrappery delegujące do nowych klas |
|
||||||
|
|
||||||
|
## Decisions Made
|
||||||
|
|
||||||
|
| Decision | Rationale | Impact |
|
||||||
|
|----------|-----------|--------|
|
||||||
|
| Single token per session (CsrfToken) | Wzór shopPRO, prostsze, bezpieczniejsze | Legacy multi-token array zastąpiony |
|
||||||
|
| get_token() = regenerate() + getToken() | Zachowuje semantykę: każde wywołanie daje nowy token | Kompatybilność z kodem który zakłada jednorazowy token |
|
||||||
|
| PHPMailer require via __DIR__ | Absolute paths, działa z każdego entry pointa | Eliminuje problem relatywnych ścieżek |
|
||||||
|
|
||||||
|
## Deviations from Plan
|
||||||
|
|
||||||
|
### Summary
|
||||||
|
|
||||||
|
| Type | Count | Impact |
|
||||||
|
|------|-------|--------|
|
||||||
|
| Scope additions | 1 | Minimal — Email.send() ma $replay param z cmsPRO |
|
||||||
|
|
||||||
|
Email.send() w cmsPRO ma dodatkowy parametr `$replay` (reply-to) którego shopPRO nie ma. Zachowano dla kompatybilności z istniejącym kodem.
|
||||||
|
|
||||||
|
## Issues Encountered
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
## Next Phase Readiness
|
||||||
|
|
||||||
|
**Ready:**
|
||||||
|
- Shared layer kompletny (Cache, Helpers, Html, Image, Tpl, Email, Security)
|
||||||
|
- Fazy 3-5 (Domain repositories) mogą startować
|
||||||
|
|
||||||
|
**Concerns:**
|
||||||
|
- None
|
||||||
|
|
||||||
|
**Blockers:**
|
||||||
|
- None
|
||||||
|
|
||||||
|
---
|
||||||
|
*Phase: 02-shared-email-security, Plan: 01*
|
||||||
|
*Completed: 2026-04-04*
|
||||||
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*
|
||||||
1
.phpunit.result.cache
Normal file
1
.phpunit.result.cache
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":2,"defects":{"Tests\\Unit\\Domain\\Settings\\SettingsRepositoryTest::testAllSettingsReturnsEmptyArrayWhenDbReturnsNull":8,"Tests\\Unit\\Domain\\Settings\\SettingsRepositoryTest::testAllSettingsUsesCache":8},"times":{"Tests\\Unit\\Domain\\Languages\\LanguagesRepositoryTest::testLanguagesListReturnsArray":0.041,"Tests\\Unit\\Domain\\Languages\\LanguagesRepositoryTest::testLanguagesListReturnsEmptyWhenNull":0.001,"Tests\\Unit\\Domain\\Languages\\LanguagesRepositoryTest::testLanguageDetailsReturnsRowWhenFound":0,"Tests\\Unit\\Domain\\Languages\\LanguagesRepositoryTest::testLanguageDetailsReturnsNullWhenNotFound":0.001,"Tests\\Unit\\Domain\\Languages\\LanguagesRepositoryTest::testActiveLanguagesQueriesDbAndCaches":0.001,"Tests\\Unit\\Domain\\Languages\\LanguagesRepositoryTest::testActiveLanguagesReturnsEmptyWhenNull":0,"Tests\\Unit\\Domain\\Languages\\LanguagesRepositoryTest::testMaxOrderReturnsInteger":0,"Tests\\Unit\\Domain\\Languages\\LanguagesRepositoryTest::testTranslationDeleteReturnsTrueOnSuccess":0.001,"Tests\\Unit\\Domain\\Languages\\LanguagesRepositoryTest::testTranslationDeleteReturnsFalseOnFailure":0.001,"Tests\\Unit\\Domain\\Languages\\LanguagesRepositoryTest::testTranslationDetailsReturnsRowOrNull":0,"Tests\\Unit\\Domain\\Settings\\SettingsRepositoryTest::testAllSettingsReturnsMappedArray":0.001,"Tests\\Unit\\Domain\\Settings\\SettingsRepositoryTest::testAllSettingsReturnsEmptyArrayWhenDbReturnsNull":0,"Tests\\Unit\\Domain\\Settings\\SettingsRepositoryTest::testAllSettingsUsesCache":0,"Tests\\Unit\\Domain\\Settings\\SettingsRepositoryTest::testUpdateCallsDbUpdateWhenParamExists":0.001,"Tests\\Unit\\Domain\\Settings\\SettingsRepositoryTest::testUpdateCallsDbInsertWhenParamMissing":0,"Tests\\Unit\\Domain\\Settings\\SettingsRepositoryTest::testVisitCounterReturnsValue":0,"Tests\\Unit\\Domain\\Settings\\SettingsRepositoryTest::testVisitCounterReturnsNullWhenEmpty":0,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testFindReturnsUserArray":0.001,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testFindReturnsNullWhenNotFound":0,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testFindByLoginReturnsUser":0.002,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testAllReturnsArray":0.001,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testAllReturnsEmptyArrayWhenNull":0,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testHasPrivilegeReturnsTrueForAdminUser":0,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testHasPrivilegeReturnsTrueWhenPrivilegeExists":0,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testHasPrivilegeReturnsFalseWhenPrivilegeMissing":0,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testLogonReturnsZeroWhenUserNotFound":0,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testLogonReturnsMinusOneWhenAccountBlocked":0.001,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testLogonReturnsOneOnSuccess":0.001,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testIsLoginTakenReturnsTrueWhenExists":0,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testIsLoginTakenReturnsFalseWhenFree":0,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testVerifyTwofaCodeReturnsFalseWhenUserNotFound":0,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testVerifyTwofaCodeReturnsFalseWhenTooManyFailedAttempts":0.075,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testVerifyTwofaCodeReturnsFalseWhenExpired":0.073,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testVerifyTwofaCodeReturnsTrueOnValidCode":0.148,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testDeleteReturnsTrueOnSuccess":0,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testSaveReturnsErrorWhenPasswordTooShort":0,"Tests\\Unit\\Domain\\User\\UserRepositoryTest::testSaveReturnsErrorWhenPasswordsMismatch":0}}
|
||||||
@@ -45,7 +45,9 @@ ignored_paths: []
|
|||||||
# Added on 2025-04-18
|
# Added on 2025-04-18
|
||||||
read_only: false
|
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.
|
# Below is the complete list of tools for convenience.
|
||||||
# To make sure you have the latest list of tools, and to view their descriptions,
|
# To make sure you have the latest list of tools, and to view their descriptions,
|
||||||
# execute `uv run scripts/print_tool_overview.py`.
|
# 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.
|
# * `write_memory`: Writes a named memory (for future reference) to Serena's project-specific memory store.
|
||||||
excluded_tools: []
|
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: []
|
included_optional_tools: []
|
||||||
|
|
||||||
# fixed set of tools to use as the base tool set (if non-empty), replacing Serena's default set of 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).
|
# (contrary to the memories, which are loaded on demand).
|
||||||
initial_prompt: ""
|
initial_prompt: ""
|
||||||
|
|
||||||
# override of the corresponding setting in serena_config.yml, see the documentation there.
|
# time budget (seconds) per tool call for the retrieval of additional symbol information
|
||||||
# If null or missing, the value from the global config is used.
|
# 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:
|
symbol_info_budget:
|
||||||
|
|
||||||
# The language backend to use for this project.
|
# 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
|
# Note: the backend is fixed at startup. If a project with a different backend
|
||||||
# is activated post-init, an error will be returned.
|
# is activated post-init, an error will be returned.
|
||||||
language_backend:
|
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: {}
|
||||||
|
|||||||
8
.vscode/ftp-kr.json
vendored
8
.vscode/ftp-kr.json
vendored
@@ -12,6 +12,12 @@
|
|||||||
"ignoreRemoteModification": true,
|
"ignoreRemoteModification": true,
|
||||||
"ignore": [
|
"ignore": [
|
||||||
".git",
|
".git",
|
||||||
"/.vscode"
|
"/.vscode",
|
||||||
|
"/.claude",
|
||||||
|
"/.serena",
|
||||||
|
"/docs",
|
||||||
|
"AGENTS.md",
|
||||||
|
"CLAUDE.md",
|
||||||
|
"/.paul"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
41
AGENTS.md
Normal file
41
AGENTS.md
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# Workflow
|
||||||
|
|
||||||
|
## Sposób pracy
|
||||||
|
- Pisz do mnie po polsku, zwięźle i krótko, ale merytorycznie
|
||||||
|
|
||||||
|
## Zasady pisania kodu
|
||||||
|
- Kod ma być czytelny „dla obcego”: jasne nazwy, mało magii
|
||||||
|
- Brak „skrótów na szybko” typu logika w widokach, copy-paste, losowe helpery bez spójności
|
||||||
|
- Każda funkcja/klasa ma mieć jedną odpowiedzialność, zwykle do 30–50 linii (jeśli dłuższe – dzielić)
|
||||||
|
- max 3 poziomy zagnieżdżeń (if/foreach), reszta do osobnych metod
|
||||||
|
- Nazewnictwo:
|
||||||
|
- klasy: PascalCase
|
||||||
|
- metody/zmienne: camelCase
|
||||||
|
- stałe: UPPER_SNAKE_CASE
|
||||||
|
- Zero „skrótologii” w nazwach (np. $d, $tmp, $x1) poza pętlami 2–3 linijki
|
||||||
|
- medoo + prepared statements bez wyjątków (żadnego sklejania SQL stringiem)
|
||||||
|
- XSS: escape w widokach (np. helper e())
|
||||||
|
- CSRF dla formularzy, sensowna obsługa sesji
|
||||||
|
- Kod ma mieć komentarze tylko tam, gdzie wyjaśniają „dlaczego”, nie „co”
|
||||||
|
|
||||||
|
|
||||||
|
## Wprowadzanie zmian
|
||||||
|
- Przeanalizuj wprowadzone zadanie
|
||||||
|
- Jeżeli masz jakieś wątpliwości pytaj
|
||||||
|
- Przedstaw plan
|
||||||
|
- Po akceptacji wdróź plan
|
||||||
|
|
||||||
|
## 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.
|
||||||
13
CLAUDE.md
13
CLAUDE.md
@@ -2,15 +2,4 @@
|
|||||||
|
|
||||||
## KONIEC PRACY
|
## KONIEC PRACY
|
||||||
|
|
||||||
Gdy użytkownik napisze `KONIEC PRACY`, wykonaj kolejno:
|
Gdy użytkownik napisze `KONIEC PRACY`, uruchom komendę `/koniec-pracy`.
|
||||||
|
|
||||||
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.
|
|
||||||
|
|||||||
@@ -1,25 +1,6 @@
|
|||||||
<?
|
<?
|
||||||
error_reporting( E_ALL ^ E_NOTICE ^ E_STRICT ^ E_WARNING ^ E_DEPRECATED );
|
error_reporting( E_ALL ^ E_NOTICE ^ E_STRICT ^ E_WARNING ^ E_DEPRECATED );
|
||||||
function __autoload_my_classes( $classname )
|
require_once __DIR__ . '/../autoload/autoloader.php';
|
||||||
{
|
|
||||||
$q = explode( '\\' , $classname );
|
|
||||||
$c = array_pop( $q );
|
|
||||||
|
|
||||||
if ( $c == 'Savant3' )
|
|
||||||
{
|
|
||||||
require_once( '../autoload/Savant3.php' );
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. Legacy: class.ClassName.php
|
|
||||||
$f = '../autoload/' . implode( '/' , $q ) . '/class.' . $c . '.php';
|
|
||||||
if ( file_exists( $f ) ) { require_once( $f ); return; }
|
|
||||||
|
|
||||||
// 2. PSR-4: ClassName.php
|
|
||||||
$f = '../autoload/' . implode( '/' , $q ) . '/' . $c . '.php';
|
|
||||||
if ( file_exists( $f ) ) require_once( $f );
|
|
||||||
}
|
|
||||||
spl_autoload_register( '__autoload_my_classes' );
|
|
||||||
|
|
||||||
require_once '../config.php';
|
require_once '../config.php';
|
||||||
require_once '../libraries/medoo/medoo.php';
|
require_once '../libraries/medoo/medoo.php';
|
||||||
|
|||||||
@@ -12,20 +12,7 @@ if ( file_exists( 'ip.conf' ) )
|
|||||||
}
|
}
|
||||||
|
|
||||||
error_reporting( E_ALL ^ E_NOTICE ^ E_STRICT ^ E_WARNING ^ E_DEPRECATED );
|
error_reporting( E_ALL ^ E_NOTICE ^ E_STRICT ^ E_WARNING ^ E_DEPRECATED );
|
||||||
function __autoload_my_classes( $classname )
|
require_once __DIR__ . '/../autoload/autoloader.php';
|
||||||
{
|
|
||||||
$q = explode( '\\' , $classname );
|
|
||||||
$c = array_pop( $q );
|
|
||||||
|
|
||||||
// 1. Legacy: class.ClassName.php
|
|
||||||
$f = '../autoload/' . implode( '/' , $q ) . '/class.' . $c . '.php';
|
|
||||||
if ( file_exists( $f ) ) { require_once( $f ); return; }
|
|
||||||
|
|
||||||
// 2. PSR-4: ClassName.php
|
|
||||||
$f = '../autoload/' . implode( '/' , $q ) . '/' . $c . '.php';
|
|
||||||
if ( file_exists( $f ) ) require_once( $f );
|
|
||||||
}
|
|
||||||
spl_autoload_register( '__autoload_my_classes' );
|
|
||||||
|
|
||||||
require_once '../config.php';
|
require_once '../config.php';
|
||||||
require_once '../libraries/medoo/medoo.php';
|
require_once '../libraries/medoo/medoo.php';
|
||||||
|
|||||||
15
ajax.php
15
ajax.php
@@ -1,19 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
error_reporting( E_ALL ^ E_NOTICE ^ E_STRICT ^ E_WARNING ^ E_DEPRECATED );
|
error_reporting( E_ALL ^ E_NOTICE ^ E_STRICT ^ E_WARNING ^ E_DEPRECATED );
|
||||||
function __autoload_my_classes( $classname )
|
require_once __DIR__ . '/autoload/autoloader.php';
|
||||||
{
|
|
||||||
$q = explode( '\\' , $classname );
|
|
||||||
$c = array_pop( $q );
|
|
||||||
|
|
||||||
// 1. Legacy: class.ClassName.php
|
|
||||||
$f = 'autoload/' . implode( '/' , $q ) . '/class.' . $c . '.php';
|
|
||||||
if ( file_exists( $f ) ) { require_once( $f ); return; }
|
|
||||||
|
|
||||||
// 2. PSR-4: ClassName.php
|
|
||||||
$f = 'autoload/' . implode( '/' , $q ) . '/' . $c . '.php';
|
|
||||||
if ( file_exists( $f ) ) require_once( $f );
|
|
||||||
}
|
|
||||||
spl_autoload_register( '__autoload_my_classes' );
|
|
||||||
date_default_timezone_set( 'Europe/Warsaw' );
|
date_default_timezone_set( 'Europe/Warsaw' );
|
||||||
|
|
||||||
require_once 'config.php';
|
require_once 'config.php';
|
||||||
|
|||||||
17
api.php
17
api.php
@@ -1,19 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
error_reporting(E_ALL ^ E_NOTICE ^ E_STRICT ^ E_WARNING ^ E_DEPRECATED);
|
error_reporting( E_ALL ^ E_NOTICE ^ E_STRICT ^ E_WARNING ^ E_DEPRECATED );
|
||||||
function __autoload_my_classes($classname)
|
require_once __DIR__ . '/autoload/autoloader.php';
|
||||||
{
|
|
||||||
$q = explode('\\', $classname);
|
|
||||||
$c = array_pop($q);
|
|
||||||
|
|
||||||
// 1. Legacy: class.ClassName.php
|
|
||||||
$f = 'autoload/' . implode('/', $q) . '/class.' . $c . '.php';
|
|
||||||
if (file_exists($f)) { require_once($f); return; }
|
|
||||||
|
|
||||||
// 2. PSR-4: ClassName.php
|
|
||||||
$f = 'autoload/' . implode('/', $q) . '/' . $c . '.php';
|
|
||||||
if (file_exists($f)) require_once($f);
|
|
||||||
}
|
|
||||||
spl_autoload_register('__autoload_my_classes');
|
|
||||||
date_default_timezone_set('Europe/Warsaw');
|
date_default_timezone_set('Europe/Warsaw');
|
||||||
|
|
||||||
require_once 'config.php';
|
require_once 'config.php';
|
||||||
|
|||||||
648
autoload/Domain/Articles/ArticlesRepository.php
Normal file
648
autoload/Domain/Articles/ArticlesRepository.php
Normal file
@@ -0,0 +1,648 @@
|
|||||||
|
<?php
|
||||||
|
namespace Domain\Articles;
|
||||||
|
|
||||||
|
class ArticlesRepository
|
||||||
|
{
|
||||||
|
private $db;
|
||||||
|
|
||||||
|
public function __construct( $db )
|
||||||
|
{
|
||||||
|
$this->db = $db;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function filesOrderSave( $articleId, $order ): void
|
||||||
|
{
|
||||||
|
$i = 0;
|
||||||
|
$order = explode( ';', $order );
|
||||||
|
|
||||||
|
if ( is_array( $order ) && !empty( $order ) )
|
||||||
|
foreach ( $order as $fileId )
|
||||||
|
$this->db->update( 'pp_articles_files', [ 'o' => (int)$i++ ], [
|
||||||
|
'AND' => [ 'article_id' => $articleId, 'id' => $fileId ]
|
||||||
|
] );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function galleryOrderSave( $articleId, $order ): void
|
||||||
|
{
|
||||||
|
$i = 0;
|
||||||
|
$order = explode( ';', $order );
|
||||||
|
|
||||||
|
if ( is_array( $order ) && !empty( $order ) )
|
||||||
|
foreach ( $order as $imageId )
|
||||||
|
$this->db->update( 'pp_articles_images', [ 'o' => $i++ ], [
|
||||||
|
'AND' => [ 'article_id' => $articleId, 'id' => $imageId ]
|
||||||
|
] );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function additionalParams( $language = 0 )
|
||||||
|
{
|
||||||
|
return $this->db->select( 'pp_articles_additional_params', '*', [ 'AND' => [ 'status' => 1, 'language' => $language ] ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function imageAltChange( $imageId, $imageAlt )
|
||||||
|
{
|
||||||
|
$result = $this->db->update( 'pp_articles_images', [ 'alt' => $imageAlt ], [ 'id' => $imageId ] );
|
||||||
|
\S::delete_cache();
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function articleUrl( $articleId )
|
||||||
|
{
|
||||||
|
$results = $this->db->query(
|
||||||
|
"SELECT seo_link FROM pp_articles_langs AS pal, pp_langs AS pl WHERE lang_id = pl.id AND article_id = " . (int)$articleId . " AND seo_link != '' ORDER BY o ASC LIMIT 1"
|
||||||
|
)->fetchAll();
|
||||||
|
|
||||||
|
if ( !$results[0]['seo_link'] )
|
||||||
|
{
|
||||||
|
$title = $this->articleTitle( $articleId );
|
||||||
|
return 'a-' . $articleId . '-' . \S::seo( $title );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $results[0]['seo_link'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function articlePages( $articleId )
|
||||||
|
{
|
||||||
|
$pagesRepo = new \Domain\Pages\PagesRepository( $this->db );
|
||||||
|
$results = $this->db->query( "SELECT page_id FROM pp_articles_pages WHERE article_id = " . (int)$articleId )->fetchAll();
|
||||||
|
|
||||||
|
if ( is_array( $results ) and !empty( $results ) ) foreach ( $results as $row )
|
||||||
|
{
|
||||||
|
if ( $out == '' )
|
||||||
|
$out .= ' - ';
|
||||||
|
|
||||||
|
$out .= $pagesRepo->pageTitle( $row['page_id'] );
|
||||||
|
|
||||||
|
if ( end( $results ) != $row )
|
||||||
|
$out .= ' / ';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function articleTitle( $articleId )
|
||||||
|
{
|
||||||
|
$results = $this->db->query(
|
||||||
|
"SELECT title FROM pp_articles_langs AS pal, pp_langs AS pl WHERE lang_id = pl.id AND article_id = " . (int)$articleId . " AND title != '' ORDER BY o ASC LIMIT 1"
|
||||||
|
)->fetchAll();
|
||||||
|
|
||||||
|
return $results[0]['title'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteFile( $fileId ): bool
|
||||||
|
{
|
||||||
|
$this->db->update( 'pp_articles_files', [ 'to_delete' => 1 ], [ 'id' => (int)$fileId ] );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteImg( $imageId ): bool
|
||||||
|
{
|
||||||
|
$this->db->update( 'pp_articles_images', [ 'to_delete' => 1 ], [ 'id' => (int)$imageId ] );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function articleDetails( $articleId )
|
||||||
|
{
|
||||||
|
if ( $article = $this->db->get( 'pp_articles', '*', [ 'id' => (int)$articleId ] ) )
|
||||||
|
{
|
||||||
|
$results = $this->db->select( 'pp_articles_langs', '*', [ 'article_id' => (int)$articleId ] );
|
||||||
|
if ( is_array( $results ) )
|
||||||
|
foreach ( $results as $row )
|
||||||
|
$article['languages'][ $row['lang_id'] ] = $row;
|
||||||
|
|
||||||
|
$article['images'] = $this->db->select( 'pp_articles_images', '*', [ 'article_id' => (int)$articleId, 'ORDER' => [ 'o' => 'ASC', 'id' => 'ASC' ] ] );
|
||||||
|
$article['files'] = $this->db->select( 'pp_articles_files', '*', [ 'article_id' => (int)$articleId, 'ORDER' => [ 'o' => 'ASC', 'id' => 'ASC' ] ] );
|
||||||
|
$article['pages'] = $this->db->select( 'pp_articles_pages', 'page_id', [ 'article_id' => (int)$articleId ] );
|
||||||
|
$article['tags'] = $this->db->select( 'pp_tags', [ '[><]pp_articles_tags' => [ 'id' => 'tag_id' ] ], 'name', [ 'article_id' => (int)$articleId ] );
|
||||||
|
$article['params'] = $this->db->select( 'pp_articles_additional_values', [ 'param_id', 'value', 'language_id' ], [ 'article_id' => (int)$articleId ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $article;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function insertMissingHash(): bool
|
||||||
|
{
|
||||||
|
if ( $this->db->count( 'pp_articles', [ 'hash' => null ] ) )
|
||||||
|
{
|
||||||
|
$rows = $this->db->select( 'pp_articles', [ 'id', 'date_add' ], [ 'hash' => null ] );
|
||||||
|
if ( is_array( $rows ) )
|
||||||
|
foreach ( $rows as $row )
|
||||||
|
$this->db->update( 'pp_articles', [ 'hash' => md5( $row['id'] . $row['date_add'] ) ], [ 'id' => $row['id'] ] );
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function articlesByDateAdd( $dateStart, $dateEnd )
|
||||||
|
{
|
||||||
|
$results = $this->db->query(
|
||||||
|
'SELECT id FROM pp_articles WHERE status = 1 AND date_add BETWEEN \'' . $dateStart . '\' AND \'' . $dateEnd . '\' ORDER BY date_add DESC'
|
||||||
|
)->fetchAll();
|
||||||
|
|
||||||
|
if ( is_array( $results ) && !empty( $results ) )
|
||||||
|
foreach ( $results as $row )
|
||||||
|
$articles[] = \front\factory\Articles::article_details( $row['id'], 'pl' );
|
||||||
|
|
||||||
|
return isset( $articles ) ? $articles : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function articlesSetArchive( $articleId )
|
||||||
|
{
|
||||||
|
$result = $this->db->update( 'pp_articles', [ 'status' => -1 ], [ 'id' => (int)$articleId ] );
|
||||||
|
\S::htacces();
|
||||||
|
\S::delete_cache();
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function fileNameChange( $fileId, $fileName ): bool
|
||||||
|
{
|
||||||
|
$this->db->update( 'pp_articles_files', [ 'name' => $fileName ], [ 'id' => (int)$fileId ] );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteNonassignedFiles(): void
|
||||||
|
{
|
||||||
|
$results = $this->db->select( 'pp_articles_files', '*', [ 'article_id' => null ] );
|
||||||
|
if ( is_array( $results ) )
|
||||||
|
foreach ( $results as $row )
|
||||||
|
if ( file_exists( '../' . $row['src'] ) )
|
||||||
|
unlink( '../' . $row['src'] );
|
||||||
|
|
||||||
|
$this->db->delete( 'pp_articles_files', [ 'article_id' => null ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteNonassignedImages(): void
|
||||||
|
{
|
||||||
|
$results = $this->db->select( 'pp_articles_images', '*', [ 'article_id' => null ] );
|
||||||
|
if ( is_array( $results ) )
|
||||||
|
foreach ( $results as $row )
|
||||||
|
if ( file_exists( '../' . $row['src'] ) )
|
||||||
|
unlink( '../' . $row['src'] );
|
||||||
|
|
||||||
|
$this->db->delete( 'pp_articles_images', [ 'article_id' => null ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function duplicateArticle( $articleId, $userId ): bool
|
||||||
|
{
|
||||||
|
$article = $this->articleDetails( $articleId );
|
||||||
|
|
||||||
|
if ( !$article )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
$this->db->insert( 'pp_articles', [
|
||||||
|
'show_title' => $article['show_title'],
|
||||||
|
'show_date_add' => $article['show_date_add'],
|
||||||
|
'show_date_modify' => $article['show_date_modify'],
|
||||||
|
'date_add' => date( 'Y-m-d H:i:s' ),
|
||||||
|
'date_modify' => date( 'Y-m-d H:i:s' ),
|
||||||
|
'modify_by' => $userId,
|
||||||
|
'layout_id' => $article['layout_id'],
|
||||||
|
'status' => $article['status'],
|
||||||
|
'repeat_entry' => $article['repeat_entry'],
|
||||||
|
'social_icons' => $article['social_icons'],
|
||||||
|
'date_start' => $article['date_start'],
|
||||||
|
'date_end' => $article['event_date'],
|
||||||
|
'priority' => $article['priority'],
|
||||||
|
'password' => $article['password'],
|
||||||
|
'pixieset' => $article['pixieset']
|
||||||
|
] );
|
||||||
|
|
||||||
|
$articleTmpId = $this->db->id();
|
||||||
|
if ( !$articleTmpId )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
foreach ( $article['languages'] as $key => $val )
|
||||||
|
$this->db->insert( 'pp_articles_langs', [
|
||||||
|
'article_id' => $articleTmpId,
|
||||||
|
'lang_id' => $key,
|
||||||
|
'title' => 'Kopia: ' . $val['title'],
|
||||||
|
'entry' => $val['entry'],
|
||||||
|
'text' => $val['text'],
|
||||||
|
'meta_title' => null,
|
||||||
|
'meta_description' => null,
|
||||||
|
'meta_keywords' => null,
|
||||||
|
'seo_link' => null,
|
||||||
|
'copy_from' => $val['copy_from'],
|
||||||
|
'block_direct_access' => $val['block_direct_access']
|
||||||
|
] );
|
||||||
|
|
||||||
|
foreach ( $article['params'] as $param )
|
||||||
|
$this->db->insert( 'pp_articles_additional_values', [
|
||||||
|
'param_id' => $param['param_id'],
|
||||||
|
'value' => $param['value'],
|
||||||
|
'article_id' => $articleTmpId,
|
||||||
|
'language_id' => $param['language_id']
|
||||||
|
] );
|
||||||
|
|
||||||
|
foreach ( $article['pages'] as $page )
|
||||||
|
{
|
||||||
|
$order = $this->maxOrder() + 1;
|
||||||
|
$this->db->insert( 'pp_articles_pages', [
|
||||||
|
'article_id' => $articleTmpId,
|
||||||
|
'page_id' => $page,
|
||||||
|
'o' => (int)$order
|
||||||
|
] );
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function articleSave(
|
||||||
|
$articleId, $title, $mainImage, $entry, $text, $tableOfContents, $status, $showTitle, $showTableOfContents, $showDateAdd, $dateAdd, $showDateModify, $dateModify, $seoLink, $metaTitle, $metaDescription,
|
||||||
|
$metaKeywords, $layoutId, $pages, $noindex, $repeatEntry, $copyFrom, $socialIcons, $eventDate, $tags, $blockDirectAccess, $priority, $password, $pixieset, $idAuthor, $params, $userId
|
||||||
|
)
|
||||||
|
{
|
||||||
|
$eventDate = explode( ' - ', $eventDate );
|
||||||
|
|
||||||
|
if ( !$articleId )
|
||||||
|
{
|
||||||
|
$this->db->insert( 'pp_articles', [
|
||||||
|
'show_title' => $showTitle == 'on' ? 1 : 0,
|
||||||
|
'show_table_of_contents' => $showTableOfContents == 'on' ? 1 : 0,
|
||||||
|
'show_date_add' => $showDateAdd == 'on' ? 1 : 0,
|
||||||
|
'show_date_modify' => $showDateModify == 'on' ? 1 : 0,
|
||||||
|
'date_add' => date( 'Y-m-d H:i:s' ),
|
||||||
|
'date_modify' => date( 'Y-m-d H:i:s' ),
|
||||||
|
'modify_by' => $userId,
|
||||||
|
'layout_id' => $layoutId ? (int)$layoutId : null,
|
||||||
|
'status' => $status == 'on' ? 1 : 0,
|
||||||
|
'repeat_entry' => $repeatEntry == 'on' ? 1 : 0,
|
||||||
|
'social_icons' => $socialIcons == 'on' ? 1 : 0,
|
||||||
|
'date_start' => $eventDate[0] ? $eventDate[0] : null,
|
||||||
|
'date_end' => $eventDate[1] ? $eventDate[1] : null,
|
||||||
|
'priority' => $priority == 'on' ? 1 : 0,
|
||||||
|
'password' => $password ? $password : null,
|
||||||
|
'pixieset' => $pixieset,
|
||||||
|
'id_author' => $idAuthor ? $idAuthor : null
|
||||||
|
] );
|
||||||
|
|
||||||
|
$id = $this->db->id();
|
||||||
|
if ( !$id )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
$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_articles_langs', [
|
||||||
|
'article_id' => (int)$id,
|
||||||
|
'lang_id' => $row['id'],
|
||||||
|
'title' => $title[ $i ] != '' ? $title[ $i ] : null,
|
||||||
|
'main_image' => $mainImage[$i] != '' ? $mainImage[$i] : null,
|
||||||
|
'entry' => $entry[ $i ] != '' ? $entry[ $i ] : null,
|
||||||
|
'text' => $text[ $i ] != '' ? $text[ $i ] : null,
|
||||||
|
'table_of_contents' => $tableOfContents[$i] != '' ? $tableOfContents[$i] : null,
|
||||||
|
'meta_title' => $metaTitle[ $i ] != '' ? $metaTitle[ $i ] : null,
|
||||||
|
'meta_description' => $metaDescription[ $i ] != '' ? $metaDescription[ $i ] : null,
|
||||||
|
'meta_keywords' => $metaKeywords[ $i ] != '' ? $metaKeywords[ $i ] : null,
|
||||||
|
'seo_link' => \S::seo( $seoLink[ $i ] ) != '' ? \S::seo( $seoLink[ $i ] ) : null,
|
||||||
|
'noindex' => $noindex[ $i ],
|
||||||
|
'copy_from' => $copyFrom[ $i ] != '' ? $copyFrom[ $i ] : null,
|
||||||
|
'block_direct_access' => $blockDirectAccess[ $i ]
|
||||||
|
] );
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
else if ( is_array( $results ) and count( $results ) == 1 ) foreach ( $results as $row )
|
||||||
|
{
|
||||||
|
$this->db->insert( 'pp_articles_langs', [
|
||||||
|
'article_id' => (int)$id,
|
||||||
|
'lang_id' => $row['id'],
|
||||||
|
'title' => $title != '' ? $title : null,
|
||||||
|
'main_image' => $mainImage != '' ? $mainImage : null,
|
||||||
|
'entry' => $entry != '' ? $entry : null,
|
||||||
|
'text' => $text != '' ? $text : null,
|
||||||
|
'table_of_contents' => $tableOfContents != '' ? $tableOfContents : null,
|
||||||
|
'meta_title' => $metaTitle != '' ? $metaTitle : null,
|
||||||
|
'meta_description' => $metaDescription != '' ? $metaDescription : null,
|
||||||
|
'meta_keywords' => $metaKeywords != '' ? $metaKeywords : null,
|
||||||
|
'seo_link' => \S::seo( $seoLink ) != '' ? \S::seo( $seoLink ) : null,
|
||||||
|
'noindex' => $noindex,
|
||||||
|
'copy_from' => $copyFrom != '' ? $copyFrom : null,
|
||||||
|
'block_direct_access' => $blockDirectAccess
|
||||||
|
] );
|
||||||
|
}
|
||||||
|
|
||||||
|
$results = $this->db->select( 'pp_articles_additional_params', '*', [ 'AND' => [ 'status' => 1, 'language' => 0 ] ] );
|
||||||
|
if ( is_array( $results ) and !empty( $results ) ) foreach ( $results as $row )
|
||||||
|
{
|
||||||
|
$this->db->insert( 'pp_articles_additional_values', [
|
||||||
|
'param_id' => $row['id'],
|
||||||
|
'value' => $params[ 'ap_' . $row['name'] ],
|
||||||
|
'article_id' => (int)$id,
|
||||||
|
'language_id' => null
|
||||||
|
] );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( is_array( $pages ) ) foreach ( $pages as $page )
|
||||||
|
{
|
||||||
|
$order = $this->maxOrder() + 1;
|
||||||
|
$this->db->insert( 'pp_articles_pages', [
|
||||||
|
'article_id' => (int)$id,
|
||||||
|
'page_id' => (int)$page,
|
||||||
|
'o' => (int)$order
|
||||||
|
] );
|
||||||
|
}
|
||||||
|
else if ( $pages )
|
||||||
|
{
|
||||||
|
$order = $this->maxOrder() + 1;
|
||||||
|
$this->db->insert( 'pp_articles_pages', [
|
||||||
|
'article_id' => (int)$id,
|
||||||
|
'page_id' => (int)$pages,
|
||||||
|
'o' => (int)$order
|
||||||
|
] );
|
||||||
|
}
|
||||||
|
|
||||||
|
$results = $this->db->select( 'pp_articles_files', '*', [ 'article_id' => null ] );
|
||||||
|
if ( is_array( $results ) ) foreach ( $results as $row )
|
||||||
|
{
|
||||||
|
$dir = '/upload/article_files/article_' . $id;
|
||||||
|
$new_file_name = str_replace( '/upload/article_files/tmp', $dir, $row['src'] );
|
||||||
|
|
||||||
|
if ( file_exists( '..' . $row['src'] ) )
|
||||||
|
{
|
||||||
|
if ( !is_dir( '../' . $dir ) and $created !== true )
|
||||||
|
{
|
||||||
|
if ( mkdir( '../' . $dir, 0755, true ) )
|
||||||
|
$created = true;
|
||||||
|
}
|
||||||
|
rename( '..' . $row['src'], '..' . $new_file_name );
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->db->update( 'pp_articles_files', [ 'src' => $new_file_name, 'article_id' => $id ], [ 'id' => $row['id'] ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
$created = false;
|
||||||
|
|
||||||
|
$results = $this->db->select( 'pp_articles_images', '*', [ 'article_id' => null ] );
|
||||||
|
if ( is_array( $results ) ) foreach ( $results as $row )
|
||||||
|
{
|
||||||
|
$dir = '/upload/article_images/article_' . $id;
|
||||||
|
$new_file_name = str_replace( '/upload/article_images/tmp', $dir, $row['src'] );
|
||||||
|
|
||||||
|
if ( file_exists( '../' . $new_file_name ) )
|
||||||
|
{
|
||||||
|
$ext = strrpos( $new_file_name, '.' );
|
||||||
|
$fileName_a = substr( $new_file_name, 0, $ext );
|
||||||
|
$fileName_b = substr( $new_file_name, $ext );
|
||||||
|
$count = 1;
|
||||||
|
|
||||||
|
while ( file_exists( '../' . $fileName_a . '_' . $count . $fileName_b ) )
|
||||||
|
$count++;
|
||||||
|
|
||||||
|
$new_file_name = $fileName_a . '_' . $count . $fileName_b;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( file_exists( '..' . $row['src'] ) )
|
||||||
|
{
|
||||||
|
if ( !is_dir( '../' . $dir ) and $created !== true )
|
||||||
|
{
|
||||||
|
if ( mkdir( '../' . $dir, 0755, true ) )
|
||||||
|
$created = true;
|
||||||
|
}
|
||||||
|
rename( '..' . $row['src'], '..' . $new_file_name );
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->db->update( 'pp_articles_images', [ 'src' => $new_file_name, 'article_id' => (int)$id ], [ 'id' => $row['id'] ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
$tags = explode( ',', $tags );
|
||||||
|
if ( is_array( $tags ) ) foreach ( $tags as $tag )
|
||||||
|
{
|
||||||
|
if ( trim( $tag ) != '' )
|
||||||
|
{
|
||||||
|
$tag_id = $this->db->get( 'pp_tags', 'id', [ 'name' => $tag ] );
|
||||||
|
if ( !$tag_id )
|
||||||
|
{
|
||||||
|
$this->db->insert( 'pp_tags', [ 'name' => $tag ] );
|
||||||
|
$tag_id = $this->db->id();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->db->insert( 'pp_articles_tags', [ 'article_id' => (int)$id, 'tag_id' => (int)$tag_id ] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
\S::htacces();
|
||||||
|
\S::delete_cache();
|
||||||
|
|
||||||
|
return $id;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$this->db->update( 'pp_articles', [
|
||||||
|
'show_title' => $showTitle == 'on' ? 1 : 0,
|
||||||
|
'show_table_of_contents' => $showTableOfContents == 'on' ? 1 : 0,
|
||||||
|
'show_date_add' => $showDateAdd == 'on' ? 1 : 0,
|
||||||
|
'date_add' => $dateAdd,
|
||||||
|
'show_date_modify' => $showDateModify == 'on' ? 1 : 0,
|
||||||
|
'date_modify' => $dateModify ? $dateModify : date( 'Y-m-d H:i:s' ),
|
||||||
|
'modify_by' => $userId,
|
||||||
|
'layout_id' => $layoutId ? (int)$layoutId : null,
|
||||||
|
'status' => $status == 'on' ? 1 : 0,
|
||||||
|
'repeat_entry' => $repeatEntry == 'on' ? 1 : 0,
|
||||||
|
'social_icons' => $socialIcons == 'on' ? 1 : 0,
|
||||||
|
'date_start' => $eventDate[0] ? $eventDate[0] : null,
|
||||||
|
'date_end' => $eventDate[1] ? $eventDate[1] : null,
|
||||||
|
'priority' => $priority == 'on' ? 1 : 0,
|
||||||
|
'password' => $password ? $password : null,
|
||||||
|
'pixieset' => $pixieset,
|
||||||
|
'id_author' => $idAuthor ? $idAuthor : null
|
||||||
|
], [
|
||||||
|
'id' => (int)$articleId
|
||||||
|
] );
|
||||||
|
|
||||||
|
if ( $dateAdd )
|
||||||
|
$this->db->update( 'pp_articles', [ 'date_add' => $dateAdd ], [ 'id' => (int)$articleId ] );
|
||||||
|
|
||||||
|
$i = 0;
|
||||||
|
$this->db->delete( 'pp_articles_langs', [ 'article_id' => (int)$articleId ] );
|
||||||
|
|
||||||
|
$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_articles_langs', [
|
||||||
|
'article_id' => (int)$articleId,
|
||||||
|
'lang_id' => $row['id'],
|
||||||
|
'title' => $title[ $i ] != '' ? $title[ $i ] : null,
|
||||||
|
'main_image' => $mainImage[$i] != '' ? $mainImage[$i] : null,
|
||||||
|
'entry' => $entry[ $i ] != '' ? $entry[ $i ] : null,
|
||||||
|
'text' => $text[ $i ] != '' ? $text[ $i ] : null,
|
||||||
|
'table_of_contents' => $tableOfContents[$i] != '' ? $tableOfContents[$i] : null,
|
||||||
|
'meta_title' => $metaTitle[ $i ] != '' ? $metaTitle[ $i ] : null,
|
||||||
|
'meta_description' => $metaDescription[ $i ] != '' ? $metaDescription[ $i ] : null,
|
||||||
|
'meta_keywords' => $metaKeywords[ $i ] != '' ? $metaKeywords[ $i ] : null,
|
||||||
|
'seo_link' => \S::seo( $seoLink[ $i ] ) != '' ? \S::seo( $seoLink[ $i ] ) : null,
|
||||||
|
'noindex' => $noindex[ $i ],
|
||||||
|
'copy_from' => $copyFrom[ $i ] != '' ? $copyFrom[ $i ] : null,
|
||||||
|
'block_direct_access' => $blockDirectAccess[ $i ]
|
||||||
|
] );
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
else if ( is_array( $results ) and count( $results ) == 1 ) foreach ( $results as $row )
|
||||||
|
{
|
||||||
|
$this->db->insert( 'pp_articles_langs', [
|
||||||
|
'article_id' => (int)$articleId,
|
||||||
|
'lang_id' => $row['id'],
|
||||||
|
'title' => $title != '' ? $title : null,
|
||||||
|
'main_image' => $mainImage != '' ? $mainImage : null,
|
||||||
|
'entry' => $entry != '' ? $entry : null,
|
||||||
|
'text' => $text != '' ? $text : null,
|
||||||
|
'table_of_contents' => $tableOfContents != '' ? $tableOfContents : null,
|
||||||
|
'meta_title' => $metaTitle != '' ? $metaTitle : null,
|
||||||
|
'meta_description' => $metaDescription != '' ? $metaDescription : null,
|
||||||
|
'meta_keywords' => $metaKeywords != '' ? $metaKeywords : null,
|
||||||
|
'seo_link' => \S::seo( $seoLink ) != '' ? \S::seo( $seoLink ) : null,
|
||||||
|
'noindex' => $noindex,
|
||||||
|
'copy_from' => $copyFrom != '' ? $copyFrom : null,
|
||||||
|
'block_direct_access' => $blockDirectAccess
|
||||||
|
] );
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->db->delete( 'pp_articles_additional_values', [ 'article_id' => (int)$articleId ] );
|
||||||
|
|
||||||
|
$results = $this->db->select( 'pp_articles_additional_params', '*', [ 'AND' => [ 'status' => 1, 'language' => 0 ] ] );
|
||||||
|
if ( is_array( $results ) and !empty( $results ) ) foreach ( $results as $row )
|
||||||
|
{
|
||||||
|
$this->db->insert( 'pp_articles_additional_values', [
|
||||||
|
'param_id' => $row['id'],
|
||||||
|
'value' => $params[ 'ap_' . $row['name'] ],
|
||||||
|
'article_id' => (int)$articleId,
|
||||||
|
'language_id' => null
|
||||||
|
] );
|
||||||
|
}
|
||||||
|
|
||||||
|
$results = $this->db->select( 'pp_articles_additional_params', '*', [ 'AND' => [ 'status' => 1, 'language' => 1 ] ] );
|
||||||
|
if ( is_array( $results ) and !empty( $results ) ) foreach ( $results as $row )
|
||||||
|
{
|
||||||
|
$results2 = $this->db->select( 'pp_langs', [ 'id' ], [ 'status' => 1, 'ORDER' => [ 'o' => 'ASC' ] ] );
|
||||||
|
if ( is_array( $results2 ) ) foreach ( $results2 as $row2 )
|
||||||
|
{
|
||||||
|
$this->db->insert( 'pp_articles_additional_values', [
|
||||||
|
'param_id' => $row['id'],
|
||||||
|
'value' => $params[ 'ap_' . $row['name'] . '_' . $row2['id'] ],
|
||||||
|
'article_id' => (int)$articleId,
|
||||||
|
'language_id' => $row2['id']
|
||||||
|
] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$not_in = [ 0 ];
|
||||||
|
if ( is_array( $pages ) ) foreach ( $pages as $page )
|
||||||
|
$not_in[] = $page;
|
||||||
|
else if ( $pages )
|
||||||
|
$not_in[] = $pages;
|
||||||
|
|
||||||
|
$this->db->delete( 'pp_articles_pages', [ 'AND' => [ 'article_id' => (int)$articleId, 'page_id[!]' => $not_in ] ] );
|
||||||
|
|
||||||
|
$pages_tmp = $this->db->select( 'pp_articles_pages', 'page_id', [ 'article_id' => (int)$articleId ] );
|
||||||
|
if ( !is_array( $pages ) )
|
||||||
|
$pages = [ $pages ];
|
||||||
|
|
||||||
|
$pages = array_diff( $pages, $pages_tmp );
|
||||||
|
if ( is_array( $pages ) ) foreach ( $pages as $page )
|
||||||
|
{
|
||||||
|
$order = $this->maxOrder() + 1;
|
||||||
|
$this->db->insert( 'pp_articles_pages', [
|
||||||
|
'article_id' => (int)$articleId,
|
||||||
|
'page_id' => (int)$page,
|
||||||
|
'o' => (int)$order
|
||||||
|
] );
|
||||||
|
}
|
||||||
|
|
||||||
|
$results = $this->db->select( 'pp_articles_files', '*', [ 'article_id' => null ] );
|
||||||
|
if ( is_array( $results ) ) foreach ( $results as $row )
|
||||||
|
{
|
||||||
|
$dir = '/upload/article_files/article_' . $articleId;
|
||||||
|
$new_file_name = str_replace( '/upload/article_files/tmp', $dir, $row['src'] );
|
||||||
|
|
||||||
|
if ( file_exists( '..' . $row['src'] ) )
|
||||||
|
{
|
||||||
|
if ( !is_dir( '../' . $dir ) and $created !== true )
|
||||||
|
{
|
||||||
|
if ( mkdir( '../' . $dir, 0755, true ) )
|
||||||
|
$created = true;
|
||||||
|
}
|
||||||
|
rename( '..' . $row['src'], '..' . $new_file_name );
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->db->update( 'pp_articles_files', [ 'src' => $new_file_name, 'article_id' => (int)$articleId ], [ 'id' => $row['id'] ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
$created = false;
|
||||||
|
|
||||||
|
$results = $this->db->select( 'pp_articles_images', '*', [ 'article_id' => null ] );
|
||||||
|
if ( is_array( $results ) ) foreach ( $results as $row )
|
||||||
|
{
|
||||||
|
$dir = '/upload/article_images/article_' . $articleId;
|
||||||
|
$new_file_name = str_replace( '/upload/article_images/tmp', $dir, $row['src'] );
|
||||||
|
|
||||||
|
if ( file_exists( '../' . $new_file_name ) )
|
||||||
|
{
|
||||||
|
$ext = strrpos( $new_file_name, '.' );
|
||||||
|
$fileName_a = substr( $new_file_name, 0, $ext );
|
||||||
|
$fileName_b = substr( $new_file_name, $ext );
|
||||||
|
$count = 1;
|
||||||
|
|
||||||
|
while ( file_exists( '../' . $fileName_a . '_' . $count . $fileName_b ) )
|
||||||
|
$count++;
|
||||||
|
|
||||||
|
$new_file_name = $fileName_a . '_' . $count . $fileName_b;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( file_exists( '..' . $row['src'] ) )
|
||||||
|
{
|
||||||
|
if ( !is_dir( '../' . $dir ) and $created !== true )
|
||||||
|
{
|
||||||
|
if ( mkdir( '../' . $dir, 0755, true ) )
|
||||||
|
$created = true;
|
||||||
|
}
|
||||||
|
rename( '..' . $row['src'], '..' . $new_file_name );
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->db->update( 'pp_articles_images', [ 'src' => $new_file_name, 'article_id' => (int)$articleId ], [ 'id' => $row['id'] ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
$results = $this->db->select( 'pp_articles_images', '*', [ 'AND' => [ 'article_id' => (int)$articleId, 'to_delete' => 1 ] ] );
|
||||||
|
if ( is_array( $results ) ) foreach ( $results as $row )
|
||||||
|
if ( file_exists( '../' . $row['src'] ) )
|
||||||
|
unlink( '../' . $row['src'] );
|
||||||
|
|
||||||
|
$this->db->delete( 'pp_articles_images', [ 'AND' => [ 'article_id' => (int)$articleId, 'to_delete' => 1 ] ] );
|
||||||
|
|
||||||
|
$results = $this->db->select( 'pp_articles_files', '*', [ 'AND' => [ 'article_id' => (int)$articleId, 'to_delete' => 1 ] ] );
|
||||||
|
if ( is_array( $results ) ) foreach ( $results as $row )
|
||||||
|
if ( file_exists( '../' . $row['src'] ) )
|
||||||
|
unlink( '../' . $row['src'] );
|
||||||
|
|
||||||
|
$this->db->delete( 'pp_articles_files', [ 'AND' => [ 'article_id' => (int)$articleId, 'to_delete' => 1 ] ] );
|
||||||
|
|
||||||
|
$this->db->delete( 'pp_articles_tags', [ 'article_id' => (int)$articleId ] );
|
||||||
|
|
||||||
|
$tags = explode( ',', $tags );
|
||||||
|
if ( is_array( $tags ) ) foreach ( $tags as $tag )
|
||||||
|
{
|
||||||
|
if ( trim( $tag ) != '' )
|
||||||
|
{
|
||||||
|
$tag_id = $this->db->get( 'pp_tags', 'id', [ 'name' => $tag ] );
|
||||||
|
if ( !$tag_id )
|
||||||
|
{
|
||||||
|
$this->db->insert( 'pp_tags', [ 'name' => $tag ] );
|
||||||
|
$tag_id = $this->db->id();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->db->insert( 'pp_articles_tags', [ 'article_id' => (int)$articleId, 'tag_id' => (int)$tag_id ] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
\S::htacces();
|
||||||
|
\S::delete_cache();
|
||||||
|
return $articleId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function maxOrder()
|
||||||
|
{
|
||||||
|
return $this->db->max( 'pp_articles_pages', 'o' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
123
autoload/Domain/Layouts/LayoutsRepository.php
Normal file
123
autoload/Domain/Layouts/LayoutsRepository.php
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
<?php
|
||||||
|
namespace Domain\Layouts;
|
||||||
|
|
||||||
|
class LayoutsRepository
|
||||||
|
{
|
||||||
|
private $db;
|
||||||
|
|
||||||
|
public function __construct( $db )
|
||||||
|
{
|
||||||
|
$this->db = $db;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function layoutDelete( $layoutId )
|
||||||
|
{
|
||||||
|
if ( $this->db->count( 'pp_layouts' ) > 1 )
|
||||||
|
return $this->db->delete( 'pp_layouts', [ 'id' => (int)$layoutId ] );
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function layoutDetails( $layoutId )
|
||||||
|
{
|
||||||
|
$layout = $this->db->get( 'pp_layouts', '*', [ 'id' => (int)$layoutId ] );
|
||||||
|
$layout['pages'] = $this->db->select( 'pp_layouts_pages', 'page_id', [ 'layout_id' => (int)$layoutId ] );
|
||||||
|
|
||||||
|
return $layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function layoutSave( $layoutId, $name, $status, $pages, $html, $css, $js, $mHtml, $mCss, $mJs )
|
||||||
|
{
|
||||||
|
if ( !$layoutId )
|
||||||
|
return $this->createLayout( $name, $status, $pages, $html, $css, $js, $mHtml, $mCss, $mJs );
|
||||||
|
|
||||||
|
return $this->updateLayout( $layoutId, $name, $status, $pages, $html, $css, $js, $mHtml, $mCss, $mJs );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function menusList()
|
||||||
|
{
|
||||||
|
$pagesRepo = new \Domain\Pages\PagesRepository( $this->db );
|
||||||
|
$results = $this->db->select( 'pp_menus', 'id', [ 'ORDER' => [ 'name' => 'ASC' ] ] );
|
||||||
|
if ( is_array( $results ) )
|
||||||
|
foreach ( $results as $row )
|
||||||
|
{
|
||||||
|
$menu = $pagesRepo->menuDetails( $row );
|
||||||
|
$menu['pages'] = $pagesRepo->menuPages( $row );
|
||||||
|
$menus[] = $menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isset( $menus ) ? $menus : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function layoutsList()
|
||||||
|
{
|
||||||
|
return $this->db->select( 'pp_layouts', '*', [ 'ORDER' => [ 'name' => 'ASC' ] ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createLayout( $name, $status, $pages, $html, $css, $js, $mHtml, $mCss, $mJs )
|
||||||
|
{
|
||||||
|
if ( $status == 'on' )
|
||||||
|
$this->db->update( 'pp_layouts', [ 'status' => 0 ] );
|
||||||
|
|
||||||
|
$this->db->insert( 'pp_layouts', [
|
||||||
|
'name' => $name,
|
||||||
|
'html' => $html,
|
||||||
|
'css' => $css,
|
||||||
|
'js' => $js,
|
||||||
|
'm_html' => $mHtml,
|
||||||
|
'm_css' => $mCss,
|
||||||
|
'm_js' => $mJs,
|
||||||
|
'status' => $status == 'on' ? 1 : 0
|
||||||
|
] );
|
||||||
|
|
||||||
|
$id = $this->db->id();
|
||||||
|
if ( !$id )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
$this->replaceLayoutPages( (int)$id, $pages );
|
||||||
|
\S::delete_cache();
|
||||||
|
|
||||||
|
return $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function updateLayout( $layoutId, $name, $status, $pages, $html, $css, $js, $mHtml, $mCss, $mJs )
|
||||||
|
{
|
||||||
|
if ( $status == 'on' )
|
||||||
|
$this->db->update( 'pp_layouts', [ 'status' => 0 ] );
|
||||||
|
|
||||||
|
$this->db->update( 'pp_layouts', [
|
||||||
|
'name' => $name,
|
||||||
|
'html' => $html,
|
||||||
|
'css' => $css,
|
||||||
|
'js' => $js,
|
||||||
|
'm_html' => $mHtml,
|
||||||
|
'm_css' => $mCss,
|
||||||
|
'm_js' => $mJs,
|
||||||
|
'status' => $status == 'on' ? 1 : 0
|
||||||
|
], [
|
||||||
|
'id' => $layoutId
|
||||||
|
] );
|
||||||
|
|
||||||
|
$this->db->delete( 'pp_layouts_pages', [ 'layout_id' => (int)$layoutId ] );
|
||||||
|
$this->replaceLayoutPages( (int)$layoutId, $pages );
|
||||||
|
|
||||||
|
\S::delete_cache();
|
||||||
|
return $layoutId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function replaceLayoutPages( int $layoutId, $pages ): void
|
||||||
|
{
|
||||||
|
if ( is_array( $pages ) )
|
||||||
|
foreach ( $pages as $page )
|
||||||
|
{
|
||||||
|
$this->db->delete( 'pp_layouts_pages', [ 'page_id' => (int)$page ] );
|
||||||
|
$this->db->insert( 'pp_layouts_pages', [ 'layout_id' => $layoutId, 'page_id' => (int)$page ] );
|
||||||
|
}
|
||||||
|
else if ( $pages )
|
||||||
|
{
|
||||||
|
$this->db->delete( 'pp_layouts_pages', [ 'page_id' => (int)$pages ] );
|
||||||
|
$this->db->insert( 'pp_layouts_pages', [ 'layout_id' => $layoutId, 'page_id' => (int)$pages ] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
451
autoload/Domain/Pages/PagesRepository.php
Normal file
451
autoload/Domain/Pages/PagesRepository.php
Normal file
@@ -0,0 +1,451 @@
|
|||||||
|
<?php
|
||||||
|
namespace Domain\Pages;
|
||||||
|
|
||||||
|
class PagesRepository
|
||||||
|
{
|
||||||
|
private $db;
|
||||||
|
|
||||||
|
public function __construct( $db )
|
||||||
|
{
|
||||||
|
$this->db = $db;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function saveArticlesOrder( $pageId, $articles ): bool
|
||||||
|
{
|
||||||
|
if ( is_array( $articles ) )
|
||||||
|
{
|
||||||
|
$this->db->update( 'pp_articles_pages', [ 'o' => 0 ], [ 'page_id' => (int) $pageId ] );
|
||||||
|
$x = 0;
|
||||||
|
|
||||||
|
for ( $i = 0; $i < count( $articles ); $i++ )
|
||||||
|
{
|
||||||
|
if ( $articles[$i]['item_id'] )
|
||||||
|
{
|
||||||
|
$x++;
|
||||||
|
$this->db->update( 'pp_articles_pages', [ 'o' => $x ], [
|
||||||
|
'AND' => [ 'page_id' => (int) $pageId, 'article_id' => $articles[$i]['item_id'] ]
|
||||||
|
] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function pageArticles( $pageId )
|
||||||
|
{
|
||||||
|
$results = $this->db->query(
|
||||||
|
'SELECT article_id, o, status FROM pp_articles_pages AS ap INNER JOIN pp_articles AS a ON a.id = ap.article_id WHERE page_id = ' . (int) $pageId . ' AND status != -1 ORDER BY o ASC'
|
||||||
|
)->fetchAll();
|
||||||
|
|
||||||
|
$articles = [];
|
||||||
|
if ( is_array( $results ) )
|
||||||
|
foreach ( $results as $row )
|
||||||
|
{
|
||||||
|
$row['title'] = \admin\factory\Articles::article_title( $row['article_id'] );
|
||||||
|
$articles[] = $row;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $articles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function menusList()
|
||||||
|
{
|
||||||
|
return $this->db->select( 'pp_menus', '*', [ 'ORDER' => [ 'name' => 'ASC' ] ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function savePagesOrder( $menuId, $pages ): bool
|
||||||
|
{
|
||||||
|
if ( is_array( $pages ) )
|
||||||
|
{
|
||||||
|
$this->db->update( 'pp_pages', [ 'o' => 0 ], [ 'menu_id' => (int) $menuId ] );
|
||||||
|
$x = 0;
|
||||||
|
|
||||||
|
for ( $i = 0; $i < count( $pages ); $i++ )
|
||||||
|
{
|
||||||
|
if ( $pages[$i]['item_id'] )
|
||||||
|
{
|
||||||
|
$parentId = $pages[$i]['parent_id'] ? $pages[$i]['parent_id'] : 0;
|
||||||
|
|
||||||
|
if ( $pages[$i]['item_id'] && $pages[$i]['depth'] > 1 )
|
||||||
|
{
|
||||||
|
if ( $pages[$i]['depth'] == 2 )
|
||||||
|
$parentId = null;
|
||||||
|
|
||||||
|
$x++;
|
||||||
|
$this->db->update( 'pp_pages', [ 'o' => $x, 'parent_id' => $parentId ], [ 'id' => (int) $pages[$i]['item_id'] ] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
\S::delete_cache();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function pageDelete( $pageId ): bool
|
||||||
|
{
|
||||||
|
if ( $this->db->count( 'pp_pages', [ 'parent_id' => (int) $pageId ] ) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ( $this->db->delete( 'pp_pages', [ 'id' => (int) $pageId ] ) )
|
||||||
|
{
|
||||||
|
\S::delete_cache();
|
||||||
|
\S::htacces();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function maxOrder(): int
|
||||||
|
{
|
||||||
|
return (int) $this->db->max( 'pp_pages', 'o' );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateSubpagesMenuId( int $parentId, int $menuId ): void
|
||||||
|
{
|
||||||
|
$this->updateSubpagesMenuIdRecursive( $parentId, $menuId );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateSeoLink( $title, $pageId, $articleId, $lang, $pid )
|
||||||
|
{
|
||||||
|
$seoLink = \S::seo( $title );
|
||||||
|
$seoLinkCheck = false;
|
||||||
|
$i = 0;
|
||||||
|
|
||||||
|
while ( !$seoLinkCheck )
|
||||||
|
{
|
||||||
|
if ( $this->db->count( 'pp_pages_langs', [ 'AND' => [ 'seo_link' => $seoLink, 'page_id[!]' => (int) $pageId ] ] ) )
|
||||||
|
$seoLink = $seoLink . '-' . ( ++$i );
|
||||||
|
else
|
||||||
|
$seoLinkCheck = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$seoLinkCheck = false;
|
||||||
|
|
||||||
|
while ( !$seoLinkCheck )
|
||||||
|
{
|
||||||
|
if ( $this->db->count( 'pp_articles_langs', [ 'AND' => [ 'seo_link' => $seoLink, 'article_id[!]' => (int) $articleId ] ] ) )
|
||||||
|
$seoLink = $seoLink . '-' . ( ++$i );
|
||||||
|
else
|
||||||
|
$seoLinkCheck = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $seoLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function googleUrlPreview( $pageId, $title, $lang, $pid, $id, $seoLink, $languageLink = '' )
|
||||||
|
{
|
||||||
|
$prefix = $languageLink;
|
||||||
|
$status = true;
|
||||||
|
$idPage = $pageId;
|
||||||
|
$seo = '';
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if ( $pageId )
|
||||||
|
{
|
||||||
|
$parent = $this->pageDetails( $pageId );
|
||||||
|
$parentId = $parent['parent_id'];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
$parentId = $pid;
|
||||||
|
|
||||||
|
if ( $parentId )
|
||||||
|
{
|
||||||
|
$results = $this->db->query(
|
||||||
|
"SELECT title, seo_link, page_id FROM pp_pages_langs AS ppl, pp_langs AS pl WHERE lang_id = pl.id AND page_id = " . (int) $parentId . " AND ppl.lang_id = '" . $lang . "' "
|
||||||
|
)->fetchAll();
|
||||||
|
if ( $results[0]['seo_link'] )
|
||||||
|
$seo = $results[0]['seo_link'] . '/' . $seo;
|
||||||
|
else
|
||||||
|
$seo = 's-' . $results[0]['page_id'] . '-' . \S::seo( $results[0]['title'] ) . '/' . $seo;
|
||||||
|
$pageId = $results[0]['page_id'];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
$status = false;
|
||||||
|
}
|
||||||
|
while ( $status );
|
||||||
|
|
||||||
|
if ( $id )
|
||||||
|
{
|
||||||
|
if ( !$seoLink )
|
||||||
|
$seo = $seo . 's-' . $id . '-' . \S::seo( $title );
|
||||||
|
else
|
||||||
|
$seo = $seo . $seoLink;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ( !$seoLink )
|
||||||
|
$seo = $seo . 's-' . $idPage . '-' . \S::seo( $title );
|
||||||
|
else
|
||||||
|
$seo = $seo . $seoLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $prefix )
|
||||||
|
$seo = $prefix . $seo;
|
||||||
|
|
||||||
|
return $seo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function menuDelete( $menuId )
|
||||||
|
{
|
||||||
|
if ( $this->db->count( 'pp_pages', [ 'menu_id' => (int) $menuId ] ) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return $this->db->delete( 'pp_menus', [ 'id' => (int) $menuId ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function menuDetails( $menuId )
|
||||||
|
{
|
||||||
|
return $this->db->get( 'pp_menus', '*', [ 'id' => (int) $menuId ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function menuSave( $menuId, $name, $status )
|
||||||
|
{
|
||||||
|
$status == 'on' ? $status = 1 : $status = 0;
|
||||||
|
|
||||||
|
if ( !$menuId )
|
||||||
|
{
|
||||||
|
return $this->db->insert( 'pp_menus', [ 'name' => $name, 'status' => $status ] );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$this->db->update( 'pp_menus', [ 'name' => $name, 'status' => $status ], [ 'id' => (int) $menuId ] );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function menuLists()
|
||||||
|
{
|
||||||
|
return $this->db->select( 'pp_menus', '*', [ 'ORDER' => [ 'id' => 'ASC' ] ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function pageDetails( $pageId )
|
||||||
|
{
|
||||||
|
$page = $this->db->get( 'pp_pages', '*', [ 'id' => (int) $pageId ] );
|
||||||
|
|
||||||
|
$results = $this->db->select( 'pp_pages_langs', '*', [ 'page_id' => (int) $pageId ] );
|
||||||
|
if ( is_array( $results ) )
|
||||||
|
foreach ( $results as $row )
|
||||||
|
$page['languages'][$row['lang_id']] = $row;
|
||||||
|
|
||||||
|
$page['layout_id'] = $this->db->get( 'pp_layouts_pages', 'layout_id', [ 'page_id' => (int) $pageId ] );
|
||||||
|
|
||||||
|
return $page;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function pageUrl( $pageId )
|
||||||
|
{
|
||||||
|
$results = $this->db->query(
|
||||||
|
"SELECT seo_link, title lang_id FROM pp_pages_langs AS ppl, pp_langs AS pl WHERE lang_id = pl.id AND page_id = " . (int) $pageId . " AND seo_link != '' ORDER BY o ASC LIMIT 1"
|
||||||
|
)->fetchAll();
|
||||||
|
|
||||||
|
if ( !$results[0]['seo_link'] )
|
||||||
|
{
|
||||||
|
$title = $this->pageTitle( $article_id );
|
||||||
|
return 's-' . $pageId . '-' . \S::seo( $title );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return $results[0]['seo_link'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function pageTitle( $pageId )
|
||||||
|
{
|
||||||
|
$result = $this->db->select( 'pp_pages_langs', [ '[><]pp_langs' => [ 'lang_id' => 'id' ] ], 'title', [
|
||||||
|
'AND' => [ 'page_id' => (int) $pageId, 'title[!]' => '' ], 'ORDER' => [ 'o' => 'ASC' ], 'LIMIT' => 1
|
||||||
|
] );
|
||||||
|
return $result[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function pageLanguages( $pageId )
|
||||||
|
{
|
||||||
|
return $this->db->select( 'pp_pages_langs', '*', [ 'AND' => [ 'page_id' => (int) $pageId, 'title[!]' => null ] ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function menuPages( $menuId, $parentId = null )
|
||||||
|
{
|
||||||
|
$results = $this->db->select( 'pp_pages', [ 'id', 'menu_id', 'status', 'parent_id', 'start' ], [
|
||||||
|
'AND' => [ 'menu_id' => $menuId, 'parent_id' => $parentId ], 'ORDER' => [ 'o' => 'ASC' ]
|
||||||
|
] );
|
||||||
|
|
||||||
|
if ( !is_array( $results ) || !count( $results ) )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
foreach ( $results as $row )
|
||||||
|
{
|
||||||
|
$row['title'] = $this->pageTitle( $row['id'] );
|
||||||
|
$row['languages'] = $this->pageLanguages( $row['id'] );
|
||||||
|
$row['subpages'] = $this->menuPages( $menuId, $row['id'] );
|
||||||
|
|
||||||
|
$pages[] = $row;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $pages;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function pageSave(
|
||||||
|
$pageId, $title, $seoLink, $metaTitle, $metaDescription, $metaKeywords, $menuId, $parentId, $pageType, $sortType, $layoutId, $articlesLimit, $showTitle, $status, $link, $noindex, $start,
|
||||||
|
$siteTitle, $blockDirectAccess, $cache, $canonical
|
||||||
|
)
|
||||||
|
{
|
||||||
|
$parentId = $parentId ? $parentId : null;
|
||||||
|
|
||||||
|
if ( !$pageId )
|
||||||
|
return $this->createPage(
|
||||||
|
$title, $seoLink, $metaTitle, $metaDescription, $metaKeywords, $menuId, $parentId, $pageType, $sortType, $layoutId, $articlesLimit, $showTitle, $status, $link, $noindex, $start,
|
||||||
|
$siteTitle, $blockDirectAccess, $cache, $canonical
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->updatePage(
|
||||||
|
$pageId, $title, $seoLink, $metaTitle, $metaDescription, $metaKeywords, $menuId, $parentId, $pageType, $sortType, $layoutId, $articlesLimit, $showTitle, $status, $link, $noindex, $start,
|
||||||
|
$siteTitle, $blockDirectAccess, $cache, $canonical
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createPage(
|
||||||
|
$title, $seoLink, $metaTitle, $metaDescription, $metaKeywords, $menuId, $parentId, $pageType, $sortType, $layoutId, $articlesLimit, $showTitle, $status, $link, $noindex, $start, $siteTitle,
|
||||||
|
$blockDirectAccess, $cache, $canonical
|
||||||
|
)
|
||||||
|
{
|
||||||
|
$order = $this->maxOrder() + 1;
|
||||||
|
|
||||||
|
$this->db->insert( 'pp_pages', [
|
||||||
|
'menu_id' => (int) $menuId,
|
||||||
|
'page_type' => $pageType,
|
||||||
|
'sort_type' => $sortType,
|
||||||
|
'articles_limit' => $articlesLimit,
|
||||||
|
'show_title' => $showTitle == 'on' ? 1 : 0,
|
||||||
|
'status' => $status == 'on' ? 1 : 0,
|
||||||
|
'o' => (int) $order,
|
||||||
|
'parent_id' => $parentId,
|
||||||
|
'start' => $start == 'on' ? 1 : 0,
|
||||||
|
'cache' => $cache == 'on' ? 1 : 0,
|
||||||
|
] );
|
||||||
|
|
||||||
|
$id = $this->db->id();
|
||||||
|
if ( !$id )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ( $start )
|
||||||
|
$this->db->update( 'pp_pages', [ 'start' => 0 ], [ 'id[!]' => (int) $id ] );
|
||||||
|
|
||||||
|
if ( $layoutId )
|
||||||
|
$this->db->insert( 'pp_layouts_pages', [ 'page_id' => (int) $id, 'layout_id' => (int) $layoutId ] );
|
||||||
|
|
||||||
|
$languages = $this->activeLanguages();
|
||||||
|
$this->savePageLanguages( (int) $id, $languages, $title, $metaDescription, $metaKeywords, $metaTitle, $seoLink, $noindex, $siteTitle, $link, $blockDirectAccess, $canonical );
|
||||||
|
|
||||||
|
\S::htacces();
|
||||||
|
\S::delete_cache();
|
||||||
|
|
||||||
|
return $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function updatePage(
|
||||||
|
$pageId, $title, $seoLink, $metaTitle, $metaDescription, $metaKeywords, $menuId, $parentId, $pageType, $sortType, $layoutId, $articlesLimit, $showTitle, $status, $link, $noindex, $start,
|
||||||
|
$siteTitle, $blockDirectAccess, $cache, $canonical
|
||||||
|
)
|
||||||
|
{
|
||||||
|
$this->db->update( 'pp_pages', [
|
||||||
|
'menu_id' => (int) $menuId,
|
||||||
|
'page_type' => $pageType,
|
||||||
|
'sort_type' => $sortType,
|
||||||
|
'articles_limit' => $articlesLimit,
|
||||||
|
'show_title' => $showTitle == 'on' ? 1 : 0,
|
||||||
|
'status' => $status == 'on' ? 1 : 0,
|
||||||
|
'parent_id' => $parentId,
|
||||||
|
'start' => $start == 'on' ? 1 : 0,
|
||||||
|
'cache' => $cache == 'on' ? 1 : 0,
|
||||||
|
], [
|
||||||
|
'id' => (int) $pageId
|
||||||
|
] );
|
||||||
|
|
||||||
|
if ( $layoutId )
|
||||||
|
{
|
||||||
|
$this->db->delete( 'pp_layouts_pages', [ 'page_id' => (int) $pageId ] );
|
||||||
|
$this->db->insert( 'pp_layouts_pages', [ 'layout_id' => (int) $layoutId, 'page_id' => (int) $pageId ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $start )
|
||||||
|
$this->db->update( 'pp_pages', [ 'start' => 0 ], [ 'id[!]' => (int) $pageId ] );
|
||||||
|
|
||||||
|
$this->db->delete( 'pp_pages_langs', [ 'page_id' => (int) $pageId ] );
|
||||||
|
|
||||||
|
$languages = $this->activeLanguages();
|
||||||
|
$this->savePageLanguages( (int) $pageId, $languages, $title, $metaDescription, $metaKeywords, $metaTitle, $seoLink, $noindex, $siteTitle, $link, $blockDirectAccess, $canonical );
|
||||||
|
|
||||||
|
$this->updateSubpagesMenuIdRecursive( (int) $pageId, (int) $menuId );
|
||||||
|
|
||||||
|
\S::htacces();
|
||||||
|
\S::delete_cache();
|
||||||
|
|
||||||
|
return $pageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function activeLanguages(): array
|
||||||
|
{
|
||||||
|
return $this->db->select( 'pp_langs', [ 'id' ], [ 'status' => 1, 'ORDER' => [ 'o' => 'ASC' ] ] ) ?: [];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function savePageLanguages(
|
||||||
|
int $pageId, array $languages, $title, $metaDescription, $metaKeywords, $metaTitle, $seoLink, $noindex, $siteTitle, $link, $blockDirectAccess, $canonical
|
||||||
|
): void
|
||||||
|
{
|
||||||
|
$isMulti = count( $languages ) > 1;
|
||||||
|
|
||||||
|
foreach ( $languages as $i => $row )
|
||||||
|
{
|
||||||
|
$titleValue = $this->languageValue( $title, $i, $isMulti );
|
||||||
|
$metaDescriptionValue = $this->languageValue( $metaDescription, $i, $isMulti );
|
||||||
|
$metaKeywordsValue = $this->languageValue( $metaKeywords, $i, $isMulti );
|
||||||
|
$metaTitleValue = $this->languageValue( $metaTitle, $i, $isMulti );
|
||||||
|
$seoLinkValue = $this->languageValue( $seoLink, $i, $isMulti );
|
||||||
|
$noindexValue = $this->languageValue( $noindex, $i, $isMulti );
|
||||||
|
$siteTitleValue = $this->languageValue( $siteTitle, $i, $isMulti );
|
||||||
|
$linkValue = $this->languageValue( $link, $i, $isMulti );
|
||||||
|
$blockDirectAccessValue = $this->languageValue( $blockDirectAccess, $i, $isMulti );
|
||||||
|
$canonicalValue = $this->languageValue( $canonical, $i, $isMulti );
|
||||||
|
|
||||||
|
$seo = \S::seo( $seoLinkValue );
|
||||||
|
|
||||||
|
$this->db->insert( 'pp_pages_langs', [
|
||||||
|
'page_id' => $pageId,
|
||||||
|
'lang_id' => $row['id'],
|
||||||
|
'title' => $this->nullIfEmpty( $titleValue ),
|
||||||
|
'meta_description' => $this->nullIfEmpty( $metaDescriptionValue ),
|
||||||
|
'meta_keywords' => $this->nullIfEmpty( $metaKeywordsValue ),
|
||||||
|
'meta_title' => $this->nullIfEmpty( $metaTitleValue ),
|
||||||
|
'seo_link' => $seo != '' ? $seo : null,
|
||||||
|
'noindex' => $noindexValue,
|
||||||
|
'site_title' => $this->nullIfEmpty( $siteTitleValue ),
|
||||||
|
'link' => $this->nullIfEmpty( $linkValue ),
|
||||||
|
'block_direct_access' => $blockDirectAccessValue,
|
||||||
|
'canonical' => $this->nullIfEmpty( $canonicalValue )
|
||||||
|
] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function languageValue( $value, int $index, bool $isMulti )
|
||||||
|
{
|
||||||
|
if ( $isMulti )
|
||||||
|
return is_array( $value ) ? ( $value[$index] ?? null ) : null;
|
||||||
|
|
||||||
|
return is_array( $value ) ? ( $value[0] ?? null ) : $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function nullIfEmpty( $value )
|
||||||
|
{
|
||||||
|
return $value != '' ? $value : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function updateSubpagesMenuIdRecursive( int $parentId, int $menuId ): void
|
||||||
|
{
|
||||||
|
$this->db->update( 'pp_pages', [ 'menu_id' => $menuId ], [ 'parent_id' => $parentId ] );
|
||||||
|
|
||||||
|
$results = $this->db->select( 'pp_pages', [ 'id' ], [ 'parent_id' => $parentId ] );
|
||||||
|
if ( is_array( $results ) )
|
||||||
|
foreach ( $results as $row )
|
||||||
|
$this->updateSubpagesMenuIdRecursive( (int) $row['id'], $menuId );
|
||||||
|
}
|
||||||
|
}
|
||||||
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 ] );
|
||||||
|
}
|
||||||
|
}
|
||||||
94
autoload/Shared/Email/Email.php
Normal file
94
autoload/Shared/Email/Email.php
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
<?php
|
||||||
|
namespace Shared\Email;
|
||||||
|
|
||||||
|
class Email
|
||||||
|
{
|
||||||
|
public $table = 'pp_newsletter_templates';
|
||||||
|
public $text = '';
|
||||||
|
|
||||||
|
public function load_by_name( string $name )
|
||||||
|
{
|
||||||
|
global $mdb;
|
||||||
|
|
||||||
|
$result = $mdb->get( $this->table, '*', [ 'name' => $name ] );
|
||||||
|
if ( is_array( $result ) ) foreach ( $result as $key => $val )
|
||||||
|
$this->$key = $val;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function email_check( $email )
|
||||||
|
{
|
||||||
|
return filter_var( $email, FILTER_VALIDATE_EMAIL );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function send( string $email, string $subject, $replay = '', $file = '' )
|
||||||
|
{
|
||||||
|
global $settings;
|
||||||
|
|
||||||
|
$base = dirname( dirname( dirname( __DIR__ ) ) );
|
||||||
|
|
||||||
|
if ( file_exists( $base . '/libraries/phpmailer/class.phpmailer.php' ) )
|
||||||
|
require_once $base . '/libraries/phpmailer/class.phpmailer.php';
|
||||||
|
if ( file_exists( $base . '/libraries/phpmailer/class.smtp.php' ) )
|
||||||
|
require_once $base . '/libraries/phpmailer/class.smtp.php';
|
||||||
|
|
||||||
|
$text = $this->text;
|
||||||
|
|
||||||
|
$regex = "-(<img[^>]+src\s*=\s*['\"])(((?!'|\"|https?://).)*)(['\"][^>]*>)-i";
|
||||||
|
$text = preg_replace( $regex, "$1https://" . $_SERVER['SERVER_NAME'] . "$2$4", $text );
|
||||||
|
|
||||||
|
$regex = "-(<a[^>]+href\s*=\s*['\"])(((?!'|\"|https?://).)*)(['\"][^>]*>)-i";
|
||||||
|
$text = preg_replace( $regex, "$1https://" . $_SERVER['SERVER_NAME'] . "$2$4", $text );
|
||||||
|
|
||||||
|
if ( $this->email_check( $email ) and $subject )
|
||||||
|
{
|
||||||
|
$mail = new \PHPMailer();
|
||||||
|
$mail->IsSMTP();
|
||||||
|
$mail->SMTPAuth = true;
|
||||||
|
$mail->Host = $settings['email_host'];
|
||||||
|
$mail->Port = $settings['email_port'];
|
||||||
|
$mail->Username = $settings['email_login'];
|
||||||
|
$mail->Password = $settings['email_password'];
|
||||||
|
$mail->CharSet = "UTF-8";
|
||||||
|
$mail->SMTPOptions = array(
|
||||||
|
'ssl' => array(
|
||||||
|
'verify_peer' => false,
|
||||||
|
'verify_peer_name' => false,
|
||||||
|
'allow_self_signed' => true
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( $this->email_check( $replay ) )
|
||||||
|
{
|
||||||
|
$mail->AddReplyTo( $replay, $replay );
|
||||||
|
$mail->SetFrom( $settings['contact_email'], $settings['contact_email'] );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$mail->AddReplyTo( $settings['contact_email'], $settings['firm_name'] );
|
||||||
|
$mail->SetFrom( $settings['contact_email'], $settings['firm_name'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
$mail->AddAddress( $email, '' );
|
||||||
|
$mail->Subject = $subject;
|
||||||
|
$mail->Body = $text;
|
||||||
|
|
||||||
|
if ( is_array( $file ) )
|
||||||
|
{
|
||||||
|
foreach ( $file as $file_tmp )
|
||||||
|
{
|
||||||
|
if ( file_exists( $file_tmp ) )
|
||||||
|
$mail->AddAttachment( $file_tmp );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ( file_exists( $file ) )
|
||||||
|
$mail->AddAttachment( $file );
|
||||||
|
}
|
||||||
|
|
||||||
|
$mail->IsHTML( true );
|
||||||
|
return $mail->Send();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -320,22 +320,13 @@ class Helpers
|
|||||||
|
|
||||||
public static function is_token_valid($token)
|
public static function is_token_valid($token)
|
||||||
{
|
{
|
||||||
if (!empty($_SESSION['tokens'][$token]))
|
return \Shared\Security\CsrfToken::validate($token);
|
||||||
{
|
|
||||||
unset($_SESSION['tokens'][$token]);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function get_token()
|
public static function get_token()
|
||||||
{
|
{
|
||||||
$token = sha1(mt_rand());
|
\Shared\Security\CsrfToken::regenerate();
|
||||||
if (!isset($_SESSION['tokens']))
|
return \Shared\Security\CsrfToken::getToken();
|
||||||
$_SESSION['tokens'] = [$token => 1];
|
|
||||||
else
|
|
||||||
$_SESSION['tokens'][$token] = 1;
|
|
||||||
return $token;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function get_domain($url)
|
public static function get_domain($url)
|
||||||
@@ -1222,60 +1213,8 @@ class Helpers
|
|||||||
|
|
||||||
public static function send_email( $email, $subject, $text, $replay = '', $file = '' )
|
public static function send_email( $email, $subject, $text, $replay = '', $file = '' )
|
||||||
{
|
{
|
||||||
global $settings;
|
$emailObj = new \Shared\Email\Email();
|
||||||
|
$emailObj->text = $text;
|
||||||
if ( file_exists('libraries/phpmailer/class.phpmailer.php') ) require_once 'libraries/phpmailer/class.phpmailer.php';
|
return $emailObj->send( $email, $subject, $replay, $file );
|
||||||
if ( file_exists('libraries/phpmailer/class.smtp.php') ) require_once 'libraries/phpmailer/class.smtp.php';
|
|
||||||
if ( file_exists('../libraries/phpmailer/class.phpmailer.php') ) require_once '../libraries/phpmailer/class.phpmailer.php';
|
|
||||||
if ( file_exists('../libraries/phpmailer/class.smtp.php') ) require_once '../libraries/phpmailer/class.smtp.php';
|
|
||||||
if ( $email and $subject )
|
|
||||||
{
|
|
||||||
$mail = new \PHPMailer();
|
|
||||||
$mail->IsSMTP();
|
|
||||||
$mail->SMTPAuth = true;
|
|
||||||
$mail->Host = $settings['email_host'];
|
|
||||||
$mail->Port = $settings['email_port'];
|
|
||||||
$mail->Username = $settings['email_login'];
|
|
||||||
$mail->Password = $settings['email_password'];
|
|
||||||
$mail->CharSet = "UTF-8";
|
|
||||||
$mail->SMTPOptions = array(
|
|
||||||
'ssl' => array(
|
|
||||||
'verify_peer' => false,
|
|
||||||
'verify_peer_name' => false,
|
|
||||||
'allow_self_signed' => true
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (self::email_check($replay))
|
|
||||||
{
|
|
||||||
$mail->AddReplyTo($replay, $replay);
|
|
||||||
$mail->SetFrom($settings['contact_email'], $settings['contact_email']);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$mail->AddReplyTo($settings['contact_email'], $settings['firm_name']);
|
|
||||||
$mail->SetFrom($settings['contact_email'], $settings['firm_name']);
|
|
||||||
}
|
|
||||||
|
|
||||||
$mail->AddAddress($email, '');
|
|
||||||
$mail->Subject = $subject;
|
|
||||||
$mail->Body = $text;
|
|
||||||
if (is_array($file))
|
|
||||||
{
|
|
||||||
foreach ($file as $file_tmp)
|
|
||||||
{
|
|
||||||
if (file_exists($file_tmp))
|
|
||||||
$mail->AddAttachment($file_tmp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (file_exists($file))
|
|
||||||
$mail->AddAttachment($file);
|
|
||||||
}
|
|
||||||
$mail->IsHTML(true);
|
|
||||||
return $mail->Send();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
28
autoload/Shared/Security/CsrfToken.php
Normal file
28
autoload/Shared/Security/CsrfToken.php
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
namespace Shared\Security;
|
||||||
|
|
||||||
|
class CsrfToken
|
||||||
|
{
|
||||||
|
const SESSION_KEY = 'csrf_token';
|
||||||
|
|
||||||
|
public static function getToken()
|
||||||
|
{
|
||||||
|
if ( empty( $_SESSION[self::SESSION_KEY] ) )
|
||||||
|
$_SESSION[self::SESSION_KEY] = bin2hex( random_bytes( 32 ) );
|
||||||
|
|
||||||
|
return $_SESSION[self::SESSION_KEY];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function validate( $token )
|
||||||
|
{
|
||||||
|
if ( empty( $_SESSION[self::SESSION_KEY] ) || empty( $token ) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return hash_equals( $_SESSION[self::SESSION_KEY], $token );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function regenerate()
|
||||||
|
{
|
||||||
|
unset( $_SESSION[self::SESSION_KEY] );
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,714 +1,117 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace admin\factory;
|
namespace admin\factory;
|
||||||
|
|
||||||
class Articles
|
class Articles
|
||||||
{
|
{
|
||||||
public static function duplicate_article( $article_id )
|
private static function repo(): \Domain\Articles\ArticlesRepository
|
||||||
{
|
{
|
||||||
global $mdb, $user;
|
global $mdb;
|
||||||
|
return new \Domain\Articles\ArticlesRepository( $mdb );
|
||||||
$article = \admin\factory\Articles::article_details( $article_id );
|
|
||||||
|
|
||||||
if ( $article )
|
|
||||||
{
|
|
||||||
$mdb -> insert( 'pp_articles', [
|
|
||||||
'show_title' => $article['show_title'],
|
|
||||||
'show_date_add' => $article['show_date_add'],
|
|
||||||
'show_date_modify' => $article['show_date_modify'],
|
|
||||||
'date_add' => date( 'Y-m-d H:i:s' ),
|
|
||||||
'date_modify' => date( 'Y-m-d H:i:s' ),
|
|
||||||
'modify_by' => $user['id'],
|
|
||||||
'layout_id' => $article['layout_id'],
|
|
||||||
'status' => $article['status'],
|
|
||||||
'repeat_entry' => $article['repeat_entry'],
|
|
||||||
'social_icons' => $article['social_icons'],
|
|
||||||
'date_start' => $article['date_start'],
|
|
||||||
'date_end' => $article['event_date'],
|
|
||||||
'priority' => $article['priority'],
|
|
||||||
'password' => $article['password'],
|
|
||||||
'pixieset' => $article['pixieset']
|
|
||||||
] );
|
|
||||||
|
|
||||||
$article_tmp_id = $mdb -> id();
|
|
||||||
|
|
||||||
if ( $article_tmp_id )
|
|
||||||
{
|
|
||||||
foreach ( $article['languages'] as $key => $val )
|
|
||||||
{
|
|
||||||
$mdb -> insert( 'pp_articles_langs', [
|
|
||||||
'article_id' => $article_tmp_id,
|
|
||||||
'lang_id' => $key,
|
|
||||||
'title' => 'Kopia: ' . $val['title'],
|
|
||||||
'entry' => $val['entry'],
|
|
||||||
'text' => $val['text'],
|
|
||||||
'meta_title' => null,
|
|
||||||
'meta_description' => null,
|
|
||||||
'meta_keywords' => null,
|
|
||||||
'seo_link' => null,
|
|
||||||
'copy_from' => $val['copy_from'],
|
|
||||||
'block_direct_access' => $val['block_direct_access']
|
|
||||||
] );
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ( $article['params'] as $param )
|
|
||||||
{
|
|
||||||
$mdb -> insert( 'pp_articles_additional_values', [
|
|
||||||
'param_id' => $param['param_id'],
|
|
||||||
'value' => $param['value'],
|
|
||||||
'article_id' => $article_tmp_id,
|
|
||||||
'language_id' => $param['language_id']
|
|
||||||
] );
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ( $article['pages'] as $page )
|
|
||||||
{
|
|
||||||
$order = self::max_order() + 1;
|
|
||||||
$mdb -> insert( 'pp_articles_pages', [
|
|
||||||
'article_id' => $article_tmp_id,
|
|
||||||
'page_id' => $page,
|
|
||||||
'o' => (int)$order
|
|
||||||
] );
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function insert_missing_hash() {
|
public static function duplicate_article( $article_id )
|
||||||
global $mdb;
|
{
|
||||||
|
global $user;
|
||||||
|
return self::repo()->duplicateArticle( $article_id, (int)$user['id'] );
|
||||||
|
}
|
||||||
|
|
||||||
if ( $mdb -> count( 'pp_articles', [ 'hash' => null ] ) ) {
|
public static function insert_missing_hash()
|
||||||
$rows = $mdb -> select( 'pp_articles', [ 'id', 'date_add' ], [ 'hash' => null ] );
|
{
|
||||||
if ( is_array( $rows ) ) foreach ( $rows as $row ) {
|
return self::repo()->insertMissingHash();
|
||||||
$mdb -> update( 'pp_articles', [ 'hash' => md5( $row['id'] . $row['date_add'] ) ], [ 'id' => $row['id'] ] );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static public function files_order_save( $article_id, $order )
|
static public function files_order_save( $article_id, $order )
|
||||||
{
|
{
|
||||||
global $mdb;
|
self::repo()->filesOrderSave( $article_id, $order );
|
||||||
|
|
||||||
$order = explode( ';', $order );
|
|
||||||
if ( is_array( $order ) and !empty( $order ) ) foreach ( $order as $file_id )
|
|
||||||
{
|
|
||||||
$mdb -> update( 'pp_articles_files', [
|
|
||||||
'o' => (int)$i++
|
|
||||||
], [
|
|
||||||
'AND' => [
|
|
||||||
'article_id' => $article_id,
|
|
||||||
'id' => $file_id
|
|
||||||
]
|
|
||||||
] );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function gallery_order_save( $article_id, $order )
|
public static function gallery_order_save( $article_id, $order )
|
||||||
{
|
{
|
||||||
global $mdb;
|
self::repo()->galleryOrderSave( $article_id, $order );
|
||||||
|
|
||||||
$order = explode( ';', $order );
|
|
||||||
if ( is_array( $order ) and !empty( $order ) ) foreach ( $order as $image_id )
|
|
||||||
{
|
|
||||||
$mdb -> update( 'pp_articles_images', [
|
|
||||||
'o' => $i++
|
|
||||||
], [
|
|
||||||
'AND' => [
|
|
||||||
'article_id' => $article_id,
|
|
||||||
'id' => $image_id
|
|
||||||
]
|
|
||||||
] );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function additional_params( $language = 0 )
|
public static function additional_params( $language = 0 )
|
||||||
{
|
{
|
||||||
global $mdb;
|
return self::repo()->additionalParams( $language );
|
||||||
return $mdb -> select( 'pp_articles_additional_params', '*', [ 'AND' => [ 'status' => 1, 'language' => $language ] ] );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function image_alt_change( $image_id, $image_alt )
|
public static function image_alt_change( $image_id, $image_alt )
|
||||||
{
|
{
|
||||||
global $mdb;
|
return self::repo()->imageAltChange( $image_id, $image_alt );
|
||||||
$result = $mdb -> update( 'pp_articles_images', [
|
|
||||||
'alt' => $image_alt
|
|
||||||
], [
|
|
||||||
'id' => $image_id
|
|
||||||
] );
|
|
||||||
\S::delete_cache();
|
|
||||||
return $result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function articles_by_date_add( $date_start, $date_end )
|
public static function articles_by_date_add( $date_start, $date_end )
|
||||||
{
|
{
|
||||||
global $mdb;
|
return self::repo()->articlesByDateAdd( $date_start, $date_end );
|
||||||
|
|
||||||
$results = $mdb -> query( 'SELECT '
|
|
||||||
. 'id '
|
|
||||||
. 'FROM '
|
|
||||||
. 'pp_articles '
|
|
||||||
. 'WHERE '
|
|
||||||
. 'status = 1 '
|
|
||||||
. 'AND '
|
|
||||||
. 'date_add BETWEEN \'' . $date_start . '\' AND \'' . $date_end . '\' '
|
|
||||||
. 'ORDER BY '
|
|
||||||
. 'date_add DESC' ) -> fetchAll();
|
|
||||||
if ( is_array( $results ) and !empty( $results ) ) foreach ( $results as $row )
|
|
||||||
$articles[] = \front\factory\Articles::article_details( $row['id'], 'pl' );
|
|
||||||
|
|
||||||
return $articles;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function article_url( $article_id )
|
public static function article_url( $article_id )
|
||||||
{
|
{
|
||||||
global $mdb;
|
return self::repo()->articleUrl( $article_id );
|
||||||
|
|
||||||
$results = $mdb -> query( "SELECT seo_link FROM pp_articles_langs AS pal, pp_langs AS pl WHERE lang_id = pl.id AND article_id = " . (int)$article_id . " AND seo_link != '' ORDER BY o ASC LIMIT 1" ) -> fetchAll();
|
|
||||||
if ( !$results[0]['seo_link'] )
|
|
||||||
{
|
|
||||||
$title = self::article_title( $article_id );
|
|
||||||
return 'a-' . $article_id . '-' . \S::seo( $title );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return $results[0]['seo_link'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function article_pages( $article_id )
|
public static function article_pages( $article_id )
|
||||||
{
|
{
|
||||||
global $mdb;
|
return self::repo()->articlePages( $article_id );
|
||||||
|
|
||||||
$results = $mdb -> query( "SELECT page_id FROM pp_articles_pages WHERE article_id = " . (int)$article_id ) -> fetchAll();
|
|
||||||
if ( is_array( $results ) and !empty( $results ) ) foreach ( $results as $row )
|
|
||||||
{
|
|
||||||
if ( $out == '' )
|
|
||||||
$out .= ' - ';
|
|
||||||
|
|
||||||
$out .= \admin\factory\Pages::page_title( $row['page_id'] );
|
|
||||||
|
|
||||||
if ( end( $results ) != $row )
|
|
||||||
$out .= ' / ';
|
|
||||||
}
|
|
||||||
|
|
||||||
return $out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function article_title( $article_id )
|
public static function article_title( $article_id )
|
||||||
{
|
{
|
||||||
global $mdb;
|
return self::repo()->articleTitle( $article_id );
|
||||||
$results = $mdb -> query( "SELECT title FROM pp_articles_langs AS pal, pp_langs AS pl WHERE lang_id = pl.id AND article_id = " . (int)$article_id . " AND title != '' ORDER BY o ASC LIMIT 1" ) -> fetchAll();
|
|
||||||
return $results[0]['title'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function articles_set_archive( $article_id )
|
public static function articles_set_archive( $article_id )
|
||||||
{
|
{
|
||||||
global $mdb;
|
return self::repo()->articlesSetArchive( $article_id );
|
||||||
$result = $mdb -> update( 'pp_articles', [ 'status' => -1 ], [ 'id' => (int)$article_id ] );
|
|
||||||
\S::htacces();
|
|
||||||
\S::delete_cache();
|
|
||||||
return $result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function file_name_change( $file_id, $file_name )
|
public static function file_name_change( $file_id, $file_name )
|
||||||
{
|
{
|
||||||
global $mdb;
|
return self::repo()->fileNameChange( $file_id, $file_name );
|
||||||
$mdb -> update( 'pp_articles_files', [ 'name' => $file_name ], [ 'id' => (int)$file_id ] );
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function delete_file( $file_id )
|
public static function delete_file( $file_id )
|
||||||
{
|
{
|
||||||
global $mdb;
|
return self::repo()->deleteFile( $file_id );
|
||||||
$mdb -> update( 'pp_articles_files', [ 'to_delete' => 1 ], [ 'id' => (int)$file_id ] );
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function delete_img( $image_id )
|
public static function delete_img( $image_id )
|
||||||
{
|
{
|
||||||
global $mdb;
|
return self::repo()->deleteImg( $image_id );
|
||||||
$mdb -> update( 'pp_articles_images', [ 'to_delete' => 1 ], [ 'id' => (int)$image_id ] );
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function article_details( $article_id )
|
public static function article_details( $article_id )
|
||||||
{
|
{
|
||||||
global $mdb;
|
return self::repo()->articleDetails( $article_id );
|
||||||
|
|
||||||
if ( $article = $mdb -> get( 'pp_articles', '*', [ 'id' => (int)$article_id ] ) )
|
|
||||||
{
|
|
||||||
$results = $mdb -> select( 'pp_articles_langs', '*', [ 'article_id' => (int)$article_id ] );
|
|
||||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
|
||||||
$article['languages'][ $row['lang_id'] ] = $row;
|
|
||||||
|
|
||||||
$article['images'] = $mdb -> select( 'pp_articles_images', '*', [ 'article_id' => (int)$article_id, 'ORDER' => [ 'o' => 'ASC', 'id' => 'ASC' ] ] );
|
|
||||||
$article['files'] = $mdb -> select( 'pp_articles_files', '*', [ 'article_id' => (int)$article_id, 'ORDER' => [ 'o' => 'ASC', 'id' => 'ASC' ] ] );
|
|
||||||
$article['pages'] = $mdb -> select( 'pp_articles_pages', 'page_id', [ 'article_id' => (int)$article_id ] );
|
|
||||||
$article['tags'] = $mdb -> select( 'pp_tags', [ '[><]pp_articles_tags' => [ 'id' => 'tag_id' ] ], 'name', [ 'article_id' => (int)$article_id ] );
|
|
||||||
$article['params'] = $mdb -> select( 'pp_articles_additional_values', [ 'param_id', 'value', 'language_id' ], [ 'article_id' => (int)$article_id ] );
|
|
||||||
}
|
|
||||||
|
|
||||||
return $article;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function max_order()
|
public static function max_order()
|
||||||
{
|
{
|
||||||
global $mdb;
|
return self::repo()->maxOrder();
|
||||||
return $mdb -> max( 'pp_articles_pages', 'o' );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function article_save(
|
public static function article_save(
|
||||||
$article_id, $title, $main_image, $entry, $text, $table_of_contents, $status, $show_title, $show_table_of_contents, $show_date_add, $date_add, $show_date_modify, $date_modify, $seo_link, $meta_title, $meta_description,
|
$article_id, $title, $main_image, $entry, $text, $table_of_contents, $status, $show_title, $show_table_of_contents, $show_date_add, $date_add, $show_date_modify, $date_modify, $seo_link, $meta_title, $meta_description,
|
||||||
$meta_keywords, $layout_id, $pages, $noindex, $repeat_entry, $copy_from, $social_icons, $event_date, $tags, $block_direct_access, $priority,
|
$meta_keywords, $layout_id, $pages, $noindex, $repeat_entry, $copy_from, $social_icons, $event_date, $tags, $block_direct_access, $priority,
|
||||||
$password, $pixieset, $id_author, $params )
|
$password, $pixieset, $id_author, $params
|
||||||
|
)
|
||||||
{
|
{
|
||||||
|
global $user;
|
||||||
global $mdb, $user;
|
return self::repo()->articleSave(
|
||||||
|
$article_id, $title, $main_image, $entry, $text, $table_of_contents, $status, $show_title, $show_table_of_contents, $show_date_add, $date_add, $show_date_modify, $date_modify, $seo_link, $meta_title, $meta_description,
|
||||||
$event_date = explode( ' - ', $event_date );
|
$meta_keywords, $layout_id, $pages, $noindex, $repeat_entry, $copy_from, $social_icons, $event_date, $tags, $block_direct_access, $priority,
|
||||||
|
$password, $pixieset, $id_author, $params, (int)$user['id']
|
||||||
if ( !$article_id )
|
);
|
||||||
{
|
|
||||||
$mdb -> insert( 'pp_articles', [
|
|
||||||
'show_title' => $show_title == 'on' ? 1 : 0,
|
|
||||||
'show_table_of_contents' => $show_table_of_contents == 'on' ? 1 : 0,
|
|
||||||
'show_date_add' => $show_date_add == 'on' ? 1 : 0,
|
|
||||||
'show_date_modify' => $show_date_modify == 'on' ? 1 : 0,
|
|
||||||
'date_add' => date( 'Y-m-d H:i:s' ),
|
|
||||||
'date_modify' => date( 'Y-m-d H:i:s' ),
|
|
||||||
'modify_by' => $user['id'],
|
|
||||||
'layout_id' => $layout_id ? (int)$layout_id : null,
|
|
||||||
'status' => $status == 'on' ? 1 : 0,
|
|
||||||
'repeat_entry' => $repeat_entry == 'on' ? 1 : 0,
|
|
||||||
'social_icons' => $social_icons == 'on' ? 1 : 0,
|
|
||||||
'date_start' => $event_date[0] ? $event_date[0] : null,
|
|
||||||
'date_end' => $event_date[1] ? $event_date[1] : null,
|
|
||||||
'priority' => $priority == 'on' ? 1 : 0,
|
|
||||||
'password' => $password ? $password : null,
|
|
||||||
'pixieset' => $pixieset,
|
|
||||||
'id_author' => $id_author ? $id_author : null
|
|
||||||
] );
|
|
||||||
|
|
||||||
$id = $mdb -> id();
|
|
||||||
|
|
||||||
if ( $id )
|
|
||||||
{
|
|
||||||
$i = 0;
|
|
||||||
|
|
||||||
/* tłumaczenia */
|
|
||||||
$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_articles_langs', [
|
|
||||||
'article_id' => (int)$id,
|
|
||||||
'lang_id' => $row['id'],
|
|
||||||
'title' => $title[ $i ] != '' ? $title[ $i ] : null,
|
|
||||||
'main_image' => $main_image[$i] != '' ? $main_image[$i] : null,
|
|
||||||
'entry' => $entry[ $i ] != '' ? $entry[ $i ] : null,
|
|
||||||
'text' => $text[ $i ] != '' ? $text[ $i ] : null,
|
|
||||||
'table_of_contents' => $table_of_contents[$i] != '' ? $table_of_contents[$i] : null,
|
|
||||||
'meta_title' => $meta_title[ $i ] != '' ? $meta_title[ $i ] : null,
|
|
||||||
'meta_description' => $meta_description[ $i ] != '' ? $meta_description[ $i ] : null,
|
|
||||||
'meta_keywords' => $meta_keywords[ $i ] != '' ? $meta_keywords[ $i ] : null,
|
|
||||||
'seo_link' => \S::seo( $seo_link[ $i ] ) != '' ? \S::seo( $seo_link[ $i ] ) : null,
|
|
||||||
'noindex' => $noindex[ $i ],
|
|
||||||
'copy_from' => $copy_from[ $i ] != '' ? $copy_from[ $i ] : null,
|
|
||||||
'block_direct_access' => $block_direct_access[ $i ]
|
|
||||||
] );
|
|
||||||
$i++;
|
|
||||||
}
|
|
||||||
else if ( is_array( $results ) and count( $results ) == 1 ) foreach ( $results as $row )
|
|
||||||
{
|
|
||||||
$mdb -> insert( 'pp_articles_langs', [
|
|
||||||
'article_id' => (int)$id,
|
|
||||||
'lang_id' => $row['id'],
|
|
||||||
'title' => $title != '' ? $title : null,
|
|
||||||
'main_image' => $main_image != '' ? $main_image : null,
|
|
||||||
'entry' => $entry != '' ? $entry : null,
|
|
||||||
'text' => $text != '' ? $text : null,
|
|
||||||
'table_of_contents' => $table_of_contents != '' ? $table_of_contents : null,
|
|
||||||
'meta_title' => $meta_title != '' ? $meta_title : null,
|
|
||||||
'meta_description' => $meta_description != '' ? $meta_description : null,
|
|
||||||
'meta_keywords' => $meta_keywords != '' ? $meta_keywords : null,
|
|
||||||
'seo_link' => \S::seo( $seo_link ) != '' ? \S::seo( $seo_link ) : null,
|
|
||||||
'noindex' => $noindex,
|
|
||||||
'copy_from' => $copy_from != '' ? $copy_from : null,
|
|
||||||
'block_direct_access' => $block_direct_access
|
|
||||||
] );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* parametry bez wersji językowych */
|
|
||||||
$results = $mdb -> select( 'pp_articles_additional_params', '*', [ 'AND' => [ 'status' => 1, 'language' => 0 ] ] );
|
|
||||||
if ( is_array( $results ) and !empty( $results ) ) foreach ( $results as $row )
|
|
||||||
{
|
|
||||||
$mdb -> insert( 'pp_articles_additional_values', [
|
|
||||||
'param_id' => $row['id'],
|
|
||||||
'value' => $params[ 'ap_' . $row['name'] ],
|
|
||||||
'article_id' => (int)$id,
|
|
||||||
'language_id' => null
|
|
||||||
] );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* strony */
|
|
||||||
if ( is_array( $pages ) ) foreach ( $pages as $page )
|
|
||||||
{
|
|
||||||
$order = self::max_order() + 1;
|
|
||||||
|
|
||||||
$mdb -> insert( 'pp_articles_pages', [
|
|
||||||
'article_id' => (int)$id,
|
|
||||||
'page_id' => (int)$page,
|
|
||||||
'o' => (int)$order
|
|
||||||
] );
|
|
||||||
}
|
|
||||||
else if ( $pages )
|
|
||||||
{
|
|
||||||
$order = self::max_order() + 1;
|
|
||||||
|
|
||||||
$mdb -> insert( 'pp_articles_pages', [
|
|
||||||
'article_id' => (int)$id,
|
|
||||||
'page_id' => (int)$pages,
|
|
||||||
'o' => (int)$order
|
|
||||||
] );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* pliki */
|
|
||||||
$results = $mdb -> select( 'pp_articles_files', '*', [ 'article_id' => null ] );
|
|
||||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
|
||||||
{
|
|
||||||
$dir = '/upload/article_files/article_' . $id;
|
|
||||||
|
|
||||||
$new_file_name = str_replace( '/upload/article_files/tmp', $dir, $row['src'] );
|
|
||||||
|
|
||||||
if ( file_exists( '..' . $row['src'] ) )
|
|
||||||
{
|
|
||||||
if ( !is_dir( '../' . $dir ) and $created !== true )
|
|
||||||
{
|
|
||||||
if ( mkdir( '../' . $dir, 0755, true ) )
|
|
||||||
$created = true;
|
|
||||||
}
|
|
||||||
rename( '..' . $row['src'], '..' . $new_file_name );
|
|
||||||
}
|
|
||||||
|
|
||||||
$mdb -> update( 'pp_articles_files', [ 'src' => $new_file_name, 'article_id' => $id ], [ 'id' => $row['id'] ] );
|
|
||||||
}
|
|
||||||
|
|
||||||
$created = false;
|
|
||||||
|
|
||||||
/* zdjęcia */
|
|
||||||
$results = $mdb -> select( 'pp_articles_images', '*', [ 'article_id' => null ] );
|
|
||||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
|
||||||
{
|
|
||||||
$dir = '/upload/article_images/article_' . $id;
|
|
||||||
|
|
||||||
$new_file_name = str_replace( '/upload/article_images/tmp', $dir, $row['src'] );
|
|
||||||
|
|
||||||
if ( file_exists( '../' . $new_file_name ) )
|
|
||||||
{
|
|
||||||
$ext = strrpos( $new_file_name, '.' );
|
|
||||||
$fileName_a = substr( $new_file_name, 0, $ext );
|
|
||||||
$fileName_b = substr( $new_file_name, $ext );
|
|
||||||
|
|
||||||
$count = 1;
|
|
||||||
|
|
||||||
while ( file_exists( '../' . $fileName_a . '_' . $count . $fileName_b ) )
|
|
||||||
$count++;
|
|
||||||
|
|
||||||
$new_file_name = $fileName_a . '_' . $count . $fileName_b;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( file_exists( '..' . $row['src'] ) )
|
|
||||||
{
|
|
||||||
if ( !is_dir( '../' . $dir ) and $created !== true )
|
|
||||||
{
|
|
||||||
if ( mkdir( '../' . $dir, 0755, true ) )
|
|
||||||
$created = true;
|
|
||||||
}
|
|
||||||
rename( '..' . $row['src'], '..' . $new_file_name );
|
|
||||||
}
|
|
||||||
|
|
||||||
$mdb -> update( 'pp_articles_images', [ 'src' => $new_file_name, 'article_id' => (int)$id ], [ 'id' => $row['id'] ] );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* tagi */
|
|
||||||
$tags = explode( ',', $tags );
|
|
||||||
if ( is_array( $tags ) ) foreach ( $tags as $tag )
|
|
||||||
{
|
|
||||||
if ( trim( $tag ) != '' )
|
|
||||||
{
|
|
||||||
$tag_id = $mdb -> get( 'pp_tags', 'id', [ 'name' => $tag ] );
|
|
||||||
if ( !$tag_id )
|
|
||||||
{
|
|
||||||
$mdb -> insert( 'pp_tags', [ 'name' => $tag ] );
|
|
||||||
$tag_id = $mdb -> id();
|
|
||||||
}
|
|
||||||
|
|
||||||
$mdb -> insert( 'pp_articles_tags', [ 'article_id' => (int)$id, 'tag_id' => (int)$tag_id ] );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
\S::htacces();
|
|
||||||
\S::delete_cache();
|
|
||||||
|
|
||||||
return $id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$mdb -> update( 'pp_articles', [
|
|
||||||
'show_title' => $show_title == 'on' ? 1 : 0,
|
|
||||||
'show_table_of_contents' => $show_table_of_contents == 'on' ? 1 : 0,
|
|
||||||
'show_date_add' => $show_date_add == 'on' ? 1 : 0,
|
|
||||||
'date_add' => $date_add,
|
|
||||||
'show_date_modify' => $show_date_modify == 'on' ? 1 : 0,
|
|
||||||
'date_modify' => $date_modify ? $date_modify : date( 'Y-m-d H:i:s' ),
|
|
||||||
'modify_by' => $user['id'],
|
|
||||||
'layout_id' => $layout_id ? (int)$layout_id : null,
|
|
||||||
'status' => $status == 'on' ? 1 : 0,
|
|
||||||
'repeat_entry' => $repeat_entry == 'on' ? 1 : 0,
|
|
||||||
'social_icons' => $social_icons == 'on' ? 1 : 0,
|
|
||||||
'date_start' => $event_date[0] ? $event_date[0] : null,
|
|
||||||
'date_end' => $event_date[1] ? $event_date[1] : null,
|
|
||||||
'priority' => $priority == 'on' ? 1 : 0,
|
|
||||||
'password' => $password ? $password : null,
|
|
||||||
'pixieset' => $pixieset,
|
|
||||||
'id_author' => $id_author ? $id_author : null
|
|
||||||
], [
|
|
||||||
'id' => (int)$article_id
|
|
||||||
] );
|
|
||||||
|
|
||||||
if ( $date_add )
|
|
||||||
$mdb -> update( 'pp_articles', [ 'date_add' => $date_add ], [ 'id' => (int)$article_id ] );
|
|
||||||
|
|
||||||
$i = 0;
|
|
||||||
|
|
||||||
/* tłumaczenia */
|
|
||||||
$mdb -> delete( 'pp_articles_langs', [ 'article_id' => (int)$article_id ] );
|
|
||||||
|
|
||||||
$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_articles_langs', [
|
|
||||||
'article_id' => (int)$article_id,
|
|
||||||
'lang_id' => $row['id'],
|
|
||||||
'title' => $title[ $i ] != '' ? $title[ $i ] : null,
|
|
||||||
'main_image' => $main_image[$i] != '' ? $main_image[$i] : null,
|
|
||||||
'entry' => $entry[ $i ] != '' ? $entry[ $i ] : null,
|
|
||||||
'text' => $text[ $i ] != '' ? $text[ $i ] : null,
|
|
||||||
'table_of_contents' => $table_of_contents[$i] != '' ? $table_of_contents[$i] : null,
|
|
||||||
'meta_title' => $meta_title[ $i ] != '' ? $meta_title[ $i ] : null,
|
|
||||||
'meta_description' => $meta_description[ $i ] != '' ? $meta_description[ $i ] : null,
|
|
||||||
'meta_keywords' => $meta_keywords[ $i ] != '' ? $meta_keywords[ $i ] : null,
|
|
||||||
'seo_link' => \S::seo( $seo_link[ $i ] ) != '' ? \S::seo( $seo_link[ $i ] ) : null,
|
|
||||||
'noindex' => $noindex[ $i ],
|
|
||||||
'copy_from' => $copy_from[ $i ] != '' ? $copy_from[ $i ] : null,
|
|
||||||
'block_direct_access' => $block_direct_access[ $i ]
|
|
||||||
] );
|
|
||||||
$i++;
|
|
||||||
}
|
|
||||||
else if ( is_array( $results ) and count( $results ) == 1 ) foreach ( $results as $row )
|
|
||||||
{
|
|
||||||
$mdb -> insert( 'pp_articles_langs', [
|
|
||||||
'article_id' => (int)$article_id,
|
|
||||||
'lang_id' => $row['id'],
|
|
||||||
'title' => $title != '' ? $title : null,
|
|
||||||
'main_image' => $main_image != '' ? $main_image : null,
|
|
||||||
'entry' => $entry != '' ? $entry : null,
|
|
||||||
'text' => $text != '' ? $text : null,
|
|
||||||
'table_of_contents' => $table_of_contents != '' ? $table_of_contents : null,
|
|
||||||
'meta_title' => $meta_title != '' ? $meta_title : null,
|
|
||||||
'meta_description' => $meta_description != '' ? $meta_description : null,
|
|
||||||
'meta_keywords' => $meta_keywords != '' ? $meta_keywords : null,
|
|
||||||
'seo_link' => \S::seo( $seo_link ) != '' ? \S::seo( $seo_link ) : null,
|
|
||||||
'noindex' => $noindex,
|
|
||||||
'copy_from' => $copy_from != '' ? $copy_from : null,
|
|
||||||
'block_direct_access' => $block_direct_access
|
|
||||||
] );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* dodatkowe parametry */
|
|
||||||
$mdb -> delete( 'pp_articles_additional_values', [ 'article_id' => (int)$article_id ] );
|
|
||||||
|
|
||||||
/* parametry bez wersji językowych */
|
|
||||||
$results = $mdb -> select( 'pp_articles_additional_params', '*', [ 'AND' => [ 'status' => 1, 'language' => 0 ] ] );
|
|
||||||
if ( is_array( $results ) and !empty( $results ) ) foreach ( $results as $row )
|
|
||||||
{
|
|
||||||
$mdb -> insert( 'pp_articles_additional_values', [
|
|
||||||
'param_id' => $row['id'],
|
|
||||||
'value' => $params[ 'ap_' . $row['name'] ],
|
|
||||||
'article_id' => (int)$article_id,
|
|
||||||
'language_id' => null
|
|
||||||
] );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* parametry z wersjami językowymi */
|
|
||||||
$results = $mdb -> select( 'pp_articles_additional_params', '*', [ 'AND' => [ 'status' => 1, 'language' => 1 ] ] );
|
|
||||||
if ( is_array( $results ) and !empty( $results ) ) foreach ( $results as $row )
|
|
||||||
{
|
|
||||||
$results2 = $mdb -> select( 'pp_langs', [ 'id' ], [ 'status' => 1, 'ORDER' => [ 'o' => 'ASC' ] ] );
|
|
||||||
if ( is_array( $results2 ) ) foreach ( $results2 as $row2 )
|
|
||||||
{
|
|
||||||
$mdb -> insert( 'pp_articles_additional_values', [
|
|
||||||
'param_id' => $row['id'],
|
|
||||||
'value' => $params[ 'ap_' . $row['name'] . '_' . $row2['id'] ],
|
|
||||||
'article_id' => (int)$article_id,
|
|
||||||
'language_id' => $row2['id']
|
|
||||||
] );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* strony */
|
|
||||||
$not_in = [ 0 ];
|
|
||||||
|
|
||||||
if ( is_array( $pages ) ) foreach ( $pages as $page )
|
|
||||||
$not_in[] = $page;
|
|
||||||
else if ( $pages )
|
|
||||||
$not_in[] = $pages;
|
|
||||||
|
|
||||||
$mdb -> delete( 'pp_articles_pages', [ 'AND' => [ 'article_id' => (int)$article_id, 'page_id[!]' => $not_in ] ] );
|
|
||||||
|
|
||||||
$pages_tmp = $mdb -> select( 'pp_articles_pages', 'page_id', [ 'article_id' => (int)$article_id ] );
|
|
||||||
|
|
||||||
if ( !is_array( $pages ) )
|
|
||||||
$pages = [ $pages ];
|
|
||||||
|
|
||||||
$pages = array_diff( $pages, $pages_tmp );
|
|
||||||
|
|
||||||
if ( is_array( $pages ) ) foreach ( $pages as $page )
|
|
||||||
{
|
|
||||||
$order = self::max_order() + 1;
|
|
||||||
|
|
||||||
$mdb -> insert( 'pp_articles_pages', [
|
|
||||||
'article_id' => (int)$article_id,
|
|
||||||
'page_id' => (int)$page,
|
|
||||||
'o' => (int)$order
|
|
||||||
] );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* pliki */
|
|
||||||
$results = $mdb -> select( 'pp_articles_files', '*', [ 'article_id' => null ] );
|
|
||||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
|
||||||
{
|
|
||||||
$dir = '/upload/article_files/article_' . $article_id;
|
|
||||||
|
|
||||||
$new_file_name = str_replace( '/upload/article_files/tmp', $dir, $row['src'] );
|
|
||||||
|
|
||||||
if ( file_exists( '..' . $row['src'] ) )
|
|
||||||
{
|
|
||||||
if ( !is_dir( '../' . $dir ) and $created !== true )
|
|
||||||
{
|
|
||||||
if ( mkdir( '../' . $dir, 0755, true ) )
|
|
||||||
$created = true;
|
|
||||||
}
|
|
||||||
rename( '..' . $row['src'], '..' . $new_file_name );
|
|
||||||
}
|
|
||||||
|
|
||||||
$mdb -> update( 'pp_articles_files', [ 'src' => $new_file_name, 'article_id' => (int)$article_id ], [ 'id' => $row['id'] ] );
|
|
||||||
}
|
|
||||||
|
|
||||||
$created = false;
|
|
||||||
|
|
||||||
/* zdjęcia */
|
|
||||||
$results = $mdb -> select( 'pp_articles_images', '*', [ 'article_id' => null ] );
|
|
||||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
|
||||||
{
|
|
||||||
$dir = '/upload/article_images/article_' . $article_id;
|
|
||||||
|
|
||||||
$new_file_name = str_replace( '/upload/article_images/tmp', $dir, $row['src'] );
|
|
||||||
|
|
||||||
if ( file_exists( '../' . $new_file_name ) )
|
|
||||||
{
|
|
||||||
$ext = strrpos( $new_file_name, '.' );
|
|
||||||
$fileName_a = substr( $new_file_name, 0, $ext );
|
|
||||||
$fileName_b = substr( $new_file_name, $ext );
|
|
||||||
|
|
||||||
$count = 1;
|
|
||||||
|
|
||||||
while ( file_exists( '../' . $fileName_a . '_' . $count . $fileName_b ) )
|
|
||||||
$count++;
|
|
||||||
|
|
||||||
$new_file_name = $fileName_a . '_' . $count . $fileName_b;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( file_exists( '..' . $row['src'] ) )
|
|
||||||
{
|
|
||||||
if ( !is_dir( '../' . $dir ) and $created !== true )
|
|
||||||
{
|
|
||||||
if ( mkdir( '../' . $dir, 0755, true ) )
|
|
||||||
$created = true;
|
|
||||||
}
|
|
||||||
rename( '..' . $row['src'], '..' . $new_file_name );
|
|
||||||
}
|
|
||||||
|
|
||||||
$mdb -> update( 'pp_articles_images', [ 'src' => $new_file_name, 'article_id' => (int)$article_id ], [ 'id' => $row['id'] ] );
|
|
||||||
}
|
|
||||||
|
|
||||||
$results = $mdb -> select( 'pp_articles_images', '*', [ 'AND' => [ 'article_id' => (int)$article_id, 'to_delete' => 1 ] ] );
|
|
||||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
|
||||||
{
|
|
||||||
if ( file_exists( '../' . $row['src'] ) )
|
|
||||||
unlink( '../' . $row['src'] );
|
|
||||||
}
|
|
||||||
|
|
||||||
$mdb -> delete( 'pp_articles_images', [ 'AND' => [ 'article_id' => (int)$article_id, 'to_delete' => 1 ] ] );
|
|
||||||
|
|
||||||
$results = $mdb -> select( 'pp_articles_files', '*', [ 'AND' => [ 'article_id' => (int)$article_id, 'to_delete' => 1 ] ] );
|
|
||||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
|
||||||
{
|
|
||||||
if ( file_exists( '../' . $row['src'] ) )
|
|
||||||
unlink( '../' . $row['src'] );
|
|
||||||
}
|
|
||||||
|
|
||||||
$mdb -> delete( 'pp_articles_files', [ 'AND' => [ 'article_id' => (int)$article_id, 'to_delete' => 1 ] ] );
|
|
||||||
|
|
||||||
/* tagi */
|
|
||||||
$mdb -> delete( 'pp_articles_tags', [ 'article_id' => (int)$article_id ] );
|
|
||||||
|
|
||||||
$tags = explode( ',', $tags );
|
|
||||||
if ( is_array( $tags ) ) foreach ( $tags as $tag )
|
|
||||||
{
|
|
||||||
if ( trim( $tag ) != '' )
|
|
||||||
{
|
|
||||||
$tag_id = $mdb -> get( 'pp_tags', 'id', [ 'name' => $tag ] );
|
|
||||||
if ( !$tag_id )
|
|
||||||
{
|
|
||||||
$mdb -> insert( 'pp_tags', [ 'name' => $tag ] );
|
|
||||||
$tag_id = $mdb -> id();
|
|
||||||
}
|
|
||||||
|
|
||||||
$mdb -> insert( 'pp_articles_tags', [ 'article_id' => (int)$article_id, 'tag_id' => (int)$tag_id ] );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
\S::htacces();
|
|
||||||
\S::delete_cache();
|
|
||||||
return $article_id;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function delete_nonassigned_files()
|
public static function delete_nonassigned_files()
|
||||||
{
|
{
|
||||||
global $mdb;
|
self::repo()->deleteNonassignedFiles();
|
||||||
|
|
||||||
$results = $mdb -> select( 'pp_articles_files', '*', [ 'article_id' => null ] );
|
|
||||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
|
||||||
{
|
|
||||||
if ( file_exists( '../' . $row['src'] ) )
|
|
||||||
unlink( '../' . $row['src'] );
|
|
||||||
}
|
|
||||||
|
|
||||||
$mdb -> delete( 'pp_articles_files', [ 'article_id' => null ] );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function delete_nonassigned_images()
|
public static function delete_nonassigned_images()
|
||||||
{
|
{
|
||||||
global $mdb;
|
self::repo()->deleteNonassignedImages();
|
||||||
|
|
||||||
$results = $mdb -> select( 'pp_articles_images', '*', [ 'article_id' => null ] );
|
|
||||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
|
||||||
{
|
|
||||||
if ( file_exists( '../' . $row['src'] ) )
|
|
||||||
unlink( '../' . $row['src'] );
|
|
||||||
}
|
|
||||||
|
|
||||||
$mdb -> delete( 'pp_articles_images', [ 'article_id' => null ] );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<?
|
<?php
|
||||||
namespace admin\factory;
|
namespace admin\factory;
|
||||||
class Authors
|
class Authors
|
||||||
{
|
{
|
||||||
@@ -6,112 +6,31 @@ class Authors
|
|||||||
static public function get_simple_list()
|
static public function get_simple_list()
|
||||||
{
|
{
|
||||||
global $mdb;
|
global $mdb;
|
||||||
return $mdb -> select( 'pp_authors', '*', [ 'ORDER' => [ 'author' => 'ASC' ] ] );
|
$repo = new \Domain\Authors\AuthorsRepository($mdb);
|
||||||
|
return $repo->simpleList();
|
||||||
}
|
}
|
||||||
|
|
||||||
// usunięcie autora
|
// usunięcie autora
|
||||||
static public function delete_author( $id_author )
|
static public function delete_author( $id_author )
|
||||||
{
|
{
|
||||||
global $mdb;
|
global $mdb;
|
||||||
|
$repo = new \Domain\Authors\AuthorsRepository($mdb);
|
||||||
$result = $mdb -> delete( 'pp_authors', [ 'id' => (int)$id_author ] );
|
return $repo->authorDelete($id_author);
|
||||||
\S::delete_cache();
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// zapis autora
|
// zapis autora
|
||||||
static public function save_author( $id_author, $author, $image, $description )
|
static public function save_author( $id_author, $author, $image, $description )
|
||||||
{
|
{
|
||||||
global $mdb;
|
global $mdb;
|
||||||
|
$repo = new \Domain\Authors\AuthorsRepository($mdb);
|
||||||
if ( !$id_author )
|
return $repo->authorSave($id_author, $author, $image, $description);
|
||||||
{
|
|
||||||
$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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// szczególy autora
|
// szczególy autora
|
||||||
static public function get_single_author( $id_author )
|
static public function get_single_author( $id_author )
|
||||||
{
|
{
|
||||||
global $mdb;
|
global $mdb;
|
||||||
|
$repo = new \Domain\Authors\AuthorsRepository($mdb);
|
||||||
$author = $mdb -> get( 'pp_authors', '*', [ 'id' => (int)$id_author ] );
|
return $repo->authorDetails($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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,123 +7,21 @@ class Banners
|
|||||||
public static function banner_delete( $banner_id )
|
public static function banner_delete( $banner_id )
|
||||||
{
|
{
|
||||||
global $mdb;
|
global $mdb;
|
||||||
|
$repo = new \Domain\Banners\BannersRepository($mdb);
|
||||||
$result = $mdb -> delete( 'pp_banners', [ 'id' => (int) $banner_id ] );
|
return $repo->bannerDelete($banner_id);
|
||||||
\S::delete_cache();
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function banner_save( $banner_id, $name, $status, $date_start, $date_end, $home_page, $src, $url, $html, $text )
|
public static function banner_save( $banner_id, $name, $status, $date_start, $date_end, $home_page, $src, $url, $html, $text )
|
||||||
{
|
{
|
||||||
global $mdb;
|
global $mdb;
|
||||||
|
$repo = new \Domain\Banners\BannersRepository($mdb);
|
||||||
if ( !$banner_id )
|
return $repo->bannerSave($banner_id, $name, $status, $date_start, $date_end, $home_page, $src, $url, $html, $text);
|
||||||
{
|
|
||||||
$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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function banner_details( $id_banner )
|
public static function banner_details( $id_banner )
|
||||||
{
|
{
|
||||||
global $mdb;
|
global $mdb;
|
||||||
|
$repo = new \Domain\Banners\BannersRepository($mdb);
|
||||||
$banner = $mdb -> get( 'pp_banners', '*', [ 'id' => (int)$id_banner ] );
|
return $repo->bannerDetails($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;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
?>
|
|
||||||
@@ -3,139 +3,36 @@ namespace admin\factory;
|
|||||||
|
|
||||||
class Layouts
|
class Layouts
|
||||||
{
|
{
|
||||||
public static function layout_delete( $layout_id )
|
private static function repo(): \Domain\Layouts\LayoutsRepository
|
||||||
{
|
{
|
||||||
global $mdb;
|
global $mdb;
|
||||||
if ( $mdb -> count( 'pp_layouts' ) > 1 )
|
return new \Domain\Layouts\LayoutsRepository( $mdb );
|
||||||
return $mdb -> delete( 'pp_layouts', [ 'id' => (int)$layout_id ] );
|
}
|
||||||
return false;
|
|
||||||
|
public static function layout_delete( $layout_id )
|
||||||
|
{
|
||||||
|
return self::repo()->layoutDelete( $layout_id );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function layout_details( $layout_id )
|
public static function layout_details( $layout_id )
|
||||||
{
|
{
|
||||||
global $mdb;
|
return self::repo()->layoutDetails( $layout_id );
|
||||||
$layout = $mdb -> get( 'pp_layouts', '*', [ 'id' => (int)$layout_id ] );
|
|
||||||
|
|
||||||
$layout['pages'] = $mdb -> select( 'pp_layouts_pages', 'page_id', [ 'layout_id' => (int)$layout_id ] );
|
|
||||||
|
|
||||||
return $layout;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function layout_save( $layout_id, $name, $status, $pages, $html, $css, $js, $m_html, $m_css, $m_js )
|
public static function layout_save( $layout_id, $name, $status, $pages, $html, $css, $js, $m_html, $m_css, $m_js )
|
||||||
{
|
{
|
||||||
global $mdb;
|
return self::repo()->layoutSave( $layout_id, $name, $status, $pages, $html, $css, $js, $m_html, $m_css, $m_js );
|
||||||
if ( !$layout_id )
|
|
||||||
{
|
|
||||||
if ( $status == 'on' )
|
|
||||||
$mdb -> update( 'pp_layouts', [ 'status' => 0 ] );
|
|
||||||
|
|
||||||
$mdb -> insert( 'pp_layouts', [
|
|
||||||
'name' => $name,
|
|
||||||
'html' => $html,
|
|
||||||
'css' => $css,
|
|
||||||
'js' => $js,
|
|
||||||
'm_html' => $m_html,
|
|
||||||
'm_css' => $m_css,
|
|
||||||
'm_js' => $m_js,
|
|
||||||
'status' => $status == 'on' ? 1 : 0
|
|
||||||
] );
|
|
||||||
|
|
||||||
$id = $mdb -> id();
|
|
||||||
|
|
||||||
if ( $id )
|
|
||||||
{
|
|
||||||
if ( is_array( $pages ) ) foreach ( $pages as $page )
|
|
||||||
{
|
|
||||||
$mdb -> delete( 'pp_layouts_pages', [ 'page_id' => (int)$page ] );
|
|
||||||
|
|
||||||
$mdb -> insert( 'pp_layouts_pages', [
|
|
||||||
'layout_id' => (int)$id,
|
|
||||||
'page_id' => (int)$page
|
|
||||||
] );
|
|
||||||
}
|
|
||||||
else if ( $pages )
|
|
||||||
{
|
|
||||||
$mdb -> delete( 'pp_layouts_pages', [ 'page_id' => (int)$pages ] );
|
|
||||||
|
|
||||||
$mdb -> insert( 'pp_layouts_pages', [
|
|
||||||
'layout_id' => (int)$id,
|
|
||||||
'page_id' => (int)$pages
|
|
||||||
] );
|
|
||||||
}
|
|
||||||
|
|
||||||
\S::delete_cache();
|
|
||||||
|
|
||||||
return $id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ( $status == 'on' )
|
|
||||||
$mdb -> update( 'pp_layouts', [ 'status' => 0 ] );
|
|
||||||
|
|
||||||
$mdb -> update( 'pp_layouts', [
|
|
||||||
'name' => $name,
|
|
||||||
'html' => $html,
|
|
||||||
'css' => $css,
|
|
||||||
'js' => $js,
|
|
||||||
'm_html' => $m_html,
|
|
||||||
'm_css' => $m_css,
|
|
||||||
'm_js' => $m_js,
|
|
||||||
'status' => $status == 'on' ? 1 : 0
|
|
||||||
], [
|
|
||||||
'id' => $layout_id
|
|
||||||
] );
|
|
||||||
|
|
||||||
$mdb -> delete( 'pp_layouts_pages', [ 'layout_id' => (int)$layout_id ] );
|
|
||||||
|
|
||||||
if ( is_array( $pages ) ) foreach ( $pages as $page )
|
|
||||||
{
|
|
||||||
$mdb -> delete( 'pp_layouts_pages', [ 'page_id' => (int)$page ] );
|
|
||||||
|
|
||||||
$mdb -> insert( 'pp_layouts_pages', [
|
|
||||||
'layout_id' => (int)$layout_id,
|
|
||||||
'page_id' => (int)$page
|
|
||||||
] );
|
|
||||||
}
|
|
||||||
else if ( $pages )
|
|
||||||
{
|
|
||||||
$mdb -> delete( 'pp_layouts_pages', [ 'page_id' => (int)$pages ] );
|
|
||||||
|
|
||||||
$mdb -> insert( 'pp_layouts_pages', [
|
|
||||||
'layout_id' => (int)$layout_id,
|
|
||||||
'page_id' => (int)$pages
|
|
||||||
] );
|
|
||||||
}
|
|
||||||
|
|
||||||
\S::delete_cache();
|
|
||||||
|
|
||||||
return $layout_id;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function menus_list()
|
public static function menus_list()
|
||||||
{
|
{
|
||||||
global $mdb;
|
return self::repo()->menusList();
|
||||||
$results = $mdb -> select( 'pp_menus', 'id', [ 'ORDER' => [ 'name' => 'ASC' ] ] );
|
|
||||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
|
||||||
{
|
|
||||||
$menu = \admin\factory\Pages::menu_details( $row );
|
|
||||||
$menu['pages'] = \admin\factory\Pages::menu_pages( $row );
|
|
||||||
|
|
||||||
$menus[] = $menu;
|
|
||||||
}
|
|
||||||
return $menus;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function layouts_list()
|
public static function layouts_list()
|
||||||
{
|
{
|
||||||
global $mdb;
|
return self::repo()->layoutsList();
|
||||||
return $mdb -> select( 'pp_layouts', '*', [ 'ORDER' => [ 'name' => 'ASC' ] ] );
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -6,100 +6,49 @@ class Newsletter
|
|||||||
public static function emails_import( $emails )
|
public static function emails_import( $emails )
|
||||||
{
|
{
|
||||||
global $mdb;
|
global $mdb;
|
||||||
|
$repo = new \Domain\Newsletter\NewsletterRepository($mdb);
|
||||||
$emails = explode( PHP_EOL, $emails );
|
return $repo->emailsImport($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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function is_admin_template( $template_id )
|
public static function is_admin_template( $template_id )
|
||||||
{
|
{
|
||||||
global $mdb;
|
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 )
|
public static function newsletter_template_delete( $template_id )
|
||||||
{
|
{
|
||||||
global $mdb;
|
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 )
|
public static function send( $dates, $template, $only_once )
|
||||||
{
|
{
|
||||||
global $mdb;
|
global $mdb;
|
||||||
|
$repo = new \Domain\Newsletter\NewsletterRepository($mdb);
|
||||||
$results = $mdb -> select( 'pp_newsletter', 'email', [ 'status' => 1 ] );
|
return $repo->send($dates, $template, $only_once);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function email_template_detalis ($id_template)
|
public static function email_template_detalis ($id_template)
|
||||||
{
|
{
|
||||||
global $mdb;
|
global $mdb;
|
||||||
|
$repo = new \Domain\Newsletter\NewsletterRepository($mdb);
|
||||||
$result = $mdb -> get ('pp_newsletter_templates', '*', [ 'id' => (int)$id_template ] );
|
return $repo->templateDetails($id_template);
|
||||||
return $result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function template_save($id, $name, $text)
|
public static function template_save($id, $name, $text)
|
||||||
{
|
{
|
||||||
global $mdb;
|
global $mdb;
|
||||||
if ( !$id )
|
$repo = new \Domain\Newsletter\NewsletterRepository($mdb);
|
||||||
{
|
return $repo->templateSave($id, $name, $text);
|
||||||
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
|
|
||||||
|
|
||||||
], [
|
|
||||||
'id' => (int)$id
|
|
||||||
] );
|
|
||||||
|
|
||||||
\S::delete_cache();
|
|
||||||
return $id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function templates_list()
|
public static function templates_list()
|
||||||
{
|
{
|
||||||
global $mdb;
|
global $mdb;
|
||||||
return $mdb -> select( 'pp_newsletter_templates', '*', [ 'is_admin' => 0, 'ORDER' => [ 'name' => 'ASC' ] ] );
|
$repo = new \Domain\Newsletter\NewsletterRepository($mdb);
|
||||||
|
return $repo->templatesList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,120 +1,53 @@
|
|||||||
<?
|
<?php
|
||||||
|
|
||||||
namespace admin\factory;
|
namespace admin\factory;
|
||||||
|
|
||||||
class Pages
|
class Pages
|
||||||
{
|
{
|
||||||
|
|
||||||
public static $_page_types = [ 0 => 'pełne artykuły', 1 => 'wprowadzenia', 2 => 'miniaturki', 3 => 'link', 4 => 'kontakt' ];
|
public static $_page_types = [ 0 => 'pełne artykuły', 1 => 'wprowadzenia', 2 => 'miniaturki', 3 => 'link', 4 => 'kontakt' ];
|
||||||
public static $_sort_types = [
|
public static $_sort_types = [
|
||||||
0 => 'data dodania - najstarsze na początku',
|
0 => 'data dodania - najstarsze na początku',
|
||||||
1 => 'data dodania - najnowsze na początku',
|
1 => 'data dodania - najnowsze na początku',
|
||||||
2 => 'data modyfikacji - rosnąco',
|
2 => 'data modyfikacji - rosnąco',
|
||||||
3 => 'data mofyfikacji - malejąco',
|
3 => 'data mofyfikacji - malejąco',
|
||||||
4 => 'ręczne',
|
4 => 'ręczne',
|
||||||
5 => 'alfabetycznie - A - Z',
|
5 => 'alfabetycznie - A - Z',
|
||||||
6 => 'alfabetycznie - Z - A'
|
6 => 'alfabetycznie - Z - A'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
private static function repo(): \Domain\Pages\PagesRepository
|
||||||
|
{
|
||||||
|
global $mdb;
|
||||||
|
return new \Domain\Pages\PagesRepository( $mdb );
|
||||||
|
}
|
||||||
|
|
||||||
public static function save_articles_order( $page_id, $articles )
|
public static function save_articles_order( $page_id, $articles )
|
||||||
{
|
{
|
||||||
global $mdb;
|
return self::repo()->saveArticlesOrder( $page_id, $articles );
|
||||||
if ( is_array( $articles ) )
|
|
||||||
{
|
|
||||||
$mdb -> update( 'pp_articles_pages', [ 'o' => 0 ],
|
|
||||||
[ 'page_id' => (int) $page_id ] );
|
|
||||||
|
|
||||||
for ( $i = 0; $i < count( $articles ); $i++ )
|
|
||||||
{
|
|
||||||
if ( $articles[$i]['item_id'] )
|
|
||||||
{
|
|
||||||
$x++;
|
|
||||||
$mdb -> update( 'pp_articles_pages', [ 'o' => $x ],
|
|
||||||
[ 'AND' => [ 'page_id' => (int) $page_id, 'article_id' => $articles[$i]['item_id'] ] ] );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function page_articles( $page_id )
|
public static function page_articles( $page_id )
|
||||||
{
|
{
|
||||||
global $mdb;
|
return self::repo()->pageArticles( $page_id );
|
||||||
$results = $mdb -> query( 'SELECT '
|
|
||||||
. 'article_id, o, status '
|
|
||||||
. 'FROM '
|
|
||||||
. 'pp_articles_pages AS ap '
|
|
||||||
. 'INNER JOIN pp_articles AS a ON a.id = ap.article_id '
|
|
||||||
. 'WHERE '
|
|
||||||
. 'page_id = ' . (int) $page_id . ' AND status != -1 '
|
|
||||||
. 'ORDER BY '
|
|
||||||
. 'o ASC' ) -> fetchAll();
|
|
||||||
if ( is_array( $results ) )
|
|
||||||
foreach ( $results as $row )
|
|
||||||
{
|
|
||||||
$row['title'] = \admin\factory\Articles::article_title( $row['article_id'] );
|
|
||||||
$articles[] = $row;
|
|
||||||
}
|
|
||||||
return $articles;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function menus_list()
|
public static function menus_list()
|
||||||
{
|
{
|
||||||
global $mdb;
|
return self::repo()->menusList();
|
||||||
return $mdb -> select( 'pp_menus', '*', [ 'ORDER' => [ 'name' => 'ASC' ] ] );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function save_pages_order( $menu_id, $pages )
|
public static function save_pages_order( $menu_id, $pages )
|
||||||
{
|
{
|
||||||
global $mdb;
|
return self::repo()->savePagesOrder( $menu_id, $pages );
|
||||||
if ( is_array( $pages ) )
|
|
||||||
{
|
|
||||||
$mdb -> update( 'pp_pages', [ 'o' => 0 ], [ 'menu_id' => (int) $menu_id ] );
|
|
||||||
|
|
||||||
for ( $i = 0; $i < count( $pages ); $i++ )
|
|
||||||
{
|
|
||||||
if ( $pages[$i]['item_id'] )
|
|
||||||
{
|
|
||||||
$pages[$i]['parent_id'] ? $parent_id = $pages[$i]['parent_id'] : $parent_id = 0;
|
|
||||||
|
|
||||||
if ( $pages[$i]['item_id'] && $pages[$i]['depth'] > 1 )
|
|
||||||
{
|
|
||||||
if ( $pages[$i]['depth'] == 2 )
|
|
||||||
$parent_id = null;
|
|
||||||
|
|
||||||
$x++;
|
|
||||||
|
|
||||||
$mdb -> update( 'pp_pages', [ 'o' => $x, 'parent_id' => $parent_id ],
|
|
||||||
[ 'id' => (int) $pages[$i]['item_id'] ] );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
\S::delete_cache();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function page_delete( $page_id )
|
public static function page_delete( $page_id )
|
||||||
{
|
{
|
||||||
global $mdb;
|
return self::repo()->pageDelete( $page_id );
|
||||||
if ( $mdb -> count( 'pp_pages', [ 'parent_id' => (int) $page_id ] ) )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if ( $mdb -> delete( 'pp_pages', [ 'id' => (int) $page_id ] ) )
|
|
||||||
{
|
|
||||||
\S::delete_cache();
|
|
||||||
\S::htacces();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function max_order()
|
public static function max_order()
|
||||||
{
|
{
|
||||||
global $mdb;
|
return self::repo()->maxOrder();
|
||||||
return $mdb -> max( 'pp_pages', 'o' );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function page_save(
|
public static function page_save(
|
||||||
@@ -122,388 +55,70 @@ class Pages
|
|||||||
$site_title, $block_direct_access, $cache, $canonical
|
$site_title, $block_direct_access, $cache, $canonical
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
global $mdb;
|
return self::repo()->pageSave(
|
||||||
|
$page_id, $title, $seo_link, $meta_title, $meta_description, $meta_keywords, $menu_id, $parent_id, $page_type, $sort_type, $layout_id, $articles_limit, $show_title, $status, $link, $noindex, $start,
|
||||||
if ( !$parent_id )
|
$site_title, $block_direct_access, $cache, $canonical
|
||||||
$parent_id = null;
|
);
|
||||||
|
|
||||||
if ( !$page_id )
|
|
||||||
{
|
|
||||||
$order = self::max_order() + 1;
|
|
||||||
|
|
||||||
$mdb -> insert( 'pp_pages', [
|
|
||||||
'menu_id' => (int) $menu_id,
|
|
||||||
'page_type' => $page_type,
|
|
||||||
'sort_type' => $sort_type,
|
|
||||||
'articles_limit' => $articles_limit,
|
|
||||||
'show_title' => $show_title == 'on' ? 1 : 0,
|
|
||||||
'status' => $status == 'on' ? 1 : 0,
|
|
||||||
'o' => (int) $order,
|
|
||||||
'parent_id' => $parent_id,
|
|
||||||
'start' => $start == 'on' ? 1 : 0,
|
|
||||||
'cache' => $cache == 'on' ? 1 : 0
|
|
||||||
] );
|
|
||||||
|
|
||||||
$id = $mdb -> id();
|
|
||||||
|
|
||||||
if ( $id )
|
|
||||||
{
|
|
||||||
if ( $start )
|
|
||||||
$mdb -> update( 'pp_pages', [ 'start' => 0 ], [ 'id[!]' => (int)$id ] );
|
|
||||||
|
|
||||||
if ( $layout_id )
|
|
||||||
$mdb -> insert( 'pp_layouts_pages', [ 'page_id' => (int) $id, 'layout_id' => (int)$layout_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_pages_langs', [
|
|
||||||
'page_id' => (int) $id,
|
|
||||||
'lang_id' => $row['id'],
|
|
||||||
'title' => $title[$i] != '' ? $title[$i] : null,
|
|
||||||
'meta_description' => $meta_description[$i] != '' ? $meta_description[$i] : null,
|
|
||||||
'meta_keywords' => $meta_keywords[$i] != '' ? $meta_keywords[$i] : null,
|
|
||||||
'meta_title' => $meta_title[$i] != '' ? $meta_title[$i] : null,
|
|
||||||
'seo_link' => \S::seo( $seo_link[$i] ) != '' ? \S::seo( $seo_link[$i] ) : null,
|
|
||||||
'noindex' => $noindex[$i],
|
|
||||||
'site_title' => $site_title[$i] != '' ? $site_title[$i] : null,
|
|
||||||
'link' => $link[$i] != '' ? $link[$i] : null,
|
|
||||||
'block_direct_access' => $block_direct_access[$i],
|
|
||||||
'canonical' => $canonical[$i] != '' ? $canonical[$i] : null
|
|
||||||
] );
|
|
||||||
$i++;
|
|
||||||
}
|
|
||||||
else if ( is_array( $results ) and count( $results ) == 1 )
|
|
||||||
{
|
|
||||||
foreach ( $results as $row )
|
|
||||||
{
|
|
||||||
$mdb -> insert( 'pp_pages_langs', [
|
|
||||||
'page_id' => (int) $id,
|
|
||||||
'lang_id' => $row['id'],
|
|
||||||
'title' => $title != '' ? $title : null,
|
|
||||||
'meta_description' => $meta_description != '' ? $meta_description : null,
|
|
||||||
'meta_keywords' => $meta_keywords != '' ? $meta_keywords : null,
|
|
||||||
'meta_title' => $meta_title != '' ? $meta_title : null,
|
|
||||||
'seo_link' => \S::seo( $seo_link ) != '' ? \S::seo( $seo_link ) : null,
|
|
||||||
'noindex' => $noindex,
|
|
||||||
'site_title' => $site_title != '' ? $site_title : null,
|
|
||||||
'link' => $link != '' ? $link : null,
|
|
||||||
'block_direct_access' => $block_direct_access,
|
|
||||||
'canonical' => $canonical != '' ? $canonical : null
|
|
||||||
] );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
\S::htacces();
|
|
||||||
\S::delete_cache();
|
|
||||||
|
|
||||||
return $id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$mdb -> update( 'pp_pages',
|
|
||||||
[
|
|
||||||
'menu_id' => (int) $menu_id,
|
|
||||||
'page_type' => $page_type,
|
|
||||||
'sort_type' => $sort_type,
|
|
||||||
'articles_limit' => $articles_limit,
|
|
||||||
'show_title' => $show_title == 'on' ? 1 : 0,
|
|
||||||
'status' => $status == 'on' ? 1 : 0,
|
|
||||||
'parent_id' => $parent_id,
|
|
||||||
'start' => $start == 'on' ? 1 : 0,
|
|
||||||
'cache' => $cache == 'on' ? 1 : 0
|
|
||||||
], [
|
|
||||||
'id' => (int) $page_id
|
|
||||||
] );
|
|
||||||
|
|
||||||
if ( $layout_id )
|
|
||||||
{
|
|
||||||
$mdb -> delete( 'pp_layouts_pages', [ 'page_id' => (int) $page_id ] );
|
|
||||||
$mdb -> insert( 'pp_layouts_pages',
|
|
||||||
[ 'layout_id' => (int) $layout_id, 'page_id' => (int) $page_id ] );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $start )
|
|
||||||
$mdb -> update( 'pp_pages', [ 'start' => 0 ],
|
|
||||||
[ 'id[!]' => (int) $page_id ] );
|
|
||||||
|
|
||||||
$i = 0;
|
|
||||||
|
|
||||||
$mdb -> delete( 'pp_pages_langs', [ 'page_id' => (int) $page_id ] );
|
|
||||||
|
|
||||||
$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_pages_langs',
|
|
||||||
[
|
|
||||||
'page_id' => (int) $page_id,
|
|
||||||
'lang_id' => $row['id'],
|
|
||||||
'title' => $title[$i] != '' ? $title[$i] : null,
|
|
||||||
'meta_description' => $meta_description[$i] != '' ? $meta_description[$i] : null,
|
|
||||||
'meta_keywords' => $meta_keywords[$i] != '' ? $meta_keywords[$i] : null,
|
|
||||||
'meta_title' => $meta_title[$i] != '' ? $meta_title[$i] : null,
|
|
||||||
'seo_link' => \S::seo( $seo_link[$i] ) != '' ? \S::seo( $seo_link[$i] ) : null,
|
|
||||||
'noindex' => $noindex[$i],
|
|
||||||
'site_title' => $site_title[$i] != '' ? $site_title[$i] : null,
|
|
||||||
'link' => $link[$i] != '' ? $link[$i] : null,
|
|
||||||
'block_direct_access' => $block_direct_access[$i],
|
|
||||||
'canonical' => $canonical[$i] != '' ? $canonical[$i] : null
|
|
||||||
] );
|
|
||||||
|
|
||||||
$i++;
|
|
||||||
}
|
|
||||||
else if ( is_array( $results ) and count( $results ) == 1 )
|
|
||||||
foreach ( $results as $row )
|
|
||||||
{
|
|
||||||
$mdb -> insert( 'pp_pages_langs',
|
|
||||||
[
|
|
||||||
'page_id' => (int) $page_id,
|
|
||||||
'lang_id' => $row['id'],
|
|
||||||
'title' => $title != '' ? $title : null,
|
|
||||||
'meta_description' => $meta_description != '' ? $meta_description : null,
|
|
||||||
'meta_keywords' => $meta_keywords != '' ? $meta_keywords : null,
|
|
||||||
'meta_title' => $meta_title != '' ? $meta_title : null,
|
|
||||||
'seo_link' => \S::seo( $seo_link ) != '' ? \S::seo( $seo_link ) : null,
|
|
||||||
'noindex' => $noindex,
|
|
||||||
'site_title' => $site_title != '' ? $site_title : null,
|
|
||||||
'link' => $link != '' ? $link : null,
|
|
||||||
'block_direct_access' => $block_direct_access,
|
|
||||||
'canonical' => $canonical != '' ? $canonical : null
|
|
||||||
] );
|
|
||||||
}
|
|
||||||
|
|
||||||
self::update_supages_menu_id( $page_id, $menu_id );
|
|
||||||
|
|
||||||
\S::htacces();
|
|
||||||
\S::delete_cache();
|
|
||||||
|
|
||||||
return $page_id;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function update_supages_menu_id( $parent_id, $menu_id )
|
public static function update_supages_menu_id( $parent_id, $menu_id )
|
||||||
{
|
{
|
||||||
global $mdb;
|
self::repo()->updateSubpagesMenuId( (int) $parent_id, (int) $menu_id );
|
||||||
|
|
||||||
$mdb -> update( 'pp_pages', [ 'menu_id' => (int) $menu_id ],
|
|
||||||
[ 'parent_id' => $parent_id ] );
|
|
||||||
|
|
||||||
$results = $mdb -> select( 'pp_pages', [ 'id' ], [ 'parent_id' => $parent_id ] );
|
|
||||||
if ( is_array( $results ) )
|
|
||||||
foreach ( $results as $row )
|
|
||||||
self::update_supages_menu_id( $row['id'], $menu_id );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function generate_seo_link( $title, $page_id, $article_id,
|
public static function generate_seo_link( $title, $page_id, $article_id, $lang, $pid )
|
||||||
$lang, $pid )
|
|
||||||
{
|
{
|
||||||
global $mdb;
|
return self::repo()->generateSeoLink( $title, $page_id, $article_id, $lang, $pid );
|
||||||
|
|
||||||
$seo_link = \S::seo( $title );
|
|
||||||
|
|
||||||
|
|
||||||
while ( !$seo_link_check )
|
|
||||||
{
|
|
||||||
if ( $mdb -> count( 'pp_pages_langs',
|
|
||||||
[ 'AND' => [ 'seo_link' => $seo_link, 'page_id[!]' => (int) $page_id ] ] ) )
|
|
||||||
$seo_link = $seo_link . '-' . ( ++$i );
|
|
||||||
else
|
|
||||||
$seo_link_check = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
$seo_link_check = false;
|
|
||||||
|
|
||||||
while ( !$seo_link_check )
|
|
||||||
{
|
|
||||||
if ( $mdb -> count( 'pp_articles_langs',
|
|
||||||
[ 'AND' => [ 'seo_link' => $seo_link, 'article_id[!]' => (int) $article_id ] ] ) )
|
|
||||||
$seo_link = $seo_link . '-' . ( ++$i );
|
|
||||||
else
|
|
||||||
$seo_link_check = true;
|
|
||||||
}
|
|
||||||
return $seo_link;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function google_url_preview( $page_id, $title, $lang, $pid, $id, $seo_link, $language_link = '' )
|
public static function google_url_preview( $page_id, $title, $lang, $pid, $id, $seo_link, $language_link = '' )
|
||||||
{
|
{
|
||||||
global $mdb;
|
return self::repo()->googleUrlPreview( $page_id, $title, $lang, $pid, $id, $seo_link, $language_link );
|
||||||
|
|
||||||
$prefix = $language_link;
|
|
||||||
$status = true;
|
|
||||||
$id_page = $page_id;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if ( $page_id )
|
|
||||||
{
|
|
||||||
$parent = \admin\factory\Pages::page_details( $page_id );
|
|
||||||
$parent_id = $parent['parent_id'];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
$parent_id = $pid;
|
|
||||||
|
|
||||||
if ( $parent_id )
|
|
||||||
{
|
|
||||||
$results = $mdb -> query( "SELECT title, seo_link, page_id FROM pp_pages_langs AS ppl, pp_langs AS pl WHERE lang_id = pl.id AND page_id = " . (int) $parent_id . " AND ppl.lang_id = '" . $lang . "' " ) -> fetchAll();
|
|
||||||
if ( $results[0]['seo_link'] )
|
|
||||||
$seo = $results[0]['seo_link'] . '/' . $seo;
|
|
||||||
else
|
|
||||||
$seo = 's-' . $results[0]['page_id'] . '-' . \S::seo( $results[0]['title'] ) . '/' . $seo;
|
|
||||||
$page_id = $results[0]['page_id'];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
$status = false;
|
|
||||||
}
|
|
||||||
while ( $status );
|
|
||||||
|
|
||||||
if ( $id )
|
|
||||||
{
|
|
||||||
if ( !$seo_link )
|
|
||||||
$seo = $seo . 's-' . $id . '-' . \S::seo( $title );
|
|
||||||
else
|
|
||||||
$seo = $seo . $seo_link;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ( !$seo_link )
|
|
||||||
$seo = $seo . 's-' . $id_page . '-' . \S::seo( $title );
|
|
||||||
else
|
|
||||||
$seo = $seo . $seo_link;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $prefix )
|
|
||||||
$seo = $prefix . $seo;
|
|
||||||
|
|
||||||
return $seo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function menu_delete( $menu_id )
|
public static function menu_delete( $menu_id )
|
||||||
{
|
{
|
||||||
global $mdb;
|
return self::repo()->menuDelete( $menu_id );
|
||||||
|
|
||||||
if ( $mdb -> count( 'pp_pages', [ 'menu_id' => (int) $menu_id ] ) )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return $mdb -> delete( 'pp_menus', [ 'id' => (int) $menu_id ] );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function menu_details( $menu_id )
|
public static function menu_details( $menu_id )
|
||||||
{
|
{
|
||||||
global $mdb;
|
return self::repo()->menuDetails( $menu_id );
|
||||||
return $mdb -> get( 'pp_menus', '*', [ 'id' => (int) $menu_id ] );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function menu_save( $menu_id, $name, $status )
|
public static function menu_save( $menu_id, $name, $status )
|
||||||
{
|
{
|
||||||
global $mdb;
|
return self::repo()->menuSave( $menu_id, $name, $status );
|
||||||
|
|
||||||
$status == 'on' ? $status = 1 : $status = 0;
|
|
||||||
|
|
||||||
if ( !$menu_id )
|
|
||||||
{
|
|
||||||
return $mdb -> insert( 'pp_menus',
|
|
||||||
[
|
|
||||||
'name' => $name,
|
|
||||||
'status' => $status
|
|
||||||
] );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$mdb -> update( 'pp_menus',
|
|
||||||
[
|
|
||||||
'name' => $name,
|
|
||||||
'status' => $status
|
|
||||||
], [
|
|
||||||
'id' => (int) $menu_id
|
|
||||||
] );
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function menu_lists()
|
public static function menu_lists()
|
||||||
{
|
{
|
||||||
global $mdb;
|
return self::repo()->menuLists();
|
||||||
return $mdb -> select( 'pp_menus', '*', [ 'ORDER' => [ 'id' => 'ASC' ] ] );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function page_details( $page_id )
|
public static function page_details( $page_id )
|
||||||
{
|
{
|
||||||
global $mdb;
|
return self::repo()->pageDetails( $page_id );
|
||||||
|
|
||||||
$page = $mdb -> get( 'pp_pages', '*', [ 'id' => (int) $page_id ] );
|
|
||||||
|
|
||||||
$results = $mdb -> select( 'pp_pages_langs', '*',
|
|
||||||
[ 'page_id' => (int) $page_id ] );
|
|
||||||
if ( is_array( $results ) )
|
|
||||||
foreach ( $results as $row )
|
|
||||||
$page['languages'][$row['lang_id']] = $row;
|
|
||||||
|
|
||||||
$page['layout_id'] = $mdb -> get( 'pp_layouts_pages', 'layout_id',
|
|
||||||
[ 'page_id' => (int) $page_id ] );
|
|
||||||
|
|
||||||
return $page;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function page_url( $page_id )
|
public static function page_url( $page_id )
|
||||||
{
|
{
|
||||||
global $mdb;
|
return self::repo()->pageUrl( $page_id );
|
||||||
|
|
||||||
$results = $mdb -> query( "SELECT seo_link, title lang_id FROM pp_pages_langs AS ppl, pp_langs AS pl WHERE lang_id = pl.id AND page_id = " . (int) $page_id . " AND seo_link != '' ORDER BY o ASC LIMIT 1" ) -> fetchAll();
|
|
||||||
|
|
||||||
if ( !$results[0]['seo_link'] )
|
|
||||||
{
|
|
||||||
$title = self::page_title( $article_id );
|
|
||||||
return 's-' . $page_id . '-' . \S::seo( $title );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return $results[0]['seo_link'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function page_title( $page_id )
|
public static function page_title( $page_id )
|
||||||
{
|
{
|
||||||
global $mdb;
|
return self::repo()->pageTitle( $page_id );
|
||||||
|
|
||||||
$result = $mdb -> select( 'pp_pages_langs',
|
|
||||||
[ '[><]pp_langs' => [ 'lang_id' => 'id' ] ], 'title',
|
|
||||||
[ 'AND' => [ 'page_id' => (int) $page_id, 'title[!]' => '' ], 'ORDER' => [ 'o' => 'ASC' ], 'LIMIT' => 1 ] );
|
|
||||||
return $result[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function page_languages( $page_id )
|
public static function page_languages( $page_id )
|
||||||
{
|
{
|
||||||
global $mdb;
|
return self::repo()->pageLanguages( $page_id );
|
||||||
return $mdb -> select( 'pp_pages_langs', '*',
|
|
||||||
[ 'AND' => [ 'page_id' => (int) $page_id, 'title[!]' => null ] ] );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function menu_pages( $menu_id, $parent_id = null )
|
public static function menu_pages( $menu_id, $parent_id = null )
|
||||||
{
|
{
|
||||||
global $mdb;
|
return self::repo()->menuPages( $menu_id, $parent_id );
|
||||||
|
|
||||||
$results = $mdb -> select( 'pp_pages',
|
|
||||||
[ 'id', 'menu_id', 'status', 'parent_id', 'start' ],
|
|
||||||
[ 'AND' => [ 'menu_id' => $menu_id, 'parent_id' => $parent_id ], 'ORDER' => [ 'o' => 'ASC' ] ] );
|
|
||||||
if ( is_array( $results ) )
|
|
||||||
foreach ( $results as $row )
|
|
||||||
{
|
|
||||||
$row['title'] = self::page_title( $row['id'] );
|
|
||||||
$row['languages'] = self::page_languages( $row['id'] );
|
|
||||||
$row['subpages'] = self::menu_pages( $menu_id, $row['id'] );
|
|
||||||
|
|
||||||
$pages[] = $row;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $pages;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
?>
|
||||||
?>
|
|
||||||
|
|||||||
@@ -7,115 +7,21 @@ class Scontainers
|
|||||||
public static function container_delete( $container_id )
|
public static function container_delete( $container_id )
|
||||||
{
|
{
|
||||||
global $mdb;
|
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 )
|
public static function container_save( $container_id, $title, $text, $status, $show_title, $src, $html )
|
||||||
{
|
{
|
||||||
global $mdb;
|
global $mdb;
|
||||||
|
$repo = new \Domain\Scontainers\ScontainersRepository($mdb);
|
||||||
if ( !$container_id )
|
return $repo->containerSave($container_id, $title, $text, $status, $show_title, $src, $html);
|
||||||
{
|
|
||||||
$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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function container_details( $container_id )
|
public static function container_details( $container_id )
|
||||||
{
|
{
|
||||||
global $mdb;
|
global $mdb;
|
||||||
|
$repo = new \Domain\Scontainers\ScontainersRepository($mdb);
|
||||||
$container = $mdb -> get( 'pp_scontainers', '*', [ 'id' => (int) $container_id ] );
|
return $repo->containerDetails($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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
32
autoload/autoloader.php
Normal file
32
autoload/autoloader.php
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Centralny autoloader — hybrydowy (PSR-4 + legacy class.*.php)
|
||||||
|
* Obsługuje namespace'y: Domain\, Shared\, Admin\, Frontend\, admin\, front\
|
||||||
|
*/
|
||||||
|
|
||||||
|
function __autoload_my_classes( $classname )
|
||||||
|
{
|
||||||
|
$base = __DIR__ . '/';
|
||||||
|
|
||||||
|
$q = explode( '\\', $classname );
|
||||||
|
$c = array_pop( $q );
|
||||||
|
|
||||||
|
// Savant3 — special case
|
||||||
|
if ( $c == 'Savant3' )
|
||||||
|
{
|
||||||
|
$f = $base . 'Savant3.php';
|
||||||
|
if ( file_exists( $f ) ) { require_once( $f ); return; }
|
||||||
|
}
|
||||||
|
|
||||||
|
$path = implode( '/', $q );
|
||||||
|
|
||||||
|
// 1. Legacy: class.ClassName.php
|
||||||
|
$f = $base . $path . '/class.' . $c . '.php';
|
||||||
|
if ( file_exists( $f ) ) { require_once( $f ); return; }
|
||||||
|
|
||||||
|
// 2. PSR-4: ClassName.php
|
||||||
|
$f = $base . $path . '/' . $c . '.php';
|
||||||
|
if ( file_exists( $f ) ) require_once( $f );
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_autoload_register( '__autoload_my_classes' );
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<?
|
<?php
|
||||||
namespace front\factory;
|
namespace front\factory;
|
||||||
class Authors
|
class Authors
|
||||||
{
|
{
|
||||||
@@ -6,17 +6,7 @@ class Authors
|
|||||||
static public function get_single_author( $id_author )
|
static public function get_single_author( $id_author )
|
||||||
{
|
{
|
||||||
global $mdb;
|
global $mdb;
|
||||||
|
$repo = new \Domain\Authors\AuthorsRepository($mdb);
|
||||||
if ( !$author = \Cache::fetch( "get_single_author:$id_author" ) )
|
return $repo->authorByLang($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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,58 +6,14 @@ class Banners
|
|||||||
public static function banners()
|
public static function banners()
|
||||||
{
|
{
|
||||||
global $mdb, $lang;
|
global $mdb, $lang;
|
||||||
|
$repo = new \Domain\Banners\BannersRepository($mdb);
|
||||||
if ( !$banners = \Cache::fetch( 'banners' ) )
|
return $repo->activeBanners($lang[0]);
|
||||||
{
|
|
||||||
$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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function main_banner()
|
public static function main_banner()
|
||||||
{
|
{
|
||||||
global $mdb, $lang;
|
global $mdb, $lang;
|
||||||
|
$repo = new \Domain\Banners\BannersRepository($mdb);
|
||||||
if ( !$banner = \Cache::fetch( "main_banner:" . $lang[0] ) )
|
return $repo->mainBanner($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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,113 +6,49 @@ class Newsletter
|
|||||||
public static function newsletter_unsubscribe( $hash )
|
public static function newsletter_unsubscribe( $hash )
|
||||||
{
|
{
|
||||||
global $mdb;
|
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 )
|
public static function newsletter_confirm( $hash )
|
||||||
{
|
{
|
||||||
global $mdb;
|
global $mdb;
|
||||||
if ( !$id = $mdb -> get( 'pp_newsletter', 'id', [ 'AND' => [ 'hash' => $hash, 'status' => 0 ] ] ) )
|
$repo = new \Domain\Newsletter\NewsletterRepository($mdb);
|
||||||
return false;
|
return $repo->confirm($hash);
|
||||||
else
|
|
||||||
$mdb -> update( 'pp_newsletter', [ 'status' => 1 ], [ 'id' => $id ] );
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function newsletter_send( $limit = 5 )
|
public static function newsletter_send( $limit = 5 )
|
||||||
{
|
{
|
||||||
global $mdb, $settings, $lang;
|
global $mdb, $settings, $lang;
|
||||||
|
$repo = new \Domain\Newsletter\NewsletterRepository($mdb);
|
||||||
$results = $mdb -> query( 'SELECT * FROM pp_newsletter_send WHERE mailed = 0 ORDER BY id ASC LIMIT ' . $limit ) -> fetchAll();
|
return $repo->newsletterSend($limit, $settings, $lang);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function get_hash( $email )
|
public static function get_hash( $email )
|
||||||
{
|
{
|
||||||
global $mdb;
|
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 )
|
public static function newsletter_signin( $email )
|
||||||
{
|
{
|
||||||
global $mdb, $lang, $settings;
|
global $mdb, $lang, $settings;
|
||||||
|
$repo = new \Domain\Newsletter\NewsletterRepository($mdb);
|
||||||
if ( !\S::email_check( $email ) )
|
return $repo->signin($email, $settings, $lang);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function get_template( $template_name )
|
public static function get_template( $template_name )
|
||||||
{
|
{
|
||||||
global $mdb;
|
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 )
|
public static function newsletter_signout( $email )
|
||||||
{
|
{
|
||||||
global $mdb;
|
global $mdb;
|
||||||
|
$repo = new \Domain\Newsletter\NewsletterRepository($mdb);
|
||||||
if ( $mdb -> get( 'pp_newsletter', 'id', [ 'email' => $email ] ) )
|
return $repo->signout($email);
|
||||||
return $mdb -> delete( 'pp_newsletter', [ 'email' => $email ] );
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,18 +6,7 @@ class Scontainers
|
|||||||
public static function scontainer_details( $scontainer_id )
|
public static function scontainer_details( $scontainer_id )
|
||||||
{
|
{
|
||||||
global $mdb, $lang;
|
global $mdb, $lang;
|
||||||
|
$repo = new \Domain\Scontainers\ScontainersRepository($mdb);
|
||||||
if ( !$scontainer = \Cache::fetch( "scontainer_details:$scontainer_id:" . $lang[0] ) )
|
return $repo->scontainerByLang($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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,14 @@
|
|||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^10.5"
|
"phpunit/phpunit": "^10.5"
|
||||||
},
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Domain\\": "autoload/Domain/",
|
||||||
|
"Shared\\": "autoload/Shared/",
|
||||||
|
"Admin\\": "autoload/Admin/",
|
||||||
|
"Frontend\\": "autoload/Frontend/"
|
||||||
|
}
|
||||||
|
},
|
||||||
"autoload-dev": {
|
"autoload-dev": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"Tests\\": "tests/"
|
"Tests\\": "tests/"
|
||||||
|
|||||||
BIN
composer.phar
Normal file
BIN
composer.phar
Normal file
Binary file not shown.
15
cron.php
15
cron.php
@@ -1,19 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
error_reporting( E_ALL ^ E_NOTICE ^ E_STRICT ^ E_WARNING );
|
error_reporting( E_ALL ^ E_NOTICE ^ E_STRICT ^ E_WARNING );
|
||||||
function __autoload_my_classes( $classname )
|
require_once __DIR__ . '/autoload/autoloader.php';
|
||||||
{
|
|
||||||
$q = explode( '\\' , $classname );
|
|
||||||
$c = array_pop( $q );
|
|
||||||
|
|
||||||
// 1. Legacy: class.ClassName.php
|
|
||||||
$f = 'autoload/' . implode( '/' , $q ) . '/class.' . $c . '.php';
|
|
||||||
if ( file_exists( $f ) ) { require_once( $f ); return; }
|
|
||||||
|
|
||||||
// 2. PSR-4: ClassName.php
|
|
||||||
$f = 'autoload/' . implode( '/' , $q ) . '/' . $c . '.php';
|
|
||||||
if ( file_exists( $f ) ) require_once( $f );
|
|
||||||
}
|
|
||||||
spl_autoload_register( '__autoload_my_classes' );
|
|
||||||
date_default_timezone_set( 'Europe/Warsaw' );
|
date_default_timezone_set( 'Europe/Warsaw' );
|
||||||
|
|
||||||
require_once 'config.php';
|
require_once 'config.php';
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
| `cron.php` | Zadania cykliczne (newsletter) |
|
| `cron.php` | Zadania cykliczne (newsletter) |
|
||||||
| `download.php` | Chronione pobieranie plików |
|
| `download.php` | Chronione pobieranie plików |
|
||||||
|
|
||||||
Każdy punkt wejścia ładuje dwa autoloadery (PSR-4 + legacy):
|
Każdy punkt wejścia ładuje centralny autoloader (hybrydowy PSR-4 + legacy):
|
||||||
```php
|
```php
|
||||||
spl_autoload_register(function($class) { /* PSR-4: src/ → autoload/ */ });
|
require_once __DIR__ . '/autoload/autoloader.php';
|
||||||
spl_autoload_register(function($class) { /* legacy: class.{Name}.php */ });
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -40,34 +39,40 @@ Projekt migruje stopniowo do architektury DDD. Stare klasy stają się
|
|||||||
cienkimi wrapperami delegującymi do nowych klas w `Shared\` i `Domain\`.
|
cienkimi wrapperami delegującymi do nowych klas w `Shared\` i `Domain\`.
|
||||||
|
|
||||||
### Faza 0 ✓ — Autoloader PSR-4
|
### Faza 0 ✓ — Autoloader PSR-4
|
||||||
Dodany do wszystkich 6 punktów wejścia. Mapowanie: namespace → `autoload/`.
|
Centralny autoloader w `autoload/autoloader.php` (hybrydowy: PSR-4 + legacy class.*.php).
|
||||||
|
Wszystkie 7 punktów wejścia używają jednego pliku. composer.json z PSR-4 mapowaniem:
|
||||||
|
Domain\, Shared\, Admin\, Frontend\ → autoload/.
|
||||||
|
|
||||||
### Faza 1 ✓ — Shared utilities (`autoload/Shared/`)
|
### Faza 1 ✓ — Shared utilities (`autoload/Shared/`)
|
||||||
|
|
||||||
```
|
```
|
||||||
autoload/Shared/
|
autoload/Shared/
|
||||||
├── Cache/CacheHandler.php ← \Shared\Cache\CacheHandler
|
├── Cache/CacheHandler.php ← \Shared\Cache\CacheHandler
|
||||||
├── Email/ ← \Shared\Email\*
|
├── Email/Email.php ← \Shared\Email\Email
|
||||||
├── Helpers/Helpers.php ← \Shared\Helpers\Helpers
|
├── Helpers/Helpers.php ← \Shared\Helpers\Helpers
|
||||||
├── Html/Html.php ← \Shared\Html\Html
|
├── Html/Html.php ← \Shared\Html\Html
|
||||||
├── Image/ImageManipulator.php ← \Shared\Image\ImageManipulator
|
├── Image/ImageManipulator.php ← \Shared\Image\ImageManipulator
|
||||||
|
├── Security/CsrfToken.php ← \Shared\Security\CsrfToken
|
||||||
└── Tpl/Tpl.php ← \Shared\Tpl\Tpl
|
└── Tpl/Tpl.php ← \Shared\Tpl\Tpl
|
||||||
```
|
```
|
||||||
|
|
||||||
Stare klasy (`class.S.php`, `class.Cache.php`, itd.) są teraz cienkimi
|
Stare klasy (`class.S.php`, `class.Cache.php`, itd.) są teraz cienkimi
|
||||||
wrapperami — zachowana pełna kompatybilność wsteczna.
|
wrapperami — zachowana pełna kompatybilność wsteczna.
|
||||||
|
Helpers::send_email() → Email, Helpers::get_token()/is_token_valid() → CsrfToken.
|
||||||
|
|
||||||
### Faza 2 (w toku) — Domain Repositories (`autoload/Domain/`)
|
### Faza 2 (w toku) - Domain Repositories (`autoload/Domain/`)
|
||||||
|
|
||||||
```
|
```
|
||||||
autoload/Domain/
|
autoload/Domain/
|
||||||
├── Languages/LanguagesRepository.php ← \Domain\Languages\LanguagesRepository ✓
|
├── Languages/LanguagesRepository.php ← \Domain\Languages\LanguagesRepository ✓
|
||||||
├── Settings/SettingsRepository.php ← \Domain\Settings\SettingsRepository ✓
|
├── Settings/SettingsRepository.php ← \Domain\Settings\SettingsRepository ✓
|
||||||
└── User/UserRepository.php ← \Domain\User\UserRepository ✓
|
├── User/UserRepository.php ← \Domain\User\UserRepository ✓
|
||||||
|
├── Pages/PagesRepository.php ← \Domain\Pages\PagesRepository ✓
|
||||||
|
├── Layouts/LayoutsRepository.php ← \Domain\Layouts\LayoutsRepository ✓
|
||||||
|
└── Articles/ArticlesRepository.php ← \Domain\Articles\ArticlesRepository ✓
|
||||||
```
|
```
|
||||||
|
|
||||||
Następne: `Domain\Pages`, `Domain\Layouts`, `Domain\Articles`, ...
|
Następne: `Domain\Scontainers`, `Domain\Banners`, `Domain\Authors`, `Domain\Newsletter`, ...
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Katalogi
|
## Katalogi
|
||||||
@@ -118,3 +123,4 @@ Główne tabele: `pp_users`, `pp_articles`, `pp_articles_langs`, `pp_pages`,
|
|||||||
- Tabela: `pp_languages`
|
- Tabela: `pp_languages`
|
||||||
- Składnia w treści: `[LANG:klucz]`
|
- Składnia w treści: `[LANG:klucz]`
|
||||||
- Cache tłumaczeń: `$_SESSION['lang-{lang_id}']`
|
- Cache tłumaczeń: `$_SESSION['lang-{lang_id}']`
|
||||||
|
|
||||||
|
|||||||
11
download.php
11
download.php
@@ -1,15 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
error_reporting( E_ALL & ~E_NOTICE & ~E_WARNING );
|
error_reporting( E_ALL & ~E_NOTICE & ~E_WARNING );
|
||||||
function __autoload_my_classes( $classname )
|
require_once __DIR__ . '/autoload/autoloader.php';
|
||||||
{
|
|
||||||
$q = explode( '\\' , $classname );
|
|
||||||
$c = array_pop( $q );
|
|
||||||
$f = 'autoload/' . implode( '/' , $q ) . '/class.' . $c . '.php';
|
|
||||||
|
|
||||||
if ( file_exists( $f ) )
|
|
||||||
require_once( $f );
|
|
||||||
}
|
|
||||||
spl_autoload_register( '__autoload_my_classes' );
|
|
||||||
date_default_timezone_set( 'Europe/Warsaw' );
|
date_default_timezone_set( 'Europe/Warsaw' );
|
||||||
|
|
||||||
require_once 'config.php';
|
require_once 'config.php';
|
||||||
|
|||||||
15
index.php
15
index.php
@@ -1,19 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
error_reporting( E_ALL ^ E_NOTICE ^ E_STRICT ^ E_WARNING ^ E_DEPRECATED );
|
error_reporting( E_ALL ^ E_NOTICE ^ E_STRICT ^ E_WARNING ^ E_DEPRECATED );
|
||||||
function __autoload_my_classes( $classname )
|
require_once __DIR__ . '/autoload/autoloader.php';
|
||||||
{
|
|
||||||
$q = explode( '\\', $classname );
|
|
||||||
$c = array_pop( $q );
|
|
||||||
|
|
||||||
// 1. Legacy: class.ClassName.php
|
|
||||||
$f = 'autoload/' . implode( '/', $q ) . '/class.' . $c . '.php';
|
|
||||||
if ( file_exists( $f ) ) { require_once( $f ); return; }
|
|
||||||
|
|
||||||
// 2. PSR-4: ClassName.php
|
|
||||||
$f = 'autoload/' . implode( '/', $q ) . '/' . $c . '.php';
|
|
||||||
if ( file_exists( $f ) ) require_once( $f );
|
|
||||||
}
|
|
||||||
spl_autoload_register( '__autoload_my_classes' );
|
|
||||||
date_default_timezone_set( 'Europe/Warsaw' );
|
date_default_timezone_set( 'Europe/Warsaw' );
|
||||||
|
|
||||||
require_once 'config.php';
|
require_once 'config.php';
|
||||||
|
|||||||
BIN
updates/1.60/ver_1.692.zip
Normal file
BIN
updates/1.60/ver_1.692.zip
Normal file
Binary file not shown.
24
updates/1.60/ver_1.692_manifest.json
Normal file
24
updates/1.60/ver_1.692_manifest.json
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"changelog": "FIX - Tpl::__isset() dla poprawnej obslugi isset() na wlasciwosciach szablonu",
|
||||||
|
"version": "1.692",
|
||||||
|
"files": {
|
||||||
|
"added": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"deleted": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"modified": [
|
||||||
|
"autoload/Domain/Settings/SettingsRepository.php",
|
||||||
|
"autoload/Shared/Tpl/Tpl.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"checksum_zip": "sha256:bbd1c61b39b5bb4a37b618efea770bed3438d7324ab389d415d24b2d87b08bd6",
|
||||||
|
"sql": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"date": "2026-02-28",
|
||||||
|
"directories_deleted": [
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
updates/1.60/ver_1.693.zip
Normal file
BIN
updates/1.60/ver_1.693.zip
Normal file
Binary file not shown.
28
updates/1.60/ver_1.693_manifest.json
Normal file
28
updates/1.60/ver_1.693_manifest.json
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"changelog": "REF - migracja admin Pages/Layouts/Articles do Domain repositories",
|
||||||
|
"version": "1.693",
|
||||||
|
"files": {
|
||||||
|
"added": [
|
||||||
|
"autoload/Domain/Articles/ArticlesRepository.php",
|
||||||
|
"autoload/Domain/Layouts/LayoutsRepository.php",
|
||||||
|
"autoload/Domain/Pages/PagesRepository.php",
|
||||||
|
"composer.phar"
|
||||||
|
],
|
||||||
|
"deleted": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"modified": [
|
||||||
|
"autoload/admin/factory/class.Articles.php",
|
||||||
|
"autoload/admin/factory/class.Layouts.php",
|
||||||
|
"autoload/admin/factory/class.Pages.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"checksum_zip": "sha256:3994561d9f3df8ed887f53c903b2a26ae6d17e6b10d98c7cb5cdc59132cef7b5",
|
||||||
|
"sql": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"date": "2026-03-04",
|
||||||
|
"directories_deleted": [
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
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'
|
'charset' => 'utf8'
|
||||||
] );
|
] );
|
||||||
|
|
||||||
$current_ver = 1691; // 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 = [];
|
$versions = [];
|
||||||
for ( $i = 1; $i <= $current_ver; $i++ )
|
for ( $i = 1; $i <= $current_ver; $i++ )
|
||||||
{
|
{
|
||||||
@@ -34,7 +34,7 @@ $license = $mdb->get( 'pp_update_licenses', '*', [ 'key' => ( $_GET['key'] ?? ''
|
|||||||
if ( !$license )
|
if ( !$license )
|
||||||
die();
|
die();
|
||||||
|
|
||||||
// 3. Sprawdź ważność daty
|
// 3. SprawdĹş waĹĽnoÄąĂË€şĂ„ĂË€ˇ daty
|
||||||
if ( $license['valid_to_date'] && $license['valid_to_date'] < date( 'Y-m-d' ) )
|
if ( $license['valid_to_date'] && $license['valid_to_date'] < date( 'Y-m-d' ) )
|
||||||
die();
|
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' ];
|
$channels = $license['beta'] ? [ 'beta', 'stable' ] : [ 'stable' ];
|
||||||
$allowed = array_flip( $mdb->select( 'pp_update_versions', 'version', [ 'channel' => $channels ] ) ?: [] );
|
$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'];
|
$valid_to_version = $license['valid_to_version'];
|
||||||
foreach ( $versions as $ver )
|
foreach ( $versions as $ver )
|
||||||
{
|
{
|
||||||
|
|||||||
22
vendor/autoload.php
vendored
Normal file
22
vendor/autoload.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload.php @generated by Composer
|
||||||
|
|
||||||
|
if (PHP_VERSION_ID < 50600) {
|
||||||
|
if (!headers_sent()) {
|
||||||
|
header('HTTP/1.1 500 Internal Server Error');
|
||||||
|
}
|
||||||
|
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
|
||||||
|
if (!ini_get('display_errors')) {
|
||||||
|
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||||
|
fwrite(STDERR, $err);
|
||||||
|
} elseif (!headers_sent()) {
|
||||||
|
echo $err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new RuntimeException($err);
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once __DIR__ . '/composer/autoload_real.php';
|
||||||
|
|
||||||
|
return ComposerAutoloaderInitedf908e1f6b0e4fca8854163be177e40::getLoader();
|
||||||
119
vendor/bin/php-parse
vendored
Normal file
119
vendor/bin/php-parse
vendored
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy PHP file generated by Composer
|
||||||
|
*
|
||||||
|
* This file includes the referenced bin path (../nikic/php-parser/bin/php-parse)
|
||||||
|
* using a stream wrapper to prevent the shebang from being output on PHP<8
|
||||||
|
*
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer;
|
||||||
|
|
||||||
|
$GLOBALS['_composer_bin_dir'] = __DIR__;
|
||||||
|
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
|
||||||
|
|
||||||
|
if (PHP_VERSION_ID < 80000) {
|
||||||
|
if (!class_exists('Composer\BinProxyWrapper')) {
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
final class BinProxyWrapper
|
||||||
|
{
|
||||||
|
private $handle;
|
||||||
|
private $position;
|
||||||
|
private $realpath;
|
||||||
|
|
||||||
|
public function stream_open($path, $mode, $options, &$opened_path)
|
||||||
|
{
|
||||||
|
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
|
||||||
|
$opened_path = substr($path, 17);
|
||||||
|
$this->realpath = realpath($opened_path) ?: $opened_path;
|
||||||
|
$opened_path = $this->realpath;
|
||||||
|
$this->handle = fopen($this->realpath, $mode);
|
||||||
|
$this->position = 0;
|
||||||
|
|
||||||
|
return (bool) $this->handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_read($count)
|
||||||
|
{
|
||||||
|
$data = fread($this->handle, $count);
|
||||||
|
|
||||||
|
if ($this->position === 0) {
|
||||||
|
$data = preg_replace('{^#!.*\r?\n}', '', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->position += strlen($data);
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_cast($castAs)
|
||||||
|
{
|
||||||
|
return $this->handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_close()
|
||||||
|
{
|
||||||
|
fclose($this->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_lock($operation)
|
||||||
|
{
|
||||||
|
return $operation ? flock($this->handle, $operation) : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_seek($offset, $whence)
|
||||||
|
{
|
||||||
|
if (0 === fseek($this->handle, $offset, $whence)) {
|
||||||
|
$this->position = ftell($this->handle);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_tell()
|
||||||
|
{
|
||||||
|
return $this->position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_eof()
|
||||||
|
{
|
||||||
|
return feof($this->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_stat()
|
||||||
|
{
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_set_option($option, $arg1, $arg2)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function url_stat($path, $flags)
|
||||||
|
{
|
||||||
|
$path = substr($path, 17);
|
||||||
|
if (file_exists($path)) {
|
||||||
|
return stat($path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|
||||||
|
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
|
||||||
|
) {
|
||||||
|
return include("phpvfscomposer://" . __DIR__ . '/..'.'/nikic/php-parser/bin/php-parse');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return include __DIR__ . '/..'.'/nikic/php-parser/bin/php-parse';
|
||||||
5
vendor/bin/php-parse.bat
vendored
Normal file
5
vendor/bin/php-parse.bat
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
@ECHO OFF
|
||||||
|
setlocal DISABLEDELAYEDEXPANSION
|
||||||
|
SET BIN_TARGET=%~dp0/php-parse
|
||||||
|
SET COMPOSER_RUNTIME_BIN_DIR=%~dp0
|
||||||
|
php "%BIN_TARGET%" %*
|
||||||
122
vendor/bin/phpunit
vendored
Normal file
122
vendor/bin/phpunit
vendored
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy PHP file generated by Composer
|
||||||
|
*
|
||||||
|
* This file includes the referenced bin path (../phpunit/phpunit/phpunit)
|
||||||
|
* using a stream wrapper to prevent the shebang from being output on PHP<8
|
||||||
|
*
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer;
|
||||||
|
|
||||||
|
$GLOBALS['_composer_bin_dir'] = __DIR__;
|
||||||
|
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
|
||||||
|
$GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST'] = $GLOBALS['__PHPUNIT_ISOLATION_BLACKLIST'] = array(realpath(__DIR__ . '/..'.'/phpunit/phpunit/phpunit'));
|
||||||
|
|
||||||
|
if (PHP_VERSION_ID < 80000) {
|
||||||
|
if (!class_exists('Composer\BinProxyWrapper')) {
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
final class BinProxyWrapper
|
||||||
|
{
|
||||||
|
private $handle;
|
||||||
|
private $position;
|
||||||
|
private $realpath;
|
||||||
|
|
||||||
|
public function stream_open($path, $mode, $options, &$opened_path)
|
||||||
|
{
|
||||||
|
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
|
||||||
|
$opened_path = substr($path, 17);
|
||||||
|
$this->realpath = realpath($opened_path) ?: $opened_path;
|
||||||
|
$opened_path = 'phpvfscomposer://'.$this->realpath;
|
||||||
|
$this->handle = fopen($this->realpath, $mode);
|
||||||
|
$this->position = 0;
|
||||||
|
|
||||||
|
return (bool) $this->handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_read($count)
|
||||||
|
{
|
||||||
|
$data = fread($this->handle, $count);
|
||||||
|
|
||||||
|
if ($this->position === 0) {
|
||||||
|
$data = preg_replace('{^#!.*\r?\n}', '', $data);
|
||||||
|
}
|
||||||
|
$data = str_replace('__DIR__', var_export(dirname($this->realpath), true), $data);
|
||||||
|
$data = str_replace('__FILE__', var_export($this->realpath, true), $data);
|
||||||
|
|
||||||
|
$this->position += strlen($data);
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_cast($castAs)
|
||||||
|
{
|
||||||
|
return $this->handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_close()
|
||||||
|
{
|
||||||
|
fclose($this->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_lock($operation)
|
||||||
|
{
|
||||||
|
return $operation ? flock($this->handle, $operation) : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_seek($offset, $whence)
|
||||||
|
{
|
||||||
|
if (0 === fseek($this->handle, $offset, $whence)) {
|
||||||
|
$this->position = ftell($this->handle);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_tell()
|
||||||
|
{
|
||||||
|
return $this->position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_eof()
|
||||||
|
{
|
||||||
|
return feof($this->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_stat()
|
||||||
|
{
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_set_option($option, $arg1, $arg2)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function url_stat($path, $flags)
|
||||||
|
{
|
||||||
|
$path = substr($path, 17);
|
||||||
|
if (file_exists($path)) {
|
||||||
|
return stat($path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|
||||||
|
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
|
||||||
|
) {
|
||||||
|
return include("phpvfscomposer://" . __DIR__ . '/..'.'/phpunit/phpunit/phpunit');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return include __DIR__ . '/..'.'/phpunit/phpunit/phpunit';
|
||||||
5
vendor/bin/phpunit.bat
vendored
Normal file
5
vendor/bin/phpunit.bat
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
@ECHO OFF
|
||||||
|
setlocal DISABLEDELAYEDEXPANSION
|
||||||
|
SET BIN_TARGET=%~dp0/phpunit
|
||||||
|
SET COMPOSER_RUNTIME_BIN_DIR=%~dp0
|
||||||
|
php "%BIN_TARGET%" %*
|
||||||
579
vendor/composer/ClassLoader.php
vendored
Normal file
579
vendor/composer/ClassLoader.php
vendored
Normal file
@@ -0,0 +1,579 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\Autoload;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
||||||
|
*
|
||||||
|
* $loader = new \Composer\Autoload\ClassLoader();
|
||||||
|
*
|
||||||
|
* // register classes with namespaces
|
||||||
|
* $loader->add('Symfony\Component', __DIR__.'/component');
|
||||||
|
* $loader->add('Symfony', __DIR__.'/framework');
|
||||||
|
*
|
||||||
|
* // activate the autoloader
|
||||||
|
* $loader->register();
|
||||||
|
*
|
||||||
|
* // to enable searching the include path (eg. for PEAR packages)
|
||||||
|
* $loader->setUseIncludePath(true);
|
||||||
|
*
|
||||||
|
* In this example, if you try to use a class in the Symfony\Component
|
||||||
|
* namespace or one of its children (Symfony\Component\Console for instance),
|
||||||
|
* the autoloader will first look for the class under the component/
|
||||||
|
* directory, and it will then fallback to the framework/ directory if not
|
||||||
|
* found before giving up.
|
||||||
|
*
|
||||||
|
* This class is loosely based on the Symfony UniversalClassLoader.
|
||||||
|
*
|
||||||
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
|
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
* @see https://www.php-fig.org/psr/psr-0/
|
||||||
|
* @see https://www.php-fig.org/psr/psr-4/
|
||||||
|
*/
|
||||||
|
class ClassLoader
|
||||||
|
{
|
||||||
|
/** @var \Closure(string):void */
|
||||||
|
private static $includeFile;
|
||||||
|
|
||||||
|
/** @var string|null */
|
||||||
|
private $vendorDir;
|
||||||
|
|
||||||
|
// PSR-4
|
||||||
|
/**
|
||||||
|
* @var array<string, array<string, int>>
|
||||||
|
*/
|
||||||
|
private $prefixLengthsPsr4 = array();
|
||||||
|
/**
|
||||||
|
* @var array<string, list<string>>
|
||||||
|
*/
|
||||||
|
private $prefixDirsPsr4 = array();
|
||||||
|
/**
|
||||||
|
* @var list<string>
|
||||||
|
*/
|
||||||
|
private $fallbackDirsPsr4 = array();
|
||||||
|
|
||||||
|
// PSR-0
|
||||||
|
/**
|
||||||
|
* List of PSR-0 prefixes
|
||||||
|
*
|
||||||
|
* Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
|
||||||
|
*
|
||||||
|
* @var array<string, array<string, list<string>>>
|
||||||
|
*/
|
||||||
|
private $prefixesPsr0 = array();
|
||||||
|
/**
|
||||||
|
* @var list<string>
|
||||||
|
*/
|
||||||
|
private $fallbackDirsPsr0 = array();
|
||||||
|
|
||||||
|
/** @var bool */
|
||||||
|
private $useIncludePath = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, string>
|
||||||
|
*/
|
||||||
|
private $classMap = array();
|
||||||
|
|
||||||
|
/** @var bool */
|
||||||
|
private $classMapAuthoritative = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, bool>
|
||||||
|
*/
|
||||||
|
private $missingClasses = array();
|
||||||
|
|
||||||
|
/** @var string|null */
|
||||||
|
private $apcuPrefix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, self>
|
||||||
|
*/
|
||||||
|
private static $registeredLoaders = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|null $vendorDir
|
||||||
|
*/
|
||||||
|
public function __construct($vendorDir = null)
|
||||||
|
{
|
||||||
|
$this->vendorDir = $vendorDir;
|
||||||
|
self::initializeIncludeClosure();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, list<string>>
|
||||||
|
*/
|
||||||
|
public function getPrefixes()
|
||||||
|
{
|
||||||
|
if (!empty($this->prefixesPsr0)) {
|
||||||
|
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
|
||||||
|
}
|
||||||
|
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, list<string>>
|
||||||
|
*/
|
||||||
|
public function getPrefixesPsr4()
|
||||||
|
{
|
||||||
|
return $this->prefixDirsPsr4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return list<string>
|
||||||
|
*/
|
||||||
|
public function getFallbackDirs()
|
||||||
|
{
|
||||||
|
return $this->fallbackDirsPsr0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return list<string>
|
||||||
|
*/
|
||||||
|
public function getFallbackDirsPsr4()
|
||||||
|
{
|
||||||
|
return $this->fallbackDirsPsr4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, string> Array of classname => path
|
||||||
|
*/
|
||||||
|
public function getClassMap()
|
||||||
|
{
|
||||||
|
return $this->classMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, string> $classMap Class to filename map
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function addClassMap(array $classMap)
|
||||||
|
{
|
||||||
|
if ($this->classMap) {
|
||||||
|
$this->classMap = array_merge($this->classMap, $classMap);
|
||||||
|
} else {
|
||||||
|
$this->classMap = $classMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-0 directories for a given prefix, either
|
||||||
|
* appending or prepending to the ones previously set for this prefix.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix
|
||||||
|
* @param list<string>|string $paths The PSR-0 root directories
|
||||||
|
* @param bool $prepend Whether to prepend the directories
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function add($prefix, $paths, $prepend = false)
|
||||||
|
{
|
||||||
|
$paths = (array) $paths;
|
||||||
|
if (!$prefix) {
|
||||||
|
if ($prepend) {
|
||||||
|
$this->fallbackDirsPsr0 = array_merge(
|
||||||
|
$paths,
|
||||||
|
$this->fallbackDirsPsr0
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->fallbackDirsPsr0 = array_merge(
|
||||||
|
$this->fallbackDirsPsr0,
|
||||||
|
$paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$first = $prefix[0];
|
||||||
|
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = $paths;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($prepend) {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||||
|
$paths,
|
||||||
|
$this->prefixesPsr0[$first][$prefix]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||||
|
$this->prefixesPsr0[$first][$prefix],
|
||||||
|
$paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-4 directories for a given namespace, either
|
||||||
|
* appending or prepending to the ones previously set for this namespace.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||||
|
* @param list<string>|string $paths The PSR-4 base directories
|
||||||
|
* @param bool $prepend Whether to prepend the directories
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function addPsr4($prefix, $paths, $prepend = false)
|
||||||
|
{
|
||||||
|
$paths = (array) $paths;
|
||||||
|
if (!$prefix) {
|
||||||
|
// Register directories for the root namespace.
|
||||||
|
if ($prepend) {
|
||||||
|
$this->fallbackDirsPsr4 = array_merge(
|
||||||
|
$paths,
|
||||||
|
$this->fallbackDirsPsr4
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->fallbackDirsPsr4 = array_merge(
|
||||||
|
$this->fallbackDirsPsr4,
|
||||||
|
$paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||||
|
// Register directories for a new namespace.
|
||||||
|
$length = strlen($prefix);
|
||||||
|
if ('\\' !== $prefix[$length - 1]) {
|
||||||
|
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||||
|
}
|
||||||
|
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||||
|
$this->prefixDirsPsr4[$prefix] = $paths;
|
||||||
|
} elseif ($prepend) {
|
||||||
|
// Prepend directories for an already registered namespace.
|
||||||
|
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||||
|
$paths,
|
||||||
|
$this->prefixDirsPsr4[$prefix]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Append directories for an already registered namespace.
|
||||||
|
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||||
|
$this->prefixDirsPsr4[$prefix],
|
||||||
|
$paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-0 directories for a given prefix,
|
||||||
|
* replacing any others previously set for this prefix.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix
|
||||||
|
* @param list<string>|string $paths The PSR-0 base directories
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function set($prefix, $paths)
|
||||||
|
{
|
||||||
|
if (!$prefix) {
|
||||||
|
$this->fallbackDirsPsr0 = (array) $paths;
|
||||||
|
} else {
|
||||||
|
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-4 directories for a given namespace,
|
||||||
|
* replacing any others previously set for this namespace.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||||
|
* @param list<string>|string $paths The PSR-4 base directories
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setPsr4($prefix, $paths)
|
||||||
|
{
|
||||||
|
if (!$prefix) {
|
||||||
|
$this->fallbackDirsPsr4 = (array) $paths;
|
||||||
|
} else {
|
||||||
|
$length = strlen($prefix);
|
||||||
|
if ('\\' !== $prefix[$length - 1]) {
|
||||||
|
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||||
|
}
|
||||||
|
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||||
|
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns on searching the include path for class files.
|
||||||
|
*
|
||||||
|
* @param bool $useIncludePath
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setUseIncludePath($useIncludePath)
|
||||||
|
{
|
||||||
|
$this->useIncludePath = $useIncludePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can be used to check if the autoloader uses the include path to check
|
||||||
|
* for classes.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function getUseIncludePath()
|
||||||
|
{
|
||||||
|
return $this->useIncludePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns off searching the prefix and fallback directories for classes
|
||||||
|
* that have not been registered with the class map.
|
||||||
|
*
|
||||||
|
* @param bool $classMapAuthoritative
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||||
|
{
|
||||||
|
$this->classMapAuthoritative = $classMapAuthoritative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should class lookup fail if not found in the current class map?
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isClassMapAuthoritative()
|
||||||
|
{
|
||||||
|
return $this->classMapAuthoritative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
||||||
|
*
|
||||||
|
* @param string|null $apcuPrefix
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setApcuPrefix($apcuPrefix)
|
||||||
|
{
|
||||||
|
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The APCu prefix in use, or null if APCu caching is not enabled.
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getApcuPrefix()
|
||||||
|
{
|
||||||
|
return $this->apcuPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers this instance as an autoloader.
|
||||||
|
*
|
||||||
|
* @param bool $prepend Whether to prepend the autoloader or not
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function register($prepend = false)
|
||||||
|
{
|
||||||
|
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||||
|
|
||||||
|
if (null === $this->vendorDir) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($prepend) {
|
||||||
|
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
|
||||||
|
} else {
|
||||||
|
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||||
|
self::$registeredLoaders[$this->vendorDir] = $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters this instance as an autoloader.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function unregister()
|
||||||
|
{
|
||||||
|
spl_autoload_unregister(array($this, 'loadClass'));
|
||||||
|
|
||||||
|
if (null !== $this->vendorDir) {
|
||||||
|
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the given class or interface.
|
||||||
|
*
|
||||||
|
* @param string $class The name of the class
|
||||||
|
* @return true|null True if loaded, null otherwise
|
||||||
|
*/
|
||||||
|
public function loadClass($class)
|
||||||
|
{
|
||||||
|
if ($file = $this->findFile($class)) {
|
||||||
|
$includeFile = self::$includeFile;
|
||||||
|
$includeFile($file);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the path to the file where the class is defined.
|
||||||
|
*
|
||||||
|
* @param string $class The name of the class
|
||||||
|
*
|
||||||
|
* @return string|false The path if found, false otherwise
|
||||||
|
*/
|
||||||
|
public function findFile($class)
|
||||||
|
{
|
||||||
|
// class map lookup
|
||||||
|
if (isset($this->classMap[$class])) {
|
||||||
|
return $this->classMap[$class];
|
||||||
|
}
|
||||||
|
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (null !== $this->apcuPrefix) {
|
||||||
|
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
||||||
|
if ($hit) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = $this->findFileWithExtension($class, '.php');
|
||||||
|
|
||||||
|
// Search for Hack files if we are running on HHVM
|
||||||
|
if (false === $file && defined('HHVM_VERSION')) {
|
||||||
|
$file = $this->findFileWithExtension($class, '.hh');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $this->apcuPrefix) {
|
||||||
|
apcu_add($this->apcuPrefix.$class, $file);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false === $file) {
|
||||||
|
// Remember that this class does not exist.
|
||||||
|
$this->missingClasses[$class] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the currently registered loaders keyed by their corresponding vendor directories.
|
||||||
|
*
|
||||||
|
* @return array<string, self>
|
||||||
|
*/
|
||||||
|
public static function getRegisteredLoaders()
|
||||||
|
{
|
||||||
|
return self::$registeredLoaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $class
|
||||||
|
* @param string $ext
|
||||||
|
* @return string|false
|
||||||
|
*/
|
||||||
|
private function findFileWithExtension($class, $ext)
|
||||||
|
{
|
||||||
|
// PSR-4 lookup
|
||||||
|
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||||
|
|
||||||
|
$first = $class[0];
|
||||||
|
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||||
|
$subPath = $class;
|
||||||
|
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
||||||
|
$subPath = substr($subPath, 0, $lastPos);
|
||||||
|
$search = $subPath . '\\';
|
||||||
|
if (isset($this->prefixDirsPsr4[$search])) {
|
||||||
|
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
||||||
|
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
||||||
|
if (file_exists($file = $dir . $pathEnd)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-4 fallback dirs
|
||||||
|
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 lookup
|
||||||
|
if (false !== $pos = strrpos($class, '\\')) {
|
||||||
|
// namespaced class name
|
||||||
|
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||||
|
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||||
|
} else {
|
||||||
|
// PEAR-like class name
|
||||||
|
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($this->prefixesPsr0[$first])) {
|
||||||
|
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||||
|
if (0 === strpos($class, $prefix)) {
|
||||||
|
foreach ($dirs as $dir) {
|
||||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 fallback dirs
|
||||||
|
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 include paths.
|
||||||
|
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private static function initializeIncludeClosure()
|
||||||
|
{
|
||||||
|
if (self::$includeFile !== null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scope isolated include.
|
||||||
|
*
|
||||||
|
* Prevents access to $this/self from included files.
|
||||||
|
*
|
||||||
|
* @param string $file
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
self::$includeFile = \Closure::bind(static function($file) {
|
||||||
|
include $file;
|
||||||
|
}, null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
396
vendor/composer/InstalledVersions.php
vendored
Normal file
396
vendor/composer/InstalledVersions.php
vendored
Normal file
@@ -0,0 +1,396 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer;
|
||||||
|
|
||||||
|
use Composer\Autoload\ClassLoader;
|
||||||
|
use Composer\Semver\VersionParser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is copied in every Composer installed project and available to all
|
||||||
|
*
|
||||||
|
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
|
||||||
|
*
|
||||||
|
* To require its presence, you can require `composer-runtime-api ^2.0`
|
||||||
|
*
|
||||||
|
* @final
|
||||||
|
*/
|
||||||
|
class InstalledVersions
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
private static $selfDir = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var mixed[]|null
|
||||||
|
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
|
||||||
|
*/
|
||||||
|
private static $installed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private static $installedIsLocalDir;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool|null
|
||||||
|
*/
|
||||||
|
private static $canGetVendors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array[]
|
||||||
|
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||||
|
*/
|
||||||
|
private static $installedByVendor = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of all package names which are present, either by being installed, replaced or provided
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
* @psalm-return list<string>
|
||||||
|
*/
|
||||||
|
public static function getInstalledPackages()
|
||||||
|
{
|
||||||
|
$packages = array();
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
$packages[] = array_keys($installed['versions']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (1 === \count($packages)) {
|
||||||
|
return $packages[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of all package names with a specific type e.g. 'library'
|
||||||
|
*
|
||||||
|
* @param string $type
|
||||||
|
* @return string[]
|
||||||
|
* @psalm-return list<string>
|
||||||
|
*/
|
||||||
|
public static function getInstalledPackagesByType($type)
|
||||||
|
{
|
||||||
|
$packagesByType = array();
|
||||||
|
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
foreach ($installed['versions'] as $name => $package) {
|
||||||
|
if (isset($package['type']) && $package['type'] === $type) {
|
||||||
|
$packagesByType[] = $name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $packagesByType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the given package is installed
|
||||||
|
*
|
||||||
|
* This also returns true if the package name is provided or replaced by another package
|
||||||
|
*
|
||||||
|
* @param string $packageName
|
||||||
|
* @param bool $includeDevRequirements
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function isInstalled($packageName, $includeDevRequirements = true)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (isset($installed['versions'][$packageName])) {
|
||||||
|
return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the given package satisfies a version constraint
|
||||||
|
*
|
||||||
|
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
|
||||||
|
*
|
||||||
|
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
|
||||||
|
*
|
||||||
|
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
|
||||||
|
* @param string $packageName
|
||||||
|
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function satisfies(VersionParser $parser, $packageName, $constraint)
|
||||||
|
{
|
||||||
|
$constraint = $parser->parseConstraints((string) $constraint);
|
||||||
|
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
|
||||||
|
|
||||||
|
return $provided->matches($constraint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a version constraint representing all the range(s) which are installed for a given package
|
||||||
|
*
|
||||||
|
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
|
||||||
|
* whether a given version of a package is installed, and not just whether it exists
|
||||||
|
*
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string Version constraint usable with composer/semver
|
||||||
|
*/
|
||||||
|
public static function getVersionRanges($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ranges = array();
|
||||||
|
if (isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||||
|
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
|
||||||
|
}
|
||||||
|
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
|
||||||
|
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
|
||||||
|
}
|
||||||
|
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
|
||||||
|
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
|
||||||
|
}
|
||||||
|
if (array_key_exists('provided', $installed['versions'][$packageName])) {
|
||||||
|
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode(' || ', $ranges);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||||
|
*/
|
||||||
|
public static function getVersion($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($installed['versions'][$packageName]['version'])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $installed['versions'][$packageName]['version'];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||||
|
*/
|
||||||
|
public static function getPrettyVersion($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $installed['versions'][$packageName]['pretty_version'];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
|
||||||
|
*/
|
||||||
|
public static function getReference($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($installed['versions'][$packageName]['reference'])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $installed['versions'][$packageName]['reference'];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
|
||||||
|
*/
|
||||||
|
public static function getInstallPath($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
|
||||||
|
*/
|
||||||
|
public static function getRootPackage()
|
||||||
|
{
|
||||||
|
$installed = self::getInstalled();
|
||||||
|
|
||||||
|
return $installed[0]['root'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the raw installed.php data for custom implementations
|
||||||
|
*
|
||||||
|
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
|
||||||
|
* @return array[]
|
||||||
|
* @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
|
||||||
|
*/
|
||||||
|
public static function getRawData()
|
||||||
|
{
|
||||||
|
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
|
||||||
|
|
||||||
|
if (null === self::$installed) {
|
||||||
|
// only require the installed.php file if this file is loaded from its dumped location,
|
||||||
|
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||||
|
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||||
|
self::$installed = include __DIR__ . '/installed.php';
|
||||||
|
} else {
|
||||||
|
self::$installed = array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$installed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the raw data of all installed.php which are currently loaded for custom implementations
|
||||||
|
*
|
||||||
|
* @return array[]
|
||||||
|
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||||
|
*/
|
||||||
|
public static function getAllRawData()
|
||||||
|
{
|
||||||
|
return self::getInstalled();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lets you reload the static array from another file
|
||||||
|
*
|
||||||
|
* This is only useful for complex integrations in which a project needs to use
|
||||||
|
* this class but then also needs to execute another project's autoloader in process,
|
||||||
|
* and wants to ensure both projects have access to their version of installed.php.
|
||||||
|
*
|
||||||
|
* A typical case would be PHPUnit, where it would need to make sure it reads all
|
||||||
|
* the data it needs from this class, then call reload() with
|
||||||
|
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
|
||||||
|
* the project in which it runs can then also use this class safely, without
|
||||||
|
* interference between PHPUnit's dependencies and the project's dependencies.
|
||||||
|
*
|
||||||
|
* @param array[] $data A vendor/composer/installed.php data set
|
||||||
|
* @return void
|
||||||
|
*
|
||||||
|
* @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
|
||||||
|
*/
|
||||||
|
public static function reload($data)
|
||||||
|
{
|
||||||
|
self::$installed = $data;
|
||||||
|
self::$installedByVendor = array();
|
||||||
|
|
||||||
|
// when using reload, we disable the duplicate protection to ensure that self::$installed data is
|
||||||
|
// always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not,
|
||||||
|
// so we have to assume it does not, and that may result in duplicate data being returned when listing
|
||||||
|
// all installed packages for example
|
||||||
|
self::$installedIsLocalDir = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private static function getSelfDir()
|
||||||
|
{
|
||||||
|
if (self::$selfDir === null) {
|
||||||
|
self::$selfDir = strtr(__DIR__, '\\', '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$selfDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array[]
|
||||||
|
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||||
|
*/
|
||||||
|
private static function getInstalled()
|
||||||
|
{
|
||||||
|
if (null === self::$canGetVendors) {
|
||||||
|
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
|
||||||
|
}
|
||||||
|
|
||||||
|
$installed = array();
|
||||||
|
$copiedLocalDir = false;
|
||||||
|
|
||||||
|
if (self::$canGetVendors) {
|
||||||
|
$selfDir = self::getSelfDir();
|
||||||
|
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
|
||||||
|
$vendorDir = strtr($vendorDir, '\\', '/');
|
||||||
|
if (isset(self::$installedByVendor[$vendorDir])) {
|
||||||
|
$installed[] = self::$installedByVendor[$vendorDir];
|
||||||
|
} elseif (is_file($vendorDir.'/composer/installed.php')) {
|
||||||
|
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
||||||
|
$required = require $vendorDir.'/composer/installed.php';
|
||||||
|
self::$installedByVendor[$vendorDir] = $required;
|
||||||
|
$installed[] = $required;
|
||||||
|
if (self::$installed === null && $vendorDir.'/composer' === $selfDir) {
|
||||||
|
self::$installed = $required;
|
||||||
|
self::$installedIsLocalDir = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) {
|
||||||
|
$copiedLocalDir = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === self::$installed) {
|
||||||
|
// only require the installed.php file if this file is loaded from its dumped location,
|
||||||
|
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||||
|
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||||
|
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
||||||
|
$required = require __DIR__ . '/installed.php';
|
||||||
|
self::$installed = $required;
|
||||||
|
} else {
|
||||||
|
self::$installed = array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self::$installed !== array() && !$copiedLocalDir) {
|
||||||
|
$installed[] = self::$installed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $installed;
|
||||||
|
}
|
||||||
|
}
|
||||||
21
vendor/composer/LICENSE
vendored
Normal file
21
vendor/composer/LICENSE
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
|
||||||
|
Copyright (c) Nils Adermann, Jordi Boggiano
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is furnished
|
||||||
|
to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
||||||
1186
vendor/composer/autoload_classmap.php
vendored
Normal file
1186
vendor/composer/autoload_classmap.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
11
vendor/composer/autoload_files.php
vendored
Normal file
11
vendor/composer/autoload_files.php
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_files.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(__DIR__);
|
||||||
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'6124b4c8570aa390c21fafd04a26c69f' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php',
|
||||||
|
'ec07570ca5a812141189b1fa81503674' => $vendorDir . '/phpunit/phpunit/src/Framework/Assert/Functions.php',
|
||||||
|
);
|
||||||
9
vendor/composer/autoload_namespaces.php
vendored
Normal file
9
vendor/composer/autoload_namespaces.php
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_namespaces.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(__DIR__);
|
||||||
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
);
|
||||||
12
vendor/composer/autoload_psr4.php
vendored
Normal file
12
vendor/composer/autoload_psr4.php
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_psr4.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(__DIR__);
|
||||||
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'Tests\\' => array($baseDir . '/tests'),
|
||||||
|
'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'),
|
||||||
|
'DeepCopy\\' => array($vendorDir . '/myclabs/deep-copy/src/DeepCopy'),
|
||||||
|
);
|
||||||
48
vendor/composer/autoload_real.php
vendored
Normal file
48
vendor/composer/autoload_real.php
vendored
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_real.php @generated by Composer
|
||||||
|
|
||||||
|
class ComposerAutoloaderInitedf908e1f6b0e4fca8854163be177e40
|
||||||
|
{
|
||||||
|
private static $loader;
|
||||||
|
|
||||||
|
public static function loadClassLoader($class)
|
||||||
|
{
|
||||||
|
if ('Composer\Autoload\ClassLoader' === $class) {
|
||||||
|
require __DIR__ . '/ClassLoader.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Composer\Autoload\ClassLoader
|
||||||
|
*/
|
||||||
|
public static function getLoader()
|
||||||
|
{
|
||||||
|
if (null !== self::$loader) {
|
||||||
|
return self::$loader;
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_autoload_register(array('ComposerAutoloaderInitedf908e1f6b0e4fca8854163be177e40', 'loadClassLoader'), true, true);
|
||||||
|
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
|
||||||
|
spl_autoload_unregister(array('ComposerAutoloaderInitedf908e1f6b0e4fca8854163be177e40', 'loadClassLoader'));
|
||||||
|
|
||||||
|
require __DIR__ . '/autoload_static.php';
|
||||||
|
call_user_func(\Composer\Autoload\ComposerStaticInitedf908e1f6b0e4fca8854163be177e40::getInitializer($loader));
|
||||||
|
|
||||||
|
$loader->register(true);
|
||||||
|
|
||||||
|
$filesToLoad = \Composer\Autoload\ComposerStaticInitedf908e1f6b0e4fca8854163be177e40::$files;
|
||||||
|
$requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
|
||||||
|
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
||||||
|
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
||||||
|
|
||||||
|
require $file;
|
||||||
|
}
|
||||||
|
}, null, null);
|
||||||
|
foreach ($filesToLoad as $fileIdentifier => $file) {
|
||||||
|
$requireFile($fileIdentifier, $file);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $loader;
|
||||||
|
}
|
||||||
|
}
|
||||||
1233
vendor/composer/autoload_static.php
vendored
Normal file
1233
vendor/composer/autoload_static.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1780
vendor/composer/installed.json
vendored
Normal file
1780
vendor/composer/installed.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
257
vendor/composer/installed.php
vendored
Normal file
257
vendor/composer/installed.php
vendored
Normal file
@@ -0,0 +1,257 @@
|
|||||||
|
<?php return array(
|
||||||
|
'root' => array(
|
||||||
|
'name' => '__root__',
|
||||||
|
'pretty_version' => 'dev-main',
|
||||||
|
'version' => 'dev-main',
|
||||||
|
'reference' => '8e6b29976c30c822440d5fc293930a9a38772801',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../../',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev' => true,
|
||||||
|
),
|
||||||
|
'versions' => array(
|
||||||
|
'__root__' => array(
|
||||||
|
'pretty_version' => 'dev-main',
|
||||||
|
'version' => 'dev-main',
|
||||||
|
'reference' => '8e6b29976c30c822440d5fc293930a9a38772801',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../../',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
|
'myclabs/deep-copy' => array(
|
||||||
|
'pretty_version' => '1.13.4',
|
||||||
|
'version' => '1.13.4.0',
|
||||||
|
'reference' => '07d290f0c47959fd5eed98c95ee5602db07e0b6a',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../myclabs/deep-copy',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => true,
|
||||||
|
),
|
||||||
|
'nikic/php-parser' => array(
|
||||||
|
'pretty_version' => 'v5.7.0',
|
||||||
|
'version' => '5.7.0.0',
|
||||||
|
'reference' => 'dca41cd15c2ac9d055ad70dbfd011130757d1f82',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../nikic/php-parser',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => true,
|
||||||
|
),
|
||||||
|
'phar-io/manifest' => array(
|
||||||
|
'pretty_version' => '2.0.4',
|
||||||
|
'version' => '2.0.4.0',
|
||||||
|
'reference' => '54750ef60c58e43759730615a392c31c80e23176',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../phar-io/manifest',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => true,
|
||||||
|
),
|
||||||
|
'phar-io/version' => array(
|
||||||
|
'pretty_version' => '3.2.1',
|
||||||
|
'version' => '3.2.1.0',
|
||||||
|
'reference' => '4f7fd7836c6f332bb2933569e566a0d6c4cbed74',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../phar-io/version',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => true,
|
||||||
|
),
|
||||||
|
'phpunit/php-code-coverage' => array(
|
||||||
|
'pretty_version' => '10.1.16',
|
||||||
|
'version' => '10.1.16.0',
|
||||||
|
'reference' => '7e308268858ed6baedc8704a304727d20bc07c77',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../phpunit/php-code-coverage',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => true,
|
||||||
|
),
|
||||||
|
'phpunit/php-file-iterator' => array(
|
||||||
|
'pretty_version' => '4.1.0',
|
||||||
|
'version' => '4.1.0.0',
|
||||||
|
'reference' => 'a95037b6d9e608ba092da1b23931e537cadc3c3c',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../phpunit/php-file-iterator',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => true,
|
||||||
|
),
|
||||||
|
'phpunit/php-invoker' => array(
|
||||||
|
'pretty_version' => '4.0.0',
|
||||||
|
'version' => '4.0.0.0',
|
||||||
|
'reference' => 'f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../phpunit/php-invoker',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => true,
|
||||||
|
),
|
||||||
|
'phpunit/php-text-template' => array(
|
||||||
|
'pretty_version' => '3.0.1',
|
||||||
|
'version' => '3.0.1.0',
|
||||||
|
'reference' => '0c7b06ff49e3d5072f057eb1fa59258bf287a748',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../phpunit/php-text-template',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => true,
|
||||||
|
),
|
||||||
|
'phpunit/php-timer' => array(
|
||||||
|
'pretty_version' => '6.0.0',
|
||||||
|
'version' => '6.0.0.0',
|
||||||
|
'reference' => 'e2a2d67966e740530f4a3343fe2e030ffdc1161d',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../phpunit/php-timer',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => true,
|
||||||
|
),
|
||||||
|
'phpunit/phpunit' => array(
|
||||||
|
'pretty_version' => '10.5.63',
|
||||||
|
'version' => '10.5.63.0',
|
||||||
|
'reference' => '33198268dad71e926626b618f3ec3966661e4d90',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../phpunit/phpunit',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => true,
|
||||||
|
),
|
||||||
|
'sebastian/cli-parser' => array(
|
||||||
|
'pretty_version' => '2.0.1',
|
||||||
|
'version' => '2.0.1.0',
|
||||||
|
'reference' => 'c34583b87e7b7a8055bf6c450c2c77ce32a24084',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../sebastian/cli-parser',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => true,
|
||||||
|
),
|
||||||
|
'sebastian/code-unit' => array(
|
||||||
|
'pretty_version' => '2.0.0',
|
||||||
|
'version' => '2.0.0.0',
|
||||||
|
'reference' => 'a81fee9eef0b7a76af11d121767abc44c104e503',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../sebastian/code-unit',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => true,
|
||||||
|
),
|
||||||
|
'sebastian/code-unit-reverse-lookup' => array(
|
||||||
|
'pretty_version' => '3.0.0',
|
||||||
|
'version' => '3.0.0.0',
|
||||||
|
'reference' => '5e3a687f7d8ae33fb362c5c0743794bbb2420a1d',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../sebastian/code-unit-reverse-lookup',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => true,
|
||||||
|
),
|
||||||
|
'sebastian/comparator' => array(
|
||||||
|
'pretty_version' => '5.0.5',
|
||||||
|
'version' => '5.0.5.0',
|
||||||
|
'reference' => '55dfef806eb7dfeb6e7a6935601fef866f8ca48d',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../sebastian/comparator',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => true,
|
||||||
|
),
|
||||||
|
'sebastian/complexity' => array(
|
||||||
|
'pretty_version' => '3.2.0',
|
||||||
|
'version' => '3.2.0.0',
|
||||||
|
'reference' => '68ff824baeae169ec9f2137158ee529584553799',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../sebastian/complexity',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => true,
|
||||||
|
),
|
||||||
|
'sebastian/diff' => array(
|
||||||
|
'pretty_version' => '5.1.1',
|
||||||
|
'version' => '5.1.1.0',
|
||||||
|
'reference' => 'c41e007b4b62af48218231d6c2275e4c9b975b2e',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../sebastian/diff',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => true,
|
||||||
|
),
|
||||||
|
'sebastian/environment' => array(
|
||||||
|
'pretty_version' => '6.1.0',
|
||||||
|
'version' => '6.1.0.0',
|
||||||
|
'reference' => '8074dbcd93529b357029f5cc5058fd3e43666984',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../sebastian/environment',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => true,
|
||||||
|
),
|
||||||
|
'sebastian/exporter' => array(
|
||||||
|
'pretty_version' => '5.1.4',
|
||||||
|
'version' => '5.1.4.0',
|
||||||
|
'reference' => '0735b90f4da94969541dac1da743446e276defa6',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../sebastian/exporter',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => true,
|
||||||
|
),
|
||||||
|
'sebastian/global-state' => array(
|
||||||
|
'pretty_version' => '6.0.2',
|
||||||
|
'version' => '6.0.2.0',
|
||||||
|
'reference' => '987bafff24ecc4c9ac418cab1145b96dd6e9cbd9',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../sebastian/global-state',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => true,
|
||||||
|
),
|
||||||
|
'sebastian/lines-of-code' => array(
|
||||||
|
'pretty_version' => '2.0.2',
|
||||||
|
'version' => '2.0.2.0',
|
||||||
|
'reference' => '856e7f6a75a84e339195d48c556f23be2ebf75d0',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../sebastian/lines-of-code',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => true,
|
||||||
|
),
|
||||||
|
'sebastian/object-enumerator' => array(
|
||||||
|
'pretty_version' => '5.0.0',
|
||||||
|
'version' => '5.0.0.0',
|
||||||
|
'reference' => '202d0e344a580d7f7d04b3fafce6933e59dae906',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../sebastian/object-enumerator',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => true,
|
||||||
|
),
|
||||||
|
'sebastian/object-reflector' => array(
|
||||||
|
'pretty_version' => '3.0.0',
|
||||||
|
'version' => '3.0.0.0',
|
||||||
|
'reference' => '24ed13d98130f0e7122df55d06c5c4942a577957',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../sebastian/object-reflector',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => true,
|
||||||
|
),
|
||||||
|
'sebastian/recursion-context' => array(
|
||||||
|
'pretty_version' => '5.0.1',
|
||||||
|
'version' => '5.0.1.0',
|
||||||
|
'reference' => '47e34210757a2f37a97dcd207d032e1b01e64c7a',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../sebastian/recursion-context',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => true,
|
||||||
|
),
|
||||||
|
'sebastian/type' => array(
|
||||||
|
'pretty_version' => '4.0.0',
|
||||||
|
'version' => '4.0.0.0',
|
||||||
|
'reference' => '462699a16464c3944eefc02ebdd77882bd3925bf',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../sebastian/type',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => true,
|
||||||
|
),
|
||||||
|
'sebastian/version' => array(
|
||||||
|
'pretty_version' => '4.0.1',
|
||||||
|
'version' => '4.0.1.0',
|
||||||
|
'reference' => 'c51fa83a5d8f43f1402e3f32a005e6262244ef17',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../sebastian/version',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => true,
|
||||||
|
),
|
||||||
|
'theseer/tokenizer' => array(
|
||||||
|
'pretty_version' => '1.3.1',
|
||||||
|
'version' => '1.3.1.0',
|
||||||
|
'reference' => 'b7489ce515e168639d17feec34b8847c326b0b3c',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../theseer/tokenizer',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
20
vendor/myclabs/deep-copy/LICENSE
vendored
Normal file
20
vendor/myclabs/deep-copy/LICENSE
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013 My C-Sense
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
406
vendor/myclabs/deep-copy/README.md
vendored
Normal file
406
vendor/myclabs/deep-copy/README.md
vendored
Normal file
@@ -0,0 +1,406 @@
|
|||||||
|
# DeepCopy
|
||||||
|
|
||||||
|
DeepCopy helps you create deep copies (clones) of your objects. It is designed to handle cycles in the association graph.
|
||||||
|
|
||||||
|
[](https://packagist.org/packages/myclabs/deep-copy)
|
||||||
|
[](https://github.com/myclabs/DeepCopy/actions/workflows/ci.yaml)
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
1. [How](#how)
|
||||||
|
1. [Why](#why)
|
||||||
|
1. [Using simply `clone`](#using-simply-clone)
|
||||||
|
1. [Overriding `__clone()`](#overriding-__clone)
|
||||||
|
1. [With `DeepCopy`](#with-deepcopy)
|
||||||
|
1. [How it works](#how-it-works)
|
||||||
|
1. [Going further](#going-further)
|
||||||
|
1. [Matchers](#matchers)
|
||||||
|
1. [Property name](#property-name)
|
||||||
|
1. [Specific property](#specific-property)
|
||||||
|
1. [Type](#type)
|
||||||
|
1. [Filters](#filters)
|
||||||
|
1. [`SetNullFilter`](#setnullfilter-filter)
|
||||||
|
1. [`KeepFilter`](#keepfilter-filter)
|
||||||
|
1. [`DoctrineCollectionFilter`](#doctrinecollectionfilter-filter)
|
||||||
|
1. [`DoctrineEmptyCollectionFilter`](#doctrineemptycollectionfilter-filter)
|
||||||
|
1. [`DoctrineProxyFilter`](#doctrineproxyfilter-filter)
|
||||||
|
1. [`ReplaceFilter`](#replacefilter-type-filter)
|
||||||
|
1. [`ShallowCopyFilter`](#shallowcopyfilter-type-filter)
|
||||||
|
1. [Edge cases](#edge-cases)
|
||||||
|
1. [Contributing](#contributing)
|
||||||
|
1. [Tests](#tests)
|
||||||
|
|
||||||
|
|
||||||
|
## How?
|
||||||
|
|
||||||
|
Install with Composer:
|
||||||
|
|
||||||
|
```
|
||||||
|
composer require myclabs/deep-copy
|
||||||
|
```
|
||||||
|
|
||||||
|
Use it:
|
||||||
|
|
||||||
|
```php
|
||||||
|
use DeepCopy\DeepCopy;
|
||||||
|
|
||||||
|
$copier = new DeepCopy();
|
||||||
|
$myCopy = $copier->copy($myObject);
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Why?
|
||||||
|
|
||||||
|
- How do you create copies of your objects?
|
||||||
|
|
||||||
|
```php
|
||||||
|
$myCopy = clone $myObject;
|
||||||
|
```
|
||||||
|
|
||||||
|
- How do you create **deep** copies of your objects (i.e. copying also all the objects referenced in the properties)?
|
||||||
|
|
||||||
|
You use [`__clone()`](http://www.php.net/manual/en/language.oop5.cloning.php#object.clone) and implement the behavior
|
||||||
|
yourself.
|
||||||
|
|
||||||
|
- But how do you handle **cycles** in the association graph?
|
||||||
|
|
||||||
|
Now you're in for a big mess :(
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
### Using simply `clone`
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
### Overriding `__clone()`
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
### With `DeepCopy`
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
## How it works
|
||||||
|
|
||||||
|
DeepCopy recursively traverses all the object's properties and clones them. To avoid cloning the same object twice it
|
||||||
|
keeps a hash map of all instances and thus preserves the object graph.
|
||||||
|
|
||||||
|
To use it:
|
||||||
|
|
||||||
|
```php
|
||||||
|
use function DeepCopy\deep_copy;
|
||||||
|
|
||||||
|
$copy = deep_copy($var);
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, you can create your own `DeepCopy` instance to configure it differently for example:
|
||||||
|
|
||||||
|
```php
|
||||||
|
use DeepCopy\DeepCopy;
|
||||||
|
|
||||||
|
$copier = new DeepCopy(true);
|
||||||
|
|
||||||
|
$copy = $copier->copy($var);
|
||||||
|
```
|
||||||
|
|
||||||
|
You may want to roll your own deep copy function:
|
||||||
|
|
||||||
|
```php
|
||||||
|
namespace Acme;
|
||||||
|
|
||||||
|
use DeepCopy\DeepCopy;
|
||||||
|
|
||||||
|
function deep_copy($var)
|
||||||
|
{
|
||||||
|
static $copier = null;
|
||||||
|
|
||||||
|
if (null === $copier) {
|
||||||
|
$copier = new DeepCopy(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $copier->copy($var);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Going further
|
||||||
|
|
||||||
|
You can add filters to customize the copy process.
|
||||||
|
|
||||||
|
The method to add a filter is `DeepCopy\DeepCopy::addFilter($filter, $matcher)`,
|
||||||
|
with `$filter` implementing `DeepCopy\Filter\Filter`
|
||||||
|
and `$matcher` implementing `DeepCopy\Matcher\Matcher`.
|
||||||
|
|
||||||
|
We provide some generic filters and matchers.
|
||||||
|
|
||||||
|
|
||||||
|
### Matchers
|
||||||
|
|
||||||
|
- `DeepCopy\Matcher` applies on a object attribute.
|
||||||
|
- `DeepCopy\TypeMatcher` applies on any element found in graph, including array elements.
|
||||||
|
|
||||||
|
|
||||||
|
#### Property name
|
||||||
|
|
||||||
|
The `PropertyNameMatcher` will match a property by its name:
|
||||||
|
|
||||||
|
```php
|
||||||
|
use DeepCopy\Matcher\PropertyNameMatcher;
|
||||||
|
|
||||||
|
// Will apply a filter to any property of any objects named "id"
|
||||||
|
$matcher = new PropertyNameMatcher('id');
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
#### Specific property
|
||||||
|
|
||||||
|
The `PropertyMatcher` will match a specific property of a specific class:
|
||||||
|
|
||||||
|
```php
|
||||||
|
use DeepCopy\Matcher\PropertyMatcher;
|
||||||
|
|
||||||
|
// Will apply a filter to the property "id" of any objects of the class "MyClass"
|
||||||
|
$matcher = new PropertyMatcher('MyClass', 'id');
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
#### Type
|
||||||
|
|
||||||
|
The `TypeMatcher` will match any element by its type (instance of a class or any value that could be parameter of
|
||||||
|
[gettype()](http://php.net/manual/en/function.gettype.php) function):
|
||||||
|
|
||||||
|
```php
|
||||||
|
use DeepCopy\TypeMatcher\TypeMatcher;
|
||||||
|
|
||||||
|
// Will apply a filter to any object that is an instance of Doctrine\Common\Collections\Collection
|
||||||
|
$matcher = new TypeMatcher('Doctrine\Common\Collections\Collection');
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Filters
|
||||||
|
|
||||||
|
- `DeepCopy\Filter` applies a transformation to the object attribute matched by `DeepCopy\Matcher`
|
||||||
|
- `DeepCopy\TypeFilter` applies a transformation to any element matched by `DeepCopy\TypeMatcher`
|
||||||
|
|
||||||
|
By design, matching a filter will stop the chain of filters (i.e. the next ones will not be applied).
|
||||||
|
Using the ([`ChainableFilter`](#chainablefilter-filter)) won't stop the chain of filters.
|
||||||
|
|
||||||
|
|
||||||
|
#### `SetNullFilter` (filter)
|
||||||
|
|
||||||
|
Let's say for example that you are copying a database record (or a Doctrine entity), so you want the copy not to have
|
||||||
|
any ID:
|
||||||
|
|
||||||
|
```php
|
||||||
|
use DeepCopy\DeepCopy;
|
||||||
|
use DeepCopy\Filter\SetNullFilter;
|
||||||
|
use DeepCopy\Matcher\PropertyNameMatcher;
|
||||||
|
|
||||||
|
$object = MyClass::load(123);
|
||||||
|
echo $object->id; // 123
|
||||||
|
|
||||||
|
$copier = new DeepCopy();
|
||||||
|
$copier->addFilter(new SetNullFilter(), new PropertyNameMatcher('id'));
|
||||||
|
|
||||||
|
$copy = $copier->copy($object);
|
||||||
|
|
||||||
|
echo $copy->id; // null
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
#### `KeepFilter` (filter)
|
||||||
|
|
||||||
|
If you want a property to remain untouched (for example, an association to an object):
|
||||||
|
|
||||||
|
```php
|
||||||
|
use DeepCopy\DeepCopy;
|
||||||
|
use DeepCopy\Filter\KeepFilter;
|
||||||
|
use DeepCopy\Matcher\PropertyMatcher;
|
||||||
|
|
||||||
|
$copier = new DeepCopy();
|
||||||
|
$copier->addFilter(new KeepFilter(), new PropertyMatcher('MyClass', 'category'));
|
||||||
|
|
||||||
|
$copy = $copier->copy($object);
|
||||||
|
// $copy->category has not been touched
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
#### `ChainableFilter` (filter)
|
||||||
|
|
||||||
|
If you use cloning on proxy classes, you might want to apply two filters for:
|
||||||
|
1. loading the data
|
||||||
|
2. applying a transformation
|
||||||
|
|
||||||
|
You can use the `ChainableFilter` as a decorator of the proxy loader filter, which won't stop the chain of filters (i.e.
|
||||||
|
the next ones may be applied).
|
||||||
|
|
||||||
|
|
||||||
|
```php
|
||||||
|
use DeepCopy\DeepCopy;
|
||||||
|
use DeepCopy\Filter\ChainableFilter;
|
||||||
|
use DeepCopy\Filter\Doctrine\DoctrineProxyFilter;
|
||||||
|
use DeepCopy\Filter\SetNullFilter;
|
||||||
|
use DeepCopy\Matcher\Doctrine\DoctrineProxyMatcher;
|
||||||
|
use DeepCopy\Matcher\PropertyNameMatcher;
|
||||||
|
|
||||||
|
$copier = new DeepCopy();
|
||||||
|
$copier->addFilter(new ChainableFilter(new DoctrineProxyFilter()), new DoctrineProxyMatcher());
|
||||||
|
$copier->addFilter(new SetNullFilter(), new PropertyNameMatcher('id'));
|
||||||
|
|
||||||
|
$copy = $copier->copy($object);
|
||||||
|
|
||||||
|
echo $copy->id; // null
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
#### `DoctrineCollectionFilter` (filter)
|
||||||
|
|
||||||
|
If you use Doctrine and want to copy an entity, you will need to use the `DoctrineCollectionFilter`:
|
||||||
|
|
||||||
|
```php
|
||||||
|
use DeepCopy\DeepCopy;
|
||||||
|
use DeepCopy\Filter\Doctrine\DoctrineCollectionFilter;
|
||||||
|
use DeepCopy\Matcher\PropertyTypeMatcher;
|
||||||
|
|
||||||
|
$copier = new DeepCopy();
|
||||||
|
$copier->addFilter(new DoctrineCollectionFilter(), new PropertyTypeMatcher('Doctrine\Common\Collections\Collection'));
|
||||||
|
|
||||||
|
$copy = $copier->copy($object);
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
#### `DoctrineEmptyCollectionFilter` (filter)
|
||||||
|
|
||||||
|
If you use Doctrine and want to copy an entity who contains a `Collection` that you want to be reset, you can use the
|
||||||
|
`DoctrineEmptyCollectionFilter`
|
||||||
|
|
||||||
|
```php
|
||||||
|
use DeepCopy\DeepCopy;
|
||||||
|
use DeepCopy\Filter\Doctrine\DoctrineEmptyCollectionFilter;
|
||||||
|
use DeepCopy\Matcher\PropertyMatcher;
|
||||||
|
|
||||||
|
$copier = new DeepCopy();
|
||||||
|
$copier->addFilter(new DoctrineEmptyCollectionFilter(), new PropertyMatcher('MyClass', 'myProperty'));
|
||||||
|
|
||||||
|
$copy = $copier->copy($object);
|
||||||
|
|
||||||
|
// $copy->myProperty will return an empty collection
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
#### `DoctrineProxyFilter` (filter)
|
||||||
|
|
||||||
|
If you use Doctrine and use cloning on lazy loaded entities, you might encounter errors mentioning missing fields on a
|
||||||
|
Doctrine proxy class (...\\\_\_CG\_\_\Proxy).
|
||||||
|
You can use the `DoctrineProxyFilter` to load the actual entity behind the Doctrine proxy class.
|
||||||
|
**Make sure, though, to put this as one of your very first filters in the filter chain so that the entity is loaded
|
||||||
|
before other filters are applied!**
|
||||||
|
We recommend to decorate the `DoctrineProxyFilter` with the `ChainableFilter` to allow applying other filters to the
|
||||||
|
cloned lazy loaded entities.
|
||||||
|
|
||||||
|
```php
|
||||||
|
use DeepCopy\DeepCopy;
|
||||||
|
use DeepCopy\Filter\Doctrine\DoctrineProxyFilter;
|
||||||
|
use DeepCopy\Matcher\Doctrine\DoctrineProxyMatcher;
|
||||||
|
|
||||||
|
$copier = new DeepCopy();
|
||||||
|
$copier->addFilter(new ChainableFilter(new DoctrineProxyFilter()), new DoctrineProxyMatcher());
|
||||||
|
|
||||||
|
$copy = $copier->copy($object);
|
||||||
|
|
||||||
|
// $copy should now contain a clone of all entities, including those that were not yet fully loaded.
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
#### `ReplaceFilter` (type filter)
|
||||||
|
|
||||||
|
1. If you want to replace the value of a property:
|
||||||
|
|
||||||
|
```php
|
||||||
|
use DeepCopy\DeepCopy;
|
||||||
|
use DeepCopy\Filter\ReplaceFilter;
|
||||||
|
use DeepCopy\Matcher\PropertyMatcher;
|
||||||
|
|
||||||
|
$copier = new DeepCopy();
|
||||||
|
$callback = function ($currentValue) {
|
||||||
|
return $currentValue . ' (copy)'
|
||||||
|
};
|
||||||
|
$copier->addFilter(new ReplaceFilter($callback), new PropertyMatcher('MyClass', 'title'));
|
||||||
|
|
||||||
|
$copy = $copier->copy($object);
|
||||||
|
|
||||||
|
// $copy->title will contain the data returned by the callback, e.g. 'The title (copy)'
|
||||||
|
```
|
||||||
|
|
||||||
|
2. If you want to replace whole element:
|
||||||
|
|
||||||
|
```php
|
||||||
|
use DeepCopy\DeepCopy;
|
||||||
|
use DeepCopy\TypeFilter\ReplaceFilter;
|
||||||
|
use DeepCopy\TypeMatcher\TypeMatcher;
|
||||||
|
|
||||||
|
$copier = new DeepCopy();
|
||||||
|
$callback = function (MyClass $myClass) {
|
||||||
|
return get_class($myClass);
|
||||||
|
};
|
||||||
|
$copier->addTypeFilter(new ReplaceFilter($callback), new TypeMatcher('MyClass'));
|
||||||
|
|
||||||
|
$copy = $copier->copy([new MyClass, 'some string', new MyClass]);
|
||||||
|
|
||||||
|
// $copy will contain ['MyClass', 'some string', 'MyClass']
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
The `$callback` parameter of the `ReplaceFilter` constructor accepts any PHP callable.
|
||||||
|
|
||||||
|
|
||||||
|
#### `ShallowCopyFilter` (type filter)
|
||||||
|
|
||||||
|
Stop *DeepCopy* from recursively copying element, using standard `clone` instead:
|
||||||
|
|
||||||
|
```php
|
||||||
|
use DeepCopy\DeepCopy;
|
||||||
|
use DeepCopy\TypeFilter\ShallowCopyFilter;
|
||||||
|
use DeepCopy\TypeMatcher\TypeMatcher;
|
||||||
|
use Mockery as m;
|
||||||
|
|
||||||
|
$this->deepCopy = new DeepCopy();
|
||||||
|
$this->deepCopy->addTypeFilter(
|
||||||
|
new ShallowCopyFilter,
|
||||||
|
new TypeMatcher(m\MockInterface::class)
|
||||||
|
);
|
||||||
|
|
||||||
|
$myServiceWithMocks = new MyService(m::mock(MyDependency1::class), m::mock(MyDependency2::class));
|
||||||
|
// All mocks will be just cloned, not deep copied
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Edge cases
|
||||||
|
|
||||||
|
The following structures cannot be deep-copied with PHP Reflection. As a result they are shallow cloned and filters are
|
||||||
|
not applied. There is two ways for you to handle them:
|
||||||
|
|
||||||
|
- Implement your own `__clone()` method
|
||||||
|
- Use a filter with a type matcher
|
||||||
|
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
DeepCopy is distributed under the MIT license.
|
||||||
|
|
||||||
|
|
||||||
|
### Tests
|
||||||
|
|
||||||
|
Running the tests is simple:
|
||||||
|
|
||||||
|
```php
|
||||||
|
vendor/bin/phpunit
|
||||||
|
```
|
||||||
|
|
||||||
|
### Support
|
||||||
|
|
||||||
|
Get professional support via [the Tidelift Subscription](https://tidelift.com/subscription/pkg/packagist-myclabs-deep-copy?utm_source=packagist-myclabs-deep-copy&utm_medium=referral&utm_campaign=readme).
|
||||||
43
vendor/myclabs/deep-copy/composer.json
vendored
Normal file
43
vendor/myclabs/deep-copy/composer.json
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"name": "myclabs/deep-copy",
|
||||||
|
"description": "Create deep copies (clones) of your objects",
|
||||||
|
"license": "MIT",
|
||||||
|
"type": "library",
|
||||||
|
"keywords": [
|
||||||
|
"clone",
|
||||||
|
"copy",
|
||||||
|
"duplicate",
|
||||||
|
"object",
|
||||||
|
"object graph"
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"php": "^7.1 || ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"doctrine/collections": "^1.6.8",
|
||||||
|
"doctrine/common": "^2.13.3 || ^3.2.2",
|
||||||
|
"phpspec/prophecy": "^1.10",
|
||||||
|
"phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
|
||||||
|
},
|
||||||
|
"conflict": {
|
||||||
|
"doctrine/collections": "<1.6.8",
|
||||||
|
"doctrine/common": "<2.13.3 || >=3 <3.2.2"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"DeepCopy\\": "src/DeepCopy/"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"src/DeepCopy/deep_copy.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-4": {
|
||||||
|
"DeepCopyTest\\": "tests/DeepCopyTest/",
|
||||||
|
"DeepCopy\\": "fixtures/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"sort-packages": true
|
||||||
|
}
|
||||||
|
}
|
||||||
328
vendor/myclabs/deep-copy/src/DeepCopy/DeepCopy.php
vendored
Normal file
328
vendor/myclabs/deep-copy/src/DeepCopy/DeepCopy.php
vendored
Normal file
@@ -0,0 +1,328 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace DeepCopy;
|
||||||
|
|
||||||
|
use ArrayObject;
|
||||||
|
use DateInterval;
|
||||||
|
use DatePeriod;
|
||||||
|
use DateTimeInterface;
|
||||||
|
use DateTimeZone;
|
||||||
|
use DeepCopy\Exception\CloneException;
|
||||||
|
use DeepCopy\Filter\ChainableFilter;
|
||||||
|
use DeepCopy\Filter\Filter;
|
||||||
|
use DeepCopy\Matcher\Matcher;
|
||||||
|
use DeepCopy\Reflection\ReflectionHelper;
|
||||||
|
use DeepCopy\TypeFilter\Date\DateIntervalFilter;
|
||||||
|
use DeepCopy\TypeFilter\Date\DatePeriodFilter;
|
||||||
|
use DeepCopy\TypeFilter\Spl\ArrayObjectFilter;
|
||||||
|
use DeepCopy\TypeFilter\Spl\SplDoublyLinkedListFilter;
|
||||||
|
use DeepCopy\TypeFilter\TypeFilter;
|
||||||
|
use DeepCopy\TypeMatcher\TypeMatcher;
|
||||||
|
use ReflectionObject;
|
||||||
|
use ReflectionProperty;
|
||||||
|
use SplDoublyLinkedList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @final
|
||||||
|
*/
|
||||||
|
class DeepCopy
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var object[] List of objects copied.
|
||||||
|
*/
|
||||||
|
private $hashMap = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters to apply.
|
||||||
|
*
|
||||||
|
* @var array Array of ['filter' => Filter, 'matcher' => Matcher] pairs.
|
||||||
|
*/
|
||||||
|
private $filters = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type Filters to apply.
|
||||||
|
*
|
||||||
|
* @var array Array of ['filter' => Filter, 'matcher' => Matcher] pairs.
|
||||||
|
*/
|
||||||
|
private $typeFilters = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $skipUncloneable = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $useCloneMethod;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool $useCloneMethod If set to true, when an object implements the __clone() function, it will be used
|
||||||
|
* instead of the regular deep cloning.
|
||||||
|
*/
|
||||||
|
public function __construct($useCloneMethod = false)
|
||||||
|
{
|
||||||
|
$this->useCloneMethod = $useCloneMethod;
|
||||||
|
|
||||||
|
$this->addTypeFilter(new ArrayObjectFilter($this), new TypeMatcher(ArrayObject::class));
|
||||||
|
$this->addTypeFilter(new DateIntervalFilter(), new TypeMatcher(DateInterval::class));
|
||||||
|
$this->addTypeFilter(new DatePeriodFilter(), new TypeMatcher(DatePeriod::class));
|
||||||
|
$this->addTypeFilter(new SplDoublyLinkedListFilter($this), new TypeMatcher(SplDoublyLinkedList::class));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If enabled, will not throw an exception when coming across an uncloneable property.
|
||||||
|
*
|
||||||
|
* @param $skipUncloneable
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function skipUncloneable($skipUncloneable = true)
|
||||||
|
{
|
||||||
|
$this->skipUncloneable = $skipUncloneable;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deep copies the given object.
|
||||||
|
*
|
||||||
|
* @template TObject
|
||||||
|
*
|
||||||
|
* @param TObject $object
|
||||||
|
*
|
||||||
|
* @return TObject
|
||||||
|
*/
|
||||||
|
public function copy($object)
|
||||||
|
{
|
||||||
|
$this->hashMap = [];
|
||||||
|
|
||||||
|
return $this->recursiveCopy($object);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addFilter(Filter $filter, Matcher $matcher)
|
||||||
|
{
|
||||||
|
$this->filters[] = [
|
||||||
|
'matcher' => $matcher,
|
||||||
|
'filter' => $filter,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function prependFilter(Filter $filter, Matcher $matcher)
|
||||||
|
{
|
||||||
|
array_unshift($this->filters, [
|
||||||
|
'matcher' => $matcher,
|
||||||
|
'filter' => $filter,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addTypeFilter(TypeFilter $filter, TypeMatcher $matcher)
|
||||||
|
{
|
||||||
|
$this->typeFilters[] = [
|
||||||
|
'matcher' => $matcher,
|
||||||
|
'filter' => $filter,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function prependTypeFilter(TypeFilter $filter, TypeMatcher $matcher)
|
||||||
|
{
|
||||||
|
array_unshift($this->typeFilters, [
|
||||||
|
'matcher' => $matcher,
|
||||||
|
'filter' => $filter,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function recursiveCopy($var)
|
||||||
|
{
|
||||||
|
// Matches Type Filter
|
||||||
|
if ($filter = $this->getFirstMatchedTypeFilter($this->typeFilters, $var)) {
|
||||||
|
return $filter->apply($var);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resource
|
||||||
|
if (is_resource($var)) {
|
||||||
|
return $var;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Array
|
||||||
|
if (is_array($var)) {
|
||||||
|
return $this->copyArray($var);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scalar
|
||||||
|
if (! is_object($var)) {
|
||||||
|
return $var;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enum
|
||||||
|
if (PHP_VERSION_ID >= 80100 && enum_exists(get_class($var))) {
|
||||||
|
return $var;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Object
|
||||||
|
return $this->copyObject($var);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy an array
|
||||||
|
* @param array $array
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function copyArray(array $array)
|
||||||
|
{
|
||||||
|
foreach ($array as $key => $value) {
|
||||||
|
$array[$key] = $this->recursiveCopy($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies an object.
|
||||||
|
*
|
||||||
|
* @param object $object
|
||||||
|
*
|
||||||
|
* @throws CloneException
|
||||||
|
*
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
private function copyObject($object)
|
||||||
|
{
|
||||||
|
$objectHash = spl_object_hash($object);
|
||||||
|
|
||||||
|
if (isset($this->hashMap[$objectHash])) {
|
||||||
|
return $this->hashMap[$objectHash];
|
||||||
|
}
|
||||||
|
|
||||||
|
$reflectedObject = new ReflectionObject($object);
|
||||||
|
$isCloneable = $reflectedObject->isCloneable();
|
||||||
|
|
||||||
|
if (false === $isCloneable) {
|
||||||
|
if ($this->skipUncloneable) {
|
||||||
|
$this->hashMap[$objectHash] = $object;
|
||||||
|
|
||||||
|
return $object;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new CloneException(
|
||||||
|
sprintf(
|
||||||
|
'The class "%s" is not cloneable.',
|
||||||
|
$reflectedObject->getName()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$newObject = clone $object;
|
||||||
|
$this->hashMap[$objectHash] = $newObject;
|
||||||
|
|
||||||
|
if ($this->useCloneMethod && $reflectedObject->hasMethod('__clone')) {
|
||||||
|
return $newObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($newObject instanceof DateTimeInterface || $newObject instanceof DateTimeZone) {
|
||||||
|
return $newObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (ReflectionHelper::getProperties($reflectedObject) as $property) {
|
||||||
|
$this->copyObjectProperty($newObject, $property);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $newObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function copyObjectProperty($object, ReflectionProperty $property)
|
||||||
|
{
|
||||||
|
// Ignore static properties
|
||||||
|
if ($property->isStatic()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore readonly properties
|
||||||
|
if (method_exists($property, 'isReadOnly') && $property->isReadOnly()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the filters
|
||||||
|
foreach ($this->filters as $item) {
|
||||||
|
/** @var Matcher $matcher */
|
||||||
|
$matcher = $item['matcher'];
|
||||||
|
/** @var Filter $filter */
|
||||||
|
$filter = $item['filter'];
|
||||||
|
|
||||||
|
if ($matcher->matches($object, $property->getName())) {
|
||||||
|
$filter->apply(
|
||||||
|
$object,
|
||||||
|
$property->getName(),
|
||||||
|
function ($object) {
|
||||||
|
return $this->recursiveCopy($object);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($filter instanceof ChainableFilter) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a filter matches, we stop processing this property
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PHP_VERSION_ID < 80100) {
|
||||||
|
$property->setAccessible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore uninitialized properties (for PHP >7.4)
|
||||||
|
if (method_exists($property, 'isInitialized') && !$property->isInitialized($object)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$propertyValue = $property->getValue($object);
|
||||||
|
|
||||||
|
// Copy the property
|
||||||
|
$property->setValue($object, $this->recursiveCopy($propertyValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns first filter that matches variable, `null` if no such filter found.
|
||||||
|
*
|
||||||
|
* @param array $filterRecords Associative array with 2 members: 'filter' with value of type {@see TypeFilter} and
|
||||||
|
* 'matcher' with value of type {@see TypeMatcher}
|
||||||
|
* @param mixed $var
|
||||||
|
*
|
||||||
|
* @return TypeFilter|null
|
||||||
|
*/
|
||||||
|
private function getFirstMatchedTypeFilter(array $filterRecords, $var)
|
||||||
|
{
|
||||||
|
$matched = $this->first(
|
||||||
|
$filterRecords,
|
||||||
|
function (array $record) use ($var) {
|
||||||
|
/* @var TypeMatcher $matcher */
|
||||||
|
$matcher = $record['matcher'];
|
||||||
|
|
||||||
|
return $matcher->matches($var);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return isset($matched) ? $matched['filter'] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns first element that matches predicate, `null` if no such element found.
|
||||||
|
*
|
||||||
|
* @param array $elements Array of ['filter' => Filter, 'matcher' => Matcher] pairs.
|
||||||
|
* @param callable $predicate Predicate arguments are: element.
|
||||||
|
*
|
||||||
|
* @return array|null Associative array with 2 members: 'filter' with value of type {@see TypeFilter} and 'matcher'
|
||||||
|
* with value of type {@see TypeMatcher} or `null`.
|
||||||
|
*/
|
||||||
|
private function first(array $elements, callable $predicate)
|
||||||
|
{
|
||||||
|
foreach ($elements as $element) {
|
||||||
|
if (call_user_func($predicate, $element)) {
|
||||||
|
return $element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
9
vendor/myclabs/deep-copy/src/DeepCopy/Exception/CloneException.php
vendored
Normal file
9
vendor/myclabs/deep-copy/src/DeepCopy/Exception/CloneException.php
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace DeepCopy\Exception;
|
||||||
|
|
||||||
|
use UnexpectedValueException;
|
||||||
|
|
||||||
|
class CloneException extends UnexpectedValueException
|
||||||
|
{
|
||||||
|
}
|
||||||
9
vendor/myclabs/deep-copy/src/DeepCopy/Exception/PropertyException.php
vendored
Normal file
9
vendor/myclabs/deep-copy/src/DeepCopy/Exception/PropertyException.php
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace DeepCopy\Exception;
|
||||||
|
|
||||||
|
use ReflectionException;
|
||||||
|
|
||||||
|
class PropertyException extends ReflectionException
|
||||||
|
{
|
||||||
|
}
|
||||||
24
vendor/myclabs/deep-copy/src/DeepCopy/Filter/ChainableFilter.php
vendored
Normal file
24
vendor/myclabs/deep-copy/src/DeepCopy/Filter/ChainableFilter.php
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace DeepCopy\Filter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a decorator filter that will not stop the chain of filters.
|
||||||
|
*/
|
||||||
|
class ChainableFilter implements Filter
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Filter
|
||||||
|
*/
|
||||||
|
protected $filter;
|
||||||
|
|
||||||
|
public function __construct(Filter $filter)
|
||||||
|
{
|
||||||
|
$this->filter = $filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function apply($object, $property, $objectCopier)
|
||||||
|
{
|
||||||
|
$this->filter->apply($object, $property, $objectCopier);
|
||||||
|
}
|
||||||
|
}
|
||||||
35
vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.php
vendored
Normal file
35
vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.php
vendored
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace DeepCopy\Filter\Doctrine;
|
||||||
|
|
||||||
|
use DeepCopy\Filter\Filter;
|
||||||
|
use DeepCopy\Reflection\ReflectionHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @final
|
||||||
|
*/
|
||||||
|
class DoctrineCollectionFilter implements Filter
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Copies the object property doctrine collection.
|
||||||
|
*
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function apply($object, $property, $objectCopier)
|
||||||
|
{
|
||||||
|
$reflectionProperty = ReflectionHelper::getProperty($object, $property);
|
||||||
|
|
||||||
|
if (PHP_VERSION_ID < 80100) {
|
||||||
|
$reflectionProperty->setAccessible(true);
|
||||||
|
}
|
||||||
|
$oldCollection = $reflectionProperty->getValue($object);
|
||||||
|
|
||||||
|
$newCollection = $oldCollection->map(
|
||||||
|
function ($item) use ($objectCopier) {
|
||||||
|
return $objectCopier($item);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$reflectionProperty->setValue($object, $newCollection);
|
||||||
|
}
|
||||||
|
}
|
||||||
30
vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.php
vendored
Normal file
30
vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.php
vendored
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace DeepCopy\Filter\Doctrine;
|
||||||
|
|
||||||
|
use DeepCopy\Filter\Filter;
|
||||||
|
use DeepCopy\Reflection\ReflectionHelper;
|
||||||
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @final
|
||||||
|
*/
|
||||||
|
class DoctrineEmptyCollectionFilter implements Filter
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Sets the object property to an empty doctrine collection.
|
||||||
|
*
|
||||||
|
* @param object $object
|
||||||
|
* @param string $property
|
||||||
|
* @param callable $objectCopier
|
||||||
|
*/
|
||||||
|
public function apply($object, $property, $objectCopier)
|
||||||
|
{
|
||||||
|
$reflectionProperty = ReflectionHelper::getProperty($object, $property);
|
||||||
|
if (PHP_VERSION_ID < 80100) {
|
||||||
|
$reflectionProperty->setAccessible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$reflectionProperty->setValue($object, new ArrayCollection());
|
||||||
|
}
|
||||||
|
}
|
||||||
22
vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.php
vendored
Normal file
22
vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace DeepCopy\Filter\Doctrine;
|
||||||
|
|
||||||
|
use DeepCopy\Filter\Filter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @final
|
||||||
|
*/
|
||||||
|
class DoctrineProxyFilter implements Filter
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Triggers the magic method __load() on a Doctrine Proxy class to load the
|
||||||
|
* actual entity from the database.
|
||||||
|
*
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function apply($object, $property, $objectCopier)
|
||||||
|
{
|
||||||
|
$object->__load();
|
||||||
|
}
|
||||||
|
}
|
||||||
18
vendor/myclabs/deep-copy/src/DeepCopy/Filter/Filter.php
vendored
Normal file
18
vendor/myclabs/deep-copy/src/DeepCopy/Filter/Filter.php
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace DeepCopy\Filter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter to apply to a property while copying an object
|
||||||
|
*/
|
||||||
|
interface Filter
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Applies the filter to the object.
|
||||||
|
*
|
||||||
|
* @param object $object
|
||||||
|
* @param string $property
|
||||||
|
* @param callable $objectCopier
|
||||||
|
*/
|
||||||
|
public function apply($object, $property, $objectCopier);
|
||||||
|
}
|
||||||
16
vendor/myclabs/deep-copy/src/DeepCopy/Filter/KeepFilter.php
vendored
Normal file
16
vendor/myclabs/deep-copy/src/DeepCopy/Filter/KeepFilter.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace DeepCopy\Filter;
|
||||||
|
|
||||||
|
class KeepFilter implements Filter
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Keeps the value of the object property.
|
||||||
|
*
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function apply($object, $property, $objectCopier)
|
||||||
|
{
|
||||||
|
// Nothing to do
|
||||||
|
}
|
||||||
|
}
|
||||||
41
vendor/myclabs/deep-copy/src/DeepCopy/Filter/ReplaceFilter.php
vendored
Normal file
41
vendor/myclabs/deep-copy/src/DeepCopy/Filter/ReplaceFilter.php
vendored
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace DeepCopy\Filter;
|
||||||
|
|
||||||
|
use DeepCopy\Reflection\ReflectionHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @final
|
||||||
|
*/
|
||||||
|
class ReplaceFilter implements Filter
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var callable
|
||||||
|
*/
|
||||||
|
protected $callback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param callable $callable Will be called to get the new value for each property to replace
|
||||||
|
*/
|
||||||
|
public function __construct(callable $callable)
|
||||||
|
{
|
||||||
|
$this->callback = $callable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces the object property by the result of the callback called with the object property.
|
||||||
|
*
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function apply($object, $property, $objectCopier)
|
||||||
|
{
|
||||||
|
$reflectionProperty = ReflectionHelper::getProperty($object, $property);
|
||||||
|
if (PHP_VERSION_ID < 80100) {
|
||||||
|
$reflectionProperty->setAccessible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = call_user_func($this->callback, $reflectionProperty->getValue($object));
|
||||||
|
|
||||||
|
$reflectionProperty->setValue($object, $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
26
vendor/myclabs/deep-copy/src/DeepCopy/Filter/SetNullFilter.php
vendored
Normal file
26
vendor/myclabs/deep-copy/src/DeepCopy/Filter/SetNullFilter.php
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace DeepCopy\Filter;
|
||||||
|
|
||||||
|
use DeepCopy\Reflection\ReflectionHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @final
|
||||||
|
*/
|
||||||
|
class SetNullFilter implements Filter
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Sets the object property to null.
|
||||||
|
*
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function apply($object, $property, $objectCopier)
|
||||||
|
{
|
||||||
|
$reflectionProperty = ReflectionHelper::getProperty($object, $property);
|
||||||
|
|
||||||
|
if (PHP_VERSION_ID < 80100) {
|
||||||
|
$reflectionProperty->setAccessible(true);
|
||||||
|
}
|
||||||
|
$reflectionProperty->setValue($object, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
22
vendor/myclabs/deep-copy/src/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.php
vendored
Normal file
22
vendor/myclabs/deep-copy/src/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace DeepCopy\Matcher\Doctrine;
|
||||||
|
|
||||||
|
use DeepCopy\Matcher\Matcher;
|
||||||
|
use Doctrine\Persistence\Proxy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @final
|
||||||
|
*/
|
||||||
|
class DoctrineProxyMatcher implements Matcher
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Matches a Doctrine Proxy class.
|
||||||
|
*
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function matches($object, $property)
|
||||||
|
{
|
||||||
|
return $object instanceof Proxy;
|
||||||
|
}
|
||||||
|
}
|
||||||
14
vendor/myclabs/deep-copy/src/DeepCopy/Matcher/Matcher.php
vendored
Normal file
14
vendor/myclabs/deep-copy/src/DeepCopy/Matcher/Matcher.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace DeepCopy\Matcher;
|
||||||
|
|
||||||
|
interface Matcher
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param object $object
|
||||||
|
* @param string $property
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function matches($object, $property);
|
||||||
|
}
|
||||||
39
vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyMatcher.php
vendored
Normal file
39
vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyMatcher.php
vendored
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace DeepCopy\Matcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @final
|
||||||
|
*/
|
||||||
|
class PropertyMatcher implements Matcher
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $property;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $class Class name
|
||||||
|
* @param string $property Property name
|
||||||
|
*/
|
||||||
|
public function __construct($class, $property)
|
||||||
|
{
|
||||||
|
$this->class = $class;
|
||||||
|
$this->property = $property;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matches a specific property of a specific class.
|
||||||
|
*
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function matches($object, $property)
|
||||||
|
{
|
||||||
|
return ($object instanceof $this->class) && $property == $this->property;
|
||||||
|
}
|
||||||
|
}
|
||||||
32
vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyNameMatcher.php
vendored
Normal file
32
vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyNameMatcher.php
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace DeepCopy\Matcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @final
|
||||||
|
*/
|
||||||
|
class PropertyNameMatcher implements Matcher
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $property;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $property Property name
|
||||||
|
*/
|
||||||
|
public function __construct($property)
|
||||||
|
{
|
||||||
|
$this->property = $property;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matches a property by its name.
|
||||||
|
*
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function matches($object, $property)
|
||||||
|
{
|
||||||
|
return $property == $this->property;
|
||||||
|
}
|
||||||
|
}
|
||||||
54
vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyTypeMatcher.php
vendored
Normal file
54
vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyTypeMatcher.php
vendored
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace DeepCopy\Matcher;
|
||||||
|
|
||||||
|
use DeepCopy\Reflection\ReflectionHelper;
|
||||||
|
use ReflectionException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matches a property by its type.
|
||||||
|
*
|
||||||
|
* It is recommended to use {@see DeepCopy\TypeFilter\TypeFilter} instead, as it applies on all occurrences
|
||||||
|
* of given type in copied context (eg. array elements), not just on object properties.
|
||||||
|
*
|
||||||
|
* @final
|
||||||
|
*/
|
||||||
|
class PropertyTypeMatcher implements Matcher
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $propertyType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $propertyType Property type
|
||||||
|
*/
|
||||||
|
public function __construct($propertyType)
|
||||||
|
{
|
||||||
|
$this->propertyType = $propertyType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function matches($object, $property)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$reflectionProperty = ReflectionHelper::getProperty($object, $property);
|
||||||
|
} catch (ReflectionException $exception) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PHP_VERSION_ID < 80100) {
|
||||||
|
$reflectionProperty->setAccessible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uninitialized properties (for PHP >7.4)
|
||||||
|
if (method_exists($reflectionProperty, 'isInitialized') && !$reflectionProperty->isInitialized($object)) {
|
||||||
|
// null instanceof $this->propertyType
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $reflectionProperty->getValue($object) instanceof $this->propertyType;
|
||||||
|
}
|
||||||
|
}
|
||||||
78
vendor/myclabs/deep-copy/src/DeepCopy/Reflection/ReflectionHelper.php
vendored
Normal file
78
vendor/myclabs/deep-copy/src/DeepCopy/Reflection/ReflectionHelper.php
vendored
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace DeepCopy\Reflection;
|
||||||
|
|
||||||
|
use DeepCopy\Exception\PropertyException;
|
||||||
|
use ReflectionClass;
|
||||||
|
use ReflectionException;
|
||||||
|
use ReflectionObject;
|
||||||
|
use ReflectionProperty;
|
||||||
|
|
||||||
|
class ReflectionHelper
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Retrieves all properties (including private ones), from object and all its ancestors.
|
||||||
|
*
|
||||||
|
* Standard \ReflectionClass->getProperties() does not return private properties from ancestor classes.
|
||||||
|
*
|
||||||
|
* @author muratyaman@gmail.com
|
||||||
|
* @see http://php.net/manual/en/reflectionclass.getproperties.php
|
||||||
|
*
|
||||||
|
* @param ReflectionClass $ref
|
||||||
|
*
|
||||||
|
* @return ReflectionProperty[]
|
||||||
|
*/
|
||||||
|
public static function getProperties(ReflectionClass $ref)
|
||||||
|
{
|
||||||
|
$props = $ref->getProperties();
|
||||||
|
$propsArr = array();
|
||||||
|
|
||||||
|
foreach ($props as $prop) {
|
||||||
|
$propertyName = $prop->getName();
|
||||||
|
$propsArr[$propertyName] = $prop;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($parentClass = $ref->getParentClass()) {
|
||||||
|
$parentPropsArr = self::getProperties($parentClass);
|
||||||
|
foreach ($propsArr as $key => $property) {
|
||||||
|
$parentPropsArr[$key] = $property;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $parentPropsArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $propsArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves property by name from object and all its ancestors.
|
||||||
|
*
|
||||||
|
* @param object|string $object
|
||||||
|
* @param string $name
|
||||||
|
*
|
||||||
|
* @throws PropertyException
|
||||||
|
* @throws ReflectionException
|
||||||
|
*
|
||||||
|
* @return ReflectionProperty
|
||||||
|
*/
|
||||||
|
public static function getProperty($object, $name)
|
||||||
|
{
|
||||||
|
$reflection = is_object($object) ? new ReflectionObject($object) : new ReflectionClass($object);
|
||||||
|
|
||||||
|
if ($reflection->hasProperty($name)) {
|
||||||
|
return $reflection->getProperty($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($parentClass = $reflection->getParentClass()) {
|
||||||
|
return self::getProperty($parentClass->getName(), $name);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new PropertyException(
|
||||||
|
sprintf(
|
||||||
|
'The class "%s" doesn\'t have a property with the given name: "%s".',
|
||||||
|
is_object($object) ? get_class($object) : $object,
|
||||||
|
$name
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
33
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Date/DateIntervalFilter.php
vendored
Normal file
33
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Date/DateIntervalFilter.php
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace DeepCopy\TypeFilter\Date;
|
||||||
|
|
||||||
|
use DateInterval;
|
||||||
|
use DeepCopy\TypeFilter\TypeFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @final
|
||||||
|
*
|
||||||
|
* @deprecated Will be removed in 2.0. This filter will no longer be necessary in PHP 7.1+.
|
||||||
|
*/
|
||||||
|
class DateIntervalFilter implements TypeFilter
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @param DateInterval $element
|
||||||
|
*
|
||||||
|
* @see http://news.php.net/php.bugs/205076
|
||||||
|
*/
|
||||||
|
public function apply($element)
|
||||||
|
{
|
||||||
|
$copy = new DateInterval('P0D');
|
||||||
|
|
||||||
|
foreach ($element as $propertyName => $propertyValue) {
|
||||||
|
$copy->{$propertyName} = $propertyValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $copy;
|
||||||
|
}
|
||||||
|
}
|
||||||
42
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Date/DatePeriodFilter.php
vendored
Normal file
42
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Date/DatePeriodFilter.php
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace DeepCopy\TypeFilter\Date;
|
||||||
|
|
||||||
|
use DatePeriod;
|
||||||
|
use DeepCopy\TypeFilter\TypeFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @final
|
||||||
|
*/
|
||||||
|
class DatePeriodFilter implements TypeFilter
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @param DatePeriod $element
|
||||||
|
*
|
||||||
|
* @see http://news.php.net/php.bugs/205076
|
||||||
|
*/
|
||||||
|
public function apply($element)
|
||||||
|
{
|
||||||
|
$options = 0;
|
||||||
|
if (PHP_VERSION_ID >= 80200 && $element->include_end_date) {
|
||||||
|
$options |= DatePeriod::INCLUDE_END_DATE;
|
||||||
|
}
|
||||||
|
if (!$element->include_start_date) {
|
||||||
|
$options |= DatePeriod::EXCLUDE_START_DATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($element->getEndDate()) {
|
||||||
|
return new DatePeriod($element->getStartDate(), $element->getDateInterval(), $element->getEndDate(), $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PHP_VERSION_ID >= 70217) {
|
||||||
|
$recurrences = $element->getRecurrences();
|
||||||
|
} else {
|
||||||
|
$recurrences = $element->recurrences - $element->include_start_date;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DatePeriod($element->getStartDate(), $element->getDateInterval(), $recurrences, $options);
|
||||||
|
}
|
||||||
|
}
|
||||||
30
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/ReplaceFilter.php
vendored
Normal file
30
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/ReplaceFilter.php
vendored
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace DeepCopy\TypeFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @final
|
||||||
|
*/
|
||||||
|
class ReplaceFilter implements TypeFilter
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var callable
|
||||||
|
*/
|
||||||
|
protected $callback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param callable $callable Will be called to get the new value for each element to replace
|
||||||
|
*/
|
||||||
|
public function __construct(callable $callable)
|
||||||
|
{
|
||||||
|
$this->callback = $callable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function apply($element)
|
||||||
|
{
|
||||||
|
return call_user_func($this->callback, $element);
|
||||||
|
}
|
||||||
|
}
|
||||||
17
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/ShallowCopyFilter.php
vendored
Normal file
17
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/ShallowCopyFilter.php
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace DeepCopy\TypeFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @final
|
||||||
|
*/
|
||||||
|
class ShallowCopyFilter implements TypeFilter
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function apply($element)
|
||||||
|
{
|
||||||
|
return clone $element;
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user