Skip to content

Commit cf5bf32

Browse files
author
tai.letan
committed
Questionnaire__deletion_area_(Recycle_bin)_for_deleted_orphaned_questions
1 parent 6253d1a commit cf5bf32

19 files changed

+671
-72
lines changed

backup/moodle2/restore_questionnaire_stepslib.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,10 @@ protected function process_questionnaire_question($data) {
163163
$data = (object)$data;
164164
$oldid = $data->id;
165165
$data->surveyid = $this->get_new_parentid('questionnaire_survey');
166-
166+
// Cover for legacy backup data.
167+
if ($data->deleted === 'n') {
168+
$data->deleted = null;
169+
}
167170
// Insert the questionnaire_question record.
168171
$newitemid = $DB->insert_record('questionnaire_question', $data);
169172
$this->set_mapping('questionnaire_question', $oldid, $newitemid, true);

classes/question/question.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,8 @@ abstract class question {
103103
/** @var boolean $required The required flag. */
104104
public $required = 'n';
105105

106-
/** @var boolean $deleted The deleted flag. */
107-
public $deleted = 'n';
106+
/** @var int $deleted The deleted flag. */
107+
public $deleted = null;
108108

109109
/** @var mixed $extradata Any custom data for the question type. */
110110
public $extradata = '';
@@ -577,7 +577,7 @@ public function response_complete($responsedata) {
577577
// If $responsedata is webform data, check that its not empty.
578578
$answered = isset($responsedata->{'q'.$this->id}) && ($responsedata->{'q'.$this->id} != '');
579579
}
580-
return !($this->required() && ($this->deleted == 'n') && !$answered);
580+
return !($this->required() && (empty($this->deleted)) && !$answered);
581581
}
582582

583583
/**
@@ -642,8 +642,8 @@ public function add($questionrecord, array $choicerecords = null, $calcposition
642642
// Set the position to the end.
643643
$sql = 'SELECT MAX(position) as maxpos '.
644644
'FROM {questionnaire_question} '.
645-
'WHERE surveyid = ? AND deleted = ?';
646-
$params = ['surveyid' => $questionrecord->surveyid, 'deleted' => 'n'];
645+
'WHERE surveyid = ? AND deleted IS NULL';
646+
$params = ['surveyid' => $questionrecord->surveyid];
647647
if ($record = $DB->get_record_sql($sql, $params)) {
648648
$questionrecord->position = $record->maxpos + 1;
649649
} else {

classes/questions_form.php

Lines changed: 77 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ public function definition() {
5050

5151
$sid = $questionnaire->survey->id;
5252
$mform =& $this->_form;
53+
$qidrestore = optional_param(QUESTIONNAIRE_RESTORE_PARAM, 0, PARAM_INT);
5354

5455
$mform->addElement('header', 'questionhdr', get_string('addquestions', 'questionnaire'));
5556
$mform->addHelpButton('questionhdr', 'questiontypes', 'questionnaire');
@@ -138,7 +139,7 @@ public function definition() {
138139

139140
// No page break in first position!
140141
if ($tid == QUESPAGEBREAK && $pos == 1) {
141-
$DB->set_field('questionnaire_question', 'deleted', 'y', ['id' => $qid, 'surveyid' => $sid]);
142+
$DB->set_field('questionnaire_question', 'deleted', time(), ['id' => $qid, 'surveyid' => $sid]);
142143
if ($records = $DB->get_records_select('questionnaire_question', $select, null, 'position ASC')) {
143144
foreach ($records as $record) {
144145
$DB->set_field('questionnaire_question', 'position', $record->position - 1, array('id' => $record->id));
@@ -172,7 +173,12 @@ public function definition() {
172173
// Begin div qn-container with indent if questionnaire has child.
173174
$mform->addElement('html', '<div class="qn-container qn-indent">');
174175
} else {
175-
$mform->addElement('html', '<div class="qn-container">'); // Begin div qn-container.
176+
$containerclass = "qn-container";
177+
if (isset($qidrestore) && $qidrestore == $question->id) {
178+
$containerclass .= " restored-question";
179+
}
180+
// Begin div qn-container.
181+
$mform->addElement('html', "<div class='$containerclass'>");
176182
}
177183

178184
$mextra = array('value' => $question->id,
@@ -220,14 +226,15 @@ public function definition() {
220226
// Do not allow moving or deleting a page break if immediately followed by a child question
221227
// or immediately preceded by a question with a dependency and followed by a non-dependent question.
222228
if ($tid == QUESPAGEBREAK) {
223-
if ($nextquestion = $DB->get_record('questionnaire_question',
224-
['surveyid' => $sid, 'position' => $pos + 1, 'deleted' => 'n'], 'id, name, content') ) {
225-
229+
$select = 'surveyid = ? AND position = ? AND deleted IS NULL';
230+
$nextquestion = $DB->get_record_select('questionnaire_question', $select,
231+
[$sid, $pos + 1], 'id, name, content');
232+
if ($nextquestion) {
226233
$nextquestiondependencies = $DB->get_records('questionnaire_dependency',
227234
['questionid' => $nextquestion->id , 'surveyid' => $sid], 'id ASC');
228235

229-
if ($previousquestion = $DB->get_record('questionnaire_question',
230-
['surveyid' => $sid, 'position' => $pos - 1, 'deleted' => 'n'], 'id, name, content')) {
236+
if ($previousquestion = $DB->get_record_select('questionnaire_question', $select,
237+
[$sid, $pos - 1], 'id, name, content')) {
231238

232239
$previousquestiondependencies = $DB->get_records('questionnaire_dependency',
233240
['questionid' => $previousquestion->id , 'surveyid' => $sid], 'id ASC');
@@ -359,6 +366,66 @@ public function definition() {
359366
}
360367
}
361368

369+
// Question deletion area.
370+
$mform->addElement('header', 'deletionq', get_string('deletionquetions', 'questionnaire'));
371+
$mform->addHelpButton('deletionq', 'deletionquetions', 'questionnaire');
372+
$mform->addElement('html', '<div class="qcontainer">');
373+
if (isset($questionnaire->deletequestions)) {
374+
$restoreimg = $questionnaire->renderer->image_url('i/up');
375+
$deleteimg = $questionnaire->renderer->image_url('t/delete');
376+
$rangetimecrontask = questionnaire_get_range_time_permanently();
377+
foreach ($questionnaire->deletequestions as $deletequestion) {
378+
$delquestiongroup = [];
379+
// Preparing deleted time to display time permanently question.
380+
$timedeleted = $deletequestion->deleted ?? "";
381+
if ($rangetimecrontask == 0) {
382+
$timedeleted = get_string('recylebindisabled', 'questionnaire');
383+
} else {
384+
if (!empty($timedeleted)) {
385+
$timedeleted = get_string('timedeletednext7days', 'questionnaire',
386+
date("D j M, Y", $timedeleted + $rangetimecrontask));
387+
}
388+
}
389+
$qtypeandname = [];
390+
$qtypeandname['name'] = $deletequestion->name;
391+
$qtypeandname['type'] = questionnaire_get_type($deletequestion->type_id);
392+
393+
$content = format_text(
394+
file_rewrite_pluginfile_urls($deletequestion->content, 'pluginfile.php',
395+
$deletequestion->context->id,
396+
'mod_questionnaire', 'question', $deletequestion->id),
397+
FORMAT_HTML, ['noclean' => true]
398+
);
399+
400+
$qnumber = '<div class="qn-info"><h2 class="qn-number">NA</h2></div>';
401+
$restorextra = [
402+
'value' => $deletequestion->id,
403+
'alt' => get_string('restorebutton', 'questionnaire'),
404+
'title' => get_string('restorebutton', 'questionnaire'),
405+
];
406+
$deleleextra = [
407+
'value' => $deletequestion->id,
408+
'alt' => get_string('deletepermanentlybutton', 'questionnaire'),
409+
'title' => get_string('deletepermanentlybutton', 'questionnaire')
410+
];
411+
$mform->addElement('html', '<div class="qn-container">'); // Begin div qn-container.
412+
$delquestiongroup[] =& $mform->createElement('static', 'opentag_' . $deletequestion->id, '', '');
413+
$delquestiongroup[] =& $mform->createElement('image', 'restorebutton[' . $deletequestion->id . ']',
414+
$restoreimg, $restorextra);
415+
$delquestiongroup[] =& $mform->createElement('image', 'deletebutton[' . $deletequestion->id . ']',
416+
$deleteimg, $deleleextra);
417+
$delquestiongroup[] =& $mform->createElement('static', 'closetag_' . $deletequestion->id, '', '');
418+
$delquestiongroup[] =& $mform->createElement('static', 'qinfo_' . $deletequestion->id, '',
419+
get_string('questiontypeandname', 'questionnaire', $qtypeandname));
420+
$delquestiongroup[] =& $mform->createElement('static', 'qinfo_' . $deletequestion->id,
421+
'', $timedeleted);
422+
$mform->addGroup($delquestiongroup, 'delquestiongroup', '', '&nbsp;', false);
423+
$mform->addElement('static', 'qcontent_'.$deletequestion->id, '',
424+
$qnumber.'<div class="qn-question">'.$content.'</div>');
425+
$mform->addElement('html', '</div>'); // End div qn-container.
426+
}
427+
}
428+
362429
if ($this->moveq) {
363430
$mform->addElement('hidden', 'moveq', $this->moveq);
364431
}
@@ -373,6 +440,9 @@ public function definition() {
373440
$mform->setType('moveq', PARAM_RAW);
374441

375442
$mform->addElement('html', '</div>');
443+
$mform->setExpanded('questionhdr');
444+
$mform->setExpanded('manageq');
445+
$mform->setExpanded('deletionq');
376446
}
377447

378448
/**

classes/search/question.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@ public function get_document($record, $options = []) {
7474

7575
// Because there is no database agnostic way to combine all of the possible question content data into one record in
7676
// get_recordset_by_timestamp, I need to grab it all now and add it to the document.
77-
$recordset = $DB->get_recordset('questionnaire_question', ['surveyid' => $record->sid, 'deleted' => 'n'],
78-
'id', 'id,content');
77+
$recordset = $DB->get_recordset_select('questionnaire_question',
78+
'surveyid = ? AND deleted IS NULL', [$record->sid], 'id', 'id,content');
7979

8080
// If no question data, don't index this document.
8181
if (empty($recordset)) {

classes/task/cron_task.php

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
// This file is part of Moodle - http://moodle.org/
3+
//
4+
// Moodle is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// Moodle is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU General Public License
15+
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16+
17+
namespace mod_questionnaire\task;
18+
defined('MOODLE_INTERNAL') || die();
19+
20+
require_once($CFG->dirroot.'/mod/questionnaire/questionnaire.class.php');
21+
/**
22+
* A schedule task for mod_questionnaire cron.
23+
*
24+
* @package mod_questionnaire
25+
* @copyright 2022 The Open University
26+
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27+
*/
28+
class cron_task extends \core\task\scheduled_task {
29+
/**
30+
* Get a descriptive name for this task (shown to admins).
31+
*
32+
* @return string
33+
*/
34+
public function get_name() {
35+
return get_string('cleanrecylebin', 'mod_questionnaire');
36+
}
37+
38+
/**
39+
* Run mod_questionnaire cron.
40+
*/
41+
public function execute() {
42+
global $DB;
43+
$rangetimecrontask = questionnaire_get_range_time_permanently();
44+
$sql = "SELECT *
45+
FROM {questionnaire_question}
46+
WHERE deleted IS NOT NULL
47+
AND deleted < ?";
48+
if ($deletequestions = $DB->get_records_sql($sql, [time() - $rangetimecrontask])) {
49+
foreach ($deletequestions as $question) {
50+
questionnaire_delete_permanently_questions($question->id, $question->surveyid);
51+
}
52+
}
53+
}
54+
}

db/install.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@
7676
<FIELD NAME="position" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
7777
<FIELD NAME="content" TYPE="text" NOTNULL="true" SEQUENCE="false"/>
7878
<FIELD NAME="required" TYPE="char" LENGTH="1" NOTNULL="true" DEFAULT="n" SEQUENCE="false"/>
79-
<FIELD NAME="deleted" TYPE="char" LENGTH="1" NOTNULL="true" DEFAULT="n" SEQUENCE="false"/>
79+
<FIELD NAME="deleted" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="The timestamp record last deleted."/>
8080
<FIELD NAME="extradata" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="If a question needs more than the standard fields provided, use this field."/>
8181
</FIELDS>
8282
<KEYS>

db/tasks.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,14 @@
3636
'day' => '*',
3737
'month' => '*',
3838
'dayofweek' => '*'
39-
)
39+
),
40+
[
41+
'classname' => 'mod_questionnaire\task\cron_task',
42+
'blocking' => 0,
43+
'minute' => '*',
44+
'hour' => '*',
45+
'day' => '*/7',
46+
'month' => '*',
47+
'dayofweek' => '*'
48+
],
4049
);

db/upgrade.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,6 +1002,35 @@ function xmldb_questionnaire_upgrade($oldversion=0) {
10021002
upgrade_mod_savepoint(true, 2022121600.02, 'questionnaire');
10031003
}
10041004

1005+
if ($oldversion < 2024060300.00) {
1006+
$table = new xmldb_table('questionnaire_question');
1007+
$index = new xmldb_index('quest_question_sididx', XMLDB_INDEX_NOTUNIQUE, ['surveyid', 'deleted']);
1008+
if ($dbman->index_exists($table, $index)) {
1009+
$dbman->drop_index($table, $index);
1010+
}
1011+
$field = new xmldb_field('deleted', XMLDB_TYPE_CHAR, '10', XMLDB_UNSIGNED, null, null, null, 'required');
1012+
if ($dbman->field_exists($table, $field)) {
1013+
$dbman->change_field_type($table, $field);
1014+
}
1015+
unset($field);
1016+
1017+
$field = new xmldb_field('deleted', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, null, 'required');
1018+
if ($dbman->field_exists($table, $field)) {
1019+
$sql = "UPDATE {questionnaire_question}
1020+
SET deleted = ?
1021+
WHERE deleted = 'y'";
1022+
$DB->execute($sql, [time()]);
1023+
$sql = "UPDATE {questionnaire_question}
1024+
SET deleted = null
1025+
WHERE deleted = 'n'";
1026+
$DB->execute($sql);
1027+
$dbman->change_field_type($table, $field);
1028+
}
1029+
unset($field);
1030+
// Questionnaire savepoint reached.
1031+
upgrade_mod_savepoint(true, 2024060300.00, 'questionnaire');
1032+
}
1033+
10051034
return true;
10061035
}
10071036

0 commit comments

Comments
 (0)