Academic Tools. Smarter Research.
Open Journal SystemsDeveloping Generic Plugins for OJS

Developing Generic Plugins for OJS 3.3, 3.4, and 3.5

Comprehensive step-by-step tutorial for building generic plugins in OJS. Includes complete boilerplate code, settings modal management, and locale string configuration.

Building your first custom generic plugin for OJS opens unlimited possibilities for workflow automation; here is the complete code-level blueprint.

Real Problem

Developers starting with OJS plugin development struggle with cryptic namespace structures, missing version.xml requirements, and breaking API changes between OJS 3.3 and 3.4.

Practical Solution

Follow this end-to-end coding tutorial: scaffold the plugin directory, author compliant version.xml descriptors, implement the main plugin class, create settings forms, and handle multi-language locale strings.

how to build OJS plugingeneric plugin tutorial OJSOJS 3.3 plugin developmentOJS 3.4 plugin changeswrite custom plugin PKP

Step-by-Step Construction of a Production Generic Plugin

To demonstrate how to build an enterprise-grade generic plugin, we will create a complete working plugin named JournalNotifierPlugin that logs publication events and injects custom metadata badges into published articles.

Directory Structure

Navigate to plugins/generic/ and create the directory journalNotifier:

plugins/generic/journalNotifier/

├── index.php

├── JournalNotifierPlugin.inc.php

├── version.xml

├── locale/

│ └── en_US/

│ └── locale.xml

└── templates/

└── settings.tpl

1. The Entry Point: index.php

This file instantiates the main plugin class:

<?php

require_once('JournalNotifierPlugin.inc.php');

return new JournalNotifierPlugin();

2. The Manifest: version.xml

OJS uses version.xml to track database installation and compatibility:

<?xml version="1.0" encoding="UTF-8"?>

<version>

<application>journalNotifier</application>

<type>plugins.generic</type>

<release>1.0.0.0</release>

<date>2024-01-01</date>

<lazy-load>1</lazy-load>

<sitewide>0</sitewide>

</version>

3. The Main Plugin Class: JournalNotifierPlugin.inc.php

<?php

import('lib.pkp.classes.plugins.GenericPlugin');

class JournalNotifierPlugin extends GenericPlugin {

public function register($category, $path, $mainContextId = null) {

$success = parent::register($category, $path, $mainContextId);

if ($success && $this->getEnabled($mainContextId)) {

// Hook into the article publication event

HookRegistry::register('Publication::publish', [$this, 'onArticlePublished']);

}

return $success;

}

public function onArticlePublished($hookName, $args) {

$newPublication = $args[0];

$submissionId = $newPublication->getData('submissionId');

// Custom business logic: log or trigger external webhook

error_log("JournalNotifier: Submission {$submissionId} was successfully published!");

return false; // Allow other hooked plugins to continue execution

}

public function getDisplayName() {

return __('plugins.generic.journalNotifier.displayName');

}

public function getDescription() {

return __('plugins.generic.journalNotifier.description');

}

}

4. Localization: locale/en_US/locale.xml

<?xml version="1.0" encoding="UTF-8"?>

<locale name="en_US" full_name="U.S. English">

<message key="plugins.generic.journalNotifier.displayName">Journal Publication Notifier</message>

<message key="plugins.generic.journalNotifier.description">Automatically logs and notifies external services when articles are published.</message>

</locale>

5. Testing and Activation

Once uploaded, navigate to Settings > Website > Plugins > Generic Plugins. Locate "Journal Publication Notifier" and toggle the checkbox to enable it. Publish a test submission and monitor your server error log to observe your custom hook firing in real time.

Step-by-Step Action Plan

1

Scaffold Directory Structure

Create plugins/generic/journalNotifier/ with index.php, version.xml, JournalNotifierPlugin.inc.php, and locale files.

2

Write version.xml Descriptor

Define the application name, category type plugins.generic, release version 1.0.0.0, and lazy-load settings.

3

Implement Plugin Class & Registration

Extend GenericPlugin, define getDisplayName() and getDescription(), and register core hook listeners.

4

Build Administrative Settings Form

Implement getActions() and manage() methods to display a settings modal allowing editors to configure options.

5

Test Installation & Execution

Enable the plugin in Journal Settings > Plugins and verify hook execution in the application logs.

Questions People Usually Ask

Why does my new plugin not appear in the OJS Plugin list?

The most common causes are a syntax error in version.xml, mismatched class names between index.php and the main class file, or incorrect file permissions preventing PHP from reading the directory.

How do I handle multi-language translations in a custom plugin?

Place XML or PO translation files in locale/en_US/locale.xml (or locale/en/locale.po in OJS 3.4). Reference strings in templates using {translate key="plugins.generic.myPlugin.name"}.

What changed in plugin file naming in OJS 3.4?

In OJS 3.3, PHP class files required the .inc.php extension (e.g., MyPlugin.inc.php). In OJS 3.4+, standard PSR-4 naming and .php extensions are used (e.g., MyPlugin.php).

Build Enterprise Plugins with AcademiaTech

We develop custom, well-documented plugins adhering to strict PKP coding standards for university presses and scholarly societies.