Skip to content

Commit e0f54ab

Browse files
committed
Merge branch 'update_thoth_schema-3_4_0-i1176' into 'stable-3_4_0'
Update thoth schema integration (3.4.0) See merge request softwares-pkp/plugins_ojs/thoth-omp-plugin!102
2 parents d2f4461 + 294db66 commit e0f54ab

File tree

66 files changed

+2322
-366
lines changed

Some content is hidden

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

66 files changed

+2322
-366
lines changed

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-
Enables metadata exchange between OMP and Thoth Open Metadata to facilitate easy transfer and subsequent enhancement of book- and chapter-level data in the free-to-use self-service [Thoth](https://thoth.pub/) metadata platform, enabling OMP users to generate metadata in multiple book-specific formats including ONIX, MARC, KBART, Crossref XML, etc.
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_%7C_3.4-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

@@ -17,7 +21,7 @@ This plugin is compatible with the following PKP applications:
1721

1822
1. **api_key_secret**
1923

20-
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)).
24+
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)).
2125

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

@@ -31,52 +35,59 @@ This is required to store the Thoth personal access token encrypted in the OMP d
3135

3236
## Usage
3337

34-
### Guidelines
38+
### Configuration
3539

36-
- Only basic HTML tags are preserved (`<strong>`, `<mark>`, `<em>`, `<i>`, `<u>`, `<sup>`, `<sub>`, `<ul>`, `<ol>` and `<li>`); all others will be removed
37-
- ISBN must be properly formatted (e.g., 978-3-16-148410-0).
38-
- 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.
40+
After enabling the plugin, go to the plugin settings and fill in:
3941

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

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

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

47-
![settings](/images/settings.png)
50+
#### Unpublished Monographs
4851

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

51-
- **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.
54+
<img src="/docs/images/register_field.png" alt="Publish modal with Thoth registration option" width="700">
5255

53-
![publish](/images/publish.png)
56+
#### Published Monographs
5457

55-
- **Published Monographs**: Register metadata for published monographs by using the 'Register' button next to the publication status.
58+
Register metadata for already-published monographs by using the 'Register' button next to the publication status.
5659

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

6063
### Updating Metadata
6164

62-
To update metadata in Thoth, unpublish the monograph, edit the data, and the changes will be automatically updated in Thoth.
65+
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.
66+
67+
It is also possible to manually update the metadata in Thoth by clicking the 'Update Metadata' button next to the publication status.
6368

6469
### Accessing Thoth Book Records
6570

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

68-
![link](/images/link.png)
75+
### Bulk Registration
6976

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

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

74-
![page](/images/page.png)
83+
- 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.
84+
- ISBN must be properly formatted as ISBN-13 (e.g., `978-3-16-148410-0`).
85+
- To avoid incorrect affiliation assignment in Thoth, use the [ROR plugin](https://github.com/withanage/ror) to populate affiliations in OMP.
7586

7687
## OMP-Thoth Mapping
7788

7889
<details>
79-
<summary>Click here to see the data relationship between Thoth and OMP</summary>
90+
<summary>Click here to see the data relationship between OMP and Thoth</summary>
8091

8192
| OMP | | | Thoth | | |
8293
| ----------------- | ------------------ | - | ---------------------- | ------------------- | ----------- |
@@ -133,6 +144,6 @@ Developed by [Lepidus Tecnologia](https://github.com/lepidus).
133144

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

136-
Copyright (c) 2024 Lepidus Tecnologia
147+
Copyright (c) 2024-2026 Lepidus Tecnologia
137148

138-
Copyright (c) 2024 Thoth
149+
Copyright (c) 2024-2026 Thoth Open Metadata

ThothSettingsForm.inc.php

Lines changed: 66 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ class ThothSettingsForm extends Form
3434

3535
private const SETTINGS = [
3636
'token',
37-
'testEnvironment',
37+
'customThothApi',
38+
'customThothApiUrl',
3839
];
3940

4041
public function __construct($plugin, $contextId)
@@ -47,16 +48,54 @@ public function __construct($plugin, $contextId)
4748
parent::__construct($plugin->getTemplateResource($template));
4849

4950
$form = $this;
51+
$this->addCheck(new FormValidatorCustom(
52+
$this,
53+
'customThothApiUrl',
54+
'required',
55+
'plugins.generic.thoth.settings.customThothApiUrl.required',
56+
function ($customThothApiUrl) {
57+
if (!$this->getData('customThothApi')) {
58+
return true;
59+
}
60+
return !empty(trim($customThothApiUrl));
61+
}
62+
));
63+
64+
$this->addCheck(new FormValidatorCustom(
65+
$this,
66+
'customThothApiUrl',
67+
'optional',
68+
'plugins.generic.thoth.settings.customThothApiUrl.invalid',
69+
function ($customThothApiUrl) {
70+
if (!$this->getData('customThothApi') || !trim($customThothApiUrl)) {
71+
return true;
72+
}
73+
return filter_var(trim($customThothApiUrl), FILTER_VALIDATE_URL) !== false;
74+
}
75+
));
76+
77+
$this->addCheck(new FormValidatorCustom(
78+
$this,
79+
'customThothApiUrl',
80+
'optional',
81+
'plugins.generic.thoth.settings.customThothApiUrl.unreachable',
82+
function ($customThothApiUrl) {
83+
if (!$this->getData('customThothApi')) {
84+
return true;
85+
}
86+
return $this->validateCustomThothApiUrl(trim($customThothApiUrl));
87+
}
88+
));
89+
5090
$this->addCheck(new FormValidatorCustom(
5191
$this,
5292
'token',
5393
'required',
5494
'plugins.generic.thoth.settings.invalidCredentials',
5595
function ($token) use ($form) {
56-
$testEnvironment = $this->getData('testEnvironment');
5796
$httpConfig = [];
58-
if ($testEnvironment) {
59-
$httpConfig['base_uri'] = 'http://localhost:8000/';
97+
if ($this->getData('customThothApi') && $this->getData('customThothApiUrl')) {
98+
$httpConfig['base_uri'] = trim($this->getData('customThothApiUrl'));
6099
}
61100

62101
$client = new Client($httpConfig);
@@ -79,9 +118,15 @@ public function initData()
79118
foreach (self::SETTINGS as $setting) {
80119
if ($setting == 'token') {
81120
$token = $this->plugin->getSetting($this->contextId, $setting);
82-
$this->_data[$setting] = ($this->encryption->secretConfigExists() && $token) ?
83-
$this->encryption->decryptString($token) :
84-
null;
121+
if ($this->encryption->secretConfigExists() && $token) {
122+
try {
123+
$this->_data[$setting] = $this->encryption->decryptString($token);
124+
} catch (Exception $e) {
125+
$this->_data[$setting] = '';
126+
}
127+
} else {
128+
$this->_data[$setting] = null;
129+
}
85130
continue;
86131
}
87132
$this->_data[$setting] = $this->plugin->getSetting($this->contextId, $setting);
@@ -118,4 +163,18 @@ private function encryptToken()
118163
$this->setData('token', $encryptedToken);
119164
}
120165
}
166+
167+
private function validateCustomThothApiUrl($customThothApiUrl)
168+
{
169+
if (!$customThothApiUrl) {
170+
return false;
171+
}
172+
173+
try {
174+
(new Client(['base_uri' => $customThothApiUrl]))->publisherCount();
175+
return true;
176+
} catch (Exception $e) {
177+
return false;
178+
}
179+
}
121180
}

classes/api/ThothEndpoint.inc.php

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

classes/container/providers/ThothRepositoryProvider.inc.php

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@
2929
import('plugins.generic.thoth.classes.factories.ThothLocationFactory');
3030
import('plugins.generic.thoth.classes.factories.ThothPublicationFactory');
3131
import('plugins.generic.thoth.classes.repositories.ThothAccountRepository');
32+
import('plugins.generic.thoth.classes.repositories.ThothAbstractRepository');
3233
import('plugins.generic.thoth.classes.repositories.ThothAffiliationRepository');
34+
import('plugins.generic.thoth.classes.repositories.ThothBiographyRepository');
3335
import('plugins.generic.thoth.classes.repositories.ThothBookRepository');
3436
import('plugins.generic.thoth.classes.repositories.ThothChapterRepository');
3537
import('plugins.generic.thoth.classes.repositories.ThothContributionRepository');
@@ -41,6 +43,7 @@
4143
import('plugins.generic.thoth.classes.repositories.ThothPublicationRepository');
4244
import('plugins.generic.thoth.classes.repositories.ThothReferenceRepository');
4345
import('plugins.generic.thoth.classes.repositories.ThothSubjectRepository');
46+
import('plugins.generic.thoth.classes.repositories.ThothTitleRepository');
4447
import('plugins.generic.thoth.classes.repositories.ThothWorkRelationRepository');
4548
import('plugins.generic.thoth.classes.repositories.ThothWorkRepository');
4649

@@ -53,21 +56,32 @@ public function register($container)
5356
$pluginSettingsDao = & DAORegistry::getDAO('PluginSettingsDAO');
5457
$contextId = Application::get()->getRequest()->getContext()->getId();
5558

56-
$testEnvironment = $pluginSettingsDao->getSetting($contextId, 'ThothPlugin', 'testEnvironment');
59+
$customThothApi = $pluginSettingsDao->getSetting($contextId, 'ThothPlugin', 'customThothApi');
60+
$customThothApiUrl = $pluginSettingsDao->getSetting($contextId, 'ThothPlugin', 'customThothApiUrl');
5761
$token = $pluginSettingsDao->getSetting($contextId, 'ThothPlugin', 'token') ?? '';
62+
$decryptedToken = '';
63+
64+
if ($token) {
65+
try {
66+
$decryptedToken = $encryption->decryptString($token);
67+
} catch (Exception $e) {
68+
$decryptedToken = '';
69+
}
70+
}
5871

5972
return [
60-
'testEnvironment' => $testEnvironment,
61-
'token' => $token ? $encryption->decryptString($token) : ''
73+
'customThothApi' => $customThothApi,
74+
'customThothApiUrl' => $customThothApiUrl,
75+
'token' => $decryptedToken
6276
];
6377
});
6478

6579
$container->set('client', function ($container) {
6680
$config = $container->get('config');
6781

6882
$httpConfig = [];
69-
if ($config['testEnvironment']) {
70-
$httpConfig['base_uri'] = 'http://localhost:8000/';
83+
if ($config['customThothApi'] && $config['customThothApiUrl']) {
84+
$httpConfig['base_uri'] = trim($config['customThothApiUrl']);
7185
}
7286

7387
$client = new Client($httpConfig);
@@ -78,6 +92,10 @@ public function register($container)
7892
return new ThothAccountRepository($container->get('client'));
7993
});
8094

95+
$container->set('abstractRepository', function ($container) {
96+
return new ThothAbstractRepository($container->get('client'));
97+
});
98+
8199
$container->set('affiliationRepository', function ($container) {
82100
return new ThothAffiliationRepository($container->get('client'));
83101
});
@@ -86,6 +104,10 @@ public function register($container)
86104
return new ThothBookRepository($container->get('client'));
87105
});
88106

107+
$container->set('biographyRepository', function ($container) {
108+
return new ThothBiographyRepository($container->get('client'));
109+
});
110+
89111
$container->set('chapterRepository', function ($container) {
90112
return new ThothChapterRepository($container->get('client'));
91113
});
@@ -126,6 +148,10 @@ public function register($container)
126148
return new ThothSubjectRepository($container->get('client'));
127149
});
128150

151+
$container->set('titleRepository', function ($container) {
152+
return new ThothTitleRepository($container->get('client'));
153+
});
154+
129155
$container->set('workRelationRepository', function ($container) {
130156
return new ThothWorkRelationRepository($container->get('client'));
131157
});

0 commit comments

Comments
 (0)