Skip to content

Commit 510a949

Browse files
Merge pull request #57 from lepidus/stable-3_4_0
Update thoth schema integration (OMP 3.4.0)
2 parents 07895b4 + e0f54ab commit 510a949

67 files changed

Lines changed: 2371 additions & 411 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.

.gitlab-ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ variables:
33

44
include:
55
- project: 'documentacao-e-tarefas/modelosparaintegracaocontinua'
6-
ref: main
6+
ref: stable-3_4_0
77
file:
88
- 'templates/groups/pkp_plugin.yml'
99
- 'templates/groups/omp/unit_tests.yml'

README.md

Lines changed: 36 additions & 25 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,9 +21,9 @@ 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

22-
This is required to use the API credentials provided, that are stored encrypted in the OMP database.
26+
This is required to store the Thoth personal access token encrypted in the OMP database.
2327

2428
## Installation
2529

@@ -31,52 +35,59 @@ This is required to use the API credentials provided, that are stored encrypted
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-
- **E-mail** and **Password**: Enter the credentials for a Thoth account to connect with the API.
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 of Data Fields
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
| ----------------- | ------------------ | - | ---------------------- | ------------------- | ----------- |

ThothSettingsForm.inc.php

Lines changed: 78 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ class ThothSettingsForm extends Form
3333
private $encryption;
3434

3535
private const SETTINGS = [
36-
'email',
37-
'password',
38-
'testEnvironment',
36+
'token',
37+
'customThothApi',
38+
'customThothApiUrl',
3939
];
4040

4141
public function __construct($plugin, $contextId)
@@ -50,21 +50,58 @@ public function __construct($plugin, $contextId)
5050
$form = $this;
5151
$this->addCheck(new FormValidatorCustom(
5252
$this,
53-
'password',
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+
90+
$this->addCheck(new FormValidatorCustom(
91+
$this,
92+
'token',
5493
'required',
5594
'plugins.generic.thoth.settings.invalidCredentials',
56-
function ($password) use ($form) {
57-
$email = trim($this->getData('email'));
58-
$testEnvironment = $this->getData('testEnvironment');
95+
function ($token) use ($form) {
5996
$httpConfig = [];
60-
if ($testEnvironment) {
61-
$httpConfig['base_uri'] = 'http://localhost:8000/';
97+
if ($this->getData('customThothApi') && $this->getData('customThothApiUrl')) {
98+
$httpConfig['base_uri'] = trim($this->getData('customThothApiUrl'));
6299
}
63100

64101
$client = new Client($httpConfig);
65102

66103
try {
67-
$client->login($email, $password);
104+
$client->setToken(trim($token))->me();
68105
} catch (QueryException $e) {
69106
return false;
70107
}
@@ -79,11 +116,17 @@ function ($password) use ($form) {
79116
public function initData()
80117
{
81118
foreach (self::SETTINGS as $setting) {
82-
if ($setting == 'password') {
83-
$password = $this->plugin->getSetting($this->contextId, $setting);
84-
$this->_data[$setting] = ($this->encryption->secretConfigExists() && $password) ?
85-
$this->encryption->decryptString($password) :
86-
null;
119+
if ($setting == 'token') {
120+
$token = $this->plugin->getSetting($this->contextId, $setting);
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+
}
87130
continue;
88131
}
89132
$this->_data[$setting] = $this->plugin->getSetting($this->contextId, $setting);
@@ -104,20 +147,34 @@ public function fetch($request, $template = null, $display = false)
104147

105148
public function execute(...$functionArgs)
106149
{
107-
$this->encryptPassword();
150+
$this->encryptToken();
108151
foreach (self::SETTINGS as $setting) {
109152
$this->plugin->updateSetting($this->contextId, $setting, trim($this->getData($setting)), 'string');
110153
}
111154
parent::execute(...$functionArgs);
112155
}
113156

114-
private function encryptPassword()
157+
private function encryptToken()
115158
{
116-
$password = $this->getData('password');
159+
$token = trim($this->getData('token'));
160+
161+
if (!$this->encryption->textIsEncrypted($token)) {
162+
$encryptedToken = $this->encryption->encryptString($token);
163+
$this->setData('token', $encryptedToken);
164+
}
165+
}
166+
167+
private function validateCustomThothApiUrl($customThothApiUrl)
168+
{
169+
if (!$customThothApiUrl) {
170+
return false;
171+
}
117172

118-
if (!$this->encryption->textIsEncrypted($password)) {
119-
$encryptedPassword = $this->encryption->encryptString($password);
120-
$this->setData('password', $encryptedPassword);
173+
try {
174+
(new Client(['base_uri' => $customThothApiUrl]))->publisherCount();
175+
return true;
176+
} catch (Exception $e) {
177+
return false;
121178
}
122179
}
123180
}

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: 33 additions & 9 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,33 +56,46 @@ public function register($container)
5356
$pluginSettingsDao = & DAORegistry::getDAO('PluginSettingsDAO');
5457
$contextId = Application::get()->getRequest()->getContext()->getId();
5558

56-
$testEnvironment = $pluginSettingsDao->getSetting($contextId, 'ThothPlugin', 'testEnvironment');
57-
$email = $pluginSettingsDao->getSetting($contextId, 'ThothPlugin', 'email');
58-
$password = $pluginSettingsDao->getSetting($contextId, 'ThothPlugin', 'password') ?? '';
59+
$customThothApi = $pluginSettingsDao->getSetting($contextId, 'ThothPlugin', 'customThothApi');
60+
$customThothApiUrl = $pluginSettingsDao->getSetting($contextId, 'ThothPlugin', 'customThothApiUrl');
61+
$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+
}
5971

6072
return [
61-
'testEnvironment' => $testEnvironment,
62-
'email' => $email,
63-
'password' => $encryption->decryptString($password)
73+
'customThothApi' => $customThothApi,
74+
'customThothApiUrl' => $customThothApiUrl,
75+
'token' => $decryptedToken
6476
];
6577
});
6678

6779
$container->set('client', function ($container) {
6880
$config = $container->get('config');
6981

7082
$httpConfig = [];
71-
if ($config['testEnvironment']) {
72-
$httpConfig['base_uri'] = 'http://localhost:8000/';
83+
if ($config['customThothApi'] && $config['customThothApiUrl']) {
84+
$httpConfig['base_uri'] = trim($config['customThothApiUrl']);
7385
}
7486

7587
$client = new Client($httpConfig);
76-
return $client->login($config['email'], $config['password']);
88+
return $client->setToken($config['token']);
7789
});
7890

7991
$container->set('accountRepository', function ($container) {
8092
return new ThothAccountRepository($container->get('client'));
8193
});
8294

95+
$container->set('abstractRepository', function ($container) {
96+
return new ThothAbstractRepository($container->get('client'));
97+
});
98+
8399
$container->set('affiliationRepository', function ($container) {
84100
return new ThothAffiliationRepository($container->get('client'));
85101
});
@@ -88,6 +104,10 @@ public function register($container)
88104
return new ThothBookRepository($container->get('client'));
89105
});
90106

107+
$container->set('biographyRepository', function ($container) {
108+
return new ThothBiographyRepository($container->get('client'));
109+
});
110+
91111
$container->set('chapterRepository', function ($container) {
92112
return new ThothChapterRepository($container->get('client'));
93113
});
@@ -128,6 +148,10 @@ public function register($container)
128148
return new ThothSubjectRepository($container->get('client'));
129149
});
130150

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

0 commit comments

Comments
 (0)