PageRenderTime 53ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/mod/lesson/pagetypes/essay.php

https://bitbucket.org/moodle/moodle
PHP | 441 lines | 338 code | 49 blank | 54 comment | 43 complexity | 9874f7a1ee2baf233ffd60b603cb4205 MD5 | raw file
Possible License(s): Apache-2.0, LGPL-2.1, BSD-3-Clause, MIT, GPL-3.0
  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. * Essay
  18. *
  19. * @package mod_lesson
  20. * @copyright 2009 Sam Hemelryk
  21. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  22. **/
  23. defined('MOODLE_INTERNAL') || die();
  24. /** Essay question type */
  25. define("LESSON_PAGE_ESSAY", "10");
  26. class lesson_page_type_essay extends lesson_page {
  27. protected $type = lesson_page::TYPE_QUESTION;
  28. protected $typeidstring = 'essay';
  29. protected $typeid = LESSON_PAGE_ESSAY;
  30. protected $string = null;
  31. public function get_typeid() {
  32. return $this->typeid;
  33. }
  34. public function get_typestring() {
  35. if ($this->string===null) {
  36. $this->string = get_string($this->typeidstring, 'lesson');
  37. }
  38. return $this->string;
  39. }
  40. public function get_idstring() {
  41. return $this->typeidstring;
  42. }
  43. /**
  44. * Unserialize attempt useranswer and add missing responseformat if needed
  45. * for compatibility with old records.
  46. *
  47. * @param string $useranswer serialized object
  48. * @return object
  49. */
  50. static public function extract_useranswer($useranswer) {
  51. $essayinfo = unserialize($useranswer);
  52. if (!isset($essayinfo->responseformat)) {
  53. $essayinfo->response = text_to_html($essayinfo->response, false, false);
  54. $essayinfo->responseformat = FORMAT_HTML;
  55. }
  56. return $essayinfo;
  57. }
  58. public function display($renderer, $attempt) {
  59. global $PAGE, $CFG, $USER;
  60. $context = context_module::instance($PAGE->cm->id);
  61. $options = array(
  62. 'contents' => $this->get_contents(),
  63. 'lessonid' => $this->lesson->id,
  64. 'attemptid' => $attempt ? $attempt->id : null,
  65. 'editoroptions' => array(
  66. 'maxbytes' => $PAGE->course->maxbytes,
  67. 'context' => $context,
  68. 'noclean' => true,
  69. 'maxfiles' => EDITOR_UNLIMITED_FILES,
  70. 'enable_filemanagement' => false
  71. )
  72. );
  73. $mform = new lesson_display_answer_form_essay($CFG->wwwroot.'/mod/lesson/continue.php', $options);
  74. $data = new stdClass;
  75. $data->id = $PAGE->cm->id;
  76. $data->pageid = $this->properties->id;
  77. if (isset($USER->modattempts[$this->lesson->id])) {
  78. $essayinfo = self::extract_useranswer($attempt->useranswer);
  79. $data->answer = $essayinfo->answer;
  80. }
  81. $data = file_prepare_standard_editor($data, 'answer', $options['editoroptions'],
  82. $context, 'mod_lesson', 'essay_answers');
  83. $mform->set_data($data);
  84. // Trigger an event question viewed.
  85. $eventparams = array(
  86. 'context' => context_module::instance($PAGE->cm->id),
  87. 'objectid' => $this->properties->id,
  88. 'other' => array(
  89. 'pagetype' => $this->get_typestring()
  90. )
  91. );
  92. $event = \mod_lesson\event\question_viewed::create($eventparams);
  93. $event->trigger();
  94. return $mform->display();
  95. }
  96. public function create_answers($properties) {
  97. global $DB;
  98. // now add the answers
  99. $newanswer = new stdClass;
  100. $newanswer->lessonid = $this->lesson->id;
  101. $newanswer->pageid = $this->properties->id;
  102. $newanswer->timecreated = $this->properties->timecreated;
  103. if (isset($properties->jumpto[0])) {
  104. $newanswer->jumpto = $properties->jumpto[0];
  105. }
  106. if (isset($properties->score[0])) {
  107. $newanswer->score = $properties->score[0];
  108. }
  109. $newanswer->id = $DB->insert_record("lesson_answers", $newanswer);
  110. $answers = array($newanswer->id => new lesson_page_answer($newanswer));
  111. $this->answers = $answers;
  112. return $answers;
  113. }
  114. /**
  115. * Overridden function
  116. *
  117. * @param object $attempt
  118. * @param object $result
  119. * @return array
  120. */
  121. public function on_after_write_attempt($attempt, $result) {
  122. global $PAGE;
  123. if ($formdata = $result->postdata) {
  124. // Save any linked files if we are using an editor.
  125. $editoroptions = array(
  126. 'maxbytes' => $PAGE->course->maxbytes,
  127. 'context' => context_module::instance($PAGE->cm->id),
  128. 'noclean' => true, 'maxfiles' => EDITOR_UNLIMITED_FILES,
  129. 'enable_filemanagement' => false,
  130. );
  131. $formdata = file_postupdate_standard_editor($formdata, 'answer', $editoroptions,
  132. $editoroptions['context'], 'mod_lesson', 'essay_answers', $attempt->id);
  133. // Update the student response to have the modified link.
  134. $useranswer = unserialize($attempt->useranswer);
  135. $useranswer->answer = $formdata->answer;
  136. $useranswer->answerformat = $formdata->answerformat;
  137. $attempt->useranswer = serialize($useranswer);
  138. $result->studentanswer = $formdata->answer;
  139. $result->studentanswerformat = $formdata->answerformat;
  140. return [$attempt, $result];
  141. }
  142. return parent::on_after_write_attempt($attempt, $result);
  143. }
  144. /**
  145. * Custom formats the answer to display
  146. *
  147. * @param string $answer
  148. * @param context $context
  149. * @param int $answerformat
  150. * @param array $options Optional param for additional options.
  151. * @return string Returns formatted string
  152. */
  153. public function format_answer($answer, $context, $answerformat, $options = []) {
  154. $answer = file_rewrite_pluginfile_urls($answer, 'pluginfile.php', $context->id,
  155. 'mod_lesson', 'essay_answers', $options->attemptid);
  156. return parent::format_answer($answer, $context, $answerformat, $options);
  157. }
  158. public function check_answer() {
  159. global $PAGE, $CFG;
  160. $result = parent::check_answer();
  161. $result->isessayquestion = true;
  162. $context = context_module::instance($PAGE->cm->id);
  163. $options = array(
  164. 'contents' => $this->get_contents(),
  165. 'editoroptions' => array(
  166. 'maxbytes' => $PAGE->course->maxbytes,
  167. 'context' => $context,
  168. 'noclean' => true,
  169. 'maxfiles' => EDITOR_UNLIMITED_FILES,
  170. 'enable_filemanagement' => false,
  171. )
  172. );
  173. $mform = new lesson_display_answer_form_essay($CFG->wwwroot.'/mod/lesson/continue.php', $options);
  174. $data = $mform->get_data();
  175. require_sesskey();
  176. if (!$data) {
  177. $result->inmediatejump = true;
  178. $result->newpageid = $this->properties->id;
  179. return $result;
  180. }
  181. if (is_array($data->answer_editor) && strlen($data->answer_editor['text'])) {
  182. $studentanswer = $data->answer_editor['text']; // Will be reset later.
  183. $studentanswerformat = $data->answer_editor['format']; // Will be reset later.
  184. } else {
  185. $studentanswer = isset($data->answer) ? $data->answer : '';
  186. $studentanswerformat = FORMAT_HTML;
  187. }
  188. if (trim($studentanswer) === '') {
  189. $result->noanswer = true;
  190. return $result;
  191. }
  192. $answers = $this->get_answers();
  193. foreach ($answers as $answer) {
  194. $result->answerid = $answer->id;
  195. $result->newpageid = $answer->jumpto;
  196. }
  197. $userresponse = new stdClass;
  198. $userresponse->sent=0;
  199. $userresponse->graded = 0;
  200. $userresponse->score = 0;
  201. $userresponse->answer = $studentanswer;
  202. $userresponse->answerformat = $studentanswerformat;
  203. $userresponse->response = '';
  204. $userresponse->responseformat = FORMAT_HTML;
  205. $result->userresponse = serialize($userresponse);
  206. $result->studentanswerformat = $studentanswerformat;
  207. $result->studentanswer = $studentanswer;
  208. $result->postdata = $data;
  209. return $result;
  210. }
  211. public function update($properties, $context = null, $maxbytes = null) {
  212. global $DB, $PAGE;
  213. $answers = $this->get_answers();
  214. $properties->id = $this->properties->id;
  215. $properties->lessonid = $this->lesson->id;
  216. $properties->timemodified = time();
  217. $properties = file_postupdate_standard_editor($properties, 'contents', array('noclean'=>true, 'maxfiles'=>EDITOR_UNLIMITED_FILES, 'maxbytes'=>$PAGE->course->maxbytes), context_module::instance($PAGE->cm->id), 'mod_lesson', 'page_contents', $properties->id);
  218. $DB->update_record("lesson_pages", $properties);
  219. // Trigger an event: page updated.
  220. \mod_lesson\event\page_updated::create_from_lesson_page($this, $context)->trigger();
  221. if (!array_key_exists(0, $this->answers)) {
  222. $this->answers[0] = new stdClass;
  223. $this->answers[0]->lessonid = $this->lesson->id;
  224. $this->answers[0]->pageid = $this->id;
  225. $this->answers[0]->timecreated = $this->timecreated;
  226. }
  227. if (isset($properties->jumpto[0])) {
  228. $this->answers[0]->jumpto = $properties->jumpto[0];
  229. }
  230. if (isset($properties->score[0])) {
  231. $this->answers[0]->score = $properties->score[0];
  232. }
  233. if (!isset($this->answers[0]->id)) {
  234. $this->answers[0]->id = $DB->insert_record("lesson_answers", $this->answers[0]);
  235. } else {
  236. $DB->update_record("lesson_answers", $this->answers[0]->properties());
  237. }
  238. return true;
  239. }
  240. public function stats(array &$pagestats, $tries) {
  241. $temp = $this->lesson->get_last_attempt($tries);
  242. $essayinfo = self::extract_useranswer($temp->useranswer);
  243. if ($essayinfo->graded) {
  244. if (isset($pagestats[$temp->pageid])) {
  245. $essaystats = $pagestats[$temp->pageid];
  246. $essaystats->totalscore += $essayinfo->score;
  247. $essaystats->total++;
  248. $pagestats[$temp->pageid] = $essaystats;
  249. } else {
  250. $essaystats = new stdClass();
  251. $essaystats->totalscore = $essayinfo->score;
  252. $essaystats->total = 1;
  253. $pagestats[$temp->pageid] = $essaystats;
  254. }
  255. }
  256. return true;
  257. }
  258. public function report_answers($answerpage, $answerdata, $useranswer, $pagestats, &$i, &$n) {
  259. global $PAGE, $DB;
  260. $formattextdefoptions = new stdClass();
  261. $formattextdefoptions->noclean = true;
  262. $formattextdefoptions->para = false;
  263. $formattextdefoptions->context = $answerpage->context;
  264. $answers = $this->get_answers();
  265. $context = context_module::instance($PAGE->cm->id);
  266. foreach ($answers as $answer) {
  267. $hasattempts = $DB->record_exists('lesson_attempts', ['answerid' => $answer->id]);
  268. if ($useranswer != null) {
  269. $essayinfo = self::extract_useranswer($useranswer->useranswer);
  270. $essayinfo->answer = file_rewrite_pluginfile_urls($essayinfo->answer, 'pluginfile.php',
  271. $context->id, 'mod_lesson', 'essay_answers', $useranswer->id);
  272. if ($essayinfo->response == null) {
  273. $answerdata->response = get_string("nocommentyet", "lesson");
  274. } else {
  275. $essayinfo->response = file_rewrite_pluginfile_urls($essayinfo->response, 'pluginfile.php',
  276. $answerpage->context->id, 'mod_lesson', 'essay_responses', $useranswer->id);
  277. $answerdata->response = format_text($essayinfo->response, $essayinfo->responseformat, $formattextdefoptions);
  278. }
  279. if (isset($pagestats[$this->properties->id])) {
  280. $percent = $pagestats[$this->properties->id]->totalscore / $pagestats[$this->properties->id]->total * 100;
  281. $percent = round($percent, 2);
  282. $percent = get_string("averagescore", "lesson").": ". $percent ."%";
  283. } else {
  284. // dont think this should ever be reached....
  285. $percent = get_string("nooneansweredthisquestion", "lesson");
  286. }
  287. if ($essayinfo->graded) {
  288. if ($this->lesson->custom) {
  289. $answerdata->score = get_string("pointsearned", "lesson").": " . $essayinfo->score;
  290. } elseif ($essayinfo->score) {
  291. $answerdata->score = get_string("receivedcredit", "lesson");
  292. } else {
  293. $answerdata->score = get_string("didnotreceivecredit", "lesson");
  294. }
  295. } else {
  296. $answerdata->score = get_string("havenotgradedyet", "lesson");
  297. }
  298. } else {
  299. $essayinfo = new stdClass();
  300. if ($hasattempts && has_capability('mod/lesson:grade', $answerpage->context)) {
  301. $essayinfo->answer = html_writer::link(new moodle_url("/mod/lesson/essay.php",
  302. ['id' => $PAGE->cm->id]), get_string("viewessayanswers", "lesson"));
  303. } else {
  304. $essayinfo->answer = "";
  305. }
  306. $essayinfo->answerformat = null;
  307. }
  308. // The essay question has been graded.
  309. if (isset($pagestats[$this->properties->id])) {
  310. $avescore = $pagestats[$this->properties->id]->totalscore / $pagestats[$this->properties->id]->total;
  311. $avescore = round($avescore, 2);
  312. $avescore = get_string("averagescore", "lesson").": ". $avescore ;
  313. } else {
  314. $avescore = $hasattempts ? get_string("essaynotgradedyet", "lesson") :
  315. get_string("nooneansweredthisquestion", "lesson");
  316. }
  317. // This is the student's answer so it should be cleaned.
  318. $answerdata->answers[] = array(format_text($essayinfo->answer, $essayinfo->answerformat,
  319. array('para' => true, 'context' => $answerpage->context)), $avescore);
  320. $answerpage->answerdata = $answerdata;
  321. }
  322. return $answerpage;
  323. }
  324. public function is_unanswered($nretakes) {
  325. global $DB, $USER;
  326. if (!$DB->count_records("lesson_attempts", array('pageid'=>$this->properties->id, 'userid'=>$USER->id, 'retry'=>$nretakes))) {
  327. return true;
  328. }
  329. return false;
  330. }
  331. public function requires_manual_grading() {
  332. return true;
  333. }
  334. public function get_earnedscore($answers, $attempt) {
  335. $essayinfo = self::extract_useranswer($attempt->useranswer);
  336. return $essayinfo->score;
  337. }
  338. }
  339. class lesson_add_page_form_essay extends lesson_add_page_form_base {
  340. public $qtype = 'essay';
  341. public $qtypestring = 'essay';
  342. public function custom_definition() {
  343. $this->add_jumpto(0);
  344. $this->add_score(0, null, 1);
  345. }
  346. }
  347. class lesson_display_answer_form_essay extends moodleform {
  348. public function definition() {
  349. global $USER, $OUTPUT;
  350. $mform = $this->_form;
  351. $contents = $this->_customdata['contents'];
  352. $editoroptions = $this->_customdata['editoroptions'];
  353. $hasattempt = false;
  354. $attrs = '';
  355. $useranswer = '';
  356. $useranswerraw = '';
  357. if (isset($this->_customdata['lessonid'])) {
  358. $lessonid = $this->_customdata['lessonid'];
  359. if (isset($USER->modattempts[$lessonid]->useranswer) && !empty($USER->modattempts[$lessonid]->useranswer)) {
  360. $attrs = array('disabled' => 'disabled');
  361. $hasattempt = true;
  362. $useranswertemp = lesson_page_type_essay::extract_useranswer($USER->modattempts[$lessonid]->useranswer);
  363. $useranswer = htmlspecialchars_decode($useranswertemp->answer, ENT_QUOTES);
  364. $useranswerraw = $useranswertemp->answer;
  365. }
  366. }
  367. // Disable shortforms.
  368. $mform->setDisableShortforms();
  369. $mform->addElement('header', 'pageheader');
  370. $mform->addElement('html', $OUTPUT->container($contents, 'contents'));
  371. $options = new stdClass;
  372. $options->para = false;
  373. $options->noclean = true;
  374. $mform->addElement('hidden', 'id');
  375. $mform->setType('id', PARAM_INT);
  376. $mform->addElement('hidden', 'pageid');
  377. $mform->setType('pageid', PARAM_INT);
  378. if ($hasattempt) {
  379. $mform->addElement('hidden', 'answer', $useranswerraw);
  380. $mform->setType('answer', PARAM_RAW);
  381. $mform->addElement('html', $OUTPUT->container(get_string('youranswer', 'lesson'), 'youranswer'));
  382. $useranswer = file_rewrite_pluginfile_urls($useranswer, 'pluginfile.php', $editoroptions['context']->id,
  383. 'mod_lesson', 'essay_answers', $this->_customdata['attemptid']);
  384. $mform->addElement('html', $OUTPUT->container($useranswer, 'reviewessay'));
  385. $this->add_action_buttons(null, get_string("nextpage", "lesson"));
  386. } else {
  387. $mform->addElement('editor', 'answer_editor', get_string('youranswer', 'lesson'), null, $editoroptions);
  388. $mform->setType('answer_editor', PARAM_RAW);
  389. $this->add_action_buttons(null, get_string("submit", "lesson"));
  390. }
  391. }
  392. }