diff --git a/classes/output/mobile.php b/classes/output/mobile.php new file mode 100644 index 0000000..82325c0 --- /dev/null +++ b/classes/output/mobile.php @@ -0,0 +1,55 @@ +. + +/** + * Mobile output class for qtype_aitext + * + * @package qtype_aitext + * @copyright 2018 Marcus Green + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace qtype_aitext\output; + +/** + * Mobile output class for aitext question type + * + * @package qtype_aitext + * @copyright 2018 Marcus Green + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class mobile { + + /** + * Returns the aite question type for the quiz in the mobile app. + * @param array $args + * @return array + */ + public static function mobile_get_aitext($args) { + global $CFG; + $args = (object) $args; + $templatepath = $CFG->dirroot."/question/type/aitext/mobile/qtype_aitext.html"; + return [ + 'templates' => [ + [ + 'id' => 'main', + 'html' => file_get_contents($templatepath) + ] + ], + 'javascript' => file_get_contents($CFG->dirroot . '/question/type/aitext/mobile/mobile.js') + ]; + } +} \ No newline at end of file diff --git a/db/mobile.php b/db/mobile.php new file mode 100644 index 0000000..a7c2903 --- /dev/null +++ b/db/mobile.php @@ -0,0 +1,53 @@ +. + +/** + * Mobile app areas for AI Text + * + * Documentation: {@link https://moodledev.io/general/app/development/plugins-development-guide} + * + * @package qtype_aitext + * @copyright 2024 Marcus Green + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$addons = [ + "qtype_aitext" => [ + "handlers" => [ // Different places where the add-on will display content. + 'aitext' => [ // Handler unique name (can be anything). + 'displaydata' => [ + 'title' => 'AIText question', + 'icon' => '/question/type/aitext/pix/icon.gif', + 'class' => '', + ], + 'delegate' => 'CoreQuestionDelegate', // Delegate (where to display the link to the add-on). + 'method' => 'mobile_get_aitext', + 'offlinefunctions' => [ + 'mobile_get_aitext' => [], // Function in classes/output/mobile.php. + ], // Function needs caching for offline. + 'styles' => [ + 'url' => $CFG->wwwroot.'/question/type/aitext/mobile/qtype_aitext_app.css', + 'version' => '0.1' + ] + ] + ], + 'lang' => [ + ['pluginname', 'qtype_aitext'], // Matching value in lang/en/qtype_gapfill. + ], + ] +]; \ No newline at end of file diff --git a/edit_aitext_form.php b/edit_aitext_form.php index 3e04299..470305f 100755 --- a/edit_aitext_form.php +++ b/edit_aitext_form.php @@ -63,7 +63,7 @@ protected function definition_inner($mform) { $mform->setType('markscheme', PARAM_RAW); $mform->setDefault('markscheme', get_config('qtype_aitext', 'defaultmarksscheme')); $mform->addHelpButton('markscheme', 'markscheme', 'qtype_aitext'); - if (get_config('qtype_aitext', 'markprompt_required')== 1) { + if (get_config('qtype_aitext', 'markprompt_required') == 1) { $mform->addRule('markscheme', get_string('markschememissing', 'qtype_aitext'), 'required'); } $models = explode(",", get_config('tool_aiconnect', 'model')); diff --git a/mobile/mobile.js b/mobile/mobile.js new file mode 100644 index 0000000..8ef5209 --- /dev/null +++ b/mobile/mobile.js @@ -0,0 +1,153 @@ +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . + +/** + * PHP calls this from within + * classes/output/mobile.php + */ +/* eslint-disable no-console */ +/* eslint-env es6 */ +var that = this; +var result = { + componentInit: function() { + + // Check that "this.question" was provided. + if (! this.question) { + return that.CoreQuestionHelperProvider.showComponentError(that.onAbort); + } + + // Create a temporary div to ease extraction of parts of the provided html. + var div = document.createElement('div'); + div.innerHTML = this.question.html; + + // Replace Moodle's correct/incorrect classes, feedback and icons with mobile versions. + that.CoreQuestionHelperProvider.replaceCorrectnessClasses(div); + that.CoreQuestionHelperProvider.replaceFeedbackClasses(div); + that.CoreQuestionHelperProvider.treatCorrectnessIcons(div); + + // Get useful parts of the data provided in the question's html. + var text = div.querySelector('.qtext'); + if (text) { + this.question.text = text.innerHTML; + } + + var textarea = div.querySelector('.answer textarea'); + if (textarea === null) { + // review or check + textarea = div.querySelector('.answer .qtype_aitext_response'); + } + if (textarea) { + textarea.style.borderRadius = '4px'; + textarea.style.padding = '6px 12px'; + if (textarea.matches('.readonly')) { + textarea.style.border = '2px #b8dce2 solid'; // light blue + textarea.style.backgroundColor = '#e7f3f5'; // lighter blue + } else { + textarea.style.backgroundColor = '#edf6f7'; // lightest blue + } + this.question.textarea = textarea.outerHTML; + } + + var itemcount = div.querySelector('.itemcount'); + if (itemcount) { + + // Replace bootstrap styles with inline styles because + // adding styles to 'mobile/styles_app.css' doesn't seem to be effective :-( + that.replaceBootstrapClasses(itemcount); + + itemcount.querySelectorAll('p').forEach(function(p){ + that.replaceBootstrapClasses(p); + }); + + // Fix background and text color on "wordswarning" span. + var warning = itemcount.querySelector(".warning"); + if (warning) { + that.replaceBootstrapClasses(warning); + } + + this.question.itemcount = itemcount.outerHTML; + } + + /** + * questionRendered + */ + this.questionRendered = function(){ + + var textarea = this.componentContainer.querySelector('textarea'); + var itemcount = this.componentContainer.querySelector('.itemcount'); + if (textarea && itemcount) { + + // Maybe "this.CoreLangProvider" has a method for fetching a string + // but I can't find it, so we use our own method, thus: + var minwordswarning = that.getPluginString("qtype_aitext", "minwordswarning"); + var maxwordswarning = that.getPluginString("qtype_aitext", "maxwordswarning"); + + var countitems = itemcount.querySelector(".countitems"); + var value = countitems.querySelector(".value"); + var warning = countitems.querySelector(".warning"); + + var itemtype = itemcount.dataset.itemtype; + var minitems = parseInt(itemcount.dataset.minitems); + var maxitems = parseInt(itemcount.dataset.maxitems); + + var itemsplit = ''; + switch (itemtype) { + case "chars": itemsplit = ""; break; + case "words": itemsplit = "[\\sā€”ā€“]+"; break; + case "sentences": itemsplit = "[\\.?!]+"; break; + case "paragraphs": itemsplit = "[\\r\\n]+"; break; + } + + if (itemsplit) { + itemsplit = new RegExp(itemsplit); + textarea.addEventListener("keyup", function() { + var text = textarea.value; + var warningtext = ""; + var count = 0; + if (text) { + count = text.split(itemsplit).filter(function(item) { + return (item !== ""); + }).length; + if (minitems && (count < minitems)) { + warningtext = minwordswarning; + } + if (maxitems && (count > maxitems)) { + warningtext = maxwordswarning; + } + } + value.innerText = count; + if (warning) { + warning.innerText = warningtext; + if (warningtext == "") { + warning.style.display = "none"; + } else { + warning.style.display = "inline"; + } + } + }); + } + } + }; + + if (text && textarea) { + return true; + } + + // Oops, the expected elements, text and textarea, were not found !! + return that.CoreQuestionHelperProvider.showComponentError(that.onAbort); + } +}; +/* eslint-disable-next-line */ +result; \ No newline at end of file diff --git a/mobile/qtype_aitext.html b/mobile/qtype_aitext.html new file mode 100644 index 0000000..1e5b44b --- /dev/null +++ b/mobile/qtype_aitext.html @@ -0,0 +1,16 @@ +
+ +

+ +

+
+ + + + + + +
\ No newline at end of file diff --git a/mobile/qtype_aitext_app.css b/mobile/qtype_aitext_app.css new file mode 100644 index 0000000..4c02250 --- /dev/null +++ b/mobile/qtype_aitext_app.css @@ -0,0 +1,9 @@ +.qtype-aitext .countwords { + margin-top: 0.5rem; /* mt-2 */ + margin-bottom: 0; +} +.qtype-aitext .minwords, +.qtype-aitext .minwords { + margin-top: 0; + margin-bottom: 0; +} \ No newline at end of file diff --git a/version.php b/version.php index abea491..f657268 100755 --- a/version.php +++ b/version.php @@ -25,10 +25,7 @@ defined('MOODLE_INTERNAL') || die(); $plugin->component = 'qtype_aitext'; -$plugin->version = 2024051109; +$plugin->version = 2024051110; $plugin->requires = 2020110900; -$plugin->release = '0.01'; +$plugin->release = '0.02'; $plugin->maturity = MATURITY_BETA; -/*$plugin->dependencies = [ - 'local_ai_manager' => ANY_VERSION, -];*/