Articles: finish admin refactor, uploads hardening, and attachment sorting (0.262)

This commit is contained in:
2026-02-13 09:00:24 +01:00
parent 42e4396064
commit 5354f97baa
31 changed files with 1951 additions and 1512 deletions

View File

@@ -37,7 +37,15 @@ class ArticleRepository
'article_id' => $articleId,
'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]);
return $article;
@@ -70,6 +78,8 @@ class ArticleRepository
$this->savePages($id, $data['pages'] ?? null, true);
$this->assignTempFiles($id);
$this->assignTempImages($id);
$this->applyGalleryOrderIfProvided($id, $data);
$this->applyFilesOrderIfProvided($id, $data);
\S::htacces();
\S::delete_dir('../temp/');
@@ -87,6 +97,8 @@ class ArticleRepository
$this->savePages($articleId, $data['pages'] ?? null, false);
$this->assignTempFiles($articleId);
$this->assignTempImages($articleId);
$this->applyGalleryOrderIfProvided($articleId, $data);
$this->applyFilesOrderIfProvided($articleId, $data);
$this->deleteMarkedImages($articleId);
$this->deleteMarkedFiles($articleId);
@@ -99,16 +111,16 @@ class ArticleRepository
private function buildArticleRow(array $data, int $userId, bool $isNew): array
{
$row = [
'show_title' => ($data['show_title'] ?? '') == 'on' ? 1 : 0,
'show_date_add' => ($data['show_date_add'] ?? '') == 'on' ? 1 : 0,
'show_date_modify' => ($data['show_date_modify'] ?? '') == 'on' ? 1 : 0,
'show_title' => $this->isCheckedValue($data['show_title'] ?? null) ? 1 : 0,
'show_date_add' => $this->isCheckedValue($data['show_date_add'] ?? null) ? 1 : 0,
'show_date_modify' => $this->isCheckedValue($data['show_date_modify'] ?? null) ? 1 : 0,
'date_modify' => date('Y-m-d H:i:s'),
'modify_by' => $userId,
'layout_id' => !empty($data['layout_id']) ? (int)$data['layout_id'] : null,
'status' => ($data['status'] ?? '') == 'on' ? 1 : 0,
'repeat_entry' => ($data['repeat_entry'] ?? '') == 'on' ? 1 : 0,
'social_icons' => ($data['social_icons'] ?? '') == 'on' ? 1 : 0,
'show_table_of_contents' => ($data['show_table_of_contents'] ?? '') == 'on' ? 1 : 0,
'status' => $this->isCheckedValue($data['status'] ?? null) ? 1 : 0,
'repeat_entry' => $this->isCheckedValue($data['repeat_entry'] ?? null) ? 1 : 0,
'social_icons' => $this->isCheckedValue($data['social_icons'] ?? null) ? 1 : 0,
'show_table_of_contents' => $this->isCheckedValue($data['show_table_of_contents'] ?? null) ? 1 : 0,
];
if ($isNew) {
@@ -131,12 +143,32 @@ class ArticleRepository
'meta_description' => ($data['meta_description'][$langId] ?? '') != '' ? $data['meta_description'][$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,
'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,
'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
{
$titles = $data['title'] ?? [];
@@ -573,6 +605,182 @@ class ArticleRepository
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(
array &$where,
array &$params,