Skip to content

Commit 93452cd

Browse files
committed
feat(admin-log): added tracking for all content changes (#3833)
1 parent b4ad88a commit 93452cd

File tree

9 files changed

+98
-20
lines changed

9 files changed

+98
-20
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
/**
4+
* The abstract Administration API controller
5+
*
6+
* This Source Code Form is subject to the terms of the Mozilla Public License,
7+
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
8+
* obtain one at https://mozilla.org/MPL/2.0/.
9+
*
10+
* @package phpMyFAQ
11+
* @author Thorsten Rinne <[email protected]>
12+
* @copyright 2026 phpMyFAQ Team
13+
* @license https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
14+
* @link https://www.phpmyfaq.de
15+
* @since 2026-01-06
16+
*/
17+
18+
namespace phpMyFAQ\Controller\Administration\Api;
19+
20+
use phpMyFAQ\Administration\AdminLog;
21+
use phpMyFAQ\Controller\AbstractController;
22+
23+
class AbstractAdministrationApiController extends AbstractController
24+
{
25+
protected ?AdminLog $adminLog = null;
26+
27+
public function __construct()
28+
{
29+
parent::__construct();
30+
31+
$this->adminLog = $this->container->get(id: 'phpmyfaq.admin.admin-log');
32+
}
33+
}

phpmyfaq/src/phpMyFAQ/Controller/Administration/Api/AttachmentController.php

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
use phpMyFAQ\Attachment\AttachmentException;
2323
use phpMyFAQ\Attachment\AttachmentFactory;
2424
use phpMyFAQ\Attachment\Filesystem\File\FileException;
25-
use phpMyFAQ\Controller\AbstractController;
2625
use phpMyFAQ\Core\Exception;
26+
use phpMyFAQ\Enums\AdminLogType;
2727
use phpMyFAQ\Enums\PermissionType;
2828
use phpMyFAQ\Session\Token;
2929
use phpMyFAQ\Translation;
@@ -33,7 +33,7 @@
3333
use Symfony\Component\HttpFoundation\Response;
3434
use Symfony\Component\Routing\Attribute\Route;
3535

36-
final class AttachmentController extends AbstractController
36+
final class AttachmentController extends AbstractAdministrationApiController
3737
{
3838
/**
3939
* @throws \Exception
@@ -51,6 +51,11 @@ public function delete(Request $request): JsonResponse
5151

5252
$attachment = AttachmentFactory::create($deleteData->attId);
5353
if ($attachment->delete()) {
54+
$this->adminLog->log(
55+
$this->currentUser,
56+
AdminLogType::ATTACHMENT_DELETE->value . ':' . $deleteData->attId,
57+
);
58+
5459
return $this->json(['success' => Translation::get(key: 'msgAttachmentsDeleted')], Response::HTTP_OK);
5560
}
5661

@@ -150,6 +155,15 @@ public function upload(Request $request): JsonResponse
150155
}
151156
}
152157

158+
if (!empty($uploadedFiles)) {
159+
$adminLog = $this->container->get(id: 'phpmyfaq.admin.admin-log');
160+
$attachmentIds = array_column($uploadedFiles, 'attachmentId');
161+
$adminLog->log(
162+
$this->currentUser,
163+
AdminLogType::ATTACHMENT_ADD->value . ':' . implode(',', $attachmentIds),
164+
);
165+
}
166+
153167
return $this->json($uploadedFiles, Response::HTTP_OK);
154168
}
155169
}

phpmyfaq/src/phpMyFAQ/Controller/Administration/Api/CategoryController.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
use phpMyFAQ\Category;
2323
use phpMyFAQ\Category\Permission;
2424
use phpMyFAQ\Category\Relation;
25-
use phpMyFAQ\Controller\AbstractController;
2625
use phpMyFAQ\Core\Exception;
26+
use phpMyFAQ\Enums\AdminLogType;
2727
use phpMyFAQ\Enums\PermissionType;
2828
use phpMyFAQ\Filter;
2929
use phpMyFAQ\Session\Token;
@@ -34,7 +34,7 @@
3434
use Symfony\Component\HttpFoundation\Response;
3535
use Symfony\Component\Routing\Attribute\Route;
3636

37-
final class CategoryController extends AbstractController
37+
final class CategoryController extends AbstractAdministrationApiController
3838
{
3939
/**
4040
* @throws Exception
@@ -83,6 +83,8 @@ public function delete(Request $request): JsonResponse
8383
$category->delete((int) $data->categoryId, $data->language)
8484
&& $categoryRelation->delete((int) $data->categoryId, $data->language)
8585
) {
86+
$this->adminLog->log($this->currentUser, AdminLogType::CATEGORY_DELETE->value . ':' . $data->categoryId);
87+
8688
return $this->json(['success' => Translation::get(key: 'ad_categ_deleted')], Response::HTTP_OK);
8789
}
8890

@@ -158,6 +160,8 @@ public function updateOrder(Request $request): JsonResponse
158160
$category->setGroups($currentAdminGroups);
159161
$category->updateParentCategory((int) $data->categoryId, $parentId);
160162

163+
$this->adminLog->log($this->currentUser, AdminLogType::CATEGORY_REORDER->value . ':' . $data->categoryId);
164+
161165
return $this->json(['success' => Translation::get(key: 'ad_categ_save_order')], Response::HTTP_OK);
162166
}
163167
}

phpmyfaq/src/phpMyFAQ/Controller/Administration/Api/CommentController.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
namespace phpMyFAQ\Controller\Administration\Api;
2121

22-
use phpMyFAQ\Controller\AbstractController;
22+
use phpMyFAQ\Enums\AdminLogType;
2323
use phpMyFAQ\Enums\PermissionType;
2424
use phpMyFAQ\Session\Token;
2525
use phpMyFAQ\Translation;
@@ -28,7 +28,7 @@
2828
use Symfony\Component\HttpFoundation\Response;
2929
use Symfony\Component\Routing\Attribute\Route;
3030

31-
final class CommentController extends AbstractController
31+
final class CommentController extends AbstractAdministrationApiController
3232
{
3333
/**
3434
* @throws \Exception
@@ -57,6 +57,13 @@ public function delete(Request $request): JsonResponse
5757
$result = $comments->delete($data->type, $commentId);
5858
}
5959

60+
if ($result) {
61+
$this->adminLog->log(
62+
$this->currentUser,
63+
AdminLogType::COMMENT_DELETE->value . ':' . implode(',', $commentIds),
64+
);
65+
}
66+
6067
return $this->json(['success' => $result], Response::HTTP_OK);
6168
}
6269

phpmyfaq/src/phpMyFAQ/Controller/Administration/Api/ConfigurationTabController.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
namespace phpMyFAQ\Controller\Administration\Api;
2121

2222
use phpMyFAQ\Administration\Helper;
23-
use phpMyFAQ\Controller\AbstractController;
2423
use phpMyFAQ\Core\Exception;
24+
use phpMyFAQ\Enums\AdminLogType;
2525
use phpMyFAQ\Enums\PermissionType;
2626
use phpMyFAQ\Filter;
2727
use phpMyFAQ\Helper\LanguageHelper;
@@ -37,7 +37,7 @@
3737
use Symfony\Component\Routing\Attribute\Route;
3838
use Twig\Error\LoaderError;
3939

40-
final class ConfigurationTabController extends AbstractController
40+
final class ConfigurationTabController extends AbstractAdministrationApiController
4141
{
4242
/**
4343
* @throws TemplateException
@@ -183,7 +183,7 @@ public function save(Request $request): JsonResponse
183183
$newConfigValues[$key] = $value;
184184
}
185185

186-
// Replace main.referenceUrl in FAQs
186+
// Replace "main.referenceUrl" in FAQs
187187
if ($oldConfigurationData['main.referenceURL'] !== $newConfigValues['main.referenceURL']) {
188188
$this->configuration->replaceMainReferenceUrl(
189189
$oldConfigurationData['main.referenceURL'],
@@ -193,6 +193,9 @@ public function save(Request $request): JsonResponse
193193

194194
$this->configuration->update($newConfigValues);
195195

196+
$changedKeys = array_keys(array_diff_assoc($newConfigValues, $oldConfigurationData));
197+
$this->adminLog->log($this->currentUser, AdminLogType::CONFIG_CHANGE->value . ':' . implode(',', $changedKeys));
198+
196199
return $this->json(['success' => Translation::get(key: 'ad_config_saved')], Response::HTTP_OK);
197200
}
198201

phpmyfaq/src/phpMyFAQ/Controller/Administration/Api/FaqController.php

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@
2727
use phpMyFAQ\Category;
2828
use phpMyFAQ\Category\Permission as CategoryPermission;
2929
use phpMyFAQ\Category\Relation;
30-
use phpMyFAQ\Controller\AbstractController;
3130
use phpMyFAQ\Entity\FaqEntity;
3231
use phpMyFAQ\Entity\SeoEntity;
32+
use phpMyFAQ\Enums\AdminLogType;
3333
use phpMyFAQ\Enums\PermissionType;
3434
use phpMyFAQ\Enums\SeoType;
3535
use phpMyFAQ\Faq;
@@ -54,7 +54,7 @@
5454
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
5555
use Symfony\Component\Routing\Attribute\Route;
5656

57-
final class FaqController extends AbstractController
57+
final class FaqController extends AbstractAdministrationApiController
5858
{
5959
/**
6060
* @throws \phpMyFAQ\Core\Exception
@@ -70,7 +70,6 @@ public function create(Request $request): JsonResponse
7070
$faq = $this->container->get(id: 'phpmyfaq.faq');
7171
$tagging = $this->container->get(id: 'phpmyfaq.tags');
7272
$notification = $this->container->get(id: 'phpmyfaq.notification');
73-
$logging = $this->container->get(id: 'phpmyfaq.admin.admin-log');
7473
$changelog = $this->container->get(id: 'phpmyfaq.admin.changelog');
7574
$visits = $this->container->get(id: 'phpmyfaq.visits');
7675
$seo = $this->container->get(id: 'phpmyfaq.seo');
@@ -115,7 +114,7 @@ public function create(Request $request): JsonResponse
115114
// Permissions
116115
$permissions = $faqPermission->createPermissionArray();
117116

118-
$logging->log($this->currentUser, 'admin-save-new-faq');
117+
$this->adminLog->log($this->currentUser, AdminLogType::FAQ_ADD->value);
119118

120119
if ($question === '' && $content === '') {
121120
return $this->json(['error' => Translation::get(key: 'msgNoQuestionAndAnswer')], Response::HTTP_CONFLICT);
@@ -221,7 +220,7 @@ public function create(Request $request): JsonResponse
221220
}
222221
}
223222

224-
// Let the admin and the category owners to be informed by email of this new entry
223+
// Let the admin and the category owners be informed by email of this new entry
225224
try {
226225
$categoryHelper = new CategoryHelper();
227226
$categoryHelper->setCategory($category)->setConfiguration($this->configuration);
@@ -334,9 +333,9 @@ public function update(Request $request): JsonResponse
334333
// Permissions
335334
$permissions = $faqPermission->createPermissionArray();
336335

337-
$logging->log($this->currentUser, 'admin-save-existing-faq ' . $faqId);
336+
$logging->log($this->currentUser, AdminLogType::FAQ_EDIT->value . ':' . $faqId);
338337
if ($active === 'yes') {
339-
$logging->log($this->currentUser, 'admin-publish-existing-faq ' . $faqId);
338+
$logging->log($this->currentUser, AdminLogType::FAQ_PUBLISH->value . ':' . $faqId);
340339
}
341340

342341
if ('yes' === $revision && $this->configuration->get(item: 'records.enableAutoRevisions')) {
@@ -563,6 +562,7 @@ public function activate(Request $request): JsonResponse
563562
}
564563

565564
if ($success) {
565+
$this->adminLog->log($this->currentUser, AdminLogType::FAQ_EDIT->value);
566566
return $this->json(['success' => Translation::get(key: 'ad_entry_savedsuc')], Response::HTTP_OK);
567567
}
568568

@@ -633,8 +633,7 @@ public function delete(Request $request): JsonResponse
633633
], Response::HTTP_UNAUTHORIZED);
634634
}
635635

636-
$adminLog = $this->container->get(id: 'phpmyfaq.admin.admin-log');
637-
$adminLog->log($this->currentUser, 'Deleted FAQ ID ' . $faqId);
636+
$this->adminLog->log($this->currentUser, AdminLogType::FAQ_DELETE->value . ':' . $faqId);
638637

639638
try {
640639
$faq->delete($faqId, $faqLanguage);

phpmyfaq/src/phpMyFAQ/Controller/Administration/Api/NewsController.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
namespace phpMyFAQ\Controller\Administration\Api;
2121

2222
use DateTime;
23-
use phpMyFAQ\Controller\AbstractController;
2423
use phpMyFAQ\Entity\NewsMessage;
24+
use phpMyFAQ\Enums\AdminLogType;
2525
use phpMyFAQ\Enums\PermissionType;
2626
use phpMyFAQ\Filter;
2727
use phpMyFAQ\News;
@@ -32,7 +32,7 @@
3232
use Symfony\Component\HttpFoundation\Response;
3333
use Symfony\Component\Routing\Attribute\Route;
3434

35-
final class NewsController extends AbstractController
35+
final class NewsController extends AbstractAdministrationApiController
3636
{
3737
/**
3838
* @throws \Exception
@@ -76,6 +76,8 @@ public function create(Request $request): JsonResponse
7676
->setCreated(new DateTime());
7777

7878
if ($news->create($newsMessage)) {
79+
$this->adminLog->log($this->currentUser, AdminLogType::NEWS_ADD->value);
80+
7981
return $this->json(['success' => Translation::get(key: 'ad_news_updatesuc')], Response::HTTP_OK);
8082
}
8183

@@ -101,6 +103,8 @@ public function delete(Request $request): JsonResponse
101103
$deleteId = Filter::filterVar($data->id, FILTER_VALIDATE_INT);
102104

103105
if ($news->delete((int) $deleteId)) {
106+
$this->adminLog->log($this->currentUser, AdminLogType::NEWS_DELETE->value . ':' . $deleteId);
107+
104108
return $this->json(['success' => Translation::get(key: 'ad_news_delsuc')], Response::HTTP_OK);
105109
}
106110

@@ -151,6 +155,8 @@ public function update(Request $request): JsonResponse
151155
->setCreated(new DateTime());
152156

153157
if ($news->update($newsMessage)) {
158+
$this->adminLog->log($this->currentUser, AdminLogType::NEWS_EDIT->value . ':' . $newsId);
159+
154160
return $this->json(['success' => Translation::get(key: 'ad_news_updatesuc')], Response::HTTP_OK);
155161
}
156162

@@ -177,10 +183,14 @@ public function activate(Request $request): JsonResponse
177183

178184
if ($status) {
179185
$news->activate($newsId);
186+
$this->adminLog->log($this->currentUser, AdminLogType::NEWS_EDIT->value . ':' . $newsId);
187+
180188
return $this->json(['success' => Translation::get(key: 'ad_news_updatesuc')], Response::HTTP_OK);
181189
}
182190

183191
$news->deactivate($newsId);
192+
$this->adminLog->log($this->currentUser, AdminLogType::NEWS_EDIT->value . ':' . $newsId);
193+
184194
return $this->json(['success' => Translation::get(key: 'ad_news_updatesuc')], Response::HTTP_OK);
185195
}
186196
}

phpmyfaq/src/phpMyFAQ/Controller/Administration/CategoryController.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
use phpMyFAQ\Database;
2727
use phpMyFAQ\Entity\CategoryEntity;
2828
use phpMyFAQ\Entity\SeoEntity;
29+
use phpMyFAQ\Enums\AdminLogType;
2930
use phpMyFAQ\Enums\PermissionType;
3031
use phpMyFAQ\Enums\SeoType;
3132
use phpMyFAQ\Filter;
@@ -281,6 +282,9 @@ public function create(Request $request): Response
281282
));
282283
$seo->create($seoEntity);
283284

285+
// Admin Log
286+
$this->adminLog->log($this->currentUser, AdminLogType::CATEGORY_ADD->value . ':' . $categoryId);
287+
284288
$templateVars = [
285289
...$templateVars,
286290
'isSuccess' => true,
@@ -721,6 +725,9 @@ public function update(Request $request): Response
721725
$seoService->update($seoEntity);
722726
}
723727

728+
// Admin Log
729+
$this->adminLog->log($this->currentUser, AdminLogType::CATEGORY_EDIT->value . ':' . $categoryId);
730+
724731
$templateVars = [
725732
...$templateVars,
726733
'isSuccess' => true,

phpmyfaq/src/phpMyFAQ/Enums/AdminLogType.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ enum AdminLogType: string
3232
case FAQ_TRANSLATE = 'faq-translate';
3333
case FAQ_ANSWER_ADD = 'faq-answer-add';
3434
case FAQ_DELETE = 'faq-delete';
35+
case FAQ_PUBLISH = 'faq-publish';
3536

3637
// Category operations
3738
case CATEGORY_ADD = 'category-add';

0 commit comments

Comments
 (0)