Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions backup/moodle2/backup_questionnaire_stepslib.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ protected function define_structure() {

$responsedate = new backup_nested_element('response_date', array('id'), array('response_id', 'question_id', 'response'));

$responsefiles = new backup_nested_element('response_files');

$responsefile = new backup_nested_element('response_file', ['id'], ['response_id', 'question_id', 'fileid']);

$responsemultiples = new backup_nested_element('response_multiples');

$responsemultiple = new backup_nested_element('response_multiple', array('id'), array(
Expand Down Expand Up @@ -137,6 +141,9 @@ protected function define_structure() {
$response->add_child($responsedates);
$responsedates->add_child($responsedate);

$response->add_child($responsefiles);
$responsefiles->add_child($responsefile);

$response->add_child($responsemultiples);
$responsemultiples->add_child($responsemultiple);

Expand Down Expand Up @@ -178,6 +185,8 @@ protected function define_structure() {
$response->set_source_table('questionnaire_response', array('questionnaireid' => backup::VAR_PARENTID));
$responsebool->set_source_table('questionnaire_response_bool', array('response_id' => backup::VAR_PARENTID));
$responsedate->set_source_table('questionnaire_response_date', array('response_id' => backup::VAR_PARENTID));
$responsefile->set_source_table('questionnaire_response_file', ['response_id' => backup::VAR_PARENTID]);
$responsefile->annotate_files('mod_questionnaire', 'response_file', null);
$responsemultiple->set_source_table('questionnaire_resp_multiple', array('response_id' => backup::VAR_PARENTID));
$responseother->set_source_table('questionnaire_response_other', array('response_id' => backup::VAR_PARENTID));
$responserank->set_source_table('questionnaire_response_rank', array('response_id' => backup::VAR_PARENTID));
Expand Down
82 changes: 82 additions & 0 deletions backup/moodle2/restore_questionnaire_stepslib.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ class restore_questionnaire_activity_structure_step extends restore_activity_str
*/
protected $olddependencies = [];

/**
* @var array $responsefileids Contains new questionnaire_response_file.id in key and old id in value.
*/
protected $responsefileids = [];

/**
* Implementation of define_structure.
* @return mixed
Expand Down Expand Up @@ -92,6 +97,8 @@ protected function define_structure() {
'/activity/questionnaire/responses/response/response_bools/response_bool');
$paths[] = new restore_path_element('questionnaire_response_date',
'/activity/questionnaire/responses/response/response_dates/response_date');
$paths[] = new restore_path_element('questionnaire_response_file',
'/activity/questionnaire/responses/response/response_files/response_file');
$paths[] = new restore_path_element('questionnaire_response_multiple',
'/activity/questionnaire/responses/response/response_multiples/response_multiple');
$paths[] = new restore_path_element('questionnaire_response_other',
Expand Down Expand Up @@ -336,6 +343,22 @@ protected function process_questionnaire_response_date($data) {
$DB->insert_record('questionnaire_response_date', $data);
}

/**
* Process file responses.
* @param array $data
* @throws dml_exception
*/
protected function process_questionnaire_response_file($data) {
global $DB;

$data = (object)$data;
$data->response_id = $this->get_new_parentid('questionnaire_response');
$data->question_id = $this->get_mappingid('questionnaire_question', $data->question_id);
// Insert the questionnaire_response_file record.
$newid = $DB->insert_record('questionnaire_response_file', $data);
$this->responsefileids[$newid] = $data->id;
}

/**
* Process multiple responses.
* @param array $data
Expand Down Expand Up @@ -425,6 +448,64 @@ protected function process_questionnaire_response_text($data) {
$DB->insert_record('questionnaire_response_text', $data);
}

/**
* Handle related files for response file assignments and update the references between the
* records in the questionnaire_response_file and the files table.
*/
protected function handle_releated_files_for_response_file_assignments() {
global $DB;
// If there are no response file IDs mapped before, then there are no response files to handle.
if (empty($this->responsefileids)) {
return;
}
$newctx = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'context', $this->task->get_old_contextid());
restore_dbops::send_files_to_pool(
$this->get_basepath(),
$this->get_restoreid(),
'mod_questionnaire',
'response_file',
$this->task->get_old_contextid(),
$this->task->get_userid()
);
// Lookup all file records that where just created for this context and component/filearea.
[$sql, $params] = $DB->get_in_or_equal(array_values($this->responsefileids), SQL_PARAMS_NAMED, 'id');
$params['component'] = 'mod_questionnaire';
$params['filearea'] = 'response_file';
$params['ctx'] = $newctx->newitemid;
$filerecords = $DB->get_records_select(
'files',
'contextid = :ctx AND component = :component AND filearea = :filearea AND itemid ' . $sql,
$params
);
$oldtonew = array_flip($this->responsefileids);
foreach ($filerecords as $filerecord) {
if (array_key_exists($filerecord->itemid, $oldtonew)) {
if ($filerecord->filename != '.') {
// We have a real file, use that file.id to update the questionnaire_response_file.fileid.
$DB->set_field(
'questionnaire_response_file',
'fileid',
$filerecord->id,
['id' => $oldtonew[$filerecord->itemid]]
);
}
// Update the itemid to the new response_file.id.
$filerecord->itemid = $oldtonew[$filerecord->itemid];
// Calculate new pathnamehash.
$filerecord->pathnamehash = \file_storage::get_pathname_hash(
$filerecord->contextid,
$filerecord->component,
$filerecord->filearea,
$filerecord->itemid,
$filerecord->filepath,
$filerecord->filename
);
// Adjust the files record with the new itemid and pathnamehash.
$DB->update_record('files', $filerecord);
}
}
}

/**
* Stuff to do after execution.
*/
Expand Down Expand Up @@ -473,6 +554,7 @@ protected function after_execute() {
$this->add_related_files('mod_questionnaire', 'question', 'questionnaire_question');
$this->add_related_files('mod_questionnaire', 'sectionheading', 'questionnaire_fb_sections');
$this->add_related_files('mod_questionnaire', 'feedback', 'questionnaire_feedback');
$this->handle_releated_files_for_response_file_assignments();

// Process any old rate question named degree choices after all questions and choices have been restored.
if ($this->task->get_old_moduleversion() < 2018110103) {
Expand Down
1 change: 1 addition & 0 deletions classes/feedback_section_form.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
require_once($CFG->libdir . '/formslib.php');
require_once($CFG->dirroot.'/mod/questionnaire/lib.php');

#[\AllowDynamicProperties]
/**
* Print the form to add or edit a questionnaire-instance
*
Expand Down
2 changes: 1 addition & 1 deletion classes/file_storage.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
namespace mod_questionnaire;

/**
* Defines the file stoeage class for questionnaire.
* Defines the file storage class for questionnaire.
* @package mod_questionnaire
* @copyright 2020 onwards Mike Churchward (mike.churchward@poetopensource.org)
* @author Mike Churchward
Expand Down
1 change: 1 addition & 0 deletions classes/output/renderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,7 @@ public function print_preview_formend($url, $submitstr, $resetstr) {
$output .= \html_writer::start_tag('div');
$output .= \html_writer::empty_tag('input', ['type' => 'submit', 'name' => 'submit', 'value' => $submitstr,
'class' => 'btn btn-primary']);
$output .= \html_writer::empty_tag('input', ['type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey()]);
$output .= ' ';
$output .= \html_writer::tag('a', $resetstr, ['href' => $url, 'class' => 'btn btn-secondary mr-1']);
$output .= \html_writer::end_tag('div') . "\n";
Expand Down
227 changes: 227 additions & 0 deletions classes/question/file.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
<?php
// 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 <http://www.gnu.org/licenses/>.

namespace mod_questionnaire\question;
use core_media_manager;
use form_filemanager;
use mod_questionnaire\responsetype\response\response;
use moodle_url;
use MoodleQuickForm;

/**
* This file contains the parent class for text question types.
*
* @author Laurent David
* @author Martin Cornu-Mansuy
* @copyright 2023 onward CALL Learning <martin@call-learning.fr>
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package mod_questionnaire
*/
class file extends question {
/**
* Get name.
*
* @return string
*/
public function helpname() {
return 'file';
}

/**
* Override and return a form template if provided. Output of question_survey_display is iterpreted based on this.
*
* @return boolean | string
*/
public function question_template() {
return false;
}

/**
* Override and return a response template if provided. Output of response_survey_display is iterpreted based on this.
*
* @return boolean | string
*/
public function response_template() {
return false;
}

/**
* Get response class.
*
* @return object|string
*/
protected function responseclass() {
return '\\mod_questionnaire\\responsetype\\file';
}

/**
* Survey display output.
*
* @param response $response
* @param object $descendantsdata
* @param bool $blankquestionnaire
* @return string
*/
protected function question_survey_display($response, $descendantsdata, $blankquestionnaire = false) {
global $CFG, $PAGE;
require_once($CFG->libdir . '/filelib.php');

$elname = 'q' . $this->id;
// If there is a response and the response is resumed, get the original itemid.
$itemid = (
isset($response->answers[$this->id]) &&
!empty($response->answers[$this->id]) &&
isset($_REQUEST['resume']) &&
$_REQUEST['resume'] === '1'
) ? (int)$response->answers[$this->id][0]->id : 0;
// Prepare the draft area.
$draftitemid = file_get_submitted_draft_itemid($elname);
file_prepare_draft_area(
$draftitemid,
$this->context->id,
'mod_questionnaire',
'response_file',
$itemid,
self::get_file_manager_option()
);
// Filemanager form element implementation is far from optimal, we need to rework this if we ever fix it...
require_once("$CFG->dirroot/lib/form/filemanager.php");
$fm = new form_filemanager((object)self::get_file_manager_option($draftitemid));
$output = $PAGE->get_renderer('core', 'files');
$html = '<div class="form-filemanager" data-fieldtype="filemanager">' .
$output->render($fm) .
'<input type="hidden" name="' . $elname . '" value="' . $draftitemid . '" />' .
'</div>';
return $html;
}

/**
* Check question's form data for complete response.
* @param \stdClass $responsedata The data entered into the response.
* @return bool
*/
public function response_complete($responsedata) {
$answered = false;
// If $responsedata is a response object, look through the answers.
if (
is_a($responsedata, 'mod_questionnaire\responsetype\response\response') &&
isset($responsedata->answers[$this->id]) &&
!empty($responsedata->answers[$this->id])
) {
$answer = reset($responsedata->answers[$this->id]);
$answered = ((int)$answer->value > 0);
} else if (isset($responsedata->{'q' . $this->id})) { // If $responsedata is webform data, check that it is not empty.
$draftitemid = (int)$responsedata->{'q' . $this->id};
if ($draftitemid > 0) {
$info = file_get_draft_area_info($draftitemid);
$answered = $info['filecount'] > 0;
}
}
return !($this->required() && ($this->deleted == 'n') && !$answered);
}

/**
* Get file manager options, here allow only one image or pdf file, without
* creating subdirectories.
*
* @param ?int $draftitemid
* @return array
*/
public static function get_file_manager_option(?int $draftitemid = 0) {
return [
'subdirs' => false,
'accepted_types' => ['image', 'document'],
'maxfiles' => 1,
'itemid' => $draftitemid,
];
}

/**
* Response display output, e.g. render the file image/pdf or show a linkt to download it.
*
* @param \stdClass $data
* @return string
*/
protected function response_survey_display($data) {
global $PAGE, $CFG;
require_once($CFG->libdir . '/filelib.php');
require_once($CFG->libdir . '/resourcelib.php');
if (isset($data->answers[$this->id])) {
$answer = reset($data->answers[$this->id]);
} else {
return '';
}
$fs = get_file_storage();
$file = $fs->get_file_by_id($answer->value);
$code = '';

if ($file) {
// There is a file.
$moodleurl = moodle_url::make_pluginfile_url(
$file->get_contextid(),
$file->get_component(),
$file->get_filearea(),
$file->get_itemid(),
$file->get_filepath(),
$file->get_filename()
);

$mimetype = $file->get_mimetype();
$title = '';

$mediamanager = core_media_manager::instance($PAGE);
$embedoptions = [
core_media_manager::OPTION_TRUSTED => true,
core_media_manager::OPTION_BLOCK => true,
];

if (file_mimetype_in_typegroup($mimetype, 'web_image')) { // It's an image.
$code = resourcelib_embed_image($moodleurl->out(), $title);
} else if ($mimetype === 'application/pdf') { // PDF document.
$code = resourcelib_embed_pdf($moodleurl->out(), $title, get_string('view'));
} else if ($mediamanager->can_embed_url($moodleurl, $embedoptions)) { // Media (audio/video) file.
$code = $mediamanager->embed_url($moodleurl, $title, 0, 0, $embedoptions);
} else { // We need a way to discover if we are loading remote docs inside an iframe.
$moodleurl->param('embed', 1);
// Anything else - just try object tag enlarged as much as possible.
$code = resourcelib_embed_general($moodleurl, $title, get_string('view'), $mimetype);
}
}
return '<div class="response text">' . $code . '</div>';
}

/**
* Add the length element as hidden.
*
* @param \MoodleQuickForm $mform
* @param string $helpname
* @return \MoodleQuickForm
*/
protected function form_length(MoodleQuickForm $mform, $helpname = '') {
return question::form_length_hidden($mform);
}

/**
* Add the precise element as hidden.
*
* @param \MoodleQuickForm $mform
* @param string $helpname
* @return \MoodleQuickForm
*/
protected function form_precise(MoodleQuickForm $mform, $helpname = '') {
return question::form_precise_hidden($mform);
}
}
Loading