Skip to content

Commit 7c47914

Browse files
committed
feat(api): refactored private API to update user data
1 parent a059085 commit 7c47914

File tree

17 files changed

+341
-95
lines changed

17 files changed

+341
-95
lines changed

phpmyfaq/.htaccess

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ Header set Access-Control-Allow-Headers "Content-Type, Authorization"
9595
RewriteRule user/(ucp|bookmarks|request-removal|logout) index.php?action=$1 [L,QSA]
9696

9797
# Private APIs
98-
RewriteRule api/(autocomplete|bookmark/([0-9]+)) api/index.php [L,QSA]
98+
RewriteRule api/(autocomplete|bookmark/([0-9]+)|user/data/update) api/index.php [L,QSA]
9999

100100
# Setup APIs
101101
RewriteRule api/setup/(check|backup|update-database) api/index.php [L,QSA]

phpmyfaq/api.service.php

Lines changed: 0 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -844,97 +844,6 @@
844844

845845
break;
846846

847-
//
848-
// Save user data from UCP
849-
//
850-
case 'submit-user-data':
851-
$postData = json_decode(file_get_contents('php://input'), true, 512, JSON_THROW_ON_ERROR);
852-
853-
$csrfToken = Filter::filterVar($postData[Token::PMF_SESSION_NAME], FILTER_SANITIZE_SPECIAL_CHARS);
854-
855-
if (!Token::getInstance()->verifyToken('ucp', $csrfToken)) {
856-
$response->setStatusCode(Response::HTTP_UNAUTHORIZED);
857-
$response->setData(['error' => Translation::get('ad_msg_noauth')]);
858-
break;
859-
}
860-
861-
$userId = Filter::filterVar($postData['userid'], FILTER_VALIDATE_INT);
862-
$userName = trim((string) Filter::filterVar($postData['name'], FILTER_SANITIZE_SPECIAL_CHARS));
863-
$email = Filter::filterVar($postData['email'], FILTER_VALIDATE_EMAIL);
864-
$isVisible = Filter::filterVar($postData['is_visible'], FILTER_SANITIZE_SPECIAL_CHARS);
865-
$password = trim((string) Filter::filterVar($postData['faqpassword'], FILTER_SANITIZE_SPECIAL_CHARS));
866-
$confirm = trim((string) Filter::filterVar($postData['faqpassword_confirm'], FILTER_SANITIZE_SPECIAL_CHARS));
867-
$twoFactorEnabled = Filter::filterVar($postData['twofactor_enabled'] ?? 'off', FILTER_SANITIZE_SPECIAL_CHARS);
868-
$deleteSecret = Filter::filterVar($postData['newsecret'] ?? '', FILTER_SANITIZE_SPECIAL_CHARS);
869-
870-
$user = CurrentUser::getFromSession($faqConfig);
871-
872-
$isAzureAdUser = $user->getUserAuthSource() === 'azure';
873-
874-
$secret = $deleteSecret === 'on' ? '' : $user->getUserData('secret');
875-
876-
if ($userId !== $user->getUserId()) {
877-
$response->setStatusCode(Response::HTTP_BAD_REQUEST);
878-
$response->setData(['error' => 'User ID mismatch!']);
879-
break;
880-
}
881-
882-
if (!$isAzureAdUser) {
883-
if ($password !== $confirm) {
884-
$response->setStatusCode(Response::HTTP_CONFLICT);
885-
$response->setData(['error' => Translation::get('ad_user_error_passwordsDontMatch')]);
886-
break;
887-
}
888-
889-
if (strlen($password) <= 7 || strlen($confirm) <= 7) {
890-
$response->setStatusCode(Response::HTTP_CONFLICT);
891-
$response->setData(['error' => Translation::get('ad_passwd_fail')]);
892-
break;
893-
} else {
894-
$userData = [
895-
'display_name' => $userName,
896-
'email' => $email,
897-
'is_visible' => $isVisible === 'on' ? 1 : 0,
898-
'twofactor_enabled' => $twoFactorEnabled === 'on' ? 1 : 0,
899-
'secret' => $secret
900-
];
901-
902-
$success = $user->setUserData($userData);
903-
904-
foreach ($user->getAuthContainer() as $auth) {
905-
if ($auth->setReadOnly()) {
906-
continue;
907-
}
908-
909-
if (!$auth->update($user->getLogin(), $password)) {
910-
$response->setStatusCode(Response::HTTP_BAD_REQUEST);
911-
$response->setData(['error' => $auth->error()]);
912-
$success = false;
913-
} else {
914-
$success = true;
915-
}
916-
}
917-
}
918-
} else {
919-
$userData = [
920-
'is_visible' => $isVisible === 'on' ? 1 : 0,
921-
'twofactor_enabled' => $twoFactorEnabled === 'on' ? 1 : 0,
922-
'secret' => $secret
923-
];
924-
925-
$success = $user->setUserData($userData);
926-
}
927-
928-
if ($success) {
929-
$response->setStatusCode(Response::HTTP_OK);
930-
$response->setData(['success' => Translation::get('ad_entry_savedsuc')]);
931-
} else {
932-
$response->setStatusCode(Response::HTTP_BAD_REQUEST);
933-
$response->setData(['error' => Translation::get('ad_entry_savedfail')]);
934-
}
935-
936-
break;
937-
938847
//
939848
// Change password
940849
//

phpmyfaq/assets/src/api/user.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* Private User API functionality
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public License,
5+
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
6+
* obtain one at https://mozilla.org/MPL/2.0/.
7+
*
8+
* @package phpMyFAQ
9+
* @author Thorsten Rinne <[email protected]>
10+
* @copyright 2024 phpMyFAQ Team
11+
* @license https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
12+
* @link https://www.phpmyfaq.de
13+
* @since 2024-03-03
14+
*/
15+
16+
import { serialize } from '../utils';
17+
18+
export const updateUserControlPanelData = async (data) => {
19+
try {
20+
const response = await fetch('api/user/data/update', {
21+
method: 'PUT',
22+
cache: 'no-cache',
23+
headers: {
24+
'Content-Type': 'application/json',
25+
},
26+
body: JSON.stringify(serialize(data)),
27+
redirect: 'follow',
28+
referrerPolicy: 'no-referrer',
29+
});
30+
31+
if (response.ok) {
32+
return await response.json();
33+
} else {
34+
return await response.json();
35+
}
36+
} catch (error) {
37+
console.error(error);
38+
}
39+
};

phpmyfaq/assets/src/frontend.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { handleComments, handleSaveComment, handleUserVoting } from './faq';
2121
import { handleAutoComplete, handleQuestion } from './search';
2222
import { calculateReadingTime, handlePasswordStrength, handlePasswordToggle, handleReloadCaptcha } from './utils';
2323
import './utils/tooltip';
24+
import { handleUserControlPanel } from './user';
2425

2526
//
2627
// Reload Captchas
@@ -60,6 +61,11 @@ handleComments();
6061
//
6162
handleBookmarks();
6263

64+
//
65+
// Handle user control panel
66+
//
67+
handleUserControlPanel();
68+
6369
//
6470
// Masonry on startpage
6571
//

phpmyfaq/assets/src/user/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './ucp';

phpmyfaq/assets/src/user/ucp.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* User Control Panel functionality
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public License,
5+
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
6+
* obtain one at https://mozilla.org/MPL/2.0/.
7+
*
8+
* @package phpMyFAQ
9+
* @author Thorsten Rinne <[email protected]>
10+
* @copyright 2024 phpMyFAQ Team
11+
* @license https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
12+
* @link https://www.phpmyfaq.de
13+
* @since 2024-03-03
14+
*/
15+
16+
import { updateUserControlPanelData } from '../api/user';
17+
import { addElement } from '../utils';
18+
19+
export const handleUserControlPanel = () => {
20+
const userControlPanelSubmit = document.getElementById('pmf-submit-user-control-panel');
21+
22+
if (userControlPanelSubmit) {
23+
userControlPanelSubmit.addEventListener('click', async (event) => {
24+
event.preventDefault();
25+
26+
const form = document.querySelector('#pmf-user-control-panel-form');
27+
const loader = document.getElementById('loader');
28+
const formData = new FormData(form);
29+
30+
const response = await updateUserControlPanelData(formData);
31+
32+
if (response.success) {
33+
loader.classList.add('d-none');
34+
const message = document.getElementById('pmf-user-control-panel-response');
35+
message.insertAdjacentElement(
36+
'afterend',
37+
addElement('div', { classList: 'alert alert-success', innerText: response.success })
38+
);
39+
}
40+
41+
if (response.error) {
42+
loader.classList.add('d-none');
43+
const message = document.getElementById('pmf-user-control-panel-response');
44+
message.insertAdjacentElement(
45+
'afterend',
46+
addElement('div', { classList: 'alert alert-danger', innerText: response.error })
47+
);
48+
}
49+
});
50+
}
51+
};

phpmyfaq/assets/themes/default/templates/ucp.html

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ <h5 class="h6 m-0 font-weight-bold text-primary">{{ msgHeaderUserData }}</h5>
2121
<div class="spinner-border text-primary d-none" id="loader" role="status">
2222
<span class="visually-hidden">Loading...</span>
2323
</div>
24-
<div id="faqs"></div>
24+
<div id="pmf-user-control-panel-response"></div>
2525
</div>
2626
</div>
2727

28-
<form id="formValues" action="#" method="post" class="needs-validation" novalidate>
28+
<form id="pmf-user-control-panel-form" action="#" method="post" class="needs-validation" novalidate>
2929
<input type="hidden" name="userid" value="{{ userid }}" />
3030
{{ csrf }}
3131
<input type="hidden" name="lang" id="lang" value="{{ lang }}" />
@@ -180,7 +180,8 @@ <h1 class="modal-title fs-5" id="twofactor_configLabel">{{ msgTwofactorConfigMod
180180

181181
<div class="row">
182182
<div class="col-lg-12 text-end">
183-
<button class="btn btn-primary" type="submit" id="pmf-submit-values" data-pmf-form="submit-user-data">
183+
<button class="btn btn-primary" type="submit" id="pmf-submit-user-control-panel"
184+
data-pmf-form="submit-user-data">
184185
{{ msgSave }}
185186
</button>
186187
</div>

phpmyfaq/src/api-routes.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
use phpMyFAQ\Controller\Api\VersionController;
3434
use phpMyFAQ\Controller\Frontend\AutoCompleteController;
3535
use phpMyFAQ\Controller\Frontend\BookmarkController;
36+
use phpMyFAQ\Controller\Frontend\UserController;
3637
use phpMyFAQ\Controller\Setup\SetupController;
3738
use phpMyFAQ\System;
3839
use Symfony\Component\Routing\Route;
@@ -163,6 +164,10 @@
163164
'api.bookmark',
164165
new Route('bookmark/{bookmarkId}', ['_controller' => [BookmarkController::class, 'delete'], '_methods' => 'DELETE'])
165166
);
167+
$routes->add(
168+
'api.user.update',
169+
new Route('user/data/update', ['_controller' => [UserController::class, 'updateData'], '_methods' => 'PUT'])
170+
);
166171

167172
// Setup REST API
168173
$routes->add(

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
use phpMyFAQ\Attachment\Filesystem\File\FileException;
2323
use phpMyFAQ\Configuration;
2424
use phpMyFAQ\Controller\AbstractController;
25+
use phpMyFAQ\Core\Exception;
2526
use phpMyFAQ\Enums\PermissionType;
2627
use phpMyFAQ\Session\Token;
2728
use phpMyFAQ\Translation;
@@ -32,6 +33,9 @@
3233

3334
class AttachmentController extends AbstractController
3435
{
36+
/**
37+
* @throws Exception
38+
*/
3539
#[Route('./admin/api/content/attachments')]
3640
public function delete(Request $request): JsonResponse
3741
{
@@ -68,6 +72,7 @@ public function delete(Request $request): JsonResponse
6872
/**
6973
* @throws AttachmentException
7074
* @throws FileException
75+
* @throws Exception
7176
*/
7277
#[Route('./admin/api/content/attachments/upload')]
7378
public function upload(Request $request): JsonResponse
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace phpMyFAQ\Controller\Frontend;
4+
5+
use phpMyFAQ\Controller\AbstractController;
6+
use Symfony\Component\HttpFoundation\JsonResponse;
7+
8+
class CommentController extends AbstractController
9+
{
10+
public function create(): JsonResponse
11+
{
12+
return new JsonResponse(['status' => 'ok']);
13+
}
14+
}

0 commit comments

Comments
 (0)