CCM NG: UI for configuration finished

git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@4079 8810af33-2d31-482b-a856-94f89814c4df
pull/2/head
jensp 2016-05-18 14:29:27 +00:00
parent 670e009365
commit c6746eb5e9
37 changed files with 1046 additions and 373 deletions

View File

@ -12,6 +12,9 @@
<Logger name="com.arsdigita.ui.admin.AdminServlet" <Logger name="com.arsdigita.ui.admin.AdminServlet"
level="debug"> level="debug">
</Logger> </Logger>
<Logger name="com.arsdigita.ui.admin.configuration.ConfigurationTable"
level="debug">
</Logger>
<Logger name="com.arsdigita.ui.admin.usersgroupsroles.UsersTable" <Logger name="com.arsdigita.ui.admin.usersgroupsroles.UsersTable"
level="debug"> level="debug">
</Logger> </Logger>

View File

@ -47,7 +47,7 @@ public class ActionLink extends ControlLink {
/** /**
* The value for the XML type attribute for an {@link ActionLink}. * The value for the XML type attribute for an {@link ActionLink}.
*/ */
protected final String TYPE_ACTION = "action"; protected static final String TYPE_ACTION = "action";
/** /**
* Constructs a new ActionLink. The link encapsulates * Constructs a new ActionLink. The link encapsulates

View File

@ -159,10 +159,10 @@ public final class UIConfig {
@Override @Override
public String toString() { public String toString() {
final StringJoiner joiner = new StringJoiner(", "); // final StringJoiner joiner = new StringJoiner(", ");
if (defaultLayout != null) { // if (defaultLayout != null) {
defaultLayout.forEach(s -> joiner.add(s)); // defaultLayout.forEach(s -> joiner.add(s));
} // }
return String.format("%s{ " return String.format("%s{ "
+ "defaultLayout = \"%s\", " + "defaultLayout = \"%s\", "

View File

@ -40,11 +40,11 @@ import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.configuration.ConfigurationManager; import org.libreccm.configuration.ConfigurationManager;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.math.BigDecimal;
import static com.arsdigita.ui.admin.AdminUiConstants.*; import static com.arsdigita.ui.admin.AdminUiConstants.*;
/** /**
* An abstract base class for a form for editing settings with a single value.
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
* @param <T> * @param <T>
@ -56,6 +56,16 @@ public abstract class AbstractSettingFormSingleValue<T> extends Form {
private static final String VALUE_FIELD = "valueField"; private static final String VALUE_FIELD = "valueField";
private final SaveCancelSection saveCancelSection;
/**
* Constructor, initialises the form and the supporting widgets.
*
* @param configurationTab The configuration tab in which the form is shown.
* @param selectedConf Parameter containing the selected configuration
* class.
* @param selectedSetting Parameter containing the selected setting.
*/
public AbstractSettingFormSingleValue( public AbstractSettingFormSingleValue(
final ConfigurationTab configurationTab, final ConfigurationTab configurationTab,
final ParameterSingleSelectionModel<String> selectedConf, final ParameterSingleSelectionModel<String> selectedConf,
@ -63,7 +73,9 @@ public abstract class AbstractSettingFormSingleValue<T> extends Form {
super("settingFormSingleValue", new BoxPanel(BoxPanel.VERTICAL)); super("settingFormSingleValue", new BoxPanel(BoxPanel.VERTICAL));
add(new SettingFormHeader(selectedConf, selectedSetting)); add(new SettingFormHeader(configurationTab,
selectedConf,
selectedSetting));
add(new SettingFormCurrentValuePanel(selectedConf, selectedSetting)); add(new SettingFormCurrentValuePanel(selectedConf, selectedSetting));
@ -72,7 +84,7 @@ public abstract class AbstractSettingFormSingleValue<T> extends Form {
"ui.admin.configuration.setting.edit.new_value", ADMIN_BUNDLE)); "ui.admin.configuration.setting.edit.new_value", ADMIN_BUNDLE));
add(valueField); add(valueField);
final SaveCancelSection saveCancelSection = new SaveCancelSection(); saveCancelSection = new SaveCancelSection();
add(saveCancelSection); add(saveCancelSection);
addInitListener(new InitListener(selectedConf, addInitListener(new InitListener(selectedConf,
@ -87,8 +99,20 @@ public abstract class AbstractSettingFormSingleValue<T> extends Form {
} }
/**
* Converts a string to the value type of the setting. Must be overwritten
* by the none abstract sub classes.
*
* @param valueData The data to convert.
*
* @return The converted data.
*/
abstract T convertValue(final String valueData); abstract T convertValue(final String valueData);
/**
* {@link FormInitListener} for the form. Loads the current value of the
* setting from the database and puts it into the input field.
*/
private class InitListener implements FormInitListener { private class InitListener implements FormInitListener {
private final ParameterSingleSelectionModel<String> selectedConf; private final ParameterSingleSelectionModel<String> selectedConf;
@ -126,10 +150,14 @@ public abstract class AbstractSettingFormSingleValue<T> extends Form {
final Object value; final Object value;
try { try {
value = confClass.getField(selectedSetting final Field field = confClass.getDeclaredField(selectedSetting
.getSelectedKey(state)).get(config); .getSelectedKey(state));
} catch (NoSuchFieldException | SecurityException | field.setAccessible(true);
IllegalAccessException | ClassCastException ex) { value = field.get(config);
} catch (NoSuchFieldException |
SecurityException |
IllegalAccessException |
ClassCastException ex) {
LOGGER.warn("Failed to read setting {} from configuration {}", LOGGER.warn("Failed to read setting {} from configuration {}",
selectedSetting.getSelectedKey(state), selectedSetting.getSelectedKey(state),
selectedConf.getSelectedKey(state)); selectedConf.getSelectedKey(state));
@ -142,13 +170,21 @@ public abstract class AbstractSettingFormSingleValue<T> extends Form {
} }
/**
* {@link FormValidationListener} which checks if the value provided by the
* user can be converted into the type of the setting.
*
*/
private class ValidationListener implements FormValidationListener { private class ValidationListener implements FormValidationListener {
@Override @Override
public void validate(final FormSectionEvent event) throws public void validate(final FormSectionEvent event)
FormProcessException { throws FormProcessException {
final FormData data = event.getFormData();
final FormData data = event.getFormData();
final PageState state = event.getPageState();
if (saveCancelSection.getSaveButton().isSelected(state)) {
final String valueData = data.getString(VALUE_FIELD); final String valueData = data.getString(VALUE_FIELD);
if (Strings.isBlank(valueData)) { if (Strings.isBlank(valueData)) {
data.addError(VALUE_FIELD, data.addError(VALUE_FIELD,
@ -160,15 +196,21 @@ public abstract class AbstractSettingFormSingleValue<T> extends Form {
final T value = convertValue(valueData); final T value = convertValue(valueData);
LOGGER.debug("New value {} is a valid BigDecimal.", value); LOGGER.debug("New value {} is a valid BigDecimal.", value);
} catch (NumberFormatException ex) { } catch (NumberFormatException ex) {
data.addError(VALUE_FIELD, data.addError(
VALUE_FIELD,
new GlobalizedMessage( new GlobalizedMessage(
"ui.admin.configuration.setting.error.not_a_bigdecimal", "ui.admin.configuration.setting.error.incorrect_format",
ADMIN_BUNDLE)); ADMIN_BUNDLE));
} }
} }
}
} }
/**
* {@link FormProcessListener} to store the new value of the setting
* in the database.
*/
private class ProcessListener implements FormProcessListener { private class ProcessListener implements FormProcessListener {
private final ConfigurationTab configurationTab; private final ConfigurationTab configurationTab;
@ -189,6 +231,8 @@ public abstract class AbstractSettingFormSingleValue<T> extends Form {
throws FormProcessException { throws FormProcessException {
final PageState state = event.getPageState(); final PageState state = event.getPageState();
if (saveCancelSection.getSaveButton().isSelected(state)) {
final FormData data = event.getFormData(); final FormData data = event.getFormData();
final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
@ -208,8 +252,14 @@ public abstract class AbstractSettingFormSingleValue<T> extends Form {
final Field field; final Field field;
try { try {
field = confClass.getField(settingName); field = confClass.getDeclaredField(settingName);
field.setAccessible(true);
} catch (NoSuchFieldException | SecurityException ex) { } catch (NoSuchFieldException | SecurityException ex) {
LOGGER.error("Failed to retrieve field \"{}\" "
+ "from configuration class \"{}\".",
settingName,
confClass.getName());
LOGGER.error(ex);
throw new FormProcessException( throw new FormProcessException(
String.format( String.format(
"Failed to retrieve field \"%s\" " "Failed to retrieve field \"%s\" "
@ -228,8 +278,14 @@ public abstract class AbstractSettingFormSingleValue<T> extends Form {
try { try {
field.set(config, value); field.set(config, value);
confManager.saveConfiguration(config);
configurationTab.hideSettingForms(state); configurationTab.hideSettingForms(state);
} catch (IllegalArgumentException | IllegalAccessException ex) { } catch (IllegalArgumentException | IllegalAccessException ex) {
LOGGER.error("Failed to change value of field \"{}\" "
+ "of configuration class \"{}\".",
settingName,
confClass.getName());
LOGGER.error(ex);
throw new FormProcessException( throw new FormProcessException(
String.format( String.format(
"Failed to change value of field \"%s\" " "Failed to change value of field \"%s\" "
@ -243,6 +299,9 @@ public abstract class AbstractSettingFormSingleValue<T> extends Form {
} }
} }
configurationTab.hideSettingForms(state);
}
} }
} }

View File

@ -31,10 +31,18 @@ import com.arsdigita.bebop.form.TextField;
import com.arsdigita.bebop.parameters.StringParameter; import com.arsdigita.bebop.parameters.StringParameter;
import com.arsdigita.globalization.GlobalizedMessage; import com.arsdigita.globalization.GlobalizedMessage;
import com.arsdigita.toolbox.ui.LayoutPanel; import com.arsdigita.toolbox.ui.LayoutPanel;
import com.arsdigita.util.UncheckedWrapperException;
import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.configuration.ConfigurationInfo;
import org.libreccm.configuration.ConfigurationManager;
import org.libreccm.l10n.GlobalizationHelper;
import static com.arsdigita.ui.admin.AdminUiConstants.*; import static com.arsdigita.ui.admin.AdminUiConstants.*;
/** /**
* Tab for the admin application containing the UI to manage the settings in the
* various configuration classes.
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
@ -42,31 +50,86 @@ public class ConfigurationTab extends LayoutPanel {
private static final String CONF_CLASSES_FILTER = "confClassesFilter"; private static final String CONF_CLASSES_FILTER = "confClassesFilter";
/**
* Parameter for the selected configuration.
*/
private final StringParameter selectedConfParam; private final StringParameter selectedConfParam;
private final ParameterSingleSelectionModel<String> selectedConf; private final ParameterSingleSelectionModel<String> selectedConf;
/**
* Parameter for the selected setting.
*/
private final StringParameter selectedSettingParam; private final StringParameter selectedSettingParam;
private final ParameterSingleSelectionModel<String> selectedSetting; private final ParameterSingleSelectionModel<String> selectedSetting;
/**
* Parameter for the selected value of multi-value settings.
*/
private final StringParameter selectedValueParam; private final StringParameter selectedValueParam;
private final ParameterSingleSelectionModel<String> selectedValue; private final ParameterSingleSelectionModel<String> selectedValue;
/**
* Heading of the table of configurations.
*/
private final Label confClassesFilterHeading; private final Label confClassesFilterHeading;
/**
* Form for filtering the table of configurations.
*/
private final Form confClassesFilterForm; private final Form confClassesFilterForm;
/**
* The table which lists all available configurations.
*/
private final ConfigurationsTable configurationsTable; private final ConfigurationsTable configurationsTable;
/**
* Link to go back to the listing of configurations.
*/
private final ActionLink configurationBackLink; private final ActionLink configurationBackLink;
/**
* Heading for the list of settings of a configuration.
*/
private final Label configurationHeading;
/**
* The table which lists a settings of configuration.
*/
private final ConfigurationTable configurationTable; private final ConfigurationTable configurationTable;
/**
* The form for editing a setting of the type {@code boolean}.
*/
private final SettingFormBoolean settingFormBoolean; private final SettingFormBoolean settingFormBoolean;
/**
* The form for editing a setting of the type {@code long}.
*/
private final SettingFormLong settingFormLong; private final SettingFormLong settingFormLong;
/**
* The form for editing a setting of the type {@code double}.
*/
private final SettingFormDouble settingFormDouble; private final SettingFormDouble settingFormDouble;
/**
* The form for editing a setting of the type {@code BigDecimal}.
*/
private final SettingFormBigDecimal settingFormBigDecimal; private final SettingFormBigDecimal settingFormBigDecimal;
/**
* The form for editing a setting of the type {@code String}.
*/
private final SettingFormString settingFormString; private final SettingFormString settingFormString;
/**
* The form for editing a setting of the type {@code LocalizedString}.
*/
private final SettingEditorLocalizedString settingEditorLocalizedString; private final SettingEditorLocalizedString settingEditorLocalizedString;
/**
* The form for editing a setting of the type {@code List<String>}.
*/
private final SettingEditorStringList settingEditorStringList; private final SettingEditorStringList settingEditorStringList;
/**
* The form for editing a setting of the type {@code Set<String>}.
*/
private final SettingEditorEnum settingEditorEnum; private final SettingEditorEnum settingEditorEnum;
/**
* Initialises the parameters, widgets and forms.
*/
public ConfigurationTab() { public ConfigurationTab() {
super(); super();
@ -116,6 +179,37 @@ public class ConfigurationTab extends LayoutPanel {
hideConfiguration(state); hideConfiguration(state);
}); });
body.add(configurationBackLink); body.add(configurationBackLink);
configurationHeading = new Label(e -> {
//This print listener includes the (localised title) of the selected
//configuration in the heading
final PageState state = e.getPageState();
final Class<?> confClass;
try {
confClass = Class.forName(selectedConf.getSelectedKey(state));
} catch (ClassNotFoundException ex) {
throw new UncheckedWrapperException(
String.format("Configuration class \"%s\" not found.",
selectedConf.getSelectedKey(state)),
ex);
}
final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
final ConfigurationManager confManager = cdiUtil.findBean(
ConfigurationManager.class);
final ConfigurationInfo confInfo = confManager.getConfigurationInfo(
confClass);
final GlobalizationHelper globalizationHelper = cdiUtil.findBean(
GlobalizationHelper.class);
final Label target = (Label) e.getTarget();
final String confTitle = confInfo.getTitle(globalizationHelper
.getNegotiatedLocale());
target.setLabel(new GlobalizedMessage(
"ui.admin.configuration.editing_configuration",
ADMIN_BUNDLE,
new String[]{confTitle}));
});
configurationHeading.setClassAttr("heading");
body.add(configurationHeading);
configurationTable = new ConfigurationTable(this, configurationTable = new ConfigurationTable(this,
selectedConf, selectedConf,
selectedSetting); selectedSetting);
@ -161,6 +255,13 @@ public class ConfigurationTab extends LayoutPanel {
setBody(body); setBody(body);
} }
/**
* Registers all forms and widgets in the page for visibility management and
* registers all parameters. The method is called by Bebop when the tab is
* rendered.
*
* @param page The page which this tab is part of.
*/
@Override @Override
public void register(final Page page) { public void register(final Page page) {
super.register(page); super.register(page);
@ -174,6 +275,7 @@ public class ConfigurationTab extends LayoutPanel {
page.setVisibleDefault(configurationsTable, true); page.setVisibleDefault(configurationsTable, true);
page.setVisibleDefault(configurationBackLink, false); page.setVisibleDefault(configurationBackLink, false);
page.setVisibleDefault(configurationHeading, false);
page.setVisibleDefault(configurationTable, false); page.setVisibleDefault(configurationTable, false);
page.setVisibleDefault(settingFormBoolean, false); page.setVisibleDefault(settingFormBoolean, false);
@ -186,6 +288,12 @@ public class ConfigurationTab extends LayoutPanel {
page.setVisibleDefault(settingEditorEnum, false); page.setVisibleDefault(settingEditorEnum, false);
} }
/**
* Shows the {@link #configurationsTable} and hides all other widgets and
* forms.
*
* @param state The current {@link PageState}.
*/
protected void showConfigurationsTable(final PageState state) { protected void showConfigurationsTable(final PageState state) {
confClassesFilterHeading.setVisible(state, true); confClassesFilterHeading.setVisible(state, true);
confClassesFilterForm.setVisible(state, true); confClassesFilterForm.setVisible(state, true);
@ -203,22 +311,41 @@ public class ConfigurationTab extends LayoutPanel {
settingEditorEnum.setVisible(state, false); settingEditorEnum.setVisible(state, false);
} }
/**
* Hides the {@link #configurationsTable} and its supporting widgets.
*
* @param state The current {@link PageState}.
*/
protected void hideConfigurationsTable(final PageState state) { protected void hideConfigurationsTable(final PageState state) {
confClassesFilterHeading.setVisible(state, false); confClassesFilterHeading.setVisible(state, false);
confClassesFilterForm.setVisible(state, false); confClassesFilterForm.setVisible(state, false);
configurationsTable.setVisible(state, false); configurationsTable.setVisible(state, false);
} }
/**
* Shows the {@link #configurationTable} which lists all settings of a
* configuration.
*
* @param state The current {@link PageState}.
*/
protected void showConfiguration(final PageState state) { protected void showConfiguration(final PageState state) {
hideConfigurationsTable(state); hideConfigurationsTable(state);
hideSettingForms(state); hideSettingForms(state);
configurationBackLink.setVisible(state, true); configurationBackLink.setVisible(state, true);
configurationHeading.setVisible(state, true);
configurationTable.setVisible(state, true); configurationTable.setVisible(state, true);
} }
/**
* Hides the configuration table and resets the {@link #selectedConf}
* parameter.
*
* @param state The current {@link PageState}.
*/
protected void hideConfiguration(final PageState state) { protected void hideConfiguration(final PageState state) {
configurationBackLink.setVisible(state, false); configurationBackLink.setVisible(state, false);
configurationHeading.setVisible(state, false);
configurationTable.setVisible(state, false); configurationTable.setVisible(state, false);
selectedConf.clearSelection(state); selectedConf.clearSelection(state);
@ -226,11 +353,18 @@ public class ConfigurationTab extends LayoutPanel {
showConfigurationsTable(state); showConfigurationsTable(state);
} }
/**
* Shows the {@link #settingFormBigDecimal}.
*
* @param state The current {@link PageState}.
*/
protected void showBigDecimalSettingForm(final PageState state) { protected void showBigDecimalSettingForm(final PageState state) {
confClassesFilterHeading.setVisible(state, false); confClassesFilterHeading.setVisible(state, false);
confClassesFilterForm.setVisible(state, false); confClassesFilterForm.setVisible(state, false);
configurationsTable.setVisible(state, false); configurationsTable.setVisible(state, false);
configurationBackLink.setVisible(state, false);
configurationHeading.setVisible(state, false);
configurationTable.setVisible(state, false); configurationTable.setVisible(state, false);
settingFormBoolean.setVisible(state, false); settingFormBoolean.setVisible(state, false);
@ -243,11 +377,18 @@ public class ConfigurationTab extends LayoutPanel {
settingEditorEnum.setVisible(state, false); settingEditorEnum.setVisible(state, false);
} }
/**
* Shows the {@link #settingFormBoolean}.
*
* @param state The current {@link PageState}.
*/
protected void showBooleanSettingForm(final PageState state) { protected void showBooleanSettingForm(final PageState state) {
confClassesFilterHeading.setVisible(state, false); confClassesFilterHeading.setVisible(state, false);
confClassesFilterForm.setVisible(state, false); confClassesFilterForm.setVisible(state, false);
configurationsTable.setVisible(state, false); configurationsTable.setVisible(state, false);
configurationBackLink.setVisible(state, false);
configurationHeading.setVisible(state, false);
configurationTable.setVisible(state, false); configurationTable.setVisible(state, false);
settingFormBoolean.setVisible(state, true); settingFormBoolean.setVisible(state, true);
@ -260,11 +401,18 @@ public class ConfigurationTab extends LayoutPanel {
settingEditorEnum.setVisible(state, false); settingEditorEnum.setVisible(state, false);
} }
/**
* Shows the {@link #settingFormDouble}.
*
* @param state The current {@link PageState}.
*/
protected void showDoubleSettingForm(final PageState state) { protected void showDoubleSettingForm(final PageState state) {
confClassesFilterHeading.setVisible(state, false); confClassesFilterHeading.setVisible(state, false);
confClassesFilterForm.setVisible(state, false); confClassesFilterForm.setVisible(state, false);
configurationsTable.setVisible(state, false); configurationsTable.setVisible(state, false);
configurationBackLink.setVisible(state, false);
configurationHeading.setVisible(state, false);
configurationTable.setVisible(state, false); configurationTable.setVisible(state, false);
settingFormBoolean.setVisible(state, false); settingFormBoolean.setVisible(state, false);
@ -277,11 +425,18 @@ public class ConfigurationTab extends LayoutPanel {
settingEditorEnum.setVisible(state, false); settingEditorEnum.setVisible(state, false);
} }
/**
* Shows the {@link #settingEditorEnum}.
*
* @param state The current {@link PageState}.
*/
protected void showEnumSettingForm(final PageState state) { protected void showEnumSettingForm(final PageState state) {
confClassesFilterHeading.setVisible(state, false); confClassesFilterHeading.setVisible(state, false);
confClassesFilterForm.setVisible(state, false); confClassesFilterForm.setVisible(state, false);
configurationsTable.setVisible(state, false); configurationsTable.setVisible(state, false);
configurationBackLink.setVisible(state, false);
configurationHeading.setVisible(state, false);
configurationTable.setVisible(state, false); configurationTable.setVisible(state, false);
settingFormBoolean.setVisible(state, false); settingFormBoolean.setVisible(state, false);
@ -294,11 +449,18 @@ public class ConfigurationTab extends LayoutPanel {
settingEditorEnum.setVisible(state, true); settingEditorEnum.setVisible(state, true);
} }
/**
* Show the {@link #settingEditorLocalizedString}.
*
* @param state The current {@link PageState}.
*/
protected void showLocalizedStringSettingForm(final PageState state) { protected void showLocalizedStringSettingForm(final PageState state) {
confClassesFilterHeading.setVisible(state, false); confClassesFilterHeading.setVisible(state, false);
confClassesFilterForm.setVisible(state, false); confClassesFilterForm.setVisible(state, false);
configurationsTable.setVisible(state, false); configurationsTable.setVisible(state, false);
configurationBackLink.setVisible(state, false);
configurationHeading.setVisible(state, false);
configurationTable.setVisible(state, false); configurationTable.setVisible(state, false);
settingFormBoolean.setVisible(state, false); settingFormBoolean.setVisible(state, false);
@ -311,11 +473,18 @@ public class ConfigurationTab extends LayoutPanel {
settingEditorEnum.setVisible(state, false); settingEditorEnum.setVisible(state, false);
} }
/**
* Shows the {@link #settingFormLong}.
*
* @param state The current {@link PageState}.
*/
protected void showLongSettingForm(final PageState state) { protected void showLongSettingForm(final PageState state) {
confClassesFilterHeading.setVisible(state, false); confClassesFilterHeading.setVisible(state, false);
confClassesFilterForm.setVisible(state, false); confClassesFilterForm.setVisible(state, false);
configurationsTable.setVisible(state, false); configurationsTable.setVisible(state, false);
configurationBackLink.setVisible(state, false);
configurationHeading.setVisible(state, false);
configurationTable.setVisible(state, false); configurationTable.setVisible(state, false);
settingFormBoolean.setVisible(state, false); settingFormBoolean.setVisible(state, false);
@ -328,11 +497,18 @@ public class ConfigurationTab extends LayoutPanel {
settingEditorEnum.setVisible(state, false); settingEditorEnum.setVisible(state, false);
} }
/**
* Shows the {@link #settingEditorStringList}.
*
* @param state The current {@link PageState}.
*/
protected void showStringListSettingForm(final PageState state) { protected void showStringListSettingForm(final PageState state) {
confClassesFilterHeading.setVisible(state, false); confClassesFilterHeading.setVisible(state, false);
confClassesFilterForm.setVisible(state, false); confClassesFilterForm.setVisible(state, false);
configurationsTable.setVisible(state, false); configurationsTable.setVisible(state, false);
configurationBackLink.setVisible(state, false);
configurationHeading.setVisible(state, false);
configurationTable.setVisible(state, false); configurationTable.setVisible(state, false);
settingFormBoolean.setVisible(state, false); settingFormBoolean.setVisible(state, false);
@ -345,11 +521,18 @@ public class ConfigurationTab extends LayoutPanel {
settingEditorEnum.setVisible(state, false); settingEditorEnum.setVisible(state, false);
} }
/**
* Shows the {@link #settingFormString}.
*
* @param state The current {@link PageState}.
*/
protected void showStringSettingForm(final PageState state) { protected void showStringSettingForm(final PageState state) {
confClassesFilterHeading.setVisible(state, false); confClassesFilterHeading.setVisible(state, false);
confClassesFilterForm.setVisible(state, false); confClassesFilterForm.setVisible(state, false);
configurationsTable.setVisible(state, false); configurationsTable.setVisible(state, false);
configurationBackLink.setVisible(state, false);
configurationHeading.setVisible(state, false);
configurationTable.setVisible(state, false); configurationTable.setVisible(state, false);
settingFormBoolean.setVisible(state, false); settingFormBoolean.setVisible(state, false);
@ -362,12 +545,20 @@ public class ConfigurationTab extends LayoutPanel {
settingEditorEnum.setVisible(state, false); settingEditorEnum.setVisible(state, false);
} }
/**
* Hides all settings forms/editors and resets the {@link #selectedSetting}
* and {@link #selectedValue} parameters.
*
* @param state The current {@link PageState}.
*/
protected void hideSettingForms(final PageState state) { protected void hideSettingForms(final PageState state) {
confClassesFilterHeading.setVisible(state, false); confClassesFilterHeading.setVisible(state, false);
confClassesFilterForm.setVisible(state, false); confClassesFilterForm.setVisible(state, false);
configurationsTable.setVisible(state, false); configurationsTable.setVisible(state, false);
configurationTable.setVisible(state, false); configurationBackLink.setVisible(state, true);
configurationHeading.setVisible(state, true);
configurationTable.setVisible(state, true);
settingFormBoolean.setVisible(state, false); settingFormBoolean.setVisible(state, false);
settingFormLong.setVisible(state, false); settingFormLong.setVisible(state, false);

View File

@ -34,6 +34,7 @@ import com.arsdigita.bebop.table.TableModelBuilder;
import com.arsdigita.globalization.GlobalizedMessage; import com.arsdigita.globalization.GlobalizedMessage;
import com.arsdigita.util.LockableImpl; import com.arsdigita.util.LockableImpl;
import com.arsdigita.util.UncheckedWrapperException; import com.arsdigita.util.UncheckedWrapperException;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
@ -43,12 +44,20 @@ import org.libreccm.configuration.ConfigurationManager;
import org.libreccm.configuration.SettingInfo; import org.libreccm.configuration.SettingInfo;
import org.libreccm.configuration.SettingManager; import org.libreccm.configuration.SettingManager;
import org.libreccm.l10n.GlobalizationHelper; import org.libreccm.l10n.GlobalizationHelper;
import org.libreccm.l10n.LocalizedString;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.Set;
import static com.arsdigita.ui.admin.AdminUiConstants.*; import static com.arsdigita.ui.admin.AdminUiConstants.*;
/** /**
* This table lists all settings of a configuration class. The table shows the
* current value and the description of each setting. If there is localised
* label for the setting this label is used, otherwise the name of the setting
* is used.
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
@ -131,27 +140,36 @@ public class ConfigurationTable extends Table {
switch (getTypeOfSelectedSetting(state)) { switch (getTypeOfSelectedSetting(state)) {
case "boolean": case "boolean":
LOGGER.debug("Setting is of type boolean"); LOGGER.debug("Setting is of type boolean");
configurationTab.showBooleanSettingForm(state);
break; break;
case "long": case "long":
LOGGER.debug("Setting is of type long"); LOGGER.debug("Setting is of type long");
configurationTab.showLongSettingForm(state);
break; break;
case "double": case "double":
LOGGER.debug("Setting is of type double"); LOGGER.debug("Setting is of type double");
configurationTab.showDoubleSettingForm(state);
break; break;
case "java.math.BigDecimal": case "java.math.BigDecimal":
LOGGER.debug("Setting is of type BigDecimal"); LOGGER.debug("Setting is of type BigDecimal");
configurationTab.showBigDecimalSettingForm(state);
break; break;
case "org.libreccm.l10n.LocalizedString": case "org.libreccm.l10n.LocalizedString":
LOGGER.debug("Setting is of type LocalizedString"); LOGGER.debug("Setting is of type LocalizedString");
configurationTab.showLocalizedStringSettingForm(
state);
break; break;
case "java.lang.String": case "java.lang.String":
LOGGER.debug("Setting is of type String"); LOGGER.debug("Setting is of type String");
configurationTab.showStringSettingForm(state);
break; break;
case "java.util.Set": case "java.util.Set":
LOGGER.debug("Setting is of type Enum"); LOGGER.debug("Setting is of type Enum");
configurationTab.showEnumSettingForm(state);
break; break;
case "java.util.List": case "java.util.List":
LOGGER.debug("Setting is of type List"); LOGGER.debug("Setting is of type List");
configurationTab.showStringListSettingForm(state);
break; break;
default: default:
throw new IllegalArgumentException(String.format( throw new IllegalArgumentException(String.format(
@ -270,9 +288,8 @@ public class ConfigurationTable extends Table {
final Field field = configuration.getClass(). final Field field = configuration.getClass().
getDeclaredField(setting); getDeclaredField(setting);
field.setAccessible(true); field.setAccessible(true);
return field.get(configuration); return buildValueColumn(settingInfo,
// return configuration.getClass(). field.get(configuration));
// getDeclaredField(setting).get(configuration);
} catch (NoSuchFieldException | } catch (NoSuchFieldException |
SecurityException | SecurityException |
IllegalAccessException ex) { IllegalAccessException ex) {
@ -294,8 +311,39 @@ public class ConfigurationTable extends Table {
} }
} }
private String buildValueColumn(final SettingInfo settingInfo,
final Object settingValue) {
switch (settingInfo.getValueType()) {
case "java.util.List": {
@SuppressWarnings("unchecked")
final List<String> value = (List<String>) settingValue;
return String.join(", ", value);
}
case "java.util.Set": {
@SuppressWarnings("unchecked")
final Set<String> value = (Set<String>) settingValue;
return String.join(", ", value);
}
case "org.libreccm.l10n.LocalizedString": {
final LocalizedString value = (LocalizedString) settingValue;
final List<String> entries = new ArrayList<>();
value.getValues().entrySet().forEach(e -> {
entries.add(String.format("%s: %s",
e.getKey(),
e.getValue()));
});
return String.join("; ", entries);
}
default: {
return Objects.toString(settingValue);
}
}
}
@Override @Override
public Object getKeyAt(final int columnIndex) { public Object getKeyAt(final int columnIndex
) {
return settings.get(index); return settings.get(index);
} }

View File

@ -50,6 +50,10 @@ import java.util.stream.Collectors;
import static com.arsdigita.ui.admin.AdminUiConstants.*; import static com.arsdigita.ui.admin.AdminUiConstants.*;
/** /**
* This table lists all available configuration classes together with their
* descriptions (if any). If there is localised title for the configuration this
* title is used, otherwise the fully qualified name of the configuration is
* used.
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */

View File

@ -18,7 +18,6 @@
*/ */
package com.arsdigita.ui.admin.configuration; package com.arsdigita.ui.admin.configuration;
import com.arsdigita.bebop.ActionLink;
import com.arsdigita.bebop.BoxPanel; import com.arsdigita.bebop.BoxPanel;
import com.arsdigita.bebop.Component; import com.arsdigita.bebop.Component;
import com.arsdigita.bebop.ControlLink; import com.arsdigita.bebop.ControlLink;
@ -46,7 +45,9 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.libreccm.cdi.utils.CdiUtil; import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.configuration.ConfigurationManager; import org.libreccm.configuration.ConfigurationManager;
import org.libreccm.configuration.EnumSetting;
import java.lang.reflect.Field;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -55,6 +56,10 @@ import java.util.Set;
import static com.arsdigita.ui.admin.AdminUiConstants.*; import static com.arsdigita.ui.admin.AdminUiConstants.*;
/** /**
* Editor for {@link EnumSetting}s. The editor consists of the usual header (see
* {@link SettingFormHeader}) which is used by all setting forms/editors, a
* table which displays all current values together with links for editing and
* deleting the values and a form for adding and editing values.
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
@ -85,15 +90,9 @@ public class SettingEditorEnum extends BoxPanel {
this.selectedSetting = selectedSetting; this.selectedSetting = selectedSetting;
this.selectedValue = selectedValue; this.selectedValue = selectedValue;
final ActionLink backLink = new ActionLink(new GlobalizedMessage( add(new SettingFormHeader(configurationTab,
"ui.admin.configuration.setting.localized_string.back", selectedConf,
ADMIN_BUNDLE)); selectedSetting));
backLink.addActionListener(e -> {
configurationTab.hideSettingForms(e.getPageState());
});
add(backLink);
add(new SettingFormHeader(selectedConf, selectedSetting));
add(new ValuesTable()); add(new ValuesTable());
@ -194,10 +193,14 @@ public class SettingEditorEnum extends BoxPanel {
final Set<String> values; final Set<String> values;
try { try {
values = (Set<String>) confClass.getField( final Field field = confClass.getDeclaredField(
selectedSetting.getSelectedKey(state)).get(config); selectedSetting.getSelectedKey(state));
} catch (NoSuchFieldException | SecurityException | field.setAccessible(true);
IllegalAccessException | ClassCastException ex) { values = (Set<String>) field.get(config);
} catch (NoSuchFieldException |
SecurityException |
IllegalAccessException |
ClassCastException ex) {
LOGGER.warn( LOGGER.warn(
"Failed to read setting {} from configuration {}", "Failed to read setting {} from configuration {}",
selectedSetting.getSelectedKey(state), selectedSetting.getSelectedKey(state),
@ -274,14 +277,17 @@ public class SettingEditorEnum extends BoxPanel {
final Object config = confManager.findConfiguration(confClass); final Object config = confManager.findConfiguration(confClass);
try { try {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Set<String> valuesSet = (Set<String>) confClass.getField( final Field field = confClass.getDeclaredField(selectedSetting
selectedSetting .getSelectedKey(state));
.getSelectedKey(state)).get(config); field.setAccessible(true);
final Set<String> valuesSet = (Set<String>) field.get(config);
values = new ArrayList<>(valuesSet); values = new ArrayList<>(valuesSet);
} catch (NoSuchFieldException | SecurityException | } catch (NoSuchFieldException |
IllegalAccessException | ClassCastException ex) { SecurityException |
IllegalAccessException |
ClassCastException ex) {
LOGGER.warn("Failed to read setting {} from configuration {}", LOGGER.warn("Failed to read setting {} from configuration {}",
selectedSetting.getSelectedKey(state), selectedSetting.getSelectedKey(state),
selectedConf.getSelectedKey(state)); selectedConf.getSelectedKey(state));
@ -378,18 +384,23 @@ public class SettingEditorEnum extends BoxPanel {
final Set<String> enumSetting; final Set<String> enumSetting;
try { try {
final Object value = confClass.getField(selectedSetting final Field field = confClass.getDeclaredField(
.getSelectedKey(state)).get(config); selectedSetting.getSelectedKey(state));
field.setAccessible(true);
final Object value = field.get(config);
if (value == null) { if (value == null) {
enumSetting = new HashSet<>(); enumSetting = new HashSet<>();
confClass.getField(selectedSetting.getSelectedKey( confClass.getDeclaredField(selectedSetting
state)).set(config, enumSetting); .getSelectedKey(state)).set(config,
enumSetting);
} else { } else {
enumSetting = (Set<String>) value; enumSetting = (Set<String>) value;
} }
} catch (NoSuchFieldException | SecurityException | } catch (NoSuchFieldException |
IllegalAccessException | ClassCastException ex) { SecurityException |
IllegalAccessException |
ClassCastException ex) {
LOGGER.warn( LOGGER.warn(
"Failed to read setting {} from configuration {}", "Failed to read setting {} from configuration {}",
selectedSetting.getSelectedKey(state), selectedSetting.getSelectedKey(state),
@ -411,6 +422,9 @@ public class SettingEditorEnum extends BoxPanel {
} }
confManager.saveConfiguration(config); confManager.saveConfiguration(config);
} }
selectedValue.clearSelection(state);
valueField.setValue(state, null);
}); });
} }

View File

@ -18,7 +18,6 @@
*/ */
package com.arsdigita.ui.admin.configuration; package com.arsdigita.ui.admin.configuration;
import com.arsdigita.bebop.ActionLink;
import com.arsdigita.bebop.BoxPanel; import com.arsdigita.bebop.BoxPanel;
import com.arsdigita.bebop.Component; import com.arsdigita.bebop.Component;
import com.arsdigita.bebop.ControlLink; import com.arsdigita.bebop.ControlLink;
@ -51,6 +50,7 @@ import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.configuration.ConfigurationManager; import org.libreccm.configuration.ConfigurationManager;
import org.libreccm.l10n.LocalizedString; import org.libreccm.l10n.LocalizedString;
import java.lang.reflect.Field;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -61,6 +61,10 @@ import java.util.TooManyListenersException;
import static com.arsdigita.ui.admin.AdminUiConstants.*; import static com.arsdigita.ui.admin.AdminUiConstants.*;
/** /**
* Editor for {@link LocalizedStringSetting}s. The editor consists of the usual
* header (see {@link SettingFormHeader}) which is used by all setting
* forms/editors, a table which displays all current values together with links
* for editing and deleting the values and a form for adding and editing values.
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
@ -92,15 +96,9 @@ public class SettingEditorLocalizedString extends BoxPanel {
this.selectedSetting = selectedSetting; this.selectedSetting = selectedSetting;
this.selectedValue = selectedValue; this.selectedValue = selectedValue;
final ActionLink backLink = new ActionLink(new GlobalizedMessage( add(new SettingFormHeader(configurationTab,
"ui.admin.configuration.setting.localized_string.back", selectedConf,
ADMIN_BUNDLE)); selectedSetting));
backLink.addActionListener(e -> {
configurationTab.hideSettingForms(e.getPageState());
});
add(backLink);
add(new SettingFormHeader(selectedConf, selectedSetting));
add(new ValuesTable()); add(new ValuesTable());
@ -134,7 +132,7 @@ public class SettingEditorLocalizedString extends BoxPanel {
columnModel.add(new TableColumn( columnModel.add(new TableColumn(
COL_EDIT, COL_EDIT,
new Label(new GlobalizedMessage( new Label(new GlobalizedMessage(
"ui.admin.configuration.setting.localized_string.col_del", "ui.admin.configuration.setting.localized_string.col_edit",
ADMIN_BUNDLE)))); ADMIN_BUNDLE))));
columnModel.add(new TableColumn( columnModel.add(new TableColumn(
COL_DEL, COL_DEL,
@ -212,19 +210,21 @@ public class SettingEditorLocalizedString extends BoxPanel {
final LocalizedString localizedStr; final LocalizedString localizedStr;
try { try {
final Object value = confClass.getField( final Field field = confClass.getDeclaredField(
selectedSetting selectedSetting.getSelectedKey(state));
.getSelectedKey(state)).get(config); field.setAccessible(true);
final Object value = field.get(config);
if (value == null) { if (value == null) {
localizedStr = new LocalizedString(); localizedStr = new LocalizedString();
confClass.getField(selectedSetting field.set(config, localizedStr);
.getSelectedKey(
state)).set(config, localizedStr);
} else { } else {
localizedStr = (LocalizedString) value; localizedStr = (LocalizedString) value;
} }
} catch (NoSuchFieldException | SecurityException | IllegalAccessException | ClassCastException ex) { } catch (NoSuchFieldException |
SecurityException |
IllegalAccessException |
ClassCastException ex) {
LOGGER.warn( LOGGER.warn(
"Failed to read setting {} from configuration {}", "Failed to read setting {} from configuration {}",
selectedSetting.getSelectedKey(state), selectedSetting.getSelectedKey(state),
@ -294,16 +294,20 @@ public class SettingEditorLocalizedString extends BoxPanel {
final Object config = confManager.findConfiguration(confClass); final Object config = confManager.findConfiguration(confClass);
try { try {
value = (LocalizedString) confClass.getField(selectedSetting final Field field = confClass.getDeclaredField(selectedSetting
.getSelectedKey(state)).get(config); .getSelectedKey(state));
field.setAccessible(true);
value = (LocalizedString) field.get(config);
locales = new ArrayList<>(); locales = new ArrayList<>();
locales.addAll(value.getAvailableLocales()); locales.addAll(value.getAvailableLocales());
locales.sort((s1, s2) -> { locales.sort((s1, s2) -> {
return s1.toString().compareTo(s2.toString()); return s1.toString().compareTo(s2.toString());
}); });
} catch (NoSuchFieldException | SecurityException | } catch (NoSuchFieldException |
IllegalAccessException | ClassCastException ex) { SecurityException |
IllegalAccessException |
ClassCastException ex) {
LOGGER.warn("Failed to read setting {} from configuration {}", LOGGER.warn("Failed to read setting {} from configuration {}",
selectedSetting.getSelectedKey(state), selectedSetting.getSelectedKey(state),
selectedConf.getSelectedKey(state)); selectedConf.getSelectedKey(state));
@ -391,10 +395,14 @@ public class SettingEditorLocalizedString extends BoxPanel {
final LocalizedString value; final LocalizedString value;
try { try {
value = (LocalizedString) confClass.getField( final Field field = confClass.getDeclaredField(
selectedSetting.getSelectedKey(state)).get( selectedSetting.getSelectedKey(state));
config); field.setAccessible(true);
} catch (NoSuchFieldException | SecurityException | IllegalAccessException | ClassCastException ex) { value = (LocalizedString) field.get(config);
} catch (NoSuchFieldException |
SecurityException |
IllegalAccessException |
ClassCastException ex) {
LOGGER.warn( LOGGER.warn(
"Failed to read setting {} from configuration {}", "Failed to read setting {} from configuration {}",
selectedSetting.getSelectedKey(state), selectedSetting.getSelectedKey(state),
@ -471,10 +479,11 @@ public class SettingEditorLocalizedString extends BoxPanel {
confClass); confClass);
try { try {
final Field field = confClass.getDeclaredField(
selectedSetting.getSelectedKey(state));
field.setAccessible(true);
final LocalizedString localizedStr final LocalizedString localizedStr
= (LocalizedString) confClass = (LocalizedString) field.get(
.getField(
selectedSetting.getSelectedKey(state)).get(
config); config);
final String value = localizedStr.getValue(new Locale( final String value = localizedStr.getValue(new Locale(
@ -497,7 +506,9 @@ public class SettingEditorLocalizedString extends BoxPanel {
addProcessListener(e -> { addProcessListener(e -> {
final PageState state = e.getPageState(); final PageState state = e.getPageState();
if (saveCancelSection.getSaveButton().isSelected(state)) { if (saveCancelSection.getSaveButton().isSelected(state)) {
final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
final Class<?> confClass; final Class<?> confClass;
@ -516,18 +527,22 @@ public class SettingEditorLocalizedString extends BoxPanel {
final LocalizedString localizedStr; final LocalizedString localizedStr;
try { try {
final Object value = confClass.getField(selectedSetting final Field field = confClass.getDeclaredField(
.getSelectedKey(state)).get(config); selectedSetting.getSelectedKey(state));
field.setAccessible(true);
final Object value = field.get(config);
if (value == null) { if (value == null) {
localizedStr = new LocalizedString(); localizedStr = new LocalizedString();
confClass.getField(selectedSetting.getSelectedKey(
state)).set(config, localizedStr); field.set(config, localizedStr);
} else { } else {
localizedStr = (LocalizedString) value; localizedStr = (LocalizedString) value;
} }
} catch (NoSuchFieldException | SecurityException | } catch (NoSuchFieldException |
IllegalAccessException | ClassCastException ex) { SecurityException |
IllegalAccessException |
ClassCastException ex) {
LOGGER.warn( LOGGER.warn(
"Failed to read setting {} from configuration {}", "Failed to read setting {} from configuration {}",
selectedSetting.getSelectedKey(state), selectedSetting.getSelectedKey(state),
@ -545,6 +560,10 @@ public class SettingEditorLocalizedString extends BoxPanel {
confManager.saveConfiguration(config); confManager.saveConfiguration(config);
} }
selectedValue.clearSelection(state);
localeSelect.setValue(state, null);
localizedValue.setValue(state, null);
}); });
} }
@ -553,8 +572,6 @@ public class SettingEditorLocalizedString extends BoxPanel {
if (super.isVisible(state)) { if (super.isVisible(state)) {
if (selectedValue.getSelectedKey(state) == null) { if (selectedValue.getSelectedKey(state) == null) {
return true;
} else {
final ConfigurationManager confManager = CdiUtil final ConfigurationManager confManager = CdiUtil
.createCdiUtil() .createCdiUtil()
.findBean(ConfigurationManager.class); .findBean(ConfigurationManager.class);
@ -572,10 +589,14 @@ public class SettingEditorLocalizedString extends BoxPanel {
final LocalizedString value; final LocalizedString value;
try { try {
value = (LocalizedString) confClass.getField( final Field field = confClass.getDeclaredField(
selectedSetting.getSelectedKey(state)).get( selectedSetting.getSelectedKey(state));
config); field.setAccessible(true);
} catch (NoSuchFieldException | SecurityException | IllegalAccessException | ClassCastException ex) { value = (LocalizedString) field.get(config);
} catch (NoSuchFieldException |
SecurityException |
IllegalAccessException |
ClassCastException ex) {
LOGGER.warn( LOGGER.warn(
"Failed to read setting {} from configuration {}", "Failed to read setting {} from configuration {}",
selectedSetting.getSelectedKey(state), selectedSetting.getSelectedKey(state),
@ -597,6 +618,8 @@ public class SettingEditorLocalizedString extends BoxPanel {
}); });
return !assignedLanguages.equals(supportedLanguages); return !assignedLanguages.equals(supportedLanguages);
} else {
return true;
} }
} else { } else {

View File

@ -18,7 +18,6 @@
*/ */
package com.arsdigita.ui.admin.configuration; package com.arsdigita.ui.admin.configuration;
import com.arsdigita.bebop.ActionLink;
import com.arsdigita.bebop.BoxPanel; import com.arsdigita.bebop.BoxPanel;
import com.arsdigita.bebop.Component; import com.arsdigita.bebop.Component;
import com.arsdigita.bebop.ControlLink; import com.arsdigita.bebop.ControlLink;
@ -46,13 +45,19 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.libreccm.cdi.utils.CdiUtil; import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.configuration.ConfigurationManager; import org.libreccm.configuration.ConfigurationManager;
import org.libreccm.configuration.EnumSetting;
import java.lang.reflect.Field;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static com.arsdigita.ui.admin.AdminUiConstants.*; import static com.arsdigita.ui.admin.AdminUiConstants.*;
/** /**
* Editor for {@link EnumSetting}s. The editor consists of the usual header (see
* {@link SettingFormHeader}) which is used by all setting forms/editors, a
* table which displays all current values together with links for moving,
* editing and deleting the values and a form for adding and editing values.
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
@ -85,15 +90,9 @@ public class SettingEditorStringList extends BoxPanel {
this.selectedSetting = selectedSetting; this.selectedSetting = selectedSetting;
this.selectedValue = selectedValue; this.selectedValue = selectedValue;
final ActionLink backLink = new ActionLink(new GlobalizedMessage( add(new SettingFormHeader(configurationTab,
"ui.admin.configuration.setting.localized_string.back", selectedConf,
ADMIN_BUNDLE)); selectedSetting));
backLink.addActionListener(e -> {
configurationTab.hideSettingForms(e.getPageState());
});
add(backLink);
add(new SettingFormHeader(selectedConf, selectedSetting));
add(new ValuesTable()); add(new ValuesTable());
@ -153,7 +152,7 @@ public class SettingEditorStringList extends BoxPanel {
if (row > 0) { if (row > 0) {
return new ControlLink((Component) value); return new ControlLink((Component) value);
} else { } else {
return null; return new Text("");
} }
} }
@ -162,6 +161,7 @@ public class SettingEditorStringList extends BoxPanel {
columnModel.get(COL_DOWN).setCellRenderer(new TableCellRenderer() { columnModel.get(COL_DOWN).setCellRenderer(new TableCellRenderer() {
@Override @Override
@SuppressWarnings("unchecked")
public Component getComponent(final Table table, public Component getComponent(final Table table,
final PageState state, final PageState state,
final Object value, final Object value,
@ -188,9 +188,14 @@ public class SettingEditorStringList extends BoxPanel {
final List<String> values; final List<String> values;
try { try {
values = (List<String>) confClass.getField( final Field field = confClass.getDeclaredField(
selectedSetting.getSelectedKey(state)).get(config); selectedSetting.getSelectedKey(state));
} catch (NoSuchFieldException | SecurityException | IllegalAccessException | ClassCastException ex) { field.setAccessible(true);
values = (List<String>) field.get(config);
} catch (NoSuchFieldException |
SecurityException |
IllegalAccessException |
ClassCastException ex) {
LOGGER.warn( LOGGER.warn(
"Failed to read setting {} from configuration {}", "Failed to read setting {} from configuration {}",
selectedSetting.getSelectedKey(state), selectedSetting.getSelectedKey(state),
@ -203,7 +208,7 @@ public class SettingEditorStringList extends BoxPanel {
if (row < values.size()) { if (row < values.size()) {
return new ControlLink((Component) value); return new ControlLink((Component) value);
} else { } else {
return null; return new Text("");
} }
} }
@ -251,6 +256,7 @@ public class SettingEditorStringList extends BoxPanel {
addTableActionListener(new TableActionListener() { addTableActionListener(new TableActionListener() {
@Override @Override
@SuppressWarnings("unchecked")
public void cellSelected(final TableActionEvent event) { public void cellSelected(final TableActionEvent event) {
final PageState state = event.getPageState(); final PageState state = event.getPageState();
@ -273,8 +279,10 @@ public class SettingEditorStringList extends BoxPanel {
final List<String> values; final List<String> values;
try { try {
values = (List<String>) confClass.getField( final Field field = confClass.getDeclaredField(
selectedSetting.getSelectedKey(state)).get(config); selectedSetting.getSelectedKey(state));
field.setAccessible(true);
values = (List<String>) field.get(config);
} catch (NoSuchFieldException | SecurityException | } catch (NoSuchFieldException | SecurityException |
IllegalAccessException | ClassCastException ex) { IllegalAccessException | ClassCastException ex) {
LOGGER.warn( LOGGER.warn(
@ -289,7 +297,8 @@ public class SettingEditorStringList extends BoxPanel {
switch (event.getColumn()) { switch (event.getColumn()) {
case COL_UP: { case COL_UP: {
final int currentIndex = (int) event.getRowKey(); final int currentIndex = Integer.parseInt(
(String) event.getRowKey());
final int previousIndex = currentIndex - 1; final int previousIndex = currentIndex - 1;
final String currentValue = values.get(currentIndex); final String currentValue = values.get(currentIndex);
@ -305,7 +314,8 @@ public class SettingEditorStringList extends BoxPanel {
} }
case COL_DOWN: { case COL_DOWN: {
final int currentIndex = (int) event.getRowKey(); final int currentIndex = Integer.parseInt(
(String) event.getRowKey());
final int nextIndex = currentIndex + 1; final int nextIndex = currentIndex + 1;
final String currentValue = values.get(currentIndex); final String currentValue = values.get(currentIndex);
@ -324,7 +334,8 @@ public class SettingEditorStringList extends BoxPanel {
break; break;
} }
case COL_DEL: { case COL_DEL: {
values.remove((int) event.getRowKey()); values.remove(Integer.parseInt((String) event
.getRowKey()));
confManager.saveConfiguration(config); confManager.saveConfiguration(config);
@ -365,6 +376,7 @@ public class SettingEditorStringList extends BoxPanel {
private final List<String> values; private final List<String> values;
private int index = -1; private int index = -1;
@SuppressWarnings("unchecked")
public ValuesTableModel(final PageState state) { public ValuesTableModel(final PageState state) {
final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
@ -383,10 +395,14 @@ public class SettingEditorStringList extends BoxPanel {
final Object config = confManager.findConfiguration(confClass); final Object config = confManager.findConfiguration(confClass);
try { try {
values = (List<String>) confClass.getField(selectedSetting final Field field = confClass.getDeclaredField(selectedSetting
.getSelectedKey(state)).get(config); .getSelectedKey(state));
} catch (NoSuchFieldException | SecurityException | field.setAccessible(true);
IllegalAccessException | ClassCastException ex) { values = (List<String>) field.get(config);
} catch (NoSuchFieldException |
SecurityException |
IllegalAccessException |
ClassCastException ex) {
LOGGER.warn("Failed to read setting {} from configuration {}", LOGGER.warn("Failed to read setting {} from configuration {}",
selectedSetting.getSelectedKey(state), selectedSetting.getSelectedKey(state),
selectedConf.getSelectedKey(state)); selectedConf.getSelectedKey(state));
@ -480,8 +496,10 @@ public class SettingEditorStringList extends BoxPanel {
try { try {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final List<String> stringList = (List<String>) confClass final Field field = confClass.getDeclaredField(
.getField(selectedSetting.getSelectedKey(state)) selectedSetting.getSelectedKey(state));
field.setAccessible(true);
final List<String> stringList = (List<String>) field
.get(config); .get(config);
final String str = stringList.get(Integer.parseInt( final String str = stringList.get(Integer.parseInt(
@ -525,18 +543,21 @@ public class SettingEditorStringList extends BoxPanel {
final List<String> stringList; final List<String> stringList;
try { try {
final Object value = confClass.getField(selectedSetting final Field field = confClass.getDeclaredField(
.getSelectedKey(state)).get(config); selectedSetting.getSelectedKey(state));
field.setAccessible(true);
final Object value = field.get(config);
if (value == null) { if (value == null) {
stringList = new ArrayList<>(); stringList = new ArrayList<>();
confClass.getField(selectedSetting.getSelectedKey( field.set(config, stringList);
state)).set(config, stringList);
} else { } else {
stringList = (List<String>) value; stringList = (List<String>) value;
} }
} catch (NoSuchFieldException | SecurityException | } catch (NoSuchFieldException |
IllegalAccessException | ClassCastException ex) { SecurityException |
IllegalAccessException |
ClassCastException ex) {
LOGGER.warn( LOGGER.warn(
"Failed to read setting {} from configuration {}", "Failed to read setting {} from configuration {}",
selectedSetting.getSelectedKey(state), selectedSetting.getSelectedKey(state),
@ -559,6 +580,9 @@ public class SettingEditorStringList extends BoxPanel {
confManager.saveConfiguration(config); confManager.saveConfiguration(config);
} }
selectedValue.clearSelection(state);
valueField.setValue(state, null);
}); });
} }

View File

@ -20,9 +20,13 @@ package com.arsdigita.ui.admin.configuration;
import com.arsdigita.bebop.ParameterSingleSelectionModel; import com.arsdigita.bebop.ParameterSingleSelectionModel;
import org.libreccm.configuration.BigDecimalSetting;
import java.math.BigDecimal; import java.math.BigDecimal;
/** /**
* A subclass of {@link AbstractSettingFormSingleValue} for editing the value
* of a {@link BigDecimalSetting}.
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */

View File

@ -41,6 +41,8 @@ import org.libreccm.configuration.ConfigurationManager;
import static com.arsdigita.ui.admin.AdminUiConstants.*; import static com.arsdigita.ui.admin.AdminUiConstants.*;
/** /**
* A subclass of {@link AbstractSettingFormSingleValue} for editing the value
* of a {@code boolean}.
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
@ -59,7 +61,9 @@ public class SettingFormBoolean extends Form {
super("settingFormBoolean", new BoxPanel(BoxPanel.VERTICAL)); super("settingFormBoolean", new BoxPanel(BoxPanel.VERTICAL));
add(new SettingFormHeader(selectedConf, selectedSetting)); add(new SettingFormHeader(configurationTab,
selectedConf,
selectedSetting));
add(new SettingFormCurrentValuePanel(selectedConf, selectedSetting)); add(new SettingFormCurrentValuePanel(selectedConf, selectedSetting));
@ -70,6 +74,7 @@ public class SettingFormBoolean extends Form {
new Label(new GlobalizedMessage( new Label(new GlobalizedMessage(
"ui.admin.configuration.setting.edit.new_value", "ui.admin.configuration.setting.edit.new_value",
ADMIN_BUNDLE)))); ADMIN_BUNDLE))));
add(valueFieldGroup);
final SaveCancelSection saveCancelSection = new SaveCancelSection(); final SaveCancelSection saveCancelSection = new SaveCancelSection();
add(saveCancelSection); add(saveCancelSection);
@ -93,10 +98,14 @@ public class SettingFormBoolean extends Form {
final Boolean value; final Boolean value;
try { try {
value = (Boolean) confClass.getField(selectedSetting final Field field = confClass.getDeclaredField(selectedSetting
.getSelectedKey(state)).get(config); .getSelectedKey(state));
} catch (NoSuchFieldException | SecurityException | field.setAccessible(true);
IllegalAccessException | ClassCastException ex) { value = (Boolean) field.get(config);
} catch (NoSuchFieldException |
SecurityException |
IllegalAccessException |
ClassCastException ex) {
LOGGER.warn("Failed to read setting {} from configuration {}", LOGGER.warn("Failed to read setting {} from configuration {}",
selectedSetting.getSelectedKey(state), selectedSetting.getSelectedKey(state),
selectedConf.getSelectedKey(state)); selectedConf.getSelectedKey(state));
@ -111,6 +120,7 @@ public class SettingFormBoolean extends Form {
addProcessListener(e -> { addProcessListener(e -> {
final PageState state = e.getPageState(); final PageState state = e.getPageState();
if (saveCancelSection.getSaveButton().isSelected(state)) {
final FormData data = e.getFormData(); final FormData data = e.getFormData();
final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
@ -130,8 +140,10 @@ public class SettingFormBoolean extends Form {
final Field field; final Field field;
try { try {
field = confClass.getField(settingName); field = confClass.getDeclaredField(settingName);
} catch (NoSuchFieldException | SecurityException ex) { field.setAccessible(true);
} catch (NoSuchFieldException |
SecurityException ex) {
throw new FormProcessException( throw new FormProcessException(
String.format( String.format(
"Failed to retrieve field \"%s\" " "Failed to retrieve field \"%s\" "
@ -153,9 +165,13 @@ public class SettingFormBoolean extends Form {
} else { } else {
field.set(config, Boolean.FALSE); field.set(config, Boolean.FALSE);
} }
configurationTab.hideSettingForms(state); } else {
field.set(config, Boolean.FALSE);
} }
confManager.saveConfiguration(config);
configurationTab.hideSettingForms(state);
} catch (IllegalArgumentException | IllegalAccessException ex) { } catch (IllegalArgumentException | IllegalAccessException ex) {
LOGGER.error(ex);
throw new FormProcessException( throw new FormProcessException(
String.format( String.format(
"Failed to change value of field \"%s\" " "Failed to change value of field \"%s\" "
@ -167,7 +183,9 @@ public class SettingFormBoolean extends Form {
ADMIN_BUNDLE), ADMIN_BUNDLE),
ex); ex);
} }
}
configurationTab.hideSettingForms(state);
}); });
} }

View File

@ -25,15 +25,20 @@ import com.arsdigita.bebop.ParameterSingleSelectionModel;
import com.arsdigita.bebop.Text; import com.arsdigita.bebop.Text;
import com.arsdigita.globalization.GlobalizedMessage; import com.arsdigita.globalization.GlobalizedMessage;
import com.arsdigita.util.UncheckedWrapperException; import com.arsdigita.util.UncheckedWrapperException;
import java.util.Objects; import java.util.Objects;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.libreccm.cdi.utils.CdiUtil; import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.configuration.ConfigurationManager; import org.libreccm.configuration.ConfigurationManager;
import java.lang.reflect.Field;
import static com.arsdigita.ui.admin.AdminUiConstants.*; import static com.arsdigita.ui.admin.AdminUiConstants.*;
/** /**
* A panel which show the current value of a (single value) setting.
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
@ -72,9 +77,12 @@ public class SettingFormCurrentValuePanel extends GridPanel {
final Object value; final Object value;
try { try {
value = confClass.getField(selectedSetting final Field field = confClass.getDeclaredField(selectedSetting
.getSelectedKey(state)).get(config); .getSelectedKey(state));
} catch (NoSuchFieldException | SecurityException | field.setAccessible(true);
value = field.get(config);
} catch (NoSuchFieldException |
SecurityException |
IllegalAccessException ex) { IllegalAccessException ex) {
LOGGER.warn("Failed to read setting {} from configuration {}", LOGGER.warn("Failed to read setting {} from configuration {}",
selectedSetting.getSelectedKey(state), selectedSetting.getSelectedKey(state),

View File

@ -21,6 +21,8 @@ package com.arsdigita.ui.admin.configuration;
import com.arsdigita.bebop.ParameterSingleSelectionModel; import com.arsdigita.bebop.ParameterSingleSelectionModel;
/** /**
* A subclass of {@link AbstractSettingFormSingleValue} for editing the value
* of a {@link DoubleSetting}.
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */

View File

@ -18,6 +18,7 @@
*/ */
package com.arsdigita.ui.admin.configuration; package com.arsdigita.ui.admin.configuration;
import com.arsdigita.bebop.ActionLink;
import com.arsdigita.bebop.BoxPanel; import com.arsdigita.bebop.BoxPanel;
import com.arsdigita.bebop.Label; import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.PageState; import com.arsdigita.bebop.PageState;
@ -38,17 +39,29 @@ import org.libreccm.l10n.GlobalizationHelper;
import static com.arsdigita.ui.admin.AdminUiConstants.*; import static com.arsdigita.ui.admin.AdminUiConstants.*;
/** /**
* Header used by all setting forms/editors. The header contains a link to go
* back to the list of settings and heading for contains the title of the
* configuration class and the label of the setting which is edited.
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
public class SettingFormHeader extends BoxPanel { public class SettingFormHeader extends BoxPanel {
public SettingFormHeader( public SettingFormHeader(
final ConfigurationTab configurationTab,
final ParameterSingleSelectionModel<String> selectedConf, final ParameterSingleSelectionModel<String> selectedConf,
final ParameterSingleSelectionModel<String> selectedSetting) { final ParameterSingleSelectionModel<String> selectedSetting) {
super(BoxPanel.VERTICAL); super(BoxPanel.VERTICAL);
final ActionLink backLink = new ActionLink(new GlobalizedMessage(
"ui.admin.configuration.setting.edit.back", ADMIN_BUNDLE));
backLink.addActionListener(e -> {
final PageState state = e.getPageState();
configurationTab.hideSettingForms(state);
});
add(backLink);
final Label heading = new Label(new HeadingPrintListener( final Label heading = new Label(new HeadingPrintListener(
selectedConf, selectedSetting)); selectedConf, selectedSetting));
heading.setClassAttr("heading"); heading.setClassAttr("heading");

View File

@ -21,6 +21,8 @@ package com.arsdigita.ui.admin.configuration;
import com.arsdigita.bebop.ParameterSingleSelectionModel; import com.arsdigita.bebop.ParameterSingleSelectionModel;
/** /**
* A subclass of {@link AbstractSettingFormSingleValue} for editing the value
* of a {@link LongSetting}.
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */

View File

@ -21,6 +21,8 @@ package com.arsdigita.ui.admin.configuration;
import com.arsdigita.bebop.ParameterSingleSelectionModel; import com.arsdigita.bebop.ParameterSingleSelectionModel;
/** /**
* A subclass of {@link AbstractSettingFormSingleValue} for editing the value
* of a {@link StringSetting}.
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */

View File

@ -60,13 +60,16 @@ public abstract class AbstractSetting<T> implements Serializable {
private static final long serialVersionUID = 1631163618980178142L; private static final long serialVersionUID = 1631163618980178142L;
/**
* Database ID of the setting (primary key).
*/
@Column(name = "SETTING_ID") @Column(name = "SETTING_ID")
@Id @Id
@GeneratedValue(strategy = GenerationType.AUTO) @GeneratedValue(strategy = GenerationType.AUTO)
private long settingId; private long settingId;
/** /**
* This configuration class this setting belongs to * This configuration class this setting belongs to.
*/ */
@Column(name = "CONFIGURATION_CLASS", nullable = false, length = 512) @Column(name = "CONFIGURATION_CLASS", nullable = false, length = 512)
@NotBlank @NotBlank

View File

@ -25,6 +25,8 @@ import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
/** /**
* Setting for values of type {@link BigDecimal}. Use this for rational numbers
* if precision is required.
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */

View File

@ -23,6 +23,7 @@ import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
/** /**
* Setting of boolean values.
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */

View File

@ -19,9 +19,8 @@
package org.libreccm.configuration; package org.libreccm.configuration;
import org.libreccm.configuration.*;
/** /**
* Exception type which can be used in configuration classes to indicate errors.
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
@ -36,7 +35,6 @@ public class ConfigurationException extends RuntimeException {
super(); super();
} }
/** /**
* Constructs an instance of <code>ConfigurationException</code> with the specified detail message. * Constructs an instance of <code>ConfigurationException</code> with the specified detail message.
* *

View File

@ -55,7 +55,8 @@ public class ConfigurationManager {
/** /**
* Finds all configuration classes listed by the installed modules. * Finds all configuration classes listed by the installed modules.
* *
* @return A sorted set containing all configuration classes. * @return A sorted set (see {@link SortedSet}) containing all configuration
* classes.
* *
* @see Module#configurations() * @see Module#configurations()
*/ */
@ -256,43 +257,6 @@ public class ConfigurationManager {
} }
} }
/**
* Create a setting instance of a specific value type.
*
* @param <T> Type variable.
* @param valueType The type of the value of the setting to create.
*
* @return An setting instance of the provided value type.
*
* @throws IllegalArgumentException If there is not setting type for the
* provided value type.
*/
// @SuppressWarnings("unchecked")
// <T> AbstractSetting<T> createSettingForValueType(
// final Class<T> valueType) {
//
// final String valueTypeName = valueType.getName();
// if (BigDecimal.class.getName().equals(valueTypeName)) {
// return (AbstractSetting<T>) new BigDecimalSetting();
// } else if (Boolean.class.getName().equals(valueTypeName)) {
// return (AbstractSetting<T>) new BooleanSetting();
// } else if (Double.class.getName().equals(valueTypeName)) {
// return (AbstractSetting<T>) new DoubleSetting();
// } else if (List.class.getName().equals(valueTypeName)) {
// return (AbstractSetting<T>) new StringListSetting();
// } else if (LocalizedString.class.getName().equals(valueTypeName)) {
// return (AbstractSetting<T>) new LocalizedStringSetting();
// } else if (Long.class.getName().equals(valueTypeName)) {
// return (AbstractSetting<T>) new LongSetting();
// } else if (Set.class.getName().equals(valueTypeName)) {
// return (AbstractSetting<T>) new EnumSetting();
// } else if (String.class.getName().equals(valueTypeName)) {
// return (AbstractSetting<T>) new StringSetting();
// } else {
// throw new IllegalArgumentException(String.format(
// "No setting type for value type \"%s\".", valueTypeName));
// }
// }
/** /**
* Sets a value on a setting in the registry. * Sets a value on a setting in the registry.
* *

View File

@ -19,10 +19,16 @@
package org.libreccm.configuration; package org.libreccm.configuration;
import java.io.Serializable; import java.io.Serializable;
import java.math.BigDecimal;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
/** /**
* Setting for values for type double (rational numbers). Be aware: Because how
* double values are handled by Java (and all other programming languages) the
* precision of a value of the type {@code double} can be guaranteed. If full
* precision is required use {@link BigDecimal} and {@link BigDecimalSetting}.
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */

View File

@ -29,11 +29,12 @@ import java.util.Objects;
import javax.persistence.ElementCollection; import javax.persistence.ElementCollection;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn; import javax.persistence.JoinColumn;
import javax.persistence.JoinTable; import javax.persistence.JoinTable;
/** /**
* A setting class for storing a list a strings. This can be used to generate * A setting class for storing a set of strings. This can be used to generate
* enums which can be configured by the administrator. * enums which can be configured by the administrator.
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
@ -44,7 +45,7 @@ public class EnumSetting
private static final long serialVersionUID = 1763168269981687340L; private static final long serialVersionUID = 1763168269981687340L;
@ElementCollection @ElementCollection(fetch = FetchType.EAGER)
@JoinTable(name = "SETTINGS_ENUM_VALUES", @JoinTable(name = "SETTINGS_ENUM_VALUES",
schema = DB_SCHEMA, schema = DB_SCHEMA,
joinColumns = { joinColumns = {

View File

@ -0,0 +1,219 @@
/*
* 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 org.libreccm.configuration;
import org.libreccm.l10n.LocalizedString;
import java.math.BigDecimal;
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.Set;
/**
* Example of configuration with all available setting types. Not used anywhere
* else, only for testing.
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@Configuration
public final class ExampleConfiguration {
@Setting
private boolean enabled = false;
@Setting
private long itemsPerPage = 42;
@Setting
private double minTemperature = 23.42;
@Setting
private BigDecimal price = new BigDecimal("10.42");
@Setting
private String hostname = "srv-01.example.org";
@Setting
private LocalizedString title = new LocalizedString();
@Setting
private List<String> components = Arrays.asList(new String[]{"component1",
"component2",
"component3"});
@Setting
private Set<String> supportedLanguages = new HashSet<>(Arrays.asList(
new String[]{"en", "de"}));
public ExampleConfiguration() {
title.addValue(Locale.GERMAN, "Dies ist ein Test");
title.addValue(Locale.ENGLISH, "This is a test");
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(final boolean enabled) {
this.enabled = enabled;
}
public long getItemsPerPage() {
return itemsPerPage;
}
public void setItemsPerPage(final long itemsPerPage) {
this.itemsPerPage = itemsPerPage;
}
public double getMinTemperature() {
return minTemperature;
}
public void setMinTemperature(final double minTemperature) {
this.minTemperature = minTemperature;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(final BigDecimal price) {
this.price = price;
}
public String getHostname() {
return hostname;
}
public void setHostname(final String hostname) {
this.hostname = hostname;
}
public LocalizedString getTitle() {
return title;
}
public void setTitle(LocalizedString title) {
this.title = title;
}
public List<String> getComponents() {
return Collections.unmodifiableList(components);
}
public void setComponents(final List<String> components) {
this.components = components;
}
public Set<String> getSupportedLanguages() {
return Collections.unmodifiableSet(supportedLanguages);
}
public void setSupportedLanguages(final Set<String> supportedLanguages) {
this.supportedLanguages = supportedLanguages;
}
@Override
public int hashCode() {
int hash = 3;
if (enabled) {
hash = 13 * hash + 1;
} else {
hash = 13 * hash;
}
hash
= 13 * hash + (int) (itemsPerPage ^ (itemsPerPage >>> 32));
hash
= 13 * hash + (int) (Double.doubleToLongBits(minTemperature)
^ (Double.doubleToLongBits(minTemperature)
>>> 32));
hash = 13 * hash + Objects.hashCode(price);
hash = 13 * hash + Objects.hashCode(hostname);
hash = 13 * hash + Objects.hashCode(title);
hash = 13 * hash + Objects.hashCode(components);
hash = 13 * hash + Objects.hashCode(supportedLanguages);
return hash;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof ExampleConfiguration)) {
return false;
}
final ExampleConfiguration other = (ExampleConfiguration) obj;
if (enabled != other.isEnabled()) {
return false;
}
if (itemsPerPage != other.getItemsPerPage()) {
return false;
}
if (Double.doubleToLongBits(minTemperature) != Double.doubleToLongBits(
other.getMinTemperature())) {
return false;
}
if (!Objects.equals(hostname, other.getHostname())) {
return false;
}
if (!Objects.equals(price, other.getPrice())) {
return false;
}
if (!Objects.equals(title, other.getTitle())) {
return false;
}
if (!Objects.equals(components, other.getComponents())) {
return false;
}
return Objects.equals(supportedLanguages, other.getSupportedLanguages());
}
@Override
public String toString() {
return String.format("%s{ "
+ "enabled = %b, "
+ "itemsPerPage = %d, "
+ "minTemperature = %f, "
+ "hostname = %s, "
+ "price = %f, "
+ "title = %s, "
+ "components = %s, "
+ "supportedLanguages = %s"
+ " }",
super.toString(),
enabled,
itemsPerPage,
minTemperature,
hostname,
price,
Objects.toString(title),
Objects.toString(components),
Objects.toString(supportedLanguages));
}
}

View File

@ -34,7 +34,7 @@ import javax.persistence.JoinTable;
/** /**
* A setting which stores a {@link LocalizedString} . This can be used for * A setting which stores a {@link LocalizedString} . This can be used for
* storing values for text in the user interface which should be customisable by * storing values for text in the user interface which should be customisable by
* the administrator. * the administrator. It is also useful for storing email templates.
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */

View File

@ -18,13 +18,11 @@
*/ */
package org.libreccm.configuration; package org.libreccm.configuration;
import static org.libreccm.core.CoreConstants.*;
import java.io.Serializable; import java.io.Serializable;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.Table;
/** /**
* Setting for storing a long value. * Setting for storing a long value.

View File

@ -18,7 +18,6 @@
*/ */
package org.libreccm.configuration; package org.libreccm.configuration;
import org.libreccm.configuration.*;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;

View File

@ -18,6 +18,8 @@
*/ */
package org.libreccm.configuration; package org.libreccm.configuration;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.libreccm.l10n.LocalizedString; import org.libreccm.l10n.LocalizedString;
import java.math.BigDecimal; import java.math.BigDecimal;
@ -29,24 +31,35 @@ import java.util.Set;
import javax.enterprise.context.RequestScoped; import javax.enterprise.context.RequestScoped;
/** /**
* Helper class for converting values to settings. * Helper class for converting values to settings of the appropriate type.
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
@RequestScoped @RequestScoped
class SettingConverter { class SettingConverter {
private static final Logger LOGGER = LogManager.getLogger(
SettingConverter.class);
/**
* Stores a map of the Java types and the corresponding subtypes
* {@link AbstractSetting}. We are using a map here to avoid a large if or
* switch statement.
*/
private final Map<String, Class<? extends AbstractSetting<?>>> typeMap; private final Map<String, Class<? extends AbstractSetting<?>>> typeMap;
public SettingConverter() { public SettingConverter() {
typeMap = new HashMap<>(); typeMap = new HashMap<>();
typeMap.put(BigDecimal.class.getName(), BigDecimalSetting.class); typeMap.put(BigDecimal.class.getName(), BigDecimalSetting.class);
typeMap.put(Boolean.class.getName(), BooleanSetting.class); typeMap.put(Boolean.class.getName(), BooleanSetting.class);
typeMap.put("boolean", BooleanSetting.class);
typeMap.put(Double.class.getName(), DoubleSetting.class); typeMap.put(Double.class.getName(), DoubleSetting.class);
typeMap.put("double", DoubleSetting.class);
typeMap.put(List.class.getName(), StringListSetting.class); typeMap.put(List.class.getName(), StringListSetting.class);
typeMap.put(LocalizedString.class.getName(), typeMap.put(LocalizedString.class.getName(),
LocalizedStringSetting.class); LocalizedStringSetting.class);
typeMap.put(Long.class.getName(), LongSetting.class); typeMap.put(Long.class.getName(), LongSetting.class);
typeMap.put("long", LongSetting.class);
typeMap.put(Set.class.getName(), EnumSetting.class); typeMap.put(Set.class.getName(), EnumSetting.class);
typeMap.put(String.class.getName(), StringSetting.class); typeMap.put(String.class.getName(), StringSetting.class);
} }
@ -70,12 +83,15 @@ class SettingConverter {
valueTypeName); valueTypeName);
if (clazz == null) { if (clazz == null) {
LOGGER.error("No setting type for value type \"{}\".",
valueTypeName);
throw new IllegalArgumentException(String.format( throw new IllegalArgumentException(String.format(
"No setting type for value type \"%s\".", valueTypeName)); "No setting type for value type \"%s\".", valueTypeName));
} else { } else {
try { try {
return (AbstractSetting<T>) clazz.newInstance(); return (AbstractSetting<T>) clazz.newInstance();
} catch (InstantiationException | IllegalAccessException ex) { } catch (InstantiationException | IllegalAccessException ex) {
LOGGER.error("Failed to create setting instance.", ex);
throw new IllegalStateException( throw new IllegalStateException(
"Failed to create setting instance.", ex); "Failed to create setting instance.", ex);
} }

View File

@ -192,6 +192,8 @@ public class SettingManager {
* @return The requested setting if it exists in the registry, {@code null} * @return The requested setting if it exists in the registry, {@code null}
* otherwise. * otherwise.
*/ */
@Transactional(Transactional.TxType.REQUIRED)
@SuppressWarnings("unchecked")
public <T> AbstractSetting<T> findSetting(final String confName, public <T> AbstractSetting<T> findSetting(final String confName,
final String name, final String name,
final Class<T> clazz) { final Class<T> clazz) {
@ -233,7 +235,7 @@ public class SettingManager {
if (confAnnotation.descBundle() == null if (confAnnotation.descBundle() == null
|| confAnnotation.descBundle().isEmpty()) { || confAnnotation.descBundle().isEmpty()) {
return String.join("", return String.join("",
configuration.getClass().getName(), configuration.getName(),
"Description"); "Description");
} else { } else {
return confAnnotation.descBundle(); return confAnnotation.descBundle();

View File

@ -26,10 +26,13 @@ import java.util.Objects;
import javax.persistence.ElementCollection; import javax.persistence.ElementCollection;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn; import javax.persistence.JoinColumn;
import javax.persistence.JoinTable; import javax.persistence.JoinTable;
/** /**
* Setting for a list of strings. In contrast to the {@link EnumSetting} which
* uses a {@link java.util.Set} a list maintains the order of its elements.
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
@ -38,12 +41,15 @@ public class StringListSetting extends AbstractSetting<List<String>> {
private static final long serialVersionUID = 7093818804712916413L; private static final long serialVersionUID = 7093818804712916413L;
@ElementCollection @ElementCollection(fetch = FetchType.EAGER)
@JoinTable(name = "SETTINGS_STRING_LIST", @JoinTable(name = "SETTINGS_STRING_LIST",
schema = DB_SCHEMA, schema = DB_SCHEMA,
joinColumns = {@JoinColumn(name = "LIST_ID")}) joinColumns = {@JoinColumn(name = "LIST_ID")})
private List<String> value; private List<String> value;
/**
* Returns a <em>copy</em> of the list managed by this setting.
*/
@Override @Override
public List<String> getValue() { public List<String> getValue() {
if (value == null) { if (value == null) {
@ -53,15 +59,30 @@ public class StringListSetting extends AbstractSetting<List<String>> {
} }
} }
/**
* Replaces the list managed by this setting.
*
* @param value
*/
@Override @Override
public void setValue(final List<String> value) { public void setValue(final List<String> value) {
this.value = value; this.value = value;
} }
/**
* Adds a value to the list.
*
* @param value The value to add.
*/
public void addListValue(final String value) { public void addListValue(final String value) {
this.value.add(value); this.value.add(value);
} }
/**
* Removes a value from the list.
*
* @param value the value to add.
*/
public void removeListValue(final String value) { public void removeListValue(final String value) {
this.value.remove(value); this.value.remove(value);
} }

View File

@ -18,18 +18,12 @@
*/ */
package org.libreccm.core; package org.libreccm.core;
import com.arsdigita.bebop.BebopConfig;
import com.arsdigita.kernel.KernelConfig;
import com.arsdigita.mail.MailConfig;
import com.arsdigita.notification.NotificationConfig;
import com.arsdigita.ui.admin.AdminApplicationCreator; import com.arsdigita.ui.admin.AdminApplicationCreator;
import com.arsdigita.ui.admin.AdminServlet; import com.arsdigita.ui.admin.AdminServlet;
import com.arsdigita.ui.admin.AdminApplicationSetup; import com.arsdigita.ui.admin.AdminApplicationSetup;
import com.arsdigita.ui.login.LoginApplicationCreator; import com.arsdigita.ui.login.LoginApplicationCreator;
import com.arsdigita.ui.login.LoginServlet; import com.arsdigita.ui.login.LoginServlet;
import com.arsdigita.ui.login.LoginApplicationSetup; import com.arsdigita.ui.login.LoginApplicationSetup;
import com.arsdigita.workflow.simple.WorkflowConfig;
import com.arsdigita.xml.XmlConfig;
import org.libreccm.modules.CcmModule; import org.libreccm.modules.CcmModule;
import org.libreccm.modules.InitEvent; import org.libreccm.modules.InitEvent;
@ -37,8 +31,6 @@ import org.libreccm.modules.InstallEvent;
import org.libreccm.modules.Module; import org.libreccm.modules.Module;
import org.libreccm.modules.ShutdownEvent; import org.libreccm.modules.ShutdownEvent;
import org.libreccm.modules.UnInstallEvent; import org.libreccm.modules.UnInstallEvent;
import org.libreccm.security.EmailTemplates;
import org.libreccm.security.OneTimeAuthConfig;
import org.libreccm.security.SystemUsersSetup; import org.libreccm.security.SystemUsersSetup;
import org.libreccm.web.ApplicationType; import org.libreccm.web.ApplicationType;
@ -72,6 +64,7 @@ import org.libreccm.web.ApplicationType;
com.arsdigita.workflow.simple.WorkflowConfig.class, com.arsdigita.workflow.simple.WorkflowConfig.class,
com.arsdigita.xml.XmlConfig.class, com.arsdigita.xml.XmlConfig.class,
com.arsdigita.xml.formatters.DateFormatterConfig.class, com.arsdigita.xml.formatters.DateFormatterConfig.class,
org.libreccm.configuration.ExampleConfiguration.class,
org.libreccm.security.EmailTemplates.class, org.libreccm.security.EmailTemplates.class,
org.libreccm.security.OneTimeAuthConfig.class, org.libreccm.security.OneTimeAuthConfig.class,
}) })

View File

@ -188,7 +188,6 @@ public class LocalizedString implements Serializable {
return obj instanceof LocalizedString; return obj instanceof LocalizedString;
} }
@Override @Override
public String toString() { public String toString() {
return String.format( return String.format(

View File

@ -422,7 +422,7 @@ ui.admin.configuration.settings.table.col_edit_setting.header=Edit
ui.admin.configuration.settings.edit=Edit ui.admin.configuration.settings.edit=Edit
ui.admin.configuration.settings.read_error=Failed to read setting value. ui.admin.configuration.settings.read_error=Failed to read setting value.
ui.admin.configuration.setting.edit.heading=Edit setting {0}/{1} ui.admin.configuration.setting.edit.heading=Edit setting {0}/{1}
ui.admin.configuration.setting.edit.current_value=Current value ui.admin.configuration.setting.edit.current_value=Current value:
ui.admin.configuration.setting.edit.new_value=New value ui.admin.configuration.setting.edit.new_value=New value
ui.admin.configuration.setting.error.blank=New value can't be blank. ui.admin.configuration.setting.error.blank=New value can't be blank.
ui.admin.configuration.setting.localized_string.no_values=No values for this setting. ui.admin.configuration.setting.localized_string.no_values=No values for this setting.
@ -450,3 +450,12 @@ ui.admin.configuration.setting.enum.value.edit=Edit
ui.admin.configuration.setting.enum.value.del=Delete ui.admin.configuration.setting.enum.value.del=Delete
ui.admin.configuration.setting.enum.value.label=Value ui.admin.configuration.setting.enum.value.label=Value
ui.admin.configuration.back_to_configurations=Back to list of configurations ui.admin.configuration.back_to_configurations=Back to list of configurations
ui.admin.configuration.editing_configuration=Edit configuration {0}
ui.admin.configuration.setting.edit.back=Back to list of settings
ui.admin.configuration.setting.error.incorrect_format=The provided value is not in the correct format.
ui.admin.configuration.setting.stringlist.value.label=Value
ui.admin.configuration.setting.localized_string.value.label=Value
ui.admin.configuration.setting.localized_string.col_lang=Language
ui.admin.configuration.setting.localized_string.col_value=Value
ui.admin.configuration.setting.localized_string.col_del=Delete
ui.admin.configuration.setting.localized_string.col_edit=Edit

View File

@ -425,7 +425,7 @@ ui.admin.configuration.settings.table.col_edit_setting.header=Bearbeiten
ui.admin.configuration.settings.edit=Bearbeiten ui.admin.configuration.settings.edit=Bearbeiten
ui.admin.configuration.settings.read_error=Fehler beim lesen des Parameters. ui.admin.configuration.settings.read_error=Fehler beim lesen des Parameters.
ui.admin.configuration.setting.edit.heading=Parameter {0}/{1} bearbeiten ui.admin.configuration.setting.edit.heading=Parameter {0}/{1} bearbeiten
ui.admin.configuration.setting.edit.current_value=Aktueller Wert ui.admin.configuration.setting.edit.current_value=Aktueller Wert:
ui.admin.configuration.setting.edit.new_value=Neuer Wert ui.admin.configuration.setting.edit.new_value=Neuer Wert
ui.admin.configuration.setting.error.blank=Der neue Wert darf nicht leer sein. ui.admin.configuration.setting.error.blank=Der neue Wert darf nicht leer sein.
ui.admin.configuration.setting.localized_string.no_values=Keine Werte f\u00fcr diesen Parameter. ui.admin.configuration.setting.localized_string.no_values=Keine Werte f\u00fcr diesen Parameter.
@ -453,3 +453,12 @@ ui.admin.configuration.setting.enum.value.edit=Bearbeiten
ui.admin.configuration.setting.enum.value.del=L\u00f6schen ui.admin.configuration.setting.enum.value.del=L\u00f6schen
ui.admin.configuration.setting.enum.value.label=Wert ui.admin.configuration.setting.enum.value.label=Wert
ui.admin.configuration.back_to_configurations=Zur\u00fcck zur Liste der Konfigurationen ui.admin.configuration.back_to_configurations=Zur\u00fcck zur Liste der Konfigurationen
ui.admin.configuration.editing_configuration=Konfiguration {0} bearbeiten
ui.admin.configuration.setting.edit.back=Zur\u00fcck zur Liste der Einstellungen
ui.admin.configuration.setting.error.incorrect_format=Der angegeben Wert ist nicht im korrekten Format angegeben.
ui.admin.configuration.setting.stringlist.value.label=Wert
ui.admin.configuration.setting.localized_string.value.label=Wert
ui.admin.configuration.setting.localized_string.col_lang=Sprache
ui.admin.configuration.setting.localized_string.col_value=Wert
ui.admin.configuration.setting.localized_string.col_del=L\u00f6schen
ui.admin.configuration.setting.localized_string.col_edit=Bearbeiten

View File

@ -398,7 +398,7 @@ ui.admin.configuration.settings.table.col_edit_setting.header=Edit
ui.admin.configuration.settings.edit=Edit ui.admin.configuration.settings.edit=Edit
ui.admin.configuration.settings.read_error=Failed to read setting value. ui.admin.configuration.settings.read_error=Failed to read setting value.
ui.admin.configuration.setting.edit.heading=Edit setting {0}/{1} ui.admin.configuration.setting.edit.heading=Edit setting {0}/{1}
ui.admin.configuration.setting.edit.current_value=Current value ui.admin.configuration.setting.edit.current_value=Current value:
ui.admin.configuration.setting.edit.new_value=New value ui.admin.configuration.setting.edit.new_value=New value
ui.admin.configuration.setting.error.blank=New value can't be blank. ui.admin.configuration.setting.error.blank=New value can't be blank.
ui.admin.configuration.setting.localized_string.no_values=No values for this setting. ui.admin.configuration.setting.localized_string.no_values=No values for this setting.
@ -426,3 +426,12 @@ ui.admin.configuration.setting.enum.value.edit=Edit
ui.admin.configuration.setting.enum.value.del=Delete ui.admin.configuration.setting.enum.value.del=Delete
ui.admin.configuration.setting.enum.value.label=Value ui.admin.configuration.setting.enum.value.label=Value
ui.admin.configuration.back_to_configurations=Back to list of configurations ui.admin.configuration.back_to_configurations=Back to list of configurations
ui.admin.configuration.editing_configuration=Edit configuration {0}
ui.admin.configuration.setting.edit.back=Back to list of settings
ui.admin.configuration.setting.error.incorrect_format=The provided value is not in the correct format.
ui.admin.configuration.setting.stringlist.value.label=Value
ui.admin.configuration.setting.localized_string.value.label=Value
ui.admin.configuration.setting.localized_string.col_lang=Language
ui.admin.configuration.setting.localized_string.col_value=Value
ui.admin.configuration.setting.localized_string.col_del=Delete
ui.admin.configuration.setting.localized_string.col_edit=Edit

View File

@ -389,7 +389,7 @@ ui.admin.configuration.settings.table.col_edit_setting.header=Edit
ui.admin.configuration.settings.edit=Edit ui.admin.configuration.settings.edit=Edit
ui.admin.configuration.settings.read_error=Failed to read setting value. ui.admin.configuration.settings.read_error=Failed to read setting value.
ui.admin.configuration.setting.edit.heading=Edit setting {0}/{1} ui.admin.configuration.setting.edit.heading=Edit setting {0}/{1}
ui.admin.configuration.setting.edit.current_value=Current value ui.admin.configuration.setting.edit.current_value=Current value:
ui.admin.configuration.setting.edit.new_value=New value ui.admin.configuration.setting.edit.new_value=New value
ui.admin.configuration.setting.error.blank=New value can't be blank. ui.admin.configuration.setting.error.blank=New value can't be blank.
ui.admin.configuration.setting.localized_string.no_values=No values for this setting. ui.admin.configuration.setting.localized_string.no_values=No values for this setting.
@ -417,3 +417,12 @@ ui.admin.configuration.setting.enum.value.edit=Edit
ui.admin.configuration.setting.enum.value.del=Delete ui.admin.configuration.setting.enum.value.del=Delete
ui.admin.configuration.setting.enum.value.label=Value ui.admin.configuration.setting.enum.value.label=Value
ui.admin.configuration.back_to_configurations=Back to list of configurations ui.admin.configuration.back_to_configurations=Back to list of configurations
ui.admin.configuration.editing_configuration=Edit configuration {0}
ui.admin.configuration.setting.edit.back=Back to list of settings
ui.admin.configuration.setting.error.incorrect_format=The provided value is not in the correct format.
ui.admin.configuration.setting.stringlist.value.label=Value
ui.admin.configuration.setting.localized_string.value.label=Value
ui.admin.configuration.setting.localized_string.col_lang=Language
ui.admin.configuration.setting.localized_string.col_value=Value
ui.admin.configuration.setting.localized_string.col_del=Delete
ui.admin.configuration.setting.localized_string.col_edit=Edit