From 8b88ecd2e6848d9200b102b5143500b660412381 Mon Sep 17 00:00:00 2001 From: seth-shaw-asu <108362375+seth-shaw-asu@users.noreply.github.com> Date: Wed, 28 Aug 2024 17:21:28 +0000 Subject: [PATCH] =?UTF-8?q?Deploying=20to=20gh-pages=20from=20@=20Islandor?= =?UTF-8?q?a/documentation@920261aca4d43502f6a6acf7a25eb893fa408112=20?= =?UTF-8?q?=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alpaca/alpaca-technical-stack/index.html | 2 +- concepts/collection/index.html | 2 +- concepts/derivatives/index.html | 2 +- concepts/node-concepts/index.html | 2 +- concepts/node-media/index.html | 2 +- concepts/rdf/index.html | 2 +- concepts/starter-site/index.html | 2 +- contributing/CONTRIBUTING/index.html | 2 +- contributing/committers/index.html | 2 +- contributing/contributing-workflow/index.html | 2 +- contributing/create-issues/index.html | 2 +- contributing/docs-style-guide/index.html | 2 +- contributing/editing-docs/index.html | 2 +- contributing/readme-template/index.html | 2 +- contributing/releasing-islandora/index.html | 2 +- contributing/sandbox/index.html | 2 +- .../testing-a-pull-request/index.html | 2 +- index.html | 2 +- installation/component-overview/index.html | 2 +- installation/docker/converting/index.html | 2 +- .../docker/docker-introduction/index.html | 2 +- installation/docker/docker-prereq/index.html | 2 +- .../docker-available-commands/index.html | 2 +- .../docker-available-configuration/index.html | 2 +- .../isle-dc/docker-basic-usage/index.html | 2 +- .../docker/isle-dc/docker-custom/index.html | 2 +- .../docker/isle-dc/docker-local/index.html | 2 +- .../isle-dc/docker-maintain-drupal/index.html | 2 +- .../isle-dc/docker-maintain-isle/index.html | 2 +- .../isle-dc/docker-troubleshooting/index.html | 2 +- .../docker/site-template/backup/index.html | 2 +- .../docker-modifications/index.html | 2 +- .../docker/site-template/setup/index.html | 2 +- .../site-template/site-template/index.html | 2 +- .../docker/site-template/updating/index.html | 2 +- installation/install-a-demo/index.html | 2 +- .../manual/configuring-drupal/index.html | 2 +- .../manual/installing-alpaca/index.html | 2 +- .../index.html | 2 +- .../manual/installing-crayfish/index.html | 2 +- .../index.html | 2 +- .../manual/installing-solr/index.html | 2 +- .../index.html | 2 +- installation/manual/introduction/index.html | 2 +- .../manual/preparing-a-webserver/index.html | 2 +- installation/playbook/index.html | 2 +- installation/quickstart/index.html | 2 +- models/audio/index.html | 2 +- models/video/index.html | 2 +- placeholder/index.html | 2 +- release_notes/8.x-2.0/index.html | 2 +- search/search_index.json | 2 +- sitemap.xml | 224 +++++++++--------- sitemap.xml.gz | Bin 1317 -> 1316 bytes .../adding-format-jsonld/index.html | 2 +- .../alpaca-tips/index.html | 2 +- .../checking-coding-standards/index.html | 2 +- technical-documentation/diagram/index.html | 2 +- technical-documentation/docs-build/index.html | 2 +- .../install-enable-drupal-modules/index.html | 2 +- technical-documentation/migrate-7x/index.html | 2 +- .../migrate-csv/index.html | 2 +- .../migration-islandora-workbench/index.html | 2 +- .../migration-migrate-api/index.html | 2 +- .../migration-overview/index.html | 2 +- .../migration-rest-api/index.html | 2 +- .../ppa-documentation/index.html | 2 +- .../resizing-vm/index.html | 2 +- .../rest-authorization/index.html | 2 +- .../rest-create/index.html | 2 +- .../rest-delete/index.html | 2 +- technical-documentation/rest-get/index.html | 2 +- technical-documentation/rest-patch/index.html | 2 +- .../rest-signposting/index.html | 2 +- .../running-automated-tests/index.html | 2 +- .../testing-notes/index.html | 2 +- .../updating-drupal/index.html | 2 +- .../using-rest-endpoints/index.html | 2 +- technical-documentation/versioning/index.html | 2 +- tutorials/blocks/index.html | 2 +- tutorials/create-a-resource-node/index.html | 2 +- tutorials/create-update-views/index.html | 2 +- tutorials/how-to-create-collection/index.html | 2 +- tutorials/switch-homepage-to-twig/index.html | 2 +- user-documentation/access-control/index.html | 2 +- user-documentation/accessibility/index.html | 2 +- user-documentation/advanced-search/index.html | 2 +- user-documentation/breadcrumbs/index.html | 2 +- user-documentation/content-models/index.html | 2 +- user-documentation/content-types/index.html | 2 +- user-documentation/context/index.html | 2 +- user-documentation/extending/index.html | 8 +- user-documentation/faceting/index.html | 2 +- user-documentation/file-viewers/index.html | 2 +- user-documentation/flysystem/index.html | 2 +- user-documentation/glossary/index.html | 2 +- user-documentation/iiif/index.html | 2 +- .../jwt-authentication/index.html | 2 +- user-documentation/linked-data/index.html | 2 +- user-documentation/media/index.html | 2 +- .../metadata-harvesting/index.html | 2 +- user-documentation/metadata/index.html | 2 +- user-documentation/multilingual/index.html | 2 +- user-documentation/paged-content/index.html | 2 +- .../recipes/alexa-search/index.html | 2 +- user-documentation/searching/index.html | 2 +- .../index.html | 2 +- user-documentation/transcripts/index.html | 2 +- .../uploading-large-files/index.html | 2 +- user-documentation/url-aliases/index.html | 2 +- user-documentation/usage-stats/index.html | 2 +- user-documentation/user-intro/index.html | 2 +- user-documentation/users/index.html | 2 +- user-documentation/versioning/index.html | 2 +- user-documentation/video-docs/index.html | 2 +- 115 files changed, 229 insertions(+), 227 deletions(-) diff --git a/alpaca/alpaca-technical-stack/index.html b/alpaca/alpaca-technical-stack/index.html index 747e25df0..c81299e1c 100644 --- a/alpaca/alpaca-technical-stack/index.html +++ b/alpaca/alpaca-technical-stack/index.html @@ -2720,7 +2720,7 @@

ReferencesAugust 26, 2024 + August 28, 2024 diff --git a/concepts/collection/index.html b/concepts/collection/index.html index 53e3aa51c..a396e8d84 100644 --- a/concepts/collection/index.html +++ b/concepts/collection/index.html @@ -2733,7 +2733,7 @@

Permissions on a CollectionAugust 26, 2024 + August 28, 2024 diff --git a/concepts/derivatives/index.html b/concepts/derivatives/index.html index 7e1b05081..5d843dd02 100644 --- a/concepts/derivatives/index.html +++ b/concepts/derivatives/index.html @@ -2859,7 +2859,7 @@

Derivative Swimlane DiagramAugust 26, 2024 + August 28, 2024 diff --git a/concepts/node-concepts/index.html b/concepts/node-concepts/index.html index baaa2f008..05d0cbc89 100644 --- a/concepts/node-concepts/index.html +++ b/concepts/node-concepts/index.html @@ -2762,7 +2762,7 @@

Nodes are attached to MediaAugust 26, 2024 + August 28, 2024 diff --git a/concepts/node-media/index.html b/concepts/node-media/index.html index 4555ee226..0bad3511d 100644 --- a/concepts/node-media/index.html +++ b/concepts/node-media/index.html @@ -2761,7 +2761,7 @@

Media have different usesAugust 26, 2024 + August 28, 2024 diff --git a/concepts/rdf/index.html b/concepts/rdf/index.html index fc8863e9c..cffe941fd 100644 --- a/concepts/rdf/index.html +++ b/concepts/rdf/index.html @@ -2712,7 +2712,7 @@

Syncing to Fedora and Blazegraph Last update: - August 26, 2024 + August 28, 2024 diff --git a/concepts/starter-site/index.html b/concepts/starter-site/index.html index f191e104d..bc309214c 100644 --- a/concepts/starter-site/index.html +++ b/concepts/starter-site/index.html @@ -2598,7 +2598,7 @@

Islandora Starter Site Last update: - August 26, 2024 + August 28, 2024 diff --git a/contributing/CONTRIBUTING/index.html b/contributing/CONTRIBUTING/index.html index 439cc7789..90daf8826 100644 --- a/contributing/CONTRIBUTING/index.html +++ b/contributing/CONTRIBUTING/index.html @@ -2738,7 +2738,7 @@

Contributor License AgreementsAugust 26, 2024 + August 28, 2024 diff --git a/contributing/committers/index.html b/contributing/committers/index.html index 649663695..016675976 100644 --- a/contributing/committers/index.html +++ b/contributing/committers/index.html @@ -2995,7 +2995,7 @@

Process for inducting new Committe Last update: - August 26, 2024 + August 28, 2024 diff --git a/contributing/contributing-workflow/index.html b/contributing/contributing-workflow/index.html index 69f8907f4..6abbac4a8 100644 --- a/contributing/contributing-workflow/index.html +++ b/contributing/contributing-workflow/index.html @@ -2736,7 +2736,7 @@

Development Workflow:August 26, 2024 + August 28, 2024 diff --git a/contributing/create-issues/index.html b/contributing/create-issues/index.html index 9cd7eccf5..bf7e37a73 100644 --- a/contributing/create-issues/index.html +++ b/contributing/create-issues/index.html @@ -2691,7 +2691,7 @@

How to create an is Last update: - August 26, 2024 + August 28, 2024 diff --git a/contributing/docs-style-guide/index.html b/contributing/docs-style-guide/index.html index 119007b9a..538db3565 100644 --- a/contributing/docs-style-guide/index.html +++ b/contributing/docs-style-guide/index.html @@ -2705,7 +2705,7 @@

Don'ts Last update: - August 26, 2024 + August 28, 2024 diff --git a/contributing/editing-docs/index.html b/contributing/editing-docs/index.html index c0db94f66..98395c02f 100644 --- a/contributing/editing-docs/index.html +++ b/contributing/editing-docs/index.html @@ -2747,7 +2747,7 @@

How to edit docume Last update: - August 26, 2024 + August 28, 2024 diff --git a/contributing/readme-template/index.html b/contributing/readme-template/index.html index e0f2960fe..c184f0193 100644 --- a/contributing/readme-template/index.html +++ b/contributing/readme-template/index.html @@ -2651,7 +2651,7 @@ Last update: - August 26, 2024 + August 28, 2024 diff --git a/contributing/releasing-islandora/index.html b/contributing/releasing-islandora/index.html index 629a10457..642217f6c 100644 --- a/contributing/releasing-islandora/index.html +++ b/contributing/releasing-islandora/index.html @@ -3093,7 +3093,7 @@

Undoing a ReleaseAugust 26, 2024 + August 28, 2024 diff --git a/contributing/sandbox/index.html b/contributing/sandbox/index.html index 337546db1..8c46eed55 100644 --- a/contributing/sandbox/index.html +++ b/contributing/sandbox/index.html @@ -2662,7 +2662,7 @@

Maintaining the back-end containers Last update: - August 26, 2024 + August 28, 2024 diff --git a/contributing/testing-a-pull-request/index.html b/contributing/testing-a-pull-request/index.html index 5fa583d64..8b9eb2b77 100644 --- a/contributing/testing-a-pull-request/index.html +++ b/contributing/testing-a-pull-request/index.html @@ -2993,7 +2993,7 @@

... using source repositoriesAugust 26, 2024 + August 28, 2024 diff --git a/index.html b/index.html index 6f96f55b9..35616b227 100644 --- a/index.html +++ b/index.html @@ -2667,7 +2667,7 @@

Join the CommunityAugust 26, 2024 + August 28, 2024 diff --git a/installation/component-overview/index.html b/installation/component-overview/index.html index 763032c66..d6ba13699 100644 --- a/installation/component-overview/index.html +++ b/installation/component-overview/index.html @@ -2767,7 +2767,7 @@

Finalized Drupal Configurations Last update: - August 26, 2024 + August 28, 2024 diff --git a/installation/docker/converting/index.html b/installation/docker/converting/index.html index 99c71f099..ed08f9c1c 100644 --- a/installation/docker/converting/index.html +++ b/installation/docker/converting/index.html @@ -2811,7 +2811,7 @@

Other CustomizationsAugust 26, 2024 + August 28, 2024 diff --git a/installation/docker/docker-introduction/index.html b/installation/docker/docker-introduction/index.html index 0e5bbfd52..dc3eb579b 100644 --- a/installation/docker/docker-introduction/index.html +++ b/installation/docker/docker-introduction/index.html @@ -2669,7 +2669,7 @@

Where is ISLE? Last update: - August 26, 2024 + August 28, 2024 diff --git a/installation/docker/docker-prereq/index.html b/installation/docker/docker-prereq/index.html index 3c56dc86a..e3256b3f4 100644 --- a/installation/docker/docker-prereq/index.html +++ b/installation/docker/docker-prereq/index.html @@ -2888,7 +2888,7 @@

Installing DockerAugust 26, 2024 + August 28, 2024 diff --git a/installation/docker/isle-dc/docker-available-commands/index.html b/installation/docker/isle-dc/docker-available-commands/index.html index e2a9fa7e8..a64128217 100644 --- a/installation/docker/isle-dc/docker-available-commands/index.html +++ b/installation/docker/isle-dc/docker-available-commands/index.html @@ -2815,7 +2815,7 @@

Reindex the Triplestore Last update: - August 26, 2024 + August 28, 2024 diff --git a/installation/docker/isle-dc/docker-available-configuration/index.html b/installation/docker/isle-dc/docker-available-configuration/index.html index 3d3ebe7ce..a4d966166 100644 --- a/installation/docker/isle-dc/docker-available-configuration/index.html +++ b/installation/docker/isle-dc/docker-available-configuration/index.html @@ -3035,7 +3035,7 @@

FEDORA_6August 26, 2024 + August 28, 2024 diff --git a/installation/docker/isle-dc/docker-basic-usage/index.html b/installation/docker/isle-dc/docker-basic-usage/index.html index 9979ca805..c7132c720 100644 --- a/installation/docker/isle-dc/docker-basic-usage/index.html +++ b/installation/docker/isle-dc/docker-basic-usage/index.html @@ -2869,7 +2869,7 @@

Tailing Logs Last update: - August 26, 2024 + August 28, 2024 diff --git a/installation/docker/isle-dc/docker-custom/index.html b/installation/docker/isle-dc/docker-custom/index.html index 094445fe2..e745b5976 100644 --- a/installation/docker/isle-dc/docker-custom/index.html +++ b/installation/docker/isle-dc/docker-custom/index.html @@ -2914,7 +2914,7 @@

Reque Last update: - August 26, 2024 + August 28, 2024 diff --git a/installation/docker/isle-dc/docker-local/index.html b/installation/docker/isle-dc/docker-local/index.html index a620ba3de..7f477e66c 100644 --- a/installation/docker/isle-dc/docker-local/index.html +++ b/installation/docker/isle-dc/docker-local/index.html @@ -2705,7 +2705,7 @@

Demo Content Last update: - August 26, 2024 + August 28, 2024 diff --git a/installation/docker/isle-dc/docker-maintain-drupal/index.html b/installation/docker/isle-dc/docker-maintain-drupal/index.html index a8a9ac2e8..519ff7af3 100644 --- a/installation/docker/isle-dc/docker-maintain-drupal/index.html +++ b/installation/docker/isle-dc/docker-maintain-drupal/index.html @@ -2760,7 +2760,7 @@

Drupal Database Updates Last update: - August 26, 2024 + August 28, 2024 diff --git a/installation/docker/isle-dc/docker-maintain-isle/index.html b/installation/docker/isle-dc/docker-maintain-isle/index.html index 7d054b00b..0bad716b6 100644 --- a/installation/docker/isle-dc/docker-maintain-isle/index.html +++ b/installation/docker/isle-dc/docker-maintain-isle/index.html @@ -2826,7 +2826,7 @@

Version 1.x to 2.xAugust 26, 2024 + August 28, 2024 diff --git a/installation/docker/isle-dc/docker-troubleshooting/index.html b/installation/docker/isle-dc/docker-troubleshooting/index.html index c89978133..e72ae0d0b 100644 --- a/installation/docker/isle-dc/docker-troubleshooting/index.html +++ b/installation/docker/isle-dc/docker-troubleshooting/index.html @@ -2721,7 +2721,7 @@

504 Bad GatewayAugust 26, 2024 + August 28, 2024 diff --git a/installation/docker/site-template/backup/index.html b/installation/docker/site-template/backup/index.html index 7f67b4be6..4aae7f7b6 100644 --- a/installation/docker/site-template/backup/index.html +++ b/installation/docker/site-template/backup/index.html @@ -3010,7 +3010,7 @@

RestoreAugust 26, 2024 + August 28, 2024 diff --git a/installation/docker/site-template/docker-modifications/index.html b/installation/docker/site-template/docker-modifications/index.html index 6c6e35b17..eb6eba261 100644 --- a/installation/docker/site-template/docker-modifications/index.html +++ b/installation/docker/site-template/docker-modifications/index.html @@ -2683,7 +2683,7 @@

Removing ServicesAugust 26, 2024 + August 28, 2024 diff --git a/installation/docker/site-template/setup/index.html b/installation/docker/site-template/setup/index.html index 0a5a56b26..79709ba48 100644 --- a/installation/docker/site-template/setup/index.html +++ b/installation/docker/site-template/setup/index.html @@ -2723,7 +2723,7 @@

Custom Themes & Modules Last update: - August 26, 2024 + August 28, 2024 diff --git a/installation/docker/site-template/site-template/index.html b/installation/docker/site-template/site-template/index.html index 971fc7fa7..d0a47e5bb 100644 --- a/installation/docker/site-template/site-template/index.html +++ b/installation/docker/site-template/site-template/index.html @@ -2697,7 +2697,7 @@

Usage& Last update: - August 26, 2024 + August 28, 2024 diff --git a/installation/docker/site-template/updating/index.html b/installation/docker/site-template/updating/index.html index 0cbe1c7b2..1ad22d74a 100644 --- a/installation/docker/site-template/updating/index.html +++ b/installation/docker/site-template/updating/index.html @@ -2824,7 +2824,7 @@

ProductionAugust 26, 2024 + August 28, 2024 diff --git a/installation/install-a-demo/index.html b/installation/install-a-demo/index.html index f4f7fcfc1..d9639ca78 100644 --- a/installation/install-a-demo/index.html +++ b/installation/install-a-demo/index.html @@ -2626,7 +2626,7 @@

August 26, 2024 + August 28, 2024 diff --git a/installation/manual/configuring-drupal/index.html b/installation/manual/configuring-drupal/index.html index b0538347d..10a00500d 100644 --- a/installation/manual/configuring-drupal/index.html +++ b/installation/manual/configuring-drupal/index.html @@ -2938,7 +2938,7 @@

Enabling EVA ViewsAugust 26, 2024 + August 28, 2024 diff --git a/installation/manual/installing-alpaca/index.html b/installation/manual/installing-alpaca/index.html index 17d37cde2..c4a523469 100644 --- a/installation/manual/installing-alpaca/index.html +++ b/installation/manual/installing-alpaca/index.html @@ -3071,7 +3071,7 @@

Deploying/RunningAugust 26, 2024 + August 28, 2024 diff --git a/installation/manual/installing-composer-drush-and-drupal/index.html b/installation/manual/installing-composer-drush-and-drupal/index.html index 54da0d003..9899c8afb 100644 --- a/installation/manual/installing-composer-drush-and-drupal/index.html +++ b/installation/manual/installing-composer-drush-and-drupal/index.html @@ -2947,7 +2947,7 @@

Restarting the Apache ServiceAugust 26, 2024 + August 28, 2024 diff --git a/installation/manual/installing-fedora-syn-and-blazegraph/index.html b/installation/manual/installing-fedora-syn-and-blazegraph/index.html index 6bdced80d..8d80ac263 100644 --- a/installation/manual/installing-fedora-syn-and-blazegraph/index.html +++ b/installation/manual/installing-fedora-syn-and-blazegraph/index.html @@ -3535,7 +3535,7 @@

Installing Blazegraph Na Last update: - August 26, 2024 + August 28, 2024 diff --git a/installation/manual/installing-solr/index.html b/installation/manual/installing-solr/index.html index c6f270efc..0dbba5769 100644 --- a/installation/manual/installing-solr/index.html +++ b/installation/manual/installing-solr/index.html @@ -2863,7 +2863,7 @@

Adding an IndexAugust 26, 2024 + August 28, 2024 diff --git a/installation/manual/installing-tomcat-and-cantaloupe/index.html b/installation/manual/installing-tomcat-and-cantaloupe/index.html index 8f1cd6312..291d306dd 100644 --- a/installation/manual/installing-tomcat-and-cantaloupe/index.html +++ b/installation/manual/installing-tomcat-and-cantaloupe/index.html @@ -2935,7 +2935,7 @@

Installing and confi Last update: - August 26, 2024 + August 28, 2024 diff --git a/installation/manual/introduction/index.html b/installation/manual/introduction/index.html index 7918f33ff..fcb1498de 100644 --- a/installation/manual/introduction/index.html +++ b/installation/manual/introduction/index.html @@ -2857,7 +2857,7 @@

TroubleshootingAugust 26, 2024 + August 28, 2024 diff --git a/installation/manual/preparing-a-webserver/index.html b/installation/manual/preparing-a-webserver/index.html index 6f614408a..0b253f913 100644 --- a/installation/manual/preparing-a-webserver/index.html +++ b/installation/manual/preparing-a-webserver/index.html @@ -2875,7 +2875,7 @@

InstallAugust 26, 2024 + August 28, 2024 diff --git a/installation/playbook/index.html b/installation/playbook/index.html index 5ac91c926..3b2db7eeb 100644 --- a/installation/playbook/index.html +++ b/installation/playbook/index.html @@ -3153,7 +3153,7 @@

Help&par Last update: - August 26, 2024 + August 28, 2024 diff --git a/installation/quickstart/index.html b/installation/quickstart/index.html index c4e2b9667..652f35780 100644 --- a/installation/quickstart/index.html +++ b/installation/quickstart/index.html @@ -2715,7 +2715,7 @@

ISLE-DCAugust 26, 2024 + August 28, 2024 diff --git a/models/audio/index.html b/models/audio/index.html index e26fdf1d9..b20b223ef 100644 --- a/models/audio/index.html +++ b/models/audio/index.html @@ -2678,7 +2678,7 @@

DisplayAugust 26, 2024 + August 28, 2024 diff --git a/models/video/index.html b/models/video/index.html index 654f31783..954bafc35 100644 --- a/models/video/index.html +++ b/models/video/index.html @@ -2680,7 +2680,7 @@

DisplayAugust 26, 2024 + August 28, 2024 diff --git a/placeholder/index.html b/placeholder/index.html index e469e6cc9..ed74bf003 100644 --- a/placeholder/index.html +++ b/placeholder/index.html @@ -2577,7 +2577,7 @@

Placeholder pageAugust 26, 2024 + August 28, 2024 diff --git a/release_notes/8.x-2.0/index.html b/release_notes/8.x-2.0/index.html index 0a38680cc..df295c95d 100644 --- a/release_notes/8.x-2.0/index.html +++ b/release_notes/8.x-2.0/index.html @@ -2833,7 +2833,7 @@

Adding CaptionsAugust 26, 2024 + August 28, 2024 diff --git a/search/search_index.json b/search/search_index.json index 49850e9f1..d23ac72f3 100644 --- a/search/search_index.json +++ b/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"About","text":""},{"location":"#this-is-islandora","title":"This is Islandora","text":"

Islandora is an open-source framework that provides the necessary tools to use a Drupal website as a fully-functional Digital Assets Management System. See Quickstart to get started.

Islandora:

  • Is native Drupal - With Islandora, you can create preservation-ready digital resources using Drupal nodes, media, files, and taxonomy terms.
  • Provides a sensible starting place - No two Islandora sites are the same, but we provide a starting point for Drupal - the Islandora Starter Site - as well as several methods of installation that set up a working suite of services external to Drupal and show off Islandora's capabilities.
  • Integrates with Fedora - Drupal resources can be stored in Lyrasis's Fedora Repository (version 5.0 or greater) as binaries (files) and RDF metadata.
  • Uses microservices - Islandora provides an architecture for messaging and integration with any number of microservices, that provide services outside of the Drupal framework. Islandora's Crayfish suite of microservices provides functionality for synchronizing resources into Fedora and for automatically generating helper files, called derivatives.
  • Can handle messages at scale - Islandora created Alpaca, an integration middleware based on Apache Camel, to handle messaging and queueing at an enterprise scale. To the user, this means large batch uploads can be processed gracefully.

  • Offers digital preservation features - Using a robust storage layer for preservation (Fedora), and generating technical metadata with FITS, are digital preservation tools provided by our automated installation methods. Community members have created additional features for doing digital preservation using Islandora, which are not yet part of our automated setup.

  • Exposes data to harvesters - Metadata about resources is available as linked data through the JSON-LD serialization module, and can be made available through Drupal, Fedora, or a triplestore. Islandora Starter Site also offers a full configuration for exposing OAI-PMH so that metadata can be harvested by aggregators, and IIIF support means images in Islandora can be viewed in any IIIF-compliant viewer.
  • Offers flexibility - As Islandora content is Drupal content, migrations and batch editing can be done through Drupal's built-in migrate framework and vocabularies can be created using Drupal taxonomies. Contributed Drupal modules such as Solr Search API enable in-site search, and Matomo Analytics provides usage metrics for site analytics.
  • Is a community - A dedicated, active community of users and developers is working to push new features, collaborate on improvements, design custom solutions, and create extended functionality. Some of these for Islandora 8 take the form of Recipes.
"},{"location":"#join-the-community","title":"Join the Community","text":"

The Islandora community is an active group of users, managers, librarians, documenters, and developers from galleries, libraries, archives, museums, and other institutions worldwide. We welcome discussion and contribution through various mailing lists, channels, interest groups, and calls. The Islandora community operates under the Islandora Code Of Conduct. See our Contributing Guidelines for more information.

Documentation for previous versions

Documentation for Islandora Legacy (6 and 7) is on the Lyrasis documentation wiki.

"},{"location":"placeholder/","title":"Placeholder page","text":"

This page serves as a placeholder for functionality that still needs documentation.

Interested in contributing to the Islandora documentation? Join the Community!

The Islandora community is an active group of users, managers, librarians, documenters, and developers from GLAM (and beyond!) institutions worldwide. We welcome discussion and contribution through various mailing lists, channels, interest groups, and calls. The Islandora community operates under the Islandora Code Of Conduct. See our Contributing Guidelines for more information, or drop by a meeting of the Documentation Interest Group for a helping hand.

Documentation for previous versions

Documentation for Islandora 6 and 7 is on the Lyrasis documentation wiki.

"},{"location":"alpaca/alpaca-technical-stack/","title":"Alpaca Technical Stack","text":"

As of version 2.0.0, Alpaca contains several tools bundled into a single runnable jar file. The different tools can be enabled/disabled depending on the configuration you define.

"},{"location":"alpaca/alpaca-technical-stack/#gradle","title":"Gradle","text":"

Gradle is used by Alpaca as a build and package management tool. It is similar to Maven.

"},{"location":"alpaca/alpaca-technical-stack/#apache-camel","title":"Apache Camel","text":"

Apache Camel is an integration framework that aids in implementing integration patterns.

"},{"location":"alpaca/alpaca-technical-stack/#apache-activemq","title":"Apache ActiveMQ","text":"

Apache ActiveMQ is a JMS compliant Messaging Queue. Messaging client can make use of JMS to send messages.

"},{"location":"alpaca/alpaca-technical-stack/#installing-activemq","title":"Installing ActiveMQ","text":"

Installing ActiveMQ is relatively easy. Download the latest stable release here. Go to the activemq_install_dir/bin. Start the ActiveMQ by using the activemq script or batch file and start command.

$ cd activemq_install_dir/bin\n$ ./activemq start\n

When ActiveMQ gets started, go to http://localhost:8161/admin/. You can login using admin:admin.

Note that ActiveMQ in Islandora playbook does not have a UI.

"},{"location":"alpaca/alpaca-technical-stack/#references","title":"References","text":"
  • ActiveMQ Introduction
"},{"location":"concepts/collection/","title":"Collections","text":"

Collections are groups of related content that can be viewed or managed as a unit. Islandora-specific use cases include:

  • an archival fonds that needs to be grouped together, with internal hierarchy
  • various collections of artifacts, grouped for display
  • theses and dissertations, which are organized and managed separately from other objects.
"},{"location":"concepts/collection/#islandora-features","title":"Islandora features","text":"

Islandora provides:

  • a mechanism for grouping nodes under a \"Parent\" node through the generic \"Member Of\" relationship field (field_member_of). This mechanism is also used by Paged Content and Compound Objects. Islandora on its own does not prescribe any particular Content Type, so this field can be configured for any node bundle intended to represent Islandora resources.
  • a \"Children\" tab on resources, which provides a management interface to access, re-order, add, or delete the members of a resource based on the Member Of field.
  • a \"Model\" field (field_model) which can take various values including \"Collection\".
"},{"location":"concepts/collection/#islandora-starter-site-features","title":"Islandora Starter Site features","text":"

Islandora Starter Site is an optional set of presets for Islandora, intended to provide a more user-friendly out-of-the-box experience and starting point for more specific customization.

Islandora Starter Site provides:

  • a Content Type \"Repository Item\" that uses the field_member_of field, so that users may add nodes of this type to a collection (or paged content, or compound resource),
  • A View showing the members of the collection.
  • logic (a Context) such that if a resource is tagged as a \"Collection\" (in the \"Model\" field, then a view of its members will show on the collection's page.

For more details, see the tutorial on How to create and add to a collection

"},{"location":"concepts/collection/#bulk-management-of-members-of-a-collection","title":"Bulk management of members of a collection","text":"

Bulk management of items can be done from a Views Bulk Operations-compatible View, including the main Drupal Content View (at admin/content) and the Manage Members View (at node/[node]/members). This is possible due to the Drupal contrib modules Views Bulk Operations (VBO) and Views Bulk Edit. VBO allows you to perform Drupal Actions on objects. These Actions include making Islandora Derivatives such as \"Audio - Generate a Service File from an Original File\" and also to perform Drupal core Actions such as publishing/unpublishing content. The Views Bulk Edit module extends VBO and allows you to edit field values (such as the Member Of field, which would change which collection a group of nodes are in).

For instance, if you want to move a number of nodes from one collection into another, then you can

  • select them all in a View such as the Members view of a collection
  • select \"Edit content\" from the Action dropdown, and click \"Apply to selected Items\"
  • under \"SELECT FIELDS TO CHANGE\", select \"Member of\"
  • in the Member Of autocomplete that appears, type the name of the target collection
  • Careful! Under \"Change method\", choose between \"Replace the current value\" and \"Add a new value to the multi-value field\". The first one will wipe out ALL existing collections including the one you're working on - which may be what you're after.
  • Select \"Confirm\" at the bottom of the form to save your changes.

For more information see the video tutorial on Batch Editing.

"},{"location":"concepts/collection/#deleting-a-collection","title":"Deleting A Collection","text":"

Deleting Collections creates orphans.

When you delete a Collection object, it will be deleted from the \"Member of\" field of all its Children. This means that it will not be possible to track down the children after the Collection has been deleted. If you intend to move the children, see above. If you intend to delete the children, this must be done before-hand. This is a known issue.

Deleting content creates orphan media.

Deleting content (Collections or otherwise) will NOT delete any attached Media. This is a known issue.

"},{"location":"concepts/collection/#permissions-on-a-collection","title":"Permissions on a Collection","text":"

No reliable access control methods currently allow you to assign people to administer and manage only specific collections.

"},{"location":"concepts/derivatives/","title":"Derivatives","text":"

Derivatives are files, usually generated automatically from a source file, which may be useful to the repository. Examples of derivatives include:

  • smaller or compressed service files
  • thumbnails or poster images for display
  • preservation files in open file formats
  • files containing technical metadata about a file.
"},{"location":"concepts/derivatives/#derivative-models","title":"Derivative Models","text":"

There are two schemes you can use for derivatives:

  • In the standard model, each derivative is a new Media entity linked to the original's parent node. The \"Media Use\" field is used to distinguish the roles that the various files and media have (Thumbnail, Service File, etc.).
  • In the multi-file media model, derivatives are added to additional file fields on the original file's Media entity (see Multi-File Media).
"},{"location":"concepts/derivatives/#derivatives-are-actions-in-drupal","title":"Derivatives are Actions in Drupal","text":"

Derivative configuration is stored using Drupal's Actions. This means that all derivative configuration, such as parameters dictating the derivative size and quality can be edited by a repository administrator in the Drupal GUI under Manage > System > Actions.

As Actions, they can be executed on nodes manually using Views Bulk Operations. They can also be configured to run automatically on media save thanks to Islandora's additions to the Drupal [Contexts] module.

Derivative actions will replace existing files

If a derivative action runs but the target derivative (as identified by its taxonomy term) already exists, the new file will replace the old file (leaving the rest of the Media intact).

"},{"location":"concepts/derivatives/#derivatives-can-run-automatically-with-contexts","title":"Derivatives can run automatically with Contexts","text":"

With the Contexts module, you can configure derivatives to run under specific conditions. These are set in the \"Conditions\" section. Islandora provides a number of context conditions including:

  • entity bundle
  • media mimetype
  • term (attached to a media or node, or a node's parent node)
  • whether a node or media \"is islandora\"

You can set up as many of these as you like, with \"and\" or \"or\" logic between them.

In the \"Reactions\" section is where you can set up derivatives. For standard model derivatives, choose the \"Derivatives\" reaction. This lists all actions, including derivative actions. Note that multifile derivatives won't work here.

Multi-file media

The multi-file media derivatives can NOT be selected from within the \"Derivatives\" reactions. From the \"Reactions\" pop-up window, you must choose \"Derive file for Existing Media\". This panel lists only Multi-file media-type derivatives.

"},{"location":"concepts/derivatives/#derivatives-have-types","title":"Derivatives have Types","text":"

When creating a new Derivative Action, there are a number of flavours of derivative \"types\" available. All derivative Actions fall into one of these types.

Derivative type name machine name Supplying module Expected Microservice (software) Generate a Technical metadata derivative generate_fits_derivative Islandora FITS (roblib) Crayfits (FITS) Generate a audio derivative generate_audio_derivative Islandora Audio Homarus (FFmpeg) Generate a video derivative generate_video_derivative Islandora Video Homarus (FFmpeg) Generate an image derivative generate_image_derivative Islandora Image Houdini (ImageMagick) Get OCR from image generate_ocr_derivative Islandora Text Extraction Hypercube (tesseract/pdftotext)

Multi-file media

The derivatives types available for multi-file media are the ones marked as \"for Media Attachment\" e.g. \"Generate an Image Derivative for Media Attachment\".

"},{"location":"concepts/derivatives/#derivatives-are-created-by-microservices-crayfish","title":"Derivatives are created by microservices (\"Crayfish\")","text":"

In Islandora, we generate derivatives using microservices, which are small web applications that do a single task. Each microservice takes the name of a file as well as some parameters. It runs an executable and returns a transformed file, which can be loaded back into the repository. The microservices in Islandora stack are:

Repository Microservice name executable Crayfish Homarus FFmpeg Crayfish Houdini ImageMagick Crayfish Hypercube tesseract/pdftotext Crayfits Crayfits FITS"},{"location":"concepts/derivatives/#derivatives-are-created-using-an-external-queue","title":"Derivatives are created using an external queue","text":"

To send orders to the microservices, Islandora sends messages to an external queue, from which the microservices process the jobs as they are able. This is a robust system that can \"operate at scale\", i.e. can large handle batches of uploads without slowing down the repository.

The queue system used by Islandora is ActiveMQ, and the listeners are part of \"Alpaca\"

"},{"location":"concepts/derivatives/#derivative-swimlane-diagram","title":"Derivative Swimlane Diagram","text":"

The following diagram shows the flow of derivative generation from start to finish. A user saves a Media in Drupal, which may trigger Drupal to emit a derivative event to a queue, which is read by Alpaca and sent to a microservice. The microservice gets the original file and makes a transformation, returning the derivative file to Alpaca, which sends it back to Drupal to become a Drupal Media.

"},{"location":"concepts/node-concepts/","title":"Node Concepts","text":"

This page describes the core fields and features that Islandora uses to manage content as nodes in an Islandora repository.

"},{"location":"concepts/node-concepts/#nodes-hold-metadata","title":"Nodes hold metadata","text":"

In Islandora, Drupal nodes are created to hold descriptive metadata about content in the repository. This metadata is held in the usual way for nodes, which is by populating Drupal fields. Fields are configured on a Drupal content types, which serve as metadata profiles.

"},{"location":"concepts/node-concepts/#nodes-can-have-memberschildren","title":"Nodes can have members/children","text":"

To build the capacity for creating hierarchical structures like collections, books and their pages, and complex objects, Islandora introduces a special field, \"Member of\" (field_member_of) which must be present on all Islandora content types. It enables a \"Children\" tab to display on Islandora nodes which lists the children of that node, and allows a repository manager to perform bulk operations on them. It also enables a repository manager to create children of nodes directly, individually or in bulk.

"},{"location":"concepts/node-concepts/#nodes-have-models","title":"Nodes have models","text":"

Within a single content type (i.e. metadata profile), Islandora provides the ability to designate some objects as different \"types\" than others. Key behaviours, such as what derivatives are created or what viewer is used, can be configured (see Contexts) based on this value. The available values are taxonomy terms in the Islandora Models vocabulary, and they are attached to nodes via the special mandatory field, \"Model\" (field_model), which must be present on all Islandora content types. These values are installed through a Drupal Migration after the Islandora module is installed. All installation methods perform this migration, so out of the box, the following values should be available in the Islandora Models vocabulary:

Name External URI Audio http://purl.org/coar/resource_type/c_18cc Binary http://purl.org/coar/resource_type/c_1843 Collection http://purl.org/dc/dcmitype/Collection Image http://purl.org/coar/resource_type/c_c513 Video http://purl.org/coar/resource_type/c_12ce Digital Document https://schema.org/DigitalDocument Paged Content https://schema.org/Book Page http://id.loc.gov/ontologies/bibframe/part Publication Issue https://schema.org/PublicationIssue Compound Object http://vocab.getty.edu/aat/300242735 Newspaper https://schema.org/Newspaper

With Islandora alone, choosing a value from this list will have zero effects. The contingent behaviour must be configured during repository implementation. Islandora Starter Site provides an example of what behaviours are possible for these types.

The External URI field

This vocabulary, like many others in Islandora, includes an External URI field. This is intended to be used when transforming Islandora content into RDF, but also serves to make it easier to share configuration. Islandora provides code so that context conditions and derivative configs can be created without referencing the taxonomy term by ID, rather, using the taxonomy term's External URI. Since terms IDs are likely to change across sites, this makes our configs more portable.

"},{"location":"concepts/node-concepts/#nodes-are-attached-to-media","title":"Nodes are attached to Media","text":"

In an Islandora repository, the files in the repository are uploaded as Media, which are linked to the node providing the descriptive metadata. Media belonging to a specific node can be found in the Islandora-provided \"Media\" tab on that node. For more, see the [Media in Islandora] section.

"},{"location":"concepts/node-media/","title":"Media in Islandora","text":"

In Islandora, Media are created to hold the various files that pertain to the descriptive metadata provided by a Node. Islandora provides some fields and features to make this possible.

"},{"location":"concepts/node-media/#media-are-wrappers-for-files","title":"Media are wrappers for files","text":"

Drupal Media are said to be wrappers around files, that allow that file to have fields attached. These fields may contain technical metadata about that file, such as its mimetype or size. This is how Islandora uses Media (though our approach to [technical metadata] differs).

"},{"location":"concepts/node-media/#media-may-store-files-in-different-locations","title":"Media may store files in different locations.","text":"

Configuration on Media types determines where uploaded files will be stored - for example, the Drupal public or private filesystem, or through a tool called Flysystem to another data store such as Fedora. Islandora does not dictate where you put your files. Islandora Starter Site sets all media types to store their files in Fedora. This can be overridden when creating media programmatically, such as for derivatives.

"},{"location":"concepts/node-media/#media-belong-to-nodes","title":"Media belong to nodes","text":"

While in Drupal, media are said to be \"reusable files\" that can be referred to by any number of content items, in Islandora there is a special relationship, the \"Media Of\" (media_of) field, that links media to a single node that describes that file and how it fits into the repository. The field \"Media Of\" must be on all Islandora media types for media of those types to behave as Islandora media.

Then, the media \"of\" an Islandora node (if any) will appear in that node's \"Media\" tab. From this tab, there are also links to add new Media, individually or in batch. When media are created this way, their \"Media of\" field is automatically populated with the current node.

"},{"location":"concepts/node-media/#media-have-different-uses","title":"Media have different uses","text":"

In a repository, a node may have several media that belong to it, which represent the originally uploaded file, a smaller service file, a thumbnail, and perhaps a file transformed into a specific format for preservation. All of these media can be distinguished using the Islandora-provided field, \"Media Use\" (field_media_use). This is a taxonomy reference field that points to the vocabulary, Islandora Media Use.

The Islandora module provides, through a migration, the following values:

Media Use term External URI Extracted Text http://pcdm.org/use#ExtractedText Intermediate File http://pcdm.org/use#IntermediateFile Original File http://pcdm.org/use#OriginalFile Preservation Master File http://pcdm.org/use#PreservationMasterFile Service File http://pcdm.org/use#ServiceFile Thumbnail Image http://pcdm.org/use#ThumbnailImage Transcript http://pcdm.org/use#Transcript

This migration is performed by all installation methods, so these values should be available \"out of the box\". These values were provided by the PCDM data model (see RDF in Islandora), and not all values are associated with behaviours you might expect - for instance, Islandora is not configured out-of-the box to display a transcript if you add a Media (file) and tag it as \"Transcript\". See Audio and Video for setting up transcripts.

Standard vs Multi-file media model

This describes the standard file-media relationship in Islandora. There is an alternative method of arranging files and their derivatives which we call the \"Multi-file media\" method.

"},{"location":"concepts/rdf/","title":"RDF in an Islandora Repository","text":"

Islandora was build on the idea that a repository can be represented in Drupal in a way that can be mapped to RDF and present the repository as Linked Data.

"},{"location":"concepts/rdf/#portland-common-data-model","title":"Portland Common Data Model","text":"

Islandora uses the Portland Common Data Model (PCDM) to arrange elements of the repository. Nodes correspond to pcdm:Objects, and Media to pcdm:Files. The \"Member of\" field on nodes allows us to create pcdm:memberOf relationships (the opposite of pcdm:hasMember), and the \"Media of\" field represents pcdm:fileOf relationships (the opposite of pcdm:hasFile).

PCDM, plus additional metadata mappings, is the organizing model which allows us to create an RDF version of the repository in Fedora and/or in an RDF triplestore like Blazegraph.

"},{"location":"concepts/rdf/#mapping-to-rdf-rdf-module","title":"Mapping to RDF (RDF Module)","text":"

In Islandora, the RDF module provides a way to map metadata from fields into RDF. This provides a mechanism of using configuration entities (YAML files, as there is no robust UI) to map fields on nodes, media, and taxonomy terms to RDF predicates.

Namepspaces in RDF must be registered before they can be used. The Islandora module registers a number of namespaces and more can be added using hook_rdf_namespaces(). See RDF Mappings for more details.

"},{"location":"concepts/rdf/#exposure-as-json-ld","title":"Exposure as JSON-LD","text":"

Nodes, Media, and taxonomy terms can have their RDF (per their mappings) exposed to the world as RDF formatted in the JSON-LD syntax thanks to the Islandora-built JSON-LD Drupal module. The JSON-LD module converts the RDF metadata, with some alterations, to a JSON-LD format that can be consumed by RDF consumers such as Fedora and Blazegraph.

"},{"location":"concepts/rdf/#syncing-to-fedora-and-blazegraph","title":"Syncing to Fedora and Blazegraph","text":"

Islandora provides the pathways for objects and media in the repository to be synced to Fedora and Blazegraph.

Objects are sent to Fedora and Blazegraph through an \"Indexing\" Drupal Action, which, after being put on a queue, is read by an indexer which pushes the JSON-LD information to the appropriate target.

Files can be stored in Fedora directly, using the Flysystem module. Whether or not a file is in Fedora, information about that file can be synced (from Drupal Media) into Fedora.

Neither Fedora nor Blazegraph are read as part of the standard Islandora configuration.

"},{"location":"concepts/starter-site/","title":"Islandora Starter Site","text":"

The Islandora Starter Site is an out-of-the-box deployment of Islandora. It is a complete exported Drupal site, that makes use of the Islandora modules and configures them in a way that is illustrative and useful.

  • For evaluators, it is intended to show off the features and capabilities of Islandora.
  • For interest groups, it is intended to be a place to develop solutions to shared problems.
  • For site builders, it is intended to be a starting point for configuring a site.

The Islandora Starter Site contains no code, only references to other modules and lots of Drupal configuration. A very motivated person could re-create the Starter Site just by installing and configuring modules. This means there's nothing tying you to using the Starter Site. There's also nothing tying you to doing things in a particular way. Also, it means that you won't be getting any \"updates\" - there's no code to update.

To experience the full Islandora Starter Site, it requires access to external services such as Solr, Fedora, Alpaca, and Matomo. It is therefore suggested to deploy the Starter Site using one of our deployment platforms: ISLE-DC (using the make starter or make starter_dev commands), ISLE Site Template, or the Islandora Playbook (using the starter (default) or starter_dev option in the Vagrantfile).

"},{"location":"contributing/CONTRIBUTING/","title":"Welcome!","text":"

If you are reading this document then you are interested in contributing to Islandora. You do not need to be a programmer to speak up! By joining the Islandora community, you agree to abide by the Islandora Code of Conduct.

"},{"location":"contributing/CONTRIBUTING/#join-the-community","title":"Join the community!","text":"

Onboarding Form: Submit your information through the Onboarding Form, and we can invite you to all the listed channels below.

  • Slack: Use this invite link to join our Slack workspace. If you're not sure what channels you're looking for, get started by asking your questions in the #general channel!

  • Google Group: Subscribe to the mailing list by joining the Google Group.

  • Github: Although you can participate without a Github account, having one ensures you can be made a member of the Islandora General Members team to better enable your access to the Islandora project. Additionally, it will allow you to be tagged on relevant issues or pull requests that are of interest to you! To become a part of the Islandora General Members Github team, email community@islandora.ca or use the Onboarding form.

"},{"location":"contributing/CONTRIBUTING/#weekly-open-tech-call","title":"Weekly Open Tech Call","text":"

Attend our weekly Zoom meetings on Wednesdays at 1:00 PM EST! You can access these meetings through our Agendas or our Community Calendar. We discuss the past week's Github activity in Issues and Pull Requests, and any other topics that are raised. Anybody is welcome to join the call, and add items to the agenda.

"},{"location":"contributing/CONTRIBUTING/#github-issues","title":"Github Issues","text":"

Anyone with a Github account can make issues. Please check the applicable places first to see if there's already a ticket.

Issues should be added in the individual module or component's repository where applicable. For issues that are broader in scope than one module, or about the official Islandora documentation, open an issue in the Documentation repository. This is also the repository for hundreds of \"legacy\" (but still valid) tickets from before we opened issue queues on individual repositories.

Issue templates are provided for the following:

  • Bug Report: Report something not working in Islandora software.
  • Documentation: Let us know if documentation is unclear, or missing.
  • Feature Request: Start here if you would like to request a specific feature. Note that these should usually be supported by Use Cases.
  • Use Case: Start here if you would like to outline a need with a defined scope to be addressed by Islandora software.

Issues are reviewed during the Weekly Open Tech Call every Wednesday at 1:00 p.m. Eastern.

"},{"location":"contributing/CONTRIBUTING/#github-pull-requests","title":"Github Pull Requests","text":"

If you have code to address an issue, or can offer documentation, please create a pull request (PR) in the appropriate repository. Before we can merge your pull request, you must have a signed CLA or CCLA (see below). Code PRs must conform to Drupal Coding Standards, and Documentation PRs must conform to our Documentation Style Guide. There are no guides specific to READMEs or other documentation (help text, etc) that lives in Drupal, but please try to write clearly and follow existing practices. See more in our Contributing Workflow.

"},{"location":"contributing/CONTRIBUTING/#new-modules","title":"New Modules","text":"

If you have code that doesn't fit within the scope of Islandora's existing repositories, but that the Islandora community is in a better position to maintain than you are, please consider using the LSAP Process to contribute your code to the ownership of the Islandora Foundation.

"},{"location":"contributing/CONTRIBUTING/#contributor-license-agreements","title":"Contributor License Agreements","text":"

Before you set out to contribute code you will need to have completed a Contributor License Agreement or be covered by a Corporate Contributor License Agreement. This license is for your protection as a contributor as well as the protection of the Foundation and its users; it does not change your rights to use your own contributions for any other purpose.

"},{"location":"contributing/committers/","title":"Islandora Committers","text":"

Islandora is open source and released under open-source (usually MIT and GPLv2) licenses. The software, associated documentation, and community is developed collectively by a community of contributors and committers. All interested community members are encouraged to contribute to the project. Contributors who demonstrate sustained engagement with the project through quality participation in meetings, mailing lists, documentation and code updates can be self-nominated, or nominated by existing committers, to become a committer. Committers need not be limited to software developers! Contributors with skills in documentation and testing, or who are very active in the community for events and support, can also be committers.

Committers are supported in their specific work by having permissions to do things like merge code as well as manage Github, Slack, and the Website. The current list of committers is managed as a group in Github, and can be viewed in the Github repository by existing members. A separate list of committers is publicly available at the bottom of this page. If a committer does not make a contribution in a year (without known mitigating circumstances) they are usually moved out of the committers GitHub group and listed as an Emeritus committer. There are subgroups with specific enhanced access depending on their skills and levels of commitment to the codebase. These are more fully explained in this spreadsheet of keys and permissions. If you have questions about committers, you can send them to the Chair of the TAG committee, or to community@islandora.ca

"},{"location":"contributing/committers/#rights","title":"Rights","text":"

Committers share the following rights:

  • Access to key Islandora logins.
  • Permissions in Islandora repositories.
  • Nominating and voting on new committers.
"},{"location":"contributing/committers/#responsibilities","title":"Responsibilities","text":"
  • Engage productively in the evolution in the community and codebase using your skills and ability.
  • Have a GitHub account and maintain contact information with Islandora Foundation.
  • Guide and mentor new committers
  • Remain active in Islandora and/or communicate if you cannot participate further, or if your commitment level needs to change.
  • Support the community through activity on community channels, including monitoring and responding to mailing list/Slack inquiries.
"},{"location":"contributing/committers/#committers","title":"Committers","text":"

The following is an alphabetized list of the current Islandora committers:

Name Organization Github username Bryan Brown Florida State University bryjbrown Joe Corall Lehigh University joecorall Jordan Dukart discoverygarden jordandukart Willow Gillingham Born-Digital wgilling Jonathan Hunt Catalyst.Net kayakr Mark Jordan Simon Fraser University mjordan Danny Lamb Born-Digital dannylamb Natkeeran Ledchumykanthan University of Toronto Scarborough natkeeran Rosie Le Faive University of Prince Edward Island rosiel Gavin Morris Born-Digital g7morris Annie Oelschlager Northern Illinois University aOelschlager Alexander O'Neill Born-Digital alxp Don Richards Johns Hopkins University DonRichards Bethany Seeger Johns Hopkins University bseeger Seth Shaw University of Nevada, Las Vegas seth-shaw-unlv Alan Stanley Agile Humanities ajstanley Yamil Suarez Berklee College of Music ysuarez Adam Vessey discoverygarden adam-vessey Jared Whiklo University of Manitoba whikloj"},{"location":"contributing/committers/#emeritus-committers","title":"Emeritus Committers","text":"

The following is an alphabetized list of the prior Islandora committers:

Name Organization Daniel Aitken discoverygarden Melissa Anez LYRASIS Aaron Coburn Amherst College Jonathan Green LYRASIS Debbie Flitner Arizona State University Diego Pino METRO Nick Ruest York University Eli Zoller Arizona State University"},{"location":"contributing/committers/#how-are-you-evaluated-as-a-potential-committer","title":"How are you evaluated as a potential committer?","text":"

When you are nominated, the other committers review these categories before recommending you as a committer. Note that appointment to committers/TAG is governed by the TAG Terms of reference, and appointment to TAG comes with additional rights and responsibilities. If you become a committer and a member of committers/code, you are eligible to serve in TAG.

"},{"location":"contributing/committers/#you-are-committed-to-the-islandora-community","title":"You are committed to the Islandora Community","text":"

You\u2019ve been making strong and consistent community contributions, you can stick it out through tough issues and have demonstrated a willingness to lend a hand.

"},{"location":"contributing/committers/#you-have-skills-and-abilities-suited-to-the-role","title":"You have skills and abilities suited to the role","text":"

You demonstrate that you understand the project and community and have skills suited to being a committer. Perhaps you have participated in multiple event planning sessions, you write a lot of documentation, or you chair an effective interest group. If you are a software developer, you\u2019ve written good code. If your patches are easy to apply, this could be you!

"},{"location":"contributing/committers/#you-are-a-great-colleague","title":"You are a great colleague","text":"

You respond to and deliver criticism well, and do what you say you will do. You participate actively in decision-making processes, keeping the larger good of the Islandora codebase and community in your purview. You have a helpful attitude and respect everybody\u2019s ideas.

"},{"location":"contributing/committers/#you-mentor-the-less-experienced","title":"You mentor the less experienced","text":"

You\u2019ve demonstrated a willingness to reach out to less experienced community members, and link them to resources they need (maybe you made some of these resources - that\u2019s great!)

"},{"location":"contributing/committers/#committers-acting-as-maintainers","title":"Committers acting as Maintainers","text":"

If you are maintaining a specific Islandora repository, you commit to the following responsibilities for as long as you act as maintainer:

  • To oversee contributions to the code, including facilitating the testing and merging of pull requests and the resolution of issues.
  • To ensure the timely resolution of security fixes, including (but not limited to) monitoring dependabot security alerts.
  • To ensure the documentation is correct and is updated as needed.
  • To ensure appropriate backwards compatibility and to manage releases in accordance with the Islandora versioning approach.

A great maintainer also communicates important changes within the component to the community.

As maintainer, you are empowered to:

  • Merge your own pull requests or those you contributed code to (subject to the usual restrictions of waiting for and addressing feedback, tests passing, etc). Other Committers are still encouraged to create, review, and merge pull requests. This is a special privilege to prevent code from stagnating. You are still responsible for the quality of the code committed and for ensuring that releases are fully tested and do not break backward compatibility.

If these responsibilities are not being performed, and/or you choose to step down as maintainer, the TAG will attempt to find another maintainer. If no maintainer can be found, and the TAG/Committers do not elect to jointly maintain the component, it may be deprecated.

"},{"location":"contributing/committers/#process-for-inducting-new-committers","title":"Process for inducting new Committers","text":"

Contributors can be self-nominated or be nominated by others. These nominations can go to any committer willing to shepherd the process, or be sent to community@islandora.ca to be forwarded by IF staff. Whoever initiates the process is the sponsor and responsible for either completing the following steps, or delegating them to IF staff.

Upon receiving a nomination:

  1. An email is sent by sponsor or designate describing the nomination and calling for a vote to islandora-committers@googlegroups.com. (templates/committerVote.txt).
  2. The sponsor or designate closes the vote (templates/closeCommitterVote.txt).
  3. The sponsor or designate invites the new committer (templates/committerInvite.txt), if no CLA is on file (templates/committerInviteCLA.txt).

Checklist for a new committer (can be completed by sponsor or designate):

  • Complete the CLA form. Ensure the entry shows up on our public sheet linked on our Contributer License Agreements wiki page.
  • Add to the Islandora Committer team of the Github Islandora organization.
  • Assign to the appropriate GitHub teams, ensuring updates are reflected in the community wiki documentation.
  • Add to the Committer team of GitHub Islandora-Labs organization.
  • Add to islandora-committers google group. Announce the new committer (template/committerAnnounce.txt).

For additional information about committers, visit this Islandora community wiki page.

"},{"location":"contributing/contributing-workflow/","title":"Contributing Workflow","text":""},{"location":"contributing/contributing-workflow/#terms","title":"Terms","text":"
  • User: Anyone who uses Islandora whether or not they participate in the community directly.
  • Contributor: Anyone who contributes in any form to Islandora (code, documentation, posts on the lists, etc).
  • Committer: Individuals with merging privileges, and binding votes on procedural, code modification, and release issues, etc. (further outlined here).
  • Community: All of the above.
"},{"location":"contributing/contributing-workflow/#overview","title":"Overview","text":"

Choosing how we work together is an important privilege the Islandora community offers to its contributors. Our workflow is built around engagement and consensus to encourage high quality code that is expected to meet defined standards. All contributors are expected to follow the defined workflow. Any contributor can propose changes to the workflow at any time by initiating a conversation on the mailing list. Any proposed changes must be confirmed by the committers by way of an Apache rules vote in order to take effect.

"},{"location":"contributing/contributing-workflow/#social-dynamics","title":"Social Dynamics","text":"

We operate under the Islandora Community Code of Conduct. Some additional general observations to keep in mind when interacting with others in this workflow:

  • We are all volunteers
    • Time/attention of others is a limited resource.
    • You cannot impose on others what to do.
    • You should be motivated to do things by yourself.
  • Common need is the driving force.
    • Friendly cooperation is how its done
    • Your contribution should not overstrain others.
    • Your contribution is valuable when others appreciate it.
  • Islandora is constantly improving
    • You are expected to make high quality contributions
    • You must be able to adapt to change
"},{"location":"contributing/contributing-workflow/#workflow","title":"Workflow","text":""},{"location":"contributing/contributing-workflow/#general-guidelines","title":"General Guidelines:","text":"
  • Identify if a Github issue is needed:
    • Minor grammar / php warning: no ticket required
    • Feature or Fix beyond the minimal: Github Issue
    • Unsure? Play it safe and make a Github Issue
  • Prioritizing pull requests:
    • If there is an urgent need for the pull request to be addressed quickly, indicate the need in the pull request template or a comment.
    • Complexity should also be taken into account when evaluating how quickly to merge a pull request. Changes that affect core modules or make extensive changes should receive more review and testing.
  • All interested parties should be satisfied before something is merged; no hard numbers. If you know who is likely to be interested, tag them. Tag the creator of the issue if possible. Make a reasonable effort.
  • If a pull request languishes without response when one is needed, tag @Islandora/committers (or @Islandora-Devops/committers if you\u2019re working on install code) with a reminder and/or put the issue on the agenda for the next Islandora Tech Call
  • All contributions to GitHub must be accompanied by either an Individual Contributor License or a Corporate Contributor License covering the contributor.
"},{"location":"contributing/contributing-workflow/#development-workflow","title":"Development Workflow:","text":"
  • Create a Github Issue if none exists
    • Assign the issue to yourself or request it to be assigned in a comment on the issue. Be sure to tag @Islandora/committers to bring attention to it.
  • Perform development on a \u2018feature\u2019 branch. Give your branch a name that describes the issue or feature. Using something like \u2018issue-xxx\u2018 and including the issue number is always a safe bet if you don\u2019t know what to name it.
  • When the code is ready for review, issue a pull request on Github from your feature branch into the development branch (\u20188.x-1.x\u2019 for Drupal modules and \u2018dev\u2019 for everything else)
  • Continuous integration checks need to be satisfied before the code can be merged
    • Coding standards checks and copy/paste mess detection must be passing before code can be merged.
    • Automated tests are also expected to pass before code can be merged, however at times there can be extenuating circumstances preventing this. For example, sometimes there is a disconnect between running tests locally versus on the continuous integration server. If tests run locally and everyone involved in the pull request is in agreement, the check for automated tests can be ignored. But this isn\u2019t a common occurrence.
    • Code coverage checks are more of a guideline than a strict requirement. A reasonable effort to provide tests for contributed code is expected. However, demanding 100% code coverage for all contributed code is unreasonable. If tests have been provided or updated and everyone involved in the pull request is in agreement, code coverage checks can be ignored.
  • The code is reviewed and tested by the community, with all feedback and further development performed within the pull request.
  • Once all feedback has been addressed, all applicable status checks have passed, and all interested parties are satisfied, the code can be merged by a committer
    • Pull requests should sit open for a minimum of 24 hours from the last meaningful commit to allow for any feedback.
    • Meaningful changes (not bugfixes) should come to at least one open tech call meeting before being committed.
    • Pull requests should not be merged by committers who have provided code in the pull request, unless that person is named as a maintainer.
    • Always \u201cSquash and Merge\u201d, please.
  • Close the Github issue
    • If you add the keyword \u201cfixes\u201d or \u201cresolves\u201d on the same line when referencing the Github Issue in the PR description, it will auto-close the issue when the PR is merged.
    • Otherwise, make a comment referencing the new commit and close the issue manually.
"},{"location":"contributing/create-issues/","title":"Create a GitHub Issue","text":""},{"location":"contributing/create-issues/#overview","title":"Overview","text":"

The Islandora community uses GitHub issues to track bug reports, requests for improvements, requests for new features, and use cases. Issues are welcome from anyone who works with Islandora.

The Islandora issue queue is maintained in the Islandora Documentation repository on GitHub. Issues posted to the queue are reviewed weekly on the Islandora Tech Call. Members of the Islandora community can then respond to posted issues by replying with comments, taking on the assignment to do the work described in the issue, or making pull requests relating to the issue.

Video version available

The material in this tutorial is presented in our video, Create a GitHub Issue.

"},{"location":"contributing/create-issues/#before-you-start","title":"Before you start","text":"
  • You will need to have an account on GitHub. These are free to register and require only a name and email address.
"},{"location":"contributing/create-issues/#how-to-create-an-issue-in-the-islandora-issue-queue","title":"How to create an issue in the Islandora issue queue","text":"
  1. Go to https://github.com/Islandora/documentation/issues
  2. Click on New issue:
  3. Select the type of issue you are creating, as this will provide you with a template to describe your issue.
  4. Fill in the information for your issue:

    1. Give your issue a descriptive title following the text that is already provided in the template title, ex. [BUG].
    2. Fill in the body of your issue under the Write tab. A template of questions will be provided based on the type of issue selected. Depending on your issue, you may need to: describe a bug you are seeing and how to reproduce it, describe how an existing feature could be improved, describe a new feature and how it should work, or describe documentation that needs to be written or expanded.
    3. Use the built-in text editor to help format your issue in Markdown.
    4. Use the Preview button to see how your issue will be published and ensure the formatting looks the way you want.
    5. Click Submit new issue to add your issue to the queue.
    6. Optionally, Add labels to your issue to assign it to available categories, such as \"documentation\" or \"question.\" Click on as may labels as you like. When you click outside of the drop-down list, the selected labels will be applied to your issue.

"},{"location":"contributing/docs-style-guide/","title":"Documentation Style Guide","text":""},{"location":"contributing/docs-style-guide/#dos","title":"Do's","text":"
  • Use a GitHub Pull Request to submit documentation.
    • See the Editing Documentation documentation page for information on creating a Pull Request.
  • Make it clear if the documentation is based on a particular configuration (such as the Install Profile Demo) or if it applies to any deployment of Islandora.
  • Submit documentation formatted in Markdown format.

    • Include a top-level heading for the whole page (using #)
    • Please add Markdown headings (# and ##) to the content sections.
  • Use the \"bold/emphasis\" style in Markdown by enclosing text in double asterisks or underscores, **bold text** or __bold text__, for UI elements that users will interact with. For example, a button label for a button that must be pressed should be made bold in Markdown.

  • Use the \"italics\" style in Markdown by enclosing text in single asterisks or underscores, *italic text* or _italic text_, for UI elements that have a label or title if you need to reference them in the documentation. For example, a title of a screen or page that will visit should be made italic in Markdown.
  • Use >> and **bold text** to indicate clicking through nested menu items, and also include the direct path. Example:
    **Administration** >> **Structure** >> **Views** (/admin/structure/views)\n
  • Use - instead of * for bulleted lists. Indent four (4) spaces for nested lists (Github renders nesting in markdown with 2 spaces, but mkdocs needs 4). Example:
    - I am a list item\n    - And I am a sub-item.\n
  • Upload images to the 'assets' folder and reference them from there.
    • For file naming, use underscores between words and prefix all file names with the page name, e.g. context_display_hints.jpg for the image showing how to set display hints in the context menu.
  • Use the Admonition syntax to create notes like this (four-space indent required):

Example:

!!! note \"Helpful Tip\"\n    I am a helpful tip!\n

Result:

Helpful Tip

I am a helpful tip!

  • Use our custom islandora type within the Admonition syntax to call attention to areas where Islandora configuration differs from standard Drupal configuration:

Example:

!!! islandora \"Lobster trap\"\n    This setting is specific to Islandora and is not standard to Drupal.\n

Result:

Lobster trap

This setting is specific to Islandora and is not standard to Drupal.

"},{"location":"contributing/docs-style-guide/#donts","title":"Don'ts","text":"
  • Do not leave any \"trailing spaces\" at the end of lines of content.
  • Do not use \"curly\" quotes and apostrophes, use only \"straight\" quotes and apostrophes.
  • Do not upload images that are excessively large in file size (remember, these docs are part of the software!)
"},{"location":"contributing/editing-docs/","title":"Editing Documentation","text":""},{"location":"contributing/editing-docs/#overview","title":"Overview","text":"

These documentation pages are written and maintained by volunteers from the Islandora community, and new contributions are always welcomed. The documentation that you see here is formatted in the easy-to-use Markdown markup language, which can add formatting such as links, headings, and bolded text. Then the documentation HTML site is built using mkdocs from the Markdown files hosted in the Islandora documentation GitHub repo. This documentation is rebuilt automatically upon changes being committed to GitHub, so you will see changes reflected here within a few minutes of making an edit.

The Islandora Documentation Interest Group holds regular meetings on the second Wednesday of each month between 2:00 p.m. - 3:00 p.m. EST to discuss how to improve and expand Islandora's documentation. If you are interested in becoming involved, all are welcome to join! You can view DIG meetings on the Islandora community calendar and add them to your own calendar by clicking the event and selecting \"copy to my calendar\". If you just want to make a contribution to the existing documentation, that process is outlined here.

"},{"location":"contributing/editing-docs/#before-you-start","title":"Before you start","text":"

To edit documentation in Islandora, you must:

  • have a GitHub account.
    • Github accounts are free and only require a valid email address.
  • have a very basic familiarity with Git terms such as fork, branch, and pull request.
  • optionally, be a member of the Islandora GitHub Organization.
    • To request an invite, email community@islandora.ca or ask on the Islandora Slack.
    • This is only needed if you want to commit changes directly to the documentation. Non-members may still make pull requests.
  • be willing to learn Markdown - a good Markdown cheat sheet can help.
    • for example, you may use Markdown syntax to create links and section headings.
  • follow the Islandora Documentation Style Guide.
  • have either an individual Contributor License Agreement (CLA) on file with the Islandora Foundation, or work for an organization that has a corporate Contributor License Agreement on file with the Islandora Foundation.
    • for information on how to fill out and submit a Contributor License Agreement (CLA) for yourself and/or your organization visit the License Agreements section of the \"How to contribute\" documentation page.
  • by participating in the Islandora Community, you are agreeing to act according to the Islandora Code of Conduct.
"},{"location":"contributing/editing-docs/#how-to-edit-documentation-using-a-web-browser","title":"How to edit documentation using a web browser","text":"
  • Starting from the page you want to edit on a web browser, look for the pencil icon in the top righthand corner of the page and click on it.
  • You will be taken to GitHub. If you are not logged in, you will be prompted to log in or register for an account. If you are logged in, you will be taken to an in-browser editor where you will view the page in Markdown.
  • Make your changes directly in the browser editor, then scroll to the bottom to \"commit\" your changes. This is the equivalent of saving.

\u2192 If you are not a member of the Islandora GitHub organization, GitHub will create a new fork of the documentation repository in your GitHub account. From there you can make a pull request to have your changes sent back to the main Islandora documentation repo to be reviewed and accepted.

\u2192 If you are a member of the Islandora GitHub organization, you will be able to create a new temporary branch within the main Islandora documentation repo, and then create a pull request by selecting Create a new branch for this commit and start a pull request. Give your branch a brief name starting with your username, with no spaces, indicating what it is for. Then click Commit changes.

  • You will be taken to the pull request template which will prompt you to fill out some basic information about what you have changed, and why. Replace all relevant instances of Replace this text with your own text.
  • You will have the option to tag Interested Parties, or people you would like to review your work, by writing in their GitHub account name after the @ symbol. If you don't have anyone specific in mind, you may tag @Islandora/committers to alert all Islandora 8 Committers that there is a new pull request for their review.

  • When you have completed the template, submit your changes for review by clicking Create pull request.

  • Once your pull request has been made, it will go into the pull request queue and anyone tagged in the request will receive a notification. Any member of the Islandora GitHub organization can review and merge your pull request, committing your changes to the documentation, with a few exceptions:

    • Your pull request should not be merged by you.
    • Your pull request should not be merged by anyone who contributed to it (i.e, if some one collaborated with you on writing the changes, that person cannot merge those changes).
    • Your pull request should not be merged by anyone who works at the same organization as you.

Once your changes are committed, these documentation pages will be rebuilt to reflect your changes within a few minutes.

For very minor changes

For members of the Islandora GitHub organization (i.e., those with write access to the documentation repo): If the change you made was very small, such as a formatting or spelling correction that does not change the semantics of the document, you may push your changes into the documentation immediately by selecting commit directly to the main branch and clicking Commit changes.

"},{"location":"contributing/editing-docs/#how-to-edit-documentation-using-git-on-your-computer","title":"How to edit documentation using Git on your computer","text":"

Warning

These steps are for more advanced users that are familiar with using the Git source control software, which is different from just using the GitHub website with a web browser.

  • For information on how to submit your Git software based changes see the Contribute code and Create a pull request sections of the \"How to contribute\" page of the Islandora documentation.
"},{"location":"contributing/readme-template/","title":"Readme template","text":"
# ![Alt text](Mascot Image) Repository Name\n\n[![Minimum PHP Version](link)](link)\n[![Build Status](link)](link)\n[![Contribution Guidelines](http://img.shields.io/badge/CONTRIBUTING-Guidelines-blue.svg)](./CONTRIBUTING.md)\n[![LICENSE](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](./LICENSE)\n[![codecov](link)](link)\n\n## Introduction\n\nA brief introduction and summary of the module.\n\n## Requirements\n\nThis module requires the following modules/libraries:\n\n* [Name](Link)\n* [Name](Link)\n* Any\n* Requirements\n\n## Installation\n\nInstallations instructions.\n\n## Configuration\n\nDescribe path to configuration.\n\nInclude a screenshot of configuration page. When using your choice of screenshot software, resize your browser\nfirst to avoid wide screenshots. Here are a few browser extension examples to take screenshots.\n\n  - [Fireshots](https://chrome.google.com/webstore/detail/take-webpage-screenshots/mcbpblocgmgfnpjjppndjkmgjaogfceg)\n  - [Nimbus](https://chrome.google.com/webstore/detail/nimbus-screenshot-screen/bpconcjcammlapcogcnnelfmaeghhagj)\n\nTo upload the image drag the image into the comment section of an existing Pull Request.\n\nThis will generate the image URL link for you\n  ![Configuration Screenshot](https://user-images.githubusercontent.com/2857697/39014759-e2ef9c1e-43e0-11e8-921c-c2a3234d65d2.jpg)\n\nVideo example on [How to attach an Image in README.md file with Github](https://youtu.be/wVHJtL-y7P0)\n\n## Other Sections As Needed\n\nSections specific to this repo, but not found in all repos, should go here.\n\n## Documentation\n\nFurther documentation for this module is available on the [Islandora documentation site](https://islandora.github.io/documentation/).\n\n## Troubleshooting/Issues\n\nHaving problems or solved a problem? Check out the Islandora google groups for a solution.\n\n- [Islandora Group](https://groups.google.com/forum/?hl=en&fromgroups#!forum/islandora)\n- [Islandora Dev Group](https://groups.google.com/forum/?hl=en&fromgroups#!forum/islandora-dev)\n\n## FAQ\n\nQ. Is this normal?\n\nA. Yes. This is normal. Why ...\n\n## Maintainers/Sponsors\n\nCurrent maintainers:\n\n- [Maintainer Name](https://github.com/maintainer_github)\n- [Another Maintainer](https://github.com/maintainer_github)\n\nThis project has been sponsored by:\n\n- Some really awesome sponsor\n\n## Development\n\nIf you would like to contribute, please get involved by attending our weekly [Tech Call](https://github.com/Islandora/documentation/wiki). We love to hear from you!\n\nIf you would like to contribute code to the project, you need to be covered by an Islandora Foundation [Contributor License Agreement](https://drive.google.com/file/d/1k6eCM5EV-w4I4ErkiGj4NJwvLnXkejyk/view?usp=sharing) or [Corporate Contributor License Agreement](https://drive.google.com/file/d/1-SQYuHWRxvltQYgkFWpYv7nGbvJp1u8h/view?usp=sharing). Please see the [Contributors](https://github.com/Islandora/islandora-community/wiki/Onboarding-Checklist) pages on Islandora.ca for more information.\n\nWe recommend using the [islandora-playbook](https://github.com/Islandora-Devops/islandora-playbook) to get started.  If you want to pull down the submodules for development, don't forget to run git submodule update --init --recursive after cloning.\n\nAlso include any Travis gotcha's here.\n\n## License\n\n[Name](link). GPLv2 for Drupal modules. MIT for other modules.\n
"},{"location":"contributing/releasing-islandora/","title":"Releasing Islandora","text":"

Islandora is an ecosystem of repositories and follows a Semantic Versioning Approach. This allows the community to remain aligned with Drupal's approach and support more a more modular approach and more frequent releases, as well as better upgrade paths for those using components of the system. In semantic versioning, a version has three elements 'MAJOR.MINOR.PATCH'. This looks something like 2.1.1, or you may see major versions labelled as 2.x.x. To guide repository maintainers, we recommend you increment the:

  • MAJOR version when you make incompatible API changes,
  • MINOR version when you add functionality in a backwards compatible manner, and
  • PATCH version when you make backwards compatible bug fixes.

Learn more about Islandora's Versioning Policy

Note to module maintainers specifying composer requirements: please familiarize yourself with the Next Significant Release Modifiers so that module dependencies are as permissive as possible. Most often dependency versions should be tagged using the caret (^) so that bug-fixes and minor releases can be pulled in without updating their module\u2019s composer file.

"},{"location":"contributing/releasing-islandora/#how-to-release-java-code","title":"How to Release Java Code","text":"

You will need Java 8 on your system to release java code. The rest of the work is handled handled by Gradle, which is included in the Git repositories. If you cannot get Java 8, you can still release Syn using Docker and the openjdk:8-jdk image. For Alpaca, because of our use of keys, Java 8 is required.

"},{"location":"contributing/releasing-islandora/#release-syn","title":"Release Syn","text":"

To release Syn

  1. Drop the -SNAPSHOT from projectVersion in build.gradle
  2. Build Syn
    1. If you have Java 8, this can be done with $ ./gradlew build shadowJar
    2. If you don't have Java 8, you can do this with Docker $ docker run --rm -v /path/to/Syn:/opt/Syn openjdk:8-jdk bash -lc 'cd /opt/Syn && ./gradlew build shadowJar'
  3. Push this to Github and slice a new version
    1. Note that this repository prepends a v to the release tag (i.e. use vX.X.X instead of just X.X.X)
  4. Upload both artifacts to the release in Github. These are located in /path/to/Syn/build/libs. You want both islandora-syn-X.X.X.jar and islandora-syn-X.X.X-all.jar.
  5. Bump the projectVersion in build.gradle and add -SNAPSHOT to the end again.
  6. Push this to Github with a commit message of \"Preparing for next development iteration\"
"},{"location":"contributing/releasing-islandora/#release-alpaca","title":"Release Alpaca","text":"

To make sure the release goes smoothly, you should ensure that:

  • You have an account with commit access for Alpaca on GitHub. As a committer, you should already have this level of access.
  • You have an oss.sonatype.org account and have requested to be given permission to publish to the ca.islandora groupId by adding a comment to the Islandora Sonatype Hosting Ticket
  • Ensure you have a trusted code signing key; create if you haven't before and add it to the contributor keys list below
  • Your gradle settings file (~/.gradle/gradle.properties) exists and includes the following:
    ossrhUsername = jiraname\nossrhPassword = jirapass\nsigning.keyId = pubkeyid\nsigning.password = keypassphrase\nsigning.secretKeyRingFile = /your/home/.gnupg/secring.gpg\n
  • Your git is configured (locally or globally) to cache github credentials for https or use ssh

Note: As of GPG 2.1 secring.gpg has been removed so you need to export secret keys to create the secret key ring.

gpg --export-secret-keys -o secring.gpg\n

"},{"location":"contributing/releasing-islandora/#steps","title":"Steps:","text":"

The following assumes you are using ssh (e.g. git@github.com for authentication).

It will also work for https if you properly cache your github credentials. The credentials must be cached and valid because Gradle will not prompt you for them.

"},{"location":"contributing/releasing-islandora/#release-artifacts-to-sonatype-and-github","title":"Release artifacts to Sonatype and Github","text":"

You need to merge the latest code into the master branch and use Gradle to release. You can do this by running

  • git clone git@github.com:Islandora-CLAW/Alpaca.git
  • cd Alpaca
  • git checkout master
  • git pull origin dev
  • ./gradlew release

The script will start but then will ask you to confirm the version to release as.

??> This release version: [0.3.1]  (WAITING FOR INPUT BELOW)\n<-------------> 0% EXECUTING [13s]\n> :release\n> :alpaca-release:confirmReleaseVersion\n
You then type in the version (ie. 0.4.0) or nothing to use the suggested version (0.3.1 in this example) and hit ENTER.

The gradle release task will take care of dropping -SNAPSHOT from the version, uploading artifacts to Maven central for staging, tagging and pushing a release to github, and bumping master of the Alpaca repository up by a point release for the next development iteration.

"},{"location":"contributing/releasing-islandora/#documentation","title":"Documentation.","text":"

The release process will also generate documentation at ./docs/<new-version>. This will need to be added to the git repository and pushed to the master branch:

  • git add docs/<new-version>
  • git commit -m \"Add documentation for <new-version>\"
  • git push origin
"},{"location":"contributing/releasing-islandora/#release-from-sonatype","title":"Release from Sonatype","text":""},{"location":"contributing/releasing-islandora/#point-of-no-return","title":"Point of no return","text":"

\u26a0\ufe0f The following steps, once completed are final. They cannot be undone, revoked or altered. Only proceed if you've completed all the above steps and are absolutely certain the release is ready for publication.

  • Login to https://oss.sonatype.org
  • Click on Staging Repositories on the left hand side under Build Promotion
  • On the far left hand side search box enter 'islandora'
  • Locate the artifacts you want to release and click on them
  • Click Close, then Refresh, then Release
"},{"location":"contributing/releasing-islandora/#contributor-keys","title":"Contributor Keys","text":"Name Organization Address Code Signing Key Fingerprint Key Id Danny Lamb Born-Digital hello at born-digital.com 2D609DB0380A7637A6B72B328D7E7725D47A05FA D47A05FA Jared Whiklo University of Manitoba jwhiklo at gmail.com 9F45FC2BE09F4D70DA0C7A5CA51C36E8D4F78790 D4F78790 Nick Ruest York University ruestn at yorku.ca 159493E15691C84D615B7D1B417FAF1A0E1080CD 0E1080CD Seth Shaw University of Nevada, Las Vegas seth.shaw at unlv.edu 2FF65B22AFA7B2A57F054F89D160AA658DAE385F D160AA658DAE385F"},{"location":"contributing/releasing-islandora/#jsonld","title":"JSONLD","text":"

Release the jsonld module by creating a new release for it in Github and on Drupal.org.

"},{"location":"contributing/releasing-islandora/#release-openseadragon","title":"Release OpenSeadragon","text":"

Release the openseadragon module by creating a new release for it in Github and on Drupal.org.

"},{"location":"contributing/releasing-islandora/#release-islandora-mirador","title":"Release Islandora Mirador","text":"

Release the islandora_mirador module by creating a new release for it in Github and on Drupal.org.

"},{"location":"contributing/releasing-islandora/#release-migrate_islandora_csv","title":"Release migrate_islandora_csv","text":"

Release the migrate_islandora_csv module by creating a new release for it in Github. This module is minimally maintained on Drupal.org and if you are updating it, consider editing that.

"},{"location":"contributing/releasing-islandora/#release-chullo","title":"Release Chullo","text":"

Release chullo by creating a new release for it in Github.

"},{"location":"contributing/releasing-islandora/#release-crayfish-commons","title":"Release Crayfish-Commons","text":"

Crayfish commons depends on the chullo library, and must have its dependencies updated before release.

  1. Bump the dependency on islandora/chullo from dev-dev to the release you just made in the previous step.
  2. composer update -W
  3. Commit and push the composer.json and composer.lock files to Github.
  4. Release crayfish-commons by creating a new release in Github
  5. Put the dependency on islandora/chullo back to dev-dev
  6. composer update -W again
  7. Commit and push the composer.json and composer.lock files to Github with a commit message of \"Preparing for next development iteration\".
"},{"location":"contributing/releasing-islandora/#release-crayfish","title":"Release Crayfish","text":"

Crayfish depends on the crayfish-commons library, and must have its dependencies updated before release.

  1. Bump the dependency on islandora/crayfish-commons in each composer.json for each microservice except for Houdini. Houdini needs the dev-symfony-flex branch of Crayfish Commons, so just leave that dependency alone for now.
  2. Run composer update -W on each microservice. I did this with a little bash-fu: for D in */; do (cd $D; composer update -W) done
  3. Commit and push the composer.json and composer.lock files to Github.
  4. Release the microservices by creating a new release for them in Github.
  5. Put the dependencies on islandora/crayfish-commons back to dev-dev except for Houdini.
  6. Run composer update -W on each microservice again. for D in */; do (cd $D; composer update -W) done makes this easy.
  7. Commit and push the composer.json and composer.lock files to Github with a commit message of \"Preparing for next development iteration\".
"},{"location":"contributing/releasing-islandora/#release-controlled-access-terms","title":"Release Controlled Access Terms","text":"

Release controlled_access_terms by creating a new release for it in Github and on Drupal.org.

"},{"location":"contributing/releasing-islandora/#release-islandora","title":"Release Islandora","text":"

Release islandora by creating a new release for it in Github and on Drupal.org.

"},{"location":"contributing/releasing-islandora/#undoing-a-release","title":"Undoing a Release","text":"

Dependencies mean that if you are going to release all of the Islandora ecosystem, order is very important. At any point, releases can be deleted, updated, and redone in Github. You can reach out to the community if you have questions. Note that if you want to 'redo' a release, you can follow these steps:

  1. Delete the release in Github through their UI
  2. Delete the tag in Git both locally and remotely: git tag -d TAG_NAME; git push --delete origin TAG_NAME
  3. Begin Releasing again.

You cannot follow these steps when publishing to Sonatype with Alpaca, but this should rarely be an issue. Instead, increment the version number and tag a new release.

Learn more about Islandora's Versioning Policy

"},{"location":"contributing/sandbox/","title":"Updating the Sandbox","text":"

Note

This page is about the online sandbox, https://sandbox.islandora.ca. If you are looking to set up your own site, see the Installation section.

The sandbox at https://sandbox.islandora.ca is built, configured, and deployed, by the https://github.com/Islandora-Devops/sandbox repository. This build process happens nightly from the Sandbox's latest release.

"},{"location":"contributing/sandbox/#maintaining-the-front-end-site","title":"Maintaining the front-end site","text":"

The Sandbox makes use of the following components, which are version-locked in the drupal/Dockerfile file:

  • Islandora Workbench
  • Islandora Demo Objects
  • Islandora Starter Site

In order to update them, you need to update the desired XXX_COMMIT hash; compute and add a sha-256 checksum; and then cut a release of the Sandbox repository. It will be deployed to the production site overnight. See full instructions at the Sandbox README.

Note that some of these components will need to be updated together, for example a new column in the Demo Objects may require a new field in the Starter Site.

"},{"location":"contributing/sandbox/#maintaining-the-back-end-containers","title":"Maintaining the back-end containers","text":"

The containers used in the Sandbox are specified by the ISLANDORA_TAG value in the .env file.

"},{"location":"contributing/testing-a-pull-request/","title":"Testing a Pull Request (i.e. Running Custom Code)","text":"

If you are testing a pull request, or for other reasons need to replace the \"official\" code with code that's in a PR, or a different branch or fork, this page offers three methods: using Composer Patches, using Composer to require the branch and/or fork, or installing source repositories with Composer.

This documentation applies to Drupal modules, themes, and recipes, or any other project that is managed by Composer.

Can't I just git clone?

When managing a non-developmental site with Composer, it's very fragile to use commands like git clone to obtain code, as it can be overwritten by a composer update.

For those who are comfortable cloning code onto a development environment, refer to Installing Git repositories with Composer.

"},{"location":"contributing/testing-a-pull-request/#applying-a-patch-using-composer-patches","title":"Applying a Patch using Composer Patches","text":"

This method is best for testing pull requests, because it's very easy to get a patch from a pull request. If the desired code is not the subject of a PR, you can still use this method but must generate a .patch file yourself. You may wish to use the fork or branch method, as it is more dynamic.

Run the following commands from within your site's root folder. They will update your composer.json file.

# Enable Composer Patches\ncomposer config allow-plugins.cweagans/composer-patches true\ncomposer require cweagans/composer-patches\n

For the next step, prepare the following replacement tokens:

  • MY_PACKAGE: The full Composer name of the package to patch. It takes the form [vendor name]/[project name]. Example: drupal/controlled_access_terms
  • MY_ISSUE_TITLE: A descriptive way to identify what the patch is for. Best practice is to include a link to the related issue - especially if you're going to keep this patch around for a while. Example: Updated config format https://github.com/Islandora/controlled_access_terms/issues/117
  • MY_PATCH_LOCATION: Where to access the patch. See below.

To get the URL of a patch for a PR, go to the PR's main URL, and append .patch to the URL. Make sure that your URL ends with pull/XX.patch and not pull/XX/files.patch - the latter will not work.

If you don't have a PR, you could create a patch using diff. However, this patch will be static and will need to be updated manually if your code changes. You can put patches in a folder in your root directory such as [COMPOSER_ROOT]/assets/ and then the patch location would be assets/my_patch_name.patch.

This one-liner, with the substitutions above, will add the patch to your composer.json:

# Add patch to composer.json\ncomposer config extra.patches --merge --json '{\"MY_PACKAGE\": {\"MY_ISSUE_TITLE\": \"MY_PATCH_LOCATION\"}}'\n

Or you could manually edit composer.json so it contains the following (... denotes omitted content):

{\n...\nextra: {\n...   patches: {\n\"MY_PACKAGE\": {\n\"MY_ISSUE_TITLE\": \"MY_PATCH_LOCATION\"\n}\n}\n}\n}\n

Then, update your package (recall e.g. drupal/controlled_access_terms) using Composer:

composer update MY_PACKAGE\n

The patch should apply, and then you will be running a patched version! If you're using a dynamic patch, then running composer update again should pull in changes to the code.

"},{"location":"contributing/testing-a-pull-request/#using-composer-to-require-a-fork-or-branch","title":"Using Composer to require a fork or branch","text":"

This method is best if you don't have a pull request open for the code.

"},{"location":"contributing/testing-a-pull-request/#step-1-add-a-repository-if-necessary","title":"Step 1: Add a repository (if necessary)","text":"

If your code is on a fork, then you will need to add a repository to Composer so that it knows where to get your package.

If the code that you want to test is a different branch/tag on the same repository that you're currently getting your code from, then you do not need to add a repository.

Prepare the following replacement tokens before adding a repository:

  • REPO_NAME: a name for this repository (mandatory if using the composer one-liner), e.g. rosiel-islandora
  • REPO_URL: the URL to the repository, e.g. https://github.com/rosiel/islandora

# Add custom repo\ncomposer config repo.REPO_NAME vcs REPO_URL\n
Your composer.json file should now contain

{\n...\n\"repositories\": {\n...\n\"REPO_NAME\": {\n\"type\": \"vcs\",\n\"url\": \"REPO_URL\"\n}\n}\n}\n

Order of precedence of repositories

If you have a matching version spec (e.g. branch name) that's available from multiple repositories, e.g. both islandora's Gitlab and your personal fork both have an enable-hocr branch, then the repository that's first in the list in composer.json will take precedence.

"},{"location":"contributing/testing-a-pull-request/#step-2-require-the-custom-branch","title":"Step 2: Require the custom branch","text":"

This step could be as simple as

composer require MY_PACKAGE:dev-MY_BRANCH_NAME\n

with the following replacements:

  • MY_PACKAGE: the full Composer name of the package. Example: drupal/islandora
  • MY_BRANCH_NAME: the name of the branch you want to run. Example: testing-fedora-6. Note that in the case that your branch name is \"version-like\" for example 2.x, then the dev goes at the end, as in 2.x-dev, instead of preceding the branch name as in the template above.

However, if your component is a dependency of another component, then you will probably need to use an alias. This allows your custom code to \"act as\" a version that will meet the requirements of your other component. For example, if the drupal/islandora_mirador package requires drupal/islandora:^2.4.1, then using Composer to require the enable-hocr branch of drupal/islandora will not meet the requirements. Instead, use as to provide an alias, to a version that will match the constraints. Note the quotes around the package name and version spec. This takes the form:

composer require \"MY_PACKAGE:dev-MY_BRANCH_NAME as ALIAS\"\n
For example:
composer require \"drupal/islandora:dev-enable-hocr as 2.12.1\"\n

That will install the specified branch and allow it to work with your dependencies.

"},{"location":"contributing/testing-a-pull-request/#installing-git-repositories-with-composer","title":"Installing Git repositories with Composer","text":"

This method pulls the source (i.e. Git) repositories directly into your project and will allow you to pull in open pull requests by simply following a normal Git workflow.

Note

If the site has already been installed without using --prefer-source you will need to clear Composer's cache via composer clearcache or including --no-cache for any install or reinstall commands (as below).

"},{"location":"contributing/testing-a-pull-request/#step-1-re-install-the-code-from-source-if-required","title":"Step 1: Re-install the code from source (if required)","text":"

Assuming that the environment has not been installed with --prefer-source, reinstall the package.

composer reinstall MY_PACKAGE --prefer-source\n
with the following replacements:

  • MY_PACKAGE: the full Composer name of the package. Example: drupal/islandora

This will pull the code from the source repository (i.e. including the .git files) and add the package code at the same version of the MY_PACKAGE that was previously installed and is in the lock file.

For example:

composer reinstall \"drupal/islandora\" --prefer-source --no-cache\n

"},{"location":"contributing/testing-a-pull-request/#step-2-pull-the-code-from-the-pull-request-to-review","title":"Step 2: Pull the code from the pull request to review.","text":"

Follow Github's documentation to pull the code from the pull request to review.

Now you can test your specified code, as well as edit the code and create commits. Note that if you're doing this in a throwaway environment such as a VM or a Docker Container, you will need to configure authentication (e.g. install an SSH key with Github) before you can push your commits.

"},{"location":"contributing/testing-a-pull-request/#to-reset-these-changes","title":"To reset these changes","text":""},{"location":"contributing/testing-a-pull-request/#using-composer-patches","title":"... using Composer Patches","text":"

When you no longer need to be applying the patch, simply remove it from the patches: section of composer.json (and of course, take care to ensure the json remains valid, by adjusting commas!) and run composer update MY_PACKAGE.

"},{"location":"contributing/testing-a-pull-request/#using-composer-require","title":"... using Composer require","text":"

When you no longer want to pull from a separate branch or fork, reset the version constraint back to what it used to be, or, if your package was not originally in composer.json (because it is required as a dependency by another package), you can delete the requirement from your composer.json. Then run composer update MY_PACKAGE. If you added a repository, it's safest to delete the repository, as it could lead to you getting stale branches from a fork rather than the desired active code from the canonical repository.

"},{"location":"contributing/testing-a-pull-request/#using-source-repositories","title":"... using source repositories","text":"

When you no longer want the custom code present simply reset the branch back to the default branch or tag.

More great information is available in the Composer Documentation.

"},{"location":"installation/component-overview/","title":"Component Overview","text":"

A functioning Islandora Stack is made up of dozens of components working in synchronization with each other to store information in your repository, manage that information, and disseminate it intelligently to users. Whether running an installation using the provided Ansible playbook or installing the stack manually, it may be helpful to have a brief overview of all the components we're going to need, in the order we're going to install them, as well as a brief introduction to each component's installation and configuration process.

This list includes four different kinds of components:

  • Components which are hard-required (such as Drupal and the Islandora module)
  • Components for which defaults are provided but which can be swapped out (such as the software managing databases, or the repository's storage system)
  • Components that can't easily be swapped out but are not necessarily required (such as using Solr as the site's internal search engine)
  • Components which do not have official alternatives and are not necessarily required, but will likely exist on the vast majority of Islandora installations (such as Alpaca and Crayfish)
"},{"location":"installation/component-overview/#the-webserver-stack-apache-php-and-mysqlpostgresql","title":"The Webserver Stack - Apache, PHP, and MySQL/PostgreSQL","text":"

Combined together, Apache, PHP, and MySQL/PostgreSQL comprise a LAMP or LAPP server used to provide end-user-facing components - namely, the website.

Apache is the webserver that will serve up webpages to the public. It will also manage some internal functionality provided by Crayfish, and will expose Cantaloupe to the public. We\u2019ll be making changes to the VirtualHost entry, enabling some modules, and modifying the ports configuration. The VirtualHost entry will eventually be modified when we need to expose other services like Cantaloupe to the public.

PHP is the runtime interpreter for all the code Drupal and Crayfish need to be processed. By default, installing PHP 7.2 will give us a command-line interpreter, as well as an interpreter for Apache. We\u2019re going to install several PHP modules required and/or useful for the components that make use of PHP.

MySQL and PostgreSQL are database management systems that we will use to store information for many different components like Drupal and Fedora. By default, the Ansible playbook installs MySQL, though this can be switched to PostgreSQL. The manual installation guide recommends and walks through installing and using PostgreSQL.

"},{"location":"installation/component-overview/#the-front-facing-cdm-composer-drush-and-drupal","title":"The Front-Facing CDM - Composer, Drush, and Drupal","text":"

Composer will be used to install both Drupal and Drush simultaneously using Islandora's fork of the drupal-project repository.

Composer is an installer and dependency manager for PHP projects. We're going to need it to install components for any PHP code we need to make use of, including Drupal and Crayfish.

Drush and Drupal are installed simultaneously using drupal-project. Drupal will serve up webpages and manage Islandora content, and Drush will help us get some things done from the command-line.

"},{"location":"installation/component-overview/#the-web-application-server-tomcat-and-cantaloupe","title":"The Web Application Server - Tomcat and Cantaloupe","text":"

Several applets will be deployed via their .war files into Tomcat, including Fedora and Cantaloupe.

Tomcat serves up webpages and other kinds of content much like Apache, but is specifically designed to deploy Java applications as opposed to running PHP code.

Cantaloupe is an image tileserver that Islandora will connect to and use to serve up extremely large images in a way that doesn't have an adverse effect on the overall system.

"},{"location":"installation/component-overview/#the-back-end-file-management-repository-fedora-syn-and-blazegraph","title":"The Back-End File Management Repository - Fedora, Syn, and Blazegraph","text":"

Fedora will be installed in its own section, rather than as part of the Tomcat installation, as the installation process is rather involved and requires some authorization pieces to be set up in order to connect them back to Drupal and other components.

Fedora is the default backend repository that Islandora content will be synchronized with and stored in. A great deal of configuration will be required to get it up and running, including ensuring a database is created and accessible.

Syn is the authorization piece that allows Fedora to connect to other components.

Blazegraph will store representative graph data about the repository that can be queried using SPARQL. Some configuration will also be required to link it back to Fedora, as well as to ensure it is being properly indexed.

"},{"location":"installation/component-overview/#the-search-engine-solr-and-search_api_solr","title":"The Search Engine - Solr and search_api_solr","text":"

The installation of Solr itself is rather straightforward, but a configuration will have to be generated and applied from the Drupal side.

Solr will be installed as a standalone application. Nothing of particular importance needs to happen here; the configuration will be applied when search_api_solr is installed.

search_api_solr is the Drupal module that implements the Solr API for Drupal-side searches. After installing and configuring the module, the drush solr-gsc command will be used to generate Solr configs, and these configs will be moved to the Solr configuration location.

"},{"location":"installation/component-overview/#the-asynchronous-background-services-crayfish","title":"The Asynchronous Background Services - Crayfish","text":"

Crayfish is a series of microservices that perform different asynchronous tasks kicked off by Islandora. It contains a series of submodules that will be installed via Composer. Later, these configured components will be connected to Alpaca.

"},{"location":"installation/component-overview/#the-broker-connecting-everything-karaf-and-alpaca","title":"The Broker Connecting Everything - Karaf and Alpaca","text":"

Karaf\u2019s job is similar to Tomcat, except where Tomcat is a web-accessible endpoint for Java applets, Karaf is simply meant to be a container for system-level applets to communicate via its OSGI. Alpaca is one such applet; it will broker messages between Fedora and Drupal, and between Drupal and various derivative generation applications.

Alpaca contains Karaf services to manage moving information between Islandora, Fedora, and Blazegraph as well as kicking off derivative services in Crayfish. These will be configured to broker between Drupal and Fedora using an ActiveMQ queue.

"},{"location":"installation/component-overview/#finalized-drupal-configurations","title":"Finalized Drupal Configurations","text":"

Drupal configuration exists as a series of .yaml files that can either be created in a feature, or exported from Drupal using the content_sync module. It can also be manually entered in via the UI. We're going to place configuration in a few different ways; Some content will be synchronized onto the site, and some core configurations from the main Islandora module will need to be run in order to facilitate ingest.

"},{"location":"installation/install-a-demo/","title":"Portainer Docker Demo","text":"

To test out Islandora, there is a Starter-Site based Portainer demo available (Download the .zip file). This demo is loaded with sample content to demonstrate features of Islandora and provide a starting point for exploration. Follow the instructions in the README.md.html file provided in the download.

It is recommended that you have a minimum of 4GB of hard drive space available on your machine before getting started. It will take some time to build the image, do not be surprised if it takes a long time. Thanks, and happy building!

"},{"location":"installation/install-a-demo/#installation-video-tutorial","title":"Installation Video Tutorial","text":"

The README.md.html is the most up to date version of the installation documentation, but you can also view this video: How to Install Docker Desktop and Set up Starter Site (video tutorial).

"},{"location":"installation/install-a-demo/#troubleshooting","title":"Troubleshooting","text":"

These troubleshooting issues and solutions for Docker Desktop have arised from and been used with Windows 10.

"},{"location":"installation/install-a-demo/#docker-failed-to-start-error-message","title":"\"Docker Failed to Start\" Error Message","text":"

For Windows 10, you may receive the \"Docker Failed to Start\" Error message. To resolve this:

  • Download WSL 2 from Step 4 in these instructions, but do not do any other steps in these instructions.
  • Under Windows \u2018Firewall & Network Protection\u2019, \u2018Allow an app through the firewall\u2019, and \u2018Change settings\u2019 to select the \u2018Private\u2019 box for \u2018Virtual Machine Monitoring\u2019.
  • Restart computer for all changes to take effect.
"},{"location":"installation/install-a-demo/#no-content-showing-on-islandoraio","title":"No Content Showing on islandora.io","text":"

If no content is showing up in your local installation when on islandora.io in a browser, you may need to wait longer for the deployment process to complete. 1. Go to \u2018containers\u2019, click the \u2018logs\u2019 for \u2018sandbox-drupal-1\u2019 to see the deployment process. When completed you will see:

#####################\n# Install Completed #\n#####################\n
* This can take anywhere from 5 minutes to over an hour.

"},{"location":"installation/install-a-demo/#docker-desktop-and-virtual-machine-vmmem-in-task-manager-taking-up-too-much-cpu-and-ram","title":"Docker Desktop and Virtual Machine (Vmmem in Task Manager) Taking up too much CPU and RAM","text":"

If Docker Desktop and Virtual Machine (Vmmem in Task Manager) are taking up too much CPU and RAM, you can create a .wslconfig file to limit how much of your computers resources are allocated to these processes: 1. Open Notepad++ and type the following:

[wsl2]\nmemory=4GB\nprocessors=2\n
2. Modify the numbers to reflect how much you want to allocate to Docker Desktop (i.e. if you only have 4GB of RAM then allocate 2GB). * Save As and select \u2018All File Types\u2019 from the dropdown. * Name the file x.wslconfig and save it. * Go to the location of the saved file and rename it, removing the \u2018x\u2019. * The file should look like this: 3. Put the .wslconfig file in the appropriate location. * In your File Explorer address bar type \u2018%UserProfile% and hit enter. * Move the .wslconfig file to this folder. 4. Restart your computer and start up Docker Desktop. It will now be using only the specified memory and processors.

"},{"location":"installation/playbook/","title":"Islandora Playbook","text":"

The Islandora Playbook (Islandora-Devops/islandora-playbook) is a tool for installing the Islandora stack on a single virtual machine. It can be used both as a Vagrant project to create a local development environment, or as an Ansible playbook to provision a local or remote server. It can set up a Drupal based either on Islandora Starter Site, or your own custom site configs.

"},{"location":"installation/playbook/#basic-usage-vagrant","title":"Basic Usage (Vagrant):","text":"

Install requirements (below), then:

$ git clone -b dev https://github.com/Islandora-Devops/islandora-playbook\n$ cd islandora-playbook\n
First, create the islandora base box:
$ ISLANDORA_BUILD_BASE=true vagrant up\n$ vagrant package --output islandora_base\n$ vagrant destroy\n
Second, build a VM using the islandora base box:
$ vagrant up\n

When used this way, you can trash your working site and get a fresh Islandora relatively quickly, with vagrant destroy (you will be asked to confirm, as this will delete all your changes and your content), and then vagrant up.

Full instructions below.

"},{"location":"installation/playbook/#requirements-vagrant","title":"Requirements (Vagrant)","text":"

To create a local VM, download and install the following.

  1. Virtual Box
  2. Vagrant (version 2.0 or higher required)
  3. Git
  4. OpenSSL
  5. Ansible (Tested on version 2.11+, versions back to 2.9 should work.)
"},{"location":"installation/playbook/#installing-git-and-ansible-on-macos","title":"Installing Git and Ansible on MacOS","text":"

OpenSSL is already pre-installed on MacOS. Git can be installed using XCode's command line tools (see below). Python and Pip can either be installed via the downloaded installer direct from the site or via Homebrew (not shown below). Ansible is best installed using Homebrew (see below).

# Use xcode-select to install command line components, including git\n$ xcode-select --install\n# Install homebrew\n$ /usr/bin/ruby -e \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)\"\n# Use homebrew to install ansible\n$ brew install ansible\n
"},{"location":"installation/playbook/#installing-a-local-development-environment-vagrant","title":"Installing a local development environment (Vagrant)","text":"

You will use vagrant up twice, first to create an Islandora base box, and then again to provision that base box into a full Islandora. This method uses Vagrant, VirtualBox, and Ansible.

Warn

Make sure that no required ports are currently in use.

"},{"location":"installation/playbook/#clone-the-playbook","title":"Clone the playbook","text":"
$ git clone -b dev https://github.com/Islandora-Devops/islandora-playbook\n$ cd islandora-playbook\n
"},{"location":"installation/playbook/#create-the-base-box","title":"Create the Base Box","text":"

The Playbook will create a \"base box\" that includes core software (PHP, Apache, etc). Since these do not need to be updated too often, this can be packaged as a Vagrant base box that will be used to quickly provision the Islandora part.

Notes: - If building a CentOS box, you also need to install the vbguest additions with vagrant plugin install vagrant-vbguest. - If this is not your first time spinning up the Islandora Playbook from this directory, you may want to clear cached ansible roles in roles/external\" rm -rf roles/external

First, create the islandora base box by overriding the Vagrant variable ISLANDORA_BUILD_BASE (which defaults to false in the Vagrantfile):

$ ISLANDORA_BUILD_BASE=true vagrant up\n

Once it is complete (without errors), store it as a Vagrant base box. It will appear as islandora_base in the current directory.

$ vagrant package --output islandora_base\n

Then, get rid of the existing VM since we've saved what we need:

$ vagrant destroy\n     default: Are you sure you want to destroy the 'default' VM? [y/N] y\n==> default: Forcing shutdown of VM...\n==> default: Destroying VM and associated drives...\n

"},{"location":"installation/playbook/#provision-the-islandora-code","title":"Provision the Islandora code","text":"

Then, to install Islandora (including Drupal and its configuration, Crayfish, Alpaca, etc) on top of the new base box:

vagrant up\n

Access your site at http://localhost:8000.

"},{"location":"installation/playbook/#rebuilding-your-islandora-development-box","title":"Rebuilding your Islandora development box","text":"

You can quickly create a new \"out of the box\" Islandora VM by just re-doing the last step (provisioning Islandora) while using your existing base box. In doing this, you will destroy your existing VM, and all of its configuration and data. This will not upgrade PHP, apache, or other back-end services (see below, \"Refreshing your base box.\")

vagrant destroy\n
You will be asked to confirm, and Vagrant will inform you it is destroying the VM and associated drives.
vagrant up\n
This will create a new development environment on the existing islandora_base box.

"},{"location":"installation/playbook/#refreshing-your-base-box","title":"Refreshing your base box.","text":"

If you want to use the Playbook to rebuild the base box (e.g. if this playbook now includes updated versions of PHP or Apache), then you will need to take some extra steps to ensure that Vagrant sees your new base box.

vagrant destroy  # Delete your existing VM\nvagrant box remove islandora_base  # clear Vagrant's cached version of islandora_base\nISLANDORA_BUILD_BASE=true vagrant up # and proceed to package the box, as above.\n

This is because once you use the base box once, it is stored in the vagrant box list under your home directory. While subsequent builds of ISLANDORA_BUILD_BASE=true vagrant up will build a new box and you can package it, but that file does not update the cached box and consequently you still get the old base box when you try build a VM. See vagrant box documentation for more useful commands for managing base boxes.

"},{"location":"installation/playbook/#deploying-to-a-remote-environment","title":"Deploying to a remote environment","text":"

The ansible-playbook command using playbook.yml can provision a remote environment. First, you will need to create a new folder in inventory/ with the details of your remote deployment. This includes:

  • Configuring the SSH parameters so Ansible can log in
  • Changing usernames and passwords to something more sensible than the default
  • Changing IP addresses to use the remote machine's IP
  • Changing Apache to serve at port 80 (as opposed to 8000, which we use for development purposes)

Before beginning, you may want to:

  • Create a non-root user on the remote machine. Ansible will need to \"escalate\" (i.e. sudo) as this user. See Ansible Docs on Understanding privilege escalation: become
  • Ensure all ports except the Drupal port and ports used by Ansible are behind a firewall.

We're going to build up this new remote environment configuration from the default provided Vagrant configuration. To start, take the inventory for the vagrant development environment and make a copy of it. Be sure to give it an appropriate name. Here we're using production.

$ git clone https://github.com/Islandora-Devops/islandora-playbook\n$ cd islandora-playbook\n$ cp -r inventory/vagrant inventory/production\n

Then you can update the following entries in the following files using your own information. If an entry does not exist in a file, just add it. Ansible will then use the value you provide instead of relying on its defaults.

We're using changeme to represent passwords and assume the server will be available at example.org, but you'll want to provide your own values.

"},{"location":"installation/playbook/#group_varsallpasswordsyml","title":"group_vars/all/passwords.yml","text":"
# Drupal\ndrupal_db_password: changeme\ndrupal_account_pass: changeme\n\n# MySQL/Postgres\nislandora_db_root_password: changeme\n\n# Tomcat\nislandora_tomcat_password: changeme\n\n# Syn\nislandora_syn_token: changeme\n\n# Cantaloupe\ncantaloupe_admin_password: changeme\n\n# Fedora\nfcrepo_db_password: changeme\n
"},{"location":"installation/playbook/#group_varswebserverapacheyml","title":"group_vars/webserver/apache.yml","text":"

This is where we specify that the webserver is listening on the default port 80, instead of the development machine port 8000.

apache_listen_port: 80\n

"},{"location":"installation/playbook/#group_varswebservergeneralyml","title":"group_vars/webserver/general.yml","text":"

You will have to add the matomo line.

matomo_site_url: http://example.org\n
"},{"location":"installation/playbook/#group_varswebserverapacheyml_1","title":"group_vars/webserver/apache.yml","text":"

If you have a domain name, change the default to your domain name.

 - servername: \"myactualddomain.com\"\n
"},{"location":"installation/playbook/#group_varswebserverdrupalyml","title":"group_vars/webserver/drupal.yml","text":"

If you have a domain name, change the default to your domain name. And set the Drupal site name to whatever you need it to be.

drupal_domain: \"myactualdomain.com\"\ndrupal_site_name: \"Example Sitename\"\n
Also set your domain in drupal_trusted_hosts:

drupal_trusted_hosts:\n  - ^localhost$\n  - \"{{ hostvars[groups['webserver'][0]].ansible_host }}\"\n  - '^myactualdomain\\.com$'\n

Note the backslash which escapes the period (which would otherwise match any character). Because of this escape character, the string needs to be surrounded by single quotes.

"},{"location":"installation/playbook/#hosts","title":"hosts","text":"

You'll need to put particulars for logging into your server in the inventory/production/hosts file . This example is set up to login as the ansible user (to avoid trying to run Composer as root) and uses an SSH key. You'll need to get the details for logging into your remote server from your hosting provider (AWS, Digital Ocean, etc...) or your systems administrator if you're running the server in-house. See this page for more details about what you can put into a host file

default ansible_host=example.org ansible_port=22 ansible_user=ansible ansible_ssh_private_key_file='/home/username/.ssh/id_rsa'\n
"},{"location":"installation/playbook/#running-the-remote-installer","title":"Running the remote installer","text":"

First, you'll want to install the ansible roles that are needed for the version of Islandora you are trying to install. This can be done with

$ ansible-galaxy install -r requirements.yml\n

Then, if you are on Ubuntu 22.04, run the following commands:

$ ansible-playbook -i inventory/production playbook.yml -e \"islandora_distro=ubuntu/jammy64\" -e \"islandora_profile=starter\" -e \"islandora_build_base_box=true\"\n$ ansible-playbook -i inventory/production playbook.yml -e \"islandora_distro=ubuntu/jammy64\" -e \"islandora_profile=starter\" -e \"islandora_build_base_box=false\"\n
"},{"location":"installation/playbook/#troubleshooting","title":"Troubleshooting","text":""},{"location":"installation/playbook/#out-of-date-playbooks","title":"Out of date playbooks","text":"

Ansible caches the code used to provision the environment, so if you've already installed once you may not be getting the latest version of things even if you've git pull'd the latest playbook. The code is stored in roles/external, so if you want to clear it out you can remove these before attempting to provision an environment

$ rm -rf roles/external\n
"},{"location":"installation/playbook/#port-clashes-for-local-environments","title":"Port clashes for local environments","text":"

When provisioning using a local environment, you should be aware of any ports that are already in use by your computer that are also going to be used by Vagrant, as these may clash and cause problems during and after provisioning. These include:

  • 8000 (Apache)
  • 8080 (Tomcat)
  • 3306 (MySQL)
  • 5432 (PostgreSQL)
  • 8983 (Solr)
  • 8161 (ActiveMQ)
  • 8081 (API-X)

If there are port clashes for any of these, you will need to either find and replace them in the configuration .yml files under inventory/vagrant/group_vars, or provide new values for the different playbooks that support changing the ports (for example, postgresql_databases supports adding a port property which is currently simply unused). You will also need to replace the port forwarding values in Vagrantfile.

Additionally, Ansible attempts to use port 2200 for SSH. If this port is already in use, your local environment cannot be provisioned. To change this, set a new value for ansible_port in inventory/vagrant/hosts.

"},{"location":"installation/playbook/#help","title":"Help","text":"

If you run into any issues installing the environment, do not hesitate to email the mailing list to ask for help. If you think you've stumbled across a bug in the installer, please create an issue in the Islandora issue queue and give it an ansible tag.

"},{"location":"installation/quickstart/","title":"Quickstart","text":"

There are several ways to try Islandora, which are presented here in order of increasing sophistication. By default, they all install the Islandora Starter Site which is a starting place for customizing your own site, as well as our suite of external services.

"},{"location":"installation/quickstart/#online-sandbox","title":"Online Sandbox","text":"

Try Islandora without installing anything at sandbox.islandora.ca. Login credentials for the sandbox can be found on our Wiki.

Anyone can log in to this sandbox as an administrator and explore the interface! However, this site is refreshed every night so your changes will not be permanent. This site uses the Islandora Starter Site. This sandbox includes some sample content and configuration (such as views and blocks) to increase its usefulness as a sandbox. .

"},{"location":"installation/quickstart/#docker-portainer-demo","title":"Docker Portainer Demo","text":"

Alternately, with Docker Desktop, you can run a demo locally using Docker's Portainer extension. The command line is not required. See Docker Portainer Demo for instructions. Note that this method does not support installing modules or themes that are not included with the demo.

"},{"location":"installation/quickstart/#sandbox-running-locally","title":"Sandbox running locally","text":"

Alternately, you can use Docker and install the Online Sandbox repository locally, with minimal command-line usage. Note that this method does not support installing modules or themes that are not included with the demo. See the instructions for \"Running Locally\" in the Sandbox's README.

"},{"location":"installation/quickstart/#ansible-playbook","title":"Ansible Playbook","text":"

To provision a local Vagrant or remote Ubuntu virtual machine (without Docker), you can use the Islandora Ansible Playbook. The playbook results in all services installed on a single machine, but can be altered to spread services across various machines. This is a full-fledged VM where you can install modules and themes using Composer. This method requires basic command-line usage and it's advantageous if you are familiar with provisioning software on Ubuntu. This Playbook is suitable for local or production use, though local use (through VirtualBox and Vagrant) is not supported yet by Apple hardware (i.e. M1/M2 machines). See documentation: Installation - Ansible Playbook for more details.

"},{"location":"installation/quickstart/#isle-site-template","title":"ISLE Site Template","text":"

ISLE Site Template uses Docker and is based off images created by ISLE Buildkit, but is a simpler tool than ISLE-DC. This is a full-fledged Docker installation where you can install modules and themes using Composer, either by executing commands in the container or by using the built-in IDE. It is suitable for local development or production. See documentation: Installation - Site Template for more details.

"},{"location":"installation/quickstart/#isle-dc","title":"ISLE-DC","text":"

ISLE-DC uses Docker and provisions each service in the Islandora stack in a separate container. The containers are also based off of the images in ISLE Buildkit. ISLE-DC uses the GNU Make tool to provide several shortcuts to performing common management functions. It is suitable for local development or production. See documentation: Installation - Docker ISLE for more details.

"},{"location":"installation/docker/converting/","title":"Converting ISLE-DC to Site Template","text":"

The following instructions explain how to convert your ISLE-DC site into the Site Template style of docker-compose.yml.

"},{"location":"installation/docker/converting/#development-instructions","title":"Development Instructions","text":"

ISLE-DC and the Site Template both use isle-builkit images to spin up an Islandora site. Since the images are the same, you can transfer an ISLE-DC site to a Site Template setup by doing the following:

  1. Follow the instructions in the Site Template README to create a new site from the Islandora Starter Site

    Note

    Use the same TAG in your Site Template .env that you used in ISLE-DC, so you have the same set of containers.

  2. Replace the Starter Site's composer.json and composer.lock files with yours, and install the correct Drupal modules using Composer

    Note

    You may need to make some modifications to your composer.json file. Specifically, the scripts and drupal-scaffold sections may have changed.

    You can look at the most recent starter site composer.json for comparison.

    If you have custom modules and/or themes that are not installed via Composer you will need to install those as well.

  3. Compare your old Dockerfile with the one in the Site Template repository. If you have made customizations, you may want to replicate them in the new Dockerfile.

  4. Backup your ISLE-DC site's Fedora, Drupal database, and public/private files.

  5. Import your backups to the new Site Template site.

  6. Delete your Solr core, and regenerate new configs.

    Note

    Site Template uses a different core name than ISLE-DC did

  7. Commit and push your git repository so it is ready for production.

"},{"location":"installation/docker/converting/#production-instructions","title":"Production Instructions","text":"

Once you have converted the development instance of your site, moving it to production requires the following:

  1. Clone the git repository that you set up in the development instructions above

  2. Prepare your images/containers as described in the Site Template README

    1. Run the generate secrets script included with the site template, or copy your secrets from your existing site if you want them to be the same
    2. Add custom modules or themes, if you have some that are not included in your composer.json
    3. Build your Drupal container with docker compose --profile prod build
    4. Pull in the remaining containers with docker compose --profile prod pull --ignore-buildable --ignore-pull-failures
    5. Start the containers with docker compose --profile prod up -d
  3. Import the backups from your ISLE-DC site that you made in the development instructions above

  4. Delete your Solr core, and regenerate new configs.

"},{"location":"installation/docker/converting/#converting-your-docker-composeyml","title":"Converting Your docker-compose.yml","text":"

The main difference between ISLE-DC and the Site Template is the way the docker-compose.yml file is generated. In ISLE-DC we generate it based on your .env variables and a make command, but in Site Template it is ready to go out of the box.

This means that in ISLE-DC you had separate docker-compose.yml files for development and production, but in site template there is one file for both, which contains instructions for a \u201cdev\u201d and \u201cprod\u201d profile. The end result is the same set of containers, but instead of running docker compose up you would run docker compose --profile dev up

"},{"location":"installation/docker/converting/#environment-variables","title":"Environment Variables","text":"

You should compare the environment sections of your docker-compose.yml files. You may have some variables set in your ISLE-DC docker-compose.yml that will need to be added to the docker-compose.yml in the new Site Template site.

For example, the ISLE Buildkit Nginx README lists the available variables for Nginx that you may have changed on your Drupal containers to do things like increase the timeout time for PHP or the max POST size PHP will accept.

"},{"location":"installation/docker/converting/#urls","title":"URLs","text":"

The Site Template also handles the URLs for microservices differently. You will need to follow the instructions in the Site Template README to allow access to your microservices as a subdomain. For example, solr.mydomain.com.

Subdomains

If you are already using a subdomain and can\u2019t use URLs like solr.mysubdomain.mydomain.com, you can change this in the Traefik section of the docker-compose.yml file, by changing the aliases from solr.{DOMAIN} to something else, like solr-{DOMAIN}. You will also need to change this in a few other spots in your docker-compose.yml. You can find them by searching for .{DOMAIN}

"},{"location":"installation/docker/converting/#converting-the-makefile","title":"Converting the Makefile","text":"

Because ISLE-DC and Site Template use the same containers, much of what is in an ISLE-DC Makefile will work within the site template environment. The main differences that need to be addressed are with the way new sites are built, and the name of the containers.

Commands to build a site, like make starter and make production are not necessary with the Site Template, so they can be removed from your Makefile.

Many of the other commands specify which containers to run commands in, for example docker compose exec drupal, but in the Site Template environment you need to specify -dev or -prod, so that would be changed to docker compose exec drupal-dev or docker compose exec drupal-prod.

"},{"location":"installation/docker/converting/#other-customizations","title":"Other Customizations","text":"

If you have modified your ISLE-DC repository, those modifications will need to be evaluated on a case-by-case basis. This includes editing the Dockerfile and environment variables mentioned above, as well as any other customizations you may have made, such as to settings.php, robots.txt, nginx configs, etc.

"},{"location":"installation/docker/docker-introduction/","title":"Introduction to ISLE","text":""},{"location":"installation/docker/docker-introduction/#what-is-isle","title":"What is ISLE?","text":"

ISLE, short for ISLandora Enterprise, is a community initiative to ease the installation and maintenance of Islandora by using Docker. It was originally started by the Islandora Collaboration Group for Islandora Legacy. When transitioning to Islandora, the project came under the purview of the Islandora Foundation. All documentation on this site assumes you're trying to install Islandora. See here if you are looking for ISLE for Islandora Legacy.

"},{"location":"installation/docker/docker-introduction/#why-use-isle","title":"Why use ISLE?","text":"

ISLE's architecture using Docker separates out the \"state\" of your site (i.e. all the content, files, and configurations that you've entered) from the underlying software that runs it (e.g. webserver, database, microservices, etc). This allows for easier upgrades, faster development, and more flexible deployment. It is hands down the easiest way to install, run, and maintain and Islandora instance.

"},{"location":"installation/docker/docker-introduction/#where-is-isle","title":"Where is ISLE?","text":"

ISLE is a suite of Docker containers that run the various components of Islandora: drupal, fedora, solr, alpaca, crayfish, matomo, etc. The individual containers are created (and automatically pushed to Docker Hub) by ISLE BuildKit.

In order to deploy the containers, however, you need to use a container orchestration tool. The ISLE project provides tools for running and maintaining the containers using docker-compose with ISLE Docker Compose or ISLE Site Template.

"},{"location":"installation/docker/docker-prereq/","title":"Prerequisites","text":""},{"location":"installation/docker/docker-prereq/#software-involved-in-isle","title":"Software involved in ISLE","text":"

ISLE uses the following software in order to help you set up and run your site. If you are new to any of the following software, that's OK, but learning more about each piece will make it much easier to modify ISLE to suit your particular needs, and maintain your Islandora site.

The following pieces of software are not unique to Islandora, and there are many great resources online to learn more about them. Below is a brief introduction to each piece and how they fit into your Islandora site. There are also many great videos on the Islandora Foundation's YouTube page if you would like to learn more about how they are used within the context of Islandora.

"},{"location":"installation/docker/docker-prereq/#git","title":"Git","text":"

Git is a version control system that is used to store the code for Islandora. When you first download ISLE you will be downloading it using Git. Git is also useful for managing your site's Drupal codebase, once you have a site up and running.

More about using Git for Drupal sites can be found in the Drupal documentation.

"},{"location":"installation/docker/docker-prereq/#docker","title":"Docker","text":"

Docker allows us to run our Islandora sites in virtualized environments, called containers. Each container runs one part of the Islandora software, and it takes several containers to run a complete Islandora site.

The three main components of Docker are Images, Containers, and Volumes.

"},{"location":"installation/docker/docker-prereq/#images","title":"Images","text":"

Images are what our containers are built from. Images are read-only, and allow us to quickly create containers based on them.

The Islandora images are provided by Islandora Buildkit.

"},{"location":"installation/docker/docker-prereq/#containers","title":"Containers","text":"

Containers are where the Islandora software actually runs. ISLE gives us tools to easily download the Islandora images and start our containers from them.

"},{"location":"installation/docker/docker-prereq/#volumes","title":"Volumes","text":"

Volumes are where our persistent storage lives. Because containers are often rebuilt from images, any data stored in them is easily lost. Volumes allow us to store specific parts of our containers that we don't want to lose. For example, our Drupal database will live in a volume so that it is not lost when a container is shut down.

"},{"location":"installation/docker/docker-prereq/#docker-compose","title":"Docker Compose","text":"

Docker Compose is a tool to simplify the process of running multiple Docker containers for a single project. It uses a file called docker-compose.yml to store the settings for your project's containers. ISLE gives us tools to create this docker-compose.yml file, so we can use it to manage the containers for our site.

"},{"location":"installation/docker/docker-prereq/#gnu-make","title":"GNU Make","text":"

Make allows us to define commands that simplify installing and maintaining our Islandora site. For a complete list of available commands see the Makefile included with ISLE-DC.

"},{"location":"installation/docker/docker-prereq/#composer","title":"Composer","text":"

Composer is a dependency manager for PHP, and is the recommended way to install and update Drupal modules. Composer is installed in the Islandora Drupal container and should be run within the container to manage your Drupal site. More information on using Composer with Drupal is available in the Drupal documentation.

"},{"location":"installation/docker/docker-prereq/#drupal","title":"Drupal","text":"

Drupal is a Content Management System used to create websites. During the setup of your Islandora site, a Drupal site is created for you in your Drupal container, by the commands in the Makefile.

"},{"location":"installation/docker/docker-prereq/#drush","title":"Drush","text":"

Drush is a command line tool for managing your Drupal site. It comes installed in your Drupal container and allows you to perform Drupal actions from the command line inside your container.

"},{"location":"installation/docker/docker-prereq/#requirements-for-using-isle-with-docker-compose","title":"Requirements for using ISLE with Docker Compose","text":"
  • Docker 19.x+
  • Docker Compose version 2.x+
  • GNU Make 4.0+
  • Git 2.0+
  • At least 8GB of RAM (ideally 16GB)
  • An administrator account your machine (a.k.a. the host machine)
  • (Mac OS) Apple Developer Tools
  • (Windows) The following setup has been tested:
    • Windows 10
    • Windows Subsystem for Linux v. 2 (WSL 2)
    • Ubuntu 20.04 running on WSL 2
    • GNU make, run sudo apt update and sudo apt install make to install
    • Docker Desktop for Windows, using the WSL 2 based engine (Settings > General) and with the WSL integration active for Ubuntu (Settings > Resources > WSL integration)

What are we missing?

Are you, or your computer, new to spinning up development sandboxes? Do you have wisdom about installing make on Windows? We suspect these requirements may be incomplete and your experience would be appreciated at Issue #1640.

"},{"location":"installation/docker/docker-prereq/#installing-docker","title":"Installing Docker","text":"

To see if you have Docker installed, type docker --version in a terminal.

If you need to install Docker, we recommend using the application Docker Desktop. It provides a GUI for managing Docker container in Windows and MacOS, along with the Docker engine and suite of command-line tools. Linux users don't get a desktop client, but can install the Engine and command-line tools the instructions here.

Memory, Processors, and Swap Requirements

To run ISLE on Docker Desktop, you must increase the resources allocated to the software. See Docker docs on setting resources on Windows (see note on how to allocate/restrict memory when using WSL 2) or setting resources on Mac.

CPUs (processors): The CPUs allowed to Docker Desktop are still shared with the host machine, so increasing this to the maximum value should allow both the Docker containers and your host machine to run simultaneously.

Memory (RAM): This memory is completely dedicated to Docker while Docker Desktop is running, so do not allocate more than you can spare and still run your host machine. Windows users may not require as much memory for Docker as Mac users. Current suggestions for memory allocated to Docker Desktop are below, but please edit this document if you have new information.

  • Sandbox or development environment: 8GB
  • Production or production-like development: 16GB

Swap: Swap space is space borrowed from your hard disk drive to serve as makeshift RAM as needed. If you cannot provide as much RAM as you would like, increase this as is reasonable given your free disk space.

"},{"location":"installation/docker/isle-dc/docker-available-commands/","title":"Available Commands","text":"

There's a lot of useful commands available to you from within the isle-dc folder.

"},{"location":"installation/docker/isle-dc/docker-available-commands/#rebuild-docker-composeyml","title":"Rebuild docker-compose.yml","text":"

make -B docker-compose.yml will rebuild your docker-compose.yml file based on configuration in your .env file. make demo and make local will automatically do this for you, but if you change configuration at a later point in time, you'll need to run this command to see your changes reflected.

"},{"location":"installation/docker/isle-dc/docker-available-commands/#config-export","title":"Config Export","text":"

make config-export will export your site's configuration to your site's config sync directory (usually config/sync inside your Drupal root folder).

"},{"location":"installation/docker/isle-dc/docker-available-commands/#config-import","title":"Config Import","text":"

make config-import will import site's configuration from your site's config sync directory (usually config/sync inside your Drupal root folder).

"},{"location":"installation/docker/isle-dc/docker-available-commands/#database-dump","title":"Database Dump","text":"

make drupal-database-dump DEST=/your/path/dump.sql will dump your Drupal database and place the file at DEST.

"},{"location":"installation/docker/isle-dc/docker-available-commands/#database-import","title":"Database Import","text":"

make drupal-database-import SRC=/your/path/dump.sql will import your Drupal database from the file at SRC.

"},{"location":"installation/docker/isle-dc/docker-available-commands/#export-public-files","title":"Export Public Files","text":"

make drupal-public-files-dump DEST=/your/path/public_files.tgz will export your public filesystem and place it as a single zipped tarball at DEST.

"},{"location":"installation/docker/isle-dc/docker-available-commands/#import-public-files","title":"Import Public Files","text":"

make drupal-public-files-import SRC=/your/path/public_files.tgz will import your public filesystem from a single zipped tarball at SRC.

"},{"location":"installation/docker/isle-dc/docker-available-commands/#fcrepo-export","title":"Fcrepo Export","text":"

make fcrepo-export DEST=/your/path/fcrepo-export.tgz will export your Fedora repository and place it as a single zipped tarball at DEST

"},{"location":"installation/docker/isle-dc/docker-available-commands/#fcrepo-import","title":"Fcrepo Import","text":"

make fcrepo-import SRC=/your/path/fcrepo-export.tgz will import your Fedora repository from a single zipped tarball at SRC

"},{"location":"installation/docker/isle-dc/docker-available-commands/#reindex-fedora-metadata","title":"Reindex Fedora Metadata","text":"

make reindex-fcrepo-metadata will reindex RDF metadata from Drupal into Fedora. Requires the Views Bulk Operations Module.

"},{"location":"installation/docker/isle-dc/docker-available-commands/#reindex-solr","title":"Reindex Solr","text":"

make reindex-solr will rebuild rebuild Solr search index for your repository.

"},{"location":"installation/docker/isle-dc/docker-available-commands/#reindex-the-triplestore","title":"Reindex the Triplestore","text":"

make reindex-triplestore will reindex RDF metadata from Drupal into Blazegraph. Requires the Views Bulk Operations Module.

"},{"location":"installation/docker/isle-dc/docker-available-configuration/","title":"Available Configuration","text":"

ISLE Docker Compose has a single configuration file, .env. Within it, here is all the values you can set and their effects. This list is subjectively sorted in order of importance and usefulness.

"},{"location":"installation/docker/isle-dc/docker-available-configuration/#environment","title":"ENVIRONMENT","text":"

Setting the ENVIRONMENT changes how your docker-compose.yml file gets constructed. When you switch from a demo to development to production environment, this is the variable to change.

Available values for this setting are

  • demo - For demo environments where you do not need access to the codebase
  • local - For development environments where you need edit the codebase
  • custom - For production environments where your codebase gets baked into a custom container

By default, this is set to demo.

If you change this setting, you need to make -B docker-compose.yml to generate a new docker-compose.yml file that contains the changes you've made.

"},{"location":"installation/docker/isle-dc/docker-available-configuration/#domain","title":"DOMAIN","text":"

What domain your Islandora site will be available at. If you are deploying anywhere other than your personal machine, you'll probably want to change this.

By default, this is set to islandora.traefik.me.

If you change this setting, you need to make -B docker-compose.yml to generate a new docker-compose.yml file that contains the changes you've made.

Changing this after installation has consequences

If you are indexing RDF metadata in Fedora and/or a triplestore, please be aware of the implications of changing this once you have installed. By changing your domain, all of the subjects of your triples will be incorrect and you will have to reindex. There are make commands to streamline this process, but if you have lots of data it can potentially be costly in terms of time and effort.

"},{"location":"installation/docker/isle-dc/docker-available-configuration/#repository","title":"REPOSITORY","text":"

Repository to use for pulling isle-buildkit images.

By default, this is set to islandora. Change to local to use images you have built locally with isle-buildkit, or use your custom docker registry if you have set up one.

If you change this setting, you need to make -B docker-compose.yml to generate a new docker-compose.yml file that contains the changes you've made.

"},{"location":"installation/docker/isle-dc/docker-available-configuration/#tag","title":"TAG","text":"

The version of the isle-buildkit images to use. Non isle-buildkit images have their versions specified explicitly in their respective docker-compose files.

By default, this is set to the latest stable release of isle-buildkit.

If you change this setting, you need to make -B docker-compose.yml to generate a new docker-compose.yml file that contains the changes you've made.

"},{"location":"installation/docker/isle-dc/docker-available-configuration/#use_secrets","title":"USE_SECRETS","text":"

Whether or not you want to use secrets. For demo and development environments, secrets are not required. They are essential if you are running a production environment, though.

Secrets are contained in the secrets folder within your isle-dc installation. Each file represents an individual secret, and its contents are the value you're trying to protect.

Available values for this setting are:

  • true
  • false

If you change this setting, you need to make -B docker-compose.yml to generate a new docker-compose.yml file that contains the changes you've made.

"},{"location":"installation/docker/isle-dc/docker-available-configuration/#drupal_install_profile","title":"DRUPAL_INSTALL_PROFILE","text":"

Which install profile to use when making an initial installation. Valid values for this setting are the machine names of any Drupal profile.

By default, this is set to standard.

"},{"location":"installation/docker/isle-dc/docker-available-configuration/#install_existing_config","title":"INSTALL_EXISTING_CONFIG","text":"

Set this to true if you want to install an existing Drupal site whose configuration was exported with drush config:export

Available values for this setting are - true - false

By default, this is set to false.

If you set this to true, be sure to set DRUPAL_INSTALL_PROFILE to minimal.

"},{"location":"installation/docker/isle-dc/docker-available-configuration/#include_watchtower_service","title":"INCLUDE_WATCHTOWER_SERVICE","text":"

Whether or not to include Watchtower as a service. When developing isle-buildkit, this is extremely useful and will auto-deploy new containers as you make changes. You should not use watchtower on production environments, though.

Available values for this setting are - true - false

By default, this is set to true.

If you change this setting, you need to make -B docker-compose.yml to generate a new docker-compose.yml file that contains the changes you've made.

"},{"location":"installation/docker/isle-dc/docker-available-configuration/#include_etcd_service","title":"INCLUDE_ETCD_SERVICE","text":"

Whether or not to include etcd as a service.

Available values for this setting are - true - false

By default, this is set to false. If you don't know what etcd is, then leave this be.

If you change this setting, you need to make -B docker-compose.yml to generate a new docker-compose.yml file that contains the changes you've made.

"},{"location":"installation/docker/isle-dc/docker-available-configuration/#include_code_server_service","title":"INCLUDE_CODE_SERVER_SERVICE","text":"

Whether or not to include the coder IDE as a service. If you're developing on Islandora, this can be pretty useful when developing. You should not deploy this service on production environments.

Available values for this setting are - true - false

By default, this is set to false.

If you change this setting, you need to make -B docker-compose.yml to generate a new docker-compose.yml file that contains the changes you've made.

"},{"location":"installation/docker/isle-dc/docker-available-configuration/#drupal_database_service","title":"DRUPAL_DATABASE_SERVICE","text":"

Which database engine to use for Drupal.

Available values are - mariadb - postgres

By default, this value is set to mariadb.

If you change this setting, you need to make -B docker-compose.yml to generate a new docker-compose.yml file that contains the changes you've made.

Changing this after installation has consequences

If you are changing from mariadb to postgres or vice versa, you MUST migrate your data yourself. ISLE will not convert your database from one to the other, and it's generally not advised to change this once you've installed.

"},{"location":"installation/docker/isle-dc/docker-available-configuration/#fcrepo_database_service","title":"FCREPO_DATABASE_SERVICE","text":"

Which database engine to use for Fedora.

Available values are - mariadb - postgres

By default, this value is set to mariadb.

If you change this setting, you need to make -B docker-compose.yml to generate a new docker-compose.yml file that contains the changes you've made.

Changing this after installation has consequences

If you are changing from mariadb to postgres or vice versa, you MUST migrate your data yourself. ISLE will not convert your database from one to the other, and it's generally not advised to change this once you've installed.

"},{"location":"installation/docker/isle-dc/docker-available-configuration/#compose_http_timeout","title":"COMPOSE_HTTP_TIMEOUT","text":"

Sometimes when bringing up containers, you can encounter timeouts with an error like this:

ERROR: An HTTP request took too long to complete. Retry with --verbose to obtain debug information.\nIf you encounter this issue regularly because of slow network conditions, consider setting COMPOSE_HTTP_TIMEOUT to a higher value (current value: XXX).\n

By default, this value is set to 480, but if you have slow network conditions and encounter this error, consider bumping it higher.

"},{"location":"installation/docker/isle-dc/docker-available-configuration/#compose_project_name","title":"COMPOSE_PROJECT_NAME","text":"

Used for naming services in traefik as well as defining network alias and urls. For example the drupal service will be found at ${COMPOSE_PROJECT_NAME}_drupal_1. This is useful to change if you are running multiple ISLE instances on one machine.

By default, this value is set to isle-dc.

If you change this setting, you need to make -B docker-compose.yml to generate a new docker-compose.yml file that contains the changes you've made.

"},{"location":"installation/docker/isle-dc/docker-available-configuration/#compose_docker_cli_build","title":"COMPOSE_DOCKER_CLI_BUILD","text":"

Allows building your custom image when setting ENVIRONMENT=custom

By default, this is set to 1.

You most likely will never have a need to change this.

"},{"location":"installation/docker/isle-dc/docker-available-configuration/#docker_buildkit","title":"DOCKER_BUILDKIT","text":"

Instructs Docker to use buildkit when building your custom image.

By default, this is set to 1.

You most likely will never have a need to change this. Trust us, you want buildkit.

"},{"location":"installation/docker/isle-dc/docker-available-configuration/#project_drupal_dockerfile","title":"PROJECT_DRUPAL_DOCKERFILE","text":"

The name of the Dockerfile to use after setting ENVIRONMENT=custom.

By default, this is set to Dockerfile.

You most likely will never have a need to change this.

If you change this setting, you need to make -B docker-compose.yml to generate a new docker-compose.yml file that contains the changes you've made.

"},{"location":"installation/docker/isle-dc/docker-available-configuration/#disable_syn","title":"DISABLE_SYN","text":"

Set this to true to disable JWT authentication for Fedora. This is necessary when performing a Fedora import using their import/export tools.

By default, this is set to false.

"},{"location":"installation/docker/isle-dc/docker-available-configuration/#fedora_6","title":"FEDORA_6","text":"

Whether or not to use Fedora 6. If you set this to false, you will be given Fedora 5. In general, unless you already have an existing site on Fedora 5, you'll want Fedora 6.

By default, this is set to true.

If you change this setting, you need to make -B docker-compose.yml to generate a new docker-compose.yml file that contains the changes you've made.

"},{"location":"installation/docker/isle-dc/docker-basic-usage/","title":"Basic Usage","text":"

After you've finished installing Islandora using ISLE, here's some useful information to keep close at hand about running your site.

"},{"location":"installation/docker/isle-dc/docker-basic-usage/#important-files","title":"Important Files","text":"

The make commands that you used to install Islandora will leave you with two very important files.

File Purpose .env A configuration file that is yours to customize. This file controls how the docker-compose.yml file gets generated to meet your use case.It also allows you to set variables that make their way into the final docker-compose.yml file, such as your site's domain. docker-compose.yml A ready to run docker-compose.yml file based on your .env file. This file is considered disposable. When you change your .env file, you will generate a new one."},{"location":"installation/docker/isle-dc/docker-basic-usage/#available-services","title":"Available Services","text":"

Here's a list of all the available services. Note that there are some services over http and not https. Those aren't meant to be exposed to the public, but internally people from your organization will want to access them. In practice, you can restrict access to these services using firewall rules to just those who you trust.

Service Url Drupal https://islandora.traefik.me Traefik http://islandora.traefik.me:8080 Fedora http://islandora.traefik.me:8081/fcrepo/rest Blazegraph http://islandora.traefik.me:8082/bigdata Activemq http://islandora.traefik.me:8161 Solr http://islandora.traefik.me:8983 Cantaloupe https://islandora.traefik.me/cantaloupe Matomo https://islandora.traefik.me/matomo/ Code Server http://islandora.traefik.me:8443/"},{"location":"installation/docker/isle-dc/docker-basic-usage/#basic-commands","title":"Basic Commands","text":""},{"location":"installation/docker/isle-dc/docker-basic-usage/#stopping-islandora","title":"Stopping Islandora","text":"

If you want to stop Islandora, you can bring down all the containers with

docker-compose down\n
"},{"location":"installation/docker/isle-dc/docker-basic-usage/#restarting-islandora","title":"Restarting Islandora","text":"

If you want to start Islandora back up after stopping it, use

docker-compose up -d\n
"},{"location":"installation/docker/isle-dc/docker-basic-usage/#deleting-islandora","title":"Deleting Islandora","text":"

If you want to stop Islandora and delete all of its content, use

docker-compose down -v\n
"},{"location":"installation/docker/isle-dc/docker-basic-usage/#regenerating-docker-composeyml","title":"Regenerating docker-compose.yml","text":"

If you make changes to configuration in the .env file, you may need to regenerate your docker-compose.yml file so that those changes take effect.

make -B docker-compose.yml\n

Once you have a new docker-compose.yml file, you'll need to restart your containers that have had configuration change. You can do this easily with

docker-compose up -d\n

Even if the site is up and running, that command will only restart the containers it needs to.

"},{"location":"installation/docker/isle-dc/docker-basic-usage/#listing-services","title":"Listing services","text":"

You can see a list of all the containers that you have running and their statuses by running

docker ps -a\n
"},{"location":"installation/docker/isle-dc/docker-basic-usage/#tailing-logs","title":"Tailing Logs","text":"

You can tail logs using

docker-compose logs service_name

For example, to tail nginx logs for Drupal, use docker-compose logs drupal.

If you don't know what you're looking for exactly, you can turn on the fire hose and look through all logs by dropping the service name and simply using

docker-compose logs

"},{"location":"installation/docker/isle-dc/docker-custom/","title":"Installing a Production/Staging Server","text":"

If you are deploying Islandora on a server that is publicly accessible, there are extra precautions you should take for performance and security reasons.

For local development we bind mount our codebase (Drupal) folder so that we can do development work locally, outside of our Docker container. For a production site we don\u2019t want to do this. Instead, we make changes to our development site, then build a Drupal image with our changes that we can use on our production site instead of the default Islandora Drupal image.

"},{"location":"installation/docker/isle-dc/docker-custom/#creating-your-image","title":"Creating your Image","text":"

In order to generate that custom Drupal image we need to set up a development environment. You will do this on your development computer, rather than your production server.

Once your development site is set up you will need somewhere to store your custom Drupal image. You should create a private repository in your container registry of choice (Dockerhub, GitHub, GitLab, etc.)

Once you have a place to store it, you can create your custom Drupal image by editing your .env to set CUSTOM_IMAGE_NAMESPACE to your dockerhub username or the URL and username for your container repository, and CUSTOM_IMAGE_NAME to the name of the repository you just created.

Once this is done you can run make build to create the image from a Dockerfile. If you don't have a custom Dockerfile, it will create one from sample.Dockerfile. This will create a new image from the Islandora Drupal image, with your codebase folder copied into the image during the build process.

You should now have a custom Drupal image on your local machine. You will need to docker login to your container registry to push your image, so make sure to do that if you haven't yet (you should only need to do this once). You can then push this image to your container registry by running make push-image.

"},{"location":"installation/docker/isle-dc/docker-custom/#set-up-a-git-repository","title":"Set up a Git Repository","text":"

Now that you have a development site set up with your own codebase folder, you should create a new git repository for your site. This way you can easily spin up a new site based on your modules and configuration, instead of the Islandora Starter Site. This will also allow you to sync changes between your production, staging, and development sites.

You will likely want to include the Isle-dc directory as well as your codebase folder in your git repository. This will allow you to make modifications to your Makefile, Dockerfile, docker-compose.yml, etc. and keep those modifications alongside your Drupal files in the codebase folder. To store these files in your own private repository you should change the git remote repository URL from the Isle-DC URL to the URL for your private repository.

Note that Isle-DC has the codebase folder in its .gitignore, so you will want to remove codebase* from your .gitignore. You don't need the web or vendor directories to be stored in your repo, since they will be added when you do a composer install during the site setup, so you can add those into your .gitignore as codebase/vendor/* and codebase/web/*.

"},{"location":"installation/docker/isle-dc/docker-custom/#set-up-your-production-staging-site","title":"Set up your Production / Staging Site","text":"

At this point you should have a custom git repository and a custom Drupal image, and both should be accessible by your production server. On your production server you will need to clone your custom copy of Isle-dc, copy the sample.env file, and name it .env.

In that .env you should set the following variables:

ENVIRONMENT=custom\nCOMPOSE_PROJECT_NAME=(same as dev site)\nCUSTOM_IMAGE_NAMESPACE=(same as dev site)\nCUSTOM_IMAGE_NAME=(same as dev site)\nDOMAIN=your-domain.com\n

Once this is set up, run make production to install your drupal site. You can generate new secrets for your production passwords, or copy them from your dev server if you would like them to be the same.

At this point, your site is ready, but you won't be able to access it at your URL until you update the SSL certificates (see TLS section below).

Codebase Folder

Because your codebase folder is in your git repository it will be cloned to your development server, but unlike in your development environment, it is not bound to the Drupal container. This means that any change you make to those files will not be represented in your Drupal site.

You can also use this folder to build your Drupal image on the production server instead of on your development server if you like.

"},{"location":"installation/docker/isle-dc/docker-custom/#secrets","title":"Secrets","text":"

Sensitive information, such as passwords, should never be built into a container. It also shouldn't ever be bind-mounted in like we do with our codebase folder. If you use secrets, it's like bind-mounting in a file, except that file is provided from the host machine to the container using an encrypted channel.

Secrets are on by default. They can be toggled in your .env file, but you should never turn them off for production sites.

USE_SECRETS=true\n

The secrets themselves are stored in the secrets/live folder of isle-dc. If you navigate to that directory, you'll see several small files, where each file represents a different password. When you run make production you can choose whether to generate new random secrets or create them yourself.

"},{"location":"installation/docker/isle-dc/docker-custom/#tls","title":"TLS","text":"

All public facing sites need to use HTTPS, and it's definitely a stumbling block for the uninitiated. Fortunately, isle-dc is set up to use HTTPS by default. Even when running make demo, your site runs over HTTPS at https://islandora.traefik.me.

"},{"location":"installation/docker/isle-dc/docker-custom/#using-your-own-certificates","title":"Using your own certificates","text":"

The default certificates are stored in the certs folder of isle-dc, and you can simply overwrite them with certificates from your certificate authority. As long as the certificates match the DOMAIN variable in your .env file, that is. Changing the files in the certs folder requires restarting the Traefik container, which you can do by running docker-compose restart traefik.

File Purpose cert.pem A PEM encoded certificate that also contains the issuer's certificate as well. Most certificate authorities offer \"Full Chain\" or \"With Issuer\" certificates that contain everything you need. Occasionally, you may find yourself needing to manually concatenate your certificate with the issuer certificate by hand. In that case, the certificate for your site goes first, and the issuer's certificate is appended afterwards. privkey.pem A PEM encoded private key used to sign your certificate"},{"location":"installation/docker/isle-dc/docker-custom/#requesting-certificates-through-lets-encrypt","title":"Requesting Certificates through Let's Encrypt","text":"

To use Let's Encrypt to acquire your SSL Certificate, set the following in your .env file and run make -B docker-compose.yml && make up.

USE_ACME=true\nACME_EMAIL=your-email@example.org\n

Be sure to replace your-email@example.org with the email address you've associated with Let's Encrypt.

The way this is setup, is it performs an HTTP Challenge to verify you are in control of the domain. So your system will need to be accessible at http://DOMAIN/.

??? warning \"Let's Encrypt Rate Limit\" If you aren't careful, you can hit Let's Encrypt's rate limit, and you'll be locked out for up to a week! If you want to use their staging server instead while testing things out, add the following to your .env file

```\nACME_SERVER=https://acme-staging-v02.api.letsencrypt.org/directory\n```\n

You'll still get security exceptions when it's working, but you should be able to check the certificate from the browser and confirm you are getting it from the staging server.

\n
"},{"location":"installation/docker/isle-dc/docker-custom/#troubleshooting-certificate-issues","title":"Troubleshooting Certificate Issues","text":"

If you are still getting security exceptions, check what certificate is being used through your browser. Setting TRAEFIK_LOG_LEVEL=DEBUG in your .env file will help out greatly when debugging Traefik. You can tail the logs with docker-compose logs -tf traefik.

"},{"location":"installation/docker/isle-dc/docker-custom/#traefikme-ssl-certificate-expired-or-revoked","title":"traefik.me SSL certificate expired or revoked","text":"

The *.traefik.me certificate that covers islandora.traefik.me will need to be redownloaded occasionally, due to the certificate expiring or possibly being revoked. You can download the updated certificates by performing the following commands:

rm certs/cert.pem\nrm certs/privkey.pem\nmake download-default-certs\ndocker-compose restart traefik\n

traefik.me Certificate Note

Please note that sometimes the upstream provider of the traefik.me certificate takes a couple of days to update the certificate after it expires or is accidentally revoked.

"},{"location":"installation/docker/isle-dc/docker-custom/#requesting-certificates-through-acme-and-external-account-binding","title":"Requesting Certificates through ACME and External Account Binding","text":"

To request certificates through another Certificate Authority (CA) that supports External Accounting Binding through ACME such as InCommon or ZeroSSL you will need to add the following to your .env file:

USE_ACME=true\nACME_EMAIL=your-email@example.org\nACME_SERVER=\nACME_EAB_KID=\nACME_EAB_HMAC=\n

Where ACME_SERVER is the CA server to use, ACME_EAB_KID is the key identifier from the External CA, and ACME_EAB_HMAC is the HMAC key from the External CA.

Once you have added these commands you will need to run the following commands:

make -B docker-compose.yml\nmake up\n
"},{"location":"installation/docker/isle-dc/docker-local/","title":"Installing a Development or Demo Server","text":"

When developing locally, your Drupal site resides in your isle-dc/codebase folder and is bind-mounted into your Drupal container. This lets you update code using the IDE of your choice on your host machine, and the changes are automatically reflected on the Drupal container.

"},{"location":"installation/docker/isle-dc/docker-local/#installing-isle-docker-compose","title":"Installing ISLE Docker Compose","text":"

Use Git to install the ISLE Docker Compose tool:

git clone https://github.com/islandora-devops/isle-dc

Tagged versions are available here.

"},{"location":"installation/docker/isle-dc/docker-local/#getting-started","title":"Getting Started","text":"

If you don't already have a Drupal site, you'll be given a basic setup using Drupal 10 and the Islandora Starter Site.

If you do already have a Drupal site, use git to clone it into place as the codebase folder.

cd /path/to/isle-dc\ngit clone https://github.com/your_org/your_repo codebase\n

Now you'll need to tell isle-dc to look for it by setting the ENVIRONMENT variable in your .env file. If you don't have one, copy over sample.env and name it .env. Then set

ENVIRONMENT=starter\n

You should also change the COMPOSE_PROJECT_NAME variable. This determines the name of the Docker containers and volumes that are created when you run make starter. If you leave this as the default you will need to be careful not to overwrite the containers with another install of isle-dc later.

COMPOSE_PROJECT_NAME=isle-dc\n

If your site includes exported configuration from drush config:export, then you'll also need to set

INSTALL_EXISTING_CONFIG=true\nDRUPAL_INSTALL_PROFILE=minimal\n

Once you are ready, run

make starter\n

to install the Drupal site in your codebase folder and spin up all the other containers with it.

Enjoy your Islandora instance! Check out the basic usage documentation to see all the endpoints that are available and how to do things like start and stop Islandora. Your passwords, including the Drupal admin password, can be found in the secrets/live directory after you run make starter.

"},{"location":"installation/docker/isle-dc/docker-local/#demo-content","title":"Demo Content","text":"

To populate your site with some demo content, you can run make demo_content. This will import some sample objects into your Islandora site.

"},{"location":"installation/docker/isle-dc/docker-maintain-drupal/","title":"Maintaining Your Drupal Site","text":"

Drupal has a dedicated security team, and regularly produces updates to address security issues that are discovered. You should always keep your Drupal site up to date so that you are protected against known vulnerabilities. Updating Drupal can be intimidating, but we have step-by-step instructions to help you do it safely.

"},{"location":"installation/docker/isle-dc/docker-maintain-drupal/#note-on-production-sites","title":"Note on Production Sites","text":"

Please note that maintaining Drupal should be done on your development site. For production sites, you should build an image from the codebase folder on your development machine, and run that image in production. For more information on this, please see the production install instructions.

"},{"location":"installation/docker/isle-dc/docker-maintain-drupal/#running-commands","title":"Running commands","text":"

Once you have a codebase folder, how do you maintain it and keep it up to date? The quick answer is \"the same way you maintain any Drupal site with Composer and Drush\"... with one small caveat. You most likely do not have PHP or Composer on your machine, and even if you do, you want to make sure you're using the exact same version that your Docker container is using. So to ensure all the versions of things line up, we use Docker to execute Drush and Composer from the Drupal container. The general template for running a command in your Drupal container looks like this:

docker compose exec -T drupal with-contenv bash -lc 'YOUR COMMAND'\n

You can also just shell into the Drupal container and run commands as well, just be aware that if you shut down your container for any reason, you'll lose your bash history. If you want to shell in to run commands, drop the -T and -lc 'YOUR COMMAND' bits.

docker compose exec drupal with-contenv bash\n
"},{"location":"installation/docker/isle-dc/docker-maintain-drupal/#updating-your-drupal-site","title":"Updating your Drupal Site","text":"

Use Composer to update your site's modules and their dependencies. The working directory of the Drupal container is the Drupal root (a.k.a. codebase), so you don't need to cd into any other directory before running the command. The following command will update all modules and their dependencies that are not pinned to specific versions.

docker compose exec -T drupal with-contenv bash -lc \"su nginx -s /bin/bash -c 'composer update -W'\"\n
Note that we run this command as the nginx user. By default, commands are run as root, which can cause some ownership issues when running Composer. By running this as nginx, we ensure that new files are owned by the nginx user.

"},{"location":"installation/docker/isle-dc/docker-maintain-drupal/#permission-issue","title":"Permission Issue","text":"

When running Composer commands you may come across the following error

[ErrorException]\nfile_put_contents(/var/www/drupal/web/sites/default/settings.php): failed to open stream: Operation not permitted\n
This means that Composer is not able to write to your settings.php file. If you run into this error, giving write permission to the nginx user should fix it.

"},{"location":"installation/docker/isle-dc/docker-maintain-drupal/#drupal-database-updates","title":"Drupal Database Updates","text":"

After getting the newest code, you'll want to use Drush to update the Drupal database and run any other update hooks that have been introduced. However, YOU SHOULD BACK UP YOUR DATABASE BEFORE GOING ANY FURTHER. You never know when something will go wrong and you don't want to be stuck with an unusable database and no plan B.

make drupal-database-dump DEST=/path/to/dump.sql\n

Now you can safely update the Drupal database with Drush via

docker compose exec -T drupal with-contenv bash -lc 'drush updb'\n

If for any reason, something goes wrong, you can Restore the Drupal database at any time by running

make drupal-database-import SRC=/path/to/dump.sql\n
"},{"location":"installation/docker/isle-dc/docker-maintain-isle/","title":"Maintaining Your ISLE Infrastructure","text":"

You will regularly be updating your Drupal site as security patches and module updates are released. Less often, you will also need to update the rest of your Islandora installation. ISLE makes this easy. In fact, it was specifically designed to streamline this process.

Since Islandora is not a single piece of software, but instead many pieces of software working together in concert, maintaining all of it is a daunting task. There's nginx, tomcat, karaf, etc... Then there's everything needed for the authentication layer and JWT keys. Plus there's all the microservices. You can see that all this adds up to a significant maintenance burden.

Now imagine if all that time and effort spent on security updates and getting the newest versions could be boiled down to a handful of simple commands. That's exactly what ISLE does!

"},{"location":"installation/docker/isle-dc/docker-maintain-isle/#updating-isle","title":"Updating ISLE","text":"

Updating

Before we update ISLE we should pull the most recent version of the isle-dc repository. This ensures that our docker-compose.yml file has any new additions that the new version of ISLE will need.

You may also want to check your .env file against the sample.env found in the repository to see if there are new variables you wish to make use of.

Updating ISLE is easy. When a new release is made available, you update the TAG variable in your .env file to the latest version. Suppose you are on ISLE 1.0.0, and ISLE 1.1.0 has been released. Then we would set

TAG=1.1.0\n

Before we update our docker-compose.yml file we want to stop the running containers with

make down\n

We'll then generate a new docker-compose.yml file that includes the new tag with

make -B docker-compose.yml\n

After that, we pull the new containers with

make pull\n

And finally we deploy the updated containers by running

make up\n

You can check that everything is running at the version you've specified with

docker ps -a\n

The version that's running can be confirmed by looking at the IMAGE column in the output.

"},{"location":"installation/docker/isle-dc/docker-maintain-isle/#major-version-updates","title":"Major Version Updates","text":"

Major version updates may require a bit more work after the new containers are up and running, if the new images are running higher major versions of software. For example, if there has been an update to PHP or Solr.

"},{"location":"installation/docker/isle-dc/docker-maintain-isle/#solr","title":"Solr","text":"

When upgrading to a new major version of Solr, you need to regenerate a new set of config files. You will need to do this on both your development site, and your production site.

First, remove the old solr data. This will remove everything that has been indexed, so we will have to reindex our site in a later step.

docker compose exec -T solr with-contenv bash -lc 'rm -r server/solr/ISLANDORA'\n

Next, recreate our solr core with a new set of config files.

make solr-cores\n

Finally, we reindex our site.

make reindex-solr\n
"},{"location":"installation/docker/isle-dc/docker-maintain-isle/#drupal","title":"Drupal","text":"

Drupal updates will be handled by your composer.json file, but you may require a certain version of ISLE in order to have the correct version of PHP. Once you have the correct PHP version installed, you can update Drupal to a new major version in the same way you normally update modules. It may be helpful to reference the Starter Site's composer.json file at https://github.com/Islandora-Devops/islandora-starter-site/blob/main/composer.json

Once you have done this on your development site, the process for your production site is the same as any other Drupal updates.

"},{"location":"installation/docker/isle-dc/docker-maintain-isle/#mariadb","title":"Mariadb","text":"

After updating Mariadb, you may need to run mariadb-upgrade inside your mariadb container, to update your system tables. This should be safe to run any time, but it is a good idea to back up your database first, just in case.

You can run this from your isle directory with

docker compose exec mariadb mariadb-upgrade\n

"},{"location":"installation/docker/isle-dc/docker-maintain-isle/#specific-update-notes","title":"Specific Update Notes","text":""},{"location":"installation/docker/isle-dc/docker-maintain-isle/#version-1x-to-2x","title":"Version 1.x to 2.x","text":"

Upgrading ISLE from 1.x to the next major version requires the TAG be set to 2.0.5 or higher. Once you create your containers you will need to follow the above instructions for Mariadb, Solr, and Drupal.

ISLE 2.0 bumps PHP up to version 8.1, which allows you to upgrade to Drupal 10.

"},{"location":"installation/docker/isle-dc/docker-troubleshooting/","title":"Troubleshooting","text":""},{"location":"installation/docker/isle-dc/docker-troubleshooting/#make-command-not-found","title":"Make command not found","text":"

If you get the error bash: make: command not found then you need to install GNU make. Run sudo apt update and sudo apt install make to install.

"},{"location":"installation/docker/isle-dc/docker-troubleshooting/#docker-versions","title":"Docker Versions","text":"

If you get an error such as: ERROR: Version in \"./docker-compose.activemq.yml\" is unsupported., then you need to upgrade Docker. Enter the command make clean before re-attempting to make demo.

"},{"location":"installation/docker/isle-dc/docker-troubleshooting/#re-attempting-make-demo","title":"Re-attempting make demo","text":"

If make fails for any reason, enter make clean before attempting to make again. If not, you may see an error such as: ERROR: Top level object in './docker-compose.yml' needs to be an object not '<class 'NoneType'>'.

"},{"location":"installation/docker/isle-dc/docker-troubleshooting/#docker-containers-exit-without-warning","title":"Docker containers exit without warning","text":"

If you notice some Docker containers drop (exited(0)), and (in Docker Desktop) the isle-dc app icon is yellow instead of green, try increasing the resources allocated to Docker (see note above).

"},{"location":"installation/docker/isle-dc/docker-troubleshooting/#connection-timed-out-mac","title":"Connection timed out (Mac).","text":"

If you are using Docker Desktop for Mac, and get timeout errors when spinning up the containers (during docker-compose up -d or during make local) such as this:

ERROR: for isle-dc_mariadb_1  UnixHTTPConnectionPool(host='localhost', port=None): Read timed out. (read timeout=480)\n

you can try quitting Docker completely (make sure there is no whale icon in your top toolbar - may need to select \"Quit\" from the whale icon itself) and then restart Docker.

"},{"location":"installation/docker/isle-dc/docker-troubleshooting/#504-bad-gateway","title":"504 Bad Gateway","text":"

If you get a white screen with a 504 Bad Gateway error, this means your containers haven't finished initializing themselves yet. If you've waiting an appropriate amount of time (2-5 minutes), then there is most likely an error in a container's startup script. Use docker ps -a to see which services have Exited status, and then tail their logs with docker-compose logs service_name.

"},{"location":"installation/docker/site-template/backup/","title":"Backups & Transferring Data Between Sites","text":"

The following instructions describe how to back up and restore an islandora site. This is also used to transfer data between a development and production site, or to sync a staging site with a production site.

Note that containers are named with a suffix of -dev or -prod depending on your environment. For example, backing up your development site\u2019s Drupal database requires running the command on the drupal-dev container, but backing up the production site\u2019s database requires running the command on the drupal-prod container. The following instructions are mostly written for development sites, but the same commands will work on production containers.

Before attempting the following commands, you should familiarize yourself with running commands inside a container and with docker compose cp

"},{"location":"installation/docker/site-template/backup/#drupal-configuration","title":"Drupal Configuration","text":"

The typical use case of this is to export your drupal configuration files out of your development site\u2019s Drupal database and onto the host machine. This process lets you check your configuration into your git repository so you can import it into your production site.

"},{"location":"installation/docker/site-template/backup/#export","title":"Export","text":"

To export your development config run:

docker compose exec -T drupal-dev drush -l default config:export -y\n

Then commit and push your git repo so the new config files are included.

"},{"location":"installation/docker/site-template/backup/#import","title":"Import","text":"

To import your config to a production or staging site you first need to git pull the config files you pushed from your development site, then rebuild and restart your custom Drupal container.

git pull\ndocker compose --profile prod build\ndocker compose --profile prod down\ndocker compose --profile prod up -d\n

Once this is done, your config files will be included in the container. You can then import them by running

docker compose exec -T drupal-prod drush -l default config:import -y\n
"},{"location":"installation/docker/site-template/backup/#drupal-database","title":"Drupal Database","text":"

The Drupal database contains all your Drupal settings and content. If you want to move configuration settings from development to production you should use the Drupal Configuration instructions above.

These instructions can also be used to move your content from production to staging or development.

"},{"location":"installation/docker/site-template/backup/#back-up","title":"Back Up","text":"

Backing up the Drupal database involves doing a database dump in the drupal container:

docker compose exec -T drupal-dev with-contenv bash -lc 'mysqldump -u ${DRUPAL_DEFAULT_DB_USER} -p${DRUPAL_DEFAULT_DB_PASSWORD} -h mariadb ${DRUPAL_DEFAULT_DB_NAME} > /tmp/dump.sql'\n

and then copying that dump from the container to the host machine:

docker compose cp drupal-dev:/tmp/dump.sql [path/on/host/dump.sql]\n
"},{"location":"installation/docker/site-template/backup/#restore","title":"Restore","text":"

Restoring from a database dump requires copying the dump file into the drupal container:

docker compose cp [path-to/dump.sql] drupal-dev:/tmp/dump.sql\n

Then replacing the database with the dump:

docker compose exec -T drupal-dev with-contenv bash -lc 'chown root:root /tmp/dump.sql && mysql -u ${DRUPAL_DEFAULT_DB_USER} -p${DRUPAL_DEFAULT_DB_PASSWORD} -h mariadb ${DRUPAL_DEFAULT_DB_NAME} < /tmp/dump.sql'\n

And finally, rebuilding the Drupal cache:

docker compose exec drupal-dev drush cr\n
"},{"location":"installation/docker/site-template/backup/#drupal-public-files","title":"Drupal Public Files","text":"

Drupal's public files contain any files used on static pages. This is also where your Islandora derivatives are stored, which includes FITS, thumbnails, etc.

These instructions can also be used to move your content from production to staging or development.

"},{"location":"installation/docker/site-template/backup/#back-up_1","title":"Back Up","text":"

Drupal public files can be compressed to a tgz file in the Drupal container:

docker compose exec -T drupal-dev with-contenv bash -lc 'tar zcvf /tmp/public-files.tgz -C /var/www/drupal/web/sites/default/files .'\n

Then copied to the host machine:

docker compose cp drupal-dev:/tmp/public-files.tgz  [path/on/host/public-files.tgz]\n
"},{"location":"installation/docker/site-template/backup/#restore_1","title":"Restore","text":"

Drupal files can be restored from a tgz file by copying them into the Drupal container:

docker compose cp public-files.tgz drupal-dev:/tmp/public-files.tgz\n

Then placed in the correct directory inside the Drupal volume:

docker compose exec -T drupal-dev with-contenv bash -lc 'tar zxvf /tmp/public-files.tgz -C /var/www/drupal/web/sites/default/files && chown -R nginx:nginx /var/www/drupal/web/sites/default/files && rm /tmp/public-files.tgz'\n

Note

This will overwrite existing files if they have the same filename, but does not remove existing files otherwise. If you want to make sure that the public files directory does not contain anything but the newly imported files, you will want to empty the directory before copying the new files in.

docker compose exec -T drupal-dev with-contenv bash -lc 'rm -r /var/www/drupal/web/sites/default/files/*'\n
"},{"location":"installation/docker/site-template/backup/#drupal-private-files","title":"Drupal Private Files","text":"

These instructions can also be used to move your content from production to staging or development.

"},{"location":"installation/docker/site-template/backup/#back-up_2","title":"Back Up","text":"

Drupal public files can be compressed to a tgz file in the Drupal container:

docker compose exec -T drupal-dev with-contenv bash -lc 'tar zcvf /tmp/private-files.tgz -C /var/www/drupal/private .'\n

Then copied to the host machine:

docker compose cp drupal-prod:/tmp/private-files.tgz [path/on/host/private-files.tgz]\n
"},{"location":"installation/docker/site-template/backup/#restore_2","title":"Restore","text":"

Drupal files can be restored from a tgz file by copying them into the Drupal container:

docker compose cp private-files.tgz drupal-dev:/tmp/private-files.tgz\n

Then placed in the correct directory inside the Drupal volume:

docker compose exec -T drupal-dev with-contenv bash -lc 'tar zxvf /tmp/private-files.tgz -C /var/www/drupal/private && chown -R nginx:nginx /var/www/drupal/private && rm /tmp/private-files.tgz'\n

Note

This will overwrite existing files if they have the same filename, but does not remove existing files otherwise. If you want to make sure that the private files directory does not contain anything but the newly imported files, you will want to empty the directory before copying the new files in.

docker compose exec -T drupal-dev with-contenv bash -lc 'rm -r /var/www/drupal/private/*'\n
"},{"location":"installation/docker/site-template/backup/#fedora","title":"Fedora","text":"

Fedora 6 uses a file structure called OCFL to store files and metadata. The Fedora database is built based on this OCFL file structure, so we don't actually back up our fedora database. Instead, we back up the files and let Fedora rebuild the database based on them.

Note

These instructions involve moving the entirety of your Fedora data. For small sites this is fine, but you may find this becomes cumbersome as your site grows. You may wish to bind your oclf-root directory as a volume to eliminate the need to run the docker compose cp commands.

"},{"location":"installation/docker/site-template/backup/#back-up_3","title":"Back Up","text":"

To back up Fedora we create a .tgz file from the ocfl-root directory in the Fedora container:

docker compose exec -T fcrepo-dev with-contenv bash -lc 'tar zcvf /tmp/fcrepo-export.tgz -C /data/home/data/ocfl-root/ .'\n

Then we copy that file to the host machine:

docker compose cp fcrepo-dev:/tmp/fcrepo-export.tgz [path/on/host/fcrepo-export.tgz]\n
"},{"location":"installation/docker/site-template/backup/#restore_3","title":"Restore","text":"

To restore our fedora database we need to copy the backup into the Fedora container:

docker compose cp [path-to/fcrepo-export.tgz] fcrepo-dev:/tmp/fcrepo-export.tgz\n

Empty the existing ocfl-root directory:

docker compose exec -T fcrepo-dev bash -lc 'rm -r /data/home/data/ocfl-root/*'\n

Put our backup files in the ocfl-root directory:

docker compose exec -T fcrepo-dev with-contenv bash -lc 'tar zxvf /tmp/fcrepo-export.tgz -C /data/home/data/ocfl-root/ && chown -R tomcat:tomcat /data/home/data/ocfl-root/ && rm /tmp/fcrepo-export.tgz'\n

Drop the existing Fedora database:

docker compose exec -T mariadb-dev bash -lc 'mysql -e \"drop database fcrepo;\"'\n

Restart Fedora to reinitialize the database from the ocfl-root directory:

docker compose --profile dev restart fcrepo-dev\n

Note

It may take a while for the database to be restored. In the meantime, you may see error messages that say Fedora is not connected.

"},{"location":"installation/docker/site-template/docker-modifications/","title":"Docker Modifications","text":"

ISLE Site Template provides you with a docker-compose.yml file that allows you to get an Islandora site running quickly, but it makes some assumptions about how the site will run, and which containers you will use. Once you have your site running you may want to make some modifications to the default setup that the Site Template uses.

"},{"location":"installation/docker/site-template/docker-modifications/#adding-editing-environment-variables","title":"Adding / Editing Environment Variables","text":"

Islandora Buildkit provides several environment variables that can be modified when creating containers.

Please see the README for the different buildkit images to see what is available:

  • ActiveMQ
  • Alpaca
  • Blazegraph
  • Cantaloupe
  • Code Server
  • Crayfits
  • Drupal
  • Fedora
  • Fits
  • Homarus
  • Houdini
  • Hypercube
  • MariaDB
  • Milliner
  • Solr

You can add these environment variables to your docker-compose.yml in order to change their values. For example, if you want to increase the PHP memory limit in your production Drupal container, you can do so like this:

    drupal-prod:\n        <<: [*prod, *drupal]\n        Environment:\n            PHP_MEMORY_LIMIT: 1G\n
"},{"location":"installation/docker/site-template/docker-modifications/#removing-services","title":"Removing Services","text":"

You may not want to use all the images that are included in the Site Template\u2019s docker-compose.yml. You can remove containers by deleting their sections in the docker-compose.yml file.

For example, to remove Fedora, you would delete the services called fcrepo-dev and fcrepo-prod.

Depending on the container you are removing, you may need to delete references to it as well. For example, some containers are referenced by others in the depends_on field. You will need to also delete these references, so if you delete the fedora-dev service, you will need to remove the rule that traefik-dev depends on it.

If you are removing a container which is referenced by Drupal, ensure that you update Drupal as well (e.g. if removing Fedora, ensure your Media's files are not writing to the Fedora filesystem).

After doing docker compose down, run docker compose up -d --remove-orphans to remove the containers you removed from the docker-compose.yml file.

"},{"location":"installation/docker/site-template/setup/","title":"Initial Setup","text":"

Instructions for creating your site can be found in the project's README file

This page lists a few things to note about the process, but is not meant to be a replacement for the instructions in the README file.

"},{"location":"installation/docker/site-template/setup/#custom-drupal-image","title":"Custom Drupal Image","text":"

ISLE Site Template uses a custom Drupal image that you build on top of the provided Islandora Drupal image. This means you will not be running the islandora/drupal image directly, but the provided Dockerfile will use it to build your image.

Note for those coming from ISLE-DC

In ISLE-DC, we only use a custom image in production, but in the ISLE Site Template, we use it for both.

Building your custom Drupal image is done by running

docker compose --profile dev build for your development image

or

docker compose --profile prod build for your production image

This builds the docker image based on the Dockerfile in the drupal directory, which uses your composer files to pull the Drupal modules it needs into the image. Because the Dockerfile and composer files are part of the git repository, you can build your Drupal image locally, or on your production server.

This documentation assumes you will be building your production image on the production server. If you do it this way, it is not necessary to push your image to a container registry. Instead you just pull your git repository anytime you make changes to your composer files, and run docker compose build again.

Using a Container Registry

If you want to build your production images somewhere other than on your production server, you can do so. The .env file allows you to set your image repository URL, which will allow you to push / pull your Drupal image to / from your container registry. If you do this, you can then run docker compose pull instead of docker compose build on your production server, to pull the already built image to that server.

For more information please see the documentation on docker compose pull and docker compose build

"},{"location":"installation/docker/site-template/setup/#adding-a-staging-site","title":"Adding a Staging Site","text":"

The process for setting up a staging site is the same as production, but you will need to change the DOMAIN variable in your .env file on your staging server, to contain the URL for your staging site.

Note

By default, the .env file is stored in the git repository for your site, which means there is only support for one URL. If you are adding a staging site, you may wish to modify this so that you do not accidentally push your staging URL to your git repository.

Restricting Access to Staging Servers

Using letsencrypt to generate your certs requires port 80 to be accessible on your server. If you would like to keep your site private by limiting access to certain IP addresses, you can still firewall port 443, but you will have to leave port 80 open. If you need to firewall port 80 as well, you will have to either use your own certs or look into another method of generating certs.

"},{"location":"installation/docker/site-template/setup/#adding-demo-content","title":"Adding Demo Content","text":"

If you are spinning up a new site for testing, you can add some demo content to your site by running

[ -d \"islandora_workbench\" ] || (git clone https://github.com/mjordan/islandora_workbench)\ncd islandora_workbench ; cd islandora_demo_objects || git clone https://github.com/Islandora-Devops/islandora_demo_objects.git\n$(SED_DASH_I) 's#^host.*#host: $(SITE)/#g' islandora_workbench/islandora_demo_objects/create_islandora_objects.yml\n$(SED_DASH_I) 's/^password.*/password: \"$(shell cat secrets/DRUPAL_DEFAULT_ACCOUNT_PASSWORD | sed s#/#\\\\\\\\\\\\\\\\/#g)\"/g' islandora_workbench/islandora_demo_objects/create_islandora_objects.yml\ncd islandora_workbench && docker build -t workbench-docker .\ncd islandora_workbench && docker run -it --rm --network=\"host\" -v $(QUOTED_CURDIR)/islandora_workbench:/workbench --name my-running-workbench workbench-docker bash -lc \"./workbench --config /workbench/islandora_demo_objects/create_islandora_objects.yml\"\n`docker compose exec -T drupal-dev with-contenv bash -lc 'drush --root /var/www/drupal/web -l ${DRUPAL_DEFAULT_SITE_URL} search-api-reindex'\ndocker compose exec -T drupal-dev with-contenv bash -lc 'drush --root /var/www/drupal/web -l ${DRUPAL_DEFAULT_SITE_URL} search-api-index'\n

"},{"location":"installation/docker/site-template/setup/#custom-themes-modules","title":"Custom Themes & Modules","text":"

You may wish to copy themes and modules into your project directly, instead of using Composer to manage them. For example, if you are creating your own theme instead of using a contributed one.

Isle Site Template provides directories at drupal/rootfs/var/www/drupal/web/modules/custom and drupal/rootfs/var/www/drupal/web/themes/custom for you to add your custom themes and modules.

These directories are mounted in development, so any changes to them will be shared between your host machine and your Drupal container.

In production, these themes and modules will be included when the Drupal image is built.

"},{"location":"installation/docker/site-template/site-template/","title":"ISLE Site Template","text":""},{"location":"installation/docker/site-template/site-template/#what-is-the-isle-site-template","title":"What is the ISLE Site Template?","text":"

The ISLE Site Template is a system for installing Islandora on Docker. As with ISLE-DC, it uses Docker Compose to orchestrate the installation of all the different services (Docker containers) that make up Islandora. Unlike ISLE-DC, in ISLE Site Template you use Docker Compose commands directly, helping you to get familiar with the kinds of commands that will be a key part of running and maintaining Islandora.

"},{"location":"installation/docker/site-template/site-template/#usage","title":"Usage","text":"
  1. Do not clone the Isle Site Template!

    • Unlike most other repositories we provide, the Isle Site Template is not meant to be cloned or forked. Rather, it can be downloaded using curl and installed either manually or automatically.
  2. Instead, follow the instructions in the ISLE Site Template's README.md and README.template.md files.

    • Instructions are provided both for dev and prod environments, with different services available on each.
  3. During installation, you will install a copy of the Islandora Starter Site.

    • Though, if you select the manual installation option, you can change that out for a different base composer project. This will form the basis of your Drupal site. If you don't have a custom version, we recommend using the Islandora Starter Site (and it's installed automatically during the automatic install).
  4. Customizing your site can be persisted to your own repo.

    • In the process of setting you the ISLE Site Template, you are encouraged to create a custom Git repository for this project. When you do, you can save your changes to several components of your own site, for example the site name in Docker, which services you have running, and all changes made to your entire Drupal site configuration.
"},{"location":"installation/docker/site-template/updating/","title":"Updating","text":"

The following sections describe how to keep your Islandora install up to date with current versions of Drupal modules and Docker images.

"},{"location":"installation/docker/site-template/updating/#updating-isle-buildkit-islandora-docker-images","title":"Updating Isle Buildkit (Islandora Docker Images)","text":"

Updating to a new version of Isle Buildkit is done by setting the ISLANDORA_TAG variable in your .env file. Once you have updated the .env file, you need to pull the new Islandora images, and rebuild your custom Drupal image from the specified Drupal image.

For example, to upgrade from Buildkit 2.0.0 to 3.0.0 you would do the following steps:

  1. Change your .env file to say ISLANDORA_TAG=3.0.0

  2. Stop your Docker containers

    docker compose --profile dev down

  3. Pull the new Docker images (except the Drupal image)

    docker compose --profile dev pull --ignore-buildable --ignore-pull-failures

  4. Build your custom Drupal image

    docker compose --profile dev build

  5. Start your containers from the new images

    docker compose --profile dev up -d

Once you have upgraded your images, you may need to perform extra steps for Solr and Mariadb, depending on whether these have new versions in the new images.

Production Sites

Upgrading a production site works the same way, just replace dev with prod in the above instructions.

"},{"location":"installation/docker/site-template/updating/#solr","title":"Solr","text":"

You may need to regenerate your Solr configs if Solr has been updated to a new version, or when the Search API Solr Drupal module has been updated. If you visit /admin/config/search/search-api/server/default_solr_server on your Islandora site it will tell you if the configs need to be updated.

To generate new configs perform the following steps:

  1. Remove existing solr configs

    docker compose exec -T solr-dev with-contenv bash -lc 'rm -r server/solr/default/*'

  2. Restart the Solr container

    docker compose --profile dev restart solr-dev

  3. Recreate solr configs for new solr versions

    docker compose exec -T drupal-dev with-contenv bash -lc \"for_all_sites create_solr_core_with_default_config\"

  4. Reindex Solr through the admin page or via Drush

"},{"location":"installation/docker/site-template/updating/#mariadb","title":"MariaDB","text":"

After updating MariaDB, you may need to run mariadb-upgrade inside your MariaDB container, to update your system tables. This should be safe to run any time, but it is a good idea to back up your database first, just in case.

You can run this with

docker compose exec mariadb-dev mariadb-upgrade

"},{"location":"installation/docker/site-template/updating/#updating-traefik","title":"Updating Traefik","text":"

Traefik is not updated with the other buildkit images. It is recommended that you periodically update Traefik by changing the image version in your docker-compose.yml file to the current version in use by Isle Site Template

"},{"location":"installation/docker/site-template/updating/#updating-drupal-modules","title":"Updating Drupal Modules","text":"

Drupal updates are performed through composer on your development site. Once the modules have been added/removed/updated, your composer.json and composer.lock files can be checked into your git repository and you can rebuild your production Drupal container with the new files.

"},{"location":"installation/docker/site-template/updating/#development","title":"Development","text":"

Composer commands need to run in your Drupal container. For example:

\u200b\u200bdocker compose exec drupal-dev composer update -W

Or

docker compose exec drupal-dev composer require 'drupal/islandora:^2.11'

Running database updates is also done in the container like this:

docker compose exec drupal-dev drush updb

Note

You should backup your database before running database updates

If you are enabling or uninstalling modules, you will also need to export your Drupal configuration.

Once you have finished your composer changes you can commit and push your repository with the new composer.json and composer.lock changes.

"},{"location":"installation/docker/site-template/updating/#production","title":"Production","text":"

First you will git pull to get the composer.json and composer.lock changes you made in development.

Next you build your Drupal image again, which will install the modules specified in those files:

docker compose --profile prod build

Then you stop and start your containers to get the new image:

docker compose --profile prod down

docker compose --profile prod up -d

And if necessary, run database updates:

docker compose exec drupal-prod drush updb

"},{"location":"installation/manual/configuring-drupal/","title":"Configuring Drupal","text":"

Needs Maintenance

The manual installation documentation is in need of attention. We are aware that some components no longer work as documented here. If you are interested in helping us improve the documentation, please see Contributing.

After all of the above pieces are in place, installed, configured, started, and otherwise prepared, the last thing we need to do is to finally configure the front-end Drupal instance to wire all the installed components together.

"},{"location":"installation/manual/configuring-drupal/#drupal-pre-configuration","title":"Drupal Pre-Configuration","text":""},{"location":"installation/manual/configuring-drupal/#settingsphp","title":"settings.php","text":"

Notice

By default, settings.php is read-only for all users. It should be made writable while this pre-configuration is being done, then set back to 444 afterwards.

Some additional settings will need to be established in your default settings.php before Drupal-side configuration can occur.

The below configuration will establish localhost as a trusted host pattern, but on production sites this will need to be expanded to include the actual host patterns used by the site.

/opt/drupal/web/sites/default/settings.php

Before (at around line 789):

'driver' => 'mysql',\n);\n

After:

'driver' => 'mysql',\n);\n\n$settings['trusted_host_patterns'] = [\n  'localhost',\n];\n\n$settings['flysystem'] = [\n  'fedora' => [\n    'driver' => 'fedora',\n    'config' => [\n      'root' => 'http://localhost:8080/fcrepo/rest/',\n    ],\n  ],\n];\n

Once this is done, refresh the cache to take hold of the new settings.

cd /opt/drupal\ndrush -y cr\n
"},{"location":"installation/manual/configuring-drupal/#islandora","title":"Islandora","text":"

Skip this by using the Islandora Starter Site

The Islandora Starter Site, which was presented as an option in the \"Installing Composer, Drush, and Drupal\" step, installs Islandora and other modules and configures them, allowing you to skip this section. You may want to use this manual method in the case where you want to pick and choose which Islandora modules you use.

"},{"location":"installation/manual/configuring-drupal/#downloading-islandora","title":"Downloading Islandora","text":"

The Islandora Drupal module contains the core code to create a repository ecosystem in a Drupal environment. It also includes several submodules; of importance to us is islandora_core_feature, which contains the key configurations that allow you to use Islandora features.

Take note of some of the other comments in the below bash script for an idea of what the other components are expected, and which may be considered optional.

cd /opt/drupal\n# Since islandora_defaults is near the bottom of the dependency chain, requiring\n# it will get most of the modules and libraries we need to deploy a standard\n# Islandora site.\nsudo -u www-data composer require \"drupal/flysystem:^2.0@alpha\"\nsudo -u www-data composer require \"islandora/islandora:^2.4\"\nsudo -u www-data composer require \"islandora/controlled_access_terms:^2\"\nsudo -u www-data composer require \"islandora/openseadragon:^2\"\n# These can be considered important or required depending on your site's\n# requirements; some of them represent dependencies of Islandora submodules.\nsudo -u www-data composer require \"drupal/pdf:1.1\"\nsudo -u www-data composer require \"drupal/rest_oai_pmh:^2.0@beta\"\nsudo -u www-data composer require \"drupal/search_api_solr:^4.2\"\nsudo -u www-data composer require \"drupal/facets:^2\"\nsudo -u www-data composer require \"drupal/content_browser:^1.0@alpha\" ## TODO do we need this?\nsudo -u www-data composer require \"drupal/field_permissions:^1\"\nsudo -u www-data composer require \"drupal/transliterate_filenames:^2.0\"\n# These tend to be good to enable for a development environment, or just for a\n# higher quality of life when managing Islandora. That being said, devel should\n# NEVER be enabled on a production environment, as it intentionally gives the\n# user tools that compromise the security of a site.\nsudo -u www-data composer require drupal/restui:^1.21\nsudo -u www-data composer require drupal/console:~1.0\nsudo -u www-data composer require drupal/devel:^2.0\nsudo -u www-data composer require drupal/admin_toolbar:^2.0\n
"},{"location":"installation/manual/configuring-drupal/#enabling-downloaded-components","title":"Enabling Downloaded Components","text":"

Components we've now downloaded using composer require can be enabled simultaneously via drush, which will ensure they are installed in the correct dependent order. Enabling islandora_defaults will also ensure all content types and configurations are set up in Islandora. The installation process for all of these modules will likely take some time.

Notice

This list of modules assumes that all of the above components were downloaded using composer require; if this is not the case, you may need to pare down this list manually. It also includes devel, which again, should not be enabled on production sites.

cd /opt/drupal\ndrush -y en rdf responsive_image devel syslog serialization basic_auth rest restui search_api_solr facets content_browser pdf admin_toolbar controlled_access_terms_defaults islandora_breadcrumbs islandora_iiif islandora_oaipmh\n# After all of this, rebuild the cache.\ndrush -y cr\n
"},{"location":"installation/manual/configuring-drupal/#adding-a-jwt-configuration-to-drupal","title":"Adding a JWT Configuration to Drupal","text":"

To allow our installation to talk to other services via Syn, we need to establish a Drupal-side JWT configuration using the keys we generated at that time.

Log onto your site as an administrator at /user, then navigate to /admin/config/system/keys/add. Some of the settings here are unimportant, but pay close attention to the Key type, which should match the key we created earlier (an RSA key), and the File location, which should be the ultimate location of the key we created for Syn on the filesystem, /opt/keys/syn_private.key.

Click Save to create the key.

Once this key is created, navigate to /admin/config/system/jwt to select the key you just created from the list. Note that before the key will show up in the Private Key list, you need to select that key's type in the Algorithm section, namely RSASSA-PKCS1-v1_5 using SHA-256 (RS256).

Click Save configuration to establish this as the JWT key configuration.

"},{"location":"installation/manual/configuring-drupal/#configuring-islandora","title":"Configuring Islandora","text":"

Navigate to the Islandora core configuration page at /admin/config/islandora/core to set up the core configuration to connect to Gemini. Of note here, the Gemini URL will need to be established to facilitate the connection to Fedora, and the appropriate Bundles with Gemini URI pseudo field types will need to be checked off.

Notice

Any other Drupal content types you wish to synchronize with Fedora should also be checked off here.

"},{"location":"installation/manual/configuring-drupal/#configuring-islandora-iiif","title":"Configuring Islandora IIIF","text":"

Navigate to /admin/config/islandora/iiif to ensure that Islandora IIIF is pointing to our Cantaloupe server.

Next, configure OpenSeadragon by navigating to /admin/config/media/openseadragon and ensuring everything is set up properly.

"},{"location":"installation/manual/configuring-drupal/#establishing-flysystem-as-the-default-download-method","title":"Establishing Flysystem as the Default Download Method","text":"

Navigate to /admin/config/media/file-system to set the Default download method to the one we created in our settings.php.

"},{"location":"installation/manual/configuring-drupal/#giving-the-administrative-user-the-fedoraadmin-role","title":"Giving the Administrative User the fedoraAdmin Role","text":"

In order for data to be pushed back to Fedora, the site administrative user needs the fedoraAdmin role.

cd /opt/drupal\nsudo -u www-data drush -y urol \"fedoraadmin\" islandora\n
"},{"location":"installation/manual/configuring-drupal/#running-feature-migrations","title":"Running Feature Migrations","text":"

Finally, to get everything up and running, run the Islandora Core Features and Islandora Defaults migrations.

cd /opt/drupal\nsudo -u www-data drush -y -l localhost --userid=1 mim --group=islandora\n
"},{"location":"installation/manual/configuring-drupal/#enabling-eva-views","title":"Enabling EVA Views","text":"

Some views provided by Islandora are not enabled by default.

cd /opt/drupal\ndrush -y views:enable display_media\n
"},{"location":"installation/manual/installing-alpaca/","title":"Installing ActiveMQ and Alpaca","text":"

Karaf no longer needed

You no longer need to install Karaf. We no longer do this, we just deploy the java apps.

"},{"location":"installation/manual/installing-alpaca/#in-this-section-we-will-install","title":"In this section, we will install:","text":"
  • Apache ActiveMQ, a messaging server that will be used to handle communication between Alpaca and other components
  • Islandora/Alpaca, Java middleware that handle communication between various components of Islandora.
"},{"location":"installation/manual/installing-alpaca/#installing-activemq","title":"Installing ActiveMQ","text":"

Some users have been able to install ActiveMQ from the standard package repositories. Others, however, have needed to install it manually.

"},{"location":"installation/manual/installing-alpaca/#option-1-system-provided-packages","title":"Option 1: System Provided Packages","text":"
sudo apt-get -y install activemq\n

This will give us:

  • A base configuration at /var/lib/activemq/conf
  • A data storage directory at /var/lib/activemq/data
  • The base ActiveMQ installation at /usr/share/activemq
  • An activemq service that will be run on boot
  • A user, activemq, who will be in charge of the ActiveMQ service

Note the port used by ActiveMQ as this will be added to the JMS setting in the alpaca config below.

sudo apt-cache policy activemq\n

Write down the version listed under Installed:.

"},{"location":"installation/manual/installing-alpaca/#option-2-manual-install","title":"Option 2: Manual Install","text":"

Git the latest ActiveMQ 5.x version number from https://archive.apache.org/dist/activemq which will be put in place of [ACTIVEMQ_VERSION_NUMBER].

cd /opt\nsudo wget http://archive.apache.org/dist/activemq/[ACTIVEMQ_VERSION_NUMBER]/apache-activemq-[ACTIVEMQ_VERSION_NUMBER]-bin.tar.gz\nsudo tar -xvzf apache-activemq-[ACTIVEMQ_VERSION_NUMBER]-bin.tar.gz\nsudo mv apache-activemq-[ACTIVEMQ_VERSION_NUMBER] /opt/activemq\nsudo addgroup --quiet --system activemq\nsudo adduser --quiet --system --ingroup activemq --no-create-home --disabled-password activemq\nsudo chown -R activemq:activemq /opt/activemq\nsudo rm -R apache-activemq-[ACTIVEMQ_VERSION_NUMBER]-bin.tar.gz\n

Add ActiveMQ as a service: `/etc/systemd/system/activemq.service | root:root/644

[Unit]\nDescription=Apache ActiveMQ\nAfter=network.target\n\n[Service]\nType=forking\nUser=activemq\nGroup=activemq\n\nExecStart=/opt/activemq/bin/activemq start\nExecStop=/opt/activemq/bin/activemq stop\n\n[Install]\nWantedBy=multi-user.target\n

Update the WebConsolePort host property settings in /opt/activemq/conf/jetty.xml from <property name=\"host\" value=\"127.0.0.1\"/> to <property name=\"host\" value=\"0.0.0.0\"/> so that you can access the dashboard from outside the local machine.

Optionally, change the dashboard user credentials in /opt/activemq/conf/users.properties.

Security Warning

Updating the web console port and user properties are potential security holes. It is best to restrict the host setting and create a more secure username/password combination for production.

Set the service to start on machine startup and start it up:

sudo systemctl daemon-reload\nsudo systemctl start activemq\nsudo systemctl enable activemq\nsudo systemctl status activemq\nsudo systemctl restart activemq\nsudo apt-cache policy activemq   # note version number\n

The service should now be available at http://localhost:8161/

"},{"location":"installation/manual/installing-alpaca/#installing-alpaca","title":"Installing Alpaca","text":"

Install Java 11+ if you haven't already.

Make a directory for Alpaca and download the latest version of Alpaca from the Maven repository. E.g.

sudo mkdir /opt/alpaca\ncd /opt/alpaca\nsudo curl -L https://repo1.maven.org/maven2/ca/islandora/alpaca/islandora-alpaca-app/2.2.0/islandora-alpaca-app-2.2.0-all.jar -o alpaca.jar\n

"},{"location":"installation/manual/installing-alpaca/#configuration","title":"Configuration","text":"

Alpaca is made up of several services, each of these can be enabled or disabled individually.

Alpaca takes an external file to configure its behaviour.

Look at the example.properties file to see some example settings.

The properties are:

# Common options\nerror.maxRedeliveries=5\n
This defines how many times to retry a message before failing completely.

There are also common ActiveMQ properties to setup the connection.

# ActiveMQ options\njms.brokerUrl=tcp://localhost:61616\n

This defines the url to the ActiveMQ broker which you installed earlier.

jms.username=\njms.password=\n
This defines the login credentials (if required)

jms.connections=10\n
This defines the pool of connections to the ActiveMQ instance.

"},{"location":"installation/manual/installing-alpaca/#islandora-indexing-fcrepo","title":"islandora-indexing-fcrepo","text":"

This service manages a Drupal node into a corresponding Fedora resource.

It's properties are:

# Fcrepo indexer options\nfcrepo.indexer.enabled=true\n

This defines whether the Fedora indexer is enabled or not.

fcrepo.indexer.node=queue:islandora-indexing-fcrepo-content\nfcrepo.indexer.delete=queue:islandora-indexing-fcrepo-delete\nfcrepo.indexer.media=queue:islandora-indexing-fcrepo-media\nfcrepo.indexer.external=queue:islandora-indexing-fcrepo-file-external\n

These define the various queues to listen on for the indexing/deletion messages. The part after queue: should match your Islandora instance \"Actions\".

fcrepo.indexer.milliner.baseUrl=http://localhost/milliner\n
This defines the location of your Milliner microservice.

fcrepo.indexer.concurrent-consumers=-1\nfcrepo.indexer.max-concurrent-consumers=-1\n
These define the default number of concurrent consumers and maximum number of concurrent consumers working off your ActiveMQ instance. A value of -1 means no setting is applied.

fcrepo.indexer.async-consumer=false\n

This property allows the concurrent consumers to process concurrently; otherwise, the consumers will wait to the previous message has been processed before executing.

"},{"location":"installation/manual/installing-alpaca/#islandora-indexing-triplestore","title":"islandora-indexing-triplestore","text":"

This service indexes the Drupal node into the configured triplestore

It's properties are:

# Triplestore indexer options\ntriplestore.indexer.enabled=true\n

This defines whether the Triplestore indexer is enabled or not.

triplestore.index.stream=queue:islandora-indexing-triplestore-index\ntriplestore.delete.stream=queue:islandora-indexing-triplestore-delete\n

These define the various queues to listen on for the indexing/deletion messages. The part after queue: should match your Islandora instance \"Actions\".

triplestore.baseUrl=http://localhost:8080/bigdata/namespace/kb/sparql\n

This defines the location of your triplestore's SPARQL update endpoint.

triplestore.indexer.concurrent-consumers=-1\ntriplestore.indexer.max-concurrent-consumers=-1\n

These define the default number of concurrent consumers and maximum number of concurrent consumers working off your ActiveMQ instance. A value of -1 means no setting is applied.

triplestore.indexer.async-consumer=false\n

This property allows the concurrent consumers to process concurrently; otherwise, the consumers will wait to the previous message has been processed before executing.

"},{"location":"installation/manual/installing-alpaca/#islandora-connector-derivative","title":"islandora-connector-derivative","text":"

This service is used to configure an external microservice. This service will deploy multiple copies of its routes with different configured inputs and outputs based on properties.

The routes to be configured are defined with the property derivative.systems.installed which expects a comma separated list. Each item in the list defines a new route and must also define 3 additional properties.

derivative.<item>.enabled=true\n

This defines if the item service is enabled.

derivative.<item>.in.stream=queue:islandora-item-connector-<item>\n

This is the input queue for the derivative microservice. The part after queue: should match your Islandora instance \"Actions\".

derivative.<item>.service.url=http://example.org/derivative/convert\n

This is the microservice URL to process the request.

derivative.<item>.concurrent-consumers=-1\nderivative.<item>.max-concurrent-consumers=-1\n

These define the default number of concurrent consumers and maximum number of concurrent consumers working off your ActiveMQ instance. A value of -1 means no setting is applied.

derivative.<item>.async-consumer=false\n

This property allows the concurrent consumers to process concurrently; otherwise, the consumers will wait to the previous message has been processed before executing.

For example, with two services defined (houdini and crayfits) my configuration would have

derivative.systems.installed=houdini,fits\n\nderivative.houdini.enabled=true\nderivative.houdini.in.stream=queue:islandora-connector-houdini\nderivative.houdini.service.url=http://127.0.0.1/houdini/convert\nderivative.houdini.concurrent-consumers=1\nderivative.houdini.max-concurrent-consumers=4\nderivative.houdini.async-consumer=true\n\nderivative.fits.enabled=true\nderivative.fits.in.stream=queue:islandora-connector-fits\nderivative.fits.service.url=http://127.0.0.1/crayfits\nderivative.fits.concurrent-consumers=2\nderivative.fits.max-concurrent-consumers=2\nderivative.fits.async-consumer=false\n
"},{"location":"installation/manual/installing-alpaca/#customizing-http-client-timeouts","title":"Customizing HTTP client timeouts","text":"

You can alter the HTTP client from the defaults for its request, connection and socket timeouts. To do this you want to enable the request configurer.

request.configurer.enabled=true\n

Then set the next 3 timeouts (measured in milliseconds) to the desired timeout.

request.timeout=-1\nconnection.timeout=-1\nsocket.timeout=-1\n

The default for all three is -1 which indicates no timeout.

"},{"location":"installation/manual/installing-alpaca/#alter-http-options","title":"Alter HTTP options","text":"

By default, Alpaca uses two settings for the HTTP component, these are * disableStreamCache=true * connectionClose=true

If you want to send additional configuration parameters or alter the existing defaults. You can add them as a comma separated list of key=value pairs.

For example

http.additional_options=authMethod=Basic,authUsername=Jim,authPassword=1234\n

These will be added to ALL http endpoint requests.

Check Camel Configuration Parameters

We are currently running Camel 3.7.6, some configuration parameters on the above linked page might not be supported.

"},{"location":"installation/manual/installing-alpaca/#deployingrunning","title":"Deploying/Running","text":"

You can see the options by passing the -h|--help flag

> java -jar /opt/alpaca/alpaca.jar -h\nUsage: alpaca [-hV] [-c=<configurationFilePath>]\n-h, --help      Show this help message and exit.\n  -V, --version   Print version information and exit.\n  -c, --config=<configurationFilePath>\n                  The path to the configuration file\n

Using the -V|--version flag will just return the current version of the application.

> java -jar /opt/alpaca/alpaca.jar -v\n2.0.0\n

To start Alpaca you would pass the external property file with the -c|--config flag.

For example if you are using an external properties file located at /opt/alpaca/alpaca.properties, you would run:

java -jar alpaca.jar -c /opt/alpaca/alpaca.properties\n

To use systemd to start and stop the service create the file /etc/systemd/system/alpaca.service:

[Unit]\nDescription=Alpaca service\nAfter=network.target\n\n[Service]\nType=simple\nExecStart=java -jar /opt/alpaca/alpaca.jar -c /opt/alpaca/alpaca.properties\nExecStop=/bin/kill -15 $MAINPID\nSuccessExitStatus=143\nRestart=always\n\n[Install]\nWantedBy=default.target\n

Now you can start the service by running sudo systemctl start alpaca and set it to come up when the system reboots with sudo systemctl enable alpaca. Check the status by running sudo systemctl status alpaca.

"},{"location":"installation/manual/installing-composer-drush-and-drupal/","title":"Installing Composer, Drush, and Drupal","text":"

Needs Maintenance

The manual installation documentation is in need of attention. We are aware that some components no longer work as documented here. If you are interested in helping us improve the documentation, please see Contributing.

"},{"location":"installation/manual/installing-composer-drush-and-drupal/#in-this-section-we-will-install","title":"In this section, we will install:","text":"
  • cURL is used by composer to efficiently download libraries
  • Composer at its current latest version, the package manager that will allow us to install PHP applications
  • Either the Islandora Starter Site, or the Drupal recommended-project, which will install, among other things:
    • Drush at its latest version, the command-line PHP application for running tasks in Drupal
    • Drupal at its latest version, the content management system Islandora uses for content modelling and front-end display
"},{"location":"installation/manual/installing-composer-drush-and-drupal/#install-curl","title":"Install cURL","text":"

cURL may already be installed. Check by running curl --version. If it isn't, install it:

sudo apt install curl\n
"},{"location":"installation/manual/installing-composer-drush-and-drupal/#install-composer","title":"Install Composer","text":""},{"location":"installation/manual/installing-composer-drush-and-drupal/#download-and-install-composer-2x","title":"Download and install Composer 2.x","text":"

Composer provides PHP code that we can use to install it. After downloading and running the installer, we\u2019re going to move the generated executable to a place in $PATH, removing its extension:

curl \"https://getcomposer.org/installer\" > composer-install.php\nchmod +x composer-install.php\nphp composer-install.php\nsudo mv composer.phar /usr/local/bin/composer\n
"},{"location":"installation/manual/installing-composer-drush-and-drupal/#download-and-scaffold-drupal","title":"Download and Scaffold Drupal","text":"

At this point, you have the option of using the Islandora Starter Site, with its pre-selected modules and configurations which function \"out of the box,\" or build a clean stock Drupal via the Drupal Recommended Project and install Islandora modules as you desire.

On a default Ubuntu install the /var/www directory is owned by root, but we want the webserver to control this space, so we'll give it ownership:

sudo chown -R www-data: /var/www\n
"},{"location":"installation/manual/installing-composer-drush-and-drupal/#option-1-create-a-project-using-the-islandora-starter-site","title":"Option 1: Create a project using the Islandora Starter Site","text":"

Navigate to the folder where you want to put your Islandora project (in our case /var/www/html). You can give your site any name you like, but for these instructions we will simply call it \"drupal\":

cd /var/www/html\nsudo -u www-data mkdir drupal\nsudo -u www-data composer create-project islandora/islandora-starter-site drupal\n

This will install all PHP dependencies, including Drush, and scaffold the site.

Drush is not accessible via $PATH, but is available using the command composer exec -- drush

"},{"location":"installation/manual/installing-composer-drush-and-drupal/#option-2-create-a-basic-drupal-recommended-project","title":"Option 2: Create a basic Drupal Recommended Project","text":"

Navigate to the folder where you want to put your Drupal project (in our case /var/www/html), and create the Drupal Recommended Project:

cd /var/www/html\nsudo -u www-data composer create-project drupal/recommended-project drupal\n
"},{"location":"installation/manual/installing-composer-drush-and-drupal/#make-the-new-webroot-accessible-in-apache","title":"Make the new webroot accessible in Apache","text":"

Before we can proceed with the actual site installation, we\u2019re going to need to make our new Drupal installation the default web-accessible location Apache serves up. This will include an appropriate ports.conf file, and replacing the default enabled site.

Notice

Out of the box, these files will contain support for SSL, which we will not be setting up in this guide (and therefore removing with these overwritten configurations), but which are absolutely indispensable to a production site. This guide does not recommend any particular SSL certificate authority or installation method, but you may find DigitalOcean's tutorial helpful.

/etc/apache2/ports.conf | root:root/644

Listen 80\n

Remove everything but the \"Listen 80\" line. You can leave the comments in if you want.

Create a drupal virtual host: /etc/apache2/sites-available/islandora.conf | root:root/644

<VirtualHost *:80>\nServerName SERVER_NAME\n  DocumentRoot \"/var/www/html/drupal/web\"\n  <Directory \"/var/www/html/drupal/web\">\nOptions Indexes FollowSymLinks MultiViews\n    AllowOverride all\n    Require all granted\n  </Directory>\n# Ensure some logging is in place.\n  ErrorLog \"/var/log/apache2/localhost_error.log\"\n  CustomLog \"/var/log/apache2/localhost_access.log\" combined\n</VirtualHost>\n
- SERVER_NAME: localhost - For a development environment hosted on your own machine or a VM, localhost should suffice. Realistically, this should be the domain or IP address the server will be accessed at.

Set permissions and enable the virtual host:

sudo systemctl restart apache2\nsudo a2ensite islandora.conf\nsudo systemctl reload apache2\n
"},{"location":"installation/manual/installing-composer-drush-and-drupal/#prepare-the-mysql-database","title":"Prepare the MySQL database","text":"

We're going to create a user in MySQL for this Drupal site. Then create a database that we can use to install Drupal.

The following values can (and in the case of the password, should) be changed to local values.

  • DRUPAL_DATABASE_NAME: This will be used as the core database that Drupal is installed into
  • MYSQL_USER_FOR_DRUPAL: Specifically, this is the user that will connect to the MySQL database being created, not the user that will be logging into Drupal
  • MYSQL_PASSWORD_FOR_DRUPAL: This should be a secure password; it\u2019s recommended to use a password generator to create this such as the one provided by random.org
sudo mysql -u root\nCREATE DATABASE [DRUPAL_DATABASE_NAME];\nCREATE USER '[MYSQL_USER_FOR_DRUPAL]'@'localhost' IDENTIFIED BY '[MYSQL_PASSWORD_FOR_DRUPAL]';\nGRANT ALL PRIVILEGES ON [DRUPAL_DATABASE_NAME].* TO '[MYSQL_USER_FOR_DRUPAL]'@'localhost';\nexit\n
"},{"location":"installation/manual/installing-composer-drush-and-drupal/#install-drupal-using-drush","title":"Install Drupal using Drush","text":"

The Drupal installation process can be done through the GUI in a series of form steps, or can be done quickly using Drush's site-install command. It can be invoked with the full list of parameters (such as --db-url and --site-name), but if parameters are missing, they will be asked of you interactively.

"},{"location":"installation/manual/installing-composer-drush-and-drupal/#option-1-site-install-the-starter-site-with-existing-configs","title":"Option 1: Site install the Starter Site with existing configs","text":"

Follow the instructions in the README of the Islandora Starter Site. The steps are not reproduced here to remove redundancy. But specifically,

  1. Configure the database connection information (see the section above) and fedora flysystem in /var/www/html/drupal/web/sites/default/settings.php.
  2. Install the site using sudo -u www-data composer exec -- drush site:install --existing-config.

When this installation is done, you'll have a starter site ready-to-go. Once you set up the external services in the next sections, you'll need to configure Drupal to know where they are.

"},{"location":"installation/manual/installing-composer-drush-and-drupal/#option-2-site-install-the-basic-drupal-recommended-project","title":"Option 2: Site install the basic Drupal Recommended Project","text":"

cd /var/www/drupal\nsudo -u www-data drush -y site-install standard --db-url=\"mysql://MYSQL_USER_FOR_DRUPAL:MYSQL_PASSWORD_FOR_DRUPAL@127.0.0.1:3306/DRUPAL_DATABASE_NAME\" --site-name=\"SITE_NAME\" --account-name=DRUPAL_LOGIN --account-pass=DRUPAL_PASS\n
This uses the same parameters from the above step, as well as:

  • SITE_NAME: Islandora 2.0
    • This is arbitrary, and is simply used to title the site on the home page
  • DRUPAL_LOGIN: islandora
    • The Drupal administrative username to use
  • DRUPAL_PASS: islandora
    • The password to use for the Drupal administrative user

Congratulations, you have a Drupal site! It currently isn\u2019t really configured to do anything, but we\u2019ll get those portions set up in the coming sections.

"},{"location":"installation/manual/installing-crayfish/","title":"Installing Crayfish","text":"

Needs Maintenance

The manual installation documentation is in need of attention. We are aware that some components no longer work as documented here. If you are interested in helping us improve the documentation, please see Contributing.

"},{"location":"installation/manual/installing-crayfish/#in-this-section-we-will-install","title":"In this section, we will install:","text":"
  • FITS Web Service, a webservice for identifying file metadata
  • Islandora/Crayfish, the suite of microservices that power the backend of Islandora 2.0
  • Indvidual microservices underneath Crayfish
"},{"location":"installation/manual/installing-crayfish/#fits-web-service","title":"FITS Web Service","text":"

The FITS Web Service is used to extract file metadata from files. The Crayfish microservice CrayFits will use this service to push FITS metadata back to Drupal. It comes in two pieces, the actual FITS tool and the FITS Webservice which runs in Tomcat.

FITS itself wraps other file identification and metadata tools which may require installing additional libraries. On Ububtu 20.04, the version this guide is using, we will install a few:

sudo apt install mediainfo python3-jpylyzer\n

To set up the FITS application, first find the latest FITS version on GitHub to replace the [FITS_VERSION_NUMBER] and then run the following commands:

cd /opt\nsudo wget https://github.com/harvard-lts/fits/releases/download/[FITS_VERSION_NUMBER]/fits-[FITS_VERSION_NUMBER].zip\nsudo unzip /opt/fits-[FITS_VERSION_NUMBER].zip -d /opt/fits\n

Similarly with the FITS webservice, get the current service version number to replace [FITS_SERVICE_WAR_VERSION_NUMBER]:

Download the FITS webservice:

sudo -u tomcat wget -O /opt/tomcat/webapps/fits.war https://github.com/harvard-lts/FITSservlet/releases/download/[FITS_SERVICE_WAR_VERSION_NUMBER]/fits-service-[FITS_SERVICE_WAR_VERSION_NUMBER].war\n

Configure the webservice but adding the following lines to the bottom of /opt/tomcat/conf/catalina.properties:

fits.home=/opt/fits\nshared.loader=/opt/fits/lib/*.jar\n

Restart Tomcat:

sudo systemctl restart tomcat\n

Wait for a few minutes to let the service start up the first time and then visit http://localhost:8080/fits/ to ensure it is working. You can also follow the catalina logs to see how tomcat is progressing in setting up each service it is running: sudo tail -f /opt/tomcat/logs/catalina.out. To stop following the logs, hit control-C.

"},{"location":"installation/manual/installing-crayfish/#crayfish-20","title":"Crayfish 2.0","text":""},{"location":"installation/manual/installing-crayfish/#installing-prerequisites","title":"Installing Prerequisites","text":"

Some packages need to be installed before we can proceed with installing Crayfish; these packages are used by the microservices within Crayfish. These include:

  • Imagemagick, which will be used for image processing. We'll be using the LYRASIS build of imagemagick here, which supports JP2 files.
  • Tesseract, which will be used for optical character recognition; note that by default Tesseract can only understand English; several other individual Tesseract language packs can be installed using apt-get, and a list of available packs can be procured with sudo apt-cache search tesseract-ocr
  • FFMPEG, which will be used for video processing
  • Poppler, which will be used for generating PDFs
sudo apt-get install software-properties-common\nsudo add-apt-repository -y ppa:lyrasis/imagemagick-jp2\nsudo apt-get update\nsudo apt-get -y install imagemagick tesseract-ocr ffmpeg poppler-utils\n
"},{"location":"installation/manual/installing-crayfish/#cloning-and-installing-crayfish","title":"Cloning and Installing Crayfish","text":"

We\u2019re going to clone Crayfish to /opt, and individually run composer install against each of the microservice subdirectories.

cd /opt\nsudo git clone https://github.com/Islandora/Crayfish.git crayfish\nsudo chown -R www-data:www-data crayfish\nsudo -u www-data composer install -d crayfish/Homarus\nsudo -u www-data composer install -d crayfish/Houdini\nsudo -u www-data composer install -d crayfish/Hypercube\nsudo -u www-data composer install -d crayfish/Milliner\nsudo -u www-data composer install -d crayfish/Recast\nsudo -u www-data composer install -d crayfish/CrayFits\n
"},{"location":"installation/manual/installing-crayfish/#preparing-logging","title":"Preparing Logging","text":"

Not much needs to happen here; Crayfish opts for a simple logging approach, with one .log file for each component. We\u2019ll create a folder where each logfile can live.

sudo mkdir /var/log/islandora\nsudo chown www-data:www-data /var/log/islandora\n
"},{"location":"installation/manual/installing-crayfish/#configuring-crayfish-components","title":"Configuring Crayfish Components","text":"

Each Crayfish component requires one or more .yaml file(s) to ensure everything is wired up correctly.

Update the defaults to meet your needs

The following configuration files represent somewhat sensible defaults; you should take consideration of the logging levels in use, as this can vary in desirability from installation to installation. Also note that in all cases, http URLs are being used, as this guide does not deal with setting up https support. In a production installation, this should not be the case. These files also assume a connection to a PostgreSQL database; use a pdo_mysql driver and the appropriate 3306 port if using MySQL.

Using JWT for Crayfish Authentication

For Crayfish microservices use the lexik_jwt_authentication package. They are configured to use the JWT_PUBLIC_KEY environment variable to find the public key we created earlier (/opt/keys/syn_public.key). Later on in this guide we will add the environment variable to the Apache configs, but you may alternatively write the path to the key in the lexik_jwt_authentication.yaml file that resides along-side the security.yaml files we edit in this section.

"},{"location":"installation/manual/installing-crayfish/#homarus-audiovideo-derivatives","title":"Homarus (Audio/Video derivatives)","text":"

Enable JSON Web Token (JWT) based access to the service by updating the security settings. Edit /opt/crayfish/Homarus/config/packages/security.yaml to set firewalls: main: anonymous to false and uncomment the provider and jwt lines further down in that section.

Edit /opt/crayfish/Homarus/config/packages/monolog.yaml to point to the new logging directory:

        homarus:\n            type: rotating_file\n            path: /var/logs/islandora/Homarus.log\n

Edit the commons config to update it with Fedora's location (if necessary) and enable the apix middleware in /opt/crayfish/Homarus/config/packages/crayfish_commons.yaml:

crayfish_commons:\n  fedora_base_uri: 'http://localhost:8080/fcrepo/rest'\n  apix_middleware_enabled: true\n
"},{"location":"installation/manual/installing-crayfish/#houdini-image-derivatives","title":"Houdini (Image derivatives)","text":"

Currently the Houdini microservice uses a different system (Symfony) than the other microservices, this requires different configuration.

/opt/crayfish/Houdini/config/services.yaml | www-data:www-data/644

# This file is the entry point to configure your own services.\n# Files in the packages/ subdirectory configure your dependencies.\n# Put parameters here that don't need to change on each machine where the app is deployed\n# https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration\nparameters:\napp.executable: /usr/bin/convert\napp.formats.valid:\n- image/jpeg\n- image/png\n- image/tiff\n- image/jp2\napp.formats.default: image/jpeg\nservices:\n# default configuration for services in *this* file\n_defaults:\nautowire: true      # Automatically injects dependencies in your services.\nautoconfigure: true # Automatically registers your services as commands, event subscribers, etc.\n# makes classes in src/ available to be used as services\n# this creates a service per class whose id is the fully-qualified class name\nApp\\Islandora\\Houdini\\:\nresource: '../src/*'\nexclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'\n# controllers are imported separately to make sure services can be injected\n# as action arguments even if you don't extend any base controller class\nApp\\Islandora\\Houdini\\Controller\\HoudiniController:\npublic: false\nbind:\n$formats: '%app.formats.valid%'\n$default_format: '%app.formats.default%'\n$executable: '%app.executable%'\ntags: ['controller.service_arguments']\n# add more service definitions when explicit configuration is needed\n# please note that last definitions always *replace* previous ones\n

/opt/crayfish/Houdini/config/packages/crayfish_commons.yaml | www-data:www-data/644

crayfish_commons:\nfedora_base_uri: 'http://localhost:8080/fcrepo/rest'\nsyn_config: /opt/fcrepo/config/syn-settings.xml\nsyn_enabled: True\n

/opt/crayfish/Houdini/config/packages/monolog.yaml | www-data:www-data/644

monolog:\nhandlers:\nhoudini:\ntype: rotating_file\npath: /var/log/islandora/Houdini.log\nlevel: DEBUG\nmax_files: 1\n

The below files are two versions of the same file to enable or disable JWT token authentication.

/opt/crayfish/Houdini/config/packages/security.yaml | www-data:www-data/644

Enabled JWT token authentication:

# To disable Syn checking, set syn_enabled=false in crayfish_commons.yaml and remove this configuration file.\nsecurity:\n# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers\nproviders:\nusers_in_memory: { memory: null }\njwt:\nlexik_jwt: ~\nfirewalls:\ndev:\npattern: ^/(_(profiler|wdt)|css|images|js)/\nsecurity: false\nmain:\n# To enable Syn, change anonymous to false and uncomment the lines further below\nanonymous: false\n# Need stateless or it reloads the User based on a token.\nstateless: true\n# To enable JWT authentication, uncomment the below 2 lines and change anonymous to false above.\nprovider: jwt\njwt: ~\n# activate different ways to authenticate\n# https://symfony.com/doc/5.4/security.html#firewalls-authentication\n# https://symfony.com/doc/5.4/security/impersonating_user.html\n# switch_user: true\n# Easy way to control access for large sections of your site\n# Note: Only the *first* access control that matches will be used\naccess_control:\n# - { path: ^/admin, roles: ROLE_ADMIN }\n# - { path: ^/profile, roles: ROLE_USER }\n

Disabled JWT token authentication:

security:\n# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers\nproviders:\njwt_user_provider:\nid: Islandora\\Crayfish\\Commons\\Syn\\JwtUserProvider\nfirewalls:\ndev:\npattern: ^/(_(profiler|wdt)|css|images|js)/\nsecurity: false\nmain:\nanonymous: true\n# Need stateless or it reloads the User based on a token.\nstateless: true\n

"},{"location":"installation/manual/installing-crayfish/#hypercube-ocr","title":"Hypercube (OCR)","text":"

Enable JSON Web Token (JWT) based access to the service by updating the security settings. Edit /opt/crayfish/Hypercube/config/packages/security.yaml to set firewalls: main: anonymous to false and uncomment the provider and jwt lines further down in that section.

Edit /opt/crayfish/Hypercube/config/packages/monolog.yaml to point to the new logging directory:

        hypercube:\n            type: rotating_file\n            path: /var/logs/islandora/Hypercube.log\n

Edit the commons config to update it with Fedora's location (if necessary) and enable the apix middleware in /opt/crayfish/Hypercube/config/packages/crayfish_commons.yaml:

crayfish_commons:\n  fedora_base_uri: 'http://localhost:8080/fcrepo/rest'\n  apix_middleware_enabled: true\n
"},{"location":"installation/manual/installing-crayfish/#milliner-fedora-indexing","title":"Milliner (Fedora indexing)","text":"

Enable JSON Web Token (JWT) based access to the service by updating the security settings. Edit /opt/crayfish/Milliner/config/packages/security.yaml to set firewalls: main: anonymous to false and uncomment the provider and jwt lines further down in that section.

Edit /opt/crayfish/Milliner/config/packages/monolog.yaml to point to the new logging directory:

        milliner:\n            type: rotating_file\n            path: /var/logs/islandora/Milliner.log\n

Edit the commons config to update it with Fedora's location (if necessary) and enable the apix middleware in /opt/crayfish/Milliner/config/packages/crayfish_commons.yaml:

"},{"location":"installation/manual/installing-crayfish/#creating-apache-configurations-for-crayfish-components","title":"Creating Apache Configurations for Crayfish Components","text":"

Finally, we need appropriate Apache configurations for Crayfish; these will allow other services to connect to Crayfish components via their HTTP endpoints.

Each endpoint we need to be able to connect to will get its own .conf file, which we will then enable.

Possible Route Collisions

These configurations would potentially have collisions with Drupal routes, if any are created in Drupal with the same name. If this is a concern, it would likely be better to reserve a subdomain or another port specifically for Crayfish. For the purposes of this installation guide, these endpoints will suffice.

/etc/apache2/conf-available/Homarus.conf | root:root/644

Alias \"/homarus\" \"/opt/crayfish/Homarus/public\"\n<Directory \"/opt/crayfish/Homarus/public\">\n  FallbackResource /homarus/index.php\n  Require all granted\n  DirectoryIndex index.php\n  SetEnv JWT_PUBLIC_KEY /opt/keys/syn_public.key\n  SetEnvIf Authorization \"(.*)\" HTTP_AUTHORIZATION=$1\n</Directory>\n

/etc/apache2/conf-available/Houdini.conf | root:root/644

Alias \"/houdini\" \"/opt/crayfish/Houdini/public\"\n<Directory \"/opt/crayfish/Houdini/public\">\n  FallbackResource /houdini/index.php\n  Require all granted\n  DirectoryIndex index.php\n  SetEnv JWT_PUBLIC_KEY /opt/keys/syn_public.key\n  SetEnvIf Authorization \"(.*)\" HTTP_AUTHORIZATION=$1\n</Directory>\n

/etc/apache2/conf-available/Hypercube.conf | root:root/644

Alias \"/hypercube\" \"/opt/crayfish/Hypercube/public\"\n<Directory \"/opt/crayfish/Hypercube/public\">\n  FallbackResource /hypercube/index.php\n  Require all granted\n  DirectoryIndex index.php\n  SetEnv JWT_PUBLIC_KEY /opt/keys/syn_public.key\n  SetEnvIf Authorization \"(.*)\" HTTP_AUTHORIZATION=$1\n</Directory>\n

/etc/apache2/conf-available/Milliner.conf | root:root/644

Alias \"/milliner\" \"/opt/crayfish/Milliner/public\"\n<Directory \"/opt/crayfish/Milliner/public\">\n  FallbackResource /milliner/index.php\n  Require all granted\n  DirectoryIndex index.php\n  SetEnv JWT_PUBLIC_KEY /opt/keys/syn_public.key\n  SetEnvIf Authorization \"(.*)\" HTTP_AUTHORIZATION=$1\n</Directory>\n

/etc/apache2/conf-available/CrayFits.conf | root:root/644

Alias \"/crayfits\" \"/opt/crayfish/CrayFits/public\"\n<Directory \"/opt/crayfish/CrayFits/public\">\n  FallbackResource /crayfits/index.php\n  Require all granted\n  DirectoryIndex index.php\n  SetEnv JWT_PUBLIC_KEY /opt/keys/syn_public.key\n  SetEnvIf Authorization \"(.*)\" HTTP_AUTHORIZATION=$1\n</Directory>\n

"},{"location":"installation/manual/installing-crayfish/#enabling-each-crayfish-component-apache-configuration","title":"Enabling Each Crayfish Component Apache Configuration","text":"

Enabling each of these configurations involves creating a symlink to them in the conf-enabled directory; the standardized method of doing this in Apache is with a2enconf.

sudo a2enconf Homarus Houdini Hypercube Milliner CrayFits\n
"},{"location":"installation/manual/installing-crayfish/#restarting-the-apache-service","title":"Restarting the Apache Service","text":"

Finally, to get these new endpoints up and running, we need to restart the Apache service.

sudo systemctl restart apache2\n
"},{"location":"installation/manual/installing-fedora-syn-and-blazegraph/","title":"Installing Fedora, Syn, and Blazegraph","text":""},{"location":"installation/manual/installing-fedora-syn-and-blazegraph/#in-this-section-we-will-install","title":"In this section, we will install:","text":"
  • Fedora 6, the back-end repository that Islandora will use
  • Syn, the authentication broker that will manage communication with Fedora
  • Blazegraph, the resource index layer on top of Fedora for managing discoverability via RDF
"},{"location":"installation/manual/installing-fedora-syn-and-blazegraph/#fedora-6","title":"Fedora 6","text":""},{"location":"installation/manual/installing-fedora-syn-and-blazegraph/#stop-the-tomcat-service","title":"Stop the Tomcat Service","text":"

We're going to stop the Tomcat service while working on setting up Fedora to prevent any autodeploy misconfigurations.

sudo systemctl stop tomcat\n
"},{"location":"installation/manual/installing-fedora-syn-and-blazegraph/#creating-a-working-space-for-fedora","title":"Creating a Working Space for Fedora","text":"

Fedora\u2019s configuration and data won\u2019t live with Tomcat itself; rather, we\u2019re going to prepare a space for them to make them easier to manage.

sudo mkdir -p /opt/fcrepo/data/objects\nsudo mkdir /opt/fcrepo/config\nsudo chown -R tomcat:tomcat /opt/fcrepo\n
"},{"location":"installation/manual/installing-fedora-syn-and-blazegraph/#creating-a-database-for-fedora","title":"Creating a Database for Fedora","text":"

The method for creating the database here will closely mimic the method we used to create our database for Drupal.

sudo mysql -u root\nCREATE DATABASE [FEDORA_DB];\nCREATE USER '[MYSQL_USER_FOR_FEDORA]'@'localhost' IDENTIFIED BY '[MYSQL_PASSWORD_FOR_FEDORA]';\nGRANT ALL PRIVILEGES ON [FEDORA_DB].* TO '[MYSQL_USER_FOR_FEDORA]'@'localhost';\n
  • FEDORA_DB: fcrepo
    • This will be used as the database Fedora will store the repository in.
  • FEDORA_DB_USER: fedora
  • FEDORA_DB_PASSWORD: fedora
    • Again, this should be a secure password of some kind; leaving it as fedora is not recommended.
"},{"location":"installation/manual/installing-fedora-syn-and-blazegraph/#adding-a-fedora-configuration","title":"Adding a Fedora Configuration","text":"

The Fedora configuration is going to come in a few different chunks that need to be in place before Fedora will be functional. We\u2019re going to place several files outright, with mildly modified parameters according to our configuration.

The basics of these configuration files have been pulled largely from the templates in Islandora-Devops/islandora-playbook internal Fedora role; you may consider referencing the playbook\u2019s templates directory for more details.

"},{"location":"installation/manual/installing-fedora-syn-and-blazegraph/#namespace-prefixes","title":"Namespace prefixes","text":"

i8_namespaces.yml is a list of namespaces used by Islandora that may not necessarily be present in Fedora; we add them here to ensure we can use them in queries.

/opt/fcrepo/config/i8_namespaces.yml | tomcat:tomcat/644

# Islandora 8/Fedora namespaces\n#\n# This file contains ALL the prefix mappings, if a URI\n# does not appear in this file it will be displayed as \n# the full URI in Fedora. \nacl: http://www.w3.org/ns/auth/acl#\nbf: http://id.loc.gov/ontologies/bibframe/\ncc: http://creativecommons.org/ns#\ndc: http://purl.org/dc/elements/1.1/\ndcterms: http://purl.org/dc/terms/\ndwc: http://rs.tdwg.org/dwc/terms/\nebucore: http://www.ebu.ch/metadata/ontologies/ebucore/ebucore#\nexif: http://www.w3.org/2003/12/exif/ns#\nfedoraconfig: http://fedora.info/definitions/v4/config#\nfedoramodel: info:fedora/fedora-system:def/model#\nfoaf: http://xmlns.com/foaf/0.1/\ngeo: http://www.w3.org/2003/01/geo/wgs84_pos#\ngn: http://www.geonames.org/ontology#\niana: http://www.iana.org/assignments/relation/\nislandorarelsext: http://islandora.ca/ontology/relsext#\nislandorarelsint: http://islandora.ca/ontology/relsint#\nldp: http://www.w3.org/ns/ldp#\nmemento: http://mementoweb.org/ns#\nnfo: http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#\nore: http://www.openarchives.org/ore/terms/\nowl: http://www.w3.org/2002/07/owl#\npremis: http://www.loc.gov/premis/rdf/v1#\nprov: http://www.w3.org/ns/prov#\nrdf: http://www.w3.org/1999/02/22-rdf-syntax-ns#\nrdfs: http://www.w3.org/2000/01/rdf-schema#\nrel: http://id.loc.gov/vocabulary/relators/\nschema: http://schema.org/\nskos: http://www.w3.org/2004/02/skos/core#\ntest: info:fedora/test/\nvcard: http://www.w3.org/2006/vcard/ns#\nwebac: http://fedora.info/definitions/v4/webac#\nxml: http://www.w3.org/XML/1998/namespace\nxmlns: http://www.w3.org/2000/xmlns/\nxs: http://www.w3.org/2001/XMLSchema\nxsi: http://www.w3.org/2001/XMLSchema-instance\n

"},{"location":"installation/manual/installing-fedora-syn-and-blazegraph/#allowed-external-content-hosts","title":"Allowed External Content Hosts","text":"

We have Fedora provide metadata for some resources that are contained in Drupal. Fedora needs to know to allow access to these External Content hosts.

We create a file /opt/fcrepo/config/allowed_external_hosts.txt | tomcat:tomcat/644

http://localhost:8000/\n

Note: the trailing backslash is important here. For more information on Fedora's External Content and configuring it, see the Fedora Wiki pages

"},{"location":"installation/manual/installing-fedora-syn-and-blazegraph/#fedora-configuration-properties-file","title":"Fedora configuration properties file","text":"

Fedora 6 now allows you to put all your configuration properties into a single file. We use 0640 permissions as you will want to put your database credentials in here.

/opt/fcrepo/config/fcrepo.properties | tomcat:tomcat/640

fcrepo.home=FCREPO_HOME\n# External content using path defined above.\nfcrepo.external.content.allowed=/opt/fcrepo/config/allowed_external_hosts.txt\n# Namespace registry using path defined above.\nfcrepo.namespace.registry=/opt/fcrepo/config/i8_namespaces.yml\nfcrepo.auth.principal.header.enabled=true\n# The principal header is the syn-setting.xml \"config\" element's \"header\" attribute\nfcrepo.auth.principal.header.name=X-Islandora\n# false to use manual versioning, true to create a version on each change\nfcrepo.autoversioning.enabled=true\nfcrepo.db.url=FCREPO_DB_URL\nfcrepo.db.user=FCREPO_DB_USERNAME\nfcrepo.db.password=FCREPO_DB_PASSWORD\nfcrepo.ocfl.root=FCREPO_OCFL_ROOT\nfcrepo.ocfl.temp=FCREPO_TEMP_ROOT\nfcrepo.ocfl.staging=FCREPO_STAGING_ROOT\n# Can be sha512 or sha256\nfcrepo.persistence.defaultDigestAlgorithm=sha512\n# Jms moved from 61616 to allow external ActiveMQ to use that port\nfcrepo.dynamic.jms.port=61626\n# Same as above\nfcrepo.dynamic.stomp.port=61623\nfcrepo.velocity.runtime.log=FCREPO_VELOCITY_LOG\nfcrepo.jms.baseUrl=FCREPO_JMS_BASE\n

  • FCREPO_HOME - The home directory for all Fedora generated output and state. Unless otherwise specified, all logs, metadata, binaries, and internally generated indexes, etc. It would default to the Tomcat starting directory. A good default would be /opt/fcrepo
  • FCREPO_DB_URL - This parameter allows you to set the database connection url. In general the format is as follows:

    jdbc:<database_type>://<database_host>:<database_port>/<database_name>

    Fedora currently supports H2, PostgresQL 12.3, MariaDB 10.5.3, and MySQL 8.0

    So using the default ports for the supported databases here are the values we typically use:

    • PostgresQL: jdbc:postgresql://localhost:5432/fcrepo
    • MariaDB: jdbc:mariadb://localhost:3306/fcrepo
    • MySQL: jdbc:mysql://localhost:3306/fcrepo
  • FCREPO_DB_USERNAME - The database username

  • FCREPO_DB_PASSWORD - The database password
  • FCREPO_OCFL_ROOT - Sets the root directory of the OCFL. Defaults to FCREPO_HOME/data/ocfl-root if this line is deleted.
  • FCREPO_TEMP_ROOT - Sets the temp directory used by OCFL. Defaults to FCREPO_HOME/data/temp if this line is deleted.
  • FCREPO_STAGING_ROOT - Sets the staging directory used by OCFL. Defaults to FCREPO_HOME/data/staging if this line is deleted.
  • FCREPO_VELOCITY_LOG - The Fedora HTML template code uses Apache Velocity, which generates a runtime log called velocity.log. Defaults to FCREPO_HOME/logs/velocity. A good choice might be /opt/tomcat/logs/velocity.log
  • FCREPO_JMS_BASE - This specifies the baseUrl to use when generating JMS messages. You can specify the hostname with or without port and with or without path. If your system is behind a NAT firewall you may need this to avoid your message consumers trying to access the system on an invalid port. If this system property is not set, the host, port and context from the user's request will be used in the emitted JMS messages. If your Alpaca is on the same machine as your Fedora and you use the islandora-indexing-fcrepo, you could use http://localhost:8080/fcrepo/rest.

Check the Lyrasis Wiki to find all of Fedora's properties

"},{"location":"installation/manual/installing-fedora-syn-and-blazegraph/#adding-the-fedora-variables-to-java_opts","title":"Adding the Fedora Variables to JAVA_OPTS","text":"

We need our Tomcat JAVA_OPTS to include references to our repository configuration.

/opt/tomcat/bin/setenv.sh

Before:

3 | export JAVA_OPTS=\"-Djava.awt.headless=true -server -Xmx1500m -Xms1000m\"

After:

3 | export JAVA_OPTS=\"-Djava.awt.headless=true -Dfcrepo.config.file=/opt/fcrepo/config/fcrepo.properties -DconnectionTimeout=-1 -server -Xmx1500m -Xms1000m\"

"},{"location":"installation/manual/installing-fedora-syn-and-blazegraph/#ensuring-tomcat-users-are-in-place","title":"Ensuring Tomcat Users Are In Place","text":"

While not strictly necessary, we can use the tomcat-users.xml file to give us direct access to the Fedora endpoint. Fedora defines, out of the box, a fedoraAdmin and fedoraUser role that can be reflected in the users list for access. The following file will also include the base tomcat user. As always, these default passwords should likely not stay as the defaults.

/opt/tomcat/conf/tomcat-users.xml | tomcat:tomcat/600

<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<tomcat-users xmlns=\"http://tomcat.apache.org/xml\"\nxmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\nxsi:schemaLocation=\"http://tomcat.apache.org/xml tomcat-users.xsd\"\nversion=\"1.0\">\n<role rolename=\"tomcat\"/>\n<role rolename=\"fedoraAdmin\"/>\n<role rolename=\"fedoraUser\"/>\n<user username=\"tomcat\" password=\"TOMCAT_PASSWORD\" roles=\"tomcat\"/>\n<user username=\"fedoraAdmin\" password=\"FEDORA_ADMIN_PASSWORD\" roles=\"fedoraAdmin\"/>\n<user username=\"fedoraUser\" password=\"FEDORA_USER_PASSWORD\" roles=\"fedoraUser\"/>\n</tomcat-users>\n

  • TOMCAT_PASSWORD: tomcat
  • FEDORA_ADMIN_PASSWORD: islandora
  • FEDORA_USER_PASSWORD: islandora
"},{"location":"installation/manual/installing-fedora-syn-and-blazegraph/#downloading-and-placing-the-latest-release","title":"Downloading and Placing the Latest Release","text":"

Fedora .war files are packaged up as releases on the official GitHub repository. You should download the most recent stable release.

sudo wget -O fcrepo.war FCREPO_WAR_URL\nsudo mv fcrepo.war /opt/tomcat/webapps\nsudo chown tomcat:tomcat /opt/tomcat/webapps/fcrepo.war\n
  • FCREPO_WAR_URL: This can be found at the fcrepo downloads page; the file you're looking for is:
    • Tagged in green as the 'Latest release'
    • Named \"fcrepo-webapp-VERSION.war\"
"},{"location":"installation/manual/installing-fedora-syn-and-blazegraph/#start-the-tomcat-service","title":"Start the Tomcat Service","text":"

As before, start the Tomcat service to get Fedora up and running.

sudo systemctl start tomcat\n

Note: sometimes it takes a while for Fedora and Tomcat to start up, usually it shouldn't take longer than 5 minutes.

Once it starts up, Fedora REST API should be available at http://localhost:8080/fcrepo/rest. The username is fedoraAdmin and we defined the password before as FEDORA_ADMIN_PASSWORD (default: \"islandora\").

"},{"location":"installation/manual/installing-fedora-syn-and-blazegraph/#syn","title":"Syn","text":""},{"location":"installation/manual/installing-fedora-syn-and-blazegraph/#downloading-the-syn-jar-file","title":"Downloading the Syn JAR File","text":"

A compiled JAR of Syn can be found on the Syn releases page. We\u2019re going to add this to the list libraries accessible to Tomcat.

sudo -u tomcat wget -P /opt/tomcat/lib SYN_JAR_URL\n# Ensure the library has the correct permissions.\nsudo chmod -R 640 /opt/tomcat/lib\n
  • SYN_JAR_URL: The latest stable release of the Syn JAR from the releases page. Specifically, the JAR compiled as -all.jar is required.
"},{"location":"installation/manual/installing-fedora-syn-and-blazegraph/#generating-an-ssl-key-for-syn","title":"Generating an SSL Key for Syn","text":"

For Islandora and Fedora to talk to each other, an SSL key needs to be generated for use with Syn. We\u2019re going to make a spot where such keys can live, and generate one.

sudo mkdir /opt/keys\nsudo openssl genrsa -out \"/opt/keys/syn_private.key\" 2048\nsudo openssl rsa -pubout -in \"/opt/keys/syn_private.key\" -out \"/opt/keys/syn_public.key\"\nsudo chown www-data:www-data /opt/keys/syn*\n
"},{"location":"installation/manual/installing-fedora-syn-and-blazegraph/#placing-the-syn-settings","title":"Placing the Syn Settings","text":"

Syn sites and tokens belong in a settings file that we\u2019re going to reference in Tomcat.

/opt/fcrepo/config/syn-settings.xml | tomcat:tomcat/600

<config version='1' header='X-Islandora'>\n<site algorithm='RS256' encoding='PEM' anonymous='true' default='true' path='/opt/keys/syn_public.key'/>\n<token user='islandora' roles='fedoraAdmin'>ISLANDORA_SYN_TOKEN</token>\n</config>\n

  • ISLANDORA_SYN_TOKEN: islandora
    • This should be a secure generated token rather than this default; it will be configured on the Drupal side later.
"},{"location":"installation/manual/installing-fedora-syn-and-blazegraph/#adding-the-syn-valve-to-tomcat","title":"Adding the Syn Valve to Tomcat","text":"

Referencing the valve we\u2019ve created in our syn-settings.xml involves creating a <Valve> entry in Tomcat\u2019s context.xml:

There are two options here:

"},{"location":"installation/manual/installing-fedora-syn-and-blazegraph/#1-enable-the-syn-valve-for-all-of-tomcat","title":"1. Enable the Syn Valve for all of Tomcat.","text":"

/opt/tomcat/conf/context.xml

Before:

30 | -->

31 | </Context>

After:

30 | -->

31 | <Valve className=\"ca.islandora.syn.valve.SynValve\" pathname=\"/opt/fcrepo/config/syn-settings.xml\"/>

32 | </Context>

"},{"location":"installation/manual/installing-fedora-syn-and-blazegraph/#2-enable-the-syn-valve-for-only-fedora","title":"2. Enable the Syn Valve for only Fedora.","text":"

Create a new file at

/opt/tomcat/conf/Catalina/localhost/fcrepo.xml

<Context>\n<Valve className=\"ca.islandora.syn.valve.SynValve\" pathname=\"/opt/fcrepo/config/syn-settings.xml\"/>\n</Context>\n

Your Fedora web application needs to be deployed in Tomcat with the name fcrepo.war. Otherwise, change the name of the above XML file to match the deployed web application's name.

"},{"location":"installation/manual/installing-fedora-syn-and-blazegraph/#restarting-tomcat","title":"Restarting Tomcat","text":"

Finally, restart tomcat to apply the new configurations.

sudo systemctl restart tomcat\n

Note: sometimes it takes a while for Fedora and Tomcat to start up, usually it shouldn't take longer than 5 minutes.

Note: after installing the Syn valve, you'll no longer be able to manually create/edit or delete objects via Fedora Web UI. All communication with Fedora will now be handled from the Islandora module in Drupal.

"},{"location":"installation/manual/installing-fedora-syn-and-blazegraph/#redhat-logging","title":"Redhat logging","text":"

Redhat systems have stopped generating an all inclusive catalina.out, the catalina.<date>.log does not include web application's log statements. To get Fedora log statements flowing, you can create your own LogBack configuration file and point to it.

/opt/fcrepo/config/fcrepo-logback.xml | tomcat:tomcat/644

<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE configuration>\n<configuration>\n<appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n<encoder>\n<pattern>%p %d{HH:mm:ss.SSS} [%thread] \\(%c{0}\\) %m%n</pattern>\n</encoder>\n</appender>\n<appender name=\"FILE\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n<file>${catalina.base}/logs/fcrepo.log</file>\n<append>true</append>\n<rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n<fileNamePattern>${catalina.base}/logs/fcrepo.%d{yyyy-MM-dd}.log.%i</fileNamePattern>\n<maxFileSize>10MB</maxFileSize>\n<maxHistory>30</maxHistory>\n<totalSizeCap>2GB</totalSizeCap>\n</rollingPolicy>\n<encoder>\n<pattern>%p %d{HH:mm:ss.SSS} [%thread] \\(%c{0}\\) %m%n</pattern>\n</encoder>\n</appender>\n<logger name=\"org.fcrepo.auth\" additivity=\"false\" level=\"${fcrepo.log.auth:-null}\">\n<appender-ref ref=\"STDOUT\"/>\n<appender-ref ref=\"FILE\"/>\n</logger>\n<logger name=\"org.fcrepo.config\" additivity=\"false\" level=\"${fcrepo.log.config:-null}\">\n<appender-ref ref=\"STDOUT\"/>\n<appender-ref ref=\"FILE\"/>\n</logger>\n<logger name=\"org.fcrepo.event\" additivity=\"false\" level=\"${fcrepo.log.event:-null}\">\n<appender-ref ref=\"STDOUT\"/>\n<appender-ref ref=\"FILE\"/>\n</logger>\n<logger name=\"org.fcrepo.http.api\" additivity=\"false\" level=\"${fcrepo.log.http.api:-null}\">\n<appender-ref ref=\"STDOUT\"/>\n<appender-ref ref=\"FILE\"/>\n</logger>\n<logger name=\"org.fcrepo.http.commons\" additivity=\"false\" level=\"${fcrepo.log.http.commons:-null}\">\n<appender-ref ref=\"STDOUT\"/>\n<appender-ref ref=\"FILE\"/>\n</logger>\n<logger name=\"org.fcrepo.jms\" additivity=\"false\" level=\"${fcrepo.log.jms:-null}\">\n<appender-ref ref=\"STDOUT\"/>\n<appender-ref ref=\"FILE\"/>\n</logger>\n<logger name=\"org.fcrepo.kernel\" additivity=\"false\" level=\"${fcrepo.log.kernel:-null}\">\n<appender-ref ref=\"STDOUT\"/>\n<appender-ref ref=\"FILE\"/>\n</logger>\n<logger name=\"org.fcrepo.persistence\" additivity=\"false\" level=\"${fcrepo.log.persistence:-null}\">\n<appender-ref ref=\"STDOUT\"/>\n<appender-ref ref=\"FILE\"/>\n</logger>\n<logger name=\"org.fcrepo.search\" additivity=\"false\" level=\"${fcrepo.log.search:-null}\">\n<appender-ref ref=\"STDOUT\"/>\n<appender-ref ref=\"FILE\"/>\n</logger>\n<logger name=\"org.fcrepo.storage\" additivity=\"false\" level=\"${fcrepo.log.storage:-null}\">\n<appender-ref ref=\"STDOUT\"/>\n<appender-ref ref=\"FILE\"/>\n</logger>\n<logger name=\"org.fcrepo\" additivity=\"false\" level=\"${fcrepo.log:-INFO}\">\n<appender-ref ref=\"STDOUT\"/>\n<appender-ref ref=\"FILE\"/>\n</logger>\n<root level=\"${fcrepo.log.root:-WARN}\">\n<appender-ref ref=\"STDOUT\"/>\n<appender-ref ref=\"FILE\"/>\n</root>\n</configuration>\n

Then alter your $JAVA_OPTS like above to include

-Dlogback.configurationFile=/opt/fcrepo/config/fcrepo-logback.xml\n

This will generate a log file at ${catalina.base}/logs/fcrepo.log and will rotate each day or if the logs reaches 10MB. It will maintain 30 days of old logs, or 2GB whichever comes first.

"},{"location":"installation/manual/installing-fedora-syn-and-blazegraph/#blazegraph-2","title":"Blazegraph 2","text":""},{"location":"installation/manual/installing-fedora-syn-and-blazegraph/#creating-a-working-space-for-blazegraph","title":"Creating a Working Space for Blazegraph","text":"

Blazegraph needs a space for configurations and data; we\u2019re going to create this space in /opt.

sudo mkdir -p /opt/blazegraph/data\nsudo mkdir /opt/blazegraph/conf\nsudo chown -R tomcat:tomcat /opt/blazegraph\n
"},{"location":"installation/manual/installing-fedora-syn-and-blazegraph/#downloading-and-placing-the-blazegraph-war","title":"Downloading and Placing the Blazegraph WAR","text":"

The Blazegraph .war file can be found in a few different places, but to ensure we\u2019re able to easily wget it, we\u2019re going to use the maven.org repository link to grab it.

cd /opt\nsudo wget -O blazegraph.war BLAZEGRAPH_WARFILE_LINK\nsudo mv blazegraph.war /opt/tomcat/webapps\nsudo chown tomcat:tomcat /opt/tomcat/webapps/blazegraph.war\n
  • BLAZEGRAPH_WAR_URL: You can find a link to this at the Maven repository for Blazegraph; you\u2019ll want to click the link for the latest version of Blazegraph 2.1.x, then get the link to the .war file within that version folder.

Once this is downloaded, give it a moment to expand before moving on to the next step.

"},{"location":"installation/manual/installing-fedora-syn-and-blazegraph/#configuring-logging","title":"Configuring Logging","text":"

We would like to have an appropriate logging configuration for Blazegraph, which can be useful for looking at incoming traffic and determining if anything has gone wrong with Blazegraph. Our logger isn\u2019t going to be much different than the default logger; it can be made more or less verbose by changing the default WARN levels. There are several other loggers that can be enabled, like a SPARQL query trace or summary query evaluation log; if these are desired they should be added in. Consult the Blazegraph documentation for more details.

/opt/blazegraph/conf/log4j.properties | tomcat:tomcat/644

log4j.rootCategory=WARN, dest1\n\n# Loggers.\nlog4j.logger.com.bigdata=WARN\nlog4j.logger.com.bigdata.btree=WARN\n\n# Normal data loader (single threaded).\n#log4j.logger.com.bigdata.rdf.store.DataLoader=INFO\n\n# dest1\nlog4j.appender.dest1=org.apache.log4j.ConsoleAppender\nlog4j.appender.dest1.layout=org.apache.log4j.PatternLayout\nlog4j.appender.dest1.layout.ConversionPattern=%-5p: %F:%L: %m%n\n#log4j.appender.dest1.layout.ConversionPattern=%-5p: %r %l: %m%n\n#log4j.appender.dest1.layout.ConversionPattern=%-5p: %m%n\n#log4j.appender.dest1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n\n#log4j.appender.dest1.layout.ConversionPattern=%-4r(%d) [%t] %-5p %c(%l:%M) %x - %m%n\n\n# Rule execution log. This is a formatted log file (comma delimited).\nlog4j.logger.com.bigdata.relation.rule.eval.RuleLog=INFO,ruleLog\nlog4j.additivity.com.bigdata.relation.rule.eval.RuleLog=false\nlog4j.appender.ruleLog=org.apache.log4j.FileAppender\nlog4j.appender.ruleLog.Threshold=ALL\nlog4j.appender.ruleLog.File=/var/log/blazegraph/rules.log\nlog4j.appender.ruleLog.Append=true\nlog4j.appender.ruleLog.BufferedIO=false\nlog4j.appender.ruleLog.layout=org.apache.log4j.PatternLayout\nlog4j.appender.ruleLog.layout.ConversionPattern=%m\n

"},{"location":"installation/manual/installing-fedora-syn-and-blazegraph/#adding-a-blazegraph-configuration","title":"Adding a Blazegraph Configuration","text":"

Our configuration will be built from a few different files that we will eventually reference in JAVA_OPTS and directly apply to Blazegraph; these include most of the functional pieces Blazegraph requires, as well as a generalized configuration for the islandora namespace it will use. As with most large configurations like this, these should likely be tuned to your preferences, and the following files only represent sensible defaults.

/opt/blazegraph/conf/RWStore.properties | tomcat:tomcat/644

com.bigdata.journal.AbstractJournal.file=/opt/blazegraph/data/blazegraph.jnl\ncom.bigdata.journal.AbstractJournal.bufferMode=DiskRW\ncom.bigdata.service.AbstractTransactionService.minReleaseAge=1\ncom.bigdata.journal.Journal.groupCommit=false\ncom.bigdata.btree.writeRetentionQueue.capacity=4000\ncom.bigdata.btree.BTree.branchingFactor=128\ncom.bigdata.journal.AbstractJournal.initialExtent=209715200\ncom.bigdata.journal.AbstractJournal.maximumExtent=209715200\ncom.bigdata.rdf.sail.truthMaintenance=false\ncom.bigdata.rdf.store.AbstractTripleStore.quads=true\ncom.bigdata.rdf.store.AbstractTripleStore.statementIdentifiers=false\ncom.bigdata.rdf.store.AbstractTripleStore.textIndex=false\ncom.bigdata.rdf.store.AbstractTripleStore.axiomsClass=com.bigdata.rdf.axioms.NoAxioms\ncom.bigdata.namespace.kb.lex.com.bigdata.btree.BTree.branchingFactor=400\ncom.bigdata.namespace.kb.spo.com.bigdata.btree.BTree.branchingFactor=1024\ncom.bigdata.journal.Journal.collectPlatformStatistics=false\n

/opt/blazegraph/conf/blazegraph.properties | tomcat:tomcat/644

com.bigdata.rdf.store.AbstractTripleStore.textIndex=false\ncom.bigdata.rdf.store.AbstractTripleStore.axiomsClass=com.bigdata.rdf.axioms.OwlAxioms\ncom.bigdata.rdf.sail.isolatableIndices=false\ncom.bigdata.rdf.store.AbstractTripleStore.justify=true\ncom.bigdata.rdf.sail.truthMaintenance=true\ncom.bigdata.rdf.sail.namespace=islandora\ncom.bigdata.rdf.store.AbstractTripleStore.quads=false\ncom.bigdata.namespace.islandora.lex.com.bigdata.btree.BTree.branchingFactor=400\ncom.bigdata.journal.Journal.groupCommit=false\ncom.bigdata.namespace.islandora.spo.com.bigdata.btree.BTree.branchingFactor=1024\ncom.bigdata.rdf.store.AbstractTripleStore.geoSpatial=false\ncom.bigdata.rdf.store.AbstractTripleStore.statementIdentifiers=false\n

/opt/blazegraph/conf/inference.nt | tomcat:tomcat/644

<http://pcdm.org/models#memberOf> <http://www.w3.org/2002/07/owl#inverseOf> <http://pcdm.org/models#hasMember> .\n<http://pcdm.org/models#fileOf> <http://www.w3.org/2002/07/owl#inverseOf> <http://pcdm.org/models#hasFile> .\n

"},{"location":"installation/manual/installing-fedora-syn-and-blazegraph/#specifying-the-rwstoreproperties-in-java_opts","title":"Specifying the RWStore.properties in JAVA_OPTS","text":"

In order to enable our configuration when Tomcat starts, we need to add the location of RWStore.properties to the existing JAVA_OPTS environment variable that Tomcat uses in /opt/tomcat/bin/setenv.sh: -Dcom.bigdata.rdf.sail.webapp.ConfigParams.propertyFile=/opt/blazegraph/conf/RWStore.properties -Dlog4j.configuration=file:/opt/blazegraph/conf/log4j.properties

"},{"location":"installation/manual/installing-fedora-syn-and-blazegraph/#restarting-tomcat_1","title":"Restarting Tomcat","text":"

Finally, restart Tomcat to pick up the changes we\u2019ve made.

sudo systemctl restart tomcat\n
"},{"location":"installation/manual/installing-fedora-syn-and-blazegraph/#installing-blazegraph-namespaces-and-inference","title":"Installing Blazegraph Namespaces and Inference","text":"

The two other files we created, blazegraph.properties and inference.nt, contain information that Blazegraph requires in order to establish and correctly use the datasets Islandora will send to it. First, we need to create a dataset - contained in blazegraph.properties - and then we need to inform that dataset of the inference set we have contained in inference.nt.

curl -X POST -H \"Content-Type: text/plain\" --data-binary @/opt/blazegraph/conf/blazegraph.properties http://localhost:8080/blazegraph/namespace\n
If this worked correctly, Blazegraph should respond with \"CREATED: islandora\" to let us know it created the islandora namespace.
curl -X POST -H \"Content-Type: text/plain\" --data-binary @/opt/blazegraph/conf/inference.nt http://localhost:8080/blazegraph/namespace/islandora/sparql\n
If this worked correctly, Blazegraph should respond with some XML letting us know it added the 2 entries from inference.nt to the namespace.

"},{"location":"installation/manual/installing-solr/","title":"Installing Solr","text":"

Needs Maintenance

The manual installation documentation is in need of attention. We are aware that some components no longer work as documented here. If you are interested in helping us improve the documentation, please see Contributing.

"},{"location":"installation/manual/installing-solr/#in-this-section-we-will-install","title":"In this section, we will install:","text":"
  • Apache Solr 8, the search engine used to index and find Drupal content
  • search_api_solr, the Solr implementation of Drupal's search API
"},{"location":"installation/manual/installing-solr/#solr-9","title":"Solr 9","text":""},{"location":"installation/manual/installing-solr/#downloading-and-placing-solr","title":"Downloading and Placing Solr","text":"

The Solr binaries can be found at the Solr downloads page; the most recent stable release of Solr 9 should be used.

# While generally we download tarballs as .tar.gz files without version\n# information, the Solr installer is a bit particular in that it expects a .tgz\n# file with the same name as the extracted folder it contains. It's odd, and we\n# can't really get around it.\ncd /opt\nsudo wget -O SOLR_TARBALL SOLR_DOWNLOAD_LINK\nsudo tar -xzvf SOLR_TARBALL\n
- SOLR_DOWNLOAD_LINK: NOTICE: This will depend on a few different things, not least of all the current version of Solr. The link to the .tgz for the binary on the downloads page will take you to a list of mirrors that Solr can be downloaded from, and provide you with a preferred mirror at the top. This preferred mirror should be used as the SOLR_DOWNLOAD_LINK. - SOLR_TARBALL: The filename that was downloaded, e.g., solr-9.6.1.tgz

"},{"location":"installation/manual/installing-solr/#running-the-solr-installer","title":"Running the Solr Installer","text":"

Solr includes an installer that does most of the heavy lifting of ensuring we have a Solr user, a location where Solr lives, and configurations in place to ensure it\u2019s running on boot.

sudo UNTARRED_SOLR_FOLDER/bin/install_solr_service.sh SOLR_TARBALL\n
- UNTARRED_SOLR_FOLDER: This will likely simply be solr-VERSION, where VERSION is the version number that was downloaded.

The installer will start the service for you. Check the status and stop and restart if needed:

sudo systemctl status solr\nsudo systemctl stop solr\nsudo systemctl start solr\n

If you want to use the web dashboard (for development only) you can edit the solr.in.sh file to make it more accessible.

Find #SOLR_JETTY_HOST=\"127.0.0.1\" and change it to SOLR_JETTY_HOST=\"0.0.0.0\". (Note the lack of # now.)

Restart Solr sudo systemctl restart solr and go to http://localhost:8983/solr.

"},{"location":"installation/manual/installing-solr/#increasing-the-open-file-limit-optional","title":"Increasing the Open File Limit (Optional)","text":"

Solr's installation guide recommends that you increase the open file limit so that operations aren't disrupted while Solr is trying to access things in its index. This limit can be increased while the system is running, but doing so won't persist after a reboot. You can hard-increase this limit using your system's sysctl file:

/etc/sysctl.conf

Add the following line to the end of the file:

fs.file-max = 65535\n

Then apply your new configuration.

sudo sysctl -p\n
"},{"location":"installation/manual/installing-solr/#installing-search_api_solr","title":"Installing search_api_solr","text":"

Rather than use an out-of-the-box configuration that won\u2019t be suitable for our purposes, we\u2019re going to use the Drupal search_api_solr module to generate one for us. This module was already installed if you used the starter site, but you can install it if you didn't:

cd /var/www/html/drupal\nsudo -u www-data composer require drupal/search_api_solr:^4.2\ndrush -y en search_api_solr\n

You should see an output similar to this:

The following module(s) will be enabled: search_api_solr, language, search_api\n\n // Do you want to continue?: yes.\n\n [success] Successfully enabled: search_api_solr, language, search_api\n

"},{"location":"installation/manual/installing-solr/#configuring-search_api_solr","title":"Configuring search_api_solr","text":"

Before we can create configurations to use with Solr, the core we created earlier needs to be referenced in Drupal. Again, the starter site provides this already; but if you installed it yourself, the directions below should help.

Log in to the Drupal site at /user using the sitewide administrator username and password (if using defaults from previous chapters this should be islandora and islandora), then navigate to /admin/config/search/search-api/add-server.

Fill out the server addition form using the following options:

  • SERVER_NAME: islandora
    • This is completely arbitrary, and is simply used to differentiate this search server configuration from all others. Write down or otherwise pay attention to the machine_name generated next to the server name you type in; this will be used in the next step.

As a recap for this configuration:

  • Server name should be an arbitrary identifier for this server
  • Enabled should be checked
  • Backend should be set to Solr
  • Under CONFIGURE SOLR BACKEND, Solr Connector should be set to Standard
  • Under CONFIGURE STANDARD SOLR CONNECTOR:
    • HTTP protocol is simply set to http since we've set this up on the same machine Drupal lives on. On a production installation, Solr should likely be installed behind an HTTPS connection.
    • Solr host can be set to localhost since, again, this is set up on the same machine Drupal lives on. On a production installation, this may vary, especially if parts of the installation live on different severs
    • Solr port should be set to the port Solr was installed on, which is 8983 by default
    • Solr path should be set to the configured path to the instance of Solr; in a default installation, there is only one Solr instance, and it lives at /
    • Solr core should be the name of the Solr core you created earlier, which is why it's listed as SOLR_CORE here
  • Under ADVANCED SERVER CONFIGURATION, solr.install.dir should be set to the path where we installed Solr, which this guide has established at /opt/solr

Click Save to create the server configuration.

NOTICE You can ignore the error about an incompatible Solr schema; we're going to set this up in the next step. In fact, if you refresh the page after restarting Solr in the next step, you should see the error disappear.

"},{"location":"installation/manual/installing-solr/#generating-and-applying-solr-configurations","title":"Generating and Applying Solr Configurations","text":"

Now that our core is in place and our Drupal-side configurations exist, we\u2019re ready to generate Solr configuration files to connect this site to our search engine.

cd /var/www/html/drupal\ndrush solr-gsc SERVER_MACHINE_NAME solrconfig.zip\nunzip -d ~/solrconfig solrconfig.zip\nsudo -u solr /opt/solr/bin/solr create_core -c SOLR_CORE -d ~/solrconfig -n SOLR_CORE\nsudo systemctl restart solr\n
- SERVER_MACHINE_NAME: This should be the machine_name that was automatically generated when creating the configuration in the above step. The starter site uses default_solr_server.

"},{"location":"installation/manual/installing-solr/#adding-an-index","title":"Adding an Index","text":"

The site template provides an index for us; but if you didn't use it, you need to set up your index configuration. Navigate to /admin/config/search/search-api/add-index and check off the things you'd like to be indexed.

NOTICE You should come back here later and reconfigure this after completing the last step in this guide. The default indexing configuration is pretty permissive, and you may want to restrict, for example, indexed content to just Islandora-centric bundles. This guide doesn't set up the index's fields either, which are going to be almost wholly dependent on the needs of your installation. Once you complete that configuration later on, re-index Solr from the configuration page of the index we're creating here.

Click Save to add your index and kick off indexing of existing items.

"},{"location":"installation/manual/installing-tomcat-and-cantaloupe/","title":"Installing Tomcat and Cantaloupe","text":"

Needs Maintenance

The manual installation documentation is in need of attention. We are aware that some components no longer work as documented here. If you are interested in helping us improve the documentation, please see Contributing.

"},{"location":"installation/manual/installing-tomcat-and-cantaloupe/#in-this-section-we-will-install","title":"In this section, we will install:","text":"
  • Tomcat 9, the Java servlet container that will serve up some Java applications on various endpoints, including, importantly, Fedora
  • Cantaloupe 5, the image tileserver - running in Tomcat - that will be used to serve up large images in a web-accessible fashion
"},{"location":"installation/manual/installing-tomcat-and-cantaloupe/#tomcat-9","title":"Tomcat 9","text":""},{"location":"installation/manual/installing-tomcat-and-cantaloupe/#creating-a-tomcat-user","title":"Creating a tomcat User","text":"

Apache Tomcat, and all its processes, will be owned and managed by a specific user for the purposes of keeping parts of the stack segregated and accountable.

sudo addgroup tomcat\nsudo adduser tomcat --ingroup tomcat --home /opt/tomcat --shell /usr/bin\n

You will be prompted to create a password for the tomcat user; all the other information as part of the adduser command can be ignored.

"},{"location":"installation/manual/installing-tomcat-and-cantaloupe/#downloading-and-placing-tomcat-9","title":"Downloading and Placing Tomcat 9","text":"

Tomcat 9 itself can be installed in several different ways; while it\u2019s possible to install via apt-get, this doesn\u2019t give us a great deal of control over exactly how we\u2019re going to run and manage it; as a critical part of the stack, it is beneficial for our purposes to have a good frame of reference for the inner workings of Tomcat.

We\u2019re going to download the latest version of Tomcat to /opt and set it up so that it runs automatically. Bear in mind that with the following commands, this is going to be entirely relative to the current version of Tomcat 9, which we\u2019ll try to mitigate as we go.

cd /opt\nsudo wget -O tomcat.tar.gz TOMCAT_TARBALL_LINK\nsudo tar -zxvf tomcat.tar.gz\nsudo mv /opt/TOMCAT_DIRECTORY/* /opt/tomcat\nsudo chown -R tomcat:tomcat /opt/tomcat\n
- TOMCAT_TARBALL_LINK: No default can be provided here; you should navigate to the Tomcat 9 downloads page and grab the link to the latest .tar.gz file under the \u201cCore\u201d section of \u201cBinary Distributions\u201d. It is highly recommended to grab the latest version of Tomcat 9, as it will come with associated security patches and fixes. - TOMCAT_DIRECTORY: This will also depend entirely on the exact version of tomcat downloaded - for example, apache-tomcat-9.0.50. Again, ls /opt can be used to find this.

"},{"location":"installation/manual/installing-tomcat-and-cantaloupe/#creating-a-setenvsh-script","title":"Creating a setenv.sh Script","text":"

When Tomcat runs, some configuration needs to be pre-established as a series of environment variables that will be used by the script that runs it.

/opt/tomcat/bin/setenv.sh | tomcat:tomcat/755

export CATALINA_HOME=\"/opt/tomcat\"\nexport JAVA_HOME=\"PATH_TO_JAVA_HOME\"\nexport JAVA_OPTS=\"-Djava.awt.headless=true -server -Xmx1500m -Xms1000m\"\n
- PATH_TO_JAVA_HOME: This will vary a bit depending on the environment, but will likely live in /usr/lib/jvm somewhere (e.g., /usr/lib/jvm/java-11-openjdk-amd64); again, in an Ubunutu environment you can check a part of this using update-alternatives --list java, which will give you the path to the JRE binary within the Java home. Note that update-alternatives --list java will give you the path to the binary, so for PATH_TO_JAVA_HOME delete the /bin/java at the end to get the Java home directory, so it should look something like this:
export JAVA_HOME=\"/usr/lib/jvm/java-11-openjdk-amd64\"\n

"},{"location":"installation/manual/installing-tomcat-and-cantaloupe/#creating-the-tomcat-service","title":"Creating the Tomcat Service","text":"

Tomcat includes two shell scripts we\u2019re going to make use of - startup.sh and shutdown.sh - which are light wrappers on top of a third script, catalina.sh, which manages spinning up and shutting down the Tomcat server.

Debian and Ubuntu use systemctl to manage services; we\u2019re going to create a .service file that can run these shell scripts.

/etc/systemd/system/tomcat.service | root:root/755

[Unit]\nDescription=Tomcat\n\n[Service]\nType=forking\nExecStart=/opt/tomcat/bin/startup.sh\nExecStop=/opt/tomcat/bin/shutdown.sh\nSyslogIdentifier=tomcat\n\n[Install]\nWantedBy=multi-user.target\n

"},{"location":"installation/manual/installing-tomcat-and-cantaloupe/#enabling-and-starting-tomcat","title":"Enabling and Starting Tomcat","text":"

We\u2019re going to both enable and start Tomcat. Enabling Tomcat will ensure that it starts on boot, the timing of which is defined by the [Install] section\u2019s WantedBy statement, which specifies what it should start after. This is separate from starting it, which we need to do now in order to get Tomcat up and running without requiring a reboot.

sudo systemctl enable tomcat\nsudo systemctl start tomcat\n

We can check that Tomcat has started by running sudo systemctl status tomcat | grep Active; we should see that Tomcat is active (running), which is the correct result of startup.sh finishing its run successfully.

"},{"location":"installation/manual/installing-tomcat-and-cantaloupe/#installing-cantaloupe-5","title":"Installing Cantaloupe 5","text":"

Since version 5, Cantaloupe is released as a standalone Java application and is no longer deployed in Tomcat via a .war file. Even so, we can still fine-tune how it runs and even install it as a service.

"},{"location":"installation/manual/installing-tomcat-and-cantaloupe/#downloading-cantaloupe","title":"Downloading Cantaloupe","text":"

Releases of Cantaloupe live on the Cantaloupe release page; the latest version can be found here as a .zip file.

cd /opt/\nsudo wget -O cantaloupe.zip CANTALOUPE_RELEASE_URL\nsudo unzip cantaloupe.zip\nsudo mv [CANTALOUPE_VERSION] cantaloupe\nsudo rm cantaloupe.zip\n
- CANTALOUPE_RELEASE_URL: It\u2019s recommended we grab the latest version of Cantaloupe 5. This can be found on the above-linked release page, as the .zip version; for example, https://github.com/cantaloupe-project/cantaloupe/releases/download/v5.0.6/cantaloupe-5.0.6.zip - make sure not to download the source code zip file as that isn't compiled for running out-of-the-box. - CANTALOUPE_VERSION: This will depend on the exact version of Cantaloupe downloaded; in the above example release, this would be cantaloupe-5.0.6

"},{"location":"installation/manual/installing-tomcat-and-cantaloupe/#creating-a-cantaloupe-configuration","title":"Creating a Cantaloupe Configuration","text":"

Cantaloupe pulls its configuration from a file called cantaloupe.properties; there are also some other files that can contain instructions for Cantaloupe while it\u2019s running; specifically, we\u2019re going to copy over the delegates.rb file, which can also contain custom configuration. We won\u2019t make use of this file; we\u2019re just copying it over for demonstration purposes.

Creating these files from scratch is not recommended; rather, we\u2019re going to take the default cantaloupe configurations and plop them into their own folder so we can work with them.

cd cantaloupe\nsudo cp cantaloupe.properties.sample cantaloupe.properties\nsudo cp delegates.rb.sample delegates.rb\n

Most of the out-of-the-box configuration will work fine for our purposes. We will change the source lookup and logging, but it\u2019s highly recommended that you take a look through the rest of the cantaloupe.properties and see what changes can be made. Review the config block below and change the related portions of yours to match.

/opt/cantaloupe/cantaloupe.properties

##############\n# SOURCES\n##############\n\nsource.static = HttpSource\n\n##############\n# HttpSource\n##############\n\nHttpSource.BasicLookupStrategy.url_prefix =\n\n##############\n# LOGGING\n##############\n\nlog.application.FileAppender.pathname = /var/log/islandora/cantaloupe-application.log\n\nlog.application.RollingFileAppender.enabled = true\nlog.application.RollingFileAppender.pathname = /var/log/islandora/cantaloupe-application.log\nlog.application.RollingFileAppender.TimeBasedRollingPolicy.filename_pattern = /var/log/islandora/cantaloupe-application-%d{yyyy-MM-dd}.log\n\nlog.error.FileAppender.pathname = /var/log/islandora/cantaloupe-error.log\n\nlog.error.RollingFileAppender.enabled = true\nlog.error.RollingFileAppender.pathname = /var/log/islandora/cantaloupe-error.log\nlog.error.RollingFileAppender.TimeBasedRollingPolicy.filename_pattern = /var/log/islandora/cantaloupe-error-%d{yyyy-MM-dd}.log\n\nlog.access.FileAppender.pathname = /var/log/islandora/cantaloupe-access.log\n\nlog.access.RollingFileAppender.enabled = true\nlog.access.RollingFileAppender.pathname = /var/log/islandora/cantaloupe-access.log\nlog.access.RollingFileAppender.TimeBasedRollingPolicy.filename_pattern = /var/log/islandora/cantaloupe-access-%d{yyyy-MM-dd}.log\n

"},{"location":"installation/manual/installing-tomcat-and-cantaloupe/#installing-and-configuring-cantaloupe-as-a-service","title":"Installing and configuring Cantaloupe as a service","text":"

Since it is a standalone application, we can configure Cantaloupe as a systemd service like we did with Tomcat, so it can start on boot:

/etc/systemd/system/cantaloupe.service | root:root/755

[Unit]\nDescription=Cantaloupe\n\n[Service]\nExecStart=java -cp /opt/cantaloupe/cantaloupe-CANTALOUPE_VER.jar -Dcantaloupe.config=/opt/cantaloupe/cantaloupe.properties -Xmx1500m -Xms1000m edu.illinois.library.cantaloupe.StandaloneEntry\nSyslogIdentifier=cantaloupe\n\n[Install]\nWantedBy=multi-user.target\n
- CANTALOUPE_VER: This will depend on the exact version of Cantaloupe downloaded; in the above example release, this would be cantaloupe-5.0.3

We can now enable the service and run it:

sudo systemctl enable cantaloupe\nsudo systemctl start cantaloupe\n

We can check the service status with sudo systemctl status cantaloupe | grep Active and the splash screen of Cantaloupe should be available at http://localhost:8182/iiif/2.

If you have trouble connecting, check the status of your port and allow it if necessary:

sudo ufw status verbose\nsudo ufw allow 8182/tcp\n
"},{"location":"installation/manual/introduction/","title":"Introduction","text":"

Needs Maintenance

The manual installation documentation is in need of attention. We are aware that some components no longer work as documented here. If you are interested in helping us improve the documentation, please see Contributing.

Notice

The manual installation guide is not intended to describe the Islandora installation but rather an Islandora installation. The server created using this guide is not hardened, will not be easily scalable, and the components may not be configured in a way you consider easy to work with. A production instance of Islandora should be installed and maintained by a professional with an understanding of Linux and server administration.

This guide will contain generalized steps on installation and configuration of the various components, but will contain specific example commands for executing these steps on an Ubuntu 20.04 Server.

"},{"location":"installation/manual/introduction/#some-prerequisite-knowledge","title":"Some Prerequisite Knowledge","text":"

This guide assumes the user has some knowledge:

  • A general idea of how to work on the command-line of a Linux server using Bash. Commands are described in detail, but servers are volatile, and knowledge is still assumed in case anything happens outside of your expectations.
  • An understanding of how to modify files from the command line. Configurations will often need to be created or modified in order to get things up and running. This might involve using an application like nano or vi/vim, or creating these files locally and uploading them to the server. It should also generally be assumed that most of these configuration files will have to be created or edited using sudo, and that permissions and ownership may need to be specified on these files.
"},{"location":"installation/manual/introduction/#conventions-used-in-this-guide","title":"Conventions Used in This Guide","text":""},{"location":"installation/manual/introduction/#chronological-organization","title":"Chronological Organization","text":"

The steps in this guide are listed in chronological order of installation and configuration. Some sections will reference variables and rely on components installed in previous sections. It does not account for skipping over or otherwise changing the order of installation steps; bear this in mind if you decide to do things out of the provided order.

"},{"location":"installation/manual/introduction/#replacement-variables","title":"Replacement Variables","text":"

It is expected that the person setting up the site may want to use different usernames, passwords, and other such variables than the ones presented by default in this guide. Additionally, some defaults can't be provided, such as up-to-date version information for externally-provided components. In such cases, the replacement variables will be placed in all capital letters, and a description of the variables, any possible defaults, and how to get up-to-date information, will be listed below.

"},{"location":"installation/manual/introduction/#bash-commands","title":"Bash Commands","text":"

Notice

Command blocks are always assumed to start at the home folder of the user originally created during the server installation. They are never run as root; if root access is required, sudo will be specified, and if files are created belonging to root that should not belong to root, chmod and chown will be run against them immediately afterwards to ensure correct permissions. If commands need to be run from a different working directory, an absolute path will be specified to use with cd. If you're concerned about whether or not a code block can be run from your current working directory, run cd ~ before executing any commands in it.

Commands to be run on the command line will be placed in code blocks, with one command per line, and any replacement variables below, e.g.,

sudo run --this-command\npython3 run.py /this/other/command --with-param PARAMETER\n
- PARAMETER: some_sensible_default, perhaps with an explanation of why, or how to determine alternatives

"},{"location":"installation/manual/introduction/#editing-files-in-place","title":"Editing Files In Place","text":"

When an individual file needs to be modified in place (as opposed to replacing it outright), a Before and After quote will be provided that identifies one or more lines in the file, what the default installed version of that file looks like on that line, and what the line should look like after it has been modified, like so:

/path/to/file_being_modified

Before:

174 | Here is what line 174 in the file looked like before

175 | And here is what the following line looked like in the file before

After:

174 | Here is what line 174 should look like after modification

175 | And here is what the following line should look like after modification: VARIABLE

  • VARIABLE: some_value, perhaps with an explanation of why

It should be noted that configuration files and the like are subject to change by the organizations that maintain their respective applications; this guide generally recommends installing the latest version of these applications, as these generally include security updates. It is expected that the implementer will be able to search through a file and find specific lines in the case where the maintaining organization has moved it in a subsequent patch. In most cases, configuration files will be provided outright to avoid these scenarios.

"},{"location":"installation/manual/introduction/#adding-or-replacing-files","title":"Adding or Replacing Files","text":"

When a file needs to be added or replaced, it will be described in three sections:

  • A line that describes the path to the file, as well as the owner, group, and umask for the file; it is assumed that the person following the guide will use chmod and chown appropriately to apply the owner, group, and umask
  • The entire contents of the file in a code block, including any portions that need to be replaced with specific values
  • Those replacement values

/the/path/to/some/file.php | owner:group/umask

<?php\n/**\n * Here is the entire contents of a PHP file\n */\nfunction do_something($to_this) {\n$to_this += THE_NUMBER_TO_ADD_TO_THIS;\n}\n?>\n
- THE_NUMBER_TO_ADD_TO_THIS: 12, perhaps with an explanation of why, or other numbers that may be appropriate

"},{"location":"installation/manual/introduction/#account-tracker","title":"Account Tracker","text":"

We will create several accounts during the installation process. For some items the instructions use placeholders in square brackets ([]). Create your own and use them in place of these placeholders as appropriate.

  • MySQL root account
  • username: root
  • password: [mysql_root_password]
  • MySQL account for Drupal database access
  • username: [mysql_drupal]
  • password: [mysql_drupal_password]
  • MySQL account for Fedora access
  • username: [mysql_fedora]
  • password: [mysql_fedora_password]
  • Tomcat
  • username: tomcat
  • password: [tomcat_user_password]
  • Fedora fedoraAdmin account
  • username: fedoraAdmin
  • password: [fedora_admin_password]
  • Fedora fedoraUser account
  • username: fedoraUser
  • password: [fedora_user_password]
  • ActiveMQ
  • username: [activemq_user]
  • password: [activemq_user_password]
"},{"location":"installation/manual/introduction/#troubleshooting","title":"Troubleshooting","text":"

The most common issues you will likely run into when manually provisioning a server are:

  • Files or directories are not owned by the user who needs access to them, and can therefore not be written to. Check the ownership of files using ls -la, and ensure their ownership using chown USER for files, and chown -R USER for directories
  • Replacement variables were left in place in files specified by the guide. Ensure any replacement variables such as server addresses and passwords are swapped out when writing files to the server

For any other issues, don't hesitate to email the mailing list to ask for help. If you think that a part of the installation documentation is incorrect or could be improved, please create an issue in the documentation issues queue and give it a documentation tag. Bear in mind that this guide is built for Ubuntu 20.04 and attempts to give generalized instructions; you will likely naturally encounter situations where your own environment needs to differ from the guide.

"},{"location":"installation/manual/preparing-a-webserver/","title":"Preparing a LAPP Server","text":"

Needs Maintenance

The manual installation documentation is in need of attention. We are aware that some components no longer work as documented here. If you are interested in helping us improve the documentation, please see Contributing.

"},{"location":"installation/manual/preparing-a-webserver/#in-this-section-we-will-install","title":"In this section, we will install:","text":"
  • Java/OpenJDK is the Java runtime environment used by multiple components: Solr, Cantaloupe, Alpaca, Fedora, and Blazegraph.
  • Apache 2, the webserver that will deliver webpages to end users
  • PHP 8, the runtime code interpreter that Drupal will use to generate webpages and other services via apache, as well as that Drush and Composer will use to run tasks from the command line
  • Several modules for PHP 8 which are required to run the PHP code that Drupal and other applications will be executing
  • MySQL, the database that Drupal will use for storage (as well as other applications down the line)
"},{"location":"installation/manual/preparing-a-webserver/#installing-openjdk-11","title":"Installing OpenJDK 11","text":"

Tomcat runs in a Java runtime environment, so we'll need one to continue. In our case, OpenJDK 11 is open-source, free to use, and can fairly simply be installed using apt-get:

sudo apt-get -y install openjdk-11-jdk openjdk-11-jre\n

The installation of OpenJDK via apt-get establishes it as the de-facto Java runtime environment to be used on the system, so no further configuration is required.

The resultant location of the java JRE binary (and therefore, the correct value of JAVA_HOME when it\u2019s referenced) will vary based on the specifics of the machine it\u2019s being installed on; that being said, you can find its exact location using update-alternatives:

update-alternatives --list java\n
Take a note of this path as we will need it later.

"},{"location":"installation/manual/preparing-a-webserver/#apache-2","title":"Apache 2","text":""},{"location":"installation/manual/preparing-a-webserver/#install-apache-2","title":"Install Apache 2","text":"

Apache can typically be installed and configured outright by your operating system\u2019s package manager:

sudo apt-get -y install apache2 apache2-utils\n

This will install:

  • A systemd service that will ensure Apache can be stopped and started, and will run when the machine is powered on
  • A set of Apache configurations in /etc/apache2, including the basic configuration, ports configuration, enabled mods, and enabled sites
  • An Apache webroot in /var/www/html, configured to be the provided server on port :80 in /etc/apache2/sites-enabled/000-default.conf; we\u2019ll make changes and additions to this file later
  • A user and group, www-data, which we will use to read/write web documents.
"},{"location":"installation/manual/preparing-a-webserver/#enable-apache-mods","title":"Enable Apache Mods","text":"

We\u2019re going to enable a couple of Apache mods that Drupal highly recommends installing, and which are de-facto considered required by Islandora:

sudo a2enmod ssl\nsudo a2enmod rewrite\nsudo systemctl restart apache2\n
"},{"location":"installation/manual/preparing-a-webserver/#add-the-current-user-to-the-www-data-group","title":"Add the Current User to the www-data Group","text":"

Since the user we are currently logged in as is going to work quite a bit inside the Drupal directory, we want to give it group permissions to anything the www-data group has access to. When we run composer, www-data will also be caching data in our own home directory, so we want this group modification to go in both directions.

N.B. This code block uses backticks, not single quotes; this is an important distinction as backticks have special meaning in bash.

Note If doing this in the terminal, replace \"whoami\" with your username and remove the backticks

sudo usermod -a -G www-data `whoami`\nsudo usermod -a -G `whoami` www-data\n# Immediately log back in to apply the new group.\nsudo su `whoami`\n
"},{"location":"installation/manual/preparing-a-webserver/#php-8x","title":"PHP 8.x","text":"

Installing Alternate Versions

Although the instructions below will install PHP 8.3, the instructions should work for future versions by replacing the 8.3 with whatever version you are attempting to install.

"},{"location":"installation/manual/preparing-a-webserver/#install-php-8x","title":"Install PHP 8.x","text":"

If you're running Ununtu 20.04+ you should be able to install PHP 8 from the apt packages directly, although the ondrej/php repository provides additional libraries:

sudo apt update\nsudo add-apt-repository ppa:ondrej/php\nsudo apt update\nsudo apt install php8.3 libapache2-mod-php unzip\nsudo apt install php8.3-{cli,common,curl,gd,imap,intl,mysql,opcache,redis,xdebug,xml,yaml,zip}\n

Restart Apache to make the changes active:

sudo systemctl restart apache2\n

Installation directories created:

  • /etc/php/8.3 (this is where you can edit PHP settings, such as timeouts, as needed for your site)
  • /usr/bin/php8.3
"},{"location":"installation/manual/preparing-a-webserver/#mysql","title":"MySQL","text":""},{"location":"installation/manual/preparing-a-webserver/#install","title":"Install","text":"
sudo apt install mysql-server\n

There are a few ways to check the MySQL status:

sudo service mysql status  # press \"q\" to exit\nsudo ss -tap | grep mysql\nsudo service mysql restart\nsudo journalctl -u mysql   # helps troubleshooting\n
"},{"location":"models/audio/","title":"Audio","text":""},{"location":"models/audio/#media-and-file-types","title":"Media and File Types","text":"

Islandora Starter Site uses the built-in Drupal \"Audio\" media type for video. Islandora Starter Site configures it to accept files of type mp3, wav, and aac.

"},{"location":"models/audio/#derivatives","title":"Derivatives","text":"

Islandora uses Homarus (ffmpeg as a microservice) to create audio derivatives. Islandora Starter Site sets you up to create:

  • \"Service file\" from the original file, with parameters -codec:a libmp3lame -q:a 5, stored in the public filesystem

These parameters can be changed in the configuration for the Drupal Action that Islandora uses to generate an Audio derivative.

Islandora Starter Site sets up a context to automatically create this derivative when:

  • The Audio media is tagged with the \"Original File\" term (a term with External URI http://pcdm.org/use#OriginalFile)
  • The media's parent node is tagged with the \"Audio\" model (a term with External URI http://purl.org/coar/resource_type/c_18cc)

The mimetype formats allowed by Homarus are configured in Homarus itself - see Installing Crayfish

"},{"location":"models/audio/#display","title":"Display","text":"

Drupal provides an \"Audio\" field formatter for file fields that displays a simple playable audio widget. It works but does not support captions/subtitles. Islandora provides an \"Audio with captions\" formatter that allows for captions.

To use the captions feature out of the box, add the captions track as a WEBVTT file (.vtt) in the Audio media's \"Track\" field (see below regarding which Audio media to use). If you don't have the \"Track\" field (provided by Islandora Starter Site), create a field of type \"Media Track\" (a type provided by Islandora) on the same Media (or more broadly, same entity) as your audio file. Then use the Manage Display page to set your audio file field to render using the \"Audio with captions\" field formatter.

If you're using Islandora Starter Site, you can expect to see an audio player on your node. This is done with a special view (an EVA view) that displays service files, which is configured to show on Repository Item's default display mode (on the \"Manage Display\" page). The EVA view renders the service file media using the \"Source\" view mode. For Audio media, this is configured to show only the audio file using the \"Audio with Captions\" widget. Note that captions, to be displayed, must be on the media that is playing. Thus, when the Service file media is being played, captions on the Original File media are ignored.

"},{"location":"models/video/","title":"Video","text":""},{"location":"models/video/#media-and-file-types","title":"Media and file types","text":"

Islandora Starter Site uses the built-in Drupal \"Video\" media type for videos. Islandora Starter Site configures it to accept files of type mp4.

The Install Profile also makes available the \"Remote video\" media type, but its behaviour has not been publicly documented.

"},{"location":"models/video/#derivatives","title":"Derivatives","text":"

Islandora uses Homarus (ffmpeg as a microservice) to create video derivatives. Islandora Starter Site sets you up to create:

  • Service file with mimetype video/mp4 [yes, this is the same as the input, i'm not sure if it downsamples]
  • Thumbnail with mimetype image/jpeg, derived from one second into the video and scaled down to ~100 px using the command -ss 00:00:01.000 -frames 1 -vf scale=100:-2.

These parameters can be changed in the configuration for the Drupal Actions that Islandora uses to create video derivatives.

Islandora Starter Site sets up a context to automatically create this derivative when:

  • The Video media is tagged with the \"Original File\" term (a term with External URI http://pcdm.org/use#OriginalFile)
  • The media's parent node is tagged with the \"Video\" model (a term with External URI http://purl.org/coar/resource_type/c_18cc)

The mimetype formats allowed by Homarus are configured in Homarus itself - see Installing Crayfish

"},{"location":"models/video/#display","title":"Display","text":"

Drupal provides a \"Video\" field formatter for file fields that displays a simple playable video widget. It works but does not support captions/subtitles. Islandora provides a \"Video with Captions\" formatter that allows for captions.

To use the Captions feature out of the box, add the captions track as a WEBVTT file (.vtt) in the Video media's \"Track\" field (see below regarding which Video media to use). If you don't have the \"Track\" field (provided by Islandora Starter Site), create a field of type \"Media Track\" (a type provided by Islandora) on the same Media (or more broadly, same entity) as your audio file. Then use the Manage Display page to set your audio file field to render using the \"Audio with captions\" field formatter.

If you're using Islandora Starter Site, you can expect to see a video player on your node. This is done with a special view (an EVA view) that displays service files, which is configured to show on the default view of Repository Items (see the \"Manage Display\" page). The EVA view renders the service file media using the \"Source\" view mode. For Video media, this is configured to show only the video file using the \"Video with Captions\" formatter. Thus, when the Service file media is being played, captions on the Original File media are ignored.

"},{"location":"release_notes/8.x-2.0/","title":"Islandora 2.0.0 Release Notes / Upgrade Instructions","text":""},{"location":"release_notes/8.x-2.0/#update-to-php-74","title":"Update to PHP 7.4","text":"

Upstream dependencies have forced us to upgrade PHP to 7.4. How to do this varies by operating system, but for Ubuntu 18.04 the following will work:

  • sudo apt install software-properties-common
  • sudo add-apt-repository ppa:ondrej/php
  • sudo apt update
  • sudo apt install php7.4
  • sudo apt install php7.4-curl php7.4-xml php7.4-gd php7.4-imap php7.4-mbstring php7.4-mysql
"},{"location":"release_notes/8.x-2.0/#updating-alpaca","title":"Updating Alpaca","text":"

Here's the steps to update Alpaca code in general. None of this is specific to this release, it's just the process.

Move your blueprint config out of the deploy directory temporarily

  • mv /opt/karaf/deploy/ca.islandora.* /opt/karaf

Now we'll uninstall the old features and install the new ones. We're assuming you're currently on 1.0.3 and want to update to 1.0.5, but you may be on an older version. Keep that in mind when copy/pasting these commands.

  • /opt/karaf/bin/client \"feature:uninstall islandora-connector-derivative\"
  • /opt/karaf/bin/client \"feature:uninstall islandora-indexing-fcrepo\"
  • /opt/karaf/bin/client \"feature:uninstall islandora-indexing-triplestore\"
  • /opt/karaf/bin/client \"feature:uninstall islandora-http-client\"
  • /opt/karaf/bin/client \"feature:repo-remove mvn:ca.islandora.alpaca/islandora-karaf/1.0.3/xml/features\"
  • /opt/karaf/bin/client \"feature:repo-add mvn:ca.islandora.alpaca/islandora-karaf/1.0.5/xml/features\"
  • /opt/karaf/bin/client \"feature:install islandora-http-client\"
  • /opt/karaf/bin/client \"feature:install islandora-indexing-triplestore\"
  • /opt/karaf/bin/client \"feature:install islandora-indexing-fcrepo\"
  • /opt/karaf/bin/client \"feature:install islandora-connector-derivative\"

Move the blueprint files back to the deploy folder to deploy the derivative routes

  • mv /opt/karaf/ca.islandora.* /opt/karaf/deploy

You can check that everything is working with

  • /opt/karaf/bin/client \"la | grep islandora\"

If all the bundles have a state of Active, then you're all good!

"},{"location":"release_notes/8.x-2.0/#configuring-alpaca-timeouts","title":"Configuring Alpaca Timeouts","text":"

Some users have experienced timeouts when generating derivatives. We've added the ability to set the timeout length to your routes. In order to do so, edit each ca.islandora.* file in the deploy directory and replace

<bean id=\"http\" class=\"org.apache.camel.component.http4.HttpComponent\"/>\n<bean id=\"https\" class=\"org.apache.camel.component.http4.HttpComponent\"/>\n

with

<bean id=\"requestConfigConfigurer\" class=\"ca.islandora.alpaca.connector.derivative.RequestConfigConfigurer\">\n  <property name=\"connectionRequestTimeoutMs\" value=\"10000\"/>\n  <property name=\"connectTimeoutMs\" value=\"10000\"/>\n  <property name=\"socketTimeoutMs\" value=\"10000\"/>\n</bean>\n\n<bean id=\"http\" class=\"org.apache.camel.component.http4.HttpComponent\">\n  <property name=\"httpClientConfigurer\" ref=\"requestConfigConfigurer\"/>\n</bean>\n\n<bean id=\"https\" class=\"org.apache.camel.component.http4.HttpComponent\">\n  <property name=\"httpClientConfigurer\" ref=\"requestConfigConfigurer\"/>\n</bean>\n

You can set the timeout values to whatever you like, we're using 10000 in this example.

"},{"location":"release_notes/8.x-2.0/#update-drupal-core-and-modules","title":"Update Drupal core and modules","text":"

To get the latest Drupal code, edit your composer.json file and change the islandora/* packages versions' to the latest (2.0.0 for most). Then run the following command to update them, your dependencies, and Drupal core:

sudo php -d memory_limit=-1 `which composer` update --with-all-dependencies\n

Afterwards, you need to run drush updb or visit /update.php in your browser to trigger the database updates. This includes important changes, like stripping out configuration we no longer use as well as a fix to support large media (>2.2 GB). The database updates may take a while depending on how many media you have.

"},{"location":"release_notes/8.x-2.0/#update-crayfish-and-remove-gemini","title":"Update Crayfish and Remove Gemini","text":"

We removed the Gemini microservice and pushed its code into Crayfish-Commons so that it can be used by the other microservices and Drupal modules. Once you've updated your Drupal installation, Gemini will no longer be utilized. It will still, however, need to be removed. It is recommended to test this method in a staging/development environment before running in your production environment.

  1. Make sure you have the 2.0.0 version of the islandora module. If you followed the steps above to update Drupal core and its modules, you'll have this.
  2. Import two new Islandora views

    i. This can be done by coping the views into a new directory and using drush such as

    mkdir /var/www/html/drupal/config/1time\ncp /var/www/html/drupal/web/modules/contrib/islandora/modules/islandora_core_feature/config/install/views.view.all_taxonomy_terms.yml /var/www/html/drupal/config/1time/\ncp /var/www/html/drupal/web/modules/contrib/islandora/modules/islandora_core_feature/config/install/views.view.non_fedora_files.yml /var/www/html/drupal/config/1time/\ndrush config:import --partial --source /var/www/html/drupal/config/1time\n
    ii. It can also be done through the UI at /admin/config/development/configuration/single/import 4. Update Crayfish to 2.0.0 1. cd /var/www/html/Crayfish 2. git fetch origin --tags 3. git checkout 2.0.0 6. Run composer install for Milliner such as cd /var/www/html/Crayfish/Milliner && composer install 7. Run composer install for Recast such as cd /var/www/html/Crayfish/Recast && composer install 8. Update the Recast config 1. cd /var/www/html/Crayfish/Recast/cfg 2. open config.yaml 3. Remove gemini_base_url 4. Update base_url in fedora_resource section. It should contain a value something like http://localhost:8080/fcrepo/rest 8. Clear your cache in Drupal using Drush (cd /var/www/html/drupal && drush cr) or the UI. 9. Restart apache sudo systemctl restart apache2 9. Test to make sure you can view existing objects in Fedora by going to the Drupal UI and clicking one of the Fedora URI links 10. Create a new object and make sure it persists to Fedora and the Fedora URI link resolves correctly. Make sure that no new entries get added to Gemini. 11. Once you've confirmed everything is working as expected, you can then remove Gemini like 1. rm /var/www/html/Crayfish/Gemini 2. Remove the gemini database and the associated mysql user 3. Remove the vhost file for gemini: sudo rm /etc/apache2/conf-enabled/Gemini.conf 4. Restart apache: sudo systemctl restart apache2

"},{"location":"release_notes/8.x-2.0/#adding-captions","title":"Adding Captions","text":"

You can now add captions to A/V content! If you are starting with a new installation, there's nothing to do, but if you have an existing install, here's how to configure Drupal to display captions/

  1. Make sure you have the 2.0.0 version of islandora and islandora_defaults. If you followed the steps above to update Drupal core and its modules, you'll have this.
  2. Selectively import the following configurations from islandora_defaults using the Features UI (recommended) or by copying them into a temporary directory and using drush to do a partial config import.
    1. core.entity_form_display.media.audio.default
    2. core.entity_form_display.media.video.default
    3. core.entity_view_display.media.audio.default
    4. core.entity_view_display.media.audio.source
    5. core.entity_view_display.media.video.default
    6. core.entity_view_display.media.video.source
    7. field.field.media.audio.field_track
    8. field.field.media.video.field_track
    9. field.storage.media.field_track
  3. Clear your drupal cache drush cr
  4. You should now see a \"track\" field on audio and video media objects
"},{"location":"technical-documentation/adding-format-jsonld/","title":"Adding back ?_format=jsonld","text":"

Drupal requires the use of a _format query parameter to get alternate representations of a node/media.

By default, Islandora deploys with the jsonld module and the Milliner microservice. These two components are configured to strip this _format query parameter off of the end of URIs.

This means that when your content is indexed in Fedora, the triplestore, etc... it's URI will be something like http://localhost:8000/node/1 and not http://localhost:8000/node/1?_format=jsonld.

"},{"location":"technical-documentation/adding-format-jsonld/#pre-10-installations","title":"Pre-1.0 installations.","text":"

If you are using a very early version of Islandora \"8\" (pre-release), then you may have URIs with _format=jsonld at the end of them.

If you update to newer code, you will need to ensure that your site is configured to add ?_format=jsonld back to the URLs if you want to maintain consistency.

If you don't do this, you can end up with two copies of your objects in your Fedora repository (one with and one without ?_format=jsonld). You will also have two sets of triples in your triplestore.

"},{"location":"technical-documentation/adding-format-jsonld/#adding-_formatjsonld-to-your-uris","title":"Adding ?_format=jsonld to your URIs","text":"

To turn the ?_format parameter back on:

  • Go to admin/config/search/jsonld and confirm the \"Remove jsonld parameter from @ids\" checkbox is unchecked.
  • Add strip_format_jsonld: false to your Milliner config. If you deployed using the default Islandora-playbook this file would be located at /var/www/html/Crayfish/Milliner/cfg/config.yaml.

If you are using Islandora-playbook and are provisioning new environments for your older Islandora, you'll want to lock down the variables in your inventory that control this config.

  • crayfish_milliner_strip_format_jsonld: true
  • webserver_app_jsonld_remove_format: 1
"},{"location":"technical-documentation/alpaca-tips/","title":"Alpaca Tips","text":"

Alpaca is event-driven middleware based on Apache Camel for Islandora

Currently, Alpaca ships with four event-driven components

  • islandora-connector-derivative
  • islandora-http-client
  • islandora-indexing-fcrepo
  • islandora-indexing-triplestore
"},{"location":"technical-documentation/alpaca-tips/#islandora-connector-derivative","title":"islandora-connector-derivative","text":"

This service receives requests from Drupal when it wants to create derivatives and passes that request along to a microservice in Crayfish. When it receives the derivative file back from the microservice, it passes the file back to Drupal.

"},{"location":"technical-documentation/alpaca-tips/#islandora-http-client","title":"islandora-http-client","text":"

This service overrides the default http client with Islandora specific configuration.

"},{"location":"technical-documentation/alpaca-tips/#islandora-indexing-fcrepo","title":"islandora-indexing-fcrepo","text":"

This service receives requests from Drupal in response to write operations on entities. These requests are passed along to Milliner microservice in Crayfish to convert Drupal entities into Fedora resources and communicate with Fedora (via Chullo).

"},{"location":"technical-documentation/alpaca-tips/#islandora-indexing-triplestore","title":"islandora-indexing-triplestore","text":"

This service receives requests from Drupal on indexing and deleting in order to persist/delete content in the triplestore.

"},{"location":"technical-documentation/alpaca-tips/#steps-for-developing-with-alpaca","title":"Steps for developing with Alpaca","text":"

Alpaca now runs as a single executable jar which can enable none, some or all of the available services.

To develop your own module, start by cloning the Alpaca code base.

Then create a new directory (for example my-new-module) along side the islandora-indexing-fcrepo, islandora-indexing-triplestore directories

Add your new directory to the settings.gradle file, following the pattern of the others.

  include ':islandora-support'\ninclude ':islandora-indexing-triplestore'\ninclude ':islandora-indexing-fcrepo'\ninclude ':islandora-connector-derivative'\ninclude ':islandora-http-client'\ninclude ':islandora-alpaca-app'\n+ include ':my-new-module'\nproject(':islandora-alpaca-app').setProjectDir(\"$rootDir/islandora-alpaca-app\" as File)\nproject(':islandora-support').setProjectDir(\"$rootDir/islandora-support\" as File)\nproject(':islandora-indexing-triplestore').setProjectDir(\"$rootDir/islandora-indexing-triplestore\" as File)\nproject(':islandora-indexing-fcrepo').setProjectDir(\"$rootDir/islandora-indexing-fcrepo\" as File)\nproject(':islandora-connector-derivative').setProjectDir(\"$rootDir/islandora-connector-derivative\" as File)\nproject(':islandora-http-client').setProjectDir(\"$rootDir/islandora-http-client\" as File)\n+ project(':my-new-module').setProjectDir(\"$rootDir/my-new-module\" as File)\n

You can explore the islandora-indexing-fcrepo module to see the pattern to develop your own module.

This module contains three classes.

You can ignore the CommonProcessor class, that is just some processing that is split out for reusability.

The first class is the FcrepoIndexer, this class extends the Apache Camel RouteBuilder and requires a configure method which defines the processing elements of your workflow. This is the Camel \"route\".

The second class is the FcrepoIndexerOptions, this class extends the Alpaca PropertyConfig base class which gets common configuration parameters into your module. It also contains any custom configuration parameters needed for your route.

Lastly it uses the @Conditional(FcrepoIndexerOptions.FcrepoIndexerEnabled.class) to define when this module is enabled.

FcrepoIndexerOptions.FcrepoIndexerEnabled.class refers to the static inner class.

This class is inside of FcrepoIndexerOptions and works like this:

[1]  static class FcrepoIndexerEnabled extends ConditionOnPropertyTrue { \n[2]    FcrepoIndexerEnabled() {\n[3]      super(FcrepoIndexerOptions.FCREPO_INDEXER_ENABLED, false);\n[4]    }\n[5]  }\n
Line 1 extends the class that will register (enable) this module when a defined property is \"TRUE\"

Line 2 is the constructor for this static class

Line 3 passes to the parent constructor two things.

  1. the property name to check for enabling this module.
  2. the default value to use if the property (above) is not found.

So in this case we check for the property fcrepo.indexer.enabled and if we don't find it, we pass false. So this module is assumed to be \"off\" unless the property fcrepo.indexer.enabled=true is located.

The last thing is to add your new module to the islandora-alpaca-app build.gradle file as a dependencies, like the existing modules. i.e.

dependencies {\n    implementation \"info.picocli:picocli:${versions.picocli}\"\n    implementation \"org.apache.camel:camel-spring-javaconfig:${versions.camel}\"\n    implementation \"org.slf4j:slf4j-api:${versions.slf4j}\"\n    implementation \"org.springframework:spring-context:${versions.spring}\"\n    implementation project(':islandora-support')\n    implementation project(':islandora-connector-derivative')\n    implementation project(':islandora-indexing-fcrepo')\n    implementation project(':islandora-indexing-triplestore')\n+   implementation project(':my-new-module')\n\n    runtimeOnly \"ch.qos.logback:logback-classic:${versions.logback}\"\n\n}\n

Finally from the top-level directory of Alpaca execute

./gradlew clean build shadowJar\n

This tells Gradle to clean the modules, then build the modules and finally create a single jar with all needed code (the shadow jar).

The final executable jar is:

<alpaca directory>/islandora-alpaca-app/build/libs/islandora-alpaca-<version>-all.jar\n

"},{"location":"technical-documentation/checking-coding-standards/","title":"Checking Code Style","text":"

Before opening a pull request, you should check your code style. If you are using the Vagrant, you can run phpcs within the Drupal installation directory (on the Vagrant, that is /var/www/html/drupal) or from within the web directory (/var/www/html/drupal/web) as follows:

  • from within Drupal's root directory: ./vendor/bin/phpcs --standard=./vendor/drupal/coder/coder_sniffer/Drupal modules/contrib/my_module, where modules/contrib/my_module is the relative or full path to the PHP file you want to check.
  • from within Drupal's web directory: ../vendor/bin/phpcs --standard=../vendor/drupal/coder/coder_sniffer/Drupal yourfile, where yourfile is the relative or full path to the PHP file you want to check.

In both cases:

  • the path to the coding standard file can be relative to where you are running it from, e.g. when in web: --standard=../vendor/drupal/coder/coder_sniffer/Drupal
  • you can specify a single file to check, or a directory path; in the latter case, all files in that directory will be checked.

Islandora runs phpcs in its Github continuous integration environment, and there, it specifies which files to ignore and which files to check. It is a good idea for developers to specify the same options when running phpcs locally, prior to opening a pull request. For example (running phpcs from the within Drupal's web directory), you should use the following --ignore and --extensions options:

../vendor/bin/phpcs --standard=../vendor/drupal/coder/coder_sniffer/Drupal --ignore=*.md --extensions=php,module,inc,install,test,profile,theme,css,info modules/contrib/my_module

"},{"location":"technical-documentation/diagram/","title":"Islandora Architecture Diagram","text":"

Diagram prepared by Bethany Seeger based on work done by Gavin Morris

"},{"location":"technical-documentation/diagram/#components","title":"Components","text":""},{"location":"technical-documentation/diagram/#islandora","title":"Islandora","text":"

The following components are microservices developed and maintained by the Islandora community. They are bundled under Islandora Crayfish:

  • FITS - A Symfony 4 Microservice to generate FITS data and persist it as a Drupal media node. Works with Islandora FITS
  • Homarus - Provides FFmpeg as a microservice for generating video and audio derivatives.
  • Houdini - ImageMagick as a microservice for generating image-based derivatives, including thumbnails.
  • Hypercube - Tesseract as a microservice for optical character recognition (OCR).
  • Milliner - A microservice that converts Drupal entities into Fedora resources.
  • Recast - A microservice that remaps Drupal URIs to add Fedora-to-Fedora links based on associated Drupal URIs in RDF.
"},{"location":"technical-documentation/diagram/#other-open-source","title":"Other Open Source","text":"

The following components are deployed with Islandora, but are developed and maintained by other open source projects:

  • Apache - The Apache HTTP Server, colloquially called Apache, is a free and open-source cross-platform web server software. Provides the environment in which Islandora and its components run.
  • ActiveMQ - Apache ActiveMQ is an open source message broker written in Java together with a full Java Message Service client.
  • Karaf - A modular open source OSGi runtime environment.
  • Tomcat - an open-source implementation of the Java Servlet, JavaServer Pages, Java Expression Language and WebSocket technologies. Tomcat provides a \"pure Java\" HTTP web server environment in which Java code can run.
  • Solr - An open-source enterprise-search platform. Solr is the default search and discover layer of Islandora, and a key component in some methods for migration to Islandora from Islandora Legacy
  • Blazegraph - Blazegraph is a triplestore and graph database.
  • Cantaloupe - an open-source dynamic image server for on-demand generation of derivatives of high-resolution source images. Used in Islandora to support IIIF
  • Drupal - Drupal is an open source content management system, and the heart of Islandora. All user and site-building aspects of Islandora are experienced through Drupal as a graphical user interface.
  • Fedora - A robust, modular, open source repository system for the management and dissemination of digital content. The default smart storage for Islandora.
  • Matomo - Matomo, formerly Piwik, is a free and open source web analytics application. It provides usage statistics and a rich dashboard for Islandora.
  • MySQL - MySQL is an open-source relational database management system. Used as a Drupal database in Islandora, it can be easily replaced with other database management systems such as PostgreSQL
  • Triplestore - See Blazegraph.
"},{"location":"technical-documentation/docs-build/","title":"Introduction","text":"

This documentation is built using MkDocs, a static site generator that is geared towards building project documentation. The documentation is created in the Markdown format, and it all resides in the docs directory in the repository. The organization of the documentation is controlled by the mkdocs.yml in the root of the repository.

Video version available

Some of the material in this tutorial is presented in our video, How to Build Documentation.

"},{"location":"technical-documentation/docs-build/#prerequisites","title":"Prerequisites","text":"

You will need to have mkdocs software installed locally, as well as a required plugin and the MkDocs Material theme. Below we will show you how to install mkdocs using the Python language's pip tool. For more details on installing and using MkDocs visit the MkDocs installation guide.

  • Open a terminal window.

  • Install mkdocs:

    Windows / Linux:

    sudo -H pip install mkdocs

    macOS:

    pip3 install mkdocs

  • Install plugin to enable display of the last revision date:

    Windows / Linux:

    sudo -H pip install mkdocs-git-revision-date-localized-plugin

    macOS:

    pip3 install mkdocs-git-revision-date-localized-plugin

  • Install plugin to enable redirects:

    Windows / Linux:

    sudo -H pip install mkdocs-redirects

    macOS:

    pip3 install mkdocs-redirects

  • Install Material theme:

    Windows / Linux:

    sudo -H pip install mkdocs-material

    macOS:

    pip3 install mkdocs-material

"},{"location":"technical-documentation/docs-build/#build-and-deploy-documentation","title":"Build and Deploy documentation","text":"

Make sure you have all the submodules:

git submodule update --init --recursive

Documentation is build by running to the following command in the root of the repository:

mkdocs build --clean

This command will create a static site folder in the root of the repository.

You can preview any changes you have made to the documentation by running the following command:

mkdocs serve

And then visiting http://localhost:8111 in your browser.

To deploy documentation to GitHub Pages, issue the following command:

mkdocs gh-deploy --clean

To stop the mkdocs serve command just type the key combination \"Control-c\".

"},{"location":"technical-documentation/install-enable-drupal-modules/","title":"Installing Modules","text":""},{"location":"technical-documentation/install-enable-drupal-modules/#downloading-and-enabling-drupal-modules-and-themes","title":"Downloading and Enabling Drupal Modules and Themes","text":"

Islandora can make use of the majority of Drupal modules and themes. Common use cases have been documented in the Islandora Cookbook. There are several ways to download and install Drupal modules. Please refer to this guide on Drupal.org.

Composer is the recommended method to install and update drupal modules and themes in Islandora. Drupal modules provided by Islandora can be accessed at the drupal/ namespace.

$ composer require \"<vendor>/<package>:<version>\"\n# Example\n$ composer require \"drupal/jsonld:^2\"\n

In the Islandora playbook, you can add a Drupal module's or theme's machine name to the drupal_composer_dependencies variable here. To enable the Drupal module or theme, add the module machine name to the drupal_enable_modules variable as well.

For modules that require additional steps, additional tasks may need to be added to the Ansible playbook. Re-provisioning your instance via Ansible will install the module.

"},{"location":"technical-documentation/install-enable-drupal-modules/#video-walkthroughs-modules-installing-modules-with-composer","title":"Video Walkthroughs: Modules & Installing modules with Composer","text":"

Click the image below to open the introduction to Modules video tutorial on the Islandora Youtube channel.

Click the image below to open the Installing modules with Composer video tutorial on the Islandora Youtube channel.

See more videos from the Drupal 101 series here.

"},{"location":"technical-documentation/migrate-7x/","title":"Islandora 7","text":""},{"location":"technical-documentation/migrate-7x/#migrating-from-islandora-legacy-to-islandora-youtube-video","title":"Migrating from Islandora Legacy to Islandora (YouTube Video)","text":"

Nov 21, 2019

!! note \"Migration from Islandora Legacy\" For tools to migrate into Islandora from an existing Islandora Legacy instance, please see migrate_7x_claw.

"},{"location":"technical-documentation/migrate-7x/#project-planning-for-migration","title":"Project Planning for Migration","text":"

Presented by Mark Jordan and Janice Banser, this session will outline strategies for understanding your data and planning for a move to Islandora.

"},{"location":"technical-documentation/migrate-csv/","title":"CSV","text":""},{"location":"technical-documentation/migrate-csv/#table-of-contents","title":"Table of Contents","text":"
  • Summary
  • Introduction
  • Overview
  • Installation
  • Ingesting Files
    • Anatomy of a migration
      • Source
      • Process
      • Destination
      • The Process Section in Depth
    • Running the File Migration
  • Ingesting Nodes
    • Complex Fields
    • Running the node migration
  • Migrating Media
  • What have we learned
  • Where to go from here
"},{"location":"technical-documentation/migrate-csv/#summary","title":"Summary","text":"

This tutorial introduces you to the Drupal Migrate tools available to create Islandora content. Whether you will eventually use CSVs or other sources (such as XML or directly from a Legacy Islandora) this tutorial should be useful as it covers the basics and mechanics of migration.

This tutorial uses the configurations and code available in the migrate_islandora_csv module which, when enabled, will create three example migrations ready for you to use with the Migrate API. Each migration comes from one of the files in the config/install folder. We'll walk through them in detail below. The module is also uses Features which allows you to make changes to the configuration files and sync those changes into your Drupal site.

A note on using Features

This tutorial (and Islandora in general) makes heavy use of Features, which is an easy way to ship and install Drupal configuration. However, after enabling a Feature module, the code in that module's directory is no longer \"live\", as the configuration now resides in the Drupal database. If you change code in the YAML files, it will not take effect until you re-import the Feature. There is a walkthrough in the \"Configuration\" section of the Migrate 7.x to Islandora tutorial.

Sample CSV and images are also included in the module as a convenience so they are easily available on the Drupal server running the migration. (This is not the recommended method for making files available to Drupal in a real migration.)

The module also contains a Migrate process plugin that transforms strings into associative arrays. This is useful for populating multiple Contributor (field_linked_agent) fields. (See \"Typed Relation\" for more information on the Contributor field's type.) It will be available when this module is enabled, and the node migration uses it. It was written generically and will hopefully become part of Migrate Plus, but for now it is here.

When you are ready to create your actual migrations, the contents of this repository can function as a template for you to create the YAML files defining your own migrations.

"},{"location":"technical-documentation/migrate-csv/#introduction","title":"Introduction","text":"

Why CSV? CSV files (whether separated by commas, tabs, or other delimiters) are easy to understand and work with, and there's good tooling available for using them with Drupal's Migrate API. The Drupal contrib module migrate_source_csv provides a source plugin that reads from a CSV file, and migrate_plus provides more tools and flexibility for creating migrations, including the ability to create customized migrations using YAML and package them up as Features.

In this tutorial, we'll be inspecting each migration file in detail before running it. You'll start out by migrating the images themselves first, and then you'll create various Drupal entities to describe the files from the metadata in the CSV. It's not as scary as it sounds (especially since this module contains the data we'll be using in a data directory), but you will need a few things before beginning:

  1. An instance of Islandora. Use Islandora Playbook to spin up an environment pre-loaded with all the modules you need (except this one)
  2. Some basic command line skills. You won't need to know much, but you'll have to vagrant ssh into the box, navigate into Drupal, and use git and drush, etc... If you can copy/paste into a terminal, you'll survive.

The configuration referred to in this tutorial comes from the Islandora Starter Site, which defines the content model and metadata fields that we'll be migrating into. You're not required to use the Islandora Starter Site for your repository, but for the purposes of demonstration, it saves you a lot of user interface administrivia so you can focus just on the learning how to migrate. By the time you are done with this exercise, you'll be able to easily apply your knowledge to migrate using any custom metadata profile you can build using Drupal.

"},{"location":"technical-documentation/migrate-csv/#overview","title":"Overview","text":"

The Migrate API is the main way to ingest batches of data into Drupal (and because Islandora is Drupal, into Islandora). The Migrate module only provides the framework, it's up to you to create the rules that take data from a source, through a process (i.e. a mapping) to a destination. A set of these rules is called a \"migration\". It has to be set up (as a Configuration Entity, either by importing a YAML file or by installing a Feature) and then it has to be run.

Once a migration has been run, it will have created (or updated) a bunch of Drupal entities of one type - whether that's taxonomy terms, nodes, files, etc. Since an Object in Islandora is made up of several different Drupal entities that refer to each other, it's going to take multiple migrations to create an Islandora object, and it's important to perform these migrations in a sensible order.

A basic Islandora object is at minimum: - a file, which holds the actual binary contents of an item - a node, which holds the descriptive metadata for an item and makes it discoverable - a media, which holds technical metadata and references the file and the node, linking the two together.

Therefore, each row in your CSV must contain enough information to create these.

Using multiple sources for object components

While this tutorial uses a single CSV to describe all the parts of an islandora object, a site can use multiple sources (CSV or otherwise) to migrate the various parts. The only requirements then is to have keys in place matching the parts together. For example, a 'files.csv' can use paths that serve as file identifiers and 'metadata.csv' can use an object identifier. As long as the source for the media migration has the key values for the file and metadata migrations (the file paths and object identifiers, respectively, in this example), we can create the necessary links between the object's components. This is very useful when you need to migrate multiple media/file pairs associated with a single node, such as pre-processed derivatives that need to be migrated.

Buried in your descriptive metadata are often references to other things which aren't repository items themselves, but records still need to be kept for them. Authors, publishers, universities, places, etc... can be modeled as Drupal Entities, so that they can be referenced by other Entities. So there's the potential to have a lot of different entity types described in a single row in a CSV.

In this tutorial, we will use the three migrations defined in this module to migrate into Drupal entities. The entities we are migrating content into have already been defined by the Islandora Starter Site:

  • file
  • node
  • media
  • subject
  • person
  • corporate_body

We can do this because subjects, persons, and corporate bodies are (in this example) represented by simple 'name' strings. We create them on the fly, as if we were adding new tags to a tag vocabulary. If we wanted to model subjects, people, or corporate bodies as entities with multiple fields (first name, last name, date, subheadings, URI, etc.) then we would need up to six migrations.

Migrations follow the Extract-Transform-Load pattern. You extract the information from a source, process the data to transform it into the format you need, and load it into the destination system (i.e. Drupal). Migrations are stored in Drupal as configuration, which means they can be represented in YAML, transferred to and from different sites, and are compatible with Drupal's configuration synchronization tools. And the structure of each YAML file is arranged to follow the Extract-Transform-Load pattern.

To perform the migrations, we'll be using drush. We will be able to run each of the file, node, and media migrations separately or all at once in a group. We will also learn how to roll back a migration in case it didn't go as planned.

The sample migrations here will give us a chance to show off some techniques for working with multi-valued fields, entity reference fields, and complex field types like controlled_access_terms's typed_relation field. We'll also see how the migrate framework can help de-duplicate, and at the same time, linked data-ize :tm: your data by looking up previously migrated entities.

So hold on to your hats. First, let's get this module onto your Islandora instance.

"},{"location":"technical-documentation/migrate-csv/#installation","title":"Installation","text":"

From your islandora-playbook directory, issue the following commands to enable this module: - vagrant ssh to open a shell in your Islandora instance. - cd /var/www/html/drupal/web/modules/contrib to get to your modules directory. - git clone https://github.com/Islandora/migrate_islandora_csv to clone down the repository from GitHub. - drush en -y migrate_islandora_csv to enable the module, installing the migrations as configuration.

Optionally, flush the cache (drush cr), so the migrations become visible in the GUI at Manage > Structure > Migrations > migrate_islandora_csv (http://localhost:8000/admin/structure/migrate/manage/migrate_islandora_csv/migrations)

Now lets go migrate some files.

Caution

As you saw, you can git clone into the modules directory, but if you're installing a custom module that's intended to stay installed for the long term (unlike a migration feature, which you should probably uninstall and delete when you're done with it) then you may want to check with your devops folks and use Composer instead. However, using Git directly allows you to be more flexible when iterating and testing.

"},{"location":"technical-documentation/migrate-csv/#ingesting-files","title":"Ingesting Files","text":"

To migrate files (i.e. just the raw binaries) from a CSV, you need: - a column in the CSV containing paths to the files you wish to ingest, and - the files need to be accessible from the server that's running Drupal so that the Migrate framework can find them.

This tutorial assumes you're working with the sample images provided in the module, which will be located at /var/www/html/drupal/web/modules/contrib/migrate_islandora_csv/data/images. When you're migrating for real, the files will have to be uploaded or otherwise made accessible to the server before this point.

Open up the CSV file at data/migration.csv and you'll see a file column containing paths to the sample images. You can use your favorite shell-based utility to open it at /var/www/html/drupal/web/modules/contrib/migrate_islandora_csv/data/migration.csv, or browse to it on GitHub, or just look at it pasted below as this tutorial does not require you to edit the files.

file /var/www/html/drupal/web/modules/contrib/migrate_islandora_csv/data/images/Nails Nails Nails.jpg /var/www/html/drupal/web/modules/contrib/migrate_islandora_csv/data/images/Free Smells.jpg /var/www/html/drupal/web/modules/contrib/migrate_islandora_csv/data/images/Nothing to See Here.jpg /var/www/html/drupal/web/modules/contrib/migrate_islandora_csv/data/images/Call For Champagne.jpg /var/www/html/drupal/web/modules/contrib/migrate_islandora_csv/data/images/This Must Be The Place.jpg

Open up the \"files\" migration at config/install/migrate_plus.migration.file.yml. You'll see the following migration config:

id: file\nlabel: Import Image Files\nmigration_group: migrate_islandora_csv\n\nsource:\n  plugin: csv\n  path: '/var/www/html/drupal/web/modules/contrib/migrate_islandora_csv/data/migration.csv'\n  delimiter: ','\n\n  # 1 means you have a header row, 0 means you don't\n  header_row_count: 1\n\n  # Each migration needs a unique key (\"id\") per row in the CSV.  Here we're using the file path.\n  ids:\n    - file\n\n  # You can't enter string literals into a process plugin, but you can give it a constant as a 'source'.\n  constants:\n    # Islandora uses Flysystem and stream wrappers to work with files.  What we're really saying here is\n    # to put these files in Fedora in a 'csv_migration' folder.  It doesn't matter if the directory\n    # doesn't exist yet, it will get created for you automatically.\n    destination_dir: 'fedora://csv_migration'\n\nprocess:\n\n  ##\n  # The following two fields are temporary, and just used to generate a destination for the file.\n  ##\n\n  # Hack the file name out of the full path provided in the 'file' column.\n  filename:\n    -\n      plugin: callback\n      callable: pathinfo\n      source: file\n    -\n      plugin: extract\n      index:\n        - basename\n\n  # Construct the destination URI using the file name.\n  destination:\n    plugin: concat\n    delimiter: /\n    source:\n      - constants/destination_dir\n      - '@filename'\n\n  ##\n  # Here's where we copy the file over and set the URI of the file entity.\n  ##\n  uri:\n    plugin: file_copy\n    source:\n      - file # The source column in the CSV\n      - '@destination' # The destination entry from above\n\ndestination:\n  # These are Drupal 'image' entities we're making, not just plain 'file' entities.\n  plugin: 'entity:file'\n  type: image\n
"},{"location":"technical-documentation/migrate-csv/#anatomy-of-a-migration","title":"Anatomy of a Migration","text":"

It seems like a lot to take in at first, but there's a pattern to Drupal migrations. They always contain core identification information and three key sections: source, process, and destination. And these sections correspond exactly to Extract, Transform, and Load.

"},{"location":"technical-documentation/migrate-csv/#identification","title":"Identification","text":"

The first section of a migration contains metadata for Drupal about the migration itself. The id parameter is the machine name of this migration, and must not conflict with existing migrations. Note: use alphanumeric characters and underscores. Hyphens (-) will cause the migration to fail. The label is a human-readable string to identify this migration, and the migration_group is where this migration will be grouped in the GUI.

"},{"location":"technical-documentation/migrate-csv/#source","title":"Source","text":"

The source section configures a Drupal source plugin that will extract the data. A source plugin provides \"rows\" of data to processing plugins so that they can be worked on. In this case, we're using the csv source plugin, which very literally uses rows, however you can have source plugins that work with other data formats like XML and JSON. Look at the config from this section.

source:\n  plugin: csv\n  path: '/var/www/html/drupal/web/modules/contrib/migrate_islandora_csv/data/migration.csv'\n  delimiter: ','\n  header_row_count: 1\n  ids:\n    - file\n  constants:\n    destination_dir: 'fedora://csv_migration'\n
You can see we provide a path to its location, what delimiter to use, if it uses a header row, and which column contains a unique id for each entry. Constants can also be defined here (more on those later).

"},{"location":"technical-documentation/migrate-csv/#process","title":"Process","text":"

We're going to dive into the details of this step below, but in summary: the process section is where we extract the desired bits from that row, transform them as desired, and populate them into an associative array. This section is a series of named steps, that call one or more process plugins. These plugins are executed in sequence, with the results getting passed from one to the next, forming a pipeline. By the end of the step, you have transformed some element of data (perhaps through text manipulation, concatenation, etc...) into the form that Drupal is expecting. The resulting value gets associated with the name of the step.

If the name of a step is the same as a field or property name on the target entity, the migrated entity will have that value for that field or property. This is how you can apply metadata from the CSV to an entity. If the step name is not the name of a field or property on the target entity, the migrate framework assumes it's a temporary value you're using as part of more complex logic. It won't wind up on the entity when the migration is done, but it will be available for you to use within other process plugins. You can always spot when a temporary value is being used by the fact that it's prefixed with an @ and surrounded by quotes. You can also pass constants into process plugins, which are prefixed with constants/.

"},{"location":"technical-documentation/migrate-csv/#destination","title":"Destination","text":"

The destination section contains the configuration that describes what gets loaded into Drupal.

destination:\n  plugin: 'entity:file'\n  type: image\n
You can create any type of content entity in Drupal. In this case, we're making file entities. Specifically, we're making images, which are a special type of file entity that's provided by core Drupal.

"},{"location":"technical-documentation/migrate-csv/#the-process-section-in-depth","title":"The Process Section in Depth","text":"

In the process section of the migration, we're copying the images over into a Drupal file system and setting the uri property on the corresponding File entity.

  uri:\n    plugin: file_copy\n    source:\n      - file\n      - '@destination'\n
To do this, we're using the file_copy process plugin. But first, we have to know where a file is located and where it should be copied to. We know where the file resides, we have that in the CSV's file column. But we're going to have to do some string manipulation in order to generate the new location where we want the file copied. We're trying to convert something like /var/www/html/drupal/web/modules/contrib/migrate_islandora_csv/data/images/Free Smells.jpg to fedora://csv_migration/Free Smells.jpg.

The URI we're constructing is a stream wrapper of the form scheme://path/to/file. Islandora uses Flysystem, which allows for integration with many different types of filesystems, both remote and local. With Flysystem, the scheme part of the URI is the name of a filesystem. By default, Fedora is exposed using the scheme fedora://. So by setting uri to fedora://csv_migration/Free Smells.jpg, we're saying \"put Free Smells.jpg in the csv_migration directory in Fedora.\"

Now, to perform this string manipulation in PHP, we'd do something like

$info = pathinfo($filepath);\n$filename = $info['basename'];\n$destination = \"fedora://csv_migration/\" . $filename;\n

We will mimic this exactly in the process section of our migration config. Just like we declare variables and call functions with PHP code, we can make entries in the process section to store the output of Drupal process plugins. We'll build up a destination \"variable\" using a filename \"variable\" and pass it into the file_copy process plugin.

To start, we'll get the filename using two process plugins, which do the same steps as the first two lines of the PHP above:

  filename:\n    -\n      plugin: callback\n      callable: pathinfo\n      source: file\n    -\n      plugin: extract\n      index:\n        - basename\n
The first process plugin, callback, lets you execute any PHP function that takes a single input and returns an output. It's not as flexible as making your own custom process plugin, but it's still pretty useful in a lot of situations. Here we're using it to call pathinfo(), telling it to use the file column in the CSV as input. We pass the resulting array from pathinfo() to the extract process plugin, which pulls data out of arrays using the keys you provide it under index.

Now that we have the file name, we have to prepend it with fedora://csv_migration/ to make the destination URI. In our PHP code above, we used . to concatenate the strings. In the migration framework, we use the concat process plugin. You provide it with two or more strings to concatenate, as well as a delimiter.

  destination:\n    plugin: concat\n    delimiter: /\n    source:\n      - constants/destination_dir\n      - '@filename'\n

In our PHP code, we concatenated the $filename variable with a string literal. In our process plugin, we can provide the variable, e.g. the output of the filename process step, by prefixing it with an @. We can't, however, pass in fedora://csv_migration directly as a string. At first glance, you might think something like this would work, but it totally doesn't:

  # Can't do this.  Won't work at all.\n  destination:\n    plugin: concat\n    delimiter: /\n    source:\n      - 'fedora://csv_migration'\n      - '@filename'\n
That's because the migrate framework only interprets source values as names of columns from the CSV or names of other process steps. Even if they're wrapped in quotes. It will never try to use the string directly as a value. To circumvent this, we declare a constant in the source section of the migration config.

  constants:\n    destination_dir: 'fedora://csv_migration'\n

This constant can be referenced as constants/destination_dir and passed into the concat process plugin as a source.

There are a lot more process plugins available through the (core) Migrate and Migrate Plus modules, and they are documented on Drupal.org.

"},{"location":"technical-documentation/migrate-csv/#running-the-file-migration","title":"Running the File Migration","text":"

Migrations can be executed via drush using the migrate:import command. You specify which migration to run by using the id defined in its YAML. You also need to set parameters to tell Drush who you are and what your site's URL is. Failing to do so will result in derivatives not being generated and malformed/improper RDF. So don't forget them! To run the file migration from the command line, make sure you're within /var/www/html/drupal/web (or any subdirectory) and enter

drush -y --userid=1 --uri=localhost:8000 migrate:import file\n
If you've already run the migration before, but want to re-run it for any reason, use the --update flag.
drush -y --userid=1 --uri=localhost:8000 migrate:import file --update\n
You may have noticed that migrations can be grouped, and that they define a migration_group in their configuration. You can execute an entire group of migrations using the --group flag. For example, to run the entire group defined in this module
drush -y --userid=1 --uri=localhost:8000 migrate:import --group migrate_islandora_csv\n
You can also use the migrate:rollback command to delete all migrated entities. Like migrate:import, it also respects the --group flag and --uri parameter. So to rollback everything we just did:
drush -y --uri=localhost:8000 migrate:rollback --group migrate_islandora_csv\n
If something goes bad during development, sometimes migrations can get stuck in a bad state. Use the migrate:reset command to put a migration back to Idle. For example, with the file migration, use
drush -y --uri=localhost:8000 migrate:reset file\n

Make sure you've run (and not rolled back) the file migration. It should tell you that it successfully created 5 files. You can confirm its success by visiting http://localhost:8000/admin/content/files. You should see 5 images of neon signs in the list.

"},{"location":"technical-documentation/migrate-csv/#ingesting-nodes","title":"Ingesting Nodes","text":"

Those five images are nice, but we need something to hold their descriptive metadata and show them off. We use nodes in Drupal to do this, and that means we have another migration file to work with. Nestled in with our nodes' descriptive metadata, though, are more Drupal entities, and we're going to generate them on the fly while we're making nodes. While we're doing it, we'll see how to use pipe delimited strings for multiple values as well as how to handle typed_relation fields that are provided by controlled_access_terms. Open up /var/www/html/drupal/web/modules/contrib/migrate_islandora_csv/config/install/migrate_plus.migration.node.yml and check it out.

# Uninstall this config when the feature is uninstalled\ndependencies:\n  enforced:\n    module:\n      - migrate_islandora_csv\n\nid: node\nlabel: Import Nodes from CSV\nmigration_group: migrate_islandora_csv\n\nsource:\n  plugin: csv\n  path: modules/contrib/migrate_islandora_csv/data/migration.csv\n\n  # 1 means you have a header row, 0 means you don't\n  header_row_count: 1\n\n  # Each migration needs a unique id per row in the csv.  Here we're using the file path.\n  ids:\n    - file\n\n  # You can't enter string literals into a process plugin, but you can give it a constant as a 'source'.\n  constants:\n    # We're tagging our nodes as Images\n    model: Image\n\n    # Everything gets created as admin\n    uid: 1\n\n# Set fields using values from the CSV\nprocess:\n  title: title\n  uid: constants/uid\n\n  # We use the skip_on_empty plugin because\n  # not every row in the CSV has subtitle filled\n  # in.\n  field_alternative_title:\n    plugin: skip_on_empty\n    source: subtitle\n    method: process\n\n  field_description: description\n\n  # Dates are EDTF strings\n  field_edtf_date: issued\n\n  # Make the object an 'Image'\n  field_model:\n    plugin: entity_lookup\n    source: constants/model\n    entity_type: taxonomy_term\n    value_key: name\n    bundle_key: vid\n    bundle: islandora_models\n\n  # Split up our pipe-delimited string of\n  # subjects, and generate terms for each.\n  field_subject:\n    -\n      plugin: skip_on_empty\n      source: subject\n      method: process\n    -\n      plugin: explode\n      delimiter: '|'\n    -\n      plugin: entity_generate\n      entity_type: taxonomy_term\n      value_key: name\n      bundle_key: vid\n      bundle: subject\n\n  # If you have multiple values of a complex\n  # field, iterate over them using sub_process.\n  # But sub_process requires structured data\n  # i.e. an associative array, not a string\n  # or list of strings. To turn strings into\n  # associative arrays, use the custom\n  # process plugin str_to_assoc.\n\n  # Extract a list of names from the column\n  # called photographer, and transform it into\n  # an array of associative arrays.\n  photographers:\n    -\n      source: photographer\n      plugin: skip_on_empty\n      method: process\n    -\n      plugin: explode\n      delimiter: '|'\n    -\n      plugin: str_to_assoc\n      key: 'name'\n\n  # Iterate over the array of associative arrays.\n  # We create the taxonomy terms here so that we\n  # can specify the bundle - other columns which\n  # might feed into field_linked_agent may contain\n  # corporate bodies or families. The resulting\n  # array contains the resulting term id (tid)\n  # under the key 'target_id'.\n  # We also add a key-value pair\n  # 'rel_type' => 'relators:pht'. Other columns\n  # might use different relators.\n  linked_agent_pht:\n    plugin: sub_process\n    source: '@photographers'\n    process:\n      target_id:\n        plugin: entity_generate\n        source: name\n        entity_type: taxonomy_term\n        value_key: name\n        bundle_key: vid\n        bundle: person\n      rel_type:\n        plugin: default_value\n        default_value: 'relators:pht'\n\n  # Extract an array of names from the column\n  # called provider\n  providers:\n    -\n      source: provider\n      plugin: skip_on_empty\n      method: process\n    -\n      plugin: explode\n      delimiter: '|'\n    -\n      plugin: str_to_assoc\n      key: 'name'\n  # Generate/lookup taxonomy terms in the\n  # corporate body vocab, and add the relator.\n  linked_agent_prv:\n    plugin: sub_process\n    source: '@providers'\n    process:\n      target_id:\n        plugin: entity_generate\n        source: name\n        entity_type: taxonomy_term\n        value_key: name\n        bundle_key: vid\n        bundle: 'corporate_body'\n      rel_type:\n        plugin: default_value\n        default_value: 'relators:prv'\n\n  # Write to the linked agent field. In this case\n  # we first want to merge the info from the\n  # photographer and provider columns. Since we\n  # already prepared our structured array using\n  # the components of the typed_relation field as\n  # keys ('target_id' and 'rel_type'), we can just\n  # pass this array into field_linked_agent.\n  field_linked_agent:\n    plugin: merge\n    source:\n      - '@linked_agent_pht'\n      - '@linked_agent_prv'\n\n# We're making nodes\ndestination:\n  plugin: 'entity:node'\n  default_bundle: islandora_object\n
The Breakdown

The source section looks mostly the same other than some different constants we're defining - the string \"Image\" (no quotes needed) and the drupal ID of the user who will be assigned as the author. If values contained special characters such as colons, quotes would be needed.

If you look at the process section, you can see we're taking the title, description, and issued columns from the CSV and applying them directly to the migrated nodes without any manipulation.

  title: title\n  field_description: description\n  field_edtf_date: issued\n
For subtitle, we're passing it through the skip_on_empty process plugin because not every row in our CSV has a subtitle entry. It's very useful when you have spotty data, and you'll end up using it a lot. The method: process bit tells the migrate framework only skip that particular field if the value is empty, and not to skip the whole row. It's important, so don't forget it. The full YAML for setting field_alternative_title from subtitle looks like this:
  field_alternative_title:\n    plugin: skip_on_empty\n    source: subtitle\n    method: process\n

Now here's where things get interesting. We can look up other entities to populate entity reference fields. For example, all Repository Items have an entity reference field that holds a taxonomy term from the islandora_models vocabulary. All of our examples are images, so we'll look up the Image model in the vocabulary since it already exists (it gets made for you when you use islandora-playbook). We use the entity_lookup process plugin to do this.

  field_model:\n    plugin: entity_lookup\n    source: constants/model\n    entity_type: taxonomy_term\n    # 'name' is the string value of the term, e.g. 'Original file', 'Thumbnail'.\n    value_key: name\n    bundle_key: vid\n    bundle: islandora_models\n

The entity_lookup process plugin looks up an entity based on the configuration you give it. You use the entity_type, bundle_key, and bundle configurations to limit which entities you search through. entity_type is, as you'd suspect, the type of entity: node, media, file, taxonomy_term, etc... bundle_key tells the migrate framework which property holds the bundle of the entity, and bundle is the actual bundle id you want to restrict by. In this case we specify the vid (vocabulary id) has to be islandora_models - which is the machine name of the vocabulary we're interested in. In this plugin, source is the value to search for - in this case we're looking for the string \"Image\", which we've defined as a constant. And we're comparing it to the name field on each term by setting the value_key config.

This approach applies the same taxonomy term to all objects. If you want to assign a taxonomy term at the node level (that is, potentially a different term for each node) rather than to all the nodes being imported, you can use a configuration as illustrated next. In this example, we have a column in our CSV input file with the header 'model', which we define as the source of the field_model values:

  field_model:\n    plugin: entity_lookup\n    # 'model' is the header of a field in our input CSV that contains the string value of the taxonomy term.\n    source: model\n    entity_type: taxonomy_term\n    value_key: name\n    bundle_key: vid\n    bundle: islandora_models\n

If you're not sure that the entities you're looking up already exist, you can use the entity_generate plugin, which takes the same config, but will create a new entity if the lookup fails. We use this plugin to create subject taxonomy terms that we tag our nodes with. A node can have multiple subjects, so we've encoded them in the CSV as pipe delimited strings.

subject Neon signs|Night Neon signs|Night|Funny Neon signs|Night Drinking|Neon signs Neon signs

We can hack those apart easily enough. In PHP we'd do something like

$subjects = explode($string, '|');\n$terms = [];\nforeach ($subjects as $name) {\n    $terms[] = \\Drupal::service('entity_type.manager')->getStorage('taxonomy_term')->create([\n        ...\n        'vid' => 'subject',\n        'name' => $name,\n        ...\n    ]);\n}\n$node->set('field_subject', $terms);\n

With process plugins, that logic looks like

field_subject:\n    -\n      plugin: skip_on_empty\n      source: subject\n      method: process\n    -\n      plugin: explode\n      delimiter: '|'\n    -\n      plugin: entity_generate\n      entity_type: taxonomy_term\n      value_key: name\n      bundle_key: vid\n      bundle: subject\n
Here we've got a small pipeline that uses the skip_on_empty process plugin, which we've already seen, followed by explode. The explode process plugin operates exactly like its PHP counterpart, taking an array and a delimiter as input. The combination of skip_on_empty and explode behave like a foreach loop on the explode results. If we have an empty string, nothing happens. If there's one or more pipe delimited subject names in the string, then entity_generate gets called for each name that's found. The entity_generate process plugin will try to look up a subject by name, and if that fails, it creates one using the name and saves a reference to it in the node. So entity_generate is actually smarter than our pseudo-code above, because it can be run over and over again and it won't duplicate entities. :champagne:

"},{"location":"technical-documentation/migrate-csv/#complex-fields","title":"Complex Fields","text":"

Some fields don't hold just a single type of value. In other words, not everything is just text, numbers, or references. Using Drupal 8's Typed Data API, fields can hold groups of named values with different types. Consider a field that holds an RGB color. You could set it with PHP like so:

$node->set('field_color', ['R' => 255, 'G' => 255, 'B' => 255]);\n

You could even have a multi-valued color field, and do something like this

$node->set('field_color', [\n  ['R' => 0, 'G' => 0, 'B' => 0],\n  ['R' => 255, 'G' => 255, 'B' => 255],\n]);\n

In the migrate framework, you have two options for handling these types of fields. You can build up the full array they're expecting, which is difficult and requires a custom process plugin. Or, if you only have one value going into a complex field, you can set each named component in the field with separate process pipelines.

In controlled_access_terms, we define a new field type of typed_relation, which is an entity reference coupled with a MARC relator. It expects an associative array that looks like this:

[ 'target_id' => 1, 'rel_type' => 'relators:ctb']\n

The target_id portion takes an entity id, and rel_type takes the predicate for the MARC relator we want to use to describe the relationship the entity has with the repository item. This example would reference taxonomy_term 1 and give it the relator for \"Contributor\".

If we have a single name to deal with, we can set those values in YAML, accessing field_linked_agent/target_id and field_linked_agent/rel_type independently.

  field_linked_agent/target_id:\n    plugin: entity_generate\n    source: photographer\n    entity_type: taxonomy_term\n    value_key: name\n    bundle_key: vid\n    bundle: person\n\n  field_linked_agent/rel_type: constants/relator\n

Here we're looking at the photographer column in the CSV, which contains the names of the photographers that captured these images. Since we know these are photographers, and not publishers or editors, we can bake in the relator constant we set to relators:pht in the source section of the migration. So all that's left to do is to set the taxonomy term's id via entity_generate. If the lookup succeeds, the id is returned. If it fails, a term is created and its id is returned. In the end, by using the / syntax to set properties on complex fields, everything gets wrapped up into that nice associative array structure for you automatically.

However, if you have multiple names, and/or multiple columns that contain values that will go into the same typed_relation field, you do need to build up a full array of structured data.

We start by extracting the values in the photographer column, and busting them into a list. (In this case, given the sample data, the lists will all have length 1). Then, we use a custom process plugin to make each value the value in an associative array (see example data below).

  photographers:\n    -\n      source: photographer\n      plugin: skip_on_empty\n      method: process\n    -\n      plugin: explode\n      delimiter: '|'\n    -\n      plugin: str_to_assoc\n      key: 'name'\n

So, if we started with a column containing

'Alice|Bob|Charlie'\n
at the end of this pipeline, the photographers temporary variable would contain
[\n  ['name' => 'Alice'],\n  ['name' => 'Bob'],\n  ['name' => 'Charlie]\n]\n

Next, we use the sub_process plugin. It takes an array of associative arrays (as seen above) and iterates over them. From within subprocess' process parameter, you can access only what's defined in that associative array. Here, when we do our entity_generate lookup, our source is name, the (only) key in that array.

  linked_agent_pht:\n    plugin: sub_process\n    source: '@photographers'\n    process:\n      target_id:\n        plugin: entity_generate\n        source: name\n        entity_type: taxonomy_term\n        value_key: name\n        bundle_key: vid\n        bundle: person\n      rel_type:\n        plugin: default_value\n        default_value: 'relators:pht'\n
Within sub_process, we cannot access the temporary variables or constants that we've created in the outer migration. This is why we use the default_value plugin when for the rel_type. It would have been simpler to define a constant as we did with 'Image', but we wouldn't be able to access it. The output of this pipeline is now formatted as the structured data expected by a typed_relation field:
[\n  ['target_id' => 42, 'rel_type' => 'relators:pht' ],\n  ['target_id' => 43, 'rel_type' => 'relators:pht' ],\n  ['target_id' => 44, 'rel_type' => 'relators:pht' ],\n]\n
The final step will be to assign this array to the Contributor field (field_linked_agent). But first, we repeat the process for another column, which contains names that have a different relator, and a different bundle. Finally, we merge the two temporary variables and pass the result to field_linked_agent. We don't have to assign the sub-components of field_linked_agent here, because this is already the structured data it is looking for.

  field_linked_agent:\n    plugin: merge\n    source:\n      - '@linked_agent_pht'\n      - '@linked_agent_prv'\n

Clear as mud? Great. Now let's run that migration.

"},{"location":"technical-documentation/migrate-csv/#running-the-node-migration","title":"Running the node migration","text":"

Like with the file migration, run drush -y --userid=1 --uri=http://localhost:8000 migrate:import node from anywhere within the Drupal installation directory will fire off the migration. Go to http://localhost:8000/admin/content and you should see five new nodes. Click on one, though, and you'll see it's just a stub with metadata. The CSV metadata is there, links to other entities like subjects and photographers are there, but there's no trace of the corresponding files. Here's where media entities come into play.

"},{"location":"technical-documentation/migrate-csv/#migrating-media","title":"Migrating Media","text":"

Media entities are Drupal's solution for fieldable files. Since you can't put fields on a file, what you can do is wrap the file with a Media entity. In addition to a reference to the file (binary), technical metadata and structural metadata for the file go on the Media entity (e.g. MIME type, file size, resolution). Media also have a few special fields that are required for Islandora, field_media_of and field_use, which denote what node owns the media and what role the media serves, respectively. Since the Media entity references both the file it wraps and the node that owns it, Media entities act as a bridge between files and nodes, tying them together. And to do this, we make use of one last process plugin, migration_lookup. Open up /var/www/html/drupal/web/modules/contrib/migrate_islandora_csv/config/install/migrate_plus.migration.media.yml and give it a look.

# Uninstall this config when the feature is uninstalled\ndependencies:\n  enforced:\n    module:\n      - migrate_islandora_csv\n\nid: media\nlabel: Import Media from CSV\nmigration_group: migrate_islandora_csv\n\nsource:\n  plugin: csv\n  path: modules/contrib/migrate_islandora_csv/data/migration.csv\n\n  # 1 means you have a header row, 0 means you don't\n  header_row_count: 1\n\n  # Each migration needs a unique key per row in the csv.  Here we're using the file path.\n  ids:\n    - file\n\n  # You can't enter string literals into a process plugin, but you can give it a constant as a 'source'.\n  constants:\n    # We're tagging our media as Original Files\n    use: Original File\n\n    # Everything gets created as admin\n    uid: 1\n\nprocess:\n\n  name: title\n  uid: constants/uid\n\n  # Make the media an 'Original File'\n  field_media_use:\n    plugin: entity_lookup\n    source: constants/use\n    entity_type: taxonomy_term\n    value_key: name\n    bundle_key: vid\n    bundle: islandora_media_use\n\n  # Lookup the migrated file in the file migration.\n  field_media_image:\n    plugin: migration_lookup\n    source: file\n    migration: file\n    no_stub: true\n\n  # Lookup the migrated node in the node migration\n  field_media_of:\n    plugin: migration_lookup\n    source: file\n    migration: node\n    no_stub: true\n\ndestination:\n  # These are 'image' media we're making.\n  plugin: 'entity:media'\n  default_bundle: image\n\nmigration_dependencies:\n  required:\n    - migrate_plus.migration.file\n    - migrate_plus.migration.node\n  optional: {  }\n

The Breakdown

Compared to the other migrations, this one is very straightforward. There's no string or array manipulation in YAML, and at most there's only one process plugin per field. Title and user are set directly, with no processing required

  name: title\n  uid: constants/uid\n
The field_media_use field is a tag that's used to denote the purpose of a file with regard to the node it belongs to. E.g. is this the original file? a lower quality derivative? thumbnail? etc... In many ways it bears a resemblance to DSID in Islandora 7.x. Like field_model with nodes, the vocabulary already exists in your Islandora install, so all you have to do is look it up with the entity_lookup plugin.
  # Make the media an 'Original File'\n  field_media_use:\n    plugin: entity_lookup\n    source: constants/use\n    entity_type: taxonomy_term\n    value_key: name\n    bundle_key: vid\n    bundle: islandora_media_use\n
The field_media_image and field_media_of fields are how the media binds a file to a node. You could use entity_lookup or entity_generate, but we've already migrated them and can very easily look them up by the id assigned to them during migration. But what's the benefit of doing so? The entity_lookup and entity_generate process plugins do the job fine, right?

The main advantage of using migration_lookup and defining migrations whenever possible, is that migrated entities can be rolled back. If you were to hop into your console and execute

drush -y --uri=http://localhost:8000 migrate:rollback --group migrate_islandora_csv\n
Your nodes, media, and files would all be gone. But your subjects and photographers would remain. If you want to truly and cleanly roll back every entity in a migration, you need to define those migrations and use migration_lookup to set entity reference fields.

"},{"location":"technical-documentation/migrate-csv/#running-the-media-migration","title":"Running the media migration","text":"

Run drush -y --uri=http://localhost:8000 migrate:import media from anywhere within the Drupal installation directory. You should now be able to see the media files attached to the nodes you created earlier. At this point, you might want to create derivatives, such as thumbnails, using the appropriate Drupal actions on the main content admin window.

"},{"location":"technical-documentation/migrate-csv/#what-have-we-learned","title":"What have we learned?","text":"

If you've made it all the way to the end here, then you've learned that you can migrate files and CSV metadata into Islandora using only YAML files. You've seen how to transform data with pipelines of processing plugins and can handle numeric, text, and entity reference fields. You can handle multiple values for fields, and even more complicated things like typed_relation fields. And as big as this walkthrough was, we're only scratching the surface of what can be done with the Migrate API.

"},{"location":"technical-documentation/migrate-csv/#where-to-go-from-here","title":"Where to go from here?","text":"

There's certainly more you can do with Drupal 8's Migrate API. There's a plethora of source and processing plugins out there that can handle pretty much anything you throw at it. XML and JSON are fair game. You can also request sources using HTTP, so you can always point it at an existing systems REST API and go from there. If you can't make the Migrate API's existing workflow make the necessary changes to your data, you can expand its capabilities by writing your own process plugins. Reading the Drupal.org documentation on the Migrate API would be a good place to start.

But really the best thing to do is try and get your data into Islandora! We intend to create a boilerplate branch of this repository that will allow you to clone down a migration template, ready for you to customize to fit your data. And as you assemble it into CSV format, keep in mind that if you have more than just names for things like subjects and authors, that you can always make more CSVs. Think of it like maintaining tables in an SQL database. Each CSV has unique keys, so you can lookup/join entities between CSVs using those keys. And you can still pipe-delimit the keys like we did in our example to handle multi-valued fields.

In some repositories, these CSVs can be used to make bulk updates to metadata. Just make your changes to maintain the CSVs, then run the migration(s) again with the --update flag. This will not always be efficient, as you'll update every entity, even if it didn't change. But, by breaking down your CSVs per collection or object type, you could keep them small enough to use this process for a small repository.

There is also a tool for migrating directly from a Legacy Islandora to Islandora (migrate_7x_claw), using Solr, XPaths, and Fedora calls to pull files and objects directly into Islandora. It may be worth checking out, and/or using in conjunction with a CSV migration.

"},{"location":"technical-documentation/migration-islandora-workbench/","title":"Islandora Workbench","text":""},{"location":"technical-documentation/migration-islandora-workbench/#islandora-workbench","title":"Islandora Workbench","text":"

Islandora Workbench provides a command-line solution to create, update, and delete Islandora content from CSV data. The Islandora Workbench repository can be found on Github. Full details and documentation for Islandora Workbench are maintained separately (Islandora Workbench Documentation).

"},{"location":"technical-documentation/migration-islandora-workbench/#overview","title":"Overview","text":"

Islandora Workbench is tailored towards end users with less technical knowledge or limited server access.

Workbench provides an alternative to Drupal's Migrate framework, as it does not need to be run on the Drupal server. Islandora Workbench uses Islandora's REST API and offers cross-platform support (Windows, Mac, Linux) to run on your computer, using your provided CSVs and files.

"},{"location":"technical-documentation/migration-islandora-workbench/#islandora-workbench-highlights","title":"Islandora Workbench highlights","text":"

Islandora Workbench:

  • Enables you to perform write operations: Create, update, and delete content.
  • Is opinionated: Workbench provides MUCH less configuration than Drupal's Migrate framework. Decisions are made for you.
  • Does not provide pre-processing: Your CSVs have to be in the right format. More information on preparing your data can be found in the Islandora Workbench documentation.
  • Provides data validation:: The YAML configuration file and CSVs you provide are validated with the --check option.
"},{"location":"technical-documentation/migration-islandora-workbench/#islandora-workbench-basics","title":"Islandora Workbench Basics","text":"
  • Column names are field names.
  • If a cell value contains a comma, make sure the value is wrapped in double quotes. Spreadsheet applications will do this for you
  • Multiple values are pipe delimited.
  • Entity references are done via numeric id (nid, mid, tid).
"},{"location":"technical-documentation/migration-islandora-workbench/#taxonomy-terms","title":"Taxonomy Terms","text":"

Using Islandora Workbench, you can assign both existing and new taxonomy terms to nodes. Within the CSVs you provide, the values of the taxonomy field/columns can:

  • Use term IDs (integers), term names, or both. For example:
    • 26
    • Cats
    • 26|Cats
  • Use multiple vocabularies, by prefixing the value with the vocabulary id:
    • cats:Calico|dogs:Dachshund
  • Create new terms that don't exist yet in your taxonomy.

If you need to create terms with multiple fields (such as an External URI) or which use term hierarchy, you can create terms in a separate task.

"},{"location":"technical-documentation/migration-islandora-workbench/#other-field-types","title":"Other Field Types","text":"
  • Typed Relations - Prefix term ids with namespace:rel:. More available on typed relation fields here. For example:
    • relators:pht:30
    • Relators:pht:30|relators:pub:45
  • Geolocation fields - Workbench allows geocoordinates to be provides in \u201cLat,Long\u201d format. For example:
    • \"49.16667,-123.93333\"
"},{"location":"technical-documentation/migration-islandora-workbench/#paged-content","title":"Paged Content","text":"

There are multiple ways to create paged content with Islandora Workbench. More information on each option is available here. You may:

  • Use a specific subdirectory structure to define the relationship between the parent item and its children.
  • Use page-level metadata in the CSV to create the relationship.
  • Create a secondary task in Workbench.
"},{"location":"technical-documentation/migration-islandora-workbench/#workbench-instructional-videos","title":"Workbench Instructional Videos","text":"

Click the video previews shown below to open the corresponding video in Youtube.

"},{"location":"technical-documentation/migration-islandora-workbench/#islandoracon-2022-migration-strategies","title":"IslandoraCon 2022: Migration Strategies","text":"

This video (August 2022) is an overview of how to harvest data from an existing Islandora 7 site using Workbench, how to sanitize and prep that data with custom Python tools, and how to pull that data into a new Islandora 2 site.

"},{"location":"technical-documentation/migration-islandora-workbench/#summer-of-islandora-workbench-introduction-to-islandora-workbench","title":"Summer of Islandora Workbench: Introduction to Islandora Workbench","text":"

This video (July 29, 2021) is an introduction of Islandora Workbench.

"},{"location":"technical-documentation/migration-islandora-workbench/#islandora-workbench-demo","title":"Islandora Workbench Demo","text":"

This video (Dec 17, 2020) is a demo of Islandora Workbench.

"},{"location":"technical-documentation/migration-islandora-workbench/#islandora-online-islandora-migration-tools","title":"Islandora Online: Islandora Migration Tools","text":"

This video (Aug 10, 2020) provides an overview of the Islandora Workbench and the two other options available to migrate data into an Islandora installation.

"},{"location":"technical-documentation/migration-migrate-api/","title":"Migrate API","text":""},{"location":"technical-documentation/migration-migrate-api/#migrate-api","title":"Migrate API","text":"

Uses the Drupal Migrate API, which \"provides services for migrating data from a source system\" to Drupal 8, 9, or 10.

The \"source system\" can be almost anything:

  • an Islandora Legacy system
  • a group of scanned images and their metadata inside a CSV file
  • a web API

Why use the Migrate API?

  • You can (potentially) do everything with configs!
  • Leverage contrib module plugins.
  • Making plugins for more complex sources and processes is (relatively) simple.
  • Updating metadata is as simple as: drush mim node --update
"},{"location":"technical-documentation/migration-migrate-api/#a-migration-configuration-defines-an-extract-transform-load-etl-process","title":"A Migration Configuration defines an Extract, Transform, Load (ETL) process","text":"
  • Source plugins extract data from a source
  • Process plugins transform the data
  • Destination plugins load the data (create new entities)
"},{"location":"technical-documentation/migration-migrate-api/#weve-built-two-tools-for-you-using-the-migrate-api","title":"We\u2019ve built two tools for you using the Migrate API","text":"
  • migrate_islandora_csv
    • https://github.com/Islandora/migrate_islandora_csv
      • Tutorial with a sample migration using some files and a CSV
    • Documentation section on migrate_islandora_csv
  • migrate_7x_claw
    • https://github.com/Islandora-Devops/migrate_7x_claw
    • A tool to get all your Islandora Legacy content migrated over
    • Documentation section on migrate_7x_claw
"},{"location":"technical-documentation/migration-migrate-api/#recap-of-migrate_islandora_csv","title":"Recap of migrate_islandora_csv","text":"
  • CSVs
    • Everyone understands and knows how to work with CSVs
  • Documented
    • It\u2019s a step-by-step walkthrough
  • Process Metadata
    • Clean up / transform the metadata using processors
  • Build Relationships
    • Migrations can reference other migrated content or generate new content on the fly
"},{"location":"technical-documentation/migration-migrate-api/#recap-of-migrate_7x_claw","title":"Recap of migrate_7x_claw","text":"
  • Designed to migrate Islandora Legacy data to Islandora.
  • DATASTREAMS
    • All of your datastreams, including the audit trail, are migrated
  • METADATA
    • Migrate metadata from Solr or any XML datastream
  • CUSTOMIZABLE
    • Migrate_7x_claw is a starting point, meant to be tailored to your metadata
"},{"location":"technical-documentation/migration-migrate-api/#to-make-migrate_7x_claw-work-you-need","title":"To make migrate_7x_claw work you need","text":"
  • Access
    • You need credentials to both your Islandora Legacy and Islandora installs.
  • Migrate API Knowledge
    • The tutorial for migrate_islandora_csv Is still relevant
  • Config Sync
    • You need to understand Drupal config synchronization. Features knowledge helps too.
  • Command Line Skills
    • This is best done with shell access and drush
"},{"location":"technical-documentation/migration-migrate-api/#migrate-api-demo-video","title":"Migrate API demo video","text":"

Check out this video that demonstrates the Drupal Migrate API migration process: Islandora Webinar: Migrating from Islandora Legacy to Islandora (Nov 21, 2019)

"},{"location":"technical-documentation/migration-overview/","title":"Migration Overview","text":""},{"location":"technical-documentation/migration-overview/#migration-and-batch-loading","title":"Migration and Batch Loading","text":"

This video (Aug 10, 2020) provides an overview of the various options available to migrate data into an Islandora installation.

The three main migration options are:

  • REST API
  • Migrate API
    • migrate_islandora_csv
    • migrate_7x_claw
  • Islandora Workbench
"},{"location":"technical-documentation/migration-overview/#rest-api","title":"REST API","text":"

Why use the rest API?

  • Works anywhere: You don\u2019t have to work on the Drupal server. Migrate from your laptop!
  • No PHP required: Use any language that can make an http request. Even cURL will do just fine.
  • JSON: Why use XML if you don\u2019t have to?
  • Relies on Drupal\u2019s own REST API

Visit the REST API migration documentation section for more details.

"},{"location":"technical-documentation/migration-overview/#migrate-api","title":"Migrate API","text":"

Why use the Migrate API?

  • You can (potentially) do everything with configs!
  • Leverage contrib module plugins.
  • Making plugins for more complex sources and processes is (relatively) simple.
  • Updating metadata is as simple as: drush mim node --update

Two tools that use the Migrate API are migrate_islandora_csv and migrate_7x_claw.

Visit the Migrate API migration documentation section for more details.

"},{"location":"technical-documentation/migration-overview/#islandora-workbench","title":"Islandora Workbench","text":"

Why use the Migrate API?

  • More tailored for end users with less technical knowledge or limited server access.
  • Uses Islandora\u2019s REST API
  • Runs on your computer
  • \u201cCSVs and a pile of scans\u201d
  • Cross Platform - Python

Visit the Islandora Workbench migration documentation section for more details.

"},{"location":"technical-documentation/migration-rest-api/","title":"REST API","text":""},{"location":"technical-documentation/migration-rest-api/#rest-api","title":"REST API","text":"

Why use the REST API?

  • Works anywhere: You don\u2019t have to work on the Drupal server. Migrate from your laptop!
  • No PHP required: Use any language that can make an HTTP request. Even cURL will do just fine.
  • JSON: Why use XML if you don\u2019t have to?

BONUS: It\u2019s just Drupal\u2019s REST API

"},{"location":"technical-documentation/migration-rest-api/#islandora-only-provides-two-additional-api-endpoints","title":"Islandora only provides two additional API endpoints","text":"
  • /media/{mid}/source
    • PUT a file to this endpoint to create/update a Media\u2019s file
  • /node/{nid}/media/{media_type}/{taxonomy_term}
    • PUT a file to this endpoint to create/update a Media for a Node

Just be aware, you are writing everything yourself! (In other words you are making all of the migration decisions yourself.)

"},{"location":"technical-documentation/migration-rest-api/#videos","title":"Videos","text":"

This video (Aug 10, 2020) provides an overview of the REST API and the two other options available to migrate data into an Islandora installation.

"},{"location":"technical-documentation/ppa-documentation/","title":"Updating a deb and adding it to LYRASIS PPA","text":""},{"location":"technical-documentation/ppa-documentation/#this-page-is-here-for-archival-purposes","title":"This page is here for archival purposes.","text":"

If you are running Ubuntu 20.04 or higher, installing a specially-compiled imagemagick is no longer necessary. We are leaving this page available in case in the future we have a need to package our own deb and add it to the Lyrasis PPA.

"},{"location":"technical-documentation/ppa-documentation/#background","title":"Background","text":"

Ubuntu removed JPEG 2000 support from ImageMagick since Vivid Vervet's 8:6.8.9.9-5 version. The PPA that we have created adds JPEG 2000 support back into ImageMagick for Ubuntu 16.04 and 18.04. More information on why JPEG 2000 support was removed can be found here, and more information on openjpeg in Ubuntu can be found here.

"},{"location":"technical-documentation/ppa-documentation/#prerequisites","title":"Prerequisites","text":"

Review the Ubuntu Packaging Guide. Key items needed are in the Getting Set Up section:

  • Basic packaging software:
  • sudo apt install gnupg pbuilder ubuntu-dev-tools apt-file
  • Make sure you have your GPG, if you do not, follow the instructions in the guide.
  • Setup pbuilder:
  • pbuilder-dist bionic create
  • Configure your shell with some exports for debuild:
  • export DEBFULLNAME=\"Bob Dobbs\"
  • export DEBEMAIL=\"subgenius@example.com\"
  • Contact Jonathan Green to get setup on the LYRASIS PPA team.
"},{"location":"technical-documentation/ppa-documentation/#patching-source","title":"Patching source","text":"

imagemagick is used an example. If you need to only change or patch the actual source code, then you will need to use quilt. More information on using quilt can be found in the Patches to Packages section. If you are only change dependencies, or information in the debian directory of a package, quilt is not required, and if used will cause build failures.

  • Create a directory to work in:
  • mkdir imagemagick-bionic-jp2
  • cd imagemagick-bionic-jp2
  • Pull down the source:
  • pull-lp-source imagemagick bionic
  • cd imagemagick-6.9.7.4+dfsg
  • Edit files as needed
  • Document the fix:
  • dch (Make sure to change UNRELEASED to the Ubuntu release name. For example: bionic)
  • Build the package:
  • debuild -S
"},{"location":"technical-documentation/ppa-documentation/#example-patch","title":"Example Patch","text":"
Index: imagemagick-6.9.7.4+dfsg/debian/control\n===================================================================\n--- imagemagick-6.9.7.4+dfsg.orig/debian/control\n+++ imagemagick-6.9.7.4+dfsg/debian/control\n@@ -26,8 +26,7 @@ Build-Depends: debhelper (>= 10),\n libx11-dev, libxext-dev, libxt-dev,\n # for plugins\n  ghostscript, libdjvulibre-dev, libexif-dev,\n- libjpeg-dev,\n-# libopenjp2-7-dev, Needed for JPEG 2000 but not in main see MIR #711061\n+ libjpeg-dev, libopenjp2-7-dev,\n libopenexr-dev, libperl-dev, libpng-dev, libtiff-dev,\n  libwmf-dev,\n # libgraphviz-dev, incompatible license against fftw\n@@ -273,8 +272,7 @@ Depends: libmagickcore-6-headers (= ${so\n libmagickcore-6.q16-3 (= ${binary:Version}),\n  libmagickcore-6.q16-3-extra (= ${binary:Version}),\n  libbz2-dev, libdjvulibre-dev,\n- libexif-dev, libfreetype6-dev, libjpeg-dev,\n-# libopenjp2-7-dev, Needed for JPEG 2000 but not in main see MIR #711061\n+ libexif-dev, libfreetype6-dev, libjpeg-dev, libopenjp2-7-dev,\n liblcms2-dev, liblqr-1-0-dev, libltdl-dev, libopenexr-dev, libpng-dev,\n  librsvg2-dev, libtiff-dev, libwmf-dev, libx11-dev, libxext-dev, libxml2-dev,\n  libxt-dev, zlib1g-dev,\n@@ -483,8 +481,7 @@ Depends: libmagickcore-6-headers (= ${so\n libmagickcore-6.q16hdri-3 (= ${binary:Version}),\n  libmagickcore-6.q16hdri-3-extra (= ${binary:Version}),\n  libbz2-dev, libdjvulibre-dev,\n- libexif-dev, libfreetype6-dev, libjpeg-dev,\n-# libopenjp2-7-dev, Needed for JPEG 2000 but not in main see MIR #711061\n+ libexif-dev, libfreetype6-dev, libjpeg-dev, libopenjp2-7-dev,\n liblcms2-dev, liblqr-1-0-dev, libltdl-dev, libopenexr-dev, libpng-dev,\n  librsvg2-dev, libtiff-dev, libwmf-dev, libx11-dev, libxext-dev, libxml2-dev,\n  libxt-dev, zlib1g-dev,\nIndex: imagemagick-6.9.7.4+dfsg/debian/control.d/noquantum.in\n===================================================================\n--- imagemagick-6.9.7.4+dfsg.orig/debian/control.d/noquantum.in\n+++ imagemagick-6.9.7.4+dfsg/debian/control.d/noquantum.in\n@@ -26,8 +26,7 @@ Build-Depends: debhelper (>= 10),\n libx11-dev, libxext-dev, libxt-dev,\n # for plugins\n  ghostscript, libdjvulibre-dev, libexif-dev,\n- libjpeg-dev,\n-# libopenjp2-7-dev, Needed for JPEG 2000 but not in main see MIR #711061\n+ libjpeg-dev, libopenjp2-7-dev,\n libopenexr-dev, libperl-dev, libpng-dev, libtiff-dev,\n  libwmf-dev,\n # libgraphviz-dev, incompatible license against fftw\nIndex: imagemagick-6.9.7.4+dfsg/debian/control.d/quantum.in\n===================================================================\n--- imagemagick-6.9.7.4+dfsg.orig/debian/control.d/quantum.in\n+++ imagemagick-6.9.7.4+dfsg/debian/control.d/quantum.in\n@@ -78,8 +78,7 @@ Depends: libmagickcore-${IMVERSION}-head\n libmagickcore-${IMVERSION}.${QUANTUMDEPTH}-${CORESOVERSION} (= ${binary:Version}),\n  libmagickcore-${IMVERSION}.${QUANTUMDEPTH}-${CORESOVERSION}-extra (= ${binary:Version}),\n  libbz2-dev, libdjvulibre-dev,\n- libexif-dev, libfreetype6-dev, libjpeg-dev,\n-# libopenjp2-7-dev, Needed for JPEG 2000 but not in main see MIR #711061\n+ libexif-dev, libfreetype6-dev, libjpeg-dev, libopenjp2-7-dev,\n liblcms2-dev, liblqr-1-0-dev, libltdl-dev, libopenexr-dev, libpng-dev,\n  librsvg2-dev, libtiff-dev, libwmf-dev, libx11-dev, libxext-dev, libxml2-dev,\n  libxt-dev, zlib1g-dev,\nIndex: imagemagick-6.9.7.4+dfsg/debian/rules\n===================================================================\n--- imagemagick-6.9.7.4+dfsg.orig/debian/rules\n+++ imagemagick-6.9.7.4+dfsg/debian/rules\n@@ -98,7 +98,7 @@ STATIC_CONFIGURE_OPTIONS := \\\n   --with-gs-font-dir=/usr/share/fonts/type1/gsfonts \\\n    --with-magick-plus-plus \\\n    --with-djvu \\\n-        --with-openjp2 \\\n+  --with-openjp2 \\\n   --with-wmf \\\n    --without-gvc \\\n    --enable-shared \\\n
"},{"location":"technical-documentation/ppa-documentation/#uploading-to-lyrasis-ppa","title":"Uploading to LYRASIS PPA","text":"

Once the package is built successfully, you'll upload the changes file to Launchpad. For example:

  • dput ppa:lyrasis/imagemagick-jp2 imagemagick_6.9.7.4+dfsg-16ubuntu6.8_source.changes

After the package is successfully uploaded to the PPA, you'll receive an email from Launchpad. Something like this:

Accepted:\n OK: imagemagick_6.9.7.4+dfsg.orig.tar.xz\n OK: imagemagick_6.9.7.4+dfsg-16ubuntu6.8.debian.tar.xz\n OK: imagemagick_6.9.7.4+dfsg-16ubuntu6.8.dsc\n     -> Component: main Section: graphics\n\nimagemagick (8:6.9.7.4+dfsg-16ubuntu6.8) bionic; urgency=medium\n\n  * Add jp2 support.\n

And you will see it in the interface for the Lyrasis PPA:

"},{"location":"technical-documentation/resizing-vm/","title":"Resizing a VM","text":""},{"location":"technical-documentation/resizing-vm/#resize-vagrant-machine","title":"Resize vagrant machine","text":"

To expand virtual machine's hard drive for testing of larger files. Once the VM has started, you'll need to halt the VM, download and run the script, tell it what size (in MB) and then start the VM. The last step vagrant ssh --command \"sudo resize2fs /dev/sda1\" is a check. It should return there was nothing to do. If you already provisioned you VM you can skip the 2 steps with provisioning in them.

# Skip this if you VM is already provisioned.\n$ vagrant up --no-provision  <-- Exclude if already running and provisioned.\n\n$ vagrant halt\n\n# Download and run. This will default to the correct name (just press enter) then give the size.\n# Example: `350000` is equal to 350GB\n$ wget https://gist.githubusercontent.com/DonRichards/6dc6c81ae9fc22cba8d7a57b90ab1509/raw/45017e07a3b93657f8822dfbbe4fc690169cdabc/expand_disk.py\n$ chmod +x expand_disk.py\n$ python expand_disk.py\n$ vagrant up --no-provision\n\n# This step isn't needed but acts as a check to verify it worked.\n$ vagrant ssh --command \"sudo resize2fs /dev/sda1\"\n# Skip this if you VM is already provisioned.\n$ vagrant provision           <-- Exclude if already provisioned.\n
"},{"location":"technical-documentation/resizing-vm/#troubleshooting-expand_diskpy","title":"Troubleshooting expand_disk.py","text":"

You may need to remove the \"resized\" version. Assuming your VM location is ~/VirtualBox\\ VMs

$ rm -rf ~/VirtualBox\\ VMs/Islandora\\ CLAW\\ Ansible_resized\n

"},{"location":"technical-documentation/rest-authorization/","title":"Authorization","text":""},{"location":"technical-documentation/rest-authorization/#rest-authorization","title":"REST Authorization","text":"

If your resources are restricted (hidden) you will need to have authorization to access them.

You can specify which types of authentication are allowed for which HTTP methods.

These are common to all HTTP methods against the REST API.

In the above screenshot we have 3 allowed methods. 1. basic_auth 1. jwt_auth 1. cookie

"},{"location":"technical-documentation/rest-authorization/#basic-authentication-basic_auth","title":"Basic authentication (basic_auth)","text":"

To use basic authentication with a client like cURL use the -u username:password argument.

For example:

curl -u admin:islandora http://localhost:8000/node/3\n

"},{"location":"technical-documentation/rest-authorization/#jwt-authentication-jwt_auth","title":"JWT authentication (jwt_auth)","text":"

By default JWTs are passed internally from Drupal to various microservices and Fedora.

To use a JWT yourself you need to enable the JWT Authentication Issuer module.

Once enabled this module makes a /jwt/token endpoint. You can perform a GET against this endpoint as an authenticated user to receive a JWT.

For example:

curl -i -u admin:islandora http://localhost:8000/jwt/token\n\nHTTP/1.1 200 OK\nDate: Mon, 04 Mar 2019 22:08:37 GMT\nServer: Apache/2.4.18 (Ubuntu)\nX-Powered-By: PHP/7.1.26-1+ubuntu16.04.1+deb.sury.org+1\nCache-Control: must-revalidate, no-cache, private\nX-UA-Compatible: IE=edge\nContent-language: en\nX-Content-Type-Options: nosniff\nX-Frame-Options: SAMEORIGIN\nExpires: Sun, 19 Nov 1978 05:00:00 GMT\nVary:\nX-Generator: Drupal 8 (https://www.drupal.org)\nContent-Length: 620\nContent-Type: application/json\n\n{\n  \"token\" : \"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE1NTE3MzczMTcsImV4cCI6MTU1MTc0NDUxNywiZHJ1cGFsIjp7InVpZCI6IjEifSwid2ViaWQiOiIxIiwiaXNzIjoiaHR0cHM6XC9cL2xvY2FsaG9zdDo4MDAwIiwic3ViIjoiYWRtaW4iLCJyb2xlcyI6WyJhdXRoZW50aWNhdGVkIiwiYWRtaW5pc3RyYXRvciIsImZlZG9yYWFkbWluIl19.QUTrMiK_DyBxqQY4LnibLYtieEW3-MyjjQO9NSFI7bPylNm1S5ZY0uvzjDob3ckYgRN4uCyMFZO4BPytpQVA_jyeSuZyUA_10v33ItpoKyjrJ_S057iykNd_rWmxe8tT8T1fPypq_-Z7Th_PkyZWrYBqoBBVO1iVQt5txxfGWMqhxd2FgsXw6N-aR9sYOSc4UrLmFRmPP5Zk_CNIZP6NtBaM9JNr8CnWyPEFSAR75xfyH3ge5qjBqLlDS389k07pyJFB5rOT59txzLE9WLvpp9JK3oQv821Q1Bp-PMiASghXc0dBCHxM8o41BzLE88UstRA7agBAkUqG3hMpoNZqfA\"\n}\n

You can then take the same token and re-use it.

curl -H\"Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE1NTE3MzczMTcsImV4cCI6MTU1MTc0NDUxNywiZHJ1cGFsIjp7InVpZCI6IjEifSwid2ViaWQiOiIxIiwiaXNzIjoiaHR0cHM6XC9cL2xvY2FsaG9zdDo4MDAwIiwic3ViIjoiYWRtaW4iLCJyb2xlcyI6WyJhdXRoZW50aWNhdGVkIiwiYWRtaW5pc3RyYXRvciIsImZlZG9yYWFkbWluIl19.QUTrMiK_DyBxqQY4LnibLYtieEW3-MyjjQO9NSFI7bPylNm1S5ZY0uvzjDob3ckYgRN4uCyMFZO4BPytpQVA_jyeSuZyUA_10v33ItpoKyjrJ_S057iykNd_rWmxe8tT8T1fPypq_-Z7Th_PkyZWrYBqoBBVO1iVQt5txxfGWMqhxd2FgsXw6N-aR9sYOSc4UrLmFRmPP5Zk_CNIZP6NtBaM9JNr8CnWyPEFSAR75xfyH3ge5qjBqLlDS389k07pyJFB5rOT59txzLE9WLvpp9JK3oQv821Q1Bp-PMiASghXc0dBCHxM8o41BzLE88UstRA7agBAkUqG3hMpoNZqfA\" http://localhost:8000/node/3?_format=jsonld\n\nHTTP/1.1 200 OK\nDate: Mon, 04 Mar 2019 22:10:02 GMT\nServer: Apache/2.4.18 (Ubuntu)\nX-Powered-By: PHP/7.1.26-1+ubuntu16.04.1+deb.sury.org+1\nCache-Control: must-revalidate, no-cache, private\nLink: <http://localhost:8000/node/3>; rel=\"canonical\"\nLink: <http://localhost:8000/node/3/delete>; rel=\"https://drupal.org/link-relations/delete-form\"\nLink: <http://localhost:8000/admin/content/node/delete?node=3>; rel=\"https://drupal.org/link-relations/delete-multiple-form\"\nLink: <http://localhost:8000/node/3/edit>; rel=\"edit-form\"\nLink: <http://localhost:8000/node/3/revisions>; rel=\"version-history\"\nLink: <http://localhost:8000/node/3>; rel=\"https://drupal.org/link-relations/revision\"\nLink: <http://localhost:8000/node?node=3>; rel=\"https://drupal.org/link-relations/create\"\nLink: <http://purl.org/coar/resource_type/c_c513>; rel=\"tag\"; title=\"Image\"\nLink: <http://localhost:8000/media/1>; rel=\"related\"; title=\"Original File\"\nLink: <http://localhost:8000/media/2>; rel=\"related\"; title=\"Service File\"\nLink: <http://localhost:8000/media/3>; rel=\"related\"; title=\"Thumbnail Image\"\nLink: <http://localhost:8000/node/3?_format=json>; rel=\"alternate\"; type=\"application/json\"\nX-Drupal-Dynamic-Cache: HIT\nX-UA-Compatible: IE=edge\nContent-language: en\nX-Content-Type-Options: nosniff\nX-Frame-Options: SAMEORIGIN\nExpires: Sun, 19 Nov 1978 05:00:00 GMT\nVary:\nX-Generator: Drupal 8 (https://www.drupal.org)\nContent-Length: 858\nContent-Type: application/ld+json\n\n{\n  \"@graph\": [\n    {\n      \"@id\": \"http:\\\\/\\\\/localhost:8000\\\\/node\\\\/3?_format=jsonld\",\n      \"@type\": [\n        \"http:\\\\/\\\\/pcdm.org\\\\/models#Object\"\n      ],\n      \"http:\\\\/\\\\/purl.org\\\\/dc\\\\/terms\\\\/title\": [\n        {\n          \"@value\": \"Custom item\",\n          \"@language\": \"en\"\n        }\n      ],\n      \"http:\\\\/\\\\/schema.org\\\\/author\": [\n        {\n          \"@id\": \"http:\\\\/\\\\/localhost:8000\\\\/user\\\\/1?_format=jsonld\"\n        }\n      ],\n      \"http:\\\\/\\\\/schema.org\\\\/dateCreated\": [\n        {\n          \"@value\": \"2019-03-01T19:42:54+00:00\",\n          \"@type\": \"http:\\\\/\\\\/www.w3.org\\\\/2001\\\\/XMLSchema#dateTime\"\n        }\n      ],\n      \"http:\\\\/\\\\/schema.org\\\\/dateModified\": [\n        {\n          \"@value\": \"2019-03-01T19:43:12+00:00\",\n          \"@type\": \"http:\\\\/\\\\/www.w3.org\\\\/2001\\\\/XMLSchema#dateTime\"\n        }\n      ],\n      \"http:\\\\/\\\\/purl.org\\\\/dc\\\\/terms\\\\/extent\": [\n        {\n          \"@value\": \"1 item\",\n          \"@type\": \"http:\\\\/\\\\/www.w3.org\\\\/2001\\\\/XMLSchema#string\"\n        }\n      ],\n      \"http:\\\\/\\\\/schema.org\\\\/sameAs\": [\n        {\n          \"@value\": \"http:\\\\/\\\\/localhost:8000\\\\/node\\\\/1?_format=jsonld\"\n        }\n      ]\n    },\n    {\n      \"@id\": \"http:\\\\/\\\\/localhost:8000\\\\/user\\\\/1?_format=jsonld\",\n      \"@type\": [\n        \"http:\\\\/\\\\/schema.org\\\\/Person\"\n      ]\n    }\n  ]\n}\n
"},{"location":"technical-documentation/rest-authorization/#cookie-authentication-cookie","title":"Cookie authentication (cookie)","text":"

This allows you to use a cookie stored in your web browser when you log in to Drupal to access these REST endpoint pages.

This is what allows you to access the URIs like http://localhost:8000/node/1?_format=json with your web browser.

"},{"location":"technical-documentation/rest-create/","title":"POST/PUT","text":""},{"location":"technical-documentation/rest-create/#creating-resources-media-and-files-postput-requests","title":"Creating resources, media and files - POST/PUT Requests","text":"
  • Authorization
  • Content
  • Files and Media
"},{"location":"technical-documentation/rest-create/#authorization","title":"Authorization","text":"

You will need to use one of the configured authorization methods to create content, media and/or files.

These are defined under Authorization on the overview.

"},{"location":"technical-documentation/rest-create/#content-nodes","title":"Content (Nodes)","text":"

The above setup shows a configuration where the JSON format is enabled for GET, PATCH, DELETE, and POST, with authentication types \"basic_auth\" and \"jwt_auth\" enabled for each method. Thus, with this configuration, you can perform a POST request against a node at the /node endpoint with a body in the JSON format.

To create a node, you need to provide two elements in your message body: the node type and any required field values.

For the Repository Item content type included in the Islandora Starter Site, these are:

  1. A type - this tells Drupal what content type we are creating
  2. A title - this is a required field of all nodes.
  3. A model - this is a required by Islandora to tell the type of object (i.e. Image, Audio, Collection)

A good way to make your first POST request is to perform a GET request against an existing node and erase all the extra content.

You can find more information about GET requests here.

Again we are using the json format.

\ud83c\udf4e For example curl -X GET 'http://localhost:8000/node/3?_format=json

Look for the type element

\"type\" : [\n  {\n    \"target_id\" : \"islandora_object\",\n    \"target_type\" : \"node_type\",\n    \"target_uuid\" : \"62189bec-3ef3-4196-b847-b17e5ce61fd5\"\n  }\n]\n

In our example \"islandora_object\" is the machine name of the content type \"Repository Item\". If you have created a new type you will have a different target_id.

You will not need the target_uuid.

Next look for the title element

\"title\" : [\n  {\n    \"value\" : \"An example Islandora object\"\n  }\n]\n

Lastly look for the field_model element

\"field_model\": [\n  {\n    \"target_id\": 24,\n    \"target_type\": \"taxonomy_term\",\n    \"target_uuid\": \"e7560b68-e95a-4e76-9671-2a3041cd9800\",\n    \"url\": \"\\\\/taxonomy\\\\/term\\\\/24\"\n  }\n]\n

You can find the models by browsing the taxonomy terms available at http://localhost:8000/admin/structure/taxonomy/manage/islandora_models/overview

In my example installation, term 24 is an \"Image\", but let's create a collection which is term 23.

Note: Taxonomy terms may vary between instances and you should verify the correct number for your installation.

So the body of the request will be:

{\n  \"type\": [\n    {\n      \"target_id\": \"islandora_object\",\n      \"target_type\": \"node_type\"\n    }\n  ],\n  \"title\": [\n    {\n      \"value\": \"Created a collection with POST\"\n    }\n  ],\n  \"field_model\": [\n    {\n      \"target_id\": 23,\n      \"target_type\": \"taxonomy_term\"\n    }\n  ]\n}\n

Note: You must include an appropriate Content-type header for the format you're requesting

Other Note: You must include some authentication credentials to say who you are and so Drupal can check if you are allowed to create this object. Otherwise you will receive a 401 Unauthorized response.

If you do provide credentials but don't have permission, you will receive a 403 Forbidden response.

You can find more information about Authorization here

\ud83c\udf4e For example:

curl -i -X POST -u admin:islandora -H\"Content-type: application/json\" --data '{\"type\":[{\"target_id\":\"islandora_object\",\"target_type\":\"node_type\"}],\"title\":[{\"value\":\"Created a collection with POST\"}],\"field_model\":[{\"target_id\":23,\"target_type\":\"taxonomy_term\"}]}' 'http://localhost:8000/node?_format=json'\n\nHTTP/1.1 201 Created\nDate: Tue, 05 Mar 2019 18:07:00 GMT\nServer: Apache/2.4.18 (Ubuntu)\nX-Powered-By: PHP/7.1.26-1+ubuntu16.04.1+deb.sury.org+1\nLocation: http://localhost:8000/node/3\nCache-Control: must-revalidate, no-cache, private\nX-UA-Compatible: IE=edge\nContent-language: en\nX-Content-Type-Options: nosniff\nX-Frame-Options: SAMEORIGIN\nExpires: Sun, 19 Nov 1978 05:00:00 GMT\nVary:\nX-Generator: Drupal 8 (https://www.drupal.org)\nContent-Length: 1564\nContent-Type: application/json\n\n{\"nid\":[{\"value\":3}],\"uuid\":[{\"value\":\"3f618cdc-3f2a-4e77-b932-9ff1d461a57a\"}],\"vid\":[{\"value\":3}],\"langcode\":[{\"value\":\"en\"}],\"type\":[{\"target_id\":\"islandora_object\",\"target_type\":\"node_type\",\"target_uuid\":\"62189bec-3ef3-4196-b847-b17e5ce61fd5\"}],\"revision_timestamp\":[{\"value\":\"2019-03-05T18:07:00+00:00\",\"format\":\"Y-m-d\\\\TH:i:sP\"}],\"revision_uid\":[{\"target_id\":1,\"target_type\":\"user\",\"target_uuid\":\"46a47057-de2d-4ce2-87ae-dbe4551209b8\",\"url\":\"\\/user\\/1\"}],\"revision_log\":[],\"status\":[{\"value\":true}],\"title\":[{\"value\":\"Created a collection with POST\"}],\"uid\":[{\"target_id\":1,\"target_type\":\"user\",\"target_uuid\":\"46a47057-de2d-4ce2-87ae-dbe4551209b8\",\"url\":\"\\/user\\/1\"}],\"created\":[{\"value\":\"2019-03-05T18:07:00+00:00\",\"format\":\"Y-m-d\\\\TH:i:sP\"}],\"changed\":[{\"value\":\"2019-03-05T18:07:00+00:00\",\"format\":\"Y-m-d\\\\TH:i:sP\"}],\"promote\":[{\"value\":true}],\"sticky\":[{\"value\":false}],\"default_langcode\":[{\"value\":true}],\"revision_translation_affected\":[{\"value\":true}],\"content_translation_source\":[{\"value\":\"und\"}],\"content_translation_outdated\":[{\"value\":false}],\"field_access_terms\":[],\"field_alternative_title\":[],\"field_description\":[],\"field_display_hints\":[],\"field_edtf_date\":[],\"field_edtf_date_created\":[],\"field_edtf_date_issued\":[],\"field_extent\":[{\"value\":\"1 item\"}],\"field_identifier\":[],\"field_linked_agent\":[],\"field_member_of\":[],\"field_model\":[{\"target_id\":23,\"target_type\":\"taxonomy_term\",\"target_uuid\":\"6a3b293d-4617-417b-99d2-23d75b57f7c2\",\"url\":\"\\/taxonomy\\/term\\/23\"}],\"field_pid\":[],\"field_resource_type\":[],\"field_rights\":[],\"field_subject\":[]}\n

The parts of the above request are:

  1. -i - return the response headers
  2. -X POST - send a POST request
  3. -u admin:islandora - use these basic authentication credentials
  4. -H\"Content-type: application/json\" - send the content-type header
  5. --data '{...}' - send the request body (seen above)
  6. 'http://localhost:8000/node?_format=json' - the endpoint of the request
"},{"location":"technical-documentation/rest-create/#files-and-media","title":"Files and Media","text":"

The Drupal REST UI is supposed to have a way to upload files, but this seems to require the use of an X-CSRF-Token, which can only be retrieved using Cookie authentication and even then does not allow you to upload.

However there is a special REST endpoint created by Islandora, which is less configurable and is not part of the above-mentioned REST UI.

This endpoint is available at http://localhost:8000/node/{node id}/media/{media type}/{media use}

It only accepts PUT requests. If the media and file don't exist they are created, if they exist the file is updated with the new body.

The node id and taxonomy term id are used to search (via an entity query) for a matching media. If this media exists the body of the file is replaced with the new content, otherwise a new file and media are created to hold the content.

The tokens to this URI are as follows:

  1. node id : The numeric ID of the node you wish to link this media/file to.
  2. media type : The media type name you wish to create (i.e. image, file, audio)
  3. media use : The numeric ID of the media use taxonomy term to set for this media

You can find the media use taxonomy terms at http://localhost:8000/admin/structure/taxonomy/manage/islandora_media_use/overview

The body of the request is the actual binary file to upload.

\ud83c\udf4e For example:

With a local file called my-image.png that I wanted to link to a node with ID 3.

I am using the taxonomy term \"Original file\", which on my machine is 16

> curl -i -X PUT -u admin:islandora -H\"Content-type: image/png\" --data-binary \"@my-image.png\" -H\"Content-Location: public://images/my-image.png\" 'http://localhost:8000/node/3/media/image/16'\n\nHTTP/1.1 100 Continue\n\nHTTP/1.1 201 Created\nDate: Tue, 05 Mar 2019 22:01:39 GMT\nServer: Apache/2.4.18 (Ubuntu)\nX-Powered-By: PHP/7.1.26-1+ubuntu16.04.1+deb.sury.org+1\nCache-Control: must-revalidate, no-cache, private\nLocation: http://localhost:8000/media/4\nX-UA-Compatible: IE=edge\nContent-language: en\nX-Content-Type-Options: nosniff\nX-Frame-Options: SAMEORIGIN\nExpires: Sun, 19 Nov 1978 05:00:00 GMT\nVary:\nX-Generator: Drupal 8 (https://www.drupal.org)\nContent-Length: 0\nContent-Type: text/html; charset=UTF-8\n

The parts of the above request are:

  1. -i - return the response headers
  2. -X PUT - send a PUT request
  3. -u admin:islandora - use these basic authentication credentials
  4. -H\"Content-type: image/png\" - send the content-type header
  5. --data-binary \"@my-image.png\" - send the contents of the file located at my-image.png as binary
  6. -H\"Content-Location: public://images/my-image.png\" - store the file in the public scheme (ie. in Drupal) at the path images/my-image.png, to store the file in Fedora use the fedora// scheme (ie. fedora://images/my-image.png)
  7. 'http://localhost:8000/node/3/media/image/16' - the endpoint of the request specifying the node, media type and taxonomy term.
"},{"location":"technical-documentation/rest-delete/","title":"Removing resources, media and files - DELETE Requests","text":"

Deleting is as easy as getting resources and more difficult than creating resources.

If you can perform a GET request then you have the information required to perform a DELETE request.

Check out the examples below.

  • Authorization
  • Content
  • Media
  • Files
"},{"location":"technical-documentation/rest-delete/#authorization","title":"Authorization","text":"

If you have restricted access to view your content, you will need to use one of the configured authorization methods to access your content, media and/or files.

These are defined under Authorization on the overview.

"},{"location":"technical-documentation/rest-delete/#content-nodes","title":"Content (Nodes)","text":"

You will need your node id, you can find more information in the GET documentation.

A delete is simply the same request as a GET but sending a DELETE http verb.

Our example node has a node id of 2

> curl -i -u admin:islandora -X GET 'http://localhost:8000/node/2?_format=json'\n\nHTTP/1.1 200 OK\nDate: Fri, 15 Mar 2019 15:02:00 GMT\nServer: Apache/2.4.18 (Ubuntu)\nX-Powered-By: PHP/7.1.26-1+ubuntu16.04.1+deb.sury.org+1\nCache-Control: must-revalidate, no-cache, private\nLink: <http://localhost:8000/node/2>; rel=\"canonical\"\nLink: <http://localhost:8000/node/2/delete>; rel=\"https://drupal.org/link-relations/delete-form\"\nLink: <http://localhost:8000/admin/content/node/delete?node=2>; rel=\"https://drupal.org/link-relations/delete-multiple-form\"\nLink: <http://localhost:8000/node/2/edit>; rel=\"edit-form\"\nLink: <http://localhost:8000/node/2/revisions>; rel=\"version-history\"\nLink: <http://localhost:8000/node/2>; rel=\"https://drupal.org/link-relations/revision\"\nLink: <http://localhost:8000/node?node=2>; rel=\"https://drupal.org/link-relations/create\"\nLink: <http://purl.org/dc/dcmitype/Collection>; rel=\"tag\"; title=\"Collection\"\nLink: <http://localhost:8000/node/2?_format=jsonld>; rel=\"alternate\"; type=\"application/ld+json\"\nX-Drupal-Dynamic-Cache: MISS\nX-UA-Compatible: IE=edge\nContent-language: en\nX-Content-Type-Options: nosniff\nX-Frame-Options: SAMEORIGIN\nExpires: Sun, 19 Nov 1978 05:00:00 GMT\nVary:\nX-Generator: Drupal 8 (https://www.drupal.org)\nContent-Length: 1564\nContent-Type: application/json\n\n{\"nid\":[{\"value\":2}],\"uuid\":[{\"value\":\"413135a6-0bd1-4d6b-8bcb-059cf7784d83\"}],\"vid\":[{\"value\":2}],\"langcode\":[{\"value\":\"en\"}],\"type\":[{\"target_id\":\"islandora_object\",\"target_type\":\"node_type\",\"target_uuid\":\"62189bec-3ef3-4196-b847-b17e5ce61fd5\"}],\"revision_timestamp\":[{\"value\":\"2019-03-05T18:04:43+00:00\",\"format\":\"Y-m-d\\\\TH:i:sP\"}],\"revision_uid\":[{\"target_id\":1,\"target_type\":\"user\",\"target_uuid\":\"46a47057-de2d-4ce2-87ae-dbe4551209b8\",\"url\":\"\\/user\\/1\"}],\"revision_log\":[],\"status\":[{\"value\":true}],\"title\":[{\"value\":\"Created a collection with POST\"}],\"uid\":[{\"target_id\":1,\"target_type\":\"user\",\"target_uuid\":\"46a47057-de2d-4ce2-87ae-dbe4551209b8\",\"url\":\"\\/user\\/1\"}],\"created\":[{\"value\":\"2019-03-05T18:04:43+00:00\",\"format\":\"Y-m-d\\\\TH:i:sP\"}],\"changed\":[{\"value\":\"2019-03-05T18:04:43+00:00\",\"format\":\"Y-m-d\\\\TH:i:sP\"}],\"promote\":[{\"value\":true}],\"sticky\":[{\"value\":false}],\"default_langcode\":[{\"value\":true}],\"revision_translation_affected\":[{\"value\":true}],\"content_translation_source\":[{\"value\":\"und\"}],\"content_translation_outdated\":[{\"value\":false}],\"field_access_terms\":[],\"field_alternative_title\":[],\"field_description\":[],\"field_display_hints\":[],\"field_edtf_date\":[],\"field_edtf_date_created\":[],\"field_edtf_date_issued\":[],\"field_extent\":[{\"value\":\"1 item\"}],\"field_identifier\":[],\"field_linked_agent\":[],\"field_member_of\":[],\"field_model\":[{\"target_id\":23,\"target_type\":\"taxonomy_term\",\"target_uuid\":\"6a3b293d-4617-417b-99d2-23d75b57f7c2\",\"url\":\"\\/taxonomy\\/term\\/23\"}],\"field_pid\":[],\"field_resource_type\":[],\"field_rights\":[],\"field_subject\":[]}%\n

Then we switch GET to DELETE

> curl -i -u admin:islandora -X DELETE 'http://localhost:8000/node/2?_format=json'\n\nHTTP/1.1 204 No Content\nDate: Fri, 15 Mar 2019 15:02:30 GMT\nServer: Apache/2.4.18 (Ubuntu)\nX-Powered-By: PHP/7.1.26-1+ubuntu16.04.1+deb.sury.org+1\nCache-Control: must-revalidate, no-cache, private\nX-UA-Compatible: IE=edge\nContent-language: en\nX-Content-Type-Options: nosniff\nX-Frame-Options: SAMEORIGIN\nExpires: Sun, 19 Nov 1978 05:00:00 GMT\nVary:\nX-Generator: Drupal 8 (https://www.drupal.org)\nContent-Type: text/html; charset=UTF-8\n

All subsequent requests to the above URI will return a 404 Not Found status code.

> curl -i -u admin:islandora -X GET 'http://localhost:8000/node/2?_format=json'\n\nHTTP/1.1 404 Not Found\nDate: Fri, 15 Mar 2019 15:12:58 GMT\nServer: Apache/2.4.18 (Ubuntu)\nX-Powered-By: PHP/7.1.26-1+ubuntu16.04.1+deb.sury.org+1\nCache-Control: must-revalidate, no-cache, private\nX-UA-Compatible: IE=edge\nContent-language: en\nX-Content-Type-Options: nosniff\nX-Frame-Options: SAMEORIGIN\nExpires: Sun, 19 Nov 1978 05:00:00 GMT\nVary:\nX-Generator: Drupal 8 (https://www.drupal.org)\nContent-Length: 149\nContent-Type: application/json\n\n{\"message\":\"The \\u0022node\\u0022 parameter was not converted for the path \\u0022\\/node\\/{node}\\u0022 (route name: \\u0022rest.entity.node.GET\\u0022)\"}\n
"},{"location":"technical-documentation/rest-delete/#media","title":"Media","text":"

You will need a media id as used in GET documentation.

A delete is simply the same request as a GET but sending a DELETE http verb.

With a media id of 1 for our example, I'll perform a GET

> curl -i -u admin:islandora -X GET 'http://localhost:8000/media/1?_format=json'\n\nHTTP/1.1 200 OK\nDate: Fri, 15 Mar 2019 14:53:54 GMT\nServer: Apache/2.4.18 (Ubuntu)\nX-Powered-By: PHP/7.1.26-1+ubuntu16.04.1+deb.sury.org+1\nCache-Control: must-revalidate, no-cache, private\nLink: <http://localhost:8000/media/add>; rel=\"https://drupal.org/link-relations/add-page\"\nLink: <http://localhost:8000/media/add/image>; rel=\"https://drupal.org/link-relations/add-form\"\nLink: <http://localhost:8000/media/1>; rel=\"canonical\"\nLink: <http://localhost:8000/admin/content/media>; rel=\"collection\"\nLink: <http://localhost:8000/media/1/delete>; rel=\"https://drupal.org/link-relations/delete-form\"\nLink: <http://localhost:8000/media/delete?media=1>; rel=\"https://drupal.org/link-relations/delete-multiple-form\"\nLink: <http://localhost:8000/media/1/edit>; rel=\"edit-form\"\nLink: <http://localhost:8000/media/1>; rel=\"https://drupal.org/link-relations/revision\"\nLink: <http://localhost:8000/node/1>; rel=\"related\"; title=\"Media of\"\nLink: <http://pcdm.org/use#OriginalFile>; rel=\"tag\"; title=\"Original File\"\nLink: <http://localhost:8000/media/1?_format=jsonld>; rel=\"alternate\"; type=\"application/ld+json\"\nLink: <http://localhost:8000/media/1/source>; rel=\"edit-media\"\nLink: <http://localhost:8000/_flysystem/fedora/2019-03/Louis_Riel.jpg>; rel=\"describes\"; type=\"image/jpeg\"\nX-Drupal-Dynamic-Cache: HIT\nX-UA-Compatible: IE=edge\nContent-language: en\nX-Content-Type-Options: nosniff\nX-Frame-Options: SAMEORIGIN\nExpires: Sun, 19 Nov 1978 05:00:00 GMT\nVary:\nX-Generator: Drupal 8 (https://www.drupal.org)\nContent-Length: 1937\nContent-Type: application/json\n\n{\"mid\":[{\"value\":1}],\"uuid\":[{\"value\":\"d8893926-ddb7-4125-b2da-30428af0fe3d\"}],\"vid\":[{\"value\":1}],\"langcode\":[{\"value\":\"en\"}],\"bundle\":[{\"target_id\":\"image\",\"target_type\":\"media_type\",\"target_uuid\":\"3860e653-201b-4509-89dd-628c446d81cb\"}],\"revision_created\":[{\"value\":\"2019-03-01T19:43:46+00:00\",\"format\":\"Y-m-d\\\\TH:i:sP\"}],\"revision_user\":[{\"target_id\":1,\"target_type\":\"user\",\"target_uuid\":\"46a47057-de2d-4ce2-87ae-dbe4551209b8\",\"url\":\"\\/user\\/1\"}],\"revision_log_message\":[],\"status\":[{\"value\":true}],\"name\":[{\"value\":\"An image\"}],\"thumbnail\":[{\"target_id\":2,\"alt\":\"A portrait of Louis Riel\",\"title\":null,\"width\":800,\"height\":1333,\"target_type\":\"file\",\"target_uuid\":\"b0625129-c592-463a-93c3-3eff7cd3567e\",\"url\":\"http:\\/\\/localhost:8000\\/_flysystem\\/fedora\\/2019-03\\/Louis_Riel.jpg\"}],\"uid\":[{\"target_id\":1,\"target_type\":\"user\",\"target_uuid\":\"46a47057-de2d-4ce2-87ae-dbe4551209b8\",\"url\":\"\\/user\\/1\"}],\"created\":[{\"value\":\"2019-03-01T19:43:22+00:00\",\"format\":\"Y-m-d\\\\TH:i:sP\"}],\"changed\":[{\"value\":\"2019-03-01T19:43:46+00:00\",\"format\":\"Y-m-d\\\\TH:i:sP\"}],\"default_langcode\":[{\"value\":true}],\"revision_translation_affected\":[{\"value\":true}],\"content_translation_source\":[{\"value\":\"und\"}],\"content_translation_outdated\":[{\"value\":false}],\"field_access_terms\":[],\"field_file_size\":[{\"value\":166613}],\"field_height\":[{\"value\":1333}],\"field_media_image\":[{\"target_id\":2,\"alt\":\"A portrait of Louis Riel\",\"title\":\"\",\"width\":800,\"height\":1333,\"target_type\":\"file\",\"target_uuid\":\"b0625129-c592-463a-93c3-3eff7cd3567e\",\"url\":\"http:\\/\\/localhost:8000\\/_flysystem\\/fedora\\/2019-03\\/Louis_Riel.jpg\"}],\"field_media_of\":[{\"target_id\":1,\"target_type\":\"node\",\"target_uuid\":\"8322e36e-f8ec-4fd9-919d-52aed7b17a52\",\"url\":\"\\/node\\/1\"}],\"field_media_use\":[{\"target_id\":16,\"target_type\":\"taxonomy_term\",\"target_uuid\":\"08e01ff9-eb72-42f5-ae3a-8b21ba0c0bc3\",\"url\":\"\\/taxonomy\\/term\\/16\"}],\"field_mime_type\":[{\"value\":\"image\\/jpeg\"}],\"field_width\":[{\"value\":800}]}\n

Then we replace GET with DELETE.

> curl -i -u admin:islandora -X DELETE 'http://localhost:8000/media/1?_format=json'\n\nHTTP/1.1 204 No Content\nDate: Fri, 15 Mar 2019 14:54:55 GMT\nServer: Apache/2.4.18 (Ubuntu)\nX-Powered-By: PHP/7.1.26-1+ubuntu16.04.1+deb.sury.org+1\nCache-Control: must-revalidate, no-cache, private\nX-UA-Compatible: IE=edge\nContent-language: en\nX-Content-Type-Options: nosniff\nX-Frame-Options: SAMEORIGIN\nExpires: Sun, 19 Nov 1978 05:00:00 GMT\nVary:\nX-Generator: Drupal 8 (https://www.drupal.org)\nContent-Type: text/html; charset=UTF-8\n

Subsequent requests to the media return 404 Not Found statuses.

> curl -i -u admin:islandora -X GET 'http://localhost:8000/media/1?_format=json'\n\nHTTP/1.1 404 Not Found\nDate: Fri, 15 Mar 2019 15:13:45 GMT\nServer: Apache/2.4.18 (Ubuntu)\nX-Powered-By: PHP/7.1.26-1+ubuntu16.04.1+deb.sury.org+1\nCache-Control: must-revalidate, no-cache, private\nX-UA-Compatible: IE=edge\nContent-language: en\nX-Content-Type-Options: nosniff\nX-Frame-Options: SAMEORIGIN\nExpires: Sun, 19 Nov 1978 05:00:00 GMT\nVary:\nX-Generator: Drupal 8 (https://www.drupal.org)\nContent-Length: 153\nContent-Type: application/json\n\n{\"message\":\"The \\u0022media\\u0022 parameter was not converted for the path \\u0022\\/media\\/{media}\\u0022 (route name: \\u0022rest.entity.media.GET\\u0022)\"}\n
"},{"location":"technical-documentation/rest-delete/#files","title":"Files","text":"

You'll need the file id, there is more information at the top of the GET requests for files documentation.

A delete is simply the same request as a GET but sending a DELETE http verb.

With a file id of 2 for our example, I can perform a test GET

> curl -i -u admin:islandora 'http://localhost:8000/entity/file/2?_format=json'\n\nHTTP/1.1 200 OK\nDate: Fri, 15 Mar 2019 14:40:40 GMT\nServer: Apache/2.4.18 (Ubuntu)\nX-Powered-By: PHP/7.1.26-1+ubuntu16.04.1+deb.sury.org+1\nCache-Control: must-revalidate, no-cache, private\nX-Drupal-Dynamic-Cache: MISS\nX-UA-Compatible: IE=edge\nContent-language: en\nX-Content-Type-Options: nosniff\nX-Frame-Options: SAMEORIGIN\nExpires: Sun, 19 Nov 1978 05:00:00 GMT\nVary:\nX-Generator: Drupal 8 (https://www.drupal.org)\nContent-Length: 617\nContent-Type: application/json\n\n{\"fid\":[{\"value\":2}],\"uuid\":[{\"value\":\"b0625129-c592-463a-93c3-3eff7cd3567e\"}],\"langcode\":[{\"value\":\"en\"}],\"uid\":[{\"target_id\":1,\"target_type\":\"user\",\"target_uuid\":\"46a47057-de2d-4ce2-87ae-dbe4551209b8\",\"url\":\"\\/user\\/1\"}],\"filename\":[{\"value\":\"Louis_Riel.jpg\"}],\"uri\":[{\"value\":\"fedora:\\/\\/2019-03\\/Louis_Riel.jpg\",\"url\":\"\\/_flysystem\\/fedora\\/2019-03\\/Louis_Riel.jpg\"}],\"filemime\":[{\"value\":\"image\\/jpeg\"}],\"filesize\":[{\"value\":166613}],\"status\":[{\"value\":true}],\"created\":[{\"value\":\"2019-03-01T19:43:35+00:00\",\"format\":\"Y-m-d\\\\TH:i:sP\"}],\"changed\":[{\"value\":\"2019-03-01T19:43:46+00:00\",\"format\":\"Y-m-d\\\\TH:i:sP\"}]}\n

If this is the correct file, I can delete it.

> curl -i -u admin:islandora -X DELETE 'http://localhost:8000/entity/file/2?_format=json'\n\nHTTP/1.1 204 No Content\nDate: Fri, 15 Mar 2019 14:43:22 GMT\nServer: Apache/2.4.18 (Ubuntu)\nX-Powered-By: PHP/7.1.26-1+ubuntu16.04.1+deb.sury.org+1\nCache-Control: must-revalidate, no-cache, private\nX-UA-Compatible: IE=edge\nContent-language: en\nX-Content-Type-Options: nosniff\nX-Frame-Options: SAMEORIGIN\nExpires: Sun, 19 Nov 1978 05:00:00 GMT\nVary:\nX-Generator: Drupal 8 (https://www.drupal.org)\nContent-Type: text/html; charset=UTF-8\n

Subsequent requests to the URI now return a 404 Not Found status.

> curl -i -u admin:islandora 'http://localhost:8000/entity/file/2?_format=json'\n\nHTTP/1.1 404 Not Found\nDate: Fri, 15 Mar 2019 14:43:33 GMT\nServer: Apache/2.4.18 (Ubuntu)\nX-Powered-By: PHP/7.1.26-1+ubuntu16.04.1+deb.sury.org+1\nCache-Control: must-revalidate, no-cache, private\nX-UA-Compatible: IE=edge\nContent-language: en\nX-Content-Type-Options: nosniff\nX-Frame-Options: SAMEORIGIN\nExpires: Sun, 19 Nov 1978 05:00:00 GMT\nVary:\nX-Generator: Drupal 8 (https://www.drupal.org)\nContent-Length: 157\nContent-Type: application/json\n\n{\"message\":\"The \\u0022file\\u0022 parameter was not converted for the path \\u0022\\/entity\\/file\\/{file}\\u0022 (route name: \\u0022rest.entity.file.GET\\u0022)\"}\n
"},{"location":"technical-documentation/rest-get/","title":"Getting resources, media and files - GET Requests","text":"
  • Authorization
  • Content
  • Media
  • Files
"},{"location":"technical-documentation/rest-get/#authorization","title":"Authorization","text":"

If you have restricted access to view your content, you will need to use one of the configured authorization methods to access your content, media and/or files.

These are defined under Authorization on the overview.

"},{"location":"technical-documentation/rest-get/#content-nodes","title":"Content (Nodes)","text":"

The above setup shows that you can perform a GET request against a node at the /node/{id} endpoint. This is the same as the URL when viewed through a web browser. For example http://localhost:8000/node/2

It also defines the formats you can access. In this example json and jsonld are available.

To access a resource in a specific format use the _format= argument which is appended to the end of the URI after a ?.

To get the resource located at http://localhost:8000/node/3 in JSON, you can go to http://localhost:8000/node/3?_format=json.

\ud83c\udf4e For example:

You can do this in your web browser or with a command line client like cURL. (Note: response body here has been formatted, yours will look different)

vagrant@claw:~$ curl -i http://localhost:8000/node/3?_format=json\n\nHTTP/1.1 200 OK\nDate: Mon, 04 Mar 2019 21:53:50 GMT\nServer: Apache/2.4.18 (Ubuntu)\nX-Powered-By: PHP/7.1.26-1+ubuntu16.04.1+deb.sury.org+1\nCache-Control: must-revalidate, no-cache, private\nLink: <http://localhost:8000/node/3>; rel=\"canonical\"\nLink: <http://localhost:8000/node/3/delete>; rel=\"https://drupal.org/link-relations/delete-form\"\nLink: <http://localhost:8000/admin/content/node/delete?node=3>; rel=\"https://drupal.org/link-relations/delete-multiple-form\"\nLink: <http://localhost:8000/node/3/edit>; rel=\"edit-form\"\nLink: <http://localhost:8000/node/3/revisions>; rel=\"version-history\"\nLink: <http://localhost:8000/node/3>; rel=\"https://drupal.org/link-relations/revision\"\nLink: <http://localhost:8000/node?node=3>; rel=\"https://drupal.org/link-relations/create\"\nLink: <http://purl.org/coar/resource_type/c_c513>; rel=\"tag\"; title=\"Image\"\nLink: <http://localhost:8000/media/1>; rel=\"related\"; title=\"Original File\"\nLink: <http://localhost:8000/media/2>; rel=\"related\"; title=\"Service File\"\nLink: <http://localhost:8000/media/3>; rel=\"related\"; title=\"Thumbnail Image\"\nLink: <http://localhost:8000/node/3?_format=jsonld>; rel=\"alternate\"; type=\"application/ld+json\"\nX-Drupal-Dynamic-Cache: MISS\nX-UA-Compatible: IE=edge\nContent-language: en\nX-Content-Type-Options: nosniff\nX-Frame-Options: SAMEORIGIN\nExpires: Sun, 19 Nov 1978 05:00:00 GMT\nVary:\nX-Generator: Drupal 8 (https://www.drupal.org)\nX-Drupal-Cache: MISS\nContent-Length: 1521\nContent-Type: application/json\n\n{\n  \"nid\": [\n    {\n      \"value\": 1\n    }\n  ],\n  \"uuid\": [\n    {\n      \"value\": \"8322e36e-f8ec-4fd9-919d-52aed7b17a52\"\n    }\n  ],\n  \"vid\": [\n    {\n      \"value\": 1\n    }\n  ],\n  \"langcode\": [\n    {\n      \"value\": \"en\"\n    }\n  ],\n  \"type\": [\n    {\n      \"target_id\": \"islandora_object\",\n      \"target_type\": \"node_type\",\n      \"target_uuid\": \"62189bec-3ef3-4196-b847-b17e5ce61fd5\"\n    }\n  ],\n  \"revision_timestamp\": [\n    {\n      \"value\": \"2019-03-01T19:43:12+00:00\",\n      \"format\": \"Y-m-d\\\\TH:i:sP\"\n    }\n  ],\n  \"revision_uid\": [\n    {\n      \"target_id\": 1,\n      \"target_type\": \"user\",\n      \"target_uuid\": \"46a47057-de2d-4ce2-87ae-dbe4551209b8\",\n      \"url\": \"\\\\/user\\\\/1\"\n    }\n  ],\n  \"revision_log\": [],\n  \"status\": [\n    {\n      \"value\": true\n    }\n  ],\n  \"title\": [\n    {\n      \"value\": \"Custom item\"\n    }\n  ],\n  \"uid\": [\n    {\n      \"target_id\": 1,\n      \"target_type\": \"user\",\n      \"target_uuid\": \"46a47057-de2d-4ce2-87ae-dbe4551209b8\",\n      \"url\": \"\\\\/user\\\\/1\"\n    }\n  ],\n  \"created\": [\n    {\n      \"value\": \"2019-03-01T19:42:54+00:00\",\n      \"format\": \"Y-m-d\\\\TH:i:sP\"\n    }\n  ],\n  \"changed\": [\n    {\n      \"value\": \"2019-03-01T19:43:12+00:00\",\n      \"format\": \"Y-m-d\\\\TH:i:sP\"\n    }\n  ],\n  \"promote\": [\n    {\n      \"value\": true\n    }\n  ],\n  \"sticky\": [\n    {\n      \"value\": false\n    }\n  ],\n  \"default_langcode\": [\n    {\n      \"value\": true\n    }\n  ],\n  \"revision_translation_affected\": [\n    {\n      \"value\": true\n    }\n  ],\n  \"content_translation_source\": [\n    {\n      \"value\": \"und\"\n    }\n  ],\n  \"content_translation_outdated\": [\n    {\n      \"value\": false\n    }\n  ],\n  \"field_alternative_title\": [],\n  \"field_description\": [],\n  \"field_display_hints\": [],\n  \"field_edtf_date\": [],\n  \"field_edtf_date_created\": [],\n  \"field_edtf_date_issued\": [],\n  \"field_extent\": [\n    {\n      \"value\": \"1 item\"\n    }\n  ],\n  \"field_identifier\": [],\n  \"field_linked_agent\": [],\n  \"field_member_of\": [],\n  \"field_model\": [\n    {\n      \"target_id\": 24,\n      \"target_type\": \"taxonomy_term\",\n      \"target_uuid\": \"e7560b68-e95a-4e76-9671-2a3041cd9800\",\n      \"url\": \"\\\\/taxonomy\\\\/term\\\\/24\"\n    }\n  ],\n  \"field_pid\": [],\n  \"field_resource_type\": [],\n  \"field_rights\": [],\n  \"field_subject\": []\n}\n
"},{"location":"technical-documentation/rest-get/#media","title":"Media","text":"

The above steps are the same for media, it is just the URI that has changed.

For media you use /media/{id}

\ud83c\udf4e For example:

curl -u admin:islandora http://localhost:8000/media/2?_format=json\n\n{\n  \"mid\": [\n    {\n      \"value\": 2\n    }\n  ],\n  \"uuid\": [\n    {\n      \"value\": \"e75e609d-510c-4764-9280-665bb7026161\"\n    }\n  ],\n  \"vid\": [\n    {\n      \"value\": 2\n    }\n  ],\n  \"langcode\": [\n    {\n      \"value\": \"en\"\n    }\n  ],\n  \"bundle\": [\n    {\n      \"target_id\": \"image\",\n      \"target_type\": \"media_type\",\n      \"target_uuid\": \"3860e653-201b-4509-89dd-628c446d81cb\"\n    }\n  ],\n  \"revision_created\": [\n    {\n      \"value\": \"2019-03-01T19:43:48+00:00\",\n      \"format\": \"Y-m-d\\\\TH:i:sP\"\n    }\n  ],\n  \"revision_user\": [],\n  \"revision_log_message\": [],\n  \"status\": [\n    {\n      \"value\": true\n    }\n  ],\n  \"name\": [\n    {\n      \"value\": \"1-Service File.jpg\"\n    }\n  ],\n  \"thumbnail\": [\n    {\n      \"target_id\": 3,\n      \"alt\": null,\n      \"title\": null,\n      \"width\": 800,\n      \"height\": 1333,\n      \"target_type\": \"file\",\n      \"target_uuid\": \"e97ad038-dcb3-4d81-aa91-c945b2fe092c\",\n      \"url\": \"http:\\\\/\\\\/localhost:8000\\\\/sites\\\\/default\\\\/files\\\\/2019-03\\\\/1-Service%20File.jpg\"\n    }\n  ],\n  \"uid\": [\n    {\n      \"target_id\": 1,\n      \"target_type\": \"user\",\n      \"target_uuid\": \"46a47057-de2d-4ce2-87ae-dbe4551209b8\",\n      \"url\": \"\\\\/user\\\\/1\"\n    }\n  ],\n  \"created\": [\n    {\n      \"value\": \"2019-03-01T19:43:48+00:00\",\n      \"format\": \"Y-m-d\\\\TH:i:sP\"\n    }\n  ],\n  \"changed\": [\n    {\n      \"value\": \"2019-03-01T19:43:48+00:00\",\n      \"format\": \"Y-m-d\\\\TH:i:sP\"\n    }\n  ],\n  \"default_langcode\": [\n    {\n      \"value\": true\n    }\n  ],\n  \"revision_translation_affected\": [\n    {\n      \"value\": true\n    }\n  ],\n  \"content_translation_source\": [\n    {\n      \"value\": \"und\"\n    }\n  ],\n  \"content_translation_outdated\": [\n    {\n      \"value\": false\n    }\n  ],\n  \"field_access_terms\": [],\n  \"field_file_size\": [\n    {\n      \"value\": 166608\n    }\n  ],\n  \"field_height\": [\n    {\n      \"value\": 1333\n    }\n  ],\n  \"field_media_image\": [\n    {\n      \"target_id\": 3,\n      \"alt\": null,\n      \"title\": null,\n      \"width\": 800,\n      \"height\": 1333,\n      \"target_type\": \"file\",\n      \"target_uuid\": \"e97ad038-dcb3-4d81-aa91-c945b2fe092c\",\n      \"url\": \"http:\\\\/\\\\/localhost:8000\\\\/sites\\\\/default\\\\/files\\\\/2019-03\\\\/1-Service%20File.jpg\"\n    }\n  ],\n  \"field_media_of\": [\n    {\n      \"target_id\": 1,\n      \"target_type\": \"node\",\n      \"target_uuid\": \"8322e36e-f8ec-4fd9-919d-52aed7b17a52\",\n      \"url\": \"\\\\/node\\\\/1\"\n    }\n  ],\n  \"field_media_use\": [\n    {\n      \"target_id\": 18,\n      \"target_type\": \"taxonomy_term\",\n      \"target_uuid\": \"e8c698a7-8c15-47c4-8567-1c27f8f0c19e\",\n      \"url\": \"\\\\/taxonomy\\\\/term\\\\/18\"\n    }\n  ],\n  \"field_mime_type\": [\n    {\n      \"value\": \"image\\\\/jpeg\"\n    }\n  ],\n  \"field_width\": [\n    {\n      \"value\": 800\n    }\n  ]\n}\n

field_media_of indicates which node(s) the media is associated with. The node can be referenced using the value of the target_id key (in this example, 1) or the target_uuid key (8322e36e-f8ec-4fd9-919d-52aed7b17a52).

"},{"location":"technical-documentation/rest-get/#files","title":"Files","text":"

Through the REST API you are accessing metadata about files, but not the actual binary object.

Based on the above configuration the path for a GET request is /entity/file/{id}

The file ID refers to the integer counter and not the UUID of a file.

Through the UI you can go to the File listing at http://localhost:8000/admin/content/files.

For the file you want to access, choose the USED IN link.

That URI has the format http://localhost:8000/admin/content/files/usage/{file id}

Alternatively the file ID can be found by viewing the media metadata (above section). In the above example if you look through the response from a GET request to the /media/{id} you will find a section like:

  \"field_media_image\": [\n    {\n      \"target_id\": 3,\n      \"alt\": null,\n      \"title\": null,\n      \"width\": 800,\n      \"height\": 1333,\n      \"target_type\": \"file\",\n      \"target_uuid\": \"e97ad038-dcb3-4d81-aa91-c945b2fe092c\",\n      \"url\": \"http:\\\\/\\\\/localhost:8000\\\\/sites\\\\/default\\\\/files\\\\/2019-03\\\\/1-Service%20File.jpg\"\n    }\n  ],\n

Note: Depending on the \"type\" of file (image, video, audio, file) this field will have a different name.

This has both the url which is the URI to retrieve the binary content of the file as well as the target_id which is the file ID.

So we can retrieve the file with a GET request to the URI http://localhost:8000/sites/default/files/2019-03/1-Service%20File.jpg.

But more importantly we can retrieve the file metadata by making a request to http://localhost:8000/entity/file/3 with one of the allowed formats

\ud83c\udf4e For example:

> curl -i -X GET \"http://localhost:8000/entity/file/3?_format=json\"\n\nHTTP/1.1 200 OK\nDate: Tue, 05 Mar 2019 16:52:06 GMT\nServer: Apache/2.4.18 (Ubuntu)\nX-Powered-By: PHP/7.1.26-1+ubuntu16.04.1+deb.sury.org+1\nCache-Control: must-revalidate, no-cache, private\nX-Drupal-Dynamic-Cache: MISS\nX-UA-Compatible: IE=edge\nContent-language: en\nX-Content-Type-Options: nosniff\nX-Frame-Options: SAMEORIGIN\nExpires: Sun, 19 Nov 1978 05:00:00 GMT\nVary:\nX-Generator: Drupal 8 (https://www.drupal.org)\nX-Drupal-Cache: HIT\nContent-Length: 634\nContent-Type: application/json\n\n{\n  \"fid\": [\n    {\n      \"value\": 3\n    }\n  ],\n  \"uuid\": [\n    {\n      \"value\": \"e97ad038-dcb3-4d81-aa91-c945b2fe092c\"\n    }\n  ],\n  \"langcode\": [\n    {\n      \"value\": \"en\"\n    }\n  ],\n  \"uid\": [\n    {\n      \"target_id\": 1,\n      \"target_type\": \"user\",\n      \"target_uuid\": \"46a47057-de2d-4ce2-87ae-dbe4551209b8\",\n      \"url\": \"\\\\/user\\\\/1\"\n    }\n  ],\n  \"filename\": [\n    {\n      \"value\": \"1-Service File.jpg\"\n    }\n  ],\n  \"uri\": [\n    {\n      \"value\": \"public:\\\\/\\\\/2019-03\\\\/1-Service File.jpg\",\n      \"url\": \"\\\\/sites\\\\/default\\\\/files\\\\/2019-03\\\\/1-Service%20File.jpg\"\n    }\n  ],\n  \"filemime\": [\n    {\n      \"value\": \"image\\\\/jpeg\"\n    }\n  ],\n  \"filesize\": [\n    {\n      \"value\": 166608\n    }\n  ],\n  \"status\": [\n    {\n      \"value\": true\n    }\n  ],\n  \"created\": [\n    {\n      \"value\": \"2019-03-01T19:43:48+00:00\",\n      \"format\": \"Y-m-d\\\\TH:i:sP\"\n    }\n  ],\n  \"changed\": [\n    {\n      \"value\": \"2019-03-01T19:43:48+00:00\",\n      \"format\": \"Y-m-d\\\\TH:i:sP\"\n    }\n  ]\n}\n

The uri: url: is the path to your file, so for the above example of \"url\": \"\\\\/sites\\\\/default\\\\/files\\\\/2019-03\\\\/1-Service%20File.jpg\".

I can perform a GET against <my hostname>/sites/default/files/2019-03/1-Service%20File.jpg and get the contents of this file.

"},{"location":"technical-documentation/rest-patch/","title":"Updating resources - PATCH request","text":"

PATCH requests allow you to update resources in place via a REST call. In this case you send a few required fields and then any changed fields. PATCH requests are available for nodes and media entities, but not for files. PATCH requests are very consistent between node and media entities so we will just show one set of examples here.

Our example node is at http://localhost:8000/node/3.

If you perform a GET against another node or media you can see all of the fields, some of these are calculated (change based on others, i.e. \"changed\"), some are references to other entities (nodes, media, files in Drupal. i.e. \"field_model\") and others are pure text fields (i.e. \"title\").

The fields will be different between different node types and media types, but the syntax to update them is the same.

  • Authorization
  • Change a text field
  • Change an entity reference
"},{"location":"technical-documentation/rest-patch/#authorization","title":"Authorization","text":"

If you have restricted access to view your content, you will need to use one of the configured authorization methods to access your content, media and/or files.

These are defined under Authorization on the overview.

This with assume you have already created a node or media at some location.

"},{"location":"technical-documentation/rest-patch/#change-a-text-field","title":"Change a text field","text":"

To change the title of a node, you need to supply the new title and the node_type. The node_type is required as this defines what fields are available to the node.

A successful PATCH request will return a 200 status code and the body will contain the newly updated body.

curl -i -u admin:islandora -H\"Content-type: application/json\" -X PATCH -d '{ \"type\": [{\"target_id\": \"islandora_object\"}], \"title\": [{\"value\":\"Updated with a PATCH request\"}]}' 'http://localhost:8000/node/3?_format=json'\n\nHTTP/1.1 200 OK\nDate: Mon, 11 Mar 2019 17:01:23 GMT\nServer: Apache/2.4.18 (Ubuntu)\nX-Powered-By: PHP/7.1.26-1+ubuntu16.04.1+deb.sury.org+1\nCache-Control: must-revalidate, no-cache, private\nX-UA-Compatible: IE=edge\nContent-language: en\nX-Content-Type-Options: nosniff\nX-Frame-Options: SAMEORIGIN\nExpires: Sun, 19 Nov 1978 05:00:00 GMT\nVary:\nX-Generator: Drupal 8 (https://www.drupal.org)\nContent-Length: 1562\nContent-Type: application/json\n\n{\"nid\":[{\"value\":3}],\"uuid\":[{\"value\":\"3f618cdc-3f2a-4e77-b932-9ff1d461a57a\"}],\"vid\":[{\"value\":3}],\"langcode\":[{\"value\":\"en\"}],\"type\":[{\"target_id\":\"islandora_object\",\"target_type\":\"node_type\",\"target_uuid\":\"62189bec-3ef3-4196-b847-b17e5ce61fd5\"}],\"revision_timestamp\":[{\"value\":\"2019-03-05T18:07:00+00:00\",\"format\":\"Y-m-d\\\\TH:i:sP\"}],\"revision_uid\":[{\"target_id\":1,\"target_type\":\"user\",\"target_uuid\":\"46a47057-de2d-4ce2-87ae-dbe4551209b8\",\"url\":\"\\/user\\/1\"}],\"revision_log\":[],\"status\":[{\"value\":true}],\"title\":[{\"value\":\"Updated with a PATCH request\"}],\"uid\":[{\"target_id\":1,\"target_type\":\"user\",\"target_uuid\":\"46a47057-de2d-4ce2-87ae-dbe4551209b8\",\"url\":\"\\/user\\/1\"}],\"created\":[{\"value\":\"2019-03-05T18:07:00+00:00\",\"format\":\"Y-m-d\\\\TH:i:sP\"}],\"changed\":[{\"value\":\"2019-03-11T17:01:23+00:00\",\"format\":\"Y-m-d\\\\TH:i:sP\"}],\"promote\":[{\"value\":true}],\"sticky\":[{\"value\":false}],\"default_langcode\":[{\"value\":true}],\"revision_translation_affected\":[{\"value\":true}],\"content_translation_source\":[{\"value\":\"und\"}],\"content_translation_outdated\":[{\"value\":false}],\"field_access_terms\":[],\"field_alternative_title\":[],\"field_description\":[],\"field_display_hints\":[],\"field_edtf_date\":[],\"field_edtf_date_created\":[],\"field_edtf_date_issued\":[],\"field_extent\":[{\"value\":\"1 item\"}],\"field_identifier\":[],\"field_linked_agent\":[],\"field_member_of\":[],\"field_model\":[{\"target_id\":23,\"target_type\":\"taxonomy_term\",\"target_uuid\":\"6a3b293d-4617-417b-99d2-23d75b57f7c2\",\"url\":\"\\/taxonomy\\/term\\/23\"}],\"field_pid\":[],\"field_resource_type\":[],\"field_rights\":[],\"field_subject\":[]}\n
"},{"location":"technical-documentation/rest-patch/#change-an-entity-reference-field","title":"Change an entity reference field","text":"

This example is how to change a field that references some other entity. For this example we will use the field_model field, this is a reference to the taxonomy term that holds the \"model\" of the resource (ie. Image, Collection, Audio, Video, etc)

On our example installation taxonomy term 22 is \"Binary\", so to change a node from what it was to a Binary you would do.

> curl -i -u admin:islandora -H\"Content-type: application/json\" -X PATCH -d '{ \"type\": [{\"target_id\": \"islandora_object\"}], \"field_model\": [{\"target_id\": 22, \"target_type\": \"taxonomy_term\"}]}' 'http://localhost:8000/node/3?_format=json'\n\nHTTP/1.1 200 OK\nDate: Mon, 11 Mar 2019 17:51:47 GMT\nServer: Apache/2.4.18 (Ubuntu)\nX-Powered-By: PHP/7.1.26-1+ubuntu16.04.1+deb.sury.org+1\nCache-Control: must-revalidate, no-cache, private\nX-UA-Compatible: IE=edge\nContent-language: en\nX-Content-Type-Options: nosniff\nX-Frame-Options: SAMEORIGIN\nExpires: Sun, 19 Nov 1978 05:00:00 GMT\nVary:\nX-Generator: Drupal 8 (https://www.drupal.org)\nContent-Length: 1562\nContent-Type: application/json\n\n{\"nid\":[{\"value\":3}],\"uuid\":[{\"value\":\"3f618cdc-3f2a-4e77-b932-9ff1d461a57a\"}],\"vid\":[{\"value\":4}],\"langcode\":[{\"value\":\"en\"}],\"type\":[{\"target_id\":\"islandora_object\",\"target_type\":\"node_type\",\"target_uuid\":\"62189bec-3ef3-4196-b847-b17e5ce61fd5\"}],\"revision_timestamp\":[{\"value\":\"2019-03-11T17:36:10+00:00\",\"format\":\"Y-m-d\\\\TH:i:sP\"}],\"revision_uid\":[{\"target_id\":1,\"target_type\":\"user\",\"target_uuid\":\"46a47057-de2d-4ce2-87ae-dbe4551209b8\",\"url\":\"\\/user\\/1\"}],\"revision_log\":[],\"status\":[{\"value\":true}],\"title\":[{\"value\":\"Updated with a PATCH request\"}],\"uid\":[{\"target_id\":1,\"target_type\":\"user\",\"target_uuid\":\"46a47057-de2d-4ce2-87ae-dbe4551209b8\",\"url\":\"\\/user\\/1\"}],\"created\":[{\"value\":\"2019-03-05T18:07:00+00:00\",\"format\":\"Y-m-d\\\\TH:i:sP\"}],\"changed\":[{\"value\":\"2019-03-11T17:51:47+00:00\",\"format\":\"Y-m-d\\\\TH:i:sP\"}],\"promote\":[{\"value\":true}],\"sticky\":[{\"value\":false}],\"default_langcode\":[{\"value\":true}],\"revision_translation_affected\":[{\"value\":true}],\"content_translation_source\":[{\"value\":\"und\"}],\"content_translation_outdated\":[{\"value\":false}],\"field_access_terms\":[],\"field_alternative_title\":[],\"field_description\":[],\"field_display_hints\":[],\"field_edtf_date\":[],\"field_edtf_date_created\":[],\"field_edtf_date_issued\":[],\"field_extent\":[{\"value\":\"1 item\"}],\"field_identifier\":[],\"field_linked_agent\":[],\"field_member_of\":[],\"field_model\":[{\"target_id\":22,\"target_type\":\"taxonomy_term\",\"target_uuid\":\"e1f167e1-124d-4db4-96ab-30641ca4e21b\",\"url\":\"\\/taxonomy\\/term\\/22\"}],\"field_pid\":[],\"field_resource_type\":[],\"field_rights\":[],\"field_subject\":[]}\n

To patch an object and make it part of a collection, you need the id number of the collection object. In this example node 2 will be our collection.

target_type can be a confusing one, if you are ever unsure have a look at the returned values for an existing object.

> curl -i -u admin:islandora -H\"Content-type: application/json\" -X PATCH -d '{ \"type\": [{\"target_id\": \"islandora_object\"}], \"field_member_of\": [{\"target_id\": 2, \"target_type\": \"node_type\"}]}' 'http://localhost:8000/node/3?_format=json'\n\nHTTP/1.1 200 OK\nDate: Mon, 11 Mar 2019 18:01:40 GMT\nServer: Apache/2.4.18 (Ubuntu)\nX-Powered-By: PHP/7.1.26-1+ubuntu16.04.1+deb.sury.org+1\nCache-Control: must-revalidate, no-cache, private\nX-UA-Compatible: IE=edge\nContent-language: en\nX-Content-Type-Options: nosniff\nX-Frame-Options: SAMEORIGIN\nExpires: Sun, 19 Nov 1978 05:00:00 GMT\nVary:\nX-Generator: Drupal 8 (https://www.drupal.org)\nContent-Length: 1669\nContent-Type: application/json\n\n{\"nid\":[{\"value\":3}],\"uuid\":[{\"value\":\"3f618cdc-3f2a-4e77-b932-9ff1d461a57a\"}],\"vid\":[{\"value\":4}],\"langcode\":[{\"value\":\"en\"}],\"type\":[{\"target_id\":\"islandora_object\",\"target_type\":\"node_type\",\"target_uuid\":\"62189bec-3ef3-4196-b847-b17e5ce61fd5\"}],\"revision_timestamp\":[{\"value\":\"2019-03-11T17:36:10+00:00\",\"format\":\"Y-m-d\\\\TH:i:sP\"}],\"revision_uid\":[{\"target_id\":1,\"target_type\":\"user\",\"target_uuid\":\"46a47057-de2d-4ce2-87ae-dbe4551209b8\",\"url\":\"\\/user\\/1\"}],\"revision_log\":[],\"status\":[{\"value\":true}],\"title\":[{\"value\":\"Updated with a PATCH request\"}],\"uid\":[{\"target_id\":1,\"target_type\":\"user\",\"target_uuid\":\"46a47057-de2d-4ce2-87ae-dbe4551209b8\",\"url\":\"\\/user\\/1\"}],\"created\":[{\"value\":\"2019-03-05T18:07:00+00:00\",\"format\":\"Y-m-d\\\\TH:i:sP\"}],\"changed\":[{\"value\":\"2019-03-11T18:01:40+00:00\",\"format\":\"Y-m-d\\\\TH:i:sP\"}],\"promote\":[{\"value\":true}],\"sticky\":[{\"value\":false}],\"default_langcode\":[{\"value\":true}],\"revision_translation_affected\":[{\"value\":true}],\"content_translation_source\":[{\"value\":\"und\"}],\"content_translation_outdated\":[{\"value\":false}],\"field_access_terms\":[],\"field_alternative_title\":[],\"field_description\":[],\"field_display_hints\":[],\"field_edtf_date\":[],\"field_edtf_date_created\":[],\"field_edtf_date_issued\":[],\"field_extent\":[{\"value\":\"1 item\"}],\"field_identifier\":[],\"field_linked_agent\":[],\"field_member_of\":[{\"target_id\":2,\"target_type\":\"node\",\"target_uuid\":\"413135a6-0bd1-4d6b-8bcb-059cf7784d83\",\"url\":\"\\/node\\/2\"}],\"field_model\":[{\"target_id\":22,\"target_type\":\"taxonomy_term\",\"target_uuid\":\"e1f167e1-124d-4db4-96ab-30641ca4e21b\",\"url\":\"\\/taxonomy\\/term\\/22\"}],\"field_pid\":[],\"field_resource_type\":[],\"field_rights\":[],\"field_subject\":[]}\n
"},{"location":"technical-documentation/rest-signposting/","title":"Signposting","text":"

Signposting is a technique used in RESTful APIs where other relevant resources are exposed to clients as Link headers in GET and HEAD requests. These Link headers follow a standard format as specified in RFC8288. Drupal already makes use of this technique for content entities, and Islandora takes it even further by providing additional Link headers that enable the client to navigate the repository and discover additional information about various resources. Because the links are returned in response headers, they can be relied upon without having to parse the message body. This makes them consistent across all serialization formats that can be returned in a message body (XML, JSON, JSONLD, etc...).

As a general precaution, link headers for Drupal entities are not exposed to users that do not have the permissions to view the entity linked in the header. So making GET and HEAD requests anonymously will yield a different set of headers than what an authenticated user would see. For example, anonymous users don't have the view media permission, so they will not see the link headers for media associated with a node.

"},{"location":"technical-documentation/rest-signposting/#link-headers-provided-by-islandora","title":"Link Headers Provided by Islandora","text":""},{"location":"technical-documentation/rest-signposting/#alternate-representations","title":"Alternate Representations","text":"

Other representations generated by different serializers available through Drupal's REST API are exposed as link headers with rel=\"alternate\" and type equal to the mimetype that will be received when dereferencing the link. For example, if an entity in Drupal has a JSONLD representation, then the link header returned in a GET or HEAD response would look like

Link: <http://example.org/node/1?_format=jsonld>; rel=\"alternate\"; type=\"application/ld+json\"

"},{"location":"technical-documentation/rest-signposting/#referenced-entities","title":"Referenced Entities","text":"

Entity reference fields are exposed as link headers with rel=\"related\" and a title equal to the entity reference field's display label. For example, if http://example.org/node/1 has an entity reference field name \"Associated Content\" that references http://example.org/node/2, then the link header returned in a GET or HEAD response would look like

Link: <http://example.org/node/2>; rel=\"related\"; title=\"Associated Content\"

"},{"location":"technical-documentation/rest-signposting/#referenced-taxonomy-terms","title":"Referenced Taxonomy Terms","text":"

Entity reference fields for taxonomy terms get special handling. The taxonomy terms used to tag content are exposed as link headers with rel=\"tag\" and a title equal to the taxonomy term's display label. If the term has an external URI in a controlled vocabulary, then that URI is provided. Otherwise, the local Drupal URI is provided. For example, if a piece of content is tagged with taxonomy/term/1, which has a display label of \"Example Term\", then the link header returned in a GET or HEAD response would look like

Link: <http://example.org/taxonomy/term/1>; rel=\"tag\"; title=\"Example Term\"

If instead the term were to have the field_external_uri field with a value of http://exampletwo.org/vocab#term then the link header would look like

Link: <http://exampletwo.org/vocab#term>; rel=\"tag\"; title=\"Example Term\".

"},{"location":"technical-documentation/rest-signposting/#associated-media","title":"Associated Media","text":"

Media entities belonging to nodes are exposed as link headers with rel=\"related\" and a title equal to the display label of their field_media_use taxonomy term. For example, if a media is tagged as Original File indicating that it is the initial file uploaded, the link header returned in a GET or HEAD response for a node would look like

Link: <http://example.org/media/1>; rel=\"related\"; title=\"Original File\".

"},{"location":"technical-documentation/rest-signposting/#source-files","title":"Source Files","text":"

Files that are the source for media entities are exposed as Link headers in the GET and HEAD responses with rel=\"describes\". The endpoint to edit the contents of the source file is also exposed using rel=\"edit-media\". For example, if http://example.org/media/1 has the source file http://example.org/file.txt, then a GET or HEAD response would contain both

  • Link: <http://example.org/file.txt>; rel=\"describes\"
  • Link: <http://example.org/media/1/source>; rel=\"edit-media\"
"},{"location":"technical-documentation/rest-signposting/#examples","title":"Examples","text":""},{"location":"technical-documentation/rest-signposting/#requesting-a-node","title":"Requesting a Node","text":"

After creating a node, adding it to a Collection, uploading a file and kicking off derivatives, the link headers returned for said node would look like the following. Note that non-Link headers have been removed for brevity:

vagrant@claw:~$ curl -I http://localhost:8000/node/1?_format=json\nHTTP/1.1 200 OK\n...\n# These are provided by Drupal core\nLink: <http://localhost:8000/node/1>; rel=\"canonical\"\nLink: <http://localhost:8000/node/1/delete>; rel=\"https://drupal.org/link-relations/delete-form\"\nLink: <http://localhost:8000/admin/content/node/delete?node=1>; rel=\"https://drupal.org/link-relations/delete-multiple-form\"\nLink: <http://localhost:8000/node/1/edit>; rel=\"edit-form\"\nLink: <http://localhost:8000/node/1/revisions>; rel=\"version-history\"\nLink: <http://localhost:8000/node/1>; rel=\"https://drupal.org/link-relations/revision\"\nLink: <http://localhost:8000/node?node=1>; rel=\"https://drupal.org/link-relations/create\"\n# These are provided by Islandora\nLink: <http://localhost:8000/node/2>; rel=\"related\"; title=\"Member of\"\nLink: <http://purl.org/coar/resource_type/c_c513>; rel=\"tag\"; title=\"Image\"\nLink: <http://localhost:8000/media/1>; rel=\"related\"; title=\"Original File\"\nLink: <http://localhost:8000/media/2>; rel=\"related\"; title=\"Service File\"\nLink: <http://localhost:8000/media/3>; rel=\"related\"; title=\"Thumbnail Image\"\nLink: <http://localhost:8000/node/1?_format=jsonld>; rel=\"alternate\"; type=\"application/ld+json\"\n
"},{"location":"technical-documentation/rest-signposting/#requesting-a-media","title":"Requesting a Media","text":"

If we were to inspect one of the Media associated with this node (which we would've gotten in the response above), the results would look like:

vagrant@claw:~$ curl -I http://localhost:8000/media/1?_format=json\nHTTP/1.1 200 OK\n...\n\n# These are provided by Drupal core\nLink: <http://localhost:8000/media/add>; rel=\"https://drupal.org/link-relations/add-page\"\nLink: <http://localhost:8000/media/add/image>; rel=\"https://drupal.org/link-relations/add-form\"\nLink: <http://localhost:8000/media/1>; rel=\"canonical\"\nLink: <http://localhost:8000/admin/content/media>; rel=\"collection\"\nLink: <http://localhost:8000/media/1/delete>; rel=\"https://drupal.org/link-relations/delete-form\"\nLink: <http://localhost:8000/media/delete?media=1>; rel=\"https://drupal.org/link-relations/delete-multiple-form\"\nLink: <http://localhost:8000/media/1/edit>; rel=\"edit-form\"\nLink: <http://localhost:8000/media/1>; rel=\"https://drupal.org/link-relations/revision\"\n# These are provided by Islandora\nLink: <http://localhost:8000/node/1>; rel=\"related\"; title=\"Media of\"\nLink: <http://pcdm.org/use#OriginalFile>; rel=\"tag\"; title=\"Original File\"\nLink: <http://localhost:8000/media/1?_format=jsonld>; rel=\"alternate\"; type=\"application/ld+json\"\nLink: <http://localhost:8000/media/1/source>; rel=\"edit-media\"\nLink: <http://localhost:8000/_flysystem/fedora/2019-03/IF-Org-Chart_0.jpg>; rel=\"describes\"; type=\"image/jpeg\"\n
"},{"location":"technical-documentation/running-automated-tests/","title":"Running Automated Tests","text":"

See the Drupal Documentation on PHPUnit in Drupal.

"},{"location":"technical-documentation/running-automated-tests/#setting-up-phpunit","title":"Setting up PhpUnit","text":"

Before you can use phpunit, you must first install the following:

composer require --dev -W phpspec/prophecy-phpunit drupal/core-dev

In ISLE, you need to make the database port available to PHPUnit. To do that, edit docker-compose.yml and find the section including image: islandora/mariadb[version number]. Shortly below is a labels: heading; set the value of the traefik.enable: to \"true\". Apply the changes made to the docker_compose.yml using docker compose up -d.

Follow the Configure PHPUnit and Create a directory for HTML output sections in Drupal Documentation on running phpunit tests to make a phpunit.xml file. Note that:

  • If you place the phpunit.xml file in any directory other than [drupal root]/web/core, you need to change the 'bootstrap' in the <phpunit> tag near the top of the file to point to the relative or absolute location of the [drupal root]/web/core folder.

  • When setting the SIMPLETEST_DB database credentials in ISLE,

    • the default username and db_name are drupal_default
    • your db_password can be found in codebase/web/sites/default/settings.php
  • Unless you changed the default values, just swap out [password] for your actual db password in the following:

mysql://drupal_default:[password]@islandora.traefik.me:3306/drupal_default`.\n
"},{"location":"technical-documentation/running-automated-tests/#running-phpunit","title":"Running PHPUnit","text":"

If you are in the Drupal root directory (codebase on ISLE; the one containing web) and your phpunit.xml file is also in the Drupal root directory, use the following command to run phpunit for a single test file (here, Islandora's DeleteNodeWithMediaAndFile.php):

vendor/bin/phpunit web/modules/contrib/islandora/tests/src/Functional/DeleteNodeWithMediaAndFile.php

If your phpunit.xml is in a different directory, such as web/core, then use the -c flag to specify the path to the directory containing phpunit.xml:

vendor/bin/phpunit -c web/core web/modules/contrib/islandora/tests/src/Functional/DeleteNodeWithMediaAndFile.php

"},{"location":"technical-documentation/running-automated-tests/#setting-up-phpunit-in-phpstorm","title":"Setting up PHPUnit in PHPStorm","text":"
  • Drupal Documentation on running phpunit tests
"},{"location":"technical-documentation/testing-notes/","title":"Testing Notes","text":""},{"location":"technical-documentation/testing-notes/#how-to-find-things-in-fedora","title":"How to find things in Fedora?","text":"

For any Drupal URI, the corresponding Fedora URIs is computed by the Crayfish Commons library using the Drupal-assigned UUID. In Drupal, a \"pseudo-field\" is available that will display the corresponding Fedora URI on the page of a node, taxonomy term, or media object. The presence of this URI does NOT guarantee the existence of the corresponding entity in Fedora.

"},{"location":"technical-documentation/testing-notes/#enabling-the-fedora-uri-pseudo-field","title":"Enabling the Fedora URI Pseudo-field","text":"

To display the Fedora URI pseudo-field on a Drupal node, media, or taxonomy term, go to Manage > Configuration > Islandora and select all the bundles for which you would like the Fedora URI displayed. Once you have selected the bundles, and cleared the cache, the new pseudo-field will appear at the bottom of all display modes. You can alter where in the display the Fedora URI field appears, by going to the \"Manage Display\" page for the bundle. For example, for a Repository Item, you'd go to Manage > Structure > Content Types, and under the dropdown for \"Repository Item, select \"Manage Display\". In that list you should see Fedora URI which you can move around (or hide) as desired. This will need to be repeated in each Display mode (tab). Clearing cache may be necessary to refresh the node display.

Note: This information used to be stored in a service called Gemini, which kept track of corresponding minted Fedora URIs and their minted equivalents. Gemini was removed for Islandora 2.0.0.

"},{"location":"technical-documentation/testing-notes/#how-do-i-search-for-a-object-in-the-solr","title":"How do I search for a object in the Solr?","text":"
  • Go to http://localhost:8983/solr/#/islandora/query
  • Issue a Solr query.

Example

ss_search_api_id:\"entity:node/4:en\"\n

"},{"location":"technical-documentation/testing-notes/#sample-triplestore-queries","title":"Sample Triplestore queries","text":"
  • Go to http://localhost:8080/bigdata/#query
  • Under namespaces (http://localhost:8080/bigdata/#namespaces), make sure islandora is selected.
"},{"location":"technical-documentation/testing-notes/#find-all-triples-with-given-object-as-the-subject","title":"Find all triples with given object as the subject","text":"
select ?p ?o  where { <drupal_url> ?p ?o }\n

Example:

select ?p ?o  where { <http://localhost:8000/media/8?_format=jsonld> ?p ?o }\n
"},{"location":"technical-documentation/testing-notes/#getting-objects-in-a-collection","title":"Getting objects in a collection","text":"
select ?s where { ?s <http://pcdm.org/models#memberOf> <drual_url_of_the_collection?_format=jsonld> }\n

Example:

select ?s where { ?s <http://pcdm.org/models#memberOf> <http://localhost:8000/node/7?_format=jsonld> }\n
"},{"location":"technical-documentation/testing-notes/#find-all-mediafiles-belonging-to-a-node","title":"Find all media/files belonging to a node","text":"
select ?s where { ?s <http://pcdm.org/models#fileOf> <drupal_url_of_the_object?_format=jsonld> }\n

Example:

select ?s where { ?s <http://pcdm.org/models#fileOf> <http://localhost:8000/node/4?_format=jsonld> }\n
"},{"location":"technical-documentation/updating-drupal/","title":"Updating Drupal","text":""},{"location":"technical-documentation/updating-drupal/#introduction","title":"Introduction","text":"

This section describes how to update Drupal and its modules using Composer. If you installed Islandora using the Islandora Playbook or ISLE, then your Drupal was installed by Composer, so it is best practice to continue using Composer for updates. The method on this section is not specific to Islandora, and does not (yet) include how to update Islandora Features.

How to upgrade Drupal in ISLE

For specific instructions on how to upgrade Drupal core and the Drupal modules installed within ISLE, please refer to the documentation page: Maintaining Your Drupal Site

"},{"location":"technical-documentation/updating-drupal/#what-is-composer","title":"What is Composer","text":"

It is recommended by Drupal.org and the Islandora community to use Composer with Drupal for various tasks.

\"Composer is a dependency manager for PHP. Drupal core uses Composer to manage core dependencies like Symfony components and Guzzle.\" [Source]

"},{"location":"technical-documentation/updating-drupal/#always-create-backs-ups-db-and-files-before-updating","title":"Always create backs ups (DB and files) before updating","text":"

Before updating either Drupal core or Drupal modules:

  • Back up both your files and database. Having a complete backup makes it easy to revert to the prior version if the update fails.
  • Optionally, if you made manual modifications to files like .htaccess, composer.json, or robots.txt, copy them somewhere easy to find. Because after you've installed the new Drupal core, you will need to re-apply the changes. For example, Acquia Dev Desktop places a .htaccess file in the top-level directory and without it, only the homepage on your site will work.

Warning: Always revert to a backup if you get a fatal error in the update process.

"},{"location":"technical-documentation/updating-drupal/#updating-drupal-core","title":"Updating Drupal Core","text":"

Over time new versions of Drupal \u201ccore\u201d are released, and Islandora users are encouraged to install official Drupal core updates and security patches. On the other hand \u201calpha\u201d and \u201cbeta\" versions of Drupal core should only be installed by advanced users for testing purposes.

The Islandora community STRONGLY recommends that the \"Composer\" method of upgrading Drupal core be used with Islandora as mentioned here.

"},{"location":"technical-documentation/updating-drupal/#here-is-an-overview-of-the-steps-for-updating-drupal-core-using-composer","title":"Here is an overview of the steps for updating Drupal core using Composer","text":"

Back Up

First make sure you have made database and file back ups.

1) First, verify that an update of Drupal core actually is available:

composer outdated \"drupal/*\"

If there is no line starting with drupal/core, Composer isn't aware of any update. If there is an update, continue with the commands below.

2) Assuming you are used to updating Drupal and know all the precautions that you should take, the update is as simple as:

composer update drupal/core --with-dependencies

If you want to know all packages that will be updated by the update command, use the --dry-run option first.

Alternate syntax for Islandora 8 needed

If you are running the older Islandora 8 codebase that predates the Islandora 2 release, note that Islandora 8 is configured to use a fork of drupal-composer/drupal-project which requires this specific composer syntax compared to other Drupal 8+ sites:

composer update drupal/core webflo/drupal-core-require-dev \"symfony/*\" --with-dependencies

In addition, if you are upgrading from 8.5 to 8.7, you need to replace \"~8.5.x\" with \"^8.7.0\" for drupal/core and webflo/drupal-core-require-dev in composer.json. [Source]

3) Apply any required database updates using drush updatedb, or use the web admin user interface.

drush updatedb

4) Clear the cache using drush cache:rebuild, or use the web admin user interface.

drush cache:rebuild

For stepwise update instructions visit this page: https://www.drupal.org/docs/8/update/update-core-via-composer#s-stepwise-update-instructions

"},{"location":"technical-documentation/updating-drupal/#updating-drupal-modules","title":"Updating Drupal Modules","text":"

Islandora uses several general Drupal modules and some specialized Islandora Drupal modules, and over time new versions of these modules are released. There are two approaches to updating Drupal modules in Islandora: using Composer or updating modules individually. Islandora uses Composer to determine which Drupal module versions should be installed for each release of Islandora. Therefore if you update the Islandora specific Drupal modules using Composer you will also update any dependent general Drupal modules as well. The second method is to individually update Drupal modules.

For more information about how to update Drupal modules visit:

https://www.drupal.org/docs/8/extending-drupal-8/updating-modules

Back Up

First make sure you have made database and file back ups.

"},{"location":"technical-documentation/using-rest-endpoints/","title":"Islandora via REST","text":"

Each node, media and file in Drupal has its own URI and we can GET the resources, some in a variety of formats.

We can also create nodes, media and files in Drupal by using PUT and/or POST requests.

We can update the field content by using a PATCH request and lastly we can DELETE a node, media or file resource.

To perform these actions, you will need the RESTful Web Services module enabled.

To configure your setup via the UI, you also need the RESTful UI module enabled.

Then you can configure your REST services at https://<yourmachine>/admin/config/services/rest

This screenshot shows the setup for resources, you can see the various HTTP methods and what formats they will respond in and what authentication methods they support.

  1. Authorization
  2. Getting resources - GET
  3. Creating resources - POST/PUT
  4. Updating resources - PATCH
  5. Deleting resources - DELETE
"},{"location":"technical-documentation/using-rest-endpoints/#further-reading","title":"Further Reading","text":"
  • RESTful Web Services API overview
"},{"location":"technical-documentation/versioning/","title":"Versioning Policy","text":"

Islandora uses semantic versioning for all non-Legacy code in the github.com/Islandora organization. This allows us to be compatible with Composer and with Drupal's release naming conventions for contributed modules.

"},{"location":"technical-documentation/versioning/#semantic-versioning","title":"Semantic Versioning","text":"

Semantic Versioning is a common versioning standard. Versions have the form: Major version, Minor version, or Patch.

  • Major version: Major changes, and breaks the API.
  • Minor version: New features, and does not break the API.
  • Patch: Bug fixes, and never breaks backward compatibility.
"},{"location":"technical-documentation/versioning/#examples","title":"Examples","text":"
  • 1.2.3 => 1.2.4 - Just a bug fix, should be a drop-in replacement.
  • 1.2.3 => 1.3.0 - Adds in new features, should be a drop-in replacement to get new functionality.
  • 1.2.3 => 2.0.0 - Major changes, may require a migration or changes to your set-up.
"},{"location":"technical-documentation/versioning/#repositories-under-semantic-versioning","title":"Repositories under semantic versioning","text":"

The following Islandora components use semantic versioning:

  • Alpaca
  • Chullo
  • Crayfish
  • Crayfish Commons
  • Syn
  • controlled_access_terms (Drupal module)
  • islandora (Drupal module)
  • jsonld (Drupal module)
  • openseadragon (Drupal module)
  • islandora_mirador (Drupal module)

Drupal submodules, which are included in several of the above modules, share versions with their parents.

Drupal module versions switched from 8.x-1.x to 2.x

In October 2021, Islandora switched from the \"core compatibility\" based numbering scheme (8.x-1.x) to a pure semantic versioning scheme for its Drupal modules. In accordance with Drupal's requirements, this transition required us to bump the major version, from 1.x to 2.x, despite there not being any major API-breaking changes to the code itself.

"},{"location":"technical-documentation/versioning/#implications-for-release-process","title":"Implications for Release Process","text":"

Committers should now create (i.e. \"tag\") new versions of components when new bug fixes, features, or API changes are successfully added. This means that \"releases\" (new versions) will be happening individually, continually, and far more frequently than before. See Releasing Islandora.

"},{"location":"technical-documentation/versioning/#module-interdependencies","title":"Module Interdependencies","text":"

When Islandora components require other Islandora components in their composer.json files, we prefer the version specification syntax ^2 to point to the latest-released compatible version within the specified major version.

"},{"location":"tutorials/blocks/","title":"Using Blocks","text":"

In Drupal, blocks are snippets of content that can be placed on pages within your website's layout. Some examples of blocks are your site's menu, breadcrumbs, search block, etc... You have a lot of control over when and where you see blocks using Drupal's administrative interface. You can create and delete blocks, as well as move them around in different locations on the page (called 'regions' in Drupal theme terminology). You can also control on which pages the blocks are visible, allowing you to conditionally show/hide them as appropriate. Blocks are made available to place by enabling the Drupal modules that provide them, and there's lots out there to choose from. Placing blocks is an essential skill for any site administrator, so let's jump right in and get our hands dirty. There are two methods discussed here: the \"Block Layout\" interface, and using \"Contexts\". There is a third option, \"Display Suite,\" that is not discussed here.

"},{"location":"tutorials/blocks/#using-block-layout","title":"Using Block Layout","text":"

Using the admin toolbar, navigate to Admin > Structure > Block layout (admin/structure/block). You will see a table listing all of the available regions provided by the current theme for block placement. You can switch between available themes using the tabs above the table. For each region, enabled blocks are listed and can be re-arranged. Take a look at the \"Main Content\" region for example.

If you wanted to move the content above its tabs, you could drag the \"Main page content\" block above the \"Tabs\" block. If you scroll all the way down to the bottom and click the \"Save blocks\" button, now when you go to view content you should see something like this

If you want to add a new block to a region, hit the \"Place block\" button for that region and you'll be brought to a modal that will let you select which block to place.

After selecting your block, you will be brought to its configuration form.

Here you can give the block a different title, change its region using a dropdown, and control its visibility settings by configuring various conditions. In our case, we're adding the \"Development\" block to the \"Footer\" region. And since we want to see this block everywhere, we'll leave the visibility settings alone for now. Click the \"Save block\" button, and now if we go view some content we should see the \"Development\" menu in the footer of our site.

If we want to limit this eyesore to only the front page of our site, we can revisit the visibility settings of the block. Navigate to the block we just placed on the block placement page, and click its \"Configure\" button. Here you can limit the block to show only for certain roles or content types. You can also explicitly set a white or black list of pages, which is what we'll do.

Click the Pages tab and you'll have a text area you can list pages in. We're going to enter <front>, which is a special value that means \"The front page of your site\". Underneath the text area, you can select whether you are showing or hiding the block for this list of pages. We want to show this only on the front page, so we'll leave it set at \"Show for the listed pages\". Click \"Save block\" and go back to a piece of content. You shouldn't see the development menu.

But go to the frontpage and...

"},{"location":"tutorials/blocks/#using-context","title":"Using Context","text":"

The visibility conditions in the block placement user interface are simple and effective. They cover the majority of use cases, however, there are times when you need even more control. This is where the Context module really shines. Any condition available to the Context module can be used to control block visibility. And you can even combine conditions using Boolean (AND/OR) logic.

For example, in the Islandora Starter Site, we provide a context specifically for showing a list of members of a collection. All of our content is the same content type (Repository Item), and Collections are distinguished by being tagged with the \"Collection\" taxonomy term. Using the admin toolbar, if you navigate to Admin > Structure > Context (admin/structure/context), you should see a list of all contexts available to the site.

Click the \"Configure\" button on the \"Collection\" context.

You can see in its \"Condition\" section, we have the \"Node has term\" condition, which checks for content tagged with the term provided by the user. In this case, it's set to look for the \"Collection\" tag. Underneath, in the \"Reaction\" section, you can see that the \"Block\" reaction has been selected, and its configuration looks a lot like the core block placement UI. The only difference here is that when the \"Include blocks from block layout\" box is checked, you are only placing the blocks you want to add in addition to an already existing block layout. If you uncheck the box, you are creating an entirely new block layout from scratch, and will have to replace basic things like breadcrumbs and menus.

Here you can see we've added only the members block to this block layout. If we go and navigate to a collection, you'll see its list of members underneath its content.

If we edit the collection and change its model tag from \"Collection\" to anything else, you'll see the block disappears!

As you can see, block placement can be very flexible, and allow you to customize your site in a very granular fashion. Between using core block placement and the context module, there's no block you can't get into the right place on your site.

"},{"location":"tutorials/blocks/#video-walkthrough-blocks","title":"Video Walkthrough: Blocks","text":"

Click the image below to open the Blocks video tutorial on the Islandora Youtube channel.

See more videos from the Drupal 101 series here.

Islandora Quick Lessons (2020)

Learn more with this video on Creating a Custom Block.

"},{"location":"tutorials/create-a-resource-node/","title":"Create a Resource Node","text":""},{"location":"tutorials/create-a-resource-node/#overview","title":"Overview","text":"

This tutorial will walk you through adding a piece of digital content (an image and some metadata) into an Islandora repository, creating an Islandora Resource Node. At the end, you will have metadata in a Drupal node, a file stored in Fedora, and derivatives automatically created.

Video version available

The material in this tutorial is presented in our video, Adding Content.

"},{"location":"tutorials/create-a-resource-node/#tools","title":"Tools","text":"

For this tutorial you will need an instance of Islandora Starter Site. You can also use the online sandbox or one of the sandbox VMs.

Online Sandbox

login credentials for online sandbox

"},{"location":"tutorials/create-a-resource-node/#step-1-create-an-islandora-node","title":"Step 1: Create an Islandora Node","text":"

To add content to Islandora, start by creating a node. When logged in to Drupal as an administrator, navigate with the top menu bar to Manage >> Content >> Add content or click on Add content under the Tools menu.

Next, select a content type that is configured with Islandora behaviours. In the Islandora Starter Site, there is an Islandora content type. It is called Repository Item.

At the top of the form, the Model field is a mandatory Islandora field. Select Image from the Model drop box. This will enable image display behaviours, and cause image derivatives to be generated once we upload a file.

The rest of the form contains a large number of metadata fields, grouped into collapsible sections. Fill out the Title (mandatory) and whichever other fields are applicable to you.

Keep it simple

For this tutorial, do not attempt to fill out any fields with an autocomplete symbol - a small circle at the right-hand-side of the text box.

Then, click Save. You have now created a node, and since it is of an Islandora content type, we call it a \"resource node.\"

The new node page displays, with the title, any metadata that we entered, and a Fedora URI which you can click on to see if this new node has been synced into Fedora (this only works if you have permission to access Fedora). Congratulations, you have created a resource node!

"},{"location":"tutorials/create-a-resource-node/#step-2-upload-an-original-file","title":"Step 2: Upload an Original File","text":"

Alas, your node has no files. To upload a file, click on the node's Media tab.

Concept: Media tab

The \"Media\" tab shows Media that we say \"belong to\" or are \"media of\" that node. The idea of media \"belonging to\" nodes, and the \"Media\" tab, are part of Islandora but not part of standard Drupal.

The Media tab shows nothing listed, because this node has no files. So click on Add Media. You will be taken to a list of media types to choose from.

We want to add a jpg image, so click on Image. You are now presented with a form to create a media.

Images vs TIFFs

Drupal considers any type of image that can be viewed natively in the browser as an Image. For other image types that require special viewers, such as TIFFs, you would have to choose File. Learn more about Media in Islandora.

This form contains fields for metadata about the file, including \"technical metadata\". There are three important parts of the form:

  1. Name, which identifies this media.
  2. Image, the file to upload, and its alternative text.
  3. Media Use, describing what this file is in the context of its resource node. To trigger derivative generation, select the checkbox for Original File.

With the mandatory fields filled out (do not edit the \"Media of\" field), click Save, and a new Media will be created attached to your resource node. You will land on the administrator's list of Media (i.e. all media on your site, not just those attached to the resource node), and the new media object you created is at the top of the list.

Select the Media you just created, and you will see a page containing that Media's information and a rendering of the image that you uploaded.

"},{"location":"tutorials/create-a-resource-node/#step-3-verify-derivatives-were-created","title":"Step 3: Verify derivatives were created","text":"

Now return to the node you created, and you will see the image along with its (minimal) descriptive metadata. The image you are viewing is not the one you uploaded, but a lower-resolution service file that was created and stored in the Drupal public filesystem.

If you open the node's Media tab, you should see new Media have been added - these are derivatives have been created automatically, including a service file, a thumbnail, and a FITS technical metadata file.

You now have an Islandora resource node, which is a special case of a Drupal node, and it has Media attached to it including an Original File, Thumbnail, Service File, and FITS Technical Metadata file.

"},{"location":"tutorials/create-a-resource-node/#learn-more","title":"Learn More","text":"
  • Drupal documentation on Nodes
  • Drupal documentation on Media.
"},{"location":"tutorials/create-update-views/","title":"Create or Update a View","text":""},{"location":"tutorials/create-update-views/#overview","title":"Overview","text":"

Views are powerful content filters that enable you to present Islandora (and other) content in interesting and exciting ways. For more documentation on views:

  • Drupal.org documentation on Views
"},{"location":"tutorials/create-update-views/#before-you-start","title":"Before you start","text":"
  • The following How-To assumes that you are using the (optional) Islandora Starter Site configuration. This configuration is deployed automatically if you build your Islandora site using the Ansible Playbook, ISLE, or are using the sandbox or a Virtual Machine Image
  • This How-To assumes familiarity with Drupal terms such as Node, Content Type, and Media.
"},{"location":"tutorials/create-update-views/#how-to-modify-an-existing-view","title":"How to modify an existing view","text":"

Islandora Starter Site ships with some views already created and turned on. The Islandora home page displays content items that have been added to Islandora. This view is named Frontpage, and it lists items that meet the following filter criteria:

  • The item is in the published state.
  • The checkbox Promoted to front page is selected.

This view will display all content items added to Islandora, as the checkbox Promoted to front page is on by default.

As you develop your Islandora Website it is likely that you will need to change the default behaviour of the Frontpage View. As an example, the following describes how to edit the Frontpage page view to only show content items and not collections.

For this example, we added six collection items to Islandora. In total there are eight items in the repository. In addition to the six collection items, there is one audio item and one image item.

  1. Using your Web browser, open the Islandora front page.
  2. To edit the front page view, hover over the view (Frontpage view) and select Edit view when displayed.

  3. Select Add under the filter criteria section.

  4. We do not want to display collections, so we need to add a filter criteria that does not filter for the Islandora model type 'Collection'.

    1. Select Model from the list and then Apply (all displays).

    2. Select Islandora Model to select filters on Islandora model types and select Apply and continue.

    3. Select the operator Is none of and the Collection model (autocomplete should work here to help you). To finish click Apply (all displays).

    4. Save the view. Now the 'Frontpage' View does not display collections.

"},{"location":"tutorials/create-update-views/#how-to-create-a-new-view","title":"How to create a new view","text":"

For this example, we create a new view that only shows collections. It will be created as a Block (also see the tutorial on Configuring Blocks) that will only display on the front page. We will add the new collection list block below the existing frontpage view that lists items.

  1. Using your Web browser, open the Islandora front page
  2. Navigate to Administration >> Structure >> Views
  3. Create a new view by selecting Add view
  4. Name the view and select Create a block. Give the block a title and decide how you want it to display (Grid, Table, List, Paging). To progress, select Save and edit.

    5. Customize the view format and sorting as required. 6. Add a filter criteria to only show the Islandora model type of 'Collection' and Save the view.

    7. To place the view on front page, the new block must be added to the 'Main page content' area (using 'Block layout') and set to display on the front page. 1. Navigate to Administration >> Structure >> Block layout (/admin/structure/block). Under Main content select Place block

    ![Frontpage view collection list place block](../assets/frontpage_view_collection_list_place_block.png)\n
    1. Find the new block, 'Collection List' and select Place block.
    2. Restrict the block to only display on the frontpage by adding the text '' to the Page vertical tab. Then select Save block.

      4. Review the block placement and move if required.

      8. The 'Collection list' now only displays on the front page. It displays below the Main page content.

      Islandora Quick Lessons

      Learn more with videos on Basic Views and Advanced Views.

      "},{"location":"tutorials/how-to-create-collection/","title":"How to create and add to a Collection","text":"

      This how-to demonstrates creating a collection and adding items to it in the Islandora Starter Site. For more about collections, see Concept: Collection.

      "},{"location":"tutorials/how-to-create-collection/#introduction","title":"Introduction","text":"

      In the Islandora Starter Site, nodes that have \"Collection\" in their Model field will show a view of their member (\"child\") objects. A member is any object that is \"Member of\" that object, and can be added via the \"Children\" tab. This is the mechanism in the Islandora Starter Site, and your individual instances may vary.

      Collections and Deleting

      Collections and their members are independent of each other, and removing something from a collection does not delete it. Similarly, deleting a collection does not delete its members.

      "},{"location":"tutorials/how-to-create-collection/#creating-a-collection","title":"Creating a Collection","text":"

      When logged in, click Manage > Content on the admin toolbar. Then, click on Add content.

      Then click on 'Repository Item' to give your collection the default metadata profile for Islandora.

      Fill out the form.

      At the top of the form, select \"Collection\" from the Model dropdown list.

      When done filling out the form, click Save.

      The collection has been created. Now let's add some members to this collection.

      "},{"location":"tutorials/how-to-create-collection/#add-existing-items-to-a-collection","title":"Add Existing Items to a Collection","text":"

      To populate a collection with existing items, return to any existing content and click on its Edit tab. This brings up the form for this item.

      Scroll down to the bottom of the form to find the System section. In the Member of field, start typing in the name of the collection this item should belong to. Select the name of the collection you want from the autocomplete. It is important that you select it from the dropdown, not just type in the correct title, because selecting it causes the node id to appear beside the title and allows Drupal to create a relationship.

      The correct collection is now selected. Click Save when ready.

      To confirm, return to the collection and verify the new item appears in the collection's list of members.

      "},{"location":"tutorials/how-to-create-collection/#add-a-new-item-as-a-member-of-a-collection","title":"Add a New Item as a Member of a Collection","text":"

      To create an item and add it as a member to a collection in one step, visit a collection and click on its Children tab. From the Children tab, you can manage the members of a collection and perform actions on them.

      Click on the +Add Child button, and then select Repository Item as your new item's Content Type. Only content types that have the field_member_of field will be available from this list.

      You are taken to the creation form for a Repository Item, but if you scroll down to the \"System\" section, you should see the widget for \"Member Of\" is already filled out for you with the appropriate collection.

      Click 'Save' at the end of the form to create the new item and add it as a member of the collection.

      Islandora Quick Lessons

      Learn more with this video on Making a Collection.

      "},{"location":"tutorials/switch-homepage-to-twig/","title":"Format Homepage with TWIG","text":""},{"location":"tutorials/switch-homepage-to-twig/#twig-debugging","title":"TWIG Debugging","text":"

      It's helpful to identify which TWIGs are available to use and where they're stored but not required use TWIGs to format the homepage.

      # Copy the default service.\n$ cp web/sites/default/default.services.yml web/sites/default/services.yml\n\n# fix permissions (just in case)\n$ chown nginx:nginx web/sites/default/services.yml\n\n# I use nano to edit but you can pick whichever editor you want.\n# For this example we'll install the editor\n$ apk add nano\n\n# Now open the newly created service file and set these 3 values under the TWIG config section.\n$ nano web/sites/default/services.yml\n\n...yml\ntwig.config:\n  debug: true\nauto_reload: true\ncache: true\n# Now save and exit (in NANO it's CTRL + x)\n
      For a video tutorial on this, see Enabling Twig Debugging in Drupal 8/9

      "},{"location":"tutorials/switch-homepage-to-twig/#copying-templates","title":"Copying Templates","text":"

      Copy the default TWIG into your theme's template directory.

      $ cp web/themes/contrib/bootstrap/templates/node/node.html.twig web/themes/contrib/solid/templates/node--6--full.html.twig\n\n# Clear cache\n$ drush cr\n
      And now if you view the home page's source code you should now see the X next to the loaded TWIG file. Please note that the file name corresponds to the node number. To use the URL alias instead of the node ID requires additional work. Here's a tutorial on this topic.
      <!-- FILE NAME SUGGESTIONS:\n   x node--6--full.html.twig\n   * node--6.html.twig\n   * node--page--full.html.twig\n   * node--page.html.twig\n   * node--full.html.twig\n   * node.html.twig\n-->\n

      Now edit the TWIG file (web/themes/contrib/solid/templates/node--6--full.html.twig) to say whatever you want, and it should show up immediately without needing to clear cache.

      "},{"location":"tutorials/switch-homepage-to-twig/#clean-up","title":"Clean up","text":"

      Don't forget to turn off TWIG debugging in config file (web/sites/default/services.yml). This will likely have unexpected consequences on production system performance.

      twig.config:\n  debug: false\n  auto_reload: false\n
      "},{"location":"user-documentation/access-control/","title":"Access control in Islandora","text":"

      This page is about controlling who can view or edit your Islandora content (metadata and binaries).

      Islandora recommends using Drupal's access control features. Contributed modules such as those described below, can provide additional flexibility and configurability. However, these only apply when content is accessed through Drupal, and are not applied to data in Fedora, the Triplestore, or Solr.

      Known strategies for implementing access control in Drupal include:

      • Core Drupal
      • Group
      • Permissions by Term
      • Field Permissions

      Exposed Endpoints

      If access control is a concern, you will want to lock down access to the following services that are part of the Islandora Suite:

      • Fedora (REST API and admin client)
      • Solr (API and admin client)
      • Blazegraph (API and admin client)

      as anyone accessing data through those services are bypassing Drupal's access control. In all out-of-the-box sandboxes and demo instances, these services are wide open.

      "},{"location":"user-documentation/access-control/#access-control-in-core-drupal","title":"Access control in Core Drupal","text":"

      Summary: Core Drupal's access control features include the \"published\"/\"unpublished\" states and some basic permissions that can be granted globally or at the bundle level.

      Drupal's concepts of \"published\" and \"unpublished\" for nodes and media apply to Islandora just as they do in Drupal. Usually, published content is visible to the entire world (without any authentication), while unpublished content can only be accessed by selected users - usually administrators, other privileged roles, and the user who created the content. This is configurable through the Drupal Permissions interface.

      All Drupal permissions are granted to roles, not individual users. Read more documentation on Users, roles, and permissions.

      The extent of the configurability of Drupal Core's access control (excluding revisions) is provided by the following permissions:

      View permissions (apply to all content or all media regardless of bundle) - View all published content - View own unpublished content - View (published) media - View own unpublished media \"Editing\" permissions (can be granted for a specific bundle, or for all nodes or media): - Create new content - Edit (your) own content - Edit any(one's) content - Delete (your) own content - Delete any(one's) content Administrative permission (includes all the above and more, give to trusted users only): - Administer content

      These are the basic access control options built into Drupal Core. There are many contributed modules that do access control, some are described below. Before using access control modules, please see Comparison and Overview of Access Control modules on drupal.org.

      Contributed modules are required for the following cases:

      • individual nodes or media having their own access policies (in Core Drupal, access can only be configured at the content type or media type level)
      • access policies that grant privileges to users (in Core Drupal, access can be granted only to roles, and/or to that content's author)
      • contents of specific \"management\" fields being reserved so that only privileged users can view or edit.
      "},{"location":"user-documentation/access-control/#islandora-resources","title":"Islandora resources","text":"

      Given the [Collection] \u2192 Node \u2192 Media \u2192 File structure of Islandora resources, it is reasonable to wonder if access to a Node, Media, or File is influenced by the permissions on its \"parent\" resource.

      There is not yet a mechanism for Drupal's access control to be \"inherited\" from parent resources to child resources. An exception is non-public files, as described below.

      "},{"location":"user-documentation/access-control/#sidebar-access-to-files-in-drupal","title":"Sidebar: Access to files in Drupal","text":"

      Drupal's access control applies to files except those stored in Drupal's public filesystem.

      If you upload a file to Drupal, it's usually stored in a field on an entity. In Islandora, that entity is a Media. Drupal's access checks mean that a user must have permission to view the Media, and (if configured) the file field on that media, to see the file. However, file fields can be configured with various forms of file storage, and the file storage determines whether access control checks are applied at all. Files stored in Drupal's public filesystem are always public, viewable by anyone in the world.

      Drupal's access control applies to files accessed through Drupal where the file storage is:

      • on Fedora using Flysystem
      • on a Drupal private filesystem
      • on another location such as S3 or Dropbox, through flysystem.

      Direct access to file storage

      Files stored using Flysystem are actually located on a separate service. It is the administrator's responsibility to implement appropriate access control on the target storage system, as there is a way to link directly to files in Fedora, S3, or Dropbox. These direct links bypass Drupal and therefore Drupal's access control.

      The filesystem used by a field is usually configured through \"Manage Fields\" on the appropriate entity. However, it is possible for a field to be configured to use a certain filesystem, but for actual files created through back-end processes to use a different one. For example, derivatives generated by Islandora as configured in the Islandora Starter Site are written to the public filesystem, despite the field configuration which points to Fedora.

      To determine the actual location of a file, right-click the existing file in the Media Edit page and select \"copy location\". A file that is saved in flysystem will include /_flysystem/ in the URL. These links (you can test it!) only work if you have permission to view the media. A file with /sites/default/files/ is probably publicly accessible (this is the public filesystem on most Drupal instances).

      In the Islandora Starter Site, All Derivatives Are Public

      Out of the box using the Islandora Starter Site, derivatives are created for all Islandora media (as long as the conditions of the contexts are met), and they are stored in the public Drupal filesystem. This happens even if the media and/or node are not published.

      "},{"location":"user-documentation/access-control/#access-control-in-solr","title":"Access control in Solr","text":"

      The Search API Module, which connects Drupal to Solr, provides configuration so that access considerations are respected by the Drupal solr results display. The \"Processors\" configuration (e.g. at admin/config/search/search-api/index/default_solr_index/processors) provides checkboxes for the following options:

      • \"Content access\": Adds content access checks for nodes and comments
      • \"Entity status\": Exclude inactive users and unpublished entities (which have a \"Published\" state) from being indexed.

      Both are enabled out of the box in the Islandora Starter Site. This will ensure that queries through Drupal never show content that the active user shouldn't see, as well as preventing information about unpublished entities from ever being entered into Solr.

      Solr Admin Client

      Anyone with access to the Solr Admin Client may see the full contents of the index, regardless of permissions. If this is not desired, you should restrict access to the Solr endpoint and GUI. By default, the Islandora Playbook exposes Solr at http://localhost:8983/solr/. ISLE-DC on the other hand can expose or not expose individual services, including Solr. Since December 2021 this has been available as an option in the .env file and since that time, Solr (and some other services) are not exposed by default.

      "},{"location":"user-documentation/access-control/#group-contributed-module","title":"Group (contributed module)","text":"

      \"The Group module allows you to create arbitrary collections of your content and users on your site and grant access control permissions on those collections.\"

      Opinion

      Group is one of the more hefty modules, and is difficult to learn. It is good if you have static or semi-static groups of users who need access to a static or semi-static group of content, but sharing an item with an arbitrary group of users is cumbersome.

      "},{"location":"user-documentation/access-control/#permissions-by-term-contributed-module","title":"Permissions By Term (contributed module)","text":"

      \"The Permissions by Term module extends Drupal by functionality for restricting view and edit access to single nodes via taxonomy terms. [...] Taxonomy term permissions can be coupled to specific user accounts and/or user roles.\" By default, this module only affects nodes. To enable Permissions by Term for Media and other entities, enable the \"Permissions by Entity\" submodule.

      The Islandora Starter Site includes an empty vocabulary called \"Islandora Access\", which is intended to hold such taxonomy terms. However, permissions_by_term or a similar mechanism to control access must be installed and configured on your own.

      This module is known to work, and appears to be supported well by the Drupal community.

      Warning

      This module is known in the Islandora community to cause performance degradation when large numbers of nodes are involved. Seth Shaw describes this in a blog post, Content Access Control Solutions Investigation.

      Examples Wanted

      Do you have experience setting up Permissions By Term? We'd love some illustrative examples here.

      "},{"location":"user-documentation/access-control/#field-permissions-contributed-module","title":"Field Permissions (contributed module)","text":"

      \"The Field Permissions module allows site administrators to set field-level permissions to edit, view and create fields on any entity.\"

      "},{"location":"user-documentation/access-control/#other-contributed-modules","title":"Other contributed modules","text":"

      Workflow Participants allows for granting permissions on individual nodes or media to individual users, as an extension to the Workflow suite of modules. However, it is not well-supported and the \"manage workflow participants\" permission should not be given to untrusted users as it may grant users the ability to add participants even to content they are otherwise not able to see or edit.

      "},{"location":"user-documentation/access-control/#access-control-in-fedora","title":"Access control in Fedora","text":"

      In Fedora, it is possible to control access to resources using Access Control Lists (ACLs) per the Fedora API Specification. ACLs are inherited through Fedora's LDP Containment relationships.

      Islandora does not create customized ACLs when syncing content into Fedora.

      Islandora does not create Fedora resources in hierarchies that use LDP containment relationships.

      At this time, access control in Fedora is NOT reflective of access control in Drupal. In the sandboxes and demo installations, all resources in Fedora are open for the world to view. This includes unpublished nodes and media, as well as the files uploaded into Fedora. Access controls configured in Drupal are not synced in any way to Fedora.

      Sites concerned with access control wish to \"lock down\" their Fedora to only be accessible through Drupal.

      "},{"location":"user-documentation/access-control/#access-control-in-the-triplestore","title":"Access control in the Triplestore","text":"

      Anyone with access to Blazegraph can access all the repository information within Blazegraph. If this is not desired, you should restrict access to the Blazegraph endpoint and GUI. By default, the Islandora Playbook exposes Blazegraph at http://localhost:8080/bigdata/. ISLE-DC on the other hand can expose or not expose individual services, including Blazegraph. Since December 2021 this has been available as an option in the .env file and since that time, Blazegraph (and some other services) are not exposed by default.

      "},{"location":"user-documentation/access-control/#see-also","title":"See Also","text":"

      Meta-Issue: Access Restrictions and Embargoes

      "},{"location":"user-documentation/accessibility/","title":"Accessibility","text":"

      Accessibility is the ability of a site to be used fully by all users, including those using screen reader technologies or keyboard navigation.

      "},{"location":"user-documentation/accessibility/#drupal-documentation","title":"Drupal documentation","text":"

      Islandora's accessibility features are currently provided by Drupal and contributed Drupal modules. These pages within the Accessibility section of the Drupal documentation can provide guidance:

      • Drupal Accessibility Features (included in core Drupal)
      • Contributed Modules for Extending Accessibility in Drupal
      • Hiding Content Properly (for all users including users with screen readers)
      • How to do an accessibility review?
      • List of External Accessibility Resources
      "},{"location":"user-documentation/accessibility/#accessible-themes","title":"Accessible themes","text":"

      Much of the accessibility of a website is dependent on how specific HTML tags and attributes are used, thus falls largely into the realm of Drupal Themes. The Olivero theme and the Claro admin theme were designed by the Drupal community with accessibility as a guiding principle.

      "},{"location":"user-documentation/accessibility/#automatic-alt-text","title":"Automatic alt-text","text":"

      When creating image media, alt-text is a required attribute. An Islandora function currently automatically populates the alt-text, if that media is the \"media of\" a node, with the respective node's title.

      This is not considered good accessibility as the node's title will likely be already on the page, and it doesn't add anything to describing the image itself. Please fill out the alt text with something meaningful when adding image content to Islandora.

      "},{"location":"user-documentation/advanced-search/","title":"Islandora advanced search","text":"
      • Introduction
      • Requirements
      • Installation
      • Configuration
      • Configuring Solr
      • Configure Collection Search
      • Configure Views
      • Collection Search
      • Paging
      • Sorting
      • Configure Facets
      • Include / Exclude Facets
      • Configure Blocks
      • Advanced Search Block
      "},{"location":"user-documentation/advanced-search/#introduction","title":"Introduction","text":"

      Advanced Search adds additional functionality beyond the basic Solr search. It enables the use of Ajax with search blocks, facets, and search results.

      "},{"location":"user-documentation/advanced-search/#requirements","title":"Requirements","text":"

      Use composer to download the required libraries and modules.

      composer require drupal/facets \"^1.3\"\ncomposer require drupal/search_api_solr \"^4.1\"\ncomposer require drupal/search_api \"^1.5\"\n

      However, for reference, islandora_advanced_search requires the following drupal modules:

      • facets
      • search_api_solr
      "},{"location":"user-documentation/advanced-search/#installation","title":"Installation","text":"

      To download/enable just this module, use the following from the command line:

      composer require drupal/advanced_search\ndrush en advanced_search\n
      "},{"location":"user-documentation/advanced-search/#configuration","title":"Configuration","text":"

      You can set the following configuration at Administration >> Configuration >> Advanced Search Settings (admin/config/search/advanced):

      "},{"location":"user-documentation/advanced-search/#configuring-solr","title":"Configuring Solr","text":"

      Please review Configure Search before continuing. The following assumes you already have a working Solr and the Drupal Search API setup.

      "},{"location":"user-documentation/advanced-search/#configure-collection-search","title":"Configure collection search","text":"

      To support collection based searches you need to index the field_member_of for every repository item as well define a new field that captures the full hierarchy of field_member_of for each repository item.

      Add a new Content solr field field_descendant_of to the solr index at admin/config/search/search-api/index/default_solr_index/fields.

      Then under admin/config/search/search-api/index/default_solr_index/processors enable Index hierarchy and set up the new field to index the hierarchy.

      The field can now be used limit a search to all the descendants of a given object.

      Re-Indexing

      You may have to re-index to make sure the field is populated.

      "},{"location":"user-documentation/advanced-search/#configure-views","title":"Configure views","text":"

      The configuration of views is outside the scope of this document, please read the Drupal Documentation, as well as the Search API Documentation.

      "},{"location":"user-documentation/advanced-search/#collection-search","title":"Collection search","text":"

      It will be typical that you require the following Relationships and Contextual Filters when setting up a search view to enable Collection Search searches.

      Here a relationship is set up with Member Of field and we have two contextual filters:

      1. field_member_of (Direct descendants of the Entity)
      2. field_descendant_of (All descendants of the Entity)

      Both of these filters are configured the exact same way.

      These filters are toggled by the Advanced Search block to allow the search to include all descendants or just direct descendants (documented below).

      "},{"location":"user-documentation/advanced-search/#paging","title":"Paging","text":"

      The paging options specified here can have an effect on the pager block (documented below).

      "},{"location":"user-documentation/advanced-search/#sorting","title":"Sorting","text":"

      Additionally, the fields listed as Sort Criteria as Exposed will be made available in the pager block (documented below).

      "},{"location":"user-documentation/advanced-search/#configure-facets","title":"Configure facets","text":"

      The facets can be configured at admin/config/search/facets. Facets are linked to a Source which is a Search API View Display so it will be typically to have to duplicate your configuration for a given facet across each of the displays where you want it to show up.

      "},{"location":"user-documentation/advanced-search/#include-exclude-facets","title":"Include / exclude facets","text":"

      To be able to display exclude facet links as well as include links in the facets block we have to duplicate the configuration for the facet like so.

      Both the include / exclude facets must use the widget List of links that allow the user to include / exclude facets

      The excluded facet also needs the following settings to appear and function correctly.

      The URL alias must match the same value as the included facet except it must be prefixed with ~ character that is what links to the two facets to each other.

      And it must also explicitly be set to exclude:

      You may also want to enable Hide active items and Hide non-narrowing results for a cleaner presentation of facets.

      "},{"location":"user-documentation/advanced-search/#configure-blocks","title":"Configure blocks","text":"

      For each block type:

      • Facet
      • Pager
      • Advanced Search

      There will be one block per View Display. The block should be limited to only appear when the view it was derived from is also being displayed on the same page.

      This requires configuring the visibility of the block as appropriate. For collection based searches be sure to limit the display of the Facets block to the models you want to display the search on, e.g:

      "},{"location":"user-documentation/advanced-search/#advanced-search-block","title":"Advanced search block","text":"

      For any valid search field, you can drag / drop and reorder the fields to display in the advanced search form on. The configuration resides on the block so this can differ across views / displays if need be. Additionally, if the View the block was derived from has multiple contextual filters you can choose which one corresponds to direct children, this will enable the recursive search checkbox.

      "},{"location":"user-documentation/breadcrumbs/","title":"Breadcrumbs","text":"

      Breadcrumbs are a Drupal concept. They provide a hierarchical path of links to \"ancestors\" of the current content item.

      "},{"location":"user-documentation/breadcrumbs/#islandora-breadcrumbs","title":"Islandora Breadcrumbs","text":"

      Islandora provides a module, \"Islandora Breadcrumbs\" (a submodule of the Islandora module) that creates breadcrumbs based on the value of configured reference fields (by default, field_member_of). To use Islandora Breadcrumbs, simply enable the module. Islandora breadcrumbs will apply to nodes that have the configured Entity Reference fields.

      There are a few configuration options for this module, accessible at Manage > Configuration > Islandora > Breadcrumbs Settings (/admin/config/islandora/breadcrumbs). These include:

      • Maximum number of ancestor breadcrumbs - an optional feature to stop adding \"ancestor\" links after a certain number
      • Include the current node in the breadcrumbs?
      • Entity Reference fields to follow - if you're using other fields to refer to parents, you can add them here.

      "},{"location":"user-documentation/breadcrumbs/#troubleshooting-breadcrumbs","title":"Troubleshooting Breadcrumbs","text":"

      Breadcrumbs are cached, so if you aren't seeing the results that you expect, try clearing the Drupal cache.

      "},{"location":"user-documentation/content-models/","title":"Content models in Islandora","text":""},{"location":"user-documentation/content-models/#resource-nodes","title":"Resource Nodes","text":"

      This section describes the Islandora concept of a Resource Node. For a step-by-step demonstration, see the tutorial Create a resource node.

      A resource node holds the descriptive metadata for an Islandora object, as well as groups together the various files that are part of the object for preservation or display, such as the original file and various derivative files generated from it.

      The model for exactly what constitutes an object in Islandora is flexible and can be adapted to the needs of specific users. For example, the Islandora Starter Site configuration considers an object as a resource node of the type \"Repository Item\" which contains descriptive metadata about the object. Attached to that Node are one or more Media, each representing a file that is part of this object, such as \"Original File\", \"Thumbnail\", \"Preservation Master\", etc. With this model, every original file uploaded into Islandora has its own resource node.

      Multi-file Media configurations also attach Media to a parent node, but allow for that node to be represented by multiple \"Original File\"s. In this model, a Media contains the original file as well as any derivative files created from it (thumbnail, service file, etc.).

      For an example of where these two different approaches could apply, the basic configuration might make sense for a book that has rich page-level metadata, so that each page would be its own Node with its own metadata record; the multi-file media configuration might be a better fit for a book that does not have page-level metadata (except an ordering or page numbers), so that each Media would represent one page, and all pages (Media) would be attached to a single parent Node/metadata record for the entire book.

      As we learned in the introduction, objects in an Islandora repository are represented as a combination of resource nodes, media, and files in Drupal. Because of this, their metadata profile, display, form (and much more) are configurable through the Drupal UI. This gives repository administrators a huge degree of control over their repository without any need for coding. Much more so than ever before. And since we're using a core Drupal solution for modeling our resource nodes and media, compatibility with third-party modules is virtually guaranteed. This opens up a plethora of solutions from the Drupal community that will save you untold time and effort when implementing your repository with Islandora.

      "},{"location":"user-documentation/content-models/#properties","title":"Properties","text":"

      Resource nodes, as Drupal nodes, have some common basic properties regardless of content type. These properties are not fields. This means that they cannot be removed and have limited configurability. Their name, what type of data they hold, etc... are all baked in. Here's an example of the basic properties on nodes:

      nid: 1\nuid: 1\ntitle: \"I am an Islandora object\"\ncreated: 1550703004\nchanged: 1550703512\nuuid: 02932f2c-e4c2-4b7e-95e1-4aceab78c638\ntype: islandora_object\nstatus: 1\n

      As you can see, it's all system data used at the Drupal level to track the basics.

      Property Value nid The local ID for the node uid The ID of the Drupal user who created the node uuid The global ID for any entity title The title for the node created Timestamp of when the node was created changed Timestamp of when the node was last updated type Content type (e.g. which group of fields are present on the node) status Published, unpublished, etc...

      Compared to Islandora Legacy

      These node properties are analogous to following Islandora Legacy object properties:

      Islandora Legacy Islandora owner uid dc.title title PID uuid status status

      The small amount of configurability available for these properties is found on the content type editing form where a user can choose to change the label of the title field, whether to display author information on the node's page, etcetera. These settings will only apply to nodes of that particular content type.

      To view all of a node's property and field values administrators can use the 'Devel' tab's 'Load' section:

      "},{"location":"user-documentation/content-models/#fields","title":"Fields","text":"

      In addition to the basic node properties identified above, resource nodes (like all Drupal nodes) can have fields. Most of what we would think of as descriptive metadata is stored as fields. Resource nodes use 'content types' to define a specific set of required and optional fields it has; we can think of content types as metadata profiles for our objects. For example, you might have a content type for a set of repository objects that have very specialized metadata requirements but another content type for generic repository objects that share a more general set of metadata fields. A resource node's content type is set on its creation and is immutable. The section on metadata describes in more detail how fields on Islandora objects work.

      Configuring fields (adding, editing, removing) is usually done through the Manage > Content types interface, as is described in the tutorial, Create/Update a Content Type.

      Islandora has a notion of a content model, which is used to identify what type of content is being represented by a node (e.g. an image, a video, a collection of other items, etc...). This is done using a special field, Model, which accepts taxonomy terms from the Islandora Models vocabulary. By applying a term from the Islandora Models vocabulary to a node, Islandora will become aware of how to handle the node in response to certain events, like choosing a viewer or generating derivatives.

      Compared to Islandora Legacy

      Content models in Islandora Legacy were immutable and contained restrictions as to what types of datastreams could be associated with an object. Islandora imposes no such restrictions. Content models can be changed at any time, and they in no way dictate what types of media can be associated with a node.

      "},{"location":"user-documentation/content-models/#media","title":"Media","text":"

      All resource nodes can be linked to any number of media. The media associated with a resource node can be managed using the \"Media\" tab when viewing a node. Much like the \"Members\" tab, Actions can be performed in bulk using the checkboxes and Actions dropdown.

      See the media section for more details.

      "},{"location":"user-documentation/content-models/#display-modes","title":"Display modes","text":"

      Drupal uses \"display modes\" (also called \"view modes\") as alternative ways to present content to users. You may be familiar with the \"full\" and \"teaser\" versions of nodes, which are rendered using two corresponding display modes. Islandora makes use of display modes to control how media content is displayed. Islandora Starter Site provides two display modes for Media, one which renders the OpenSeadragon viewer and the other which renders the pdf.js viewer. These two display modes can be enabled by using \"Display hints\" in the node edit form, or you can configure Islandora to use a specific display mode for all media based on the file's Mime type. Both methods make use of Contexts.

      To set the display mode on the resource node's edit form, select the display mode you want to use for that node in the Display hints field:

      Due to the associated Context configurations (\"OpenSeadragon\" and \"PDFjs Viewer\") that are shipped with the Islandora Starter Site, the selected display mode will then be used when the resource node's page is rendered.

      At a global level, there are a couple of ways to tell Drupal to use the PDFjs viewer to render the content of the media field whenever the media has a Mime type of application/pdf.

      The first way is to edit the \"PDFjs Viewer\" Context. By default, this Context tells Drupal to use the PDFjs viewer if the node has the term \"PDFjs\" (yes, that's a taxonomy term):

      If you add the Condition \"Node has Media with Mime type\" and configure it to use application/pdf as the Mime type, like this:

      Context will use whichever Condition applies (as long as you don't check \"Require all conditions\"). That is, if the \"PDFjs\" display hint option in the node edit form is checked, or if the node's media has a Mime type of application/pdf, the media content will be rendered using the PDFjs viewer.

      The second way to use the media's Mime type to render its content with the PDFjs viewer is to create a separate Context that will detect the media's Mime type and use the configured display mode automatically. To do this, create a new Context. Add a \"Node has Media with Mime type\" condition and specify the Mime type, and then add a \"Change View mode\" Reaction that selects the desired display mode:

      Finally, save your Context. From that point on, whenever the media for a node has the configured Mime type, Drupal will render the media using the corresponding display mode.

      The node-level and global approaches are not exclusive to one another. One Context can override another depending on the order of execution. Contexts are applied in the order they are displayed on the Contexts page, which is editable through a drag-and-drop interface. Whichever Condition appears last in the list of Contexts between the node-level Condition (which in this case is the \"Node has term\" condition) the global Condition (which is \"Node has Media with Mime type\"), that one will override the other. An example of having the display mode specified in the node edit form intentionally override the display mode based on Mime type is to have media with the image/jp2 mime-type configured to use the OpenSeadragon viewer, but to manually select the OpenSeadragon display mode for nodes with JPEG media (for example, a very large JPEG image of a map, where the OpenSeadragon's pan and zoom features would be useful).

      "},{"location":"user-documentation/content-models/#members","title":"Members","text":"

      Islandora has a notion of membership, which is used to create a parent/child relationship between nodes. Membership is denoted using another special field, \"Member Of\". This is used to create the link between members and their parent collection, pages and their book (\"paged content\"), or members of a compound object and the compound object itself.

      Any two nodes can be related in this way, though typically, the parent node has a content model of Collection or Paged Content (see their respective pages for more details). The \"Member Of\" field can hold multiple references, so it is possible for a single child to belong to multiple parents, but may also complicate the creation of breadcrumbs.

      Compared to Islandora Legacy

      In Islandora Legacy, there was a distinction between belonging to a collection and belonging to a compound object. In Islandora, this distinction is not present. Since all nodes can have members , essentially every node has the potential to be a compound object or collection.

      Child v. Member

      Islandora uses the \"child\" and \"member\" descriptor for resource nodes that store a reference to another resource node in the \"Member Of\" field interchangeably. Administrators will more often see the \"member\" terminology more often while front-end users will usually see \"child\" terminology.

      For any node, its Children tab can be used to see all its members. You can also perform Actions in bulk on members using the checkboxes and the Actions dropdown as well as clicking on the Reorder Children tab to adjust the order in which they display.

      "},{"location":"user-documentation/content-models/#more-information","title":"More information","text":"

      The following pages expand on the concepts discussed above:

      • Media
      • Content Types: Metadata -- Create / Update a Content Type
      "},{"location":"user-documentation/content-models/#copyright-and-usage","title":"Copyright and Usage","text":"

      This document was originally developed by Alex Kent and has been adapted for general use by the Islandora community.

      [^1] In the Islandora Starter Site, this is the field_model field, which is populated by taxonomy terms in the islandora_models taxonomy vocabulary provided by the islandora_core_feature submodule of Islandora/islandora

      [^2] In the Islandora Starter Site, this is the field_member_of field.

      "},{"location":"user-documentation/content-models/#islandora-legacy-objects-versus-islandora-resource-nodes","title":"Islandora Legacy Objects versus Islandora Resource Nodes","text":"

      The conventional Islandora Legacy definition of an object is a file loaded in the repository with associated derivatives. In Islandora Legacy, objects (video files, audio files, PDFs, etc.) are loaded through the user interface, and Datastreams are generated automatically. These consist of access and display copies, the metadata, OCH/HOCR, technical metadata, and more. All of these Datastreams are directly connected to the object and accessed through the admin interface.

      In Islandora, the traditional Islandora Legacy objects (video files, audio files, etc. that were represented in different content models) are now Drupal nodes. Islandora object nodes are a special kind of Drupal node, distinct from nodes that exist for other content types such as a blog post, an article, a page (like the About page on a site), and others. These Islandora objects are still loaded through the interface and described with the data entry form, and derivatives are still generated. However, the Datastreams are no longer connected to the original object in the same immutable way. Each of these Datastreams can be manipulated through Drupal by non-developers. You can create a variety of ways to view this metadata and information related to the objects. Doing so requires knowledge of Drupal 8, but this essentially means that there are many ways to view the metadata and access the related objects in Islandora.

      In Islandora it is therefore helpful to think of objects as resource nodes. The term reflects the new nature of objects in Islandora. A resource node does not just refer to the individual object file, but encompasses multiple elements that all relate to each other, even if they are no longer directly connected like objects in Islandora Legacy.

      The typical elements of a resource node:

      • A content type defining metadata fields defined for the node. A content type may include any number of custom fields defined to store descriptive metadata about the object represented by the node. To function as an Islandora resource node, a content type must define two further fields:
        • A field denoting the 'type' of thing represented by the node (image, book, newspaper, etc.). The value of this field is used by Islandora to control views, derivative processing, and other behavior.[^1]
        • A field in which to record the node's membership in another node. If populated, this field creates a hierarchical relationship between parent (the node recorded in the field) and child (the node in which the parent is recorded). This may be left empty, but is required for building hierarchies for collections, subcollections, and members of collections, as well as objects (books, \"compound objects\", etc.) consisting of paged content.[^2]
      • Media files (the actual files of JPEGs, MP3s, .zip, etc.) that get loaded through the form
      • Derivative files (thumbnails, web-friendly service files, technical metadata, and more)

      These resource nodes are what the librarian, student, archivist, technician, or general non-developer creates through the data entry form. It is possible to configure all elements of a resource node in Islandora through Drupal. This fact allows control over how one accesses the node and how nodes are displayed and discovered online by non-developers. It also allows a repository to take full advantage of all third-party Drupal modules, themes, and distributions available.

      "},{"location":"user-documentation/content-types/","title":"Creating and updating content types","text":""},{"location":"user-documentation/content-types/#overview","title":"Overview","text":"

      Since metadata in Islandora is stored as fields in Nodes, the standard Drupal Content Types system provides our 'ingest forms'. For more information about Content Types in general, please see Content Types in Drupal. If you are already familiar with Drupal Field UI, you\u2019re already well-equipped to create and modify your own ingest forms in Islandora.

      This page will address how to create and modify ingest forms by editing fields and form display settings on Content Types via the graphical user interface (GUI). This page will also cover editing the RDF mapping to accommodate changes to fields.

      Islandora forms are Drupal forms, and for help working with forms via the API, please check out the Further Reading section for links to more advanced Drupal documentation.

      "},{"location":"user-documentation/content-types/#before-you-start","title":"Before you start","text":"
      • The following How-To assumes that you are using the (optional) Islandora Starter Site configuration. This configuration is deployed automatically if you build your Islandora site using the Ansible Playbook, ISLE with Docker-Compose, or are using the sandbox or a Virtual Machine Image
      • This How-To assumes familiarity with Drupal terms such as Node, Content Type, and Media.
      "},{"location":"user-documentation/content-types/#how-to-modify-a-content-type","title":"How to modify a Content Type","text":"

      If you have deployed your Islandora with the Islandora Starter Site configuration, you will already have a Repository Item content type available, with pre-configured fields and repository behaviours.

      1. In the Admin menu, go to Structure >> Content Types and find the Repository Item content type.
      2. Select Manage Fields.

      There are multiple tabs with different options to configure your Content Type:

      • Manage Fields: A list of the fields available in this form. This is where you can add new fields and make adjustments to existing fields, such as whether the field has access restrictions or is required.
      • Manage form display: Set the order in which fields appear in a form, including nesting; set how the user will enter data into a field (i.e., text field, drop-down list, radio buttons, etc.); set fields to be hidden in the form.
      • Manage display: Set how the data stored in the fields will be displayed on the Node. Custom display settings can be set for different \"view modes.\" For instance, a different view mode is applied for items using the OpenSeadragon viewer, which includes a field that displays the Media in OpenSeadragon instead of the standard Drupal image viewer.

      Changes not displaying?

      If you make changes under Manage display and don't see them reflected in your Node, double check that you have edited the right view mode

      • Devel: This tab is generated by an optional module that is useful for development and troubleshooting; it can be ignored in this How-To. For more information, see Devel.
      "},{"location":"user-documentation/content-types/#add-a-field","title":"Add a field","text":"

      This example adds a new field where a user can indicate if the repository item needs to be reviewed:

      1. Click Add Field
      2. In some cases an existing field may be available to use instead of creating a new one. The dropdown box labeled Re-use an existing field has a list of available fields. For this example we will create a brand-new field. Since the example field is a \u201cyes/no\u201d decision (whether the item needs review or not), choose \"Boolean\" from the dropdown menu and give the Label field a name. See the list of Drupal 8 FieldTypes, FieldWidgets, and FieldFormatters for descriptions of the different types available by default. Additional modules, such as the controlled_access_terms module, can provide their own Field types to choose from as well.
      3. Click Save and continue.
      4. Next, configure how the field is stored in the Drupal database. For this field type you can select how many values will be allowed. The default settings, \"Limited\" in the dropdown box and \"1\" for the allowed number of values works for our example.
      5. Click Save field settings.
      6. Configure how the field is described (including its display label and the help text for when it appears on a form) and constraints on its use. In this screenshot, the field will be required for this Content Type, and will be set to \u201con\u201d by default. In the Default Value section, click the checkbox next to Needs Review to indicate all new repository items need review by default.
      7. Click Save settings.

      The new field has been added:

      It appears in the ingest form when creating a new repository object. To test this, go to Content >> Add content >> Repository item:

      RDF Mappings

      New fields, except for Typed Relation fields, are not automatically indexed in Fedora and the triple-store. Update the Content Type's RDF Mapping to enable indexing the field (see below).

      Search

      New fields will not automatically be searchable. They need to be added to the Solr index configuration. See the 'Setup and Configure Search' page for more information.

      Context

      To add new behavior based on the results of this new field, check out Context.

      "},{"location":"user-documentation/content-types/#change-the-form-display","title":"Change the form display","text":"

      To change where in the form a field is displayed, go to the Admin menu, return to Structure >> Content Types, and find the Repository Item content type again. Select Manage form display from the dropdown menu or select the Manage form display tab.

      1. All the fields in this content type are available, in a list, with a simple drag-and-place UI. Drag the new field to the top of the form. You can also change the way the Boolean options are displayed, with radio buttons as opposed to a single checkbox. Different display options will be available from the dropdown menu depending on field type. For more information, please check out List of Drupal 8 FieldTypes, FieldWidgets, and FieldFormatters
      2. Click Save.

      When creating a new Repository Item, the new field appears at the top, as a set of radio buttons.

      "},{"location":"user-documentation/content-types/#change-the-content-display","title":"Change the content display","text":"

      Finally, change how the results of this example field are displayed. Initially the new field shows up at the bottom of repository object pages:

      In the Admin menu, return to Structure >> Content Types and find the Repository Item content type again. Select Manage display from the dropdown menu or select the Manage display tab.

      1. Find the new field. You can change how the field title or label is displayed.
      2. Click the dropdown menu to choose from inline/above/hidden/visually hidden.
        • You can also replace the options displayed with variations on a binary choice. Click the gear to choose from the following: On/Off, Yes/No, Enabled/Disabled, 1/0, checkmark/X, or hide the field completely.
        • You can also drag the field into the Disabled section so that neither its label nor its contents appear in the display, although the field is saved on the Node.
      3. Drag the field to \"Disabled\" and save.
      4. The contents of the field are no longer displayed on the Node, but it is available when editing the node.
      "},{"location":"user-documentation/content-types/#create-a-content-type","title":"Create a Content Type","text":"

      To create your own custom content type from scratch, please refer to this guide on Drupal.org.

      Your custom content types can contain whatever fields you like, but there are two mandatory fields that all Islandora content types should contain:

      1. In order for a custom content type to be considered an Islandora Object, it needs to have the field \"Member of\" ('field_member_of'). This allows it to be included in contexts that have the \"Node is an Islandora node\" condition. Nodes that have this field will automatically be synced to Fedora and indexed by the triple store if you are using the context provided by the Islandora Starter Site. Having this field present in your content type also gives you tabs for adding children and media when viewing an item of that content type.

      2. The other mandatory field is \"Model\" ('field_model'). This is used in several of the contexts that the Islandora Starter Site provides. This field determines how Islandora objects are displayed, and how media derivatives are created.

      "},{"location":"user-documentation/content-types/#updating-and-creating-an-rdf-mapping","title":"Updating and creating an RDF Mapping","text":"

      RDF mapping aligns Drupal fields with RDF ontology properties. For example, the title field of a content model can be mapped to dcterms:title and/or schema:title. In Islandora, triples expressed by these mappings get synced to Fedora and indexed in the Blazegraph triplestore. RDF mappings are defined/stored in Drupal as a YAML file (to learn more about YAML, there are several tutorials on the web. Currently, Drupal 8 does not have a UI to create/update RDF mappings to ontologies other than Schema.org. This requires repository managers to update the configuration files themselves. Consider using the RDF mappings included in the Islandora Starter Site as templates by copying and modifying one to meet your needs.

      The Drupal 8 Configuration Synchronization export (e.g. http://localhost:8000/admin/config/development/configuration/single/export) and import (e.g. http://localhost:8000/admin/config/development/configuration/single/import) can be used to get a copy of the mappings for editing in a text editor before being uploaded again. Alternatively, a repository manager can update the configuration on the server and use Features to import the edits.

      An RDF mapping configuration file has two main areas: the mapping's metadata and the mapping itself. Most of the mapping's metadata should be left alone unless you are creating a brand-new mapping for a new Content Type or Taxonomy Vocabulary. A partial example from islandora_default's islandora_object (Repository Item) is included below:

      langcode: en\nstatus: true\ndependencies:\n  config:\n    - node.type.islandora_object\n  enforced:\n    module:\n      - islandora_demo\n  module:\n    - node\nid: node.islandora_object\ntargetEntityType: node\nbundle: islandora_object\ntypes:\n  - 'pcdm:Object'\nfieldMappings:\n  title:\n    properties:\n      - 'dc:title'\n  field_alternative_title:\n    properties:\n      - 'dc:alternative'\n  field_edtf_date:\n    properties:\n      - 'dc:date'\n    datatype_callback:\n      callable: 'Drupal\\controlled_access_terms\\EDTFConverter::dateIso8601Value'\n  field_description:\n    properties:\n      - 'dc:description'\n

      The required mapping metadata fields when creating a brand-new mapping include the id, status, targetEntityType, and bundle. (uuid and _core, not seen in the example above but may be present in exported copies, will be added by Drupal automatically.) bundle is the machine name for the Content Type or Taxonomy Vocabulary you are creating the mapping for. targetEntityType is node for Content Types or taxonomy_term for Taxonomy Vocabularies. The id configuration is a concatenation of target entity type and bundle ('node' and 'islandora_object' in the example above). The id is also used to name the configuration file: e.g. rdf.mapping.node.islandora_object.yml is rdf.mapping. plus the id (node.islandora_object) and then .yml.

      The mapping itself consists of the types' and fieldMappings configurations.

      All the mappings use RDF namespaces instead of fully-qualified URIs. For example, the type for islandora_object is entered in the RDF config as pcdm:Object instead of http://pcdm.org/models#Object. The available namespaces are defined in module hooks (hook_rdf_namespaces) but can also be entered manually in a configuration interface. Repository managers wanting to add additional namespaces need to go to Configuration > Search and Metadata > JSONLD and enter their desired namespaces in the \"Additional RDF Namespaces\" box.

      Namespaces currently supported (ordered by the module that supplies them) include:

      • rdf
        • content: http://purl.org/rss/1.0/modules/content/
        • dc: http://purl.org/dc/terms/
        • foaf: http://xmlns.com/foaf/0.1/
        • og: http://ogp.me/ns#
        • rdfs: http://www.w3.org/2000/01/rdf-schema#
        • schema: http://schema.org/
        • sioc: http://rdfs.org/sioc/ns#
        • sioct: http://rdfs.org/sioc/types#
        • skos: http://www.w3.org/2004/02/skos/core#
        • xsd: http://www.w3.org/2001/XMLSchema#
      • islandora
        • ldp: http://www.w3.org/ns/ldp#
        • dc11: http://purl.org/dc/elements/1.1/
        • nfo: http://www.semanticdesktop.org/ontologies/2007/03/22/nfo/v1.1/
        • ebucore: http://www.ebu.ch/metadata/ontologies/ebucore/ebucore#
        • fedora: http://fedora.info/definitions/v4/repository#
        • owl: http://www.w3.org/2002/07/owl#
        • ore: http://www.openarchives.org/ore/terms/
        • rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns#
        • islandora: http://islandora.ca
        • pcdm: http://pcdm.org/models#
        • use: http://pcdm.org/use#
        • iana: http://www.iana.org/assignments/relation/
      • islandora-starter-site
        • relators: http://id.loc.gov/vocabulary/relators/
      • controlled_access_terms
        • wgs84_pos: http://www.w3.org/2003/01/geo/wgs84_pos#
        • org: https://www.w3.org/TR/vocab-org/#org:
        • xs: http://www.w3.org/2001/XMLSchema#

      The types corresponds to the rdf:type predicate (which corresponds to JSON-LD's @type) and can have multiple values. This type value will be applied to every node or taxonomy term using the mapped content type or vocabulary.

      In some cases a repository may want a node or taxonomy term's rdf:type to be configurable. For example, the Corporate Body Vocabulary (provided by the Controlled Access Terms Default Configuration module) has schema:Organization set as the default type in the RDF mapping. However, more granular types may apply to one organization and not another, such as schema:GovernmentOrganization or schema:Corporation. The alter_jsonld_type Context reaction allows Content Types and Taxonomy Vocabularies to add a field's values as rdf:types to its JSON-LD serialization (the format used to index a node or taxonomy term in Fedora and the triple-store).

      fieldMappings specifies the fields to be included, their RDF property mappings, and any necessary data converters (the datatype_callback). One field can be mapped to more than one RDF property by adding them to the field's properties list. The datatype_callback is defined by the 'callable' key and the fully qualified static method used to convert it to the desired data format. For example, fields of the Drupal datetime type need to be converted to ISO 8601 values, so we use the Drupal\\rdf\\CommonDataConverter::dateIso8601Value function to perform the conversion.

      Islandora Quick Lessons

      Learn more with this video on Customizing a Form.

      "},{"location":"user-documentation/context/","title":"Context","text":""},{"location":"user-documentation/context/#context-in-islandora","title":"Context in Islandora","text":"

      Context is a Drupal contrib module that allows configuration of \"if this, then that\" logic using an administrative user interface. Context defines \"conditions\" and \"reactions\" to enact the \"if this [condition], then that [reaction]\" logic. Some Islandora Legacy repositories use the community-contributed Islandora Context module to apply this sort of logic to Islandora objects. An example Context from Islandora Legacy is

      If an object's namespace is 'customnamespace', render the block containing the right's statement \"Please contact Special Collections before reusing this item\".

      The Context project page on drupal.org says \"You can think of each context as representing a 'section' of your site\", but that doesn't tell the whole story, since it gives the impression that Context is all about defining subsites. Context in Drupal 8 (and above) is much more powerful than in Drupal 7 due to how it interacts with the rest of Drupal (specifically, through Drupal 8's new plugin API). Because of this increased flexibility and power, and because Context provides a ready-made user interface for site administrators to combine conditions and reactions, Islandora uses Context to drive the logic underlying many important aspects of Islandora, including which derivatives get generated and how objects are displayed. In Islandora Legacy, many of these things are managed (and often hard-coded) within solution packs and utility modules.

      "},{"location":"user-documentation/context/#an-example","title":"An example","text":"

      Let's look at the example of how Context can determine how an object is displayed. Drupal 8 has the idea of \"view modes\", which allow site builders to choose what happens when an object is viewed by the user (it has nothing to do with Drupal Views). In the node edit form for Islandora objects, there is a checkbox that, if checked, tells Drupal to render the image using the OpenSeadragon viewer:

      This functionality is accomplished via the \"OpenSeadragon\" Context, which, as its configuration suggests, checks as its condition whether the node as the \"OpenSeadragon\" tag and if so, reacts by using the view mode \"OpenSeadragon\":

      "},{"location":"user-documentation/context/#context-and-actions","title":"Context and actions","text":"

      Context in Drupal 8 uses Drupal Actions to execute the \"then that\" part of their logic. An implication of this is some configuration options relevant to the outcome of the \"then that\" reaction are configured in the Action admin interface (located at admin/config/system/actions), not within the Context admin interface (located at admin/structure/context). While this makes for a clunky admin experience, the advantage is that it allows the reuse of a single piece of Drupal code in multiple Contexts.

      One important group of functionality in Islandora repositories that admins might want to control is what types of media get persisted to Fedora. Islandora allows administrators to choose what types of media get persisted to Fedora and what types of media get persisted to the Drupal public file system. For example, you may want to persist your thumbnails to Fedora along with your \"Preservation Master\" and \"Original File\" files. This can be accomplished using Context.

      By default, Islandora stores thumbnails in Drupal's public file system, not in Fedora. Thumbnails are automatically generated by a Context (the \"Image Service File\" Context to be specific), but if you want to change the file system where your thumbnails are stored to the Fedora file system, you need to change that setting in the \"Image - Generate a thumbnail from a service file\" Action, not in the Context that uses that action:

      This configuration should be done as part of your initial Islandora setup, since there is currently no way to move thumbnails that were persisted to Drupal's public file system to Fedora, or vice versa. But, at least site admins can choose one or the other using this Action.

      You can create custom Actions and then use them in Contexts. For example, if you want to present a message to the user every time they upload a media tagged as \"Preservation Master File\", create an Action of type \"Display a message to the user\" and enter your message:

      Then, create a Context that uses this Action as its Reaction, and as its Condition, uses \"Media has term\" with a value of \"Preservation Master File\":

      Now, when a user creates a Media and tags it as a \"Preservation Master File\", they will be shown the message you created in your Action:

      "},{"location":"user-documentation/context/#tips-on-using-islandora-specific-contexts","title":"Tips on using Islandora-specific contexts","text":"

      Islandora provides some Conditions and Reactions that aren't available on other Drupal sites. When using them, you may find the following tips useful:

      • When using the \"Node has term with URI\", \"Media has term with URI\", and \"Parent node for media has term with URI\" Islandora-specific Conditions, you may want to enter multiple terms to take advantage of the AND/OR logic they provide. To do so, add a comma (,) after the first term in the autocomplete field and continue typing.
      • These three Conditions limit taxonomy terms that show up in their autocomplete Term field to terms that have URIs associated with them, for example from the Islandora Media Use or Islandora Models vocabularies (although any term that has a URI will appear). If you want to configure Conditions using terms that do not have a URI, you can use the contrib Term Condition module.
      • Be as specific as possible in configuring your Conditions. If your Conditions seem to apply too broadly or in unexpected ways, try adding a \"Content Entity Type\" Condition to limit the Context to pages that render Islandora objects or media.
      "},{"location":"user-documentation/context/#increased-flexibility","title":"Increased flexibility","text":"

      Most Islandora repository administrators will not need to alter or configure any Contexts. But since much of Islandora's underlying functionality is governed by this powerful Drupal contrib module, administrators should become comfortable using it, and Actions, to customize and refine their repositories. Also, since Islandora objects are ordinary Drupal nodes, Islandora objects can take advantage of standard Drupal 8 Context functionality out of the box.

      "},{"location":"user-documentation/extending/","title":"Extending Islandora: The Islandora Cookbook","text":"

      Because newer Islandora is very tightly integrated with Drupal, many use cases that required custom Islandora modules in Islandora Legacy and earlier can now be solved with a combination of configuration and modules from the Drupal community. Other use cases may still require custom code, but are not common enough, or complete enough, for that custom code to be a part of the core software.

      Both scenarios are covered by the Islandora Cookbook below, which contains an annotated list of custom Islandora tools and modules developed by our community, a list of Drupal contributed modules that may be useful in an Islandora context, and a set of recipes which each provide a detailed outline for solving a particular use case.

      Islandora is also compatible with most Drupal themes.

      "},{"location":"user-documentation/extending/#introduction","title":"Introduction","text":"

      In the spirit of Islandora Awesome, Islandora Cookbook is a curated list of great modules and other tools for Islandora. Because Islandora is more tightly integrated with Drupal, most of these 'recipes' require only Drupal modules as ingredients.

      We offer this list for discovery, but do not officially provide support for any of these modules.

      "},{"location":"user-documentation/extending/#recipes","title":"Recipes","text":"
      • Amazon Alexa - This recipe explains how to access Islandora with Amazon Alexa.
      • Collection Searching - This recipe provides instructions how to configure a shallow or deep search in a specific collection.
      • Date Range Slider Facet - This recipe explains how to include a facet for date field(s) that presents itself as a range slider.
      • Exhibitions and TimeLine JS - This recipe outlines how to build exhibits in Islandora, including the deployment of TimeLineJS.
      • Digital Preservation - A detailed overview of how to approach digital preservation in Islandora. Includes features that are currently available, both within Islandora and by using compatible tools.
      "},{"location":"user-documentation/extending/#ingredients","title":"Ingredients","text":"

      Below are modules and tools that might be useful to solve common use cases, presented without specific recipes.

      "},{"location":"user-documentation/extending/#islandora-contributed-modules","title":"Islandora contributed modules","text":"

      Warning! - All modules are under development.

      • Basic Ingest - Basic repository item ingest improvements.
      • Islandora RipRap - Fixity auditing
      • Islandora Repository Reports - Graphical reports on various aspects of an Islandora repository.
      • Islandora Bagger - Utility to generate Bags for objects using Islandora's REST interface using either a command-line tool or via a batch-oriented queue.
      • Islandora Citations - An alternative to Citation Select (bundled with Starter Site). It uses CSL to render citations, allows you to set a default (which is rendered on page load), and lets you set the field mappings as a third party setting when editing each field itself. It works with Typed Relation fields (using the relation to set the CSL parameter such as author, editor, or contributor), and also works with Paragraphs.
      • Islandora RDM - An entire suite of tools and documentation to turn Islandora into a fully functional Research Data Management platform.
      • Islandora Whole Object - Islandora module that provides some Drupal blocks containing various representations of an Islandora object.
      • IP Range Access - A context condition to check the user's IP address against a range, and provide 403 if not accepted.
      • Typed Relation with Display Name - A field type that's a Typed Relation field plus a display name that is unique to the instantiation. Allows you to transcribe what's on the object, while still linking to a controlled form of a name.
      • Linked Data Lookup Field - allows you to autocomplete from external authorities. It creates a two-part field and stores the label and the URI. It is extensible and can be made to work with more APIs. Currently it supports Library of Congress Subject Headings, Global Research Identifier Database (GRID) entries, and Australian and New Zealand Standard Research Classification Fields of Research.
      "},{"location":"user-documentation/extending/#access-control","title":"Access Control","text":""},{"location":"user-documentation/extending/#access-control-by-taxonomy-tags","title":"Access control by taxonomy tags","text":"

      Permissions by Term

      By default, Drupal allows you only to restrict access to Drupal nodes by coupling node content types to user roles. The Permissions by Term module extends Drupal by functionality for restricting view and edit access to single nodes via taxonomy terms. Since Islandora objects can have taxonomy terms, this can be used to control access at the node and collection level. The submodule Permissions by Entity extends this control to the media level.

      "},{"location":"user-documentation/extending/#setting-a-date-and-time-for-publication","title":"Setting a date and time for publication","text":"

      Moderation Scheduler

      Moderation Scheduler gives content editors the ability to schedule Islandora nodes to be published at specified dates and times in the future. Can be combined with Views Bulk Edit to set the same scheduled publication date/time on multiple nodes at once.

      "},{"location":"user-documentation/extending/#tombstoning","title":"Tombstoning","text":"

      Tombstones

      A module that informs users who attempt to view deleted content that the resource has been removed instead of showing a generic 404 page. Fielded so that the date, reason, and alternative links can be given, along with a citation for the deleted object.

      "},{"location":"user-documentation/extending/#displays","title":"Displays","text":""},{"location":"user-documentation/extending/#image-slideshow","title":"Image slideshow","text":"

      Views Slideshow

      Views Slideshow can be used to create a slideshow of any content (not just images) that can appear in a View. Powered by jQuery, it is heavily customizable: you may choose slideshow settings for each View you create. It can be used to create an easy, adjustable slideshow of images from an Islandora repository. Views Slideshow can be seen in action with Islandora objects on the front page here

      "},{"location":"user-documentation/extending/#csv-file-formatter","title":"CSV File Formatter","text":"

      CSV File Formatter

      This module provides a formatter that displays CSV files in the browser.

      "},{"location":"user-documentation/extending/#timelines","title":"Timelines","text":"

      TimelineJS

      Integration with TimelineJS to create timeline displays.

      "},{"location":"user-documentation/extending/#sort-titles-without-initial-articles-the-a-etc","title":"Sort titles without initial articles (The, A, etc)","text":"

      Views Natural Sort

      This module adds a new sort option to Content views that sorts while skipping a configurable list of initial articles like \"The\", \"A\", \"An\", \"L'\", etc. Note that it works only with Content views and not with Search API (solr) views.

      "},{"location":"user-documentation/extending/#ingest","title":"Ingest","text":""},{"location":"user-documentation/extending/#batch-uploading-via-csv","title":"Batch uploading via CSV","text":"

      Migrate Islandora CSV

      This repository is a tutorial that will introduce you to using the Drupal 8 Migrate tools to create Islandora content. Whether you will eventually use CSVs or other sources (such as XML or directly from a 7.x Islandora) this tutorial should be useful as it covers the basics and mechanics of migration.

      This repository is also a Drupal Feature that, when enabled as a module, will create three example migrations ready for you to use with the Migrate API.

      "},{"location":"user-documentation/extending/#alternative-way-to-batch-upload-via-csv","title":"Alternative way to batch upload via CSV","text":"

      Islandora Workbench

      Command-line tool for ingesting (and updating) nodes and media from anywhere - you don't need to access to the Drupal server's command line. Provides robust data validation, flexible organization of your input data (can use CSV, Google Sheets, or Excel files) plus creation of taxonomy terms on the fly.

      "},{"location":"user-documentation/extending/#content-management","title":"Content Management","text":""},{"location":"user-documentation/extending/#batch-editing","title":"Batch editing","text":"

      Views Bulk Edit

      A powerful tool that turns Views into a means of batch editing nodes, including Islandora repository objects. Once installed, create a view, add the fields that you would like to edit, add a Views bulk operations (Edit) field to the view, and select which actions you would like to have available. The Modify field values action will allow you to batch edit the value for the same field across multiple objects. A demonstration of a simple bulk-editing view with a few fields and actions can be found here

      "},{"location":"user-documentation/extending/#clone-a-content-type","title":"Clone a content type","text":"

      Content Type Clone

      A tool that allows you to clone an existing content type. Can be used to copy and easily make your own version of the Repository Item Content Type with fewer or edited fields, without starting over. Has options to also copy all nodes from the old type to the new, and to delete from the old type when copying.

      "},{"location":"user-documentation/extending/#prevent-orphaned-entity-relationships","title":"Prevent orphaned entity relationships","text":"
      • Entity Reference Integrity
      • Entity Reference Integrity Extras

      Normally when deleting content (nodes, taxonomy terms, etc), any content that references the deleted entity isn't altered so you end up with orphan/zombie references, which are visible in the JSON representation but invisible otherwise. With Entity Reference Integrity's submodule (-Enforce), you won't be able to delete content that's referenced from elsewhere, preserving your database integrity.

      Because the Drupal module doesn't play with Typed Relation fields, DGI's -Extras module is useful.

      "},{"location":"user-documentation/extending/#content-management-workflows","title":"Content Management Workflows","text":"

      Content moderation

      This module lets you set up workflows that \"transition\" content between \"states\", which may be published or unpublished. It also allows you to set revisions as mandatory (normally an editor can decide to make an edit without creating a revision, making it very hard to track for auditing).

      "},{"location":"user-documentation/extending/#shared-content-between-sites","title":"Shared content between sites","text":"

      Entity Share

      Share entities between different Drupal instances. Works with nodes, taxonomy terms, media, etc.

      "},{"location":"user-documentation/extending/#external-content","title":"External content","text":"

      External Entities

      Lets you use sources of content external to Drupal as though they were internal.

      "},{"location":"user-documentation/extending/#paragraphs-for-structuredhierarchical-content","title":"Paragraphs for structured/hierarchical content","text":"

      Paragraphs

      Paragraphs is based on Entity Reference Revisions and allows you to create an on-the-fly entity with structured fields. Paragraphs could be used for complex titles with title type, subtitle, part name, etc. They can be used for grouping fields together that are, as a block, repeatable.

      "},{"location":"user-documentation/extending/#richer-content","title":"Richer Content","text":""},{"location":"user-documentation/extending/#displaying-equations-and-formulae","title":"Displaying equations and formulae","text":"

      MathJax

      This module is a plug-and-play solution for rendering LaTeX. It uses a CDN so no library has to be installed on your system, and the processing is done in the user's browser. In terms of Drupal, it provides a text filter that must be enabled in at least one text format (such as Full HTML). With this you can enter mathematical formulae in abstracts! Need to recognize unfamiliar symbols? Try this LaTeX symbol reference thanks to the On-Line Encyclopedia of Integer Sequences.

      "},{"location":"user-documentation/extending/#search","title":"Search","text":""},{"location":"user-documentation/extending/#custom-search-weighting","title":"Custom search weighting","text":"

      Search Overrides

      This module provides a method for users with the necessary permissions to manually override the results being returned by Search API Solr. They will be able to choose a specific search term, and pick which nodes should be at the top, and also choose to exclude nodes so they will not be shown in the results. Currently, only nodes are supported.

      "},{"location":"user-documentation/extending/#render-field-as-link-to-faceted-search","title":"Render field as link to faceted search","text":"

      Entity Reference Facet Link

      Provides a field formatter that points an entity reference field to a facet search for that value. Could be used in search result displays with taxonomy terms (for example) to stay within the \"search ecosystem\". Does not work with Typed Relation fields.

      "},{"location":"user-documentation/extending/#auditing","title":"Auditing","text":""},{"location":"user-documentation/extending/#logging-administrative-events","title":"Logging administrative \"events\"","text":"

      Events Logging

      This module provides a separate log to record \"events\" such as the creation, editing, and deletion of content (actually any entities you configure it to, including config entities). This can provide a log of who did what, when. The log is not mixed in with the \"Watchdog\"/\"Recent log entries\" log, but still uses the database (unless you have another log method enabled).

      When content is updated, the log only says that it was updated but does not say how or provide a diff. It does not seem to have the ability to link log messages to the revisions that may have been created during edits.

      "},{"location":"user-documentation/extending/#other","title":"Other","text":""},{"location":"user-documentation/extending/#gather-user-feedback","title":"Gather user feedback","text":"

      Content Feedback

      The Content Feedback module allows users and visitors to quickly send feedback messages about the currently displayed content, and can be applied to Islandora nodes. All content feedback messages are listed and grouped by status in an administrative feedback list.

      "},{"location":"user-documentation/extending/#sending-your-content-to-archiveorg","title":"Sending your content to Archive.org","text":"

      Wayback Submit to Archive.org

      A tool for automatically submitting the contents of your site to Archive.org. The Wayback Submit module will submit all node types on schedule, according to criteria set by the site admin (only certain node types, only certain views, etc).

      "},{"location":"user-documentation/extending/#other-resources","title":"Other resources","text":"
      • Drupal Contributed Modules
      • Drupal Contributed Themes
      "},{"location":"user-documentation/faceting/","title":"Faceting in Islandora","text":""},{"location":"user-documentation/faceting/#overview","title":"Overview","text":"

      This tutorial contains are step-by-step instructions for adding a facet on a vocabulary reference field. After a search, a block containing the configured facets appears letting the user filter their search.

      This is a walk-through. For a more in-depth discussion of creating facets, see Configure Advanced Search

      This tutorial currently works best with the \"standard\" install of the Islandora and Islandora Default modules which can be obtained from the playbook (as opposed to the install profile, which handles facet blocks differently).

      "},{"location":"user-documentation/faceting/#step-1-add-vocabulary-bundles","title":"Step 1: Add vocabulary bundles","text":"

      Steps 1 and 2 add the field to the Solr index.

      1. Go to Admin >> Configuration >> Search and Metadata >> Search API (or, /admin/config/search/search-api)
      2. Edit the Default Solr content index
      3. Open the Configure the Taxonomy term datasource field-set, choose the vocabulary bundles you want to add to be indexed
      4. Click Save

      "},{"location":"user-documentation/faceting/#step-2-add-fields","title":"Step 2: Add fields","text":"
      1. Back at the edit Default Solr content index, choose Fields from the tabs across the top
      2. Click on the Add fields button
      3. Scroll down to Content
      4. Click on the + next to the field you want to add.
      5. Click on the + next to \"Taxonomy term\". A bunch of subfields will appear.
      6. Look for the field that contains \"YOUR_FIELD:entity:name\" and click on the \"Add\" button at the end of the bulleted point.
      7. Click \u201cDone.\u201d
      8. IMPORTANT: Find your newly added fields In the list, and ensure the \"Type\" of the new field is \"string\" so it can be compatible with Facets.
      9. If the \"Machine name\" of the new field is generic, like \"name_1\", change it to be the same as the part of the \"Property path\" up to the first : (this will be the same as the field's machine name).
      10. Click on the Save changes button.
      "},{"location":"user-documentation/faceting/#step-3-rebuild-solr-index","title":"Step 3: Rebuild Solr index","text":"

      Now that you've added the field, you need to rebuild your Solr index.

      1. Go back to Default Solr content index (admin/config/search/search-api/index/default_solr_index)
      2. Click on the Index now button.
      "},{"location":"user-documentation/faceting/#step-4-add-and-configure-facet","title":"Step 4: Add and configure facet","text":"

      Step 4 adds and configures the facet itself.

      1. Go to Admin >> Configuration >> Search and Metadata >> Facets** (admin/config/search/facets)
      2. Click on the Add facet button
      3. Select the View Solr search content source
      4. In the Field list, select the field you added above
      5. You can adjust the admin Name of the facet
      6. Click **Save\".
      7. Choose your configuration options (for example, \"List item label\")
      8. Click Save.
      "},{"location":"user-documentation/faceting/#step-5-add-facet-as-a-block","title":"Step 5: Add facet as a block","text":"

      This step adds the facets in a single block.

      1. To place a Facets block for the facets you have created, go to Admin >> Structure >> Blocks layout (admin/structure/block).
      2. Click Place block in the desired block region, for example, Sidebar second.
      3. Start typing the facets block in the Filter and click Place block
      4. Choose the facets to include
      5. Choose other configuration options for the block, for example change the title that displays and restrict by content type, etc.
      6. Click Save.

      At this point, searching for content that has facet values should cause the block to appear. For more in-depth overview of search, see Configure Advanced Search

      Facets aren't necessarily searchable

      While this will create facets, the values that appear won't work (won't necessarily bring back any content) if you type them in the search box. This is because the fulltext search box uses only fulltext fields, and facets, as mentioned above, requires string fields.

      If you want to be able to search for taxonomy term values and bring up the related nodes, you could either include the full rendered item for your content type, or you may wish to repeat step 2 for each entity reference field, and set the new fields to fulltext so that searching for term values brings back node results.

      "},{"location":"user-documentation/file-viewers/","title":"File Viewers","text":""},{"location":"user-documentation/file-viewers/#what-are-viewers","title":"What are viewers?","text":"

      Viewers allow site builders to display files in interactive JavaScript-based widgets, that provide functionality like zooming in/out, turning pages, playing/pausing, viewing in full screen, etc.

      In Drupal, a common way to implement a viewer is through a module that provides a Drupal field formatter that interfaces with the appropriate JavaScript library. The field formatter will work with specific types of Drupal fields (e.g. file fields or image fields, some may even provide their own fields). Some viewer modules in Islandora also provide a block, that can display appropriate files based on the context.

      Viewers that are known to work with Islandora include:

      • OpenSeadragon, via the Drupal module OpenSeadragon (maintained by the Islandora community).
      • Mirador, via the Drupal module Islandora Mirador (maintained by the Islandora community).
      • pdf.js, via the Drupal contrib module PDF
      • Islandora Image, via the Islandora module
      • Audio with captions, via the Islandora module
      • Video with captions, via the Islandora module
      "},{"location":"user-documentation/file-viewers/#configuring-field-formatters-as-viewers","title":"Configuring Field Formatters as Viewers","text":"

      The simplest Drupal-y way of making a viewer appear is to configure a Media to render. You can do this by configuring a View Mode that shows the desired file field, displayed in a field formatter that invokes the desired viewer.

      In the Starter Site:

      1. On all Media Types, there is a \"Source\" view mode which is configured to show only the main (\"source\") file of that Media in a reasonable default viewer.
      2. By default, on a node's page, a Block is configured to appear that shows an attached \"Service File\" Media, or an \"Original File\" if no Service File is present. This block displays the media in the \"Source\" view mode, i.e. in its default viewer. This block placement is done using a Context. The block itself is a rendering of a View.
      3. On a node-by-node-basis, you can override the viewer used by setting the \"Viewer Override\" field to a different viewer (such as PDF.js). This will cause a different Context to be activated instead, which will render the Service File or Original File media in a different view mode, where a different viewer is used.

      Note

      Formerly, this field was called \"Display Hints\". That field name has been retired in order to reduce confusion, since this uses a different mechanism. This mechanism no longer relies on Node View Modes, or EVA views. However, the basic EVA view still persists in the starter site as it is part of the Islandora Core Feature. Again, it will first look for a Service File, then fall back to the Original File.

      "},{"location":"user-documentation/file-viewers/#changing-a-viewer-for-all-media-of-a-media-type","title":"Changing a Viewer for all media of a media type","text":"

      With the above configuration:

      • Navigate to the \"Manage Display\" page for that media type
      • Select the \"Source\" view mode (the secondary tabs along the top)
      • Make sure that only the appropriate fields are being rendered
      • For the \"main\" file field (it's named different things in different media types: field_media_file, field_media_image... as appropriate), select a different field formatter and configure it how you like it.
      "},{"location":"user-documentation/file-viewers/#configuring-an-optional-viewer","title":"Configuring an \"optional\" viewer","text":"

      Suppose you have a new viewer available, for example, for zip files. You could either:

      • create a new media type specially for zip files, and configure this viewer in the \"Source\" view mode, or,
      • configure an alternative viewer for the File media type.

      Either would work! The choice is yours to make. They're honestly both good.

      Should you choose the latter:

      • create a new Display mode for media at Structure > Display Modes > View modes. Make sure you select a \"Media\" view mode.
      • Configure the relevant (File) Media Type to display your file in your viewer. In the File media type, go to Manage Display, and on the Default tab, enable this view mode for \"Custom Display Settings\" (it's all the way at the bottom). A tab for new display mode should have appeared. Go there and set up your field so that only the file field displays, and it displays using your viewer.
      • in the \"Media Display\" view, create a new Block (or pair of Blocks) that (just for this Block) render the Media in your new view mode. If desired, create a pair with one selecting a Service File and one selecting a Original File, and use \"No results behaviour\" to place a fallback.
      • in the Islandora Display taxonomy, add a new term, with an external URI.
      • create a Context that finds Islandora Nodes that have a term with that URI. In that context, place the block you created in the Media Display view.
      • Finally, edit the Default Media display Context to not be in effect if the node has a term with the URI that you set.
      "},{"location":"user-documentation/file-viewers/#configuring-viewers-that-use-blocks","title":"Configuring Viewers that use Blocks","text":"

      Both OpenSeadragon and Mirador provide blocks that act as multi-page viewers. To configure one of these viewers:

      • Place the block on relevant pages. Usually this is a node page. In the Starter Site this is done by a Context (\"Openseadragon Block - Multipaged items\"). Other methods of placing blocks include the standard Block interface, and Layout Builder.
      • While placing the block, it will ask you to configure the \"IIIF manifest URL\". In the Starter Site, we have a IIIF Manifest view configured to create a manifest based on the \"original file\" media attached to the pages (children) of a given node. In the view, it is configured with path node/%node/book-manifest-original; in the block, we enter this as node/[node:nid]/book-manifest-original. When the block is rendered on a node page, such as node/18, then the nid (18) will be passed into the view.
      • If placing a block using Contexts, make sure that \"Include blocks from block layout\" is selected. (If you find yourself missing normal page elements, this may be why).
      "},{"location":"user-documentation/flysystem/","title":"Flysystem","text":"

      Islandora uses Flysystem and the associated Drupal module to persist binary files to Fedora instead of keeping a copy in both Drupal and Fedora.

      "},{"location":"user-documentation/flysystem/#background","title":"Background","text":"

      \"Flysystem is a filesystem abstraction library for PHP\" which allows applications to read from and write to a variety of data source beyond the local file system, such as an SFTP server, Amazon S3, and Zip files provided an Adapter is available to support it. Flysystem Adapters extend a single class implementing League\\Flysystem\\FilesystemInterface although some separate adapter traits for common actions and properties, such as the StreamedCopyTrait, are available.

      The Drupal Flysystem module extends Flysystem to work within the Drupal filesystem structure. Drupal flysystem plugins include a Flysystem adapter (if not provided by default or in another library) and a class implementing Drupal\\flysystem\\Plugin\\FlysystemPluginInterface which instantiates the Flysystem adapter based on the Drupal site's configuration.

      The Drupal Flysystem module uses flysystem stream wrappers to define filesystem descriptors which are configured in the site's settings.php file. The configurations including the filesystem prefix, adapter (driver), and any adapter-specific configurations such as API endpoints and authorization information.

      "},{"location":"user-documentation/flysystem/#islandoras-implementation","title":"Islandora's Implementation","text":""},{"location":"user-documentation/flysystem/#the-plugin-and-adapter","title":"The Plugin and Adapter","text":"

      Islandora implements a Flysystem adapter and a Drupal Flysystem plugin. The Flysystem adapter acts as an intermediary between the Flysystem filesystem API and Fedora, translating requests and responses between them. The adapter interacts with Fedora using an instance of the Chullo Fedora API it receives from the Drupal Flysystem plugin. The Drupal Flysystem plugin's main responsibility is to instantiate the Chullo Fedora API object with the proper authentication and pass it to the Flysystem adapter. To authenticate with Fedora the plugin adds a handler to the Chullo's Guzzle client which adds a JWT authentication header to each request.

      The Fedora Flysystem adapter does not use Gemini to map the relationship between Drupal URIs and Fedora URIs, so they are indexed separately using the \"files_in_fedora\" Context which triggers the \"Index Fedora File in Gemini\" and \"Delete Fedora File in Gemini\" actions as appropriate.

      "},{"location":"user-documentation/flysystem/#configuration","title":"Configuration","text":"

      The fedora file system is configured in the site's settings.php file. An example configuration can be seen in the islandora-playbook web server role's drupal tasks:

      $settings['flysystem'] = [\n  'fedora' => [\n    'driver' => 'fedora',\n    'config' => [\n      'root' => 'http://localhost:8080/fcrepo/rest/',\n    ],\n  ],\n];\n
      The configuration array's top-level key is the name of the Drupal stream wrapper, which also serves as the filesystem prefix. Any Drupal file path using \"fedora://\" will use this Flysystem adapter. Drupal will translate this prefix to the site's domain plus \"_flystem/fedora/\". For example, using the default configuration provided by the islandora-playbook, a file stored at \"fedora://test.tif\" will persist to Fedora with the URI http://localhost:8080/fcrepo/rest/test.tif and will be accessible from the Drupal URL http://localhost:8000/_flysystem/fedora/test.tif. The driver value fedora corresponds to the plugin's machine name. The config section contains all the adapter-specific configurations. In this case, the only thing configured for the site is the Fedora REST end-point. (Change this value to match your own Fedora's location, if needed.) The JWT is configured separately.

      Other examples of Drupal Flysystem configurations can be seen in the module's README.

      Islandora is configured to have all Media use the Fedora file system by default in the islandora_core_feature. For example, the field storage uri_scheme setting for field_media_image (and the other media types) is \"fedora\". This can also be viewed in the UI on the field's \"Field settings\" page; e.g. http://localhost:8000/admin/structure/media/manage/image/fields/media.image.field_media_image/storage, look for \"Upload destination\" and see that \"Flysystem: fedora\" is selected.

      However, there are methods for saving files that can explicitly set a different filesystem than the default. Migrations can explicitly set which file system a file is saved to and Islandora can emit events that also specify which file system a derivative should be saved to.

      "},{"location":"user-documentation/flysystem/#derivatives","title":"Derivatives","text":"

      As hinted in the previous section, Islandora, by default saves derivatives to the Drupal public file system.

      For example, if I upload a TIFF to a repository item as a File Media with the term \"Original File\", the \"Image Original File\" (image_original_file) Context is triggered. This fires the 'image_generate_a_service_file_from_an_original_file' action which emits an event using the 'public' scheme (file system).

      To make Islandora save future derivatives to Fedora instead of to Drupal, change the corresponding action's \"File system\" setting ('scheme' in the corresponding config file) to 'fedora' instead of 'public'.

      "},{"location":"user-documentation/glossary/","title":"Glossary","text":"

      The following glossary of terms addresses an Islandora context. When comparing new Islandora and Fedora to older versions it may also be helpful to reference the Islandora 7 Glossary.

      "},{"location":"user-documentation/glossary/#alpaca","title":"Alpaca","text":"

      Islandora's event-driven middleware based on Apache Camel that handles communication between various components of Islandora, for instance synchronizing Drupal data with a Fedora repository and the Blazegraph triple store.

      "},{"location":"user-documentation/glossary/#ansible","title":"Ansible","text":"

      Open source software for provisioning, configuration management and application deployment. In the context of Islandora, Ansible can be used to install and maintain the Islandora software stack more conveniently and efficiently on a server or group of servers. The configuration and installation instructions are captured in a human-readable list of tasks, called 'Playbook'. The Islandora Playbook for Ansible is one of the installation methods currently supported by the Islandora community.

      "},{"location":"user-documentation/glossary/#apache","title":"Apache","text":"

      Refers to the Apache Software Foundation, a not-for-profit organization supporting various open source software development projects. The Islandora software stack consists of different components that are developed under the umbrella of the Apache Software Foundation, for instance Apache ActiveMQ, Apache Camel, the Apache HTTP server (webserver), Apache Karaf, Apache Solr, and Apache Tomcat.

      Can in a narrower sense refer to the Apache HTTP server.

      "},{"location":"user-documentation/glossary/#api","title":"API","text":"

      See Application Programming Interface

      "},{"location":"user-documentation/glossary/#application-programming-interface","title":"Application Programming Interface","text":"

      Also API; a connection between computers or between computer programs. It is a type of software interface, offering a service to other pieces of software.

      "},{"location":"user-documentation/glossary/#blazegraph","title":"Blazegraph","text":"

      Blazegraph is an open source triplestore and graph database. Islandora ships Blazegraph as part of the software stack. Metadata about Resource nodes is synchronized between the Drupal database and Blazegraph. Data in the Blazegraph triple store can be queried using SPARQL.

      "},{"location":"user-documentation/glossary/#bundle","title":"Bundle","text":"

      A bundle is the generic name for a sub-type of a Content Entity type in Drupal. To illustrate: Node and Taxonomy Term are both names of Content Entity types, and both have sub-types (\"bundles\"). The bundles of Node are called \"Content Types\" and the bundles of Taxonomy Term are called \"Vocabularies\". Each bundle includes its own configurations of what fields are present on the bundle and how they are entered and displayed. A bundle is thus part of the configuration of your site. Some fieldable Content Entity Types, such as User, do not have bundles.

      "},{"location":"user-documentation/glossary/#cantaloupe","title":"Cantaloupe","text":"

      Cantaloupe is an image server written in Java. It implements the IIIF Image API, which means it handles deep zooming of large images and other image manipulations. It is required to serve images to some viewers such as Mirador and OpenSeadragon.

      "},{"location":"user-documentation/glossary/#checksum","title":"Checksum","text":"

      Checksums are a sequence of numbers and letters to check data for errors. If you know the checksum of an original file, you can use a checksum utility to confirm your copy is identical. Checksums can be used to check the Fixity of a file.

      "},{"location":"user-documentation/glossary/#claw","title":"CLAW","text":"

      CLAW (CLAW Linked Asset WebDataFrameWork) was the development code name for the software released in June 2019 as Islandora 8, now called Islandora.

      "},{"location":"user-documentation/glossary/#collection","title":"Collection","text":"

      A collection is a way of grouping related resources together, much like a directory on a computer. Collections can contain any number of related resource Nodes and sub-collections.

      "},{"location":"user-documentation/glossary/#configuration","title":"Configuration","text":"

      See also: Configuration entity

      Contrast: Content

      In Drupal, your configuration is the total set of configuration entities that are live in your site. Configuration is usually managed through the Drupal GUI, and it can also be exported and imported. When it is active in your site, configuration lives in the Drupal database. When it is exported or serialized, configuration appears as a set of YAML (.yml) files, one file per configuration entity. Configuration can be overridden in the settings.php file.

      "},{"location":"user-documentation/glossary/#configuration-entity","title":"Configuration entity","text":"

      See also: Configuration

      Contrast: Content entity

      A Drupal configuration entity (or \"config entity\") is an individual piece that makes up your site's configuration. It is usually represented as a single YAML (.yml) file, though the actual (\"live\") configuration lives in the database. A config entity usually represents the results of saving a single form in the administration interface, and may contain multiple (usually related) individual settings. Each configuration entity can be exported or imported as a \"single item\" through the Configuration Synchronization GUI, or with the Devel module's \"config editor\" can be edited individually. However, config entities are often interrelated and manual editing is usually not recommended.

      "},{"location":"user-documentation/glossary/#content","title":"Content","text":"

      See also: Content Entity

      Contrast: Configuration

      In Drupal, your content is the total set of things that have been created or uploaded \"as content\" in your website. This includes all content entities - the actual nodes, media, files, taxonomy terms, etc, but does not include anything that is configuration. Content can be exported and imported, but only between sites with exactly the same configuration.

      Sometimes, \"Content\" is used to refer to Nodes but not other content entities. This is the case when creating a new View and one of the options is to make a view of \"Content\".

      "},{"location":"user-documentation/glossary/#content-entity","title":"Content entity","text":"

      See also: Content

      Contrast: Configuration entity

      In Drupal, content entities are the actual nodes, media, taxonomy terms, users, comments, and files that you've created on your site. For example, you may have 223 nodes, 534 media, 1000 taxonomy terms, 14 users, and 535 files in your site - those counts represent the numbers of content entities present in your site. \"Node\", \"Media\", \"Taxonomy term\" etc. are the high-level \"types\" of content entities. Some of these types have sub-types which are called bundles.

      Content entities should not be confused with content types, which are bundles of nodes, and are part of a site's configuration.

      "},{"location":"user-documentation/glossary/#content-model","title":"Content model","text":"

      Deprecated concept used in Islandora Legacy; see Islandora Model.

      "},{"location":"user-documentation/glossary/#content-type","title":"Content type","text":"

      A type of Node. Content types are the \"bundles\" of Nodes, which are a type of Content Entity in Drupal. A content type importantly defines a set of fields and how they are displayed. While a content type describes a type of content entity, the information that makes up the content type itself is all part of your site's configuration.

      The standard Drupal Content types are 'Article' and 'Basic page'. Islandora Starter Site adds 'Repository Item' as a Content type, defining metadata fields typically used to describe digital resources. You can easily create your own content types.

      "},{"location":"user-documentation/glossary/#context","title":"Context","text":"

      An \"if-this-then-that\" configuration created using the Drupal Context contrib module. Islandora extends the capabilities of Context by adding custom Conditions, custom Reactions, and by evaluating context at specific times to allow Contexts to be used for derivatives, indexing, and display.

      "},{"location":"user-documentation/glossary/#crayfish","title":"Crayfish","text":"

      A collection of Islandora microservices. Some microservices are built specifically for use with a Fedora repository, while others are just for general use within Islandora.

      "},{"location":"user-documentation/glossary/#datastream","title":"Datastream","text":"

      Deprecated terminology, refers to how Fedora 3/Islandora Legacy stored files as part of a resource ('object') in the Fedora repository. Replaced by Drupal Media entities, which 'wraps' Files in an intermediate structure. This allows Fields to be attached to files, for instance for storing technical metadata.

      "},{"location":"user-documentation/glossary/#derivative","title":"Derivative","text":"

      A version of a file which is derived from an uploaded file. For example, a thumbnail generated from an uploaded image. Islandora uses microservices to generate derivatives. See the concept page for Derivatives.

      "},{"location":"user-documentation/glossary/#docker","title":"Docker","text":"

      Docker is a platform that use OS-level virtualization to deliver software in packages called containers. Islandora uses Docker as part of ISLE, a suite of Docker containers that run the various components of Islandora.

      "},{"location":"user-documentation/glossary/#drupal","title":"Drupal","text":"

      Drupal is an open source web content management system (CMS) written in PHP. Known for being extremely flexible and extensible, Drupal is supported by a community of over 630,000 users and developers. Drupal sites can be customized and themed in a wide variety of ways. Drupal sites must include Drupal Core and usually involve additional, Contributed code.

      "},{"location":"user-documentation/glossary/#drupal-core","title":"Drupal Core","text":"

      The files, themes, profile, and modules included with the standard project software download.

      "},{"location":"user-documentation/glossary/#drupal-roles","title":"Drupal Roles","text":"

      Roles are a way of assigning specific permissions to a group of users. Any user assigned to a role will have the same permissions as all other users assigned to that role. This allows you to control which users have permission to view, edit, or delete content in Drupal. Islandora provides a special role called fedoraAdmin that is required to have actions in Drupal reflected in Fedora.

      "},{"location":"user-documentation/glossary/#entity","title":"Entity","text":"

      A Drupal term for an item of either content or configuration data. Examples include Nodes (content items), Blocks, Taxonomy terms, and definitions of content types; the first three are content entities, and the last is a configuration entity. In common usage, the term often refers to Drupal content entities like Nodes or Taxonomy terms.

      "},{"location":"user-documentation/glossary/#fedora-repository-software","title":"Fedora (Repository Software)","text":"

      Fedora is a digital asset management architecture upon which institutional repositories, digital archives, and digital library systems might be built. Fedora is the underlying architecture for a digital repository, and is not a complete management, indexing, discovery, and delivery application.

      The Fedora repository functions as the standard smart storage for Islandora.

      "},{"location":"user-documentation/glossary/#ffmpeg","title":"FFmpeg","text":"

      FFmpeg is a cross-platform audio and video processing software. In Islandora, FFmpeg is provided by the Crayfish microservice, Homarus.

      "},{"location":"user-documentation/glossary/#field","title":"Field","text":"

      Data of a certain type that is attached to a content entity. A field is made up of field storage which inclues some low-level configuration that is shared across all field instances, and a field instance, which configures how that field appears on a bundle. Fields also have field widgets that govern data entry, and field formatters which control how the field is displayed to site visitors.

      "},{"location":"user-documentation/glossary/#field-instance","title":"Field instance","text":"

      A field, configured to show up on a bundle. It is possible to reuse fields on different bundles, and they share the same field storage but are different field instances and can have different configurations (such as field name, description, cardinality, required, etc).

      For example, the Repository Item Content type has a field instance for Alternative Title, with the following configuration:

      • Display name: Alternative Title
      • Help text: (blank)
      • Required: no
      • Translatable: yes
      • Field visibility and permissions: not set
      • Default value: none
      "},{"location":"user-documentation/glossary/#field-formatter","title":"Field formatter","text":"

      A field formatter configures how a field instance is displayed to site visitors. The formatters available depend on the field type. For example, an EDTF-type field can use the \"Default EDTF Formatter\", which can be configured (per field instance) to be big- or little-endian, and to show year, month, and day info in a variety of ways. Field Formatters are configured on the \"Manage Display\" tab of a bundle.

      "},{"location":"user-documentation/glossary/#field-type","title":"Field type","text":"

      The type of a field instance or field_storage. Drupal Core defines several field types and modules can define additional field types. The field type determines what widgets and field formatters are available, as well as what configuration options are available in the field instance. Some commonly used field types in Islandora include: