Skip to content

Commit 6971a73

Browse files
committed
feat(api): refactored private API for voting
1 parent 27a87fe commit 6971a73

File tree

7 files changed

+137
-101
lines changed

7 files changed

+137
-101
lines changed

phpmyfaq/.htaccess

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ Header set Access-Control-Allow-Headers "Content-Type, Authorization"
9898
RewriteRule ^admin/api/(.*) admin/api/index.php [L,QSA]
9999

100100
# Private APIs
101-
RewriteRule ^api/(autocomplete|bookmark/([0-9]+)|user/data/update|user/password/update|user/request-removal|contact) api/index.php [L,QSA]
101+
RewriteRule ^api/(autocomplete|bookmark/([0-9]+)|user/data/update|user/password/update|user/request-removal|contact|voting) api/index.php [L,QSA]
102102

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

phpmyfaq/api.service.php

Lines changed: 0 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -672,62 +672,6 @@
672672

673673
break;
674674

675-
case 'add-voting':
676-
$faq = new Faq($faqConfig);
677-
$rating = new Rating($faqConfig);
678-
679-
$faqId = Filter::filterVar($postData['id'] ?? null, FILTER_VALIDATE_INT, 0);
680-
$vote = Filter::filterVar($postData['value'], FILTER_VALIDATE_INT);
681-
$userIp = Filter::filterVar($request->server->get('REMOTE_ADDR'), FILTER_VALIDATE_IP);
682-
683-
if (isset($vote) && $rating->check($faqId, $userIp) && $vote > 0 && $vote < 6) {
684-
try {
685-
$faqSession->userTracking('save_voting', $faqId);
686-
} catch (Exception) {
687-
// @todo handle the exception
688-
}
689-
690-
$votingData = [
691-
'record_id' => $faqId,
692-
'vote' => $vote,
693-
'user_ip' => $userIp,
694-
];
695-
696-
if ($rating->getNumberOfVotings($faqId) === 0) {
697-
$rating->addVoting($votingData);
698-
} else {
699-
$rating->update($votingData);
700-
}
701-
702-
$response->setStatusCode(Response::HTTP_OK);
703-
$response->setData([
704-
'success' => Translation::get('msgVoteThanks'),
705-
'rating' => $rating->getVotingResult($faqId),
706-
]);
707-
} elseif (!$rating->check($faqId, $userIp)) {
708-
try {
709-
$faqSession->userTracking('error_save_voting', $faqId);
710-
} catch (Exception $exception) {
711-
$response->setStatusCode(Response::HTTP_BAD_REQUEST);
712-
$response->setData(['error' => $exception->getMessage()]);
713-
}
714-
715-
$response->setStatusCode(Response::HTTP_BAD_REQUEST);
716-
$response->setData(['error' => Translation::get('err_VoteTooMuch')]);
717-
} else {
718-
try {
719-
$faqSession->userTracking('error_save_voting', $faqId);
720-
} catch (Exception $exception) {
721-
$response->setStatusCode(Response::HTTP_BAD_REQUEST);
722-
$response->setData(['error' => $exception->getMessage()]);
723-
}
724-
725-
$response->setStatusCode(Response::HTTP_BAD_REQUEST);
726-
$response->setData(['error' => Translation::get('err_noVote')]);
727-
}
728-
729-
break;
730-
731675
// Send mails to friends
732676
case 'sendtofriends':
733677
$postData = json_decode(file_get_contents('php://input'), true, 512, JSON_THROW_ON_ERROR);

phpmyfaq/assets/src/api/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ export * from './contact';
44
export * from './forms';
55
export * from './setup';
66
export * from './user';
7+
export * from './voting';

phpmyfaq/assets/src/api/voting.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/**
2+
* Private Voting 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-09
14+
*/
15+
16+
export const saveVoting = async (votingId, votingLanguage, selectedIndex) => {
17+
try {
18+
const response = await fetch('api/voting', {
19+
method: 'POST',
20+
cache: 'no-cache',
21+
headers: {
22+
'Content-Type': 'application/json',
23+
},
24+
body: JSON.stringify({
25+
id: votingId,
26+
lang: votingLanguage,
27+
value: selectedIndex,
28+
}),
29+
redirect: 'follow',
30+
referrerPolicy: 'no-referrer',
31+
});
32+
33+
if (response.ok) {
34+
return await response.json();
35+
} else {
36+
return await response.json();
37+
}
38+
} catch (error) {
39+
console.error(error);
40+
}
41+
};

phpmyfaq/assets/src/faq/voting.js

Lines changed: 20 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
* @since 2023-03-20
1414
*/
1515
import { addElement } from '../utils';
16+
import { saveVoting } from '../api';
1617

1718
export const handleUserVoting = () => {
1819
const votingForm = document.querySelector('.pmf-voting-form');
@@ -35,7 +36,7 @@ export const handleUserVoting = () => {
3536

3637
votingForm.addEventListener(
3738
'submit',
38-
(event) => {
39+
async (event) => {
3940
event.preventDefault();
4041

4142
const selectedButton = document.activeElement;
@@ -64,47 +65,24 @@ export const handleUserVoting = () => {
6465
// Save to backend
6566
const votingId = document.getElementById('voting-id').value;
6667
const votingLanguage = document.getElementById('voting-language').value;
67-
fetch(`api.service.php?action=add-voting`, {
68-
method: 'POST',
69-
headers: {
70-
Accept: 'application/json, text/plain, */*',
71-
'Content-Type': 'application/json',
72-
},
73-
body: JSON.stringify({
74-
id: votingId,
75-
lang: votingLanguage,
76-
value: selectedIndex,
77-
}),
78-
})
79-
.then(async (response) => {
80-
if (response.ok) {
81-
return response.json();
82-
}
83-
throw new Error('Network response was not ok: ', { cause: { response } });
84-
})
85-
.then((response) => {
86-
if (response.success) {
87-
const message = document.getElementById('pmf-voting-result');
88-
message.insertAdjacentElement(
89-
'afterend',
90-
addElement('div', { classList: 'alert alert-success', innerText: response.success })
91-
);
92-
} else {
93-
const element = document.getElementById('pmf-voting-result');
94-
element.insertAdjacentElement(
95-
'afterend',
96-
addElement('div', { classList: 'alert alert-danger', innerText: response.error })
97-
);
98-
}
99-
})
100-
.catch(async (error) => {
101-
const element = document.getElementById('pmf-voting-result');
102-
const errorMessage = await error.cause.response.json();
103-
element.insertAdjacentElement(
104-
'afterend',
105-
addElement('div', { classList: 'alert alert-danger', innerText: errorMessage.error })
106-
);
107-
});
68+
69+
const response = await saveVoting(votingId, votingLanguage, selectedIndex);
70+
71+
if (response.success) {
72+
const message = document.getElementById('pmf-voting-result');
73+
message.insertAdjacentElement(
74+
'afterend',
75+
addElement('div', { classList: 'alert alert-success', innerText: response.success })
76+
);
77+
}
78+
79+
if (response.error) {
80+
const element = document.getElementById('pmf-voting-result');
81+
element.insertAdjacentElement(
82+
'afterend',
83+
addElement('div', { classList: 'alert alert-danger', innerText: response.error })
84+
);
85+
}
10886
},
10987
false
11088
);

phpmyfaq/src/api-routes.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
use phpMyFAQ\Controller\Frontend\BookmarkController;
3636
use phpMyFAQ\Controller\Frontend\ContactController;
3737
use phpMyFAQ\Controller\Frontend\UserController;
38+
use phpMyFAQ\Controller\Frontend\VotingController;
3839
use phpMyFAQ\Controller\Setup\SetupController;
3940
use phpMyFAQ\System;
4041
use Symfony\Component\Routing\Route;
@@ -184,6 +185,10 @@
184185
'api.user.update',
185186
new Route('user/data/update', ['_controller' => [UserController::class, 'updateData'], '_methods' => 'PUT'])
186187
);
188+
$routes->add(
189+
'api.voting',
190+
new Route('voting', ['_controller' => [VotingController::class, 'create'], '_methods' => 'POST'])
191+
);
187192

188193
// Setup REST API
189194
$routes->add(

phpmyfaq/src/phpMyFAQ/Controller/Frontend/VotingController.php

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,80 @@
22

33
namespace phpMyFAQ\Controller\Frontend;
44

5+
use Exception;
6+
use phpMyFAQ\Configuration;
57
use phpMyFAQ\Controller\AbstractController;
8+
use phpMyFAQ\Filter;
9+
use phpMyFAQ\Rating;
10+
use phpMyFAQ\Session;
11+
use phpMyFAQ\Translation;
12+
use phpMyFAQ\User\CurrentUser;
613
use Symfony\Component\HttpFoundation\JsonResponse;
14+
use Symfony\Component\HttpFoundation\Request;
15+
use Symfony\Component\HttpFoundation\Response;
716

817
class VotingController extends AbstractController
918
{
10-
public function create(): JsonResponse
19+
/**
20+
* @throws Exception
21+
*/
22+
public function create(Request $request): JsonResponse
1123
{
12-
return $this->json(['status' => 'ok']);
24+
$configuration = Configuration::getConfigurationInstance();
25+
$user = CurrentUser::getCurrentUser($configuration);
26+
27+
$rating = new Rating($configuration);
28+
$session = new Session($configuration);
29+
$session->setCurrentUser($user);
30+
31+
$data = json_decode($request->getContent());
32+
33+
$faqId = Filter::filterVar($data->id ?? null, FILTER_VALIDATE_INT, 0);
34+
$vote = Filter::filterVar($data->value, FILTER_VALIDATE_INT);
35+
$userIp = Filter::filterVar($request->server->get('REMOTE_ADDR'), FILTER_VALIDATE_IP);
36+
37+
if (isset($vote) && $rating->check($faqId, $userIp) && $vote > 0 && $vote < 6) {
38+
try {
39+
$session->userTracking('save_voting', $faqId);
40+
} catch (Exception $exception) {
41+
$configuration->getLogger()->error('Error saving voting', ['exception' => $exception]);
42+
}
43+
44+
$votingData = [
45+
'record_id' => $faqId,
46+
'vote' => $vote,
47+
'user_ip' => $userIp,
48+
];
49+
50+
if ($rating->getNumberOfVotings($faqId) === 0) {
51+
$rating->addVoting($votingData);
52+
} else {
53+
$rating->update($votingData);
54+
}
55+
56+
return $this->json(
57+
[
58+
'success' => Translation::get('msgVoteThanks'),
59+
'rating' => $rating->getVotingResult($faqId),
60+
],
61+
Response::HTTP_OK
62+
);
63+
} elseif (!$rating->check($faqId, $userIp)) {
64+
try {
65+
$session->userTracking('error_save_voting', $faqId);
66+
} catch (Exception $exception) {
67+
return $this->json(['error' => $exception->getMessage()], Response::HTTP_BAD_REQUEST);
68+
}
69+
70+
return $this->json(['error' => Translation::get('err_VoteTooMuch')], Response::HTTP_BAD_REQUEST);
71+
} else {
72+
try {
73+
$session->userTracking('error_save_voting', $faqId);
74+
} catch (Exception $exception) {
75+
return $this->json(['error' => $exception->getMessage()], Response::HTTP_BAD_REQUEST);
76+
}
77+
78+
return $this->json(['error' => Translation::get('err_noVote')], Response::HTTP_BAD_REQUEST);
79+
}
1380
}
1481
}

0 commit comments

Comments
 (0)