From 6e3099730e592b5532ae5960066cf25726982f05 Mon Sep 17 00:00:00 2001 From: David Mudrak Date: Mon, 4 Jan 2010 17:46:05 +0000 Subject: [PATCH] MDL-19717 First drafts of allocation support --- mod/workshop/allocation.php | 87 ++++ mod/workshop/allocation/lib.php | 170 ++++++++ mod/workshop/allocation/manual/allocator.php | 395 +++++++++++++++++++ mod/workshop/allocation/manual/ui.css | 81 ++++ mod/workshop/allocation/random/allocator.php | 79 ++++ mod/workshop/assessment.php | 7 +- mod/workshop/editform.php | 7 +- mod/workshop/lang/en_utf8/workshop.php | 35 +- mod/workshop/lib.php | 85 ++-- mod/workshop/locallib.php | 341 ++++++++++++++-- mod/workshop/simpletest/testlib.php | 2 +- mod/workshop/simpletest/testworkshopapi.php | 65 +++ mod/workshop/submission.php | 12 +- mod/workshop/view.php | 20 +- 14 files changed, 1303 insertions(+), 83 deletions(-) create mode 100644 mod/workshop/allocation.php create mode 100644 mod/workshop/allocation/lib.php create mode 100644 mod/workshop/allocation/manual/allocator.php create mode 100644 mod/workshop/allocation/manual/ui.css create mode 100644 mod/workshop/allocation/random/allocator.php create mode 100644 mod/workshop/simpletest/testworkshopapi.php diff --git a/mod/workshop/allocation.php b/mod/workshop/allocation.php new file mode 100644 index 0000000000..8f61587667 --- /dev/null +++ b/mod/workshop/allocation.php @@ -0,0 +1,87 @@ +. + + +/** + * At this page, teachers allocate submissions to students for a review + * + * The allocation logic itself is delegated to allocators - subplugins in ./allocation + * folder. + * + * @package mod-workshop + * @copyright 2009 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once(dirname(dirname(dirname(__FILE__))).'/config.php'); +require_once(dirname(__FILE__).'/locallib.php'); +require_once(dirname(__FILE__).'/allocation/lib.php'); + +$cmid = required_param('cmid', PARAM_INT); // course module +$method = optional_param('method', 'manual', PARAM_ALPHA); // method to use + +$PAGE->set_url('mod/workshop/allocation.php', array('cmid' => $cmid)); + +if (!$cm = get_coursemodule_from_id('workshop', $cmid)) { + print_error('invalidcoursemodule'); +} +if (!$course = $DB->get_record('course', array('id' => $cm->course))) { + print_error('coursemisconf'); +} +if (!$workshop = $DB->get_record('workshop', array('id' => $cm->instance))) { + print_error('err_invalidworkshopid', 'workshop'); +} + +$workshop = new workshop_api($workshop, $cm); + +require_login($course, false, $cm); + +$context = $PAGE->context; +require_capability('mod/workshop:allocate', $context); + +$PAGE->set_title($workshop->name); +$PAGE->set_heading($course->fullname); + +// todo navigation will be changed yet for Moodle 2.0 +$navigation = build_navigation(get_string('allocation', 'workshop'), $cm); + +$allocator = workshop_allocator_instance($workshop, $method); +try { + $allocator->init(); +} +catch (moodle_workshop_exception $e) { + echo $OUTPUT->header($navigation); + throw $e; +} + +// +// Output starts here +// +echo $OUTPUT->header($navigation); + +$allocators = workshop_installed_allocators(); +$tabrow = array(); +foreach ($allocators as $methodid => $methodname) { + $tabrow[] = new tabobject($methodid, "allocation.php?cmid={$cmid}&method={$methodid}", $methodname); +} +print_tabs(array($tabrow), $method); + +echo $OUTPUT->container_start('allocator allocator-' . $method); +echo $allocator->ui(); +echo $OUTPUT->container_end(); + +echo $OUTPUT->footer(); diff --git a/mod/workshop/allocation/lib.php b/mod/workshop/allocation/lib.php new file mode 100644 index 0000000000..e65bc04447 --- /dev/null +++ b/mod/workshop/allocation/lib.php @@ -0,0 +1,170 @@ +. + + +/** + * Code for the submissions allocation support is defined here + * + * @package mod-workshop + * @copyright 2009 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + + +/** + * Allocators are responsible for assigning submissions to reviewers for assessments + * + * The task of the allocator is to assign the correct number of submissions to reviewers + * for assessment. Several allocation methods are expected and they can be combined. For + * example, teacher can allocate several submissions manually (by 'manual' allocator) and + * then let the other submissions being allocated randomly (by 'random' allocator). + * Allocation is actually done by creating an initial assessment record in the + * workshop_assessments table. + */ +interface workshop_allocator { + + /** + * Initialize the allocator and eventually process submitted data + * + * This method is called soon after the allocator is constructed and before any output + * is generated. Therefore is may process any data submitted and do other tasks. + * It should not generate any output + * + * @throws moodle_workshop_exception + * @return void + */ + public function init(); + + + /** + * Returns HTML to be displayed as the user interface + * + * If a form is part of the UI, the caller should have call $PAGE->set_url(...) + * + * @access public + * @return string HTML to be displayed + */ + public function ui(); + +} + + +/** + * Return list of available allocation methods + * + * @access public + * @return array Array ['string' => 'string'] of localized allocation method names + */ +function workshop_installed_allocators() { + + $installed = get_list_of_plugins('mod/workshop/allocation'); + $forms = array(); + foreach ($installed as $allocation) { + $forms[$allocation] = get_string('allocation' . $allocation, 'workshop'); + } + // usability - make sure that manual allocation appears the first + if (isset($forms['manual'])) { + $m = array('manual' => $forms['manual']); + unset($forms['manual']); + $forms = array_merge($m, $forms); + } + return $forms; +} + + +/** + * Returns instance of submissions allocator + * + * @param object $workshop Workshop record + * @param object $method The name of the allocation method, must be PARAM_ALPHA + * @return object Instance of submissions allocator + */ +function workshop_allocator_instance(workshop $workshop, $method) { + + $allocationlib = dirname(__FILE__) . '/' . $method . '/allocator.php'; + if (is_readable($allocationlib)) { + require_once($allocationlib); + } else { + throw new moodle_exception('missingallocator', 'workshop'); + } + $classname = 'workshop_' . $method . '_allocator'; + return new $classname($workshop); +} + + +/** + * Returns the list of submissions and assessments allocated to them in the given workshop + * + * Submissions without allocated assessment are returned too, having assessment attributes null. + * This also fetches all other associated information (like details about the author and reviewer) + * needed to produce allocation reports. + * The returned structure is recordset of objects with following properties: + * [submissionid] [submissiontitle] [authorid] [authorfirstname] + * [authorlastname] [authorpicture] [authorimagealt] [assessmentid] + * [timeallocated] [reviewerid] [reviewerfirstname] [reviewerlastname] + * [reviewerpicture] [reviewerimagealt] + * + * @param object $workshop The workshop object + * @return object Recordset of allocations + */ +function workshop_get_allocations(workshop $workshop) { + global $DB; + + $sql = 'SELECT s.id AS submissionid, s.title AS submissiontitle, s.userid AS authorid, + author.firstname AS authorfirstname, author.lastname AS authorlastname, + author.picture AS authorpicture, author.imagealt AS authorimagealt, + a.id AS assessmentid, a.timecreated AS timeallocated, a.userid AS reviewerid, + reviewer.firstname AS reviewerfirstname, reviewer.lastname AS reviewerlastname, + reviewer.picture as reviewerpicture, reviewer.imagealt AS reviewerimagealt + FROM {workshop_submissions} s + LEFT JOIN {workshop_assessments} a ON (s.id = a.submissionid) + LEFT JOIN {user} author ON (s.userid = author.id) + LEFT JOIN {user} reviewer ON (a.userid = reviewer.id) + WHERE s.workshopid = ? + ORDER BY author.lastname,author.firstname,reviewer.lastname,reviewer.firstname'; + return $DB->get_recordset_sql($sql, array($workshop->id)); +} + + +/** + * Allocate a submission to a user for review + * + * @param object $workshop Workshop record + * @param object $submission Submission record + * @param int $reviewerid User ID + * @access public + * @return int ID of the new assessment or an error code + */ +function workshop_add_allocation(workshop $workshop, stdClass $submission, $reviewerid) { + global $DB; + + if ($DB->record_exists('workshop_assessments', array('submissionid' => $submission->id, 'userid' => $reviewerid))) { + return WORKSHOP_ALLOCATION_EXISTS; + } + + $now = time(); + $assessment = new stdClass(); + $assessment->submissionid = $submission->id; + $assessment->userid = $reviewerid; + $assessment->timecreated = $now; + $assessment->timemodified = $now; + + return $DB->insert_record('workshop_assessments', $assessment); +} + diff --git a/mod/workshop/allocation/manual/allocator.php b/mod/workshop/allocation/manual/allocator.php new file mode 100644 index 0000000000..310fde1a65 --- /dev/null +++ b/mod/workshop/allocation/manual/allocator.php @@ -0,0 +1,395 @@ +. + + +/** + * Allows user to allocate the submissions manually + * + * @package mod-workshop + * @copyright 2009 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +require_once(dirname(dirname(__FILE__)) . '/lib.php'); // interface definition +require_once(dirname(dirname(dirname(__FILE__))) . '/locallib.php'); // workshop internal API + + +/** + * These constants are used to pass status messages between init() and ui() + */ +define('WORKSHOP_ALLOCATION_MANUAL_MSG_ADDED', 1); +define('WORKSHOP_ALLOCATION_MANUAL_MSG_NOSUBMISSION', 2); +define('WORKSHOP_ALLOCATION_MANUAL_MSG_EXISTS', 3); +define('WORKSHOP_ALLOCATION_MANUAL_MSG_WOSUBMISSION', 4); +define('WORKSHOP_ALLOCATION_MANUAL_MSG_CONFIRM_DEL', 5); +define('WORKSHOP_ALLOCATION_MANUAL_MSG_DELETED', 6); + + +/** + * Allows users to allocate submissions for review manually + */ +class workshop_manual_allocator implements workshop_allocator { + + /** workshop instance */ + protected $workshop; + + + /** + * @param stdClass $workshop Workshop record + */ + public function __construct(workshop $workshop) { + + $this->workshop = $workshop; + } + + + /** + * Allocate submissions as requested by user + */ + public function init() { + global $PAGE; + + $mode = optional_param('mode', 'display', PARAM_ALPHA); + + switch ($mode) { + case 'new': + if (!confirm_sesskey()) { + throw new moodle_workshop_exception($this->workshop, 'confirmsesskeybad'); + } + $reviewerid = required_param('by', PARAM_INT); + $authorid = required_param('of', PARAM_INT); + $m = array(); // message object to be passed to the next page + $rs = $this->workshop->get_submissions($authorid); + $submission = $rs->current(); + $rs->close(); + if (!$submission) { + // nothing submitted by the given user + $m[] = WORKSHOP_ALLOCATION_MANUAL_MSG_NOSUBMISSION; + $m[] = $authorid; + + } else { + // ok, we have the submission + $res = $this->workshop->add_allocation($submission, $reviewerid); + if ($res == WORKSHOP_ALLOCATION_EXISTS) { + $m[] = WORKSHOP_ALLOCATION_MANUAL_MSG_EXISTS; + $m[] = $submission->userid; + $m[] = $reviewerid; + } elseif ($res == WORKSHOP_ALLOCATION_WOSUBMISSION) { + $m[] = WORKSHOP_ALLOCATION_MANUAL_MSG_WOSUBMISSION; + $m[] = $submission->userid; + $m[] = $reviewerid; + } else { + $m[] = WORKSHOP_ALLOCATION_MANUAL_MSG_ADDED; + $m[] = $submission->userid; + $m[] = $reviewerid; + } + } + $m = implode('-', $m); // serialize message object to be passed via URL + redirect($PAGE->url->out(false, array('m' => $m), false)); + break; + case 'del': + if (!confirm_sesskey()) { + throw new moodle_workshop_exception($this->workshop, 'confirmsesskeybad'); + } + $assessmentid = required_param('what', PARAM_INT); + $confirmed = optional_param('confirm', 0, PARAM_INT); + $rs = $this->workshop->get_assessments('all', $assessmentid); + $assessment = $rs->current(); + $rs->close(); + if ($assessment) { + if (!$confirmed) { + $m[] = WORKSHOP_ALLOCATION_MANUAL_MSG_CONFIRM_DEL; + $m[] = $assessment->id; + $m[] = $assessment->authorid; + $m[] = $assessment->reviewerid; + if (is_null($assessment->grade)) { + $m[] = 0; + } else { + $m[] = 1; + } + } else { + $res = $this->workshop->delete_assessment($assessment->id); + $m[] = WORKSHOP_ALLOCATION_MANUAL_MSG_DELETED; + $m[] = $assessment->authorid; + $m[] = $assessment->reviewerid; + } + $m = implode('-', $m); // serialize message object to be passed via URL + redirect($PAGE->url->out(false, array('m' => $m), false)); + } + break; + } + + // if we stay on this page, set the environment + $PAGE->requires->css('mod/workshop/allocation/manual/ui.css'); + } + + + /** + * Prints user interface - current allocation and a form to edit it + */ + public function ui() { + global $PAGE; + + $o = ''; // output buffer + $hlauthorid = -1; // highlight this author + $hlreviewerid = -1; // highlight this reviewer + $msg = ''; // msg text + $sty = ''; // msg style + $m = optional_param('m', '', PARAM_ALPHANUMEXT); // message object + + if ($m) { + $m = explode('-', $m); // unserialize + switch ($m[0]) { + case WORKSHOP_ALLOCATION_MANUAL_MSG_ADDED: + $hlauthorid = $m[1]; + $hlreviewerid = $m[2]; + $msg = get_string('allocationadded', 'workshop'); + $sty = 'ok'; + break; + case WORKSHOP_ALLOCATION_MANUAL_MSG_EXISTS: + $hlauthorid = $m[1]; + $hlreviewerid = $m[2]; + $msg = get_string('allocationexists', 'workshop'); + $sty = 'info'; + break; + case WORKSHOP_ALLOCATION_MANUAL_MSG_NOSUBMISSION: + $hlauthorid = $m[1]; + $msg = get_string('nosubmissionfound', 'workshop'); + $sty = 'error'; + break; + case WORKSHOP_ALLOCATION_MANUAL_MSG_WOSUBMISSION: + $hlauthorid = $m[1]; + $hlreviewerid = $m[2]; + $msg = get_string('cantassesswosubmission', 'workshop'); + $sty = 'error'; + break; + case WORKSHOP_ALLOCATION_MANUAL_MSG_CONFIRM_DEL: + $hlauthorid = $m[2]; + $hlreviewerid = $m[3]; + if ($m[4] == 0) { + $msg = get_string('areyousuretodeallocate', 'workshop'); + $sty = 'info'; + } else { + $msg = get_string('areyousuretodeallocategraded', 'workshop'); + $sty = 'error'; + } + break; + case WORKSHOP_ALLOCATION_MANUAL_MSG_DELETED: + $hlauthorid = $m[1]; + $hlreviewerid = $m[2]; + $msg = get_string('assessmentdeleted', 'workshop'); + $sty = 'ok'; + break; + } + $o .= '
'; + $o .= ' ' . $msg . ''; + $o .= ' '; + if ($m[0] == WORKSHOP_ALLOCATION_MANUAL_MSG_CONFIRM_DEL) { + $handler = $PAGE->url->out_action(); + $o .= print_single_button($handler, array('mode' => 'del', 'what' => $m[1], 'confirm' => 1), + get_string('iamsure', 'workshop'), 'post', '', true); + } + $o .= '
'; + } + + $peer = array(); // singular chosen due to readibility + $rs = $this->workshop->get_allocations(); + foreach ($rs as $allocation) { + $currentuserid = $allocation->authorid; + if (!isset($peer[$currentuserid])) { + $peer[$currentuserid] = new stdClass(); + $peer[$currentuserid]->id = $allocation->authorid; + $peer[$currentuserid]->firstname = $allocation->authorfirstname; + $peer[$currentuserid]->lastname = $allocation->authorlastname; + $peer[$currentuserid]->picture = $allocation->authorpicture; + $peer[$currentuserid]->imagealt = $allocation->authorimagealt; + $peer[$currentuserid]->avatar = print_user_picture($peer[$currentuserid], + $this->workshop->course, null, 16, true); + $peer[$currentuserid]->submissionid = $allocation->submissionid; + $peer[$currentuserid]->submissiontitle = $allocation->submissiontitle; + $peer[$currentuserid]->submissiongrade = $allocation->submissiongrade; + $peer[$currentuserid]->reviewedby = array(); // users who are reviewing this user's submission + $peer[$currentuserid]->reviewerof = array(); // users whom submission is being reviewed by this user + } + if (!empty($allocation->reviewerid)) { + // example: "submission of user with id 45 is reviewed by user with id 87 in the assessment record 12" + $peer[$currentuserid]->reviewedby[$allocation->reviewerid] = $allocation->assessmentid; + } + } + $rs->close(); + + foreach ($peer as $author) { + foreach ($author->reviewedby as $reviewerid => $assessmentid) { + // example: "user with id 87 is reviewer of the work submitted by user id 45 in the assessment record 12" + if (isset($peer[$reviewerid])) { + $peer[$reviewerid]->reviewerof[$author->id] = $assessmentid; + } + } + } + + if (empty($peer)) { + $o .= '
' . get_string('nosubmissions', 'workshop') . '
'; + } else { + $o .= '' . "\n"; + $o .= ''; + $o .= ''; + $o .= ''; + $o .= ''; + $o .= ''; + $counter = 0; + foreach ($peer as $user) { + $o .= '' . "\n"; + + if ($user->id == $hlauthorid) { + $highlight=' highlight'; + } else { + $highlight=''; + } + $o .= '' . "\n"; + $o .= '' . "\n"; + + if ($user->id == $hlreviewerid) { + $highlight=' highlight'; + } else { + $highlight=''; + } + $o .= '' . "\n"; + $o .= '' . "\n"; + $counter++; + } + $o .= '
' . get_string('participantreviewedby', 'workshop') . '' . get_string('participant', 'workshop') . '' . get_string('participantrevierof', 'workshop') . '
' . "\n"; + if (is_null($user->submissionid)) { + $o .= '' . "\n"; + $o .= get_string('nothingtoreview', 'workshop'); + $o .= '' . "\n"; + } else { + $handler = $PAGE->url->out_action() . '&mode=new&of=' . $user->id . '&by='; + $o .= popup_form($handler, $this->available_reviewers($user->id), 'addreviewof' . $user->id, '', + get_string('chooseuser', 'workshop'), '', '', true, 'self', get_string('addreviewer', 'workshop')); + } + $o .= '
    ' . "\n"; + foreach ($user->reviewedby as $reviewerid => $assessmentid) { + $o .= '
  • '; + $o .= print_user_picture($peer[$reviewerid], $this->workshop->course, null, 16, true); + $o .= fullname($peer[$reviewerid]); + + // delete + $handler = $PAGE->url->out_action(array('mode' => 'del', 'what' => $assessmentid)); + $o .= ' X '; // todo icon and link title + + $o .= '
  • '; + } + $o .= '
' . "\n"; + + $o .= '
' . "\n"; + $o .= print_user_picture($user, $this->workshop->course, null, 35, true); + $o .= fullname($user); + $o .= '
' . "\n"; + if (is_null($user->submissionid)) { + $o .= '' . get_string('nosubmissionfound', 'workshop'); + } else { + $o .= ''; + if (is_null($user->submissiongrade)) { + $o .= '
' . get_string('nogradeyet', 'workshop') . '
'; + } else { + $o .= '
' . s($user->submissiongrade) . '
'; // todo calculate + } + } + $o .= '
' . "\n"; + $o .= '
' . "\n"; + if (!($this->workshop->assesswosubmission) && is_null($user->submissionid)) { + $o .= '' . "\n"; + $o .= get_string('cantassesswosubmission', 'workshop'); + $o .= '' . "\n"; + } else { + $handler = $PAGE->url->out_action() . '&mode=new&by=' . $user->id . '&of='; + $o .= popup_form($handler, $this->available_reviewees($user->id), 'addreviewby' . $user->id, '', + get_string('chooseuser', 'workshop'), '', '', true, 'self', get_string('addreviewee', 'workshop')); + $o .= '
    ' . "\n"; + foreach ($user->reviewerof as $authorid => $assessmentid) { + $o .= '
  • '; + $o .= print_user_picture($peer[$authorid], $this->workshop->course, null, 16, true); + $o .= fullname($peer[$authorid]); + + // delete + $handler = $PAGE->url->out_action(array('mode' => 'del', 'what' => $assessmentid)); + $o .= ' X '; // todo icon and link title + + $o .= '
  • '; + } + $o .= '
' . "\n"; + } + $o .= '
' . "\n"; + } + return $o; + } + + + /** + * Return a list of reviewers that can review a submission + * + * @param int $authorid User ID of the submission author + * @return array Select options + */ + protected function available_reviewers($authorid) { + + $users = $this->workshop->get_peer_reviewers(); + $options = array(); + foreach ($users as $user) { + $options[$user->id] = fullname($user); + } + if (0 == $this->workshop->useselfassessment) { + // students can not review their own submissions in this workshop + if (isset($options[$authorid])) { + unset($options[$authorid]); + } + } + + return $options; + } + + + /** + * Return a list of reviewees whom work can be reviewed by a given user + * + * @param int $reviewerid User ID of the reviewer + * @return array Select options + */ + protected function available_reviewees($reviewerid) { + + $rs = $this->workshop->get_submissions(); + $options = array(); + foreach ($rs as $submission) { + $options[$submission->userid] = fullname((object)array('firstname' => $submission->authorfirstname, + 'lastname' => $submission->authorlastname)); + } + $rs->close(); + if (0 == $this->workshop->useselfassessment) { + // students can not be reviewed by themselves in this workshop + if (isset($options[$reviewerid])) { + unset($options[$reviewerid]); + } + } + + return $options; + } + +} + diff --git a/mod/workshop/allocation/manual/ui.css b/mod/workshop/allocation/manual/ui.css new file mode 100644 index 0000000000..1439d6749b --- /dev/null +++ b/mod/workshop/allocation/manual/ui.css @@ -0,0 +1,81 @@ +.allocations { + margin: 0px auto; +} + +.allocations .r0 { + background-color: #eee; +} + +.allocations .highlight { + background-color: #fff3d2; +} + +.allocations tr td { + vertical-align: top; + padding: 5px; +} + +.allocations tr td.peer { + border-left: 1px solid #ccc; + border-right: 1px solid #ccc; +} + +.allocations .reviewedby .info, +.allocations .peer .info, +.allocations .reviewerof .info { + font-size: 80%; + color: #888; + font-style: italic; +} + +.allocations .reviewedby img.userpicture, +.allocations .reviewerof img.userpicture { + height: 16px; + width: 16px; + margin-right: 3px; + vertical-align: middle; +} + +.allocations .peer img.userpicture { + height: 35px; + width: 35px; + vertical-align: middle; + margin-right: 5px; +} + +.allocations .peer .submission { + font-size: 90%; + margin-top: 1em; +} + +#message { + padding: 5px 5em 5px 15px; + margin: 0px auto 20px auto; + width: 60%; + font-size: 80%; + position: relative; +} + +#message-close { + font-weight: bold; + position: absolute; + top: 5px; + right: 15px; +} + +#message.ok { + color: #547c22; + background-color: #e7f1c3; +} + +#message.error { + color: #dd0221; + background-color: #ffd3d9; +} + +#message.info { + color: #1666a9; + background-color: #d2ebff; +} + + diff --git a/mod/workshop/allocation/random/allocator.php b/mod/workshop/allocation/random/allocator.php new file mode 100644 index 0000000000..228824d8b5 --- /dev/null +++ b/mod/workshop/allocation/random/allocator.php @@ -0,0 +1,79 @@ +. + + +/** + * Allocates the submissions randomly + * + * @package mod-workshop + * @copyright 2009 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +require_once(dirname(dirname(__FILE__)) . '/lib.php'); // interface definition + + +class workshop_random_allocator implements workshop_allocator { + + /** workshop instance */ + protected $workshop; + + /** array of allocations */ + protected $allocation = array(); + + public function __construct(stdClass $workshop) { + global $DB, $USER; + + $this->workshop = $workshop; + + // submissions to be allocated + $submissions = $DB->get_records('workshop_submissions', array('workshopid' => $this->workshop->id, 'example' => 0), + '', 'id,userid,title'); + + + // dummy allocation - allocate all submissions to the current USER + foreach ($submissions as $submissionid => $submission) { + $this->allocation[$submissionid] = new stdClass; + $this->allocation[$submissionid]->submissionid = $submissionid; + $this->allocation[$submissionid]->title = $submission->title; + $this->allocation[$submissionid]->authorid = $submission->userid; + $this->allocation[$submissionid]->reviewerid = $USER->id; + $this->allocation[$submissionid]->assessmentid = NULL; + } + + // already created assessments + $assessments = $DB->get_records_list('workshop_assessments', 'submissionid', array_keys($submissions), + '', 'id,submissionid,userid'); + + foreach ($assessments as $assessmentid => $assessment) { + $this->allocation[$assessment->submissionid]->assessmentid = $assessmentid; + } + } + + + public function init() { + } + + + public function ui() { + return 'TODO'; + } + + +} diff --git a/mod/workshop/assessment.php b/mod/workshop/assessment.php index 34c9f68d8f..9b926434c3 100644 --- a/mod/workshop/assessment.php +++ b/mod/workshop/assessment.php @@ -28,7 +28,6 @@ */ require_once(dirname(dirname(dirname(__FILE__))).'/config.php'); -//require_once(dirname(__FILE__).'/lib.php'); require_once(dirname(__FILE__).'/locallib.php'); if ($preview = optional_param('preview', 0, PARAM_INT)) { @@ -66,7 +65,9 @@ if ($preview = optional_param('preview', 0, PARAM_INT)) { require_login($course, false, $cm); -$context = get_context_instance(CONTEXT_MODULE, $cm->id); +$workshop = new workshop_api($workshop, $cm); + +$context = $PAGE->context; if (isguestuser()) { print_error('err_noguests', 'workshop', "$CFG->wwwroot/mod/workshop/view.php?id=$cmid"); @@ -84,7 +85,7 @@ if ($mode == 'preview') { $editurl = "{$CFG->wwwroot}/mod/workshop/editform.php?cmid={$cm->id}"; // load the grading strategy logic -$strategy = workshop_strategy_instance($workshop); +$strategy = $workshop->grading_strategy_instance(); // load the assessment form definition from the database // this must be called before get_assessment_form() where we have to know diff --git a/mod/workshop/editform.php b/mod/workshop/editform.php index b675ae06bf..87b9030b8c 100644 --- a/mod/workshop/editform.php +++ b/mod/workshop/editform.php @@ -25,7 +25,6 @@ */ require_once(dirname(dirname(dirname(__FILE__))).'/config.php'); -require_once(dirname(__FILE__).'/lib.php'); require_once(dirname(__FILE__).'/locallib.php'); $cmid = required_param('cmid', PARAM_INT); // course module id @@ -40,7 +39,7 @@ if (!$course = $DB->get_record('course', array('id' => $cm->course))) { require_login($course, false, $cm); -$context = get_context_instance(CONTEXT_MODULE, $cm->id); +$context = $PAGE->context; if (isguestuser()) { print_error('err_noguests', 'workshop', "$CFG->wwwroot/mod/workshop/view.php?id=$cmid"); @@ -50,6 +49,8 @@ if (!$workshop = $DB->get_record('workshop', array('id' => $cm->instance))) { print_error('err_invalidworkshopid', 'workshop'); } +$workshop = new workshop_api($workshop, $cm)l + // where should the user be sent after closing the editing form $returnurl = "{$CFG->wwwroot}/mod/workshop/view.php?id={$cm->id}"; // the URL of this editing form @@ -58,7 +59,7 @@ $selfurl = "{$CFG->wwwroot}/mod/workshop/editform.php?cmid={$cm->id}"; $previewurl = "{$CFG->wwwroot}/mod/workshop/assessment.php?preview={$cm->id}"; // load the grading strategy logic -$strategy = workshop_strategy_instance($workshop); +$strategy = $workshop->grading_strategy_instance(); // load the assessment form definition from the database // this must be called before get_edit_strategy_form() where we have to know diff --git a/mod/workshop/lang/en_utf8/workshop.php b/mod/workshop/lang/en_utf8/workshop.php index 61ab3f81c2..67c7ee1461 100644 --- a/mod/workshop/lang/en_utf8/workshop.php +++ b/mod/workshop/lang/en_utf8/workshop.php @@ -40,26 +40,26 @@ $string[''] = ''; $string[''] = ''; $string[''] = ''; $string[''] = ''; -$string[''] = ''; -$string[''] = ''; -$string[''] = ''; -$string[''] = ''; -$string[''] = ''; -$string[''] = ''; -$string[''] = ''; -$string[''] = ''; -$string[''] = ''; -$string[''] = ''; -$string[''] = ''; -$string[''] = ''; $string['accesscontrol'] = 'Access control'; $string['addmoredimensionsaccumulative'] = 'Blanks for $a more aspects'; $string['addmoredimensionsnoerrors'] = 'Blanks for $a more assertions'; +$string['addreviewee'] = 'Add reviewee'; +$string['addreviewer'] = 'Add reviewer'; +$string['addsubmissiontoreview'] = 'Assign submission'; $string['agreeassessments'] = 'Assessments must be agreed'; $string['agreeassessmentsdesc'] = 'Authors may comment assessments of their work and agree/disagree with it'; +$string['allocationadded'] = 'The submission has been successfully allocated'; +$string['allocationexists'] = 'The allocation already exists'; +$string['allocationmanual'] = 'Manual allocation'; +$string['allocationrandom'] = 'Random allocation'; +$string['allocation'] = 'Submission allocation'; +$string['allocationview'] = 'View current allocations'; +$string['areyousuretodeallocate'] = 'Are you sure you want deallocate the selected assessment?'; +$string['areyousuretodeallocategraded'] = 'You are going to remove the assessment that has already been graded. Are you really sure you want to do it?'; $string['assessallexamples'] = 'Assess all examples'; $string['assessingsubmission'] = 'Assessing submission'; $string['assessmentcomps'] = 'Required level of assessments similarity'; +$string['assessmentdeleted'] = 'Submission deallocated and assessment deleted'; $string['assessmentend'] = 'End of assessment phase'; $string['assessmentform'] = 'Assessment form'; $string['assessmentsettings'] = 'Assessment settings'; @@ -67,6 +67,7 @@ $string['assessmentstart'] = 'Start of assessment phase'; $string['assesswosubmission'] = 'Assess without submission'; $string['assesswosubmissiondesc'] = 'Users can assess peers even without their own submission'; $string['backtoeditform'] = 'Back to editing form'; +$string['cantassesswosubmission'] = 'Users can\'t assess without own submission in this workshop'; $string['comparisonhigh'] = 'High'; $string['comparisonlow'] = 'Low'; $string['comparisonnormal'] = 'Normal'; @@ -97,10 +98,13 @@ $string['examplesvoluntary'] = 'Assessment of example submission is voluntary'; $string['gradeforassessment'] = 'Grade for assessment'; $string['gradeforsubmission'] = 'Grade for submission'; $string['gradingsettings'] = 'Grading settings'; +$string['chooseuser'] = 'Choose user...'; +$string['iamsure'] = 'Yes, I am sure'; $string['introduction'] = 'Introduction'; $string['latesubmissionsdesc'] = 'Allow submitting the work after the deadline'; $string['latesubmissions'] = 'Late submissions'; $string['maxbytes'] = 'Maximum file size'; +$string['messageclose'] = '(hide)'; $string['modulenameplural'] = 'Workshops'; $string['modulename'] = 'Workshop'; $string['nattachments'] = 'Maximum number of submission attachments'; @@ -112,7 +116,14 @@ $string['noerrorsgrade1default'] = 'Yes'; $string['noerrorsgrade1'] = 'Word for the success'; $string['noerrorsmaperror'] = 'Number of errors is less than or equals'; $string['noerrorsmapgrade'] = 'Grade for submission'; +$string['nogradeyet'] = 'No grade yet'; +$string['nosubmissionfound'] = 'No submission found for this user'; +$string['nosubmissions'] = 'No submissions yet in this workshop'; +$string['nothingtoreview'] = 'Nothing to review'; $string['nsassessments'] = 'Number of required assessments of other users\' work'; +$string['participant'] = 'Participant'; +$string['participantrevierof'] = 'Participant is reviewer of'; +$string['participantreviewedby'] = 'Participant is reviewed by'; $string['percents'] = '$a%'; $string['previewassessmentform'] = 'Preview'; $string['releasegrades'] = 'Push final grades into the gradebook'; diff --git a/mod/workshop/lib.php b/mod/workshop/lib.php index 0ab7d0143d..73b6aac4f8 100644 --- a/mod/workshop/lib.php +++ b/mod/workshop/lib.php @@ -50,22 +50,73 @@ define('WORKSHOP_COMPARISON_HIGH', 3); /* f = 3.00 */ define('WORKSHOP_COMPARISON_VERYHIGH', 4); /* f = 5.00 */ +/** + * The base class of workshop instances + * + * The class just wraps the database record from the {workshop} table and adds some + * methods that implement the compulsory activity module API. + * For full-featured class see {@link workshop_api}. + */ +class workshop { + + /** course module record */ + public $cm; + + /** + * Defines methods that are part of any activity module API and may be called by Moodle core + * + * Initializes the object using the data from DB. Makes deep copy of all $dbrecord properties. + * + * @param object $instance The instance data row from {workshop} table + * @param object $cm Course module record + */ + public function __construct(stdClass $instance, stdClass $cm) { + + foreach ($instance as $key => $val) { + if (is_object($val) || (is_array($val))) { + // this should not happen if the $dbrecord is really just the record returned by $DB + $this->{$key} = unserialize(serialize($val)); + } else { + $this->{$key} = $val; + } + } + $this->cm = $cm; + } + + + /** + * Saves a new instance of the workshop into the database + * + * Given an object containing all the necessary data, + * (defined by the form in mod_form.php) this function + * will save a new instance and return the id number + * of the new instance. + * + * @param object $data An object from the form in mod_form.php + * @return int The id of the newly inserted workshop record + */ + public static function add_instance($data) { + global $DB; + + $data->timecreated = time(); + $data->timemodified = $data->timecreated; + + return $DB->insert_record('workshop', $data); + } +} + + /** * Given an object containing all the necessary data, * (defined by the form in mod_form.php) this function * will create a new instance and return the id number * of the new instance. * - * @param object $workshop An object from the form in mod_form.php + * @param object $data An object from the form in mod_form.php * @return int The id of the newly inserted workshop record */ -function workshop_add_instance($workshop) { - global $DB; - - $workshop->timecreated = time(); - $workshop->timemodified = $workshop->timecreated; - - return $DB->insert_record('workshop', $workshop); +function workshop_add_instance($data) { + return workshop::add_instance($data); } @@ -339,24 +390,6 @@ function workshop_get_strategies() { } -/** - * Return an array of the localized allocation names - * - * @access public - * @return array Array ['string' => 'string'] - */ -function workshop_get_allocations() { - - $installed = get_list_of_plugins('mod/workshop/allocation'); - $forms = array(); - foreach ($installed as $allocation) { - $forms[$allocation] = get_string('allocation' . $allocation, 'workshop'); - } - - return $forms; -} - - /** * Return an array of available example assessment modes * diff --git a/mod/workshop/locallib.php b/mod/workshop/locallib.php index 9fe1d30c3e..8d31faf6e7 100644 --- a/mod/workshop/locallib.php +++ b/mod/workshop/locallib.php @@ -17,10 +17,12 @@ /** - * Library of internal functions for module workshop + * Library of internal classes and functions for module workshop * * All the workshop specific functions, needed to implement the module - * logic, should go to here. + * logic, should go to here. Instead of having bunch of function named + * workshop_something() taking the workshop instance as the first + * parameter, we use a class workshop_api that provides all methods. * * @package mod-workshop * @copyright 2009 David Mudrak @@ -29,39 +31,332 @@ defined('MOODLE_INTERNAL') || die(); -function workshop_strategy_instance($workshop) { - /** static variable to hold the singleton */ - static $instance = null; +require_once(dirname(__FILE__).'/lib.php'); // we extend this library here - if (is_null($instance)) { - $strategylib = dirname(__FILE__) . '/grading/' . $workshop->strategy . '/strategy.php'; - if (is_readable($strategylib)) { - require_once($strategylib); +define('WORKSHOP_ALLOCATION_EXISTS', -1); // return status of {@link add_allocation} +define('WORKSHOP_ALLOCATION_WOSUBMISSION', -2); // return status of {@link add_allocation} + + +/** + * Full-featured workshop API + * + * This extends the module base API and adds the internal methods that are called + * from the module itself. The class should be initialized right after you get + * $workshop and $cm records at the begining of the script. + */ +class workshop_api extends workshop { + + /** grading strategy instance */ + protected $strategy_api=null; + + /** + * Initialize the object using the data from DB + * + * @param object $instance The instance data row from {workshop} table + * @param object $md Course module record + */ + public function __construct($instance, $cm) { + parent::__construct($instance, $cm); + } + + + /** + * Fetches all users with the capability mod/workshop:submit in the current context + * + * Static variable used to cache the results. The returned objects contain id, lastname + * and firstname properties and are ordered by lastname,firstname + * + * @param object $context The context instance where the capability should be checked + * @return array Array of '(int)userid => (stdClass)userinfo' + */ + public function get_peer_authors() { + static $users=null; + + if (is_null($users)) { + $context = get_context_instance(CONTEXT_MODULE, $this->cm->id); + $users = get_users_by_capability($context, 'mod/workshop:submit', + 'u.id, u.lastname, u.firstname', 'u.lastname,u.firstname', '', '', '', '', false, false, true); + } + return $users; + } + + + /** + * Fetches all users with the capability mod/workshop:peerassess in the current context + * + * Static variable used to cache the results. The returned objects contain id, lastname + * and firstname properties and are ordered by lastname,firstname + * + * @param object $context The context instance where the capability should be checked + * @return array Array of '(int)userid => (stdClass)userinfo' + */ + public function get_peer_reviewers() { + global $DB; + static $users=null; + + if (is_null($users)) { + $context = get_context_instance(CONTEXT_MODULE, $this->cm->id); + $users = get_users_by_capability($context, 'mod/workshop:peerassess', + 'u.id, u.lastname, u.firstname', 'u.lastname,u.firstname', '', '', '', '', false, false, true); + } + if (!$this->assesswosubmission) { + $userswithsubmission = array(); + // users without their own submission can not be reviewers + $rs = $DB->get_recordset_list('workshop_submissions', 'userid', array_keys($users),'', 'id,userid'); + foreach ($rs as $submission) { + if (isset($users[$submission->userid])) { + $userswithsubmission[$submission->userid] = 'submission_exists'; + } else { + // this is a submission by a user who does not have mod/workshop:peerassess + // this is either bug or workshop capabilities have been overridden after the submission + } + } + $rs->close(); + return array_intersect_key($users, $userswithsubmission); } else { - throw new moodle_exception('missingstrategy', 'workshop'); + return $users; } - $classname = 'workshop_' . $workshop->strategy . '_strategy'; - $instance = new $classname($workshop); } - return $instance; + + /** + * Returns submissions from this workshop + * + * Fetches data from {workshop_submissions} and adds some useful information from other + * tables. + * + * @param mixed $userid If set to integer ID, return submission of the given user only + * @param mixed $examples false|true|all Only regular submissions, only examples, all submissions + * @todo unittest + * @return object moodle_recordset + */ + public function get_submissions($userid='all', $examples=false) { + global $DB; + + $sql = 'SELECT s.*, u.lastname AS authorlastname, u.firstname AS authorfirstname + FROM {workshop_submissions} s + JOIN {user} u ON (s.userid = u.id) + WHERE s.workshopid = ?'; + $params[0] = $this->id; + + if ($examples === false) { + $sql .= ' AND example = 0'; + } + if ($examples === true) { + $sql .= ' AND example = 1'; + } + if (is_int($userid)) { + $sql .= ' AND userid = ?'; + $params = array_merge($params, array($userid)); + } + if (is_array($userid)) { + list($usql, $uparams) = $DB->get_in_or_equal($userid); + $sql .= ' AND userid ' . $usql; + $params = array_merge($params, $uparams); + } + + return $DB->get_recordset_sql($sql, $params); + } + + + /** + * Returns the list of assessments with some data added + * + * Fetches data from {workshop_assessments} and adds some useful information from other + * tables. + * + * @param mixed $reviewerid 'all'|int|array User ID of the reviewer + * @param mixed $id 'all'|int Assessment ID + * @return object moodle_recordset + */ + public function get_assessments($reviewerid='all', $id='all') { + global $DB; + + $sql = 'SELECT a.*, + reviewer.id AS reviewerid,reviewer.firstname AS reviewerfirstname,reviewer.lastname as reviewerlastname, + s.title, + author.id AS authorid, author.firstname AS authorfirstname,author.lastname as authorlastname + FROM {workshop_assessments} a + LEFT JOIN {user} reviewer ON (a.userid = reviewer.id) + LEFT JOIN {workshop_submissions} s ON (a.submissionid = s.id) + LEFT JOIN {user} author ON (s.userid = author.id) + WHERE s.workshopid = ?'; + $params = array($this->id); + if (is_int($reviewerid)) { + $sql .= ' AND reviewerid = ?'; + $params = array_merge($params, array($reviewerid)); + } + if (is_array($reviewerid)) { + list($usql, $uparams) = $DB->get_in_or_equal($reviewerid); + $sql .= ' AND reviewerid ' . $usql; + $params = array_merge($params, $uparams); + } + if (is_int($id)) { + $sql .= ' AND a.id = ?'; + $params = array_merge($params, array($id)); + } + + return $DB->get_recordset_sql($sql, $params); + } + + + /** + * Returns the list of allocations in the workshop + * + * This returns the list of all users who can submit their work or review submissions (or both + * which is the common case). So basically this is to return list of all students participating + * in the workshop. For every participant, it adds information about their submission and their + * reviews. This is mainly intended for allocation reports and originally was written for + * manula allocation ui. + * + * The returned structure is recordset of objects with following properties: + * [authorid] [authorfirstname] [authorlastname] [authorpicture] [authorimagealt] + * [submissionid] [submissiontitle] [submissiongrade] [assessmentid] + * [timeallocated] [reviewerid] [reviewerfirstname] [reviewerlastname] + * [reviewerpicture] [reviewerimagealt] + * + * This should be refactored when capability handling proposed by Petr is implemented so that + * we can check capabilities directly in SQL joins. + * + * @return object moodle_recordset + */ + public function get_allocations() { + global $DB; + static $users=null; + + if (is_null($users)) { + $context = get_context_instance(CONTEXT_MODULE, $this->cm->id); + $users = get_users_by_capability($context, array('mod/workshop:submit', 'mod/workshop:peerassess'), + 'u.id', 'u.lastname,u.firstname', '', '', '', '', false, false, true); + } + + list($usql, $params) = $DB->get_in_or_equal(array_keys($users)); + $params[] = $this->id; + + $sql = 'SELECT author.id AS authorid, author.firstname AS authorfirstname, author.lastname AS authorlastname, + author.picture AS authorpicture, author.imagealt AS authorimagealt, + s.id AS submissionid, s.title AS submissiontitle, s.grade AS submissiongrade, + a.id AS assessmentid, a.timecreated AS timeallocated, a.userid AS reviewerid, + reviewer.firstname AS reviewerfirstname, reviewer.lastname AS reviewerlastname, + reviewer.picture as reviewerpicture, reviewer.imagealt AS reviewerimagealt + FROM {user} author + LEFT JOIN {workshop_submissions} s ON (s.userid = author.id) + LEFT JOIN {workshop_assessments} a ON (s.id = a.submissionid) + LEFT JOIN {user} reviewer ON (a.userid = reviewer.id) + WHERE author.id ' . $usql . ' AND (s.workshopid = ? OR s.workshopid IS NULL) + ORDER BY author.lastname,author.firstname,reviewer.lastname,reviewer.firstname'; + return $DB->get_recordset_sql($sql, $params); + } + + + /** + * Allocate a submission to a user for review + * + * @param object $submission Submission record + * @param int $reviewerid User ID + * @return int ID of the new assessment or an error code + */ + public function add_allocation(stdClass $submission, $reviewerid) { + global $DB; + + if ($DB->record_exists('workshop_assessments', array('submissionid' => $submission->id, 'userid' => $reviewerid))) { + return WORKSHOP_ALLOCATION_EXISTS; + } + + if (!$this->assesswosubmission) { + // reviewer must have submitted his own work + if (!$DB->record_exists('workshop_submissions', array('workshopid' => $this->id, 'userid' => $reviewerid))) { + return WORKSHOP_ALLOCATION_WOSUBMISSION; + } + } + + $now = time(); + $assessment = new stdClass(); + $assessment->submissionid = $submission->id; + $assessment->userid = $reviewerid; + $assessment->timecreated = $now; + $assessment->timemodified = $now; + + return $DB->insert_record('workshop_assessments', $assessment); + } + + + /** + * delete_assessment + * + * @todo finish and document this method + * + */ + public function delete_assessment($id) { + global $DB; + + // todo remove all given grades from workshop_grades; + return $DB->delete_records('workshop_assessments', array('id' => $id)); + } + + + /** + * Returns instance of grading strategy class + * + * @param object $workshop Workshop record + * @return object Instance of a grading strategy + */ + public function grading_strategy_instance() { + + if (!($this->strategy === clean_param($workshop->strategy, PARAM_ALPHA))) { + throw new moodle_workshop_exception($this, 'invalidstrategyname'); + } + + if (is_null($this->strategy_api)) { + $strategylib = dirname(__FILE__) . '/grading/' . $workshop->strategy . '/strategy.php'; + if (is_readable($strategylib)) { + require_once($strategylib); + } else { + throw new moodle_exception('missingstrategy', 'workshop'); + } + $classname = 'workshop_' . $workshop->strategy . '_strategy'; + $this->strategy_api = new $classname($this); + if (!in_array('workshop_strategy', class_implements($this->strategy_api))) { + throw new moodle_workshop_exception($this, 'strategynotimplemented'); + } + } + + return $this->strategy_api; + } + + + } + + /** - * Return the user's submission record in the given workshop + * Class for workshop exceptions. Just saves a couple of arguments of the + * constructor for a moodle_exception. * - * Example submissions are not returned. This is intended to return a submission for given - * student. - * - * @param int $workshopid Workshop id - * @param int $userid Owner id - * @return mixed A fieldset object containing the first matching record or false if not found + * @param object $workshop Should be workshop or its subclass + * @param string $errorcode + * @param mixed $a Object/variable to pass to get_string + * @param string $link URL to continue after the error notice + * @param $debuginfo */ -function workshop_get_user_submission($workshopid, $userid) { - global $DB; +class moodle_workshop_exception extends moodle_exception { - return $DB->get_record('workshop_submissions', array('workshopid' => $workshopid, 'userid' => $userid, 'example' => 0)); + function __construct($workshop, $errorcode, $a = NULL, $link = '', $debuginfo = null) { + global $CFG; + + if (!$link) { + $link = $CFG->wwwroot . '/mod/workshop/view.php?a=' . $workshop->id; + } + if ('confirmsesskeybad' == $errorcode) { + $module = ''; + } else { + $module = 'workshop'; + } + parent::__construct($errorcode, $module, $link, $a, $debuginfo); + } } + diff --git a/mod/workshop/simpletest/testlib.php b/mod/workshop/simpletest/testlib.php index 4b8b431ae7..7496308564 100644 --- a/mod/workshop/simpletest/testlib.php +++ b/mod/workshop/simpletest/testlib.php @@ -24,7 +24,7 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -defined('MOODLE_INTERNAL')) || die(); +defined('MOODLE_INTERNAL') || die(); // Make sure the code being tested is accessible. require_once($CFG->dirroot . '/mod/workshop/lib.php'); // Include the code to test diff --git a/mod/workshop/simpletest/testworkshopapi.php b/mod/workshop/simpletest/testworkshopapi.php new file mode 100644 index 0000000000..3777d8c6fa --- /dev/null +++ b/mod/workshop/simpletest/testworkshopapi.php @@ -0,0 +1,65 @@ +. + + +/** + * Unit tests for workshop_api class defined in mod/workshop/locallib.php + * + * @package mod-workshop + * @copyright 2009 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +// Make sure the code being tested is accessible. +require_once($CFG->dirroot . '/mod/workshop/locallib.php'); // Include the code to test + + +/** + * Test subclass that makes all the protected methods we want to test public. + * Also re-implements bridging methods so we can test more easily. + */ +class testable_workshop_api extends workshop_api { + +} + + +/** + * Test cases for the internal workshop api + */ +class workshop_api_test extends UnitTestCase { + + /** workshop instance emulation */ + protected $workshop; + + /** setup testing environment */ + public function setUp() { + $workshoprecord = new stdClass; + $workshoprecord->id = 42; + + $cm = new stdClass; + $this->workshop = new testable_workshop_api($workshoprecord, $cm); + } + + public function tearDown() { + $this->workshop = null; + } + + + +} diff --git a/mod/workshop/submission.php b/mod/workshop/submission.php index a1c06b40ae..766051e324 100644 --- a/mod/workshop/submission.php +++ b/mod/workshop/submission.php @@ -61,7 +61,7 @@ if ($id) { // submission is specified } else { // no submission specified //todo require_capability('mod/workshop:submit', $context); - if (!$submission = workshop_get_user_submission($workshop->id, $USER->id)) { + if (!$submission = workshop_get_user_submission($workshop, $USER->id)) { $submission = new object(); $submission->id = null; } @@ -84,14 +84,8 @@ $submission->cmid = $cm->id; $mform = new workshop_submission_form(null, array('current' => $submission, 'cm' => $cm, 'workshop'=>$workshop, 'dataoptions' => $dataoptions, 'attachmentoptions'=>$attachmentoptions)); -if ($mform->is_cancelled()){ - die(); // todo - if ($id){ - redirect("view.php?id=$cm->id"); - } else { - redirect("view.php?id=$cm->id"); - } - +if ($mform->is_cancelled()) { + redirect("view.php?id=$cm->id"); } else if ($submission = $mform->get_data()) { $timenow = time(); diff --git a/mod/workshop/view.php b/mod/workshop/view.php index cc507f9030..871c167f28 100644 --- a/mod/workshop/view.php +++ b/mod/workshop/view.php @@ -28,7 +28,6 @@ */ require_once(dirname(dirname(dirname(__FILE__))).'/config.php'); -require_once(dirname(__FILE__).'/lib.php'); require_once(dirname(__FILE__).'/locallib.php'); $id = optional_param('id', 0, PARAM_INT); // course_module ID, or @@ -64,13 +63,18 @@ if ($id) { require_login($course, true, $cm); +$context = $PAGE->context; +$workshop = new workshop_api($workshop, $cm); + +// todo has_capability() check + add_to_log($course->id, "workshop", "view", "view.php?id=$cm->id", "$workshop->id"); /// Print the page header $PAGE->set_url('mod/workshop/view.php', array('id' => $cm->id)); $PAGE->set_title($workshop->name); -$PAGE->set_heading($course->shortname); +$PAGE->set_heading($course->fullname); $PAGE->set_button(update_module_button($cm->id, $course->id, get_string('modulename', 'workshop'))); // other things you may want to set - remove if not needed @@ -94,7 +98,10 @@ echo $OUTPUT->header($navigation, $menu); echo $OUTPUT->box_start(); echo $OUTPUT->heading('Workshop administration tools', 3); -echo "id}\">Edit grading form (".get_string('strategy' . $workshop->strategy, 'workshop').")"; +echo ''; echo $OUTPUT->box_end(); echo $OUTPUT->box_start(); @@ -105,13 +112,14 @@ echo $OUTPUT->box_end(); echo $OUTPUT->box_start(); echo $OUTPUT->heading(get_string('assessment', 'workshop'), 3); -$reviewstogive = workshop_get_assessments_for_reviewer($workshop->id, $USER->id); +$rs = $workshop->get_assessments($USER->id); echo "You are expected to assess following submissions:"; echo ""; +$rs->close(); echo $OUTPUT->box_end(); echo $OUTPUT->footer(); -- 2.39.5