Plugins for Skosmos 3 - NatLibFi/Skosmos GitHub Wiki
It is possible to extend and customize Skosmos 3 through plugins written in JavaScript.
A plugin consists of a folder, with a unique name. The folder name is also used as the plugin name. Within the folder, there should be a plugin.json
file, which provides information about each plugin's resources. For example:
{
"js": ["widget.js"],
"css": ["stylesheet.css", "anotherfile.css"],
"callback": ["myCallback"]
}
The js resources are used to define JavaScript files used in the plugin. They are loaded for every page and are located near the bottom of HTML <body>
section, specified in scripts.inc.twig
.
The css resources are used to define CSS stylesheets used in the plugin. They are also loaded for each page and are located within the HTML <head>
section, specified in base-template.twig
.
The callback resources are used to define JavaScript functions that are called for each loaded page.
Template resources are no longer supported in Skosmos 3. Any templating is to be done within the JavaScript resources.
When you install Skosmos, there should be a folder in the root of the application, called "plugins". Inside this folder you can put your plugin folders. As explained in the previous section, the folder names are used as plugin names as well.
For each request, Skosmos loads the content of each enabled plugin's plugin.json
, and utilizes its resources, as explained in the previous section.
Plugins are enabled in the configuration file, config.ttl
. There are four ways to enable plugins. This is explained in the next section.
The first method for enabling plugins in Skosmos is through global configuration. This is done via the skosmos:globalPlugins
property in the config.ttl
configuration file.
:config a skosmos:Configuration ;
# ...
skosmos:globalPlugins ( "plugin1" "plugin2" "plugin3" ) .
This will initialize the plugins "plugin1", "plugin2", and "plugin3" for all Skosmos.
The second method to enable plugins is through vocabulary-specific configuration. This is done via the skosmos:vocabularyPlugins
property. This property is added to each skosmos:Vocabulary
, zero or multiple times.
Vocabulary-level plugin configuration has been simplified in Skosmos 3 compared to Skosmos 2, which had many overlapping mechanisms. The skosmos:hasParamPlugin
and skosmos:hasPlugin
configuration settings for vocabularies have been removed in Skosmos 3. Instead, skosmos:vocabularyPlugins
is the only vocabulary-level configuration property.
:ysa a skosmos:Vocabulary, void:Dataset ;
dc:title "YSA - Yleinen suomalainen asiasanasto"@fi,
"YSA - Allmän tesaurus på finska"@sv,
"YSA - General Finnish thesaurus"@en ;
# ...
void:sparqlEndpoint <http://api.dev.finto.fi/sparql> ;
skosmos:vocabularyPlugins ( "plugin1" "plugin2" ) ;
skosmos:sparqlGraph <http://www.yso.fi/onto/ysa/>
.
This will initialize plugins "plugin1" and "plugin2" for the vocabulary ysa
. These plugins will appear only on the pages of the vocabularies for which they were enabled via the skosmos:vocabularyPlugins
property.
The skosmos:vocabularyPlugins
property determines both the enabling and ordering of vocabulary-specific and global plugins. The list can contain either plugin names as plain literals or references to parameterized plugin resources (see below). The order in the list determines the execution and rendering order of the plugins.
It is possible to include parameterized plugin resources in the skosmos:vocabularyPlugins
list. These resources must be of type skosmos:ParameterizedPlugin
and contain a skosmos:usePlugin
property to identify the plugin, along with skosmos:parameters
containing the plugin-specific configuration.
:ysa a skosmos:Vocabulary, void:Dataset ;
dc:title "YSA - Yleinen suomalainen asiasanasto"@fi,
"YSA - Allmän tesaurus på finska"@sv,
"YSA - General Finnish thesaurus"@en ;
# ...
void:sparqlEndpoint <http://api.dev.finto.fi/sparql> ;
skosmos:vocabularyPlugins ( "plugin2" :messageWidget "plugin1" ) ;
skosmos:sparqlGraph <http://www.yso.fi/onto/ysa/>
.
:messageWidget a skosmos:ParameterizedPlugin ;
skosmos:usePlugin "awesome-message-widget";
skosmos:parameters [
a schema:PropertyValue ;
schema:propertyID "msg";
Schema:value "Message in Finnish"@fi, "Message in Swedish"@sv, "Default message without language code";
] ,
[
a schema:PropertyValue ;
schema:propertyID "color" ;
schema:value "#800000" ;
] ,
[
a schema:PropertyValue ;
schema:propertyID "imageUrl" ;
schema:value "http://example.com/media/unicorn.png" ;
] .
This way it is possible to pass parameters to a plugin. The plugin is identified by skosmos:usePlugin
determined for the :messageWidget
resource. Each parameter is identified by schema:propertyID
. The parameters are accessible through the global window.SKOSMOS.pluginParameters
JavaScript object. It is up to the plugin to interpret these parameters. An example of this can be seen here.
It is recommended to prepare a backup of your installation before installing plugins (as well as updating, patching, etc), or to use a testbed server. You may also want to enable exceptions logging, as well as log to a file, and monitor both the Skosmos log file, as well as the web application and system logs for any unexpected errors.
Finally, as some resources load JavaScript and CSS files, comparing the browser console before and after installing a plugin can also be very helpful.
This section presents two simple examples of adding plugins to Skosmos.
This example plugin is implemented using vanilla JavaScript with no external dependencies. It displays a message below the content in the header of vocabulary home pages. Plugins can be inserted anywhere on the page but Skosmos 3 offers several designated slots for plugins:
-
#headerbar-top-slot
above the header bar -
#headerbar-bottom-slot
below the header bar -
#main-content-top-slot
above main content on vocabulary home page and concept page -
#main-content-bottom-slot
below main content on vocabulary home page and concept page
First, create a folder with the name "vanilla-js-plugin" under the Skosmos/plugins
folder. Also add the plugin.json
file, with the following content:
{
"js": ["widget.js"],
"css": ["stylesheet.css"],
"callback": ["vanillaJSCallback"]
}
Now we will go through each resource file used in this plugin. First, the JavaScript file widget.js
:
const VANILLA_JS_PLUGIN = {
message: 'Vanilla JS plugin',
template: function () {
return `<p id="vanilla-js-plugin-message">${ this.message }</p>`
},
render: function() {
// Check if the plugin already exists
const existingPlugin = document.getElementById('vanilla-js-plugin')
if (existingPlugin) {
// Remove the existing plugin
existingPlugin.remove()
}
// Create a new div for the plugin and add the template to it
const newPlugin = document.createElement('div')
newPlugin.id = 'vanilla-js-plugin'
newPlugin.innerHTML = this.template()
// Add the new plugin to the DOM
document.getElementById('headerbar-bottom-slot').appendChild(newPlugin)
},
remove: function() {
// Remove the plugin if it exists
const existingPlugin = document.getElementById('vanilla-js-plugin')
if (existingPlugin) {
existingPlugin.remove()
}
}
}
document.addEventListener('DOMContentLoaded', function() {
// Avoid polluting global namespace, or use meaningful and less likely to collide names
window.vanillaJSCallback = function(params) {
console.log('This is executed for each page, once loaded')
if (params.pageType == 'vocab-home') {
// Render plugin on the vocabulary home page
VANILLA_JS_PLUGIN.render()
} else {
// Remove plugin on any other page
VANILLA_JS_PLUGIN.remove()
}
}
})
The code above contains the callback definition near the end, where we define the global vanillaJSCallback
function. Note that it matches exactly the name of the callback declared in plugin.json
. It will be executed for each page that is loaded. Skosmos offers the current uri
, prefLabels
, pageType
and embedded JsonLd
data as parameters for callback functions.
The code is used to render a simple template defined in the template
property of the VANILLA_JS_PLUGIN
namespace object. You can also include an external templating language as a dependency for more complex applications.
Finally, the stylesheet file:
/*
* Be aware values here may impact other parts of the template. Use
* good names, that won't affect other plugins or the rest of Skosmos.
* We recommend using a common prefix for all ids and class names of a plugin
*/
#vanilla-js-plugin-message {
color: blue;
}
Putting it all together, the plugin is complete. We just need to enable it. Modify the config.ttl
file, making sure the globalPlugins
property loads the plugin.
:config a skosmos:Configuration ;
# ...
skosmos:globalPlugins ( "vanilla-js-plugin" ) .
Load Skosmos, and you should now have a custom message being displayed on all vocabulary home pages, as well as the callback message in the browser console.
Skosmos 3 offers native support for plugins utilizing the Vue JavaScript framework. This example is implemented using Vue 3 and it displays a message in the #headerbar-bottom-slot
dedicated plugin slot on concept pages.
First, create a folder with the name "vue-plugin" under the Skosmos/plugins
folder. Also add the plugin.json
file, with the following content:
{
"js": ["widget.js"],
"css": ["stylesheet.css"],
"callback": ["vueCallback"]
}
Now we will go through each resource file used in this plugin. First, the JavaScript file widget.js
:
const VUE_PLUGIN = {
vueApp: null,
createVueApp: function() {
return Vue.createApp({
data() {
return {
message: 'Vue plugin'
}
},
template: `<p id="vue-plugin-message">{{ message }}</p>`
})
},
render: function() {
// Check if a mount point already exists
const mountPoint = document.getElementById('vue-plugin')
if (mountPoint) {
// Unmount the Vue app if it exists
if (this.vueApp) {
this.vueApp.unmount()
}
// Remove the old mount point div
mountPoint.remove()
}
// Create a new div for the Vue app
const newMountPoint = document.createElement('div')
newMountPoint.id = 'vue-plugin'
document.getElementById('headerbar-bottom-slot').appendChild(newMountPoint)
// Create a new Vue app instance and mount it to the new mount point
this.vueApp = this.createVueApp()
this.vueApp.mount('#vue-plugin')
},
remove: function() {
// Unmount and remove the vue app if it exists
if (this.vueApp) {
this.vueApp.unmount()
this.vueApp = null
}
}
}
document.addEventListener('DOMContentLoaded', function() {
// Avoid polluting global namespace, or use meaningful and less likely to collide names
window.vueCallback = function(params) {
if (params.pageType == 'concept') {
// Render plugin on the concept page
VUE_PLUGIN.render()
} else {
// Remove plugin on any other page
VUE_PLUGIN.remove()
}
}
})
The code above contains the callback definition near the end, where we define the global vueCallback
function. Note that it matches exactly the name of the callback declared in plugin.json
. It will be executed for each page that is loaded. Skosmos offers the current uri
, prefLabels
, pageType
and embedded JsonLd
data as parameters for callback functions.
Finally, the stylesheet file:
/*
* Be aware values here may impact other parts of the template. Use
* good names, that won't affect other plugins or the rest of Skosmos.
* We recommend using a common prefix for all ids and class names of a plugin
*/
#vue-plugin-message {
color: red;
}
Putting it all together, the plugin is complete. We just need to enable it. Modify the config.ttl
file, making sure the globalPlugins
property loads the plugin.
:config a skosmos:Configuration ;
# ...
skosmos:globalPlugins ( "vue-plugin" ) .
Load Skosmos, and you should now have a custom message being displayed on all concept pages, as well as the callback message in the browser console.
Search for Skosmos plugins created at NatLibFi (they are all named according to the pattern Skosmos-widget-*)