From 9742c273defe5105a5b48ab0bd8c6c1315d6934c Mon Sep 17 00:00:00 2001
From: Wojtek
Date: Thu, 15 Oct 2026 12:22:11 +0200
Subject: [PATCH] Add frontend test scoring
---
README.md | 3 +-
.../sql/updates/mysql/1.7.0.sql | 1 +
com_testquestions.xml | 2 +-
.../language/en-GB/com_testquestions.ini | 7 +++
.../src/Controller/DisplayController.php | 10 ++++
.../src/Model/QuestionsModel.php | 2 +-
.../src/View/Questions/HtmlView.php | 49 +++++++++++++++++++
.../tmpl/questions/default.php | 48 +++++++++++++++---
8 files changed, 113 insertions(+), 9 deletions(-)
create mode 100644 administrator/components/com_testquestions/sql/updates/mysql/1.7.0.sql
diff --git a/README.md b/README.md
index 24264b4..a4a42b2 100644
--- a/README.md
+++ b/README.md
@@ -13,6 +13,7 @@ Szkielet komponentu Joomla 6 do gromadzenia pytań testowych.
- frontendowy widok pytań filtrowany po symbolu arkusza egzaminacyjnego,
- pozycję menu „Arkusz egzaminacyjny” z opcjonalnym symbolem arkusza,
- listę wyboru dostępnych symboli arkuszy na stronie klienckiej,
+- możliwość rozwiązania arkusza i wyświetlenia wyniku oraz odpowiedzi poprawnych i zaznaczonych,
- opcjonalny symbol testu, rok egzaminu i numer pytania z arkusza,
- wskazanie poprawnej odpowiedzi,
- opcjonalne zdjęcie wybierane z Media Managera,
@@ -21,6 +22,6 @@ Szkielet komponentu Joomla 6 do gromadzenia pytań testowych.
## Instalacja
-W Joomla wybierz **System → Install → Install from Folder** i wskaż ten katalog. Manifestem instalacyjnym jest `com_testquestions.xml` (wersja 1.6.3).
+W Joomla wybierz **System → Install → Install from Folder** i wskaż ten katalog. Manifestem instalacyjnym jest `com_testquestions.xml` (wersja 1.7.0).
Po instalacji komponent jest dostępny w panelu administratora jako **Pytania testowe**.
diff --git a/administrator/components/com_testquestions/sql/updates/mysql/1.7.0.sql b/administrator/components/com_testquestions/sql/updates/mysql/1.7.0.sql
new file mode 100644
index 0000000..c16724d
--- /dev/null
+++ b/administrator/components/com_testquestions/sql/updates/mysql/1.7.0.sql
@@ -0,0 +1 @@
+-- Add client-side test completion and result calculation.
diff --git a/com_testquestions.xml b/com_testquestions.xml
index edc3bbe..5f2fca2 100644
--- a/com_testquestions.xml
+++ b/com_testquestions.xml
@@ -4,7 +4,7 @@
2026-10-14
Komponent
GNU General Public License version 2 or later
- 1.6.3
+ 1.7.0
COM_TESTQUESTIONS_DESCRIPTION
com_testquestions
Komponent\Component\Testquestions
diff --git a/components/com_testquestions/language/en-GB/com_testquestions.ini b/components/com_testquestions/language/en-GB/com_testquestions.ini
index aa75e78..5e582de 100644
--- a/components/com_testquestions/language/en-GB/com_testquestions.ini
+++ b/components/com_testquestions/language/en-GB/com_testquestions.ini
@@ -6,6 +6,13 @@ COM_TESTQUESTIONS_NO_TEST_QUESTIONS="Nie znaleziono pytań dla podanego arkusza.
COM_TESTQUESTIONS_TEST_SHEET="Arkusz"
COM_TESTQUESTIONS_TIME_LIMIT="Czas"
COM_TESTQUESTIONS_POINTS="Punkty"
+COM_TESTQUESTIONS_FINISH_TEST="Zakończ test"
+COM_TESTQUESTIONS_RESULT="Wynik"
+COM_TESTQUESTIONS_ANSWER_CORRECT="Poprawna odpowiedź"
+COM_TESTQUESTIONS_ANSWER_INCORRECT="Niepoprawna odpowiedź"
+COM_TESTQUESTIONS_CORRECT_ANSWER="Poprawna odpowiedź"
+COM_TESTQUESTIONS_CORRECT_PATTERN="Wzorzec poprawnej odpowiedzi"
+COM_TESTQUESTIONS_SELECTED_ANSWER="Zaznaczona odpowiedź"
COM_TESTQUESTIONS_MENU_ITEM_TITLE="Arkusz egzaminacyjny"
COM_TESTQUESTIONS_MENU_ITEM_DESCRIPTION="Wyświetla pytania z wybranego arkusza egzaminacyjnego."
COM_TESTQUESTIONS_MENU_TEST_SYMBOL_DESC="Opcjonalnie wybierz symbol arkusza, który ma być wyświetlany po wejściu z menu."
diff --git a/components/com_testquestions/src/Controller/DisplayController.php b/components/com_testquestions/src/Controller/DisplayController.php
index 5800747..7737332 100644
--- a/components/com_testquestions/src/Controller/DisplayController.php
+++ b/components/com_testquestions/src/Controller/DisplayController.php
@@ -5,8 +5,18 @@ namespace Komponent\Component\Testquestions\Site\Controller;
defined('_JEXEC') or die;
use Joomla\CMS\MVC\Controller\BaseController;
+use Joomla\CMS\Session\Session;
class DisplayController extends BaseController
{
protected $default_view = 'questions';
+
+ public function check()
+ {
+ if (!Session::checkToken()) {
+ throw new \RuntimeException('Invalid token', 403);
+ }
+
+ return $this->display();
+ }
}
diff --git a/components/com_testquestions/src/Model/QuestionsModel.php b/components/com_testquestions/src/Model/QuestionsModel.php
index 818d2c0..c3e15cb 100644
--- a/components/com_testquestions/src/Model/QuestionsModel.php
+++ b/components/com_testquestions/src/Model/QuestionsModel.php
@@ -22,7 +22,7 @@ class QuestionsModel extends ListModel
{
$db = Factory::getContainer()->get('DatabaseDriver');
$query = $db->getQuery(true)
- ->select(['question_type', 'question', 'answer_a', 'answer_b', 'answer_c', 'answer_d', 'answer_open', 'correct_answer', 'image', 'time_limit', 'points'])
+ ->select(['id', 'test_symbol', 'question_type', 'question', 'answer_a', 'answer_b', 'answer_c', 'answer_d', 'answer_open', 'correct_answer', 'image', 'time_limit', 'points'])
->from($db->quoteName('#__testquestions_questions'))
->where($db->quoteName('published') . ' = 1')
->order($db->quoteName('ordering') . ' ASC, ' . $db->quoteName('id') . ' ASC');
diff --git a/components/com_testquestions/src/View/Questions/HtmlView.php b/components/com_testquestions/src/View/Questions/HtmlView.php
index fa735db..c6364ee 100644
--- a/components/com_testquestions/src/View/Questions/HtmlView.php
+++ b/components/com_testquestions/src/View/Questions/HtmlView.php
@@ -5,12 +5,14 @@ namespace Komponent\Component\Testquestions\Site\View\Questions;
defined('_JEXEC') or die;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
+use Joomla\CMS\Factory;
class HtmlView extends BaseHtmlView
{
protected $items;
protected $symbol;
protected $symbols;
+ protected $results;
public function formatSheetLabel($sheet)
{
@@ -43,6 +45,53 @@ class HtmlView extends BaseHtmlView
$this->items = $this->get('Items');
$this->symbol = $this->get('State')->get('filter.test_symbol');
$this->symbols = $this->get('TestSymbols');
+ $this->results = $this->buildResults($this->items);
return parent::display($tpl);
}
+
+ protected function buildResults(array $items)
+ {
+ $input = Factory::getApplication()->getInput();
+
+ if ($input->getCmd('task') !== 'check' || $input->getMethod() !== 'POST') {
+ return null;
+ }
+
+ $answers = (array) $input->post->get('answers', [], 'array');
+ $results = [];
+ $correct = 0;
+
+ foreach ($items as $item) {
+ $selected = isset($answers[$item->id]) ? trim((string) $answers[$item->id]) : '';
+ $isCorrect = false;
+
+ if ($item->question_type === 'open') {
+ $patterns = preg_split('/\s*\|\s*/', (string) $item->answer_open, -1, PREG_SPLIT_NO_EMPTY);
+ foreach ($patterns as $pattern) {
+ if (mb_strtolower(trim($pattern), 'UTF-8') === mb_strtolower($selected, 'UTF-8')) {
+ $isCorrect = true;
+ break;
+ }
+ }
+ } else {
+ $isCorrect = (string) $item->correct_answer === $selected;
+ }
+
+ if ($isCorrect) {
+ $correct++;
+ }
+
+ $results[$item->id] = [
+ 'selected' => $selected,
+ 'correct' => $isCorrect,
+ ];
+ }
+
+ return [
+ 'items' => $results,
+ 'correct' => $correct,
+ 'total' => count($items),
+ 'percentage' => count($items) ? round(($correct / count($items)) * 100, 2) : 0,
+ ];
+ }
}
diff --git a/components/com_testquestions/tmpl/questions/default.php b/components/com_testquestions/tmpl/questions/default.php
index f720447..70140db 100644
--- a/components/com_testquestions/tmpl/questions/default.php
+++ b/components/com_testquestions/tmpl/questions/default.php
@@ -23,8 +23,17 @@ use Joomla\CMS\HTML\HTMLHelper;
symbol !== '') : ?>
: symbol, ENT_QUOTES, 'UTF-8'); ?>
+ results) : ?>
+
+ :
+ results['correct']; ?> / results['total']; ?>
+ (results['percentage'], ENT_QUOTES, 'UTF-8'); ?>%)
+
+
+ items) : ?>
+
question_type === 'open') : ?>
-
+
-
- - answer_a; ?>
- - answer_b; ?>
- - answer_c; ?>
- - answer_d; ?>
+
+ $answerField) : ?>
+ results && $this->results['items'][$item->id]['selected'] === (string) ($answerIndex + 1); ?>
+ results && (string) $item->correct_answer === (string) ($answerIndex + 1); ?>
+ -
+
+
+
+ results) : ?>
+
+ results['items'][$item->id]['correct'] ? '✓ ' . \Joomla\CMS\Language\Text::_('COM_TESTQUESTIONS_ANSWER_CORRECT') : '✗ ' . \Joomla\CMS\Language\Text::_('COM_TESTQUESTIONS_ANSWER_INCORRECT'); ?>
+ question_type === 'closed') : ?>
+ — : correct_answer); ?>
+
+ — : answer_open, ENT_QUOTES, 'UTF-8'); ?>
+
+
+
+
+
+
+
+