diff --git a/classes/observer.php b/classes/observer.php
index 0530c12..19bdafe 100644
--- a/classes/observer.php
+++ b/classes/observer.php
@@ -101,14 +101,15 @@ class plagiarism_pchkorg_observer {
}
/**
- * Handle the quiz attempt_updated event.
- * @param \mod_quiz\event\attempt_updated $event
+ * Handle the forum reply updated event.
+ * @param \mod_forum\event\assessable_uploaded $event
*/
- public static function quiz_updated(
- \mod_quiz\event\attempt_submitted $event) {
+ public static function forum_assessable_uploaded(
+ \mod_forum\event\assessable_uploaded$event
+ ) {
$eventdata = $event->get_data();
- $eventdata['eventtype'] = 'quiz_submitted';
- $eventdata['other']['modulename'] = 'quiz';
+ $eventdata['eventtype'] = 'forum_attachment';
+ $eventdata['other']['modulename'] = 'forum';
$plugin = new plagiarism_plugin_pchkorg();
$plugin->event_handler($eventdata);
diff --git a/db/events.php b/db/events.php
index 47e21ea..afcfdd1 100644
--- a/db/events.php
+++ b/db/events.php
@@ -45,7 +45,7 @@ $observers = array (
'callback' => 'plagiarism_pchkorg_observer::quiz_submitted',
),
array(
- 'eventname' => '\mod_quiz\event\attempt_updated',
- 'callback' => 'plagiarism_pchkorg_observer::quiz_updated',
- )
+ 'eventname' => '\mod_forum\event\assessable_uploaded',
+ 'callback' => 'plagiarism_pchkorg_observer::forum_assessable_uploaded',
+ ),
);
diff --git a/form/plagiarism_pchkorg_setup_form.php b/form/plagiarism_pchkorg_setup_form.php
index 801c3a8..cb0aeda 100644
--- a/form/plagiarism_pchkorg_setup_form.php
+++ b/form/plagiarism_pchkorg_setup_form.php
@@ -75,6 +75,13 @@ class plagiarism_pchkorg_setup_form extends moodleform {
array(get_string('no'), get_string('yes'))
);
+ $mform->addElement(
+ 'select',
+ 'pchkorg_enable_forum',
+ get_string('pchkorg_enable_forum', 'plagiarism_pchkorg'),
+ array(get_string('no'), get_string('yes'))
+ );
+
$this->add_action_buttons(true);
}
diff --git a/lang/en/plagiarism_pchkorg.php b/lang/en/plagiarism_pchkorg.php
index 09f88c4..bfc2101 100644
--- a/lang/en/plagiarism_pchkorg.php
+++ b/lang/en/plagiarism_pchkorg.php
@@ -42,7 +42,8 @@ $string['pchkorg_min_percent_range'] = 'Must be between 0 and 99';
$string['pchkorg_exclude_self_plagiarism'] = 'Exclude self-plagiarism';
$string['pchkorg_include_referenced'] = 'Include References';
$string['pchkorg_include_citation'] = 'Include Quotes';
-$string['pchkorg_enable_quiz'] = 'Enable Quiz';
+$string['pchkorg_enable_quiz'] = 'Enable PlagiarismCheck in Quizzes';
+$string['pchkorg_enable_forum'] = 'Enable PlagiarismCheck in Forum Activity';
$string['pchkorg_disclosure'] = 'Submission will be sent to PlagiarismCheck.org for check.
By submitting assignment I agree with Terms & Conditions
diff --git a/lib.php b/lib.php
index d3b1de6..c3353d3 100644
--- a/lib.php
+++ b/lib.php
@@ -52,11 +52,13 @@ function plagiarism_pchkorg_coursemodule_standard_elements($formwrapper, $mform)
$pchkorgconfigmodel = new plagiarism_pchkorg_config_model();
$config = $pchkorgconfigmodel->get_system_config('pchkorg_use');
- $isquizenabled = '1' === $pchkorgconfigmodel->get_system_config('pchkorg_enable_quiz');
$enabled = has_capability(capability::ENABLE, $context);
- if ($isquizenabled) {
+ if ( '1' === $pchkorgconfigmodel->get_system_config('pchkorg_enable_quiz')) {
$allowedmodules[] = 'quiz';
}
+ if ( '1' === $pchkorgconfigmodel->get_system_config('pchkorg_enable_forum')) {
+ $allowedmodules[] = 'forum';
+ }
if ('1' == $config && $enabled) {
if (!in_array($modulename, $allowedmodules, true)) {
return;
@@ -165,9 +167,6 @@ function plagiarism_pchkorg_coursemodule_edit_post_actions($data, $course)
}
-
-
-
/**
* Class plagiarism_plugin_pchkorg
*/
@@ -206,9 +205,7 @@ class plagiarism_plugin_pchkorg extends plagiarism_plugin {
if ('1' !== $config) {
return '';
}
-
$context = null;
-
$component = !empty($linkarray['component']) ? $linkarray['component'] : '';
if ($cmid === null && $component == 'qtype_essay' && !empty($linkarray['area'])) {
$questions = question_engine::load_questions_usage_by_activity($linkarray['area']);
@@ -237,7 +234,6 @@ class plagiarism_plugin_pchkorg extends plagiarism_plugin {
return '';
}
-
// Only for some type of account, method will call a remote HTTP API.
// The API will be called only once, because result is static.
// Also, there is timeout 2 seconds for response.
@@ -260,10 +256,16 @@ class plagiarism_plugin_pchkorg extends plagiarism_plugin {
} else {
$where->fileid = $file->get_id();
}
-
$filerecords = $DB->get_records('plagiarism_pchkorg_files', (array) $where,
'id', '*', 0, 1);
+ if (!$filerecords && $file === null) {
+ $where->signature = sha1(trim(strip_tags($linkarray['content'])));
+ $where->fileid = null;
+ $filerecords = $DB->get_records('plagiarism_pchkorg_files', (array) $where,
+ 'id', '*', 0, 1);
+ }
+
if ($filerecords) {
$filerecord = end($filerecords);
@@ -287,7 +289,21 @@ class plagiarism_plugin_pchkorg extends plagiarism_plugin {
} else {
$color = '#f04343';
}
- $PAGE->requires->js_amd_inline("
+ $jsdata = array(
+ 'id' => $filerecord->id,
+ 'title' => $title,
+ 'action' => $action,
+ 'token' => $reporttoken,
+ 'image' => $imgsrc,
+ 'label' => $label,
+ 'color' => $color
+ );
+ static $isjsfuncinjected = false;
+ if (!$isjsfuncinjected) {
+ $isjsfuncinjected = true;
+ $PAGE->requires->js_amd_inline(
+ "
+window.plagiarism_check_data = [];
window.plagiarism_check_full_report = function (action, token) {
const form = document.createElement('form');
const element1 = document.createElement('input');
@@ -311,23 +327,56 @@ window.plagiarism_check_full_report = function (action, token) {
form.submit();
};
- ");
+window.onload = function () {
+ var spans = window.document.getElementsByClassName('plagiarism-pchkorg-widget');
+ for (var span of spans) {
+ for (var classname of span.classList) {
+ if (classname.includes('plagiarism-pchkorg-widget-id-')) {
+ var id = classname.replace('plagiarism-pchkorg-widget-id-', '');
+ if (id) {
+ for (var data of window.plagiarism_check_data) {
+ if (data.id == id) {
+ var a = document.createElement('a');
+ a.setAttribute('href', '#');
+ a.setAttribute('title', data.title);
+ a.style.padding = '5px 3px';
+ a.style.textDecoration = 'none';
+ a.style.backgroundColor = data.color;
+ a.style.color = 'black';
+ a.style.cursor = 'pointer';
+ a.style.borderRadius = '3px 3px 3px 3px';
+ a.style.margin = '4px';
+ a.style.display = 'inline-block';
+ a.onclick = function() {
+ window.plagiarism_check_full_report(data.action, data.token);
+ return false;
+ };
+ var label = document.createTextNode(data.label);
+ var img = document.createElement('img');
+ img.setAttribute('alt', 'PlagiarismCheck.org');
+ img.setAttribute('src', data.image);
+ img.setAttribute('width', '20');
+ a.appendChild(img);
+ a.appendChild(label);
+ span.appendChild(a);
+ break;
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+}
+ "
+ );
+ }
+
+ $PAGE->requires->js_amd_inline("window.plagiarism_check_data.push(".json_encode($jsdata).")");
+
return '
-
-
- ' . $label . '
- ';
+ ';
} else if ($filerecord->state == 10) {
$label = get_string('pchkorg_label_queued', 'plagiarism_pchkorg');
return '
@@ -339,7 +388,7 @@ border-radius: 3px 3px 3px 3px;
margin: 4px;
display: inline-block;"
href="#" class="plagiarism_pchkorg_report_id_score">
-
+
' . $label . '
';
} else if ($filerecord->state == 12) {
@@ -353,7 +402,7 @@ border-radius: 3px 3px 3px 3px;
margin: 4px;
display: inline-block;"
href="#" class="plagiarism_pchkorg_report_id_score">
-
+
' . $label . '
';
}
@@ -448,15 +497,17 @@ display: inline-block;"
$configmodel = new plagiarism_pchkorg_config_model();
$enabled = $configmodel->get_system_config('pchkorg_use');
- $isquizenabled = '1' === $configmodel->get_system_config('pchkorg_enable_quiz');
if ($enabled !== '1') {
return '';
}
$modulename = $cm->modname;
$allowedmodules = array('assign', 'mod_assign');
- if ($isquizenabled) {
+ if ($configmodel->get_system_config('pchkorg_enable_quiz')) {
$allowedmodules[] = 'quiz';
}
+ if ($configmodel->get_system_config('pchkorg_enable_forum')) {
+ $allowedmodules[] = 'forum';
+ }
if (!in_array($modulename, $allowedmodules, true)) {
return '';
}
@@ -499,19 +550,22 @@ display: inline-block;"
$pchkorgconfigmodel = new plagiarism_pchkorg_config_model();
// Token is needed for API auth.
$apitoken = $pchkorgconfigmodel->get_system_config('pchkorg_token');
- $isquizenabled = '1' === $pchkorgconfigmodel->get_system_config('pchkorg_enable_quiz');
$apiprovider = new plagiarism_pchkorg_api_provider($apitoken);
// SQL will be called only once, result is static.
$config = $pchkorgconfigmodel->get_system_config('pchkorg_use');
if ('1' !== $config) {
return true;
}
- if ($isquizenabled) {
+ if ('1' === $pchkorgconfigmodel->get_system_config('pchkorg_enable_quiz')) {
$allowedmodules[] = 'quiz';
}
+ if ('1' === $pchkorgconfigmodel->get_system_config('pchkorg_enable_forum')) {
+ $allowedmodules[] = 'forum';
+ }
if (!in_array($modulename, $allowedmodules, true)) {
return true;
}
+
// Receive couser moudle id.
$cmid = $eventdata['contextinstanceid'];
// Remove the event if the course module no longer exists.
@@ -550,6 +604,82 @@ display: inline-block;"
}
}
+ if ($eventdata['other']['modulename'] === 'forum'
+ && $eventdata['eventtype'] === 'forum_attachment') {
+ if (!empty($eventdata['other']['content'])) {
+ $content = trim(strip_tags($eventdata['other']['content']));
+ if (strlen($content) > 80) {
+ $signature = sha1($content);
+ $filesconditions = array(
+ 'signature' => $signature,
+ 'cm' => $cmid,
+ 'userid' => $USER->id,
+ 'itemid' => $eventdata['objectid']
+ );
+ $oldfile = $DB->get_record('plagiarism_pchkorg_files', $filesconditions);
+ if (!$oldfile) {
+ $filerecord = new \stdClass();
+ $filerecord->fileid = null;
+ $filerecord->cm = $cmid;
+ $filerecord->userid = $USER->id;
+ $filerecord->textid = null;
+ $filerecord->state = 10;
+ $filerecord->created_at = time();
+ $filerecord->itemid = $eventdata['objectid'];
+ $filerecord->signature = $signature;
+
+ $DB->insert_record('plagiarism_pchkorg_files', $filerecord);
+ }
+ }
+ }
+ if (!empty($eventdata['other']['pathnamehashes'])) {
+ foreach ($eventdata['other']['pathnamehashes'] as $pathnamehash) {
+ $file = get_file_storage()->get_file_by_hash($pathnamehash);
+ if (!$file) {
+ // We can not find file so we do not send it in queue.
+ continue;
+ } else {
+ try {
+ // Check that we can fetch content without exception.
+ $content = $file->get_content();
+ } catch (Exception $e) {
+ // No we can not.
+ continue;
+ }
+ }
+ if ($file->get_filename() === '.') {
+ continue;
+ }
+ $filemime = $file->get_mimetype();
+
+ // File type is not supported.
+ if (!$apiprovider->is_supported_mime($filemime)) {
+ continue;
+ }
+ $signature = sha1($content);
+ $filesconditions = array(
+ 'fileid' => $file->get_id()
+ );
+ $oldfile = $DB->get_record('plagiarism_pchkorg_files', $filesconditions);
+ if (!$oldfile) {
+ $filerecord = new \stdClass();
+ $filerecord->fileid = $file->get_id();
+ $filerecord->cm = $cmid;
+ $filerecord->userid = $USER->id;
+ $filerecord->textid = null;
+ $filerecord->state = 10;
+ $filerecord->created_at = time();
+ $filerecord->itemid = $eventdata['objectid'];
+ $filerecord->signature = $signature;
+
+ $DB->insert_record('plagiarism_pchkorg_files', $filerecord);
+ }
+ }
+ }
+
+ return true;
+ }
+
if ($eventdata['other']['modulename'] === 'quiz'
&& $eventdata['eventtype'] === 'quiz_submitted') {
@@ -558,36 +688,76 @@ display: inline-block;"
$questionattempt = $attempt->get_question_attempt($slot);
$qtype = $questionattempt->get_question()->qtype;
if ($qtype instanceof qtype_essay) {
+ $attachments = $questionattempt->get_last_qt_files('attachments', $eventdata['contextid']);
$content = $questionattempt->get_response_summary();
- if (strlen($content) < 80) {
- continue;
+ if (strlen($content) > 80) {
+ $signature = sha1($content);
+ $filesconditions = array(
+ 'signature' => $signature,
+ 'cm' => $cmid,
+ 'userid' => $USER->id,
+ 'itemid' => $eventdata['objectid']
+ );
+
+ $oldfile = $DB->get_record('plagiarism_pchkorg_files', $filesconditions);
+ if ($oldfile) {
+ // There is the same check in database, so we can skip this one.
+ return true;
+ }
+
+ $filerecord = new \stdClass();
+ $filerecord->fileid = null;
+ $filerecord->cm = $cmid;
+ $filerecord->userid = $USER->id;
+ $filerecord->textid = null;
+ $filerecord->state = 10;
+ $filerecord->created_at = time();
+ $filerecord->itemid = $eventdata['objectid'];
+ $filerecord->signature = $signature;
+
+ $DB->insert_record('plagiarism_pchkorg_files', $filerecord);
}
- $signature = sha1($content);
+ foreach ($attachments as $pathnamehash => $file) {
+ if (!$file) {
+ // We can not find file so we do not send it in queue.
+ continue;
+ } else {
+ try {
+ // Check that we can fetch content without exception.
+ $content = $file->get_content();
+ } catch (Exception $e) {
+ // No we can not.
+ continue;
+ }
+ }
+ if ($file->get_filename() === '.') {
+ continue;
+ }
+ $filemime = $file->get_mimetype();
- $filesconditions = array(
- 'signature' => $signature,
- 'cm' => $cmid,
- 'userid' => $USER->id,
- 'itemid' => $eventdata['objectid']
- );
+ // File type is not supported.
+ if (!$apiprovider->is_supported_mime($filemime)) {
+ continue;
+ }
+ $signature = sha1($content);
+ $filesconditions = array(
+ 'fileid' => $file->get_id()
+ );
+ $oldfile = $DB->get_record('plagiarism_pchkorg_files', $filesconditions);
+ if (!$oldfile) {
+ $filerecord = new \stdClass();
+ $filerecord->fileid = $file->get_id();
+ $filerecord->cm = $cmid;
+ $filerecord->userid = $USER->id;
+ $filerecord->textid = null;
+ $filerecord->state = 10;
+ $filerecord->created_at = time();
+ $filerecord->itemid = $eventdata['objectid'];
+ $filerecord->signature = $signature;
- $oldfile = $DB->get_record('plagiarism_pchkorg_files', $filesconditions);
- if ($oldfile) {
- // There is the same check in database, so we can skip this one.
- return true;
+ $DB->insert_record('plagiarism_pchkorg_files', $filerecord);
+ }
}
-
- $filerecord = new \stdClass();
- $filerecord->fileid = null;
- $filerecord->cm = $cmid;
- $filerecord->userid = $USER->id;
- $filerecord->textid = null;
- $filerecord->state = 10;
- $filerecord->created_at = time();
- $filerecord->itemid = $eventdata['objectid'];
- $filerecord->signature = $signature;
-
- $DB->insert_record('plagiarism_pchkorg_files', $filerecord);
}
}
}
@@ -659,7 +829,8 @@ display: inline-block;"
// Queue text content to send to plagiarismcheck.org.
// If there was an error when creating the assignment then still queue the submission so it can be saved as failed.
- if (in_array($eventdata['eventtype'], array("content_uploaded", "assessable_submitted"))
+ if ($eventdata['other']['modulename'] === 'assign'
+ && in_array($eventdata['eventtype'], array("content_uploaded", "assessable_submitted"))
&& !empty($eventdata['other']['content'])) {
$signature = sha1($eventdata['other']['content']);
@@ -790,8 +961,9 @@ display: inline-block;"
$apiprovider->save_accepted_agreement($user->email);
$DB->insert_record('plagiarism_pchkorg_config', $agreementwhere);
}
- if ($filedb->fileid === null) {
- if ($cm->modname === 'quiz') {
+
+ if ($cm->modname === 'quiz') {
+ if ($filedb->fileid === null) {
$questionanswers = $DB->get_records_sql(
"SELECT {question_attempts}.responsesummary "
." FROM {question_attempts} "
@@ -821,6 +993,110 @@ display: inline-block;"
}
}
} else {
+ $file = $fs->get_file_by_id($filedb->fileid);
+ // We can not receive file by id.
+ // Maybe file does not exist anymore.
+ // So we mark it as error and continue.
+ if (!$file || !is_object($file)) {
+ $filedbnew = new stdClass();
+ $filedbnew->id = $filedb->id;
+ $filedbnew->attempt = $filedb->attempt + 1;
+ $filedbnew->state = 11; // Sending error.
+
+ $DB->update_record('plagiarism_pchkorg_files', $filedbnew);
+
+ continue;
+ }
+ $textid = $apiprovider->general_send_check(
+ $apiprovider->user_email_to_hash($user->email),
+ $cm->course,
+ $cm->id,
+ $cm->name,
+ $filedb->itemid,
+ $file->get_id(),
+ $file->get_content(),
+ $file->get_mimetype(),
+ $file->get_filename(),
+ $filters
+ );
+ }
+ }
+ if ($cm->modname === 'forum') {
+ if ($filedb->fileid === null) {
+ $post = $DB->get_record_sql(
+ "SELECT subject, message"
+ ." FROM {forum_posts}"
+ ." WHERE {forum_posts}.id = ?", array(
+ $filedb->itemid
+ )
+ );
+ if ($post) {
+ $subject = $post->subject;
+ $content = $post->message;
+ $signature = sha1($content);
+ $ismatched = false;
+ if ($signature === $filedb->signature) {
+ $ismatched = true;
+ }
+ if (!$ismatched) {
+ $signature = sha1(trim(strip_tags($content)));
+ if ($signature === $filedb->signature) {
+ $ismatched = true;
+ }
+ }
+
+ if ($ismatched) {
+ $textid = $apiprovider->general_send_check(
+ $apiprovider->user_email_to_hash($user->email),
+ $cm->course,
+ $cm->id,
+ $cm->name,
+ $subject,
+ null,
+ html_to_text($content, 75, false),
+ 'plain/text',
+ sprintf('%s-quiz.txt', $filedb->itemid),
+ $filters,
+ );
+ }
+ }
+ } else {
+ $moodlesubmission = $DB->get_record('assign_submission', array(
+ 'assignment' => $cm->instance,
+ 'userid' => $filedb->userid,
+ 'id' => $filedb->itemid,
+ ), 'id');
+ $file = $fs->get_file_by_id($filedb->fileid);
+
+ // We can not receive file by id.
+ // Maybe file does not exist anymore.
+ // So we mark it as error and continue.
+ if (!$file || !is_object($file)) {
+ $filedbnew = new stdClass();
+ $filedbnew->id = $filedb->id;
+ $filedbnew->attempt = $filedb->attempt + 1;
+ $filedbnew->state = 11; // Sending error.
+
+ $DB->update_record('plagiarism_pchkorg_files', $filedbnew);
+
+ continue;
+ }
+ $textid = $apiprovider->general_send_check(
+ $apiprovider->user_email_to_hash($user->email),
+ $cm->course,
+ $cm->id,
+ $cm->name,
+ $moodlesubmission->id,
+ $file->get_id(),
+ $file->get_content(),
+ $file->get_mimetype(),
+ $file->get_filename(),
+ $filters
+ );
+ }
+ }
+ if ($cm->modname === 'assign') {
+ if ($filedb->fileid === null) {
$moodletextsubmission = $DB->get_record(
'assignsubmission_onlinetext',
array('submission' => $filedb->itemid),
@@ -841,41 +1117,42 @@ display: inline-block;"
$filters,
);
}
+ } else {
+ $moodlesubmission = $DB->get_record('assign_submission', array(
+ 'assignment' => $cm->instance,
+ 'userid' => $filedb->userid,
+ 'id' => $filedb->itemid,
+ ), 'id');
+ $file = $fs->get_file_by_id($filedb->fileid);
+
+ // We can not receive file by id.
+ // Maybe file does not exist anymore.
+ // So we mark it as error and continue.
+ if (!$file || !is_object($file)) {
+ $filedbnew = new stdClass();
+ $filedbnew->id = $filedb->id;
+ $filedbnew->attempt = $filedb->attempt + 1;
+ $filedbnew->state = 11; // Sending error.
+
+ $DB->update_record('plagiarism_pchkorg_files', $filedbnew);
+
+ continue;
+ }
+ $textid = $apiprovider->general_send_check(
+ $apiprovider->user_email_to_hash($user->email),
+ $cm->course,
+ $cm->id,
+ $cm->name,
+ $moodlesubmission->id,
+ $file->get_id(),
+ $file->get_content(),
+ $file->get_mimetype(),
+ $file->get_filename(),
+ $filters
+ );
}
- } else {
- $moodlesubmission = $DB->get_record('assign_submission', array(
- 'assignment' => $cm->instance,
- 'userid' => $filedb->userid,
- 'id' => $filedb->itemid,
- ), 'id');
- $file = $fs->get_file_by_id($filedb->fileid);
-
- // We can not receive file by id.
- // Maybe file does not exist anymore.
- // So we mark it as error and continue.
- if (!$file || !is_object($file)) {
- $filedbnew = new stdClass();
- $filedbnew->id = $filedb->id;
- $filedbnew->attempt = $filedb->attempt + 1;
- $filedbnew->state = 11; // Sending error.
-
- $DB->update_record('plagiarism_pchkorg_files', $filedbnew);
-
- continue;
- }
- $textid = $apiprovider->general_send_check(
- $apiprovider->user_email_to_hash($user->email),
- $cm->course,
- $cm->id,
- $cm->name,
- $moodlesubmission->id,
- $file->get_id(),
- $file->get_content(),
- $file->get_mimetype(),
- $file->get_filename(),
- $filters
- );
}
+
$filedbnew = new stdClass();
$filedbnew->id = $filedb->id;
if ($textid) {
diff --git a/version.php b/version.php
index 7d1da3d..b640ef1 100644
--- a/version.php
+++ b/version.php
@@ -26,9 +26,9 @@ defined('MOODLE_INTERNAL') || die();
if (!isset($plugin)) {
$plugin = new stdClass();
}
-$plugin->version = 2022051619;
+$plugin->version = 2022052621;
$plugin->requires = 2020061501; // Requires Moodle 3.9 .
-$plugin->release = 'v3.9.1';
+$plugin->release = 'v3.9.2';
$plugin->component = 'plagiarism_pchkorg';
$plugin->maturity = MATURITY_STABLE;
$plugin->dependencies = array(