Skip to content

Add Reviewer Certificate plugin (generic)#473

Open
ssemerikov wants to merge 4 commits intopkp:mainfrom
ssemerikov:add-reviewer-certificate-plugin
Open

Add Reviewer Certificate plugin (generic)#473
ssemerikov wants to merge 4 commits intopkp:mainfrom
ssemerikov:add-reviewer-certificate-plugin

Conversation

@ssemerikov
Copy link

@ssemerikov ssemerikov commented Mar 12, 2026

Summary

Adds the Reviewer Certificate plugin to the Plugin Gallery.

The plugin generates personalized PDF certificates for peer reviewers after completing review assignments. Features include customizable templates with background images, QR code verification, batch generation, download tracking, and 32 language translations.

Release details (v1.4.0)

Three version-specific packages per reviewer recommendation:

OJS Version Package MD5
3.3.x v1.4.0-3.3 5101e4cba8f77da3106297bd05c12ab0
3.4.x v1.4.0-3.4 90b8fd33b000f741e7f1a00de0df45ba
3.5.x v1.4.0-3.5 c2881550df8064a57949c357566a7206

Changes in v1.4.0 (addressing review feedback):

  1. Version-specific branches (stable-3_3_0, stable-3_4_0, stable-3_5_0)
  2. Context isolation — all handlers validate context_id to prevent cross-journal access
  3. HTML title sanitization — strip_tags() for PDF, Smarty |escape for templates
  4. Input validation — certificate codes checked against /^[A-F0-9]{16}$/

Checklist

  • Packages are .tar.gz with correct root folder (reviewerCertificate/)
  • version.xml present at top level with matching <application> name
  • MD5 checksums match packages (all 3 verified)
  • Package URLs return HTTP 200 (all 3 verified)
  • No <certification> element (community plugin, not PKP-reviewed)
  • Compatibility versions use ~ wildcard notation
  • Separate <release> blocks per OJS major version

Generates personalized PDF certificates for peer reviewers
after completing reviews. Compatible with OJS 3.3, 3.4, and 3.5.

Homepage: https://github.com/ssemerikov/reviewerCertificate
@asmecher
Copy link
Member

@ssemerikov, thanks for the PR! I have a few notes to consider...

  • I don't recommend making a single plugin package that's compatible with multiple major releases. Instead, create a stable-3_3_0, stable-3_4_0 and stable-3_5_0 branch, with each adapted for the equivalent version of OJS. This will keep things much more maintainable for future releases. As we continue to modernize the codebase for 3.6 and beyone, it will get harder to keep one codebase compatible. This is just a recommendation, I won't hold back the addition to the plugin gallery on this account, but I think it'll make your life much easier and your code shorter!
  • Please make sure to validate your parameters. For example, some handlers take a reviewId parameter, which is never checked to ensure it's part of the current journal. Same with other user-supplied IDs.
  • OJS 3.5 and later support HTML markup in submission titles; I don't think the plugin currently accommodates this.

Split single multi-version release into three separate packages
(OJS 3.3, 3.4, 3.5) per reviewer recommendation. v1.4.0 adds
context isolation, HTML title sanitization, and input validation.
@ssemerikov
Copy link
Author

Thanks for the review, @asmecher! All three items addressed in v1.4.0:

  1. Version-specific branches — Created stable-3_3_0, stable-3_4_0, stable-3_5_0 branches with separate releases. Each branch's version.xml declares compatibility only with its target OJS version. I've updated the PR XML below to use per-version packages.
  2. Parameter validation — All handlers now enforce context isolation. download() and generateBatch() join review_assignments with submissions to filter by context_id, preventing cross-journal data access. verify() checks the certificate's context_id against the request context. Certificate code input is validated against /^[A-F0-9]{16}$/ before any DB lookup.
  3. HTML in submission titles — strip_tags() applied to all title retrieval paths in PDF generation. Verify page template variables use Smarty |escape filter for XSS prevention.

Details: 596402b

@asmecher
Copy link
Member

Thanks, @ssemerikov! I have some more recommendations...

  • I'd recommend committing a Composer configuration file to the repo, rather than adding the whole tcpdf dependency to the repo. The pkp-plugin tool can take care of installing Composer-based packages when building the plugin package, and you'll have an easier time staying up to date using a dependency management tool.
  • The miscellaneous tools like CLEAR_CACHE.php and debug_config.php are potentially dangerous. They can be invoked remotely by guessing the URL. (You can exclude things like tests by adding them to an exclusions.txt file like this example. Anything in that list will not be included in the .tar.gz package when it is built using pkp-plugin. But this won't help anyone who deploys the plugin from github!)
  • I still see compatibility code for multiple branches -- e.g. stable-3_3_0's index.php still contains 3.5.0-x compatibility code. Each branch should be trimmed down to support only that branch of OJS.

It looks like a fair amount of the plugin is AI-coded; there's no problem with that in theory, but we do ask that devs who work with AI take extra care to make sure the results are written with the level of care and brevity the developer would themselves take. The speed of using AI to generate code -- and lots of it! -- can easily overwhelm our capacity to review it.

ssemerikov and others added 2 commits March 20, 2026 07:59
- TCPDF now installed via Composer instead of bundled (27MB reduction)
- Removed dangerous root-level PHP utility scripts
- Eliminated code duplication across plugin classes
- Each stable branch contains only version-specific code paths
- All packages tested with Playwright E2E on OJS 3.3, 3.4, 3.5

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@ssemerikov
Copy link
Author

Thank you for the detailed feedback, @asmecher. All four items have been addressed in v1.5.0:

1. Composer for TCPDF

TCPDF is now declared as a Composer dependency ("tecnickcom/tcpdf": "^6.7" in composer.json). The bundled lib/tcpdf/ directory (27MB, 214 files) has been removed from the repository. The ensureTCPDF() method searches for TCPDF in this order:

  1. Plugin's own vendor/tecnickcom/tcpdf/tcpdf.php (Composer — primary)
  2. OJS's lib/pkp/lib/vendor/tecnickcom/tcpdf/tcpdf.php (OJS vendor)
  3. OJS's lib/pkp/lib/tcpdf/tcpdf.php (legacy OJS path)

Commit: 9b17b78 on main

2. Removed dangerous utility files

All 9 root-level PHP scripts removed from git tracking: BATCH_GENERATE_CLI.php, BATCH_GENERATE_MANUAL.php, BATCH_GENERATE_SIMPLE.php, CHECK_DATABASE_LOCKS.php, CLEAR_CACHE.php, FORCE_RELOAD_OPCACHE.php, QUERY_CERTIFICATES.php, TEST_DIRECT_INSERT.php, debug_config.php. Also removed 9 development documentation files. Created exclusions.txt for the pkp-plugin build tool.

3. Trimmed stable branches

Each stable branch now contains only the code paths for its target OJS version:

  • stable-3_3_0: Keeps compat_autoloader.php, \HookRegistry, import(), _getInsertId(), raw SQL migration. No Mailable. No method_exists()/class_exists() version branching.
  • stable-3_4_0: No compat_autoloader.php, uses Hook::register(), namespaced classes, Repo facade, HANDLER_CLASS pattern. No Mailable. No version branching.
  • stable-3_5_0: Keeps Mailable, uses $params[3] handler, DB::getPdo()->lastInsertId(), getCurrentPublication(). No version branching.

4. Code brevity

Eliminated 5 duplicated methods across the codebase:

  • generateCertificateCode()Certificate::generateCode() (static)
  • createReviewAssignmentFromRow()CertificateDAO::reviewAssignmentFromRow()
  • createJSONMessage() → single method on PluginCore, called via $this->getPlugin()
  • getDefaultBodyTemplate()CertificateGenerator::getDefaultBodyTemplate() (public static)
  • Removed callFirstAvailable() multi-version helper — stable branches use direct API calls

Testing

  • Unit tests: 156 passed on main
  • E2E tests (Playwright): 33/33 passed on main (11 tests × 3 OJS versions), 11/11 on each stable branch against its respective OJS Docker instance (OJS 3.3.0-22, 3.4.0-10, 3.5.0-3)

Updated releases

  • v1.5.0-3.3 — MD5: 44c2f5c3a69106682cf5a63da8a79f8d
  • v1.5.0-3.4 — MD5: f4f848e60ece5b5c5d11d55b8aef7338
  • v1.5.0-3.5 — MD5: 0576ae4c3fdb5499d883f5686d38ce82

Plugin gallery XML updated in this PR to reference v1.5.0 releases.

@asmecher
Copy link
Member

@ssemerikov, just to confirm -- has a human reviewed all of this thoroughly before it gets sent to me?

@ssemerikov
Copy link
Author

@asmecher, honestly, not as thoroughly as I should have. Claude generated much of the code and the PR responses. I tested the basic functionality, and the automated tests pass, but I haven't reviewed every line.

@asmecher
Copy link
Member

Thanks, @ssemerikov, and actually this will be helpful in setting an AI policy, which we don't have yet. (I've started a discussion on that. Feel free to join in! I picked this among several examples.)

I would like to see this plugin move ahead, but need to reduce the burden on us as the reviewer. We now have 4 branches to consider; each should be tuned to the application it's compatible with, and there should be minimal changes between them -- just what's required to ensure compatibility.

If it helps, my process in reviewing these is typically...

  • Review the oldest stable branch (stable-3-3_0) fully
  • Review the changes from there forward, e.g. git diff -w stable-3-3_0..stable-3_4_0
  • Repeat as necessary to the newest branch

As you can see, it's important for the volume of code to be kept low, clarity high, and for the branches to be closely synchronized. This will be easier for you in the long run too.

This is a valuable contribution -- but it's one of many, and we have to balance time spent on reviews carefully against dev work! Your human stewardship of what you send, before you send it, is critical to keeping it manageable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants