CCM NG/ccm-cms: Progress for ItemLanguages and related classes (not finished yet)

git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@4454 8810af33-2d31-482b-a856-94f89814c4df
pull/2/head
jensp 2016-11-23 18:35:16 +00:00
parent 681ba87a68
commit 582bcab967
14 changed files with 509 additions and 232 deletions

View File

@ -53,9 +53,6 @@ public class ItemSelectionModel extends CcmObjectSelectionModel<ContentItem> {
private Long typeId;
private static final Logger LOGGER = Logger.getLogger(
ItemSelectionModel.class);
/**
* Construct a new <code>ItemSelectionModel</code>
*

View File

@ -23,6 +23,7 @@ import com.arsdigita.bebop.Form;
import com.arsdigita.bebop.FormProcessException;
import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.SingleSelectionModel;
import com.arsdigita.bebop.event.FormProcessListener;
import com.arsdigita.bebop.event.FormSectionEvent;
import com.arsdigita.bebop.event.PrintEvent;
@ -30,13 +31,17 @@ import com.arsdigita.bebop.event.PrintListener;
import com.arsdigita.bebop.form.Option;
import com.arsdigita.bebop.form.OptionGroup;
import com.arsdigita.bebop.form.Submit;
import org.librecms.contentsection.ContentItem;
import org.librecms.contentsection.ContentSection;
import org.librecms.contentsection.ContentType;
import com.arsdigita.cms.ItemSelectionModel;
import com.arsdigita.cms.ui.ContentItemPage;
import com.arsdigita.cms.ui.authoring.LanguageWidget;
import org.librecms.util.LanguageUtil;
import com.arsdigita.globalization.GlobalizedMessage;
import com.arsdigita.toolbox.ui.ActionGroup;
import com.arsdigita.toolbox.ui.LayoutPanel;
@ -46,12 +51,17 @@ import com.arsdigita.util.Pair;
import com.arsdigita.util.UncheckedWrapperException;
import com.arsdigita.web.RedirectSignal;
import com.arsdigita.web.URL;
import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.workflow.Workflow;
import org.libreccm.workflow.WorkflowTemplate;
import org.librecms.contentsection.ContentItemL10NManager;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.TooManyListenersException;
import java.util.stream.Collectors;
/**
* Displays the "Language instances" pane, with all the language instances in
@ -63,6 +73,7 @@ import java.util.TooManyListenersException;
public class ItemLanguages extends LayoutPanel {
private final ItemSelectionModel selectionModel;
private final SingleSelectionModel<String> selectedLanguage;
private final LanguageWidget languageWidget;
private final Submit changeSubmit;
private final Submit createSubmit;
@ -70,11 +81,15 @@ public class ItemLanguages extends LayoutPanel {
/**
* Constructs a new <code>ItemLanguages</code>.
*
* @param selectionModel the {@link ItemSelectionModel} which will supply
* the current item
* @param selectionModel the {@link ItemSelectionModel} which will supply
* the current item
* @param selectedLanguage {@link SingleSelectionModel} for the selected
* language.
*/
public ItemLanguages(final ItemSelectionModel selectionModel) {
public ItemLanguages(final ItemSelectionModel selectionModel,
final SingleSelectionModel<String> selectedLanguage) {
this.selectionModel = selectionModel;
this.selectedLanguage = selectedLanguage;
final Section section = new Section(gz("cms.ui.item.languages"));
setBody(section);
@ -82,15 +97,17 @@ public class ItemLanguages extends LayoutPanel {
final ActionGroup group = new ActionGroup();
section.setBody(group);
group.setSubject(new ItemLanguagesTable(selectionModel));
group.setSubject(new ItemLanguagesTable(selectionModel,
selectedLanguage));
final Form form = new Form("newLanguage", new BoxPanel(
BoxPanel.HORIZONTAL));
group.addAction(form);
form.setRedirecting(true);
languageWidget = new LanguageWidget(ContentItem.LANGUAGE) {
languageWidget = new LanguageWidget("language_widget") {
@Override
protected void setupOptions() {
// Don't do anything.
}
@ -99,8 +116,8 @@ public class ItemLanguages extends LayoutPanel {
try {
languageWidget.addPrintListener(new OptionPrinter());
} catch (TooManyListenersException tmle) {
new UncheckedWrapperException(tmle);
} catch (TooManyListenersException ex) {
throw new UncheckedWrapperException(ex);
}
form.add(languageWidget);
@ -116,16 +133,26 @@ public class ItemLanguages extends LayoutPanel {
*/
private class OptionPrinter implements PrintListener {
public final void prepare(final PrintEvent e) {
final PageState state = e.getPageState();
final OptionGroup optionGroup = (OptionGroup) e.getTarget();
final ContentPage item = (ContentPage) selectionModel.
getSelectedItem(state);
final Collection languages = LanguageUtil.convertToG11N(
LanguageUtil.getCreatableLanguages(item));
@Override
public final void prepare(final PrintEvent event) {
final PageState state = event.getPageState();
final OptionGroup optionGroup = (OptionGroup) event.getTarget();
final ContentItem item = selectionModel.getSelectedItem(state);
for (Iterator iter = languages.iterator(); iter.hasNext();) {
final Pair pair = (Pair) iter.next();
final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
final LanguageUtil languageUtil = cdiUtil.findBean(
LanguageUtil.class);
final ContentItemL10NManager l10NManager = cdiUtil.findBean(
ContentItemL10NManager.class);
final List<String> creatableLangs = l10NManager.creatableLocales(
item).stream()
.map(locale -> locale.toString())
.collect(Collectors.toList());
final List<Pair> languages = languageUtil.convertToG11N(
creatableLangs);
for(final Pair pair : languages) {
final String langCode = (String) pair.getKey();
final GlobalizedMessage langName
= (GlobalizedMessage) pair
@ -141,9 +168,10 @@ public class ItemLanguages extends LayoutPanel {
*/
private class ProcessListener implements FormProcessListener {
public final void process(final FormSectionEvent e)
@Override
public final void process(final FormSectionEvent event)
throws FormProcessException {
PageState state = e.getPageState();
PageState state = event.getPageState();
String lang = (String) languageWidget.getValue(state);
ContentPage item = (ContentPage) selectionModel.getSelectedItem(
state);

View File

@ -0,0 +1,89 @@
/*
* Copyright (C) 2016 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 com.arsdigita.cms.ui.item;
import com.arsdigita.bebop.table.RowData;
import org.libreccm.l10n.GlobalizationHelper;
import org.librecms.contentsection.ContentItem;
import org.librecms.contentsection.ContentItemL10NManager;
import org.librecms.contentsection.ContentItemRepository;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.transaction.Transactional;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
public class ItemLanguagesController {
@Inject
private ContentItemRepository itemRepo;
@Inject
private ContentItemL10NManager itemL10NManager;
@Inject
private GlobalizationHelper globalizationHelper;
public List<RowData<String>> retrieveLanguageVariants(final ContentItem item) {
return retrieveLanguageVariants(item.getObjectId());
}
@Transactional(Transactional.TxType.REQUIRED)
public List<RowData<String>> retrieveLanguageVariants(final long itemId) {
final Optional<ContentItem> item = itemRepo.findById(itemId);
if (!item.isPresent()) {
throw new IllegalArgumentException(String.format(
"No content item with id %d found.", itemId));
}
final List<Locale> availableLangs = new ArrayList<>(itemL10NManager
.availableLanguages(item.get()));
availableLangs.sort((locale1, locale2) -> {
return locale1.toString().compareTo(locale2.toString());
});
return availableLangs.stream()
.map(lang -> createRow(item.get(), lang))
.collect(Collectors.toList());
}
private RowData<String> createRow(final ContentItem item,
final Locale lang) {
final RowData<String> row = new RowData<>(2);
row.setRowKey(lang.toString());
row.setColData(0, lang.getDisplayName(globalizationHelper.getNegotiatedLocale()));
row.setColData(1, item.getTitle().getValue(lang));
return row;
}
}

View File

@ -18,237 +18,162 @@
*/
package com.arsdigita.cms.ui.item;
import com.arsdigita.bebop.event.TableActionAdapter;
import com.arsdigita.bebop.Component;
import com.arsdigita.bebop.ControlLink;
import com.arsdigita.bebop.FormProcessException;
import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.Link;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.SingleSelectionModel;
import com.arsdigita.bebop.Table;
import com.arsdigita.bebop.event.TableActionEvent;
import com.arsdigita.bebop.event.TableActionListener;
import com.arsdigita.bebop.table.TableCellRenderer;
import com.arsdigita.bebop.table.TableColumn;
import com.arsdigita.bebop.table.TableColumnModel;
import com.arsdigita.cms.ItemSelectionModel;
import com.arsdigita.cms.dispatcher.MultilingualItemResolver;
import com.arsdigita.cms.ui.ContentItemPage;
import com.arsdigita.globalization.GlobalizedMessage;
import org.libreccm.cdi.utils.CdiUtil;
import org.librecms.CmsConstants;
import org.librecms.contentsection.ContentItem;
import org.librecms.contentsection.ContentItemL10NManager;
import org.librecms.util.LanguageUtil;
import com.arsdigita.toolbox.ui.DataTable;
import com.arsdigita.util.LockableImpl;
import com.arsdigita.web.RedirectSignal;
import com.arsdigita.web.URL;
import java.math.BigDecimal;
import org.apache.log4j.Logger;
import java.util.Locale;
/**
* Displays a list of all language instances of an item.
*
*/
public class ItemLanguagesTable extends DataTable {
public class ItemLanguagesTable extends Table {
private static final Logger LOGGER = Logger.getLogger(ItemLanguagesTable.class);
private final ItemSelectionModel m_model;
private final TableColumn m_deleteColumn;
public static final int COL_LANGUAGE = 0;
public static final int COL_TITLE = 1;
public static final int COL_DELETE = 2;
private final ItemSelectionModel itemSelectionModel;
private final SingleSelectionModel<String> langSelectionModel;
// private final TableColumn deleteColumn;
/**
* Construct a new
* <code>ItemHistoryTable</code>
* Construct a new {@code ItemLanguagesTable} which shows all language
* variants of an item.
*
* @param model the ItemSelectionModel that supplies the current item
* @param itemSelectionModel the ItemSelectionModel that supplies the
* current item
* @param langSelectionModel the single selection model which stores the
* selected language.
*/
public ItemLanguagesTable(ItemSelectionModel model) {
super(new LanguagesBuilder(model));
m_model = model;
public ItemLanguagesTable(final ItemSelectionModel itemSelectionModel,
final SingleSelectionModel<String> langSelectionModel) {
super();
setIdAttr("item_languages_table");
addColumn("cms.ui.language.header",
ContentPage.LANGUAGE,
false,
new LanguageCellRenderer(m_model));
addColumn("cms.ui.title",
ContentPage.TITLE);
m_deleteColumn = addColumn("cms.ui.action", new ActionCellRenderer(
m_model));
setResourceBundle(GlobalizationUtil.getBundleName());
addTableActionListener(new InstanceDeleter(m_model));
}
this.itemSelectionModel = itemSelectionModel;
this.langSelectionModel = langSelectionModel;
setModelBuilder(new ItemLanguagesTableModelBuilder(itemSelectionModel));
/**
* Builds the query for all the language instances in the current Bundle
*/
private static class LanguagesBuilder extends LockableImpl
implements DataQueryBuilder {
final TableColumnModel columnModel = getColumnModel();
columnModel.add(new TableColumn(COL_LANGUAGE,
new Label(new GlobalizedMessage(
"cms.ui.language.header",
CmsConstants.CMS_BUNDLE))));
columnModel.add(new TableColumn(COL_TITLE,
new Label(new GlobalizedMessage(
"cms.ui.language.title",
CmsConstants.CMS_BUNDLE))));
columnModel.add(new TableColumn(COL_DELETE,
new Label(new GlobalizedMessage(
"cms.ui.action",
CmsConstants.CMS_BUNDLE))));
ItemSelectionModel m_model;
columnModel.get(COL_LANGUAGE).setCellRenderer(new TableCellRenderer() {
public LanguagesBuilder(ItemSelectionModel model) {
super();
m_model = model;
}
@Override
public Component getComponent(final Table table,
final PageState state,
final Object value,
final boolean isSelected,
final Object key,
final int row,
final int column) {
public DataQuery makeDataQuery(DataTable t, PageState s) {
ContentPage multiLingual =
(ContentPage) m_model.getSelectedObject(s);
DataQuery q = SessionManager.getSession().retrieveQuery(
"com.arsdigita.cms.getBundledItems");
q.setParameter("bundleID", multiLingual.getContentBundle().getID());
return q;
}
final Locale locale = new Locale((String) value);
public String getKeyColumn() {
return ContentPage.ID;
}
}
final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
final LanguageUtil langUtil = cdiUtil.findBean(
LanguageUtil.class);
/**
* Renders the full language name.
*/
private static class LanguageCellRenderer implements TableCellRenderer {
final Label label = new Label(langUtil.getLangFull(locale));
private ItemSelectionModel m_model;
public LanguageCellRenderer(ItemSelectionModel model) {
m_model = model;
}
public Component getComponent(Table table, PageState state, Object value,
boolean isSelected, Object key,
int row, int column) {
BigDecimal id = (BigDecimal) key;
ContentPage cp;
try {
cp = new ContentPage(id);
} catch (DataObjectNotFoundException ex) {
// Content item was not found, return nothing
return new Label();
}
ContentBundle bundle = cp.getContentBundle();
if (bundle != null
&& !(cp instanceof LanguageInvariantContentItem
&& ((LanguageInvariantContentItem) cp).isLanguageInvariant())) {
StringBuilder fontWeight = new StringBuilder(2);
StringBuilder classes = new StringBuilder(20);
if (cp.isLive()) {
fontWeight.append(Label.BOLD);
classes.append("live ");
}
if (bundle.getPrimaryInstance().equals(cp)) {
fontWeight.append(Label.ITALIC);
classes.append("primaryInstance");
}
String target = ContentItemPage.getItemURL(cp, ContentItemPage.AUTHORING_TAB);
Label langLabel = new Label(LanguageUtil.getLangFull((String) value));
langLabel.setFontWeight(fontWeight.toString().trim());
langLabel.setClassAttr(classes.toString().trim());
if (m_model.getSelectedKey(state).equals(key)) {
// Current instance: no link
return langLabel;
if (langSelectionModel.getSelectedKey(state).equals(key)) {
// Current variant, no link
return label;
} else {
return new Link(langLabel, target);
final String target = ContentItemPage.getItemURL(
itemSelectionModel.getSelectedItem(state),
ContentItemPage.AUTHORING_TAB);
return new Link(label, target);
}
}
return new Label();
}
);
columnModel.get(COL_DELETE).setCellRenderer(new TableCellRenderer() {
@Override
public Component getComponent(final Table table,
final PageState state,
final Object value,
final boolean isSelected,
final Object key,
final int row,
final int column) {
final ControlLink link = new ControlLink(new Label(
new GlobalizedMessage("cms.ui.delete",
CmsConstants.CMS_BUNDLE)));
link.setConfirmation(new GlobalizedMessage(
"cms.ui.delete.confirmation", CmsConstants.CMS_BUNDLE));
return link;
}
});
addTableActionListener(new TableActionListener() {
@Override
public void cellSelected(final TableActionEvent event)
throws FormProcessException {
if (event.getColumn() != COL_DELETE) {
//Nothing to do
return;
}
final PageState state = event.getPageState();
final ContentItem item = itemSelectionModel
.getSelectedItem(state);
final String selectedLang = (String) event.getRowKey();
final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
final ContentItemL10NManager l10nManager = cdiUtil.findBean(
ContentItemL10NManager.class);
l10nManager.removeLangauge(item, new Locale(selectedLang));
}
@Override
public void headSelected(final TableActionEvent event) {
//Nothing
}
});
}
/**
* Delete language instance action link.
*/
private static class ActionCellRenderer implements TableCellRenderer {
private static final Logger logger =
Logger.getLogger(ActionCellRenderer.class);
private static final Label s_noAction;
private static final Label s_primary;
private static final ControlLink s_link;
static {
logger.debug("Static initializer is starting...");
s_noAction = new Label("&nbsp;", false);
s_noAction.lock();
s_primary = new Label(GlobalizationUtil.globalize(
"cms.ui.primary_instance"), false);
s_primary.lock();
s_link = new ControlLink(new Label(GlobalizationUtil.globalize(
"cms.ui.delete")));
s_link.setConfirmation(GlobalizationUtil.globalize(
"cms.ui.delete_confirmation"));
logger.debug("Static initalizer finished.");
}
private ItemSelectionModel m_model;
public ActionCellRenderer(ItemSelectionModel model) {
m_model = model;
}
public Component getComponent(Table table, PageState state, Object value,
boolean isSelected, Object key,
int row, int column) {
// check if primary instance
BigDecimal id = new BigDecimal(key.toString());
OID oid = new OID(ContentPage.BASE_DATA_OBJECT_TYPE, id);
try {
ContentPage item = (ContentPage) DomainObjectFactory.newInstance(oid);
if (item.getLanguage().equals(
item.getContentBundle().getDefaultLanguage())) {
return s_primary;
} else if (item.isLive()) {
return s_noAction;
}
} catch (DataObjectNotFoundException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not get item with id " + id);
}
return s_noAction;
}
return s_link;
}
}
// delete one language instance
private class InstanceDeleter extends TableActionAdapter {
private ItemSelectionModel m_model;
public InstanceDeleter(ItemSelectionModel model) {
m_model = model;
}
@Override
public void cellSelected(TableActionEvent e) {
int col = e.getColumn().intValue();
if (m_deleteColumn != getColumn(col)) {
return;
}
PageState s = e.getPageState();
BigDecimal id = new BigDecimal(e.getRowKey().toString());
OID oid = new OID(ContentPage.BASE_DATA_OBJECT_TYPE, id);
try {
ContentPage item =
(ContentPage) DomainObjectFactory.newInstance(oid);
ContentBundle bundle = item.getContentBundle();
bundle.removeInstance(item);
item.delete();
if (m_model.getSelectedKey(s).equals(id)) {
throw new RedirectSignal(
URL.there((new MultilingualItemResolver()
.generateItemURL(s,
bundle.getPrimaryInstance(),
bundle.getContentSection(),
ContentItem.DRAFT)),
null),
true);
}
} catch (com.arsdigita.domain.DataObjectNotFoundException ex) {
// Object not found is ok, it has probably been deleted already
}
((Table) e.getSource()).clearSelection(s);
}
}
}

View File

@ -0,0 +1,79 @@
/*
* Copyright (C) 2016 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 com.arsdigita.cms.ui.item;
import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.table.RowData;
import com.arsdigita.bebop.table.TableModel;
import com.arsdigita.globalization.GlobalizedMessage;
import org.librecms.CmsConstants;
import java.util.Iterator;
import java.util.List;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
class ItemLanguagesTableModel implements TableModel {
private final Iterator<RowData<String>> iterator;
private RowData<String> curentRow;
protected ItemLanguagesTableModel(final List<RowData<String>> rows) {
iterator = rows.iterator();
}
@Override
public int getColumnCount() {
return 3;
}
@Override
public boolean nextRow() {
if (iterator.hasNext()) {
curentRow = iterator.next();
return true;
} else {
return false;
}
}
@Override
public Object getElementAt(final int columnIndex) {
switch (columnIndex) {
case ItemLanguagesTable.COL_LANGUAGE:
return curentRow.getColData(0);
case ItemLanguagesTable.COL_TITLE:
return curentRow.getColData(1);
case ItemLanguagesTable.COL_DELETE:
return new Label(new GlobalizedMessage("cms.ui.delete",
CmsConstants.CMS_BUNDLE));
default:
throw new IllegalArgumentException("Invalid column index.");
}
}
@Override
public Object getKeyAt(final int columnIndex) {
return curentRow.getRowKey();
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright (C) 2016 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 com.arsdigita.cms.ui.item;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.Table;
import com.arsdigita.bebop.table.RowData;
import com.arsdigita.bebop.table.TableModel;
import com.arsdigita.bebop.table.TableModelBuilder;
import com.arsdigita.cms.ItemSelectionModel;
import com.arsdigita.util.LockableImpl;
import org.libreccm.cdi.utils.CdiUtil;
import org.librecms.contentsection.ContentItem;
import java.util.List;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
class ItemLanguagesTableModelBuilder
extends LockableImpl
implements TableModelBuilder {
private final ItemSelectionModel itemSelectionModel;
protected ItemLanguagesTableModelBuilder(
final ItemSelectionModel itemSelectionModel) {
this.itemSelectionModel = itemSelectionModel;
}
@Override
public TableModel makeModel(final Table table, final PageState state) {
final ContentItem item = itemSelectionModel.getSelectedItem(state);
final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
final ItemLanguagesController controller = cdiUtil.findBean(ItemLanguagesController.class);
final List<RowData<String>> rows = controller.retrieveLanguageVariants(
item);
return new ItemLanguagesTableModel(rows);
}
}

View File

@ -24,6 +24,7 @@ import com.arsdigita.util.UncheckedWrapperException;
import org.libreccm.configuration.ConfigurationManager;
import org.libreccm.l10n.LocalizedString;
import org.libreccm.security.AuthorizationRequired;
import org.libreccm.security.PermissionChecker;
import org.libreccm.security.RequiresPrivilege;
import org.librecms.CmsConstants;
import org.librecms.contentsection.privileges.ItemPrivileges;
@ -34,9 +35,11 @@ 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;
@ -60,6 +63,9 @@ public class ContentItemL10NManager {
@Inject
private ContentItemRepository itemRepo;
@Inject
private PermissionChecker permissionChecker;
private Locale defaultLocale;
private List<Locale> supportedLocales;
@ -112,6 +118,50 @@ public class ContentItemL10NManager {
return locales;
}
/**
* Helper method for reading methods in this class for verifying that the
* current user is permitted to read the item.
*
* If the provided {@code item} is a live item
* ({@link ContentItem#getVersion()} returns
* {@link ContentItemVersion#LIVE}) the current user needs the a permission
* which grants the {@link ItemPrivileges#VIEW_PUBLISHED} privilege for the
* provided {@code item}. If the item is a draft item
* ({@link ContentItem#getVersion()} returns
* {@link ContentItemVersion#DRAFT}, {@link ContentItemVersion#PENDING} or
* {@link ContentItemVersion#PUBLISHING} the user needs a permission which
* grants the {@link ItemPrivileges#PREVIEW} privilege for the provided
* {@code item}.
*
* @param item The item for which the read permission is verified.
*/
private void checkReadPermission(final ContentItem item) {
final String requiredPrivilege;
if (ContentItemVersion.LIVE == item.getVersion()) {
requiredPrivilege = ItemPrivileges.VIEW_PUBLISHED;
} else {
requiredPrivilege = ItemPrivileges.PREVIEW;
}
permissionChecker.checkPermission(requiredPrivilege, item);
}
/**
* Retrieves all languages in which content item is available.
*
* @param item The item.
*
* @return A (unmodifiable) {@link Set} containing all languages in which
* the item is available.
*/
@Transactional(Transactional.TxType.REQUIRED)
public Set<Locale> availableLanguages(
final ContentItem item) {
checkReadPermission(item);
return Collections.unmodifiableSet(collectLanguages(item));
}
/**
* Checks if an content item has data for particular language.
*
@ -121,8 +171,13 @@ public class ContentItemL10NManager {
* @return {@link true} if the item has data for the language, {@code false}
* if not.
*/
@AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED)
public boolean hasLanguage(final ContentItem item, final Locale locale) {
public boolean hasLanguage(
@RequiresPrivilege(ItemPrivileges.VIEW_PUBLISHED)
final ContentItem item,
final Locale locale) {
if (item == null) {
throw new IllegalArgumentException("Can't check if item null has a"
+ "specific locale.");
@ -132,14 +187,27 @@ public class ContentItemL10NManager {
throw new IllegalArgumentException("Can't check for locale null.");
}
checkReadPermission(item);
return collectLanguages(item).contains(locale);
}
/**
* Returns a {@link Set} containing the locales for which the item does not
* yet have a variant.
*
* @param item The item.
*
* @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 ContentItem item) {
checkReadPermission(item);
return supportedLocales.stream()
.filter(locale -> hasLanguage(item, locale))
.collect(Collectors.toSet());
.filter(locale -> hasLanguage(item, locale))
.collect(Collectors.toSet());
}
/**
@ -149,7 +217,11 @@ public class ContentItemL10NManager {
* configured in {@link KernelConfig}. If a field does not have an entry for
* the default language the first value found is used.
*
* @param item The item to which the language variant is added.
* @param item The item to which the language variant is added. The item
* <strong>must be</strong> the <strong>draft version</strong>
* of the item! If a <em>live version</em> or a
* <em>pending version</em> is provided the method will throw an
* {@link IllegalArgumentException}!
* @param locale The locale of the language variant to add.
*/
@AuthorizationRequired
@ -160,7 +232,14 @@ public class ContentItemL10NManager {
final Locale locale) {
if (item == null) {
throw new IllegalArgumentException("Can't add language to item null.");
throw new IllegalArgumentException(
"Can't add language to item null.");
}
if (ContentItemVersion.DRAFT != item.getVersion()) {
throw new IllegalArgumentException(String.format(
"The provided item %s is not the draft version of an item.",
Objects.toString(item)));
}
if (locale == null) {
@ -220,7 +299,11 @@ public class ContentItemL10NManager {
* the entry for the provided locale if the field has an entry for that
* locale.
*
* @param item The item from which the language variant is removed.
* @param item The item from which the language variant is removed. The
* item <strong>must be</strong> the <strong>draft
* version</strong> of the item! If a <em>live version</em> or
* a <em>pending version</em> is provided the method will
* throw an {@link IllegalArgumentException}!
* @param locale The locale of the language variant to remove.
*/
@AuthorizationRequired
@ -235,6 +318,12 @@ public class ContentItemL10NManager {
"Can't remove language from item null.");
}
if (ContentItemVersion.DRAFT != item.getVersion()) {
throw new IllegalArgumentException(String.format(
"The provided item %s is not the draft version of an item.",
Objects.toString(item)));
}
if (locale == null) {
throw new IllegalArgumentException(
"Can't remove language null from an item.");
@ -268,7 +357,10 @@ public class ContentItemL10NManager {
* {@link LocalizedString} has an entry for every language in the set. If
* not an entry for the language is added.
*
* @param item The item to normalise.
* @param item The item to normalise. The item <strong>must be</strong> the
* <strong>draft version</strong> of the item! If a <em>live
* version</em> or a <em>pending version</em> is provided the
* method will throw an {@link IllegalArgumentException}!
*/
@AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED)
@ -280,6 +372,12 @@ public class ContentItemL10NManager {
throw new IllegalArgumentException("Can't normalise item null.");
}
if (ContentItemVersion.DRAFT != item.getVersion()) {
throw new IllegalArgumentException(String.format(
"The provided item %s is not the draft version of an item.",
Objects.toString(item)));
}
final Set<Locale> languages = collectLanguages(item);
findLocalizedStringProperties(item)