v1.6.10 — fix suppression article (permissions répertoire) #78
@@ -5,6 +5,16 @@ Format : [Keep a Changelog](https://keepachangelog.com/fr/1.0.0/) — versionnag
|
||||
|
||||
---
|
||||
|
||||
## [1.6.10] - 2026-05-15
|
||||
|
||||
### Corrigé
|
||||
- Suppression d'article échouait silencieusement quand le répertoire UUID appartenait à un autre utilisateur que `www-data` (permissions `2755` → groupe sans écriture) — `removeDir()` échouait sans erreur visible et l'article restait accessible
|
||||
- `ArticleManager::delete()` retourne maintenant `bool` : si le répertoire existe encore après tentative de suppression, les index ne sont pas reconstruits et l'utilisateur est redirigé vers l'article avec un message d'erreur
|
||||
- `removeDir()` : erreurs PHP supprimées silencieusement (`@unlink`, `@rmdir`, `@scandir`) pour éviter les warnings qui cassaient les redirects
|
||||
- `mkArticleDir()` : nouvelle méthode privée qui crée les répertoires d'articles avec `chmod 0775` explicite (contourne le umask), garantissant que `www-data` (groupe) a toujours les droits d'écriture
|
||||
|
||||
---
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
---
|
||||
|
||||
+7
-1
@@ -1114,7 +1114,13 @@ switch ($action) {
|
||||
case 'delete':
|
||||
requireAuth();
|
||||
if ($uuid !== '') {
|
||||
$articles->delete($uuid);
|
||||
if (!$articles->delete($uuid)) {
|
||||
$failedArt = $articles->getByUuid($uuid);
|
||||
$failedSlug = $failedArt['slug'] ?? '';
|
||||
$back = $failedSlug !== '' ? '/post/' . rawurlencode($failedSlug) : '/';
|
||||
header('Location: ' . $back . '?delete_failed=1');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
header('Location: /');
|
||||
exit;
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
1.6.9
|
||||
1.6.10
|
||||
|
||||
+20
-10
@@ -105,8 +105,8 @@ class ArticleManager
|
||||
$publishedAt = $publishedAt !== '' ? $publishedAt : $now;
|
||||
|
||||
$dir = $this->dataDir . '/' . $uuid;
|
||||
mkdir($dir, 0755, true);
|
||||
mkdir($dir . '/files', 0755, true);
|
||||
$this->mkArticleDir($dir);
|
||||
$this->mkArticleDir($dir . '/files');
|
||||
|
||||
$meta = [
|
||||
'uuid' => $uuid,
|
||||
@@ -155,7 +155,7 @@ class ArticleManager
|
||||
if ($contentChanged || $titleChanged) {
|
||||
$revDir = $this->dataDir . '/' . $uuid . '/revisions';
|
||||
if (!is_dir($revDir)) {
|
||||
mkdir($revDir, 0755, true);
|
||||
$this->mkArticleDir($revDir);
|
||||
}
|
||||
$n = count($revisions) + 1;
|
||||
$revFile = sprintf('%s/%04d.md', $revDir, $n);
|
||||
@@ -482,7 +482,7 @@ class ArticleManager
|
||||
$isImage = str_starts_with($mime, 'image/');
|
||||
$filesDir = $this->dataDir . '/' . $uuid . '/files';
|
||||
if (!is_dir($filesDir)) {
|
||||
mkdir($filesDir, 0755, true);
|
||||
$this->mkArticleDir($filesDir);
|
||||
}
|
||||
|
||||
if ($isImage) {
|
||||
@@ -806,10 +806,10 @@ class ArticleManager
|
||||
$this->git?->commit("featured: " . ($meta['title'] ?? $uuid) . " (" . ($featured ? 'on' : 'off') . ")");
|
||||
}
|
||||
|
||||
public function delete(string $uuid): void
|
||||
public function delete(string $uuid): bool
|
||||
{
|
||||
if (!$this->isValidUuid($uuid)) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
$dir = $this->dataDir . '/' . $uuid;
|
||||
$title = null;
|
||||
@@ -823,9 +823,13 @@ class ArticleManager
|
||||
@unlink($this->slugIndexPath());
|
||||
$this->removeDir($dir);
|
||||
}
|
||||
if (is_dir($dir)) {
|
||||
return false;
|
||||
}
|
||||
$this->rebuildSearchIndex();
|
||||
$this->rebuildBacklinksCache();
|
||||
$this->git?->commit("delete: " . ($title ?? $uuid));
|
||||
return true;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ //
|
||||
@@ -1110,7 +1114,7 @@ class ArticleManager
|
||||
}
|
||||
$dir = $this->dataDir . '/' . $uuid . '/files';
|
||||
if (!is_dir($dir)) {
|
||||
mkdir($dir, 0755, true);
|
||||
$this->mkArticleDir($dir);
|
||||
}
|
||||
|
||||
$mime = mime_content_type($uploadedFile['tmp_name']) ?: 'application/octet-stream';
|
||||
@@ -1370,13 +1374,19 @@ class ArticleManager
|
||||
*/
|
||||
private function removeDir(string $dir): void
|
||||
{
|
||||
foreach (scandir($dir) as $entry) {
|
||||
foreach (@scandir($dir) ?: [] as $entry) {
|
||||
if ($entry === '.' || $entry === '..') {
|
||||
continue;
|
||||
}
|
||||
$path = $dir . '/' . $entry;
|
||||
is_dir($path) ? $this->removeDir($path) : unlink($path);
|
||||
is_dir($path) ? $this->removeDir($path) : @unlink($path);
|
||||
}
|
||||
rmdir($dir);
|
||||
@rmdir($dir);
|
||||
}
|
||||
|
||||
private function mkArticleDir(string $path): void
|
||||
{
|
||||
mkdir($path, 0777, true);
|
||||
chmod($path, 0775);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,6 +173,12 @@ $hasSources = (!empty($externalLinks) || !empty($files))
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<?php if (($_GET['delete_failed'] ?? '') === '1' && function_exists('isAdmin') && isAdmin()): ?>
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
Suppression impossible — droits insuffisants sur le répertoire de données.
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<div class="card-text post-content">
|
||||
<?= $_renderedContent ?>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user