Developers' Forum

Global Extra in Template

Global Extra in Template

by Andreas Siebel -
Number of replies: 11
Hello,

i have developed custom HTML+CSS question types for my courses, which check the code live and then evaluate the result again on the server.

My problem: My Global-Extra field looks like this:

<div id="editors">
<div class="column">
<h3>HTML</h3>

<textarea id="html_area" name="crui_html" class="coderunner-ui-element" style="width:100%;"></textarea>
<div id="ace_editor1" class="html_question">

<script>
var html_area = $('textarea[name="crui_html"]').hide();
var editor1 = ace.edit("ace_editor1");
editor1.setTheme("ace/theme/github");
editor1.getSession().setMode("ace/mode/html");
editor1.getSession().setValue(html_area.val());
editor1.getSession().on('change', function() {
html_area.val(editor1.getSession().getValue());
html_area.change();
});
</script>
</div>
</div>
<div class="column">
<h3>CSS</h3>

<textarea id="css_area" name="crui_css" class="coderunner-ui-element" style="width:100%;"></textarea>
<div id="ace_editor2" class="css_question">

<script>
var css_area = $('textarea[name="crui_css"]').hide();
var editor2 = ace.edit("ace_editor2");
editor2.setTheme("ace/theme/github");
editor2.getSession().setMode("ace/mode/css");
editor2.getSession().setValue(css_area.val());
editor2.getSession().on('change', function() {
css_area.val(editor2.getSession().getValue());
css_area.change();
});
</script>
</div>
</div>
</div>
<iframe id="rendered_html" style="width: 100%; min-height: 400px; border: 1px solid black;"></iframe>


<script>
function render() {
var html_area = new DOMParser().parseFromString($("#html_area").val(), "text/html");;
var style_tag = document.createElement("style");
style_tag.textContent = $("#css_area").val();

$(html_area).find("html").find("head").append(style_tag);
console.info($(html_area).find("html").html());
document.getElementById("rendered_html").srcdoc = $(html_area).find("html").clone().html();
}

function check() {
$(".coderunnerexamples thead tr>:first-child").hide();
$(".coderunnerexamples tbody tr").each(function() {
var testresult;
$(this).find(">:first-child").each(function() {
testresult = new Function("html_area", "rendered_html", $(this).text())($("#html_area").val(), $("#rendered_html").contents().find("html"));
// First to arguments are the variable-names, then in second bracket the variable values
// third parameter is the function body

$(this).hide();
if (testresult == true) {
$(this).parent().children().each(function() {
$(this).css("background-color", "#4c4");
});
} else {
$(this).parent().children().each(function() {
$(this).css("background", "#c44");
});
}
});

});
}
$(document).ready(function() {
render();
check();
});
$("#html_area").on('change keyup paste input', function() {
console.info("change", $(this).val())
render();
check();
});
$("#css_area").on('change keyup paste input', function() {
console.info("change", $(this).val())
render();
check();
});
</script>
</div>

<style>
.que.coderunner .ace_editor.html_question {
height: 400px;
}

.que.coderunner .ace_editor.css_question {
height: 400px;
}

#editors {
display: flex
}

.column {
flex: 50%;
}
</style>

Is there a way to put this somewhere in the template so that I don't have to copy the code into the Global-Extra field every time I want to create a new question?
In reply to Andreas Siebel

Re: Global Extra in Template

by Richard Lobb -

Well, that's certainly an impressive use of the html_ui. I wasn't expecting it to be used as a way of creating new question types. So unfortunately the short answer to your question is no: there's no way at present to get that code into the question type.

Really what you're doing is constructing a new UI. You can add new UIs to CodeRunner fairly easily if you have admin rights on the Moodle server. Any JavaScript file in the directory /question/type/coderunner/amd/src that starts with 'ui_' is assumed to define a user interface, which replaces the simple text-area answer box with alternative html + JavaScript code. The list of available UIs is populated dynamically in the question authoring form, so you can just drop new UIs in at any time and they immediately become active. The API for the UI plugins is defined here.

However, I realise that most users don't have admin access to the Moodle server, so this probably doesn't help you. Moodle defines a sub-plugin architecture that could in principle be used to allow other developers to define custom user interfaces independently of CodeRunner, but unfortunately sub-plugins are not currently permitted with question types.

I will be doing some more CodeRunner development over the next couple of months, so I'll think about whether it's possible to generalise how the html_ui behaves to allow it to be used to provide new UIs at the question type level rather than just on a per-question basis.

One thing that puzzles me about your code: it assumes the ace editor is loaded and available via the global ace variable. So your code doesn't work in my servers. And in fact since ace_ui and html_ui are two different mutually exclusive user interfaces, I wouldn't have expected it to work on any server. How are you resolving that on your server?


In reply to Richard Lobb

Re: Global Extra in Template

by Andreas Siebel -
Hello,

thanks for your answer.

I embedded the ace editor into Moodle-Admin via "design->additional html". There you could embed the ace-editor as an external library even without admin rights on the server. Maybe this would be more elegant, since Coderunner brings the ACE-editor with it? I have not tried this yet.

For example, I have created code here that can be used to execute Python processing code: https://oer-moodle.de/processing.html Since the libraries are loaded via a CDN, you can simply embed them in a Moodle text field (in HTML format) or in a Coderunner-Global-Extra-Field if you want to do coderunner-processing.

An idea for further development: Maybe it is simply possible to load the global-extra field itself from a template, e.g. {{{template.extra}}, which is stored on the template level. This way you could dynamically load the HTML UI without losing backwards compatibility.

In fact, it is not an option for me to write a plugin for a question type. I *have* admin access to my server, but this would lose compatibility with other instances of Moodle. I would like to make as much of what I do available as OER to other teachers.
In reply to Andreas Siebel

Re: Global Extra in Template

by Richard Lobb -

[I've moved this discussion to the Developers' Forum, since I think it's getting too detailed for most question authors.]

That's very interesting, Andreas, and indeed points the way forward.

Firstly, your suggestion of loading the global-extra field from a template parameter does actually work already. If you create a new question type with the html_ui and a template parameters field of the form

    { "global_extra_preload": "<your_global_extra_as_a_json_string>"}

then in the children questions of that type you can check the Twig-all box and set the global extra field to

    {{ global_extra_preload }}

To turn your existing global extra field into a json string, replace all newline characters with '\n' and escape all double quotes with a backslash. The result is one long line, horrible to maintain, but I've tested it and it does work.

Secondly, I'm planning on doing some more CodeRunner development over the next month of two and the top of my to-do list is refactoring how UI plugin parameters are handled. I'm planning on have a json config file for each UI plugin which will specify the allowed UI parameters and their default values. The question editing form will have an extra section for editing those UI parameters (if there are any). So if the html_ui plugin is changed to take its html either from a UI parameter if non-empty or global-extra otherwise, that should provide a much more editable/maintainable solution than the above and without the need for each child question to explicitly use Twig to fill in the global extra field.

Thirdly, I tried to load the Ace editor using the Additional HTML options in the Appearance > Admin form but without success. I tried 

    <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.12/ace.js"></script>

in both the head and the body and I also tried inserting the code directly. I either got require is not a function or a syntax error, neither of which I understand. What exactly did you do?

Your Brython demo is nice. I played a bit with Brython a few years back and tried to interest a group of advanced students in it, but didn't have much success. Your combining of it with the Ace editor re-kindles my interest.

Thanks

Richard


In reply to Richard Lobb

Re: Global Extra in Template

by Andreas Siebel -

I just tested it:

I'm embetting:

<script src="//ajaxorg.github.io/ace-builds/src-min-noconflict/ace.js" type="text/javascript" charset="utf-8"></script>

in the head of my html.

When I replace this version with the cloudfare version, I get the require error, so somewhere there is the problem. The no-require-Version of ace does not conflict with existing libraries. It uses ace.require instead of require.

Unfortunately I also can't get the code to work if I put this version with a script tag directly into the global-extra field instead of the head. (Still trying)

EDIT: It does not work if i put the ace-loading-script in the global-extra field. But it does work if i put it in the question field.

In reply to Andreas Siebel

Re: Global Extra in Template

by Richard Lobb -

Thanks, that now works.

Did you succeed in moving your global-extra code into a new question prototype ?

In reply to Richard Lobb

Re: Global Extra in Template

by Andreas Siebel -
Thank you very much!

It works exactly as you have described. I now have the original file offline and convert it with a Python script into the required JSON format.

This way I can revise the original question offline and only need to change the code in Moodle in one place.

The only thing I haven't been able to do is embed the ace editor without additional code in the head.. If that still works, my html/css question types are fully portable.

Notice: I'm using cheerio in jobe for server-side code-checking. I added the html-question prototype.
In reply to Andreas Siebel

Re: Global Extra in Template

by M. César Rodríguez -
Hi,

In a previous thread (https://coderunner.org.nz/mod/forum/discuss.php?d=312) I asked about the possibility of preloading a set of TWIG macros in all "global-extra", "question text" and "solution" fields. I'm also using the HTML ui and using TWIG to randomize the questions, format the input fields and computing & displaying the solutions. Thus, the TWIG code starts to be large enough (say ~100 lines) to be uncomfortable to manage and copy in all such fields ensuring that all of them always have the very same versions of the TWIG macros.

May this "global extra preload" be used to preload a set of TWIG macros in such fields?

Thanks & regards.
In reply to M. César Rodríguez

Re: Global Extra in Template

by Richard Lobb -

No, I'm afraid the capability of merging prototype template parameters with the current question's template parameters doesn't work for macros. The two sets of template parameters are separately evaluated by Twig, yielding JSON in each case. The two JSON records are decoded to PHP arrays then merged to yield a set of environment variables that are passed back into Twig as the set of environment variables for expanding the template and (if TwigAll checked) all the other question fields. 

How general purpose are your macros - would they be useful to many other html-ui users? If so I might be able to incorporate some of them into the current set (here). 

An alternative to using macros in all question fields is to generate all the text snippets you want to insert into the various question fields as template parameter variables. That's how we usually use the TwigAll capability. Then you wouldn't need macros at all. But ... not as elegant, I admit.

If you want to post an example of one of your questions, I might get a better feel for the problem and possible solutions.

In reply to Richard Lobb

Re: Global Extra in Template

by M. César Rodríguez -

Hi,

I have worked an example, but I find it big enough not to be useful if posted here. Thus I think it is more adequate to send the whole XML of the question to you (around 210 KB).

Is there a way to send PMs here?

Regards.

In reply to M. César Rodríguez

Re: Global Extra in Template

by Richard Lobb -

If you don't want to attach it to a posting here, you can email it to me directly: trampgeek@gmail.com. But I'm on holiday for a bit so won't be replying for two or three weeks.