0) {
$message = 'Загружено: ' . $result['ok'];
}
}
}
if ($action === 'photo_update') {
$category = sanitizeCategoryName((string)($_POST['category'] ?? ''));
$currentFile = basename((string)($_POST['photo_current'] ?? ''));
$newBase = sanitizeFileBase((string)($_POST['photo_new_name'] ?? ''));
$sortIndex = (int)($_POST['photo_sort'] ?? 1000);
$src = $photosDir . '/' . $category . '/' . $currentFile;
if ($category === '' || $currentFile === '' || !is_file($src)) {
$errors[] = 'Фото не найдено.';
} else {
$finalName = $currentFile;
if ($newBase !== '') {
$ext = strtolower(pathinfo($currentFile, PATHINFO_EXTENSION));
$candidate = uniqueFileNameForRename($photosDir . '/' . $category, $newBase, $ext, $currentFile);
if ($candidate !== $currentFile) {
$dst = $photosDir . '/' . $category . '/' . $candidate;
if (@rename($src, $dst)) {
$oldThumb = $thumbsDir . '/' . $category . '/' . pathinfo($currentFile, PATHINFO_FILENAME) . '.jpg';
$newThumb = $thumbsDir . '/' . $category . '/' . pathinfo($candidate, PATHINFO_FILENAME) . '.jpg';
if (is_file($oldThumb)) {
@rename($oldThumb, $newThumb);
}
if (isset($sortData['photos'][$category][$currentFile])) {
$sortData['photos'][$category][$candidate] = $sortData['photos'][$category][$currentFile];
unset($sortData['photos'][$category][$currentFile]);
}
$finalName = $candidate;
}
}
}
$sortData['photos'][$category][$finalName] = $sortIndex;
$message = 'Фото обновлено.';
}
}
if ($action === 'photo_delete') {
$category = sanitizeCategoryName((string)($_POST['category'] ?? ''));
$file = basename((string)($_POST['photo_current'] ?? ''));
$src = $photosDir . '/' . $category . '/' . $file;
if ($category === '' || $file === '' || !is_file($src)) {
$errors[] = 'Фото не найдено.';
} else {
@unlink($src);
$thumb = $thumbsDir . '/' . $category . '/' . pathinfo($file, PATHINFO_FILENAME) . '.jpg';
if (is_file($thumb)) {
@unlink($thumb);
}
unset($sortData['photos'][$category][$file]);
$message = 'Фото удалено.';
}
}
saveSortData($sortFile, $sortData);
}
$categories = listCategories($photosDir, $sortData);
$selectedCategory = sanitizeCategoryName((string)($_GET['edit_category'] ?? ($_POST['category'] ?? '')));
$photos = $selectedCategory !== '' ? listPhotos($photosDir, $thumbsDir, $selectedCategory, $sortData) : [];
?>
Админка галереи
Админка галереи
← В галерею
= h($message) ?>
= h($e) ?>
Категории (редактирование / сортировка / удаление)
Фото в категории: = h($selectedCategory ?: '—') ?>
Только JPG/PNG/WEBP/GIF, максимум 3 МБ на файл.
Сначала выбери категорию в блоке выше (клик по её названию).
В категории пока нет фото.
[],'photos'=>[]]; $d=json_decode((string)file_get_contents($file),true); return is_array($d)?['categories'=>(array)($d['categories']??[]),'photos'=>(array)($d['photos']??[])]:['categories'=>[],'photos'=>[]]; }
function saveSortData(string $file, array $data): void { file_put_contents($file, json_encode($data, JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT)); }
function nextSortIndex(array $map): int { return $map===[]?10:((int)max(array_map('intval',$map))+10); }
function listCategories(string $photosDir, array $sortData): array { $out=[]; foreach((@scandir($photosDir)?:[]) as $x){ if($x==='.'||$x==='..')continue; if(is_dir($photosDir.'/'.$x))$out[]=$x; } usort($out, fn($a,$b)=>((int)($sortData['categories'][$a]??1000)<=> (int)($sortData['categories'][$b]??1000)) ?: strnatcasecmp($a,$b)); return $out; }
function listPhotos(string $photosDir, string $thumbsDir, string $category, array $sortData): array { $out=[]; $dir=$photosDir.'/'.$category; foreach((@scandir($dir)?:[]) as $f){ if($f==='.'||$f==='..')continue; $p=$dir.'/'.$f; if(!is_file($p)||!isImageExt($f)) continue; $thumbAbs=$thumbsDir.'/'.$category.'/'.pathinfo($f, PATHINFO_FILENAME).'.jpg'; $thumb=is_file($thumbAbs)?('thumbs/'.rawurlencode($category).'/'.rawurlencode(pathinfo($f, PATHINFO_FILENAME).'.jpg')):''; $out[]=['file'=>$f,'sort'=>(int)($sortData['photos'][$category][$f]??1000),'thumb'=>$thumb]; } usort($out, fn($a,$b)=>($a['sort']<=>$b['sort']) ?: strnatcasecmp($a['file'],$b['file'])); return $out; }
function reconcileSortData(string $photosDir, array $sortData): array {
$clean=['categories'=>[],'photos'=>[]];
$cats=[];
foreach((@scandir($photosDir)?:[]) as $c){
if($c==='.'||$c==='..') continue;
if(!is_dir($photosDir.'/'.$c)) continue;
$cats[]=$c;
}
foreach($cats as $c){
$clean['categories'][$c]=(int)($sortData['categories'][$c] ?? 1000);
$clean['photos'][$c]=[];
foreach((@scandir($photosDir.'/'.$c)?:[]) as $f){
if($f==='.'||$f==='..') continue;
if(!is_file($photosDir.'/'.$c.'/'.$f) || !isImageExt($f)) continue;
$clean['photos'][$c][$f]=(int)($sortData['photos'][$c][$f] ?? 1000);
}
}
return $clean;
}
function isImageExt(string $file): bool { return in_array(strtolower(pathinfo($file, PATHINFO_EXTENSION)), ['jpg','jpeg','png','webp','gif'], true); }
function rrmdir(string $dir): void { if(!is_dir($dir)) return; $it=scandir($dir)?:[]; foreach($it as $x){ if($x==='.'||$x==='..')continue; $p=$dir.'/'.$x; if(is_dir($p)) rrmdir($p); else @unlink($p);} @rmdir($dir); }
function uniqueFileNameForRename(string $dir,string $base,string $ext,string $current): string{ $n=0; do{ $cand=$n===0?"{$base}.{$ext}":"{$base}_{$n}.{$ext}"; if($cand===$current||!file_exists($dir.'/'.$cand)) return $cand; $n++; }while(true); }
function handleUploads(array $files, string $targetDir, array &$sortData, string $category): array {
$allowedMime=['image/jpeg','image/png','image/webp','image/gif']; $allowedExt=['jpg','jpeg','png','webp','gif']; $ok=0; $errors=[];
$names=$files['name']??[]; $tmp=$files['tmp_name']??[]; $sizes=$files['size']??[]; $errs=$files['error']??[];
if(!is_array($names)){ $names=[$names]; $tmp=[$tmp]; $sizes=[$sizes]; $errs=[$errs]; }
$finfo=finfo_open(FILEINFO_MIME_TYPE);
foreach($names as $i=>$orig){ if((int)($errs[$i]??UPLOAD_ERR_NO_FILE)!==UPLOAD_ERR_OK){$errors[]="{$orig}: ошибка загрузки";continue;}
$size=(int)($sizes[$i]??0); if($size<1||$size>MAX_UPLOAD_BYTES){$errors[]="{$orig}: >3MB";continue;}
$tmpFile=(string)($tmp[$i]??''); if($tmpFile===''||!is_uploaded_file($tmpFile)){ $errors[]="{$orig}: источник"; continue;}
$mime=$finfo?(string)finfo_file($finfo,$tmpFile):''; if(!in_array($mime,$allowedMime,true)){ $errors[]="{$orig}: тип {$mime}"; continue;}
$ext=strtolower(pathinfo((string)$orig, PATHINFO_EXTENSION)); if(!in_array($ext,$allowedExt,true)){ $errors[]="{$orig}: расширение"; continue;}
$base=sanitizeFileBase(pathinfo((string)$orig, PATHINFO_FILENAME)); if($base==='')$base='photo'; $name=uniqueFileNameForRename($targetDir,$base,$ext,'');
if(!move_uploaded_file($tmpFile,$targetDir.'/'.$name)){ $errors[]="{$orig}: не сохранить"; continue; }
$sortData['photos'][$category][$name]=nextSortIndex((array)($sortData['photos'][$category]??[])); $ok++;
}
if($finfo)finfo_close($finfo);
return ['ok'=>$ok,'errors'=>$errors];
}