From: David Mudrak Date: Mon, 4 Jan 2010 18:00:54 +0000 (+0000) Subject: Mostly working on the view.php UI plus some changes X-Git-Url: http://git.mjollnir.org/gw?a=commitdiff_plain;h=b761e6d9c619733eea20bdb03f3dc7b27d613b5b;p=moodle.git Mostly working on the view.php UI plus some changes --- diff --git a/mod/workshop/allocation.php b/mod/workshop/allocation.php index d3e35b65c4..cb03276a17 100644 --- a/mod/workshop/allocation.php +++ b/mod/workshop/allocation.php @@ -70,7 +70,7 @@ if (!empty($initresult)) { echo $OUTPUT->container_end(); } else { echo $OUTPUT->container_start('allocator-ui'); - $allocator->ui($wsoutput); + echo $allocator->ui(); echo $OUTPUT->container_end(); } echo $OUTPUT->footer(); diff --git a/mod/workshop/allocation/lib.php b/mod/workshop/allocation/lib.php index 7b429c02cd..ec9fb60300 100644 --- a/mod/workshop/allocation/lib.php +++ b/mod/workshop/allocation/lib.php @@ -42,10 +42,11 @@ interface workshop_allocator { * * 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 + * It should not generate any output. However if it does so, the output is rendered + * using the method {@link moodle_mod_workshop_renderer::allocation_init_result()} * * @throws moodle_exception - * @return mixed void or optional HTML string + * @return void|string */ public function init(); @@ -53,13 +54,9 @@ interface workshop_allocator { * Print HTML to be displayed as the user interface * * If a form is part of the UI, the caller should have called $PAGE->set_url(...) - * The methods must produce output instead of just returning it so mform->display() can - * be used there. This should be changed once we make quickforms deprecated and then, - * this method will just return the required HTML code. * * @param stdClass $wsoutput workshop module renderer can be used - * @return void + * @return string HTML code to be echoed */ - public function ui(moodle_mod_workshop_renderer $wsoutput); - + public function ui(); } diff --git a/mod/workshop/allocation/manual/allocator.php b/mod/workshop/allocation/manual/allocator.php index 40f04aa246..3fc6156c4a 100644 --- a/mod/workshop/allocation/manual/allocator.php +++ b/mod/workshop/allocation/manual/allocator.php @@ -28,22 +28,20 @@ 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_CONFIRM_DEL', 4); -define('WORKSHOP_ALLOCATION_MANUAL_MSG_DELETED', 5); -define('WORKSHOP_ALLOCATION_MANUAL_MSG_DELETE_ERROR', 6); - /** * Allows users to allocate submissions for review manually */ class workshop_manual_allocator implements workshop_allocator { - /** workshop instance */ + /** constants that are used to pass status messages between init() and ui() */ + const MSG_ADDED = 1; + const MSG_NOSUBMISSION = 2; + const MSG_EXISTS = 3; + const MSG_CONFIRM_DEL = 4; + const MSG_DELETED = 5; + const MSG_DELETE_ERROR = 6; + + /** @var workshop instance */ protected $workshop; /** @@ -72,18 +70,18 @@ class workshop_manual_allocator implements workshop_allocator { $submission = $this->workshop->get_submission_by_author($authorid); if (!$submission) { // nothing submitted by the given user - $m[] = WORKSHOP_ALLOCATION_MANUAL_MSG_NOSUBMISSION; + $m[] = self::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; + if ($res == workshop::ALLOCATION_EXISTS) { + $m[] = self::MSG_EXISTS; $m[] = $submission->userid; $m[] = $reviewerid; } else { - $m[] = WORKSHOP_ALLOCATION_MANUAL_MSG_ADDED; + $m[] = self::MSG_ADDED; $m[] = $submission->userid; $m[] = $reviewerid; } @@ -100,7 +98,7 @@ class workshop_manual_allocator implements workshop_allocator { $assessment = $this->workshop->get_assessment_by_id($assessmentid); if ($assessment) { if (!$confirmed) { - $m[] = WORKSHOP_ALLOCATION_MANUAL_MSG_CONFIRM_DEL; + $m[] = self::MSG_CONFIRM_DEL; $m[] = $assessment->id; $m[] = $assessment->authorid; $m[] = $assessment->reviewerid; @@ -111,11 +109,11 @@ class workshop_manual_allocator implements workshop_allocator { } } else { if($this->workshop->delete_assessment($assessment->id)) { - $m[] = WORKSHOP_ALLOCATION_MANUAL_MSG_DELETED; + $m[] = self::MSG_DELETED; $m[] = $assessment->authorid; $m[] = $assessment->reviewerid; } else { - $m[] = WORKSHOP_ALLOCATION_MANUAL_MSG_DELETE_ERROR; + $m[] = self::MSG_DELETE_ERROR; $m[] = $assessment->authorid; $m[] = $assessment->reviewerid; } @@ -130,9 +128,10 @@ class workshop_manual_allocator implements workshop_allocator { /** * Prints user interface - current allocation and a form to edit it */ - public function ui(moodle_mod_workshop_renderer $wsoutput) { + public function ui() { global $PAGE; global $CFG; // bacause we include other libs here + global $OUTPUT; $hlauthorid = -1; // highlight this author $hlreviewerid = -1; // highlight this reviewer @@ -142,24 +141,24 @@ class workshop_manual_allocator implements workshop_allocator { if ($m) { $m = explode('-', $m); // unserialize switch ($m[0]) { - case WORKSHOP_ALLOCATION_MANUAL_MSG_ADDED: + case self::MSG_ADDED: $hlauthorid = $m[1]; $hlreviewerid = $m[2]; $msg->text = get_string('allocationadded', 'workshopallocation_manual'); $msg->sty = 'ok'; break; - case WORKSHOP_ALLOCATION_MANUAL_MSG_EXISTS: + case self::MSG_EXISTS: $hlauthorid = $m[1]; $hlreviewerid = $m[2]; $msg->text = get_string('allocationexists', 'workshopallocation_manual'); $msg->sty = 'info'; break; - case WORKSHOP_ALLOCATION_MANUAL_MSG_NOSUBMISSION: + case self::MSG_NOSUBMISSION: $hlauthorid = $m[1]; $msg->text = get_string('nosubmissionfound', 'workshop'); $msg->sty = 'error'; break; - case WORKSHOP_ALLOCATION_MANUAL_MSG_CONFIRM_DEL: + case self::MSG_CONFIRM_DEL: $hlauthorid = $m[2]; $hlreviewerid = $m[3]; if ($m[4] == 0) { @@ -170,55 +169,58 @@ class workshop_manual_allocator implements workshop_allocator { $msg->sty = 'error'; } break; - case WORKSHOP_ALLOCATION_MANUAL_MSG_DELETED: + case self::MSG_DELETED: $hlauthorid = $m[1]; $hlreviewerid = $m[2]; $msg->text = get_string('assessmentdeleted', 'workshop'); $msg->sty = 'ok'; break; - case WORKSHOP_ALLOCATION_MANUAL_MSG_DELETE_ERROR: + case self::MSG_DELETE_ERROR: $hlauthorid = $m[1]; $hlreviewerid = $m[2]; $msg->text = get_string('assessmentnotdeleted', 'workshop'); $msg->sty = 'error'; break; } - if ($m[0] == WORKSHOP_ALLOCATION_MANUAL_MSG_CONFIRM_DEL) { - $handler = $PAGE->url->out_action(); - $msg->extra = print_single_button($handler, array('mode' => 'del', 'what' => $m[1], 'confirm' => 1), - get_string('iamsure', 'workshop'), 'post', '', true); + if ($m[0] == self::MSG_CONFIRM_DEL) { + $form = new html_form(); + $form->url = new moodle_url($PAGE->url, array('mode' => 'del', 'what' => $m[1], 'confirm' => 1)); + $form->button = new html_button(); + $form->button->text = get_string('iamsure', 'workshop'); + $form->method = 'post'; + $msg->extra = $OUTPUT->button($form); } } - $peer = array(); // singular chosen due to readibility + $peers = array(); $rs = $this->workshop->get_allocations_recordset(); 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]->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 (!isset($peers[$currentuserid])) { + $peers[$currentuserid] = new stdClass(); + $peers[$currentuserid]->id = $allocation->authorid; + $peers[$currentuserid]->firstname = $allocation->authorfirstname; + $peers[$currentuserid]->lastname = $allocation->authorlastname; + $peers[$currentuserid]->picture = $allocation->authorpicture; + $peers[$currentuserid]->imagealt = $allocation->authorimagealt; + $peers[$currentuserid]->submissionid = $allocation->submissionid; + $peers[$currentuserid]->submissiontitle = $allocation->submissiontitle; + $peers[$currentuserid]->submissiongrade = $allocation->submissiongrade; + $peers[$currentuserid]->reviewedby = array(); // users who are reviewing this user's submission + $peers[$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; + $peers[$currentuserid]->reviewedby[$allocation->reviewerid] = $allocation->assessmentid; } } $rs->close(); - foreach ($peer as $author) { + foreach ($peers as $author) { foreach ($author->reviewedby as $reviewerid => $assessmentid) { - if (isset($peer[$reviewerid])) { + if (isset($peers[$reviewerid])) { // example: "user with id 87 is reviewer of the work submitted by user id 45 in the assessment record 12" - $peer[$reviewerid]->reviewerof[$author->id] = $assessmentid; + $peers[$reviewerid]->reviewerof[$author->id] = $assessmentid; } } } @@ -227,8 +229,7 @@ class workshop_manual_allocator implements workshop_allocator { // Here, we do not use neither the core renderer nor the workshop one but use an own one require_once(dirname(__FILE__) . '/renderer.php'); $uioutput = $PAGE->theme->get_renderer('workshopallocation_manual', $PAGE); - echo $uioutput->display_allocations($this->workshop, $peer, $hlauthorid, $hlreviewerid, $msg); + return $uioutput->display_allocations($this->workshop, $peers, $hlauthorid, $hlreviewerid, $msg); } } - diff --git a/mod/workshop/allocation/random/allocator.php b/mod/workshop/allocation/random/allocator.php index fe11840259..a4e10108e5 100644 --- a/mod/workshop/allocation/random/allocator.php +++ b/mod/workshop/allocation/random/allocator.php @@ -31,22 +31,18 @@ require_once(dirname(dirname(__FILE__)) . '/lib.php'); // inter require_once(dirname(dirname(dirname(__FILE__))) . '/locallib.php'); // workshop internal API require_once(dirname(__FILE__) . '/settings_form.php'); // settings form -/** - * Constants used to pass status messages between init() and ui() - */ -define('WORKSHOP_ALLOCATION_RANDOM_MSG_SUCCESS', 1); - -/** - * Constants used in allocation settings form - */ -define('WORKSHOP_USERTYPE_AUTHOR', 1); -define('WORKSHOP_USERTYPE_REVIEWER', 2); - /** * Allocates the submissions randomly */ class workshop_random_allocator implements workshop_allocator { + /** constants used to pass status messages between init() and ui() */ + const MSG_SUCCESS = 1; + + /** constants used in allocation settings form */ + const USERTYPE_AUTHOR = 1; + const USERTYPE_REVIEWER = 2; + /** workshop instance */ protected $workshop; @@ -157,21 +153,30 @@ class workshop_random_allocator implements workshop_allocator { } /** - * Prints user interface + * Returns the HTML code to print the user interface */ - public function ui(moodle_mod_workshop_renderer $wsoutput) { - global $OUTPUT; + public function ui() { + global $OUTPUT, $PAGE; $m = optional_param('m', null, PARAM_INT); // status message code $msg = new stdClass(); - if ($m == WORKSHOP_ALLOCATION_RANDOM_MSG_SUCCESS) { + if ($m == self::MSG_SUCCESS) { $msg = (object)array('text' => get_string('randomallocationdone', 'workshopallocation_random'), 'sty' => 'ok'); } - echo $OUTPUT->container_start('random-allocator'); - echo $wsoutput->status_message($msg); + $out = ''; + $out .= $OUTPUT->container_start('random-allocator'); + $wsoutput = $PAGE->theme->get_renderer('mod_workshop', $PAGE); + $out .= $wsoutput->status_message($msg); + // the nasty hack follows to bypass the sad fact that moodle quickforms do not allow to actually + // return the HTML content, just to display it + ob_start(); $this->mform->display(); - echo $OUTPUT->container_end(); + $out .= ob_get_contents(); + ob_end_clean(); + $out .= $OUTPUT->container_end(); + + return $out; } /** @@ -232,7 +237,7 @@ class workshop_random_allocator implements workshop_allocator { } $submission = $submissions[$authorid]; $status = $this->workshop->add_allocation($submission, $reviewerid, true); - if (WORKSHOP_ALLOCATION_EXISTS == $status) { + if (workshop::ALLOCATION_EXISTS == $status) { debugging('newallocations array contains existing allocation, this should not happen'); } } @@ -342,14 +347,14 @@ class workshop_random_allocator implements workshop_allocator { // nothing to be done return array(); } - if (WORKSHOP_USERTYPE_AUTHOR == $numper) { + if (self::USERTYPE_AUTHOR == $numper) { // circles are authors, squares are reviewers $o[] = 'info::Trying to allocate ' . $numofreviews . ' review(s) per author'; // todo translate $allcircles = $authors; $allsquares = $reviewers; // get current workload list($circlelinks, $squarelinks) = $this->convert_assessments_to_links($assessments); - } elseif (WORKSHOP_USERTYPE_REVIEWER == $numper) { + } elseif (self::USERTYPE_REVIEWER == $numper) { // circles are reviewers, squares are authors $o[] = 'info::trying to allocate ' . $numofreviews . ' review(s) per reviewer'; // todo translate $allcircles = $reviewers; @@ -458,7 +463,7 @@ class workshop_random_allocator implements workshop_allocator { } // end of processing circles in the group } // end of processing circle groups $returned = array(); - if (WORKSHOP_USERTYPE_AUTHOR == $numper) { + if (self::USERTYPE_AUTHOR == $numper) { // circles are authors, squares are reviewers foreach ($circlelinks as $circleid => $squares) { foreach ($squares as $squareid) { @@ -466,7 +471,7 @@ class workshop_random_allocator implements workshop_allocator { } } } - if (WORKSHOP_USERTYPE_REVIEWER == $numper) { + if (self::USERTYPE_REVIEWER == $numper) { // circles are reviewers, squares are authors foreach ($circlelinks as $circleid => $squares) { foreach ($squares as $squareid) { diff --git a/mod/workshop/allocation/random/settings_form.php b/mod/workshop/allocation/random/settings_form.php index 178a009314..c2585f16a2 100644 --- a/mod/workshop/allocation/random/settings_form.php +++ b/mod/workshop/allocation/random/settings_form.php @@ -60,14 +60,16 @@ class workshop_random_allocator_form extends moodleform { } $mform->addElement('static', 'groupmode', get_string('groupmode', 'group'), $grouplabel); - $options_numofreviewes = array(0=>0,1=>1, 2=>2, 3=>3, 4=>4); - $options_numper = array(WORKSHOP_USERTYPE_AUTHOR => get_string('numperauthor', 'workshopallocation_random'), - WORKSHOP_USERTYPE_REVIEWER => get_string('numperreviewer', 'workshopallocation_random')); + $options_numofreviewes = array(0=>0,1=>1, 2=>2, 3=>3, 4=>4); // todo + $options_numper = array( + workshop_random_allocator::USERTYPE_AUTHOR => get_string('numperauthor', 'workshopallocation_random'), + workshop_random_allocator::USERTYPE_REVIEWER => get_string('numperreviewer', 'workshopallocation_random') + ); $grpnumofreviews = array(); $grpnumofreviews[] = $mform->createElement('select', 'numofreviews', '', $options_numofreviewes); $mform->setDefault('numofreviews', 4); $grpnumofreviews[] = $mform->createElement('select', 'numper', '', $options_numper); - $mform->setDefault('numper', WORKSHOP_USERTYPE_AUTHOR); + $mform->setDefault('numper', workshop_random_allocator::USERTYPE_AUTHOR); $mform->addGroup($grpnumofreviews, 'grpnumofreviews', get_string('numofreviews', 'workshop'), array(' '), false); $mform->addElement('advcheckbox', 'removecurrent', get_string('removecurrentallocations', 'workshopallocation_random')); diff --git a/mod/workshop/assessment.php b/mod/workshop/assessment.php index d2da52f6e1..246f20547e 100644 --- a/mod/workshop/assessment.php +++ b/mod/workshop/assessment.php @@ -58,19 +58,22 @@ if ('preview' == $mode) { $PAGE->set_url($workshop->previewform_url()); $PAGE->set_title($workshop->name); $PAGE->set_heading($course->fullname); - $PAGE->navbar->add(get_string('editingassessmentform', 'workshop'), null, null, navigation_node::TYPE_CUSTOM, - $workshop->editform_url()); + $PAGE->navbar->add(get_string('editingassessmentform', 'workshop'), $workshop->editform_url(), navigation_node::TYPE_CUSTOM); $PAGE->navbar->add(get_string('previewassessmentform', 'workshop')); + $currenttab = 'editform'; } elseif ('assessment' == $mode) { - if (!has_any_capability(array('mod/workshop:peerassess', 'mod/workshop:assessallsubmissions'), $PAGE->context)) { + // we do not require 'mod/workshop:peerassess' here, we just check that the assessment record + // has been prepared for the current user. So even a user without the peerassess capability + // (like a 'teacher', for example) can become a reviewer + if ($USER->id !== $assessment->userid) { print_error('nopermissions', '', $workshop->view_url()); } - // todo do a check that the user is allowed to assess this submission $PAGE->set_url($workshop->assess_url($assessment->id)); $PAGE->set_title($workshop->name); $PAGE->set_heading($course->fullname); $PAGE->navbar->add(get_string('assessingsubmission', 'workshop')); + $currenttab = 'assessment'; } // load the grading strategy logic @@ -90,6 +93,7 @@ if ($mform->is_cancelled()) { $rawgrade = $strategy->save_assessment($assessment, $data); if (!is_null($rawgrade) and isset($data->saveandclose)) { echo $OUTPUT->header(); + include(dirname(__FILE__) . '/tabs.php'); echo $OUTPUT->heading(get_string('assessmentresult', 'workshop'), 2); echo $OUTPUT->box('Given grade: ' . sprintf("%01.2f", $rawgrade * 100) . ' %'); // todo more detailed info using own renderer echo $OUTPUT->continue_button($workshop->view_url()); @@ -105,6 +109,7 @@ if ($mform->is_cancelled()) { // Output starts here echo $OUTPUT->header(); +include(dirname(__FILE__) . '/tabs.php'); echo $OUTPUT->heading(get_string('assessmentform', 'workshop'), 2); if ('assessment' === $mode) { diff --git a/mod/workshop/editform.php b/mod/workshop/editform.php index 6e613cd2f8..63db56c066 100644 --- a/mod/workshop/editform.php +++ b/mod/workshop/editform.php @@ -68,6 +68,8 @@ if ($mform->is_cancelled()) { // Output starts here echo $OUTPUT->header(); +$currenttab = 'editform'; +include(dirname(__FILE__) . '/tabs.php'); echo $OUTPUT->heading(get_string('pluginname', 'workshopgrading_' . $workshop->strategy)); $mform->display(); diff --git a/mod/workshop/grading/accumulative/strategy.php b/mod/workshop/grading/accumulative/strategy.php index 027db51ba7..6b750e6df2 100644 --- a/mod/workshop/grading/accumulative/strategy.php +++ b/mod/workshop/grading/accumulative/strategy.php @@ -226,6 +226,18 @@ class workshop_accumulative_strategy implements workshop_strategy { return $this->update_peer_grade($assessment); } + /** + * Has the assessment form been defined and is ready to be used by the reviewers? + * + * @return boolean + */ + public function form_ready() { + if (count($this->dimensions) > 0) { + return true; + } + return false; + } + /// Internal methods /** diff --git a/mod/workshop/grading/lib.php b/mod/workshop/grading/lib.php index 0beafa65d0..1efde37335 100644 --- a/mod/workshop/grading/lib.php +++ b/mod/workshop/grading/lib.php @@ -71,4 +71,11 @@ interface workshop_strategy { * @return float|null Raw grade (0 to 1) for submission as suggested by the peer or null if impossible to count */ public function save_assessment(stdClass $assessment, stdClass $data); + + /** + * Has the assessment form been defined and is ready to be used by the reviewers? + * + * @return boolean + */ + public function form_ready(); } diff --git a/mod/workshop/grading/noerrors/strategy.php b/mod/workshop/grading/noerrors/strategy.php index e92b2c48f8..77e9021610 100644 --- a/mod/workshop/grading/noerrors/strategy.php +++ b/mod/workshop/grading/noerrors/strategy.php @@ -263,6 +263,18 @@ class workshop_noerrors_strategy implements workshop_strategy { return $this->update_peer_grade($assessment); } + /** + * Has the assessment form been defined and is ready to be used by the reviewers? + * + * @return boolean + */ + public function form_ready() { + if (count($this->dimensions) > 0) { + return true; + } + return false; + } + /// Internal methods /** diff --git a/mod/workshop/lang/en_utf8/workshop.php b/mod/workshop/lang/en_utf8/workshop.php index 45f93c29da..c439afec08 100644 --- a/mod/workshop/lang/en_utf8/workshop.php +++ b/mod/workshop/lang/en_utf8/workshop.php @@ -26,6 +26,16 @@ defined('MOODLE_INTERNAL') || die(); $string[''] = ''; +$string['phaseclosed'] = 'Closed'; +$string['phaseevaluation'] = 'Grading evaluation phase'; +$string['phaseassessment'] = 'Assessment phase'; +$string['phasesubmission'] = 'Submission phase'; +$string['phasesetup'] = 'Setup'; +$string['taskassessself'] = 'Assess yourself'; +$string['taskassesspeers'] = 'Assess peers'; +$string['taskassesspeersinfo'] = 'total: $a->total
pending: $a->todo'; +$string['tasksubmit'] = 'Submit your work'; +$string['taskeditform'] = 'Define the assessment form'; $string['accesscontrol'] = 'Access control'; $string['agreeassessments'] = 'Assessments must be agreed'; $string['agreeassessmentsdesc'] = 'Authors may comment assessments of their work and agree/disagree with it'; @@ -62,6 +72,7 @@ $string['confignexassessments'] = 'Default number of examples to be reviewed by $string['confignsassessments'] = 'Default number of allocated submissions to be reviewed by a user in the assessment phase'; $string['configstrategy'] = 'Default grading strategy for workshops'; $string['editassessmentform'] = 'Edit assessment form'; +$string['editassessmentformstrategy'] = 'Edit assessment form ($a)'; $string['editingassessmentform'] = 'Editing assessment form'; $string['editingsubmission'] = 'Editing submission'; $string['editsubmission'] = 'Edit submission'; diff --git a/mod/workshop/lib.php b/mod/workshop/lib.php index 57443bc46b..016f254a09 100644 --- a/mod/workshop/lib.php +++ b/mod/workshop/lib.php @@ -47,6 +47,29 @@ define('WORKSHOP_COMPARISON_NORMAL', 2); /* f = 2.50 */ define('WORKSHOP_COMPARISON_HIGH', 3); /* f = 3.00 */ define('WORKSHOP_COMPARISON_VERYHIGH', 4); /* f = 5.00 */ +/** + * Returns the information if the module supports a feature + * + * @see plugin_supports() in lib/moodlelib.php + * @param string $feature FEATURE_xx constant for requested feature + * @return mixed true if the feature is supported, null if unknown + */ +function workshop_supports($feature) { + switch($feature) { + case FEATURE_GRADE_HAS_GRADE: return true; + case FEATURE_GROUPS: return true; + case FEATURE_GROUPINGS: return true; + case FEATURE_GROUPMEMBERSONLY: return true; + case FEATURE_MOD_INTRO: return true; + case FEATURE_MOD_SUBPLUGINS: return array( + 'workshopgrading' => 'mod/workshop/grading', + 'workshopallocation' => 'mod/workshop/allocation' + ); + default: return null; + } +} + + /** * Saves a new instance of the workshop into the database * @@ -223,29 +246,6 @@ function workshop_install() { return true; } -/** - * Returns the information if the module supports a feature - * - * @see plugin_supports() in lib/moodlelib.php - * @todo review and add features - * @param string $feature FEATURE_xx constant for requested feature - * @return mixed true if the feature is supported, null if unknown - */ -function workshop_supports($feature) { - switch($feature) { - case FEATURE_GRADE_HAS_GRADE: return true; - case FEATURE_GROUPS: return true; - case FEATURE_GROUPINGS: return true; - case FEATURE_GROUPMEMBERSONLY: return true; - case FEATURE_MOD_INTRO: return true; - case FEATURE_MOD_SUBPLUGINS: return array( - 'workshopgrading' => 'mod/workshop/grading', - 'workshopallocation' => 'mod/workshop/allocation' - ); - default: return null; - } -} - /** * Returns all other caps used in the module * @@ -436,6 +436,8 @@ function workshop_get_file_info($browser, $areas, $course, $cm, $context, $filea /** * Extends the global navigation tree by adding workshop nodes if there is a relevant content * + * This can be called by an AJAX request so do not rely on $PAGE as it might not be set up properly. + * * @param navigation_node $navref An object representing the navigation tree node of the workshop module instance * @param stdClass $course * @param stdClass $module @@ -446,14 +448,16 @@ function workshop_extend_navigation(navigation_node $navref, stdClass $course, s if (has_capability('mod/workshop:submit', $cm->context)) { $url = new moodle_url($CFG->wwwroot.'/mod/workshop/submission.php', array('cmid' => $cm->id)); - $mysubmissionkey = $navref->add(get_string('mysubmission', 'workshop'), null, null, navigation_node::TYPE_CUSTOM, $url); + $mysubmissionkey = $navref->add(get_string('mysubmission', 'workshop'), $url); + $navref->get($mysubmissionkey)->mainnavonly = true; } } /** * Extends the settings navigation with the Workshop settings - * This function is called when the context for the page is a workshop module. + * This function is called when the context for the page is a workshop module. This is not called by AJAX + * so it is safe to rely on the $PAGE. * * @param settings_navigation $settingsnav {@link settings_navigation} * @param stdClass $module @@ -467,13 +471,13 @@ function workshop_extend_settings_navigation(settings_navigation $settingsnav, s $workshopnode->forceopen = true; //$workshopobject = $DB->get_record("workshop", array("id" => $PAGE->cm->instance)); - if (has_capability('mod/workshop:editdimensions', $PAGE->context)) { + if (has_capability('mod/workshop:editdimensions', $PAGE->cm->context)) { $url = new moodle_url($CFG->wwwroot . '/mod/workshop/editform.php', array('cmid' => $PAGE->cm->id)); - $workshopnode->add(get_string('editassessmentform', 'workshop'), null, null, settings_navigation::TYPE_SETTING, $url); + $workshopnode->add(get_string('editassessmentform', 'workshop'), $url, settings_navigation::TYPE_SETTING); } if (has_capability('mod/workshop:allocate', $PAGE->context)) { $url = new moodle_url($CFG->wwwroot . '/mod/workshop/allocation.php', array('cmid' => $PAGE->cm->id)); - $workshopnode->add(get_string('allocate', 'workshop'), null, null, settings_navigation::TYPE_SETTING, $url); + $workshopnode->add(get_string('allocate', 'workshop'), $url, settings_navigation::TYPE_SETTING); } } diff --git a/mod/workshop/locallib.php b/mod/workshop/locallib.php index 6b861dcec4..d95295dea9 100644 --- a/mod/workshop/locallib.php +++ b/mod/workshop/locallib.php @@ -32,16 +32,6 @@ defined('MOODLE_INTERNAL') || die(); require_once(dirname(__FILE__).'/lib.php'); // we extend this library here -define('WORKSHOP_ALLOCATION_EXISTS', -1); // return status of {@link add_allocation} -define('WORKSHOP_ALLOCATION_ERROR', -2); // can be passed to a workshop renderer method - -/** workshop phases */ -define('WORKSHOP_PHASE_SETUP', 10); // The moderator is setting up the workshop -define('WORKSHOP_PHASE_SUBMISSION', 20); // Participants work on their submissions -define('WORKSHOP_PHASE_ASSESSMENT', 30); // -define('WORKSHOP_PHASE_EVALUATION', 40); -define('WORKSHOP_PHASE_CLOSED', 50); - /** * Full-featured workshop API * @@ -51,13 +41,30 @@ define('WORKSHOP_PHASE_CLOSED', 50); */ class workshop { + /** return statuses of {@link add_allocation} to be passed to a workshop renderer method */ + const ALLOCATION_EXISTS = -1; + const ALLOCATION_ERROR = -2; + + /** the internal code of the workshop phases as are stored in the database */ + const PHASE_SETUP = 10; + const PHASE_SUBMISSION = 20; + const PHASE_ASSESSMENT = 30; + const PHASE_EVALUATION = 40; + const PHASE_CLOSED = 50; + /** @var stdClass course module record */ public $cm = null; /** @var stdClass course record */ public $course = null; - /** @var workshop_strategy grading strategy instance */ + /** @var stdClass the workshop instance context */ + public $context = null; + + /** + * @var workshop_strategy grading strategy instance + * Do not use directly, get the instance using {@link workshop::grading_strategy_instance()} + */ protected $strategyinstance = null; /** @var stdClass underlying database record */ @@ -77,6 +84,7 @@ class workshop { $this->dbrecord = $dbrecord; $this->cm = $cm; $this->course = $course; + $this->context = get_context_instance(CONTEXT_MODULE, $this->cm->id); } /** @@ -104,8 +112,7 @@ class workshop { public function get_peer_authors($musthavesubmission=true) { global $DB; - $context = get_context_instance(CONTEXT_MODULE, $this->cm->id); - $users = get_users_by_capability($context, 'mod/workshop:submit', + $users = get_users_by_capability($this->context, 'mod/workshop:submit', 'u.id, u.lastname, u.firstname', 'u.lastname,u.firstname', '', '', '', '', false, false, true); if ($musthavesubmission) { @@ -135,8 +142,7 @@ class workshop { public function get_peer_reviewers($musthavesubmission=false) { global $DB; - $context = get_context_instance(CONTEXT_MODULE, $this->cm->id); - $users = get_users_by_capability($context, 'mod/workshop:peerassess', + $users = get_users_by_capability($this->context, 'mod/workshop:peerassess', 'u.id, u.lastname, u.firstname', 'u.lastname,u.firstname', '', '', '', '', false, false, true); if ($musthavesubmission) { @@ -417,8 +423,7 @@ class workshop { public function get_allocations_recordset() { global $DB; - $context = get_context_instance(CONTEXT_MODULE, $this->cm->id); - $users = get_users_by_capability($context, array('mod/workshop:submit', 'mod/workshop:peerassess'), + $users = get_users_by_capability($this->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), SQL_PARAMS_NAMED); @@ -452,7 +457,7 @@ class workshop { global $DB; if ($DB->record_exists('workshop_assessments', array('submissionid' => $submission->id, 'userid' => $reviewerid))) { - return WORKSHOP_ALLOCATION_EXISTS; + return self::ALLOCATION_EXISTS; } $now = time(); @@ -496,12 +501,12 @@ class workshop { if (is_readable($strategylib)) { require_once($strategylib); } else { - throw new moodle_exception('missingstrategy', 'workshop'); + throw new coding_exception('the grading subplugin must contain library ' . $strategylib); } $classname = 'workshop_' . $this->strategy . '_strategy'; $this->strategyinstance = new $classname($this); if (!in_array('workshop_strategy', class_implements($this->strategyinstance))) { - throw new moodle_exception('strategynotimplemented', 'workshop'); + throw new coding_exception($classname . ' does not implement workshop_strategy interface'); } } return $this->strategyinstance; @@ -624,4 +629,124 @@ class workshop { public function strategy_name() { return get_string('pluginname', 'workshopgrading_' . $this->strategy); } + + /** + * Prepare an individual workshop plan for the given user. + * + * @param mixed $userid + * @return TODO + */ + public function prepare_user_plan($userid) { + global $DB; + + $phases = array(); + + // Prepare tasks for the setup phase + $phase = new stdClass(); + $phase->title = get_string('phasesetup', 'workshop'); + $phase->tasks = array(); + if (has_capability('mod/workshop:editdimensions', $this->context, $userid)) { + $task = new stdClass(); + $task->title = get_string('taskeditform', 'workshop'); + $task->completed = $this->assessment_form_ready(); + $phase->tasks['editform'] = $task; + } + $phases[self::PHASE_SETUP] = $phase; + + // Prepare tasks for the submission phase + $phase = new stdClass(); + $phase->title = get_string('phasesubmission', 'workshop'); + $phase->tasks = array(); + if (has_capability('mod/workshop:submit', $this->context, $userid)) { + $task = new stdClass(); + $task->title = get_string('tasksubmit', 'workshop'); + $task->completed = $DB->record_exists('workshop_submissions', + array('workshopid' => $this->id, 'example' => 0, 'userid' => $userid)); + $phase->tasks['submit'] = $task; + } + $phases[self::PHASE_SUBMISSION] = $phase; + + // Prepare tasks for the peer-assessment phase (includes eventual self-assessments) + $phase = new stdClass(); + $phase->title = get_string('phaseassessment', 'workshop'); + $phase->tasks = array(); + $phase->isreviewer = has_capability('mod/workshop:peerassess', $this->context, $userid); + $phase->assessments = $this->get_assessments($userid); // todo make sure this does not contain assessment of examples + $numofpeers = 0; // number of allocated peer-assessments + $numofpeerstodo = 0; // number of peer-assessments to do + $numofself = 0; // number of allocated self-assessments - should be 0 or 1 + $numofselftodo = 0; // number of self-assessments to do - should be 0 or 1 + foreach ($phase->assessments as $a) { + if ($a->authorid == $userid) { + $numofself++; + if (is_null($a->grade)) { + $numofselftodo++; + } + } else { + $numofpeers++; + if (is_null($a->grade)) { + $numofpeerstodo++; + } + } + } + unset($a); + if ($numofpeers) { + $task = new stdClass(); + $task->completed = ($numofpeerstodo == 0); + $a = new stdClass(); + $a->total = $numofpeers; + $a->todo = $numofpeerstodo; + $task->title = get_string('taskassesspeers', 'workshop'); + $task->info = get_string('taskassesspeersinfo', 'workshop', $a); + unset($a); + $phase->tasks['assesspeers'] = $task; + } + if ($numofself) { + $task = new stdClass(); + $task->completed = ($numofselftodo == 0); + $task->title = get_string('taskassessself', 'workshop'); + $phase->tasks['assessself'] = $task; + } + $phases[self::PHASE_ASSESSMENT] = $phase; + + // Prepare tasks for the grading evaluation phase - todo + $phase = new stdClass(); + $phase->title = get_string('phaseevaluation', 'workshop'); + $phase->tasks = array(); + $phases[self::PHASE_EVALUATION] = $phase; + + // Prepare tasks for the "workshop closed" phase - todo + $phase = new stdClass(); + $phase->title = get_string('phaseclosed', 'workshop'); + $phase->tasks = array(); + $phases[self::PHASE_CLOSED] = $phase; + + // Polish data, set default values if not done explicitly + foreach ($phases as $phasecode => $phase) { + $phase->title = isset($phase->title) ? $phase->title : ''; + $phase->tasks = isset($phase->tasks) ? $phase->tasks : array(); + if ($phasecode == $this->phase) { + $phase->active = true; + } else { + $phase->active = false; + } + + foreach ($phase->tasks as $taskcode => $task) { + $task->title = isset($task->title) ? $task->title : ''; + $task->info = isset($task->info) ? $task->info : ''; + $task->completed = isset($task->completed) ? $task->completed : null; + } + } + return $phases; + } + + /** + * Has the assessment form been defined? + * + * @return bool + */ + public function assessment_form_ready() { + return $this->grading_strategy_instance()->form_ready(); + } + } diff --git a/mod/workshop/renderer.php b/mod/workshop/renderer.php index 33616593c8..16cbdb31e1 100644 --- a/mod/workshop/renderer.php +++ b/mod/workshop/renderer.php @@ -87,7 +87,7 @@ class moodle_mod_workshop_renderer extends moodle_renderer_base { */ public function allocation_init_result($result='') { $msg = new stdClass(); - if ($result === WORKSHOP_ALLOCATION_ERROR) { + if ($result === workshop::ALLOCATION_ERROR) { $msg = (object)array('text' => get_string('allocationerror', 'workshop'), 'sty' => 'error'); } else { $msg = (object)array('text' => get_string('allocationdone', 'workshop'), 'sty' => 'ok'); @@ -241,4 +241,68 @@ class moodle_mod_workshop_renderer extends moodle_renderer_base { return $this->output->output_tag('div', array('class' => 'attachments'), $outputimgs . $outputfiles); } + /** + * TODO: short description. + * + * @param array $plan + * @return TODO + */ + public function user_plan(array $plan) { + if (empty($plan)) { + throw new coding_exception('you must provide the prepared user plan to be rendered'); + } + $table = new html_table(); + $table->set_classes('userplan'); + $table->head = array(); + $table->colclasses = array(); + $row = new html_table_row(); + $row->set_classes('phasetasks'); + foreach ($plan as $phasecode => $phase) { + $table->head[] = $this->output->container($this->output->output_tag('span', array(), $phase->title)); + $classes = 'phase' . $phasecode; + if ($phase->active) { + $classes .= ' active'; + } else { + $classes .= ' nonactive'; + } + $table->colclasses[] = $classes; + $cell = new html_table_cell(); + $cell->text = $this->user_plan_tasks($phase->tasks); + $row->cells[] = $cell; + } + $table->data = array($row); + + return $this->output->table($table); + } + + /** + * TODO: short description. + * + * @param stdClass $tasks + * @return TODO + */ + protected function user_plan_tasks(array $tasks) { + $out = ''; + foreach ($tasks as $taskcode => $task) { + $classes = ''; + $icon = null; + if ($task->completed === true) { + $icon = new html_image(); + $icon->src = $this->old_icon_url('i/tick_green_big.gif'); + $icon->alt = '+'; + $classes .= ' completed'; + } elseif ($task->completed === false) { + $classes .= ' pending'; + } else { + $classes .= ' statusunknown'; + } + $title = $this->output->container($task->title, 'title'); + $info = $this->output->container($task->info, 'info'); + $out .= $this->output->output_tag('li', array('class' => $classes), $title . $info); + } + if ($out) { + $out = $this->output->output_tag('ul', array('class' => 'tasks'), $out); + } + return $out; + } } diff --git a/mod/workshop/styles.php b/mod/workshop/styles.php index 3b0d543223..d5e63606db 100644 --- a/mod/workshop/styles.php +++ b/mod/workshop/styles.php @@ -179,3 +179,72 @@ .assessmentform .description { margin: 0px 1em; } + +/** + * User plan + */ +.userplan { + width: 70%; + margin: 1em auto 1em auto; +} + +.userplan th { + vertical-align: bottom; + white-space: normal; + color: #999; + border-bottom: 1px solid #ddd; + padding: 3px; +} + +.userplan th.active { + vertical-align: top; + color: black; + font-size: 110%; + border: 1px solid #ddd; + border-bottom: none; + background-color: #e7f1c3; +} + +.userplan td { + width: 20%; + vertical-align: top; + border-right: 1px solid #ddd; + background-color: #f5f5f5; + color: #999; +} + +.userplan td.lastcol { + border-right: none; +} + +.userplan td.active { + color: black; + border-left: 1px solid #ddd; + border-right: 1px solid #ddd; + background-color: #e7f1c3; +} + +.userplan tr.phasetasks li { + background-image: url(../../pix/i/completion-auto-n.gif); + background-position: top left; + background-repeat: no-repeat; +} + +.userplan tr.phasetasks .completed { + background-image: url(../../pix/i/completion-auto-y.gif); +} + +.userplan tr.phasetasks .tasks { + list-style:none; + margin: 3px; + padding: 0px; +} + +.userplan tr.phasetasks .title { + padding: 0px 10px 0px 20px; +} + +.userplan tr.phasetasks .info { + padding: 0px 10px 0px 25px; + font-size: 80%; +} diff --git a/mod/workshop/submission.php b/mod/workshop/submission.php index ed84de8a6e..60f5e11931 100644 --- a/mod/workshop/submission.php +++ b/mod/workshop/submission.php @@ -102,11 +102,11 @@ if ($mform->is_cancelled()) { $PAGE->set_url('mod/workshop/submission.php', array('cmid' => $cm->id)); $PAGE->set_title($workshop->name); $PAGE->set_heading($course->fullname); -// the default navbar node for non-editing mode is set in {@link workshop_extend_navigation()} if ($edit) { - $PAGE->navbar->add(get_string('mysubmission', 'workshop'), null, null, navigation_node::TYPE_CUSTOM, - $workshop->submission_url()); + $PAGE->navbar->add(get_string('mysubmission', 'workshop'), $workshop->submission_url(), navigation_node::TYPE_CUSTOM); $PAGE->navbar->add(get_string('editingsubmission', 'workshop')); +} else { + $PAGE->navbar->add(get_string('mysubmission', 'workshop')); } // Output starts here diff --git a/mod/workshop/tabs.php b/mod/workshop/tabs.php new file mode 100644 index 0000000000..4d633b063a --- /dev/null +++ b/mod/workshop/tabs.php @@ -0,0 +1,51 @@ +. + +/** + * Defines and prints the workshop navigation tabs + * + * Can be included from within a workshop script only + * + * @package mod-workshop + * @copyright 2009 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +if (empty($workshop) or !is_a($workshop, 'workshop')) { + print_error('cannotcallscript'); +} +if (!isset($currenttab)) { + $currenttab = 'info'; +} + +$tabs = array(); +$row = array(); +$inactive = array(); +$activated = array(); + +// top level tabs +if (has_capability('mod/workshop:view', $PAGE->context)) { + $row[] = new tabobject('info', $workshop->view_url()->out(), get_string('info', 'workshop')); +} +if (has_capability('mod/workshop:editdimensions', $PAGE->context)) { + $row[] = new tabobject('editform', $workshop->editform_url()->out(), get_string('editassessmentform', 'workshop')); +} +$tabs[] = $row; + +print_tabs($tabs, $currenttab, $inactive, $activated); diff --git a/mod/workshop/view.php b/mod/workshop/view.php index 090b1d67d2..4f0954f81b 100644 --- a/mod/workshop/view.php +++ b/mod/workshop/view.php @@ -44,14 +44,10 @@ if ($id) { } require_login($course, true, $cm); -$workshop = new workshop($workshop, $cm, $course); - -// todo has_capability() check using something like -// if (!(($workshop->is_open() && has_capability('mod/workshop:view')) || has_capability(...) || has_capability(...))) { -// unable to view this page -// +require_capability('mod/workshop:view', $PAGE->context); +add_to_log($course->id, 'workshop', 'view', 'view.php?id='.$cm->id, $workshop->id); -// todo logging add_to_log($course->id, "workshop", "view", "view.php?id=$cm->id", "$workshop->id"); +$workshop = new workshop($workshop, $cm, $course); if (!is_null($edit) && $PAGE->user_allowed_editing()) { $USER->editing = $edit; @@ -60,6 +56,8 @@ if (!is_null($edit) && $PAGE->user_allowed_editing()) { $PAGE->set_url($workshop->view_url()); $PAGE->set_title($workshop->name); $PAGE->set_heading($course->fullname); + +// todo $buttons = array(); if ($PAGE->user_allowed_editing()) { $editblocks = new html_form(); @@ -71,9 +69,32 @@ if ($PAGE->user_allowed_editing()) { $buttons[] = $OUTPUT->update_module_button($cm->id, 'workshop'); $PAGE->set_button(implode('', $buttons)); +$wsoutput = $PAGE->theme->get_renderer('mod_workshop', $PAGE); + /// Output starts here echo $OUTPUT->header(); +include(dirname(__FILE__) . '/tabs.php'); + +$workshop->phase = 10; // todo xxx devel hack +echo $wsoutput->user_plan($workshop->prepare_user_plan($USER->id)); + + +switch ($workshop->phase) { +case workshop::PHASE_SETUP: + // print workshop name and description + echo $OUTPUT->heading(format_string($workshop->name)); + if (trim(strip_tags($workshop->intro))) { + echo $OUTPUT->box(format_module_intro('workshop', $workshop, $workshop->cm->id), 'generalbox', 'intro'); + } + break; +case workshop::PHASE_SUBMISSION: +case workshop::PHASE_ASSESSMENT: +case workshop::PHASE_EVALUATION: +case workshop::PHASE_CLOSED: +default: +} + /// Print the main part of the page - todo these are just links to help during development echo $OUTPUT->box_start();