first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-06-08 17:09:23 -04:00
commit df3a033196
17887 changed files with 8637778 additions and 0 deletions
+285
View File
@@ -0,0 +1,285 @@
<?php
/**
* @file classes/log/EmailLogDAO.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2003-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class EmailLogDAO
*
* @ingroup log
*
* @see EmailLogEntry, Log
*
* @brief Class for inserting/accessing email log entries.
*/
namespace PKP\log;
use APP\facades\Repo;
use Illuminate\Support\Facades\DB;
use PKP\db\DAOResultFactory;
use PKP\plugins\Hook;
class EmailLogDAO extends \PKP\db\DAO
{
/**
* Retrieve a log entry by ID.
*
* @param int $logId
* @param int $assocType optional
* @param int $assocId optional
*
* @return EmailLogEntry
*/
public function getById($logId, $assocType = null, $assocId = null)
{
$params = [(int) $logId];
if (isset($assocType)) {
$params[] = (int) $assocType;
$params[] = (int) $assocId;
}
$result = $this->retrieve(
'SELECT * FROM email_log WHERE log_id = ?' .
(isset($assocType) ? ' AND assoc_type = ? AND assoc_id = ?' : ''),
$params
);
$row = $result->current();
return $row ? $this->build((array) $row) : null;
}
/**
* Retrieve a log entry by event type.
*
* @param int $assocType
* @param int $assocId
* @param int $eventType
* @param int $userId optional
* @param ?\PKP\db\DBResultRange $rangeInfo optional
*
* @return DAOResultFactory<EmailLogEntry>
*/
public function _getByEventType($assocType, $assocId, $eventType, $userId = null, $rangeInfo = null)
{
$params = [
(int) $assocType,
(int) $assocId,
(int) $eventType
];
if ($userId) {
$params[] = $userId;
}
$result = $this->retrieveRange(
$sql = 'SELECT e.*
FROM email_log e' .
($userId ? ' LEFT JOIN email_log_users u ON e.log_id = u.email_log_id' : '') .
' WHERE e.assoc_type = ? AND
e.assoc_id = ? AND
e.event_type = ?' .
($userId ? ' AND u.user_id = ?' : ''),
$params,
$rangeInfo
);
return new DAOResultFactory($result, $this, 'build', [], $sql, $params, $rangeInfo); // Counted in submissionEmails.tpl
}
/**
* Retrieve all log entries for an object matching the specified association.
*
* @param int $assocType
* @param int $assocId
* @param ?\PKP\db\DBResultRange $rangeInfo optional
*
* @return DAOResultFactory<EmailLogEntry> containing matching EmailLogEntry ordered by sequence
*/
public function getByAssoc($assocType = null, $assocId = null, $rangeInfo = null)
{
$result = $this->retrieveRange(
'SELECT *
FROM email_log
WHERE assoc_type = ?
AND assoc_id = ?
ORDER BY log_id DESC',
[(int) $assocType, (int) $assocId],
$rangeInfo
);
return new DAOResultFactory($result, $this, 'build');
}
/**
* Internal function to return an EmailLogEntry object from a row.
*
* @param array $row
*
* @return EmailLogEntry
*/
public function build($row)
{
$entry = $this->newDataObject();
$entry->setId($row['log_id']);
$entry->setAssocType($row['assoc_type']);
$entry->setAssocId($row['assoc_id']);
$entry->setSenderId($row['sender_id']);
$entry->setDateSent($this->datetimeFromDB($row['date_sent']));
$entry->setEventType($row['event_type']);
$entry->setFrom($row['from_address']);
$entry->setRecipients($row['recipients']);
$entry->setCcs($row['cc_recipients']);
$entry->setBccs($row['bcc_recipients']);
$entry->setSubject($row['subject']);
$entry->setBody($row['body']);
Hook::call('EmailLogDAO::build', [&$entry, &$row]);
return $entry;
}
/**
* Insert a new log entry.
*
* @param EmailLogEntry $entry
*/
public function insertObject($entry)
{
$this->update(
sprintf(
'INSERT INTO email_log
(sender_id, date_sent, event_type, assoc_type, assoc_id, from_address, recipients, cc_recipients, bcc_recipients, subject, body)
VALUES
(?, %s, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
$this->datetimeToDB($entry->getDateSent())
),
[
$entry->getSenderId(),
$entry->getEventType(),
$entry->getAssocType(),
$entry->getAssocId(),
$entry->getFrom(),
$entry->getRecipients(),
$entry->getCcs(),
$entry->getBccs(),
$entry->getSubject(),
$entry->getBody()
]
);
$entry->setId($this->getInsertId());
$this->_insertLogUserIds($entry);
return $entry->getId();
}
/**
* Delete a single log entry for an object.
*
* @param int $logId
* @param int $assocType optional
* @param int $assocId optional
*/
public function deleteObject($logId, $assocType = null, $assocId = null)
{
$params = [(int) $logId];
if (isset($assocType)) {
$params[] = (int) $assocType;
$params[] = (int) $assocId;
}
return $this->update(
'DELETE FROM email_log WHERE log_id = ?' .
(isset($assocType) ? ' AND assoc_type = ? AND assoc_id = ?' : ''),
$params
);
}
/**
* Delete all log entries for an object.
*
* @param int $assocType
*
* @praam $assocId int
*/
public function deleteByAssoc($assocType, $assocId)
{
return $this->update(
'DELETE FROM email_log WHERE assoc_type = ? AND assoc_id = ?',
[(int) $assocType, (int) $assocId]
);
}
/**
* Transfer all log and log users entries to another user.
*
* @param int $oldUserId
* @param int $newUserId
*/
public function changeUser($oldUserId, $newUserId)
{
return [
$this->update(
'UPDATE email_log SET sender_id = ? WHERE sender_id = ?',
[(int) $newUserId, (int) $oldUserId]
),
$this->update(
'UPDATE email_log_users
SET user_id = ?
WHERE user_id = ? AND email_log_id NOT IN (SELECT t1.email_log_id
FROM (SELECT email_log_id FROM email_log_users WHERE user_id = ?) AS t1
INNER JOIN
(SELECT email_log_id FROM email_log_users WHERE user_id = ?) AS t2
ON t1.email_log_id = t2.email_log_id);',
[(int) $newUserId, (int) $oldUserId, (int) $newUserId, (int) $oldUserId]
)
];
}
//
// Private helper methods.
//
/**
* Stores the correspondent user ids of the all recipient emails.
*
* @param EmailLogEntry $entry
*/
public function _insertLogUserIds($entry)
{
$recipients = $entry->getRecipients();
// We can use a simple regex to get emails, since we don't want to validate it.
$pattern = '/(?<=\<)[^\>]*(?=\>)/';
preg_match_all($pattern, $recipients, $matches);
if (!isset($matches[0])) {
return;
}
foreach ($matches[0] as $emailAddress) {
$user = Repo::user()->getByEmail($emailAddress, true);
if ($user instanceof \PKP\user\User) {
// We use replace here to avoid inserting duplicated entries
// in table (sometimes the recipients can have the same email twice).
DB::table('email_log_users')->updateOrInsert(
['email_log_id' => $entry->getId(), 'user_id' => $user->getId()]
);
}
}
}
/**
* Construct a new email log entry.
*
* @return EmailLogEntry
*/
public function newDataObject()
{
return new EmailLogEntry();
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\log\EmailLogDAO', '\EmailLogDAO');
}
+243
View File
@@ -0,0 +1,243 @@
<?php
/**
* @file classes/log/EmailLogEntry.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2003-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class EmailLogEntry
*
* @ingroup log
*
* @see EmailLogDAO
*
* @brief Describes an entry in the email log.
*/
namespace PKP\log;
use APP\facades\Repo;
class EmailLogEntry extends \PKP\core\DataObject
{
//
// Get/set methods
//
/**
* Get user ID of sender.
*
* @return int
*/
public function getSenderId()
{
return $this->getData('senderId');
}
/**
* Set user ID of sender.
*
* @param int $senderId
*/
public function setSenderId($senderId)
{
$this->setData('senderId', $senderId);
}
/**
* Get date email was sent.
*
* @return string
*/
public function getDateSent()
{
return $this->getData('dateSent');
}
/**
* Set date email was sent.
*
* @param string $dateSent
*/
public function setDateSent($dateSent)
{
$this->setData('dateSent', $dateSent);
}
/**
* Get event type.
*
* @return int
*/
public function getEventType()
{
return $this->getData('eventType');
}
/**
* Set event type.
*
* @param int $eventType
*/
public function setEventType($eventType)
{
$this->setData('eventType', $eventType);
}
/**
* Get associated type.
*
* @return int
*/
public function getAssocType()
{
return $this->getData('assocType');
}
/**
* Set associated type.
*
* @param int $assocType
*/
public function setAssocType($assocType)
{
$this->setData('assocType', $assocType);
}
/**
* Get associated ID.
*
* @return int
*/
public function getAssocId()
{
return $this->getData('assocId');
}
/**
* Set associated ID.
*
* @param int $assocId
*/
public function setAssocId($assocId)
{
$this->setData('assocId', $assocId);
}
/**
* Return the full name of the sender (not necessarily the same as the from address).
*
* @return string
*/
public function getSenderFullName()
{
$senderFullName = $this->getData('senderFullName');
if ($senderFullName) {
return $senderFullName;
}
$sender = $this->getSenderId()
? Repo::user()->get($this->getSenderId(), true)
: null;
return $sender ? $sender->getFullName() : '';
}
/**
* Return the email address of sender.
*
* @return string
*/
public function getSenderEmail()
{
$senderEmail = & $this->getData('senderEmail');
if (!isset($senderEmail)) {
$senderEmail = Repo::user()->get($this->getSenderId(), true)->getEmail();
}
return ($senderEmail ? $senderEmail : '');
}
//
// Email data
//
public function getFrom()
{
return $this->getData('from');
}
public function setFrom($from)
{
$this->setData('from', $from);
}
public function getRecipients()
{
return $this->getData('recipients');
}
public function setRecipients($recipients)
{
$this->setData('recipients', $recipients);
}
public function getCcs()
{
return $this->getData('ccs');
}
public function setCcs($ccs)
{
$this->setData('ccs', $ccs);
}
public function getBccs()
{
return $this->getData('bccs');
}
public function setBccs($bccs)
{
$this->setData('bccs', $bccs);
}
public function getSubject()
{
return $this->getData('subject');
}
public function setSubject($subject)
{
$this->setData('subject', $subject);
}
public function getBody()
{
return $this->getData('body');
}
public function setBody($body)
{
$this->setData('body', $body);
}
/**
* Returns the subject of the message with a prefix explaining the event type
*
* @return string Prefixed subject
*/
public function getPrefixedSubject()
{
return __('submission.event.subjectPrefix') . ' ' . $this->getSubject();
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\log\EmailLogEntry', '\EmailLogEntry');
}
@@ -0,0 +1,117 @@
<?php
/**
* @file classes/log/SubmissionEmailLogDAO.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2003-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class SubmissionEmailLogDAO
*
* @ingroup log
*
* @see EmailLogDAO
*
* @brief Extension to EmailLogDAO for submission-specific log entries.
*/
namespace PKP\log;
use APP\core\Application;
use APP\submission\Submission;
use Illuminate\Support\Facades\Mail;
use PKP\core\Core;
use PKP\db\DAOResultFactory;
use PKP\facades\Locale;
use PKP\mail\Mailable;
use PKP\user\User;
class SubmissionEmailLogDAO extends EmailLogDAO
{
/**
* Instantiate and return a SubmissionEmailLogEntry
*
* @return SubmissionEmailLogEntry
*/
public function newDataObject()
{
$returner = new SubmissionEmailLogEntry();
$returner->setAssocType(Application::ASSOC_TYPE_SUBMISSION);
return $returner;
}
/**
* Get submission email log entries by submission ID and event type
*
* @param int $submissionId
* @param int $userId optional Return only emails sent to this user.
*
* @return DAOResultFactory<SubmissionEmailLogEntry>
*/
public function getByEventType($submissionId, $eventType, $userId = null)
{
return parent::_getByEventType(Application::ASSOC_TYPE_SUBMISSION, $submissionId, $eventType, $userId);
}
/**
* Get submission email log entries by submission ID
*
* @param int $submissionId
*
* @return DAOResultFactory<SubmissionEmailLogEntry>
*/
public function getBySubmissionId($submissionId)
{
return $this->getByAssoc(Application::ASSOC_TYPE_SUBMISSION, $submissionId);
}
/**
* Create a log entry from data in a Mailable class
*
* @param int $eventType One of the SubmissionEmailLogEntry::SUBMISSION_EMAIL_* constants
*
* @return int The new log entry id
*/
public function logMailable(int $eventType, Mailable $mailable, Submission $submission, ?User $sender = null): int
{
$entry = $this->newDataObject();
$clonedMailable = clone $mailable;
$clonedMailable->removeFooter();
$entry->setEventType($eventType);
$entry->setAssocId($submission->getId());
$entry->setDateSent(Core::getCurrentDate());
$entry->setSenderId($sender ? $sender->getId() : 0);
$entry->setSubject(Mail::compileParams(
$clonedMailable->subject,
$clonedMailable->getData(Locale::getLocale())
));
$entry->setBody($clonedMailable->render());
$entry->setFrom($this->getContactString($clonedMailable->from));
$entry->setRecipients($this->getContactString($clonedMailable->to));
$entry->setCcs($this->getContactString($clonedMailable->cc));
$entry->setBccs($this->getContactString($clonedMailable->bcc));
return $this->insertObject($entry);
}
/**
* Get the from or to data as a string
*
* @param array $addressees Expects Mailable::$to or Mailable::$from
*/
protected function getContactString(array $addressees): string
{
$contactStrings = [];
foreach ($addressees as $addressee) {
$contactStrings[] = isset($addressee['name'])
? '"' . $addressee['name'] . '" <' . $addressee['address'] . '>'
: $addressee['address'];
}
return join(', ', $contactStrings);
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\log\SubmissionEmailLogDAO', '\SubmissionEmailLogDAO');
}
@@ -0,0 +1,137 @@
<?php
/**
* @file classes/log/SubmissionEmailLogEntry.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2003-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class SubmissionEmailLogEntry
*
* @ingroup log
*
* @see SubmissionEmailLogDAO
*
* @brief Base class for describing an entry in the submission email log.
*/
namespace PKP\log;
class SubmissionEmailLogEntry extends EmailLogEntry
{
// Author events 0x20000000
public const SUBMISSION_EMAIL_AUTHOR_NOTIFY_REVISED_VERSION = 0x20000001;
public const SUBMISSION_EMAIL_AUTHOR_SUBMISSION_ACK = 0x20000002;
// Editor events 0x30000000
public const SUBMISSION_EMAIL_EDITOR_NOTIFY_AUTHOR = 0x30000001;
public const SUBMISSION_EMAIL_EDITOR_ASSIGN = 0x30000002;
public const SUBMISSION_EMAIL_EDITOR_NOTIFY_AUTHOR_UNSUITABLE = 0x30000003;
public const SUBMISSION_EMAIL_EDITOR_RECOMMEND_NOTIFY = 0x30000004;
public const SUBMISSION_EMAIL_NEEDS_EDITOR = 0x30000005;
// Reviewer events 0x40000000
public const SUBMISSION_EMAIL_REVIEW_NOTIFY_REVIEWER = 0x40000001;
public const SUBMISSION_EMAIL_REVIEW_THANK_REVIEWER = 0x40000002;
public const SUBMISSION_EMAIL_REVIEW_CANCEL = 0x40000003;
public const SUBMISSION_EMAIL_REVIEW_REMIND = 0x40000004;
public const SUBMISSION_EMAIL_REVIEW_CONFIRM = 0x40000005;
public const SUBMISSION_EMAIL_REVIEW_DECLINE = 0x40000006;
public const SUBMISSION_EMAIL_REVIEW_CONFIRM_ACK = 0x40000008;
// Copyeditor events 0x50000000
public const SUBMISSION_EMAIL_COPYEDIT_NOTIFY_COPYEDITOR = 0x50000001;
public const SUBMISSION_EMAIL_COPYEDIT_NOTIFY_AUTHOR = 0x50000002;
public const SUBMISSION_EMAIL_COPYEDIT_NOTIFY_FINAL = 0x50000003;
public const SUBMISSION_EMAIL_COPYEDIT_NOTIFY_COMPLETE = 0x50000004;
public const SUBMISSION_EMAIL_COPYEDIT_NOTIFY_AUTHOR_COMPLETE = 0x50000005;
public const SUBMISSION_EMAIL_COPYEDIT_NOTIFY_FINAL_COMPLETE = 0x50000006;
public const SUBMISSION_EMAIL_COPYEDIT_NOTIFY_ACKNOWLEDGE = 0x50000007;
public const SUBMISSION_EMAIL_COPYEDIT_NOTIFY_AUTHOR_ACKNOWLEDGE = 0x50000008;
public const SUBMISSION_EMAIL_COPYEDIT_NOTIFY_FINAL_ACKNOWLEDGE = 0x50000009;
// Proofreader events 0x60000000
public const SUBMISSION_EMAIL_PROOFREAD_NOTIFY_AUTHOR = 0x60000001;
public const SUBMISSION_EMAIL_PROOFREAD_NOTIFY_AUTHOR_COMPLETE = 0x60000002;
public const SUBMISSION_EMAIL_PROOFREAD_THANK_AUTHOR = 0x60000003;
public const SUBMISSION_EMAIL_PROOFREAD_NOTIFY_PROOFREADER = 0x60000004;
public const SUBMISSION_EMAIL_PROOFREAD_NOTIFY_PROOFREADER_COMPLETE = 0x60000005;
public const SUBMISSION_EMAIL_PROOFREAD_THANK_PROOFREADER = 0x60000006;
public const SUBMISSION_EMAIL_PROOFREAD_NOTIFY_LAYOUTEDITOR = 0x60000007;
public const SUBMISSION_EMAIL_PROOFREAD_NOTIFY_LAYOUTEDITOR_COMPLETE = 0x60000008;
public const SUBMISSION_EMAIL_PROOFREAD_THANK_LAYOUTEDITOR = 0x60000009;
// Layout events 0x70000000
public const SUBMISSION_EMAIL_LAYOUT_NOTIFY_EDITOR = 0x70000001;
public const SUBMISSION_EMAIL_LAYOUT_THANK_EDITOR = 0x70000002;
public const SUBMISSION_EMAIL_LAYOUT_NOTIFY_COMPLETE = 0x70000003;
// Index events 0x80000000
public const SUBMISSION_EMAIL_INDEX_NOTIFY_INDEXER = 0x80000001;
public const SUBMISSION_EMAIL_INDEX_NOTIFY_COMPLETE = 0x80000002;
// Discussion
public const SUBMISSION_EMAIL_DISCUSSION_NOTIFY = 0x90000001;
/**
* Set the submission ID for the log entry.
*
* @param int $submissionId
*/
public function setSubmissionId($submissionId)
{
return $this->setAssocId($submissionId);
}
/**
* Get the submission ID for the log entry.
*
* @return int
*/
public function getSubmissionId()
{
return $this->getAssocId();
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\log\SubmissionEmailLogEntry', '\SubmissionEmailLogEntry');
foreach ([
'SUBMISSION_EMAIL_AUTHOR_NOTIFY_REVISED_VERSION',
'SUBMISSION_EMAIL_EDITOR_NOTIFY_AUTHOR',
'SUBMISSION_EMAIL_EDITOR_ASSIGN',
'SUBMISSION_EMAIL_EDITOR_NOTIFY_AUTHOR_UNSUITABLE',
'SUBMISSION_EMAIL_EDITOR_RECOMMEND_NOTIFY',
'SUBMISSION_EMAIL_REVIEW_NOTIFY_REVIEWER',
'SUBMISSION_EMAIL_REVIEW_THANK_REVIEWER',
'SUBMISSION_EMAIL_REVIEW_CANCEL',
'SUBMISSION_EMAIL_REVIEW_REMIND',
'SUBMISSION_EMAIL_REVIEW_CONFIRM',
'SUBMISSION_EMAIL_REVIEW_DECLINE',
'SUBMISSION_EMAIL_REVIEW_CONFIRM_ACK',
'SUBMISSION_EMAIL_COPYEDIT_NOTIFY_COPYEDITOR',
'SUBMISSION_EMAIL_COPYEDIT_NOTIFY_AUTHOR',
'SUBMISSION_EMAIL_COPYEDIT_NOTIFY_FINAL',
'SUBMISSION_EMAIL_COPYEDIT_NOTIFY_COMPLETE',
'SUBMISSION_EMAIL_COPYEDIT_NOTIFY_AUTHOR_COMPLETE',
'SUBMISSION_EMAIL_COPYEDIT_NOTIFY_FINAL_COMPLETE',
'SUBMISSION_EMAIL_COPYEDIT_NOTIFY_ACKNOWLEDGE',
'SUBMISSION_EMAIL_COPYEDIT_NOTIFY_AUTHOR_ACKNOWLEDGE',
'SUBMISSION_EMAIL_COPYEDIT_NOTIFY_FINAL_ACKNOWLEDGE',
'SUBMISSION_EMAIL_PROOFREAD_NOTIFY_AUTHOR',
'SUBMISSION_EMAIL_PROOFREAD_NOTIFY_AUTHOR_COMPLETE',
'SUBMISSION_EMAIL_PROOFREAD_THANK_AUTHOR',
'SUBMISSION_EMAIL_PROOFREAD_NOTIFY_PROOFREADER',
'SUBMISSION_EMAIL_PROOFREAD_NOTIFY_PROOFREADER_COMPLETE',
'SUBMISSION_EMAIL_PROOFREAD_THANK_PROOFREADER',
'SUBMISSION_EMAIL_PROOFREAD_NOTIFY_LAYOUTEDITOR',
'SUBMISSION_EMAIL_PROOFREAD_NOTIFY_LAYOUTEDITOR_COMPLETE',
'SUBMISSION_EMAIL_PROOFREAD_THANK_LAYOUTEDITOR',
'SUBMISSION_EMAIL_LAYOUT_NOTIFY_EDITOR',
'SUBMISSION_EMAIL_LAYOUT_THANK_EDITOR',
'SUBMISSION_EMAIL_LAYOUT_NOTIFY_COMPLETE',
] as $constantName) {
define($constantName, constant('SubmissionEmailLogEntry::' . $constantName));
}
}
+110
View File
@@ -0,0 +1,110 @@
<?php
/**
* @file classes/log/event/Collector.php
*
* Copyright (c) 2014-2023 Simon Fraser University
* Copyright (c) 2000-2023 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class Collector
*
* @brief A helper class to configure a Query Builder to get a collection of log entries
*/
namespace PKP\log\event;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\LazyCollection;
use PKP\core\interfaces\CollectorInterface;
use PKP\plugins\Hook;
class Collector implements CollectorInterface
{
public DAO $dao;
protected ?int $assocType = null;
protected ?array $assocIds = null;
protected ?array $userIds = null;
public ?int $count = null;
public ?int $offset = null;
public function __construct(DAO $dao)
{
$this->dao = $dao;
}
public function getCount(): int
{
return $this->dao->getCount($this);
}
public function getIds(): Collection
{
return $this->dao->getIds($this);
}
public function getMany(): LazyCollection
{
return $this->dao->getMany($this);
}
/**
*
* @param null|int $assocType One of the Application::ASSOC_TYPE_ constants
* @param null|array $assocIds Match for the specified assoc type
*/
public function filterByAssoc(?int $assocType, ?array $assocIds = null): self
{
$this->assocType = $assocType;
$this->assocIds = $assocIds;
return $this;
}
public function filterByUserIds(?array $userIds): self
{
$this->userIds = $userIds;
return $this;
}
public function limit(?int $count): self
{
$this->count = $count;
return $this;
}
public function offset(?int $offset): self
{
$this->offset = $offset;
return $this;
}
/**
* @inheritDoc
*/
public function getQueryBuilder(): Builder
{
$q = DB::table($this->dao->table . ' as e')
->select(['e.*'])
->when(!is_null($this->assocType), function (Builder $q) {
return $q->where('assoc_type', $this->assocType);
})
->when(!is_null($this->assocIds), function (Builder $q) {
return $q->whereIn('assoc_id', $this->assocIds);
})
->when(!is_null($this->userIds), function (Builder $q) {
return $q->whereIn('user_id', $this->userIds);
})
->orderBy('date_logged', 'desc')
->when(!is_null($this->count), function (Builder $q) {
return $q->limit($this->count);
})
->when(!is_null($this->count) && !is_null($this->offset), function (Builder $q) {
return $q->offset($this->offset);
});
Hook::call('EventLog::Collector::getQueryBuilder', [&$q, $this]);
return $q;
}
}
+169
View File
@@ -0,0 +1,169 @@
<?php
/**
* @file classes/log/event/DAO.php
*
* Copyright (c) 2014-2023 Simon Fraser University
* Copyright (c) 2000-2023 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class DAO
*
* @brief Read and write event information to the database.
*/
namespace PKP\log\event;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\LazyCollection;
use PKP\core\EntityDAO;
use PKP\services\PKPSchemaService;
class DAO extends EntityDAO
{
/** @copydoc EntityDAO::$schema */
public $schema = PKPSchemaService::SCHEMA_EVENT_LOG;
/** @copydoc EntityDAO::$table */
public $table = 'event_log';
/** @copydoc EntityDAO::$settingsTable */
public $settingsTable = 'event_log_settings';
/** @copydoc EntityDAO::$primaryKeyColumn */
public $primaryKeyColumn = 'log_id';
/** @copydoc EntityDAO::$primaryTableColumns */
public $primaryTableColumns = [
'id' => 'log_id',
'assocType' => 'assoc_type',
'assocId' => 'assoc_id',
'userId' => 'user_id',
'dateLogged' => 'date_logged',
'eventType' => 'event_type',
'message' => 'message',
'isTranslated' => 'is_translated',
];
/**
* Instantiate a new DataObject
*/
public function newDataObject(): EventLogEntry
{
return app(EventLogEntry::class);
}
/**
* Check if a log entry exists
*/
public function exists(int $id): bool
{
return DB::table($this->table)
->where($this->primaryKeyColumn, '=', $id)
->exists();
}
/**
* Get an event log entry
*/
public function get(int $id): ?EventLogEntry
{
$row = DB::table($this->table)
->where($this->primaryKeyColumn, $id)
->first();
return $row ? $this->fromRow($row) : null;
}
/**
* Get the number of log entries matching the configured query
*/
public function getCount(Collector $query): int
{
return $query
->getQueryBuilder()
->count();
}
/**
* Get a list of ids matching the configured query
*/
public function getIds(Collector $query): Collection
{
return $query
->getQueryBuilder()
->select('e.' . $this->primaryKeyColumn)
->pluck('e.' . $this->primaryKeyColumn);
}
/**
* Get a collection of log entries matching the configured query
*/
public function getMany(Collector $query): LazyCollection
{
$rows = $query
->getQueryBuilder()
->get();
return LazyCollection::make(function () use ($rows) {
foreach ($rows as $row) {
yield $row->log_id => $this->fromRow($row);
}
});
}
/**
* @copydoc EntityDAO::fromRow()
*/
public function fromRow(object $row): EventLogEntry
{
$logEntry = parent::fromRow($row);
$schema = $this->schemaService->get($this->schema);
DB::table($this->settingsTable)
->where($this->primaryKeyColumn, '=', $row->{$this->primaryKeyColumn})
->get()
->each(function ($row) use ($logEntry, $schema) {
if (!empty($schema->properties->{$row->setting_name})) {
return;
}
// Retrieve custom properties
if (!empty($row->setting_type)) {
$logEntry->setData(
$row->setting_name,
$this->convertFromDB(
$row->setting_value,
$row->setting_type
),
empty($row->locale) ? null : $row->locale
);
}
});
return $logEntry;
}
/**
* @copydoc EntityDAO::insert()
*/
public function insert(EventLogEntry $eventLog): int
{
return parent::_insert($eventLog);
}
/**
* @copydoc EntityDAO::delete()
*/
public function delete(EventLogEntry $eventLog)
{
parent::_delete($eventLog);
}
/**
* Transfer all log entries to another user.
*/
public function changeUser(int $oldUserId, int $newUserId)
{
DB::table($this->table)->where('user_id', $oldUserId)->update(['user_id' => $newUserId]);
}
}
+285
View File
@@ -0,0 +1,285 @@
<?php
/**
* @file classes/log/event/EventLogEntry.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2003-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class EventLogEntry
*
* @ingroup log
*
* @brief Describes an entry in the event log.
*/
namespace PKP\log\event;
use APP\core\Application;
use APP\facades\Repo;
use PKP\db\DAORegistry;
use PKP\facades\Locale;
use PKP\submission\reviewAssignment\ReviewAssignment;
use PKP\submissionFile\SubmissionFile;
class EventLogEntry extends \PKP\core\DataObject
{
// Information Center events
public const SUBMISSION_LOG_NOTE_POSTED = 0x01000000;
public const SUBMISSION_LOG_MESSAGE_SENT = 0x01000001;
//
// Get/set methods
//
/**
* Get user ID of user that initiated the event.
*
* @return int
*/
public function getUserId()
{
return $this->getData('userId');
}
/**
* Set user ID of user that initiated the event.
*
* @param int $userId
*/
public function setUserId($userId)
{
$this->setData('userId', $userId);
}
/**
* Get date entry was logged.
*
* @return string
*/
public function getDateLogged()
{
return $this->getData('dateLogged');
}
/**
* Set date entry was logged.
*
* @param string $dateLogged
*/
public function setDateLogged($dateLogged)
{
$this->setData('dateLogged', $dateLogged);
}
/**
* Get event type.
*
* @return int
*/
public function getEventType()
{
return $this->getData('eventType');
}
/**
* Set event type.
*
* @param int $eventType
*/
public function setEventType($eventType)
{
$this->setData('eventType', $eventType);
}
/**
* Get associated type.
*
* @return int
*/
public function getAssocType()
{
return $this->getData('assocType');
}
/**
* Set associated type.
*
* @param int $assocType
*/
public function setAssocType($assocType)
{
$this->setData('assocType', $assocType);
}
/**
* Get associated ID.
*
* @return int
*/
public function getAssocId()
{
return $this->getData('assocId');
}
/**
* Set associated ID.
*
* @param int $assocId
*/
public function setAssocId($assocId)
{
$this->setData('assocId', $assocId);
}
/**
* Get custom log message (either locale key or literal string).
*
* @return string
*/
public function getMessage()
{
return $this->getData('message');
}
/**
* Set custom log message (either locale key or literal string).
*
* @param string $message
*/
public function setMessage($message)
{
$this->setData('message', $message);
}
/**
* Get flag indicating whether or not message is translated.
*
* @return bool
*/
public function getIsTranslated()
{
return $this->getData('isTranslated');
}
/**
* Set flag indicating whether or not message is translated.
*
* @param int $isTranslated
*/
public function setIsTranslated($isTranslated)
{
$this->setData('isTranslated', $isTranslated);
}
/**
* Get translated message, translating it if necessary.
*
* @param string $locale optional
* @param bool $hideReviewerName optional Don't reveal reviewer names in
* log descriptions.
*/
public function getTranslatedMessage($locale = null, $hideReviewerName = false)
{
$message = $this->getMessage();
// If it's already translated, just return the message.
if ($this->getData('isTranslated')) {
return $message;
}
// Otherwise, translate it and include parameters.
if ($locale === null) {
$locale = Locale::getLocale();
}
$eventLog = clone $this;
if ($hideReviewerName) {
$reviewAssignmentDao = DAORegistry::getDAO('ReviewAssignmentDAO'); /** @var \PKP\submission\reviewAssignment\ReviewAssignmentDAO $reviewAssignmentDao */
// Reviewer activity log entries (assigning, accepting, declining)
if ($eventLog->getData('reviewerName')) {
$anonymousAuthor = true;
if ($reviewAssignmentId = $eventLog->getData('reviewAssignmentId')) {
$reviewAssignment = $reviewAssignmentDao->getById($reviewAssignmentId);
if ($reviewAssignment && !in_array($reviewAssignment->getReviewMethod(), [ReviewAssignment::SUBMISSION_REVIEW_METHOD_ANONYMOUS, ReviewAssignment::SUBMISSION_REVIEW_METHOD_DOUBLEANONYMOUS])) {
$anonymousAuthor = false;
}
}
if ($anonymousAuthor) {
$eventLog->setData('reviewerName', __('editor.review.anonymousReviewer'));
}
}
// Files submitted by reviewers
$fileStage = $eventLog->getData('fileStage');
if ($fileStage && $fileStage === SubmissionFile::SUBMISSION_FILE_REVIEW_ATTACHMENT) {
$submissionFileId = $eventLog->getData('submissionFileId');
assert($eventLog->getData('fileId') && $eventLog->getData('submissionId') && $submissionFileId);
$anonymousAuthor = true;
$submissionFile = Repo::submissionFile()->get($submissionFileId);
if ($submissionFile && $submissionFile->getData('assocType') === Application::ASSOC_TYPE_REVIEW_ASSIGNMENT) {
$reviewAssignment = $reviewAssignmentDao->getById($submissionFile->getData('assocId'));
if ($reviewAssignment && !in_array($reviewAssignment->getReviewMethod(), [ReviewAssignment::SUBMISSION_REVIEW_METHOD_ANONYMOUS, ReviewAssignment::SUBMISSION_REVIEW_METHOD_DOUBLEANONYMOUS])) {
$anonymousAuthor = false;
}
}
if ($eventLog->getData('username') && $anonymousAuthor) {
$eventLog->setData('username', __('editor.review.anonymousReviewer'));
$filenames = $eventLog->getData('filename');
$eventLog->setData('filename', array_map(function (string $value) {
return '';
}, $filenames));
}
}
}
$params = [];
foreach ($eventLog->getAllData() as $key => $data) {
if (!is_array($data)) {
$params[$key] = $eventLog->getData($key);
continue;
}
$params[$key] = $eventLog->getData($key, $locale);
}
return __($message, $params, $locale);
}
/**
* Return the full name of the user.
*
* @return string
*/
public function getUserFullName()
{
$userFullName = & $this->getData('userFullName');
if (!isset($userFullName) && $this->getUserId()) {
$userFullName = Repo::user()->get($this->getUserId(), true)->getFullName();
}
return $userFullName ?: '';
}
/**
* Return the email address of the user.
*
* @return string
*/
public function getUserEmail()
{
$userEmail = $this->getData('userEmail');
if (!isset($userEmail)) {
$userEmail = Repo::user()->get($this->getUserId(), true)->getEmail();
}
return $userEmail ?: '';
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\log\event\EventLogEntry', '\EventLogEntry');
define('SUBMISSION_LOG_NOTE_POSTED', EventLogEntry::SUBMISSION_LOG_NOTE_POSTED);
define('SUBMISSION_LOG_MESSAGE_SENT', EventLogEntry::SUBMISSION_LOG_MESSAGE_SENT);
}
@@ -0,0 +1,109 @@
<?php
/**
* @file classes/log/event/PKPSubmissionEventLogEntry.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2003-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class PKPSubmissionEventLogEntry
*
* @ingroup log
*
* @brief Describes an entry in the submission history log.
*/
namespace PKP\log\event;
use APP\core\Application;
class PKPSubmissionEventLogEntry extends EventLogEntry
{
public const SUBMISSION_LOG_SUBMISSION_SUBMIT = 0x10000001;
public const SUBMISSION_LOG_METADATA_UPDATE = 0x10000002;
public const SUBMISSION_LOG_ADD_PARTICIPANT = 0x10000003;
public const SUBMISSION_LOG_REMOVE_PARTICIPANT = 0x10000004;
public const SUBMISSION_LOG_METADATA_PUBLISH = 0x10000006;
public const SUBMISSION_LOG_METADATA_UNPUBLISH = 0x10000007;
public const SUBMISSION_LOG_CREATE_VERSION = 0x10000008;
public const SUBMISSION_LOG_COPYRIGHT_AGREED = 0x10000009;
public const SUBMISSION_LOG_EDITOR_DECISION = 0x30000003;
public const SUBMISSION_LOG_EDITOR_RECOMMENDATION = 0x30000004;
public const SUBMISSION_LOG_DECISION_EMAIL_SENT = 0x30000007;
public const SUBMISSION_LOG_REVIEW_ASSIGN = 0x40000001;
public const SUBMISSION_LOG_REVIEW_REINSTATED = 0x40000005;
public const SUBMISSION_LOG_REVIEW_ACCEPT = 0x40000006;
public const SUBMISSION_LOG_REVIEW_DECLINE = 0x40000007;
public const SUBMISSION_LOG_REVIEW_UNCONSIDERED = 0x40000009;
public const SUBMISSION_LOG_REVIEW_SET_DUE_DATE = 0x40000011;
public const SUBMISSION_LOG_REVIEW_CLEAR = 0x40000014;
public const SUBMISSION_LOG_REVIEW_READY = 0x40000018;
public const SUBMISSION_LOG_REVIEW_CONFIRMED = 0x40000019;
public const SUBMISSION_LOG_REVIEW_REMIND = 0x40000020;
public const SUBMISSION_LOG_REVIEW_REMIND_AUTO = 0x40000021;
//
// Getters/setters
//
/**
* Set the submission ID
*/
public function setSubmissionId($submissionId)
{
return $this->setAssocId($submissionId);
}
/**
* Get the submission ID
*
* @return int
*/
public function getSubmissionId()
{
return $this->getAssocId();
}
/**
* Get the assoc ID
*
* @return int
*/
public function getAssocType()
{
return Application::ASSOC_TYPE_SUBMISSION;
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\log\event\PKPSubmissionEventLogEntry', '\PKPSubmissionEventLogEntry');
foreach ([
'SUBMISSION_LOG_SUBMISSION_SUBMIT',
'SUBMISSION_LOG_METADATA_UPDATE',
'SUBMISSION_LOG_ADD_PARTICIPANT',
'SUBMISSION_LOG_REMOVE_PARTICIPANT',
'SUBMISSION_LOG_METADATA_PUBLISH',
'SUBMISSION_LOG_METADATA_UNPUBLISH',
'SUBMISSION_LOG_CREATE_VERSION',
'SUBMISSION_LOG_EDITOR_DECISION',
'SUBMISSION_LOG_EDITOR_RECOMMENDATION',
'SUBMISSION_LOG_REVIEW_ASSIGN',
'SUBMISSION_LOG_REVIEW_REINSTATED',
'SUBMISSION_LOG_REVIEW_DECLINE',
'SUBMISSION_LOG_REVIEW_UNCONSIDERED',
'SUBMISSION_LOG_REVIEW_SET_DUE_DATE',
'SUBMISSION_LOG_REVIEW_CLEAR',
'SUBMISSION_LOG_REVIEW_READY',
'SUBMISSION_LOG_REVIEW_CONFIRMED',
] as $constantName) {
define($constantName, constant('\PKPSubmissionEventLogEntry::' . $constantName));
}
}
+152
View File
@@ -0,0 +1,152 @@
<?php
/**
* @file classes/log/event/Repository.php
*
* Copyright (c) 2014-2023 Simon Fraser University
* Copyright (c) 2000-2023 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class Repository
*
* @brief A repository to find and manage event log entries.
*/
namespace PKP\log\event;
use APP\facades\Repo;
use PKP\context\Context;
use PKP\core\PKPApplication;
use PKP\core\PKPRequest;
use PKP\plugins\Hook;
use PKP\services\PKPSchemaService;
use PKP\validation\ValidatorFactory;
class Repository
{
public DAO $dao;
// The name of the class to map this entity to its schema
public string $schemaMap = maps\Schema::class;
protected PKPRequest $request;
protected PKPSchemaService $schemaService;
public function __construct(DAO $dao, PKPRequest $request, PKPSchemaService $schemaService)
{
$this->dao = $dao;
$this->request = $request;
$this->schemaService = $schemaService;
}
/** @copydoc DAO::newDataObject() */
public function newDataObject(array $params = []): EventLogEntry
{
$object = $this->dao->newDataObject();
if (!empty($params)) {
$object->setAllData($params);
}
return $object;
}
/** @copydoc DAO::getByKey() */
public function get(int $key): ?EventLogEntry
{
return $this->dao->get($key);
}
/** @copydoc DAO::getCollector() */
public function getCollector(): Collector
{
return app(Collector::class);
}
/**
* Get an instance of the map class for mapping
* log entries to their schema
*/
public function getSchemaMap(): maps\Schema
{
return app('maps')->withExtensions($this->schemaMap);
}
/**
* Validate properties for an event log entry
*
* Perform validation checks on data used to add or edit an event log entry.
*
* @param array $props A key/value array with the new data to validate
* @param array $allowedLocales The context's supported locales
* @param string $primaryLocale The context's primary locale
*
* @return array A key/value array with validation errors. Empty if no errors
*/
public function validate(?EventLogEntry $object, array $props, Context $context): array
{
$allowedLocales = $context->getSupportedSubmissionLocales();;
$primaryLocale = $context->getPrimaryLocale();
$validator = ValidatorFactory::make(
$props,
$this->schemaService->getValidationRules($this->dao->schema, $allowedLocales)
);
// Check required fields
ValidatorFactory::required(
$validator,
$object,
$this->schemaService->getRequiredProps($this->dao->schema),
$this->schemaService->getMultilingualProps($this->dao->schema),
$allowedLocales,
$primaryLocale
);
// Check for input from disallowed locales
ValidatorFactory::allowedLocales($validator, $this->schemaService->getMultilingualProps($this->dao->schema), $allowedLocales);
$errors = [];
if ($validator->fails()) {
$errors = $this->schemaService->formatValidationErrors($validator->errors());
}
Hook::call('EventLog::validate', [&$errors, $object, $props, $allowedLocales, $primaryLocale]);
return $errors;
}
/** @copydoc DAO::insert() */
public function add(EventLogEntry $logEntry): int
{
$id = $this->dao->insert($logEntry);
Hook::call('EventLog::add', [$logEntry]);
// Stamp the submission status modification date without triggering edit event
if ($logEntry->getData('assocType') === PKPApplication::ASSOC_TYPE_SUBMISSION) {
$submission = Repo::submission()->get($logEntry->getData('assocId'));
$submission->stampLastActivity();
Repo::submission()->dao->update($submission);
}
return $id;
}
/** @copydoc DAO::delete() */
public function delete(EventLogEntry $logEntry)
{
Hook::call('EventLog::delete::before', [$logEntry]);
$this->dao->delete($logEntry);
Hook::call('EventLog::delete', [$logEntry]);
}
/**
* Delete a collection of categories
*/
public function deleteMany(Collector $collector)
{
foreach ($collector->getMany() as $logEntry) {
/** @var EventLogEntry $logEntry */
$this->delete($logEntry);
}
}
}
@@ -0,0 +1,52 @@
<?php
/**
* @file classes/log/event/SubmissionFileEventLogEntry.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2003-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class SubmissionFileEventLogEntry
*
* @ingroup log
*
* @brief Describes an entry in the submission file history log.
*/
namespace PKP\log\event;
class SubmissionFileEventLogEntry extends EventLogEntry
{
// File upload/delete event types.
public const SUBMISSION_LOG_FILE_UPLOAD = 0x50000001;
public const SUBMISSION_LOG_FILE_DELETE = 0x50000002;
public const SUBMISSION_LOG_FILE_REVISION_UPLOAD = 0x50000008;
public const SUBMISSION_LOG_FILE_EDIT = 0x50000010;
// Audit events
public const SUBMISSION_LOG_FILE_AUDITOR_ASSIGN = 0x50000004;
public const SUBMISSION_LOG_FILE_AUDITOR_CLEAR = 0x50000005;
public const SUBMISSION_LOG_FILE_AUDIT_UPLOAD = 0x50000006;
public const SUBMISSION_LOG_FILE_SIGNOFF_SIGNOFF = 0x50000007;
// Deprecated events. Preserve for historical logs
public const SUBMISSION_LOG_FILE_REVISION_DELETE = 0x50000009; // uses submission.event.revisionDeleted
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\log\event\SubmissionFileEventLogEntry', '\SubmissionFileEventLogEntry');
foreach ([
'SUBMISSION_LOG_FILE_UPLOAD',
'SUBMISSION_LOG_FILE_DELETE',
'SUBMISSION_LOG_FILE_REVISION_UPLOAD',
'SUBMISSION_LOG_FILE_EDIT',
'SUBMISSION_LOG_FILE_AUDITOR_ASSIGN',
'SUBMISSION_LOG_FILE_AUDITOR_CLEAR',
'SUBMISSION_LOG_FILE_AUDIT_UPLOAD',
'SUBMISSION_LOG_FILE_SIGNOFF_SIGNOFF',
'SUBMISSION_LOG_FILE_REVISION_DELETE',
] as $constantName) {
define($constantName, constant('\SubmissionFileEventLogEntry::' . $constantName));
}
}
+89
View File
@@ -0,0 +1,89 @@
<?php
/**
* @file classes/log/event/maps/Schema.php
*
* Copyright (c) 2014-2023 Simon Fraser University
* Copyright (c) 2000-2023 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class Schema
*
* @brief Map event log entries to the properties defined in the event log schema
*/
namespace PKP\log\event\maps;
use Illuminate\Support\Enumerable;
use PKP\log\event\EventLogEntry;
use PKP\services\PKPSchemaService;
class Schema extends \PKP\core\maps\Schema
{
public Enumerable $collection;
public string $schema = PKPSchemaService::SCHEMA_EVENT_LOG;
/**
* Map an event log
*
* Includes all properties in the event log entry schema.
*/
public function map(EventLogEntry $item): array
{
return $this->mapByProperties($this->getProps(), $item);
}
/**
* Summarize an event log
*
* Includes properties with the apiSummary flag in the event log entry schema.
*/
public function summarize(EventLogEntry $entry): array
{
return $this->mapByProperties($this->getSummaryProps(), $entry);
}
/**
* Map a collection of event log entries
*
* @see self::map
*/
public function mapMany(Enumerable $collection): Enumerable
{
$this->collection = $collection;
return $collection->map(function ($category) {
return $this->map($category);
});
}
/**
* Summarize a collection of event log entries
*
* @see self::summarize
*/
public function summarizeMany(Enumerable $collection): Enumerable
{
$this->collection = $collection;
return $collection->map(function ($category) {
return $this->summarize($category);
});
}
/**
* Map schema properties of an event log entry to an assoc array
*/
protected function mapByProperties(array $props, EventLogEntry $entry): array
{
$output = [];
foreach ($props as $prop) {
switch ($prop) {
default:
$output[$prop] = $entry->getData($prop);
break;
}
}
return $output;
}
}