Articles: finish admin refactor, uploads hardening, and attachment sorting (0.262)
This commit is contained in:
@@ -134,10 +134,14 @@ Pliki artykułów.
|
|||||||
|
|
||||||
| Kolumna | Opis |
|
| Kolumna | Opis |
|
||||||
|---------|------|
|
|---------|------|
|
||||||
|
| id | PK |
|
||||||
| article_id | FK do pp_articles |
|
| article_id | FK do pp_articles |
|
||||||
| src | Ścieżka do pliku |
|
| src | Ścieżka do pliku |
|
||||||
|
| name | Nazwa wyświetlana załącznika (opcjonalna) |
|
||||||
|
| to_delete | Flaga miękkiego usuwania (0/1) |
|
||||||
|
| o | Kolejność załączników (używana przez sortowanie drag&drop w adminie) |
|
||||||
|
|
||||||
**Używane w:** `Domain\Article\ArticleRepository::find()`
|
**Używane w:** `Domain\Article\ArticleRepository::find()`, `Domain\Article\ArticleRepository::saveFilesOrder()`
|
||||||
|
|
||||||
## pp_units
|
## pp_units
|
||||||
Jednostki/slowniki (np. jednostki produktu).
|
Jednostki/slowniki (np. jednostki produktu).
|
||||||
|
|||||||
@@ -455,3 +455,19 @@ Aktualnie w suite są też testy modułów `Dictionaries`, `Articles` i `Users`
|
|||||||
- UPDATE: `/admin/articles_archive/view_list/` przepiete z legacy `grid` na `components/table-list`.
|
- 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`.
|
- 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.
|
- Testy: 165 tests, 424 assertions.
|
||||||
|
|
||||||
|
### 2026-02-13: Refaktoryzacja /admin/articles (ver. 0.261)
|
||||||
|
- **UPDATE:** routing DI dla `ArticlesController` obsluguje akcje AJAX: `article_image_alt_change`, `article_file_name_change`, `article_image_delete`, `article_file_delete`.
|
||||||
|
- **UPDATE:** widok `admin/templates/articles/article-edit.php` korzysta z endpointow `/admin/articles/*` zamiast `admin/ajax.php?a=article_*`.
|
||||||
|
- **UPDATE:** lista artykulow nie korzysta juz z `admin\factory\Articles::article_pages` (etykiety stron z `Domain\Article\ArticleRepository`).
|
||||||
|
- **CLEANUP:** usuniete legacy pliki `autoload/admin/view/class.Articles.php` i `admin/ajax/articles.php`; odpiecie include w `admin/ajax.php`.
|
||||||
|
- Testy: 176 tests, 439 assertions.
|
||||||
|
|
||||||
|
### 2026-02-13: Articles edit UX i sortowanie zalacznikow (ver. 0.262)
|
||||||
|
- **UPDATE:** `Domain\Article\ArticleRepository` - dodane `saveFilesOrder()` oraz obsluga `files_order` podczas `save()` (pierwszy zapis zachowuje kolejnosc).
|
||||||
|
- **UPDATE:** routing DI (`admin\Site::$actionMap`) rozszerzony o `files_order_save -> filesOrderSave`.
|
||||||
|
- **UPDATE:** `admin\Controllers\ArticlesController` - nowa akcja AJAX `filesOrderSave()`.
|
||||||
|
- **UPDATE:** `admin/templates/articles/article-edit-custom-script.php` - drag&drop sortowania listy zalacznikow + synchronizacja hidden input `files_order`.
|
||||||
|
- **UPDATE:** potwierdzenia usuwania zdjec i zalacznikow w edycji artykulu ujednolicone wizualnie z dialogiem usuwania z listy (jquery-confirm, `table-list-confirm-dialog`).
|
||||||
|
- **FIX:** dodane ladowanie biblioteki `jquery-impromptu` w widoku edycji artykulu (kompatybilnosc dla `$.prompt`).
|
||||||
|
- Testy: 178 tests, 443 assertions.
|
||||||
|
|||||||
@@ -654,3 +654,31 @@ Gdy `persist = true`:
|
|||||||
- UPDATE: routing DI (`admin\Site`) rozszerzony o modul `ArticlesArchive` + mapowanie `article_restore -> restore`
|
- 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`
|
- 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**
|
- Testy po zmianie: **165 tests, 424 assertions**
|
||||||
|
|
||||||
|
## Plan 2026-02-13 - Refaktoryzacja `/admin/articles/`
|
||||||
|
- [ ] Przeniesc zaleznosci listy artykulow z `admin\factory\Articles` do `Domain\Article\ArticleRepository` (etykiety stron, operacje pomocnicze).
|
||||||
|
- [ ] Dodac akcje routowane przez `admin\Controllers\ArticlesController` dla operacji AJAX (`article_image_alt_change`, `article_file_name_change`, `article_image_delete`, `article_file_delete`).
|
||||||
|
- [ ] Przepiac widok `admin/templates/articles/article-edit.php` z `/admin/ajax.php` na endpointy `/admin/articles/*`.
|
||||||
|
- [ ] Usunac legacy `admin\view\Articles` i zastapic rekurencje podstron przez `Tpl::view('articles/subpages-list', ...)`.
|
||||||
|
- [ ] Usunac `admin/ajax/articles.php` oraz odpiac include z `admin/ajax.php`.
|
||||||
|
- [ ] Przeszukac projekt pod pozostale zaleznosci i uruchomic testy modulu Articles.
|
||||||
|
|
||||||
|
## Aktualizacja 2026-02-13 (ver. 0.261)
|
||||||
|
- **Articles** - dalsza refaktoryzacja `/admin/articles/`
|
||||||
|
- UPDATE: `Domain\Article\ArticleRepository` rozszerzone o metody UI/admin: `pagesSummaryForArticles()`, `updateImageAlt()`, `updateFileName()`, `markImageToDelete()`, `markFileToDelete()`.
|
||||||
|
- UPDATE: `admin\Controllers\ArticlesController` obsluguje nowe akcje routingu: `article_image_alt_change`, `article_file_name_change`, `article_image_delete`, `article_file_delete`.
|
||||||
|
- UPDATE: lista artykulow (`list`) nie korzysta juz z `admin\factory\Articles::article_pages()`.
|
||||||
|
- UPDATE: `admin/templates/articles/article-edit.php` przepiete z `/admin/ajax.php?a=article_*` na endpointy `/admin/articles/article_*/`.
|
||||||
|
- UPDATE: rekurencja podstron w widoku oparta o `Tpl::view('articles/subpages-list', ...)` (bez `admin\view\Articles`).
|
||||||
|
- CLEANUP: usuniete legacy pliki `autoload/admin/view/class.Articles.php` oraz `admin/ajax/articles.php`; `admin/ajax.php` nie includuje juz `ajax/articles.php`.
|
||||||
|
- Testy po zmianie: **176 tests, 439 assertions**.
|
||||||
|
|
||||||
|
## Aktualizacja 2026-02-13 (ver. 0.262)
|
||||||
|
- **Articles (/admin/articles)**
|
||||||
|
- UPDATE: `Domain\Article\ArticleRepository` rozszerzone o `saveFilesOrder()` oraz zapis `files_order` przy `save()` (eliminuje koniecznosc drugiego zapisu po sortowaniu).
|
||||||
|
- UPDATE: routing DI (`admin\Site`) rozszerzony o mapowanie `files_order_save -> filesOrderSave`.
|
||||||
|
- UPDATE: `admin\Controllers\ArticlesController` - nowa akcja AJAX `filesOrderSave`.
|
||||||
|
- UPDATE: widok `admin/templates/articles/article-edit-custom-script.php` - drag&drop dla listy zalacznikow + hidden input `files_order`.
|
||||||
|
- UPDATE: potwierdzenia usuwania zdjec i zalacznikow przepiete na `jquery-confirm` ze stylem `table-list-confirm-dialog` (jak na liscie artykulow).
|
||||||
|
- FIX: dolaczona biblioteka `jquery-impromptu` w widoku edycji artykulu dla kompatybilnosci.
|
||||||
|
- Testy po zmianie: **178 tests, 443 assertions**.
|
||||||
|
|||||||
21
TESTING.md
21
TESTING.md
@@ -246,3 +246,24 @@ OK (165 tests, 424 assertions)
|
|||||||
Nowe testy dodane 2026-02-12:
|
Nowe testy dodane 2026-02-12:
|
||||||
- `tests/Unit/Domain/Article/ArticleRepositoryTest.php` (rozszerzenie o testy `restore`, `deletePermanently`, `listArchivedForAdmin`)
|
- `tests/Unit/Domain/Article/ArticleRepositoryTest.php` (rozszerzenie o testy `restore`, `deletePermanently`, `listArchivedForAdmin`)
|
||||||
- `tests/Unit/admin/Controllers/ArticlesArchiveControllerTest.php`
|
- `tests/Unit/admin/Controllers/ArticlesArchiveControllerTest.php`
|
||||||
|
|
||||||
|
## Aktualizacja suite (release 0.261)
|
||||||
|
Ostatnio zweryfikowano: 2026-02-13
|
||||||
|
|
||||||
|
```text
|
||||||
|
OK (176 tests, 439 assertions)
|
||||||
|
```
|
||||||
|
|
||||||
|
Nowe testy/rozszerzenia 2026-02-13:
|
||||||
|
- `tests/Unit/Domain/Article/ArticleRepositoryTest.php` (nowe przypadki dla `pagesSummaryForArticles`, `updateImageAlt`, `markFileToDelete`)
|
||||||
|
- `tests/Unit/admin/Controllers/ArticlesControllerTest.php` (nowe kontrakty dla akcji `imageAltChange`, `fileNameChange`, `imageDelete`, `fileDelete`)
|
||||||
|
|
||||||
|
## Aktualizacja suite (release 0.262)
|
||||||
|
Ostatnio zweryfikowano: 2026-02-13
|
||||||
|
|
||||||
|
```text
|
||||||
|
OK (178 tests, 443 assertions)
|
||||||
|
```
|
||||||
|
|
||||||
|
Nowe testy/rozszerzenia 2026-02-13:
|
||||||
|
- `tests/Unit/Domain/Article/ArticleRepositoryTest.php` (nowe przypadki dla `saveFilesOrder`)
|
||||||
|
|||||||
@@ -38,8 +38,7 @@ $mdb = new medoo( [
|
|||||||
] );
|
] );
|
||||||
|
|
||||||
require_once 'ajax/pages.php';
|
require_once 'ajax/pages.php';
|
||||||
require_once 'ajax/articles.php';
|
|
||||||
require_once 'ajax/shop-category.php';
|
require_once 'ajax/shop-category.php';
|
||||||
require_once 'ajax/users.php';
|
require_once 'ajax/users.php';
|
||||||
require_once 'ajax/shop.php';
|
require_once 'ajax/shop.php';
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
<?php
|
|
||||||
$a = \S::get( 'a' );
|
|
||||||
|
|
||||||
if ( $a == 'article_image_delete' )
|
|
||||||
{
|
|
||||||
$response = [ 'status' => 'error', 'msg' => 'Podczas usuwania zdjecia wystąpił błąd. Proszę spróbować ponownie.' ];
|
|
||||||
|
|
||||||
if ( \admin\factory\Articles::delete_img( \S::get( 'image_id' ) ) )
|
|
||||||
$response = [ 'status' => 'ok' ];
|
|
||||||
|
|
||||||
echo json_encode( $response );
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $a == 'article_file_delete' )
|
|
||||||
{
|
|
||||||
$response = [ 'status' => 'error', 'msg' => 'Podczas usuwania załącznika wystąpił błąd. Proszę spróbować ponownie.' ];
|
|
||||||
|
|
||||||
if ( \admin\factory\Articles::delete_file( \S::get( 'file_id' ) ) )
|
|
||||||
$response = [ 'status' => 'ok' ];
|
|
||||||
|
|
||||||
echo json_encode( $response );
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $a == 'article_image_alt_change' )
|
|
||||||
{
|
|
||||||
$response = [ 'status' => 'error', 'msg' => 'Podczas zmiany atrybutu alt zdjęcia wystąpił błąd. Proszę spróbować ponownie.' ];
|
|
||||||
|
|
||||||
if ( \admin\factory\Articles::image_alt_change( \S::get( 'image_id' ), \S::get( 'image_alt' ) ) )
|
|
||||||
$response = [ 'status' => 'ok' ];
|
|
||||||
|
|
||||||
echo json_encode( $response );
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $a == 'article_file_name_change' )
|
|
||||||
{
|
|
||||||
$response = [ 'status' => 'error', 'msg' => 'Podczas zmiany nazwy załącznika wystąpił błąd. Proszę spróbować ponownie.' ];
|
|
||||||
|
|
||||||
if ( \admin\factory\Articles::file_name_change( \S::get( 'file_id' ), \S::get( 'file_name' ) ) )
|
|
||||||
$response = [ 'status' => 'ok' ];
|
|
||||||
|
|
||||||
echo json_encode( $response );
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
583
admin/templates/articles/article-edit-custom-script.php
Normal file
583
admin/templates/articles/article-edit-custom-script.php
Normal file
@@ -0,0 +1,583 @@
|
|||||||
|
<?php
|
||||||
|
$article = is_array($this->article ?? null) ? $this->article : [];
|
||||||
|
$articleId = (int)($article['id'] ?? 0);
|
||||||
|
$userId = (int)($this->user['id'] ?? 0);
|
||||||
|
$imagesCount = is_array($article['images'] ?? null) ? count($article['images']) : 0;
|
||||||
|
$filesCount = is_array($article['files'] ?? null) ? count($article['files']) : 0;
|
||||||
|
|
||||||
|
$imageMaxPx = 1920;
|
||||||
|
if (isset($this->settings['image_px']) && (int)$this->settings['image_px'] > 0) {
|
||||||
|
$imageMaxPx = (int)$this->settings['image_px'];
|
||||||
|
} elseif (isset($GLOBALS['settings']['image_px']) && (int)$GLOBALS['settings']['image_px'] > 0) {
|
||||||
|
$imageMaxPx = (int)$GLOBALS['settings']['image_px'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$uploadToken = bin2hex(random_bytes(24));
|
||||||
|
if (!isset($_SESSION['upload_tokens']) || !is_array($_SESSION['upload_tokens'])) {
|
||||||
|
$_SESSION['upload_tokens'] = [];
|
||||||
|
}
|
||||||
|
$_SESSION['upload_tokens'][$uploadToken] = [
|
||||||
|
'user_id' => $userId,
|
||||||
|
'expires' => time() + 60 * 20,
|
||||||
|
];
|
||||||
|
|
||||||
|
$cookiePages = [];
|
||||||
|
$cookieMenus = [];
|
||||||
|
if (!empty($_COOKIE['cookie_pages'])) {
|
||||||
|
$decoded = @unserialize($_COOKIE['cookie_pages']);
|
||||||
|
if (is_array($decoded)) {
|
||||||
|
$cookiePages = $decoded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!empty($_COOKIE['cookie_menus'])) {
|
||||||
|
$decoded = @unserialize($_COOKIE['cookie_menus']);
|
||||||
|
if (is_array($decoded)) {
|
||||||
|
$cookieMenus = $decoded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
|
<link type="text/css" rel="stylesheet" href="/libraries/plupload/jquery.plupload.queue/css/jquery.plupload.queue.css" />
|
||||||
|
<link type="text/css" rel="stylesheet" href="/libraries/grid/plugins/impromptu/jquery-impromptu.css" />
|
||||||
|
|
||||||
|
<script type="text/javascript" src="/libraries/grid/plugins/impromptu/jquery-impromptu.js"></script>
|
||||||
|
<script type="text/javascript" src="/libraries/jquery/sortable/sortable.js"></script>
|
||||||
|
<script type="text/javascript" src="/libraries/plupload/plupload.js"></script>
|
||||||
|
<script type="text/javascript" src="/libraries/plupload/plupload.flash.js"></script>
|
||||||
|
<script type="text/javascript" src="/libraries/plupload/plupload.html5.js"></script>
|
||||||
|
<script type="text/javascript" src="/libraries/plupload/plupload.html4.js"></script>
|
||||||
|
<script type="text/javascript" src="/libraries/plupload/jquery.plupload.queue/jquery.plupload.queue.js"></script>
|
||||||
|
<script type="text/javascript" src="/libraries/plupload/i18n/pl.js"></script>
|
||||||
|
<script type="text/javascript" src="/libraries/jquery-nested-sortable/jquery.mjs.nestedSortable.js"></script>
|
||||||
|
<script type="text/javascript" src="/libraries/jquery/lozad.js"></script>
|
||||||
|
|
||||||
|
<style type="text/css">
|
||||||
|
.jconfirm.table-list-confirm-dialog .jconfirm-row {
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jconfirm.table-list-confirm-dialog .jconfirm-box-container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 560px;
|
||||||
|
padding-left: 12px;
|
||||||
|
padding-right: 12px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jconfirm.table-list-confirm-dialog .jconfirm-box {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
var images_count = <?= (int)$imagesCount ?>;
|
||||||
|
var files_count = <?= (int)$filesCount ?>;
|
||||||
|
var article_id = <?= (int)$articleId ?>;
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
function confirmDialog(message, onConfirm, okLabel) {
|
||||||
|
if (typeof $.confirm === 'function') {
|
||||||
|
$.confirm({
|
||||||
|
title: 'Potwierdzenie',
|
||||||
|
content: String(message),
|
||||||
|
type: 'red',
|
||||||
|
boxWidth: '560px',
|
||||||
|
useBootstrap: false,
|
||||||
|
animation: 'scale',
|
||||||
|
closeAnimation: 'scale',
|
||||||
|
backgroundDismissAnimation: 'shake',
|
||||||
|
container: 'body',
|
||||||
|
theme: 'modern',
|
||||||
|
columnClass: '',
|
||||||
|
typeAnimated: true,
|
||||||
|
lazyOpen: false,
|
||||||
|
draggable: false,
|
||||||
|
closeIcon: true,
|
||||||
|
containerFluid: true,
|
||||||
|
escapeKey: true,
|
||||||
|
backgroundDismiss: true,
|
||||||
|
animationBounce: 1.1,
|
||||||
|
watchInterval: 100,
|
||||||
|
offsetTop: 0,
|
||||||
|
offsetBottom: 0,
|
||||||
|
customClass: 'table-list-confirm-dialog',
|
||||||
|
buttons: {
|
||||||
|
cancel: {
|
||||||
|
text: 'Anuluj',
|
||||||
|
btnClass: 'btn-default'
|
||||||
|
},
|
||||||
|
confirm: {
|
||||||
|
text: okLabel || 'Usun',
|
||||||
|
btnClass: 'btn-danger',
|
||||||
|
action: function() {
|
||||||
|
if (typeof onConfirm === 'function') {
|
||||||
|
onConfirm();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.confirm(message) && typeof onConfirm === 'function') {
|
||||||
|
onConfirm();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureGalleryOrderInput() {
|
||||||
|
var $form = $('#fg-article-edit');
|
||||||
|
if (!$form.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var $input = $form.find('input[name="gallery_order"]');
|
||||||
|
if (!$input.length) {
|
||||||
|
$input = $('<input>', {
|
||||||
|
type: 'hidden',
|
||||||
|
name: 'gallery_order',
|
||||||
|
id: 'gallery_order'
|
||||||
|
});
|
||||||
|
$form.append($input);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $input;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureFilesOrderInput() {
|
||||||
|
var $form = $('#fg-article-edit');
|
||||||
|
if (!$form.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var $input = $form.find('input[name="files_order"]');
|
||||||
|
if (!$input.length) {
|
||||||
|
$input = $('<input>', {
|
||||||
|
type: 'hidden',
|
||||||
|
name: 'files_order',
|
||||||
|
id: 'files_order'
|
||||||
|
});
|
||||||
|
$form.append($input);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $input;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildGalleryOrder() {
|
||||||
|
var order = [];
|
||||||
|
$('#images-list li').each(function() {
|
||||||
|
var imageId = $(this).find('a.article_image_delete').attr('image-id');
|
||||||
|
if (imageId) {
|
||||||
|
order.push(imageId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return order.join(';');
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshGalleryOrderInput() {
|
||||||
|
var $input = ensureGalleryOrderInput();
|
||||||
|
if ($input) {
|
||||||
|
$input.val(buildGalleryOrder());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildFilesOrder() {
|
||||||
|
var order = [];
|
||||||
|
$('#files-list li').each(function() {
|
||||||
|
var fileId = $(this).find('.article_file_edit').attr('file_id');
|
||||||
|
if (fileId) {
|
||||||
|
order.push(fileId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return order.join(';');
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshFilesOrderInput() {
|
||||||
|
var $input = ensureFilesOrderInput();
|
||||||
|
if ($input) {
|
||||||
|
$input.val(buildFilesOrder());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ensureGalleryOrderInput();
|
||||||
|
refreshGalleryOrderInput();
|
||||||
|
ensureFilesOrderInput();
|
||||||
|
refreshFilesOrderInput();
|
||||||
|
|
||||||
|
var observer = lozad();
|
||||||
|
observer.observe();
|
||||||
|
|
||||||
|
var imageList = document.getElementById('images-list');
|
||||||
|
if (imageList) {
|
||||||
|
Sortable.create(imageList, {
|
||||||
|
onEnd: function() {
|
||||||
|
var order = '';
|
||||||
|
$('#images-list li').each(function() {
|
||||||
|
if (order !== '') {
|
||||||
|
order += ';';
|
||||||
|
}
|
||||||
|
order += $(this).children('a').attr('image-id');
|
||||||
|
});
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
cache: false,
|
||||||
|
url: '/admin/articles/gallery_order_save/',
|
||||||
|
data: {
|
||||||
|
article_id: article_id,
|
||||||
|
order: order
|
||||||
|
},
|
||||||
|
beforeSend: function() {
|
||||||
|
$('#overlay').show();
|
||||||
|
},
|
||||||
|
success: function(data) {
|
||||||
|
$('#overlay').hide();
|
||||||
|
var response = jQuery.parseJSON(data);
|
||||||
|
if (response.status !== 'ok') {
|
||||||
|
create_error(response.msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
refreshGalleryOrderInput();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var filesList = document.getElementById('files-list');
|
||||||
|
if (filesList) {
|
||||||
|
Sortable.create(filesList, {
|
||||||
|
onEnd: function() {
|
||||||
|
var order = buildFilesOrder();
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
cache: false,
|
||||||
|
url: '/admin/articles/files_order_save/',
|
||||||
|
data: {
|
||||||
|
article_id: article_id,
|
||||||
|
order: order
|
||||||
|
},
|
||||||
|
beforeSend: function() {
|
||||||
|
$('#overlay').show();
|
||||||
|
},
|
||||||
|
success: function(data) {
|
||||||
|
$('#overlay').hide();
|
||||||
|
var response = jQuery.parseJSON(data);
|
||||||
|
if (response.status !== 'ok') {
|
||||||
|
create_error(response.msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
refreshFilesOrderInput();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#images-uploader').pluploadQueue({
|
||||||
|
multipart_params: {
|
||||||
|
upload_token: '<?= htmlspecialchars($uploadToken, ENT_QUOTES, 'UTF-8') ?>',
|
||||||
|
article_id: article_id
|
||||||
|
},
|
||||||
|
runtimes: 'html5,flash,html4',
|
||||||
|
init: {
|
||||||
|
Refresh: function() {
|
||||||
|
$('.plupload_buttons').css('display', 'inline');
|
||||||
|
$('.plupload_upload_status').css('display', 'inline');
|
||||||
|
$('.plupload_start').addClass('plupload_disabled');
|
||||||
|
$('.plupload_start').removeClass('plupload_disabled');
|
||||||
|
},
|
||||||
|
UploadComplete: function() {
|
||||||
|
$('.plupload_buttons').css('display', 'inline');
|
||||||
|
$('.plupload_upload_status').css('display', 'inline');
|
||||||
|
$('.plupload_start').addClass('plupload_disabled');
|
||||||
|
$('.plupload_start').removeClass('plupload_disabled');
|
||||||
|
},
|
||||||
|
FileUploaded: function(up, file, response) {
|
||||||
|
var data = jQuery.parseJSON(response.response);
|
||||||
|
|
||||||
|
$('#images-list').append(
|
||||||
|
'<li id="image-' + data.image_id + '">' +
|
||||||
|
'<img class="article-image lozad" data-src="/libraries/thumb.php?img=' + data.data_link + '&w=300&h=300">' +
|
||||||
|
'<a href="#" class="input-group-addon btn btn-danger article_image_delete" image-id="' + data.image_id + '">' +
|
||||||
|
'<i class="fa fa-trash"></i>' +
|
||||||
|
'</a>' +
|
||||||
|
'<input type="text" class="form-control image-alt" value="" image-id="' + data.image_id + '" placeholder="atrybut alt...">' +
|
||||||
|
'</li>'
|
||||||
|
);
|
||||||
|
images_count++;
|
||||||
|
observer.observe();
|
||||||
|
refreshGalleryOrderInput();
|
||||||
|
|
||||||
|
$('html, body').animate({
|
||||||
|
scrollTop: $('#images-uploader').offset().top
|
||||||
|
}, 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
url: '/libraries/plupload/upload-articles-images.php',
|
||||||
|
chunk_size: '1mb',
|
||||||
|
max_file_size: '20mb',
|
||||||
|
unique_names: false,
|
||||||
|
resize: {
|
||||||
|
width: <?= (int)$imageMaxPx ?>,
|
||||||
|
height: <?= (int)$imageMaxPx ?>,
|
||||||
|
quality: 95
|
||||||
|
},
|
||||||
|
filters: [
|
||||||
|
{ title: 'Obrazki', extensions: 'jpg,gif,png,bmp,jpeg' }
|
||||||
|
],
|
||||||
|
flash_swf_url: '/../libraries/plupload/plupload.flash.swf'
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#files-uploader').pluploadQueue({
|
||||||
|
multipart_params: {
|
||||||
|
upload_token: '<?= htmlspecialchars($uploadToken, ENT_QUOTES, 'UTF-8') ?>'
|
||||||
|
},
|
||||||
|
runtimes: 'html5,flash,html4',
|
||||||
|
init: {
|
||||||
|
Refresh: function() {
|
||||||
|
$('.plupload_buttons').css('display', 'inline');
|
||||||
|
$('.plupload_upload_status').css('display', 'inline');
|
||||||
|
$('.plupload_start').addClass('plupload_disabled');
|
||||||
|
$('.plupload_start').removeClass('plupload_disabled');
|
||||||
|
},
|
||||||
|
FileUploaded: function(up, file, response) {
|
||||||
|
var data = jQuery.parseJSON(response.response);
|
||||||
|
$('#files-list').append(
|
||||||
|
'<li id="file-' + data.file_id + '">' +
|
||||||
|
'<div class="input-group">' +
|
||||||
|
'<input type="text" class="form-control article_file_edit" file_id="' + data.file_id + '" value="' + data.file_name + '" />' +
|
||||||
|
'<a href="#" class="input-group-addon btn btn-info article_file_delete" file_id="' + data.file_id + '">' +
|
||||||
|
'<i class="fa fa-trash"></i>' +
|
||||||
|
'</a>' +
|
||||||
|
'</div>' +
|
||||||
|
'</li>'
|
||||||
|
);
|
||||||
|
files_count++;
|
||||||
|
refreshFilesOrderInput();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
url: '/libraries/plupload/upload-articles-files.php',
|
||||||
|
chunk_size: '1mb',
|
||||||
|
max_file_size: '50mb',
|
||||||
|
unique_names: false,
|
||||||
|
filters: [
|
||||||
|
{ title: 'Wszystkie pliki', extensions: '*' }
|
||||||
|
],
|
||||||
|
flash_swf_url: '/../libraries/plupload/plupload.flash.swf'
|
||||||
|
});
|
||||||
|
|
||||||
|
$('ol.sortable').nestedSortable({
|
||||||
|
forcePlaceholderSize: true,
|
||||||
|
handle: 'div',
|
||||||
|
helper: 'clone',
|
||||||
|
items: 'li',
|
||||||
|
opacity: .6,
|
||||||
|
placeholder: 'placeholder',
|
||||||
|
revert: 250,
|
||||||
|
tabSize: 25,
|
||||||
|
tolerance: 'pointer',
|
||||||
|
toleranceElement: '> div',
|
||||||
|
maxLevels: 3,
|
||||||
|
isTree: true,
|
||||||
|
expandOnHover: 700,
|
||||||
|
isAllowed: function() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.disclose').on('click', function() {
|
||||||
|
$(this).closest('li').toggleClass('sort-collapsed').toggleClass('sort-expanded');
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.disclose').mousedown(function(e) {
|
||||||
|
if (e.which !== 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($(this).parent('div').hasClass('content_menu')) {
|
||||||
|
var menu_id = $(this).parent('div').parent('li').attr('menu');
|
||||||
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
cache: false,
|
||||||
|
url: '/admin/ajax.php',
|
||||||
|
data: {
|
||||||
|
a: 'cookie_menus',
|
||||||
|
menu_id: menu_id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
var page_id = $(this).parent('div').parent('li').attr('id');
|
||||||
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
cache: false,
|
||||||
|
url: '/admin/ajax.php',
|
||||||
|
data: {
|
||||||
|
a: 'cookie_pages',
|
||||||
|
page_id: page_id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
<?php foreach ($cookiePages as $key => $val): ?>
|
||||||
|
<?php if ($val): ?>$('.<?= htmlspecialchars((string)$key, ENT_QUOTES, 'UTF-8') ?>').children('div').children('span.disclose').click();<?php endif; ?>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php foreach ($cookieMenus as $key => $val): ?>
|
||||||
|
<?php if ($val): ?>$('.menu_<?= (int)$key ?>').children('div').children('span.disclose').click();<?php endif; ?>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
|
||||||
|
$('body').on('change', '.image-alt', function() {
|
||||||
|
var image_alt = $(this).val();
|
||||||
|
var image_id = $(this).attr('image-id');
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
cache: false,
|
||||||
|
url: '/admin/articles/article_image_alt_change/',
|
||||||
|
data: {
|
||||||
|
image_id: image_id,
|
||||||
|
image_alt: image_alt
|
||||||
|
},
|
||||||
|
beforeSend: function() {
|
||||||
|
$('#overlay').show();
|
||||||
|
},
|
||||||
|
success: function(data) {
|
||||||
|
$('#overlay').hide();
|
||||||
|
var response = jQuery.parseJSON(data);
|
||||||
|
if (response.status !== 'ok') {
|
||||||
|
create_error(response.msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$('body').on('change', '.article_file_edit', function() {
|
||||||
|
var file_name = $(this).val();
|
||||||
|
var file_id = $(this).attr('file_id');
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
cache: false,
|
||||||
|
url: '/admin/articles/article_file_name_change/',
|
||||||
|
data: {
|
||||||
|
file_id: file_id,
|
||||||
|
file_name: file_name
|
||||||
|
},
|
||||||
|
beforeSend: function() {
|
||||||
|
$('#overlay').show();
|
||||||
|
},
|
||||||
|
success: function(data) {
|
||||||
|
$('#overlay').hide();
|
||||||
|
var response = jQuery.parseJSON(data);
|
||||||
|
if (response.status !== 'ok') {
|
||||||
|
create_error(response.msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$('body').on('click', '.article_file_delete', function() {
|
||||||
|
$(this).blur();
|
||||||
|
var file_id = $(this).attr('file_id');
|
||||||
|
|
||||||
|
confirmDialog('Na pewno chcesz usunac wybrany plik?', function() {
|
||||||
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
cache: false,
|
||||||
|
url: '/admin/articles/article_file_delete/',
|
||||||
|
data: {
|
||||||
|
file_id: file_id
|
||||||
|
},
|
||||||
|
beforeSend: function() {
|
||||||
|
$('#file-' + file_id).children('input').addClass('disabled');
|
||||||
|
$('#file-' + file_id).children('a').addClass('disabled');
|
||||||
|
},
|
||||||
|
success: function(data) {
|
||||||
|
var response = jQuery.parseJSON(data);
|
||||||
|
if (response.status === 'ok') {
|
||||||
|
$('#file-' + file_id).remove();
|
||||||
|
refreshFilesOrderInput();
|
||||||
|
} else {
|
||||||
|
create_error(response.msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, 'Usun');
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
$('body').on('click', '.article_image_delete', function() {
|
||||||
|
$(this).blur();
|
||||||
|
var image_id = $(this).attr('image-id');
|
||||||
|
|
||||||
|
confirmDialog('Na pewno chcesz usunac wybrane zdjecie?', function() {
|
||||||
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
cache: false,
|
||||||
|
url: '/admin/articles/article_image_delete/',
|
||||||
|
data: {
|
||||||
|
image_id: image_id
|
||||||
|
},
|
||||||
|
beforeSend: function() {
|
||||||
|
$('#overlay').show();
|
||||||
|
},
|
||||||
|
success: function(data) {
|
||||||
|
$('#overlay').hide();
|
||||||
|
var response = jQuery.parseJSON(data);
|
||||||
|
if (response.status === 'ok') {
|
||||||
|
$('#image-' + image_id).remove();
|
||||||
|
refreshGalleryOrderInput();
|
||||||
|
} else {
|
||||||
|
create_error(response.msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, 'Usun');
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
$('body').on('click', '#g-edit-save, #g-edit-save-close', function() {
|
||||||
|
refreshGalleryOrderInput();
|
||||||
|
refreshFilesOrderInput();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function generate_seo_links(lang, title, article_id) {
|
||||||
|
if (title === '') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
cache: false,
|
||||||
|
url: '/admin/ajax.php',
|
||||||
|
data: {
|
||||||
|
a: 'generate_seo_link',
|
||||||
|
title: title,
|
||||||
|
article_id: article_id
|
||||||
|
},
|
||||||
|
beforeSend: function() {
|
||||||
|
$('#seo_link_' + lang).parents('.g-form-data').find('input, a').each(function() {
|
||||||
|
$(this).prop('disabled', true).addClass('disabled');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
success: function(data) {
|
||||||
|
$('#seo_link_' + lang).parents('.g-form-data').find('input, a').each(function() {
|
||||||
|
$(this).prop('disabled', false).removeClass('disabled');
|
||||||
|
});
|
||||||
|
|
||||||
|
var response = jQuery.parseJSON(data);
|
||||||
|
if (response.status === 'ok') {
|
||||||
|
$('#seo_link_' + lang).val(response.seo_link);
|
||||||
|
} else {
|
||||||
|
create_error(response.msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -1,804 +1,6 @@
|
|||||||
<script type="text/javascript" src="/libraries/ckeditor/ckeditor.js"></script>
|
<?= \Tpl::view('components/form-edit', ['form' => $this->form]); ?>
|
||||||
<script type="text/javascript" src="/libraries/ckeditor/adapters/jquery.js"></script>
|
<?= \Tpl::view('articles/article-edit-custom-script', [
|
||||||
<script type="text/javascript" src="/libraries/jquery/sortable/sortable.js"></script>
|
'article' => $this->article,
|
||||||
<?
|
'user' => $this->user,
|
||||||
global $db;
|
'languages' => $this->form->languages ?? []
|
||||||
|
]); ?>
|
||||||
$upload_token = bin2hex( random_bytes(24) );
|
|
||||||
$_SESSION['upload_tokens'][$upload_token] = [
|
|
||||||
'user_id' => $this -> user['id'],
|
|
||||||
'expires' => time() + 60*20
|
|
||||||
];
|
|
||||||
|
|
||||||
$_SESSION['rfm_akey'] = bin2hex(random_bytes(16));
|
|
||||||
$_SESSION['rfm_akey_expires'] = time() + 20*60;
|
|
||||||
$_SESSION['can_use_rfm'] = true;
|
|
||||||
$rfmAkeyJS = $_SESSION['rfm_akey'];
|
|
||||||
|
|
||||||
ob_start();
|
|
||||||
?>
|
|
||||||
<div id="settings-tabs">
|
|
||||||
<ul class="resp-tabs-list settings-tabs">
|
|
||||||
<li><i class="fa fa-file"></i>Treść</li>
|
|
||||||
<li><i class="fa fa-wrench"></i>Ustawienia</li>
|
|
||||||
<li><i class="fa fa-globe"></i>SEO</li>
|
|
||||||
<li><i class="fa fa-share-alt"></i>Wyświetlanie</li>
|
|
||||||
<li><i class="fa fa-file-image-o"></i>Galeria</li>
|
|
||||||
<li><i class="fa fa-file-archive-o"></i>Załączniki</li>
|
|
||||||
</ul>
|
|
||||||
<div class="resp-tabs-container settings-tabs">
|
|
||||||
<div>
|
|
||||||
<div id="languages-main">
|
|
||||||
<ul class="resp-tabs-list languages-main htabs">
|
|
||||||
<? if ( is_array( $this -> languages ) ): foreach ( $this -> languages as $lg ):?>
|
|
||||||
<? if ( $lg['status'] ):?>
|
|
||||||
<li><? if ( $lg['id'] == \front\factory\Languages::default_language() ) echo '<i class="fa fa-star fa-lg text-system" title="Język domyślny"></i> ';?><?= $lg['name'];?></a></li>
|
|
||||||
<? endif;?>
|
|
||||||
<? endforeach; endif;?>
|
|
||||||
</ul>
|
|
||||||
<div class="resp-tabs-container languages-main">
|
|
||||||
<? if ( is_array( $this -> languages ) ): foreach ( $this -> languages as $lg ):?>
|
|
||||||
<?
|
|
||||||
$languages = array();
|
|
||||||
|
|
||||||
$languages[''] = '---- wersja językowa ----';
|
|
||||||
if ( is_array( $this -> languages ) ) foreach ( $this -> languages as $lg_tmp )
|
|
||||||
{
|
|
||||||
if ( $lg_tmp['id'] != $lg['id'] )
|
|
||||||
$languages[ $lg_tmp['id'] ] = $lg_tmp['name'];
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
<? if ( $lg['status'] ):?>
|
|
||||||
<div>
|
|
||||||
<?= \Html::select(
|
|
||||||
array(
|
|
||||||
'label' => 'Wyświetlaj treść z wersji',
|
|
||||||
'name' => 'copy_from[' . $lg['id'] . ']',
|
|
||||||
'values' => $languages,
|
|
||||||
'value' => $this -> article['languages'][ $lg['id'] ]['copy_from'],
|
|
||||||
)
|
|
||||||
);?>
|
|
||||||
<?= \Html::input(
|
|
||||||
array(
|
|
||||||
'label' => 'Tytuł',
|
|
||||||
'name' => 'title[' . $lg['id'] . ']',
|
|
||||||
'id' => 'title_' . $lg['id'],
|
|
||||||
'value' => $this -> article['languages'][ $lg['id'] ]['title'],
|
|
||||||
'inline' => true
|
|
||||||
)
|
|
||||||
);?>
|
|
||||||
<?= \Html::input_icon( [
|
|
||||||
'label' => 'Zdjęcie tytułowe',
|
|
||||||
'name' => 'main_image[' . $lg['id'] . ']',
|
|
||||||
'id' => 'main_image_' . $lg['id'],
|
|
||||||
'value' => htmlspecialchars( $this -> article['languages'][ $lg['id'] ]['main_image'] ),
|
|
||||||
'icon_content' => 'przeglądaj',
|
|
||||||
'inline' => true,
|
|
||||||
'icon_js' => "window.open ( '/libraries/filemanager-9.14.2/dialog.php?type=1&popup=1&field_id=main_image_" . $lg['id'] . "&akey=" . $rfmAkeyJS . "', 'mywindow', 'location=1,status=1,scrollbars=1, width=1100,height=700');"
|
|
||||||
] );
|
|
||||||
?>
|
|
||||||
<?= \Html::textarea(
|
|
||||||
array(
|
|
||||||
'label' => 'Wstęp',
|
|
||||||
'name' => 'entry[' . $lg['id'] . ']',
|
|
||||||
'id' => 'entry_' . $lg['id'],
|
|
||||||
'value' => $this -> article['languages'][ $lg['id'] ]['entry'],
|
|
||||||
'inline' => true
|
|
||||||
)
|
|
||||||
);?>
|
|
||||||
<?= \Html::textarea(
|
|
||||||
array(
|
|
||||||
'label' => 'Treść',
|
|
||||||
'name' => 'text[' . $lg['id'] . ']',
|
|
||||||
'id' => 'text_' . $lg['id'],
|
|
||||||
'value' => $this -> article['languages'][ $lg['id'] ]['text'],
|
|
||||||
'inline' => true
|
|
||||||
)
|
|
||||||
);?>
|
|
||||||
<?= \Html::textarea( [
|
|
||||||
'label' => 'Spis treści',
|
|
||||||
'name' => 'table_of_contents[' . $lg['id'] . ']',
|
|
||||||
'id' => 'table_of_contents_' . $lg['id'],
|
|
||||||
'value' => $this -> article['languages'][ $lg['id'] ]['table_of_contents'],
|
|
||||||
'inline' => true
|
|
||||||
] );?>
|
|
||||||
<script type="text/javascript">
|
|
||||||
$( function() {
|
|
||||||
$( '#text_<?= $lg['id'];?>, #entry_<?= $lg['id'];?>, #table_of_contents_<?= $lg['id'];?>' ).ckeditor( {
|
|
||||||
toolbar : 'MyToolbar',
|
|
||||||
height:'250',
|
|
||||||
filebrowserBrowseUrl: '/libraries/filemanager-9.14.2/dialog.php?type=2&editor=ckeditor&akey=<?= $rfmAkeyJS;?>',
|
|
||||||
filebrowserImageBrowseUrl: '/libraries/filemanager-9.14.2/dialog.php?type=1&editor=ckeditor&akey=<?= $rfmAkeyJS;?>',
|
|
||||||
filebrowserUploadUrl: '/libraries/filemanager-9.14.2/dialog.php?type=2&editor=ckeditor&upload=1&akey=<?= $rfmAkeyJS;?>',
|
|
||||||
filebrowserImageUploadUrl: '/libraries/filemanager-9.14.2/dialog.php?type=1&editor=ckeditor&upload=1&akey=<?= $rfmAkeyJS;?>'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</div>
|
|
||||||
<? endif;?>
|
|
||||||
<? endforeach; endif;?>
|
|
||||||
</div>
|
|
||||||
<div class="clear"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<?= \Html::input_switch(
|
|
||||||
array(
|
|
||||||
'label' => 'Opublikowany',
|
|
||||||
'name' => 'status',
|
|
||||||
'checked' => $this -> article['status'] == 1 or !$this -> article['id'] ? true : false
|
|
||||||
)
|
|
||||||
);?>
|
|
||||||
<?= \Html::input_switch(
|
|
||||||
array(
|
|
||||||
'label' => 'Pokaż tytuł',
|
|
||||||
'name' => 'show_title',
|
|
||||||
'checked' => $this -> article['show_title'] == 1 ? true : false
|
|
||||||
)
|
|
||||||
);?>
|
|
||||||
<?= \Html::input_switch( [
|
|
||||||
'label' => 'Pokaż spis treści',
|
|
||||||
'name' => 'show_table_of_contents',
|
|
||||||
'checked' => $this -> article['show_table_of_contents'] == 1 ? true : false
|
|
||||||
] );?>
|
|
||||||
<?= \Html::input_switch(
|
|
||||||
array(
|
|
||||||
'label' => 'Pokaż datę dodania',
|
|
||||||
'name' => 'show_date_add',
|
|
||||||
'checked' => $this -> article['show_date_add'] == 1 ? true : false
|
|
||||||
)
|
|
||||||
);?>
|
|
||||||
<?= \Html::input_switch(
|
|
||||||
array(
|
|
||||||
'label' => 'Pokaż datę modyfikacji',
|
|
||||||
'name' => 'show_date_modify',
|
|
||||||
'checked' => $this -> article['show_date_modify'] == 1 ? true : false
|
|
||||||
)
|
|
||||||
);?>
|
|
||||||
<?= \Html::input_switch(
|
|
||||||
array(
|
|
||||||
'label' => 'Powtórz wprowadzenie',
|
|
||||||
'name' => 'repeat_entry',
|
|
||||||
'checked' => $this -> article['repeat_entry'] == 1 ? true : false
|
|
||||||
)
|
|
||||||
);?>
|
|
||||||
<?= \Html::input_switch(
|
|
||||||
array(
|
|
||||||
'label' => 'Linki do portali społecznościowych',
|
|
||||||
'name' => 'social_icons',
|
|
||||||
'checked' => $this -> article['social_icons'] == 1 ? true : false
|
|
||||||
)
|
|
||||||
);?>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div id="languages-seo">
|
|
||||||
<ul class="resp-tabs-list languages-seo htabs">
|
|
||||||
<? if ( is_array( $this -> languages ) ): foreach ( $this -> languages as $lg ):?>
|
|
||||||
<? if ( $lg['status'] ):?>
|
|
||||||
<li><? if ( $lg['id'] == \front\factory\Languages::default_language() ) echo '<i class="fa fa-star fa-lg text-system" title="Język domyślny"></i> ';?><?= $lg['name'];?></a></li>
|
|
||||||
<? endif;?>
|
|
||||||
<? endforeach; endif;?>
|
|
||||||
</ul>
|
|
||||||
<div class="resp-tabs-container languages-seo">
|
|
||||||
<? if ( is_array( $this -> languages ) ): foreach ( $this -> languages as $lg ):?>
|
|
||||||
<? if ( $lg['status'] ):?>
|
|
||||||
<div>
|
|
||||||
<?= \Html::input_icon(
|
|
||||||
array(
|
|
||||||
'label' => 'Link SEO',
|
|
||||||
'name' => 'seo_link[' . $lg['id'] . ']',
|
|
||||||
'id' => 'seo_link_' . $lg['id'],
|
|
||||||
'value' => $this -> article['languages' ][ $lg['id'] ]['seo_link'],
|
|
||||||
'icon_content' => 'generuj',
|
|
||||||
'icon_js' => 'generate_seo_links( "' . $lg['id'] . '", $( "#title_' . $lg['id'] . '" ).val(), ' . (int)$this -> article['id'] . ' );'
|
|
||||||
)
|
|
||||||
);?>
|
|
||||||
<?= \Html::input( [
|
|
||||||
'label' => 'Meta title',
|
|
||||||
'name' => 'meta_title[' . $lg['id'] . ']',
|
|
||||||
'id' => 'meta_title_' . $lg['id'],
|
|
||||||
'value' => $this -> article['languages'][ $lg['id'] ]['meta_title']
|
|
||||||
]
|
|
||||||
);?>
|
|
||||||
<?= \Html::textarea(
|
|
||||||
array(
|
|
||||||
'label' => 'Meta description',
|
|
||||||
'name' => 'meta_description[' . $lg['id'] . ']',
|
|
||||||
'id' => 'meta_description_' . $lg['id'],
|
|
||||||
'value' => $this -> article['languages'][ $lg['id'] ]['meta_description']
|
|
||||||
)
|
|
||||||
);?>
|
|
||||||
<?= \Html::textarea(
|
|
||||||
array(
|
|
||||||
'label' => 'Meta keywords',
|
|
||||||
'name' => 'meta_keywords[' . $lg['id'] . ']',
|
|
||||||
'id' => 'meta_keywords_' . $lg['id'],
|
|
||||||
'value' => $this -> article['languages'][ $lg['id'] ]['meta_keywords']
|
|
||||||
)
|
|
||||||
);?>
|
|
||||||
<?= \Html::input_switch( [
|
|
||||||
'label' => 'Blokuj indeksację',
|
|
||||||
'name' => 'noindex[' . $lg['id'] . ']',
|
|
||||||
'id' => 'noindex_' . $lg['id'],
|
|
||||||
'checked' => $this -> article['languages'][ $lg['id'] ]['noindex'] == 1 ? 1 : 0
|
|
||||||
]
|
|
||||||
);?>
|
|
||||||
<?= \Html::input_switch( [
|
|
||||||
'label' => 'Blokuj bezpośredni dostęp',
|
|
||||||
'name' => 'block_direct_access[' . $lg['id'] . ']',
|
|
||||||
'id' => 'block_direct_access_' . $lg['id'],
|
|
||||||
'checked' => $this -> article['languages'][ $lg['id'] ]['block_direct_access'] == 1 ? 1 : 0
|
|
||||||
] );?>
|
|
||||||
</div>
|
|
||||||
<? endif;?>
|
|
||||||
<? endforeach; endif;?>
|
|
||||||
</div>
|
|
||||||
<div class="clear"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<?
|
|
||||||
$layouts[''] = '---- szablon domyślny ----';
|
|
||||||
if ( is_array( $this -> layouts ) ): foreach ( $this -> layouts as $layout ):
|
|
||||||
$layouts[ $layout['id'] ] = $layout['name'];
|
|
||||||
endforeach; endif;
|
|
||||||
?>
|
|
||||||
<?= \Html::select(
|
|
||||||
array(
|
|
||||||
'label' => 'Szablon',
|
|
||||||
'name' => 'layout_id',
|
|
||||||
'id' => 'layout_id',
|
|
||||||
'values' => $layouts,
|
|
||||||
'value' => $this -> article['layout_id']
|
|
||||||
)
|
|
||||||
);?>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="col-lg-4 control-label">Wyświetlaj na:</label>
|
|
||||||
<div class="col-lg-8">
|
|
||||||
<?
|
|
||||||
if ( is_array( $this -> menus ) ) foreach ( $this -> menus as $menu )
|
|
||||||
{
|
|
||||||
?>
|
|
||||||
<div class="menu_sortable">
|
|
||||||
<ol class="sortable" id="sortable_<?= $menu['id'];?>">
|
|
||||||
<li id="list_<?= $menu['id'];?>" class="menu_<?= $menu['id'];?>" menu="<?= $menu['id'];?>">
|
|
||||||
<div class="context_0 content content_menu" <? if ( !$menu['status'] ) echo 'style="color: #cc0000;"';?>>
|
|
||||||
<span class="disclose"><span></span></span>
|
|
||||||
Menu: <b><?= $menu['name'];?></b>
|
|
||||||
</div>
|
|
||||||
<?= \admin\view\Articles::subpages_list( \admin\factory\Pages::menu_pages( $menu['id'] ), $this -> article['pages'] );?>
|
|
||||||
</li>
|
|
||||||
</ol>
|
|
||||||
</div>
|
|
||||||
<?
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
</div>
|
|
||||||
<div class="clear"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<ul id="images-list">
|
|
||||||
<?
|
|
||||||
$images_count = 0;
|
|
||||||
if ( is_array( $this -> article['images'] ) ): foreach ( $this -> article['images'] as $img ):
|
|
||||||
?>
|
|
||||||
<li id="image-<?= $img['id'];?>">
|
|
||||||
<img class="article-image lozad" data-src="/libraries/thumb.php?img=<?= $img['src'];?>&w=300&h=300">
|
|
||||||
<a href="#" class="input-group-addon btn btn-danger article_image_delete" image-id="<?= $img['id'];?>">
|
|
||||||
<i class="fa fa-trash"></i>
|
|
||||||
</a>
|
|
||||||
<input type="text" class="form-control image-alt" value="<?= $img['alt'];?>" image-id="<?= $img['id'];?>" placeholder="atrybut alt...">
|
|
||||||
</li>
|
|
||||||
<?
|
|
||||||
$images_count++;
|
|
||||||
endforeach; endif;
|
|
||||||
?>
|
|
||||||
</ul>
|
|
||||||
<div id="images-uploader">You browser doesn't have Flash installed.</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<ul id="files-list">
|
|
||||||
<?
|
|
||||||
$files_count = 0;
|
|
||||||
if ( is_array( $this -> article['files'] ) ): foreach ( $this -> article['files'] as $file ):
|
|
||||||
|
|
||||||
if ( $file['name'] )
|
|
||||||
$name = $file['name'];
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$name = explode( '/', $file['src'] );
|
|
||||||
$name = $name[ count( $name ) - 1 ];
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
<li id="file-<?= $file['id'];?>">
|
|
||||||
<div class="input-group">
|
|
||||||
<input type="text" class="article_file_edit form-control" file_id="<?= $file['id'];?>" value="<?= $name;?>" />
|
|
||||||
<a href="#" class="input-group-addon btn btn-info article_file_delete" file_id="<?= $file['id'];?>">
|
|
||||||
<i class="fa fa-trash"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<?
|
|
||||||
$files_count++;
|
|
||||||
endforeach; endif;
|
|
||||||
?>
|
|
||||||
</ul>
|
|
||||||
<div id="files-uploader">You browser doesn't have Flash installed.</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<?
|
|
||||||
$out = ob_get_clean();
|
|
||||||
|
|
||||||
$grid = new \gridEdit;
|
|
||||||
$grid -> id = 'article-edit';
|
|
||||||
$grid -> gdb_opt = $gdb;
|
|
||||||
$grid -> include_plugins = true;
|
|
||||||
$grid -> title = 'Edycja artykułu';
|
|
||||||
$grid -> fields = [
|
|
||||||
[
|
|
||||||
'db' => 'id',
|
|
||||||
'type' => 'hidden',
|
|
||||||
'value' => $this -> article['id']
|
|
||||||
]
|
|
||||||
];
|
|
||||||
$grid -> actions = [
|
|
||||||
'save' => [ 'url' => '/admin/articles/article_save/', 'back_url' => '/admin/articles/view_list/' ],
|
|
||||||
'cancel' => [ 'url' => '/admin/articles/view_list/' ]
|
|
||||||
];
|
|
||||||
$grid -> external_code = $out;
|
|
||||||
$grid -> persist_edit = true;
|
|
||||||
$grid -> id_param = 'id';
|
|
||||||
|
|
||||||
echo $grid -> draw();
|
|
||||||
?>
|
|
||||||
|
|
||||||
<link type="text/css" rel="stylesheet" href="/libraries/plupload/jquery.plupload.queue/css/jquery.plupload.queue.css" />
|
|
||||||
|
|
||||||
<script type="text/javascript" src="/libraries/plupload/plupload.js"></script>
|
|
||||||
<script type="text/javascript" src="/libraries/plupload/plupload.flash.js"></script>
|
|
||||||
<script type="text/javascript" src="/libraries/plupload/plupload.html5.js"></script>
|
|
||||||
<script type="text/javascript" src="/libraries/plupload/plupload.html4.js"></script>
|
|
||||||
<script type="text/javascript" src="/libraries/plupload/jquery.plupload.queue/jquery.plupload.queue.js"></script>
|
|
||||||
<script type="text/javascript" src="/libraries/plupload/i18n/pl.js"></script>
|
|
||||||
<script type="text/javascript" src="/libraries/jquery-nested-sortable/jquery.mjs.nestedSortable.js"></script>
|
|
||||||
<script type="text/javascript" src="/libraries/jquery/lozad.js"></script>
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
var images_count = <?= $images_count;?>;
|
|
||||||
var files_count = <?= $files_count;?>;
|
|
||||||
|
|
||||||
$( function()
|
|
||||||
{
|
|
||||||
const observer = lozad();
|
|
||||||
observer.observe();
|
|
||||||
|
|
||||||
disable_menu();
|
|
||||||
|
|
||||||
var el = document.getElementById( 'images-list' );
|
|
||||||
var sortable = Sortable.create(el, {
|
|
||||||
onEnd: function ( evt ) {
|
|
||||||
var order = '';
|
|
||||||
$( '#images-list li' ).each( function()
|
|
||||||
{
|
|
||||||
if ( order !== '' )
|
|
||||||
order += ';';
|
|
||||||
order += $( this ).children( 'a' ).attr( 'image-id' );
|
|
||||||
});
|
|
||||||
|
|
||||||
$.ajax(
|
|
||||||
{
|
|
||||||
type: 'POST',
|
|
||||||
cache: false,
|
|
||||||
url: '/admin/articles/gallery_order_save/',
|
|
||||||
data:
|
|
||||||
{
|
|
||||||
article_id: '<?= $this -> article['id'];?>',
|
|
||||||
order: order
|
|
||||||
},
|
|
||||||
beforeSend: function()
|
|
||||||
{
|
|
||||||
$( '#overlay' ).show();
|
|
||||||
},
|
|
||||||
success: function( data )
|
|
||||||
{
|
|
||||||
$( '#overlay' ).hide();
|
|
||||||
|
|
||||||
response = jQuery.parseJSON( data );
|
|
||||||
|
|
||||||
if ( response.status !== 'ok' )
|
|
||||||
create_error( response.msg );
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$( "#images-uploader" ).pluploadQueue(
|
|
||||||
{
|
|
||||||
multipart_params: {
|
|
||||||
upload_token: '<?= $upload_token ?>',
|
|
||||||
},
|
|
||||||
runtimes: 'html5,flash,html4',
|
|
||||||
init:
|
|
||||||
{
|
|
||||||
Refresh: function(up)
|
|
||||||
{
|
|
||||||
$( ".plupload_buttons" ).css( "display", "inline" );
|
|
||||||
$( ".plupload_upload_status" ).css( "display", "inline" );
|
|
||||||
$( ".plupload_start" ).addClass( "plupload_disabled" );
|
|
||||||
$( ".plupload_start" ).removeClass( "plupload_disabled" );
|
|
||||||
},
|
|
||||||
UploadComplete: function()
|
|
||||||
{
|
|
||||||
$( ".plupload_buttons" ).css( "display", "inline" );
|
|
||||||
$( ".plupload_upload_status" ).css( "display", "inline" );
|
|
||||||
$( ".plupload_start" ).addClass( "plupload_disabled" );
|
|
||||||
$( ".plupload_start" ).removeClass( "plupload_disabled" );
|
|
||||||
},
|
|
||||||
FileUploaded: function( up, file, response )
|
|
||||||
{
|
|
||||||
data = jQuery.parseJSON( response.response );
|
|
||||||
$( '#images-list' ).append(
|
|
||||||
'<li id="image-' + data.image_id + '">' +
|
|
||||||
'<img class="article-image lozad" data-src="/libraries/thumb.php?img=' + data.data_link + '&w=300&h=300">' +
|
|
||||||
'<a href="#" class="input-group-addon btn btn-danger article_image_delete" image-id="' + data.image_id + '">' +
|
|
||||||
'<i class="fa fa-trash"></i>' +
|
|
||||||
'</a>' +
|
|
||||||
'<input type="text" class="form-control image-alt" value="" image-id="' + data.image_id + '" placeholder="atrybut alt...">' +
|
|
||||||
'</li>'
|
|
||||||
);
|
|
||||||
images_count++;
|
|
||||||
observer.observe();
|
|
||||||
$( 'html, body' ).animate({
|
|
||||||
scrollTop: $( "#images-uploader" ).offset().top
|
|
||||||
}, 1 );
|
|
||||||
}
|
|
||||||
},
|
|
||||||
url: '/libraries/plupload/upload-articles-images.php',
|
|
||||||
chunk_size: '1mb',
|
|
||||||
unique_names: false,
|
|
||||||
resize : {
|
|
||||||
width : <?= $this -> settings['image_px'] ? $this -> settings['image_px'] : '1920';?>,
|
|
||||||
height : <?= $this -> settings['image_px'] ? $this -> settings['image_px'] : '1920';?>,
|
|
||||||
quality : 95
|
|
||||||
},
|
|
||||||
filters: [
|
|
||||||
{ title: "Obrazki", extensions : "jpg, gif, png, bmp, jpeg" }
|
|
||||||
],
|
|
||||||
flash_swf_url: '/../libraries/plupload/plupload.flash.swf'
|
|
||||||
});
|
|
||||||
|
|
||||||
$( "#files-uploader" ).pluploadQueue(
|
|
||||||
{
|
|
||||||
multipart_params: {
|
|
||||||
upload_token: '<?= $upload_token ?>',
|
|
||||||
},
|
|
||||||
runtimes: 'html5,flash,html4',
|
|
||||||
init:
|
|
||||||
{
|
|
||||||
Refresh: function( up )
|
|
||||||
{
|
|
||||||
$( ".plupload_buttons" ).css( "display", "inline" );
|
|
||||||
$( ".plupload_upload_status" ).css( "display", "inline" );
|
|
||||||
$( ".plupload_start" ).addClass( "plupload_disabled" );
|
|
||||||
$( ".plupload_start" ).removeClass( "plupload_disabled" );
|
|
||||||
},
|
|
||||||
FileUploaded: function( up, file, response )
|
|
||||||
{
|
|
||||||
data = jQuery.parseJSON( response.response );
|
|
||||||
$( '#files-list' ).append(
|
|
||||||
'<li id="file-' + data.file_id + '">' +
|
|
||||||
'<div class="input-group">' +
|
|
||||||
'<input type="text" class="form-control article_file_edit" file_id="' + data.file_id + '" value="' + data.file_name + '" />' +
|
|
||||||
'<a href="#" class="input-group-addon btn btn-info article_file_delete" file_id="' + data.file_id + '">' +
|
|
||||||
'<i class="fa fa-trash"></i>' +
|
|
||||||
'</a>' +
|
|
||||||
'</div>' +
|
|
||||||
'</li>'
|
|
||||||
);
|
|
||||||
files_count++;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
url: '/libraries/plupload/upload-articles-files.php',
|
|
||||||
chunk_size: '1mb',
|
|
||||||
unique_names: false,
|
|
||||||
filters: [
|
|
||||||
{ title: "Wszystkie pliki", extensions: "*" }
|
|
||||||
],
|
|
||||||
flash_swf_url: '/../libraries/plupload/plupload.flash.swf'
|
|
||||||
});
|
|
||||||
|
|
||||||
$( '#settings-tabs' ).easyResponsiveTabs({
|
|
||||||
width: 'auto',
|
|
||||||
fit: true,
|
|
||||||
tabidentify: 'settings-tabs',
|
|
||||||
type: 'vertical'
|
|
||||||
});
|
|
||||||
|
|
||||||
$( '#languages-main' ).easyResponsiveTabs({
|
|
||||||
width: 'auto',
|
|
||||||
fit: true,
|
|
||||||
tabidentify: 'languages-main'
|
|
||||||
});
|
|
||||||
|
|
||||||
$( '#languages-seo' ).easyResponsiveTabs({
|
|
||||||
width: 'auto',
|
|
||||||
fit: true,
|
|
||||||
tabidentify: 'languages-seo'
|
|
||||||
});
|
|
||||||
|
|
||||||
$('ol.sortable').nestedSortable({
|
|
||||||
forcePlaceholderSize: true,
|
|
||||||
handle: 'div',
|
|
||||||
helper: 'clone',
|
|
||||||
items: 'li',
|
|
||||||
opacity: .6,
|
|
||||||
placeholder: 'placeholder',
|
|
||||||
revert: 250,
|
|
||||||
tabSize: 25,
|
|
||||||
tolerance: 'pointer',
|
|
||||||
toleranceElement: '> div',
|
|
||||||
maxLevels: 3,
|
|
||||||
isTree: true,
|
|
||||||
expandOnHover: 700,
|
|
||||||
isAllowed: function()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$( '.disclose' ).on( 'click', function() {
|
|
||||||
$( this ).closest( 'li' ).toggleClass( 'sort-collapsed' ).toggleClass( 'sort-expanded' );
|
|
||||||
});
|
|
||||||
|
|
||||||
$( '.disclose' ).mousedown( function(e) {
|
|
||||||
if ( e.which === 1 ) {
|
|
||||||
if ( $( this ).parent( 'div' ).hasClass( 'content_menu' ) )
|
|
||||||
{
|
|
||||||
var menu_id = $( this ).parent( 'div' ).parent( 'li' ).attr( 'menu' );
|
|
||||||
|
|
||||||
$.ajax(
|
|
||||||
{
|
|
||||||
type: 'POST',
|
|
||||||
cache: false,
|
|
||||||
url: '/admin/ajax.php',
|
|
||||||
data: {
|
|
||||||
a: 'cookie_menus',
|
|
||||||
menu_id: menu_id
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var page_id = $( this ).parent( 'div' ).parent( 'li' ).attr( 'id' );
|
|
||||||
|
|
||||||
$.ajax(
|
|
||||||
{
|
|
||||||
type: 'POST',
|
|
||||||
cache: false,
|
|
||||||
url: '/admin/ajax.php',
|
|
||||||
data: {
|
|
||||||
a: 'cookie_pages',
|
|
||||||
page_id: page_id
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
<?php
|
|
||||||
$array = unserialize( $_COOKIE[ 'cookie_pages' ] );
|
|
||||||
if ( is_array( $array ) ): foreach ( $array as $key => $val ):
|
|
||||||
if ( $val ):
|
|
||||||
?>$( '.<?= $key;?>' ).children( 'div' ).children( 'span.disclose' ).click();<?
|
|
||||||
endif;
|
|
||||||
endforeach; endif;
|
|
||||||
|
|
||||||
$array = unserialize( $_COOKIE[ 'cookie_menus' ] );
|
|
||||||
if ( is_array( $array ) ): foreach ( $array as $key => $val ):
|
|
||||||
if ( $val ):
|
|
||||||
?>$( '.menu_<?= $key;?>' ).children( 'div' ).children( 'span.disclose' ).click();<?
|
|
||||||
endif;
|
|
||||||
endforeach; endif;
|
|
||||||
?>
|
|
||||||
|
|
||||||
$( 'body' ).on( 'change', '.image-alt', function()
|
|
||||||
{
|
|
||||||
var image_alt = $( this ).val();
|
|
||||||
var image_id = $( this ).attr( 'image-id' );
|
|
||||||
|
|
||||||
$.ajax(
|
|
||||||
{
|
|
||||||
type: 'POST',
|
|
||||||
cache: false,
|
|
||||||
url: '/admin/ajax.php',
|
|
||||||
data:
|
|
||||||
{
|
|
||||||
a: 'article_image_alt_change',
|
|
||||||
image_id: image_id,
|
|
||||||
image_alt: image_alt
|
|
||||||
},
|
|
||||||
beforeSend: function()
|
|
||||||
{
|
|
||||||
$( '#overlay' ).show();
|
|
||||||
},
|
|
||||||
success: function( data )
|
|
||||||
{
|
|
||||||
$( '#overlay' ).hide();
|
|
||||||
|
|
||||||
response = jQuery.parseJSON( data );
|
|
||||||
|
|
||||||
if ( response.status !== 'ok' )
|
|
||||||
create_error( response.msg );
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$( 'body' ).on( 'change', '.article_file_edit', function()
|
|
||||||
{
|
|
||||||
var file_name = $( this ).val();
|
|
||||||
var file_id = $( this ).attr( 'file_id' );
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
type: 'POST',
|
|
||||||
cache: false,
|
|
||||||
url: '/admin/ajax.php',
|
|
||||||
data:
|
|
||||||
{
|
|
||||||
a: 'article_file_name_change',
|
|
||||||
file_id: file_id,
|
|
||||||
file_name: file_name
|
|
||||||
},
|
|
||||||
beforeSend: function()
|
|
||||||
{
|
|
||||||
$( '#overlay' ).show();
|
|
||||||
},
|
|
||||||
success: function( data )
|
|
||||||
{
|
|
||||||
$( '#overlay' ).hide();
|
|
||||||
|
|
||||||
response = jQuery.parseJSON( data );
|
|
||||||
|
|
||||||
if ( response.status !== 'ok' )
|
|
||||||
create_error( response.msg );
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
$( 'body' ).on( 'click', '.article_file_delete', function()
|
|
||||||
{
|
|
||||||
$( this ).blur();
|
|
||||||
var file_id = $( this ).attr( 'file_id' );
|
|
||||||
|
|
||||||
$.prompt( 'Na pewno chcesz usunąć wybrany plik?', {
|
|
||||||
title: 'Potwierdź?',
|
|
||||||
submit: function(e,v,m,f)
|
|
||||||
{
|
|
||||||
if ( v === true )
|
|
||||||
{
|
|
||||||
$.ajax({
|
|
||||||
type: 'POST',
|
|
||||||
cache: false,
|
|
||||||
url: '/admin/ajax.php',
|
|
||||||
data:
|
|
||||||
{
|
|
||||||
a: 'article_file_delete',
|
|
||||||
file_id: file_id
|
|
||||||
},
|
|
||||||
beforeSend: function()
|
|
||||||
{
|
|
||||||
$( '#file-' + file_id ).children( 'input' ).addClass( 'disabled' );
|
|
||||||
$( '#file-' + file_id ).children( 'a' ).addClass( 'disabled' );
|
|
||||||
},
|
|
||||||
success: function( data )
|
|
||||||
{
|
|
||||||
response = jQuery.parseJSON( data );
|
|
||||||
|
|
||||||
if ( response.status === 'ok' )
|
|
||||||
$( '#file-' + file_id ).remove();
|
|
||||||
else
|
|
||||||
create_error( msg );
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
buttons: {
|
|
||||||
'tak': true,
|
|
||||||
'nie': false
|
|
||||||
},
|
|
||||||
focus: 0
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
$( 'body' ).on( 'click', '.article_image_delete', function()
|
|
||||||
{
|
|
||||||
$( this ).blur();
|
|
||||||
var image_id = $( this ).attr( 'image-id' );
|
|
||||||
|
|
||||||
$.prompt( 'Na pewno chcesz usunąć wybrane zdjęcie?',
|
|
||||||
{
|
|
||||||
title: 'Potwierdź?',
|
|
||||||
submit: function(e,v,m,f)
|
|
||||||
{
|
|
||||||
if ( v === true )
|
|
||||||
{
|
|
||||||
$.ajax(
|
|
||||||
{
|
|
||||||
type: 'POST',
|
|
||||||
cache: false,
|
|
||||||
url: '/admin/ajax.php',
|
|
||||||
data:
|
|
||||||
{
|
|
||||||
a: 'article_image_delete',
|
|
||||||
image_id: image_id
|
|
||||||
},
|
|
||||||
beforeSend: function()
|
|
||||||
{
|
|
||||||
$( '#overlay' ).show();
|
|
||||||
},
|
|
||||||
success: function( data )
|
|
||||||
{
|
|
||||||
$( '#overlay' ).hide();
|
|
||||||
response = jQuery.parseJSON( data );
|
|
||||||
|
|
||||||
if ( response.status === 'ok' )
|
|
||||||
$( '#image-' + image_id ).remove();
|
|
||||||
else
|
|
||||||
create_error( msg );
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
buttons: {
|
|
||||||
'tak': true,
|
|
||||||
'nie': false
|
|
||||||
},
|
|
||||||
focus: 0
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function generate_seo_links( lang, title, article_id )
|
|
||||||
{
|
|
||||||
if ( title === '' )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
$.ajax(
|
|
||||||
{
|
|
||||||
type: 'POST',
|
|
||||||
cache: false,
|
|
||||||
url: '/admin/ajax.php',
|
|
||||||
data:
|
|
||||||
{
|
|
||||||
a: 'generate_seo_link',
|
|
||||||
title: title,
|
|
||||||
article_id: article_id
|
|
||||||
},
|
|
||||||
beforeSend: function()
|
|
||||||
{
|
|
||||||
$( '#seo_link_' + lang ).parents( '.g-form-data' ).find( 'input, a' ).each( function()
|
|
||||||
{
|
|
||||||
$( this ).prop( 'disabled', true ).addClass( 'disabled' );
|
|
||||||
});
|
|
||||||
},
|
|
||||||
success: function( data ) {
|
|
||||||
|
|
||||||
$( '#seo_link_' + lang ).parents( '.g-form-data' ).find( 'input, a' ).each( function() {
|
|
||||||
$( this ).prop( 'disabled', false ).removeClass( 'disabled' );
|
|
||||||
});
|
|
||||||
|
|
||||||
response = jQuery.parseJSON( data );
|
|
||||||
|
|
||||||
if ( response.status === 'ok' )
|
|
||||||
$( '#seo_link_' + lang ).val( response.seo_link );
|
|
||||||
else
|
|
||||||
create_error( response.msg );
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<script>CKEDITOR.dtd.$removeEmpty['span'] = false;</script>
|
|
||||||
|
|||||||
@@ -6,11 +6,13 @@
|
|||||||
<span class="disclose"><span></span></span>
|
<span class="disclose"><span></span></span>
|
||||||
<input type="checkbox" class="g-checkbox" name="pages[]" value="<?= $page['id'];?>" <? if ( is_array( $this -> article_pages ) and in_array( $page['id'], $this -> article_pages ) ):?>checked="checked"<? endif;?> /><?= $page['title'];?>
|
<input type="checkbox" class="g-checkbox" name="pages[]" value="<?= $page['id'];?>" <? if ( is_array( $this -> article_pages ) and in_array( $page['id'], $this -> article_pages ) ):?>checked="checked"<? endif;?> /><?= $page['title'];?>
|
||||||
</div>
|
</div>
|
||||||
<?
|
<?= \Tpl::view( 'articles/subpages-list', [
|
||||||
if ( is_array( $page['subpages'] ) )
|
'pages' => $page['subpages'],
|
||||||
echo \admin\view\Articles::subpages_list( $page['subpages'], $this -> article_pages, $page['id'], $this -> step + 1 );
|
'article_pages' => $this->article_pages,
|
||||||
?>
|
'parent_id' => $page['id'],
|
||||||
|
'step' => $this->step + 1,
|
||||||
|
] ); ?>
|
||||||
</li>
|
</li>
|
||||||
<? endforeach;?>
|
<? endforeach;?>
|
||||||
</ol>
|
</ol>
|
||||||
<? endif;?>
|
<? endif;?>
|
||||||
|
|||||||
@@ -949,6 +949,7 @@ echo $grid->draw();
|
|||||||
},
|
},
|
||||||
url: '/libraries/plupload/upload-product-images.php',
|
url: '/libraries/plupload/upload-product-images.php',
|
||||||
chunk_size: '1mb',
|
chunk_size: '1mb',
|
||||||
|
max_file_size: '20mb',
|
||||||
unique_names: false,
|
unique_names: false,
|
||||||
resize: {
|
resize: {
|
||||||
width: <?= $this->settings['image_px'] ? $this->settings['image_px'] : '1920'; ?>,
|
width: <?= $this->settings['image_px'] ? $this->settings['image_px'] : '1920'; ?>,
|
||||||
@@ -991,6 +992,7 @@ echo $grid->draw();
|
|||||||
},
|
},
|
||||||
url: '/libraries/plupload/upload-product-files.php',
|
url: '/libraries/plupload/upload-product-files.php',
|
||||||
chunk_size: '1mb',
|
chunk_size: '1mb',
|
||||||
|
max_file_size: '50mb',
|
||||||
unique_names: false,
|
unique_names: false,
|
||||||
filters: [{
|
filters: [{
|
||||||
title: "Wszystkie pliki",
|
title: "Wszystkie pliki",
|
||||||
|
|||||||
@@ -37,7 +37,15 @@ class ArticleRepository
|
|||||||
'article_id' => $articleId,
|
'article_id' => $articleId,
|
||||||
'ORDER' => ['o' => 'ASC', 'id' => 'DESC']
|
'ORDER' => ['o' => 'ASC', 'id' => 'DESC']
|
||||||
]);
|
]);
|
||||||
$article['files'] = $this->db->select('pp_articles_files', '*', ['article_id' => $articleId]);
|
try {
|
||||||
|
$article['files'] = $this->db->select('pp_articles_files', '*', [
|
||||||
|
'article_id' => $articleId,
|
||||||
|
'ORDER' => ['o' => 'ASC', 'id' => 'DESC']
|
||||||
|
]);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
// Fallback for instances where pp_articles_files does not yet have "o" column.
|
||||||
|
$article['files'] = $this->db->select('pp_articles_files', '*', ['article_id' => $articleId]);
|
||||||
|
}
|
||||||
$article['pages'] = $this->db->select('pp_articles_pages', 'page_id', ['article_id' => $articleId]);
|
$article['pages'] = $this->db->select('pp_articles_pages', 'page_id', ['article_id' => $articleId]);
|
||||||
|
|
||||||
return $article;
|
return $article;
|
||||||
@@ -70,6 +78,8 @@ class ArticleRepository
|
|||||||
$this->savePages($id, $data['pages'] ?? null, true);
|
$this->savePages($id, $data['pages'] ?? null, true);
|
||||||
$this->assignTempFiles($id);
|
$this->assignTempFiles($id);
|
||||||
$this->assignTempImages($id);
|
$this->assignTempImages($id);
|
||||||
|
$this->applyGalleryOrderIfProvided($id, $data);
|
||||||
|
$this->applyFilesOrderIfProvided($id, $data);
|
||||||
|
|
||||||
\S::htacces();
|
\S::htacces();
|
||||||
\S::delete_dir('../temp/');
|
\S::delete_dir('../temp/');
|
||||||
@@ -87,6 +97,8 @@ class ArticleRepository
|
|||||||
$this->savePages($articleId, $data['pages'] ?? null, false);
|
$this->savePages($articleId, $data['pages'] ?? null, false);
|
||||||
$this->assignTempFiles($articleId);
|
$this->assignTempFiles($articleId);
|
||||||
$this->assignTempImages($articleId);
|
$this->assignTempImages($articleId);
|
||||||
|
$this->applyGalleryOrderIfProvided($articleId, $data);
|
||||||
|
$this->applyFilesOrderIfProvided($articleId, $data);
|
||||||
$this->deleteMarkedImages($articleId);
|
$this->deleteMarkedImages($articleId);
|
||||||
$this->deleteMarkedFiles($articleId);
|
$this->deleteMarkedFiles($articleId);
|
||||||
|
|
||||||
@@ -99,16 +111,16 @@ class ArticleRepository
|
|||||||
private function buildArticleRow(array $data, int $userId, bool $isNew): array
|
private function buildArticleRow(array $data, int $userId, bool $isNew): array
|
||||||
{
|
{
|
||||||
$row = [
|
$row = [
|
||||||
'show_title' => ($data['show_title'] ?? '') == 'on' ? 1 : 0,
|
'show_title' => $this->isCheckedValue($data['show_title'] ?? null) ? 1 : 0,
|
||||||
'show_date_add' => ($data['show_date_add'] ?? '') == 'on' ? 1 : 0,
|
'show_date_add' => $this->isCheckedValue($data['show_date_add'] ?? null) ? 1 : 0,
|
||||||
'show_date_modify' => ($data['show_date_modify'] ?? '') == 'on' ? 1 : 0,
|
'show_date_modify' => $this->isCheckedValue($data['show_date_modify'] ?? null) ? 1 : 0,
|
||||||
'date_modify' => date('Y-m-d H:i:s'),
|
'date_modify' => date('Y-m-d H:i:s'),
|
||||||
'modify_by' => $userId,
|
'modify_by' => $userId,
|
||||||
'layout_id' => !empty($data['layout_id']) ? (int)$data['layout_id'] : null,
|
'layout_id' => !empty($data['layout_id']) ? (int)$data['layout_id'] : null,
|
||||||
'status' => ($data['status'] ?? '') == 'on' ? 1 : 0,
|
'status' => $this->isCheckedValue($data['status'] ?? null) ? 1 : 0,
|
||||||
'repeat_entry' => ($data['repeat_entry'] ?? '') == 'on' ? 1 : 0,
|
'repeat_entry' => $this->isCheckedValue($data['repeat_entry'] ?? null) ? 1 : 0,
|
||||||
'social_icons' => ($data['social_icons'] ?? '') == 'on' ? 1 : 0,
|
'social_icons' => $this->isCheckedValue($data['social_icons'] ?? null) ? 1 : 0,
|
||||||
'show_table_of_contents' => ($data['show_table_of_contents'] ?? '') == 'on' ? 1 : 0,
|
'show_table_of_contents' => $this->isCheckedValue($data['show_table_of_contents'] ?? null) ? 1 : 0,
|
||||||
];
|
];
|
||||||
|
|
||||||
if ($isNew) {
|
if ($isNew) {
|
||||||
@@ -131,12 +143,32 @@ class ArticleRepository
|
|||||||
'meta_description' => ($data['meta_description'][$langId] ?? '') != '' ? $data['meta_description'][$langId] : null,
|
'meta_description' => ($data['meta_description'][$langId] ?? '') != '' ? $data['meta_description'][$langId] : null,
|
||||||
'meta_keywords' => ($data['meta_keywords'][$langId] ?? '') != '' ? $data['meta_keywords'][$langId] : null,
|
'meta_keywords' => ($data['meta_keywords'][$langId] ?? '') != '' ? $data['meta_keywords'][$langId] : null,
|
||||||
'seo_link' => \S::seo($data['seo_link'][$langId] ?? '') != '' ? \S::seo($data['seo_link'][$langId]) : null,
|
'seo_link' => \S::seo($data['seo_link'][$langId] ?? '') != '' ? \S::seo($data['seo_link'][$langId]) : null,
|
||||||
'noindex' => ($data['noindex'][$langId] ?? '') == 'on' ? 1 : 0,
|
'noindex' => $this->isCheckedValue($data['noindex'][$langId] ?? null) ? 1 : 0,
|
||||||
'copy_from' => ($data['copy_from'][$langId] ?? '') != '' ? $data['copy_from'][$langId] : null,
|
'copy_from' => ($data['copy_from'][$langId] ?? '') != '' ? $data['copy_from'][$langId] : null,
|
||||||
'block_direct_access' => ($data['block_direct_access'][$langId] ?? '') == 'on' ? 1 : 0,
|
'block_direct_access' => $this->isCheckedValue($data['block_direct_access'][$langId] ?? null) ? 1 : 0,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function applyGalleryOrderIfProvided(int $articleId, array $data): void
|
||||||
|
{
|
||||||
|
$order = trim((string)($data['gallery_order'] ?? ''));
|
||||||
|
if ($order === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->saveGalleryOrder($articleId, $order);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function applyFilesOrderIfProvided(int $articleId, array $data): void
|
||||||
|
{
|
||||||
|
$order = trim((string)($data['files_order'] ?? ''));
|
||||||
|
if ($order === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->saveFilesOrder($articleId, $order);
|
||||||
|
}
|
||||||
|
|
||||||
private function saveTranslations(int $articleId, array $data, bool $isNew): void
|
private function saveTranslations(int $articleId, array $data, bool $isNew): void
|
||||||
{
|
{
|
||||||
$titles = $data['title'] ?? [];
|
$titles = $data['title'] ?? [];
|
||||||
@@ -573,6 +605,182 @@ class ArticleRepository
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zapisuje kolejnosc zalacznikow artykulu.
|
||||||
|
*/
|
||||||
|
public function saveFilesOrder(int $articleId, string $order): bool
|
||||||
|
{
|
||||||
|
$fileIds = explode(';', $order);
|
||||||
|
if (!is_array($fileIds) || empty($fileIds)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$position = 0;
|
||||||
|
foreach ($fileIds as $fileId) {
|
||||||
|
if ($fileId === '' || $fileId === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->db->update('pp_articles_files', [
|
||||||
|
'o' => $position++,
|
||||||
|
], [
|
||||||
|
'AND' => [
|
||||||
|
'article_id' => $articleId,
|
||||||
|
'id' => (int)$fileId,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
// Fallback for instances where pp_articles_files does not yet have "o" column.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zwraca mape: article_id => etykieta stron (np. " - Strona A / Strona B").
|
||||||
|
*
|
||||||
|
* @param array<int, int> $articleIds
|
||||||
|
* @return array<int, string>
|
||||||
|
*/
|
||||||
|
public function pagesSummaryForArticles(array $articleIds): array
|
||||||
|
{
|
||||||
|
$normalizedIds = [];
|
||||||
|
foreach ($articleIds as $articleId) {
|
||||||
|
$id = (int)$articleId;
|
||||||
|
if ($id > 0) {
|
||||||
|
$normalizedIds[$id] = $id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($normalizedIds)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$placeholders = [];
|
||||||
|
$params = [];
|
||||||
|
foreach (array_values($normalizedIds) as $index => $id) {
|
||||||
|
$key = ':article_id_' . $index;
|
||||||
|
$placeholders[] = $key;
|
||||||
|
$params[$key] = $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql = "
|
||||||
|
SELECT
|
||||||
|
ap.article_id,
|
||||||
|
ap.page_id,
|
||||||
|
(
|
||||||
|
SELECT title
|
||||||
|
FROM pp_pages_langs AS ppl, pp_langs AS pl
|
||||||
|
WHERE ppl.lang_id = pl.id AND ppl.page_id = ap.page_id AND ppl.title != ''
|
||||||
|
ORDER BY pl.o ASC
|
||||||
|
LIMIT 1
|
||||||
|
) AS title
|
||||||
|
FROM pp_articles_pages AS ap
|
||||||
|
WHERE ap.article_id IN (" . implode(', ', $placeholders) . ")
|
||||||
|
ORDER BY ap.article_id ASC, ap.o ASC, ap.page_id ASC
|
||||||
|
";
|
||||||
|
|
||||||
|
$stmt = $this->db->query($sql, $params);
|
||||||
|
$rows = $stmt ? $stmt->fetchAll() : [];
|
||||||
|
if (!is_array($rows)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$titlesByArticle = [];
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$articleId = (int)($row['article_id'] ?? 0);
|
||||||
|
if ($articleId <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$title = trim((string)($row['title'] ?? ''));
|
||||||
|
if ($title === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$titlesByArticle[$articleId][] = $title;
|
||||||
|
}
|
||||||
|
|
||||||
|
$summary = [];
|
||||||
|
foreach (array_values($normalizedIds) as $articleId) {
|
||||||
|
if (empty($titlesByArticle[$articleId])) {
|
||||||
|
$summary[$articleId] = '';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$summary[$articleId] = ' - ' . implode(' / ', $titlesByArticle[$articleId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $summary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateImageAlt(int $imageId, string $imageAlt): bool
|
||||||
|
{
|
||||||
|
$result = $this->db->update('pp_articles_images', [
|
||||||
|
'alt' => $imageAlt,
|
||||||
|
], [
|
||||||
|
'id' => $imageId,
|
||||||
|
]);
|
||||||
|
|
||||||
|
\S::delete_cache();
|
||||||
|
|
||||||
|
return (bool)$result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateFileName(int $fileId, string $fileName): bool
|
||||||
|
{
|
||||||
|
$result = $this->db->update('pp_articles_files', [
|
||||||
|
'name' => $fileName,
|
||||||
|
], [
|
||||||
|
'id' => $fileId,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return (bool)$result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function markFileToDelete(int $fileId): bool
|
||||||
|
{
|
||||||
|
$result = $this->db->update('pp_articles_files', [
|
||||||
|
'to_delete' => 1,
|
||||||
|
], [
|
||||||
|
'id' => $fileId,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return (bool)$result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function markImageToDelete(int $imageId): bool
|
||||||
|
{
|
||||||
|
$result = $this->db->update('pp_articles_images', [
|
||||||
|
'to_delete' => 1,
|
||||||
|
], [
|
||||||
|
'id' => $imageId,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return (bool)$result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function isCheckedValue($value): bool
|
||||||
|
{
|
||||||
|
if (is_bool($value)) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_numeric($value)) {
|
||||||
|
return ((int)$value) === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_string($value)) {
|
||||||
|
$normalized = strtolower(trim($value));
|
||||||
|
return in_array($normalized, ['1', 'on', 'true', 'yes'], true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private function appendDateRangeFilter(
|
private function appendDateRangeFilter(
|
||||||
array &$where,
|
array &$where,
|
||||||
array &$params,
|
array &$params,
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ namespace admin\Controllers;
|
|||||||
use Domain\Article\ArticleRepository;
|
use Domain\Article\ArticleRepository;
|
||||||
use Domain\Languages\LanguagesRepository;
|
use Domain\Languages\LanguagesRepository;
|
||||||
use Domain\Layouts\LayoutsRepository;
|
use Domain\Layouts\LayoutsRepository;
|
||||||
|
use admin\ViewModels\Forms\FormAction;
|
||||||
|
use admin\ViewModels\Forms\FormEditViewModel;
|
||||||
|
use admin\ViewModels\Forms\FormField;
|
||||||
|
use admin\ViewModels\Forms\FormTab;
|
||||||
|
|
||||||
class ArticlesController
|
class ArticlesController
|
||||||
{
|
{
|
||||||
@@ -61,12 +65,21 @@ class ArticlesController
|
|||||||
$listRequest['perPage']
|
$listRequest['perPage']
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$articleIds = [];
|
||||||
|
foreach ($result['items'] as $item) {
|
||||||
|
$id = (int)($item['id'] ?? 0);
|
||||||
|
if ($id > 0) {
|
||||||
|
$articleIds[] = $id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$pagesSummary = $this->repository->pagesSummaryForArticles($articleIds);
|
||||||
|
|
||||||
$rows = [];
|
$rows = [];
|
||||||
$lp = ($listRequest['page'] - 1) * $listRequest['perPage'] + 1;
|
$lp = ($listRequest['page'] - 1) * $listRequest['perPage'] + 1;
|
||||||
foreach ($result['items'] as $item) {
|
foreach ($result['items'] as $item) {
|
||||||
$id = (int)$item['id'];
|
$id = (int)$item['id'];
|
||||||
$title = (string)($item['title'] ?? '');
|
$title = (string)($item['title'] ?? '');
|
||||||
$pages = (string)\admin\factory\Articles::article_pages($id);
|
$pages = (string)($pagesSummary[$id] ?? '');
|
||||||
|
|
||||||
$rows[] = [
|
$rows[] = [
|
||||||
'lp' => $lp++ . '.',
|
'lp' => $lp++ . '.',
|
||||||
@@ -146,6 +159,18 @@ class ArticlesController
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zapis kolejnosci zalacznikow (AJAX)
|
||||||
|
*/
|
||||||
|
public function filesOrderSave(): void
|
||||||
|
{
|
||||||
|
if ($this->repository->saveFilesOrder((int)\S::get('article_id'), (string)\S::get('order'))) {
|
||||||
|
echo json_encode(['status' => 'ok', 'msg' => 'Artykul zostal zapisany.']);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Zapis artykulu (AJAX)
|
* Zapis artykulu (AJAX)
|
||||||
*/
|
*/
|
||||||
@@ -153,11 +178,72 @@ class ArticlesController
|
|||||||
{
|
{
|
||||||
global $user;
|
global $user;
|
||||||
|
|
||||||
$values = json_decode(\S::get('values'), true);
|
$values = $this->resolveSavePayload();
|
||||||
$response = ['status' => 'error', 'msg' => 'Podczas zapisywania artykulu wystapil blad. Prosze sprobowac ponownie.'];
|
$articleId = (int)($values['id'] ?? \S::get('id') ?? 0);
|
||||||
|
$id = $this->repository->save($articleId, $values, (int)$user['id']);
|
||||||
|
|
||||||
if ($id = $this->repository->save((int)($values['id'] ?? 0), $values, (int)$user['id'])) {
|
if ($id) {
|
||||||
$response = ['status' => 'ok', 'msg' => 'Artykul zostal zapisany.', 'id' => $id];
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'status' => 'ok',
|
||||||
|
'message' => 'Artykul zostal zapisany.',
|
||||||
|
'msg' => 'Artykul zostal zapisany.',
|
||||||
|
'id' => (int)$id,
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Podczas zapisywania artykulu wystapil blad. Prosze sprobowac ponownie.',
|
||||||
|
'msg' => 'Podczas zapisywania artykulu wystapil blad. Prosze sprobowac ponownie.',
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function imageAltChange(): void
|
||||||
|
{
|
||||||
|
$response = ['status' => 'error', 'msg' => 'Podczas zmiany atrybutu alt zdjecia wystapil blad. Prosze sprobowac ponownie.'];
|
||||||
|
|
||||||
|
if ($this->repository->updateImageAlt((int)\S::get('image_id'), (string)\S::get('image_alt'))) {
|
||||||
|
$response = ['status' => 'ok'];
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode($response);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function fileNameChange(): void
|
||||||
|
{
|
||||||
|
$response = ['status' => 'error', 'msg' => 'Podczas zmiany nazwy zalacznika wystapil blad. Prosze sprobowac ponownie.'];
|
||||||
|
|
||||||
|
if ($this->repository->updateFileName((int)\S::get('file_id'), (string)\S::get('file_name'))) {
|
||||||
|
$response = ['status' => 'ok'];
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode($response);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function imageDelete(): void
|
||||||
|
{
|
||||||
|
$response = ['status' => 'error', 'msg' => 'Podczas usuwania zdjecia wystapil blad. Prosze sprobowac ponownie.'];
|
||||||
|
|
||||||
|
if ($this->repository->markImageToDelete((int)\S::get('image_id'))) {
|
||||||
|
$response = ['status' => 'ok'];
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode($response);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function fileDelete(): void
|
||||||
|
{
|
||||||
|
$response = ['status' => 'error', 'msg' => 'Podczas usuwania zalacznika wystapil blad. Prosze sprobowac ponownie.'];
|
||||||
|
|
||||||
|
if ($this->repository->markFileToDelete((int)\S::get('file_id'))) {
|
||||||
|
$response = ['status' => 'ok'];
|
||||||
}
|
}
|
||||||
|
|
||||||
echo json_encode($response);
|
echo json_encode($response);
|
||||||
@@ -192,12 +278,226 @@ class ArticlesController
|
|||||||
$this->repository->deleteNonassignedImages();
|
$this->repository->deleteNonassignedImages();
|
||||||
$this->repository->deleteNonassignedFiles();
|
$this->repository->deleteNonassignedFiles();
|
||||||
|
|
||||||
|
$article = $this->repository->find((int)\S::get('id')) ?: ['id' => 0, 'languages' => [], 'images' => [], 'files' => [], 'pages' => []];
|
||||||
|
$languages = $this->languagesRepository->languagesList();
|
||||||
|
$menus = \admin\factory\Pages::menus_list();
|
||||||
|
$layouts = $this->layoutsRepository->listAll();
|
||||||
|
|
||||||
|
$viewModel = $this->buildFormViewModel($article, $languages, $menus, $layouts);
|
||||||
|
|
||||||
return \Tpl::view('articles/article-edit', [
|
return \Tpl::view('articles/article-edit', [
|
||||||
'article' => $this->repository->find((int)\S::get('id')),
|
'form' => $viewModel,
|
||||||
'menus' => \admin\factory\Pages::menus_list(),
|
'article' => $article,
|
||||||
'languages' => $this->languagesRepository->languagesList(),
|
'user' => $user,
|
||||||
'layouts' => $this->layoutsRepository->listAll(),
|
|
||||||
'user' => $user
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function resolveSavePayload(): array
|
||||||
|
{
|
||||||
|
$legacyValuesRaw = \S::get('values');
|
||||||
|
if ($legacyValuesRaw !== null && $legacyValuesRaw !== '') {
|
||||||
|
$legacyValues = json_decode((string)$legacyValuesRaw, true);
|
||||||
|
if (is_array($legacyValues)) {
|
||||||
|
return $legacyValues;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$payload = $_POST;
|
||||||
|
unset($payload['_form_id']);
|
||||||
|
|
||||||
|
return is_array($payload) ? $payload : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildFormViewModel(array $article, array $languages, array $menus, array $layouts): FormEditViewModel
|
||||||
|
{
|
||||||
|
$articleId = (int)($article['id'] ?? 0);
|
||||||
|
$defaultLanguageId = (string)($this->languagesRepository->defaultLanguageId() ?? 'pl');
|
||||||
|
$title = $articleId > 0
|
||||||
|
? 'Edycja artykulu: <u>' . $this->escapeHtml((string)($article['languages'][$defaultLanguageId]['title'] ?? '')) . '</u>'
|
||||||
|
: 'Edycja artykulu';
|
||||||
|
|
||||||
|
$layoutOptions = ['' => '---- szablon domyslny ----'];
|
||||||
|
foreach ($layouts as $layout) {
|
||||||
|
$layoutOptions[(string)$layout['id']] = (string)$layout['name'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$copyFromOptions = ['' => '---- wersja jezykowa ----'];
|
||||||
|
foreach ($languages as $language) {
|
||||||
|
if (!empty($language['id'])) {
|
||||||
|
$copyFromOptions[(string)$language['id']] = (string)$language['name'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$tabs = [
|
||||||
|
new FormTab('content', 'Tresc', 'fa-file'),
|
||||||
|
new FormTab('settings', 'Ustawienia', 'fa-wrench'),
|
||||||
|
new FormTab('seo', 'SEO', 'fa-globe'),
|
||||||
|
new FormTab('display', 'Wyswietlanie', 'fa-share-alt'),
|
||||||
|
new FormTab('gallery', 'Galeria', 'fa-file-image-o'),
|
||||||
|
new FormTab('files', 'Zalaczniki', 'fa-file-archive-o'),
|
||||||
|
];
|
||||||
|
|
||||||
|
$fields = [
|
||||||
|
FormField::hidden('id', $articleId),
|
||||||
|
FormField::langSection('article_content', 'content', [
|
||||||
|
FormField::select('copy_from', [
|
||||||
|
'label' => 'Wyswietlaj tresc z wersji',
|
||||||
|
'options' => $copyFromOptions,
|
||||||
|
]),
|
||||||
|
FormField::text('title', [
|
||||||
|
'label' => 'Tytul',
|
||||||
|
'required' => true,
|
||||||
|
'attributes' => ['id' => 'title'],
|
||||||
|
]),
|
||||||
|
FormField::image('main_image', [
|
||||||
|
'label' => 'Zdjecie tytulowe',
|
||||||
|
'filemanager' => true,
|
||||||
|
'attributes' => ['id' => 'main_image'],
|
||||||
|
]),
|
||||||
|
FormField::editor('entry', [
|
||||||
|
'label' => 'Wstep',
|
||||||
|
'toolbar' => 'MyToolbar',
|
||||||
|
'height' => 250,
|
||||||
|
'attributes' => ['id' => 'entry'],
|
||||||
|
]),
|
||||||
|
FormField::editor('text', [
|
||||||
|
'label' => 'Tresc',
|
||||||
|
'toolbar' => 'MyToolbar',
|
||||||
|
'height' => 250,
|
||||||
|
'attributes' => ['id' => 'text'],
|
||||||
|
]),
|
||||||
|
FormField::editor('table_of_contents', [
|
||||||
|
'label' => 'Spis tresci',
|
||||||
|
'toolbar' => 'MyToolbar',
|
||||||
|
'height' => 250,
|
||||||
|
'attributes' => ['id' => 'table_of_contents'],
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
FormField::switch('status', ['label' => 'Opublikowany', 'tab' => 'settings', 'value' => ((int)($article['status'] ?? 0) === 1) || $articleId === 0]),
|
||||||
|
FormField::switch('show_title', ['label' => 'Pokaz tytul', 'tab' => 'settings', 'value' => (int)($article['show_title'] ?? 0) === 1]),
|
||||||
|
FormField::switch('show_table_of_contents', ['label' => 'Pokaz spis tresci', 'tab' => 'settings', 'value' => (int)($article['show_table_of_contents'] ?? 0) === 1]),
|
||||||
|
FormField::switch('show_date_add', ['label' => 'Pokaz date dodania', 'tab' => 'settings', 'value' => (int)($article['show_date_add'] ?? 0) === 1]),
|
||||||
|
FormField::switch('show_date_modify', ['label' => 'Pokaz date modyfikacji', 'tab' => 'settings', 'value' => (int)($article['show_date_modify'] ?? 0) === 1]),
|
||||||
|
FormField::switch('repeat_entry', ['label' => 'Powtorz wprowadzenie', 'tab' => 'settings', 'value' => (int)($article['repeat_entry'] ?? 0) === 1]),
|
||||||
|
FormField::switch('social_icons', ['label' => 'Linki do portali spolecznosciowych', 'tab' => 'settings', 'value' => (int)($article['social_icons'] ?? 0) === 1]),
|
||||||
|
FormField::langSection('article_seo', 'seo', [
|
||||||
|
FormField::text('seo_link', ['label' => 'Link SEO', 'attributes' => ['id' => 'seo_link']]),
|
||||||
|
FormField::text('meta_title', ['label' => 'Meta title']),
|
||||||
|
FormField::textarea('meta_description', ['label' => 'Meta description', 'rows' => 4]),
|
||||||
|
FormField::textarea('meta_keywords', ['label' => 'Meta keywords', 'rows' => 4]),
|
||||||
|
FormField::switch('noindex', ['label' => 'Blokuj indeksacje']),
|
||||||
|
FormField::switch('block_direct_access', ['label' => 'Blokuj bezposredni dostep']),
|
||||||
|
]),
|
||||||
|
FormField::select('layout_id', [
|
||||||
|
'label' => 'Szablon',
|
||||||
|
'tab' => 'display',
|
||||||
|
'options' => $layoutOptions,
|
||||||
|
'value' => $article['layout_id'] ?? '',
|
||||||
|
]),
|
||||||
|
FormField::custom('pages_tree', $this->renderPagesTree($menus, $article), ['tab' => 'display']),
|
||||||
|
FormField::custom('images_box', $this->renderImagesBox($article), ['tab' => 'gallery']),
|
||||||
|
FormField::custom('files_box', $this->renderFilesBox($article), ['tab' => 'files']),
|
||||||
|
];
|
||||||
|
|
||||||
|
$actions = [
|
||||||
|
FormAction::save('/admin/articles/article_save/' . ($articleId > 0 ? 'id=' . $articleId : ''), '/admin/articles/view_list/'),
|
||||||
|
FormAction::cancel('/admin/articles/view_list/'),
|
||||||
|
];
|
||||||
|
|
||||||
|
return new FormEditViewModel(
|
||||||
|
'article-edit',
|
||||||
|
$title,
|
||||||
|
$article,
|
||||||
|
$fields,
|
||||||
|
$tabs,
|
||||||
|
$actions,
|
||||||
|
'POST',
|
||||||
|
'/admin/articles/article_save/' . ($articleId > 0 ? 'id=' . $articleId : ''),
|
||||||
|
'/admin/articles/view_list/',
|
||||||
|
true,
|
||||||
|
['id' => $articleId],
|
||||||
|
$languages
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function renderPagesTree(array $menus, array $article): string
|
||||||
|
{
|
||||||
|
$html = '<div class="form-group row">';
|
||||||
|
$html .= '<label class="col-lg-4 control-label">Wyswietlaj na:</label>';
|
||||||
|
$html .= '<div class="col-lg-8">';
|
||||||
|
|
||||||
|
foreach ($menus as $menu) {
|
||||||
|
$menuId = (int)($menu['id'] ?? 0);
|
||||||
|
$menuName = $this->escapeHtml((string)($menu['name'] ?? ''));
|
||||||
|
$menuStatus = (int)($menu['status'] ?? 0);
|
||||||
|
$menuPages = \admin\factory\Pages::menu_pages($menuId);
|
||||||
|
|
||||||
|
$html .= '<div class="menu_sortable">';
|
||||||
|
$html .= '<ol class="sortable" id="sortable_' . $menuId . '">';
|
||||||
|
$html .= '<li id="list_' . $menuId . '" class="menu_' . $menuId . '" menu="' . $menuId . '">';
|
||||||
|
$html .= '<div class="context_0 content content_menu"' . ($menuStatus ? '' : ' style="color: #cc0000;"') . '>';
|
||||||
|
$html .= '<span class="disclose"><span></span></span>Menu: <b>' . $menuName . '</b>';
|
||||||
|
$html .= '</div>';
|
||||||
|
$html .= \Tpl::view('articles/subpages-list', [
|
||||||
|
'pages' => $menuPages,
|
||||||
|
'article_pages' => $article['pages'] ?? [],
|
||||||
|
'parent_id' => $menuId,
|
||||||
|
'step' => 1,
|
||||||
|
]);
|
||||||
|
$html .= '</li></ol></div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$html .= '</div><div class="clear"></div></div>';
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function renderImagesBox(array $article): string
|
||||||
|
{
|
||||||
|
$html = '<ul id="images-list">';
|
||||||
|
$images = is_array($article['images'] ?? null) ? $article['images'] : [];
|
||||||
|
foreach ($images as $img) {
|
||||||
|
$id = (int)($img['id'] ?? 0);
|
||||||
|
$src = $this->escapeHtml((string)($img['src'] ?? ''));
|
||||||
|
$alt = $this->escapeHtml((string)($img['alt'] ?? ''));
|
||||||
|
|
||||||
|
$html .= '<li id="image-' . $id . '">';
|
||||||
|
$html .= '<img class="article-image lozad" data-src="/libraries/thumb.php?img=' . $src . '&w=300&h=300">';
|
||||||
|
$html .= '<a href="#" class="input-group-addon btn btn-danger article_image_delete" image-id="' . $id . '"><i class="fa fa-trash"></i></a>';
|
||||||
|
$html .= '<input type="text" class="form-control image-alt" value="' . $alt . '" image-id="' . $id . '" placeholder="atrybut alt...">';
|
||||||
|
$html .= '</li>';
|
||||||
|
}
|
||||||
|
$html .= '</ul><div id="images-uploader">You browser doesn\'t have Flash installed.</div>';
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function renderFilesBox(array $article): string
|
||||||
|
{
|
||||||
|
$html = '<ul id="files-list">';
|
||||||
|
$files = is_array($article['files'] ?? null) ? $article['files'] : [];
|
||||||
|
foreach ($files as $file) {
|
||||||
|
$id = (int)($file['id'] ?? 0);
|
||||||
|
$src = (string)($file['src'] ?? '');
|
||||||
|
$name = trim((string)($file['name'] ?? ''));
|
||||||
|
if ($name === '') {
|
||||||
|
$parts = explode('/', $src);
|
||||||
|
$name = (string)end($parts);
|
||||||
|
}
|
||||||
|
$name = $this->escapeHtml($name);
|
||||||
|
|
||||||
|
$html .= '<li id="file-' . $id . '"><div class="input-group">';
|
||||||
|
$html .= '<input type="text" class="article_file_edit form-control" file_id="' . $id . '" value="' . $name . '" />';
|
||||||
|
$html .= '<a href="#" class="input-group-addon btn btn-info article_file_delete" file_id="' . $id . '"><i class="fa fa-trash"></i></a>';
|
||||||
|
$html .= '</div></li>';
|
||||||
|
}
|
||||||
|
$html .= '</ul><div id="files-uploader">You browser doesn\'t have Flash installed.</div>';
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function escapeHtml(string $value): string
|
||||||
|
{
|
||||||
|
return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ class FormFieldRenderer
|
|||||||
'name' => $field->name,
|
'name' => $field->name,
|
||||||
'id' => $field->id,
|
'id' => $field->id,
|
||||||
'value' => $value ?? '',
|
'value' => $value ?? '',
|
||||||
'options' => $field->options,
|
'values' => $field->options,
|
||||||
'class' => ($field->required ? 'require ' : '') . ($field->attributes['class'] ?? ''),
|
'class' => ($field->required ? 'require ' : '') . ($field->attributes['class'] ?? ''),
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -308,6 +308,11 @@ class FormFieldRenderer
|
|||||||
'value="' . htmlspecialchars($value ?? '') . '">';
|
'value="' . htmlspecialchars($value ?? '') . '">';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function renderCustom(FormField $field): string
|
||||||
|
{
|
||||||
|
return (string)($field->customHtml ?? '');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renderuje sekcję językową
|
* Renderuje sekcję językową
|
||||||
*/
|
*/
|
||||||
@@ -388,6 +393,16 @@ class FormFieldRenderer
|
|||||||
'id' => $id,
|
'id' => $id,
|
||||||
'checked' => (bool) $value,
|
'checked' => (bool) $value,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
case FormFieldType::SELECT:
|
||||||
|
return $this->wrapWithError(\Html::select([
|
||||||
|
'label' => $field->label,
|
||||||
|
'name' => $name,
|
||||||
|
'id' => $id,
|
||||||
|
'value' => $value ?? '',
|
||||||
|
'values' => $field->options,
|
||||||
|
'class' => ($field->required ? 'require ' : '') . ($field->attributes['class'] ?? ''),
|
||||||
|
]), $error);
|
||||||
|
|
||||||
default: // TEXT, URL, etc.
|
default: // TEXT, URL, etc.
|
||||||
return $this->wrapWithError(\Html::input([
|
return $this->wrapWithError(\Html::input([
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ class FormField
|
|||||||
// Specyficzne dla lang_section
|
// Specyficzne dla lang_section
|
||||||
public ?array $langFields;
|
public ?array $langFields;
|
||||||
public ?string $langSectionParentTab;
|
public ?string $langSectionParentTab;
|
||||||
|
public ?string $customHtml;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $name Nazwa pola (name)
|
* @param string $name Nazwa pola (name)
|
||||||
@@ -64,7 +65,8 @@ class FormField
|
|||||||
string $editorToolbar = 'MyTool',
|
string $editorToolbar = 'MyTool',
|
||||||
int $editorHeight = 300,
|
int $editorHeight = 300,
|
||||||
?array $langFields = null,
|
?array $langFields = null,
|
||||||
?string $langSectionParentTab = null
|
?string $langSectionParentTab = null,
|
||||||
|
?string $customHtml = null
|
||||||
) {
|
) {
|
||||||
$this->name = $name;
|
$this->name = $name;
|
||||||
$this->type = $type;
|
$this->type = $type;
|
||||||
@@ -82,6 +84,7 @@ class FormField
|
|||||||
$this->editorHeight = $editorHeight;
|
$this->editorHeight = $editorHeight;
|
||||||
$this->langFields = $langFields;
|
$this->langFields = $langFields;
|
||||||
$this->langSectionParentTab = $langSectionParentTab;
|
$this->langSectionParentTab = $langSectionParentTab;
|
||||||
|
$this->customHtml = $customHtml;
|
||||||
$this->id = $attributes['id'] ?? $name;
|
$this->id = $attributes['id'] ?? $name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,6 +279,29 @@ class FormField
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function custom(string $name, string $html, array $config = []): self
|
||||||
|
{
|
||||||
|
return new self(
|
||||||
|
$name,
|
||||||
|
FormFieldType::CUSTOM,
|
||||||
|
$config['label'] ?? '',
|
||||||
|
null,
|
||||||
|
$config['tab'] ?? 'default',
|
||||||
|
false,
|
||||||
|
$config['attributes'] ?? [],
|
||||||
|
[],
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
'MyTool',
|
||||||
|
300,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
$html
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sekcja językowa - grupa pól powtarzana dla każdego języka
|
* Sekcja językowa - grupa pól powtarzana dla każdego języka
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -20,4 +20,5 @@ class FormFieldType
|
|||||||
public const FILE = 'file';
|
public const FILE = 'file';
|
||||||
public const HIDDEN = 'hidden';
|
public const HIDDEN = 'hidden';
|
||||||
public const LANG_SECTION = 'lang_section';
|
public const LANG_SECTION = 'lang_section';
|
||||||
|
public const CUSTOM = 'custom';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -330,11 +330,16 @@ class Site
|
|||||||
*/
|
*/
|
||||||
private static $actionMap = [
|
private static $actionMap = [
|
||||||
'gallery_order_save' => 'galleryOrderSave',
|
'gallery_order_save' => 'galleryOrderSave',
|
||||||
|
'files_order_save' => 'filesOrderSave',
|
||||||
'view_list' => 'list',
|
'view_list' => 'list',
|
||||||
'article_edit' => 'edit',
|
'article_edit' => 'edit',
|
||||||
'article_save' => 'save',
|
'article_save' => 'save',
|
||||||
'article_delete' => 'delete',
|
'article_delete' => 'delete',
|
||||||
'article_restore' => 'restore',
|
'article_restore' => 'restore',
|
||||||
|
'article_image_alt_change' => 'imageAltChange',
|
||||||
|
'article_file_name_change' => 'fileNameChange',
|
||||||
|
'article_image_delete' => 'imageDelete',
|
||||||
|
'article_file_delete' => 'fileDelete',
|
||||||
'banner_edit' => 'edit',
|
'banner_edit' => 'edit',
|
||||||
'banner_save' => 'save',
|
'banner_save' => 'save',
|
||||||
'banner_delete' => 'delete',
|
'banner_delete' => 'delete',
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace admin\view;
|
|
||||||
|
|
||||||
class Articles
|
|
||||||
{
|
|
||||||
public static function subpages_list( $pages, $article_pages, $parent_id = 0, $step = 1 )
|
|
||||||
{
|
|
||||||
$tpl = new \Tpl();
|
|
||||||
$tpl -> pages = $pages;
|
|
||||||
$tpl -> parent_id = $parent_id;
|
|
||||||
$tpl -> step = $step;
|
|
||||||
$tpl -> article_pages = $article_pages;
|
|
||||||
return $tpl -> render( 'articles/subpages-list' );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function articles_list()
|
|
||||||
{
|
|
||||||
$tpl = new \Tpl;
|
|
||||||
return $tpl -> render( 'articles/articles-list' );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
@@ -3,7 +3,8 @@ class ImageManipulator
|
|||||||
{
|
{
|
||||||
protected int $width;
|
protected int $width;
|
||||||
protected int $height;
|
protected int $height;
|
||||||
protected \GdImage $image;
|
/** @var resource|\GdImage */
|
||||||
|
protected $image;
|
||||||
protected ?string $file = null;
|
protected ?string $file = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -37,7 +38,7 @@ class ImageManipulator
|
|||||||
throw new InvalidArgumentException("Image file $file is not readable");
|
throw new InvalidArgumentException("Image file $file is not readable");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($this->image) && $this->image instanceof \GdImage) {
|
if (isset($this->image) && $this->isValidImageResource($this->image)) {
|
||||||
imagedestroy($this->image);
|
imagedestroy($this->image);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +67,7 @@ class ImageManipulator
|
|||||||
throw new InvalidArgumentException("Image type $type not supported");
|
throw new InvalidArgumentException("Image type $type not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$this->image instanceof \GdImage) {
|
if (!$this->isValidImageResource($this->image)) {
|
||||||
throw new InvalidArgumentException("Failed to create image from $file");
|
throw new InvalidArgumentException("Failed to create image from $file");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,12 +92,12 @@ class ImageManipulator
|
|||||||
*/
|
*/
|
||||||
public function setImageString(string $data): self
|
public function setImageString(string $data): self
|
||||||
{
|
{
|
||||||
if (isset($this->image) && $this->image instanceof \GdImage) {
|
if (isset($this->image) && $this->isValidImageResource($this->image)) {
|
||||||
imagedestroy($this->image);
|
imagedestroy($this->image);
|
||||||
}
|
}
|
||||||
|
|
||||||
$image = imagecreatefromstring($data);
|
$image = imagecreatefromstring($data);
|
||||||
if (!$image instanceof \GdImage) {
|
if (!$this->isValidImageResource($image)) {
|
||||||
throw new RuntimeException('Cannot create image from data string');
|
throw new RuntimeException('Cannot create image from data string');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +125,7 @@ class ImageManipulator
|
|||||||
*/
|
*/
|
||||||
public function resample(int $width, int $height, bool $constrainProportions = true): self
|
public function resample(int $width, int $height, bool $constrainProportions = true): self
|
||||||
{
|
{
|
||||||
if (!isset($this->image) || !$this->image instanceof \GdImage) {
|
if (!isset($this->image) || !$this->isValidImageResource($this->image)) {
|
||||||
throw new RuntimeException('No image set');
|
throw new RuntimeException('No image set');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,7 +170,7 @@ class ImageManipulator
|
|||||||
*/
|
*/
|
||||||
public function enlargeCanvas(int $width, int $height, array $rgb = [], ?int $xpos = null, ?int $ypos = null): self
|
public function enlargeCanvas(int $width, int $height, array $rgb = [], ?int $xpos = null, ?int $ypos = null): self
|
||||||
{
|
{
|
||||||
if (!isset($this->image) || !$this->image instanceof \GdImage) {
|
if (!isset($this->image) || !$this->isValidImageResource($this->image)) {
|
||||||
throw new RuntimeException('No image set');
|
throw new RuntimeException('No image set');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,7 +178,7 @@ class ImageManipulator
|
|||||||
$height = max($height, $this->height);
|
$height = max($height, $this->height);
|
||||||
|
|
||||||
$temp = imagecreatetruecolor($width, $height);
|
$temp = imagecreatetruecolor($width, $height);
|
||||||
if (!$temp instanceof \GdImage) {
|
if (!$this->isValidImageResource($temp)) {
|
||||||
throw new RuntimeException('Failed to create a new image for enlarging canvas');
|
throw new RuntimeException('Failed to create a new image for enlarging canvas');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,7 +234,7 @@ class ImageManipulator
|
|||||||
*/
|
*/
|
||||||
public function crop($x1, int $y1 = 0, int $x2 = 0, int $y2 = 0): self
|
public function crop($x1, int $y1 = 0, int $x2 = 0, int $y2 = 0): self
|
||||||
{
|
{
|
||||||
if (!isset($this->image) || !$this->image instanceof \GdImage) {
|
if (!isset($this->image) || !$this->isValidImageResource($this->image)) {
|
||||||
throw new RuntimeException('No image set');
|
throw new RuntimeException('No image set');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,7 +258,7 @@ class ImageManipulator
|
|||||||
}
|
}
|
||||||
|
|
||||||
$temp = imagecreatetruecolor($cropWidth, $cropHeight);
|
$temp = imagecreatetruecolor($cropWidth, $cropHeight);
|
||||||
if (!$temp instanceof \GdImage) {
|
if (!$this->isValidImageResource($temp)) {
|
||||||
throw new RuntimeException('Failed to create a new image for cropping');
|
throw new RuntimeException('Failed to create a new image for cropping');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,17 +287,17 @@ class ImageManipulator
|
|||||||
/**
|
/**
|
||||||
* Replace current image resource with a new one
|
* Replace current image resource with a new one
|
||||||
*
|
*
|
||||||
* @param \GdImage $res New image resource
|
* @param resource|\GdImage $res New image resource
|
||||||
* @return self
|
* @return self
|
||||||
* @throws UnexpectedValueException
|
* @throws UnexpectedValueException
|
||||||
*/
|
*/
|
||||||
protected function _replace(\GdImage $res): self
|
protected function _replace($res): self
|
||||||
{
|
{
|
||||||
if (!$res instanceof \GdImage) {
|
if (!$this->isValidImageResource($res)) {
|
||||||
throw new UnexpectedValueException('Invalid image resource');
|
throw new UnexpectedValueException('Invalid image resource');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($this->image) && $this->image instanceof \GdImage) {
|
if (isset($this->image) && $this->isValidImageResource($this->image)) {
|
||||||
imagedestroy($this->image);
|
imagedestroy($this->image);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,9 +387,9 @@ class ImageManipulator
|
|||||||
/**
|
/**
|
||||||
* Returns the GD image resource
|
* Returns the GD image resource
|
||||||
*
|
*
|
||||||
* @return \GdImage
|
* @return resource|\GdImage
|
||||||
*/
|
*/
|
||||||
public function getResource(): \GdImage
|
public function getResource()
|
||||||
{
|
{
|
||||||
return $this->image;
|
return $this->image;
|
||||||
}
|
}
|
||||||
@@ -418,8 +419,22 @@ class ImageManipulator
|
|||||||
*/
|
*/
|
||||||
public function __destruct()
|
public function __destruct()
|
||||||
{
|
{
|
||||||
if (isset($this->image) && $this->image instanceof \GdImage) {
|
if (isset($this->image) && $this->isValidImageResource($this->image)) {
|
||||||
imagedestroy($this->image);
|
imagedestroy($this->image);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compatibility helper for PHP 7.4 (resource) and PHP 8+ (GdImage).
|
||||||
|
*
|
||||||
|
* @param mixed $image
|
||||||
|
*/
|
||||||
|
private function isValidImageResource($image): bool
|
||||||
|
{
|
||||||
|
if (is_resource($image)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return class_exists('GdImage', false) && $image instanceof \GdImage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,10 +12,21 @@ session_start();
|
|||||||
/* połączenie z bazą ustawić wg własnych preferencji */
|
/* połączenie z bazą ustawić wg własnych preferencji */
|
||||||
require_once dirname( __FILE__ ) . '/../../config.php';
|
require_once dirname( __FILE__ ) . '/../../config.php';
|
||||||
require_once dirname( __FILE__ ) . '/../../autoload/class.S.php';
|
require_once dirname( __FILE__ ) . '/../../autoload/class.S.php';
|
||||||
require_once dirname( __FILE__ ) . '/../../autoload/admin/factory/class.Articles.php';
|
|
||||||
require_once dirname( __FILE__ ) . '/../../autoload/admin/factory/class.Pages.php';
|
$legacyFactoryFiles = [
|
||||||
require_once dirname( __FILE__ ) . '/../../autoload/admin/factory/class.ShopCategory.php';
|
'/../../autoload/admin/factory/class.Articles.php',
|
||||||
require_once dirname( __FILE__ ) . '/../../autoload/admin/factory/class.ShopProduct.php';
|
'/../../autoload/admin/factory/class.Pages.php',
|
||||||
|
'/../../autoload/admin/factory/class.ShopCategory.php',
|
||||||
|
'/../../autoload/admin/factory/class.ShopProduct.php',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ( $legacyFactoryFiles as $legacyFactoryFile )
|
||||||
|
{
|
||||||
|
$legacyFactoryPath = dirname( __FILE__ ) . $legacyFactoryFile;
|
||||||
|
if ( file_exists( $legacyFactoryPath ) )
|
||||||
|
require_once $legacyFactoryPath;
|
||||||
|
}
|
||||||
|
|
||||||
require_once dirname( __FILE__ ) . '/../../autoload/shop/class.Product.php';
|
require_once dirname( __FILE__ ) . '/../../autoload/shop/class.Product.php';
|
||||||
require_once dirname( __FILE__ ) . '/../../libraries/medoo/medoo.php';
|
require_once dirname( __FILE__ ) . '/../../libraries/medoo/medoo.php';
|
||||||
|
|
||||||
@@ -35,4 +46,4 @@ $mdb = new medoo( [
|
|||||||
'username' => $database['user'],
|
'username' => $database['user'],
|
||||||
'password' => $database['password'],
|
'password' => $database['password'],
|
||||||
'charset' => 'utf8'
|
'charset' => 'utf8'
|
||||||
] );
|
] );
|
||||||
|
|||||||
@@ -1,154 +1,53 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once '../../config.php';
|
require_once '../../config.php';
|
||||||
require_once '../medoo/medoo.php';
|
require_once '../medoo/medoo.php';
|
||||||
date_default_timezone_set( 'Europe/Warsaw' );
|
require_once 'upload-common.php';
|
||||||
session_start();
|
|
||||||
|
|
||||||
$upload_token = $_REQUEST['upload_token'] ?? '';
|
plupload_bootstrap();
|
||||||
if ( !isset( $_SESSION['upload_tokens'][$upload_token] ) ) {
|
plupload_require_post();
|
||||||
http_response_code(403);
|
$userId = plupload_require_admin_user();
|
||||||
echo json_encode( ['error' => 'Brak tokenu uploadu'] );
|
plupload_validate_token($userId);
|
||||||
exit;
|
|
||||||
|
$fileDir = '/upload/article_files/tmp';
|
||||||
|
$targetDir = '../..' . $fileDir;
|
||||||
|
plupload_ensure_target_dir($targetDir);
|
||||||
|
|
||||||
|
list($chunk, $chunks) = plupload_get_chunks();
|
||||||
|
list($fileName, $extension, $filePath, $partPath) = plupload_build_target_paths(
|
||||||
|
$targetDir,
|
||||||
|
$_REQUEST['name'] ?? '',
|
||||||
|
null,
|
||||||
|
[
|
||||||
|
'php', 'php3', 'php4', 'php5', 'php7', 'php8', 'phtml', 'phar',
|
||||||
|
'cgi', 'pl', 'py', 'rb',
|
||||||
|
'asp', 'aspx', 'jsp',
|
||||||
|
'js', 'mjs', 'vbs', 'wsf', 'hta',
|
||||||
|
'sh', 'bash', 'zsh', 'ps1', 'bat', 'cmd', 'com',
|
||||||
|
'exe', 'msi', 'scr', 'dll', 'jar',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
plupload_cleanup_stale_parts($targetDir, $partPath, 5 * 3600);
|
||||||
|
plupload_write_chunk_to_part($partPath, $chunk);
|
||||||
|
plupload_assert_size_limit($partPath, 50 * 1024 * 1024, 'Plik przekracza dozwolony rozmiar (50 MB).');
|
||||||
|
|
||||||
|
$fileId = null;
|
||||||
|
$responseFileName = $fileName;
|
||||||
|
if (plupload_is_last_chunk($chunk, $chunks)) {
|
||||||
|
plupload_finalize_part($partPath, $filePath);
|
||||||
|
|
||||||
|
$mdb = plupload_create_medoo($database);
|
||||||
|
$mdb->insert('pp_articles_files', [
|
||||||
|
'article_id' => null,
|
||||||
|
'src' => substr($filePath, 5),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$fileId = (int)$mdb->id();
|
||||||
|
$responseFileName = basename($filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
$tokenData = $_SESSION['upload_tokens'][$upload_token];
|
plupload_send_success([
|
||||||
if ( $tokenData['expires'] < time() ) {
|
'file_name' => $responseFileName,
|
||||||
unset( $_SESSION['upload_tokens'][$upload_token] );
|
'file_id' => $fileId,
|
||||||
http_response_code(403);
|
]);
|
||||||
echo json_encode( ['error' => 'Token wygasł'] );
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$mdb = new medoo( [
|
|
||||||
'database_type' => 'mysql',
|
|
||||||
'database_name' => $database['name'],
|
|
||||||
'server' => $database['host'],
|
|
||||||
'username' => $database['user'],
|
|
||||||
'password' => $database['password'],
|
|
||||||
'charset' => 'utf8'
|
|
||||||
] );
|
|
||||||
|
|
||||||
header( "Expires: Mon, 26 Jul 1997 05:00:00 GMT" );
|
|
||||||
header( "Last-Modified: " . gmdate( "D, d M Y H:i:s" ) . " GMT" );
|
|
||||||
header( "Cache-Control: no-store, no-cache, must-revalidate" );
|
|
||||||
header( "Cache-Control: post-check=0, pre-check=0", false );
|
|
||||||
header( "Pragma: no-cache" );
|
|
||||||
|
|
||||||
$fileDir = '/upload/article_files/tmp';
|
|
||||||
$targetDir = '../..' . $fileDir;
|
|
||||||
|
|
||||||
if ( !is_dir( $targetDir ) )
|
|
||||||
mkdir( $targetDir, 0755, true );
|
|
||||||
|
|
||||||
$cleanupTargetDir = true;
|
|
||||||
$maxFileAge = 5 * 3600;
|
|
||||||
|
|
||||||
$chunk = isset( $_REQUEST["chunk"] ) ? intval( $_REQUEST["chunk"] ) : 0;
|
|
||||||
$chunks = isset( $_REQUEST["chunks"] ) ? intval( $_REQUEST["chunks"] ) : 0;
|
|
||||||
$fileName = isset( $_REQUEST["name"] ) ? $_REQUEST["name"] : '';
|
|
||||||
|
|
||||||
$fileName = preg_replace( '/[^\w\._]+/', '-', $fileName );
|
|
||||||
|
|
||||||
if ( file_exists( $targetDir . DIRECTORY_SEPARATOR . $fileName ) )
|
|
||||||
{
|
|
||||||
$ext = strrpos( $fileName, '.' );
|
|
||||||
$fileName_a = substr( $fileName, 0, $ext );
|
|
||||||
$fileName_b = substr( $fileName, $ext );
|
|
||||||
|
|
||||||
$count = 1;
|
|
||||||
|
|
||||||
while ( file_exists( $targetDir . DIRECTORY_SEPARATOR . $fileName_a . '_' . $count . $fileName_b ) )
|
|
||||||
$count++;
|
|
||||||
|
|
||||||
$fileName = $fileName_a . '_' . $count . $fileName_b;
|
|
||||||
}
|
|
||||||
|
|
||||||
$filePath = $targetDir . DIRECTORY_SEPARATOR . $fileName;
|
|
||||||
|
|
||||||
if ( $cleanupTargetDir && is_dir( $targetDir ) && ( $dir = opendir( $targetDir ) ) )
|
|
||||||
{
|
|
||||||
while ( ( $file = readdir( $dir ) ) !== false )
|
|
||||||
{
|
|
||||||
$tmpfilePath = $targetDir . DIRECTORY_SEPARATOR . $file;
|
|
||||||
|
|
||||||
if ( preg_match( '/\.part$/', $file ) && ( filemtime( $tmpfilePath ) < time() - $maxFileAge ) && ( $tmpfilePath != "{$filePath}.part" ) ) {
|
|
||||||
@unlink( $tmpfilePath );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
closedir($dir);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
die( '{"jsonrpc" : "2.0", "error" : {"code": 100, "message": "Failed to open temp directory."}, "id" : "id"}' );
|
|
||||||
|
|
||||||
if ( isset( $_SERVER["HTTP_CONTENT_TYPE"] ) )
|
|
||||||
$contentType = $_SERVER["HTTP_CONTENT_TYPE"];
|
|
||||||
|
|
||||||
if ( isset( $_SERVER["CONTENT_TYPE"] ) )
|
|
||||||
$contentType = $_SERVER["CONTENT_TYPE"];
|
|
||||||
|
|
||||||
if ( strpos( $contentType, "multipart" ) !== false )
|
|
||||||
{
|
|
||||||
if ( isset( $_FILES['file']['tmp_name'] ) && is_uploaded_file( $_FILES['file']['tmp_name'] ) )
|
|
||||||
{
|
|
||||||
$out = fopen( "{$filePath}.part", $chunk == 0 ? "wb" : "ab" );
|
|
||||||
if ( $out )
|
|
||||||
{
|
|
||||||
$in = fopen( $_FILES['file']['tmp_name'], "rb" );
|
|
||||||
|
|
||||||
if ( $in )
|
|
||||||
{
|
|
||||||
while ( $buff = fread( $in, 4096 ) )
|
|
||||||
fwrite($out, $buff);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
die( '{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}' );
|
|
||||||
fclose( $in );
|
|
||||||
fclose( $out );
|
|
||||||
@unlink( $_FILES['file']['tmp_name'] );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
die( '{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}' );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
die( '{"jsonrpc" : "2.0", "error" : {"code": 103, "message": "Failed to move uploaded file."}, "id" : "id"}' );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$out = fopen( "{$filePath}.part", $chunk == 0 ? "wb" : "ab" );
|
|
||||||
if ( $out )
|
|
||||||
{
|
|
||||||
$in = fopen( "php://input", "rb" );
|
|
||||||
|
|
||||||
if ( $in )
|
|
||||||
{
|
|
||||||
while ( $buff = fread( $in, 4096 ) )
|
|
||||||
fwrite( $out, $buff );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
die( '{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}' );
|
|
||||||
|
|
||||||
fclose( $in );
|
|
||||||
fclose( $out );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
die( '{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}' );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !$chunks || $chunk == $chunks - 1 )
|
|
||||||
{
|
|
||||||
rename( "{$filePath}.part", $filePath );
|
|
||||||
|
|
||||||
$mdb -> insert( 'pp_articles_files', [
|
|
||||||
'article_id' => null,
|
|
||||||
'src' => substr( $filePath, 5, strlen( $filePath ) )
|
|
||||||
] );
|
|
||||||
|
|
||||||
$file_id = $mdb -> id();
|
|
||||||
|
|
||||||
$file_name = explode( '/', $filePath );
|
|
||||||
$file_name = $file_name[ count( $file_name ) - 1 ];
|
|
||||||
}
|
|
||||||
|
|
||||||
die( '{"jsonrpc" : "2.0", "result" : null, "id" : "id", "file_name" : "' . $file_name . '", "file_id" : "' . $file_id . '"}' );
|
|
||||||
?>
|
|
||||||
@@ -1,153 +1,61 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once '../../config.php';
|
require_once '../../config.php';
|
||||||
require_once '../medoo/medoo.php';
|
require_once '../medoo/medoo.php';
|
||||||
date_default_timezone_set( 'Europe/Warsaw' );
|
require_once 'upload-common.php';
|
||||||
session_start();
|
|
||||||
|
|
||||||
$upload_token = $_REQUEST['upload_token'] ?? '';
|
plupload_bootstrap();
|
||||||
if ( !isset( $_SESSION['upload_tokens'][$upload_token] ) ) {
|
plupload_require_post();
|
||||||
http_response_code(403);
|
$userId = plupload_require_admin_user();
|
||||||
echo json_encode( ['error' => 'Brak tokenu uploadu'] );
|
plupload_validate_token($userId);
|
||||||
exit;
|
|
||||||
|
$fileDir = '/upload/article_images/tmp';
|
||||||
|
$targetDir = '../..' . $fileDir;
|
||||||
|
plupload_ensure_target_dir($targetDir);
|
||||||
|
|
||||||
|
list($chunk, $chunks) = plupload_get_chunks();
|
||||||
|
list($fileName, $extension, $filePath, $partPath) = plupload_build_target_paths(
|
||||||
|
$targetDir,
|
||||||
|
$_REQUEST['name'] ?? '',
|
||||||
|
['jpg', 'jpeg', 'png', 'gif', 'webp'],
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
plupload_cleanup_stale_parts($targetDir, $partPath, 5 * 3600);
|
||||||
|
plupload_write_chunk_to_part($partPath, $chunk);
|
||||||
|
plupload_assert_size_limit($partPath, 20 * 1024 * 1024, 'Plik przekracza dozwolony rozmiar (20 MB).');
|
||||||
|
|
||||||
|
$imageId = null;
|
||||||
|
if (plupload_is_last_chunk($chunk, $chunks)) {
|
||||||
|
plupload_finalize_part($partPath, $filePath);
|
||||||
|
|
||||||
|
$mime = mime_content_type($filePath) ?: '';
|
||||||
|
$imageMeta = @getimagesize($filePath);
|
||||||
|
$allowedMimeTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
|
||||||
|
$isValidImage = in_array($mime, $allowedMimeTypes, true)
|
||||||
|
&& is_array($imageMeta)
|
||||||
|
&& (int)($imageMeta[0] ?? 0) > 0
|
||||||
|
&& (int)($imageMeta[1] ?? 0) > 0;
|
||||||
|
|
||||||
|
if (!$isValidImage) {
|
||||||
|
@unlink($filePath);
|
||||||
|
plupload_send_error(400, 601, 'Plik nie jest prawidlowym obrazem.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$mdb = plupload_create_medoo($database);
|
||||||
|
$order = (int)$mdb->max('pp_articles_images', 'o');
|
||||||
|
$articleId = (int)($_POST['article_id'] ?? 0);
|
||||||
|
|
||||||
|
$mdb->insert('pp_articles_images', [
|
||||||
|
'article_id' => $articleId > 0 ? $articleId : null,
|
||||||
|
'src' => substr($filePath, 5),
|
||||||
|
'o' => $order + 1,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$imageId = (int)$mdb->id();
|
||||||
}
|
}
|
||||||
|
|
||||||
$tokenData = $_SESSION['upload_tokens'][$upload_token];
|
plupload_send_success([
|
||||||
if ( $tokenData['expires'] < time() ) {
|
'data_link' => str_replace('../../', '', $filePath),
|
||||||
unset( $_SESSION['upload_tokens'][$upload_token] );
|
'image_id' => $imageId,
|
||||||
http_response_code(403);
|
]);
|
||||||
echo json_encode( ['error' => 'Token wygasł'] );
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$mdb = new medoo( [
|
|
||||||
'database_type' => 'mysql',
|
|
||||||
'database_name' => $database['name'],
|
|
||||||
'server' => $database['host'],
|
|
||||||
'username' => $database['user'],
|
|
||||||
'password' => $database['password'],
|
|
||||||
'charset' => 'utf8'
|
|
||||||
] );
|
|
||||||
|
|
||||||
header( "Expires: Mon, 26 Jul 1997 05:00:00 GMT" );
|
|
||||||
header( "Last-Modified: " . gmdate( "D, d M Y H:i:s" ) . " GMT" );
|
|
||||||
header( "Cache-Control: no-store, no-cache, must-revalidate" );
|
|
||||||
header( "Cache-Control: post-check=0, pre-check=0", false );
|
|
||||||
header( "Pragma: no-cache" );
|
|
||||||
|
|
||||||
$fileDir = '/upload/article_images/tmp';
|
|
||||||
$targetDir = '../..' . $fileDir;
|
|
||||||
|
|
||||||
if ( !is_dir( $targetDir ) )
|
|
||||||
mkdir( $targetDir, 0755, true );
|
|
||||||
|
|
||||||
$cleanupTargetDir = true;
|
|
||||||
$maxFileAge = 5 * 3600;
|
|
||||||
|
|
||||||
$chunk = isset( $_REQUEST["chunk"] ) ? intval( $_REQUEST["chunk"] ) : 0;
|
|
||||||
$chunks = isset( $_REQUEST["chunks"] ) ? intval( $_REQUEST["chunks"] ) : 0;
|
|
||||||
$fileName = isset( $_REQUEST["name"] ) ? $_REQUEST["name"] : '';
|
|
||||||
|
|
||||||
$fileName = preg_replace( '/[^\w\._]+/', '-', $fileName );
|
|
||||||
|
|
||||||
if ( file_exists( $targetDir . DIRECTORY_SEPARATOR . $fileName ) )
|
|
||||||
{
|
|
||||||
$ext = strrpos( $fileName, '.' );
|
|
||||||
$fileName_a = substr( $fileName, 0, $ext );
|
|
||||||
$fileName_b = substr( $fileName, $ext );
|
|
||||||
|
|
||||||
$count = 1;
|
|
||||||
|
|
||||||
while ( file_exists( $targetDir . DIRECTORY_SEPARATOR . $fileName_a . '_' . $count . $fileName_b ) )
|
|
||||||
$count++;
|
|
||||||
|
|
||||||
$fileName = $fileName_a . '_' . $count . $fileName_b;
|
|
||||||
}
|
|
||||||
|
|
||||||
$filePath = $targetDir . DIRECTORY_SEPARATOR . $fileName;
|
|
||||||
|
|
||||||
if ( $cleanupTargetDir && is_dir( $targetDir ) && ( $dir = opendir( $targetDir ) ) )
|
|
||||||
{
|
|
||||||
while ( ( $file = readdir( $dir ) ) !== false )
|
|
||||||
{
|
|
||||||
$tmpfilePath = $targetDir . DIRECTORY_SEPARATOR . $file;
|
|
||||||
|
|
||||||
if ( preg_match( '/\.part$/', $file ) && ( filemtime( $tmpfilePath ) < time() - $maxFileAge ) && ( $tmpfilePath != "{$filePath}.part" ) ) {
|
|
||||||
@unlink( $tmpfilePath );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
closedir($dir);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
die( '{"jsonrpc" : "2.0", "error" : {"code": 100, "message": "Failed to open temp directory."}, "id" : "id"}' );
|
|
||||||
|
|
||||||
if ( isset( $_SERVER["HTTP_CONTENT_TYPE"] ) )
|
|
||||||
$contentType = $_SERVER["HTTP_CONTENT_TYPE"];
|
|
||||||
|
|
||||||
if ( isset( $_SERVER["CONTENT_TYPE"] ) )
|
|
||||||
$contentType = $_SERVER["CONTENT_TYPE"];
|
|
||||||
|
|
||||||
if ( strpos( $contentType, "multipart" ) !== false )
|
|
||||||
{
|
|
||||||
if ( isset( $_FILES['file']['tmp_name'] ) && is_uploaded_file( $_FILES['file']['tmp_name'] ) )
|
|
||||||
{
|
|
||||||
$out = fopen( "{$filePath}.part", $chunk == 0 ? "wb" : "ab" );
|
|
||||||
if ( $out )
|
|
||||||
{
|
|
||||||
$in = fopen( $_FILES['file']['tmp_name'], "rb" );
|
|
||||||
|
|
||||||
if ( $in )
|
|
||||||
{
|
|
||||||
while ( $buff = fread( $in, 4096 ) )
|
|
||||||
fwrite($out, $buff);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
die( '{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}' );
|
|
||||||
fclose( $in );
|
|
||||||
fclose( $out );
|
|
||||||
@unlink( $_FILES['file']['tmp_name'] );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
die( '{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}' );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
die( '{"jsonrpc" : "2.0", "error" : {"code": 103, "message": "Failed to move uploaded file."}, "id" : "id"}' );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$out = fopen( "{$filePath}.part", $chunk == 0 ? "wb" : "ab" );
|
|
||||||
if ( $out )
|
|
||||||
{
|
|
||||||
$in = fopen( "php://input", "rb" );
|
|
||||||
|
|
||||||
if ( $in )
|
|
||||||
{
|
|
||||||
while ( $buff = fread( $in, 4096 ) )
|
|
||||||
fwrite( $out, $buff );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
die( '{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}' );
|
|
||||||
|
|
||||||
fclose( $in );
|
|
||||||
fclose( $out );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
die( '{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}' );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !$chunks || $chunk == $chunks - 1 )
|
|
||||||
{
|
|
||||||
rename( "{$filePath}.part", $filePath );
|
|
||||||
|
|
||||||
$o = $mdb -> max( 'pp_articles_images', 'o' );
|
|
||||||
|
|
||||||
$mdb -> insert( 'pp_articles_images', [
|
|
||||||
'article_id' => $_POST['article_id'] ? $_POST['article_id'] : null,
|
|
||||||
'src' => substr( $filePath, 5, strlen( $filePath ) ),
|
|
||||||
'o' => ++$o
|
|
||||||
] );
|
|
||||||
$image_id = $mdb -> id();
|
|
||||||
}
|
|
||||||
|
|
||||||
die( '{"jsonrpc" : "2.0", "result" : null, "id" : "id", "data_link" : "' . str_replace( '../../', '', $filePath ) . '", "image_id" : "' . $image_id . '"}' );
|
|
||||||
?>
|
|
||||||
307
libraries/plupload/upload-common.php
Normal file
307
libraries/plupload/upload-common.php
Normal file
@@ -0,0 +1,307 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
if (!function_exists('plupload_send_error')) {
|
||||||
|
function plupload_send_error($httpCode, $code, $message)
|
||||||
|
{
|
||||||
|
http_response_code((int)$httpCode);
|
||||||
|
echo json_encode([
|
||||||
|
'jsonrpc' => '2.0',
|
||||||
|
'error' => [
|
||||||
|
'code' => (int)$code,
|
||||||
|
'message' => (string)$message,
|
||||||
|
],
|
||||||
|
'id' => 'id',
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!function_exists('plupload_bootstrap')) {
|
||||||
|
function plupload_bootstrap()
|
||||||
|
{
|
||||||
|
date_default_timezone_set('Europe/Warsaw');
|
||||||
|
|
||||||
|
if (session_status() !== PHP_SESSION_ACTIVE) {
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||||
|
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||||
|
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||||
|
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||||
|
header('Pragma: no-cache');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!function_exists('plupload_require_post')) {
|
||||||
|
function plupload_require_post()
|
||||||
|
{
|
||||||
|
if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') {
|
||||||
|
plupload_send_error(405, 405, 'Method not allowed.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!function_exists('plupload_require_admin_user')) {
|
||||||
|
function plupload_require_admin_user()
|
||||||
|
{
|
||||||
|
$adminSession = isset($_SESSION['admin']) && $_SESSION['admin'] === true;
|
||||||
|
$userId = (int)($_SESSION['user']['id'] ?? 0);
|
||||||
|
|
||||||
|
if (!$adminSession || $userId <= 0) {
|
||||||
|
plupload_send_error(403, 403, 'Brak autoryzacji.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$sessionOk = isset($_SESSION['check'], $_SESSION['ip'])
|
||||||
|
&& $_SESSION['check'] === true
|
||||||
|
&& $_SESSION['ip'] === ($_SERVER['REMOTE_ADDR'] ?? '');
|
||||||
|
|
||||||
|
if (!$sessionOk) {
|
||||||
|
plupload_send_error(403, 403, 'Nieprawidlowa sesja.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $userId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!function_exists('plupload_validate_token')) {
|
||||||
|
function plupload_validate_token($userId)
|
||||||
|
{
|
||||||
|
$uploadToken = (string)($_REQUEST['upload_token'] ?? '');
|
||||||
|
if ($uploadToken === '' || !isset($_SESSION['upload_tokens'][$uploadToken])) {
|
||||||
|
plupload_send_error(403, 403, 'Brak tokenu uploadu.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$tokenData = $_SESSION['upload_tokens'][$uploadToken];
|
||||||
|
$tokenUserId = (int)($tokenData['user_id'] ?? 0);
|
||||||
|
$tokenExpires = (int)($tokenData['expires'] ?? 0);
|
||||||
|
|
||||||
|
if ($tokenUserId <= 0 || $tokenUserId !== (int)$userId) {
|
||||||
|
plupload_send_error(403, 403, 'Token nie nalezy do aktualnego uzytkownika.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($tokenExpires < time()) {
|
||||||
|
unset($_SESSION['upload_tokens'][$uploadToken]);
|
||||||
|
plupload_send_error(403, 403, 'Token wygasl.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return [$uploadToken, $tokenData];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!function_exists('plupload_normalize_filename')) {
|
||||||
|
function plupload_normalize_filename($fileName)
|
||||||
|
{
|
||||||
|
$fileName = basename((string)$fileName);
|
||||||
|
$fileName = preg_replace('/[^\w\.-]+/', '-', $fileName);
|
||||||
|
$fileName = trim((string)$fileName, '.-');
|
||||||
|
|
||||||
|
if ($fileName === '') {
|
||||||
|
$fileName = 'file-' . bin2hex(random_bytes(8));
|
||||||
|
}
|
||||||
|
|
||||||
|
return strtolower($fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!function_exists('plupload_ensure_target_dir')) {
|
||||||
|
function plupload_ensure_target_dir($targetDir)
|
||||||
|
{
|
||||||
|
if (!is_dir($targetDir) && !mkdir($targetDir, 0755, true)) {
|
||||||
|
plupload_send_error(500, 100, 'Failed to open temp directory.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!function_exists('plupload_build_target_paths')) {
|
||||||
|
function plupload_build_target_paths($targetDir, $requestName, $allowedExtensions = null, $blockedExtensions = null, $maxNameLength = 180)
|
||||||
|
{
|
||||||
|
$fileName = plupload_normalize_filename((string)$requestName);
|
||||||
|
$extension = strtolower((string)pathinfo($fileName, PATHINFO_EXTENSION));
|
||||||
|
|
||||||
|
if (is_array($allowedExtensions)) {
|
||||||
|
if ($extension === '' || !in_array($extension, $allowedExtensions, true)) {
|
||||||
|
plupload_send_error(400, 601, 'Nieobslugiwane rozszerzenie pliku.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($blockedExtensions)) {
|
||||||
|
if ($extension !== '' && in_array($extension, $blockedExtensions, true)) {
|
||||||
|
plupload_send_error(400, 601, 'Rozszerzenie pliku jest zablokowane.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen($fileName) > (int)$maxNameLength) {
|
||||||
|
$base = substr((string)pathinfo($fileName, PATHINFO_FILENAME), 0, 140);
|
||||||
|
$suffix = '-' . bin2hex(random_bytes(4));
|
||||||
|
$fileName = $base . $suffix . ($extension !== '' ? '.' . $extension : '');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file_exists($targetDir . DIRECTORY_SEPARATOR . $fileName)) {
|
||||||
|
$nameWithoutExt = (string)pathinfo($fileName, PATHINFO_FILENAME);
|
||||||
|
$extWithDot = $extension !== '' ? '.' . $extension : '';
|
||||||
|
$count = 1;
|
||||||
|
|
||||||
|
while (file_exists($targetDir . DIRECTORY_SEPARATOR . $nameWithoutExt . '_' . $count . $extWithDot)) {
|
||||||
|
$count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fileName = $nameWithoutExt . '_' . $count . $extWithDot;
|
||||||
|
}
|
||||||
|
|
||||||
|
$filePath = $targetDir . DIRECTORY_SEPARATOR . $fileName;
|
||||||
|
$partPath = $filePath . '.part';
|
||||||
|
|
||||||
|
return [$fileName, $extension, $filePath, $partPath];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!function_exists('plupload_get_chunks')) {
|
||||||
|
function plupload_get_chunks()
|
||||||
|
{
|
||||||
|
$chunk = max(0, (int)($_REQUEST['chunk'] ?? 0));
|
||||||
|
$chunks = max(0, (int)($_REQUEST['chunks'] ?? 0));
|
||||||
|
return [$chunk, $chunks];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!function_exists('plupload_cleanup_stale_parts')) {
|
||||||
|
function plupload_cleanup_stale_parts($targetDir, $currentPartPath, $maxFileAge = 18000)
|
||||||
|
{
|
||||||
|
$dir = @opendir($targetDir);
|
||||||
|
if (!$dir) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (($file = readdir($dir)) !== false) {
|
||||||
|
$tmpFilePath = $targetDir . DIRECTORY_SEPARATOR . $file;
|
||||||
|
if (!preg_match('/\.part$/', $file)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($tmpFilePath === $currentPartPath) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (@filemtime($tmpFilePath) < (time() - (int)$maxFileAge)) {
|
||||||
|
@unlink($tmpFilePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir($dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!function_exists('plupload_write_chunk_to_part')) {
|
||||||
|
function plupload_write_chunk_to_part($partPath, $chunk)
|
||||||
|
{
|
||||||
|
$contentType = (string)($_SERVER['HTTP_CONTENT_TYPE'] ?? $_SERVER['CONTENT_TYPE'] ?? '');
|
||||||
|
$isMultipart = strpos($contentType, 'multipart') !== false;
|
||||||
|
|
||||||
|
if ($isMultipart) {
|
||||||
|
$fileInfo = $_FILES['file'] ?? null;
|
||||||
|
if (!is_array($fileInfo) || !isset($fileInfo['tmp_name']) || !is_uploaded_file($fileInfo['tmp_name'])) {
|
||||||
|
plupload_send_error(400, 103, 'Failed to move uploaded file.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((int)($fileInfo['error'] ?? UPLOAD_ERR_OK) !== UPLOAD_ERR_OK) {
|
||||||
|
plupload_send_error(400, 104, 'Upload error.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$in = fopen($fileInfo['tmp_name'], 'rb');
|
||||||
|
$out = fopen($partPath, ((int)$chunk === 0) ? 'wb' : 'ab');
|
||||||
|
|
||||||
|
if (!$in) {
|
||||||
|
plupload_send_error(500, 101, 'Failed to open input stream.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$out) {
|
||||||
|
fclose($in);
|
||||||
|
plupload_send_error(500, 102, 'Failed to open output stream.');
|
||||||
|
}
|
||||||
|
|
||||||
|
while ($buff = fread($in, 4096)) {
|
||||||
|
fwrite($out, $buff);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose($in);
|
||||||
|
fclose($out);
|
||||||
|
@unlink($fileInfo['tmp_name']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$in = fopen('php://input', 'rb');
|
||||||
|
$out = fopen($partPath, ((int)$chunk === 0) ? 'wb' : 'ab');
|
||||||
|
|
||||||
|
if (!$in) {
|
||||||
|
plupload_send_error(500, 101, 'Failed to open input stream.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$out) {
|
||||||
|
fclose($in);
|
||||||
|
plupload_send_error(500, 102, 'Failed to open output stream.');
|
||||||
|
}
|
||||||
|
|
||||||
|
while ($buff = fread($in, 4096)) {
|
||||||
|
fwrite($out, $buff);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose($in);
|
||||||
|
fclose($out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!function_exists('plupload_assert_size_limit')) {
|
||||||
|
function plupload_assert_size_limit($partPath, $maxBytes, $message)
|
||||||
|
{
|
||||||
|
if (@filesize($partPath) > (int)$maxBytes) {
|
||||||
|
@unlink($partPath);
|
||||||
|
plupload_send_error(413, 413, (string)$message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!function_exists('plupload_is_last_chunk')) {
|
||||||
|
function plupload_is_last_chunk($chunk, $chunks)
|
||||||
|
{
|
||||||
|
return ((int)$chunks === 0) || ((int)$chunk === ((int)$chunks - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!function_exists('plupload_finalize_part')) {
|
||||||
|
function plupload_finalize_part($partPath, $filePath)
|
||||||
|
{
|
||||||
|
if (!@rename($partPath, $filePath)) {
|
||||||
|
@unlink($partPath);
|
||||||
|
plupload_send_error(500, 105, 'Failed to finalize uploaded file.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!function_exists('plupload_create_medoo')) {
|
||||||
|
function plupload_create_medoo($database)
|
||||||
|
{
|
||||||
|
return new medoo([
|
||||||
|
'database_type' => 'mysql',
|
||||||
|
'database_name' => $database['name'],
|
||||||
|
'server' => $database['host'],
|
||||||
|
'username' => $database['user'],
|
||||||
|
'password' => $database['password'],
|
||||||
|
'charset' => 'utf8',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!function_exists('plupload_send_success')) {
|
||||||
|
function plupload_send_success(array $payload)
|
||||||
|
{
|
||||||
|
echo json_encode(array_merge([
|
||||||
|
'jsonrpc' => '2.0',
|
||||||
|
'result' => null,
|
||||||
|
'id' => 'id',
|
||||||
|
], $payload));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,154 +1,53 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once '../../config.php';
|
require_once '../../config.php';
|
||||||
require_once '../medoo/medoo.php';
|
require_once '../medoo/medoo.php';
|
||||||
date_default_timezone_set( 'Europe/Warsaw' );
|
require_once 'upload-common.php';
|
||||||
session_start();
|
|
||||||
|
|
||||||
$upload_token = $_REQUEST['upload_token'] ?? '';
|
plupload_bootstrap();
|
||||||
if ( !isset( $_SESSION['upload_tokens'][$upload_token] ) ) {
|
plupload_require_post();
|
||||||
http_response_code(403);
|
$userId = plupload_require_admin_user();
|
||||||
echo json_encode( ['error' => 'Brak tokenu uploadu'] );
|
plupload_validate_token($userId);
|
||||||
exit;
|
|
||||||
|
$fileDir = '/upload/product_files/tmp';
|
||||||
|
$targetDir = '../..' . $fileDir;
|
||||||
|
plupload_ensure_target_dir($targetDir);
|
||||||
|
|
||||||
|
list($chunk, $chunks) = plupload_get_chunks();
|
||||||
|
list($fileName, $extension, $filePath, $partPath) = plupload_build_target_paths(
|
||||||
|
$targetDir,
|
||||||
|
$_REQUEST['name'] ?? '',
|
||||||
|
null,
|
||||||
|
[
|
||||||
|
'php', 'php3', 'php4', 'php5', 'php7', 'php8', 'phtml', 'phar',
|
||||||
|
'cgi', 'pl', 'py', 'rb',
|
||||||
|
'asp', 'aspx', 'jsp',
|
||||||
|
'js', 'mjs', 'vbs', 'wsf', 'hta',
|
||||||
|
'sh', 'bash', 'zsh', 'ps1', 'bat', 'cmd', 'com',
|
||||||
|
'exe', 'msi', 'scr', 'dll', 'jar',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
plupload_cleanup_stale_parts($targetDir, $partPath, 5 * 3600);
|
||||||
|
plupload_write_chunk_to_part($partPath, $chunk);
|
||||||
|
plupload_assert_size_limit($partPath, 50 * 1024 * 1024, 'Plik przekracza dozwolony rozmiar (50 MB).');
|
||||||
|
|
||||||
|
$fileId = null;
|
||||||
|
$responseFileName = $fileName;
|
||||||
|
if (plupload_is_last_chunk($chunk, $chunks)) {
|
||||||
|
plupload_finalize_part($partPath, $filePath);
|
||||||
|
|
||||||
|
$mdb = plupload_create_medoo($database);
|
||||||
|
$mdb->insert('pp_shop_products_files', [
|
||||||
|
'product_id' => null,
|
||||||
|
'src' => substr($filePath, 5),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$fileId = (int)$mdb->id();
|
||||||
|
$responseFileName = basename($filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
$tokenData = $_SESSION['upload_tokens'][$upload_token];
|
plupload_send_success([
|
||||||
if ( $tokenData['expires'] < time() ) {
|
'file_name' => $responseFileName,
|
||||||
unset( $_SESSION['upload_tokens'][$upload_token] );
|
'file_id' => $fileId,
|
||||||
http_response_code(403);
|
]);
|
||||||
echo json_encode( ['error' => 'Token wygasł'] );
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$mdb = new medoo( [
|
|
||||||
'database_type' => 'mysql',
|
|
||||||
'database_name' => $database['name'],
|
|
||||||
'server' => $database['host'],
|
|
||||||
'username' => $database['user'],
|
|
||||||
'password' => $database['password'],
|
|
||||||
'charset' => 'utf8'
|
|
||||||
] );
|
|
||||||
|
|
||||||
header( "Expires: Mon, 26 Jul 1997 05:00:00 GMT" );
|
|
||||||
header( "Last-Modified: " . gmdate( "D, d M Y H:i:s" ) . " GMT" );
|
|
||||||
header( "Cache-Control: no-store, no-cache, must-revalidate" );
|
|
||||||
header( "Cache-Control: post-check=0, pre-check=0", false );
|
|
||||||
header( "Pragma: no-cache" );
|
|
||||||
|
|
||||||
$fileDir = '/upload/product_files/tmp';
|
|
||||||
$targetDir = '../..' . $fileDir;
|
|
||||||
|
|
||||||
if ( !is_dir( $targetDir ) )
|
|
||||||
mkdir( $targetDir, 0755, true );
|
|
||||||
|
|
||||||
$cleanupTargetDir = true;
|
|
||||||
$maxFileAge = 5 * 3600;
|
|
||||||
|
|
||||||
$chunk = isset( $_REQUEST["chunk"] ) ? intval( $_REQUEST["chunk"] ) : 0;
|
|
||||||
$chunks = isset( $_REQUEST["chunks"] ) ? intval( $_REQUEST["chunks"] ) : 0;
|
|
||||||
$fileName = isset( $_REQUEST["name"] ) ? $_REQUEST["name"] : '';
|
|
||||||
|
|
||||||
$fileName = preg_replace( '/[^\w\._]+/', '-', $fileName );
|
|
||||||
|
|
||||||
if ( file_exists( $targetDir . DIRECTORY_SEPARATOR . $fileName ) )
|
|
||||||
{
|
|
||||||
$ext = strrpos( $fileName, '.' );
|
|
||||||
$fileName_a = substr( $fileName, 0, $ext );
|
|
||||||
$fileName_b = substr( $fileName, $ext );
|
|
||||||
|
|
||||||
$count = 1;
|
|
||||||
|
|
||||||
while ( file_exists( $targetDir . DIRECTORY_SEPARATOR . $fileName_a . '_' . $count . $fileName_b ) )
|
|
||||||
$count++;
|
|
||||||
|
|
||||||
$fileName = $fileName_a . '_' . $count . $fileName_b;
|
|
||||||
}
|
|
||||||
|
|
||||||
$filePath = $targetDir . DIRECTORY_SEPARATOR . $fileName;
|
|
||||||
|
|
||||||
if ( $cleanupTargetDir && is_dir( $targetDir ) && ( $dir = opendir( $targetDir ) ) )
|
|
||||||
{
|
|
||||||
while ( ( $file = readdir( $dir ) ) !== false )
|
|
||||||
{
|
|
||||||
$tmpfilePath = $targetDir . DIRECTORY_SEPARATOR . $file;
|
|
||||||
|
|
||||||
if ( preg_match( '/\.part$/', $file ) && ( filemtime( $tmpfilePath ) < time() - $maxFileAge ) && ( $tmpfilePath != "{$filePath}.part" ) ) {
|
|
||||||
@unlink( $tmpfilePath );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
closedir($dir);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
die( '{"jsonrpc" : "2.0", "error" : {"code": 100, "message": "Failed to open temp directory."}, "id" : "id"}' );
|
|
||||||
|
|
||||||
if ( isset( $_SERVER["HTTP_CONTENT_TYPE"] ) )
|
|
||||||
$contentType = $_SERVER["HTTP_CONTENT_TYPE"];
|
|
||||||
|
|
||||||
if ( isset( $_SERVER["CONTENT_TYPE"] ) )
|
|
||||||
$contentType = $_SERVER["CONTENT_TYPE"];
|
|
||||||
|
|
||||||
if ( strpos( $contentType, "multipart" ) !== false )
|
|
||||||
{
|
|
||||||
if ( isset( $_FILES['file']['tmp_name'] ) && is_uploaded_file( $_FILES['file']['tmp_name'] ) )
|
|
||||||
{
|
|
||||||
$out = fopen( "{$filePath}.part", $chunk == 0 ? "wb" : "ab" );
|
|
||||||
if ( $out )
|
|
||||||
{
|
|
||||||
$in = fopen( $_FILES['file']['tmp_name'], "rb" );
|
|
||||||
|
|
||||||
if ( $in )
|
|
||||||
{
|
|
||||||
while ( $buff = fread( $in, 4096 ) )
|
|
||||||
fwrite($out, $buff);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
die( '{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}' );
|
|
||||||
fclose( $in );
|
|
||||||
fclose( $out );
|
|
||||||
@unlink( $_FILES['file']['tmp_name'] );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
die( '{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}' );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
die( '{"jsonrpc" : "2.0", "error" : {"code": 103, "message": "Failed to move uploaded file."}, "id" : "id"}' );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$out = fopen( "{$filePath}.part", $chunk == 0 ? "wb" : "ab" );
|
|
||||||
if ( $out )
|
|
||||||
{
|
|
||||||
$in = fopen( "php://input", "rb" );
|
|
||||||
|
|
||||||
if ( $in )
|
|
||||||
{
|
|
||||||
while ( $buff = fread( $in, 4096 ) )
|
|
||||||
fwrite( $out, $buff );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
die( '{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}' );
|
|
||||||
|
|
||||||
fclose( $in );
|
|
||||||
fclose( $out );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
die( '{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}' );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !$chunks || $chunk == $chunks - 1 )
|
|
||||||
{
|
|
||||||
rename( "{$filePath}.part", $filePath );
|
|
||||||
|
|
||||||
$mdb -> insert( 'pp_shop_products_files', [
|
|
||||||
'product_id' => null,
|
|
||||||
'src' => substr( $filePath, 5, strlen( $filePath ) )
|
|
||||||
] );
|
|
||||||
|
|
||||||
$file_id = $mdb -> id();
|
|
||||||
|
|
||||||
$file_name = explode( '/', $filePath );
|
|
||||||
$file_name = $file_name[ count( $file_name ) - 1 ];
|
|
||||||
}
|
|
||||||
|
|
||||||
die( '{"jsonrpc" : "2.0", "result" : null, "id" : "id", "file_name" : "' . $file_name . '", "file_id" : "' . $file_id . '"}' );
|
|
||||||
?>
|
|
||||||
@@ -1,154 +1,61 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once '../../config.php';
|
require_once '../../config.php';
|
||||||
require_once '../medoo/medoo.php';
|
require_once '../medoo/medoo.php';
|
||||||
date_default_timezone_set( 'Europe/Warsaw' );
|
require_once 'upload-common.php';
|
||||||
session_start();
|
|
||||||
|
|
||||||
$upload_token = $_REQUEST['upload_token'] ?? '';
|
plupload_bootstrap();
|
||||||
if ( !isset( $_SESSION['upload_tokens'][$upload_token] ) ) {
|
plupload_require_post();
|
||||||
http_response_code(403);
|
$userId = plupload_require_admin_user();
|
||||||
echo json_encode( ['error' => 'Brak tokenu uploadu'] );
|
plupload_validate_token($userId);
|
||||||
exit;
|
|
||||||
|
$fileDir = '/upload/product_images/tmp';
|
||||||
|
$targetDir = '../..' . $fileDir;
|
||||||
|
plupload_ensure_target_dir($targetDir);
|
||||||
|
|
||||||
|
list($chunk, $chunks) = plupload_get_chunks();
|
||||||
|
list($fileName, $extension, $filePath, $partPath) = plupload_build_target_paths(
|
||||||
|
$targetDir,
|
||||||
|
$_REQUEST['name'] ?? '',
|
||||||
|
['jpg', 'jpeg', 'png', 'gif', 'webp'],
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
plupload_cleanup_stale_parts($targetDir, $partPath, 5 * 3600);
|
||||||
|
plupload_write_chunk_to_part($partPath, $chunk);
|
||||||
|
plupload_assert_size_limit($partPath, 20 * 1024 * 1024, 'Plik przekracza dozwolony rozmiar (20 MB).');
|
||||||
|
|
||||||
|
$imageId = null;
|
||||||
|
if (plupload_is_last_chunk($chunk, $chunks)) {
|
||||||
|
plupload_finalize_part($partPath, $filePath);
|
||||||
|
|
||||||
|
$mime = mime_content_type($filePath) ?: '';
|
||||||
|
$imageMeta = @getimagesize($filePath);
|
||||||
|
$allowedMimeTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
|
||||||
|
$isValidImage = in_array($mime, $allowedMimeTypes, true)
|
||||||
|
&& is_array($imageMeta)
|
||||||
|
&& (int)($imageMeta[0] ?? 0) > 0
|
||||||
|
&& (int)($imageMeta[1] ?? 0) > 0;
|
||||||
|
|
||||||
|
if (!$isValidImage) {
|
||||||
|
@unlink($filePath);
|
||||||
|
plupload_send_error(400, 601, 'Plik nie jest prawidlowym obrazem.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$mdb = plupload_create_medoo($database);
|
||||||
|
$order = (int)$mdb->max('pp_shop_products_images', 'o');
|
||||||
|
$productId = (int)($_POST['product_id'] ?? 0);
|
||||||
|
|
||||||
|
$mdb->insert('pp_shop_products_images', [
|
||||||
|
'product_id' => $productId > 0 ? $productId : null,
|
||||||
|
'src' => substr($filePath, 5),
|
||||||
|
'o' => $order + 1,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$imageId = (int)$mdb->id();
|
||||||
}
|
}
|
||||||
|
|
||||||
$tokenData = $_SESSION['upload_tokens'][$upload_token];
|
plupload_send_success([
|
||||||
if ( $tokenData['expires'] < time() ) {
|
'data_link' => str_replace('../../', '', $filePath),
|
||||||
unset( $_SESSION['upload_tokens'][$upload_token] );
|
'image_id' => $imageId,
|
||||||
http_response_code(403);
|
]);
|
||||||
echo json_encode( ['error' => 'Token wygasł'] );
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$mdb = new medoo( [
|
|
||||||
'database_type' => 'mysql',
|
|
||||||
'database_name' => $database['name'],
|
|
||||||
'server' => $database['host'],
|
|
||||||
'username' => $database['user'],
|
|
||||||
'password' => $database['password'],
|
|
||||||
'charset' => 'utf8'
|
|
||||||
] );
|
|
||||||
|
|
||||||
header( "Expires: Mon, 26 Jul 1997 05:00:00 GMT" );
|
|
||||||
header( "Last-Modified: " . gmdate( "D, d M Y H:i:s" ) . " GMT" );
|
|
||||||
header( "Cache-Control: no-store, no-cache, must-revalidate" );
|
|
||||||
header( "Cache-Control: post-check=0, pre-check=0", false );
|
|
||||||
header( "Pragma: no-cache" );
|
|
||||||
|
|
||||||
$fileDir = '/upload/product_images/tmp';
|
|
||||||
$targetDir = '../..' . $fileDir;
|
|
||||||
|
|
||||||
if ( !is_dir( $targetDir ) )
|
|
||||||
mkdir( $targetDir, 0755, true );
|
|
||||||
|
|
||||||
$cleanupTargetDir = true;
|
|
||||||
$maxFileAge = 5 * 3600;
|
|
||||||
|
|
||||||
$chunk = isset( $_REQUEST["chunk"] ) ? intval( $_REQUEST["chunk"] ) : 0;
|
|
||||||
$chunks = isset( $_REQUEST["chunks"] ) ? intval( $_REQUEST["chunks"] ) : 0;
|
|
||||||
$fileName = isset( $_REQUEST["name"] ) ? $_REQUEST["name"] : '';
|
|
||||||
|
|
||||||
$fileName = preg_replace( '/[^\w\._]+/', '-', $fileName );
|
|
||||||
|
|
||||||
if ( file_exists( $targetDir . DIRECTORY_SEPARATOR . $fileName ) )
|
|
||||||
{
|
|
||||||
$ext = strrpos( $fileName, '.' );
|
|
||||||
$fileName_a = substr( $fileName, 0, $ext );
|
|
||||||
$fileName_b = substr( $fileName, $ext );
|
|
||||||
|
|
||||||
$count = 1;
|
|
||||||
|
|
||||||
while ( file_exists( $targetDir . DIRECTORY_SEPARATOR . $fileName_a . '_' . $count . $fileName_b ) )
|
|
||||||
$count++;
|
|
||||||
|
|
||||||
$fileName = $fileName_a . '_' . $count . $fileName_b;
|
|
||||||
}
|
|
||||||
|
|
||||||
$filePath = $targetDir . DIRECTORY_SEPARATOR . $fileName;
|
|
||||||
|
|
||||||
if ( $cleanupTargetDir && is_dir( $targetDir ) && ( $dir = opendir( $targetDir ) ) )
|
|
||||||
{
|
|
||||||
while ( ( $file = readdir( $dir ) ) !== false )
|
|
||||||
{
|
|
||||||
$tmpfilePath = $targetDir . DIRECTORY_SEPARATOR . $file;
|
|
||||||
|
|
||||||
if ( preg_match( '/\.part$/', $file ) && ( filemtime( $tmpfilePath ) < time() - $maxFileAge ) && ( $tmpfilePath != "{$filePath}.part" ) ) {
|
|
||||||
@unlink( $tmpfilePath );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
closedir($dir);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
die( '{"jsonrpc" : "2.0", "error" : {"code": 100, "message": "Failed to open temp directory."}, "id" : "id"}' );
|
|
||||||
|
|
||||||
if ( isset( $_SERVER["HTTP_CONTENT_TYPE"] ) )
|
|
||||||
$contentType = $_SERVER["HTTP_CONTENT_TYPE"];
|
|
||||||
|
|
||||||
if ( isset( $_SERVER["CONTENT_TYPE"] ) )
|
|
||||||
$contentType = $_SERVER["CONTENT_TYPE"];
|
|
||||||
|
|
||||||
if ( strpos( $contentType, "multipart" ) !== false )
|
|
||||||
{
|
|
||||||
if ( isset( $_FILES['file']['tmp_name'] ) && is_uploaded_file( $_FILES['file']['tmp_name'] ) )
|
|
||||||
{
|
|
||||||
$out = fopen( "{$filePath}.part", $chunk == 0 ? "wb" : "ab" );
|
|
||||||
if ( $out )
|
|
||||||
{
|
|
||||||
$in = fopen( $_FILES['file']['tmp_name'], "rb" );
|
|
||||||
|
|
||||||
if ( $in )
|
|
||||||
{
|
|
||||||
while ( $buff = fread( $in, 4096 ) )
|
|
||||||
fwrite($out, $buff);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
die( '{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}' );
|
|
||||||
fclose( $in );
|
|
||||||
fclose( $out );
|
|
||||||
@unlink( $_FILES['file']['tmp_name'] );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
die( '{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}' );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
die( '{"jsonrpc" : "2.0", "error" : {"code": 103, "message": "Failed to move uploaded file."}, "id" : "id"}' );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$out = fopen( "{$filePath}.part", $chunk == 0 ? "wb" : "ab" );
|
|
||||||
if ( $out )
|
|
||||||
{
|
|
||||||
$in = fopen( "php://input", "rb" );
|
|
||||||
|
|
||||||
if ( $in )
|
|
||||||
{
|
|
||||||
while ( $buff = fread( $in, 4096 ) )
|
|
||||||
fwrite( $out, $buff );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
die( '{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}' );
|
|
||||||
|
|
||||||
fclose( $in );
|
|
||||||
fclose( $out );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
die( '{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}' );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !$chunks || $chunk == $chunks - 1 )
|
|
||||||
{
|
|
||||||
rename( "{$filePath}.part", $filePath );
|
|
||||||
|
|
||||||
$o = $mdb -> max( 'pp_shop_products_images', 'o' );
|
|
||||||
|
|
||||||
$mdb -> insert( 'pp_shop_products_images', [
|
|
||||||
'product_id' => isset( $_POST['product_id'] ) ? $_POST['product_id'] : null,
|
|
||||||
'src' => substr( $filePath, 5, strlen( $filePath ) ),
|
|
||||||
'o' => ++$o
|
|
||||||
] );
|
|
||||||
|
|
||||||
$image_id = $mdb -> id();
|
|
||||||
}
|
|
||||||
|
|
||||||
die( '{"jsonrpc" : "2.0", "result" : null, "id" : "id", "data_link" : "' . str_replace( '../../', '', $filePath ) . '", "image_id" : "' . $image_id . '"}' );
|
|
||||||
?>
|
|
||||||
@@ -413,6 +413,56 @@ class ArticleRepositoryTest extends TestCase
|
|||||||
$this->assertTrue($result);
|
$this->assertTrue($result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testSaveFilesOrderUpdatesFilesOrder(): void
|
||||||
|
{
|
||||||
|
$mockDb = $this->createMock(\medoo::class);
|
||||||
|
|
||||||
|
$mockDb->expects($this->exactly(3))
|
||||||
|
->method('update')
|
||||||
|
->withConsecutive(
|
||||||
|
[
|
||||||
|
'pp_articles_files',
|
||||||
|
['o' => 0],
|
||||||
|
['AND' => ['article_id' => 12, 'id' => 70]]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'pp_articles_files',
|
||||||
|
['o' => 1],
|
||||||
|
['AND' => ['article_id' => 12, 'id' => 71]]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'pp_articles_files',
|
||||||
|
['o' => 2],
|
||||||
|
['AND' => ['article_id' => 12, 'id' => 72]]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
->willReturn(true);
|
||||||
|
|
||||||
|
$repository = new ArticleRepository($mockDb);
|
||||||
|
$result = $repository->saveFilesOrder(12, '70;71;72');
|
||||||
|
|
||||||
|
$this->assertTrue($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSaveFilesOrderSkipsEmptyValues(): void
|
||||||
|
{
|
||||||
|
$mockDb = $this->createMock(\medoo::class);
|
||||||
|
|
||||||
|
$mockDb->expects($this->once())
|
||||||
|
->method('update')
|
||||||
|
->with(
|
||||||
|
'pp_articles_files',
|
||||||
|
['o' => 0],
|
||||||
|
['AND' => ['article_id' => 7, 'id' => 101]]
|
||||||
|
)
|
||||||
|
->willReturn(true);
|
||||||
|
|
||||||
|
$repository = new ArticleRepository($mockDb);
|
||||||
|
$result = $repository->saveFilesOrder(7, ';101;');
|
||||||
|
|
||||||
|
$this->assertTrue($result);
|
||||||
|
}
|
||||||
|
|
||||||
public function testArchiveSetsStatusToMinusOne(): void
|
public function testArchiveSetsStatusToMinusOne(): void
|
||||||
{
|
{
|
||||||
$mockDb = $this->createMock(\medoo::class);
|
$mockDb = $this->createMock(\medoo::class);
|
||||||
@@ -482,6 +532,56 @@ class ArticleRepositoryTest extends TestCase
|
|||||||
$this->assertSame('pp_articles', $deleteCalls[4]['table']);
|
$this->assertSame('pp_articles', $deleteCalls[4]['table']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testPagesSummaryForArticlesBuildsLabels(): void
|
||||||
|
{
|
||||||
|
$mockDb = $this->createMock(\medoo::class);
|
||||||
|
|
||||||
|
$mockDb->expects($this->once())
|
||||||
|
->method('query')
|
||||||
|
->willReturnCallback(function ($sql, $params = []) {
|
||||||
|
return new class {
|
||||||
|
public function fetchAll()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
['article_id' => 5, 'page_id' => 10, 'title' => 'Blog'],
|
||||||
|
['article_id' => 5, 'page_id' => 11, 'title' => 'Poradniki'],
|
||||||
|
['article_id' => 8, 'page_id' => 12, 'title' => 'Aktualnosci'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
$repository = new ArticleRepository($mockDb);
|
||||||
|
$result = $repository->pagesSummaryForArticles([5, 8]);
|
||||||
|
|
||||||
|
$this->assertSame(' - Blog / Poradniki', $result[5]);
|
||||||
|
$this->assertSame(' - Aktualnosci', $result[8]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUpdateImageAltDelegatesToDatabase(): void
|
||||||
|
{
|
||||||
|
$mockDb = $this->createMock(\medoo::class);
|
||||||
|
$mockDb->expects($this->once())
|
||||||
|
->method('update')
|
||||||
|
->with('pp_articles_images', ['alt' => 'Nowy alt'], ['id' => 33])
|
||||||
|
->willReturn(true);
|
||||||
|
|
||||||
|
$repository = new ArticleRepository($mockDb);
|
||||||
|
$this->assertTrue($repository->updateImageAlt(33, 'Nowy alt'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMarkFileToDeleteDelegatesToDatabase(): void
|
||||||
|
{
|
||||||
|
$mockDb = $this->createMock(\medoo::class);
|
||||||
|
$mockDb->expects($this->once())
|
||||||
|
->method('update')
|
||||||
|
->with('pp_articles_files', ['to_delete' => 1], ['id' => 17])
|
||||||
|
->willReturn(true);
|
||||||
|
|
||||||
|
$repository = new ArticleRepository($mockDb);
|
||||||
|
$this->assertTrue($repository->markFileToDelete(17));
|
||||||
|
}
|
||||||
|
|
||||||
public function testListArchivedForAdminWhitelistsSortAndDirection(): void
|
public function testListArchivedForAdminWhitelistsSortAndDirection(): void
|
||||||
{
|
{
|
||||||
$mockDb = $this->createMock(\medoo::class);
|
$mockDb = $this->createMock(\medoo::class);
|
||||||
|
|||||||
@@ -56,6 +56,26 @@ class ArticlesControllerTest extends TestCase
|
|||||||
$this->assertTrue(method_exists($this->controller, 'galleryOrderSave'));
|
$this->assertTrue(method_exists($this->controller, 'galleryOrderSave'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testHasImageAltChangeMethod(): void
|
||||||
|
{
|
||||||
|
$this->assertTrue(method_exists($this->controller, 'imageAltChange'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testHasFileNameChangeMethod(): void
|
||||||
|
{
|
||||||
|
$this->assertTrue(method_exists($this->controller, 'fileNameChange'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testHasImageDeleteMethod(): void
|
||||||
|
{
|
||||||
|
$this->assertTrue(method_exists($this->controller, 'imageDelete'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testHasFileDeleteMethod(): void
|
||||||
|
{
|
||||||
|
$this->assertTrue(method_exists($this->controller, 'fileDelete'));
|
||||||
|
}
|
||||||
|
|
||||||
public function testListMethodReturnType(): void
|
public function testListMethodReturnType(): void
|
||||||
{
|
{
|
||||||
$reflection = new \ReflectionClass($this->controller);
|
$reflection = new \ReflectionClass($this->controller);
|
||||||
@@ -74,6 +94,30 @@ class ArticlesControllerTest extends TestCase
|
|||||||
$this->assertEquals('void', (string)$reflection->getMethod('galleryOrderSave')->getReturnType());
|
$this->assertEquals('void', (string)$reflection->getMethod('galleryOrderSave')->getReturnType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testImageAltChangeMethodReturnType(): void
|
||||||
|
{
|
||||||
|
$reflection = new \ReflectionClass($this->controller);
|
||||||
|
$this->assertEquals('void', (string)$reflection->getMethod('imageAltChange')->getReturnType());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFileNameChangeMethodReturnType(): void
|
||||||
|
{
|
||||||
|
$reflection = new \ReflectionClass($this->controller);
|
||||||
|
$this->assertEquals('void', (string)$reflection->getMethod('fileNameChange')->getReturnType());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testImageDeleteMethodReturnType(): void
|
||||||
|
{
|
||||||
|
$reflection = new \ReflectionClass($this->controller);
|
||||||
|
$this->assertEquals('void', (string)$reflection->getMethod('imageDelete')->getReturnType());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFileDeleteMethodReturnType(): void
|
||||||
|
{
|
||||||
|
$reflection = new \ReflectionClass($this->controller);
|
||||||
|
$this->assertEquals('void', (string)$reflection->getMethod('fileDelete')->getReturnType());
|
||||||
|
}
|
||||||
|
|
||||||
public function testConstructorRequiresArticleRepository(): void
|
public function testConstructorRequiresArticleRepository(): void
|
||||||
{
|
{
|
||||||
$reflection = new \ReflectionClass(ArticlesController::class);
|
$reflection = new \ReflectionClass(ArticlesController::class);
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ if (!class_exists('S')) {
|
|||||||
public static function delete_dir($path) {}
|
public static function delete_dir($path) {}
|
||||||
public static function alert($msg) {}
|
public static function alert($msg) {}
|
||||||
public static function htacces() {}
|
public static function htacces() {}
|
||||||
|
public static function delete_cache() {}
|
||||||
public static function get($key) { return null; }
|
public static function get($key) { return null; }
|
||||||
public static function set_message($msg) {}
|
public static function set_message($msg) {}
|
||||||
public static function clear_redis_cache() {}
|
public static function clear_redis_cache() {}
|
||||||
|
|||||||
BIN
updates/0.20/ver_0.262.zip
Normal file
BIN
updates/0.20/ver_0.262.zip
Normal file
Binary file not shown.
2
updates/0.20/ver_0.262_files.txt
Normal file
2
updates/0.20/ver_0.262_files.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
F: ../admin/ajax/articles.php
|
||||||
|
F: ../autoload/admin/view/class.Articles.php
|
||||||
2
updates/0.20/ver_0.262_sql.txt
Normal file
2
updates/0.20/ver_0.262_sql.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE pp_articles_files ADD COLUMN o int(11) NOT NULL DEFAULT 0;
|
||||||
|
UPDATE pp_articles_files SET o = id WHERE o = 0;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?
|
<?
|
||||||
$current_ver = 258;
|
$current_ver = 262;
|
||||||
|
|
||||||
for ($i = 1; $i <= $current_ver; $i++)
|
for ($i = 1; $i <= $current_ver; $i++)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user