PageRenderTime 52ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 2ms

/mod/assign/locallib.php

https://bitbucket.org/synergylearning/campusconnect
PHP | 7217 lines | 4865 code | 882 blank | 1470 comment | 964 complexity | 25f2d5a4f4869fd9c988edf0db8aaadd MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, LGPL-3.0, GPL-3.0, LGPL-2.1, Apache-2.0, BSD-3-Clause, AGPL-3.0

Large files files are truncated, but you can click here to view the full file

  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. * This file contains the definition for the class assignment
  18. *
  19. * This class provides all the functionality for the new assign module.
  20. *
  21. * @package mod_assign
  22. * @copyright 2012 NetSpot {@link http://www.netspot.com.au}
  23. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24. */
  25. defined('MOODLE_INTERNAL') || die();
  26. // Assignment submission statuses.
  27. define('ASSIGN_SUBMISSION_STATUS_REOPENED', 'reopened');
  28. define('ASSIGN_SUBMISSION_STATUS_DRAFT', 'draft');
  29. define('ASSIGN_SUBMISSION_STATUS_SUBMITTED', 'submitted');
  30. // Search filters for grading page.
  31. define('ASSIGN_FILTER_SUBMITTED', 'submitted');
  32. define('ASSIGN_FILTER_SINGLE_USER', 'singleuser');
  33. define('ASSIGN_FILTER_REQUIRE_GRADING', 'require_grading');
  34. // Reopen attempt methods.
  35. define('ASSIGN_ATTEMPT_REOPEN_METHOD_NONE', 'none');
  36. define('ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL', 'manual');
  37. define('ASSIGN_ATTEMPT_REOPEN_METHOD_UNTILPASS', 'untilpass');
  38. // Special value means allow unlimited attempts.
  39. define('ASSIGN_UNLIMITED_ATTEMPTS', -1);
  40. // Marking workflow states.
  41. define('ASSIGN_MARKING_WORKFLOW_STATE_NOTMARKED', 'notmarked');
  42. define('ASSIGN_MARKING_WORKFLOW_STATE_INMARKING', 'inmarking');
  43. define('ASSIGN_MARKING_WORKFLOW_STATE_READYFORREVIEW', 'readyforreview');
  44. define('ASSIGN_MARKING_WORKFLOW_STATE_INREVIEW', 'inreview');
  45. define('ASSIGN_MARKING_WORKFLOW_STATE_READYFORRELEASE', 'readyforrelease');
  46. define('ASSIGN_MARKING_WORKFLOW_STATE_RELEASED', 'released');
  47. require_once($CFG->libdir . '/accesslib.php');
  48. require_once($CFG->libdir . '/formslib.php');
  49. require_once($CFG->dirroot . '/repository/lib.php');
  50. require_once($CFG->dirroot . '/mod/assign/mod_form.php');
  51. require_once($CFG->libdir . '/gradelib.php');
  52. require_once($CFG->dirroot . '/grade/grading/lib.php');
  53. require_once($CFG->dirroot . '/mod/assign/feedbackplugin.php');
  54. require_once($CFG->dirroot . '/mod/assign/submissionplugin.php');
  55. require_once($CFG->dirroot . '/mod/assign/renderable.php');
  56. require_once($CFG->dirroot . '/mod/assign/gradingtable.php');
  57. require_once($CFG->libdir . '/eventslib.php');
  58. require_once($CFG->libdir . '/portfolio/caller.php');
  59. /**
  60. * Standard base class for mod_assign (assignment types).
  61. *
  62. * @package mod_assign
  63. * @copyright 2012 NetSpot {@link http://www.netspot.com.au}
  64. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  65. */
  66. class assign {
  67. /** @var stdClass the assignment record that contains the global settings for this assign instance */
  68. private $instance;
  69. /** @var stdClass the grade_item record for this assign instance's primary grade item. */
  70. private $gradeitem;
  71. /** @var context the context of the course module for this assign instance
  72. * (or just the course if we are creating a new one)
  73. */
  74. private $context;
  75. /** @var stdClass the course this assign instance belongs to */
  76. private $course;
  77. /** @var stdClass the admin config for all assign instances */
  78. private $adminconfig;
  79. /** @var assign_renderer the custom renderer for this module */
  80. private $output;
  81. /** @var stdClass the course module for this assign instance */
  82. private $coursemodule;
  83. /** @var array cache for things like the coursemodule name or the scale menu -
  84. * only lives for a single request.
  85. */
  86. private $cache;
  87. /** @var array list of the installed submission plugins */
  88. private $submissionplugins;
  89. /** @var array list of the installed feedback plugins */
  90. private $feedbackplugins;
  91. /** @var string action to be used to return to this page
  92. * (without repeating any form submissions etc).
  93. */
  94. private $returnaction = 'view';
  95. /** @var array params to be used to return to this page */
  96. private $returnparams = array();
  97. /** @var string modulename prevents excessive calls to get_string */
  98. private static $modulename = null;
  99. /** @var string modulenameplural prevents excessive calls to get_string */
  100. private static $modulenameplural = null;
  101. /** @var array of marking workflow states for the current user */
  102. private $markingworkflowstates = null;
  103. /** @var bool whether to exclude users with inactive enrolment */
  104. private $showonlyactiveenrol = null;
  105. /** @var array cached list of participants for this assignment. The cache key will be group, showactive and the context id */
  106. private $participants = array();
  107. /**
  108. * Constructor for the base assign class.
  109. *
  110. * @param mixed $coursemodulecontext context|null the course module context
  111. * (or the course context if the coursemodule has not been
  112. * created yet).
  113. * @param mixed $coursemodule the current course module if it was already loaded,
  114. * otherwise this class will load one from the context as required.
  115. * @param mixed $course the current course if it was already loaded,
  116. * otherwise this class will load one from the context as required.
  117. */
  118. public function __construct($coursemodulecontext, $coursemodule, $course) {
  119. $this->context = $coursemodulecontext;
  120. $this->coursemodule = $coursemodule;
  121. $this->course = $course;
  122. // Temporary cache only lives for a single request - used to reduce db lookups.
  123. $this->cache = array();
  124. $this->submissionplugins = $this->load_plugins('assignsubmission');
  125. $this->feedbackplugins = $this->load_plugins('assignfeedback');
  126. }
  127. /**
  128. * Set the action and parameters that can be used to return to the current page.
  129. *
  130. * @param string $action The action for the current page
  131. * @param array $params An array of name value pairs which form the parameters
  132. * to return to the current page.
  133. * @return void
  134. */
  135. public function register_return_link($action, $params) {
  136. global $PAGE;
  137. $params['action'] = $action;
  138. $currenturl = $PAGE->url;
  139. $currenturl->params($params);
  140. $PAGE->set_url($currenturl);
  141. }
  142. /**
  143. * Return an action that can be used to get back to the current page.
  144. *
  145. * @return string action
  146. */
  147. public function get_return_action() {
  148. global $PAGE;
  149. $params = $PAGE->url->params();
  150. if (!empty($params['action'])) {
  151. return $params['action'];
  152. }
  153. return '';
  154. }
  155. /**
  156. * Based on the current assignment settings should we display the intro.
  157. *
  158. * @return bool showintro
  159. */
  160. protected function show_intro() {
  161. if ($this->get_instance()->alwaysshowdescription ||
  162. time() > $this->get_instance()->allowsubmissionsfromdate) {
  163. return true;
  164. }
  165. return false;
  166. }
  167. /**
  168. * Return a list of parameters that can be used to get back to the current page.
  169. *
  170. * @return array params
  171. */
  172. public function get_return_params() {
  173. global $PAGE;
  174. $params = $PAGE->url->params();
  175. unset($params['id']);
  176. unset($params['action']);
  177. return $params;
  178. }
  179. /**
  180. * Set the submitted form data.
  181. *
  182. * @param stdClass $data The form data (instance)
  183. */
  184. public function set_instance(stdClass $data) {
  185. $this->instance = $data;
  186. }
  187. /**
  188. * Set the context.
  189. *
  190. * @param context $context The new context
  191. */
  192. public function set_context(context $context) {
  193. $this->context = $context;
  194. }
  195. /**
  196. * Set the course data.
  197. *
  198. * @param stdClass $course The course data
  199. */
  200. public function set_course(stdClass $course) {
  201. $this->course = $course;
  202. }
  203. /**
  204. * Get list of feedback plugins installed.
  205. *
  206. * @return array
  207. */
  208. public function get_feedback_plugins() {
  209. return $this->feedbackplugins;
  210. }
  211. /**
  212. * Get list of submission plugins installed.
  213. *
  214. * @return array
  215. */
  216. public function get_submission_plugins() {
  217. return $this->submissionplugins;
  218. }
  219. /**
  220. * Is blind marking enabled and reveal identities not set yet?
  221. *
  222. * @return bool
  223. */
  224. public function is_blind_marking() {
  225. return $this->get_instance()->blindmarking && !$this->get_instance()->revealidentities;
  226. }
  227. /**
  228. * Does an assignment have submission(s) or grade(s) already?
  229. *
  230. * @return bool
  231. */
  232. public function has_submissions_or_grades() {
  233. $allgrades = $this->count_grades();
  234. $allsubmissions = $this->count_submissions();
  235. if (($allgrades == 0) && ($allsubmissions == 0)) {
  236. return false;
  237. }
  238. return true;
  239. }
  240. /**
  241. * Get a specific submission plugin by its type.
  242. *
  243. * @param string $subtype assignsubmission | assignfeedback
  244. * @param string $type
  245. * @return mixed assign_plugin|null
  246. */
  247. public function get_plugin_by_type($subtype, $type) {
  248. $shortsubtype = substr($subtype, strlen('assign'));
  249. $name = $shortsubtype . 'plugins';
  250. if ($name != 'feedbackplugins' && $name != 'submissionplugins') {
  251. return null;
  252. }
  253. $pluginlist = $this->$name;
  254. foreach ($pluginlist as $plugin) {
  255. if ($plugin->get_type() == $type) {
  256. return $plugin;
  257. }
  258. }
  259. return null;
  260. }
  261. /**
  262. * Get a feedback plugin by type.
  263. *
  264. * @param string $type - The type of plugin e.g comments
  265. * @return mixed assign_feedback_plugin|null
  266. */
  267. public function get_feedback_plugin_by_type($type) {
  268. return $this->get_plugin_by_type('assignfeedback', $type);
  269. }
  270. /**
  271. * Get a submission plugin by type.
  272. *
  273. * @param string $type - The type of plugin e.g comments
  274. * @return mixed assign_submission_plugin|null
  275. */
  276. public function get_submission_plugin_by_type($type) {
  277. return $this->get_plugin_by_type('assignsubmission', $type);
  278. }
  279. /**
  280. * Load the plugins from the sub folders under subtype.
  281. *
  282. * @param string $subtype - either submission or feedback
  283. * @return array - The sorted list of plugins
  284. */
  285. protected function load_plugins($subtype) {
  286. global $CFG;
  287. $result = array();
  288. $names = core_component::get_plugin_list($subtype);
  289. foreach ($names as $name => $path) {
  290. if (file_exists($path . '/locallib.php')) {
  291. require_once($path . '/locallib.php');
  292. $shortsubtype = substr($subtype, strlen('assign'));
  293. $pluginclass = 'assign_' . $shortsubtype . '_' . $name;
  294. $plugin = new $pluginclass($this, $name);
  295. if ($plugin instanceof assign_plugin) {
  296. $idx = $plugin->get_sort_order();
  297. while (array_key_exists($idx, $result)) {
  298. $idx +=1;
  299. }
  300. $result[$idx] = $plugin;
  301. }
  302. }
  303. }
  304. ksort($result);
  305. return $result;
  306. }
  307. /**
  308. * Display the assignment, used by view.php
  309. *
  310. * The assignment is displayed differently depending on your role,
  311. * the settings for the assignment and the status of the assignment.
  312. *
  313. * @param string $action The current action if any.
  314. * @return string - The page output.
  315. */
  316. public function view($action='') {
  317. $o = '';
  318. $mform = null;
  319. $notices = array();
  320. $nextpageparams = array();
  321. if (!empty($this->get_course_module()->id)) {
  322. $nextpageparams['id'] = $this->get_course_module()->id;
  323. }
  324. // Handle form submissions first.
  325. if ($action == 'savesubmission') {
  326. $action = 'editsubmission';
  327. if ($this->process_save_submission($mform, $notices)) {
  328. $action = 'redirect';
  329. $nextpageparams['action'] = 'view';
  330. }
  331. } else if ($action == 'editprevioussubmission') {
  332. $action = 'editsubmission';
  333. if ($this->process_copy_previous_attempt($notices)) {
  334. $action = 'redirect';
  335. $nextpageparams['action'] = 'editsubmission';
  336. }
  337. } else if ($action == 'lock') {
  338. $this->process_lock_submission();
  339. $action = 'redirect';
  340. $nextpageparams['action'] = 'grading';
  341. } else if ($action == 'addattempt') {
  342. $this->process_add_attempt(required_param('userid', PARAM_INT));
  343. $action = 'redirect';
  344. $nextpageparams['action'] = 'grading';
  345. } else if ($action == 'reverttodraft') {
  346. $this->process_revert_to_draft();
  347. $action = 'redirect';
  348. $nextpageparams['action'] = 'grading';
  349. } else if ($action == 'unlock') {
  350. $this->process_unlock_submission();
  351. $action = 'redirect';
  352. $nextpageparams['action'] = 'grading';
  353. } else if ($action == 'setbatchmarkingworkflowstate') {
  354. $this->process_set_batch_marking_workflow_state();
  355. $action = 'redirect';
  356. $nextpageparams['action'] = 'grading';
  357. } else if ($action == 'setbatchmarkingallocation') {
  358. $this->process_set_batch_marking_allocation();
  359. $action = 'redirect';
  360. $nextpageparams['action'] = 'grading';
  361. } else if ($action == 'confirmsubmit') {
  362. $action = 'submit';
  363. if ($this->process_submit_for_grading($mform)) {
  364. $action = 'redirect';
  365. $nextpageparams['action'] = 'view';
  366. }
  367. } else if ($action == 'gradingbatchoperation') {
  368. $action = $this->process_grading_batch_operation($mform);
  369. if ($action == 'grading') {
  370. $action = 'redirect';
  371. $nextpageparams['action'] = 'grading';
  372. }
  373. } else if ($action == 'submitgrade') {
  374. if (optional_param('saveandshownext', null, PARAM_RAW)) {
  375. // Save and show next.
  376. $action = 'grade';
  377. if ($this->process_save_grade($mform)) {
  378. $action = 'redirect';
  379. $nextpageparams['action'] = 'grade';
  380. $nextpageparams['rownum'] = optional_param('rownum', 0, PARAM_INT) + 1;
  381. $nextpageparams['useridlistid'] = optional_param('useridlistid', time(), PARAM_INT);
  382. }
  383. } else if (optional_param('nosaveandprevious', null, PARAM_RAW)) {
  384. $action = 'redirect';
  385. $nextpageparams['action'] = 'grade';
  386. $nextpageparams['rownum'] = optional_param('rownum', 0, PARAM_INT) - 1;
  387. $nextpageparams['useridlistid'] = optional_param('useridlistid', time(), PARAM_INT);
  388. } else if (optional_param('nosaveandnext', null, PARAM_RAW)) {
  389. $action = 'redirect';
  390. $nextpageparams['action'] = 'grade';
  391. $nextpageparams['rownum'] = optional_param('rownum', 0, PARAM_INT) + 1;
  392. $nextpageparams['useridlistid'] = optional_param('useridlistid', time(), PARAM_INT);
  393. } else if (optional_param('savegrade', null, PARAM_RAW)) {
  394. // Save changes button.
  395. $action = 'grade';
  396. if ($this->process_save_grade($mform)) {
  397. $action = 'redirect';
  398. $nextpageparams['action'] = 'savegradingresult';
  399. }
  400. } else {
  401. // Cancel button.
  402. $action = 'redirect';
  403. $nextpageparams['action'] = 'grading';
  404. }
  405. } else if ($action == 'quickgrade') {
  406. $message = $this->process_save_quick_grades();
  407. $action = 'quickgradingresult';
  408. } else if ($action == 'saveoptions') {
  409. $this->process_save_grading_options();
  410. $action = 'redirect';
  411. $nextpageparams['action'] = 'grading';
  412. } else if ($action == 'saveextension') {
  413. $action = 'grantextension';
  414. if ($this->process_save_extension($mform)) {
  415. $action = 'redirect';
  416. $nextpageparams['action'] = 'grading';
  417. }
  418. } else if ($action == 'revealidentitiesconfirm') {
  419. $this->process_reveal_identities();
  420. $action = 'redirect';
  421. $nextpageparams['action'] = 'grading';
  422. }
  423. $returnparams = array('rownum'=>optional_param('rownum', 0, PARAM_INT),
  424. 'useridlistid'=>optional_param('useridlistid', 0, PARAM_INT));
  425. $this->register_return_link($action, $returnparams);
  426. // Now show the right view page.
  427. if ($action == 'redirect') {
  428. $nextpageurl = new moodle_url('/mod/assign/view.php', $nextpageparams);
  429. redirect($nextpageurl);
  430. return;
  431. } else if ($action == 'savegradingresult') {
  432. $message = get_string('gradingchangessaved', 'assign');
  433. $o .= $this->view_savegrading_result($message);
  434. } else if ($action == 'quickgradingresult') {
  435. $mform = null;
  436. $o .= $this->view_quickgrading_result($message);
  437. } else if ($action == 'grade') {
  438. $o .= $this->view_single_grade_page($mform);
  439. } else if ($action == 'viewpluginassignfeedback') {
  440. $o .= $this->view_plugin_content('assignfeedback');
  441. } else if ($action == 'viewpluginassignsubmission') {
  442. $o .= $this->view_plugin_content('assignsubmission');
  443. } else if ($action == 'editsubmission') {
  444. $o .= $this->view_edit_submission_page($mform, $notices);
  445. } else if ($action == 'grading') {
  446. $o .= $this->view_grading_page();
  447. } else if ($action == 'downloadall') {
  448. $o .= $this->download_submissions();
  449. } else if ($action == 'submit') {
  450. $o .= $this->check_submit_for_grading($mform);
  451. } else if ($action == 'grantextension') {
  452. $o .= $this->view_grant_extension($mform);
  453. } else if ($action == 'revealidentities') {
  454. $o .= $this->view_reveal_identities_confirm($mform);
  455. } else if ($action == 'plugingradingbatchoperation') {
  456. $o .= $this->view_plugin_grading_batch_operation($mform);
  457. } else if ($action == 'viewpluginpage') {
  458. $o .= $this->view_plugin_page();
  459. } else if ($action == 'viewcourseindex') {
  460. $o .= $this->view_course_index();
  461. } else if ($action == 'viewbatchsetmarkingworkflowstate') {
  462. $o .= $this->view_batch_set_workflow_state($mform);
  463. } else if ($action == 'viewbatchmarkingallocation') {
  464. $o .= $this->view_batch_markingallocation($mform);
  465. } else {
  466. $o .= $this->view_submission_page();
  467. }
  468. return $o;
  469. }
  470. /**
  471. * Add this instance to the database.
  472. *
  473. * @param stdClass $formdata The data submitted from the form
  474. * @param bool $callplugins This is used to skip the plugin code
  475. * when upgrading an old assignment to a new one (the plugins get called manually)
  476. * @return mixed false if an error occurs or the int id of the new instance
  477. */
  478. public function add_instance(stdClass $formdata, $callplugins) {
  479. global $DB;
  480. $err = '';
  481. // Add the database record.
  482. $update = new stdClass();
  483. $update->name = $formdata->name;
  484. $update->timemodified = time();
  485. $update->timecreated = time();
  486. $update->course = $formdata->course;
  487. $update->courseid = $formdata->course;
  488. $update->intro = $formdata->intro;
  489. $update->introformat = $formdata->introformat;
  490. $update->alwaysshowdescription = !empty($formdata->alwaysshowdescription);
  491. $update->submissiondrafts = $formdata->submissiondrafts;
  492. $update->requiresubmissionstatement = $formdata->requiresubmissionstatement;
  493. $update->sendnotifications = $formdata->sendnotifications;
  494. $update->sendlatenotifications = $formdata->sendlatenotifications;
  495. $update->duedate = $formdata->duedate;
  496. $update->cutoffdate = $formdata->cutoffdate;
  497. $update->allowsubmissionsfromdate = $formdata->allowsubmissionsfromdate;
  498. $update->grade = $formdata->grade;
  499. $update->completionsubmit = !empty($formdata->completionsubmit);
  500. $update->teamsubmission = $formdata->teamsubmission;
  501. $update->requireallteammemberssubmit = $formdata->requireallteammemberssubmit;
  502. if (isset($formdata->teamsubmissiongroupingid)) {
  503. $update->teamsubmissiongroupingid = $formdata->teamsubmissiongroupingid;
  504. }
  505. $update->blindmarking = $formdata->blindmarking;
  506. $update->attemptreopenmethod = ASSIGN_ATTEMPT_REOPEN_METHOD_NONE;
  507. if (!empty($formdata->attemptreopenmethod)) {
  508. $update->attemptreopenmethod = $formdata->attemptreopenmethod;
  509. }
  510. if (!empty($formdata->maxattempts)) {
  511. $update->maxattempts = $formdata->maxattempts;
  512. }
  513. $update->markingworkflow = $formdata->markingworkflow;
  514. $update->markingallocation = $formdata->markingallocation;
  515. if (empty($update->markingworkflow)) { // If marking workflow is disabled, make sure allocation is disabled.
  516. $update->markingallocation = 0;
  517. }
  518. $returnid = $DB->insert_record('assign', $update);
  519. $this->instance = $DB->get_record('assign', array('id'=>$returnid), '*', MUST_EXIST);
  520. // Cache the course record.
  521. $this->course = $DB->get_record('course', array('id'=>$formdata->course), '*', MUST_EXIST);
  522. if ($callplugins) {
  523. // Call save_settings hook for submission plugins.
  524. foreach ($this->submissionplugins as $plugin) {
  525. if (!$this->update_plugin_instance($plugin, $formdata)) {
  526. print_error($plugin->get_error());
  527. return false;
  528. }
  529. }
  530. foreach ($this->feedbackplugins as $plugin) {
  531. if (!$this->update_plugin_instance($plugin, $formdata)) {
  532. print_error($plugin->get_error());
  533. return false;
  534. }
  535. }
  536. // In the case of upgrades the coursemodule has not been set,
  537. // so we need to wait before calling these two.
  538. $this->update_calendar($formdata->coursemodule);
  539. $this->update_gradebook(false, $formdata->coursemodule);
  540. }
  541. $update = new stdClass();
  542. $update->id = $this->get_instance()->id;
  543. $update->nosubmissions = (!$this->is_any_submission_plugin_enabled()) ? 1: 0;
  544. $DB->update_record('assign', $update);
  545. return $returnid;
  546. }
  547. /**
  548. * Delete all grades from the gradebook for this assignment.
  549. *
  550. * @return bool
  551. */
  552. protected function delete_grades() {
  553. global $CFG;
  554. $result = grade_update('mod/assign',
  555. $this->get_course()->id,
  556. 'mod',
  557. 'assign',
  558. $this->get_instance()->id,
  559. 0,
  560. null,
  561. array('deleted'=>1));
  562. return $result == GRADE_UPDATE_OK;
  563. }
  564. /**
  565. * Delete this instance from the database.
  566. *
  567. * @return bool false if an error occurs
  568. */
  569. public function delete_instance() {
  570. global $DB;
  571. $result = true;
  572. foreach ($this->submissionplugins as $plugin) {
  573. if (!$plugin->delete_instance()) {
  574. print_error($plugin->get_error());
  575. $result = false;
  576. }
  577. }
  578. foreach ($this->feedbackplugins as $plugin) {
  579. if (!$plugin->delete_instance()) {
  580. print_error($plugin->get_error());
  581. $result = false;
  582. }
  583. }
  584. // Delete files associated with this assignment.
  585. $fs = get_file_storage();
  586. if (! $fs->delete_area_files($this->context->id) ) {
  587. $result = false;
  588. }
  589. // Delete_records will throw an exception if it fails - so no need for error checking here.
  590. $DB->delete_records('assign_submission', array('assignment'=>$this->get_instance()->id));
  591. $DB->delete_records('assign_grades', array('assignment'=>$this->get_instance()->id));
  592. $DB->delete_records('assign_plugin_config', array('assignment'=>$this->get_instance()->id));
  593. // Delete items from the gradebook.
  594. if (! $this->delete_grades()) {
  595. $result = false;
  596. }
  597. // Delete the instance.
  598. $DB->delete_records('assign', array('id'=>$this->get_instance()->id));
  599. return $result;
  600. }
  601. /**
  602. * Actual implementation of the reset course functionality, delete all the
  603. * assignment submissions for course $data->courseid.
  604. *
  605. * @param stdClass $data the data submitted from the reset course.
  606. * @return array status array
  607. */
  608. public function reset_userdata($data) {
  609. global $CFG, $DB;
  610. $componentstr = get_string('modulenameplural', 'assign');
  611. $status = array();
  612. $fs = get_file_storage();
  613. if (!empty($data->reset_assign_submissions)) {
  614. // Delete files associated with this assignment.
  615. foreach ($this->submissionplugins as $plugin) {
  616. $fileareas = array();
  617. $plugincomponent = $plugin->get_subtype() . '_' . $plugin->get_type();
  618. $fileareas = $plugin->get_file_areas();
  619. foreach ($fileareas as $filearea) {
  620. $fs->delete_area_files($this->context->id, $plugincomponent, $filearea);
  621. }
  622. if (!$plugin->delete_instance()) {
  623. $status[] = array('component'=>$componentstr,
  624. 'item'=>get_string('deleteallsubmissions', 'assign'),
  625. 'error'=>$plugin->get_error());
  626. }
  627. }
  628. foreach ($this->feedbackplugins as $plugin) {
  629. $fileareas = array();
  630. $plugincomponent = $plugin->get_subtype() . '_' . $plugin->get_type();
  631. $fileareas = $plugin->get_file_areas();
  632. foreach ($fileareas as $filearea) {
  633. $fs->delete_area_files($this->context->id, $plugincomponent, $filearea);
  634. }
  635. if (!$plugin->delete_instance()) {
  636. $status[] = array('component'=>$componentstr,
  637. 'item'=>get_string('deleteallsubmissions', 'assign'),
  638. 'error'=>$plugin->get_error());
  639. }
  640. }
  641. $assignssql = 'SELECT a.id
  642. FROM {assign} a
  643. WHERE a.course=:course';
  644. $params = array('course'=>$data->courseid);
  645. $DB->delete_records_select('assign_submission', "assignment IN ($assignssql)", $params);
  646. $status[] = array('component'=>$componentstr,
  647. 'item'=>get_string('deleteallsubmissions', 'assign'),
  648. 'error'=>false);
  649. if (!empty($data->reset_gradebook_grades)) {
  650. $DB->delete_records_select('assign_grades', "assignment IN ($assignssql)", $params);
  651. // Remove all grades from gradebook.
  652. require_once($CFG->dirroot.'/mod/assign/lib.php');
  653. assign_reset_gradebook($data->courseid);
  654. }
  655. }
  656. // Updating dates - shift may be negative too.
  657. if ($data->timeshift) {
  658. shift_course_mod_dates('assign',
  659. array('duedate', 'allowsubmissionsfromdate', 'cutoffdate'),
  660. $data->timeshift,
  661. $data->courseid, $this->get_instance()->id);
  662. $status[] = array('component'=>$componentstr,
  663. 'item'=>get_string('datechanged'),
  664. 'error'=>false);
  665. }
  666. return $status;
  667. }
  668. /**
  669. * Update the settings for a single plugin.
  670. *
  671. * @param assign_plugin $plugin The plugin to update
  672. * @param stdClass $formdata The form data
  673. * @return bool false if an error occurs
  674. */
  675. protected function update_plugin_instance(assign_plugin $plugin, stdClass $formdata) {
  676. if ($plugin->is_visible()) {
  677. $enabledname = $plugin->get_subtype() . '_' . $plugin->get_type() . '_enabled';
  678. if (!empty($formdata->$enabledname)) {
  679. $plugin->enable();
  680. if (!$plugin->save_settings($formdata)) {
  681. print_error($plugin->get_error());
  682. return false;
  683. }
  684. } else {
  685. $plugin->disable();
  686. }
  687. }
  688. return true;
  689. }
  690. /**
  691. * Update the gradebook information for this assignment.
  692. *
  693. * @param bool $reset If true, will reset all grades in the gradbook for this assignment
  694. * @param int $coursemoduleid This is required because it might not exist in the database yet
  695. * @return bool
  696. */
  697. public function update_gradebook($reset, $coursemoduleid) {
  698. global $CFG;
  699. require_once($CFG->dirroot.'/mod/assign/lib.php');
  700. $assign = clone $this->get_instance();
  701. $assign->cmidnumber = $coursemoduleid;
  702. $param = null;
  703. if ($reset) {
  704. $param = 'reset';
  705. }
  706. return assign_grade_item_update($assign, $param);
  707. }
  708. /**
  709. * Load and cache the admin config for this module.
  710. *
  711. * @return stdClass the plugin config
  712. */
  713. public function get_admin_config() {
  714. if ($this->adminconfig) {
  715. return $this->adminconfig;
  716. }
  717. $this->adminconfig = get_config('assign');
  718. return $this->adminconfig;
  719. }
  720. /**
  721. * Update the calendar entries for this assignment.
  722. *
  723. * @param int $coursemoduleid - Required to pass this in because it might
  724. * not exist in the database yet.
  725. * @return bool
  726. */
  727. public function update_calendar($coursemoduleid) {
  728. global $DB, $CFG;
  729. require_once($CFG->dirroot.'/calendar/lib.php');
  730. // Special case for add_instance as the coursemodule has not been set yet.
  731. $instance = $this->get_instance();
  732. if ($instance->duedate) {
  733. $event = new stdClass();
  734. $params = array('modulename'=>'assign', 'instance'=>$instance->id);
  735. $event->id = $DB->get_field('event', 'id', $params);
  736. $event->name = $instance->name;
  737. $event->timestart = $instance->duedate;
  738. // Convert the links to pluginfile. It is a bit hacky but at this stage the files
  739. // might not have been saved in the module area yet.
  740. $intro = $instance->intro;
  741. if ($draftid = file_get_submitted_draft_itemid('introeditor')) {
  742. $intro = file_rewrite_urls_to_pluginfile($intro, $draftid);
  743. }
  744. // We need to remove the links to files as the calendar is not ready
  745. // to support module events with file areas.
  746. $intro = strip_pluginfile_content($intro);
  747. if ($this->show_intro()) {
  748. $event->description = array(
  749. 'text' => $intro,
  750. 'format' => $instance->introformat
  751. );
  752. } else {
  753. $event->description = array(
  754. 'text' => '',
  755. 'format' => $instance->introformat
  756. );
  757. }
  758. if ($event->id) {
  759. $calendarevent = calendar_event::load($event->id);
  760. $calendarevent->update($event);
  761. } else {
  762. unset($event->id);
  763. $event->courseid = $instance->course;
  764. $event->groupid = 0;
  765. $event->userid = 0;
  766. $event->modulename = 'assign';
  767. $event->instance = $instance->id;
  768. $event->eventtype = 'due';
  769. $event->timeduration = 0;
  770. calendar_event::create($event);
  771. }
  772. } else {
  773. $DB->delete_records('event', array('modulename'=>'assign', 'instance'=>$instance->id));
  774. }
  775. }
  776. /**
  777. * Update this instance in the database.
  778. *
  779. * @param stdClass $formdata - the data submitted from the form
  780. * @return bool false if an error occurs
  781. */
  782. public function update_instance($formdata) {
  783. global $DB;
  784. $update = new stdClass();
  785. $update->id = $formdata->instance;
  786. $update->name = $formdata->name;
  787. $update->timemodified = time();
  788. $update->course = $formdata->course;
  789. $update->intro = $formdata->intro;
  790. $update->introformat = $formdata->introformat;
  791. $update->alwaysshowdescription = !empty($formdata->alwaysshowdescription);
  792. $update->submissiondrafts = $formdata->submissiondrafts;
  793. $update->requiresubmissionstatement = $formdata->requiresubmissionstatement;
  794. $update->sendnotifications = $formdata->sendnotifications;
  795. $update->sendlatenotifications = $formdata->sendlatenotifications;
  796. $update->duedate = $formdata->duedate;
  797. $update->cutoffdate = $formdata->cutoffdate;
  798. $update->allowsubmissionsfromdate = $formdata->allowsubmissionsfromdate;
  799. $update->grade = $formdata->grade;
  800. if (!empty($formdata->completionunlocked)) {
  801. $update->completionsubmit = !empty($formdata->completionsubmit);
  802. }
  803. $update->teamsubmission = $formdata->teamsubmission;
  804. $update->requireallteammemberssubmit = $formdata->requireallteammemberssubmit;
  805. if (isset($formdata->teamsubmissiongroupingid)) {
  806. $update->teamsubmissiongroupingid = $formdata->teamsubmissiongroupingid;
  807. }
  808. $update->blindmarking = $formdata->blindmarking;
  809. $update->attemptreopenmethod = ASSIGN_ATTEMPT_REOPEN_METHOD_NONE;
  810. if (!empty($formdata->attemptreopenmethod)) {
  811. $update->attemptreopenmethod = $formdata->attemptreopenmethod;
  812. }
  813. if (!empty($formdata->maxattempts)) {
  814. $update->maxattempts = $formdata->maxattempts;
  815. }
  816. $update->markingworkflow = $formdata->markingworkflow;
  817. $update->markingallocation = $formdata->markingallocation;
  818. if (empty($update->markingworkflow)) { // If marking workflow is disabled, make sure allocation is disabled.
  819. $update->markingallocation = 0;
  820. }
  821. $result = $DB->update_record('assign', $update);
  822. $this->instance = $DB->get_record('assign', array('id'=>$update->id), '*', MUST_EXIST);
  823. // Load the assignment so the plugins have access to it.
  824. // Call save_settings hook for submission plugins.
  825. foreach ($this->submissionplugins as $plugin) {
  826. if (!$this->update_plugin_instance($plugin, $formdata)) {
  827. print_error($plugin->get_error());
  828. return false;
  829. }
  830. }
  831. foreach ($this->feedbackplugins as $plugin) {
  832. if (!$this->update_plugin_instance($plugin, $formdata)) {
  833. print_error($plugin->get_error());
  834. return false;
  835. }
  836. }
  837. $this->update_calendar($this->get_course_module()->id);
  838. $this->update_gradebook(false, $this->get_course_module()->id);
  839. $update = new stdClass();
  840. $update->id = $this->get_instance()->id;
  841. $update->nosubmissions = (!$this->is_any_submission_plugin_enabled()) ? 1: 0;
  842. $DB->update_record('assign', $update);
  843. return $result;
  844. }
  845. /**
  846. * Add elements in grading plugin form.
  847. *
  848. * @param mixed $grade stdClass|null
  849. * @param MoodleQuickForm $mform
  850. * @param stdClass $data
  851. * @param int $userid - The userid we are grading
  852. * @return void
  853. */
  854. protected function add_plugin_grade_elements($grade, MoodleQuickForm $mform, stdClass $data, $userid) {
  855. foreach ($this->feedbackplugins as $plugin) {
  856. if ($plugin->is_enabled() && $plugin->is_visible()) {
  857. $mform->addElement('header', 'header_' . $plugin->get_type(), $plugin->get_name());
  858. $mform->setExpanded('header_' . $plugin->get_type());
  859. if (!$plugin->get_form_elements_for_user($grade, $mform, $data, $userid)) {
  860. $mform->removeElement('header_' . $plugin->get_type());
  861. }
  862. }
  863. }
  864. }
  865. /**
  866. * Add one plugins settings to edit plugin form.
  867. *
  868. * @param assign_plugin $plugin The plugin to add the settings from
  869. * @param MoodleQuickForm $mform The form to add the configuration settings to.
  870. * This form is modified directly (not returned).
  871. * @param array $pluginsenabled A list of form elements to be added to a group.
  872. * The new element is added to this array by this function.
  873. * @return void
  874. */
  875. protected function add_plugin_settings(assign_plugin $plugin, MoodleQuickForm $mform, & $pluginsenabled) {
  876. global $CFG;
  877. if ($plugin->is_visible() && !$plugin->is_configurable() && $plugin->is_enabled()) {
  878. $name = $plugin->get_subtype() . '_' . $plugin->get_type() . '_enabled';
  879. $pluginsenabled[] = $mform->createElement('hidden', $name, 1);
  880. $mform->setType($name, PARAM_BOOL);
  881. $plugin->get_settings($mform);
  882. } else if ($plugin->is_visible() && $plugin->is_configurable()) {
  883. $name = $plugin->get_subtype() . '_' . $plugin->get_type() . '_enabled';
  884. $label = $plugin->get_name();
  885. $label .= ' ' . $this->get_renderer()->help_icon('enabled', $plugin->get_subtype() . '_' . $plugin->get_type());
  886. $pluginsenabled[] = $mform->createElement('checkbox', $name, '', $label);
  887. $default = get_config($plugin->get_subtype() . '_' . $plugin->get_type(), 'default');
  888. if ($plugin->get_config('enabled') !== false) {
  889. $default = $plugin->is_enabled();
  890. }
  891. $mform->setDefault($plugin->get_subtype() . '_' . $plugin->get_type() . '_enabled', $default);
  892. $plugin->get_settings($mform);
  893. }
  894. }
  895. /**
  896. * Add settings to edit plugin form.
  897. *
  898. * @param MoodleQuickForm $mform The form to add the configuration settings to.
  899. * This form is modified directly (not returned).
  900. * @return void
  901. */
  902. public function add_all_plugin_settings(MoodleQuickForm $mform) {
  903. $mform->addElement('header', 'submissiontypes', get_string('submissiontypes', 'assign'));
  904. $submissionpluginsenabled = array();
  905. $group = $mform->addGroup(array(), 'submissionplugins', get_string('submissiontypes', 'assign'), array(' '), false);
  906. foreach ($this->submissionplugins as $plugin) {
  907. $this->add_plugin_settings($plugin, $mform, $submissionpluginsenabled);
  908. }
  909. $group->setElements($submissionpluginsenabled);
  910. $mform->addElement('header', 'feedbacktypes', get_string('feedbacktypes', 'assign'));
  911. $feedbackpluginsenabled = array();
  912. $group = $mform->addGroup(array(), 'feedbackplugins', get_string('feedbacktypes', 'assign'), array(' '), false);
  913. foreach ($this->feedbackplugins as $plugin) {
  914. $this->add_plugin_settings($plugin, $mform, $feedbackpluginsenabled);
  915. }
  916. $group->setElements($feedbackpluginsenabled);
  917. $mform->setExpanded('submissiontypes');
  918. }
  919. /**
  920. * Allow each plugin an opportunity to update the defaultvalues
  921. * passed in to the settings form (needed to set up draft areas for
  922. * editor and filemanager elements)
  923. *
  924. * @param array $defaultvalues
  925. */
  926. public function plugin_data_preprocessing(&$defaultvalues) {
  927. foreach ($this->submissionplugins as $plugin) {
  928. if ($plugin->is_visible()) {
  929. $plugin->data_preprocessing($defaultvalues);
  930. }
  931. }
  932. foreach ($this->feedbackplugins as $plugin) {
  933. if ($plugin->is_visible()) {
  934. $plugin->data_preprocessing($defaultvalues);
  935. }
  936. }
  937. }
  938. /**
  939. * Get the name of the current module.
  940. *
  941. * @return string the module name (Assignment)
  942. */
  943. protected function get_module_name() {
  944. if (isset(self::$modulename)) {
  945. return self::$modulename;
  946. }
  947. self::$modulename = get_string('modulename', 'assign');
  948. return self::$modulename;
  949. }
  950. /**
  951. * Get the plural name of the current module.
  952. *
  953. * @return string the module name plural (Assignments)
  954. */
  955. protected function get_module_name_plural() {
  956. if (isset(self::$modulenameplural)) {
  957. return self::$modulenameplural;
  958. }
  959. self::$modulenameplural = get_string('modulenameplural', 'assign');
  960. return self::$modulenameplural;
  961. }
  962. /**
  963. * Has this assignment been constructed from an instance?
  964. *
  965. * @return bool
  966. */
  967. public function has_instance() {
  968. return $this->instance || $this->get_course_module();
  969. }
  970. /**
  971. * Get the settings for the current instance of this assignment
  972. *
  973. * @return stdClass The settings
  974. */
  975. public function get_instance() {
  976. global $DB;
  977. if ($this->instance) {
  978. return $this->instance;
  979. }
  980. if ($this->get_course_module()) {
  981. $params = array('id' => $this->get_course_module()->instance);
  982. $this->instance = $DB->get_record('assign', $params, '*', MUST_EXIST);
  983. }
  984. if (!$this->instance) {
  985. throw new coding_exception('Improper use of the assignment class. ' .
  986. 'Cannot load the assignment record.');
  987. }
  988. return $this->instance;
  989. }
  990. /**
  991. * Get the primary grade item for this assign instance.
  992. *
  993. * @return stdClass The grade_item record
  994. */
  995. public function get_grade_item() {
  996. if ($this->gradeitem) {
  997. return $this->gradeitem;
  998. }
  999. $instance = $this->get_instance();
  1000. $params = array('itemtype' => 'mod',
  1001. 'itemmodule' => 'assign',
  1002. 'iteminstance' => $instance->id,
  1003. 'courseid' => $instance->course,
  1004. 'itemnumber' => 0);
  1005. $this->gradeitem = grade_item::fetch($params);
  1006. if (!$this->gradeitem) {
  1007. throw new coding_exception('Improper use of the assignment class. ' .
  1008. 'Cannot load the grade item.');
  1009. }
  1010. return $this->gradeitem;
  1011. }
  1012. /**
  1013. * Get the context of the current course.
  1014. *
  1015. * @return mixed context|null The course context
  1016. */
  1017. public function get_course_context() {
  1018. if (!$this->context && !$this->course) {
  1019. throw new coding_exception('Improper use of the assignment class. ' .
  1020. 'Cannot load the course context.');
  1021. }
  1022. if ($this->context) {
  1023. return $this->context->get_course_context();
  1024. } else {
  1025. return context_course::instance($this->course->id);
  1026. }
  1027. }
  1028. /**
  1029. * Get the current course module.
  1030. *
  1031. * @return mixed stdClass|null The course module
  1032. */
  1033. public function get_course_module() {
  1034. if ($this->coursemodule) {
  1035. return $this->coursemodule;
  1036. }
  1037. if (!$this->context) {
  1038. return null;
  1039. }
  1040. if ($this->context->contextlevel == CONTEXT_MODULE) {
  1041. $this->coursemodule = get_coursemodule_from_id('assign',
  1042. $this->context->instanceid,
  1043. 0,
  1044. false,
  1045. MUST_EXIST);
  1046. return $this->coursemodule;
  1047. }
  1048. return null;
  1049. }
  1050. /**
  1051. * Get context module.
  1052. *
  1053. * @return context
  1054. */
  1055. public function get_context() {
  1056. return $this->context;
  1057. }
  1058. /**
  1059. * Get the current course.
  1060. *
  1061. * @return mixed stdClass|null The course
  1062. */
  1063. public function get_course() {
  1064. global $DB;
  1065. if ($this->course) {
  1066. return $this->course;
  1067. }
  1068. if (!$this->context) {
  1069. return null;
  1070. }
  1071. $params = array('id' => $this->get_course_context()->instanceid);
  1072. $this->course = $DB->get_record('course', $params, '*', MUST_EXIST);
  1073. return $this->course;
  1074. }
  1075. /**
  1076. * Return a grade in user-friendly form, whether it's a scale or not.
  1077. *
  1078. * @param mixed $grade int|null
  1079. * @param boolean $editing Are we allowing changes to this grade?
  1080. * @param int $userid The user id the grade belongs to
  1081. * @param int $modified Timestamp from when the grade was last modified
  1082. * @return string User-friendly representation of grade
  1083. */
  1084. public function display_grade($grade, $editing, $userid=0, $modified=0) {
  1085. global $DB;
  1086. static $scalegrades = array();
  1087. $o = '';
  1088. if ($this->get_instance()->grade >= 0) {
  1089. // Normal number.
  1090. if ($editing && $this->get_instance()->grade > 0) {
  1091. if ($grade < 0) {
  1092. $displaygrade = '';
  1093. } else {
  1094. $displaygrade = format_float($grade, 2);
  1095. }
  1096. $o .= '<label class="accesshide" for="quickgrade_' . $userid . '">' .
  1097. get_string('usergrade', 'assign') .
  1098. '</label>';
  1099. $o .= '<input type="text"
  1100. id="quickgrade_' . $userid . '"
  1101. name="quickgrade_' . $userid . '"
  1102. value="' . $displaygrade . '"
  1103. size="6"
  1104. maxlength="10"
  1105. class="quickgrade"/>';
  1106. $o .= '&nbsp;/&nbsp;' . format_float($this->get_instance()->grade, 2);
  1107. return $o;
  1108. } else {
  1109. if ($grade == -1 || $grade === null) {
  1110. $o .= '-';
  1111. } else {
  1112. $item = $this->get_grade_item();
  1113. $o .= grade_format_gradevalue($grade, $item);
  1114. if ($item->get_displaytype() == GRADE_DISPLAY_TYPE_REAL) {
  1115. // If displaying the raw grade, also display the total value.
  1116. $o .= '&nbsp;/&nbsp;' . format_float($this->get_instance()->grade, 2);
  1117. }
  1118. }
  1119. return $o;
  1120. }
  1121. } else {
  1122. // Scale.
  1123. if (empty($this->cache['scale'])) {
  1124. if ($scale = $DB->get_record('scale', array('id'=>-($this->get_instance()->grade)))) {
  1125. $this->cache['scale'] = make_menu_from_list($scale->scale);
  1126. } else {
  1127. $o .= '-';
  1128. return $o;
  1129. }
  1130. }
  1131. if ($editing) {
  1132. $o .= '<label class="accesshide"
  1133. for="quickgrade_' . $userid . '">' .
  1134. get_string('usergrade', 'assign') .
  1135. '</label>';
  1136. $o .= '<select name="quickgrade_' . $userid . '" class="quickgrade">';
  1137. $o .= '<option value="-1">' . get_string('nograde') . '</option>';
  1138. foreach ($this->cache['scale'] as $optionid => $option) {
  1139. $selected = '';
  1140. if ($grade == $optionid) {
  1141. $selected = 'selected="selected"';
  1142. }
  1143. $o .= '<option value="' . $optionid . '" ' . $selected . '>' . $option . '</option>';
  1144. }
  1145. $o .= '</select>';
  1146. return $o;
  1147. } else {
  1148. $scaleid = (int)$grade;
  1149. if (isset($this->cache['scale'][$scaleid])) {
  1150. $o .= $this->cache['scale'][$scaleid];
  1151. return $o;
  1152. }
  1153. $o .= '-';
  1154. return $o;
  1155. }
  1156. }
  1157. }
  1158. /**
  1159. * Load a list of users enrolled in the current course with the specified permission and group.
  1160. * 0 for no group.
  1161. *
  1162. * @param int $currentgroup
  1163. * @param bool $idsonly
  1164. * @return array List of user records
  1165. */
  1166. public function list_participants($currentgroup, $idsonly) {
  1167. $key = $this->context->id

Large files files are truncated, but you can click here to view the full file