1. Code
  2. WordPress
  3. Plugin Development

Integrar cuestionarios de opción múltiple en WordPress: Creación del front-end

Scroll to top
This post is part of a series called Integrating Multiple Choice Quizzes in WordPress.
Integrating Multiple Choice Quizzes in WordPress - Creating the Backend

Spanish (Español) translation by Eva Collados Pascual (you can also view the original English article)

Esta es la segunda parte de la serie sobre el desarrollo de un plugin de cuestionario de opción múltiple para WordPress. En la primera parte creamos el backend de nuestro plugin para capturar los datos que necesitábamos almacenar en la base de datos.

En esta parte final, vamos a crear el front-end del plugin, donde los usuarios podrán responder a los cuestionarios y evaluar sus conocimientos.

Para completar nuestro plugin, en esta parte trataremos los siguientes temas:

  • La creación de un shortcode para mostrar un cuestionario
  • La integración de un slider jQuery para mostrar las preguntas y la navegación
  • La completa cumplimentación de un cuestionario y la generación de los resultados
  • La creación de un temporizador para el cuestionario

Para generar cuestionarios aleatorios necesitaremos una cantidad considerable de preguntas en el backend. Espero que ya hayas trabajado en el backend y almacenado suficientes preguntas para los cuestionarios.

Así que empecemos.


La creación de un shortcode para mostrar un cuestionario

En primer lugar, debemos tener una entrada o página que cargue los elementos de visualización del cuestionario. Esto se puede lograr bien mediante el uso de un shortcode o de una plantilla de página. En este plugin, utilizararemos un shortcode de manera que sea independiente del tema.

Los shortcodes nos permiten usar el plugin como un componente independiente, mientras que las plantillas de página son dependientes del tema. Por otro lado, una plantilla de página es mucho más segura que los shortcodes, ya que existe la posibilidad de eliminar por error los shortcodes colocados dentro de las páginas.

Inicialmente, el shortcode debe generar las categorías de cuestionario disponibles para que los usuarios puedan seleccionar una de ellas y así generar el cuestionario. Se añadirá un shortcode en el constructor mediante la función add_shortcode como se indica en el siguiente código.

1
add_shortcode( 'wpq_show_quiz', array( $this, 'wpq_show_quiz' ) );

Ahora, echemos un vistazo a la implementación del shortcode recuperando las categorías de cuestionario disponibles en la base de datos.

1
function wpq_show_quiz( $atts ) {
2
3
	global $post;
4
5
	$html = '<div id="quiz_panel"><form action="" method="POST">';
6
7
	$html .= '<div class="toolbar">';
8
9
	$html .= '<div class="toolbar_item"><select name="quiz_category" id="quiz_category">';
10
11
	// Retrive the quiz categories from database

12
13
	$quiz_categories = get_terms( 'quiz_categories', 'hide_empty=1' );
14
15
	foreach ( $quiz_categories as $quiz_category ) {
16
17
		$html .= '<option value="' . $quiz_category->term_id . '">' . $quiz_category->name . '</option>';
18
19
	}
20
21
	$html .= '</select></div>';
22
23
	$html .= '<input type="hidden" value="select_quiz_cat" name="wpq_action" />';
24
25
	$html .= '<div class="toolbar_item"><input type="submit" value="Select Quiz Category" /></div>';
26
27
	$html .= '</form>';
28
29
	$html .= '<div class="complete toolbar_item" ><input type="button" id="completeQuiz" value="Get Results" /></div>';
30
31
	// Implementation of Form Submission

32
33
	// Displaying the Quiz as unorderd list

34
35
	return $html;
36
37
}

Nuestro shortcode generará el formulario HTML y los controles necesarios que utiliza el cuestionario. Recuperamos la lista de categorías de cuestionario disponibles en un campo desplegable para permitir que el usuario seleccione la categoría que prefiera. Podemos usar la función get_terms con hide_empty=1 para obtener las categorías de cuestionario que tengan al menos una pregunta.

Un campo oculto llamado wpq_action se utiliza para comprobar los valores $_POST después del envío.

Después de insertar el shortcode en una página o entrada, la salida se verá como la siguiente pantalla.

Screenshot-204Screenshot-204Screenshot-204

Ahora el usuario puede seleccionar una categoría de cuestionario y enviar el formulario con el fin de obtener el cuestionario. Así que vamos a gestionar el envío del formulario dentro de un shortcode para obtener la categoría seleccionada y recuperar preguntas aleatorias para los cuestionarios.

El código siguiente contiene la implementación de la recuperación de preguntas de la categoría seleccionada.

1
$questions_str = "";
2
3
if ( isset( $_POST['wpq_action'] ) && 'select_quiz_cat' == $_POST['wpq_action'] ) {
4
5
	$html .= '<div id="timer" style="display: block;"></div>';
6
	$html .= '<div style="clear: both;"></div></div>';
7
8
	$quiz_category_id = $_POST['quiz_category'];
9
	$quiz_num = get_option( 'wpq_num_questions' );
10
	$args = array(
11
		'post_type' => 'wptuts_quiz',
12
		'tax_query' => array(
13
			array(
14
				'taxonomy' => 'quiz_categories',
15
				'field' => 'id',
16
				'terms' => $quiz_category_id
17
			)
18
		),
19
		'orderby' => 'rand',
20
		'post_status' => 'publish',
21
		'posts_per_page' => $quiz_num
22
	);
23
24
	$query = new WP_Query( $args );
25
26
	$quiz_index = 1;
27
	while ( $query->have_posts() ) : $query->the_post();
28
29
		// Generating the HTML for Questions

30
31
	endwhile;
32
	wp_reset_query();
33
34
	// Embedding Slider

35
}
36
else {
37
	$html .= '<div id="timer" style="display: none;"></div>';
38
	$html .= '<div style="clear: both;"></div></div>';
39
}

El código proporcionado debe incluirse en la sección Implementación del envío de formularios del anterior código.

Una vez enviado el formulario, comprobamos si este contiene la acción requerida utilizando el campo oculto que generamos anteriormente. Luego obtenemos la categoría de cuestionario seleccionada de la matriz $_POST.

A continuación, consultamos la base de datos para buscar entradas wptuts_quiz con la categoría de cuestionario seleccionada.

Es importante establecer orderby como rand para generar preguntas aleatorias para los cuestionarios, de lo contrario generaría el mismo conjunto de preguntas cada vez. Además, asegúrate de establecer posts_per_page para indicar el número máximo de preguntas para cualquier cuestionario.

Una vez generados los resultados necesitamos añadir las preguntas a los elementos HTML requeridos que implementaremos en la siguiente sección.


Integración de un slider jQuery para mostrar preguntas y una navegación

Los cuestionarios se pueden generar en forma de una pantalla que contenga todas las preguntas a la vez, o una pantalla que contenga una pregunta cada vez con controles de navegación. Creo que esta última técnica es la favorita entre la mayoría de la gente. Por lo tanto, vamos a mostrar este cuestionario con una sola pregunta y una navegación que nos sirva para pasar a la pregunta anterior y siguiente.

Generar esta funcionalidad desde cero puede ser una tarea que consuma mucho tiempo, además sería como reinventar la rueda. Un slider jQuery será la solución perfecta en esta situación, yo usaré RhinoSlider que es mi favorito, así que toma una copia.

Dentro de la carpeta descargada, verás que hay tres carpetas llamadas img, js y css. Copia las carpetas img y css en nuestro plugin y los archivos dentro de la carpeta js a la carpeta js de nuestro plugin de cuestionario. Ahora podemos empezar a incluir los scripts y estilos necesarios para el slider.

Incluir los scripts del front-end

En la primera parte, creamos los scripts necesarios para el back-end. En esta parte vamos a incluir los scripts necesarios para RhinoSlider, así como quiz.js para la funcionalidad personalizada.

Para aplicaciones más amplias, podemos usar archivos de script independientes para el front-end y el back-end. Yo usaré un único archivo de script para simplificar las cosas.

Ten en cuenta el siguiente código para incluir scripts y los datos de configuración necesarios.

1
function wpq_frontend_scripts() {
2
3
	wp_register_script( 'rhino', plugins_url( 'js/rhinoslider-1.05.min.js', __FILE__ ), array( 'jquery' ) );
4
5
	wp_register_script( 'rhino-mousewheel', plugins_url( 'js/mousewheel.js', __FILE__ ), array( 'jquery' ) );
6
7
	wp_register_script( 'rhino-easing', plugins_url( 'js/easing.js', __FILE__ ), array( 'jquery' ) );
8
9
	wp_register_script( 'quiz', plugins_url( 'js/quiz.js', __FILE__ ), array( 'jquery', 'rhino', 'rhino-mousewheel', 'rhino-easing' ) );
10
11
	wp_enqueue_script( 'quiz' );
12
13
	$quiz_duration = get_option( 'wpq_duration' );
14
15
	$quiz_duration = ( ! empty( $quiz_duration ) ) ? $quiz_duration : 300;
16
17
	$config_array = array(
18
19
		'ajaxURL' => admin_url( 'admin-ajax.php' ),
20
21
		'quizNonce' => wp_create_nonce( 'quiz-nonce' ),
22
23
		'quizDuration' => $quiz_duration,
24
25
		'plugin_url' => $this->plugin_url
26
27
	);
28
29
	wp_localize_script( 'quiz', 'quiz', $config_array );
30
31
}

Aquí tenemos tres archivos JavaScript utilizados para RhinoSlider y el archivo quiz.js para la funcionalidad personalizada. En la parte anterior, ya configuramos la duración del cuestionario. Podemos recuperar la duración usando la función get_option y asignarla a la matriz $config. Además, tenemos que incluir configuraciones comunes en la matriz config.

Por último podemos usar la función wp_localize_script para asignar los datos de configuración en el archivo quiz.js.

Incluir estilos front-end

Del mismo modo, podemos incluir los archivos CSS necesarios para RhinoSlider utilizando el siguiente código.

1
function wpq_frontend_styles() {
2
3
	wp_register_style( 'rhino-base', plugins_url( 'css/rhinoslider-1.05.css', __FILE__ ) );
4
5
	wp_enqueue_style( 'rhino-base' );
6
7
}

Finalmente necesitamos actualizar el constructor del plugin para añadir las acciones necesarias para incluir scripts y estilos como se indica en el siguiente código.

1
add_action( 'wp_enqueue_scripts', array( $this, 'wpq_frontend_scripts' ) );
2
3
add_action( 'wp_enqueue_scripts', array( $this, 'wpq_frontend_styles' ) );

Todo está listo para integrar el slider con las preguntas en el shortcode que creamos anteriormente. Sigamos adelante.

Incrustar el slider en el shortcode

Actualmente tenemos dos comentarios dentro de la función del shortcode, uno menciona "Generación de HTML para preguntas" y el otro "Incrustando el slider". Esas secciones deben actualizarse con el código correspondiente para generar un slider. En primer lugar tenemos que actualizar el bucle while de la siguiente manera.

1
while ( $query->have_posts() ) : $query->the_post();
2
3
	$question_id = get_the_ID();
4
5
	$question = the_title( '', '', FALSE ) . ' ' . get_the_content();
6
7
	$question_answers = json_decode( get_post_meta( $question_id, '_question_answers', true ) );
8
9
	$questions_str .= '<li>';
10
11
	$questions_str .= '<div class="ques_title"><span class="quiz_num">' . $quiz_index . '</span>' . $question . '</div>';
12
13
	$questions_str .= '<div class="ques_answers" data-quiz-id="' . $question_id . '">';
14
15
	$quiestion_index = 1;
16
17
	foreach ( $question_answers as $key => $value ) {
18
19
		if ( '' != $value ) {
20
21
			$questions_str .= $quiestion_index . ' <input type="radio" value="' . $quiestion_index . '" name="ans_' . $question_id . '[]" />' . $value . '<br/>';
22
23
		}
24
25
		$quiestion_index++;
26
27
	}
28
29
	$questions_str .= '</div></li>';
30
31
	$quiz_index++;
32
33
endwhile;

Explicación del código

  • Dentro del bucle, primero obtenemos la pregunta concatenando el título y el campo de contenido para las preguntas.
  • A continuación, recuperamos las respuestas de cada pregunta mediante la función get_post_meta.
  • Dentro del bucle foreach, todas las respuestas se asignarán a los botones de opción con los valores necesarios.
  • Los elementos de lista necesarios se asignarán dentro del bucle, incluidos los atributos de datos HTML, que serán útiles en la siguiente sección.
  • La salida final del bucle while será una variable de cadena que contiene la lista de preguntas y sus respuestas incrustadas en HTML.

A continuación, tenemos que crear los contenedores para el slider en la sección comentada como "Incrustando el slider". El código siguiente contiene el código HTML para crear estos contenedores.

1
$html .= '<ul id="slider">' . $questions_str;
2
3
$html .= '<li id="quiz_result_page"><div class="ques_title">Quiz Results <span id="score"></span></div>';
4
5
$html .= '<div id="quiz_result"></div>';
6
7
$html .= '</li></ul></div>';

Usaremos una lista desordenada llamada slider como contenedor para RhinoSlider. Inicialmente incluimos el conjunto de preguntas y respuestas generadas dentro del bucle usando $questions_str. Contendrá una colección de elementos de lista.

Luego tenemos que crear manualmente otro elemento de lista para mostrar los resultados del cuestionario y la puntuación.

Ahora que están configurados todos los datos y las diapositivas necesarias para el cuestionario. Podemos inicializar RhinoSlider en quiz.js para ver el cuestionario en acción.

1
jQuery(document).ready(function($) {
2
3
	$('#slider').rhinoslider( {
4
5
		controlsMousewheel: false,
6
7
		controlsPlayPause: false,
8
9
		showBullets: 'always',
10
11
		showControls: 'always'
12
13
	} );
14
15
} );

He utilizado algunos estilos CSS personalizados para mejorar el aspecto. Puedes encontrar todo el CSS modificado en la sección wp_quiz del archivo rhinoslider-1.05.css. Ahora deberías tener algo parecido a lo que se muestra en la siguiente imagen.

Screenshot-206Screenshot-206Screenshot-206

Completar el cuestionario y generar sus resultados

Una vez cargado el cuestionario, puedes usar los controles de navegación para moverte entre las preguntas y seleccionar las respuestas. Una vez que se hayan respondido todas las preguntas debes hacer clic en el botón "Get Results" (obtener resultados). Ahora necesitamos crear los resultados del cuestionario usando una solicitud AJAX.

Vamos a implementar el código jQuery para realizar la solicitud AJAX.

1
$("#completeQuiz").click(function() {
2
3
	wpq_quiz_results();
4
5
});
6
7
var wpq_quiz_results = function() {
8
9
	var selected_answers = {};
10
11
	$(".ques_answers").each(function() {
12
13
		var question_id = $(this).attr("data-quiz-id");
14
15
		var selected_answer = $(this).find('input[type=radio]:checked');
16
17
		if ( selected_answer.length != 0 ) {
18
19
			var selected_answer = $(selected_answer).val();
20
21
			selected_answers["qid_"+question_id] = selected_answer;
22
23
		}
24
		else {
25
26
			selected_answers["qid_"+question_id] = '';
27
28
		}
29
30
	} );
31
32
	// AJAX Request
33
34
};

Una vez que se hace clic en el botón "Get Results", llamamos a la función wpq_quiz_results mediante jQuery. Cada pregunta fue añadida al slider con una clase CSS especial llamada ques_answers.

Al recorrer cada elemento con la clase ques_answers, recuperamos el identificador de pregunta mediante el atributo de datos HTML denominado data-quiz-id y el botón de opción seleccionado mediante jQuery.

Finalmente asignamos todas las preguntas y las respuestas seleccionadas a una matriz llamada selected_answers, que será pasada a la solicitud AJAX.

Echa un vistazo al siguiente código para la implementación de la solicitud AJAX.

1
$.post(quiz.ajaxURL, {
2
3
	action: "get_quiz_results",
4
5
	nonce: quiz.quizNonce,
6
7
	data: selected_answers,
8
9
}, function(data) {
10
11
	// AJAX result handling code
12
13
}, "json" );

Primero creamos la solicitud AJAX utilizando los datos de configuración asignados desde la función wpq_frontend_scripts. La lista de respuestas generada en la sección anterior se enviará como parámetro de datos. Antes de gestionar el resultado, tenemos que examinar la implementación del código del lado del servidor en la siguiente sección.

Creación de un controlador AJAX en el lado del servidor

Primero tenemos que actualizar el constructor con las acciones necesarias para usar AJAX tanto para los usuarios que hayan iniciado sesión como para los usuarios estándar, como se muestra en el código siguiente.

1
add_action( 'wp_ajax_nopriv_get_quiz_results', array( $this, 'get_quiz_results' ) );
2
3
add_action( 'wp_ajax_get_quiz_results', array( $this, 'get_quiz_results' ) );

A continuación, podemos pasar a la implementación de la función get_quiz_results como se muestra en el siguiente código.

1
function get_quiz_results() {
2
3
	$score = 0;
4
5
	$question_answers = $_POST["data"];
6
7
	$question_results = array();
8
9
	foreach ( $question_answers as $ques_id => $answer ) {
10
11
		$question_id = trim( str_replace( 'qid_', '', $ques_id ) ) . ',';
12
13
		$correct_answer = get_post_meta( $question_id, '_question_correct_answer', true );
14
15
		if ( $answer == $correct_answer ) {
16
17
			$score++;
18
19
			$question_results["$question_id"] = array( "answer" => $answer, "correct_answer" => $correct_answer, "mark" => "correct" );
20
21
		}
22
		else {
23
24
			$question_results["$question_id"] = array( "answer" => $answer, "correct_answer" => $correct_answer, "mark" => "incorrect" );
25
26
		}
27
28
	}
29
30
	$total_questions = count( $question_answers );
31
32
	$quiz_result_data = array(
33
34
		"total_questions" => $total_questions,
35
36
		"score" => $score,
37
38
		"result" => $question_results
39
40
	);
41
42
	echo json_encode( $quiz_result_data );
43
44
	exit;
45
46
}

Explicación del código

  • Las respuestas seleccionadas de todas las preguntas se recibirán utilizando el parámetro data dentro de la matriz $_POST.
  • A continuación, recuperamos el ID de cada pregunta reemplazando el prefijo qid_.
  • Después obtenemos la respuesta correcta a cada pregunta de la base de datos utilizando la función get_post_meta.
  • A continuación comprobamos si la respuesta proporcionada coincide con la respuesta correcta y creamos $question_results en base al estado del resultado.
  • Al comprobar las respuestas, necesitamos actualizar la puntuación del cuestionario usando la variable $score.
  • Por último, asignamos los resultados y las puntuaciones de la prueba a la matriz $quiz_result_data que será enviada como respuesta.

Hasta ahora, hemos creado la solicitud AJAX y hemos implementado la respuesta del lado del servidor. En la siguiente sección vamos a completar el proceso de generación de resultados del cuestionario controlando la respuesta AJAX.

Gestión AJAX de los datos de respuesta

En la parte de gestión de las respuestas, tenemos bastantes tareas, incluyendo la visualización de los resultados del cuestionario y las puntuaciones. Así que voy a dividir el código en varias secciones para que la explicación quede más clara. Considera el siguiente código que contiene la solicitud AJAX completa.

1
$.post(
2
	quiz.ajaxURL,
3
	{
4
5
		action: 'get_quiz_results',
6
7
		nonce: quiz.quizNonce,
8
9
		data: selected_answers
10
11
	},
12
	function(data) {
13
14
		// Section 1
15
16
		var total_questions = data.total_questions;
17
18
		$('#slider').data('rhinoslider').next($('#rhino-item' + total_questions));
19
20
		$('#score').html( data.score + '/' + total_questions);
21
22
		// Section 2
23
24
		var result_html = '<table>';
25
26
		result_html += '<tr><td>Question</td><td>Answer</td><td>Correct Answer</td><td>Result</td></tr>';
27
28
		var quiz_index = 1;
29
30
		$.each(data.result, function( key, ques ) {
31
32
			result_html += '<tr><td>' + quiz_index + '</td><td>' + ques.answer + '</td><td>' + ques.correct_answer + '</td>';
33
34
			result_html += '<td><img src="' + quiz.plugin_url + 'img/' + ques.mark + '.png" /></td></tr>';
35
36
			quiz_index++;
37
38
		});
39
40
		result_html += '<tr><td>&nbsp;</td><td></td><td></td>';
41
42
		result_html += '<td></td></tr>';
43
44
		// Section 3
45
46
		$('#quiz_result').parent().css('overflow-y','scroll');
47
48
		$('#quiz_result').html(result_html);
49
50
		$('#timer').hide();
51
52
	},
53
	'json'
54
);

Explicación de la Sección 1

En primer lugar, recuperamos el total de las preguntas del cuestionario de la respuesta recibida desde el servidor. A continuación, utilizamos la función next de RhinoSlider para redirigir al usuario a la diapositiva de los resultados. Después, establecemos la puntuación del usuario con el total de preguntas dentro del contenedor #score.

Explicación de la Sección 2

La parte inicial de este código genera la tabla HTML con los encabezados necesarios para mostrar los resultados. A continuación, asignamos las preguntas a la tabla dentro del bucle jQuery each. Hemos utilizado dos imágenes para mostrar el estado de éxito o error a la pregunta.

Explicación de la Sección 3

Inicialmente tenemos que permitir el desplazamiento en la página de resultados, ya que puede ser bastante largo para los cuestionarios que tengan un gran número de preguntas. El atributo CSS overflow-y se utiliza para permitir el desplazamiento. Finalmente establecemos la tabla de resultados del cuestionario en el contenedor #quiz_result y ocultamos el temporizador, que implementaremos en la siguiente sección.

Una vez completado el cuestionario, la pantalla debería tener un aspecto similar a la siguiente imagen.

Screenshot-207Screenshot-207Screenshot-207

Creación de un temporizador para el cuestionario

Por lo general, cualquier examen o cuestionario tiene un periodo de tiempo predefinido en el cual debería ser completado. Así que vamos a utilizar la duración que establecimos en la página de configuración de nuestro plugin para generar el temporizador del cuestionario. Ya hemos configurado el temporizador para que se oculte en la carga inicial de la página, y para que sea visible en el envío del formulario, en el shortcode.

Vamos a centrarnos en el temporizador que cambia dinámicamente mediante el código jQuery tal y como se muestra a continuación.

1
var duration = quiz.quizDuration * 60;
2
3
$(document).ready(function($) {
4
5
	setTimeout("startPuzzleCount()",1000);
6
7
});
8
9
var startPuzzleCount = function() {
10
11
	duration--;
12
13
	$('#timer').html(duration+" Seconds Remaining");
14
15
	if ( duration == '0' ) {
16
17
		$('#timer').html("Time Up");
18
19
		wpq_quiz_results();
20
21
		return;
22
23
	}
24
25
	setTimeout("startPuzzleCount()",1000);
26
27
};

La duración del cuestionario se recupera mediante la matriz de configuración que se pasa mediante la función wp_localize_script. La duración se convierte en segundos multiplicando por 60.

A continuación, creamos una función setTimeout para iniciar el temporizador. Dentro de la función, reducimos la duración en 1 y la asignamos al contenedor #timer. Cuando el tiempo se reduce a cero, llamamos a la función wpq_quiz_results para completar automáticamente el cuestionario y generar los resultados.

Por último, llamamos a la función setTimeout de forma recursiva para actualizar el tiempo restante. Hemos completado la implementación del temporizador y tu cuestionario con él incluído debería parecerse a la siguiente imagen.

Screenshot-205Screenshot-205Screenshot-205

Resumen

A lo largo de esta serie de dos partes, desarrollamos un sencillo y completo plugin de cuestionario con opciones múltiples para WordPress. Puedes ampliar la funcionalidad de este plugin para adaptarlo a los requisitos de tu aplicación. Creo que podrías mejorar el plugin probando lo siguiente:

  • Creando una forma de asignar preguntas a cuestionarios en lugar de generar cuestionarios aleatorios.
  • Guardando los resultados del cuestionario para los usuarios que hayan iniciado sesión.
  • Creando una competición de cuestionarios entre usuarios.

Cuéntame tus sugerencias y cómo vas con el proceso de ampliación del plugin.

Estoy deseando escuchar tus comentarios.