Updating LTI submission viewer

This commit is contained in:
Chris Scribner 2011-09-28 11:11:20 -04:00
parent a35626f9bc
commit f4f711d79b
9 changed files with 221 additions and 698 deletions

View File

@ -44,7 +44,7 @@
$capabilities = array(
'mod/ltii:view' => array(
'mod/lti:view' => array(
'captype' => 'read',
'contextlevel' => CONTEXT_MODULE,

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="mod/lti/db" VERSION="20110901" COMMENT="XMLDB file for Moodle mod/lti"
<XMLDB PATH="mod/lti/db" VERSION="20110923" COMMENT="XMLDB file for Moodle mod/lti"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
>
@ -58,7 +58,7 @@
<INDEX NAME="tooldomain" UNIQUE="false" FIELDS="tooldomain" PREVIOUS="course"/>
</INDEXES>
</TABLE>
<TABLE NAME="lti_types_config" COMMENT="Basic LTI types configuration" PREVIOUS="lti_types">
<TABLE NAME="lti_types_config" COMMENT="Basic LTI types configuration" PREVIOUS="lti_types" NEXT="lti_submission">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="typeid"/>
<FIELD NAME="typeid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" COMMENT="Basic LTI type id" PREVIOUS="id" NEXT="name"/>
@ -72,5 +72,24 @@
<INDEX NAME="typeid" UNIQUE="false" FIELDS="typeid"/>
</INDEXES>
</TABLE>
<TABLE NAME="lti_submission" COMMENT="Keeps track of individual submissions for LTI activities." PREVIOUS="lti_types_config">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="ltiid"/>
<FIELD NAME="ltiid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" COMMENT="ID of the LTI tool instance" PREVIOUS="id" NEXT="userid"/>
<FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="ltiid" NEXT="datesubmitted"/>
<FIELD NAME="datesubmitted" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="userid" NEXT="dateupdated"/>
<FIELD NAME="dateupdated" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="datesubmitted" NEXT="gradepercent"/>
<FIELD NAME="gradepercent" TYPE="number" LENGTH="10" NOTNULL="true" UNSIGNED="false" SEQUENCE="false" DECIMALS="5" PREVIOUS="dateupdated" NEXT="originalgrade"/>
<FIELD NAME="originalgrade" TYPE="number" LENGTH="10" NOTNULL="true" UNSIGNED="false" SEQUENCE="false" DECIMALS="5" PREVIOUS="gradepercent" NEXT="launchid"/>
<FIELD NAME="launchid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="originalgrade" NEXT="state"/>
<FIELD NAME="state" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="launchid"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
</KEYS>
<INDEXES>
<INDEX NAME="ltiid" UNIQUE="false" FIELDS="ltiid"/>
</INDEXES>
</TABLE>
</TABLES>
</XMLDB>

View File

@ -89,4 +89,96 @@ require_login($course, false, $cm);
require_capability('mod/lti:grade', get_context_instance(CONTEXT_MODULE, $cm->id));
lti_submissions($cm, $course, $basiclti, $mode); // Display or process the submissions
//lti_submissions($cm, $course, $basiclti, $mode); // Display or process the submissions
$module = array(
'name' => 'mod_lti_submissions',
'fullpath' => '/mod/lti/submissions.js',
'requires' => array('base'),
'strings' => array(
),
);
$PAGE->requires->js_init_call('M.mod_lti.submissions.init', array(), true, $module);
$PAGE->requires->yui2_lib('datatable');
$submissionquery = <<<SQL
SELECT s.id, u.firstname, u.lastname, u.id AS userid, s.datesubmitted, s.gradepercent
FROM {lti_submission} s
INNER JOIN {user} u ON s.userid = u.id
WHERE s.ltiid = :ltiid
ORDER BY s.datesubmitted DESC
SQL;
$submissions = $DB->get_records_sql($submissionquery, array('ltiid' => $basiclti->id));
$html = <<<HTML
<noscript>
<!-- If javascript is disabled, we need to show the table using CSS.
The table starts out hidden to avoid flickering as it loads -->
<style type="text/css">
#lti_submissions_table_container { display: block !important; }
</style>
</noscript>
<div id="lti_submissions_table_container" style="display:none">
<table id='lti_submissions_table'>
<thead>
<tr>
<th>User</th>
<th>Date</th>
<th>Grade</th>
</tr>
</thead>
<tbody>
<!--table body-->
</tbody>
</table>
</div>
HTML;
$rowtemplate = <<<HTML
<tr>
<td>
<!--firstname--> <!--lastname-->
</td>
<td>
<!--datesubmitted-->
</td>
<td>
<!--gradepercent-->
</td>
</tr>
HTML;
$rows = '';
foreach($submissions as $submission){
$row = $rowtemplate;
foreach($submission as $key => $value){
if($key === 'datesubmitted'){
$value = userdate($value);
}
$row = str_replace('<!--' . $key . '-->', $value, $row);
}
$rows .= $row;
}
$table = str_replace('<!--table body-->', $rows, $html);
$title = 'Submissions for ' . $basiclti->name;
$PAGE->set_title(format_string($title , true));
$PAGE->set_heading($course->fullname);
echo $OUTPUT->header();
echo $OUTPUT->heading($title );
echo $table;
echo $OUTPUT->footer();

View File

@ -315,652 +315,6 @@ function lti_get_lti_types() {
return $DB->get_records('lti_types');
}
/**
* Returns Basic LTI types configuration
*
* @return array of basicLTI types
*/
/*function lti_get_types() {
$types = array();
$basicltitypes = lti_get_lti_types();
if (!empty($basicltitypes)) {
foreach ($basicltitypes as $basicltitype) {
$ltitypesconfig = lti_get_type_config($basicltitype->id);
$modclass = MOD_CLASS_ACTIVITY;
if (isset($ltitypesconfig['module_class_type'])) {
if ($ltitypesconfig['module_class_type']=='1') {
$modclass = MOD_CLASS_RESOURCE;
}
}
$type = new object();
$type->modclass = $modclass;
$type->type = 'lti&amp;type='.urlencode($basicltitype->rawname);
$type->typestr = $basicltitype->name;
$types[] = $type;
}
}
return $types;
}*/
//////////////////////////////////////////////////////////////////////////////////////
/// Any other basiclti functions go here. Each of them must have a name that
/// starts with basiclti_
/// Remember (see note in first lines) that, if this section grows, it's HIGHLY
/// recommended to move all funcions below to a new "localib.php" file.
///**
// *
// */
//function process_outcomes($userid, $course, $basiclti) {
// global $CFG, $USER;
//
// if (empty($CFG->enableoutcomes)) {
// return;
// }
//
// require_once($CFG->libdir.'/gradelib.php');
//
// if (!$formdata = data_submitted() or !confirm_sesskey()) {
// return;
// }
//
// $data = array();
// $grading_info = grade_get_grades($course->id, 'mod', 'basiclti', $basiclti->id, $userid);
//
// if (!empty($grading_info->outcomes)) {
// foreach ($grading_info->outcomes as $n => $old) {
// $name = 'outcome_'.$n;
// if (isset($formdata->{$name}[$userid]) and $old->grades[$userid]->grade != $formdata->{$name}[$userid]) {
// $data[$n] = $formdata->{$name}[$userid];
// }
// }
// }
// if (count($data) > 0) {
// grade_update_outcomes('mod/basiclti', $course->id, 'mod', 'basiclti', $basiclti->id, $userid, $data);
// }
//
//}
/**
* Top-level function for handling of submissions called by submissions.php
*
* This is for handling the teacher interaction with the grading interface
*
* @global object
* @param string $mode Specifies the kind of teacher interaction taking place
*/
function lti_submissions($cm, $course, $basiclti, $mode) {
///The main switch is changed to facilitate
///1) Batch fast grading
///2) Skip to the next one on the popup
///3) Save and Skip to the next one on the popup
//make user global so we can use the id
global $USER, $OUTPUT, $DB;
$mailinfo = optional_param('mailinfo', null, PARAM_BOOL);
if (optional_param('next', null, PARAM_BOOL)) {
$mode='next';
}
if (optional_param('saveandnext', null, PARAM_BOOL)) {
$mode='saveandnext';
}
if (is_null($mailinfo)) {
if (optional_param('sesskey', null, PARAM_BOOL)) {
set_user_preference('lti_mailinfo', $mailinfo);
} else {
$mailinfo = get_user_preferences('lti_mailinfo', 0);
}
} else {
set_user_preference('lti_mailinfo', $mailinfo);
}
switch ($mode) {
case 'grade': // We are in a main window grading
if ($submission = process_feedback()) {
lti_display_submissions($cm, $course, $basiclti, get_string('changessaved'));
} else {
lti_display_submissions($cm, $course, $basiclti);
}
break;
case 'single': // We are in a main window displaying one submission
if ($submission = process_feedback()) {
lti_display_submissions($cm, $course, $basiclti, get_string('changessaved'));
} else {
display_submission();
}
break;
case 'all': // Main window, display everything
lti_display_submissions($cm, $course, $basiclti);
break;
case 'fastgrade':
/// do the fast grading stuff - this process should work for all 3 subclasses
$grading = false;
$commenting = false;
$col = false;
if (isset($_POST['submissioncomment'])) {
$col = 'submissioncomment';
$commenting = true;
}
if (isset($_POST['menu'])) {
$col = 'menu';
$grading = true;
}
if (!$col) {
//both submissioncomment and grade columns collapsed..
lti_display_submissions($cm, $course, $basiclti);
break;
}
foreach ($_POST[$col] as $id => $unusedvalue) {
$id = (int)$id; //clean parameter name
// Get grade item
$gradeitem = $DB->get_record('grade_items', array('courseid' => $cm->course, 'iteminstance' => $cm->instance));
// Get grade
$gradeentry = $DB->get_record('grade_grades', array('userid' => $id, 'itemid' => $gradeitem->id));
$grade = $_POST['menu'][$id];
$feedback = trim($_POST['submissioncomment'][$id]);
if ((!$gradeentry) && (($grade != '-1') || ($feedback != ''))) {
$newsubmission = true;
} else {
$newsubmission = false;
}
//for fast grade, we need to check if any changes take place
$updatedb = false;
if ($gradeentry) {
if ($grading) {
$grade = $_POST['menu'][$id];
$updatedb = $updatedb || (($gradeentry->rawgrade != $grade) && ($gradeentry->rawgrade != '-1'));
if ($grade != '-1') {
$gradeentry->rawgrade = $grade;
$gradeentry->finalgrade = $grade;
} else {
$gradeentry->rawgrade = null;
$gradeentry->finalgrade = null;
}
} else {
if (!$newsubmission) {
unset($gradeentry->rawgrade); // Don't need to update this.
}
}
if ($commenting) {
$commentvalue = trim($_POST['submissioncomment'][$id]);
$updatedb = $updatedb || ($gradeentry->feedback != $commentvalue);
// Special case
if (($gradeentry->feedback == null) && ($commentvalue == "")) {
unset($gradeentry->feedback);
}
$gradeentry->feedback = $commentvalue;
} else {
unset($gradeentry->feedback); // Don't need to update this.
}
} else { // No previous grade entry found
if ($newsubmission) {
if ($grade != '-1') {
$gradeentry->rawgrade = $grade;
$updatedb = true;
}
if ($feedback != '') {
$gradeentry->feedback = $feedback;
$updatedb = true;
}
}
}
$gradeentry->usermodified = $USER->id;
if (!$gradeentry->timecreated) {
$gradeentry->timecreated = time();
}
$gradeentry->timemodified = time();
//if it is not an update, we don't change the last modified time etc.
//this will also not write into database if no submissioncomment and grade is entered.
if ($updatedb) {
if ($gradeentry->rawgrade == '-1') {
$gradeentry->rawgrade = null;
}
if ($newsubmission) {
if (!isset($gradeentry->feedback)) {
$gradeentry->feedback = '';
}
$gradeentry->itemid = $gradeitem->id;
$gradeentry->userid = $id;
$sid = $DB->insert_record("grade_grades", $gradeentry);
$gradeentry->id = $sid;
} else {
$DB->update_record("grade_grades", $gradeentry);
}
//add to log only if updating
add_to_log($course->id, 'lti', 'update grades',
'submissions.php?id='.$cm->id.'&user='.$USER->id,
$USER->id, $cm->id);
}
}
$message = $OUTPUT->notification(get_string('changessaved'), 'notifysuccess');
lti_display_submissions($cm, $course, $basiclti, $message);
break;
case 'saveandnext':
///We are in pop up. save the current one and go to the next one.
//first we save the current changes
if ($submission = process_feedback()) {
//print_heading(get_string('changessaved'));
//$extra_javascript = $this->update_main_listing($submission);
}
case 'next':
/// We are currently in pop up, but we want to skip to next one without saving.
/// This turns out to be similar to a single case
/// The URL used is for the next submission.
$offset = required_param('offset', PARAM_INT);
$nextid = required_param('nextid', PARAM_INT);
$id = required_param('id', PARAM_INT);
$offset = (int)$offset+1;
//$this->display_submission($offset+1 , $nextid);
redirect('submissions.php?id='.$id.'&userid='. $nextid . '&mode=single&offset='.$offset);
break;
case 'singlenosave':
display_submission();
break;
default:
echo "Critical error. Something is seriously wrong!!";
break;
}
}
/**
* Display all the submissions ready for grading
*
* @global object
* @global object
* @global object
* @global object
* @param string $message
* @return bool|void
*/
function lti_display_submissions($cm, $course, $basiclti, $message='') {
global $CFG, $DB, $OUTPUT, $PAGE;
require_once($CFG->libdir.'/gradelib.php');
/* first we check to see if the form has just been submitted
* to request user_preference updates
*/
$updatepref = optional_param('updatepref', 0, PARAM_INT);
if (isset($_POST['updatepref'])) {
$perpage = optional_param('perpage', 10, PARAM_INT);
$perpage = ($perpage <= 0) ? 10 : $perpage;
$filter = optional_param('filter', 0, PARAM_INT);
set_user_preference('lti_perpage', $perpage);
set_user_preference('lti_quickgrade', optional_param('quickgrade', 0, PARAM_BOOL));
set_user_preference('lti_filter', $filter);
}
/* next we get perpage and quickgrade (allow quick grade) params
* from database
*/
$perpage = get_user_preferences('lti_perpage', 10);
$quickgrade = get_user_preferences('lti_quickgrade', 0);
$filter = get_user_preferences('lti_filter', 0);
$grading_info = grade_get_grades($course->id, 'mod', 'lti', $basiclti->id);
if (!empty($CFG->enableoutcomes) and !empty($grading_info->outcomes)) {
$uses_outcomes = true;
} else {
$uses_outcomes = false;
}
$page = optional_param('page', 0, PARAM_INT);
$strsaveallfeedback = get_string('saveallfeedback', 'lti');
$tabindex = 1; //tabindex for quick grading tabbing; Not working for dropdowns yet
add_to_log($course->id, 'lti', 'view submission', 'submissions.php?id='.$cm->id, $basiclti->id, $cm->id);
$PAGE->set_title(format_string($basiclti->name, true));
$PAGE->set_heading($course->fullname);
echo $OUTPUT->header();
echo '<div class="usersubmissions">';
//hook to allow plagiarism plugins to update status/print links.
plagiarism_update_status($course, $cm);
/// Print quickgrade form around the table
if ($quickgrade) {
$formattrs = array();
$formattrs['action'] = new moodle_url('/mod/lti/submissions.php');
$formattrs['id'] = 'fastg';
$formattrs['method'] = 'post';
echo html_writer::start_tag('form', $formattrs);
echo html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'id', 'value'=> $cm->id));
echo html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'mode', 'value'=> 'fastgrade'));
echo html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'page', 'value'=> $page));
echo html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'sesskey', 'value'=> sesskey()));
}
$course_context = get_context_instance(CONTEXT_COURSE, $course->id);
if (has_capability('gradereport/grader:view', $course_context) && has_capability('moodle/grade:viewall', $course_context)) {
echo '<div class="allcoursegrades"><a href="' . $CFG->wwwroot . '/grade/report/grader/index.php?id=' . $course->id . '">'
. get_string('seeallcoursegrades', 'grades') . '</a></div>';
}
if (!empty($message)) {
echo $message; // display messages here if any
}
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
/// Check to see if groups are being used in this tool
/// find out current groups mode
$groupmode = groups_get_activity_groupmode($cm);
$currentgroup = groups_get_activity_group($cm, true);
groups_print_activity_menu($cm, $CFG->wwwroot . '/mod/lti/submissions.php?id=' . $cm->id);
/// Get all ppl that are allowed to submit tools
list($esql, $params) = get_enrolled_sql($context, 'mod/lti:view', $currentgroup);
$sql = "SELECT u.id FROM {user} u ".
"LEFT JOIN ($esql) eu ON eu.id=u.id ".
"WHERE u.deleted = 0 AND eu.id=u.id ";
$users = $DB->get_records_sql($sql, $params);
if (!empty($users)) {
$users = array_keys($users);
}
// if groupmembersonly used, remove users who are not in any group
if ($users and !empty($CFG->enablegroupmembersonly) and $cm->groupmembersonly) {
if ($groupingusers = groups_get_grouping_members($cm->groupingid, 'u.id', 'u.id')) {
$users = array_intersect($users, array_keys($groupingusers));
}
}
$tablecolumns = array('picture', 'fullname', 'grade', 'submissioncomment', 'timemodified', 'timemarked', 'status', 'finalgrade');
if ($uses_outcomes) {
$tablecolumns[] = 'outcome'; // no sorting based on outcomes column
}
$tableheaders = array('',
get_string('fullname'),
get_string('grade'),
get_string('comment', 'lti'),
get_string('lastmodified').' ('.get_string('submission', 'lti').')',
get_string('lastmodified').' ('.get_string('grade').')',
get_string('status'),
get_string('finalgrade', 'grades'));
if ($uses_outcomes) {
$tableheaders[] = get_string('outcome', 'grades');
}
require_once($CFG->libdir.'/tablelib.php');
$table = new flexible_table('mod-lti-submissions');
$table->define_columns($tablecolumns);
$table->define_headers($tableheaders);
$table->define_baseurl($CFG->wwwroot.'/mod/lti/submissions.php?id='.$cm->id.'&amp;currentgroup='.$currentgroup);
$table->sortable(true, 'lastname');//sorted by lastname by default
$table->collapsible(true);
$table->initialbars(true);
$table->column_suppress('picture');
$table->column_suppress('fullname');
$table->column_class('picture', 'picture');
$table->column_class('fullname', 'fullname');
$table->column_class('grade', 'grade');
$table->column_class('submissioncomment', 'comment');
$table->column_class('timemodified', 'timemodified');
$table->column_class('timemarked', 'timemarked');
$table->column_class('status', 'status');
$table->column_class('finalgrade', 'finalgrade');
if ($uses_outcomes) {
$table->column_class('outcome', 'outcome');
}
$table->set_attribute('cellspacing', '0');
$table->set_attribute('id', 'attempts');
$table->set_attribute('class', 'submissions');
$table->set_attribute('width', '100%');
$table->no_sorting('finalgrade');
$table->no_sorting('outcome');
// Start working -- this is necessary as soon as the niceties are over
$table->setup();
if (empty($users)) {
echo $OUTPUT->heading(get_string('noviewusers', 'lti'));
echo '</div>';
return true;
}
/// Construct the SQL
list($where, $params) = $table->get_sql_where();
if ($where) {
$where .= ' AND ';
}
if ($sort = $table->get_sql_sort()) {
$sort = ' ORDER BY '.$sort;
}
$ufields = user_picture::fields('u');
$gradeitem = $DB->get_record('grade_items', array('courseid' => $cm->course, 'iteminstance' => $cm->instance));
$select = "SELECT $ufields,
g.rawgrade, g.feedback,
g.timemodified, g.timecreated ";
$sql = 'FROM {user} u'.
' LEFT JOIN {grade_grades} g ON u.id = g.userid AND g.itemid = '.$gradeitem->id.
' LEFT JOIN {grade_items} i ON g.itemid = i.id'.
' AND i.iteminstance = '.$basiclti->id.
' WHERE '.$where.'u.id IN ('.implode(',', $users).') ';
$ausers = $DB->get_records_sql($select.$sql.$sort, $params, $table->get_page_start(), $table->get_page_size());
$table->pagesize($perpage, count($users));
///offset used to calculate index of student in that particular query, needed for the pop up to know who's next
$offset = $page * $perpage;
$strupdate = get_string('update');
$strgrade = get_string('grade');
$grademenu = make_grades_menu($basiclti->grade);
if ($ausers !== false) {
$grading_info = grade_get_grades($course->id, 'mod', 'lti', $basiclti->id, array_keys($ausers));
$endposition = $offset + $perpage;
$currentposition = 0;
foreach ($ausers as $auser) {
if ($auser->timemodified > 0) {
$timemodified = '<div id="ts'.$auser->id.'">'.userdate($auser->timemodified).'</div>';
} else {
$timemodified = '<div id="ts'.$auser->id.'">&nbsp;</div>';
}
if ($auser->timecreated > 0) {
$timecreated = '<div id="ts'.$auser->id.'">'.userdate($auser->timecreated).'</div>';
} else {
$timecreated = '<div id="ts'.$auser->id.'">&nbsp;</div>';
}
if ($currentposition == $offset && $offset < $endposition) {
$final_grade = $grading_info->items[0]->grades[$auser->id];
$grademax = $grading_info->items[0]->grademax;
$final_grade->formatted_grade = round($final_grade->grade, 2) .' / ' . round($grademax, 2);
$locked_overridden = 'locked';
if ($final_grade->overridden) {
$locked_overridden = 'overridden';
}
/// Calculate user status
$picture = $OUTPUT->user_picture($auser);
$studentmodified = '<div id="ts'.$auser->id.'">&nbsp;</div>';
$teachermodified = '<div id="tt'.$auser->id.'">&nbsp;</div>';
$status = '<div id="st'.$auser->id.'">&nbsp;</div>';
if ($final_grade->locked or $final_grade->overridden) {
$grade = '<div id="g'.$auser->id.'">'.$final_grade->formatted_grade . '</div>';
} else if ($quickgrade) { // allow editing
$attributes = array();
$attributes['tabindex'] = $tabindex++;
if ($auser->rawgrade != "") {
$menu = html_writer::select(make_grades_menu($basiclti->grade), 'menu['.$auser->id.']', round($auser->rawgrade, 0), array(-1=>get_string('nograde')), $attributes);
} else {
$menu = html_writer::select(make_grades_menu($basiclti->grade), 'menu['.$auser->id.']', -1, array(-1=>get_string('nograde')), $attributes);
}
$grade = '<div id="g'.$auser->id.'">'.$menu.'</div>';
} else if ($final_grade->grade) {
if ($auser->rawgrade != "") {
$grade = '<div id="g'.$auser->id.'">'.$final_grade->formatted_grade.'</div>';
} else {
$grade = '<div id="g'.$auser->id.'">-1</div>';
}
} else {
$grade = '<div id="g'.$auser->id.'">No Grade</div>';
}
if ($final_grade->locked or $final_grade->overridden) {
$comment = '<div id="com'.$auser->id.'">'.$final_grade->str_feedback.'</div>';
} else if ($quickgrade) {
$comment = '<div id="com'.$auser->id.'">'
. '<textarea tabindex="'.$tabindex++.'" name="submissioncomment['.$auser->id.']" id="submissioncomment'
. $auser->id.'" rows="2" cols="20">'.($auser->feedback).'</textarea></div>';
} else {
$comment = '<div id="com'.$auser->id.'">'.shorten_text(strip_tags($auser->feedback), 15).'</div>';
}
if (empty($auser->status)) { /// Confirm we have exclusively 0 or 1
$auser->status = 0;
} else {
$auser->status = 1;
}
$buttontext = ($auser->status == 1) ? $strupdate : $strgrade;
///No more buttons, we use popups ;-).
$popup_url = '/mod/lti/submissions.php?id='.$cm->id
. '&amp;userid='.$auser->id.'&amp;mode=single'.'&amp;filter='.$filter.'&amp;offset='.$offset++;
$button = $OUTPUT->action_link($popup_url, $buttontext);
$status = '<div id="up'.$auser->id.'" class="s'.$auser->status.'">'.$button.'</div>';
$finalgrade = '<span id="finalgrade_'.$auser->id.'">'.$final_grade->str_grade.'</span>';
$outcomes = '';
if ($uses_outcomes) {
foreach ($grading_info->outcomes as $n => $outcome) {
$outcomes .= '<div class="outcome"><label>'.$outcome->name.'</label>';
$options = make_grades_menu(-$outcome->scaleid);
if ($outcome->grades[$auser->id]->locked or !$quickgrade) {
$options[0] = get_string('nooutcome', 'grades');
$outcomes .= ': <span id="outcome_'.$n.'_'.$auser->id.'">'.$options[$outcome->grades[$auser->id]->grade].'</span>';
} else {
$attributes = array();
$attributes['tabindex'] = $tabindex++;
$attributes['id'] = 'outcome_'.$n.'_'.$auser->id;
$outcomes .= ' '.html_writer::select($options, 'outcome_'.$n.'['.$auser->id.']', $outcome->grades[$auser->id]->grade, array(0=>get_string('nooutcome', 'grades')), $attributes);
}
$outcomes .= '</div>';
}
}
$userlink = '<a href="' . $CFG->wwwroot . '/user/view.php?id=' . $auser->id . '&amp;course=' . $course->id . '">' . fullname($auser, has_capability('moodle/site:viewfullnames', $context)) . '</a>';
$row = array($picture, $userlink, $grade, $comment, $timemodified, $timecreated, $status, $finalgrade);
if ($uses_outcomes) {
$row[] = $outcomes;
}
$table->add_data($row);
}
$currentposition++;
}
}
$table->print_html(); /// Print the whole table
/// Print quickgrade form around the table
if ($quickgrade && $table->started_output) {
$mailinfopref = false;
if (get_user_preferences('lti_mailinfo', 1)) {
$mailinfopref = true;
}
$emailnotification = html_writer::checkbox('mailinfo', 1, $mailinfopref, get_string('enableemailnotification', 'lti'));
$emailnotification .= $OUTPUT->help_icon('enableemailnotification', 'lti');
echo html_writer::tag('div', $emailnotification, array('class'=>'emailnotification'));
$savefeedback = html_writer::empty_tag('input', array('type'=>'submit', 'name'=>'fastg', 'value'=>get_string('saveallfeedback', 'lti')));
echo html_writer::tag('div', $savefeedback, array('class'=>'fastgbutton'));
echo html_writer::end_tag('form');
} else if ($quickgrade) {
echo html_writer::end_tag('form');
}
echo '</div>';
/// End of fast grading form
/// Mini form for setting user preference
$formaction = new moodle_url('/mod/lti/submissions.php', array('id'=>$cm->id));
$mform = new MoodleQuickForm('optionspref', 'post', $formaction, '', array('class'=>'optionspref'));
$mform->addElement('hidden', 'updatepref');
$mform->setDefault('updatepref', 1);
$mform->addElement('header', 'qgprefs', get_string('optionalsettings', 'lti'));
// $mform->addElement('select', 'filter', get_string('show'), $filters);
$mform->setDefault('filter', $filter);
$mform->addElement('text', 'perpage', get_string('pagesize', 'lti'), array('size'=>1));
$mform->setDefault('perpage', $perpage);
$mform->addElement('checkbox', 'quickgrade', get_string('quickgrade', 'lti'));
$mform->setDefault('quickgrade', $quickgrade);
$mform->addHelpButton('quickgrade', 'quickgrade', 'lti');
$mform->addElement('submit', 'savepreferences', get_string('savepreferences'));
$mform->display();
echo $OUTPUT->footer();
}
/**
* Create grade item for given basiclti
*
@ -1008,3 +362,14 @@ function lti_grade_item_delete($basiclti) {
return grade_update('mod/lti', $basiclti->course, 'mod', 'lti', $basiclti->id, 0, null, array('deleted'=>1));
}
function lti_extend_settings_navigation($settings, $parentnode) {
global $PAGE;
$keys = $parentnode->get_children_key_list();
$node = navigation_node::create('Submissions',
new moodle_url('/mod/lti/grade.php', array('id'=>$PAGE->cm->id)),
navigation_node::TYPE_SETTING, null, 'mod_lti_submissions');
$parentnode->add_node($node, $keys[1]);
}

View File

@ -95,6 +95,8 @@ function lti_view($instance, $makeobject=false) {
$typeconfig['sendname'] = $instance->instructorchoicesendname;
$typeconfig['sendemailaddr'] = $instance->instructorchoicesendemailaddr;
$typeconfig['customparameters'] = $instance->instructorcustomparameters;
$typeconfig['acceptgrades'] = $instance->instructorchoiceacceptgrades;
$typeconfig['allowroster'] = $instance->instructorchoiceallowroster;
}
//Default the organizationid if not specified
@ -131,11 +133,16 @@ function lti_view($instance, $makeobject=false) {
echo $content;
}
function lti_build_sourcedid($instanceid, $userid, $servicesalt){
function lti_build_sourcedid($instanceid, $userid, $launchid = null, $servicesalt){
$data = new stdClass();
$data->instanceid = $instanceid;
$data->userid = $userid;
if(!empty($launchid)){
$data->launchid = $launchid;
} else {
$data->launchid = mt_rand();
}
$json = json_encode($data);
@ -183,7 +190,7 @@ function lti_build_request($instance, $typeconfig, $course) {
$placementsecret = $instance->servicesalt;
if ( isset($placementsecret) ) {
$sourcedid = json_encode(lti_build_sourcedid($instance->id, $USER->id, $placementsecret));
$sourcedid = json_encode(lti_build_sourcedid($instance->id, $USER->id, null, $placementsecret));
}
if ( isset($placementsecret) &&
@ -998,44 +1005,6 @@ function lti_post_launch_html($newparms, $endpoint, $debug=false) {
return $r;
}
/**
* Returns a link with info about the state of the basiclti submissions
*
* This is used by view_header to put this link at the top right of the page.
* For teachers it gives the number of submitted assignments with a link
* For students it gives the time of their submission.
* This will be suitable for most assignment types.
*
* @global object
* @global object
* @param bool $allgroup print all groups info if user can access all groups, suitable for index.php
* @return string
*/
function lti_submittedlink($cm, $allgroups=false) {
global $CFG;
$submitted = '';
$urlbase = "{$CFG->wwwroot}/mod/lti/";
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
if (has_capability('mod/lti:grade', $context)) {
if ($allgroups and has_capability('moodle/site:accessallgroups', $context)) {
$group = 0;
} else {
$group = groups_get_activity_group($cm);
}
$submitted = '<a href="'.$urlbase.'submissions.php?id='.$cm->id.'">'.
get_string('viewsubmissions', 'lti').'</a>';
} else {
if (isloggedin()) {
// TODO Insert code for students if needed
}
}
return $submitted;
}
function lti_get_type($typeid){
global $DB;

View File

@ -20,7 +20,7 @@ switch($messagetype){
lti_verify_sourcedid($ltiinstance, $parsed);
lti_verify_message($ltiinstance, $rawbody);
$gradestatus = lti_update_grade($ltiinstance, $parsed->userid, $parsed->gradeval);
$gradestatus = lti_update_grade($ltiinstance, $parsed->userid, $parsed->launchid, $parsed->gradeval);
$responsexml = lti_get_response_xml(
$gradestatus ? 'success' : 'error',

View File

@ -49,6 +49,7 @@ function lti_parse_grade_replace_message($xml){
$parsed->instanceid = $resultjson->data->instanceid;
$parsed->userid = $resultjson->data->userid;
$parsed->launchid = $resultjson->data->launchid;
$parsed->sourcedidhash = $resultjson->hash;
$parsed->messageid = lti_parse_message_id($xml);
@ -63,6 +64,7 @@ function lti_parse_grade_read_message($xml){
$parsed = new stdClass();
$parsed->instanceid = $resultjson->data->instanceid;
$parsed->userid = $resultjson->data->userid;
$parsed->launchid = $resultjson->data->launchid;
$parsed->sourcedidhash = $resultjson->hash;
$parsed->messageid = lti_parse_message_id($xml);
@ -77,6 +79,7 @@ function lti_parse_grade_delete_message($xml){
$parsed = new stdClass();
$parsed->instanceid = $resultjson->data->instanceid;
$parsed->userid = $resultjson->data->userid;
$parsed->launchid = $resultjson->data->launchid;
$parsed->sourcedidhash = $resultjson->hash;
$parsed->messageid = lti_parse_message_id($xml);
@ -84,8 +87,8 @@ function lti_parse_grade_delete_message($xml){
return $parsed;
}
function lti_update_grade($ltiinstance, $userid, $gradeval){
global $CFG;
function lti_update_grade($ltiinstance, $userid, $launchid, $gradeval){
global $CFG, $DB;
require_once($CFG->libdir . '/gradelib.php');
$params = array();
@ -97,6 +100,33 @@ function lti_update_grade($ltiinstance, $userid, $gradeval){
$status = grade_update(LTI_SOURCE, $ltiinstance->course, LTI_ITEM_TYPE, LTI_ITEM_MODULE, $ltiinstance->id, 0, $grade, $params);
$record = $DB->get_record('lti_submission', array('ltiid' => $ltiinstance->id, 'userid' => $userid, 'launchid' => $launchid), 'id');
if($record){
$id = $record->id;
} else {
$id = null;
}
if(!empty($id)){
$DB->update_record('lti_submission', array(
'id' => $id,
'dateupdated' => time(),
'gradepercent' => $gradeval,
'state' => 2
));
} else {
$DB->insert_record('lti_submission', array(
'ltiid' => $ltiinstance->id,
'userid' => $userid,
'datesubmitted' => time(),
'dateupdated' => time(),
'gradepercent' => $gradeval,
'originalgrade' => $gradeval,
'launchid' => $launchid,
'state' => 1
));
}
return $status == GRADE_UPDATE_OK;
}
@ -156,7 +186,7 @@ function lti_verify_message($ltiinstance, $body, $headers = null){
}
function lti_verify_sourcedid($ltiinstance, $parsed){
$sourceid = lti_build_sourcedid($parsed->instanceid, $parsed->userid, $ltiinstance->servicesalt);
$sourceid = lti_build_sourcedid($parsed->instanceid, $parsed->userid, $parsed->launchid, $ltiinstance->servicesalt);
if($sourceid->hash != $parsed->sourcedidhash){
throw new Exception('SourcedId hash not valid');

52
mod/lti/submissions.js Normal file
View File

@ -0,0 +1,52 @@
(function(){
var Y;
M.mod_lti = M.mod_lti || {};
M.mod_lti.submissions = {
init: function(yui3){
if(yui3){
Y = yui3;
}
this.setupTable();
},
setupTable: function(){
var lti_submissions_table = YAHOO.util.Dom.get('lti_submissions_table');
var dataSource = new YAHOO.util.DataSource(lti_submissions_table);
var configuredColumns = [
{ key: "user", label: "User", sortable:true },
{ key: "date", label: "Submission Date", sortable:true, formatter: 'date' },
{ key: "grade",
label: "Grade",
sortable:true,
formatter: function(cell, record, column, data){
cell.innerHTML = parseFloat(data).toFixed(1) + '%';
}
}
];
dataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
dataSource.responseSchema = {
fields: [
{ key: "user" },
{ key: "date", parser: "date" },
{ key: "grade", parser: "number" },
]
};
new YAHOO.widget.DataTable("lti_submissions_table_container", configuredColumns, dataSource,
{
sortedBy: {key:"date", dir:"desc"}
}
);
Y.one('#lti_submissions_table_container').setStyle('display', '');
}
}
})();

View File

@ -122,10 +122,6 @@ if($basiclti->showdescription && $basiclti->intro){
echo $OUTPUT->box($basiclti->intro, 'generalbox description', 'intro');
}
if ($basiclti->instructorchoiceacceptgrades == 1) {
echo '<div class="reportlink">'.lti_submittedlink($cm).'</div>';
}
if ( $launchcontainer == LTI_LAUNCH_CONTAINER_WINDOW ) {
echo "<script language=\"javascript\">//<![CDATA[\n";
echo "window.open('launch.php?id=".$cm->id."','lti');";