From 209e2774c9ccbb8689aea8d585d8dadab9dac277 Mon Sep 17 00:00:00 2001 From: Andrew Caya Date: Mon, 16 Jan 2023 11:51:38 -0500 Subject: [PATCH] 2023-01-16 AC: Adds more OOP extensibility to mod_questionnaire. --- classes/question/question.php | 25 +++++++++-- db/install.php | 11 +++++ db/install.xml | 1 + db/upgrade.php | 61 +++++++++++++++++++++++++ lang/en/questionnaire.php | 3 ++ locallib.php | 85 +++++++++++++++++++++++------------ questionnaire.class.php | 9 ++-- questions.php | 8 ++-- version.php | 2 +- 9 files changed, 166 insertions(+), 39 deletions(-) diff --git a/classes/question/question.php b/classes/question/question.php index b05eab7c..01698176 100644 --- a/classes/question/question.php +++ b/classes/question/question.php @@ -181,20 +181,39 @@ public function __construct($id = 0, $question = null, $context = null, $params } /** - * Short name for this question type - no spaces, etc.. + * Short name for this question type - no spaces, etc. * @return string */ abstract public function helpname(); /** - * Build a question from data. + * Build a question from id. * @param int $qtype * @param int|array $qdata * @param \stdClass $context * @return mixed */ public static function question_builder($qtype, $qdata = null, $context = null) { - $qclassname = '\\mod_questionnaire\\question\\'.self::qtypename($qtype); + $qclassname = '\\mod_questionnaire\\question\\' . self::qtypename($qtype); + $qid = 0; + if (!empty($qdata) && is_array($qdata)) { + $qdata = (object)$qdata; + } else if (!empty($qdata) && is_int($qdata)) { + $qid = $qdata; + } + return new $qclassname($qid, $qdata, $context, ['type_id' => $qtype]); + } + + /** + * Build a question from FQCN. + * @param string $qtypefqcn + * @param int $qtype + * @param int|array $qdata + * @param \stdClass $context + * @return mixed + */ + public static function question_builder_fqcn($qtypefqcn, $qtype, $qdata = null, $context = null) { + $qclassname = (string) $qtypefqcn; $qid = 0; if (!empty($qdata) && is_array($qdata)) { $qdata = (object)$qdata; diff --git a/db/install.php b/db/install.php index 2e630363..4b8cea3c 100644 --- a/db/install.php +++ b/db/install.php @@ -35,6 +35,7 @@ function xmldb_questionnaire_install() { $questiontype->type = 'Yes/No'; $questiontype->has_choices = 'n'; $questiontype->response_table = 'response_bool'; + $questiontype->fqcn = '\\mod_questionnaire\\question\\yesno'; $id = $DB->insert_record('questionnaire_question_type', $questiontype); $questiontype = new stdClass(); @@ -42,6 +43,7 @@ function xmldb_questionnaire_install() { $questiontype->type = 'Text Box'; $questiontype->has_choices = 'n'; $questiontype->response_table = 'response_text'; + $questiontype->fqcn = '\\mod_questionnaire\\question\\text'; $id = $DB->insert_record('questionnaire_question_type', $questiontype); $questiontype = new stdClass(); @@ -49,6 +51,7 @@ function xmldb_questionnaire_install() { $questiontype->type = 'Essay Box'; $questiontype->has_choices = 'n'; $questiontype->response_table = 'response_text'; + $questiontype->fqcn = '\\mod_questionnaire\\question\\essay'; $id = $DB->insert_record('questionnaire_question_type', $questiontype); $questiontype = new stdClass(); @@ -56,6 +59,7 @@ function xmldb_questionnaire_install() { $questiontype->type = 'Radio Buttons'; $questiontype->has_choices = 'y'; $questiontype->response_table = 'resp_single'; + $questiontype->fqcn = '\\mod_questionnaire\\question\\radio'; $id = $DB->insert_record('questionnaire_question_type', $questiontype); $questiontype = new stdClass(); @@ -63,6 +67,7 @@ function xmldb_questionnaire_install() { $questiontype->type = 'Check Boxes'; $questiontype->has_choices = 'y'; $questiontype->response_table = 'resp_multiple'; + $questiontype->fqcn = '\\mod_questionnaire\\question\\check'; $id = $DB->insert_record('questionnaire_question_type', $questiontype); $questiontype = new stdClass(); @@ -70,6 +75,7 @@ function xmldb_questionnaire_install() { $questiontype->type = 'Dropdown Box'; $questiontype->has_choices = 'y'; $questiontype->response_table = 'resp_single'; + $questiontype->fqcn = '\\mod_questionnaire\\question\\drop'; $id = $DB->insert_record('questionnaire_question_type', $questiontype); $questiontype = new stdClass(); @@ -77,6 +83,7 @@ function xmldb_questionnaire_install() { $questiontype->type = 'Rate (scale 1..5)'; $questiontype->has_choices = 'y'; $questiontype->response_table = 'response_rank'; + $questiontype->fqcn = '\\mod_questionnaire\\question\\rate'; $id = $DB->insert_record('questionnaire_question_type', $questiontype); $questiontype = new stdClass(); @@ -84,6 +91,7 @@ function xmldb_questionnaire_install() { $questiontype->type = 'Date'; $questiontype->has_choices = 'n'; $questiontype->response_table = 'response_date'; + $questiontype->fqcn = '\\mod_questionnaire\\question\\date'; $id = $DB->insert_record('questionnaire_question_type', $questiontype); $questiontype = new stdClass(); @@ -91,6 +99,7 @@ function xmldb_questionnaire_install() { $questiontype->type = 'Numeric'; $questiontype->has_choices = 'n'; $questiontype->response_table = 'response_text'; + $questiontype->fqcn = '\\mod_questionnaire\\question\\numerical'; $id = $DB->insert_record('questionnaire_question_type', $questiontype); $questiontype = new stdClass(); @@ -98,6 +107,7 @@ function xmldb_questionnaire_install() { $questiontype->type = 'Page Break'; $questiontype->has_choices = 'n'; $questiontype->response_table = ''; + $questiontype->fqcn = '\\mod_questionnaire\\question\\pagebreak'; $id = $DB->insert_record('questionnaire_question_type', $questiontype); $questiontype = new stdClass(); @@ -105,6 +115,7 @@ function xmldb_questionnaire_install() { $questiontype->type = 'Section Text'; $questiontype->has_choices = 'n'; $questiontype->response_table = ''; + $questiontype->fqcn = '\\mod_questionnaire\\question\\sectiontext'; $id = $DB->insert_record('questionnaire_question_type', $questiontype); } diff --git a/db/install.xml b/db/install.xml index a1b7af0b..10984978 100644 --- a/db/install.xml +++ b/db/install.xml @@ -105,6 +105,7 @@ + diff --git a/db/upgrade.php b/db/upgrade.php index daf754d2..51f4988a 100644 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -983,6 +983,67 @@ function xmldb_questionnaire_upgrade($oldversion=0) { upgrade_mod_savepoint(true, 2020062301, 'questionnaire'); } + if ($oldversion < 2022121501) { + // Add new FQCN configuration for question types. + $table = new xmldb_table('questionnaire_question_type'); + $field = new xmldb_field('fqcn', XMLDB_TYPE_CHAR, '256', null, XMLDB_NOTNULL, null, 'fqcn', null); + + // Conditionally launch add field. + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + $qtypes = $DB->get_recordset('questionnaire_question_type', null); + + foreach ($qtypes as $questiontype) { + $questiontype->fqcn = '\\mod_questionnaire\\question\\'; + switch ($questiontype->typeid) { + case 1: + $questiontype->fqcn .= 'yesno'; + break; + case 2: + $questiontype->fqcn .= 'text'; + break; + case 3: + $questiontype->fqcn .= 'essay'; + break; + case 4: + $questiontype->fqcn .= 'radio'; + break; + case 5: + $questiontype->fqcn .= 'check'; + break; + case 6: + $questiontype->fqcn .= 'drop'; + break; + case 8: + $questiontype->fqcn .= 'rate'; + break; + case 9: + $questiontype->fqcn .= 'date'; + break; + case 10: + $questiontype->fqcn .= 'numerical'; + break; + case 99: + $questiontype->fqcn .= 'pagebreak'; + break; + case 100: + $questiontype->fqcn .= 'sectiontext'; + break; + default: + $unknowntype = true; + } + + if (!$unknowntype) { + $DB->update_record('questionnaire_question_type', $questiontype); + } + } + + // Questionnaire savepoint reached. + upgrade_mod_savepoint(true, 2022121501, 'questionnaire'); + } + return $result; } diff --git a/lang/en/questionnaire.php b/lang/en/questionnaire.php index d904e11b..919b7052 100644 --- a/lang/en/questionnaire.php +++ b/lang/en/questionnaire.php @@ -149,6 +149,7 @@ $string['downloadtextformat_link'] = 'mod/questionnaire/report#Download_in_text_format'; $string['downloadtypes'] = 'Report type'; $string['dropdown'] = 'Dropdown Box'; +$string['dropdownbox'] = 'Dropdown Box'; $string['dropdown_help'] = 'There is no real advantage to using the Dropdown Box over using the Radio Buttons except perhaps for longish lists of options, to save screen space.'; $string['dropdown_link'] = 'mod/questionnaire/questions#Dropdown_Box'; @@ -386,6 +387,8 @@ $string['overviewnumrespvw1'] = 'response'; $string['owner'] = 'Owner'; $string['page'] = 'Page'; +$string['pagebreak'] = '----- Page Break -----'; +$string['pagebreak_help'] = '----- Page Break -----'; $string['pageof'] = 'Page {$a->page} of {$a->totpages}'; $string['parent'] = 'Parent'; $string['participant'] = 'Participant'; diff --git a/locallib.php b/locallib.php index f4f7b932..a0f9372d 100644 --- a/locallib.php +++ b/locallib.php @@ -466,34 +466,62 @@ function questionnaire_get_survey_select($courseid=0, $type='') { * @param int $id * @return lang_string|mixed|string * @throws coding_exception + * @deprecated Please use questionnaire_get_type_name() instead. */ -function questionnaire_get_type ($id) { - switch ($id) { - case 1: - return get_string('yesno', 'questionnaire'); - case 2: - return get_string('textbox', 'questionnaire'); - case 3: - return get_string('essaybox', 'questionnaire'); - case 4: - return get_string('radiobuttons', 'questionnaire'); - case 5: - return get_string('checkboxes', 'questionnaire'); - case 6: - return get_string('dropdown', 'questionnaire'); - case 8: - return get_string('ratescale', 'questionnaire'); - case 9: - return get_string('date', 'questionnaire'); - case 10: - return get_string('numeric', 'questionnaire'); - case 100: - return get_string('sectiontext', 'questionnaire'); - case 99: - return get_string('sectionbreak', 'questionnaire'); - default: - return $id; +function questionnaire_get_type($id) { + return questionnaire_get_type_name($id); +} + +/** + * Return the language string for the specified question type. + * @param int $id + * @return lang_string|mixed|string + * @throws coding_exception + */ +function questionnaire_get_type_name($id) { + global $DB; + + if ($qtypeobject = $DB->get_record('questionnaire_question_type', ['typeid' => $id], '*', MUST_EXIST)) { + $qtype = preg_replace("/[^a-zA-Z]+/", "", strtolower($qtypeobject->type)); + + $qtypeobjectnamespace = explode('\\', questionnaire_get_question_type_object($id)->fqcn)[1]; + + return get_string($qtype, $qtypeobjectnamespace); + } else { + return ''; + } +} + +/** + * Return the language string for all question types. + * @return array + * @throws coding_exception + */ +function questionnaire_get_all_question_types() { + global $DB; + + $qtypes = $DB->get_records('questionnaire_question_type', [], 'typeid', + 'typeid, type, has_choices, response_table'); + + foreach ($qtypes as $qtype) { + $qtypenames[$qtype->typeid] = $qtype->type; } + + return $qtypenames; +} + +/** + * Return an object of the specified question type. + * @param int $id + * @return stdClass + * @throws coding_exception + */ +function questionnaire_get_question_type_object($id) { + global $DB; + + $qtypeobject = $DB->get_record('questionnaire_question_type', ['typeid' => $id], '*', MUST_EXIST); + + return $qtypeobject; } /** @@ -876,7 +904,8 @@ function questionnaire_prep_for_questionform($questionnaire, $qid, $qtype) { } } } else { - $question = \mod_questionnaire\question\question::question_builder($qtype); + $question = questionnaire_get_question_type_object($qtype); + $question = \mod_questionnaire\question\question::question_builder_fqcn($question->fqcn, $qtype); $question->sid = $questionnaire->survey->id; $question->id = $questionnaire->cm->id; $question->type_id = $qtype; @@ -890,7 +919,7 @@ function questionnaire_prep_for_questionform($questionnaire, $qid, $qtype) { } /** - * Get the standard page contructs and check for validity. + * Get the standard page constructs and check for validity. * @param int $id The coursemodule id. * @param int $a The module instance id. * @return array An array with the $cm, $course, and $questionnaire records in that order. diff --git a/questionnaire.class.php b/questionnaire.class.php index ef599ca4..4b75fc3b 100644 --- a/questionnaire.class.php +++ b/questionnaire.class.php @@ -139,9 +139,9 @@ public function add_questions($sid = false) { $sec = 1; $isbreak = false; foreach ($records as $record) { - - $this->questions[$record->id] = \mod_questionnaire\question\question::question_builder($record->type_id, - $record, $this->context); + $qtypeobject = questionnaire_get_question_type_object($record->type_id); + $this->questions[$record->id] = \mod_questionnaire\question\question::question_builder_fqcn($qtypeobject->fqcn, + $record->type_id, $record, $this->context); if ($record->type_id != QUESPAGEBREAK) { $this->questionsbysec[$sec][] = $record->id; @@ -3018,7 +3018,8 @@ protected function get_survey_all_responses($rid = '', $userid = '', $groupid = } foreach ($uniquetypes as $type) { - $question = \mod_questionnaire\question\question::question_builder($type); + $qtypeobject = questionnaire_get_question_type_object($type); + $question = \mod_questionnaire\question\question::question_builder_fqcn($qtypeobject->fqcn, $type); if (!isset($question->responsetype)) { continue; } diff --git a/questions.php b/questions.php index aa3ed3ce..dbc75e4e 100644 --- a/questions.php +++ b/questions.php @@ -120,7 +120,7 @@ // Log question deleted event. $context = context_module::instance($questionnaire->cm->id); - $questiontype = \mod_questionnaire\question\question::qtypename($questionnaire->questions[$qid]->type_id); + $questiontype = questionnaire_get_all_question_types()[$questionnaire->questions[$qid]->type_id]; $params = array( 'context' => $context, 'courseid' => $questionnaire->course->id, @@ -220,7 +220,8 @@ $questionrec->surveyid = $qformdata->sid; $questionrec->type_id = QUESPAGEBREAK; $questionrec->content = 'break'; - $question = \mod_questionnaire\question\question::question_builder(QUESPAGEBREAK); + $qtypeobject = questionnaire_get_question_type_object(QUESPAGEBREAK); + $question = \mod_questionnaire\question\question::question_builder_fqcn($qtypeobject->fqcn, QUESPAGEBREAK); $question->add($questionrec); $reload = true; } else { @@ -296,7 +297,7 @@ // Log question created event. if (isset($qformdata)) { $context = context_module::instance($questionnaire->cm->id); - $questiontype = \mod_questionnaire\question\question::qtypename($qformdata->type_id); + $questiontype = questionnaire_get_all_question_types()[$qformdata->type_id]; $params = array( 'context' => $context, 'courseid' => $questionnaire->course->id, @@ -413,5 +414,6 @@ } else { $questionnaire->page->add_to_page('formarea', $questionsform->render()); } + echo $questionnaire->renderer->render($questionnaire->page); echo $questionnaire->renderer->footer(); diff --git a/version.php b/version.php index d17a22e1..3cda41ef 100644 --- a/version.php +++ b/version.php @@ -25,7 +25,7 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2021062301; // The current module version (Date: YYYYMMDDXX). +$plugin->version = 2022121501; // The current module version (Date: YYYYMMDDXX). $plugin->requires = 2020061500; // Moodle version (3.9). $plugin->component = 'mod_questionnaire';