diff --git a/README.txt b/README.txt index b3b5cec16..7e3de9342 100644 --- a/README.txt +++ b/README.txt @@ -16,6 +16,15 @@ To include integration tests into the reports mvn clean package test site site:stage -P$profile-name +Note: If there are test failures the package goal fails and the site is not +build. The build the site anywhy use + + mvn clean package site site:stage -Dmaven.test.failure.ignore=true + +or with a profile + + mvn clean package site site:stage -Dmaven.test.failure.ignore=true -Pwildfly-remote-h2-mem + The available profiles are listed in the documentation. All modules should provide a profile called wildfly-remote-h2-mem. This profile uses a remote Wildfly application server and its integrated H2 in-memory database for diff --git a/ccm-bundle-devel-wildfly-web/pom.xml b/ccm-bundle-devel-wildfly-web/pom.xml index d61bfb295..3c5e35032 100644 --- a/ccm-bundle-devel-wildfly-web/pom.xml +++ b/ccm-bundle-devel-wildfly-web/pom.xml @@ -93,18 +93,18 @@ org.apache.maven.plugins maven-war-plugin - + + org.libreccm ccm-theme-foundry jar - --> + diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemL10NManager.java b/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemL10NManager.java index db75240a8..0d1ef7f5d 100644 --- a/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemL10NManager.java +++ b/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemL10NManager.java @@ -19,15 +19,30 @@ package org.librecms.contentsection; import com.arsdigita.kernel.KernelConfig; +import com.arsdigita.util.UncheckedWrapperException; +import org.libreccm.configuration.ConfigurationManager; import org.libreccm.l10n.LocalizedString; import org.libreccm.security.AuthorizationRequired; import org.libreccm.security.RequiresPrivilege; import org.librecms.CmsConstants; +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; import java.util.Locale; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import javax.annotation.PostConstruct; import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; import javax.transaction.Transactional; /** @@ -38,6 +53,64 @@ import javax.transaction.Transactional; @RequestScoped public class ContentItemL10NManager { + @Inject + private ConfigurationManager confManager; + + @Inject + private ContentItemRepository itemRepo; + + private Locale defaultLocale; + private List supportedLocales; + + @PostConstruct + private void init() { + final KernelConfig kernelConfig = confManager.findConfiguration( + KernelConfig.class); + defaultLocale = kernelConfig.getDefaultLocale(); + supportedLocales = kernelConfig.getSupportedLanguages() + .stream() + .map(language -> new Locale(language)) + .collect(Collectors.toList()); + } + + private List findLocalizedStringProperties( + final ContentItem item) { + + try { + return Arrays.stream( + Introspector.getBeanInfo(item.getClass()) + .getPropertyDescriptors()) + .filter(property -> property.getPropertyType().isAssignableFrom( + LocalizedString.class)) + .collect(Collectors.toList()); + } catch (IntrospectionException ex) { + throw new UncheckedWrapperException(ex); + } + } + + private LocalizedString readLocalizedString(final ContentItem item, + final Method readMethod) { + try { + return (LocalizedString) readMethod.invoke(item); + } catch (IllegalAccessException + | IllegalArgumentException + | InvocationTargetException ex) { + throw new UncheckedWrapperException(ex); + } + } + + private Set collectLanguages(final ContentItem item) { + final Set locales = new HashSet<>(); + + findLocalizedStringProperties(item) + .stream() + .map(property -> property.getReadMethod()) + .map(readMethod -> readLocalizedString(item, readMethod)) + .forEach(str -> locales.addAll(str.getAvailableLocales())); + + return locales; + } + /** * Checks if an content item has data for particular language. * @@ -49,8 +122,16 @@ public class ContentItemL10NManager { */ @Transactional(Transactional.TxType.REQUIRED) public boolean hasLanguage(final ContentItem item, final Locale locale) { + if (item == null) { + throw new IllegalArgumentException("Can't check if item null has a" + + "specific locale."); + } - throw new UnsupportedOperationException(); + if (locale == null) { + throw new IllegalArgumentException("Can't check for locale null."); + } + + return collectLanguages(item).contains(locale); } /** @@ -70,7 +151,50 @@ public class ContentItemL10NManager { final ContentItem item, final Locale locale) { - throw new UnsupportedOperationException(); + findLocalizedStringProperties(item) + .forEach(property -> addLanguage(item, locale, property)); + + itemRepo.save(item); + } + + private void addLanguage(final ContentItem item, + final Locale locale, + final PropertyDescriptor property) { + + final Method readMethod = property.getReadMethod(); + final LocalizedString localizedStr = readLocalizedString(item, + readMethod); + addLanguage(localizedStr, locale); + } + + private void addLanguage(final LocalizedString localizedString, + final Locale locale) { + if (localizedString.hasValue(locale)) { + //Nothing to do + return; + } + + final String value; + if (localizedString.hasValue(defaultLocale)) { + value = localizedString.getValue(defaultLocale); + } else { + value = findValue(localizedString); + } + + localizedString.addValue(locale, value); + } + + private String findValue(final LocalizedString localizedStr) { + final Optional locale = supportedLocales + .stream() + .filter(current -> localizedStr.hasValue(current)) + .findAny(); + + if (locale.isPresent()) { + return localizedStr.getValue(locale.get()); + } else { + return "Lorem ipsum"; + } } /** @@ -86,10 +210,26 @@ public class ContentItemL10NManager { @Transactional(Transactional.TxType.REQUIRED) public void removeLangauge( @RequiresPrivilege(CmsConstants.PRIVILEGE_ITEMS_EDIT) - final ContentItem item, + final ContentItem item, final Locale locale) { - - throw new UnsupportedOperationException(); + + findLocalizedStringProperties(item) + .forEach(property -> removeLanguage(item, locale, property)); + + itemRepo.save(item); + } + + private void removeLanguage(final ContentItem item, + final Locale locale, + final PropertyDescriptor property) { + + final Method readMethod = property.getReadMethod(); + + final LocalizedString localizedStr = readLocalizedString(item, + readMethod); + if (localizedStr.hasValue(locale)) { + localizedStr.removeValue(locale); + } } /** @@ -108,8 +248,29 @@ public class ContentItemL10NManager { public void normalizedLanguages( @RequiresPrivilege(CmsConstants.PRIVILEGE_ITEMS_EDIT) final ContentItem item) { + + final Set languages = collectLanguages(item); + + findLocalizedStringProperties(item) + .stream() + .map(property -> property.getReadMethod()) + .map(readMethod -> readLocalizedString(item, readMethod)) + .forEach(str -> normalize(str, languages)); - throw new UnsupportedOperationException(); + itemRepo.save(item); + } + + private void normalize(final LocalizedString localizedString, + final Set languages) { + + final List missingLangs = languages.stream() + .filter(lang -> !localizedString.hasValue(lang)) + .collect(Collectors.toList()); + + if (!missingLangs.isEmpty()) { + missingLangs.stream() + .forEach(lang -> addLanguage(localizedString, lang)); + } } } diff --git a/ccm-cms/src/test/java/org/librecms/contentsection/ContentItemL10NManagerTest.java b/ccm-cms/src/test/java/org/librecms/contentsection/ContentItemL10NManagerTest.java index 0e148c205..087e28422 100644 --- a/ccm-cms/src/test/java/org/librecms/contentsection/ContentItemL10NManagerTest.java +++ b/ccm-cms/src/test/java/org/librecms/contentsection/ContentItemL10NManagerTest.java @@ -42,6 +42,7 @@ import org.junit.runner.RunWith; import org.libreccm.tests.categories.IntegrationTest; import java.util.Locale; +import java.util.Optional; import javax.inject.Inject; @@ -164,9 +165,17 @@ public class ContentItemL10NManagerTest { @Test @InSequence(10) @UsingDataSet("datasets/org/librecms/contentsection/" - + "ContentItemL10NManager/data.xml") + + "ContentItemL10NManagerTest/data.xml") public void verifyHasLanguage() { - fail(); + final Optional item = itemRepo.findById(-10100L); + assertThat(item.isPresent(), is(true)); + + assertThat(l10nManager.hasLanguage(item.get(), Locale.ENGLISH), + is(true)); + assertThat(l10nManager.hasLanguage(item.get(), Locale.FRENCH), + is(true)); + assertThat(l10nManager.hasLanguage(item.get(), Locale.GERMAN), + is(false)); } /** @@ -176,12 +185,14 @@ public class ContentItemL10NManagerTest { * for the item. */ @Test(expected = IllegalArgumentException.class) - @InSequence(10) + @InSequence(20) @UsingDataSet("datasets/org/librecms/contentsection/" - + "ContentItemL10NManager/data.xml") + + "ContentItemL10NManagerTest/data.xml") @ShouldThrowException(IllegalArgumentException.class) public void hasLanguageItemIsNull() { - fail(); + final ContentItem item = null; + + l10nManager.hasLanguage(item, Locale.ENGLISH); } /** @@ -191,12 +202,17 @@ public class ContentItemL10NManagerTest { * for the language. */ @Test(expected = IllegalArgumentException.class) - @InSequence(10) + @InSequence(30) @UsingDataSet("datasets/org/librecms/contentsection/" - + "ContentItemL10NManager/data.xml") + + "ContentItemL10NManagerTest/data.xml") @ShouldThrowException(IllegalArgumentException.class) public void hasLanguageLanguageIsNull() { - fail(); + final Optional item = itemRepo.findById(-10100L); + assertThat(item.isPresent(), is(true)); + + assertThat(l10nManager.hasLanguage(item.get(), null), + is(true)); + } /** @@ -204,14 +220,18 @@ public class ContentItemL10NManagerTest { * {@link ContentItemL10NManager#addLanguage(org.librecms.contentsection.ContentItem, java.util.Locale)}. */ @Test - @InSequence(20) + @InSequence(40) @UsingDataSet("datasets/org/librecms/contentsection/" - + "ContentItemL10NManager/data.xml") + + "ContentItemL10NManagerTest/data.xml") @ShouldMatchDataSet( value = "datasets/org/librecms/contentsection/" - + "ContentItemL10NManager/after-add-language.xml") + + "ContentItemL10NManagerTest/after-add-language.xml", + excludeColumns = {"timestamp"}) public void addLanguage() { - fail(); + final Optional item = itemRepo.findById(-10100L); + assertThat(item.isPresent(), is(true)); + + l10nManager.addLanguage(item.get(), Locale.GERMAN); } /** @@ -220,14 +240,18 @@ public class ContentItemL10NManagerTest { * with an existing language has not effect. */ @Test - @InSequence(20) + @InSequence(50) @UsingDataSet("datasets/org/librecms/contentsection/" - + "ContentItemL10NManager/data.xml") + + "ContentItemL10NManagerTest/data.xml") @ShouldMatchDataSet( value = "datasets/org/librecms/contentsection/" - + "ContentItemL10NManager/data.xml") + + "ContentItemL10NManagerTest/data.xml") public void addLanguageAlreadyPresent() { - fail(); + final Optional item = itemRepo.findById(-10100L); + assertThat(item.isPresent(), is(true)); + + l10nManager.addLanguage(item.get(), Locale.FRENCH); + } /** @@ -236,15 +260,17 @@ public class ContentItemL10NManagerTest { * {@code null} for the item. */ @Test(expected = IllegalArgumentException.class) - @InSequence(20) + @InSequence(60) @UsingDataSet("datasets/org/librecms/contentsection/" - + "ContentItemL10NManager/data.xml") + + "ContentItemL10NManagerTest/data.xml") @ShouldMatchDataSet( value = "datasets/org/librecms/contentsection/" - + "ContentItemL10NManager/data.xml") + + "ContentItemL10NManagerTest/data.xml") @ShouldThrowException(IllegalArgumentException.class) public void addLanguageItemIsNull() { - fail(); + final ContentItem item = null; + + l10nManager.addLanguage(item, Locale.GERMAN); } /** @@ -255,27 +281,35 @@ public class ContentItemL10NManagerTest { @Test(expected = IllegalArgumentException.class) @InSequence(20) @UsingDataSet("datasets/org/librecms/contentsection/" - + "ContentItemL10NManager/data.xml") + + "ContentItemL10NManagerTest/data.xml") @ShouldMatchDataSet( value = "datasets/org/librecms/contentsection/" - + "ContentItemL10NManager/data.xml") + + "ContentItemL10NManagerTest/data.xml") @ShouldThrowException(IllegalArgumentException.class) public void addLanguageLanguageIsNull() { - fail(); + final Optional item = itemRepo.findById(-10100L); + assertThat(item.isPresent(), is(true)); + + l10nManager.addLanguage(item.get(), null); } /** - * Tries to remove language from a content item by using null {@link ContentItemL10NManager#removeLangauge(org.librecms.contentsection.ContentItem, java.util.Locale) + * Tries to remove language from a content item by using + * {@link ContentItemL10NManager#removeLangauge(org.librecms.contentsection.ContentItem, java.util.Locale)}. */ @Test - @InSequence(20) + @InSequence(70) @UsingDataSet("datasets/org/librecms/contentsection/" - + "ContentItemL10NManager/data.xml") + + "ContentItemL10NManagerTest/data.xml") @ShouldMatchDataSet( value = "datasets/org/librecms/contentsection/" - + "ContentItemL10NManager/after-remove-language.xml") + + "ContentItemL10NManagerTest/after-remove-language.xml", + excludeColumns = {"timestamp"}) public void removeLanguage() { - fail(); + final Optional item = itemRepo.findById(-10100L); + assertThat(item.isPresent(), is(true)); + + l10nManager.removeLangauge(item.get(), Locale.FRENCH); } /** @@ -284,14 +318,17 @@ public class ContentItemL10NManagerTest { * has not effect if called for not present language. */ @Test - @InSequence(20) + @InSequence(80) @UsingDataSet("datasets/org/librecms/contentsection/" - + "ContentItemL10NManager/data.xml") + + "ContentItemL10NManagerTest/data.xml") @ShouldMatchDataSet( value = "datasets/org/librecms/contentsection/" - + "ContentItemL10NManager/data.xml") + + "ContentItemL10NManagerTest/data.xml") public void removeNotPresentLanguage() { - fail(); + final Optional item = itemRepo.findById(-10100L); + assertThat(item.isPresent(), is(true)); + + l10nManager.removeLangauge(item.get(), Locale.GERMAN); } /** @@ -301,15 +338,17 @@ public class ContentItemL10NManagerTest { * for the item. */ @Test(expected = IllegalArgumentException.class) - @InSequence(20) + @InSequence(90) @UsingDataSet("datasets/org/librecms/contentsection/" - + "ContentItemL10NManager/data.xml") + + "ContentItemL10NManagerTest/data.xml") @ShouldMatchDataSet( value = "datasets/org/librecms/contentsection/" - + "ContentItemL10NManager/data.xml") + + "ContentItemL10NManagerTest/data.xml") @ShouldThrowException(IllegalArgumentException.class) public void removeLanguageItemIsNull() { - fail(); + final ContentItem item = null; + + l10nManager.removeLangauge(item, Locale.GERMAN); } /** @@ -319,29 +358,38 @@ public class ContentItemL10NManagerTest { * for the language. */ @Test(expected = IllegalArgumentException.class) - @InSequence(20) + @InSequence(100) @UsingDataSet("datasets/org/librecms/contentsection/" - + "ContentItemL10NManager/data.xml") + + "ContentItemL10NManagerTest/data.xml") @ShouldMatchDataSet( value = "datasets/org/librecms/contentsection/" - + "ContentItemL10NManager/data.xml") + + "ContentItemL10NManagerTest/data.xml") @ShouldThrowException(IllegalArgumentException.class) public void removeLanguageLanguageIsNull() { - fail(); + final Optional item = itemRepo.findById(-10100L); + assertThat(item.isPresent(), is(true)); + + l10nManager.removeLangauge(item.get(), null); + } /** - * Tries to normalise the languages of a content item by using null {@link ContentItemL10NManager#normalizedLanguages(org.librecms.contentsection.ContentItem) + * Tries to normalise the languages of a content item by using null null + * null null null null null null null null null null {@link ContentItemL10NManager#normalizedLanguages(org.librecms.contentsection.ContentItem) */ @Test - @InSequence(20) + @InSequence(120) @UsingDataSet("datasets/org/librecms/contentsection/" - + "ContentItemL10NManager/data.xml") + + "ContentItemL10NManagerTest/data.xml") @ShouldMatchDataSet( value = "datasets/org/librecms/contentsection/" - + "ContentItemL10NManager/after-normalize.xml") + + "ContentItemL10NManagerTest/after-normalize.xml", + excludeColumns = {"timestamp"}) public void normalizeItem() { - fail(); + final Optional item = itemRepo.findById(-10200L); + assertThat(item.isPresent(), is(true)); + + l10nManager.normalizedLanguages(item.get()); } /** @@ -350,14 +398,18 @@ public class ContentItemL10NManagerTest { * for already normalised item has not effect. */ @Test - @InSequence(20) + @InSequence(130) @UsingDataSet("datasets/org/librecms/contentsection/" - + "ContentItemL10NManager/data.xml") + + "ContentItemL10NManagerTest/data.xml") @ShouldMatchDataSet( value = "datasets/org/librecms/contentsection/" - + "ContentItemL10NManager/data.xml") + + "ContentItemL10NManagerTest/data.xml") public void normalizeItemAlreadyNormalized() { - fail(); + final Optional item = itemRepo.findById(-10100L); + assertThat(item.isPresent(), is(true)); + + l10nManager.normalizedLanguages(item.get()); + } /** @@ -367,15 +419,17 @@ public class ContentItemL10NManagerTest { * for the item. */ @Test(expected = IllegalArgumentException.class) - @InSequence(20) + @InSequence(140) @UsingDataSet("datasets/org/librecms/contentsection/" - + "ContentItemL10NManager/data.xml") + + "ContentItemL10NManagerTest/data.xml") @ShouldMatchDataSet( value = "datasets/org/librecms/contentsection/" - + "ContentItemL10NManager/data.xml") + + "ContentItemL10NManagerTest/data.xml") @ShouldThrowException(IllegalArgumentException.class) public void normalizeItemNull() { - fail(); + final ContentItem item = null; + + l10nManager.normalizedLanguages(item); } } diff --git a/ccm-cms/src/test/java/org/librecms/contentsection/DatasetsTest.java b/ccm-cms/src/test/java/org/librecms/contentsection/DatasetsTest.java index 2b5146ac8..2a683fd12 100644 --- a/ccm-cms/src/test/java/org/librecms/contentsection/DatasetsTest.java +++ b/ccm-cms/src/test/java/org/librecms/contentsection/DatasetsTest.java @@ -54,6 +54,11 @@ public class DatasetsTest extends DatasetsVerifier { "/datasets/org/librecms/contentsection/ContentItemRepositoryTest/data.xml", "/datasets/org/librecms/contentsection/ContentItemRepositoryTest/after-save.xml", + "/datasets/org/librecms/contentsection/ContentItemL10NManagerTest/data.xml", + "/datasets/org/librecms/contentsection/ContentItemL10NManagerTest/after-add-language.xml", + "/datasets/org/librecms/contentsection/ContentItemL10NManagerTest/after-normalize.xml", + "/datasets/org/librecms/contentsection/ContentItemL10NManagerTest/after-remove-language.xml", + "/datasets/org/librecms/contentsection/ContentItemManagerTest/data.xml", "/datasets/org/librecms/contentsection/ContentItemManagerTest/after-create-contentitem.xml", "/datasets/org/librecms/contentsection/ContentItemManagerTest/after-create-contentitem-with-workflow.xml", diff --git a/ccm-cms/src/test/resources/datasets/org/librecms/contentsection/ContentItemL10NManagerTest/after-add-language.xml b/ccm-cms/src/test/resources/datasets/org/librecms/contentsection/ContentItemL10NManagerTest/after-add-language.xml new file mode 100644 index 000000000..3d29a08c2 --- /dev/null +++ b/ccm-cms/src/test/resources/datasets/org/librecms/contentsection/ContentItemL10NManagerTest/after-add-language.xml @@ -0,0 +1,572 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ccm-cms/src/test/resources/datasets/org/librecms/contentsection/ContentItemL10NManagerTest/after-normalize.xml b/ccm-cms/src/test/resources/datasets/org/librecms/contentsection/ContentItemL10NManagerTest/after-normalize.xml new file mode 100644 index 000000000..152c62580 --- /dev/null +++ b/ccm-cms/src/test/resources/datasets/org/librecms/contentsection/ContentItemL10NManagerTest/after-normalize.xml @@ -0,0 +1,565 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ccm-cms/src/test/resources/datasets/org/librecms/contentsection/ContentItemL10NManagerTest/after-remove-language.xml b/ccm-cms/src/test/resources/datasets/org/librecms/contentsection/ContentItemL10NManagerTest/after-remove-language.xml new file mode 100644 index 000000000..9a05b3f4f --- /dev/null +++ b/ccm-cms/src/test/resources/datasets/org/librecms/contentsection/ContentItemL10NManagerTest/after-remove-language.xml @@ -0,0 +1,554 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ccm-cms/src/test/resources/datasets/org/librecms/contentsection/ContentItemL10NManagerTest/data.xml b/ccm-cms/src/test/resources/datasets/org/librecms/contentsection/ContentItemL10NManagerTest/data.xml new file mode 100644 index 000000000..53e9ce7da --- /dev/null +++ b/ccm-cms/src/test/resources/datasets/org/librecms/contentsection/ContentItemL10NManagerTest/data.xml @@ -0,0 +1,533 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ccm-cms/src/test/resources/scripts/h2-cleanup.sql b/ccm-cms/src/test/resources/scripts/h2-cleanup.sql index 10ead7235..c362037da 100644 --- a/ccm-cms/src/test/resources/scripts/h2-cleanup.sql +++ b/ccm-cms/src/test/resources/scripts/h2-cleanup.sql @@ -10,6 +10,10 @@ DELETE FROM ccm_cms.articles; DELETE FROM ccm_cms.articles_aud; +DELETE FROM ccm_cms.content_item_descriptions; + +DELETE FROM ccm_cms.content_item_descriptions_aud; + DELETE FROM ccm_cms.content_item_names; DELETE FROM ccm_cms.content_item_names_aud;