Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Settings for journal managers #31

Merged
merged 52 commits into from
Jul 13, 2022
Merged
Show file tree
Hide file tree
Changes from 47 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
0da44a2
Update PlagiarismPlugin.inc.php
radekgomola Jun 15, 2022
3d949f2
Update PlagiarismPlugin.inc.php
radekgomola Jun 15, 2022
907db25
Create PlagiarismSettingsForm.inc.php
radekgomola Jun 15, 2022
a2a3295
Create settingsForm.tpl
radekgomola Jun 15, 2022
3d08477
Allow for config.inc.php settings to override
ctgraham Jun 15, 2022
5fde58b
Let the settings form know if the settings are forced.
ctgraham Jun 15, 2022
7298077
Present a message on the form if credentials are forced.
ctgraham Jun 15, 2022
4749d71
Add en_US locale strings for iThenticate settings form.
ctgraham Jun 15, 2022
2ef3fb0
Update plugin name
ctgraham Jun 15, 2022
8411f9a
Merge pull request #1 from ctgraham/main
radekgomola Jun 15, 2022
b22dcfb
Add missing locale keys for required fields
ctgraham Jun 15, 2022
1cf6026
Update PlagiarismPlugin.inc.php
radekgomola Jun 15, 2022
0273cc0
Update PlagiarismSettingsForm.inc.php
radekgomola Jun 15, 2022
763f18f
Merge pull request #2 from ctgraham/patch-1
radekgomola Jun 15, 2022
7ad9c0d
bugfixes: missing locale key, array return syntax
ctgraham Jun 15, 2022
d35ba37
Update PlagiarismPlugin.inc.php
radekgomola Jun 15, 2022
14afbc1
Update PlagiarismSettingsForm.inc.php
radekgomola Jun 15, 2022
fb1a165
debugging and README updates
ctgraham Jun 15, 2022
d181a94
Merge branch 'main' into sprint-dev
radekgomola Jun 15, 2022
714cbf4
Merge pull request #3 from ctgraham/sprint-dev
radekgomola Jun 15, 2022
cd52598
Cleanup forced credentials logic errors
ctgraham Jun 16, 2022
5f08ce0
Merge branch 'main' of https://github.com/radekgomola/plagiarism into…
ctgraham Jun 16, 2022
8720530
Merge pull request #4 from ctgraham/sprint-dev
radekgomola Jun 16, 2022
bb96dcb
Update PlagiarismPlugin.inc.php
radekgomola Jun 16, 2022
8e6dec5
Update PlagiarismSettingsForm.inc.php
radekgomola Jun 16, 2022
5e6d859
Update PlagiarismPlugin.inc.php
radekgomola Jun 16, 2022
570e2c2
add exception for bad passwords
ctgraham Jun 16, 2022
c09946b
Merge pull request #5 from ctgraham/sprint-dev
radekgomola Jun 16, 2022
6e14730
Make config.inc.php optional
ctgraham Jun 16, 2022
761d01f
Merge branch 'main' of https://github.com/radekgomola/plagiarism into…
ctgraham Jun 16, 2022
7fc1ab3
Merge pull request #6 from ctgraham/sprint-dev
radekgomola Jun 16, 2022
813fa1c
Change text field to password field
radekgomola Jun 16, 2022
1c1a9b7
Update README
ctgraham Jun 16, 2022
9eca5b4
Merge branch 'main' of https://github.com/radekgomola/plagiarism into…
ctgraham Jun 16, 2022
b07448e
Update README
ctgraham Jun 16, 2022
43506e8
Version update
radekgomola Jun 16, 2022
2509468
Draft sending error messages to managers
ctgraham Jun 16, 2022
a7e8791
Make it work for OMP too
radekgomola Jun 16, 2022
809b2ef
Merge pull request #7 from ctgraham/sprint-dev
radekgomola Jun 16, 2022
1072ef6
Notification typeo
ctgraham Jun 16, 2022
6862c9a
error messaging fixes
ctgraham Jun 16, 2022
ff02441
Merge pull request #8 from ctgraham/sprint-dev
radekgomola Jun 16, 2022
7f748e5
README images
ctgraham Jun 16, 2022
ab2b940
Include images in README
ctgraham Jun 16, 2022
79fdfd9
minor cleanups for config.inc.php settings display
ctgraham Jun 16, 2022
79c37b9
Merge branch 'sprint-dev' of ssh://github.com/ctgraham/plagiarism int…
ctgraham Jun 16, 2022
5f82f89
Merge pull request #9 from ctgraham/sprint-dev
radekgomola Jun 16, 2022
3f99871
clarify forced settings message
ctgraham Jun 16, 2022
4907b24
Clarify foldering in README
ctgraham Jun 16, 2022
86f33da
Merge branch 'main' of https://github.com/radekgomola/plagiarism into…
ctgraham Jun 16, 2022
e223660
Code review response
ctgraham Jun 29, 2022
84d7412
Merge pull request #10 from ctgraham/sprint-dev
radekgomola Jun 30, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 120 additions & 16 deletions PlagiarismPlugin.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public function register($category, $path, $mainContextId = null) {
$success = parent::register($category, $path, $mainContextId);
$this->addLocaleData();

if ($success && Config::getVar('ithenticate', 'ithenticate') && $this->getEnabled()) {
if ($success && $this->getEnabled()) {
HookRegistry::register('submissionsubmitstep4form::execute', array($this, 'callback'));
}
return $success;
Expand All @@ -37,25 +37,67 @@ public function getDisplayName() {
* @copydoc Plugin::getDescription()
*/
public function getDescription() {
return Config::getVar('ithenticate', 'ithenticate')?__('plugins.generic.plagiarism.description'):__('plugins.generic.plagiarism.description.seeReadme');
return __('plugins.generic.plagiarism.description');
}

/**
* @copydoc LazyLoadPlugin::getCanEnable()
*/
function getCanEnable() {
if (!parent::getCanEnable()) return false;
return Config::getVar('ithenticate', 'ithenticate');
function getCanEnable($contextId = null) {
return !Config::getVar('ithenticate', 'ithenticate');
}

/**
* @copydoc LazyLoadPlugin::getCanDisable()
*/
function getCanDisable($contextId = null) {
return !Config::getVar('ithenticate', 'ithenticate');
}

/**
* @copydoc LazyLoadPlugin::getEnabled()
*/
function getEnabled($contextId = null) {
if (!parent::getEnabled($contextId)) return false;
return Config::getVar('ithenticate', 'ithenticate');
return parent::getEnabled($contextId) || Config::getVar('ithenticate', 'ithenticate');
}

/**
* Fetch credentials from config.inc.php, if available
* @return array username and password, or null(s)
**/
function getForcedCredentials() {
$request = Application::getRequest();
$context = $request->getContext();
$contextPath = $context->getPath();
$username = Config::getVar('ithenticate', 'username[' . $contextPath . ']',
Config::getVar('ithenticate', 'username'));
$password = Config::getVar('ithenticate', 'password[' . $contextPath . ']',
Config::getVar('ithenticate', 'password'));
return array($username, $password);
asmecher marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Send the editor an error message
* @param $submissionid int
* @param $message string
* @return void
**/
public function sendErrorMessage($submissionid, $message) {
$request = Application::getRequest();
$context = $request->getContext();
import('classes.notification.NotificationManager');
$notificationManager = new NotificationManager();
$roleDao = DAORegistry::getDAO('RoleDAO'); /* @var $roleDao RoleDAO */
// Get the managers.
asmecher marked this conversation as resolved.
Show resolved Hide resolved
$managers = $roleDao->getUsersByRoleId(ROLE_ID_MANAGER, $context->getId());
$managersArray = $managers->toAssociativeArray();
$allUserIds = array_keys($managersArray);
foreach ($allUserIds as $userId) {
$notificationManager->createTrivialNotification($userId, NOTIFICATION_TYPE_ERROR, array('contents' => __('plugins.generic.plagiarism.errorMessage', array('submissionId' => $submissionid, 'errorMessage' => $message))));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I heard this raised already -- but I think a trivial notification probably isn't a good fit. It'll cause a toast notification that will go away, possibly before it can be read/digested, and there's no way to get it back. Maybe an email would be better as per e.g. the Paypal plugin? (The likeliest course of action will be that an editor will get an inscrutable error message and pass it on to an administrator of some kind, so maybe it should be going to the tech contact instead of all editors?)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. We'd like to migrate these to Tasks instead of Trivial Notifications, but we didn't come up with an example of creating a Task in the Sprint. Error messages should be routed to Journal Managers or Administrators, depending on if the settings come from the form, or from config.inc.php. I could also imagine creating Tasks for Editors in the future as part of the review of the plagiarism check. I'd prefer to take care of this in an enhancement round after an initial merge.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Propose to handle in a new PR.

}
error_log('iThenticate submission '.$submissionid.' failed: '.$message);
}

/**
* Send submission files to iThenticate.
* @param $hookName string
Expand All @@ -65,26 +107,37 @@ public function callback($hookName, $args) {
$request = Application::getRequest();
$context = $request->getContext();
$contextPath = $context->getPath();
$submissionDao = Application::getSubmissionDAO();
$submissionDao = DAORegistry::getDAO('SubmissionDAO'); /* @var $submissionDao SubmissionDAO */
$submission = $submissionDao->getById($request->getUserVar('submissionId'));
$publication = $submission->getCurrentPublication();

require_once(dirname(__FILE__) . '/vendor/autoload.php');

// try to get credentials for current context otherwise use default config
$username = Config::getVar('ithenticate', 'username[' . $contextPath . ']',
Config::getVar('ithenticate', 'username'));
$password = Config::getVar('ithenticate', 'password[' . $contextPath . ']',
Config::getVar('ithenticate', 'password'));
$ithenticate = new \bsobbe\ithenticate\Ithenticate($username, $password);
$contextId = $context->getId();
$credentials = $this->getForcedCredentials();
asmecher marked this conversation as resolved.
Show resolved Hide resolved
$username = $credentials[0];
$password = $credentials[1];
if (empty($username) || empty($password)) {
$username = $this->getSetting($contextId, 'ithenticate_user');
$password = $this->getSetting($contextId, 'ithenticate_pass');
}

$ithenticate = null;
try {
$ithenticate = new \bsobbe\ithenticate\Ithenticate($username, $password);
} catch (Exception $e) {
$this->sendErrorMessage($submission->getId(), $e->getMessage());
return false;
}
// Make sure there's a group list for this context, creating if necessary.
$groupList = $ithenticate->fetchGroupList();
$contextName = $context->getLocalizedName($context->getPrimaryLocale());
if (!($groupId = array_search($contextName, $groupList))) {
// No folder group found for the context; create one.
$groupId = $ithenticate->createGroup($contextName);
if (!$groupId) {
error_log('Could not create folder group for context ' . $contextName . ' on iThenticate.');
$this->sendErrorMessage($submission->getId(), 'Could not create folder group for context ' . $contextName . ' on iThenticate.');
return false;
}
}
Expand All @@ -97,7 +150,7 @@ public function callback($hookName, $args) {
true,
true
))) {
error_log('Could not create folder for submission ID ' . $submission->getId() . ' on iThenticate.');
$this->sendErrorMessage($submission->getId(), 'Could not create folder for submission ID ' . $submission->getId() . ' on iThenticate.');
return false;
}

Expand All @@ -117,12 +170,63 @@ public function callback($hookName, $args) {
Services::get('file')->fs->read($file->path),
$folderId
)) {
error_log('Could not submit "' . $submissionFile->getData('path') . '" to iThenticate.');
$this->sendErrorMessage($submission->getId(), 'Could not submit "' . $submissionFile->getData('path') . '" to iThenticate.');
}
}

return false;
}

/**
* @copydoc Plugin::getActions()
*/
function getActions($request, $verb) {
$router = $request->getRouter();
import('lib.pkp.classes.linkAction.request.AjaxModal');
return array_merge(
$this->getEnabled() ? array(
new LinkAction(
'settings',
new AjaxModal(
$router->url($request, null, null, 'manage', null, array('verb' => 'settings', 'plugin' => $this->getName(), 'category' => 'generic')),
$this->getDisplayName()
),
__('manager.plugins.settings'),
null
),
) : array(),
parent::getActions($request, $verb)
);
}

/**
* @copydoc Plugin::manage()
*/
function manage($args, $request) {
switch ($request->getUserVar('verb')) {
case 'settings':
$context = $request->getContext();

AppLocale::requireComponents(LOCALE_COMPONENT_APP_COMMON, LOCALE_COMPONENT_PKP_MANAGER);
$templateMgr = TemplateManager::getManager($request);
$templateMgr->registerPlugin('function', 'plugin_url', array($this, 'smartyPluginUrl'));

$this->import('PlagiarismSettingsForm');
$form = new PlagiarismSettingsForm($this, $context->getId());

if ($request->getUserVar('save')) {
$form->readInputData();
if ($form->validate()) {
$form->execute();
return new JSONMessage(true);
}
} else {
$form->initData();
}
return new JSONMessage(true, $form->fetch($request));
}
return parent::manage($args, $request);
}
}

/**
Expand Down
70 changes: 70 additions & 0 deletions PlagiarismSettingsForm.inc.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

import('lib.pkp.classes.form.Form');

class PlagiarismSettingsForm extends Form {

/** @var int */
var $_contextId;

/** @var object */
var $_plugin;

/**
* Constructor
* @param $plugin PlagiarismPlugin
* @param $contextId int
*/
function __construct($plugin, $contextId) {
$this->_contextId = $contextId;
$this->_plugin = $plugin;

parent::__construct($plugin->getTemplateResource('settingsForm.tpl'));

$this->addCheck(new FormValidator($this, 'ithenticate_user', 'required', 'plugins.generic.plagiarism.manager.settings.usernameRequired'));
radekgomola marked this conversation as resolved.
Show resolved Hide resolved
asmecher marked this conversation as resolved.
Show resolved Hide resolved
$this->addCheck(new FormValidator($this, 'ithenticate_pass', 'required', 'plugins.generic.plagiarism.manager.settings.passwordRequired'));
radekgomola marked this conversation as resolved.
Show resolved Hide resolved

$this->addCheck(new FormValidatorPost($this));
$this->addCheck(new FormValidatorCSRF($this));
}

/**
* Initialize form data.
*/
function initData() {
$credentials = $this->_plugin->getForcedCredentials();
$username = $credentials[0];
$password = $credentials[1];
$this->_data = array(
'ithenticate_user' => $this->_plugin->getSetting($this->_contextId, 'ithenticate_user'),
'ithenticate_pass' => $this->_plugin->getSetting($this->_contextId, 'ithenticate_pass'),
'ithenticate_forced' => !empty($username) && !empty($password)
);
}

/**
* Assign form data to user-submitted data.
*/
function readInputData() {
$this->readUserVars(array('ithenticate_user'));
asmecher marked this conversation as resolved.
Show resolved Hide resolved
$this->readUserVars(array('ithenticate_pass'));
}

/**
* @copydoc Form::fetch()
*/
function fetch($request, $template = null, $display = false) {
$templateMgr = TemplateManager::getManager($request);
$templateMgr->assign('pluginName', $this->_plugin->getName());
return parent::fetch($request, $template, $display);
}

/**
* @copydoc Form::execute()
*/
function execute(...$functionArgs) {
$this->_plugin->updateSetting($this->_contextId, 'ithenticate_user', trim($this->getData('ithenticate_user'), "\"\';"), 'string');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the trim call necessary? What if the password intentionally begins or ends with a "?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@radekgomola asserted that this was strangely needed. I have a vague memory of encountering and removing this formulation in other plugins.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hej @radekgomola! Řekni mi co jsi našel.

$this->_plugin->updateSetting($this->_contextId, 'ithenticate_pass', trim($this->getData('ithenticate_pass'), "\"\';"), 'string');
parent::execute(...$functionArgs);
}
}
54 changes: 54 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# iThenticate Plagiarism Detector Plugin

For OJS/OMP/OPS 3.x

## Overview

This plugin permits automatic submission of uploaded manuscripts to the [iThenticate service](http://www.ithenticate.com/) for plagiarism checking.
1. You need an account of ithenticate.com (costs involved)
* paid via Crossref Similarity Check
* or, paid directly to iThenticate
2. Install the plugin via the Plugin Gallery in the Dashboard
3. Configure the plugin (see below)
* Enable the plugin via config.inc.php or in a specific journal/press/preprint context
* Configure the plugin with the username and password you get from ithenticate.com
* ![Example Settings configuration](ithenticate-settings.png)
4. The author logs in and makes a submission
* The submission files will be sent to iThenticate in Step 4 of the submission process
5. The Editor logs in to ithenticate.com to see the submission
* The submission will be found in a folder named by the Submission ID
* Click to see the report
* ![Example report review](ithenticate-report.png)

Watch [the demo](https://www.ithenticate.com/demo) to know more about the features of iThenticate.

## Configuration

You may set the credentials in config.inc.php, or you may set the credentials per-journal in the plugin settings. If credentials are present in config.inc.php, they will override those entered in the plugin settings form.

The config.inc.php settings format is:

```
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; iThenticate Plugin Settings ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

[ithenticate]

; Enable iThenticate to submit manuscripts after submit step 4
;ithenticate = On
asmecher marked this conversation as resolved.
Show resolved Hide resolved

; Credentials can be set by context : specify journal path
; The username to access the API (usually an email address)
;username[MyJournal_path] = "user@email.com"
; The password to access the API
;password[MyJournal_path] = "password"

; default credentials
; The username to access the API (usually an email address)
;username = "user@email.com"

; The password to access the API
;password = "password"
```

23 changes: 0 additions & 23 deletions README.txt

This file was deleted.

Binary file added ithenticate-report.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ithenticate-settings.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 20 additions & 2 deletions locale/en_US/locale.po
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,23 @@ msgstr "iThenticate Plagiarism Detector Plugin"
msgid "plugins.generic.plagiarism.description"
msgstr "Send all submissions to iThenticate to be checked for possible plagiarism."

msgid "plugins.generic.plagiarism.description.seeReadme"
msgstr "Send all submissions to iThenticate to be checked for possible plagiarism. <strong>See the README document for this plugin for installation instructions.</strong>"
asmecher marked this conversation as resolved.
Show resolved Hide resolved
msgid "plugins.generic.plagiarism.manager.settings.description"
msgstr "Settings for the account used to upload submissions to iThenticate. Contact your iThenticate administrator for details."

msgid "plugins.generic.plagiarism.manager.settings.username"
msgstr "iThenticate Usename"

msgid "plugins.generic.plagiarism.manager.settings.password"
msgstr "iThenticate Password"

msgid "plugins.generic.plagiarism.manager.settings.usernameRequired"
msgstr "iThenticate Usename is required"

msgid "plugins.generic.plagiarism.manager.settings.passwordRequired"
msgstr "iThenticate Password is required"

msgid "plugins.generic.plagiarism.manager.settings.areForced"
msgstr "iThenticate settings are in config.inc.php and changes here will not be used."

msgid "plugins.generic.plagiarism.errorMessage"
msgstr "Upload of submission {$submissionId} to iThenticate failed with error: {$errorMessage}"
Loading