Skip to content
Snippets Groups Projects
Commit 2feaf570 authored by Stephen Mustgrave's avatar Stephen Mustgrave
Browse files

Resolve #3157376 "Countdown js"

parent 250bf066
No related branches found
No related tags found
No related merge requests found
...@@ -4,6 +4,7 @@ djdevin ...@@ -4,6 +4,7 @@ djdevin
durod durod
epub epub
findme findme
hilios
newfield newfield
passfail passfail
perpage perpage
......
...@@ -84,7 +84,21 @@ used as ...@@ -84,7 +84,21 @@ used as
### Modules that extend Quiz ### Modules that extend Quiz
* [Charts](http://drupal.org/project/charts) - used by Quiz stats to render some useful data * [Charts](http://drupal.org/project/charts) - used by Quiz stats to render some useful data
* [jQuery Countdown](http://drupal.org/project/jquery_countdown) - provides jQuery timer for timed quizzes * [jQuery Countdown](http://hilios.github.io/jQuery.countdown) - provides
jQuery timer for timed quizzes. Install manually or by composer.
````
"type": "package",
"package": {
"name": "hilios/jquery-countdown",
"version": "2.2.0",
"type": "drupal-library",
"dist": {
"url": "https://github.com/hilios/jQuery.countdown/archive/refs/tags/2.2.0.zip",
"type": "zip"
}
}
````
and then ``composer require hilios/jquery-countdown``
* [Views Data Export](http://drupal.org/project/views_data_export) - export Quiz results and user answers * [Views Data Export](http://drupal.org/project/views_data_export) - export Quiz results and user answers
* [Webform Quiz Elements](https://www.drupal.org/project/webform_quiz_elements) * [Webform Quiz Elements](https://www.drupal.org/project/webform_quiz_elements)
......
...@@ -8,7 +8,8 @@ autotitle_length: 128 ...@@ -8,7 +8,8 @@ autotitle_length: 128
pager_start: 50 pager_start: 50
pager_siblings: 5 pager_siblings: 5
time_limit_buffer: 5 time_limit_buffer: 5
has_timer: true has_timer: false
timer_format: '%-H h %M min %S sec'
admin_review_options_end: admin_review_options_end:
attempt: true attempt: true
choice: true choice: true
......
...@@ -132,6 +132,9 @@ quiz.settings: ...@@ -132,6 +132,9 @@ quiz.settings:
has_timer: has_timer:
type: boolean type: boolean
label: Enable timer label: Enable timer
timer_format:
type: string
label: Timer format
admin_review_options_end: admin_review_options_end:
type: sequence type: sequence
label: 'After the quiz' label: 'After the quiz'
......
(function ($, Drupal, drupalSettings, once) {
Drupal.behaviors.quizCountdown = {
attach: function (context) {
const countdowns = drupalSettings.jquery_countdown_quiz;
if (countdowns.length > 0 ) {
countdowns.forEach(countdown => {
const dt = new Date();
dt.setSeconds(dt.getSeconds() + countdown.since);
$(once('countdown', '.' + countdown.id, context)).countdown(dt)
.on('update.countdown', function (event) {
$(this).html(event.strftime(countdown.format));
})
.on('finish.countdown', function () {
Drupal.behaviors.quizCountdown.quizFinished();
});
});
}
},
quizFinished: function () {
let skip_button = $('#edit-navigation-skip');
if (skip_button.length > 0) {
skip_button.click();
}
else {
$('#edit-navigation-timer-expired-finish').click();
}
}
};
})(jQuery, Drupal, drupalSettings, once);
...@@ -24,6 +24,38 @@ function quiz_install(): void { ...@@ -24,6 +24,38 @@ function quiz_install(): void {
]); ]);
} }
/**
* Implements hook_requirements().
*/
function quiz_requirements($phase): array {
$requirements = [];
if ($phase === 'install') {
if (!\file_exists(DRUPAL_ROOT . '/libraries/jquery-countdown/dist/jquery.countdown.min.js')) {
$requirements['jquery_countdown'] = [
'title' => t('Quiz: jQuery countdown'),
'value' => t('Missing'),
'description' => t('If you want to use <a href=":link" target="_blank">jQuery countdown</a>, you either need to <ul><li> manually download the <a href=":npm" target="_blank">js library</a>, extract and copy it to :library and rename the js folder to :foldername or</li><li>use <a href=":url" target="_blank">Asset Packagist</a> with your composer project and require npm-asset/jquery-countdown</li></ul>', [
':link' => 'https://www.npmjs.com/package/jquery-countdown',
':github' => 'https://github.com/hilios/jQuery.countdown/releases/latest',
':library' => DRUPAL_ROOT . '/libraries/',
':foldername' => 'jquery-countdown',
':packagist' => 'https://asset-packagist.org/',
]),
'severity' => REQUIREMENT_WARNING,
];
}
else {
$requirements['jquery_countdown'] = [
'title' => t('Quiz: jQuery countdown'),
'value' => t('Installed'),
'severity' => REQUIREMENT_OK,
];
}
}
return $requirements;
}
/** /**
* Add owner field to quiz questions. * Add owner field to quiz questions.
*/ */
......
...@@ -12,3 +12,18 @@ confirm: ...@@ -12,3 +12,18 @@ confirm:
jumper: jumper:
js: js:
js/jumper.js: {} js/jumper.js: {}
countdown:
remote: https://github.com/hilios/jQuery.countdown
version: 2.2.0
js:
/libraries/jquery-countdown/dist/jquery.countdown.min.js: { minified: true }
js/countdown.timer.js: {}
license:
name: MIT
url: https://github.com/hilios/jQuery.countdown/blob/master/LICENSE.md
gpl-compatible: true
dependencies:
- core/jquery
- core/drupal
- core/drupalSettings
- core/once
...@@ -37,3 +37,11 @@ function quiz_post_update_config_schema_update(): void { ...@@ -37,3 +37,11 @@ function quiz_post_update_config_schema_update(): void {
// Just resave quiz_matching.setting config. // Just resave quiz_matching.setting config.
\Drupal::service('config.factory')->getEditable('quiz_matching.settings')->save(); \Drupal::service('config.factory')->getEditable('quiz_matching.settings')->save();
} }
/**
* Add timer format setting.
*/
function quiz_post_update_add_timer_format_setting(): void {
$quiz_settings = \Drupal::service('config.factory')->getEditable('quiz.settings');
$quiz_settings->set('timer_format', '%-H h %M min %S sec')->save();
}
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
namespace Drupal\quiz\Controller; namespace Drupal\quiz\Controller;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Core\Access\AccessResultAllowed; use Drupal\Core\Access\AccessResultAllowed;
use Drupal\Core\Access\AccessResultForbidden; use Drupal\Core\Access\AccessResultForbidden;
use Drupal\Core\Access\AccessResultInterface; use Drupal\Core\Access\AccessResultInterface;
...@@ -52,6 +53,8 @@ class QuizQuestionController extends EntityController { ...@@ -52,6 +53,8 @@ class QuizQuestionController extends EntityController {
* The form builder service. * The form builder service.
* @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
* An immutable configuration object. * An immutable configuration object.
* @param \Drupal\Component\Datetime\TimeInterface $time
* The time service.
*/ */
public function __construct( public function __construct(
EntityTypeManagerInterface $entity_type_manager, EntityTypeManagerInterface $entity_type_manager,
...@@ -64,6 +67,7 @@ class QuizQuestionController extends EntityController { ...@@ -64,6 +67,7 @@ class QuizQuestionController extends EntityController {
protected QuizSessionInterface $quizSession, protected QuizSessionInterface $quizSession,
protected FormBuilderInterface $formBuilder, protected FormBuilderInterface $formBuilder,
protected ConfigFactoryInterface $configFactory, protected ConfigFactoryInterface $configFactory,
protected TimeInterface $time,
) { ) {
parent::__construct($entity_type_manager, $entity_type_bundle_info, $entity_repository, $renderer, $string_translation, $url_generator, $route_match); parent::__construct($entity_type_manager, $entity_type_bundle_info, $entity_repository, $renderer, $string_translation, $url_generator, $route_match);
} }
...@@ -83,6 +87,7 @@ class QuizQuestionController extends EntityController { ...@@ -83,6 +87,7 @@ class QuizQuestionController extends EntityController {
$container->get('quiz.session'), $container->get('quiz.session'),
$container->get('form_builder'), $container->get('form_builder'),
$container->get('config.factory'), $container->get('config.factory'),
$container->get('datetime.time'),
); );
} }
...@@ -240,7 +245,24 @@ class QuizQuestionController extends EntityController { ...@@ -240,7 +245,24 @@ class QuizQuestionController extends EntityController {
} }
} }
// @todo add jquery countdown here. if (\file_exists(DRUPAL_ROOT . '/libraries/jquery-countdown/dist/jquery.countdown.min.js')) {
if ($this->configFactory->get('quiz.settings')->get('has_timer') && $quiz->time_limit->value) {
$settings = [
'since' => $quiz_result->time_start->value + $quiz->time_limit->value - $this->time->getRequestTime(),
'format' => $this->t('Time left: :format', [':format' => $this->configFactory->get('quiz.settings')->get('timer_format')]),
'id' => 'countdown-quiz-' . $quiz->id(),
];
$content['body']['countdown'] = [
'#markup' => '<div class="countdown-quiz-' . $quiz->id() . '"></div>',
];
// Attach library containing js files.
$content['#attached']['library'][] = 'quiz/countdown';
$content['#attached']['drupalSettings']['jquery_countdown_quiz'][] = $settings;
}
}
$form = $this->formBuilder->getForm(QuizQuestionAnsweringForm::class, $question, $this->quizSession->getResult($quiz)); $form = $this->formBuilder->getForm(QuizQuestionAnsweringForm::class, $question, $this->quizSession->getResult($quiz));
$content['body']['question'] = $form; $content['body']['question'] = $form;
......
...@@ -6,8 +6,6 @@ use Drupal\Core\Config\ConfigFactoryInterface; ...@@ -6,8 +6,6 @@ use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\ConfigFormBase; use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Link;
use Drupal\Core\Url;
use Drupal\quiz\Entity\QuizFeedbackType; use Drupal\quiz\Entity\QuizFeedbackType;
use Drupal\quiz\Util\QuizUtil; use Drupal\quiz\Util\QuizUtil;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
...@@ -200,31 +198,36 @@ class QuizAdminForm extends ConfigFormBase { ...@@ -200,31 +198,36 @@ class QuizAdminForm extends ConfigFormBase {
]; ];
} }
$target = [
'attributes' => [
'target' => '_blank',
],
];
$url = Url::fromUri('https://drupal.org/project/jquery_countdown', $target);
$links = [
'@jquery_countdown' => Link::fromTextAndUrl($this->t('JQuery Countdown'), $url)->toString(),
];
$form['quiz_addons'] = [ $form['quiz_addons'] = [
'#type' => 'fieldset', '#type' => 'fieldset',
'#title' => $this->t('Addons configuration'), '#title' => $this->t('Addons configuration'),
'#description' => $this->t('Quiz has built in integration with some other modules. Disabled checkboxes indicates that modules are not enabled.', $links), '#description' => $this->t('Quiz has built in integration with some other modules. Disabled checkboxes indicates that modules are not enabled.'),
'#collapsible' => TRUE, '#collapsible' => TRUE,
'#collapsed' => TRUE, '#collapsed' => TRUE,
]; ];
$jquery_countdown_installed = \file_exists(DRUPAL_ROOT . '/libraries/jquery-countdown/dist/jquery.countdown.min.js');
$jquery_countdown_requirements = '';
$form['quiz_addons']['has_timer'] = [ $form['quiz_addons']['has_timer'] = [
'#type' => 'checkbox', '#type' => 'checkbox',
'#title' => $this->t('Display timer'), '#title' => $this->t('Display timer'),
'#default_value' => $config->get('has_timer', 0), '#default_value' => !$jquery_countdown_installed ? FALSE : $config->get('has_timer'),
'#description' => $this->t("@jquery_countdown is an <strong>optional</strong> module for Quiz. It is used to display a timer when taking a quiz. Without this timer, the user will not know how much time they have left to complete the Quiz.", $links), '#description' => $this->t("jQuery countdown is <strong>optional</strong> for Quiz. It is used to display a timer when taking a quiz. Without this timer, the user will not know how much time they have left to complete the Quiz.") . ' ' . $jquery_countdown_requirements,
'#disabled' => !function_exists('jquery_countdown_add'), '#disabled' => !$jquery_countdown_installed,
];
$form['quiz_addons']['timer_format'] = [
'#type' => 'textfield',
'#title' => t('Timer format'),
'#default_value' => $config->get('timer_format'),
'#description' => t('Timer format'),
'#disabled' => !$jquery_countdown_installed,
'#states' => [
'visible' => [
':input[name="has_timer"]' => ['checked' => TRUE],
],
],
]; ];
$form['quiz_look_feel'] = [ $form['quiz_look_feel'] = [
......
...@@ -162,6 +162,15 @@ class QuizQuestionAnsweringForm extends FormBase { ...@@ -162,6 +162,15 @@ class QuizQuestionAnsweringForm extends FormBase {
'#attributes' => ['style' => 'display: none'], '#attributes' => ['style' => 'display: none'],
]; ];
$form['navigation']['timer_expired_finish'] = [
'#weight' => -9998,
'#type' => 'submit',
'#value' => $this->t('Timer Expired Finish'),
'#submit' => ['::submitFinalize'],
'#limit_validation_errors' => [['timer_expired_finish']],
'#attributes' => ['style' => 'display: none'],
];
$form['navigation']['actions'] = [ $form['navigation']['actions'] = [
'#type' => 'container', '#type' => 'container',
'#weight' => 5, '#weight' => 5,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment