Commit 7f1ef28f authored by Astor Bizard's avatar Astor Bizard
Browse files

Merge branch 'dev' into 'test'

Web submission and variations form improvements, minor fixes

See merge request !11
parents b1ece6da 5f78ba40
define(["jquery"],function(a){function b(b){"archive"==b?(a("#id_headersubmitarchive").show().removeClass("collapsed"),a("#id_headersubmitfiles").hide()):(a("#id_headersubmitarchive").hide(),a("#id_headersubmitfiles").show().removeClass("collapsed"))}return{setup:function(){var c=a('select[name="submitmethod"]');c.change(function(){b(c.val())}),b(c.val())}}});
\ No newline at end of file
define(['jquery'], function($) {
function updateHeaders(submitType) {
if (submitType == 'archive') {
$('#id_headersubmitarchive').show().removeClass('collapsed');
$('#id_headersubmitfiles').hide();
} else {
$('#id_headersubmitarchive').hide();
$('#id_headersubmitfiles').show().removeClass('collapsed');
}
}
return {
setup: function() {
var $select = $('select[name="submitmethod"]');
$select.change(function() {
updateHeaders($select.val());
});
updateHeaders($select.val());
}
};
});
\ No newline at end of file
......@@ -30,5 +30,9 @@ class variation_added extends variation_base {
protected function init() {
parent::init();
$this->data ['crud'] = 'c';
$this->legacyaction = 'added variation';
}
public function get_description() {
return $this->get_description_mod( 'added' );
}
}
<?php
// This file is part of VPL for Moodle - http://vpl.dis.ulpgc.es/
//
// VPL for 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.
//
// VPL for 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 VPL for Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Class for logging of assigned variation events
*
* @package mod_vpl
* @copyright 2020 onwards Juan Carlos Rodrguez-del-Pino
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @author Juan Carlos Rodrguez-del-Pino <jcrodriguez@dis.ulpgc.es>
*/
namespace mod_vpl\event;
defined( 'MOODLE_INTERNAL' ) || die();
require_once(dirname( __FILE__ ) . '/../../locallib.php');
class variation_assigned extends variation_base {
protected function init() {
parent::init();
$this->legacyaction = 'assigned variation';
$this->data ['crud'] = 'c';
}
public function get_description() {
return $this->get_description_mod( 'assigned' );
}
public static function log($vpl, $varid = null, $userid = null) {
$vplinstance = $vpl->get_instance();
$info = array (
'objectid' => $varid,
'contextid' => $vpl->get_context()->id,
'relateduserid' => $userid,
'courseid' => $vplinstance->course,
'other' => array('vplid' => $vplinstance->id),
);
parent::log( $info );
}
}
\ No newline at end of file
......@@ -26,20 +26,42 @@ namespace mod_vpl\event;
defined( 'MOODLE_INTERNAL' ) || die();
require_once(dirname( __FILE__ ) . '/../../locallib.php');
class variation_base extends vpl_base {
class variation_base extends base {
public static function get_objectid_mapping() {
return array('db' => VPL_VARIATIONS, 'restore' => VPL_VARIATIONS);
}
public static function get_other_mapping() {
// Nothing to map.
return false;
}
protected function init() {
parent::init();
$this->data ['crud'] = 'u';
$this->data ['edulevel'] = self::LEVEL_TEACHING;
$this->data ['objecttable'] = VPL_VARIATIONS;
$this->legacyaction = 'variations form';
}
public function get_description() {
return $this->get_description_mod( 'variation' );
}
public static function log($info) {
if (is_array( $info )) {
parent::log( $info );
public static function log($vpl, $varid = null) {
if (is_array($vpl)) {
$info = $vpl;
} else {
throw new \coding_exception( 'Parameter must be an array' );
$vplinstance = $vpl->get_instance();
$info = array (
'objectid' => $varid,
'contextid' => $vpl->get_context()->id,
'courseid' => $vplinstance->course,
'other' => array('vplid' => $vplinstance->id),
);
}
parent::log( $info );
}
public function get_url() {
return $this->get_url_base( 'view.php' );
}
public function get_description_mod($mod) {
$desc = 'The user with id ' . $this->userid . ' ' . $mod;
$desc .= ' variation with id ' . $this->objectid . ' of VPL activity with id ' . $this->other['vplid'];
if ($this->relateduserid) {
$desc .= ' for user with id ' . $this->relateduserid;
}
return $desc;
}
}
......@@ -28,5 +28,9 @@ class variation_deleted extends variation_base {
protected function init() {
parent::init();
$this->data['crud'] = 'd';
$this->legacyaction = 'deleted variation';
}
public function get_description() {
return $this->get_description_mod( 'deleted' );
}
}
......@@ -30,5 +30,9 @@ class variation_updated extends variation_base {
protected function init() {
parent::init();
$this->data ['crud'] = 'u';
$this->legacyaction = 'updated variation';
}
public function get_description() {
return $this->get_description_mod( 'updated' );
}
}
......@@ -45,7 +45,7 @@ class vpl_base extends base {
} else {
$einfo = array (
'objectid' => $vpl->get_instance()->id,
'context' => $vpl->get_context()
'contextid' => $vpl->get_context()->id
);
parent::log( $einfo );
}
......
......@@ -57,172 +57,23 @@ class vpl_editor_util {
$PAGE->requires->js_init_call('M.util.init_colour_picker', array('interfacetheme-colorpicker-secondary'));
echo '<style type="text/css" title="custom-interfacetheme-css"></style>';
}
public static function print_editor() {
global $OUTPUT;
echo $OUTPUT->box_start();
?>
<div id="vplide" class="vpl_ide vpl_ide_root vpl_theme_<?php p(get_user_preferences('vpl_interfacetheme', 'modernblue'))?>">
<div id="vpl_menu" class="vpl_ide_menu"></div>
<div id="vpl_tr" class="vpl_ide_tr">
<div id="vpl_filelist" style="display: none;">
<div id="vpl_filelist_header"><?php p(get_string('filelist', VPL));?></div>
<div id="vpl_filelist_content"></div>
</div>
<div id="vpl_tabs" class="vpl_ide_tabs">
<div id="vpl_tabs_scroll">
<ul id="vpl_tabs_ul"></ul>
</div>
</div>
<div id="vpl_results" class="vpl_ide_results">
<div id="vpl_results_accordion"></div>
</div>
</div>
<div id="vpl_ide_dialog_new" class="vpl_ide_dialog"
style="display: none;">
<fieldset>
<label for="vpl_ide_input_newfilename">
<?php p(get_string('new_file_name', VPL));?></label> <input
type="text" id="vpl_ide_input_newfilename"
name="vpl_ide_input_newfilename" value=""
class="ui-widget-content ui-corner-all" autofocus /><br>
</fieldset>
</div>
<div id="vpl_ide_dialog_rename" class="vpl_ide_dialog"
style="display: none;">
<fieldset>
<label for="vpl_ide_input_renamefilename">
<?php p(get_string('rename'));?></label> <input
type="text" id="vpl_ide_input_renamefilename"
name="vpl_ide_input_renamefilename" value=""
class="ui-widget-content ui-corner-all" autofocus /><br>
</fieldset>
</div>
<div id="vpl_ide_dialog_delete" class="vpl_ide_dialog"
style="display: none;">
<fieldset>
<label for="vpl_ide_input_deletefilename">
<?php p(get_string('delete'));?></label> <input
type="text" id="vpl_ide_input_deletefilename"
name="vpl_ide_input_deletefilename" value=""
class="ui-widget-content ui-corner-all" autofocus /><br>
</fieldset>
</div>
<div id="vpl_ide_dialog_sort" class="vpl_ide_dialog"
style="display: none;">
<ol id="vpl_sort_list"></ol>
</div>
<div id="vpl_ide_dialog_multidelete" class="vpl_ide_dialog"
style="display: none;">
<fieldset id="vpl_multidelete_list"></fieldset>
</div>
<div id="vpl_ide_dialog_fontsize" class="vpl_ide_dialog"
style="display: none;">
<div class="vpl_fontsize_slider_value"></div>
<div class="vpl_fontsize_slider"></div>
</div>
<div id="vpl_ide_dialog_theme" class="vpl_ide_dialog" style="display: none;">
<span><?php p(get_string('acetheme', VPL));?></span>
<select class="acetheme">
<?php
foreach (self::get_installed_themes() as $theme => $name) {
echo '<option value="' . $theme . '">' . $name . '</option>';
}
?>
</select>
<hr>
<span><?php p(get_string('interfacetheme', VPL));?></span>
<select class="interfacetheme">
<option value="default"><?php p(get_string('default'));?></option>
<option value="modernblue"><?php p('Modern Blue');?></option>
<option value="custom"><?php p(get_string('custom', VPL));?></option>
</select>
<div class="custom-theme-colorpicker">
<br>
<span><?php p(get_string('primarycolor', VPL));?></span>
<input type="text" id="interfacetheme-colorpicker-primary"
value="<?php p(get_user_preferences('vpl_custom_interface_theme_color_primary', '#000000'));?>">
<div class="admin_colourpicker"></div>
</div>
<div class="custom-theme-colorpicker">
<span><?php p(get_string('secondarycolor', VPL));?></span>
<input type="text" id="interfacetheme-colorpicker-secondary"
value="<?php p(get_user_preferences('vpl_custom_interface_theme_color_secondary', '#FFFFFF'));?>">
<div class="admin_colourpicker"></div>
</div>
</div>
<div id="vpl_ide_dialog_comments" class="vpl_ide_dialog"
style="display: none;">
<fieldset>
<label for="vpl_ide_input_comments">
<?php p(get_string('comments', VPL));?></label> <textarea
id="vpl_ide_input_comments" name="vpl_ide_input_comments"
class="ui-widget-content ui-corner-all" autofocus ></textarea>
</fieldset>
</div>
<div id="vpl_ide_dialog_about" class="vpl_ide_dialog"
style="display: none;">
<div>
<h3>IDE for VPL</h3>
This IDE is part of VPL <a href="http://vpl.dis.ulpgc.es"
target="_blank">Virtual Programming Lab for Moodle</a><br> Author:
Juan Carlos Rodríguez del Pino &lt;jcrodriguez@dis.ulpgc.es&gt;<br>
Licence: <a href="http://www.gnu.org/copyleft/gpl.html"
target="_blank">GNU GPL v3</a><br> This software uses/includes the
following software under the corresponding licence:
<ul>
<li><a href="http://ace.c9.io" target="_blank">ACE</a>: an embeddable
code editor written in JavaScript. Copyright (c) 2010, Ajax.org B.V.
(<a href="../editor/ace9/LICENSE" target="_blank">licence</a>)</li>
<li><a href="https://github.com/chjj/term.js/" target="_blank">term.js</a>:
A full xterm clone written in javascript. Copyright (c) 2012-2013,
Christopher Jeffrey (MIT License)</li>
<li><a href="http://kanaka.github.io/noVNC/" target="_blank">noVNC</a>:
VNC client using HTML5 (WebSockets, Canvas). noVNC is Copyright (C)
2011 Joel Martin &lt;github@martintribe.org&gt; (<a
href="../editor/noVNC/LICENSE.txt" target="_blank">licence</a>)</li>
<li>unzip.js: August Lilleaas</li>
<li>inflate.js: August Lilleaas and Masanao Izumo &lt;iz@onicos.co.jp&gt;</li>
<li><a href="https://developers.google.com/blockly" target="_blank">Blockly</a>:
A library for building visual programming editors
(<a href="../editor/blockly/LICENSE" target="_blank">licence</a>)</li>
<li><a href="https://github.com/NeilFraser/JS-Interpreter" target="_blank">JS-Interpreter</a>:
A sandboxed JavaScript interpreter in JavaScript
(<a href="../editor/acorn/LICENSE" target="_blank">licence</a>)</li>
</ul>
</div>
</div>
<form style="display: none;">
<input type="file" multiple="multiple" id="vpl_ide_input_file" />
</form>
<div id="vpl_ide_dialog_shortcuts" class="vpl_ide_dialog" style="display: none;" >
<div class="vpl_ide_dialog_content"></div>
</div>
<div id="vpl_dialog_terminal">
<pre id="vpl_terminal" class="vpl_terminal"></pre>
</div>
<div id="vpl_dialog_terminal_clipboard" class="vpl_ide_dialog vpl_clipboard" style="display: none;">
<div class="vpl_clipboard_label1"></div><br>
<textarea readonly="readonly" class="vpl_clipboard_entry1"></textarea><br>
<div class="vpl_clipboard_label2"></div><br>
<textarea class="vpl_clipboard_entry2"></textarea>
</div>
<div id="vpl_dialog_vnc_clipboard" class="vpl_ide_dialog vpl_clipboard" style="display: none;">
<div class="vpl_clipboard_label1"></div><br>
<textarea readonly="readonly" class="vpl_clipboard_entry1"></textarea><br>
<div class="vpl_clipboard_label2"></div><br>
<textarea class="vpl_clipboard_entry2"></textarea>
</div>
<div id="vpl_dialog_vnc" style="display: none;">
<canvas class="vpl_noVNC_canvas">
Canvas not supported.
</canvas>
</div>
</div>
<?php
echo $OUTPUT->box_end();
$context = new stdClass();
$context->currenttheme = get_user_preferences('vpl_interfacetheme', 'modernblue');
$context->currentprimarycolor = get_user_preferences('vpl_custom_interface_theme_color_primary', '#000000');
$context->currentsecondarycolor = get_user_preferences('vpl_custom_interface_theme_color_secondary', '#FFFFFF');
$context->installedthemes = array();
foreach (self::get_installed_themes() as $id => $name) {
$theme = new stdClass();
$theme->id = $id;
$theme->name = $name;
$context->installedthemes[] = $theme;
}
echo $OUTPUT->box($OUTPUT->render_from_template('mod_vpl/editor', $context));
}
public static function strings_for_js() {
global $PAGE;
$PAGE->requires->strings_for_js(
......
......@@ -440,7 +440,7 @@ class file_group_process {
* return the directory file
* @return string
*/
public function get_dir() {
public function get_dir() {
return $this->dir;
}
}
......
......@@ -40,7 +40,7 @@ class mod_vpl_grade_form extends moodleform {
$options = array ();
$options [- 1] = get_string( 'nograde' );
if ($scaleid > 0) {
for ($i = 0; $i <= $scaleid; $i ++) {
for ($i = 0; $i <= $scaleid; $i ++) {
$options [$i] = $i . ' / ' . $scaleid;
}
} else if ($scaleid < 0) {
......@@ -53,7 +53,7 @@ class mod_vpl_grade_form extends moodleform {
}
return $options;
}
public function __construct($page, & $vpl,& $submission) {
public function __construct($page, & $vpl, & $submission) {
$this->vpl = & $vpl;
$this->submission = & $submission;
parent::__construct( $page );
......@@ -93,12 +93,12 @@ class mod_vpl_grade_form extends moodleform {
}
$gridscore = $gradinginstance->get_controller()->get_min_max_score()['maxscore'];
$mform->addElement('header','hAdvancedGrading', get_string( 'gradingmanagement','grading') );
$mform->addElement('header', 'hAdvancedGrading', get_string( 'gradingmanagement', 'grading') );
$mform->addElement('grading',
'advancedgrading',
'',
array('gradinginstance' => $gradinginstance));
$mform->addElement('hidden','advancedgradinginstanceid', $gradinginstance->get_id());
$mform->addElement('hidden', 'advancedgradinginstanceid', $gradinginstance->get_id());
$mform->setType('advancedgradinginstanceid', PARAM_INT);
// Numeric grade.
if ($grade > 0) {
......@@ -106,14 +106,14 @@ class mod_vpl_grade_form extends moodleform {
$jscript = 'VPL.mergeGrade(' . $grade . ','.$graderaw.','.$gridscore.')';
//~ $html = ' <a class="btn btn-default" href="javascript:void(0);" onclick="' . $jscript . '">' . s( get_string( 'merge', VPL ) ) . '</a>';
//~ $mform->addElement('html', $html );
$mform->addElement('button','btnmerge', get_string( 'merge', VPL ),'onclick="' . $jscript . '"' );
$mform->addElement('button', 'btnmerge', get_string( 'merge', VPL ), 'onclick="' . $jscript . '"' );
//$mform->registerNoSubmitButton('btnmerge');
}
}
$mform->addElement('header','hGrade', get_string( 'grade') );
$mform->addElement('header', 'hGrade', get_string( 'grade') );
//$mform->addElement('html','<div id="vpl_grade_form">');
$buttonarray=array();
$buttonarray = array();
if ($grade != 0) {
if ($grade > 0) {
$buttonarray[] =& $mform->createElement('text', 'grade', '', 'size="6"' );
......@@ -123,7 +123,7 @@ class mod_vpl_grade_form extends moodleform {
}
}
$buttonarray[] =& $mform->createElement('submit', 'save', get_string( 'grade' ) );
if ($inpopup) {
if ($inpopup) {
$buttonarray[] =& $mform->createElement('submit', 'savenext', get_string( 'gradeandnext', VPL ) );
}
$buttonarray[] =& $mform->createElement('submit', 'removegrade', get_string( 'removegrade', VPL ) );
......@@ -147,7 +147,7 @@ class mod_vpl_grade_form extends moodleform {
}
$mform->addGroup($buttonarray, 'buttonar', get_string('grades'), array(' '), false);
//$mform->addElement('html', '</div>' );
$textarray=array();
$textarray = array();
if ($grade != 0) {
//$mform->addElement('html','</br><b>'.s( get_string( 'comments', VPL )).'</b></br>' );
//$textarray[] =& $mform->createElement('textarea', 'comments', get_string( 'comments', VPL ), 'rows="18" cols="70"' );
......
......@@ -96,7 +96,7 @@ if ($subinstance->dategraded == 0 || $subinstance->grader == $USER->id || $subin
} else {
$href = 'gradesubmission.php';
}
$gradeform = new mod_vpl_grade_form( $href, $vpl,$submission );
$gradeform = new mod_vpl_grade_form( $href, $vpl, $submission );
if ($gradeform->is_cancelled()) { // Grading canceled.
vpl_inmediate_redirect( $link );
} else if ($fromform = $gradeform->get_data()) { // Grade (new or update).
......@@ -127,15 +127,14 @@ if ($subinstance->dategraded == 0 || $subinstance->grader == $USER->id || $subin
vpl_redirect( $link, get_string( 'badinput' ), 'error' );
}
if ($submission->is_graded()) {
if ($submission->is_graded()) {
$action = 'update grade';
} else {
$action = 'grade';
}
$gradinginstance = $submission->get_grading_instance();
if ($gradinginstance) {
$advancedgrading = $gradinginstance->submit_and_get_grade($fromform->advancedgrading,
$submissionid);
$advancedgrading = $gradinginstance->submit_and_get_grade($fromform->advancedgrading, $submissionid);
}
if (! $submission->set_grade( $fromform )) {
vpl_redirect( $link, get_string( 'gradenotsaved', VPL ), 'error' );
......
......@@ -56,7 +56,7 @@ if ($userid == $USER->id) { // Make own submission.
$instance = $vpl->get_instance();
$vpl->print_header( get_string( 'submission', VPL ) );
$vpl->print_view_tabs( basename( __FILE__ ) );
$mform = new mod_vpl_submission_form( 'submission.php', $vpl );
$mform = new mod_vpl_submission_form( 'submission.php', $vpl, $userid );
if ($mform->is_cancelled()) {
vpl_inmediate_redirect( vpl_mod_href( 'view.php', 'id', $id ) );
die();
......@@ -72,23 +72,82 @@ if ($fromform = $mform->get_data()) {
$rfn = $vpl->get_fgm('required');
$reqfiles = $rfn->getFileList();
$files = array ();
for ($i = 0; $i < $instance->maxfiles; $i ++) {
$attribute = 'file' . $i;
$name = $mform->get_new_filename( $attribute );
$data = $mform->get_file_content( $attribute );
if ($data !== false && $name !== false) {
$prevsub = $vpl->last_user_submission( $userid );
$firstsub = ($prevsub === false);
$prevsubfiles = (new mod_vpl_submission( $vpl, $prevsub ))->get_submitted_fgm()->getAllFiles();
if ($fromform->submitmethod == 'archive') {
if (!$firstsub && $fromform->archiveaction == 'replace') {
// Use files of previous submission.
foreach ($prevsubfiles as $subfilename => $subfilecontent) {
$files[$subfilename] = $subfilecontent;
}
}
// Open archive.
$zipname = $mform->save_temp_file( 'archive' );
$zip = new ZipArchive();
$zip->open($zipname);
$subreqfiles = array();
$subotherfiles = array();
// Read archive and split between required / additional files.
for ($i = 0; $i < $zip->numFiles; $i++) {
$filename = $zip->statIndex($i)['name'];
if (substr($filename, -1) == '/') { // Directory.
continue;
}
$filecontent = file_get_contents('zip://' . $zipname . '#' . $filename);
// Autodetect text file encode if not binary.
if (! vpl_is_binary( $name )) {
$encode = mb_detect_encoding( $data, 'UNICODE, UTF-16, UTF-8, ISO-8859-1', true );
if ($encode > '') { // If code detected.
$data = iconv( $encode, 'UTF-8', $data );
if (! vpl_is_binary( $filename )) {
$encoding = mb_detect_encoding( $filecontent, 'UNICODE, UTF-16, UTF-8, ISO-8859-1', true );
if ($encoding > '') { // If code detected.
$filecontent = iconv( $encoding, 'UTF-8', $filecontent );
}
$files [$name] = $data;
} else {
if (in_array($name . '.b64', $reqfiles)) {
$files [$name . '.b64'] = base64_encode($data);
} else {
$files [$name] = $data;
if (in_array($filename . '.b64', $reqfiles)) {
$filename = $filename . '.b64';
$filecontent = base64_encode($filecontent);
}
}
if (in_array($filename, $reqfiles)) {
$subreqfiles[$filename] = $filecontent;
} else {
$subotherfiles[$filename] = $filecontent;
}
}
foreach ($reqfiles as $reqfile) {
if (isset($subreqfiles[$reqfile])) {
$files[$reqfile] = $subreqfiles[$reqfile];
}
}
foreach ($subotherfiles as $filename => $filecontent) {
$files[$filename] = $filecontent;
}
// Close archive.
$zip->close();
unlink($zipname);
} else {
for ($i = 0; $i < $instance->maxfiles; $i ++) {
$field = 'file' . $i;
if (!$firstsub && isset($fromform->{$field . 'action'}) && $fromform->{$field . 'action'} == 'keep') {
$filename = $fromform->{$field . 'name'};
$files[$filename] = $prevsubfiles[$filename];
} else {
$name = $mform->get_new_filename( $field );
$data = $mform->get_file_content( $field );
if ($data !== false && $name !== false) {
// Autodetect text file encode if not binary.
if (! vpl_is_binary( $name )) {
$encode = mb_detect_encoding( $data, 'UNICODE, UTF-16, UTF-8, ISO-8859-1', true );
if ($encode > '') { // If code detected.
$data = iconv( $encode, 'UTF-8', $data );
}
$files [$name] = $data;
} else {
if (in_array($name . '.b64', $reqfiles)) {
$files [$name . '.b64'] = base64_encode($data);
} else {
$files [$name] = $data;
}
}
}
}
}
......
......@@ -28,20 +28,22 @@ defined( 'MOODLE_INTERNAL' ) || die();
global $CFG;
require_once($CFG->libdir.'/formslib.php');
require_once(dirname(__FILE__).'/../locallib.php');
require_once(dirname(__FILE__).'/edit.class.php');
class mod_vpl_submission_form extends moodleform {
protected $vpl;
protected $userid;
protected function getinternalform() {
return $this->_form;
}
public function __construct($page, $vpl) {
public function __construct($page, $vpl, $userid) {
$this->vpl = $vpl;
$this->userid = $userid;
parent::__construct( $page );
}
protected function definition() {
global $CFG;
$mform = & $this->_form;
$mform->addElement( 'header', 'headersubmission', get_string( 'submission', VPL ) );
// Identification info.
$mform->addElement( 'hidden', 'id' );
$mform->setType( 'id', PARAM_INT );
......@@ -54,18 +56,87 @@ class mod_vpl_submission_form extends moodleform {
) );
$mform->setType( 'comments', PARAM_TEXT );
$firstsub = ($this->vpl->last_user_submission( $this->userid ) === false);