Skip to content

Commit b332840

Browse files
committed
Merge branch 'update_thoth_schema-3_3_0-i1176' into 'stable-3_3_0'
Update thoth schema integration (3.3.0) See merge request softwares-pkp/plugins_ojs/thoth-omp-plugin!101
2 parents de31c6f + d477d0f commit b332840

66 files changed

Lines changed: 2377 additions & 383 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22

33
# Thoth OMP Plugin
44

5-
Work in Progress Integration of OMP and [Thoth](https://thoth.pub/) for communication and synchronization of book data between the two platforms.
5+
[![Current Version](https://img.shields.io/badge/version-v0.3.0.0-blue)](https://github.com/thoth-pub/thoth-omp-plugin/releases)
6+
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-green.svg)](https://www.gnu.org/licenses/gpl-3.0)
7+
[![OMP compatibility](https://img.shields.io/badge/OMP-3.3-blue)](https://pkp.sfu.ca/software/omp/)
8+
9+
Integrates [OMP (Open Monograph Press)](https://pkp.sfu.ca/software/omp/) with [Thoth](https://thoth.pub/), an open metadata management platform for books. This plugin enables the registration and synchronization of book- and chapter-level metadata directly from OMP into Thoth, where it can be disseminated in multiple industry-standard formats including ONIX, MARC, KBART, and Crossref XML.
610

711
## Compatibility
812

@@ -16,7 +20,7 @@ This plugin is compatible with the following PKP applications:
1620

1721
1. **api_key_secret**
1822

19-
The OMP instance must have the `api_key_secret` configuration set up, you may contact your system administrator to do that (see [this post](https://forum.pkp.sfu.ca/t/how-to-generate-a-api-key-secret-code-in-ojs-3/72008)).
23+
The OMP instance must have the `api_key_secret` configuration set up. You may contact your system administrator to do that (see [this post](https://forum.pkp.sfu.ca/t/how-to-generate-a-api-key-secret-code-in-ojs-3/72008)).
2024

2125
This is required to store the Thoth personal access token encrypted in the OMP database.
2226

@@ -30,52 +34,59 @@ This is required to store the Thoth personal access token encrypted in the OMP d
3034

3135
## Usage
3236

33-
### Guidelines
37+
### Configuration
3438

35-
- Only basic HTML tags are preserved (`<strong>`, `<mark>`, `<em>`, `<i>`, `<u>`, `<sup>`, `<sub>`, `<ul>`, `<ol>` and `<li>`); all others will be removed
36-
- ISBN must be properly formatted (e.g., 978-3-16-148410-0)
37-
- To avoid incorrect assignment of affiliations in Thoth, is required the use of the [ROR plugin](https://github.com/withanage/ror) to fill the affiliations in OMP.
39+
After enabling the plugin, go to the plugin settings and fill in:
3840

39-
### Configuration
41+
- **Personal access token**: A valid Thoth personal access token used to authenticate API requests.
42+
- **Custom Thoth API**: Check this option to use a custom Thoth API instead of the official one.
43+
- **Thoth API URL**: The URL of the custom Thoth API (only required when the custom API option is enabled).
4044

41-
To configure the plugin:
45+
<img src="/docs/images/plugin_settings.png" alt="Plugin settings form with personal access token, custom API and URL fields" width="700">
4246

43-
- **Personal access token**: Enter a valid Thoth personal access token to authenticate API requests.
44-
- **Test Environment**: Check this option if you are using a local instance of the Thoth API for testing purposes.
47+
### Registering Monographs
4548

46-
![settings](/images/settings.png)
49+
#### Unpublished Monographs
4750

48-
### Managing Monographs
51+
Register metadata in Thoth during the publishing process by selecting the option to register metadata in the publish modal and choosing an imprint.
4952

50-
- **Unpublished Monographs**: Register metadata in Thoth during the publishing process by selecting the option to register metadata in the publish modal and choosing an imprint.
53+
<img src="/docs/images/register_field.png" alt="Publish modal with Thoth registration option" width="700">
5154

52-
![publish](/images/publish.png)
55+
#### Published Monographs
5356

54-
- **Published Monographs**: Register metadata for published monographs by using the 'Register' button next to the publication status.
57+
Register metadata for already-published monographs by using the 'Register' button next to the publication status.
5558

56-
![button](/images/button.png)
57-
![register](/images/register.png)
59+
<img src="/docs/images/register_button.png" alt="Register button in the publication workflow" width="700">
60+
<img src="/docs/images/register_modal.png" alt="Registration modal with imprint selection" width="700">
5861

5962
### Updating Metadata
6063

61-
To update metadata in Thoth, unpublish the monograph, edit the data, and the changes will be automatically updated in Thoth.
64+
Once a monograph is registered, metadata updates are **automatic**. Unpublish the monograph, edit the data, and the changes will be synchronized with Thoth upon republication.
65+
66+
It is also possible to manually update the metadata in Thoth by clicking the 'Update Metadata' button next to the publication status.
6267

6368
### Accessing Thoth Book Records
6469

65-
After metadata is published, a link to the book on Thoth will appear at the top of the publication.
70+
After metadata is registered, a link to the book on Thoth will appear at the top of the publication workflow.
71+
72+
<img src="/docs/images/view_button.png" alt="View link to the Thoth book record" width="700">
6673

67-
![link](/images/link.png)
74+
### Bulk Registration
6875

69-
### Bulk register
76+
On the Thoth management page, you can submit a selection of titles from OMP into Thoth in bulk.
7077

71-
On the Thoth page, you can bulk submit a selection of titles from OMP into Thoth.
78+
<img src="/docs/images/bulk_register_page.png" alt="Thoth management page with bulk registration" width="700">
79+
80+
### Guidelines
7281

73-
![page](/images/page.png)
82+
- Only basic HTML tags are preserved in text fields: `<strong>`, `<mark>`, `<em>`, `<i>`, `<u>`, `<sup>`, `<sub>`, `<ul>`, `<ol>`, and `<li>`. All other tags will be stripped.
83+
- ISBN must be properly formatted as ISBN-13 (e.g., `978-3-16-148410-0`).
84+
- To avoid incorrect affiliation assignment in Thoth, use the [ROR plugin](https://github.com/withanage/ror) to populate affiliations in OMP.
7485

7586
## OMP-Thoth Mapping
7687

7788
<details>
78-
<summary>Click here to see the data relationship between Thoth and OMP</summary>
89+
<summary>Click here to see the data relationship between OMP and Thoth</summary>
7990

8091
| OMP | | | Thoth | | |
8192
| ----------------- | ------------------ | - | ---------------------- | ------------------- | ----------- |
@@ -132,6 +143,6 @@ Developed by [Lepidus Tecnologia](https://github.com/lepidus).
132143

133144
This plugin is licensed under the GNU General Public License v3.0 - [See the License file.](/LICENSE)
134145

135-
Copyright (c) 2024-2025 Lepidus Tecnologia
146+
Copyright (c) 2024-2026 Lepidus Tecnologia
136147

137-
Copyright (c) 2024-2025 Thoth
148+
Copyright (c) 2024-2026 Thoth Open Metadata

ThothSettingsForm.inc.php

Lines changed: 68 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ class ThothSettingsForm extends Form
2929

3030
private const SETTINGS = [
3131
'token',
32-
'testEnvironment',
32+
'customThothApi',
33+
'customThothApiUrl',
3334
];
3435

3536
public function __construct($plugin, $contextId)
@@ -42,17 +43,54 @@ public function __construct($plugin, $contextId)
4243
parent::__construct($plugin->getTemplateResource($template));
4344

4445
$form = $this;
46+
$this->addCheck(new FormValidatorCustom(
47+
$this,
48+
'customThothApiUrl',
49+
'required',
50+
'plugins.generic.thoth.settings.customThothApiUrl.required',
51+
function ($customThothApiUrl) {
52+
if (!$this->getData('customThothApi')) {
53+
return true;
54+
}
55+
return !empty(trim($customThothApiUrl));
56+
}
57+
));
58+
59+
$this->addCheck(new FormValidatorCustom(
60+
$this,
61+
'customThothApiUrl',
62+
'optional',
63+
'plugins.generic.thoth.settings.customThothApiUrl.invalid',
64+
function ($customThothApiUrl) {
65+
if (!$this->getData('customThothApi') || !trim($customThothApiUrl)) {
66+
return true;
67+
}
68+
return filter_var(trim($customThothApiUrl), FILTER_VALIDATE_URL) !== false;
69+
}
70+
));
71+
72+
$this->addCheck(new FormValidatorCustom(
73+
$this,
74+
'customThothApiUrl',
75+
'optional',
76+
'plugins.generic.thoth.settings.customThothApiUrl.unreachable',
77+
function ($customThothApiUrl) {
78+
if (!$this->getData('customThothApi')) {
79+
return true;
80+
}
81+
return $this->validateCustomThothApiUrl(trim($customThothApiUrl));
82+
}
83+
));
84+
4585
$this->addCheck(new FormValidatorCustom(
4686
$this,
4787
'token',
4888
'required',
4989
'plugins.generic.thoth.settings.invalidCredentials',
5090
function ($token) use ($form) {
51-
$testEnvironment = $this->getData('testEnvironment');
52-
5391
$httpConfig = [];
54-
if ($testEnvironment) {
55-
$httpConfig['base_uri'] = 'http://localhost:8000/';
92+
if ($this->getData('customThothApi') && $this->getData('customThothApiUrl')) {
93+
$httpConfig['base_uri'] = trim($this->getData('customThothApiUrl'));
5694
}
5795

5896
$client = new Client($httpConfig);
@@ -72,13 +110,20 @@ function ($token) use ($form) {
72110

73111
public function initData()
74112
{
113+
$encryption = new DataEncryption();
114+
75115
foreach (self::SETTINGS as $setting) {
76116
if ($setting == 'token') {
77-
$encryption = new DataEncryption();
78117
$token = $this->plugin->getSetting($this->contextId, $setting);
79-
$this->_data[$setting] = ($encryption->secretConfigExists() && $token) ?
80-
$encryption->decryptString($token) :
81-
null;
118+
if ($encryption->secretConfigExists() && $token) {
119+
try {
120+
$this->_data[$setting] = $encryption->decryptString($token);
121+
} catch (Exception $e) {
122+
$this->_data[$setting] = '';
123+
}
124+
} else {
125+
$this->_data[$setting] = null;
126+
}
82127
continue;
83128
}
84129
$this->_data[$setting] = $this->plugin->getSetting($this->contextId, $setting);
@@ -116,4 +161,18 @@ private function encryptToken()
116161
$this->setData('token', $encryptedToken);
117162
}
118163
}
164+
165+
private function validateCustomThothApiUrl($customThothApiUrl)
166+
{
167+
if (!$customThothApiUrl) {
168+
return false;
169+
}
170+
171+
try {
172+
(new Client(['base_uri' => $customThothApiUrl]))->publisherCount();
173+
return true;
174+
} catch (Exception $e) {
175+
return false;
176+
}
177+
}
119178
}

classes/api/ThothEndpoint.inc.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ public function register($slimRequest, $response, $args)
9898
$this->handleNotification($request, $submission, true, $disableNotification);
9999
} catch (QueryException $e) {
100100
$thothBookService->deleteRegisteredEntry();
101-
$this->handleNotification($request, $submission, false, $disableNotification, $e->getMessage());
101+
$this->handleNotification($request, $submission, false, $disableNotification, $e);
102102
$failure['errors'][] = __('plugins.generic.thoth.register.error.log', ['reason' => $e->getMessage()]);
103103
return $response->withStatus(403)->withJson($failure);
104104
}

classes/container/providers/ThothRepositoryProvider.inc.php

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626
import('plugins.generic.thoth.classes.factories.ThothLocationFactory');
2727
import('plugins.generic.thoth.classes.factories.ThothPublicationFactory');
2828
import('plugins.generic.thoth.classes.repositories.ThothAccountRepository');
29+
import('plugins.generic.thoth.classes.repositories.ThothAbstractRepository');
2930
import('plugins.generic.thoth.classes.repositories.ThothAffiliationRepository');
31+
import('plugins.generic.thoth.classes.repositories.ThothBiographyRepository');
3032
import('plugins.generic.thoth.classes.repositories.ThothBookRepository');
3133
import('plugins.generic.thoth.classes.repositories.ThothChapterRepository');
3234
import('plugins.generic.thoth.classes.repositories.ThothContributionRepository');
@@ -38,6 +40,7 @@
3840
import('plugins.generic.thoth.classes.repositories.ThothPublicationRepository');
3941
import('plugins.generic.thoth.classes.repositories.ThothReferenceRepository');
4042
import('plugins.generic.thoth.classes.repositories.ThothSubjectRepository');
43+
import('plugins.generic.thoth.classes.repositories.ThothTitleRepository');
4144
import('plugins.generic.thoth.classes.repositories.ThothWorkRelationRepository');
4245
import('plugins.generic.thoth.classes.repositories.ThothWorkRepository');
4346

@@ -50,21 +53,32 @@ public function register($container)
5053
$pluginSettingsDao = & DAORegistry::getDAO('PluginSettingsDAO');
5154
$contextId = Application::get()->getRequest()->getContext()->getId();
5255

53-
$testEnvironment = $pluginSettingsDao->getSetting($contextId, 'ThothPlugin', 'testEnvironment');
56+
$customThothApi = $pluginSettingsDao->getSetting($contextId, 'ThothPlugin', 'customThothApi');
57+
$customThothApiUrl = $pluginSettingsDao->getSetting($contextId, 'ThothPlugin', 'customThothApiUrl');
5458
$token = $pluginSettingsDao->getSetting($contextId, 'ThothPlugin', 'token') ?? '';
59+
$decryptedToken = '';
60+
61+
if ($token) {
62+
try {
63+
$decryptedToken = $encryption->decryptString($token);
64+
} catch (Exception $e) {
65+
$decryptedToken = '';
66+
}
67+
}
5568

5669
return [
57-
'testEnvironment' => $testEnvironment,
58-
'token' => $token ? $encryption->decryptString($token) : ''
70+
'customThothApi' => $customThothApi,
71+
'customThothApiUrl' => $customThothApiUrl,
72+
'token' => $decryptedToken
5973
];
6074
});
6175

6276
$container->set('client', function ($container) {
6377
$config = $container->get('config');
6478

6579
$httpConfig = [];
66-
if ($config['testEnvironment']) {
67-
$httpConfig['base_uri'] = 'http://localhost:8000/';
80+
if ($config['customThothApi'] && $config['customThothApiUrl']) {
81+
$httpConfig['base_uri'] = trim($config['customThothApiUrl']);
6882
}
6983

7084
$client = new Client($httpConfig);
@@ -75,6 +89,10 @@ public function register($container)
7589
return new ThothAccountRepository($container->get('client'));
7690
});
7791

92+
$container->set('abstractRepository', function ($container) {
93+
return new ThothAbstractRepository($container->get('client'));
94+
});
95+
7896
$container->set('affiliationRepository', function ($container) {
7997
return new ThothAffiliationRepository($container->get('client'));
8098
});
@@ -83,6 +101,10 @@ public function register($container)
83101
return new ThothBookRepository($container->get('client'));
84102
});
85103

104+
$container->set('biographyRepository', function ($container) {
105+
return new ThothBiographyRepository($container->get('client'));
106+
});
107+
86108
$container->set('chapterRepository', function ($container) {
87109
return new ThothChapterRepository($container->get('client'));
88110
});
@@ -123,6 +145,10 @@ public function register($container)
123145
return new ThothSubjectRepository($container->get('client'));
124146
});
125147

148+
$container->set('titleRepository', function ($container) {
149+
return new ThothTitleRepository($container->get('client'));
150+
});
151+
126152
$container->set('workRelationRepository', function ($container) {
127153
return new ThothWorkRelationRepository($container->get('client'));
128154
});

0 commit comments

Comments
 (0)