CCM NG/ccm-cms: AssetForm including localisation for assets

git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@4663 8810af33-2d31-482b-a856-94f89814c4df

Former-commit-id: ee7280dc6b
pull/2/head
jensp 2017-04-07 18:41:40 +00:00
parent 65c0776a67
commit 2ece488abb
7 changed files with 751 additions and 84 deletions

View File

@ -18,18 +18,27 @@
*/ */
package com.arsdigita.cms.ui.assets; package com.arsdigita.cms.ui.assets;
import com.arsdigita.bebop.BoxPanel;
import com.arsdigita.bebop.ColumnPanel; import com.arsdigita.bebop.ColumnPanel;
import com.arsdigita.bebop.Form; import com.arsdigita.bebop.Form;
import com.arsdigita.bebop.FormProcessException; import com.arsdigita.bebop.FormProcessException;
import com.arsdigita.bebop.Label; import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.Page;
import com.arsdigita.bebop.PageState; import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.SaveCancelSection; import com.arsdigita.bebop.SaveCancelSection;
import com.arsdigita.bebop.SingleSelectionModel; import com.arsdigita.bebop.SingleSelectionModel;
import com.arsdigita.bebop.Text;
import com.arsdigita.bebop.event.FormInitListener; import com.arsdigita.bebop.event.FormInitListener;
import com.arsdigita.bebop.event.FormProcessListener; import com.arsdigita.bebop.event.FormProcessListener;
import com.arsdigita.bebop.event.FormSectionEvent; import com.arsdigita.bebop.event.FormSectionEvent;
import com.arsdigita.bebop.event.FormSubmissionListener; import com.arsdigita.bebop.event.FormSubmissionListener;
import com.arsdigita.bebop.event.PrintEvent;
import com.arsdigita.bebop.event.PrintListener;
import com.arsdigita.bebop.form.Option;
import com.arsdigita.bebop.form.SingleSelect;
import com.arsdigita.bebop.form.Submit;
import com.arsdigita.bebop.form.TextField; import com.arsdigita.bebop.form.TextField;
import com.arsdigita.bebop.parameters.StringParameter;
import com.arsdigita.globalization.GlobalizedMessage; import com.arsdigita.globalization.GlobalizedMessage;
import com.arsdigita.kernel.KernelConfig; import com.arsdigita.kernel.KernelConfig;
@ -39,9 +48,16 @@ import org.librecms.contentsection.Asset;
import org.librecms.contentsection.AssetRepository; import org.librecms.contentsection.AssetRepository;
import java.util.Optional; import java.util.Optional;
import org.libreccm.categorization.CategoryManager; import org.libreccm.categorization.CategoryManager;
import org.libreccm.core.UnexpectedErrorException;
import org.librecms.assets.AssetL10NManager;
import org.librecms.contentsection.Folder; import org.librecms.contentsection.Folder;
import org.librecms.contentsection.FolderManager;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.TooManyListenersException;
/** /**
* *
@ -55,6 +71,15 @@ public abstract class AssetForm extends Form implements FormInitListener,
private final AssetPane assetPane; private final AssetPane assetPane;
private final SingleSelectionModel<Long> selectionModel; private final SingleSelectionModel<Long> selectionModel;
// private final StringParameter selectedLocaleParameter;
private BoxPanel showLocalePanel;
private SingleSelect showLocaleSelect;
private Submit showLocaleSubmit;
private BoxPanel addLocalePanel;
private SingleSelect addLocaleSelect;
private Submit addLocaleSubmit;
private TextField title; private TextField title;
private SaveCancelSection saveCancelSection; private SaveCancelSection saveCancelSection;
@ -64,11 +89,146 @@ public abstract class AssetForm extends Form implements FormInitListener,
this.assetPane = assetPane; this.assetPane = assetPane;
selectionModel = assetPane.getSelectedAssetModel(); selectionModel = assetPane.getSelectedAssetModel();
// selectedLocaleParameter = new StringParameter("selected-locale");
initComponents(); initComponents();
} }
private void initComponents() { private void initComponents() {
showLocalePanel = new BoxPanel(BoxPanel.HORIZONTAL);
final Label showLocaleLabel = new Label(new PrintListener() {
@Override
public void prepare(final PrintEvent event) {
final PageState state = event.getPageState();
final Optional<Asset> selectedAsset = getSelectedAsset(state);
final Label target = (Label) event.getTarget();
if (selectedAsset.isPresent()) {
target.setLabel(new GlobalizedMessage(
"cms.ui.assest.show_locale",
CmsConstants.CMS_BUNDLE));
} else {
target.setLabel(new GlobalizedMessage(
"cms.ui.assest.initial_locale",
CmsConstants.CMS_BUNDLE));
}
}
}
);
showLocaleSelect = new SingleSelect("selected-locale");
try {
showLocaleSelect.addPrintListener(new PrintListener() {
@Override
public void prepare(final PrintEvent event) {
final PageState state = event.getPageState();
final Optional<Asset> selectedAsset
= getSelectedAsset(state);
if (selectedAsset.isPresent()) {
final SingleSelect target = (SingleSelect) event
.getTarget();
target.clearOptions();;
final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
final AssetL10NManager l10nManager = cdiUtil
.findBean(AssetL10NManager.class);
final List<Locale> availableLocales = new ArrayList<>(
l10nManager.availableLocales(selectedAsset.get()));
availableLocales.sort((locale1, locale2) -> {
return locale1
.toString()
.compareTo(locale2.toString());
});
availableLocales.forEach(locale -> target.addOption(
new Option(locale.toString(),
new Text(locale.toString()))));
} else {
final SingleSelect target = (SingleSelect) event
.getTarget();
target.clearOptions();
final List<String> langs = new ArrayList<>(
KernelConfig.getConfig().getSupportedLanguages());
langs.sort((lang1, lang2) -> lang1.compareTo(lang2));
langs.forEach(lang -> {
target.addOption(new Option(lang, new Text(lang)));
});
}
}
});
} catch (TooManyListenersException ex) {
throw new UnexpectedErrorException(ex);
}
showLocaleSubmit = new Submit(new GlobalizedMessage(
"cms.ui.asset.show_locale",
CmsConstants.CMS_BUNDLE));
showLocalePanel.add(showLocaleLabel);
showLocalePanel.add(showLocaleSelect);
showLocalePanel.add(showLocaleSubmit);
add(showLocalePanel);
addLocalePanel = new BoxPanel(BoxPanel.HORIZONTAL) {
@Override
public boolean isVisible(final PageState state) {
return getSelectedAsset(state).isPresent();
}
};
final Label addLocaleLabel = new Label(
new GlobalizedMessage("cms.ui.assest.add_locale",
CmsConstants.CMS_BUNDLE));
addLocaleSelect = new SingleSelect("add-locale-select");
try {
addLocaleSelect.addPrintListener(new PrintListener() {
@Override
public void prepare(final PrintEvent event) {
final PageState state = event.getPageState();
final Optional<Asset> selectedAsset
= getSelectedAsset(state);
if (selectedAsset.isPresent()) {
final SingleSelect target = (SingleSelect) event
.getTarget();
target.clearOptions();
final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
final AssetL10NManager l10nManager = cdiUtil
.findBean(AssetL10NManager.class);
final List<Locale> creatableLocales = new ArrayList<>(
l10nManager.creatableLocales(selectedAsset.get()));
creatableLocales.sort((locale1, locale2) -> {
return locale1
.toString()
.compareTo(locale2.toString());
});
creatableLocales.forEach(locale -> target.addOption(
new Option(locale.toString(),
new Text(locale.toString()))));
}
}
});
} catch (TooManyListenersException ex) {
throw new UnexpectedErrorException(ex);
}
addLocaleSubmit = new Submit(new GlobalizedMessage(
"cms.ui.asset.add_locale",
CmsConstants.CMS_BUNDLE));
addLocalePanel.add(addLocaleLabel);
addLocalePanel.add(addLocaleSelect);
addLocalePanel.add(addLocaleSubmit);
add(addLocalePanel);
add(new Label(new GlobalizedMessage("cms.ui.asset.title", add(new Label(new GlobalizedMessage("cms.ui.asset.title",
CmsConstants.CMS_BUNDLE))); CmsConstants.CMS_BUNDLE)));
title = new TextField(ASSET_TITLE); title = new TextField(ASSET_TITLE);
@ -88,6 +248,15 @@ public abstract class AssetForm extends Form implements FormInitListener,
//Nothing here //Nothing here
} }
// @Override
// public void register(final Page page) {
// super.register(page);
//
// page.addComponentStateParam(this, selectedLocaleParameter);
//
// page.setVisibleDefault(showLocalePanel, true);
// page.setVisibleDefault(addLocalePanel, true);
// }
protected String getTitle(final PageState state) { protected String getTitle(final PageState state) {
return (String) title.getValue(state); return (String) title.getValue(state);
} }
@ -118,23 +287,78 @@ public abstract class AssetForm extends Form implements FormInitListener,
final Optional<Asset> selectedAsset = getSelectedAsset(state); final Optional<Asset> selectedAsset = getSelectedAsset(state);
if (selectedAsset.isPresent()) { if (selectedAsset.isPresent()) {
// showLocalePanel.setVisible(state, true);
// addLocalePanel.setVisible(state, true);
showLocaleSelect.setValue(state,
KernelConfig
.getConfig()
.getDefaultLocale()
.toString());
title.setValue(state, title.setValue(state,
selectedAsset selectedAsset
.get() .get()
.getTitle() .getTitle()
.getValue(KernelConfig .getValue(getSelectedLocale(state)));
} else {
showLocaleSelect.setValue(state,
KernelConfig
.getConfig() .getConfig()
.getDefaultLocale())); .getDefaultLocale()
.toString());
// showLocalePanel.setVisible(state, false);
// addLocalePanel.setVisible(state, false);
} }
initForm(state, selectedAsset);
} }
protected Locale getSelectedLocale(final PageState state) {
final String selected = (String) showLocaleSelect.getValue(state);
if (selected == null) {
return KernelConfig.getConfig().getDefaultLocale();
} else {
return new Locale(selected);
}
}
protected abstract void initForm(final PageState state,
final Optional<Asset> selectedAsset);
@Override @Override
public void process(final FormSectionEvent event) public void process(final FormSectionEvent event)
throws FormProcessException { throws FormProcessException {
final PageState state = event.getPageState(); final PageState state = event.getPageState();
final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
if (showLocaleSubmit.isSelected(state)) {
final Optional<Asset> selectedAsset = getSelectedAsset(state);
if (selectedAsset.isPresent()) {
title.setValue(state,
selectedAsset
.get()
.getTitle()
.getValue(getSelectedLocale(state)));
showLocale(state);
}
return;
}
if (addLocaleSubmit.isSelected(state)) {
final AssetL10NManager l10nManager = cdiUtil
.findBean(AssetL10NManager.class);
final Locale add = new Locale((String) addLocaleSelect
.getValue(state));
final Optional<Asset> selectedAsset = getSelectedAsset(state);
l10nManager.addLanguage(selectedAsset.get(), add);
}
if (saveCancelSection.getSaveButton().isSelected(state)) { if (saveCancelSection.getSaveButton().isSelected(state)) {
final Optional<Asset> selectedAsset = getSelectedAsset(state); final Optional<Asset> selectedAsset = getSelectedAsset(state);
final Asset asset; final Asset asset;
@ -145,11 +369,9 @@ public abstract class AssetForm extends Form implements FormInitListener,
asset = createAsset(state); asset = createAsset(state);
} }
asset.getTitle().addValue( asset.getTitle().addValue(getSelectedLocale(state),
KernelConfig.getConfig().getDefaultLocale(),
(String) title.getValue(state)); (String) title.getValue(state));
final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
final AssetRepository assetRepo = cdiUtil final AssetRepository assetRepo = cdiUtil
.findBean(AssetRepository.class); .findBean(AssetRepository.class);
assetRepo.save(asset); assetRepo.save(asset);
@ -175,6 +397,8 @@ public abstract class AssetForm extends Form implements FormInitListener,
} }
} }
protected abstract void showLocale(final PageState state);
protected abstract Asset createAsset(final PageState state) protected abstract Asset createAsset(final PageState state)
throws FormProcessException; throws FormProcessException;

View File

@ -501,7 +501,7 @@ public class AssetPane extends LayoutPanel implements Resettable {
actionsSegment.add(newAssetForm); actionsSegment.add(newAssetForm);
final MetaForm editAssetForm = new MetaForm(MOVE) { final MetaForm editAssetForm = new MetaForm("editAsset") {
@Override @Override
public Form buildForm(final PageState state) { public Form buildForm(final PageState state) {

View File

@ -38,6 +38,7 @@ import org.librecms.contentsection.Asset;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.util.Objects; import java.util.Objects;
import java.util.Optional;
/** /**
* *
@ -88,6 +89,51 @@ public class BookmarkForm extends AssetForm {
} }
@Override
protected void initForm(final PageState state,
final Optional<Asset> selectedAsset) {
if (selectedAsset.isPresent()) {
if (!(selectedAsset.get() instanceof Bookmark)) {
throw new IllegalArgumentException(String.format(
"The provided asset must be an instanceof of class '%s' or "
+ "an subclass but is an instanceof of class '%s'.",
Bookmark.class.getName(),
selectedAsset.get().getClass().getName()));
}
final Bookmark bookmark = (Bookmark) selectedAsset.get();
description.setValue(state,
bookmark
.getDescription()
.getValue(getSelectedLocale(state)));
url.setValue(state, bookmark.getUrl());
}
}
@Override
protected void showLocale(final PageState state) {
final Optional<Asset> selectedAsset = getSelectedAsset(state);
if (selectedAsset.isPresent()) {
if (!(getSelectedAsset(state).get() instanceof Bookmark)) {
throw new IllegalArgumentException(
"Selected asset is not a bookmark");
}
final Bookmark bookmark = (Bookmark) selectedAsset.get();
description.setValue(state,
bookmark
.getDescription()
.getValue(getSelectedLocale(state)));
}
}
@Override @Override
protected Asset createAsset(final PageState state) protected Asset createAsset(final PageState state)
throws FormProcessException { throws FormProcessException {
@ -98,7 +144,7 @@ public class BookmarkForm extends AssetForm {
bookmark bookmark
.getDescription() .getDescription()
.addValue(KernelConfig.getConfig().getDefaultLocale(), .addValue(getSelectedLocale(state),
(String) description.getValue(state)); (String) description.getValue(state));
bookmark.setUrl((String) url.getValue(state)); bookmark.setUrl((String) url.getValue(state));
@ -125,7 +171,7 @@ public class BookmarkForm extends AssetForm {
bookmark bookmark
.getDescription() .getDescription()
.addValue(KernelConfig.getConfig().getDefaultLocale(), .addValue(getSelectedLocale(state),
(String) description.getValue(state)); (String) description.getValue(state));
bookmark.setUrl((String) url.getValue(state)); bookmark.setUrl((String) url.getValue(state));

View File

@ -29,10 +29,13 @@ import com.arsdigita.globalization.GlobalizedMessage;
import com.arsdigita.kernel.KernelConfig; import com.arsdigita.kernel.KernelConfig;
import org.librecms.CmsConstants; import org.librecms.CmsConstants;
import org.librecms.assets.Bookmark;
import org.librecms.assets.LegalMetadata; import org.librecms.assets.LegalMetadata;
import org.librecms.contentsection.Asset; import org.librecms.contentsection.Asset;
import java.util.Locale;
import java.util.Objects; import java.util.Objects;
import java.util.Optional;
/** /**
* *
@ -77,6 +80,52 @@ public class LegalMetadataForm extends AssetForm {
add(creator); add(creator);
} }
@Override
protected void initForm(final PageState state,
final Optional<Asset> selectedAsset) {
if (selectedAsset.isPresent()) {
if (!(selectedAsset.get() instanceof LegalMetadata)) {
throw new IllegalArgumentException(String.format(
"The provided asset must be an instanceof of class '%s' or "
+ "an subclass but is an instanceof of class '%s'.",
LegalMetadata.class.getName(),
selectedAsset.get().getClass().getName()));
}
final LegalMetadata legalMetadata = (LegalMetadata) selectedAsset
.get();
rightsHolder.setValue(state, legalMetadata.getRightsHolder());
rights.setValue(state,
legalMetadata
.getRights()
.getValue(getSelectedLocale(state)));
publisher.setValue(state, legalMetadata.getPublisher());
creator.setValue(state, legalMetadata.getCreator());
}
}
@Override
protected void showLocale(final PageState state) {
final Optional<Asset> selectedAsset = getSelectedAsset(state);
if (selectedAsset.isPresent()) {
if (!(getSelectedAsset(state).get() instanceof LegalMetadata)) {
throw new IllegalArgumentException(
"Selected asset is not a legal metadata");
}
final LegalMetadata legalMetadata = (LegalMetadata) selectedAsset.get();
rights.setValue(state,
legalMetadata
.getRights()
.getValue(getSelectedLocale(state)));
}
}
@Override @Override
protected Asset createAsset(final PageState state) protected Asset createAsset(final PageState state)
throws FormProcessException { throws FormProcessException {
@ -86,8 +135,7 @@ public class LegalMetadataForm extends AssetForm {
final LegalMetadata legalMetadata = new LegalMetadata(); final LegalMetadata legalMetadata = new LegalMetadata();
legalMetadata.setRightsHolder((String) rightsHolder.getValue(state)); legalMetadata.setRightsHolder((String) rightsHolder.getValue(state));
legalMetadata.getRights().addValue( legalMetadata.getRights().addValue(getSelectedLocale(state),
KernelConfig.getConfig().getDefaultLocale(),
(String) rights.getValue(state)); (String) rights.getValue(state));
legalMetadata.setPublisher((String) publisher.getValue(state)); legalMetadata.setPublisher((String) publisher.getValue(state));
@ -107,15 +155,15 @@ public class LegalMetadataForm extends AssetForm {
throw new IllegalArgumentException(String.format( throw new IllegalArgumentException(String.format(
"Provided asset is not an instance of '%s' (or a sub class) " "Provided asset is not an instance of '%s' (or a sub class) "
+ "but is an instance of class '%s'.", + "but is an instance of class '%s'.",
LegalMetadata.class.getName(), LegalMetadata.class
.getName(),
asset.getClass().getName())); asset.getClass().getName()));
} }
final LegalMetadata legalMetadata = (LegalMetadata) asset; final LegalMetadata legalMetadata = (LegalMetadata) asset;
legalMetadata.setRightsHolder((String) rightsHolder.getValue(state)); legalMetadata.setRightsHolder((String) rightsHolder.getValue(state));
legalMetadata.getRights().addValue( legalMetadata.getRights().addValue(getSelectedLocale(state),
KernelConfig.getConfig().getDefaultLocale(),
(String) rights.getValue(state)); (String) rights.getValue(state));
legalMetadata.setPublisher((String) publisher.getValue(state)); legalMetadata.setPublisher((String) publisher.getValue(state));

View File

@ -0,0 +1,344 @@
/*
* Copyright (C) 2017 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.librecms.assets;
import com.arsdigita.kernel.KernelConfig;
import org.libreccm.configuration.ConfigurationManager;
import org.libreccm.core.UnexpectedErrorException;
import org.libreccm.l10n.LocalizedString;
import org.libreccm.security.AuthorizationRequired;
import org.libreccm.security.PermissionChecker;
import org.libreccm.security.RequiresPrivilege;
import org.librecms.contentsection.Asset;
import org.librecms.contentsection.AssetRepository;
import org.librecms.contentsection.privileges.AssetPrivileges;
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.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
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;
/**
* Manages the language versions of an asset.
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
public class AssetL10NManager {
@Inject
private ConfigurationManager confManager;
@Inject
private AssetRepository assetRepo;
@Inject
private PermissionChecker permissionChecker;
private Locale defaultLocale;
private List<Locale> 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<PropertyDescriptor> findLocalizedStringProperties(
final Asset asset) {
try {
final PropertyDescriptor[] properties = Introspector
.getBeanInfo(asset.getClass())
.getPropertyDescriptors();
return Arrays.stream(properties)
.filter(property -> {
return property
.getPropertyType()
.isAssignableFrom(LocalizedString.class);
})
.collect(Collectors.toList());
} catch (IntrospectionException ex) {
throw new UnexpectedErrorException(ex);
}
}
private LocalizedString readLocalizedString(final Asset asset,
final Method readMethod) {
try {
return (LocalizedString) readMethod.invoke(asset);
} catch (IllegalAccessException
| IllegalArgumentException
| InvocationTargetException ex) {
throw new UnexpectedErrorException(ex);
}
}
private Set<Locale> collectLanguages(final Asset asset) {
final Set<Locale> locales = new HashSet<>();
findLocalizedStringProperties(asset)
.stream()
.map(property -> property.getReadMethod())
.map(readMethod -> readLocalizedString(asset, readMethod))
.forEach(str -> locales.addAll(str.getAvailableLocales()));
return locales;
}
/**
* Helper method for reading methods in this class for verifying that the
* current user is permitted to read the item.
*
* @param asset The asset for which the read permission is verified
*/
private void checkReadPermission(final Asset asset) {
final String requiredPrivilege = AssetPrivileges.VIEW;
permissionChecker.checkPermission(requiredPrivilege, asset);
}
/**
* Retrieves all languages in which an asset is available.
*
* @param asset The asset.
*
* @return An (unmodifiable) {@link Set} containing all languages in which
* the asset is available.
*/
@Transactional(Transactional.TxType.REQUIRED)
public Set<Locale> availableLocales(final Asset asset) {
checkReadPermission(asset);
return Collections.unmodifiableSet(collectLanguages(asset));
}
/**
* Checks if an asset has data for particular language.
*
* @param asset The asset to check.
* @param locale The locale to check for.
*
* @return {@link true} if the item has data for the language, {@code false}
* if not.
*/
@AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED)
public boolean hasLanguage(
@RequiresPrivilege(AssetPrivileges.VIEW)
final Asset asset,
final Locale locale) {
Objects.requireNonNull(asset,
"Can't check if asset null has a specific locale.");
Objects.requireNonNull(locale, "Can't check for locale null.");
checkReadPermission(asset);
return collectLanguages(asset).contains(locale);
}
/**
* Returns a {@link Set} containing the locales for which the asset does not
* yet have a variant.
*
* @param asset The asset.
*
* @return A {@link Set} with the locales for which the item does not have a
* variant.
*/
@Transactional(Transactional.TxType.REQUIRED)
public Set<Locale> creatableLocales(final Asset asset) {
checkReadPermission(asset);
return supportedLocales.stream()
.filter(locale -> !hasLanguage(asset, locale))
.collect(Collectors.toSet());
}
/**
* Adds a language to an asset. The method will retrieve all fields of the
* type {@link LocalizedString} from the asset and add a new entry for the
* provided locale by coping the value for the default language configured
* in {@link KernelConfig}. If a field does not have an entry for the
* default language the first value found is used.
*
* @param asset The asset to which the language variant is added.
* @param locale The locale of the language to add.
*/
@AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED)
public void addLanguage(
@RequiresPrivilege(AssetPrivileges.EDIT)
final Asset asset,
final Locale locale) {
Objects.requireNonNull(asset, "Can't add language to asset null.");
Objects.requireNonNull(asset, "Cant't add language null to an asset.");
findLocalizedStringProperties(asset)
.forEach(property -> addLanguage(asset, locale, property));
assetRepo.save(asset);
}
private void addLanguage(final Asset asset,
final Locale locale,
final PropertyDescriptor property) {
final Method readMethod = property.getReadMethod();
final LocalizedString localizedStr = readLocalizedString(asset,
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> locale = supportedLocales
.stream()
.filter(current -> localizedStr.hasValue(current))
.findAny();
if (locale.isPresent()) {
return localizedStr.getValue(locale.get());
} else {
return "Lorem ipsum";
}
}
/**
* Removes a language variant from an asset. This method will retrieve all
* fields of the type {@link LocalizedString} from the asset and remove the
* entry for the provided locale if the field has an entry for that locale.
*
* @param asset
* @param locale
*/
@AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED)
public void removeLanguage(
@RequiresPrivilege(AssetPrivileges.EDIT)
final Asset asset,
final Locale locale) {
Objects.requireNonNull(asset, "Can't remove language to asset null.");
Objects
.requireNonNull(asset, "Cant't remove language null to an asset.");
findLocalizedStringProperties(asset)
.forEach(property -> removeLanguage(asset, locale, property));
assetRepo.save(asset);
}
private void removeLanguage(final Asset asset,
final Locale locale,
final PropertyDescriptor property) {
final Method readMethod = property.getReadMethod();
final LocalizedString localizedStr = readLocalizedString(asset,
readMethod);
if (localizedStr.hasValue(locale)) {
localizedStr.removeValue(locale);
}
}
/**
* This method normalises the values of the fields of type
* {@link LocalizedString} of an asset. The method will first retrieve all
* fields of the type {@link LocalizedString} from the asset and than build
* a set with all locales provided by any of the fields. After that the
* method will iterate over all {@link LocalizedString} fields and check if
* the {@link LocalizedString} has an entry for every language in the set.
* If not an entry for the language is added.
*
* @param asset The asset to normalise.
*/
@AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED)
public void normalizeLanguages(
@RequiresPrivilege(AssetPrivileges.EDIT)
final Asset asset) {
Objects.requireNonNull(asset, "Can't normalise asset null");
final Set<Locale> languages = collectLanguages(asset);
findLocalizedStringProperties(asset)
.stream()
.map(property -> property.getReadMethod())
.map(readMethod -> readLocalizedString(asset, readMethod))
.forEach(str -> normalize(str, languages));
}
private void normalize(final LocalizedString localizedString,
final Set<Locale> languages) {
final List<Locale> missingLangs = languages.stream()
.filter(lang -> !localizedString.hasValue(lang))
.collect(Collectors.toList());
if (!missingLangs.isEmpty()) {
missingLangs.stream()
.forEach(lang -> addLanguage(localizedString, lang));
}
}
}

View File

@ -83,11 +83,16 @@ public class ContentItemL10NManager {
final ContentItem item) { final ContentItem item) {
try { try {
return Arrays.stream( final PropertyDescriptor[] properties = Introspector
Introspector.getBeanInfo(item.getClass()) .getBeanInfo(item.getClass())
.getPropertyDescriptors()) .getPropertyDescriptors();
.filter(property -> property.getPropertyType().isAssignableFrom(
LocalizedString.class)) return Arrays.stream(properties)
.filter(property -> {
return property
.getPropertyType()
.isAssignableFrom(LocalizedString.class);
})
.collect(Collectors.toList()); .collect(Collectors.toList());
} catch (IntrospectionException ex) { } catch (IntrospectionException ex) {
throw new UnexpectedErrorException(ex); throw new UnexpectedErrorException(ex);
@ -106,6 +111,7 @@ public class ContentItemL10NManager {
} }
private Set<Locale> collectLanguages(final ContentItem item) { private Set<Locale> collectLanguages(final ContentItem item) {
final Set<Locale> locales = new HashSet<>(); final Set<Locale> locales = new HashSet<>();
findLocalizedStringProperties(item) findLocalizedStringProperties(item)
@ -146,7 +152,7 @@ public class ContentItemL10NManager {
} }
/** /**
* Retrieves all languages in which content item is available. * Retrieves all languages in which a content item is available.
* *
* @param item The item. * @param item The item.
* *
@ -154,8 +160,7 @@ public class ContentItemL10NManager {
* the item is available. * the item is available.
*/ */
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public Set<Locale> availableLanguages( public Set<Locale> availableLanguages(final ContentItem item) {
final ContentItem item) {
checkReadPermission(item); checkReadPermission(item);
return Collections.unmodifiableSet(collectLanguages(item)); return Collections.unmodifiableSet(collectLanguages(item));
@ -205,7 +210,7 @@ public class ContentItemL10NManager {
checkReadPermission(item); checkReadPermission(item);
return supportedLocales.stream() return supportedLocales.stream()
.filter(locale -> hasLanguage(item, locale)) .filter(locale -> !hasLanguage(item, locale))
.collect(Collectors.toSet()); .collect(Collectors.toSet());
} }
@ -329,7 +334,7 @@ public class ContentItemL10NManager {
} }
findLocalizedStringProperties(item) findLocalizedStringProperties(item)
.forEach(property -> ContentItemL10NManager.this.removeLanguage(item, locale, property)); .forEach(property -> removeLanguage(item, locale, property));
itemRepo.save(item); itemRepo.save(item);
} }
@ -357,13 +362,13 @@ public class ContentItemL10NManager {
* not an entry for the language is added. * not an entry for the language is added.
* *
* @param item The item to normalise. The item <strong>must be</strong> the * @param item The item to normalise. The item <strong>must be</strong> the
* <strong>draft version</strong> of the item! If a <em>live * <strong>draft version</strong> of the item! If a <em>live version</em> or
* version</em> or a <em>pending version</em> is provided the * a <em>pending version</em> is provided the method will throw an
* method will throw an {@link IllegalArgumentException}! * {@link IllegalArgumentException}!
*/ */
@AuthorizationRequired @AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public void normalizedLanguages( public void normalizeLanguages(
@RequiresPrivilege(ItemPrivileges.EDIT) @RequiresPrivilege(ItemPrivileges.EDIT)
final ContentItem item) { final ContentItem item) {

View File

@ -392,7 +392,7 @@ public class ContentItemL10NManagerTest {
/** /**
* Tries to normalise the languages of a content item by using * Tries to normalise the languages of a content item by using
* {@link ContentItemL10NManager#normalizedLanguages(org.librecms.contentsection.ContentItem)} * {@link ContentItemL10NManager#normalizeLanguages(org.librecms.contentsection.ContentItem)}
*/ */
@Test @Test
@InSequence(120) @InSequence(120)
@ -406,12 +406,12 @@ public class ContentItemL10NManagerTest {
final Optional<ContentItem> item = itemRepo.findById(-10200L); final Optional<ContentItem> item = itemRepo.findById(-10200L);
assertThat(item.isPresent(), is(true)); assertThat(item.isPresent(), is(true));
l10nManager.normalizedLanguages(item.get()); l10nManager.normalizeLanguages(item.get());
} }
/** /**
* Verifies that calling * Verifies that calling
* {@link ContentItemL10NManager#normalizedLanguages(org.librecms.contentsection.ContentItem)} * {@link ContentItemL10NManager#normalizeLanguages(org.librecms.contentsection.ContentItem)}
* for already normalised item has not effect. * for already normalised item has not effect.
*/ */
@Test @Test
@ -425,13 +425,13 @@ public class ContentItemL10NManagerTest {
final Optional<ContentItem> item = itemRepo.findById(-10100L); final Optional<ContentItem> item = itemRepo.findById(-10100L);
assertThat(item.isPresent(), is(true)); assertThat(item.isPresent(), is(true));
l10nManager.normalizedLanguages(item.get()); l10nManager.normalizeLanguages(item.get());
} }
/** /**
* Verifies that * Verifies that
* {@link ContentItemL10NManager#normalizedLanguages(org.librecms.contentsection.ContentItem)} * {@link ContentItemL10NManager#normalizeLanguages(org.librecms.contentsection.ContentItem)}
* throws an {@link IllegalArgumentException} if called with {@code null} * throws an {@link IllegalArgumentException} if called with {@code null}
* for the item. * for the item.
*/ */
@ -446,7 +446,7 @@ public class ContentItemL10NManagerTest {
public void normalizeItemNull() { public void normalizeItemNull() {
final ContentItem item = null; final ContentItem item = null;
l10nManager.normalizedLanguages(item); l10nManager.normalizeLanguages(item);
} }
} }