Jump to table of contents

Release Notebook for OJS/OMP/OPS 3.4

Ready to upgrade? Read our guide on how to upgrade.

OJS, OMP and OPS version 3.4 were released on June 9, 2023. This release notebook provides technical guidance on changes that will impact software developers, plugin and theme authors, and anyone who works with the application codebase.

Hosting and Deployment #

The following changes may effect anyone who hosts or deploys one of the applications, as well as those who develop plugins or themes for the applications.

PHP 8.0+ #

The minimum required version of PHP is 8.0 or above, following PHP’s version support schedule.

Locales #

The country code has been dropped from many locale keys. For example, en_US is now en. You may need to update the locale setting in the [i18n] section of config.inc.php. Supported locales can be found in the locale directory.

Database Drivers #

mysqli and postgres9 are the only valid values for the driver setting in the [database] section of config.inc.php.

Client Charset #

The client_charset setting has been removed from config.inc.php. Only the UTF-8 charset is supported.

Email #

PHPMailer was replaced with Laravel’s Mailer. Add the preferred method for sending emails, such as sendmail or smtp, to the [email] section of your config.inc.php:

; Default method to send emails
; Available options: sendmail, smtp, log, phpmailer
default = sendmail

Servers that use sendmail may need to add the path to the sendmail binary:

; Path to the sendmail, -bs argument is for using SMTP protocol
sendmail_path = "/usr/sbin/sendmail -bs"

It is now possible to send mail through third-party service providers like Mailgun. For sandbox environments, you can redirect mail to log files. Read Utilities > Email to learn more.

Jobs and Queues #

Jobs and queues are now used to manage long-running tasks. By default, these jobs will be processed during normal web requests, which may impact your site’s performance. We recommend setting up a worker daemon to process jobs.

Timezones #

A new time_zone setting has been added to the [general] section of config.inc.php. See config.TEMPLATE.inc.php for supported timezones.

Date/Time Formats #

Valid date and time formats in config.inc.php must now be specified in PHP DateTime formatting. Recommended defaults can be found in the config.TEMPLATE.inc.php file.

date_format_short = "Y-m-d"
date_format_long = "F j, Y"
datetime_format_short = "Y-m-d h:i A"
datetime_format_long = "F j, Y - h:i A"
time_format = "h:i A"

The old formats are deprecated and support may be removed in a future version.

Remove Unvalidated Accounts #

A new user_validation_period setting has been added to the [general] section of config.inc.php. Use this setting to automatically delete accounts from users who never validated their account. See config.TEMPLATE.inc.php for a recommended default.

Integrations #

The following changes effect anyone who creates a plugin or theme for one of the applications, or wishes to integrate with the application through the REST API.

Namespaces and Constants #

Namespaces are now used in all PHP code in the software and many global constants have been replaced with class constants. This is a major change to our codebase that will require changes in most plugins or themes.

We strongly encourage you to use a code editor that supports autocomplete to help you import classes, discover methods and constants, and identify syntax errors. Type hinting is used throughout more of the codebase now in order to make it easier to understand and write code for our applications.

The import() statements and global constants like this are no longer used:


$submission = new Submission();
$author = new PKPAuthor();

$submission->setData('status', STATUS_PUBLISHED);

Replace this code with use statements and class constants:

use APP\submission\Submission;
use PKP\author\Author;

$submission = new Submission();
$author = new PKPAuthor();

$submission->setData('status', Submission::STATUS_PUBLISHED);

Plugin and theme developers who want to use namespaces in their own code should read the instructions on how to adapt plugins.

HookRegistry Renamed #

The HookRegistry class has been deprecated. Prepare for it to be removed in a future version by replacing this code:

HookRegistry::register('...', function($hookName, $args) {});
HookRegistry::call('...', [$a, $b]);

With this code:

use PKP\plugins\Hook;

Hook::add('...', function($hookName, $args) {});
Hook::call('...', [$a, $b]);

Localization #

Plugin and theme developers should rename the locale directories in their plugin according to the new locale codes mentioned above under Locales.

We have replaced the PKPLocale and APPLocale classes with a new facade class. Read the Utilities > Translation section of the developer docs to learn more about working with translations.

There is also a detailed description of changes.

Dates and Times #

There are new date and time formats specified in the config.inc.php. Also, every journal, press or preprint server can configure different formats for every supported locale.

To show a date or time to a user, use one of the helper methods on the Context (journal, press or preprint server).

$locale = Locale::getLocale();
$dateFormatShort = $context->getLocalizedDateFormatShort($locale);
$dateFormatLong = $context->getLocalizedDateFormatLong($locale);
$dateTimeFormatShort = $context->getLocalizedDateTimeFormatShort($locale);
$dateTimeFormatLong = $context->getLocalizedDateTimeFormatLong($locale);
$timeFormatShort = $context->getLocalizedTimeFormatShort($locale);
$timeFormatLong = $context->getLocalizedTimeFormatLong($locale);

Use the following to convert an old date/time format to the new format.

use PKP\core\PKPString;

$newFormat = PKPString::convertStrftimeFormat($oldOrNewFormat);

Use the format with a timestamp to convert a date into the format desired by the journal, press or preprint.

echo date($format, $timestamp);

Repositories #

Repositories were introduced to replace the entity Service class pattern that was introduced in 3.4. Repositories provided a better type-hinted interface to read and write data for many entities, such as submissions and authors. Learn more about Repositories.

When a Repository class exists, plugin and theme developers should use the Repo facade instead of the DAO class to read and write data. Replace code like this:

$submissionDao = DAORegistry::getDAO('SubmissionDAO');
$submission = $submissionDao->getById($id, $contextId);
$submission->setData('locale', 'en_US');

With code like this:

use APP\facades\Repo;

$submission = Repo::submission()->get($id, $contextId);

        'locale' => 'en',

Direct access to the DAO is discouraged. If you need to access the DAO, it is available through the Repository.

- $submissionDao = DAORegistry::getDAO('SubmissionDAO');
+ use APP\facades\Repo;
+ $submissionDao = Repo::submission()->dao;

Many of the service classes have been replaced by a repository. These often have the same or similar methods, but the function signatures may have changed. For example, the add(), edit(), and delete() methods no longer return the object that was added.

- $submission = Services::get('submission')->add($submission, $request);
+ use APP\facades\Repo;
+ $submissionId = Repo::submission()->add($submission);
+ $submission = Repo::submission()->get($submissionId);
- $submission = Services::get('submission')->edit($submission, $params, $request);
+ Repo::submission()->edit($submission, $params);
+ $submission = Repo::submission()->get($submission->getId());

The following service classes have been replaced by a repository.

- Services::get('announcement');
+ Repo::announcement()
- Services::get('author');
+ Repo::author();
- Services::get('submission');
+ Repo::submission();
- Services::get('publication');
+ Repo::publication();
- Services::get('issue');
+ Repo::publication();
- Services::get('galley');
+ Repo::galley();
- Services::get('user');
+ Repo::user();

This is a more disruptive change than we typically like to make. Modernizing our patterns in this way has significantly improved the experience of working with the codebase and detecting errors early. We recommend using an editor with autocomplete to benefit from this.

Submission Progress #

The submissionProgress property of a submission is now a string. This property used to be an int and was used to track the current step of the submission wizard for each submission. It now refers to the current step by one of the following: start, details, files, contributors, editors, review.

HTML Titles #

The title, fullTitle, and subtitle properties of a Publication may now include the following inline HTML tags <i>, <b>, <u>, <sup>, and <sub>. To get the HTML version of the title, pass html to the second argument of several publication methods.

$title = $publication->getLocalizedFullTitle(null, 'html');
$title = $publication->getLocalizedTitle(null, 'html');
$subtitle = $publication->getLocalizedSubTitle(null, 'html');

Jobs #

Laravel’s Jobs have been adopted to implement a jobs queue for long-running tasks. Plugin developers who want to use this feature should read how to create and dispatch jobs.

Events #

Laravel’s Events have been adopted to implement the observer pattern. Learn more about how to use events and listeners. Plugin developers can see an example listener.

Statistics #

The Usage Stats plugin has been removed and statistics are now part of the core application. It has been rewritten from the ground up to support COUNTER R5, protect user privacy when compiling geographic stats, and support institutional stats by IP range. Many stats are now available through the REST API in JSON or CSV format.

System administrators may want to read about how to configure and download statistics. Plugin developers and contributors may want to review the new documentation on how statistics are logged and compiled.

Editorial Decisions #

Editorial decisions have been refactored. If you have a plugin or theme which records an editorial decision or modifies the UI for recording an editorial decision, read the documentation about decisions.

Submission Wizard #

The submission wizard has been rebuilt to use the REST API and the form components. Plugins that modify the submission wizard should use one of the following methods:

  • Use the form hooks to add or edit fields in an existing form. See example in the Plugin Guide.
  • Use the TemplateManager::display, Template::SubmissionWizard::Section, and Template::SubmissionWizard::Section::Review hooks to add new forms or other components to a step in the submission wizard. An example can be found in the funding plugin.

The submissionProgress property has been changed from an integer to a string. View the REST API documentation for the new specification.


Several new endpoints were added to the REST API for working with statistics, editorial decisions, contributors and submissions. See what’s changed in the 3.4 API Reference.

UI Library #

New components were added to the UI Library, such as Dialog, Composer, FileAttacher, Steps, and more. View everything in the UI Library.

Hooks Removed #

The following hooks were removed. In most cases, these hooks were removed because code was refactored in a way that made the hooks obsolete.

Mail::send #

The Mail::send hook was removed as part of a major update to how emails are sent. To intervene when an email is sent, add a listener to the MessageSendingFromContext and MessageSendingFromSite events. Learn more about the new email system.

EditorAction::* #

The EditorAction::modifyDecisionOptions hook was removed as part of a major refactor of Decisions. Use the following to change the decision options available to an editor.

use PKP\plugins\Hook;

Hook::add('Workflow::Decisions', function(string $hookName, array $args) {
    $decisionTypes = $args[0];
    $stageId = $args[1]; // eg - WORKFLOW_STAGE_ID_*

    // ...

The EditorAction::recordDecision hook was removed as part of a major refactor of Decisions. Add a listener to the DecisionAdded event to perform some action when a decision is recorded.

*::getProperties #

Several hooks which let you append data to API results have been removed. Replace these by extending the entity’s map.

- Announcement::getProperties
- Author::getProperties::values
- EmailTemplate::getProperties
- Galley::getProperties::values
- Issue::getProperties::fullProperties
- Issue::getProperties::summaryProperties
- Issue::getProperties::values
- Publication::getProperties
- Section::getProperties::fullProperties
- Section::getProperties::summaryProperties
- Section::getProperties::values
- Submission::getProperties::values
- SubmissionFile::getProperties
- User::getProperties::fullProperties
- User::getProperties::reviewerSummaryProperties
- User::getProperties::summaryProperties
- User::getProperties::values

*::getMany #

Several hooks which let you customize the database queries for entities have been removed. They have been replaced with the Collector classes. Use the *::Collector hook to modify the query. For example, code which registered functions on Author::getMany::queryBuilder and Author::getMany::queryObject should be adapted to the hook Author::Collector.

The following hooks were removed.

- Announcement::getMany::queryBuilder
- Announcement::getMany::queryObject
- Author::getMany::queryBuilder
- Author::getMany::queryObject
- EmailTemplate::getMany::queryBuilder
- EmailTemplate::getMany::queryObject::custom
- EmailTemplate::getMany::queryObject::default
- Galley::getMany::queryBuilder
- Issue::getMany::queryBuilder
- Publication::getMany::queryBuilder
- Publication::getMany::queryObject
- Stats::getOrderedObjects::queryBuilder
- Stats::getRecords::queryBuilder
- Stats::queryBuilder
- Stats::queryObject
- Submission::getMany::queryBuilder
- Submission::getMany::queryObject
- SubmissionFile::getMany::queryBuilder
- SubmissionFile::getMany::queryObject
- User::getMany::queryBuilder
- User::getMany::queryObject
- User::getReviewers::queryBuilder

*::_fromRow #

Several hooks in the code that turns database results into a DataObject have been removed. Extend the properties of a DataObject by extending its entity schema.

- CategoryDAO::_fromRow
- IssueDAO::_fromRow
- IssueDAO::_returnIssueFromRow
- SectionDAO::_fromRow
- UserDAO::_returnUserFromRow
- UserDAO::_returnUserFromRowWithData
- UserDAO::_returnUserFromRowWithReviewerStats
- UserGroupDAO::_returnFromRow

The following hook was removed because the ReviewerSubmissionDAO object was removed.

- ReviewerSubmissionDAO::_fromRow

API::stats::* #

Several hooks in the stats API have been removed as part of major changes to the stats system.

- API::stats::publication::abstract::params
- API::stats::publication::galley::params
- API::stats::publications::abstract::params
- API::stats::publications::galley::params

PKPLocale::* #

The hook PKPLocale::installLocale was replaced with Locale::installLocale. The following hooks were removed.

- PKPLocale::installLocale
- PKPLocale::registerLocaleFile
- PKPLocale::registerLocaleFile::isValidLocaleFile
- PKPLocale::translate

Other Hooks Removed #

Some hooks have been removed because the related code was removed.

- API::submissions::files::params
- ArticleGalleyDAO::getLocalizedGalleysByArticle
- PluginGridHandler::plugin
- PluginGridHandler::plugin
- SubmissionFile::assignedFileStages
- SubmissionHandler::saveSubmit

Code Removed #

As part of our efforts to modernize the code, several functions were removed, deprecated or replaced.

Get galley file ids from the new Galley object.

- ArticleGalley::getFileId();
- ArticleGalley::setFileId();
- PreprintGalley::getFileId();
- PreprintGalley::setFileId();
+ PKP\galley\Galley::getData('submissionFileId');

Don’t use these methods to work with submission cover images. Get the cover image info from the Publication object instead.

- Submission::getCoverImage();
- Submission::getLocalizedCoverImage();
- Submission::getCoverImageAltText();
- Submission::getLocalizedCoverImageAltText();
- Submission::getLocalizedCoverImageUrl();
+ APP\publication\Publication::getLocalizedData('coverImage');
+ APP\publication\Publication::getLocalizedCoverImageUrl($contextId);

Several deprecated Submission methods have been removed. Use Publication methods instead.

- Submission::getAuthorString()
- Submission::getShortAuthorString()
- Submission::getPrimaryAuthor()
+ APP\publication\Publication::getAuthorString($userGroups)
+ APP\publication\Publication::getShortAuthorString()
+ APP\publication\Publication::getPrimaryAuthor()

For theme templates, this will require a change like this:

- {$article->getAuthorString()|escape}
+ {$article->getCurrentPublication()->getAuthorString($authorUserGroups)|escape}

Use getSignature() to get a user’s contact signature:

- User::getContactSignature()
+ PKP\user\User::getSignature(Locale::getLocale())

Use Repo::section() to get section data:

- DAORegistry('UserGroupDAO')->...;
+ Repo::section()->...

Use DB facade instead of Capsule to access Laravel’s QueryBuilder:

- use Illuminate\Database\Capsule\Manager as Capsule;
- Capsule::table('submissions')->...
+ use Illuminate\Support\Facades\DB;
+ DB::table('submissions')->...

The Mail and SubmissionMail classes were removed in favor of Mailables. Read Utilities > Email to learn more.

Deprecated shims for Smarty2 functions within PKPTemplatateManager, including get_template_vars() and register_function() were removed. Legacy usage should move to Smarty3-compatible functions PKPTemplateManager::getTemplateVars() and PKPTemplateManager::registerPlugin().