Skip to content

Commit 9422d30

Browse files
committed
feat(editor): added delete button, closes #3765
1 parent 59c96be commit 9422d30

File tree

9 files changed

+108
-24
lines changed

9 files changed

+108
-24
lines changed

phpmyfaq/admin/assets/src/content/faqs.editor.ts

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* @since 2023-04-30
1414
*/
1515

16-
import { create, update } from '../api';
16+
import { create, update, deleteFaq } from '../api';
1717
import { pushErrorNotification, pushNotification, serialize } from '../../../../assets/src/utils';
1818
import { Response } from '../interfaces';
1919

@@ -41,7 +41,7 @@ export const handleSaveFaqData = (): void => {
4141
}
4242

4343
if (response?.success) {
44-
const data = JSON.parse(response.data);
44+
const data = response.data ? JSON.parse(response.data) : {};
4545
const faqId = document.getElementById('faqId') as HTMLInputElement;
4646
const revisionId = document.getElementById('revisionId') as HTMLInputElement;
4747

@@ -50,12 +50,55 @@ export const handleSaveFaqData = (): void => {
5050

5151
pushNotification(response.success);
5252
} else {
53-
pushErrorNotification(response.error);
53+
if (response && response.error) {
54+
pushErrorNotification(response.error);
55+
}
5456
}
5557
});
5658
}
5759
};
5860

61+
export const handleDeleteFaqEditorModal = (): void => {
62+
const deleteButton = document.getElementById('faqEditorDelete') as HTMLButtonElement | null;
63+
const confirmDeleteButton = document.getElementById('pmf-confirm-delete-faq') as HTMLButtonElement | null;
64+
65+
if (!deleteButton || !confirmDeleteButton) {
66+
return;
67+
}
68+
69+
confirmDeleteButton.addEventListener('click', async (event: Event): Promise<void> => {
70+
event.preventDefault();
71+
72+
const faqId = deleteButton.getAttribute('data-faq-id') as string;
73+
const faqLanguage = deleteButton.getAttribute('data-faq-language') as string;
74+
const csrfToken = deleteButton.getAttribute('data-pmf-csrf-token') as string;
75+
76+
if (!faqId || !faqLanguage || !csrfToken) {
77+
pushErrorNotification('Fehlende Parameter zum Löschen der FAQ.');
78+
return;
79+
}
80+
81+
try {
82+
const response = await deleteFaq(faqId, faqLanguage, csrfToken);
83+
84+
if (response?.success) {
85+
pushNotification(response.success);
86+
// Nach kurzer Verzögerung zur FAQ-Übersicht umleiten
87+
window.setTimeout(() => {
88+
window.location.href = './faqs';
89+
}, 1000);
90+
} else if (response?.error) {
91+
pushErrorNotification(response.error);
92+
} else {
93+
pushErrorNotification('Beim Löschen der FAQ ist ein unbekannter Fehler aufgetreten.');
94+
}
95+
} catch (error) {
96+
console.error(error);
97+
pushErrorNotification('Beim Löschen der FAQ ist ein Fehler aufgetreten.');
98+
}
99+
});
100+
};
101+
59102
export const handleUpdateQuestion = (): void => {
60103
const input = document.getElementById('question') as HTMLInputElement | null;
61104
if (input) {

phpmyfaq/admin/assets/src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ import {
6464
handleToggleVisibility,
6565
handleResetCategoryImage,
6666
handleResetButton,
67-
handleDeleteFaqModal,
67+
handleDeleteFaqEditorModal,
6868
} from './content';
6969
import { handleUserList, handleUsers } from './user';
7070
import { handleGroups } from './group';
@@ -111,7 +111,7 @@ document.addEventListener('DOMContentLoaded', async (): Promise<void> => {
111111
handleFileFilter();
112112
handleSaveFaqData();
113113
handleUpdateQuestion();
114-
handleDeleteFaqModal();
114+
handleDeleteFaqEditorModal();
115115
handleResetButton();
116116
await handleFaqOverview();
117117

phpmyfaq/assets/templates/admin/content/faq.editor.twig

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,8 +449,18 @@
449449
<button class="btn btn-info" type="reset">
450450
{{ ad_gen_reset }}
451451
</button>
452+
453+
{% if editExistingFaq %}
454+
<button class="btn btn-danger" type="button" id="faqEditorDelete" data-bs-toggle="modal"
455+
data-bs-target="#deleteFaqModal" data-faq-id="{{ faqData['id'] }}"
456+
data-faq-language="{{ faqData['lang'] }}"
457+
data-pmf-csrf-token="{{ csrfToken }}">
458+
<i class="bi bi-trash" aria-hidden="true"></i> {{ 'msgDelete' | translate }}
459+
</button>
460+
{% endif %}
461+
452462
<button class="btn btn-primary" type="submit" id="faqEditorSubmit">
453-
{{ ad_entry_save }}
463+
<i class="bi bi-save" aria-hidden="true"></i> {{ ad_entry_save }}
454464
</button>
455465
{% endif %}
456466
</div>
@@ -610,6 +620,29 @@
610620
</div>
611621
</div>
612622

623+
<!-- Delete FAQ Modal -->
624+
<div class="modal fade" id="deleteFaqModal" tabindex="-1" role="dialog" aria-labelledby="deleteFaqModalLabel" aria-hidden="true">
625+
<div class="modal-dialog" role="document">
626+
<div class="modal-content">
627+
<div class="modal-header">
628+
<h5 class="modal-title" id="deleteFaqModalLabel">{{ 'msgDelete' | translate }}</h5>
629+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
630+
</div>
631+
<div class="modal-body">
632+
{{ 'msgConfirmDeleteFAQ' | translate }}
633+
</div>
634+
<div class="modal-footer">
635+
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
636+
{{ 'msgCancel' | translate }}
637+
</button>
638+
<button type="button" class="btn btn-danger" id="pmf-confirm-delete-faq">
639+
{{ 'msgDelete' | translate }}
640+
</button>
641+
</div>
642+
</div>
643+
</div>
644+
</div>
645+
613646
{% if isMarkdownEditorEnabled %}
614647
<!-- Markdown Image Modal -->
615648
<div class="modal modal-lg fade " id="pmf-markdown-insert-image-modal" tabindex="-1" role="dialog"

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

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
<?php
22

3-
declare(strict_types=1);
4-
53
/**
64
* The Admin FAQ Controller
75
* This Source Code Form is subject to the terms of the Mozilla Public License,
@@ -16,6 +14,8 @@
1614
* @since 2023-10-28
1715
*/
1816

17+
declare(strict_types=1);
18+
1919
namespace phpMyFAQ\Controller\Administration\Api;
2020

2121
use DateTime;
@@ -82,7 +82,10 @@ public function create(Request $request): JsonResponse
8282

8383
$data = json_decode($request->getContent())->data;
8484

85-
if (!Token::getInstance($this->container->get('session'))->verifyToken('edit-faq', $data->{'pmf-csrf-token'})) {
85+
if (!Token::getInstance($this->container->get('session'))->verifyToken(
86+
'pmf-csrf-token',
87+
$data->{'pmf-csrf-token'},
88+
)) {
8689
return $this->json(['error' => Translation::get(
8790
languageKey: 'msgNoPermission',
8891
)], Response::HTTP_UNAUTHORIZED);
@@ -292,7 +295,10 @@ public function update(Request $request): JsonResponse
292295

293296
$data = json_decode($request->getContent())->data;
294297

295-
if (!Token::getInstance($this->container->get('session'))->verifyToken('edit-faq', $data->{'pmf-csrf-token'})) {
298+
if (!Token::getInstance($this->container->get('session'))->verifyToken(
299+
'pmf-csrf-token',
300+
$data->{'pmf-csrf-token'},
301+
)) {
296302
return $this->json(['error' => Translation::get(
297303
languageKey: 'msgNoPermission',
298304
)], Response::HTTP_UNAUTHORIZED);
@@ -538,7 +544,7 @@ public function activate(Request $request): JsonResponse
538544
$faqLanguage = Filter::filterVar($data->faqLanguage, FILTER_SANITIZE_SPECIAL_CHARS);
539545
$checked = Filter::filterVar($data->checked, FILTER_VALIDATE_BOOLEAN);
540546

541-
if (!Token::getInstance($this->container->get('session'))->verifyToken('faq-overview', $data->csrf)) {
547+
if (!Token::getInstance($this->container->get('session'))->verifyToken('pmf-csrf-token', $data->csrf)) {
542548
return $this->json(['error' => Translation::get(
543549
languageKey: 'msgNoPermission',
544550
)], Response::HTTP_UNAUTHORIZED);
@@ -582,7 +588,7 @@ public function sticky(Request $request): JsonResponse
582588
$faqLanguage = Filter::filterVar($data->faqLanguage, FILTER_SANITIZE_SPECIAL_CHARS);
583589
$checked = Filter::filterVar($data->checked, FILTER_VALIDATE_BOOLEAN);
584590

585-
if (!Token::getInstance($this->container->get('session'))->verifyToken('faq-overview', $data->csrf)) {
591+
if (!Token::getInstance($this->container->get('session'))->verifyToken('pmf-csrf-token', $data->csrf)) {
586592
return $this->json(['error' => Translation::get(
587593
languageKey: 'msgNoPermission',
588594
)], Response::HTTP_UNAUTHORIZED);
@@ -627,10 +633,10 @@ public function delete(Request $request): JsonResponse
627633
$faqId = Filter::filterVar($data->faqId, FILTER_VALIDATE_INT);
628634
$faqLanguage = Filter::filterVar($data->faqLanguage, FILTER_SANITIZE_SPECIAL_CHARS);
629635

630-
if (!Token::getInstance($this->container->get('session'))->verifyToken('faq-overview', $data->csrf)) {
631-
return $this->json(['error' => Translation::get(
632-
languageKey: 'msgNoPermission',
633-
)], Response::HTTP_UNAUTHORIZED);
636+
if (!Token::getInstance($this->container->get('session'))->verifyToken('pmf-csrf-token', $data->csrf)) {
637+
return $this->json([
638+
'error' => 'CSRF Token - ' . Translation::get(languageKey: 'msgNoPermission'),
639+
], Response::HTTP_UNAUTHORIZED);
634640
}
635641

636642
$adminLog = $this->container->get('phpmyfaq.admin.admin-log');
@@ -655,7 +661,7 @@ public function search(Request $request): JsonResponse
655661

656662
$data = json_decode($request->getContent());
657663

658-
if (!Token::getInstance($this->container->get('session'))->verifyToken('edit-faq', $data->csrf)) {
664+
if (!Token::getInstance($this->container->get('session'))->verifyToken('pmf-csrf-token', $data->csrf)) {
659665
return $this->json(['error' => Translation::get(
660666
languageKey: 'msgNoPermission',
661667
)], Response::HTTP_UNAUTHORIZED);

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public function upload(Request $request): JsonResponse
4646
$validFileExtensions = ['gif', 'jpg', 'jpeg', 'png', 'webp', 'svg', 'mov', 'mp4', 'webm'];
4747
$timestamp = time();
4848

49-
if (!Token::getInstance($session)->verifyToken('edit-faq', $request->query->get('csrf'))) {
49+
if (!Token::getInstance($session)->verifyToken('pmf-csrf-token', $request->query->get('csrf'))) {
5050
return $this->json([
5151
'success' => false,
5252
'data' => ['code' => Response::HTTP_UNAUTHORIZED],

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ public function index(Request $request): Response
7575
return $this->render('@admin/content/faq.overview.twig', [
7676
...$this->getHeader($request),
7777
...$this->getFooter(),
78-
'csrfTokenSearch' => Token::getInstance($sessions)->getTokenInput('edit-faq'),
79-
'csrfTokenOverview' => Token::getInstance($sessions)->getTokenString('faq-overview'),
78+
'csrfTokenSearch' => Token::getInstance($sessions)->getTokenInput('pmf-csrf-token'),
79+
'csrfTokenOverview' => Token::getInstance($sessions)->getTokenString('pmf-csrf-token'),
8080
'categories' => $category->getCategoryTree(),
8181
'numberOfRecords' => $categoryRelation->getNumberOfFaqsPerCategory(),
8282
'numberOfComments' => $comments->getNumberOfCommentsByCategory(),
@@ -627,7 +627,7 @@ private function getBaseTemplateVars(): array
627627
);
628628

629629
return [
630-
'csrfToken' => $token->getTokenString('edit-faq'),
630+
'csrfToken' => $token->getTokenString('pmf-csrf-token'),
631631
'csrfTokenDeleteAttachment' => $token->getTokenString('delete-attachment'),
632632
'csrfTokenUploadAttachment' => $token->getTokenString('upload-attachment'),
633633
'isEditorEnabled' => $this->configuration->get(item: 'main.enableWysiwygEditor'),

phpmyfaq/src/phpMyFAQ/Faq.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
<?php
22

3-
declare(strict_types=1);
4-
53
/**
64
* The main FAQ class. Yes, it's very huge.
75
*
@@ -22,6 +20,8 @@
2220
* @since 2005-12-20
2321
*/
2422

23+
declare(strict_types=1);
24+
2525
namespace phpMyFAQ;
2626

2727
use DateTime;
@@ -1117,7 +1117,7 @@ public function getSolutionIdFromId(int $faqId, string $faqLang): int
11171117
$result = $this->configuration->getDb()->query($query);
11181118

11191119
if ($row = $this->configuration->getDb()->fetchObject($result)) {
1120-
return $row->solution_id;
1120+
return (int) $row->solution_id;
11211121
}
11221122

11231123
return $this->getNextSolutionId();

phpmyfaq/translations/language_de.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1518,5 +1518,6 @@
15181518
$PMF_LANG['msgNewestUsers'] = 'Neueste Benutzer';
15191519
$PMF_LANG['msgMemberSince'] = 'Mitglied seit';
15201520
$PMF_LANG['msgNumberRegisteredUsers'] = 'Benutzer';
1521+
$PMF_LANG['msgConfirmDeleteFAQ'] = 'Möchten Sie diese FAQ wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.';
15211522

15221523
return $PMF_LANG;

phpmyfaq/translations/language_en.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1519,5 +1519,6 @@
15191519
$PMF_LANG['msgNewestUsers'] = 'Newest users';
15201520
$PMF_LANG['msgMemberSince'] = 'Member since';
15211521
$PMF_LANG['msgNumberRegisteredUsers'] = 'Registered users';
1522+
$PMF_LANG['msgConfirmDeleteFAQ'] = 'Do you really want to delete this FAQ? This action cannot be undone.';
15221523

15231524
return $PMF_LANG;

0 commit comments

Comments
 (0)