diff --git a/DATABASE_STRUCTURE.md b/DATABASE_STRUCTURE.md
index ca26eb5..cd858d0 100644
--- a/DATABASE_STRUCTURE.md
+++ b/DATABASE_STRUCTURE.md
@@ -92,7 +92,7 @@ Artykuły.
| id | PK |
| status | -1 = archiwum, 0 = nieaktywny, 1 = aktywny |
-**Używane w:** `admin\controls\ArticlesArchive`, `Domain\Article\ArticleRepository::find()`
+**Używane w:** `admin\Controllers\ArticlesArchiveController`, `Domain\Article\ArticleRepository::find()`, `Domain\Article\ArticleRepository::listArchivedForAdmin()`
## pp_articles_pages
Strony artykułów.
@@ -287,3 +287,31 @@ Szablony tresci e-maili (uzytkownik + administracyjne/systemowe).
**Uzywane w:** `Domain\\Newsletter\\NewsletterRepository`, `admin\\Controllers\\NewsletterController`, `front\\factory\\Newsletter`
**Aktualizacja 2026-02-12 (ver. 0.257):** modul `/admin/newsletter` korzysta z `Domain\\Newsletter\\NewsletterRepository` (DI kontroler + fasada legacy).
+
+## pp_scontainers
+Kontenery statyczne (modul /admin/scontainers).
+
+| Kolumna | Opis |
+|---------|------|
+| id | PK |
+| status | 1 = aktywny, 0 = nieaktywny |
+| show_title | 1 = pokaz tytul, 0 = ukryj tytul |
+
+**Uzywane w:** `Domain\Scontainers\ScontainersRepository`, `admin\Controllers\ScontainersController`, `front\factory\Scontainers`
+
+## pp_scontainers_langs
+Tlumaczenia kontenerow statycznych (per jezyk).
+
+| Kolumna | Opis |
+|---------|------|
+| id | PK |
+| container_id | FK do pp_scontainers |
+| lang_id | ID jezyka (np. pl, en) |
+| title | Tytul kontenera |
+| text | Tresc HTML kontenera |
+
+**Uzywane w:** `Domain\Scontainers\ScontainersRepository`, `front\factory\Scontainers`
+
+**Aktualizacja 2026-02-12 (ver. 0.259):** modul `/admin/scontainers` korzysta z `Domain\Scontainers\ScontainersRepository` (DI kontroler + fasada legacy).
+
+**Aktualizacja 2026-02-12 (ver. 0.260):** modul `/admin/articles_archive` korzysta z `Domain\Article\ArticleRepository` (`listArchivedForAdmin`, `restore`, `deletePermanently`) przez `admin\Controllers\ArticlesArchiveController`.
diff --git a/PROJECT_STRUCTURE.md b/PROJECT_STRUCTURE.md
index 1f9a596..738c505 100644
--- a/PROJECT_STRUCTURE.md
+++ b/PROJECT_STRUCTURE.md
@@ -437,3 +437,21 @@ Aktualnie w suite są też testy modułów `Dictionaries`, `Articles` i `Users`
- UPDATE: modul `/admin/newsletter/` - tymczasowo wylaczono liste `email_templates_user` (Szablony uzytkownika).
- UPDATE: lista i edycja szablonow newslettera w panelu ograniczona do szablonow administracyjnych (`is_admin = 1`).
- CLEANUP: usuniete nieuzywane widoki: `admin/templates/newsletter/prepare.php`, `admin/templates/newsletter/preview.php`, `admin/templates/newsletter/email-templates-user.php`.
+
+## Aktualizacja 2026-02-12 (ver. 0.259)
+- NOWE: `Domain\Scontainers\ScontainersRepository` (listForAdmin/find/save/delete/detailsForLanguage + czyszczenie cache frontu).
+- NOWE: `admin\Controllers\ScontainersController` (DI) dla akcji `list/view_list`, `container_edit`, `container_save`, `container_delete`.
+- UPDATE: `/admin/scontainers/*` przepiete z legacy `grid/gridEdit` na `components/table-list` i `components/form-edit`.
+- UPDATE: `admin\Site` ma fabryke DI dla modulu `Scontainers` oraz mapowanie akcji `container_*` -> `edit/save/delete`.
+- UPDATE: `admin\factory\Scontainers` dziala jako fasada do `Domain\Scontainers\ScontainersRepository`.
+- UPDATE: `front\factory\Scontainers` korzysta z `Domain\Scontainers\ScontainersRepository`.
+- CLEANUP: usuniete legacy klasy `autoload/admin/controls/class.Scontainers.php`, `autoload/admin/view/class.Scontainers.php`.
+- Testy: 158 tests, 397 assertions.
+
+## Aktualizacja 2026-02-12 (ver. 0.260)
+- NOWE: `Domain\Article\ArticleRepository` rozszerzone o `listArchivedForAdmin()`, `restore()`, `deletePermanently()`.
+- NOWE: `admin\Controllers\ArticlesArchiveController` (DI) dla akcji `list/view_list`, `article_restore`, `article_delete`.
+- UPDATE: routing DI (`admin\Site`) rozszerzony o modul `ArticlesArchive` oraz mapowanie akcji `article_restore -> restore`.
+- UPDATE: `/admin/articles_archive/view_list/` przepiete z legacy `grid` na `components/table-list`.
+- CLEANUP: usuniete legacy klasy `autoload/admin/controls/class.ArticlesArchive.php`, `autoload/admin/factory/class.ArticlesArchive.php`, `autoload/admin/view/class.ArticlesArchive.php`.
+- Testy: 165 tests, 424 assertions.
diff --git a/REFACTORING_PLAN.md b/REFACTORING_PLAN.md
index 4d0de26..8e48b33 100644
--- a/REFACTORING_PLAN.md
+++ b/REFACTORING_PLAN.md
@@ -634,3 +634,23 @@ Gdy `persist = true`:
- UPDATE: tymczasowo wylaczono modul `Szablony uzytkownika`.
- UPDATE: aktywna obsluga tylko szablonow administracyjnych (`is_admin = 1`).
- CLEANUP: usuniete nieuzywane widoki `prepare.php`, `preview.php`, `email-templates-user.php`.
+
+## Aktualizacja 2026-02-12 (ver. 0.259)
+- **Scontainers** - **ZMIGROWANE** (2026-02-12)
+ - NOWE: `Domain\Scontainers\ScontainersRepository` (listForAdmin, find, save, delete, detailsForLanguage)
+ - NOWE: `admin\Controllers\ScontainersController` (DI)
+ - UPDATE: `/admin/scontainers/view_list/` migrowane na `components/table-list`
+ - UPDATE: `/admin/scontainers/container_edit/` migrowane na `components/form-edit`
+ - UPDATE: `admin\factory\Scontainers` jako fasada do repozytorium
+ - UPDATE: `front\factory\Scontainers` korzysta z repozytorium domenowego
+ - CLEANUP: usuniete `autoload/admin/controls/class.Scontainers.php` i `autoload/admin/view/class.Scontainers.php`
+- Testy po zmianie: **158 tests, 397 assertions**
+
+## Aktualizacja 2026-02-12 (ver. 0.260)
+- **ArticlesArchive** - **ZMIGROWANE** (2026-02-12)
+ - NOWE: `admin\Controllers\ArticlesArchiveController` (DI)
+ - UPDATE: `Domain\Article\ArticleRepository` rozszerzone o `listArchivedForAdmin()`, `restore()`, `deletePermanently()`
+ - UPDATE: `/admin/articles_archive/view_list/` migrowane na `components/table-list`
+ - UPDATE: routing DI (`admin\Site`) rozszerzony o modul `ArticlesArchive` + mapowanie `article_restore -> restore`
+ - CLEANUP: usuniete `autoload/admin/controls/class.ArticlesArchive.php`, `autoload/admin/factory/class.ArticlesArchive.php`, `autoload/admin/view/class.ArticlesArchive.php`
+- Testy po zmianie: **165 tests, 424 assertions**
diff --git a/TESTING.md b/TESTING.md
index 23e7e41..a5c83dd 100644
--- a/TESTING.md
+++ b/TESTING.md
@@ -224,3 +224,25 @@ Ostatnio zweryfikowano: 2026-02-12
```text
OK (150 tests, 372 assertions)
```
+
+## Aktualizacja suite (release 0.259)
+Ostatnio zweryfikowano: 2026-02-12
+
+```text
+OK (158 tests, 397 assertions)
+```
+
+Nowe testy dodane 2026-02-12:
+- `tests/Unit/Domain/Scontainers/ScontainersRepositoryTest.php`
+- `tests/Unit/admin/Controllers/ScontainersControllerTest.php`
+
+## Aktualizacja suite (release 0.260)
+Ostatnio zweryfikowano: 2026-02-12
+
+```text
+OK (165 tests, 424 assertions)
+```
+
+Nowe testy dodane 2026-02-12:
+- `tests/Unit/Domain/Article/ArticleRepositoryTest.php` (rozszerzenie o testy `restore`, `deletePermanently`, `listArchivedForAdmin`)
+- `tests/Unit/admin/Controllers/ArticlesArchiveControllerTest.php`
diff --git a/admin/templates/articles/articles-archive-list.php b/admin/templates/articles/articles-archive-list.php
index 2cd5df4..3e70c9a 100644
--- a/admin/templates/articles/articles-archive-list.php
+++ b/admin/templates/articles/articles-archive-list.php
@@ -1,67 +1,5 @@
- $this->viewModel]); ?>
-$grid = new \grid( 'pp_articles' );
-$grid -> gdb_opt = $gdb;
-$grid -> sql = 'SELECT *'
- . 'FROM ( '
- . 'SELECT '
- . 'id, date_add, date_modify, status, '
- . '( SELECT title FROM pp_articles_langs AS pal, pp_langs AS pl WHERE lang_id = pl.id AND article_id = pa.id AND title != \'\' ORDER BY o ASC LIMIT 1 ) AS title '
- . 'FROM '
- . 'pp_articles AS pa WHERE status = -1 '
- . ') AS q1 '
- . 'WHERE '
- . '1=1 [where] '
- . 'ORDER BY '
- . '[order_p1] [order_p2]';
-$grid -> sql_count = 'SELECT '
- . 'COUNT(0) FROM ( '
- . 'SELECT '
- . 'id, date_add, date_modify, status, '
- . '( SELECT title FROM pp_articles_langs AS pal, pp_langs AS pl WHERE lang_id = pl.id AND article_id = pa.id AND title != \'\' ORDER BY o ASC LIMIT 1 ) AS title '
- . 'FROM '
- . 'pp_articles AS pa WHERE status = -1 '
- . ') AS q1 '
- . 'WHERE '
- . '1=1 [where] ';
-$grid -> debug = true;
-$grid -> order = [ 'column' => 'date_add', 'type' => 'DESC' ];
-$grid -> columns_view = [
- [
- 'name' => 'Lp.',
- 'th' => [ 'class' => 'g-lp' ],
- 'td' => [ 'class' => 'g-center' ],
- 'autoincrement' => true
- ],
- [
- 'name' => 'Tytuł',
- 'db' => 'id',
- 'replace' => [ 'sql' => "SELECT title FROM pp_articles_langs AS pal, pp_langs AS pl WHERE lang_id = pl.id AND article_id = [id] AND title != '' ORDER BY o ASC LIMIT 1" ]
- ],
- [
- 'name' => 'Data dodania',
- 'td' => [ 'class' => 'g-center' ],
- 'th' => [ 'class' => 'g-center', 'style' => 'width: 140px;' ],
- 'php' => 'echo date( "Y-m-d H:i", strtotime( "[date_add]" ) );'
- ],
- [
- 'name' => 'Data modyfikacji',
- 'td' => [ 'class' => 'g-center' ],
- 'th' => [ 'class' => 'g-center', 'style' => 'width: 140px;' ],
- 'php' => 'echo date( "Y-m-d H:i", strtotime( "[date_modify]" ) );'
- ],
- [
- 'name' => 'Akcja',
- 'th' => [ 'class' => 'g-center' ],
- 'td' => [ 'class' => 'g-center', 'style' => 'width: 50px;' ],
- 'content' => 'przywróć'
- ],
- [
- 'name' => 'Akcja',
- 'action' => [ 'type' => 'delete', 'url' => '/admin/articles_archive/article_delete/id=[id]' ],
- 'th' => [ 'class' => 'g-center' ],
- 'td' => [ 'class' => 'g-center', 'style' => 'width: 50px;' ]
- ]
- ];
-echo $grid -> draw();
\ No newline at end of file
+viewModel->customScriptView)): ?>
+ = \Tpl::view($this->viewModel->customScriptView, ['list' => $this->viewModel]); ?>
+
diff --git a/autoload/Domain/Article/ArticleRepository.php b/autoload/Domain/Article/ArticleRepository.php
index 2a76031..429578c 100644
--- a/autoload/Domain/Article/ArticleRepository.php
+++ b/autoload/Domain/Article/ArticleRepository.php
@@ -331,6 +331,32 @@ class ArticleRepository
return (bool)$result;
}
+ /**
+ * Przywraca artykul z archiwum (status = 0).
+ */
+ public function restore(int $articleId): bool
+ {
+ $result = $this->db->update('pp_articles', ['status' => 0], ['id' => $articleId]);
+ return (bool)$result;
+ }
+
+ /**
+ * Trwale usuwa artykul wraz z relacjami i plikami z dysku.
+ */
+ public function deletePermanently(int $articleId): bool
+ {
+ $this->db->delete('pp_articles_pages', ['article_id' => $articleId]);
+ $this->db->delete('pp_articles_langs', ['article_id' => $articleId]);
+ $this->db->delete('pp_articles_images', ['article_id' => $articleId]);
+ $this->db->delete('pp_articles_files', ['article_id' => $articleId]);
+ $this->db->delete('pp_articles', ['id' => $articleId]);
+
+ \S::delete_dir('../upload/article_images/article_' . $articleId . '/');
+ \S::delete_dir('../upload/article_files/article_' . $articleId . '/');
+
+ return true;
+ }
+
/**
* Zwraca liste artykulow do panelu admin z filtrowaniem, sortowaniem i paginacja.
*
@@ -431,6 +457,93 @@ class ArticleRepository
];
}
+ /**
+ * Zwraca liste artykulow z archiwum do panelu admin z filtrowaniem, sortowaniem i paginacja.
+ *
+ * @return array{items: array>, total: int}
+ */
+ public function listArchivedForAdmin(
+ array $filters,
+ string $sortColumn = 'date_add',
+ string $sortDir = 'DESC',
+ int $page = 1,
+ int $perPage = 15
+ ): array {
+ $sortColumn = trim($sortColumn);
+ $sortDir = strtoupper(trim($sortDir));
+
+ $allowedSortColumns = [
+ 'title' => 'title',
+ 'date_add' => 'pa.date_add',
+ 'date_modify' => 'pa.date_modify',
+ ];
+
+ $sortSql = $allowedSortColumns[$sortColumn] ?? 'pa.date_add';
+ $sortDir = $sortDir === 'ASC' ? 'ASC' : 'DESC';
+ $page = max(1, $page);
+ $perPage = min(self::MAX_PER_PAGE, max(1, $perPage));
+ $offset = ($page - 1) * $perPage;
+
+ $where = ['pa.status = -1'];
+ $params = [];
+
+ $title = trim((string)($filters['title'] ?? ''));
+ if (strlen($title) > 255) {
+ $title = substr($title, 0, 255);
+ }
+ if ($title !== '') {
+ $where[] = "(
+ SELECT title
+ FROM pp_articles_langs AS pal, pp_langs AS pl
+ WHERE lang_id = pl.id AND article_id = pa.id AND title != ''
+ ORDER BY o ASC
+ LIMIT 1
+ ) LIKE :title";
+ $params[':title'] = '%' . $title . '%';
+ }
+
+ $this->appendDateRangeFilter($where, $params, 'pa.date_add', 'date_add_from', 'date_add_to', $filters);
+ $this->appendDateRangeFilter($where, $params, 'pa.date_modify', 'date_modify_from', 'date_modify_to', $filters);
+
+ $whereSql = implode(' AND ', $where);
+
+ $sqlCount = "
+ SELECT COUNT(0)
+ FROM pp_articles AS pa
+ WHERE {$whereSql}
+ ";
+
+ $stmtCount = $this->db->query($sqlCount, $params);
+ $countRows = $stmtCount ? $stmtCount->fetchAll() : [];
+ $total = isset($countRows[0][0]) ? (int)$countRows[0][0] : 0;
+
+ $sql = "
+ SELECT
+ pa.id,
+ pa.date_add,
+ pa.date_modify,
+ (
+ SELECT title
+ FROM pp_articles_langs AS pal, pp_langs AS pl
+ WHERE lang_id = pl.id AND article_id = pa.id AND title != ''
+ ORDER BY o ASC
+ LIMIT 1
+ ) AS title
+ FROM pp_articles AS pa
+ WHERE {$whereSql}
+ ORDER BY {$sortSql} {$sortDir}, pa.id {$sortDir}
+ LIMIT {$perPage} OFFSET {$offset}
+ ";
+
+ $stmt = $this->db->query($sql, $params);
+ $items = $stmt ? $stmt->fetchAll() : [];
+
+ return [
+ 'items' => is_array($items) ? $items : [],
+ 'total' => $total,
+ ];
+ }
+
/**
* Zapisuje kolejnosc zdjec galerii artykulu.
*/
diff --git a/autoload/admin/Controllers/ArticlesArchiveController.php b/autoload/admin/Controllers/ArticlesArchiveController.php
new file mode 100644
index 0000000..cfd0a12
--- /dev/null
+++ b/autoload/admin/Controllers/ArticlesArchiveController.php
@@ -0,0 +1,147 @@
+repository = $repository;
+ }
+
+ public function list(): string
+ {
+ $sortableColumns = ['title', 'date_add', 'date_modify'];
+
+ $filterDefinitions = [
+ [
+ 'key' => 'title',
+ 'label' => 'Tytul',
+ 'type' => 'text',
+ ],
+ ];
+
+ $listRequest = \admin\Support\TableListRequestFactory::fromRequest(
+ $filterDefinitions,
+ $sortableColumns,
+ 'date_add'
+ );
+
+ $result = $this->repository->listArchivedForAdmin(
+ $listRequest['filters'],
+ $listRequest['sortColumn'],
+ $listRequest['sortDir'],
+ $listRequest['page'],
+ $listRequest['perPage']
+ );
+
+ $rows = [];
+ $lp = ($listRequest['page'] - 1) * $listRequest['perPage'] + 1;
+
+ foreach ($result['items'] as $item) {
+ $id = (int)($item['id'] ?? 0);
+ $title = trim((string)($item['title'] ?? ''));
+
+ $rows[] = [
+ 'lp' => $lp++ . '.',
+ 'title' => '' . htmlspecialchars($title, ENT_QUOTES, 'UTF-8') . '',
+ 'date_add' => !empty($item['date_add']) ? date('Y-m-d H:i', strtotime((string)$item['date_add'])) : '-',
+ 'date_modify' => !empty($item['date_modify']) ? date('Y-m-d H:i', strtotime((string)$item['date_modify'])) : '-',
+ '_actions' => [
+ [
+ 'label' => 'Przywroc',
+ 'url' => '/admin/articles_archive/article_restore/id=' . $id,
+ 'class' => 'btn btn-xs btn-success',
+ 'confirm' => 'Na pewno chcesz przywrocic wybrany artykul?',
+ 'confirm_ok' => 'Przywroc',
+ 'confirm_cancel' => 'Anuluj',
+ ],
+ [
+ 'label' => 'Usun',
+ 'url' => '/admin/articles_archive/article_delete/id=' . $id,
+ 'class' => 'btn btn-xs btn-danger',
+ 'confirm' => 'Na pewno chcesz trwale usunac wybrany artykul?',
+ 'confirm_ok' => 'Usun',
+ 'confirm_cancel' => 'Anuluj',
+ ],
+ ],
+ ];
+ }
+
+ $total = (int)$result['total'];
+ $totalPages = max(1, (int)ceil($total / $listRequest['perPage']));
+
+ $viewModel = new PaginatedTableViewModel(
+ [
+ ['key' => 'lp', 'label' => 'Lp.', 'class' => 'text-center', 'sortable' => false],
+ ['key' => 'title', 'sort_key' => 'title', 'label' => 'Tytul', 'sortable' => true, 'raw' => true],
+ ['key' => 'date_add', 'sort_key' => 'date_add', 'label' => 'Data dodania', 'class' => 'text-center', 'sortable' => true],
+ ['key' => 'date_modify', 'sort_key' => 'date_modify', 'label' => 'Data modyfikacji', 'class' => 'text-center', 'sortable' => true],
+ ],
+ $rows,
+ $listRequest['viewFilters'],
+ [
+ 'column' => $listRequest['sortColumn'],
+ 'dir' => $listRequest['sortDir'],
+ ],
+ [
+ 'page' => $listRequest['page'],
+ 'per_page' => $listRequest['perPage'],
+ 'total' => $total,
+ 'total_pages' => $totalPages,
+ ],
+ array_merge($listRequest['queryFilters'], [
+ 'sort' => $listRequest['sortColumn'],
+ 'dir' => $listRequest['sortDir'],
+ 'per_page' => $listRequest['perPage'],
+ ]),
+ $listRequest['perPageOptions'],
+ $sortableColumns,
+ '/admin/articles_archive/view_list/',
+ 'Brak danych w tabeli.'
+ );
+
+ return \Tpl::view('articles/articles-archive-list', [
+ 'viewModel' => $viewModel,
+ ]);
+ }
+
+ public function view_list(): string
+ {
+ return $this->list();
+ }
+
+ public function restore(): void
+ {
+ if ($this->repository->restore((int)\S::get('id'))) {
+ \S::alert('Artykul zostal przywrocony.');
+ }
+
+ header('Location: /admin/articles_archive/view_list/');
+ exit;
+ }
+
+ public function article_restore(): void
+ {
+ $this->restore();
+ }
+
+ public function delete(): void
+ {
+ if ($this->repository->deletePermanently((int)\S::get('id'))) {
+ \S::alert('Artykul zostal trwale usuniety.');
+ }
+
+ header('Location: /admin/articles_archive/view_list/');
+ exit;
+ }
+
+ public function article_delete(): void
+ {
+ $this->delete();
+ }
+}
diff --git a/autoload/admin/class.Site.php b/autoload/admin/class.Site.php
index e6c453d..f519909 100644
--- a/autoload/admin/class.Site.php
+++ b/autoload/admin/class.Site.php
@@ -211,6 +211,13 @@ class Site
new \Domain\Layouts\LayoutsRepository( $mdb )
);
},
+ 'ArticlesArchive' => function() {
+ global $mdb;
+
+ return new \admin\Controllers\ArticlesArchiveController(
+ new \Domain\Article\ArticleRepository( $mdb )
+ );
+ },
'Banners' => function() {
global $mdb;
@@ -286,6 +293,14 @@ class Site
new \Domain\Newsletter\NewsletterPreviewRenderer()
);
},
+ 'Scontainers' => function() {
+ global $mdb;
+
+ return new \admin\Controllers\ScontainersController(
+ new \Domain\Scontainers\ScontainersRepository( $mdb ),
+ new \Domain\Languages\LanguagesRepository( $mdb )
+ );
+ },
];
return self::$newControllers;
@@ -319,6 +334,7 @@ class Site
'article_edit' => 'edit',
'article_save' => 'save',
'article_delete' => 'delete',
+ 'article_restore' => 'restore',
'banner_edit' => 'edit',
'banner_save' => 'save',
'banner_delete' => 'delete',
@@ -332,6 +348,9 @@ class Site
'layout_edit' => 'edit',
'layout_save' => 'save',
'layout_delete' => 'delete',
+ 'container_edit' => 'edit',
+ 'container_save' => 'save',
+ 'container_delete' => 'delete',
];
public static function route()
diff --git a/autoload/admin/controls/class.ArticlesArchive.php b/autoload/admin/controls/class.ArticlesArchive.php
deleted file mode 100644
index 853607d..0000000
--- a/autoload/admin/controls/class.ArticlesArchive.php
+++ /dev/null
@@ -1,26 +0,0 @@
- update( 'pp_articles', [ 'status' => 0 ], [ 'id' => (int)$article_id ] );
- }
-
- public static function article_delete( $article_id )
- {
- global $mdb;
-
- $mdb -> delete( 'pp_articles_pages', [ 'article_id' => (int)$article_id ] );
- $mdb -> delete( 'pp_articles_langs', [ 'article_id' => (int)$article_id ] );
- $mdb -> delete( 'pp_articles_images', [ 'article_id' => (int)$article_id ] );
- $mdb -> delete( 'pp_articles_files', [ 'article_id' => (int)$article_id ] );
- $mdb -> delete( 'pp_articles', [ 'id' => (int)$article_id ] );
-
- \S::delete_dir( '../upload/article_images/article_' . (int)$article_id . '/' );
- \S::delete_dir( '../upload/article_files/article_' . (int)$article_id . '/' );
-
- return true;
- }
-}
diff --git a/autoload/admin/view/class.ArticlesArchive.php b/autoload/admin/view/class.ArticlesArchive.php
deleted file mode 100644
index 766aa2b..0000000
--- a/autoload/admin/view/class.ArticlesArchive.php
+++ /dev/null
@@ -1,11 +0,0 @@
- render( 'articles/articles-archive-list' );
- }
-}
diff --git a/tests/Unit/Domain/Article/ArticleRepositoryTest.php b/tests/Unit/Domain/Article/ArticleRepositoryTest.php
index abf4781..06aea6a 100644
--- a/tests/Unit/Domain/Article/ArticleRepositoryTest.php
+++ b/tests/Unit/Domain/Article/ArticleRepositoryTest.php
@@ -443,6 +443,95 @@ class ArticleRepositoryTest extends TestCase
$this->assertFalse($result);
}
+ public function testRestoreSetsStatusToZero(): void
+ {
+ $mockDb = $this->createMock(\medoo::class);
+
+ $mockDb->expects($this->once())
+ ->method('update')
+ ->with('pp_articles', ['status' => 0], ['id' => 25])
+ ->willReturn(true);
+
+ $repository = new ArticleRepository($mockDb);
+ $result = $repository->restore(25);
+
+ $this->assertTrue($result);
+ }
+
+ public function testDeletePermanentlyRemovesArticleAndRelations(): void
+ {
+ $mockDb = $this->createMock(\medoo::class);
+ $deleteCalls = [];
+
+ $mockDb->expects($this->exactly(5))
+ ->method('delete')
+ ->willReturnCallback(function ($table, $where) use (&$deleteCalls) {
+ $deleteCalls[] = ['table' => $table, 'where' => $where];
+ return true;
+ });
+
+ $repository = new ArticleRepository($mockDb);
+ $result = $repository->deletePermanently(77);
+
+ $this->assertTrue($result);
+ $this->assertCount(5, $deleteCalls);
+ $this->assertSame('pp_articles_pages', $deleteCalls[0]['table']);
+ $this->assertSame('pp_articles_langs', $deleteCalls[1]['table']);
+ $this->assertSame('pp_articles_images', $deleteCalls[2]['table']);
+ $this->assertSame('pp_articles_files', $deleteCalls[3]['table']);
+ $this->assertSame('pp_articles', $deleteCalls[4]['table']);
+ }
+
+ public function testListArchivedForAdminWhitelistsSortAndDirection(): void
+ {
+ $mockDb = $this->createMock(\medoo::class);
+ $queries = [];
+
+ $mockDb->method('query')
+ ->willReturnCallback(function ($sql, $params = []) use (&$queries) {
+ $queries[] = ['sql' => $sql, 'params' => $params];
+
+ if (strpos($sql, 'COUNT(0)') !== false) {
+ return new class {
+ public function fetchAll()
+ {
+ return [[1]];
+ }
+ };
+ }
+
+ return new class {
+ public function fetchAll()
+ {
+ return [[
+ 'id' => 1,
+ 'date_add' => '2020-01-01 00:00:00',
+ 'date_modify' => '2020-01-01 00:00:00',
+ 'title' => 'A',
+ ]];
+ }
+ };
+ });
+
+ $repository = new ArticleRepository($mockDb);
+ $repository->listArchivedForAdmin(
+ [],
+ 'date_add DESC; DROP TABLE pp_articles; --',
+ 'DESC; DELETE FROM pp_users; --',
+ 1,
+ 100000
+ );
+
+ $this->assertCount(2, $queries);
+ $dataSql = $queries[1]['sql'];
+
+ $this->assertMatchesRegularExpression('/ORDER BY\s+pa\.date_add\s+DESC,\s+pa\.id\s+DESC/i', $dataSql);
+ $this->assertStringNotContainsString('DROP TABLE', $dataSql);
+ $this->assertStringNotContainsString('DELETE FROM pp_users', $dataSql);
+ $this->assertMatchesRegularExpression('/LIMIT\s+100\s+OFFSET\s+0/i', $dataSql);
+ $this->assertStringContainsString('pa.status = -1', $dataSql);
+ }
+
public function testListForAdminWhitelistsSortAndDirection(): void
{
$mockDb = $this->createMock(\medoo::class);
diff --git a/tests/Unit/admin/Controllers/ArticlesArchiveControllerTest.php b/tests/Unit/admin/Controllers/ArticlesArchiveControllerTest.php
new file mode 100644
index 0000000..5241a8d
--- /dev/null
+++ b/tests/Unit/admin/Controllers/ArticlesArchiveControllerTest.php
@@ -0,0 +1,52 @@
+repository = $this->createMock(ArticleRepository::class);
+ $this->controller = new ArticlesArchiveController($this->repository);
+ }
+
+ public function testConstructorAcceptsRepository(): void
+ {
+ $controller = new ArticlesArchiveController($this->repository);
+ $this->assertInstanceOf(ArticlesArchiveController::class, $controller);
+ }
+
+ public function testHasMainActionMethods(): void
+ {
+ $this->assertTrue(method_exists($this->controller, 'list'));
+ $this->assertTrue(method_exists($this->controller, 'view_list'));
+ $this->assertTrue(method_exists($this->controller, 'restore'));
+ $this->assertTrue(method_exists($this->controller, 'delete'));
+ }
+
+ public function testActionMethodReturnTypes(): void
+ {
+ $reflection = new \ReflectionClass($this->controller);
+
+ $this->assertEquals('string', (string)$reflection->getMethod('list')->getReturnType());
+ $this->assertEquals('string', (string)$reflection->getMethod('view_list')->getReturnType());
+ $this->assertEquals('void', (string)$reflection->getMethod('restore')->getReturnType());
+ $this->assertEquals('void', (string)$reflection->getMethod('delete')->getReturnType());
+ }
+
+ public function testConstructorRequiresArticleRepository(): void
+ {
+ $reflection = new \ReflectionClass(ArticlesArchiveController::class);
+ $constructor = $reflection->getConstructor();
+ $params = $constructor->getParameters();
+
+ $this->assertCount(1, $params);
+ $this->assertEquals('Domain\Article\ArticleRepository', $params[0]->getType()->getName());
+ }
+}
diff --git a/updates/0.20/ver_0.260.zip b/updates/0.20/ver_0.260.zip
new file mode 100644
index 0000000..da7014b
Binary files /dev/null and b/updates/0.20/ver_0.260.zip differ
diff --git a/updates/0.20/ver_0.260_files.txt b/updates/0.20/ver_0.260_files.txt
new file mode 100644
index 0000000..056c527
--- /dev/null
+++ b/updates/0.20/ver_0.260_files.txt
@@ -0,0 +1,3 @@
+F: ../autoload/admin/controls/class.ArticlesArchive.php
+F: ../autoload/admin/factory/class.ArticlesArchive.php
+F: ../autoload/admin/view/class.ArticlesArchive.php
diff --git a/updates/changelog.php b/updates/changelog.php
index 98bf05a..7e1aba0 100644
--- a/updates/changelog.php
+++ b/updates/changelog.php
@@ -1,4 +1,19 @@
-ver. 0.258 - 12.02.2026
+ver. 0.260 - 12.02.2026
+- NEW - migracja modulu `ArticlesArchive` do architektury Domain + DI (`admin\\Controllers\\ArticlesArchiveController`)
+- UPDATE - `Domain\\Article\\ArticleRepository` rozszerzone o metody `listArchivedForAdmin`, `restore`, `deletePermanently`
+- UPDATE - widok `/admin/articles_archive/view_list/` przepiety z legacy `grid` na `components/table-list`
+- UPDATE - routing DI (`admin\\Site`) rozszerzony o modul `ArticlesArchive` + mapowanie akcji `article_restore -> restore`
+- CLEANUP - usuniete legacy klasy `autoload/admin/controls/class.ArticlesArchive.php`, `autoload/admin/factory/class.ArticlesArchive.php`, `autoload/admin/view/class.ArticlesArchive.php`
+- UPDATE - plik do usuniecia dodany w `updates/0.20/ver_0.260_files.txt`
+
ver. 0.259 - 12.02.2026
+- NEW - migracja modulu `Scontainers` do architektury Domain + DI (`Domain\\Scontainers\\ScontainersRepository`, `admin\\Controllers\\ScontainersController`)
+- UPDATE - widoki `/admin/scontainers/*` przepiete z legacy `grid/gridEdit` na `components/table-list` i `components/form-edit`
+- UPDATE - routing DI (`admin\\Site`) rozszerzony o modul `Scontainers` + mapowanie akcji `container_edit/container_save/container_delete`
+- UPDATE - `admin\\factory\\Scontainers` dziala jako fasada do repozytorium (backward compatibility)
+- UPDATE - `front\\factory\\Scontainers` korzysta z `Domain\\Scontainers\\ScontainersRepository`
+- CLEANUP - usuniete legacy klasy `autoload/admin/controls/class.Scontainers.php`, `autoload/admin/view/class.Scontainers.php`
+- UPDATE - plik do usuniecia dodany w `updates/0.20/ver_0.259_files.txt`
+
ver. 0.258 - 12.02.2026
- UPDATE - modul `Newsletter`: funkcjonalnosc `Wysylka - przygotowanie` zostala tymczasowo wylaczona (menu + akcje `prepare/send/preview`)
- UPDATE - modul `Newsletter`: lista `Szablony uzytkownika` zostala tymczasowo wylaczona (menu + akcja `email_templates_user`)
- UPDATE - `NewsletterController`: lista szablonow ograniczona do szablonow administracyjnych (`is_admin = 1`)
@@ -8,7 +23,7 @@
ver. 0.257 - 12.02.2026
- NEW - migracja modulu `Newsletter` do architektury Domain + DI (`Domain\\Newsletter\\NewsletterRepository`, `Domain\\Newsletter\\NewsletterPreviewRenderer`, `admin\\Controllers\\NewsletterController`)
- UPDATE - widoki `/admin/newsletter/*` przepiete z legacy `grid/gridEdit` na nowe komponenty (`components/table-list`, `components/form-edit`) + nowy endpoint `/admin/newsletter/preview/`
-- UPDATE - routing DI (`admin\\Site`) rozszerzony o moduł `Newsletter`
+- UPDATE - routing DI (`admin\\Site`) rozszerzony o moduł `Newsletter`
- UPDATE - `admin\\factory\\Newsletter` dziala jako fasada do nowego repozytorium (backward compatibility)
- UPDATE - `front\\factory\\Newsletter` nie korzysta juz z `admin\\view\\Newsletter`
- CLEANUP - usuniete legacy klasy `autoload/admin/controls/class.Newsletter.php`, `autoload/admin/view/class.Newsletter.php`
@@ -29,7 +44,7 @@
- UPDATE - migracja widokow languages (`languages-list`, `language-edit`, `translations-list`, `translation-edit`) na `components/table-list` i `components/form-edit`
- UPDATE - routing DI dla `Languages` w `admin\\Site` oraz kompatybilna fasada `admin\\factory\\Languages` delegujaca do repozytorium
- UPDATE - naprawiono zapis edycji jezyka (ID jezyka pobierane z URL przy edycji)
-- UPDATE - globalne poprawki UX filtrów w `components/table-list` (kompaktowe kolumny `Aktywny`/`Domyslny`, spacing i pelna szerokosc selecta)
+- UPDATE - globalne poprawki UX filtrĂłw w `components/table-list` (kompaktowe kolumny `Aktywny`/`Domyslny`, spacing i pelna szerokosc selecta)
- CLEANUP - usuniete legacy klasy: `autoload/admin/controls/class.Languages.php`, `autoload/admin/view/class.Languages.php`
ver. 0.253 - 12.02.2026
@@ -92,74 +107,74 @@
- UPDATE - refaktoryzacja: article_save przeniesiony do Domain\Article\ArticleRepository::save() z prywatnymi helperami
- UPDATE - refaktoryzacja: article_delete przeniesiony do Domain\Article\ArticleRepository::archive()
- UPDATE - ArticlesController: nowe akcje save() i delete() z DI
-- UPDATE - admin\factory\Articles::article_save() i articles_set_archive() delegują do repozytorium (kompatybilność)
+- UPDATE - admin\factory\Articles::article_save() i articles_set_archive() delegują do repozytorium (kompatybilność)
ver. 0.243
-- UPDATE - refaktoryzacja: cleanup nieprzypisanych plików/zdjęć artykułów przeniesiony do Domain\Article\ArticleRepository
-- UPDATE - ArticlesController::edit() używa repozytorium do cleanupu, a admin\factory\Articles zachowuje delegowanie (kompatybilność)
+- UPDATE - refaktoryzacja: cleanup nieprzypisanych plików/zdjęć artykułów przeniesiony do Domain\Article\ArticleRepository
+- UPDATE - ArticlesController::edit() używa repozytorium do cleanupu, a admin\factory\Articles zachowuje delegowanie (kompatybilność)
ver. 0.242
- NEW - refaktoryzacja: Domain\Article\ArticleRepository + migracja article_edit do admin\Controllers\ArticlesController (DI)
-- UPDATE - admin\factory\Articles::article_details() deleguje do nowego repozytorium (kompatybilność zachowana)
-- UPDATE - metody przejęte przez nowe kontrolery oznaczone jako @deprecated w legacy kontrolerach admin\controls
+- UPDATE - admin\factory\Articles::article_details() deleguje do nowego repozytorium (kompatybilność zachowana)
+- UPDATE - metody przejęte przez nowe kontrolery oznaczone jako @deprecated w legacy kontrolerach admin\controls
ver. 0.241
-- NEW - refaktoryzacja: admin\Controllers\ProductArchiveController - archiwum produktów z DI
+- NEW - refaktoryzacja: admin\Controllers\ProductArchiveController - archiwum produktĂłw z DI
- NEW - ProductRepository::archive(), unarchive() - operacje archiwizacji w repozytorium
-- FIX - naprawiono SQL w liście archiwum (puste wyszukiwanie filtrowało wszystkie wyniki)
-- FIX - naprawiono brakujący filtr archive = 1 w zapytaniu bez wyszukiwania
-- UPDATE - wyczyszczono szablony archiwum (usunięto zbędne funkcje: apilo, baselinker, duplikowanie)
+- FIX - naprawiono SQL w liście archiwum (puste wyszukiwanie filtrowało wszystkie wyniki)
+- FIX - naprawiono brakujÄ…cy filtr archive = 1 w zapytaniu bez wyszukiwania
+- UPDATE - wyczyszczono szablony archiwum (usunięto zbędne funkcje: apilo, baselinker, duplikowanie)
ver. 0.240
- NEW - refaktoryzacja: Domain\Settings\SettingsRepository + admin\Controllers\SettingsController (architektura Domain-Driven)
-- NEW - refaktoryzacja: Domain\Cache\CacheRepository - czyszczenie cache z obsługą Redis
-- FIX - komunikat potwierdzenia zapisu ustawień w panelu administratora
+- NEW - refaktoryzacja: Domain\Cache\CacheRepository - czyszczenie cache z obsługą Redis
+- FIX - komunikat potwierdzenia zapisu ustawień w panelu administratora
- FIX - naprawiono element #content w layoucie admina (powiadomienia grid.js)
ver. 0.239
-- NEW - refaktoryzacja: Domain\Banner\BannerRepository + admin\Controllers\BannerController (pełna migracja kontrolera)
+- NEW - refaktoryzacja: Domain\Banner\BannerRepository + admin\Controllers\BannerController (pełna migracja kontrolera)
- NEW - refaktoryzacja: Domain\Product\ProductRepository::getPrice(), getName() - migracja kolejnych metod
-- NEW - router admin z obsługą nowych kontrolerów (fallback na stare)
-- UPDATE - shop\Product::get_product_price(), get_product_name() używają nowego repozytorium (kompatybilność zachowana)
+- NEW - router admin z obsługą nowych kontrolerów (fallback na stare)
+- UPDATE - shop\Product::get_product_price(), get_product_name() używają nowego repozytorium (kompatybilność zachowana)
ver. 0.238
- NEW - refaktoryzacja: Domain\Product\ProductRepository - pierwsza klasa w nowej architekturze Domain-Driven
- NEW - Dependency Injection zamiast global variables
-- UPDATE - shop\Product::get_product_quantity() używa teraz nowego repozytorium (kompatybilność zachowana)
+- UPDATE - shop\Product::get_product_quantity() używa teraz nowego repozytorium (kompatybilność zachowana)
ver. 0.237
- NEW - automatyczne czyszczenie cache produktu po aktualizacji przez CRON (Sellasist, Apilo, Baselinker)
-- UPDATE - przycisk "Wyczyść cache" w panelu administratora z obsługą AJAX i komunikatami o postępie
+- UPDATE - przycisk "Wyczyść cache" w panelu administratora z obsługą AJAX i komunikatami o postępie
ver. 0.236
-- FIX - zabezpieczenie przed duplikatami zamówień w Apilo - automatyczne pobieranie ID zamówienia przy błędzie "idExternal już wykorzystywany"
+- FIX - zabezpieczenie przed duplikatami zamówień w Apilo - automatyczne pobieranie ID zamówienia przy błędzie "idExternal już wykorzystywany"
ver. 0.235
- FIX - poprawka funkcji aktualizacji
ver. 0.234
-- NEW - przycisk zaznaczania zamówienia jako wysłane do trustmate.io
+- NEW - przycisk zaznaczania zamówienia jako wysłane do trustmate.io
ver. 0.232
- NEW - opcje GPSR
ver. 0.231
-- FIX - poprawki bezpieczeństwa + dwuetapowa weryfikacja logowania
+- FIX - poprawki bezpieczeństwa + dwuetapowa weryfikacja logowania
ver. 0.230
-- FIX - poprawki bezpieczeństwa
+- FIX - poprawki bezpieczeństwa
ver. 0.229
-- NEW - pola dodatkowe z opcją wymagane/niewymagane
+- NEW - pola dodatkowe z opcjÄ… wymagane/niewymagane
ver. 0.228
-- NEW - cron do wysyłania zamówień do trustmate.io
+- NEW - cron do wysyłania zamówień do trustmate.io
ver. 0.227
-- NEW - historia kodów rabatowych
+- NEW - historia kodĂłw rabatowych
ver. 0.226
-- NEW - dodanie opcji faktury do zamówienia
+- NEW - dodanie opcji faktury do zamĂłwienia
ver. 0.225
- NEW - przycisk czyszczenia cache
-- NEW - ponowne wysyłanie zamówienia do apilo
+- NEW - ponowne wysyłanie zamówienia do apilo
ver. 0.224
- NEW - sortowanie form dostawy
@@ -171,20 +186,20 @@
- NEW - integracja z Orlen Paczka
ver. 0.221
-- NEW - Automatyczne przekierowania adresów URL produktów, zmiany w pliku htaccess
+- NEW - Automatyczne przekierowania adresĂłw URL produktĂłw, zmiany w pliku htaccess
ver. 0.220
-- NEW - Dodanie możliwości wyświetlenia na strone ostatnio dodane produkty [PRODUKTY_NEW] lub [PRODUKTY_NEW:10].
-- NEW - Dodanie możliwości wyświetlenia na strone popularnych produktów [PRODUKTY_TOP] lub [PRODUKTY_TOP:10].
+- NEW - Dodanie możliwości wyświetlenia na strone ostatnio dodane produkty [PRODUKTY_NEW] lub [PRODUKTY_NEW:10].
+- NEW - Dodanie możliwości wyświetlenia na strone popularnych produktów [PRODUKTY_TOP] lub [PRODUKTY_TOP:10].
ver. 0.219
-- NEW - Dodanie możliwości zmiany daty w artykułach
+- NEW - Dodanie możliwości zmiany daty w artykułach
ver. 0.218
- NEW - indywidualny kod GTM
ver. 0.217
-- NEW - zwiększenie obsługi REDIS
+- NEW - zwiększenie obsługi REDIS
ver. 0.216
- NEW - aktualizacja api i cron (apilo)
@@ -199,43 +214,43 @@
- FIX - wyliczenie darmowej dostawy
ver. 0.212
-- NEW - zmiany w zapisywaniu zamówienia do apilo
+- NEW - zmiany w zapisywaniu zamĂłwienia do apilo
ver. 0.211
-- NEW - Debugowanie apilo + wyświetlanie podkategorii
+- NEW - Debugowanie apilo + wyświetlanie podkategorii
ver. 0.210
-- NEW - dodatkowe pola w widoku produktów
+- NEW - dodatkowe pola w widoku produktĂłw
ver. 0.209
-- NEW - zmiany w widoku produktów (panel administratora)
+- NEW - zmiany w widoku produktĂłw (panel administratora)
ver. 0.208
-- NEW - zmiany w wyszukiwarce produktów
+- NEW - zmiany w wyszukiwarce produktĂłw
ver. 0.204-0.207
- NEW - htaccess update
ver. 0.204-0.206
-- NEW - wysyłanie produktów do apilo
+- NEW - wysyłanie produktów do apilo
ver. 0.203
- NEW - zmiana sposobu wyliczania cen produkty z dodatkami
ver. 0.202
-- NEW - dodano "główne zdjęcie" w edycji artykułu
+- NEW - dodano "główne zdjęcie" w edycji artykułu
ver. 0.201
-- FIX - aktualizacja statusów na podstawie baselinkera
+- FIX - aktualizacja statusĂłw na podstawie baselinkera
ver. 0.200
-- NEW - wysyłanie produktów do baselinker
+- NEW - wysyłanie produktów do baselinker
ver. 0.199
- NEW - usprawnienie edycji danych do XML
ver. 0.198
-- NEW - automatyczne generowanie kodów SKU
+- NEW - automatyczne generowanie kodĂłw SKU
ver. 0.197
- FIX - poprawki w Dashboard
@@ -244,7 +259,7 @@
- FIX - integracja z apilo.com
ver. 0.195
-- FIX - aktualizacja statusów
+- FIX - aktualizacja statusĂłw
ver. 0.194
- UPDATE - integracja apilo
@@ -253,46 +268,46 @@
- UPDATE - aktualizacja synchronizacji z baselinker
ver. 0.192
-- NEW - pobieranie statusów z sellasist
+- NEW - pobieranie statusĂłw z sellasist
ver. 0.191
- NEW - integracja z selasist
ver. 0.190
-- FIX - produkty powiązane
+- FIX - produkty powiÄ…zane
ver. 0.189
-- FIX - ceny promocyjne produktów z dodatkiem
+- FIX - ceny promocyjne produktĂłw z dodatkiem
ver. 0.188
-- NEW - widok listy produktów
+- NEW - widok listy produktĂłw
ver. 0.187
- FIX - pobieranie cen z APILO
ver. 0.186
-- FIX - dodawanie do koszyka tych samych produktów ale z różną personalizacją
+- FIX - dodawanie do koszyka tych samych produktów ale z różną personalizacją
ver. 0.185
-- FIX - masowa edycja produktów
+- FIX - masowa edycja produktĂłw
ver. 0.184
-- NEW - druga część integracji z apilo, masowa edycja produktów
+- NEW - druga część integracji z apilo, masowa edycja produktów
ver. 0.183
-- NEW - pierwsza część integracji z apilo
+- NEW - pierwsza część integracji z apilo
ver. 0.182
- FIX - layout
ver. 0.181
-- NEW - infinitescroll - opcja włączy/wyłącz
+- NEW - infinitescroll - opcja włączy/wyłącz
ver. 0.180
- NEW - aktualizacja dashboard
ver. 0.179
-- NEW - obsługa EAN
+- NEW - obsługa EAN
ver. 0.177, 0.178
- FIX - custom_label
@@ -308,7 +323,7 @@
ver. 0.173
- NEW - duplikowanie produktu wraz z kombinacjami
-- NEW - dodanie przechodzenia pomiędzy zamówienia (poprzednie/następne zamówienie)
+- NEW - dodanie przechodzenia pomiędzy zamówienia (poprzednie/następne zamówienie)
ver. 0.172
- FIX - poprawki w Cache
@@ -320,22 +335,22 @@
- NEW - usuwanie cache produktu przy zapisie
ver. 0.169
-- FIX - poprawki w liście produktów
+- FIX - poprawki w liście produktów
ver. 0.168
-- NEW - archiwum produktów
+- NEW - archiwum produktĂłw
ver. 0.167
-- NEW - dodanie obsługi cen i stanów magazynowych kombinacji produktów
+- NEW - dodanie obsługi cen i stanów magazynowych kombinacji produktów
ver. 0.166
-- NEW - współpraca z GTM
+- NEW - współpraca z GTM
ver. 0.164/5
-- FIX - ukrywanie produktów nieaktywnych
+- FIX - ukrywanie produktĂłw nieaktywnych
ver. 0.163
-- NEW - automatyczne podpowiadanie produktów do zestawu na podstawie wcześniejszych zakupów klientów
+- NEW - automatyczne podpowiadanie produktów do zestawu na podstawie wcześniejszych zakupów klientów
ver. 0.162
- NEW - GA4
@@ -351,7 +366,7 @@
- FIX - cron Baselinker
ver. 0.158
-- UPDATE - poprawa kolorystyki przycisków
+- UPDATE - poprawa kolorystyki przyciskĂłw
ver. 0.157
- NEW - szybka zmiana statusu produktu
@@ -363,10 +378,10 @@
- NEW - infinite scroll w widoku kategorii
ver. 0.154
-- FIX - atrybuty produktów
+- FIX - atrybuty produktĂłw
ver. 0.153
-- FIX - atrybuty produktów
+- FIX - atrybuty produktĂłw
ver. 0.152
- FIX - tematy maili
@@ -375,7 +390,7 @@
- FIX - tematy maili
ver. 0.150
-- NEW - domyślna forma transportu
+- NEW - domyślna forma transportu
ver. 0.149
- NEW - tematy maili
@@ -393,13 +408,14 @@
- NEW - omnibus ready
ver. 0.144
-- FIX - usunięcie adresu marianek.pl z kodu
+- FIX - usunięcie adresu marianek.pl z kodu
ver. 0.143
-- FIX - poprawa generowania plików WEBP
+- FIX - poprawa generowania plikĂłw WEBP
ver. 0.142
-- FIX - poprawa adresu strony głównej
+- FIX - poprawa adresu strony głównej
+