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) : []; ?> Админка галереи

Админка галереи

← В галерею

Создать папку

Категории (редактирование / сортировка / удаление)

КатегорияПорядокНовое имяДействия

Фото в категории:

Только 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]; }