diff --git a/src/main/java/org/olat/course/nodes/edubase/EdubaseContext.java b/src/main/java/org/olat/course/nodes/edubase/EdubaseContext.java index a4cfa0350d975606d59a8e2eefe19a71371e91eb..d407ef026f54f11fe6e50614da62f364fea1df4b 100644 --- a/src/main/java/org/olat/course/nodes/edubase/EdubaseContext.java +++ b/src/main/java/org/olat/course/nodes/edubase/EdubaseContext.java @@ -30,7 +30,6 @@ import org.olat.ims.lti.LTIContext; import org.olat.ims.lti.LTIDisplayOptions; import org.olat.ims.lti.LTIManager; import org.olat.modules.edubase.EdubaseManager; -import org.olat.modules.edubase.EdubaseModule; /** * @@ -104,9 +103,10 @@ public class EdubaseContext implements LTIContext { public String getCustomProperties() { Map<String, String> customProps = new HashMap<>(); - String readerUrl = CoreSpringFactory.getImpl(EdubaseModule.class).getReaderUrl(); - if (StringHelper.containsNonWhitespace(readerUrl)) { - customProps.put(CUSTOM_APPLICATION, readerUrl); + Identity identity = identityEnvironment.getIdentity(); + String applicationUrl = CoreSpringFactory.getImpl(EdubaseManager.class).getApplicationUrl(identity); + if (StringHelper.containsNonWhitespace(applicationUrl)) { + customProps.put(CUSTOM_APPLICATION, applicationUrl); } if (pageTo != null) { customProps.put(CUSTOM_END_PAGE, Integer.toString(pageTo)); diff --git a/src/main/java/org/olat/modules/edubase/EdubaseManager.java b/src/main/java/org/olat/modules/edubase/EdubaseManager.java index 2c925ec8194c3faaebccd1d9f9165433659625a1..73bb0fbcba3416466ba8bedb49a9e5e9583afcc4 100644 --- a/src/main/java/org/olat/modules/edubase/EdubaseManager.java +++ b/src/main/java/org/olat/modules/edubase/EdubaseManager.java @@ -19,6 +19,7 @@ */ package org.olat.modules.edubase; +import org.olat.core.id.Identity; import org.olat.core.id.IdentityEnvironment; /** @@ -63,6 +64,15 @@ public interface EdubaseManager { */ public String getLtiLaunchUrl(BookSection bookSection); + /** + * The application url is the target url of the edubase reader. The url is + * depending on the module configuration unique per identity. + * + * @param identity + * @return + */ + public String getApplicationUrl(Identity identity); + /** * Request details of a book version from the Edubase InfoDocVers Service. * diff --git a/src/main/java/org/olat/modules/edubase/EdubaseModule.java b/src/main/java/org/olat/modules/edubase/EdubaseModule.java index 0e349d51478e87a8d0115d97d72a855da50246fd..81ab22f3c82d846726422e6ea6a7450a6bde6077 100644 --- a/src/main/java/org/olat/modules/edubase/EdubaseModule.java +++ b/src/main/java/org/olat/modules/edubase/EdubaseModule.java @@ -40,6 +40,7 @@ public class EdubaseModule extends AbstractSpringModule implements ConfigOnOff { public static final String EDUBASE_OAUTH_KEY = "edubase.oauthKey"; public static final String EDUBASE_OAUTH_SECRET = "edubase.oauth"; public static final String EDUBASE_READER_URL = "edubase.readerUrl"; + public static final String EDUBASE_READER_URL_UNIQUE = "edubase.readerUrl.unique"; public static final String EDUBASE_LTI_LAUNCH_URL = "edubase.ltiLaunchUrl"; public static final String EDUBASE_INFOVER_URL = "edubase.infoverUrl"; @@ -49,6 +50,8 @@ public class EdubaseModule extends AbstractSpringModule implements ConfigOnOff { private String oauthSecret; @Value("${edubase.readerUrl}") private String readerUrl; + @Value("${edubase.readerUrl.unique:true}") + private boolean readerUrlUnique; @Value("${edubase.ltiLaunchUrl}") private String ltiLaunchUrl; @Value("${edubase.infoverUrl}") @@ -85,6 +88,11 @@ public class EdubaseModule extends AbstractSpringModule implements ConfigOnOff { if (StringHelper.containsNonWhitespace(readerUrlObj)) { readerUrl = readerUrlObj; } + + String readerUrlUniqueObj = getStringPropertyValue(EDUBASE_READER_URL_UNIQUE, true); + if(StringHelper.containsNonWhitespace(readerUrlUniqueObj)) { + readerUrlUnique = "true".equals(readerUrlUniqueObj); + } String ltiLaunchUrlObj = getStringPropertyValue(EDUBASE_LTI_LAUNCH_URL, true); if (StringHelper.containsNonWhitespace(ltiLaunchUrlObj)) { @@ -129,6 +137,14 @@ public class EdubaseModule extends AbstractSpringModule implements ConfigOnOff { setStringProperty(EDUBASE_READER_URL, readerUrl, true); } + public boolean isReaderUrlUnique() { + return readerUrlUnique; + } + + public void setReaderUrlUnique(boolean readerUrlUnique) { + this.readerUrlUnique = readerUrlUnique; + } + public String getLtiLaunchUrl() { return ltiLaunchUrl; } diff --git a/src/main/java/org/olat/modules/edubase/manager/EdubaseManagerImpl.java b/src/main/java/org/olat/modules/edubase/manager/EdubaseManagerImpl.java index 11463e6d503e4afba6063b6e140b794022f03629..628d3bde81adccb478410d1ea97146428fc3a05c 100644 --- a/src/main/java/org/olat/modules/edubase/manager/EdubaseManagerImpl.java +++ b/src/main/java/org/olat/modules/edubase/manager/EdubaseManagerImpl.java @@ -23,6 +23,7 @@ package org.olat.modules.edubase.manager; import java.io.EOFException; import java.net.SocketTimeoutException; import java.util.Optional; +import java.util.UUID; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; @@ -33,10 +34,12 @@ import org.apache.http.util.EntityUtils; import org.codehaus.jackson.JsonParseException; import org.codehaus.jackson.map.ObjectMapper; import org.olat.basesecurity.AuthHelper; +import org.olat.core.id.Identity; import org.olat.core.id.IdentityEnvironment; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.util.StringHelper; +import org.olat.core.util.WebappHelper; import org.olat.login.LoginModule; import org.olat.login.auth.AuthenticationProvider; import org.olat.modules.edubase.BookDetails; @@ -137,6 +140,27 @@ public class EdubaseManagerImpl implements EdubaseManager { return url.toString(); } + @Override + public String getApplicationUrl(Identity identity) { + String readerUrl = edubaseModule.getReaderUrl(); + if (edubaseModule.isReaderUrlUnique()) { + int protocolEnd = readerUrl.indexOf("//") + 2; + String protocol = readerUrl.substring(0, protocolEnd); + String host = readerUrl.substring(protocolEnd); + // Is OpenOLAT identity ok or should it be the getUserId(identEnv)? + String identityKey = String.valueOf(identity.getKey()); + String identityHash = UUID.nameUUIDFromBytes(identityKey.getBytes()).toString().replace("-", ""); + readerUrl = new StringBuilder() + .append(protocol) + .append(identityHash) + .append("-") + .append(WebappHelper.getInstanceId()) + .append(".") + .append(host).toString(); + } + return readerUrl; + } + @Override public BookDetails fetchBookDetails(String bookId) { BookDetails infoReponse = new BookDetailsImpl(); diff --git a/src/main/java/org/olat/modules/edubase/ui/EdubaseAdminController.java b/src/main/java/org/olat/modules/edubase/ui/EdubaseAdminController.java index cb860e20441756e535e0c036d0fac0ea5f3e03ad..025fc61053b01bf9d2aa47fd1281f389ac0e5900 100644 --- a/src/main/java/org/olat/modules/edubase/ui/EdubaseAdminController.java +++ b/src/main/java/org/olat/modules/edubase/ui/EdubaseAdminController.java @@ -46,6 +46,7 @@ public class EdubaseAdminController extends FormBasicController { private TextElement edubaseOauthSecretEl; private TextElement edubaseLtiLaunchUrlEl; private TextElement edubaseReaderUrlEl; + private MultipleSelectionElement edubaseReaderUrlUniqueEl; private TextElement edubaseInfoverUrlEl; @Autowired @@ -68,9 +69,7 @@ public class EdubaseAdminController extends FormBasicController { String[] enableValues = new String[]{ translate("on") }; edubaseEnabledEl = uifactory.addCheckboxesHorizontal("admin.edubase.enabled", edubaseCont, enabledKeys, enableValues); - if (edubaseModule.isEnabled()) { - edubaseEnabledEl.select(enabledKeys[0], true); - } + edubaseEnabledEl.select(enabledKeys[0], edubaseModule.isEnabled()); String edubaseOauthKey = edubaseModule.getOauthKey(); edubaseOauthKeyEl = uifactory.addTextElement("admin.edubase.oauth.key", "admin.edubase.oauth.key", 128, edubaseOauthKey, edubaseCont); @@ -90,6 +89,10 @@ public class EdubaseAdminController extends FormBasicController { String edubaseReaderUrl = edubaseModule.getReaderUrl(); edubaseReaderUrlEl = uifactory.addTextElement("admin.edubase.reader.url", "admin.edubase.reader.url", 128, edubaseReaderUrl, edubaseCont); edubaseReaderUrlEl.setMandatory(true); + + edubaseReaderUrlUniqueEl = uifactory.addCheckboxesHorizontal("admin.edubase.reader.url.unique", edubaseCont, enabledKeys, enableValues); + edubaseReaderUrlUniqueEl.setHelpTextKey("admin.edubase.reader.url.unique.help", null); + edubaseReaderUrlUniqueEl.select(enabledKeys[0], edubaseModule.isReaderUrlUnique()); String edubaseInfoverUrl = edubaseModule.getInfoverUrl(); edubaseInfoverUrlEl = uifactory.addTextElement("admin.edubase.infover.url", "admin.edubase.infover.url", 128, edubaseInfoverUrl, edubaseCont); @@ -116,6 +119,7 @@ public class EdubaseAdminController extends FormBasicController { edubaseModule.setOauthSecret(edubaseOauthSecretEl.getValue()); edubaseModule.setLtiLaunchUrl(edubaseLtiLaunchUrlEl.getValue()); edubaseModule.setReaderUrl(edubaseReaderUrlEl.getValue()); + edubaseModule.setReaderUrlUnique(edubaseReaderUrlUniqueEl.isAtLeastSelected(1)); edubaseModule.setInfoverUrl(edubaseInfoverUrlEl.getValue()); } diff --git a/src/main/java/org/olat/modules/edubase/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/modules/edubase/ui/_i18n/LocalStrings_de.properties index 6bc9feaf0174f83adb0e1b5a167161ffa5c0a9f8..6953bd7bff015e14d07ab73d001e768f20396946 100644 --- a/src/main/java/org/olat/modules/edubase/ui/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/modules/edubase/ui/_i18n/LocalStrings_de.properties @@ -6,6 +6,8 @@ admin.edubase.lti.launch.url=URL LTI Start admin.edubase.oauth.key=Key admin.edubase.oauth.secret=Secret admin.edubase.reader.url=URL Edubase Reader +admin.edubase.reader.url.unique=URL Edubase Reader eindeutig +admin.edubase.reader.url.unique.help=Die Edubase Reader URL wird um ein pro Benutzer eindeutiges K\u00fcrzel erg\u00e4nzt, damit Probleme mit der Anzahl registrierter Ger\u00e4te vermieden werden k\u00f6nnen. admin.edubase.title=Edubase admin.expert.settings=Experteneistellungen admin.menu.title.alt=Edubase und Edubook Kursbausteine diff --git a/src/main/java/org/olat/modules/edubase/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/modules/edubase/ui/_i18n/LocalStrings_en.properties index e24e25b3860760e73ecddd33912cd9ada2aa2681..29f4d7dfe50e5fc0814d747fd5e88b3fd485d552 100644 --- a/src/main/java/org/olat/modules/edubase/ui/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/modules/edubase/ui/_i18n/LocalStrings_en.properties @@ -6,6 +6,8 @@ admin.edubase.lti.launch.url=URL LTI launch admin.edubase.oauth.key=Key admin.edubase.oauth.secret=Secret admin.edubase.reader.url=URL Edubase Reader +admin.edubase.reader.url.unique=URL Edubase Reader unique +admin.edubase.reader.url.unique.help=The Edubase Reader URL is amended by a user specific token to avoid problems with the number of devices. admin.edubase.title=Edubase admin.expert.settings=Expert settings admin.menu.title.alt=Edubase and Edubook course elements diff --git a/src/main/resources/serviceconfig/olat.properties b/src/main/resources/serviceconfig/olat.properties index ab6e4e7be6b2b8a893be212cadebf90e99289ba1..beda32fd2fe2efdedf39fbec82679d31ac8312d6 100644 --- a/src/main/resources/serviceconfig/olat.properties +++ b/src/main/resources/serviceconfig/olat.properties @@ -1351,6 +1351,7 @@ card2brain.verifyLtiUrl=https://card2brain.ch/grails/SSO/verifyLti.dispatch ######################################## edubase.enabled=false edubase.readerUrl=https://app.edubase.ch/ +edubase.readerUrl.unique=true edubase.ltiLaunchUrl=https://reader.silkcodeapps.de/lookup/srv/v2/lti/basicLaunch/ edubase.infoverUrl=https://reader.silkcodeapps.de/lookup/srv/v2/information/infodocvers/%s diff --git a/src/test/java/org/olat/modules/edubase/manager/EdubaseManagerImplTest.java b/src/test/java/org/olat/modules/edubase/manager/EdubaseManagerImplTest.java index c37dbe2bb77f5d016776053d49421f62b622d90c..936755b5a48da9dd536384989d5c941140893826 100644 --- a/src/test/java/org/olat/modules/edubase/manager/EdubaseManagerImplTest.java +++ b/src/test/java/org/olat/modules/edubase/manager/EdubaseManagerImplTest.java @@ -209,5 +209,61 @@ public class EdubaseManagerImplTest { String expectedLtiUrl = baseUrl + "/" + BOOK_ID + "/" + pageFrom; assertThat(generatedLtiUrl).isEqualTo(expectedLtiUrl); } + + @Test + public void shouldGetApplicationUrl() { + IdentityImpl identityImpl = new IdentityImpl(); + String readerUrl = "https://reader.openolat.com"; + when(edubaseModuleMock.getReaderUrl()).thenReturn(readerUrl); + when(edubaseModuleMock.isReaderUrlUnique()).thenReturn(Boolean.FALSE); + + String applicationUrl = sut.getApplicationUrl(identityImpl); + + assertThat(applicationUrl).isEqualTo(readerUrl); + } + + @Test + public void shouldGetApplicationUrlWithToken() { + IdentityImpl identityImpl = new IdentityImpl(); + identityImpl.setKey(Long.valueOf("1")); + String readerUrl = "https://reader.openolat.com"; + when(edubaseModuleMock.getReaderUrl()).thenReturn(readerUrl); + when(edubaseModuleMock.isReaderUrlUnique()).thenReturn(Boolean.TRUE); + + String applicationUrl = sut.getApplicationUrl(identityImpl); + + assertThat(applicationUrl).startsWith("https://").endsWith("reader.openolat.com"); + assertThat(applicationUrl.length()).isGreaterThan(readerUrl.length()); + } + + @Test + public void shouldGetAllwaysSameApplicationUrlForAUser() { + IdentityImpl identityImpl = new IdentityImpl(); + identityImpl.setKey(Long.valueOf("1")); + String readerUrl = "https://reader.openolat.com"; + when(edubaseModuleMock.getReaderUrl()).thenReturn(readerUrl); + when(edubaseModuleMock.isReaderUrlUnique()).thenReturn(Boolean.TRUE); + + String applicationUrl1 = sut.getApplicationUrl(identityImpl); + String applicationUrl2 = sut.getApplicationUrl(identityImpl); + + assertThat(applicationUrl1).isEqualTo(applicationUrl2); + } + + @Test + public void shouldGetDifferentApplicationUrlForDifferentUsers() { + IdentityImpl identityImpl1 = new IdentityImpl(); + identityImpl1.setKey(Long.valueOf("1")); + IdentityImpl identityImpl2 = new IdentityImpl(); + identityImpl2.setKey(Long.valueOf("2")); + String readerUrl = "https://reader.openolat.com"; + when(edubaseModuleMock.getReaderUrl()).thenReturn(readerUrl); + when(edubaseModuleMock.isReaderUrlUnique()).thenReturn(Boolean.TRUE); + + String applicationUrl1 = sut.getApplicationUrl(identityImpl1); + String applicationUrl2 = sut.getApplicationUrl(identityImpl2); + + assertThat(applicationUrl1).isNotEqualTo(applicationUrl2); + } }