PageRenderTime 47ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 1ms

/mod/workshop/lib.php

http://github.com/moodle/moodle
PHP | 2207 lines | 1470 code | 266 blank | 471 comment | 321 complexity | fcd96577e388b96d70c6cb3a7c397e50 MD5 | raw file
Possible License(s): MIT, AGPL-3.0, MPL-2.0-no-copyleft-exception, LGPL-3.0, GPL-3.0, Apache-2.0, LGPL-2.1, BSD-3-Clause

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. * Library of workshop module functions needed by Moodle core and other subsystems
  18. *
  19. * All the functions neeeded by Moodle core, gradebook, file subsystem etc
  20. * are placed here.
  21. *
  22. * @package mod_workshop
  23. * @copyright 2009 David Mudrak <david.mudrak@gmail.com>
  24. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  25. */
  26. defined('MOODLE_INTERNAL') || die();
  27. require_once($CFG->dirroot . '/calendar/lib.php');
  28. define('WORKSHOP_EVENT_TYPE_SUBMISSION_OPEN', 'opensubmission');
  29. define('WORKSHOP_EVENT_TYPE_SUBMISSION_CLOSE', 'closesubmission');
  30. define('WORKSHOP_EVENT_TYPE_ASSESSMENT_OPEN', 'openassessment');
  31. define('WORKSHOP_EVENT_TYPE_ASSESSMENT_CLOSE', 'closeassessment');
  32. define('WORKSHOP_SUBMISSION_TYPE_DISABLED', 0);
  33. define('WORKSHOP_SUBMISSION_TYPE_AVAILABLE', 1);
  34. define('WORKSHOP_SUBMISSION_TYPE_REQUIRED', 2);
  35. ////////////////////////////////////////////////////////////////////////////////
  36. // Moodle core API //
  37. ////////////////////////////////////////////////////////////////////////////////
  38. /**
  39. * Returns the information if the module supports a feature
  40. *
  41. * @see plugin_supports() in lib/moodlelib.php
  42. * @param string $feature FEATURE_xx constant for requested feature
  43. * @return mixed true if the feature is supported, null if unknown
  44. */
  45. function workshop_supports($feature) {
  46. switch($feature) {
  47. case FEATURE_GRADE_HAS_GRADE: return true;
  48. case FEATURE_GROUPS: return true;
  49. case FEATURE_GROUPINGS: return true;
  50. case FEATURE_MOD_INTRO: return true;
  51. case FEATURE_BACKUP_MOODLE2: return true;
  52. case FEATURE_COMPLETION_TRACKS_VIEWS:
  53. return true;
  54. case FEATURE_SHOW_DESCRIPTION: return true;
  55. case FEATURE_PLAGIARISM: return true;
  56. default: return null;
  57. }
  58. }
  59. /**
  60. * Saves a new instance of the workshop into the database
  61. *
  62. * Given an object containing all the necessary data,
  63. * (defined by the form in mod_form.php) this function
  64. * will save a new instance and return the id number
  65. * of the new instance.
  66. *
  67. * @param stdClass $workshop An object from the form in mod_form.php
  68. * @return int The id of the newly inserted workshop record
  69. */
  70. function workshop_add_instance(stdclass $workshop) {
  71. global $CFG, $DB;
  72. require_once(__DIR__ . '/locallib.php');
  73. $workshop->phase = workshop::PHASE_SETUP;
  74. $workshop->timecreated = time();
  75. $workshop->timemodified = $workshop->timecreated;
  76. $workshop->useexamples = (int)!empty($workshop->useexamples);
  77. $workshop->usepeerassessment = 1;
  78. $workshop->useselfassessment = (int)!empty($workshop->useselfassessment);
  79. $workshop->latesubmissions = (int)!empty($workshop->latesubmissions);
  80. $workshop->phaseswitchassessment = (int)!empty($workshop->phaseswitchassessment);
  81. $workshop->evaluation = 'best';
  82. if (isset($workshop->gradinggradepass)) {
  83. $workshop->gradinggradepass = (float)unformat_float($workshop->gradinggradepass);
  84. }
  85. if (isset($workshop->submissiongradepass)) {
  86. $workshop->submissiongradepass = (float)unformat_float($workshop->submissiongradepass);
  87. }
  88. if (isset($workshop->submissionfiletypes)) {
  89. $filetypesutil = new \core_form\filetypes_util();
  90. $submissionfiletypes = $filetypesutil->normalize_file_types($workshop->submissionfiletypes);
  91. $workshop->submissionfiletypes = implode(' ', $submissionfiletypes);
  92. }
  93. if (isset($workshop->overallfeedbackfiletypes)) {
  94. $filetypesutil = new \core_form\filetypes_util();
  95. $overallfeedbackfiletypes = $filetypesutil->normalize_file_types($workshop->overallfeedbackfiletypes);
  96. $workshop->overallfeedbackfiletypes = implode(' ', $overallfeedbackfiletypes);
  97. }
  98. // insert the new record so we get the id
  99. $workshop->id = $DB->insert_record('workshop', $workshop);
  100. // we need to use context now, so we need to make sure all needed info is already in db
  101. $cmid = $workshop->coursemodule;
  102. $DB->set_field('course_modules', 'instance', $workshop->id, array('id' => $cmid));
  103. $context = context_module::instance($cmid);
  104. // process the custom wysiwyg editors
  105. if ($draftitemid = $workshop->instructauthorseditor['itemid']) {
  106. $workshop->instructauthors = file_save_draft_area_files($draftitemid, $context->id, 'mod_workshop', 'instructauthors',
  107. 0, workshop::instruction_editors_options($context), $workshop->instructauthorseditor['text']);
  108. $workshop->instructauthorsformat = $workshop->instructauthorseditor['format'];
  109. }
  110. if ($draftitemid = $workshop->instructreviewerseditor['itemid']) {
  111. $workshop->instructreviewers = file_save_draft_area_files($draftitemid, $context->id, 'mod_workshop', 'instructreviewers',
  112. 0, workshop::instruction_editors_options($context), $workshop->instructreviewerseditor['text']);
  113. $workshop->instructreviewersformat = $workshop->instructreviewerseditor['format'];
  114. }
  115. if ($draftitemid = $workshop->conclusioneditor['itemid']) {
  116. $workshop->conclusion = file_save_draft_area_files($draftitemid, $context->id, 'mod_workshop', 'conclusion',
  117. 0, workshop::instruction_editors_options($context), $workshop->conclusioneditor['text']);
  118. $workshop->conclusionformat = $workshop->conclusioneditor['format'];
  119. }
  120. // re-save the record with the replaced URLs in editor fields
  121. $DB->update_record('workshop', $workshop);
  122. // create gradebook items
  123. workshop_grade_item_update($workshop);
  124. workshop_grade_item_category_update($workshop);
  125. // create calendar events
  126. workshop_calendar_update($workshop, $workshop->coursemodule);
  127. if (!empty($workshop->completionexpected)) {
  128. \core_completion\api::update_completion_date_event($cmid, 'workshop', $workshop->id, $workshop->completionexpected);
  129. }
  130. return $workshop->id;
  131. }
  132. /**
  133. * Given an object containing all the necessary data,
  134. * (defined by the form in mod_form.php) this function
  135. * will update an existing instance with new data.
  136. *
  137. * @param stdClass $workshop An object from the form in mod_form.php
  138. * @return bool success
  139. */
  140. function workshop_update_instance(stdclass $workshop) {
  141. global $CFG, $DB;
  142. require_once(__DIR__ . '/locallib.php');
  143. $workshop->timemodified = time();
  144. $workshop->id = $workshop->instance;
  145. $workshop->useexamples = (int)!empty($workshop->useexamples);
  146. $workshop->usepeerassessment = 1;
  147. $workshop->useselfassessment = (int)!empty($workshop->useselfassessment);
  148. $workshop->latesubmissions = (int)!empty($workshop->latesubmissions);
  149. $workshop->phaseswitchassessment = (int)!empty($workshop->phaseswitchassessment);
  150. if (isset($workshop->gradinggradepass)) {
  151. $workshop->gradinggradepass = (float)unformat_float($workshop->gradinggradepass);
  152. }
  153. if (isset($workshop->submissiongradepass)) {
  154. $workshop->submissiongradepass = (float)unformat_float($workshop->submissiongradepass);
  155. }
  156. if (isset($workshop->submissionfiletypes)) {
  157. $filetypesutil = new \core_form\filetypes_util();
  158. $submissionfiletypes = $filetypesutil->normalize_file_types($workshop->submissionfiletypes);
  159. $workshop->submissionfiletypes = implode(' ', $submissionfiletypes);
  160. }
  161. if (isset($workshop->overallfeedbackfiletypes)) {
  162. $filetypesutil = new \core_form\filetypes_util();
  163. $overallfeedbackfiletypes = $filetypesutil->normalize_file_types($workshop->overallfeedbackfiletypes);
  164. $workshop->overallfeedbackfiletypes = implode(' ', $overallfeedbackfiletypes);
  165. }
  166. // todo - if the grading strategy is being changed, we may want to replace all aggregated peer grades with nulls
  167. $DB->update_record('workshop', $workshop);
  168. $context = context_module::instance($workshop->coursemodule);
  169. // process the custom wysiwyg editors
  170. if ($draftitemid = $workshop->instructauthorseditor['itemid']) {
  171. $workshop->instructauthors = file_save_draft_area_files($draftitemid, $context->id, 'mod_workshop', 'instructauthors',
  172. 0, workshop::instruction_editors_options($context), $workshop->instructauthorseditor['text']);
  173. $workshop->instructauthorsformat = $workshop->instructauthorseditor['format'];
  174. }
  175. if ($draftitemid = $workshop->instructreviewerseditor['itemid']) {
  176. $workshop->instructreviewers = file_save_draft_area_files($draftitemid, $context->id, 'mod_workshop', 'instructreviewers',
  177. 0, workshop::instruction_editors_options($context), $workshop->instructreviewerseditor['text']);
  178. $workshop->instructreviewersformat = $workshop->instructreviewerseditor['format'];
  179. }
  180. if ($draftitemid = $workshop->conclusioneditor['itemid']) {
  181. $workshop->conclusion = file_save_draft_area_files($draftitemid, $context->id, 'mod_workshop', 'conclusion',
  182. 0, workshop::instruction_editors_options($context), $workshop->conclusioneditor['text']);
  183. $workshop->conclusionformat = $workshop->conclusioneditor['format'];
  184. }
  185. // re-save the record with the replaced URLs in editor fields
  186. $DB->update_record('workshop', $workshop);
  187. // update gradebook items
  188. workshop_grade_item_update($workshop);
  189. workshop_grade_item_category_update($workshop);
  190. // update calendar events
  191. workshop_calendar_update($workshop, $workshop->coursemodule);
  192. $completionexpected = (!empty($workshop->completionexpected)) ? $workshop->completionexpected : null;
  193. \core_completion\api::update_completion_date_event($workshop->coursemodule, 'workshop', $workshop->id, $completionexpected);
  194. return true;
  195. }
  196. /**
  197. * Given an ID of an instance of this module,
  198. * this function will permanently delete the instance
  199. * and any data that depends on it.
  200. *
  201. * @param int $id Id of the module instance
  202. * @return boolean Success/Failure
  203. */
  204. function workshop_delete_instance($id) {
  205. global $CFG, $DB;
  206. require_once($CFG->libdir.'/gradelib.php');
  207. if (! $workshop = $DB->get_record('workshop', array('id' => $id))) {
  208. return false;
  209. }
  210. // delete all associated aggregations
  211. $DB->delete_records('workshop_aggregations', array('workshopid' => $workshop->id));
  212. // get the list of ids of all submissions
  213. $submissions = $DB->get_records('workshop_submissions', array('workshopid' => $workshop->id), '', 'id');
  214. // get the list of all allocated assessments
  215. $assessments = $DB->get_records_list('workshop_assessments', 'submissionid', array_keys($submissions), '', 'id');
  216. // delete the associated records from the workshop core tables
  217. $DB->delete_records_list('workshop_grades', 'assessmentid', array_keys($assessments));
  218. $DB->delete_records_list('workshop_assessments', 'id', array_keys($assessments));
  219. $DB->delete_records_list('workshop_submissions', 'id', array_keys($submissions));
  220. // call the static clean-up methods of all available subplugins
  221. $strategies = core_component::get_plugin_list('workshopform');
  222. foreach ($strategies as $strategy => $path) {
  223. require_once($path.'/lib.php');
  224. $classname = 'workshop_'.$strategy.'_strategy';
  225. call_user_func($classname.'::delete_instance', $workshop->id);
  226. }
  227. $allocators = core_component::get_plugin_list('workshopallocation');
  228. foreach ($allocators as $allocator => $path) {
  229. require_once($path.'/lib.php');
  230. $classname = 'workshop_'.$allocator.'_allocator';
  231. call_user_func($classname.'::delete_instance', $workshop->id);
  232. }
  233. $evaluators = core_component::get_plugin_list('workshopeval');
  234. foreach ($evaluators as $evaluator => $path) {
  235. require_once($path.'/lib.php');
  236. $classname = 'workshop_'.$evaluator.'_evaluation';
  237. call_user_func($classname.'::delete_instance', $workshop->id);
  238. }
  239. // delete the calendar events
  240. $events = $DB->get_records('event', array('modulename' => 'workshop', 'instance' => $workshop->id));
  241. foreach ($events as $event) {
  242. $event = calendar_event::load($event);
  243. $event->delete();
  244. }
  245. // gradebook cleanup
  246. grade_update('mod/workshop', $workshop->course, 'mod', 'workshop', $workshop->id, 0, null, array('deleted' => true));
  247. grade_update('mod/workshop', $workshop->course, 'mod', 'workshop', $workshop->id, 1, null, array('deleted' => true));
  248. // finally remove the workshop record itself
  249. // We must delete the module record after we delete the grade item.
  250. $DB->delete_records('workshop', array('id' => $workshop->id));
  251. return true;
  252. }
  253. /**
  254. * This standard function will check all instances of this module
  255. * and make sure there are up-to-date events created for each of them.
  256. * If courseid = 0, then every workshop event in the site is checked, else
  257. * only workshop events belonging to the course specified are checked.
  258. *
  259. * @param integer $courseid The Course ID.
  260. * @param int|stdClass $instance workshop module instance or ID.
  261. * @param int|stdClass $cm Course module object or ID.
  262. * @return bool Returns true if the calendar events were successfully updated.
  263. */
  264. function workshop_refresh_events($courseid = 0, $instance = null, $cm = null) {
  265. global $DB;
  266. // If we have instance information then we can just update the one event instead of updating all events.
  267. if (isset($instance)) {
  268. if (!is_object($instance)) {
  269. $instance = $DB->get_record('workshop', array('id' => $instance), '*', MUST_EXIST);
  270. }
  271. if (isset($cm)) {
  272. if (!is_object($cm)) {
  273. $cm = (object)array('id' => $cm);
  274. }
  275. } else {
  276. $cm = get_coursemodule_from_instance('workshop', $instance->id);
  277. }
  278. workshop_calendar_update($instance, $cm->id);
  279. return true;
  280. }
  281. if ($courseid) {
  282. // Make sure that the course id is numeric.
  283. if (!is_numeric($courseid)) {
  284. return false;
  285. }
  286. if (!$workshops = $DB->get_records('workshop', array('course' => $courseid))) {
  287. return false;
  288. }
  289. } else {
  290. if (!$workshops = $DB->get_records('workshop')) {
  291. return false;
  292. }
  293. }
  294. foreach ($workshops as $workshop) {
  295. if (!$cm = get_coursemodule_from_instance('workshop', $workshop->id, $courseid, false)) {
  296. continue;
  297. }
  298. workshop_calendar_update($workshop, $cm->id);
  299. }
  300. return true;
  301. }
  302. /**
  303. * List the actions that correspond to a view of this module.
  304. * This is used by the participation report.
  305. *
  306. * Note: This is not used by new logging system. Event with
  307. * crud = 'r' and edulevel = LEVEL_PARTICIPATING will
  308. * be considered as view action.
  309. *
  310. * @return array
  311. */
  312. function workshop_get_view_actions() {
  313. return array('view', 'view all', 'view submission', 'view example');
  314. }
  315. /**
  316. * List the actions that correspond to a post of this module.
  317. * This is used by the participation report.
  318. *
  319. * Note: This is not used by new logging system. Event with
  320. * crud = ('c' || 'u' || 'd') and edulevel = LEVEL_PARTICIPATING
  321. * will be considered as post action.
  322. *
  323. * @return array
  324. */
  325. function workshop_get_post_actions() {
  326. return array('add', 'add assessment', 'add example', 'add submission',
  327. 'update', 'update assessment', 'update example', 'update submission');
  328. }
  329. /**
  330. * Return a small object with summary information about what a
  331. * user has done with a given particular instance of this module
  332. * Used for user activity reports.
  333. * $return->time = the time they did it
  334. * $return->info = a short text description
  335. *
  336. * @param stdClass $course The course record.
  337. * @param stdClass $user The user record.
  338. * @param cm_info|stdClass $mod The course module info object or record.
  339. * @param stdClass $workshop The workshop instance record.
  340. * @return stdclass|null
  341. */
  342. function workshop_user_outline($course, $user, $mod, $workshop) {
  343. global $CFG, $DB;
  344. require_once($CFG->libdir.'/gradelib.php');
  345. $grades = grade_get_grades($course->id, 'mod', 'workshop', $workshop->id, $user->id);
  346. $submissiongrade = null;
  347. $assessmentgrade = null;
  348. $info = '';
  349. $time = 0;
  350. if (!empty($grades->items[0]->grades)) {
  351. $submissiongrade = reset($grades->items[0]->grades);
  352. $time = max($time, $submissiongrade->dategraded);
  353. if (!$submissiongrade->hidden || has_capability('moodle/grade:viewhidden', context_course::instance($course->id))) {
  354. $info .= get_string('submissiongrade', 'workshop') . ': ' . $submissiongrade->str_long_grade
  355. . html_writer::empty_tag('br');
  356. } else {
  357. $info .= get_string('submissiongrade', 'workshop') . ': ' . get_string('hidden', 'grades')
  358. . html_writer::empty_tag('br');
  359. }
  360. }
  361. if (!empty($grades->items[1]->grades)) {
  362. $assessmentgrade = reset($grades->items[1]->grades);
  363. $time = max($time, $assessmentgrade->dategraded);
  364. if (!$assessmentgrade->hidden || has_capability('moodle/grade:viewhidden', context_course::instance($course->id))) {
  365. $info .= get_string('gradinggrade', 'workshop') . ': ' . $assessmentgrade->str_long_grade;
  366. } else {
  367. $info .= get_string('gradinggrade', 'workshop') . ': ' . get_string('hidden', 'grades');
  368. }
  369. }
  370. if (!empty($info) and !empty($time)) {
  371. $return = new stdclass();
  372. $return->time = $time;
  373. $return->info = $info;
  374. return $return;
  375. }
  376. return null;
  377. }
  378. /**
  379. * Print a detailed representation of what a user has done with
  380. * a given particular instance of this module, for user activity reports.
  381. *
  382. * @param stdClass $course The course record.
  383. * @param stdClass $user The user record.
  384. * @param cm_info|stdClass $mod The course module info object or record.
  385. * @param stdClass $workshop The workshop instance record.
  386. * @return string HTML
  387. */
  388. function workshop_user_complete($course, $user, $mod, $workshop) {
  389. global $CFG, $DB, $OUTPUT;
  390. require_once(__DIR__.'/locallib.php');
  391. require_once($CFG->libdir.'/gradelib.php');
  392. $workshop = new workshop($workshop, $mod, $course);
  393. $grades = grade_get_grades($course->id, 'mod', 'workshop', $workshop->id, $user->id);
  394. if (!empty($grades->items[0]->grades)) {
  395. $submissiongrade = reset($grades->items[0]->grades);
  396. if (!$submissiongrade->hidden || has_capability('moodle/grade:viewhidden', context_course::instance($course->id))) {
  397. $info = get_string('submissiongrade', 'workshop') . ': ' . $submissiongrade->str_long_grade;
  398. } else {
  399. $info = get_string('submissiongrade', 'workshop') . ': ' . get_string('hidden', 'grades');
  400. }
  401. echo html_writer::tag('li', $info, array('class'=>'submissiongrade'));
  402. }
  403. if (!empty($grades->items[1]->grades)) {
  404. $assessmentgrade = reset($grades->items[1]->grades);
  405. if (!$assessmentgrade->hidden || has_capability('moodle/grade:viewhidden', context_course::instance($course->id))) {
  406. $info = get_string('gradinggrade', 'workshop') . ': ' . $assessmentgrade->str_long_grade;
  407. } else {
  408. $info = get_string('gradinggrade', 'workshop') . ': ' . get_string('hidden', 'grades');
  409. }
  410. echo html_writer::tag('li', $info, array('class'=>'gradinggrade'));
  411. }
  412. if (has_capability('mod/workshop:viewallsubmissions', $workshop->context)) {
  413. $canviewsubmission = true;
  414. if (groups_get_activity_groupmode($workshop->cm) == SEPARATEGROUPS) {
  415. // user must have accessallgroups or share at least one group with the submission author
  416. if (!has_capability('moodle/site:accessallgroups', $workshop->context)) {
  417. $usersgroups = groups_get_activity_allowed_groups($workshop->cm);
  418. $authorsgroups = groups_get_all_groups($workshop->course->id, $user->id, $workshop->cm->groupingid, 'g.id');
  419. $sharedgroups = array_intersect_key($usersgroups, $authorsgroups);
  420. if (empty($sharedgroups)) {
  421. $canviewsubmission = false;
  422. }
  423. }
  424. }
  425. if ($canviewsubmission and $submission = $workshop->get_submission_by_author($user->id)) {
  426. $title = format_string($submission->title);
  427. $url = $workshop->submission_url($submission->id);
  428. $link = html_writer::link($url, $title);
  429. $info = get_string('submission', 'workshop').': '.$link;
  430. echo html_writer::tag('li', $info, array('class'=>'submission'));
  431. }
  432. }
  433. if (has_capability('mod/workshop:viewallassessments', $workshop->context)) {
  434. if ($assessments = $workshop->get_assessments_by_reviewer($user->id)) {
  435. foreach ($assessments as $assessment) {
  436. $a = new stdclass();
  437. $a->submissionurl = $workshop->submission_url($assessment->submissionid)->out();
  438. $a->assessmenturl = $workshop->assess_url($assessment->id)->out();
  439. $a->submissiontitle = s($assessment->submissiontitle);
  440. echo html_writer::tag('li', get_string('assessmentofsubmission', 'workshop', $a));
  441. }
  442. }
  443. }
  444. }
  445. /**
  446. * Given a course and a time, this module should find recent activity
  447. * that has occurred in workshop activities and print it out.
  448. * Return true if there was output, or false is there was none.
  449. *
  450. * @param stdClass $course
  451. * @param bool $viewfullnames
  452. * @param int $timestart
  453. * @return boolean
  454. */
  455. function workshop_print_recent_activity($course, $viewfullnames, $timestart) {
  456. global $CFG, $USER, $DB, $OUTPUT;
  457. $authoramefields = get_all_user_name_fields(true, 'author', null, 'author');
  458. $reviewerfields = get_all_user_name_fields(true, 'reviewer', null, 'reviewer');
  459. $sql = "SELECT s.id AS submissionid, s.title AS submissiontitle, s.timemodified AS submissionmodified,
  460. author.id AS authorid, $authoramefields, a.id AS assessmentid, a.timemodified AS assessmentmodified,
  461. reviewer.id AS reviewerid, $reviewerfields, cm.id AS cmid
  462. FROM {workshop} w
  463. INNER JOIN {course_modules} cm ON cm.instance = w.id
  464. INNER JOIN {modules} md ON md.id = cm.module
  465. INNER JOIN {workshop_submissions} s ON s.workshopid = w.id
  466. INNER JOIN {user} author ON s.authorid = author.id
  467. LEFT JOIN {workshop_assessments} a ON a.submissionid = s.id
  468. LEFT JOIN {user} reviewer ON a.reviewerid = reviewer.id
  469. WHERE cm.course = ?
  470. AND md.name = 'workshop'
  471. AND s.example = 0
  472. AND (s.timemodified > ? OR a.timemodified > ?)
  473. ORDER BY s.timemodified";
  474. $rs = $DB->get_recordset_sql($sql, array($course->id, $timestart, $timestart));
  475. $modinfo = get_fast_modinfo($course); // reference needed because we might load the groups
  476. $submissions = array(); // recent submissions indexed by submission id
  477. $assessments = array(); // recent assessments indexed by assessment id
  478. $users = array();
  479. foreach ($rs as $activity) {
  480. if (!array_key_exists($activity->cmid, $modinfo->cms)) {
  481. // this should not happen but just in case
  482. continue;
  483. }
  484. $cm = $modinfo->cms[$activity->cmid];
  485. if (!$cm->uservisible) {
  486. continue;
  487. }
  488. // remember all user names we can use later
  489. if (empty($users[$activity->authorid])) {
  490. $u = new stdclass();
  491. $users[$activity->authorid] = username_load_fields_from_object($u, $activity, 'author');
  492. }
  493. if ($activity->reviewerid and empty($users[$activity->reviewerid])) {
  494. $u = new stdclass();
  495. $users[$activity->reviewerid] = username_load_fields_from_object($u, $activity, 'reviewer');
  496. }
  497. $context = context_module::instance($cm->id);
  498. $groupmode = groups_get_activity_groupmode($cm, $course);
  499. if ($activity->submissionmodified > $timestart and empty($submissions[$activity->submissionid])) {
  500. $s = new stdclass();
  501. $s->title = $activity->submissiontitle;
  502. $s->authorid = $activity->authorid;
  503. $s->timemodified = $activity->submissionmodified;
  504. $s->cmid = $activity->cmid;
  505. if ($activity->authorid == $USER->id || has_capability('mod/workshop:viewauthornames', $context)) {
  506. $s->authornamevisible = true;
  507. } else {
  508. $s->authornamevisible = false;
  509. }
  510. // the following do-while wrapper allows to break from deeply nested if-statements
  511. do {
  512. if ($s->authorid === $USER->id) {
  513. // own submissions always visible
  514. $submissions[$activity->submissionid] = $s;
  515. break;
  516. }
  517. if (has_capability('mod/workshop:viewallsubmissions', $context)) {
  518. if ($groupmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
  519. if (isguestuser()) {
  520. // shortcut - guest user does not belong into any group
  521. break;
  522. }
  523. // this might be slow - show only submissions by users who share group with me in this cm
  524. if (!$modinfo->get_groups($cm->groupingid)) {
  525. break;
  526. }
  527. $authorsgroups = groups_get_all_groups($course->id, $s->authorid, $cm->groupingid);
  528. if (is_array($authorsgroups)) {
  529. $authorsgroups = array_keys($authorsgroups);
  530. $intersect = array_intersect($authorsgroups, $modinfo->get_groups($cm->groupingid));
  531. if (empty($intersect)) {
  532. break;
  533. } else {
  534. // can see all submissions and shares a group with the author
  535. $submissions[$activity->submissionid] = $s;
  536. break;
  537. }
  538. }
  539. } else {
  540. // can see all submissions from all groups
  541. $submissions[$activity->submissionid] = $s;
  542. }
  543. }
  544. } while (0);
  545. }
  546. if ($activity->assessmentmodified > $timestart and empty($assessments[$activity->assessmentid])) {
  547. $a = new stdclass();
  548. $a->submissionid = $activity->submissionid;
  549. $a->submissiontitle = $activity->submissiontitle;
  550. $a->reviewerid = $activity->reviewerid;
  551. $a->timemodified = $activity->assessmentmodified;
  552. $a->cmid = $activity->cmid;
  553. if ($activity->reviewerid == $USER->id || has_capability('mod/workshop:viewreviewernames', $context)) {
  554. $a->reviewernamevisible = true;
  555. } else {
  556. $a->reviewernamevisible = false;
  557. }
  558. // the following do-while wrapper allows to break from deeply nested if-statements
  559. do {
  560. if ($a->reviewerid === $USER->id) {
  561. // own assessments always visible
  562. $assessments[$activity->assessmentid] = $a;
  563. break;
  564. }
  565. if (has_capability('mod/workshop:viewallassessments', $context)) {
  566. if ($groupmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
  567. if (isguestuser()) {
  568. // shortcut - guest user does not belong into any group
  569. break;
  570. }
  571. // this might be slow - show only submissions by users who share group with me in this cm
  572. if (!$modinfo->get_groups($cm->groupingid)) {
  573. break;
  574. }
  575. $reviewersgroups = groups_get_all_groups($course->id, $a->reviewerid, $cm->groupingid);
  576. if (is_array($reviewersgroups)) {
  577. $reviewersgroups = array_keys($reviewersgroups);
  578. $intersect = array_intersect($reviewersgroups, $modinfo->get_groups($cm->groupingid));
  579. if (empty($intersect)) {
  580. break;
  581. } else {
  582. // can see all assessments and shares a group with the reviewer
  583. $assessments[$activity->assessmentid] = $a;
  584. break;
  585. }
  586. }
  587. } else {
  588. // can see all assessments from all groups
  589. $assessments[$activity->assessmentid] = $a;
  590. }
  591. }
  592. } while (0);
  593. }
  594. }
  595. $rs->close();
  596. $shown = false;
  597. if (!empty($submissions)) {
  598. $shown = true;
  599. echo $OUTPUT->heading(get_string('recentsubmissions', 'workshop') . ':', 6);
  600. foreach ($submissions as $id => $submission) {
  601. $link = new moodle_url('/mod/workshop/submission.php', array('id'=>$id, 'cmid'=>$submission->cmid));
  602. if ($submission->authornamevisible) {
  603. $author = $users[$submission->authorid];
  604. } else {
  605. $author = null;
  606. }
  607. print_recent_activity_note($submission->timemodified, $author, $submission->title, $link->out(), false, $viewfullnames);
  608. }
  609. }
  610. if (!empty($assessments)) {
  611. $shown = true;
  612. echo $OUTPUT->heading(get_string('recentassessments', 'workshop') . ':', 6);
  613. core_collator::asort_objects_by_property($assessments, 'timemodified');
  614. foreach ($assessments as $id => $assessment) {
  615. $link = new moodle_url('/mod/workshop/assessment.php', array('asid' => $id));
  616. if ($assessment->reviewernamevisible) {
  617. $reviewer = $users[$assessment->reviewerid];
  618. } else {
  619. $reviewer = null;
  620. }
  621. print_recent_activity_note($assessment->timemodified, $reviewer, $assessment->submissiontitle, $link->out(), false, $viewfullnames);
  622. }
  623. }
  624. if ($shown) {
  625. return true;
  626. }
  627. return false;
  628. }
  629. /**
  630. * Returns all activity in course workshops since a given time
  631. *
  632. * @param array $activities sequentially indexed array of objects
  633. * @param int $index
  634. * @param int $timestart
  635. * @param int $courseid
  636. * @param int $cmid
  637. * @param int $userid defaults to 0
  638. * @param int $groupid defaults to 0
  639. * @return void adds items into $activities and increases $index
  640. */
  641. function workshop_get_recent_mod_activity(&$activities, &$index, $timestart, $courseid, $cmid, $userid=0, $groupid=0) {
  642. global $CFG, $COURSE, $USER, $DB;
  643. if ($COURSE->id == $courseid) {
  644. $course = $COURSE;
  645. } else {
  646. $course = $DB->get_record('course', array('id'=>$courseid));
  647. }
  648. $modinfo = get_fast_modinfo($course);
  649. $cm = $modinfo->cms[$cmid];
  650. $params = array();
  651. if ($userid) {
  652. $userselect = "AND (author.id = :authorid OR reviewer.id = :reviewerid)";
  653. $params['authorid'] = $userid;
  654. $params['reviewerid'] = $userid;
  655. } else {
  656. $userselect = "";
  657. }
  658. if ($groupid) {
  659. $groupselect = "AND (authorgroupmembership.groupid = :authorgroupid OR reviewergroupmembership.groupid = :reviewergroupid)";
  660. $groupjoin = "LEFT JOIN {groups_members} authorgroupmembership ON authorgroupmembership.userid = author.id
  661. LEFT JOIN {groups_members} reviewergroupmembership ON reviewergroupmembership.userid = reviewer.id";
  662. $params['authorgroupid'] = $groupid;
  663. $params['reviewergroupid'] = $groupid;
  664. } else {
  665. $groupselect = "";
  666. $groupjoin = "";
  667. }
  668. $params['cminstance'] = $cm->instance;
  669. $params['submissionmodified'] = $timestart;
  670. $params['assessmentmodified'] = $timestart;
  671. $authornamefields = get_all_user_name_fields(true, 'author', null, 'author');
  672. $reviewerfields = get_all_user_name_fields(true, 'reviewer', null, 'reviewer');
  673. $sql = "SELECT s.id AS submissionid, s.title AS submissiontitle, s.timemodified AS submissionmodified,
  674. author.id AS authorid, $authornamefields, author.picture AS authorpicture, author.imagealt AS authorimagealt,
  675. author.email AS authoremail, a.id AS assessmentid, a.timemodified AS assessmentmodified,
  676. reviewer.id AS reviewerid, $reviewerfields, reviewer.picture AS reviewerpicture,
  677. reviewer.imagealt AS reviewerimagealt, reviewer.email AS revieweremail
  678. FROM {workshop_submissions} s
  679. INNER JOIN {workshop} w ON s.workshopid = w.id
  680. INNER JOIN {user} author ON s.authorid = author.id
  681. LEFT JOIN {workshop_assessments} a ON a.submissionid = s.id
  682. LEFT JOIN {user} reviewer ON a.reviewerid = reviewer.id
  683. $groupjoin
  684. WHERE w.id = :cminstance
  685. AND s.example = 0
  686. $userselect $groupselect
  687. AND (s.timemodified > :submissionmodified OR a.timemodified > :assessmentmodified)
  688. ORDER BY s.timemodified ASC, a.timemodified ASC";
  689. $rs = $DB->get_recordset_sql($sql, $params);
  690. $groupmode = groups_get_activity_groupmode($cm, $course);
  691. $context = context_module::instance($cm->id);
  692. $grader = has_capability('moodle/grade:viewall', $context);
  693. $accessallgroups = has_capability('moodle/site:accessallgroups', $context);
  694. $viewauthors = has_capability('mod/workshop:viewauthornames', $context);
  695. $viewreviewers = has_capability('mod/workshop:viewreviewernames', $context);
  696. $submissions = array(); // recent submissions indexed by submission id
  697. $assessments = array(); // recent assessments indexed by assessment id
  698. $users = array();
  699. foreach ($rs as $activity) {
  700. // remember all user names we can use later
  701. if (empty($users[$activity->authorid])) {
  702. $u = new stdclass();
  703. $additionalfields = explode(',', user_picture::fields());
  704. $u = username_load_fields_from_object($u, $activity, 'author', $additionalfields);
  705. $users[$activity->authorid] = $u;
  706. }
  707. if ($activity->reviewerid and empty($users[$activity->reviewerid])) {
  708. $u = new stdclass();
  709. $additionalfields = explode(',', user_picture::fields());
  710. $u = username_load_fields_from_object($u, $activity, 'reviewer', $additionalfields);
  711. $users[$activity->reviewerid] = $u;
  712. }
  713. if ($activity->submissionmodified > $timestart and empty($submissions[$activity->submissionid])) {
  714. $s = new stdclass();
  715. $s->id = $activity->submissionid;
  716. $s->title = $activity->submissiontitle;
  717. $s->authorid = $activity->authorid;
  718. $s->timemodified = $activity->submissionmodified;
  719. if ($activity->authorid == $USER->id || has_capability('mod/workshop:viewauthornames', $context)) {
  720. $s->authornamevisible = true;
  721. } else {
  722. $s->authornamevisible = false;
  723. }
  724. // the following do-while wrapper allows to break from deeply nested if-statements
  725. do {
  726. if ($s->authorid === $USER->id) {
  727. // own submissions always visible
  728. $submissions[$activity->submissionid] = $s;
  729. break;
  730. }
  731. if (has_capability('mod/workshop:viewallsubmissions', $context)) {
  732. if ($groupmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
  733. if (isguestuser()) {
  734. // shortcut - guest user does not belong into any group
  735. break;
  736. }
  737. // this might be slow - show only submissions by users who share group with me in this cm
  738. if (!$modinfo->get_groups($cm->groupingid)) {
  739. break;
  740. }
  741. $authorsgroups = groups_get_all_groups($course->id, $s->authorid, $cm->groupingid);
  742. if (is_array($authorsgroups)) {
  743. $authorsgroups = array_keys($authorsgroups);
  744. $intersect = array_intersect($authorsgroups, $modinfo->get_groups($cm->groupingid));
  745. if (empty($intersect)) {
  746. break;
  747. } else {
  748. // can see all submissions and shares a group with the author
  749. $submissions[$activity->submissionid] = $s;
  750. break;
  751. }
  752. }
  753. } else {
  754. // can see all submissions from all groups
  755. $submissions[$activity->submissionid] = $s;
  756. }
  757. }
  758. } while (0);
  759. }
  760. if ($activity->assessmentmodified > $timestart and empty($assessments[$activity->assessmentid])) {
  761. $a = new stdclass();
  762. $a->id = $activity->assessmentid;
  763. $a->submissionid = $activity->submissionid;
  764. $a->submissiontitle = $activity->submissiontitle;
  765. $a->reviewerid = $activity->reviewerid;
  766. $a->timemodified = $activity->assessmentmodified;
  767. if ($activity->reviewerid == $USER->id || has_capability('mod/workshop:viewreviewernames', $context)) {
  768. $a->reviewernamevisible = true;
  769. } else {
  770. $a->reviewernamevisible = false;
  771. }
  772. // the following do-while wrapper allows to break from deeply nested if-statements
  773. do {
  774. if ($a->reviewerid === $USER->id) {
  775. // own assessments always visible
  776. $assessments[$activity->assessmentid] = $a;
  777. break;
  778. }
  779. if (has_capability('mod/workshop:viewallassessments', $context)) {
  780. if ($groupmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
  781. if (isguestuser()) {
  782. // shortcut - guest user does not belong into any group
  783. break;
  784. }
  785. // this might be slow - show only submissions by users who share group with me in this cm
  786. if (!$modinfo->get_groups($cm->groupingid)) {
  787. break;
  788. }
  789. $reviewersgroups = groups_get_all_groups($course->id, $a->reviewerid, $cm->groupingid);
  790. if (is_array($reviewersgroups)) {
  791. $reviewersgroups = array_keys($reviewersgroups);
  792. $intersect = array_intersect($reviewersgroups, $modinfo->get_groups($cm->groupingid));
  793. if (empty($intersect)) {
  794. break;
  795. } else {
  796. // can see all assessments and shares a group with the reviewer
  797. $assessments[$activity->assessmentid] = $a;
  798. break;
  799. }
  800. }
  801. } else {
  802. // can see all assessments from all groups
  803. $assessments[$activity->assessmentid] = $a;
  804. }
  805. }
  806. } while (0);
  807. }
  808. }
  809. $rs->close();
  810. $workshopname = format_string($cm->name, true);
  811. if ($grader) {
  812. require_once($CFG->libdir.'/gradelib.php');
  813. $grades = grade_get_grades($courseid, 'mod', 'workshop', $cm->instance, array_keys($users));
  814. }
  815. foreach ($submissions as $submission) {
  816. $tmpactivity = new stdclass();
  817. $tmpactivity->type = 'workshop';
  818. $tmpactivity->cmid = $cm->id;
  819. $tmpactivity->name = $workshopname;
  820. $tmpactivity->sectionnum = $cm->sectionnum;
  821. $tmpactivity->timestamp = $submission->timemodified;
  822. $tmpactivity->subtype = 'submission';
  823. $tmpactivity->content = $submission;
  824. if ($grader) {
  825. $tmpactivity->grade = $grades->items[0]->grades[$submission->authorid]->str_long_grade;
  826. }
  827. if ($submission->authornamevisible and !empty($users[$submission->authorid])) {
  828. $tmpactivity->user = $users[$submission->authorid];
  829. }
  830. $activities[$index++] = $tmpactivity;
  831. }
  832. foreach ($assessments as $assessment) {
  833. $tmpactivity = new stdclass();
  834. $tmpactivity->type = 'workshop';
  835. $tmpactivity->cmid = $cm->id;
  836. $tmpactivity->name = $workshopname;
  837. $tmpactivity->sectionnum = $cm->sectionnum;
  838. $tmpactivity->timestamp = $assessment->timemodified;
  839. $tmpactivity->subtype = 'assessment';
  840. $tmpactivity->content = $assessment;
  841. if ($grader) {
  842. $tmpactivity->grade = $grades->items[1]->grades[$assessment->reviewerid]->str_long_grade;
  843. }
  844. if ($assessment->reviewernamevisible and !empty($users[$assessment->reviewerid])) {
  845. $tmpactivity->user = $users[$assessment->reviewerid];
  846. }
  847. $activities[$index++] = $tmpactivity;
  848. }
  849. }
  850. /**
  851. * Print single activity item prepared by {@see workshop_get_recent_mod_activity()}
  852. */
  853. function workshop_print_recent_mod_activity($activity, $courseid, $detail, $modnames, $viewfullnames) {
  854. global $CFG, $OUTPUT;
  855. if (!empty($activity->user)) {
  856. echo html_writer::tag('div', $OUTPUT->user_picture($activity->user, array('courseid'=>$courseid)),
  857. array('style' => 'float: left; padding: 7px;'));
  858. }
  859. if ($activity->subtype == 'submission') {
  860. echo html_writer::start_tag('div', array('class'=>'submission', 'style'=>'padding: 7px; float:left;'));
  861. if ($detail) {
  862. echo html_writer::start_tag('h4', array('class'=>'workshop'));
  863. $url = new moodle_url('/mod/workshop/view.php', array('id'=>$activity->cmid));
  864. $name = s($activity->name);
  865. echo $OUTPUT->image_icon('icon', $name, $activity->type);
  866. echo ' ' . $modnames[$activity->type];
  867. echo html_writer::link($url, $name, array('class'=>'name', 'style'=>'margin-left: 5px'));
  868. echo html_writer::end_tag('h4');
  869. }
  870. echo html_writer::start_tag('div', array('class'=>'title'));
  871. $url = new moodle_url('/mod/workshop/submission.php', array('cmid'=>$activity->cmid, 'id'=>$activity->content->id));
  872. $name = s($activity->content->title);
  873. echo html_writer::tag('strong', html_writer::link($url, $name));
  874. echo html_writer::end_tag('div');
  875. if (!empty($activity->user)) {
  876. echo html_writer::start_tag('div', array('class'=>'user'));
  877. $url = new moodle_url('/user/view.php', array('id'=>$activity->user->id, 'course'=>$courseid));
  878. $name = fullname($activity->user);
  879. $link = html_writer::link($url, $name);
  880. echo get_string('submissionby', 'workshop', $link);
  881. echo ' - '.userdate($activity->timestamp);
  882. echo html_writer::end_tag('div');
  883. } else {
  884. echo html_writer::start_tag('div', array('class'=>'anonymous'));
  885. echo get_string('submission', 'workshop');
  886. echo ' - '.userdate($activity->timestamp);
  887. echo html_writer::end_tag('div');
  888. }
  889. echo html_writer::end_tag('div');
  890. }
  891. if ($activity->subtype == 'assessment') {
  892. echo html_writer::start_tag('div', array('class'=>'assessment', 'style'=>'padding: 7px; float:left;'));
  893. if ($detail) {
  894. echo html_writer::start_tag('h4', array('class'=>'workshop'));
  895. $url = new moodle_url('/mod/workshop/view.php', array('id'=>$activity->cmid));
  896. $name = s($activity->name);
  897. echo $OUTPUT->image_icon('icon', $name, $activity->type);
  898. echo ' ' . $modnames[$activity->type];
  899. echo html_writer::link($url, $name, array('class'=>'name', 'style'=>'margin-left: 5px'));
  900. echo html_writer::end_tag('h4');
  901. }
  902. echo html_writer::start_tag('div', array('class'=>'title'));
  903. $url = new moodle_url('/mod/workshop/assessment.php', array('asid'=>$activity->content->id));
  904. $name = s($activity->content->submissiontitle);
  905. echo html_writer::tag('em', html_writer::link($url, $name));
  906. echo html_writer::end_tag('div');
  907. if (!empty($activity->user)) {
  908. echo html_writer::start_tag('div', array('class'=>'user'));
  909. $url = new moodle_url('/user/view.php', array('id'=>$activity->user->id, 'course'=>$courseid));
  910. $name = fullname($activity->user);
  911. $link = html_writer::link($url, $name);
  912. echo get_string('assessmentbyfullname', 'workshop', $link);
  913. echo ' - '.userdate($activity->timestamp);
  914. echo html_writer::end_tag('div');
  915. } else {
  916. echo html_writer::start_tag('div', array('class'=>'anonymous'));
  917. echo get_string('assessment', 'workshop');
  918. echo ' - '.userdate($activity->timestamp);
  919. echo html_writer::end_tag('div');
  920. }
  921. echo html_writer::end_tag('div');
  922. }
  923. echo html_writer::empty_tag('br', array('style'=>'clear:both'));
  924. }
  925. /**
  926. * @deprecated since Moodle 3.8
  927. */
  928. function workshop_scale_used() {
  929. throw new coding_exception('workshop_scale_used() can not be used anymore. Plugins can implement ' .
  930. '<modname>_scale_used_anywhere, all implementations of <modname>_scale_used are now ignored');
  931. }
  932. /**
  933. * Is a given scale used by any instance of workshop?
  934. *
  935. * The function asks all installed grading strategy subplugins. The workshop
  936. * core itself does not use scales. Both grade for submission and grade for
  937. * assessments do not use scales.
  938. *
  939. * @param int $scaleid id of the scale to check
  940. * @return bool
  941. */
  942. function workshop_scale_used_anywhere($scaleid) {
  943. global $CFG; // other files included from here
  944. $strategies = core_component::get_plugin_list('workshopform');
  945. foreach ($strategies as $strategy => $strategypath) {
  946. $strategylib = $strategypath . '/lib.php';
  947. if (is_readable($strategylib)) {
  948. require_once($strategylib);
  949. } else {
  950. throw new coding_exception('the grading forms subplugin must contain library ' . $strategylib);
  951. }
  952. $classname = 'workshop_' . $strategy . '_strategy';
  953. if (method_exists($classname, 'scale_used')) {
  954. if (call_user_func(array($classname, 'scale_used'), $scaleid)) {
  955. // no need to include any other files - scale is used
  956. return true;
  957. }
  958. }
  959. }
  960. return false;
  961. }
  962. ////////////////////////////////////////////////////////////////////////////////
  963. // Gradebook API //
  964. ////////////////////////////////////////////////////////////////////////////////
  965. /**
  966. * Creates or updates grade items for the give workshop instance
  967. *
  968. * Needed by grade_update_mod_grades() in lib/gradelib.php. Also used by
  969. * {@link workshop_update_grades()}.
  970. *
  971. * @param stdClass $workshop instance object with extra cmidnumber property
  972. * @param stdClass $submissiongrades data for the first grade item
  973. * @param stdClass $assessmentgrades data for the second grade item
  974. * @return void
  975. */
  976. function workshop_grade_item_update(stdclass $workshop, $submissiongrades=null, $assessmentgrades=null) {
  977. global $CFG;
  978. require_once($CFG->libdir.'/gradelib.php');
  979. $a = new stdclass();
  980. $a->workshopname = clean_param($workshop->name, PARAM_NOTAGS);
  981. $item = array();
  982. $item['itemname'] = get_string('gradeitemsubmission', 'workshop', $a);
  983. $item['gradetype'] = GRADE_TYPE_VALUE;
  984. $item['grademax'] = $workshop->grade;
  985. $item['grademin'] = 0;
  986. grade_update('mod/workshop', $workshop->course, 'mod', 'workshop', $workshop->id, 0, $submissiongrades , $item);
  987. $item = array();
  988. $item['itemname'] = get_string('gradeitemassessment', 'workshop', $a);
  989. $item['gradetype'] = GRADE_TYPE

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