From cc3b3fae5d83925fd35a1092bfa6181c283aa5d3 Mon Sep 17 00:00:00 2001 From: Dan Marsden Date: Tue, 27 Apr 2010 21:52:07 +0000 Subject: [PATCH] Plagiarism Lib MDL-13680 - add Plagiarism lib support for modules. - initially including the assignment module and Turnitin service - Backup deliberately excluded at this stage and I expect some of the file handling to change slightly before 2.0 release. --- admin/cron.php | 4 + admin/plagiarism.php | 131 ++ admin/plagiarism_defaults.php | 70 + admin/plagiarism_form.php | 77 ++ admin/plagiarism_reports.php | 134 ++ admin/plagiarism_tabs.php | 10 + admin/settings/plugins.php | 2 +- course/modedit.php | 2 + lang/en/plagiarism.php | 76 ++ lib/db/access.php | 27 + lib/db/install.xml | 31 +- lib/db/upgrade.php | 34 +- lib/moodlelib.php | 1 + lib/plagiarismlib.php | 1174 +++++++++++++++++ mod/assignment/db/events.php | 34 + mod/assignment/lib.php | 8 +- mod/assignment/submissions.php | 1 + .../type/upload/assignment.class.php | 10 +- .../type/uploadsingle/assignment.class.php | 6 +- mod/assignment/view.php | 1 + pix/i/grademark-grey.gif | Bin 0 -> 1004 bytes pix/i/grademark.gif | Bin 0 -> 1010 bytes version.php | 2 +- 23 files changed, 1827 insertions(+), 8 deletions(-) create mode 100644 admin/plagiarism.php create mode 100644 admin/plagiarism_defaults.php create mode 100644 admin/plagiarism_form.php create mode 100644 admin/plagiarism_reports.php create mode 100644 admin/plagiarism_tabs.php create mode 100644 lang/en/plagiarism.php create mode 100644 lib/plagiarismlib.php create mode 100644 mod/assignment/db/events.php create mode 100644 pix/i/grademark-grey.gif create mode 100644 pix/i/grademark.gif diff --git a/admin/cron.php b/admin/cron.php index eca29390660..a6da3ba591a 100644 --- a/admin/cron.php +++ b/admin/cron.php @@ -125,6 +125,10 @@ get_mailer('close'); mtrace("Finished activity modules"); + //now do plagiarism checks + require_once($CFG->libdir.'/plagiarismlib.php'); + plagiarism_check_files(); + mtrace("Starting blocks"); if ($blocks = $DB->get_records_select("block", "cron > 0 AND ((? - lastcron) > cron) AND visible = 1", array($timenow))) { // we will need the base class. diff --git a/admin/plagiarism.php b/admin/plagiarism.php new file mode 100644 index 00000000000..f13ef23c301 --- /dev/null +++ b/admin/plagiarism.php @@ -0,0 +1,131 @@ +. + +/** + * plagiarism.php - allows the admin to configure plagiarism stuff + * + * @package administration + * @author Dan Marsden + * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + require_once(dirname(dirname(__FILE__)) . '/config.php'); + require_once($CFG->libdir.'/adminlib.php'); + require_once($CFG->libdir.'/plagiarismlib.php'); + + require_login(); + admin_externalpage_setup('plagiarism'); + + $context = get_context_instance(CONTEXT_SYSTEM); + + require_capability('moodle/site:config', $context, $USER->id, true, "nopermissions"); + + require_once('plagiarism_form.php'); + $mform = new plagiarism_setup_form(); + + if ($mform->is_cancelled()) { + redirect(''); + } + + echo $OUTPUT->header(); + echo $OUTPUT->heading(get_string('plagiarism', 'plagiarism')); + + if (($data = $mform->get_data()) && confirm_sesskey()) { + if (!isset($data->turnitin_use)) { + $data->turnitin_use = 0; + } + if (!isset($data->turnitin_enablegrademark)) { + $data->turnitin_enablegrademark = 0; + } + if (!isset($data->turnitin_senduseremail)) { + $data->turnitin_senduseremail = 0; + } + foreach ($data as $field=>$value) { + if (strpos($field, 'turnitin')===0) { + if ($tiiconfigfield = $DB->get_record('config_plugins', array('name'=>$field, 'plugin'=>'plagiarism'))) { + $tiiconfigfield->value = $value; + if (! $DB->update_record('config_plugins', $tiiconfigfield)) { + error("errorupdating"); + } + } else { + $tiiconfigfield = new stdClass(); + $tiiconfigfield->value = $value; + $tiiconfigfield->plugin = 'plagiarism'; + $tiiconfigfield->name = $field; + if (! $DB->insert_record('config_plugins', $tiiconfigfield)) { + error("errorinserting"); + } + } + } + } + //now call TII settings to set up teacher account as set on this page. + if ($plagiarismsettings = plagiarism_get_settings()) { //get tii settings. + $tii = array(); + //set globals. + $tii['username'] = $plagiarismsettings['turnitin_userid']; + $tii['uem'] = $plagiarismsettings['turnitin_email']; + $tii['ufn'] = $plagiarismsettings['turnitin_firstname']; + $tii['uln'] = $plagiarismsettings['turnitin_lastname']; + $tii['uid'] = $plagiarismsettings['turnitin_userid']; + $tii['utp'] = '2'; //2 = this user is an instructor + $tii['cid'] = $plagiarismsettings['turnitin_courseprefix']; //course ID + $tii['ctl'] = $plagiarismsettings['turnitin_courseprefix']; //Course title. -this uses Course->id and shortname to ensure uniqueness. + //$tii['diagnostic'] = '1'; //debug only + $tii['fcmd'] = '2'; //when set to 2 the TII API should return XML + $tii['fid'] = '1'; //set command. - create user and login to Turnitin (fid=1) + $tiixml = plagiarism_get_xml(turnitin_get_url($tii)); + if (!empty($tiixml->rcode[0]) && $tiixml->rcode[0] == '11') { + notify(get_string('savedconfigsuccess', 'plagiarism'), 'notifysuccess'); + } else { + //disable turnitin as this config isn't correct. + $rec = $DB->get_record('config_plugins', array('name'=>'turnitin_use', 'plugin'=>'plagiarism')); + $rec->value = 0; + $DB->update_record('config_plugins', $rec); + notify(get_string('savedconfigfailure', 'plagiarism')); + } + } + } + $plagiarismsettings = plagiarism_get_settings(); + $mform->set_data($plagiarismsettings); + + $currenttab='plagiarism'; + require_once('plagiarism_tabs.php'); + + echo $OUTPUT->heading(get_string('tiiheading', 'plagiarism'), 3); + if ($plagiarismsettings) { + //Now show link to ADMIN tii interface - NOTE: this logs in the ADMIN user, should be hidden from normal teachers. + $tii['uid'] = $plagiarismsettings['turnitin_userid']; + $tii['username'] = $plagiarismsettings['turnitin_userid']; + $tii['uem'] = $plagiarismsettings['turnitin_email']; + $tii['ufn'] = $plagiarismsettings['turnitin_firstname']; + $tii['uln'] = $plagiarismsettings['turnitin_lastname']; + $tii['utp'] = '2'; //2 = this user is an instructor + $tii['utp'] = '3'; + $tii['fcmd'] = '1'; //when set to 2 this returns XML + $tii['fid'] = '12'; //set commands - Administrator login/statistics. + echo '
'; + echo ''.get_string("adminlogin","plagiarism").'
'; + $tii['utp'] = '2'; + $tii['fid'] = '1'; //set commands - Administrator login/statistics. + echo ''.get_string("teacherlogin","plagiarism").''; + echo '
'; + } + + echo $OUTPUT->box_start('generalbox boxaligncenter', 'intro'); + $mform->display(); + echo $OUTPUT->box_end(); + echo $OUTPUT->footer(); diff --git a/admin/plagiarism_defaults.php b/admin/plagiarism_defaults.php new file mode 100644 index 00000000000..f8c14cd9f14 --- /dev/null +++ b/admin/plagiarism_defaults.php @@ -0,0 +1,70 @@ +. + +/** + * turnitin_errors.php - Displays Turnitin files with a current error state. + * + * @package administration + * @author Dan Marsden + * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + require_once(dirname(dirname(__FILE__)) . '/config.php'); + require_once($CFG->libdir.'/adminlib.php'); + require_once($CFG->libdir.'/plagiarismlib.php'); + require_once('plagiarism_form.php'); + + require_login(); + admin_externalpage_setup('plagiarism'); + + $context = get_context_instance(CONTEXT_SYSTEM); + + $fileid = optional_param('fileid',0,PARAM_INT); + $resetuser = optional_param('reset',0,PARAM_INT); + $page = optional_param('page', 0, PARAM_INT); + + $mform = new plagiarism_defaults_form(null); + $plagiarismdefaults = $DB->get_records_menu('plagiarism_config', array('cm'=>0),'','name,value'); //cmid(0) is the default list. + if (!empty($plagiarismdefaults)) { + $mform->set_data($plagiarismdefaults); + } + echo $OUTPUT->header(); + echo $OUTPUT->heading(get_string('plagiarismdefaults', 'plagiarism')); + $currenttab='plagiarismdefaults'; + require_once('plagiarism_tabs.php'); + if (($data = $mform->get_data()) && confirm_sesskey()) { + $plagiarismelements = plagiarism_config_options(); + foreach($plagiarismelements as $element) { + if (isset($data->$element)) { + $newelement = new object(); + $newelement->cm = 0; + $newelement->name = $element; + $newelement->value = $data->$element; + if (isset($plagiarismdefaults[$element])) { //update + $newelement->id = $DB->get_field('plagiarism_config', 'id', (array('cm'=>0, 'name'=>$element))); + $DB->update_record('plagiarism_config', $newelement); + } else { //insert + $DB->insert_record('plagiarism_config', $newelement); + } + } + } + notify(get_string('plagiarismdefaultupdated','plagiarism'),'notifysuccess'); + } + echo $OUTPUT->box(get_string('plagiarismdefaultsdesc', 'plagiarism')); + + $mform->display(); + echo $OUTPUT->footer(); diff --git a/admin/plagiarism_form.php b/admin/plagiarism_form.php new file mode 100644 index 00000000000..3a6bb5b4aa0 --- /dev/null +++ b/admin/plagiarism_form.php @@ -0,0 +1,77 @@ +dirroot.'/lib/formslib.php'); + +class plagiarism_setup_form extends moodleform { + +/// Define the form + function definition () { + global $CFG; + + $mform =& $this->_form; + $choices = array('No','Yes'); + $mform->addElement('html', get_string('tiiexplain', 'plagiarism')); + $mform->addElement('checkbox', 'turnitin_use', get_string('usetii', 'plagiarism')); + + $mform->addElement('text', 'turnitin_api', get_string('tiiapi', 'plagiarism')); + $mform->addElement('static','turnitin_api_description', '', get_string('configtiiapi', 'plagiarism')); + $mform->addRule('turnitin_api', null, 'required', null, 'client'); + $mform->setDefault('turnitin_api', 'https://api.turnitin.com/api.asp'); + + $mform->addElement('text', 'turnitin_accountid', get_string('tiiaccountid', 'plagiarism')); + $mform->addElement('static','turnitin_accountid_description', '', get_string('configtiiaccountid', 'plagiarism')); + $mform->addRule('turnitin_accountid', null, 'numeric', null, 'client'); + + $mform->addElement('passwordunmask', 'turnitin_secretkey', get_string('tiisecretkey', 'plagiarism')); + $mform->addElement('static','turnitin_secretkey_description', '', get_string('configtiisecretkey', 'plagiarism')); + $mform->addRule('turnitin_secretkey', null, 'required', null, 'client'); + + $mform->addElement('checkbox', 'turnitin_senduseremail', get_string('tiisenduseremail', 'plagiarism')); + $mform->addElement('static','turnitin_senduseremail_description', '', get_string('config_tiisenduseremail', 'plagiarism')); + + $mform->addElement('checkbox', 'turnitin_enablegrademark', get_string('tiienablegrademark', 'plagiarism')); + $mform->addElement('static','turnitin_enablegrademark_description', '', get_string('config_tiienablegrademark', 'plagiarism')); + + $mform->addElement('text', 'turnitin_emailprefix', get_string('tiiemailprefix', 'plagiarism')); + $mform->addElement('static','turnitin_emailprefix_description', '', get_string('configtiiemailprefix', 'plagiarism')); + $mform->disabledIf('turnitin_emailprefix', 'turnitin_senduseremail', 'checked'); + + $mform->addElement('text', 'turnitin_courseprefix', get_string('tiicourseprefix', 'plagiarism')); + $mform->addElement('static','turnitin_courseprefix_description', '', get_string('configtiicourseprefix', 'plagiarism')); + $mform->addRule('turnitin_courseprefix', null, 'required', null, 'client'); + + $mform->addElement('text', 'turnitin_userid', get_string('username')); + $mform->addElement('static','turnitin_userid_description', '', get_string('configtiiuserid', 'plagiarism')); + $mform->addRule('turnitin_userid', null, 'required', null, 'client'); + + $mform->addElement('text', 'turnitin_email', get_string('email')); + $mform->addElement('static','turnitin_email_description', '', get_string('configtiiemail', 'plagiarism')); + $mform->addRule('turnitin_email', null, 'email', null, 'client'); + $mform->addRule('turnitin_email', null, 'required', null, 'client'); + + $mform->addElement('text', 'turnitin_firstname', get_string('firstname')); + $mform->addElement('static','turnitin_firstname_description', '', get_string('configtiifirstname', 'plagiarism')); + $mform->addRule('turnitin_firstname', null, 'required', null, 'client'); + + $mform->addElement('text', 'turnitin_lastname', get_string('lastname')); + $mform->addElement('static','turnitin_lastname_description', '', get_string('configtiilastname', 'plagiarism')); + $mform->addRule('turnitin_lastname', null, 'required', null, 'client'); + + $mform->addElement('textarea', 'turnitin_student_disclosure', get_string('studentdisclosure','plagiarism'),'wrap="virtual" rows="6" cols="50"'); + $mform->addElement('static','turnitin_student_disclosure_description', '', get_string('configstudentdisclosure','plagiarism')); + $mform->setDefault('turnitin_student_disclosure', get_string('studentdisclosuredefault','plagiarism')); + + $this->add_action_buttons(true); + } +} + +class plagiarism_defaults_form extends moodleform { + +/// Define the form + function definition () { + $mform =& $this->_form; + plagiarism_get_form_elements($mform); + $this->add_action_buttons(true); + } +} + diff --git a/admin/plagiarism_reports.php b/admin/plagiarism_reports.php new file mode 100644 index 00000000000..cbf3faea91f --- /dev/null +++ b/admin/plagiarism_reports.php @@ -0,0 +1,134 @@ +. + +/** + * turnitin_errors.php - Displays Turnitin files with a current error state. + * + * @package administration + * @author Dan Marsden + * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + require_once(dirname(dirname(__FILE__)) . '/config.php'); + require_once($CFG->libdir.'/adminlib.php'); + require_once($CFG->libdir.'/plagiarismlib.php'); + + require_login(); + admin_externalpage_setup('plagiarism'); + + $context = get_context_instance(CONTEXT_SYSTEM); + + $fileid = optional_param('fileid',0,PARAM_INT); + $resetuser = optional_param('reset',0,PARAM_INT); + $page = optional_param('page', 0, PARAM_INT); + + echo $OUTPUT->header(); + echo $OUTPUT->heading(get_string('plagiarismreports', 'plagiarism')); + $currenttab='plagiarismreports'; + require_once('plagiarism_tabs.php'); + + echo $OUTPUT->box(get_string('tiiexplainerrors', 'plagiarism')); + + if ($resetuser==1 && $fileid) { + $tfile = $DB->get_record('plagiarism_files', array('id'=>$fileid)); + $tfile->statuscode = 'pending'; + if ($DB->update_record('plagiarism_files', $tfile)) { + notify("File reset"); + } + } elseif ($resetuser==2) { + $sql = "statuscode <>'success' AND statuscode<>'pending' AND statuscode<>'51'"; + $tiifiles = $DB->get_records_select('plagiarism_files', $sql); + foreach($tiifiles as $tiifile) { + $tiifile->statuscode = 'pending'; + if ($DB->update_record('plagiarism_files', $tiifile)) { + notify("File reset"); + } + } + } + + $tablecolumns = array('name', 'course', 'file', 'status'); + $tableheaders = array(get_string('name'), + get_string('course'), + get_string('file'), + get_string('status')); + + require_once($CFG->libdir.'/tablelib.php'); + $table = new flexible_table('plagiarism-status'); + + $table->define_columns($tablecolumns); + $table->define_headers($tableheaders); + $table->define_baseurl($CFG->wwwroot.'/admin/plagiarism_reports.php'); + + $table->sortable(false); + $table->collapsible(true); + $table->initialbars(false); + + $table->column_suppress('fullname'); + + $table->column_class('name', 'name'); + $table->column_class('course', 'course'); + $table->column_class('file', 'file'); + $table->column_class('status', 'status'); + + $table->set_attribute('cellspacing', '0'); + $table->set_attribute('id', 'attempts'); + $table->set_attribute('class', 'submissions'); + $table->set_attribute('width', '100%'); + //$table->set_attribute('align', 'center'); + + $table->no_sorting('name'); + $table->no_sorting('course'); + $table->no_sorting('file'); + $table->no_sorting('status'); + + $table->setup(); + + $sql = "statuscode <>'success' AND statuscode<>'pending' AND statuscode<>'51'"; + $tiifiles = $DB->get_records_select('plagiarism_files', $sql); + $pagesize = 15; + $table->pagesize($pagesize, count($tiifiles)); + $start = $page * $pagesize; + $pagtiifiles = array_slice($tiifiles, $start, $pagesize); + if (!empty($pagtiifiles)) { + foreach($pagtiifiles as $tiifile) { + $fs = get_file_storage(); + $file = $fs->get_file_by_id($tiifile->file); + //check file exists + if (empty($file)) { + $DB->delete_records('plagiarism_files',array('id'=>$tiifile->id)); + continue; + } + //should tidy these up - shouldn't need to call so often + $reset = $tiifile->statuscode.' reset'; + $user = $DB->get_record('user', array('id'=>$tiifile->userid)); + $cm = $DB->get_record('course_modules', array('id'=>$tiifile->cm)); + $course = $DB->get_record('course', array('id'=>$cm->course)); + $row = array(fullname($user), $course->shortname, $file->get_filename(), $reset); + + + $table->add_data($row); + } + } + + $table->print_html(); + if (!empty($tiifiles)) { + echo '

'; + $options["reset"] = "2"; + echo $OUTPUT->single_button("plagiarism_reports.php", get_string("resetall", "plagiarism"), 'post',$options); + echo '
'; + } + echo $OUTPUT->footer(); diff --git a/admin/plagiarism_tabs.php b/admin/plagiarism_tabs.php new file mode 100644 index 00000000000..a04ab3d5676 --- /dev/null +++ b/admin/plagiarism_tabs.php @@ -0,0 +1,10 @@ +add('modsettings', new admin_externalpage('plagiarism',get_string('plagiarism', 'plagiarism'), "$CFG->wwwroot/$CFG->admin/plagiarism.php", 'moodle/site:config')); $ADMIN->add('reports', new admin_externalpage('comments', get_string('comments'), $CFG->wwwroot.'/comment/', 'moodle/site:viewreports')); /// Now add reports diff --git a/course/modedit.php b/course/modedit.php index 1066b6984d6..148b51c26c0 100644 --- a/course/modedit.php +++ b/course/modedit.php @@ -29,6 +29,7 @@ require_once($CFG->libdir.'/filelib.php'); require_once($CFG->libdir.'/gradelib.php'); require_once($CFG->libdir.'/completionlib.php'); require_once($CFG->libdir.'/conditionlib.php'); +require_once($CFG->libdir.'/plagiarismlib.php'); $add = optional_param('add', '', PARAM_ALPHA); // module name $update = optional_param('update', 0, PARAM_INT); @@ -553,6 +554,7 @@ if ($mform->is_cancelled()) { rebuild_course_cache($course->id); grade_regrade_final_grades($course->id); + plagiarism_save_form_elements($fromform); //save plagiarism settings if (isset($fromform->submitbutton)) { redirect("$CFG->wwwroot/mod/$module->name/view.php?id=$fromform->coursemodule"); diff --git a/lang/en/plagiarism.php b/lang/en/plagiarism.php new file mode 100644 index 00000000000..3ce3dbba036 --- /dev/null +++ b/lang/en/plagiarism.php @@ -0,0 +1,76 @@ +http://docs.moodle.org/en/Turnitin_administration'; +$string['configusetiimodule'] = 'Enable Turnitin submission.'; +$string['tiiaccountid'] ='Turnitin Account ID'; +$string['configtiiaccountid'] ='This is your Account ID as provided from Turnitin.com'; +$string['tiisecretkey'] ='Turnitin Secret Key'; +$string['configtiisecretkey'] ='This is normally e-mailed to you on request from your Turnitin.com Account representative'; +$string['configtiiuserid'] ='Username of the person you want all new classes/assignments in Moodle to be assigned to in the turnitin System - this user should not already exist in the Turnitin system, and should not be changed once the turnitin system is being used.'; +$string['configtiiemail'] ='Email address of the person you want all new classes/assignments in Moodle to be assigned to in the turnitin System '; +$string['configtiifirstname'] ='First name of the person you want all new classes/assignments in Moodle to be assigned to in the turnitin System'; +$string['configtiilastname'] ='Surname of the person you want all new classes/assignments in Moodle to be assigned to in the turnitin System'; +$string['tiisenduseremail'] = 'Send User E-mail'; +$string['config_tiisenduseremail'] = 'Send e-mail to every student created in the TII system with a link to allow login to www.turnitin.com with a temporary password'; +$string['tiicourseprefix'] = 'Course Prefix'; +$string['configtiicourseprefix'] = 'This places a prefix before all courses created in the TII system - you MUST do this if you have more than 1 moodle site using turnitin, and they MUST be different on each moodle site.'; +$string['similarity'] = 'Similarity'; +$string['studentdisclosuredefault'] ='All files uploaded will be submitted to the plagiarism detection service Turnitin.com'; +$string['studentdisclosure'] = 'Student Disclosure'; +$string['configstudentdisclosure'] = 'This text will be displayed to all students on the file upload page.'; +$string['tiiemailprefix'] ='Student Email prefix'; +$string['configtiiemailprefix'] ='You must set this if you do not want students to be able to log into the turnitin.com site and view full reports.'; +$string['showstudentsscore']='Show Plagiarism score to student'; +$string['showstudentsreport']='Show Plagiarism report to student'; +$string['showwhenclosed']='When Activity closed'; +$string['savedconfigsuccess'] = 'Turnitin Settings Saved, and Teacher account created'; +$string['savedconfigfailure'] = 'Unable to connect/authenticate to Turnitin - you may have an incorrect Secret Key/Account ID combination or this server cannot connect to the API'; +$string['defaultuse'] = 'Use Default'; +$string['configdefault'] = 'This is a default setting for the assignment creation page. Only users with the capability moodle/plagiarism:enableturnitin can change this setting for an individual assignment'; +$string['defaultshowscore'] ='Show Score Default'; +$string['defaultshowreport'] = 'Show Report Default'; +$string['tiierror'] = 'TII Error:'; +$string['tiiexplainerrors'] = 'This page lists any files submitted to Turnitin that are currently in an error state. A list of turnitin Error codes and their description is available here:docs.moodle.org/en/Turnitin_errors
When files are reset, the cron will attempt to submit the file to turnitin again.
NOTE: files with errors in the range 1000-1999 will not benefit from being reset, and will probably always fail.'; +$string['resetall'] = 'Reset All'; +$string['tiierror1007'] = 'Turnitin could not process this file as it is too large'; +$string['tiierror1008'] = 'An error occured when attempting to send this file to Turnitin'; +$string['tiierror1009'] = 'Turnitin could not process this file - it is an unsupported file type. Valid file types are MS Word, Acrobat PDF, Postscript, Text, HTML, WordPerfect and Rich Text Format'; +$string['tiierror1010'] = 'Turnitin could not process this file - it must contain more than 100 non-whitespace characters'; +$string['tiierror1011'] = 'Turnitin could not process this file - it is incorrectly formatted and there seems to be spaces between each letter.'; +$string['tiierror1012'] = 'Turnitin could not process this file - it\'s length exceeds the Turnitin limits'; +$string['tiierror1013'] = 'Turnitin could not process this file - it must contain more than 20 words'; +$string['tiierror1020'] = 'Turnitin could not process this file - it contains characters from a character set that is not supported'; +$string['tiierror1023'] = 'Turnitin could not process this pdf - make sure it is not password protected and contains selectable text rather than scanned images'; +$string['tiierror1024'] = 'Turnitin could not process this file - it does not meet the Turnitin criteria for a legitamate paper'; +$string['tiierrorpaperfail'] = 'Turnitin could not process this file.'; +$string['tiierrorpending'] ='File pending submission to Turnitin'; +$string['tiiconfigerror'] = 'A site configuration error occured when attempting to send this file to Turnitin'; +$string['submitondraft'] = 'Submit file when first uploaded'; +$string['submitonfinal'] = 'Submit file when student sends for marking'; +$string['draftsubmit'] = 'When should the file be submitted to Plagiarism tools'; +$string['plagiarismcomparestudents'] = 'Compare submitted files with other students files'; +$string['plagiarismcomparejournals'] = 'Compare submitted files with Journals, periodicals, publications'; +$string['plagiarismcompareinternet'] = 'Compare submitted files with Internet'; +$string['plagiarismcompareinstitution'] = 'Compare submitted files with papers submitted within this institution'; +$string['plagiarismreportgen'] = 'When to generate the Originality Reports'; +$string['reportgenimmediate'] = 'Immediately (first report is final)'; +$string['reportgenimmediateoverwrite'] = 'Immediately (can overwrite reports)'; +$string['reportgenduedate'] = 'On the due date'; +$string['plagiarismexcludebiblio'] = 'Exclude bibliography'; +$string['plagiarismexcludequoted'] = 'Exclude quoted material'; +$string['percentage'] = 'Percentage'; +$string['wordcount'] = 'Word count'; +$string['plagiarismexcludematches'] = 'Exclude small matches'; +$string['plagiarismdefaultupdated'] = 'Plagiarism defaults updated'; +$string['tiienablegrademark'] = 'Enable Grademark'; +$string['config_tiienablegrademark'] = 'Grademark is an optional feature within Turnitin - you must have included this in your Turnitin subscription to use it.'; diff --git a/lib/db/access.php b/lib/db/access.php index 8885aeaaeb3..9f9ab2afedd 100644 --- a/lib/db/access.php +++ b/lib/db/access.php @@ -1438,6 +1438,33 @@ $capabilities = array( ) ), + 'moodle/plagiarism:enable' => array( + 'captype' => 'write', + 'contextlevel' => CONTEXT_COURSE, + 'legacy' => array( + 'editingteacher' => CAP_ALLOW, + 'manager' => CAP_ALLOW + ) + ), + + 'moodle/plagiarism:viewsimilarityscore' => array( + 'captype' => 'read', + 'contextlevel' => CONTEXT_COURSE, + 'legacy' => array( + 'editingteacher' => CAP_ALLOW, + 'manager' => CAP_ALLOW + ) + ), + + 'moodle/plagiarism:viewfullreport' => array( + 'captype' => 'read', + 'contextlevel' => CONTEXT_COURSE, + 'legacy' => array( + 'editingteacher' => CAP_ALLOW, + 'manager' => CAP_ALLOW + ) + ), + 'moodle/portfolio:export' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_SYSTEM, diff --git a/lib/db/install.xml b/lib/db/install.xml index ba8dc1339ef..c8e107e25d8 100644 --- a/lib/db/install.xml +++ b/lib/db/install.xml @@ -2484,7 +2484,7 @@ - +
@@ -2500,5 +2500,34 @@
+ + + + + + + + + + + + + + + + +
+ + + + + + + + + + + +
diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php index 53d6ddc3bca..68ef0d2aed1 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -3642,7 +3642,39 @@ WHERE gradeitemid IS NOT NULL AND grademax IS NOT NULL"); // Main savepoint reached upgrade_main_savepoint($result, 2010042303); - } + } + if ($result && $oldversion < 2010042304) { /// Plagiarsim API UPGRADE + $table = new XMLDBTable('plagiarism_files'); + $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null, null); + $table->add_field('cm', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0'); + $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0'); + $table->add_field('file', XMLDB_TYPE_TEXT, 'small', null, XMLDB_NOTNULL, null, null); + $table->add_field('externalid', XMLDB_TYPE_TEXT, 'small', null, null, null, null); + $table->add_field('externalstatus', XMLDB_TYPE_INTEGER, '10', XMLDB_TYPE_INTEGER, null, null, null); + $table->add_field('statuscode', XMLDB_TYPE_CHAR, '10', null, null, null, null); + $table->add_field('similarityscore', XMLDB_TYPE_INTEGER, '5', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0'); + + //keys + $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); + $table->add_key('cm', XMLDB_KEY_FOREIGN, array('cm'), 'course_modules', array('id')); + $table->add_key('userid', XMLDB_KEY_FOREIGN, array('userid'), 'user', array('id')); + if (!$dbman->table_exists($table)) { + $result = $result && $dbman->create_table($table); + } + + $table = new XMLDBTable('plagiarism_config'); + $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null, null); + $table->add_field('cm', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0'); + $table->add_field('name', XMLDB_TYPE_TEXT, 'small', null, XMLDB_NOTNULL, null, null); + $table->add_field('value', XMLDB_TYPE_TEXT, 'small', null, null, null, null); + //keys + $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); + $table->add_key('cm', XMLDB_KEY_FOREIGN, array('cm'), 'course_modules', array('id')); + if (!$dbman->table_exists($table)) { + $result = $result && $dbman->create_table($table); + } + upgrade_main_savepoint($result, 2010042304); + } return $result; diff --git a/lib/moodlelib.php b/lib/moodlelib.php index eb072a71fe7..9b6b4209d64 100644 --- a/lib/moodlelib.php +++ b/lib/moodlelib.php @@ -6885,6 +6885,7 @@ function get_core_subsystems() { 'notes' => 'notes', 'pagetype' => NULL, 'pix' => NULL, + 'plagiarism' => 'plagiarism', 'portfolio' => 'portfolio', 'question' => 'question', 'rating' => 'rating', diff --git a/lib/plagiarismlib.php b/lib/plagiarismlib.php new file mode 100644 index 00000000000..f27255db34d --- /dev/null +++ b/lib/plagiarismlib.php @@ -0,0 +1,1174 @@ +. + +/** + * plagiarismlib.php - Contains Plagiarism related functions called by Modules. + * + * @since 2.0 + * @package moodlecore + * @subpackage plagiarism + * @copyright 2010 Dan Marsden http://danmarsden.com + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +///// GENRIC PLAGIARISM FUNCTIONS //////////////////////////////////////////////////// + +/** + * returns list of possible plagiarism config options + * + * @return array - list of config options + */ +function plagiarism_config_options() { + //list of config vars - defined here to allow re-use + return array('use_plagiarism','plagiarism_show_student_score','plagiarism_show_student_report', + 'plagiarism_draft_submit','plagiarism_compare_student_papers','plagiarism_compare_internet', + 'plagiarism_compare_journals','plagiarism_compare_institution','plagiarism_report_gen', + 'plagiarism_exclude_biblio','plagiarism_exclude_quoted','plagiarism_exclude_matches', + 'plagiarism_exclude_matches_value'); +} + +/** + * This function should be used to initialise settings and check if plagiarism is enabled + * * + * @return mixed - false if not enabled, or returns an array of relavant settings. + */ +function plagiarism_get_settings() { + global $DB; + $plagiarismsettings = (array)get_config('plagiarism'); + //check if tii enabled. + if (isset($plagiarismsettings['turnitin_use']) && $plagiarismsettings['turnitin_use'] && isset($plagiarismsettings['turnitin_accountid']) && $plagiarismsettings['turnitin_accountid']) { + //now check to make sure required settings are set! + if (empty($plagiarismsettings['turnitin_secretkey'])) { + error("Turnitin Secret Key not set!"); + } + if (empty($plagiarismsettings['turnitin_userid'])) { + error("Turnitin userid not set!"); + } + if (empty($plagiarismsettings['turnitin_email'])) { + error("Turnitin email not set!"); + } + if (empty($plagiarismsettings['turnitin_firstname']) || empty($plagiarismsettings['turnitin_lastname'])) { + error("Turnitin firstname/lastname not set!"); + } + return $plagiarismsettings; + } else { + return false; + } +} + +/** + * internal function that returns xml when provided a URL + * + * @param string $url the url being passed. + * @return xml + */ +function plagiarism_get_xml($url) { + require_once("filelib.php"); + if (!($fp = download_file_content($url))) { + error("error trying to open plagiarism XML file!".$url); + } else { + //now do something with the XML file to check to see if this has worked! + $xml = new SimpleXMLElement($fp); + return $xml; + } +} + +/** + * used by admin/cron.php to get similarity scores from submitted files. + * + */ +function plagiarism_check_files() { + $plagiarismsettings = plagiarism_get_settings(); + if (!empty($plagiarismsettings['turnitin_use'])) { + //turnitin_send_files($plagiarismsettings); //now handled by events + turnitin_get_scores($plagiarismsettings); + } +} + +/** + * generates a url to allow access to a similarity report. - helper functino for plagiarism_get_link + * + * @param object $file - single record from plagiarism_files table + * @param object $course - usually global $COURSE value + * @param array $plagiarismsettings - from a call to plagiarism_get_settings + * @return string - url to allow login/viewing of a similarity report + */ +function plagiarism_get_report_link($file, $course, $plagiarismsettings) { + global $DB; + $return = ''; + + $tii = array(); + if (!has_capability('mod/assignment:grade', get_context_instance(CONTEXT_MODULE, $file->cm))) { + $user = $DB->get_record('user', array('id'=>$file->userid)); + + $tii['username'] = $user->username; + $tii['uem'] = $user->email; + $tii['ufn'] = $user->firstname; + $tii['uln'] = $user->lastname; + $tii['uid'] = $user->username; + $tii['utp'] = '1'; //1 = this user is an student + } else { + $tii['username'] = $plagiarismsettings['turnitin_userid']; + $tii['uem'] = $plagiarismsettings['turnitin_email']; + $tii['ufn'] = $plagiarismsettings['turnitin_firstname']; + $tii['uln'] = $plagiarismsettings['turnitin_lastname']; + $tii['uid'] = $plagiarismsettings['turnitin_userid']; + $tii['utp'] = '2'; //2 = this user is an instructor + } + $tii['cid'] = $plagiarismsettings['turnitin_courseprefix'].$course->id.$course->shortname; //course ID //may need to include sitename in this to allow more than 1 moodle site with the same TII account to use TII API + $tii['ctl'] = $plagiarismsettings['turnitin_courseprefix'].$course->id.$course->shortname; //Course title. -this uses Course->id and shortname to ensure uniqueness. + $tii['fcmd'] = '1'; //when set to 2 the tii api call returns XML + $tii['fid'] = '6'; + $tii['oid'] = $file->externalid; + + return turnitin_get_url($tii); +} + +/** + * displays the similarity score and provides a link to the full report if allowed. + * + * @param int $userid - userid of the user related to the file + * @param object $file - file object + * @param object $cmid - this course module id + * @param object $module - this modules settings. + * @return string - url to allow login/viewing of a similarity report + */ +function plagiarism_get_links($userid, $file, $cmid, $course, $module) { + global $DB, $CFG, $USER; + $plagiarismvalues = $DB->get_records_menu('plagiarism_config', array('cm'=>$cmid),'','name,value'); + if (empty($plagiarismvalues['use_plagiarism'])) { + //nothing to do here... move along! + return ''; + } + $modulecontext = get_context_instance(CONTEXT_MODULE, $cmid); + $output = ''; + + //check if this is a user trying to look at their details, or a teacher with viewsimilarityscore rights. + if (($USER->id == $userid) || has_capability('moodle/plagiarism:viewsimilarityscore', $modulecontext)) { + if ($plagiarismsettings = plagiarism_get_settings()) { + $plagiarismfile = $DB->get_record('plagiarism_files', array('cm'=>$cmid, + 'userid'=>$userid, + 'file'=>$file->get_id())); + if (isset($plagiarismfile->similarityscore) && $plagiarismfile->statuscode=='success') { //if TII has returned a succesful score. + //check for open mod. + $assignclosed = false; + $time = time(); + if (!empty($module->preventlate) && !empty($module->timedue)) { + $assignclosed = ($module->timeavailable <= $time && $time <= $module->timedue); + } elseif (!empty($module->timeavailable)) { + $assignclosed = ($module->timeavailable <= $time); + } + $assignclosed = false; + $rank = plagiarism_get_css_rank($plagiarismfile->similarityscore); + if ($USER->id <> $userid) { //this is a teacher with moodle/plagiarism:viewsimilarityscore + if (has_capability('moodle/plagiarism:viewfullreport', $modulecontext)) { + $output .= ''.get_string('similarity', 'plagiarism').':'.$plagiarismfile->similarityscore.'%'; + } else { + $output .= ''.get_string('similarity', 'plagiarism').':'.$plagiarismfile->similarityscore.'%'; + } + } elseif (isset($plagiarismvalues['plagiarism_show_student_report']) && isset($plagiarismvalues['plagiarism_show_student_score']) and //if report and score fields are set. + ($plagiarismvalues['plagiarism_show_student_report']== 1 or $plagiarismvalues['plagiarism_show_student_score'] ==1 or //if show always is set + ($plagiarismvalues['plagiarism_show_student_score']==2 && $assignclosed) or //if student score to be show when assignment closed + ($plagiarismvalues['plagiarism_show_student_report']==2 && $assignclosed))) { //if student report to be shown when assignment closed + if (($plagiarismvalues['plagiarism_show_student_report']==2 && $assignclosed) or $plagiarismvalues['plagiarism_show_student_report']==1) { + $output .= ''.get_string('similarity', 'plagiarism').''; + if ($plagiarismvalues['plagiarism_show_student_score']==1 or ($plagiarismvalues['plagiarism_show_student_score']==2 && $assignclosed)) { + $output .= ':'.$plagiarismfile->similarityscore.'%'; + } + $output .= ''; + } else { + $output .= ''.get_string('similarity', 'plagiarism').':'.$plagiarismfile->similarityscore.'%'; + } + } + //now check if grademark enabled and return the status of this file. + if (!empty($plagiarismsettings['turnitin_enablegrademark'])) { + $output .= ''.turnitin_get_grademark_link($plagiarismfile, $course, $module, $plagiarismsettings).""; + } + } elseif(isset($plagiarismfile->statuscode)) { //always display errors - even if the student isn't able to see report/score. + $output .= turnitin_error_text($plagiarismfile->statuscode); + } + } + } + return $output.'
'; +} + +/** + * updates a plagiarism_files record + * + * @param object $event - data from an event trigger + * @param object $file - a file object + * @return int - id of plagiarism_files record + */ +function plagiarism_update_record($event, $file='') { + global $USER, $DB,$CFG; + + $fileid = $event->file->get_id(); + //now update or insert record into plagiarism_files + if ($plagiarism_file = $DB->get_record('plagiarism_files', array('cm'=>$event->cm->id, + 'userid'=>$event->user->id, + 'file'=>$fileid))) { + //update record. + $plagiarism_file->statuscode = 'pending'; + $plagiarism_file->similarityscore ='0'; + if (! $DB->update_record('plagiarism_files', $plagiarism_file)) { + debugging("update plagiarism_files failed!"); + } + return $plagiarism_file->id; + } else { + $plagiarism_file = new object(); + $plagiarism_file->cm = $event->cm->id; + $plagiarism_file->userid = $event->user->id; + $plagiarism_file->file = $fileid; + $plagiarism_file->statuscode = 'pending'; + if (!$pid = $DB->insert_record('plagiarism_files', $plagiarism_file)) { + debugging("insert into plagiarism_files failed"); + } + return $pid; + } +} + +/** + * saves/updates plagiarism settings from a modules config page - called by course/modedit.php + * + * @param object $data - form data + */ +function plagiarism_save_form_elements($data) { + global $DB; + if (!plagiarism_get_settings()) { + return; + } + if (isset($data->use_plagiarism)) { + //array of posible plagiarism config options. + $plagiarismelements = plagiarism_config_options(); + + //first get existing values + $existingelements = $DB->get_records_menu('plagiarism_config', array('cm'=>$data->coursemodule),'','name,id'); + foreach($plagiarismelements as $element) { + if (isset($data->$element)) { + $newelement = new object(); + $newelement->cm = $data->coursemodule; + $newelement->name = $element; + $newelement->value = $data->$element; + if (isset($existingelements[$element])) { //update + $newelement->id = $existingelements[$element]; + $DB->update_record('plagiarism_config', $newelement); + } else { //insert + $DB->insert_record('plagiarism_config', $newelement); + } + } + } + } +} + +/** + * adds the list of plagiarism settings to a form + * + * @param object $mform - Moodle form object + */ +function plagiarism_get_form_elements($mform) { + $ynoptions = array( 0 => get_string('no'), 1 => get_string('yes')); + $tiioptions = array(0 => get_string("never"), 1 => get_string("always"), 2 => get_string("showwhenclosed", "plagiarism")); + $tiidraftoptions = array(0 => get_string("submitondraft","plagiarism"), 1 => get_string("submitonfinal","plagiarism")); + $reportgenoptions = array( 0 => get_string('reportgenimmediate', 'plagiarism'), 1 => get_string('reportgenimmediateoverwrite', 'plagiarism'), 2 => get_string('reportgenduedate', 'plagiarism')); + $excludetype = array( 0 => get_string('no'), 1 => get_string('wordcount', 'plagiarism'), 2 => get_string('percentage', 'plagiarism')); + + $mform->addElement('header', 'plagiarismdesc', get_string('plagiarism', 'plagiarism')); + $mform->addElement('select', 'use_plagiarism', get_string("useplagiarism", "plagiarism"), $ynoptions); + $mform->addElement('select', 'plagiarism_show_student_score', get_string("showstudentsscore", "plagiarism"), $tiioptions); + $mform->addElement('select', 'plagiarism_show_student_report', get_string("showstudentsreport", "plagiarism"), $tiioptions); + if ($mform->elementExists('var4')) { + $mform->addElement('select', 'plagiarism_draft_submit', get_string("draftsubmit", "plagiarism"), $tiidraftoptions); + } + $mform->addElement('select', 'plagiarism_compare_student_papers', get_string("plagiarismcomparestudents", "plagiarism"), $ynoptions); + $mform->addElement('select', 'plagiarism_compare_internet', get_string("plagiarismcompareinternet", "plagiarism"), $ynoptions); + $mform->addElement('select', 'plagiarism_compare_journals', get_string("plagiarismcomparejournals", "plagiarism"), $ynoptions); + $mform->addElement('select', 'plagiarism_compare_institution', get_string("plagiarismcompareinstitution", "plagiarism"), $ynoptions); + //$mform->addElement('select', 'plagiarism_compare_institution', get_string("plagiarismcompareinstitution", "plagiarism"), $ynoptions); + $mform->addElement('select', 'plagiarism_report_gen', get_string("plagiarismreportgen", "plagiarism"), $reportgenoptions); + $mform->addElement('select', 'plagiarism_exclude_biblio', get_string("plagiarismexcludebiblio", "plagiarism"), $ynoptions); + $mform->addElement('select', 'plagiarism_exclude_quoted', get_string("plagiarismexcludequoted", "plagiarism"), $ynoptions); + $mform->addElement('select', 'plagiarism_exclude_matches', get_string("plagiarismexcludematches", "plagiarism"), $excludetype); + $mform->addElement('text', 'plagiarism_exclude_matches_value', ''); + $mform->addRule('plagiarism_exclude_matches_value', null, 'numeric', null, 'client'); + $mform->disabledIf('plagiarism_exclude_matches_value', 'plagiarism_exclude_matches', 'eq', 0); + +} + +/** + * adds the list of plagiarism settings to a form - called inside modules that have enabled plagiarism + * + * @param object $mform - Moodle form object + * @param object $context - context object + */ +function plagiarism_get_form_elements_module($mform, $context) { + global $CFG, $DB; + if (!plagiarism_get_settings()) { + return; + } + $cmid = optional_param('update', 0, PARAM_INT); //there doesn't seem to be a way to obtain the current cm a better way - $this->_cm is not available here. + if (!empty($cmid)) { + $plagiarismvalues = $DB->get_records_menu('plagiarism_config', array('cm'=>$cmid),'','name,value'); + } + $plagiarismdefaults = $DB->get_records_menu('plagiarism_config', array('cm'=>0),'','name,value'); //cmid(0) is the default list. + $plagiarismelements = plagiarism_config_options(); + + if (has_capability('moodle/plagiarism:enable', $context)) { + plagiarism_get_form_elements($mform); + if ($mform->elementExists('plagiarism_draft_submit')) { + $mform->disabledIf('plagiarism_draft_submit', 'var4', 'eq', 0); + } + //disable all plagiarism elements if use_plagiarism eg 0 + foreach ($plagiarismelements as $element) { + if ($element <> 'use_plagiarism') { //ignore this var + $mform->disabledIf($element, 'use_plagiarism', 'eq', 0); + } + } + } else { //add plagiarism settings as hidden vars. + foreach ($plagiarismelements as $element) { + $mform->addElement('hidden', $element); + } + } + //now set defaults. + foreach ($plagiarismelements as $element) { + if (isset($plagiarismvalues[$element])) { + $mform->setDefault($element, $plagiarismvalues[$element]); + } elseif (isset($plagiarismdefaults[$element])) { + $mform->setDefault($element, $plagiarismdefaults[$element]); + } + } +} +/** + * updates the status of all files within a module + * + * @param object $course - full Course object + * @param object $cm - full cm object + */ +function plagiarism_update_status($course, $cm) { + global $DB; + if (!$plagiarismsettings = plagiarism_get_settings()) { + return; + } + //currently only used for grademark - check if enabled and return if not. + if (empty($plagiarismsettings['turnitin_enablegrademark'])) { + return; + } + if (!$moduletype = $DB->get_field('modules','name', array('id'=>$cm->module))) { + debugging("invalid moduleid! - moduleid:".$cm->module." Module:".$moduletype); + continue; + } + if (!$module = $DB->get_record($moduletype, array('id'=>$cm->instance))) { + debugging("invalid instanceid! - instance:".$cm->instance." Module:".$moduletype); + continue; + } + + $tii = array(); + //set globals. + $tii['username'] = $plagiarismsettings['turnitin_userid']; + $tii['uem'] = $plagiarismsettings['turnitin_email']; + $tii['ufn'] = $plagiarismsettings['turnitin_firstname']; + $tii['uln'] = $plagiarismsettings['turnitin_lastname']; + $tii['uid'] = $plagiarismsettings['turnitin_userid']; + $tii['utp'] = '2'; //2 = this user is an instructor + $tii['cid'] = $plagiarismsettings['turnitin_courseprefix'].$course->id.$course->shortname; //course ID + $tii['ctl'] = $plagiarismsettings['turnitin_courseprefix'].$course->id.$course->shortname; //Course title. -this uses Course->id and shortname to ensure uniqueness. + $turnitin_assignid = $DB->get_field('plagiarism_config','value', array('cm'=>$cm->id, 'name'=>'turnitin_assignid')); + if (!empty($turnitin_assignid)) { + $tii['assignid'] = $turnitin_assignid; + } + $tii['assign'] = $plagiarismsettings['turnitin_courseprefix']. '_'.$module->name.'_'.$module->id; //assignment name stored in TII + $tii['fcmd'] = '2'; + $tii['fid'] = '10'; + $tiixml = plagiarism_get_xml(turnitin_get_url($tii)); + + if (!empty($tiixml->object)) { + //get full list of plagiarism_files for this cm + $grademarkstatus= array(); + foreach($tiixml->object as $tiiobject) { + $grademarkstatus[(int)$tiiobject->objectID[0]] = (int)$tiiobject->gradeMarkStatus[0]; + } + if (!empty($grademarkstatus)) { + $plagiarsim_files = $DB->get_records('plagiarism_files', array('cm'=>$cm->id)); + foreach ($plagiarsim_files as $file) { + if (isset($grademarkstatus[$file->externalid]) && $file->externalstatus <> $grademarkstatus[$file->externalid]) { + $file->externalstatus = $grademarkstatus[$file->externalid]; + $DB->update_record('plagiarism_files', $file); + } + } + } + } +} +/** +* Function that returns the name of the css class to use for a given similarity score +* @param integer $score - the similarity score +* @return string - string name of css class +*/ +function plagiarism_get_css_rank ($score) { + $rank = "none"; + if($score > 90) { $rank = "1"; } + elseif($score > 80) { $rank = "2"; } + elseif($score > 70) { $rank = "3"; } + elseif($score > 60) { $rank = "4"; } + elseif($score > 50) { $rank = "5"; } + elseif($score > 40) { $rank = "6"; } + elseif($score > 30) { $rank = "7"; } + elseif($score > 20) { $rank = "8"; } + elseif($score > 10) { $rank = "9"; } + elseif($score >= 0) { $rank = "10"; } + + return "rank$rank"; +} + +/** +* Function that prints the student disclosure notifying that the files will be checked for plagiarism +* @param integer $cmid - the cmid of this module +*/ +function plagiarism_print_disclosure($cmid) { + global $DB, $OUTPUT; + if ($plagiarismsettings = plagiarism_get_settings()) { + if (!empty($plagiarismsettings['turnitin_student_disclosure'])) { + $showdisclosure = $DB->get_field('plagiarism_config', 'value', array('cm'=>$cmid, 'name'=>'use_plagiarism')); + if ($showdisclosure) { + echo $OUTPUT->box_start('generalbox boxaligncenter', 'intro'); + $formatoptions = new stdClass; + $formatoptions->noclean = true; + echo format_text($plagiarismsettings['turnitin_student_disclosure'], FORMAT_MOODLE, $formatoptions); + echo $OUTPUT->box_end(); + } + } + } +} + +/// MODULE SPECIFIC FUNCTIONS //////////////////////////////////////////////////// +/// Functions that manage events triggered by modules. + +/** + * event handler for assignment file submission events + * @param object $eventdata - data passed by the event handler. + */ +function plagiarism_event_assignment_file_submission($eventdata) { + global $DB, $CFG; + $result = true; + + //require_once($CFG->libdir.'/filelib.php'); + $plagiarismsettings = plagiarism_get_settings(); + $plagiarismvalues = $DB->get_records_menu('plagiarism_config', array('cm'=>$eventdata->cm->id),'','name,value'); + if (!$plagiarismsettings || empty($plagiarismvalues['use_plagiarism'])) { + //nothing to do here... move along! + return $result; + } + //check if the module associated with this event still exists + if (!$DB->record_exists('course_modules', array('id' => $eventdata->cm->id))) { + return $result; + } + if (!empty($eventdata->file)) { //this is an upload event. + //hacky way to check file still exists + $fs = get_file_storage(); + $fileid = $fs->get_file_by_id($eventdata->file->get_id()); + if (empty($fileid)) { + return $result; + } + if (empty($plagiarismvalues['plagiarism_draft_submit'])) { //check if this is an advanced assignment and shouldn't send the file yet. + $pid = plagiarism_update_record($eventdata); + $result = turnitin_send_file($pid, $eventdata->file); + } + } else { //this is a finalize event + if (isset($plagiarismvalues['plagiarism_draft_submit']) && $plagiarismvalues['plagiarism_draft_submit'] == 1) { // is file to be sent on final submission? + // we need to get a list of files attached to this assignment and put them in an array, so that + // we can submit each of them for processing. + $modulecontext = get_context_instance(CONTEXT_MODULE, $eventdata->cm->id); + $fs = get_file_storage(); + if ($files = $fs->get_area_files($modulecontext->id, 'assignment_submission', $eventdata->user->id, "timemodified", false)) { + foreach ($files as $file) { + //TODO: need to check if this file has already been sent! - possible that the file was sent before draft submit was set. + $eventdata->file = $file; + $pid = plagiarism_update_record($eventdata, $file); + $result = turnitin_send_file($pid, $file); + } + } + } + } + return $result; +} +/** + * Event handler for assignment creation process + * @param object $eventdata - data passed by the event handler. + */ +function plagiarism_event_assignment_mod_created($eventdata) { + //call specific Plagiarism tools if needed. + $result = turnitin_update_assignment($eventdata, 'create'); + + return $result; +} + +/** + * Event handler for assignment update process + * @param object $eventdata - data passed by the event handler. + */ +function plagiarism_event_assignment_mod_updated($eventdata) { + //call specific Plagiarism tools if needed. + $result = turnitin_update_assignment($eventdata, 'update'); + + return $result; +} + +/** + * Event handler for assignment delete process + * @param object $eventdata - data passed by the event handler. + */ +function plagiarism_event_assignment_mod_deleted($eventdata) { + //call specific Plagiarism tools if needed. + $result = turnitin_update_assignment($eventdata, 'delete'); + + return $result; +} + +/// TURNITIN FUNCTIONS //////////////////////////////////////////////////// +//function specific to the Turnitin plagiarism tool + +/** + * generates a url including md5 for use in posting to Turnitin API. + * + * @param object $tii the intial $tii object + * @param bool $returnArray - if true, returns a formatted $tii object, if false returns a url. + * @return mixed - array or url depending on $returnArray. + */ +function turnitin_get_url($tii, $returnArray=false) { + global $CFG,$DB; + $plagiarismsettings = plagiarism_get_settings(); + + //make sure all $tii values are clean. + foreach($tii as $key => $value) { + if (!empty($value) AND $key <> 'tem' AND $key <> 'uem') { + $value = rawurldecode($value); //decode url first. (in case has already be encoded - don't want to end up with double % replacements) + $value = rawurlencode($value); + $value = str_replace('%20', '_', $value); + $tii[$key] = $value; + } + } + //TODO need to check lengths of certain vars. - some cannot be under 5 or over 50. + if (isset($plagiarismsettings['turnitin_senduseremail']) && $plagiarismsettings['turnitin_senduseremail']) { + $tii['dis'] ='0'; //sets e-mail notification for users in tii system to enabled. + } else { + $tii['dis'] ='1'; //sets e-mail notification for users in tii system to disabled. + } + //munge e-mails if prefix is set. + if (isset($plagiarismsettings['turnitin_emailprefix'])) { //if email prefix is set + if ($tii['uem'] <> $plagiarismsettings['turnitin_email']) { //if email is not the global teacher. + $tii['uem'] = $plagiarismsettings['turnitin_emailprefix'] . $tii['uem']; //munge e-mail to prevent user access. + } + } + //set vars if not set. + if (!isset($tii['encrypt'])) { + $tii['encrypt'] = '0'; + } + if (!isset($tii['diagnostic'])) { + $tii['diagnostic'] = '0'; + } + if (!isset($tii['tem'])) { + $tii['tem'] = $plagiarismsettings['turnitin_email']; + } + if (!isset($tii['upw'])) { + $tii['upw'] = ''; + } + if (!isset($tii['cpw'])) { + $tii['cpw'] = ''; + } + if (!isset($tii['ced'])) { + $tii['ced'] = ''; + } + if (!isset($tii['dtdue'])) { + $tii['dtdue'] = ''; + } + if (!isset($tii['dtstart'])) { + $tii['dtstart'] = ''; + } + if (!isset($tii['newassign'])) { + $tii['newassign'] = ''; + } + if (!isset($tii['newupw'])) { + $tii['newupw'] = ''; + } + if (!isset($tii['oid'])) { + $tii['oid'] = ''; + } + if (!isset($tii['pfn'])) { + $tii['pfn'] = ''; + } + if (!isset($tii['pln'])) { + $tii['pln'] = ''; + } + if (!isset($tii['ptl'])) { + $tii['ptl'] = ''; + } + if (!isset($tii['ptype'])) { + $tii['ptype'] = ''; + } + if (!isset($tii['said'])) { + $tii['said'] = ''; + } + if (!isset($tii['assignid'])) { + $tii['assignid'] = ''; + } + if (!isset($tii['assign'])) { + $tii['assign'] = ''; + } + if (!isset($tii['cid'])) { + $tii['cid'] = ''; + } + if (!isset($tii['ctl'])) { + $tii['ctl'] = ''; + } + + $tii['gmtime'] = turnitin_get_gmtime(); + $tii['aid'] = $plagiarismsettings['turnitin_accountid']; + $tii['version'] = rawurlencode($CFG->release); //only used internally by TII. + + //prepare $tii for md5string - need to urldecode before generating the md5. + $tiimd5 = array(); + foreach($tii as $key => $value) { + if (!empty($value) AND $key <> 'tem' AND $key <> 'uem') { + $value = rawurldecode($value); //decode url for calculating MD5 + $tiimd5[$key] = $value; + } else { + $tiimd5[$key] = $value; + } + } + + $tii['md5'] = turnitin_get_md5string($tiimd5); + + if ($returnArray) { + return $tii; + } else { + $url = $plagiarismsettings['turnitin_api']."?"; + foreach ($tii as $key => $value) { + $url .= $key .'='. $value. '&'; + } + + return $url; + } +} + +/** + * internal function gets the current time formatted for use in the Turnitin Url, used by turnitin_get_url + * + * @return string - formatted for use in Turnitin API call. + */ +function turnitin_get_gmtime() { + return substr(gmdate('YmdHi'), 0, -1); +} + +/** + * internal function that generates an md5 based on particular items in a $tii array - used by turnitin_get_url + * + * @param object $tii the intial $tii object + * @return string - calculated md5 + */ +function turnitin_get_md5string($tii){ + global $CFG,$DB; + $plagiarismsettings = plagiarism_get_settings(); + + $md5string = $plagiarismsettings['turnitin_accountid']. + $tii['assign']. + $tii['assignid']. + $tii['ced']. + $tii['cid']. + $tii['cpw']. + $tii['ctl']. + $tii['diagnostic']. + $tii['dis']. + $tii['dtdue']. + $tii['dtstart']. + $tii['encrypt']. + $tii['fcmd']. + $tii['fid']. + $tii['gmtime']. + $tii['newassign']. + $tii['newupw']. + $tii['oid']. + $tii['pfn']. + $tii['pln']. + $tii['ptl']. + $tii['ptype']. + $tii['said']. + $tii['tem']. + $tii['uem']. + $tii['ufn']. + $tii['uid']. + $tii['uln']. + $tii['upw']. + $tii['username']. + $tii['utp']. + $plagiarismsettings['turnitin_secretkey']; + + return md5($md5string); +} + + +/** + * post data to TII + * + * @param object $tii - the object containing all the settings required. + * @return xml + */ +function turnitin_post_data($tii, $file='') { + global $DB, $CFG; + $fields = turnitin_get_url($tii, 'array'); + $url = get_config('plagiarism', 'turnitin_api'); + + $status = check_dir_exists($CFG->dataroot."/plagiarism/",true); + if ($status && !empty($file)) { + //We cannot access the file location of $file directly - we must create a temp file to point to instead + $filename = $CFG->dataroot."/plagiarism/".time(); //unique name for this file. + $fh = fopen($filename,'w'); + fwrite($fh, $file->get_content()); + fclose($fh); + $fields['pdata'] = '@'.$filename; + $c = new curl(array('proxy'=>true)); + $status = new SimpleXMLElement($c->post($url,$fields)); + unlink($filename); + } else { + $c = new curl(array('proxy'=>true)); + $status = new SimpleXMLElement($c->post($url,$fields)); + } + return $status; +} + +/** + * Function that starts Turnitin session - some api calls require this + * + * @param object $plagiarismsettings - from a call to plagiarism_get_settings + * @return string - Turnitin sessionid + */ +function turnitin_start_session($plagiarismsettings) { + $tii = array(); + //set globals. + $tii['username'] = $plagiarismsettings['turnitin_userid']; + $tii['uem'] = $plagiarismsettings['turnitin_email']; + $tii['ufn'] = $plagiarismsettings['turnitin_firstname']; + $tii['uln'] = $plagiarismsettings['turnitin_lastname']; + $tii['uid'] = $plagiarismsettings['turnitin_userid']; + $tii['utp'] = '2'; //2 = this user is an instructor + $tii['fcmd'] = '2'; + $tii['fid'] = '17'; + $tiixml = plagiarism_get_xml(turnitin_get_url($tii)); + if (isset($tiixml->sessionid[0])) { + return $tiixml->sessionid[0]; + } else { + return ''; + } +} +/** + * Function that ends a Turnitin session + * + * @param object $plagiarismsettings - from a call to plagiarism_get_settings + * @param string - Turnitin sessionid - from a call to turnitin_start_session + */ + +function turnitin_end_session($plagiarismsettings, $tiisession) { + if (empty($tiisession)) { + return; + } + $tii = array(); + //set globals. + $tii['username'] = $plagiarismsettings['turnitin_userid']; + $tii['uem'] = $plagiarismsettings['turnitin_email']; + $tii['ufn'] = $plagiarismsettings['turnitin_firstname']; + $tii['uln'] = $plagiarismsettings['turnitin_lastname']; + $tii['uid'] = $plagiarismsettings['turnitin_userid']; + $tii['utp'] = '2'; //2 = this user is an instructor + $tii['fcmd'] = '2'; + $tii['fid'] = '18'; + $tii['session-id'] = $tiisession; + $tiixml = plagiarism_get_xml(turnitin_get_url($tii)); +} + +/** + * used to send files to turnitin for processing + * $pid - id of this record from plagiarism_files table + * $file - contains actual file object + * + */ +function turnitin_send_file($pid, $file) { + global $DB, $CFG; + require_once($CFG->libdir.'/filelib.php'); + $plagiarismsettings = plagiarism_get_settings(); + if (empty($plagiarismsettings['turnitin_use'])) { + return true; + } + //get information about this file + $plagiarism_file = $DB->get_record('plagiarism_files', array('id'=>$pid)); + $plagiarism_file->fileobject = $file; //store fileobject for use in submission. + + if (!$user = $DB->get_record('user', array('id'=>$plagiarism_file->userid))) { + debugging("invalid userid! - userid:".$file->userid." Module:".$moduletype." Fileid:".$plagiarism_file->id); + continue; + } + if (!$cm = $DB->get_record('course_modules', array('id'=>$plagiarism_file->cm))) { + debugging("invalid cmid! ".$file->cm." Fileid:".$file->id); + continue; + } + if (!$course = $DB->get_record('course', array('id'=>$cm->course))) { + debugging("invalid cmid! - courseid:".$file->course." Module:".$moduletype." Fileid:".$plagiarism_file->id); + continue; + } + if (!$moduletype = $DB->get_field('modules','name', array('id'=>$cm->module))) { + debugging("invalid moduleid! - moduleid:".$cm->module." Module:".$moduletype." Fileid:".$plagiarism_file->id); + continue; + } + if (!$module = $DB->get_record($moduletype, array('id'=>$cm->instance))) { + debugging("invalid instanceid! - instance:".$cm->instance." Module:".$moduletype." Fileid:".$plagiarism_file->id); + continue; + } + + //Start Turnitin Session + $tiisession = turnitin_start_session($plagiarismsettings); + //now send the file. + $tii = array(); + $tii['username'] = $user->username; + $tii['uem'] = $user->email; + $tii['ufn'] = $user->firstname; + $tii['uln'] = $user->lastname; + $tii['uid'] = $user->username; + $tii['utp'] = '1'; // 1= this is a student. + $tii['cid'] = $plagiarismsettings['turnitin_courseprefix'].$course->id.$course->shortname; + $tii['ctl'] = $plagiarismsettings['turnitin_courseprefix'].$course->id.$course->shortname; + $tii['fcmd'] = '2'; //when set to 2 the tii api call returns XML + $tii['session-id'] = $tiisession; + //$tii2['diagnostic'] = '1'; + $tii['fid'] = '1'; //set command. - create user and login student to Turnitin (fid=1) + $tiixml = plagiarism_get_xml(turnitin_get_url($tii)); + if (empty($tiixml->rcode[0]) or $tiixml->rcode[0] <> '11') { //this is the success code for uploading a file. - we need to return the oid and save it! + mtrace('could not create user/login to turnitin code:'.$tiixml->rcode[0]); + } else { + $plagiarism_file->statuscode = $tiixml->rcode[0]; + if (! $DB->update_record('plagiarism_files', $plagiarism_file)) { + debugging("Error updating plagiarism_files record"); + } + + //now enrol user in class under the given account (fid=3) + $turnitin_assignid = $DB->get_field('plagiarism_config','value', array('cm'=>$cm->id, 'name'=>'turnitin_assignid')); + if (!empty($turnitin_assignid)) { + $tii['assignid'] = $turnitin_assignid; + } + $tii['assign'] = $plagiarismsettings['turnitin_courseprefix']. '_'.$module->name.'_'.$module->id; + $tii['fid'] = '3'; + //$tii2['diagnostic'] = '1'; + $tiixml = plagiarism_get_xml(turnitin_get_url($tii)); + if (empty($tiixml->rcode[0]) or $tiixml->rcode[0] <> '31') { //this is the success code for uploading a file. - we need to return the oid and save it! + mtrace('could not enrol user in turnitin class code:'.$tiixml->rcode[0]); + } else { + $plagiarism_file->statuscode = $tiixml->rcode[0]; + if (! $DB->update_record('plagiarism_files', $plagiarism_file)) { + debugging("Error updating plagiarism_files record"); + } + + //now submit this uploaded file to Tii! (fid=5) + $tii['utp'] = '1'; //2 = instructor, 1= student. + $tii['fid'] = '5'; + $tii['ptl'] = $file->get_filename(); //paper title + $tii['ptype'] = '2'; //filetype + $tii['pfn'] = $tii['ufn']; + $tii['pln'] = $tii['uln']; + $tii['submit_date'] = gmdate('Ymd', $file->get_timemodified()); + //$tii['diagnostic'] = '1'; + $tiixml = turnitin_post_data($tii, $file); + if ($tiixml->rcode[0] == '51') { //this is the success code for uploading a file. - we need to return the oid and save it! + $plagiarism_file->externalid = $tiixml->objectID[0]; + debugging("success uploading assignment", DEBUG_DEVELOPER); + } else { + debugging("failed to upload assignment errorcode".$tiixml->rcode[0]); + } + $plagiarism_file->statuscode = $tiixml->rcode[0]; + if (! $DB->update_record('plagiarism_files', $plagiarism_file)) { + debugging("Error updating plagiarism_files record"); + } + turnitin_end_session($plagiarismsettings, $tiisession); + + return $tiixml; + } + } +} +/** + * used to obtain similarity scores from Turnitin for submitted files. + * + * @param object $plagiarismsettings - from a call to plagiarism_get_settings + * + */ +function turnitin_get_scores($plagiarismsettings) { + global $DB; + + $count = 0; + mtrace("getting Turnitin scores"); + //first do submission + //get all files set to "51" - success code for uploading. + $files = $DB->get_records('plagiarism_files',array('statuscode'=>'51')); + if (!empty($files)) { + foreach($files as $file) { + //set globals. + $user = $DB->get_record('user', array('id'=>$file->userid)); + $coursemodule = $DB->get_record('course_modules', array('id'=>$file->cm)); + $course = $DB->get_record('course', array('id'=>$coursemodule->course)); + $tii['username'] = $user->username; + $tii['uem'] = $user->email; + $tii['ufn'] = $user->firstname; + $tii['uln'] = $user->lastname; + $tii['uid'] = $user->username; + $tii['utp'] = '1'; // 1= this is a student. + $tii['cid'] = $plagiarismsettings['turnitin_courseprefix'].$course->id.$course->shortname; + $tii['ctl'] = $plagiarismsettings['turnitin_courseprefix'].$course->id.$course->shortname; + $tii['fcmd'] = '2'; //when set to 2 the tii api call returns XML + $tii['fid'] = '6'; + $tii['oid'] = $file->externalid; + $tiixml = plagiarism_get_xml(turnitin_get_url($tii)); + if ($tiixml->rcode[0] == '61') { //this is the success code for uploading a file. - we need to return the oid and save it! + $file->similarityscore = $tiixml->originalityscore[0]; + $file->statuscode = 'success'; + if (! $DB->update_record('plagiarism_files', $file)) { + debugging("Error updating plagiarism_files record"); + } + } else { + mtrace('similarity report not available yet for fileid:'.$file->id. " code:".$tiixml->rcode[0]); + } + } + } +/* if (!empty($plagiarismsettings['turnitin_enablegrademark'])) { + mtrace("check for external Grades"); + }*/ +} + + +/** + * given an error code, returns the description for this error + * @param string statuscode The Error code. + * @param boolean $notify if true, returns a notify call - otherwise just returns the text of the error. + */ +function turnitin_error_text($statuscode, $notify=true) { + $return = ''; + $statuscode = (int) $statuscode; + if (!empty($statuscode)) { + if ($statuscode < 100) { //don't return an error state for codes 0-99 + return ''; + } elseif (($statuscode > 1006 && $statuscode < 1014) or ($statuscode > 1022 && $statuscode < 1025) or $statuscode == 1020) { //these are general errors that a could be useful to students. + $return = get_string('tiierror'.$statuscode, 'plagiarism'); + } elseif ($statuscode > 1024 && $statuscode < 2000) { //don't have documentation on the other 1000 series errors, so just display a general one. + $return = get_string('tiierrorpaperfail', 'plagiarism').':'.$statuscode; + } elseif ($statuscode < 1025 || $statuscode > 2000) { //these are not errors that a student can make any sense out of. + $return = get_string('tiiconfigerror', 'plagiarism').'('.$statuscode.')'; + } + if (!empty($return) && $notify) { + $return = notify($return, 'notifyproblem', 'left', true); + } + } + return $return; +} + +/** + * creates/updates the assignment within Turnitin - used by event handlers. + * + * @param object $eventdata - data returned in an Event + * @return boolean returns false if unexpected error occurs. + */ +function turnitin_update_assignment($eventdata, $action) { + global $DB; + $result = true; + $plagiarismsettings = plagiarism_get_settings(); + $plagiarismvalues = $DB->get_records_menu('plagiarism_config', array('cm'=>$eventdata->cm),'','name,value'); + if (!$plagiarismsettings || empty($plagiarismvalues['use_plagiarism'])) { //plagiarism not enabled so don't continue + return $result; + } + if ($action=='delete') { + //delete function deliberately not handled (fid=8) + //if an assignment is deleted "accidentally" we can resotre off backups - but if + //the external Turnitin assignment is deleted, we can't easily restore that. + //maybe a config option could be added to enable/disable this + return true; + } + //first set up this assignment/assign the global teacher to this course. + $course = $DB->get_record('course', array('id'=>$eventdata->course)); + if (empty($course)) { + debugging("couldn't find course record - might have been deleted?", DEBUG_DEVELOPER); + return true; //don't let this event kill cron + } + if (!$cm = $DB->get_record('course_modules', array('id'=>$eventdata->cm))) { + debugging("invalid cmid! - might have been deleted?".$eventdata->cm, DEBUG_DEVELOPER); + return true; //don't let this event kill cron + } + if (!$moduletype = $DB->get_field('modules','name', array('id'=>$cm->module))) { + debugging("invalid moduleid! - moduleid:".$cm->module." Module:".$moduletype, DEBUG_DEVELOPER); + return true; //don't let this event kill cron + } + if (!$module = $DB->get_record($moduletype, array('id'=>$cm->instance))) { + debugging("invalid instanceid! - instance:".$cm->instance." Module:".$moduletype, DEBUG_DEVELOPER); + return true; //don't let this event kill cron + } + + $tiisession = turnitin_start_session($plagiarismsettings); + $tii = array(); + //set globals. + $tii['username'] = $plagiarismsettings['turnitin_userid']; + $tii['uem'] = $plagiarismsettings['turnitin_email']; + $tii['ufn'] = $plagiarismsettings['turnitin_firstname']; + $tii['uln'] = $plagiarismsettings['turnitin_lastname']; + $tii['uid'] = $plagiarismsettings['turnitin_userid']; + $tii['utp'] = '2'; //2 = this user is an instructor + $tii['cid'] = $plagiarismsettings['turnitin_courseprefix'].$course->id.$course->shortname; //course ID + $tii['ctl'] = $plagiarismsettings['turnitin_courseprefix'].$course->id.$course->shortname; //Course title. -this uses Course->id and shortname to ensure uniqueness. + $tii['session-id'] = $tiisession; + if ($action=='create' or $action=='update') { //TODO: split this into 2 - we don't need to call the create if we know it already exists. + $tii['fcmd'] = '2'; //when set to 2 the TII API should return XML + $tii['fid'] = '2'; // create class under the given account and assign above user as instructor (fid=2) + //$tii['diagnostic'] = '1'; + $tiixml = plagiarism_get_xml(turnitin_get_url($tii)); + if (!empty($tiixml->rcode[0]) && ($tiixml->rcode[0] == '20' or $tiixml->rcode[0] == '21' or $tiixml->rcode[0] == '22')) { //these rcodes signify that this assignment exists, or has been successfully updated. + //now create Assignment in Class + $tii['assignid'] = $plagiarismsettings['turnitin_courseprefix']. '_'.$module->name.'_'.$module->id; //assignment ID - uses $returnid to ensure uniqueness + $tii['assign'] = $plagiarismsettings['turnitin_courseprefix']. '_'.$module->name.'_'.$module->id; //assignment name stored in TII + $tii['fid'] = '4'; + $tii['ptl'] = $course->id.$course->shortname; //paper title? - assname? + $tii['ptype'] = '2'; //filetype + $tii['pfn'] = $tii['ufn']; + $tii['pln'] = $tii['uln']; + if (!empty($module->timeavailable)) { + $tii['dtstart'] = gmdate('Ymd', $module->timeavailable); + } else { + $tii['dtstart'] = gmdate('Ymd', time()); + } + if (!empty($module->timedue) && !empty($module->preventlate)) { + $tii['dtdue'] = gmdate('Ymd', $module->timedue); //set to 1 day in future from date due. + } else { + $tii['dtdue'] = gmdate('Ymd', time()+ (30 * 24 * 60 * 60)); //set to 30 days in future if not set by the module. + } + $tii['s_view_report'] = (empty($plagiarismvalues['plagiarism_show_student_report']) ? 0 : 1); //allow students to view the full report. + $tii['s_paper_check'] = (isset($plagiarismvalues['plagiarism_compare_student_papers']) ? $plagiarismvalues['plagiarism_compare_student_papers'] : ''); + $tii['internet_check'] = (isset($plagiarismvalues['plagiarism_compare_internet']) ? $plagiarismvalues['plagiarism_compare_internet'] : ''); + $tii['journal_check'] = (isset($plagiarismvalues['plagiarism_compare_journals']) ? $plagiarismvalues['plagiarism_compare_journals'] : ''); + $tii['institution_check'] = (isset($plagiarismvalues['plagiarism_compare_institution']) ? $plagiarismvalues['plagiarism_compare_institution'] : ''); + $tii['report_gen_speed'] = (isset($plagiarismvalues['plagiarism_compare_institution']) ? $plagiarismvalues['plagiarism_compare_institution'] : ''); + $tii['exclude_biblio'] = (isset($plagiarismvalues['plagiarism_exclude_biblio']) ? $plagiarismvalues['plagiarism_exclude_biblio'] : ''); + $tii['exclude_quoted'] = (isset($plagiarismvalues['plagiarism_exclude_quoted']) ? $plagiarismvalues['plagiarism_exclude_quoted'] : ''); + $tii['exclude_type'] = (isset($plagiarismvalues['plagiarism_exclude_matches']) ? $plagiarismvalues['plagiarism_exclude_matches'] : ''); + $tii['exclude_value'] = (isset($plagiarismvalues['plagiarism_exclude_matches_value']) ? $plagiarismvalues['plagiarism_exclude_matches_valuesss'] : ''); + //$tii['diagnostic'] = '1'; //debug only - uncomment when using in production. + //first check if this assignment has already been created + if (empty($plagiarismvalues['turnitin_assignid'])) { + $tii['fcmd'] = '2'; //when set to 2 create the assignment + } else { + $tii['assignid'] = $plagiarismvalues['turnitin_assignid']; + $tii['fcmd'] = '3'; //when set to 3 - it updates the course + } + $tiixml = turnitin_post_data($tii); + if ($tiixml->rcode[0]=='419') { //if assignment already exists then update it and set externalassignid correctly + $tii['fcmd'] = '3'; //when set to 3 - it updates the course + $tiixml = turnitin_post_data($tii); + } + if ($tiixml->rcode[0]=='41' or $tiixml->rcode[0]=='42') { + mtrace("Turnitin Success creating Class and assignment"); + } else { + mtrace("Error: could not create assignment in class statuscode:".$tiixml->rcode[0]); + $return = false; + } + if (!empty($tiixml->assignmentid[0])) { + if (empty($plagiarismvalues['turnitin_assignid'])) { + $configval = new stdclass(); + $configval->cm = $eventdata->cm; + $configval->name = 'turnitin_assignid'; + $configval->value = $tiixml->assignmentid[0]; + $DB->insert_record('plagiarism_config', $configval); + } else { + $configval = $DB->get_record('plagiarism_config', array('cm'=> $eventdata->cm, 'name'=> 'turnitin_assignid')); + $configval->value = $tiixml->assignmentid[0]; + $DB->update_record('plagiarism_config', $configval); + } + } + } else { + mtrace("Error: could not create class and assign global instructor statuscode:".$rcode); + $return = false; + } + } + turnitin_end_session($plagiarismsettings, $tiisession); + return $result; +} + +/** + * returns link to grademark for a file. + * this function assumes that another process has already updated the grademark status + * + * @param object $plagiarismfile - record from plagiarsim_files table + * @param object $course - course record + * @param object $course - module record + * @param object $plagiarismsettings - from a call to plagiarism_get_settings + * @return string - link to grademark function including images. + */ +function turnitin_get_grademark_link($plagiarismfile, $course, $module, $plagiarismsettings) { + global $DB, $CFG, $OUTPUT, $USER; + $output = ''; + //first check the grademark status - don't show link if not enabled + if (empty($plagiarismsettings['turnitin_enablegrademark'])) { + return $output; + } + if (empty($plagiarismfile->externalstatus) || + ($USER->id <> $plagiarism_file->userid && !empty($module->timedue) && $module->timedue > time())) { + //Grademark isn't available yet - don't provide link + $output = ''; + } else { + $tii = array(); + if (!has_capability('mod/assignment:grade', get_context_instance(CONTEXT_MODULE, $plagiarismfile->cm))) { + $user = $DB->get_record('user', array('id'=>$plagiarismfile->userid)); + + $tii['username'] = $user->username; + $tii['uem'] = $user->email; + $tii['ufn'] = $user->firstname; + $tii['uln'] = $user->lastname; + $tii['uid'] = $user->username; + $tii['utp'] = '1'; //1 = this user is an student + } else { + $tii['username'] = $plagiarismsettings['turnitin_userid']; + $tii['uem'] = $plagiarismsettings['turnitin_email']; + $tii['ufn'] = $plagiarismsettings['turnitin_firstname']; + $tii['uln'] = $plagiarismsettings['turnitin_lastname']; + $tii['uid'] = $plagiarismsettings['turnitin_userid']; + $tii['utp'] = '2'; //2 = this user is an instructor + } + $tii['cid'] = $plagiarismsettings['turnitin_courseprefix'].$course->id.$course->shortname; //course ID //may need to include sitename in this to allow more than 1 moodle site with the same TII account to use TII API + $tii['ctl'] = $plagiarismsettings['turnitin_courseprefix'].$course->id.$course->shortname; //Course title. -this uses Course->id and shortname to ensure uniqueness. + $tii['fcmd'] = '1'; //when set to 2 the tii api call returns XML + $tii['fid'] = '13'; + $tii['oid'] = $plagiarismfile->externalid; + $output = ''; + } + return $output; +} +/** + * Function that returns turnaround time for reports from Turnitin + * + * @param object $plagiarismsettings - from a call to plagiarism_get_settings + * @return xml - xml + */ +function turnitin_get_responsetime($plagiarismsettings) { + $tii = array(); + //set globals. + $tii['username'] = $plagiarismsettings['turnitin_userid']; + $tii['uem'] = $plagiarismsettings['turnitin_email']; + $tii['ufn'] = $plagiarismsettings['turnitin_firstname']; + $tii['uln'] = $plagiarismsettings['turnitin_lastname']; + $tii['uid'] = $plagiarismsettings['turnitin_userid']; + $tii['utp'] = '2'; //2 = this user is an instructor + $tii['fcmd'] = '2'; + $tii['fid'] = '14'; + $tiixml = plagiarism_get_xml(turnitin_get_url($tii)); + return $tiixml; +} diff --git a/mod/assignment/db/events.php b/mod/assignment/db/events.php new file mode 100644 index 00000000000..5101f9b190a --- /dev/null +++ b/mod/assignment/db/events.php @@ -0,0 +1,34 @@ + array ( + 'handlerfile' => '/lib/plagiarismlib.php', + 'handlerfunction' => 'plagiarism_event_assignment_file_submission', + 'schedule' => 'cron' + ), + 'assignment_finalize_sent' => array ( + 'handlerfile' => '/lib/plagiarismlib.php', + 'handlerfunction' => 'plagiarism_event_assignment_file_submission', + 'schedule' => 'cron' + ), + 'assignment_mod_created' => array ( + 'handlerfile' => '/lib/plagiarismlib.php', + 'handlerfunction' => 'plagiarism_event_assignment_mod_created', + 'schedule' => 'cron' + ), + 'assignment_mod_updated' => array ( + 'handlerfile' => '/lib/plagiarismlib.php', + 'handlerfunction' => 'plagiarism_event_assignment_mod_updated', + 'schedule' => 'cron' + ), + 'assignment_mod_deleted' => array ( + 'handlerfile' => '/lib/plagiarismlib.php', + 'handlerfunction' => 'plagiarism_event_assignment_mod_deleted', + 'schedule' => 'cron' + ), + +); \ No newline at end of file diff --git a/mod/assignment/lib.php b/mod/assignment/lib.php index e6c358e7a5b..6d94e4c68d8 100644 --- a/mod/assignment/lib.php +++ b/mod/assignment/lib.php @@ -207,6 +207,7 @@ class assignment_base { echo $OUTPUT->box_start('generalbox boxaligncenter', 'intro'); echo format_module_intro('assignment', $this->assignment, $this->cm->id); echo $OUTPUT->box_end(); + plagiarism_print_disclosure($this->cm->id); } /** @@ -470,6 +471,10 @@ class assignment_base { if (! $DB->delete_records('assignment', array('id'=>$assignment->id))) { $result = false; } + $mod = $DB->get_field('modules','id',array('name'=>'assignment')); + if (! $DB->delete_records('plagiarism_files', array('cm'=>$cm->id))) { + $result = false; + } assignment_grade_item_delete($assignment); @@ -1080,7 +1085,7 @@ class assignment_base { /* first we check to see if the form has just been submitted * to request user_preference updates */ - + plagiarism_update_status($this->course, $this->cm); if (isset($_POST['updatepref'])){ $perpage = optional_param('perpage', 10, PARAM_INT); $perpage = ($perpage <= 0) ? 10 : $perpage ; @@ -1801,6 +1806,7 @@ class assignment_base { $button->set_format_by_file($file); $output .= $button->to_html(PORTFOLIO_ADD_ICON_LINK); } + $output .= get_plagiarism_links($userid, $file); $output .= '
'; } if (count($files) > 1 && $this->portfolio_exportable() && has_capability('mod/assignment:exportownsubmission', $this->context)) { diff --git a/mod/assignment/submissions.php b/mod/assignment/submissions.php index ee0168e4f1b..2aa62f68072 100644 --- a/mod/assignment/submissions.php +++ b/mod/assignment/submissions.php @@ -2,6 +2,7 @@ require_once("../../config.php"); require_once("lib.php"); +require_once($CFG->libdir . '/plagiarismlib.php'); $id = optional_param('id', 0, PARAM_INT); // Course module ID $a = optional_param('a', 0, PARAM_INT); // Assignment ID diff --git a/mod/assignment/type/upload/assignment.class.php b/mod/assignment/type/upload/assignment.class.php index 704521662ca..5cc2ae0ef79 100644 --- a/mod/assignment/type/upload/assignment.class.php +++ b/mod/assignment/type/upload/assignment.class.php @@ -291,7 +291,7 @@ class assignment_upload extends assignment_base { $mimetype = $file->get_mimetype(); $path = file_encode_url($CFG->wwwroot.'/pluginfile.php', '/'.$this->context->id.'/assignment_submission/'.$userid.'/'.$filename); $output .= ''.$mimetype.''.s($filename).' '; - + $output .= plagiarism_get_links($userid, $file, $this->cm->id, $this->course, $this->assignment); } } @@ -358,6 +358,9 @@ class assignment_upload extends assignment_base { .' '; } + $output .= plagiarism_get_links($userid, $file, $this->cm->id, $this->course, $this->assignment); + $output .= '
'; + if (has_capability('mod/assignment:exportownsubmission', $this->context)) { $button->set_callback_options('assignment_portfolio_caller', array('id' => $this->cm->id, 'fileid' => $file->get_id()), '/mod/assignment/locallib.php'); $button->set_format_by_file($file); @@ -1024,7 +1027,7 @@ class assignment_upload extends assignment_base { } function setup_elements(&$mform) { - global $CFG, $COURSE; + global $CFG, $COURSE, $DB; $ynoptions = array( 0 => get_string('no'), 1 => get_string('yes')); @@ -1061,6 +1064,9 @@ class assignment_upload extends assignment_base { $mform->setHelpButton('var4', array('trackdrafts', get_string('trackdrafts', 'assignment'), 'assignment')); $mform->setDefault('var4', 1); + $course_context = get_context_instance(CONTEXT_COURSE, $COURSE->id); + plagiarism_get_form_elements_module($mform, $course_context); + } function portfolio_exportable() { diff --git a/mod/assignment/type/uploadsingle/assignment.class.php b/mod/assignment/type/uploadsingle/assignment.class.php index 5b4eac8f297..f43e154e48a 100644 --- a/mod/assignment/type/uploadsingle/assignment.class.php +++ b/mod/assignment/type/uploadsingle/assignment.class.php @@ -23,6 +23,8 @@ class assignment_uploadsingle extends assignment_base { $mimetype = $file->get_mimetype(); $path = file_encode_url($CFG->wwwroot.'/pluginfile.php', '/'.$this->context->id.'/assignment_submission/'.$userid.'/'.$filename); $output .= ''.$mimetype.''.s($filename).'
'; + $output .= get_plagiarism_links($userid, $file); + $output .='
'; } } @@ -136,7 +138,7 @@ class assignment_uploadsingle extends assignment_base { } function setup_elements(&$mform) { - global $CFG, $COURSE; + global $CFG, $COURSE, $DB; $ynoptions = array( 0 => get_string('no'), 1 => get_string('yes')); @@ -153,6 +155,8 @@ class assignment_uploadsingle extends assignment_base { $mform->addElement('select', 'maxbytes', get_string('maximumsize', 'assignment'), $choices); $mform->setDefault('maxbytes', $CFG->assignment_maxbytes); + $course_context = get_context_instance(CONTEXT_COURSE, $COURSE->id); + plagiarism_get_form_elements_module($mform, $course_context); } function portfolio_exportable() { diff --git a/mod/assignment/view.php b/mod/assignment/view.php index 3985143fcde..2d80577189c 100644 --- a/mod/assignment/view.php +++ b/mod/assignment/view.php @@ -3,6 +3,7 @@ require_once("../../config.php"); require_once("lib.php"); require_once($CFG->libdir . '/completionlib.php'); +require_once($CFG->libdir . '/plagiarismlib.php'); $id = optional_param('id', 0, PARAM_INT); // Course Module ID $a = optional_param('a', 0, PARAM_INT); // Assignment ID diff --git a/pix/i/grademark-grey.gif b/pix/i/grademark-grey.gif new file mode 100644 index 0000000000000000000000000000000000000000..8e011f1232a6e502bc5cb96c3680fd973743d2cc GIT binary patch literal 1004 zcmV6ciK{6%`g178e&67#J8C85tTH8XFrM z92^`S9UUGX9v>ecARr(iAt53nA|oRsBqSsyB_$>%CMPE+C@3f?DJd!{Dl021EG#T7 zEiEoCE-x=HFfcGNF)=bSGBYzXG&D3dH8nOiHa9mnI5;>tIXOByIy*Z%JUl!-Jv}}? zK0iM{KtMo2K|w-7LPJACL_|bIMMXwNMn^|SNJvOYNl8jdN=r*iOiWBoO-)WtPESuy zP*6}&QBhJ-Qd3h?R8&+|RaI72R##V7SXfwDSy@_IT3cINTwGjTU0q&YUSD5dU|?Wj zVPRroVq;@tWMpJzWo2e&W@l$-XlQ6@X=!R|YHMq2Y;0_8ZEbFDZf|dIaBy&OadC2T za&vQYbaZreb#-=jc6WDoczAeud3kzzdV70&e0+R;eSLm@et&;|fPjF3fq{a8f`fyD zgoK2Jg@uNOhKGlTh=_=ZiHVAeii?YjjEszpjg5|uj*pLzkdTm(k&%*;l9Q8@l$4Z} zm6ev3mY0{8n3$NEnVFiJnwy)OoSdAUot>VZo}ZteprD|kp`oIpqNAguq@<*!rKP5( zrl+T;sHmu^si~@}s;jH3tgNi9t*x%EuCK4Ju&}VPv9YqUva_?Zw6wIfwY9dkwzs#p zxVX5vxw*Q!y1To(yu7@dCU$jHda z$;ryf%FD~k%*@Qq&CSlv&d<-!(9qD)(b3Y<($mw^)YR0~)z#M4*4Nk9*x1lt)=I7_<=;-L_>FMg~>g((4 z?Ck9A?d|UF?(gsK@bK{Q@$vHV^7Hfa^z`)g_4W4l_V@Sq`1ttw`T6?#`uqF){QUg= z{r&#_{{R2~A^8LW3IP59EC2ui01yBW06+-I0R0IZNU&QsZ3O+T5YfU1kttyU5h6s# zNx~pAdMN3lXjZORgFq!zB4Wgd6D(G!P!T0zLO_4cq%lGciqPW|_gvGLxBQ7BkCiX6D%} zK*TZ^NV2mpU}K)c!MTc+VJ;`<3J%6q91JTt8CP;L&En->&BeHmn{fj-<4i%(jXaE- zc^T#ii_Q|2+|JLiPMCYYFvDIkw&P+9$Hf^|%SxS)WI8R$a7L2htQ5mqMWu!6+P7sG zuE{Z6muI-C!0=Fo;gJf%eO0EFh6c}68J?*zJXdG9t}V9A!eX1b!ABj2#dhYObQxAS zI({}}Ugqd=!$Na~i}zX&nH8=+>s&p4nz8>d=lE&P{@Fs{rw#utXCup6DZ5(fUv5&r zJY*gQ*l&o5SsRz3I7Q-ooc^C+4W;RlT2m!XBt)*s53rh}Za2&DN_JFHm(R}>`-Rm7 z+e%9xXGj0caQ>O$@-xfLcB%H+>foRG{wL}hW=@L!Qy!eS#wB#U-?_HtUv+W68WQgI zPdG7kPTltW@6&2q_GW#b)zrE_|LeT=Z}U1nESmgvasQW9GdCZa_i@{b_xm-COLH!hxidg#;BM_=B){67k490H0zSs1w({xj$>00AgZ zFmRk__|GZhF+pIVBa1MXS8zt}RL$@=Dk?WpxhJco-pE)uIiRak)TS-KaPm`bdCPXT zO&oiAB zpupJ9qvSP3!ElkUlN5`POhv;%Csr;iHL=12364xG3lta3{Nc{jz`!bDz#x#i=zxVw OLydxfgU6u;25SHsLR{Ve literal 0 HcmV?d00001 diff --git a/version.php b/version.php index a0fe9eb74fc..ad91c20e316 100644 --- a/version.php +++ b/version.php @@ -6,7 +6,7 @@ // This is compared against the values stored in the database to determine // whether upgrades should be performed (see lib/db/*.php) - $version = 2010042303; // YYYYMMDD = date of the last version bump + $version = 2010042304; // YYYYMMDD = date of the last version bump // XX = daily increments $release = '2.0 dev (Build: 20100427)'; // Human-friendly version name