Fix optimize ui maintenance window (#668)
* Optimize maintenance window UI Signed-off-by: Melanie Retter <melanie.retter@bosch-si.com> * Refactor Signed-off-by: Melanie Retter <melanie.retter@bosch-si.com> * Add new downloaded status to UI. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Changed Accordion to Tabsheet for better visualization of action types and maintanance window. Signed-off-by: Markus Block <markus.block@bosch-si.com> * Refined UI for maintenance window, refactoring Added ENTER shortcut for save button in dialog windows Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch-si.com> * Extended validation for maintenance window, refactored the maintenance window helper class Added text change listeners for the schedule and duration text fields in order to activate "save all" button Added client Locale identification for cron expression translation Moved maintenance window validation from TargetWithActionType constructor to saveAll method of assignment tab Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch-si.com> * Added SupressWarnings annotation for exception handling cases Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch-si.com> * Fixed Sonar issue: added private constructor to Maintenance schedule helper class Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch-si.com> * Sonar Issue: make utility class Maintenance Schedule final Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch-si.com> * Added Maintenance Window validation to Distribution Set and Target Management API Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch-si.com> * Added unit tests for MaintenanceScheduleHelper class Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch-si.com> * Added the license header to MaintenanceScheduleHelperTest class Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch-si.com> * Small changes after PR review Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch-si.com> * Added Id for Maintenance Window layout for UI Tests Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch-si.com>
This commit is contained in:
committed by
Dominic Schabel
parent
8fd601f8b9
commit
4c28c4d905
@@ -234,6 +234,10 @@
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.cronutils</groupId>
|
||||
<artifactId>cron-utils</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Test -->
|
||||
<dependency>
|
||||
|
||||
@@ -122,6 +122,11 @@ public class UiProperties implements Serializable {
|
||||
*/
|
||||
private String uploadView = "";
|
||||
|
||||
/**
|
||||
* Link to documentation of maintenance window view.
|
||||
*/
|
||||
private String maintenanceWindowView = "";
|
||||
|
||||
/**
|
||||
* Link to documentation of system configuration view.
|
||||
*/
|
||||
@@ -174,6 +179,10 @@ public class UiProperties implements Serializable {
|
||||
return uploadView;
|
||||
}
|
||||
|
||||
public String getMaintenanceWindowView() {
|
||||
return maintenanceWindowView;
|
||||
}
|
||||
|
||||
public void setDeploymentView(final String deploymentView) {
|
||||
this.deploymentView = deploymentView;
|
||||
}
|
||||
@@ -206,6 +215,10 @@ public class UiProperties implements Serializable {
|
||||
this.uploadView = uploadView;
|
||||
}
|
||||
|
||||
public void setMaintenanceWindowView(final String maintenanceWindowView) {
|
||||
this.maintenanceWindowView = maintenanceWindowView;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final Documentation documentation = new Documentation();
|
||||
|
||||
@@ -41,6 +41,7 @@ import com.vaadin.data.validator.NullValidator;
|
||||
import com.vaadin.event.FieldEvents.TextChangeEvent;
|
||||
import com.vaadin.event.FieldEvents.TextChangeListener;
|
||||
import com.vaadin.event.FieldEvents.TextChangeNotifier;
|
||||
import com.vaadin.event.ShortcutAction.KeyCode;
|
||||
import com.vaadin.server.FontAwesome;
|
||||
import com.vaadin.ui.AbstractComponent;
|
||||
import com.vaadin.ui.AbstractField;
|
||||
@@ -466,6 +467,7 @@ public class CommonDialogWindow extends Window {
|
||||
saveButton.addStyleName("default-color");
|
||||
addCloseListenerForSaveButton();
|
||||
saveButton.setEnabled(false);
|
||||
saveButton.setClickShortcut(KeyCode.ENTER);
|
||||
buttonsLayout.addComponent(saveButton);
|
||||
buttonsLayout.setComponentAlignment(saveButton, Alignment.MIDDLE_RIGHT);
|
||||
buttonsLayout.setExpandRatio(saveButton, 1.0F);
|
||||
|
||||
@@ -26,6 +26,8 @@ public class ComboBoxBuilder {
|
||||
|
||||
private String prompt;
|
||||
|
||||
private String caption;
|
||||
|
||||
public ComboBoxBuilder setValueChangeListener(final Property.ValueChangeListener valueChangeListener) {
|
||||
this.valueChangeListener = valueChangeListener;
|
||||
return this;
|
||||
@@ -36,27 +38,35 @@ public class ComboBoxBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public ComboBoxBuilder setPrompt(String prompt) {
|
||||
public ComboBoxBuilder setPrompt(final String prompt) {
|
||||
this.prompt = prompt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ComboBoxBuilder setCaption(final String caption) {
|
||||
this.caption = caption;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a new ComboBox
|
||||
*/
|
||||
public ComboBox buildCombBox() {
|
||||
final ComboBox targetFilter = SPUIComponentProvider.getComboBox(null, "", null, ValoTheme.COMBOBOX_SMALL, false,
|
||||
"", prompt);
|
||||
targetFilter.setImmediate(true);
|
||||
targetFilter.setPageLength(7);
|
||||
targetFilter.setItemCaptionPropertyId(SPUILabelDefinitions.VAR_NAME);
|
||||
targetFilter.setSizeUndefined();
|
||||
final ComboBox comboBox = SPUIComponentProvider.getComboBox(null, "", null, ValoTheme.COMBOBOX_SMALL, false, "",
|
||||
prompt);
|
||||
comboBox.setImmediate(true);
|
||||
comboBox.setPageLength(7);
|
||||
comboBox.setItemCaptionPropertyId(SPUILabelDefinitions.VAR_NAME);
|
||||
comboBox.setSizeUndefined();
|
||||
if (caption != null) {
|
||||
comboBox.setCaption(caption);
|
||||
}
|
||||
if (id != null) {
|
||||
targetFilter.setId(id);
|
||||
comboBox.setId(id);
|
||||
}
|
||||
if (valueChangeListener != null) {
|
||||
targetFilter.addValueChangeListener(valueChangeListener);
|
||||
comboBox.addValueChangeListener(valueChangeListener);
|
||||
}
|
||||
return targetFilter;
|
||||
return comboBox;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,9 +24,6 @@ import com.vaadin.ui.themes.ValoTheme;
|
||||
|
||||
/**
|
||||
* Confirmation tab of confirmation window.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class ConfirmationTab extends VerticalLayout {
|
||||
|
||||
|
||||
@@ -159,7 +159,7 @@ public class DeploymentView extends AbstractNotificationView implements BrowserW
|
||||
uiExecutor);
|
||||
this.deleteAndActionsLayout = new DeleteActionsLayout(i18n, permChecker, eventBus, uiNotification,
|
||||
targetTagManagement, distributionSetTagManagement, managementViewClientCriterion, managementUIState,
|
||||
targetManagement, targetTable, deploymentManagement, distributionSetManagement);
|
||||
targetManagement, targetTable, deploymentManagement, distributionSetManagement, uiproperties);
|
||||
|
||||
actionHistoryLayout.registerDetails(((ActionStatusGrid) actionStatusLayout.getGrid()).getDetailsSupport());
|
||||
actionStatusLayout
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.eclipse.hawkbit.repository.TargetTagManagement;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.ui.SpPermissionChecker;
|
||||
import org.eclipse.hawkbit.ui.UiProperties;
|
||||
import org.eclipse.hawkbit.ui.common.entity.DistributionSetIdName;
|
||||
import org.eclipse.hawkbit.ui.common.entity.TargetIdName;
|
||||
import org.eclipse.hawkbit.ui.common.footer.AbstractDeleteActionsLayout;
|
||||
@@ -72,7 +73,7 @@ public class DeleteActionsLayout extends AbstractDeleteActionsLayout {
|
||||
|
||||
private final ManagementUIState managementUIState;
|
||||
|
||||
private final ManangementConfirmationWindowLayout manangementConfirmationWindowLayout;
|
||||
private final ManagementConfirmationWindowLayout managementConfirmationWindowLayout;
|
||||
|
||||
private final CountMessageLabel countMessageLabel;
|
||||
|
||||
@@ -86,14 +87,15 @@ public class DeleteActionsLayout extends AbstractDeleteActionsLayout {
|
||||
final ManagementViewClientCriterion managementViewClientCriterion,
|
||||
final ManagementUIState managementUIState, final TargetManagement targetManagement,
|
||||
final TargetTable targetTable, final DeploymentManagement deploymentManagement,
|
||||
final DistributionSetManagement distributionSetManagement) {
|
||||
final DistributionSetManagement distributionSetManagement, final UiProperties uiProperties) {
|
||||
super(i18n, permChecker, eventBus, notification);
|
||||
this.distributionSetTagManagement = distributionSetTagManagement;
|
||||
this.targetTagManagement = targetTagManagement;
|
||||
this.managementViewClientCriterion = managementViewClientCriterion;
|
||||
this.managementUIState = managementUIState;
|
||||
this.manangementConfirmationWindowLayout = new ManangementConfirmationWindowLayout(i18n, eventBus,
|
||||
managementUIState, targetManagement, deploymentManagement, distributionSetManagement);
|
||||
this.managementConfirmationWindowLayout = new ManagementConfirmationWindowLayout(i18n, eventBus,
|
||||
managementUIState, targetManagement, deploymentManagement, distributionSetManagement, uiProperties,
|
||||
notification);
|
||||
this.countMessageLabel = new CountMessageLabel(eventBus, targetManagement, i18n, managementUIState,
|
||||
targetTable);
|
||||
this.targetManagement = targetManagement;
|
||||
@@ -114,7 +116,7 @@ public class DeleteActionsLayout extends AbstractDeleteActionsLayout {
|
||||
UI.getCurrent().access(() -> {
|
||||
if (!hasUnsavedActions()) {
|
||||
closeUnsavedActionsWindow();
|
||||
final String message = manangementConfirmationWindowLayout.getConsolidatedMessage();
|
||||
final String message = managementConfirmationWindowLayout.getConsolidatedMessage();
|
||||
if (message != null && message.length() > 0) {
|
||||
notification.displaySuccess(message);
|
||||
}
|
||||
@@ -215,7 +217,7 @@ public class DeleteActionsLayout extends AbstractDeleteActionsLayout {
|
||||
|
||||
@Override
|
||||
protected void unsavedActionsWindowClosed() {
|
||||
final String message = manangementConfirmationWindowLayout.getConsolidatedMessage();
|
||||
final String message = managementConfirmationWindowLayout.getConsolidatedMessage();
|
||||
if (message != null && message.length() > 0) {
|
||||
notification.displaySuccess(message);
|
||||
}
|
||||
@@ -223,8 +225,8 @@ public class DeleteActionsLayout extends AbstractDeleteActionsLayout {
|
||||
|
||||
@Override
|
||||
protected Component getUnsavedActionsWindowContent() {
|
||||
manangementConfirmationWindowLayout.initialize();
|
||||
return manangementConfirmationWindowLayout;
|
||||
managementConfirmationWindowLayout.initialize();
|
||||
return managementConfirmationWindowLayout;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -248,7 +250,7 @@ public class DeleteActionsLayout extends AbstractDeleteActionsLayout {
|
||||
final String tagName = HawkbitCommonUtil.removePrefix(source.getId(),
|
||||
SPUIDefinitions.DISTRIBUTION_TAG_ID_PREFIXS);
|
||||
if (managementUIState.getDistributionTableFilters().getDistSetTags().contains(tagName)) {
|
||||
notification.displayValidationError(i18n.getMessage("message.tag.delete", new Object[] { tagName }));
|
||||
notification.displayValidationError(i18n.getMessage("message.tag.delete", tagName));
|
||||
} else {
|
||||
distributionSetTagManagement.delete(tagName);
|
||||
|
||||
@@ -258,14 +260,14 @@ public class DeleteActionsLayout extends AbstractDeleteActionsLayout {
|
||||
new DistributionSetTagTableEvent(BaseEntityEventType.REMOVE_ENTITY, Arrays.asList(id)));
|
||||
}
|
||||
|
||||
notification.displaySuccess(i18n.getMessage("message.delete.success", new Object[] { tagName }));
|
||||
notification.displaySuccess(i18n.getMessage("message.delete.success", tagName));
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteTargetTag(final Component source) {
|
||||
final String tagName = HawkbitCommonUtil.removePrefix(source.getId(), SPUIDefinitions.TARGET_TAG_ID_PREFIXS);
|
||||
if (managementUIState.getTargetTableFilters().getClickedTargetTags().contains(tagName)) {
|
||||
notification.displayValidationError(i18n.getMessage("message.tag.delete", new Object[] { tagName }));
|
||||
notification.displayValidationError(i18n.getMessage("message.tag.delete", tagName));
|
||||
} else {
|
||||
targetTagManagement.delete(tagName);
|
||||
|
||||
@@ -274,7 +276,7 @@ public class DeleteActionsLayout extends AbstractDeleteActionsLayout {
|
||||
eventBus.publish(this, new TargetTagTableEvent(BaseEntityEventType.REMOVE_ENTITY, Arrays.asList(id)));
|
||||
}
|
||||
|
||||
notification.displaySuccess(i18n.getMessage("message.delete.success", new Object[] { tagName }));
|
||||
notification.displaySuccess(i18n.getMessage("message.delete.success", tagName));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,22 +11,26 @@ package org.eclipse.hawkbit.ui.management.footer;
|
||||
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.hawkbit.repository.MaintenanceScheduleHelper;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidMaintenanceScheduleException;
|
||||
import org.eclipse.hawkbit.ui.common.builder.LabelBuilder;
|
||||
import org.eclipse.hawkbit.ui.common.builder.TextFieldBuilder;
|
||||
import org.eclipse.hawkbit.ui.utils.SPDateTimeUtil;
|
||||
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
|
||||
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.vaadin.data.Property.ValueChangeEvent;
|
||||
import com.vaadin.data.Property.ValueChangeListener;
|
||||
import com.cronutils.descriptor.CronDescriptor;
|
||||
import com.vaadin.data.Validator;
|
||||
import com.vaadin.ui.CheckBox;
|
||||
import com.vaadin.event.FieldEvents.TextChangeEvent;
|
||||
import com.vaadin.event.FieldEvents.TextChangeListener;
|
||||
import com.vaadin.server.Page;
|
||||
import com.vaadin.ui.ComboBox;
|
||||
import com.vaadin.ui.HorizontalLayout;
|
||||
import com.vaadin.ui.Notification;
|
||||
import com.vaadin.ui.Label;
|
||||
import com.vaadin.ui.TextField;
|
||||
import com.vaadin.ui.VerticalLayout;
|
||||
import com.vaadin.ui.themes.ValoTheme;
|
||||
@@ -36,15 +40,16 @@ import com.vaadin.ui.themes.ValoTheme;
|
||||
* maintenance schedule while assigning distribution set(s) to the target(s).
|
||||
*/
|
||||
public class MaintenanceWindowLayout extends VerticalLayout {
|
||||
|
||||
private static final long serialVersionUID = 722511089585562455L;
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final VaadinMessageSource i18n;
|
||||
|
||||
private CheckBox maintenanceWindowSelection;
|
||||
private static final String CRON_VALIDATION_ERROR = "message.maintenancewindow.schedule.validation.error";
|
||||
|
||||
private TextField schedule;
|
||||
private TextField duration;
|
||||
private ComboBox timeZone;
|
||||
private Label scheduleTranslator;
|
||||
|
||||
/**
|
||||
* Constructor for the control to specify the maintenance schedule.
|
||||
@@ -55,123 +60,137 @@ public class MaintenanceWindowLayout extends VerticalLayout {
|
||||
*/
|
||||
public MaintenanceWindowLayout(final VaadinMessageSource i18n) {
|
||||
|
||||
HorizontalLayout optionContainer;
|
||||
HorizontalLayout controlContainer;
|
||||
|
||||
this.i18n = i18n;
|
||||
|
||||
optionContainer = new HorizontalLayout();
|
||||
controlContainer = new HorizontalLayout();
|
||||
addComponent(optionContainer);
|
||||
addComponent(controlContainer);
|
||||
|
||||
createMaintenanceWindowOption();
|
||||
createMaintenanceScheduleControl();
|
||||
createMaintenanceDurationControl();
|
||||
createMaintenanceTimeZoneControl();
|
||||
createMaintenanceScheduleTranslatorControl();
|
||||
|
||||
optionContainer.addComponent(maintenanceWindowSelection);
|
||||
final HorizontalLayout controlContainer = new HorizontalLayout();
|
||||
controlContainer.addComponent(schedule);
|
||||
controlContainer.addComponent(duration);
|
||||
controlContainer.addComponent(timeZone);
|
||||
addComponent(controlContainer);
|
||||
|
||||
addComponent(scheduleTranslator);
|
||||
|
||||
addValueChangeListener();
|
||||
maintenanceWindowSelection.setValue(false);
|
||||
setStyleName("dist-window-maintenance-window-layout");
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the maintenance schedule is a valid cron expression.
|
||||
*/
|
||||
private static class CronValidation implements Validator {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public void validate(final Object value) throws InvalidValueException {
|
||||
try {
|
||||
final String expr = (String) value;
|
||||
if (!expr.isEmpty()) {
|
||||
MaintenanceScheduleHelper.validateMaintenanceSchedule((String) value, "00:00:00",
|
||||
getClientTimeZone());
|
||||
}
|
||||
} catch (final IllegalArgumentException e) {
|
||||
Notification.show(e.getMessage());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the duration is specified in expected format.
|
||||
*/
|
||||
private static class DurationValidator implements Validator {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public void validate(final Object value) {
|
||||
try {
|
||||
final String expr = (String) value;
|
||||
if (!StringUtils.isEmpty(expr)) {
|
||||
MaintenanceScheduleHelper.convertToISODuration((String) value);
|
||||
}
|
||||
} catch (final DateTimeParseException e) {
|
||||
Notification.show(e.getMessage());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create check box to enable or disable maintenance window.
|
||||
*/
|
||||
private void createMaintenanceWindowOption() {
|
||||
maintenanceWindowSelection = new CheckBox(i18n.getMessage("caption.maintenancewindow.enable"));
|
||||
maintenanceWindowSelection.addStyleName(ValoTheme.CHECKBOX_SMALL);
|
||||
setId(UIComponentIdProvider.MAINTENANCE_WINDOW_LAYOUT_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Text field to specify the schedule.
|
||||
*/
|
||||
private void createMaintenanceScheduleControl() {
|
||||
schedule = new TextField();
|
||||
schedule.setCaption(i18n.getMessage("caption.maintenancewindow.schedule"));
|
||||
schedule.addValidator(new CronValidation());
|
||||
schedule.setEnabled(false);
|
||||
schedule.addStyleName(ValoTheme.TEXTFIELD_SMALL);
|
||||
schedule = new TextFieldBuilder().id(UIComponentIdProvider.MAINTENANCE_WINDOW_SCHEDULE_ID)
|
||||
.caption(i18n.getMessage("caption.maintenancewindow.schedule")).immediate(true)
|
||||
.validator(new CronValidator()).prompt("0 0 3 ? * 6").required(true).buildTextComponent();
|
||||
schedule.addTextChangeListener(new CronTranslationListener());
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the maintenance schedule is a valid cron expression.
|
||||
*/
|
||||
private class CronValidator implements Validator {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
// Exception squid:S1166 - Vaadin validation class,
|
||||
// InvalidValueException,
|
||||
// doesn't have the constructor to pass throwable, but shows the
|
||||
// validation
|
||||
// errors to the user
|
||||
@SuppressWarnings("squid:S1166")
|
||||
@Override
|
||||
public void validate(final Object value) {
|
||||
try {
|
||||
MaintenanceScheduleHelper.validateCronSchedule((String) value);
|
||||
} catch (final InvalidMaintenanceScheduleException e) {
|
||||
throw new InvalidValueException(i18n.getMessage(CRON_VALIDATION_ERROR) + ": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for cron expression translation.
|
||||
*/
|
||||
private class CronTranslationListener implements TextChangeListener {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final transient CronDescriptor cronDescriptor;
|
||||
|
||||
public CronTranslationListener() {
|
||||
cronDescriptor = CronDescriptor.instance(getClientsLocale());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void textChange(final TextChangeEvent event) {
|
||||
scheduleTranslator.setValue(translateCron(event.getText()));
|
||||
}
|
||||
|
||||
// Exception squid:S1166 - when the format of the cron expression is not
|
||||
// valid, the hint is shown to provide the valid one
|
||||
@SuppressWarnings("squid:S1166")
|
||||
private String translateCron(final String cronExpression) {
|
||||
try {
|
||||
return cronDescriptor.describe(MaintenanceScheduleHelper.getCronFromExpression(cronExpression));
|
||||
} catch (final IllegalArgumentException ex) {
|
||||
return i18n.getMessage(CRON_VALIDATION_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
private Locale getClientsLocale() {
|
||||
return Page.getCurrent().getWebBrowser().getLocale();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Text field to specify the duration.
|
||||
*/
|
||||
private void createMaintenanceDurationControl() {
|
||||
duration = new TextField();
|
||||
duration.setCaption(i18n.getMessage("caption.maintenancewindow.duration"));
|
||||
duration.addValidator(new DurationValidator());
|
||||
duration.setEnabled(false);
|
||||
schedule.addStyleName(ValoTheme.TEXTFIELD_SMALL);
|
||||
duration = new TextFieldBuilder().id(UIComponentIdProvider.MAINTENANCE_WINDOW_DURATION_ID)
|
||||
.caption(i18n.getMessage("caption.maintenancewindow.duration")).immediate(true)
|
||||
.validator(new DurationValidator()).prompt("hh:mm:ss").required(true).buildTextComponent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the duration is specified in expected format.
|
||||
*/
|
||||
private class DurationValidator implements Validator {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
// Exception squid:S1166 - Vaadin validation class,
|
||||
// InvalidValueException,
|
||||
// doesn't have the constructor to pass throwable, but shows the
|
||||
// validation
|
||||
// errors to the user
|
||||
@SuppressWarnings("squid:S1166")
|
||||
@Override
|
||||
public void validate(final Object value) {
|
||||
try {
|
||||
MaintenanceScheduleHelper.validateDuration((String) value);
|
||||
} catch (final InvalidMaintenanceScheduleException e) {
|
||||
throw new InvalidValueException(i18n.getMessage("message.maintenancewindow.duration.validation.error",
|
||||
e.getDurationErrorIndex()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Combo box to pick the time zone offset.
|
||||
*/
|
||||
private void createMaintenanceTimeZoneControl() {
|
||||
// ComboBoxBuilder cannot be used here, because Builder do
|
||||
// 'comboBox.setItemCaptionPropertyId(SPUILabelDefinitions.VAR_NAME);'
|
||||
// which interferes our code: 'timeZone.addItems(getAllTimeZones());'
|
||||
timeZone = new ComboBox();
|
||||
timeZone.setId(UIComponentIdProvider.MAINTENANCE_WINDOW_TIME_ZONE_ID);
|
||||
timeZone.setCaption(i18n.getMessage("caption.maintenancewindow.timezone"));
|
||||
|
||||
timeZone.addItems(getAllTimeZones());
|
||||
timeZone.setTextInputAllowed(false);
|
||||
timeZone.setValue(getClientTimeZone());
|
||||
|
||||
timeZone.setEnabled(false);
|
||||
timeZone.addStyleName(ValoTheme.COMBOBOX_SMALL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get time zone of the browser client to be used as default.
|
||||
*/
|
||||
private static String getClientTimeZone() {
|
||||
return ZonedDateTime.now(ZoneId.of(SPDateTimeUtil.getBrowserTimeZone().getID())).getOffset().getId()
|
||||
.replaceAll("Z", "+00:00");
|
||||
timeZone.setTextInputAllowed(false);
|
||||
timeZone.setNullSelectionAllowed(false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -186,36 +205,20 @@ public class MaintenanceWindowLayout extends VerticalLayout {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a listener to enable and disable maintenance schedule controls.
|
||||
* Get time zone of the browser client to be used as default.
|
||||
*/
|
||||
private void addValueChangeListener() {
|
||||
maintenanceWindowSelection.addValueChangeListener(new ValueChangeListener() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public void valueChange(final ValueChangeEvent event) {
|
||||
schedule.setEnabled(maintenanceWindowSelection.getValue());
|
||||
schedule.setRequired(maintenanceWindowSelection.getValue());
|
||||
schedule.setValue("");
|
||||
|
||||
duration.setEnabled(maintenanceWindowSelection.getValue());
|
||||
duration.setRequired(maintenanceWindowSelection.getValue());
|
||||
duration.setValue("");
|
||||
|
||||
timeZone.setEnabled(maintenanceWindowSelection.getValue());
|
||||
timeZone.setRequired(maintenanceWindowSelection.getValue());
|
||||
timeZone.setValue(getClientTimeZone());
|
||||
}
|
||||
});
|
||||
private static String getClientTimeZone() {
|
||||
return ZonedDateTime.now(ZoneId.of(SPDateTimeUtil.getBrowserTimeZone().getID())).getOffset().getId()
|
||||
.replaceAll("Z", "+00:00");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether the maintenance schedule option is enabled or not.
|
||||
*
|
||||
* @return boolean.
|
||||
* Label to translate the cron schedule to human readable format.
|
||||
*/
|
||||
public boolean isMaintenanceWindowEnabled() {
|
||||
return maintenanceWindowSelection.getValue();
|
||||
private void createMaintenanceScheduleTranslatorControl() {
|
||||
scheduleTranslator = new LabelBuilder().id(UIComponentIdProvider.MAINTENANCE_WINDOW_SCHEDULE_TRANSLATOR_ID)
|
||||
.name(i18n.getMessage(CRON_VALIDATION_ERROR)).buildLabel();
|
||||
scheduleTranslator.addStyleName(ValoTheme.LABEL_TINY);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -237,7 +240,7 @@ public class MaintenanceWindowLayout extends VerticalLayout {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cron expression for maintenance window timezone.
|
||||
* Get the timezone for maintenance window.
|
||||
*
|
||||
* @return {@link String}.
|
||||
*/
|
||||
@@ -245,4 +248,55 @@ public class MaintenanceWindowLayout extends VerticalLayout {
|
||||
public String getMaintenanceTimeZone() {
|
||||
return timeZone.getValue().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set all the controls to their default values.
|
||||
*/
|
||||
public void clearAllControls() {
|
||||
schedule.setValue("");
|
||||
duration.setValue("");
|
||||
timeZone.setValue(getClientTimeZone());
|
||||
}
|
||||
|
||||
/**
|
||||
* Method, used for validity check, when schedule text is changed.
|
||||
*
|
||||
* @param event
|
||||
* (@link TextChangeEvent} the event object after schedule text
|
||||
* change.
|
||||
* @return validity of maintenance window controls.
|
||||
*/
|
||||
public boolean onScheduleChange(final TextChangeEvent event) {
|
||||
schedule.setValue(event.getText());
|
||||
return isScheduleAndDurationValid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method, used for validity check, when duration text is changed.
|
||||
*
|
||||
* @param event
|
||||
* (@link TextChangeEvent} the event object after duration text
|
||||
* change.
|
||||
* @return validity of maintenance window controls.
|
||||
*/
|
||||
public boolean onDurationChange(final TextChangeEvent event) {
|
||||
duration.setValue(event.getText());
|
||||
return isScheduleAndDurationValid();
|
||||
}
|
||||
|
||||
private boolean isScheduleAndDurationValid() {
|
||||
if (schedule.isEmpty() || duration.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return schedule.isValid() && duration.isValid();
|
||||
}
|
||||
|
||||
public TextField getScheduleControl() {
|
||||
return schedule;
|
||||
}
|
||||
|
||||
public TextField getDurationControl() {
|
||||
return duration;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,16 +21,20 @@ import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||
import org.eclipse.hawkbit.repository.MaintenanceScheduleHelper;
|
||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||
import org.eclipse.hawkbit.repository.model.TargetWithActionType;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidMaintenanceScheduleException;
|
||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
||||
import org.eclipse.hawkbit.repository.model.RepositoryModelConstants;
|
||||
import org.eclipse.hawkbit.repository.model.TargetWithActionType;
|
||||
import org.eclipse.hawkbit.ui.UiProperties;
|
||||
import org.eclipse.hawkbit.ui.common.confirmwindow.layout.AbstractConfirmationWindowLayout;
|
||||
import org.eclipse.hawkbit.ui.common.confirmwindow.layout.ConfirmationTab;
|
||||
import org.eclipse.hawkbit.ui.common.entity.DistributionSetIdName;
|
||||
import org.eclipse.hawkbit.ui.common.entity.TargetIdName;
|
||||
import org.eclipse.hawkbit.ui.common.table.BaseEntityEventType;
|
||||
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
|
||||
import org.eclipse.hawkbit.ui.management.event.DistributionTableEvent;
|
||||
import org.eclipse.hawkbit.ui.management.event.PinUnpinEvent;
|
||||
import org.eclipse.hawkbit.ui.management.event.SaveActionWindowEvent;
|
||||
@@ -41,6 +45,7 @@ import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil;
|
||||
import org.eclipse.hawkbit.ui.utils.SPUIDefinitions;
|
||||
import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions;
|
||||
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
|
||||
import org.eclipse.hawkbit.ui.utils.UINotification;
|
||||
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
|
||||
import org.vaadin.spring.events.EventBus.UIEventBus;
|
||||
|
||||
@@ -49,13 +54,17 @@ import com.vaadin.data.Item;
|
||||
import com.vaadin.data.util.IndexedContainer;
|
||||
import com.vaadin.server.FontAwesome;
|
||||
import com.vaadin.ui.Button.ClickListener;
|
||||
import com.vaadin.ui.CheckBox;
|
||||
import com.vaadin.ui.HorizontalLayout;
|
||||
import com.vaadin.ui.Link;
|
||||
import com.vaadin.ui.Table.Align;
|
||||
import com.vaadin.ui.themes.ValoTheme;
|
||||
|
||||
/**
|
||||
* Confirmation window for target/distributionSet delete and assignment
|
||||
* operations on the Deployment View.
|
||||
*/
|
||||
public class ManangementConfirmationWindowLayout extends AbstractConfirmationWindowLayout {
|
||||
public class ManagementConfirmationWindowLayout extends AbstractConfirmationWindowLayout {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -69,6 +78,10 @@ public class ManangementConfirmationWindowLayout extends AbstractConfirmationWin
|
||||
|
||||
private static final String TARGET_ID = "TargetId";
|
||||
|
||||
private final UiProperties uiProperties;
|
||||
|
||||
private final UINotification uiNotification;
|
||||
|
||||
private final ManagementUIState managementUIState;
|
||||
|
||||
private final transient TargetManagement targetManagement;
|
||||
@@ -83,11 +96,13 @@ public class ManangementConfirmationWindowLayout extends AbstractConfirmationWin
|
||||
|
||||
private ConfirmationTab assignmentTab;
|
||||
|
||||
public ManangementConfirmationWindowLayout(final VaadinMessageSource i18n, final UIEventBus eventBus,
|
||||
public ManagementConfirmationWindowLayout(final VaadinMessageSource i18n, final UIEventBus eventBus,
|
||||
final ManagementUIState managementUIState, final TargetManagement targetManagement,
|
||||
final DeploymentManagement deploymentManagement,
|
||||
final DistributionSetManagement distributionSetManagement) {
|
||||
final DeploymentManagement deploymentManagement, final DistributionSetManagement distributionSetManagement,
|
||||
final UiProperties uiProperties, final UINotification uiNotification) {
|
||||
super(i18n, eventBus);
|
||||
this.uiProperties = uiProperties;
|
||||
this.uiNotification = uiNotification;
|
||||
this.managementUIState = managementUIState;
|
||||
this.targetManagement = targetManagement;
|
||||
this.deploymentManagement = deploymentManagement;
|
||||
@@ -142,12 +157,61 @@ public class ManangementConfirmationWindowLayout extends AbstractConfirmationWin
|
||||
|
||||
actionTypeOptionGroupLayout.selectDefaultOption();
|
||||
assignmentTab.addComponent(actionTypeOptionGroupLayout, 1);
|
||||
assignmentTab.addComponent(maintenanceWindowLayout, 1);
|
||||
assignmentTab.addComponent(enableMaintenanceWindowLayout(), 2);
|
||||
initMaintenanceWindow();
|
||||
assignmentTab.addComponent(maintenanceWindowLayout, 3);
|
||||
|
||||
return assignmentTab;
|
||||
|
||||
}
|
||||
|
||||
private HorizontalLayout enableMaintenanceWindowLayout() {
|
||||
final HorizontalLayout layout = new HorizontalLayout();
|
||||
layout.addComponent(enableMaintenanceWindowControl());
|
||||
layout.addComponent(maintenanceWindowHelpLinkControl());
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
private CheckBox enableMaintenanceWindowControl() {
|
||||
final CheckBox enableMaintenanceWindow = new CheckBox(i18n.getMessage("caption.maintenancewindow.enabled"));
|
||||
enableMaintenanceWindow.setId(UIComponentIdProvider.MAINTENANCE_WINDOW_ENABLED_ID);
|
||||
enableMaintenanceWindow.addStyleName(ValoTheme.CHECKBOX_SMALL);
|
||||
enableMaintenanceWindow.addStyleName("dist-window-maintenance-window-enable");
|
||||
enableMaintenanceWindow.addValueChangeListener(event -> {
|
||||
final Boolean isMaintenanceWindowEnabled = enableMaintenanceWindow.getValue();
|
||||
maintenanceWindowLayout.setVisible(isMaintenanceWindowEnabled);
|
||||
maintenanceWindowLayout.setEnabled(isMaintenanceWindowEnabled);
|
||||
enableSaveButton(!isMaintenanceWindowEnabled);
|
||||
maintenanceWindowLayout.clearAllControls();
|
||||
});
|
||||
|
||||
return enableMaintenanceWindow;
|
||||
}
|
||||
|
||||
private void enableSaveButton(final boolean enabled) {
|
||||
assignmentTab.getConfirmAll().setEnabled(enabled);
|
||||
}
|
||||
|
||||
private Link maintenanceWindowHelpLinkControl() {
|
||||
final String maintenanceWindowHelpUrl = uiProperties.getLinks().getDocumentation().getMaintenanceWindowView();
|
||||
return SPUIComponentProvider.getHelpLink(maintenanceWindowHelpUrl);
|
||||
}
|
||||
|
||||
private void initMaintenanceWindow() {
|
||||
maintenanceWindowLayout.setVisible(false);
|
||||
maintenanceWindowLayout.setEnabled(false);
|
||||
maintenanceWindowLayout.getScheduleControl()
|
||||
.addTextChangeListener(event -> enableSaveButton(maintenanceWindowLayout.onScheduleChange(event)));
|
||||
maintenanceWindowLayout.getDurationControl()
|
||||
.addTextChangeListener(event -> enableSaveButton(maintenanceWindowLayout.onDurationChange(event)));
|
||||
}
|
||||
|
||||
private void saveAllAssignments(final ConfirmationTab tab) {
|
||||
if (!isMaintenanceWindowValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Set<TargetIdName> itemIds = managementUIState.getAssignedList().keySet();
|
||||
Long distId;
|
||||
List<TargetIdName> targetIdSetList;
|
||||
@@ -159,15 +223,6 @@ public class ManangementConfirmationWindowLayout extends AbstractConfirmationWin
|
||||
? actionTypeOptionGroupLayout.getForcedTimeDateField().getValue().getTime()
|
||||
: RepositoryModelConstants.NO_FORCE_TIME;
|
||||
|
||||
final String maintenanceSchedule = maintenanceWindowLayout.isMaintenanceWindowEnabled()
|
||||
? maintenanceWindowLayout.getMaintenanceSchedule() : null;
|
||||
|
||||
final String maintenanceDuration = maintenanceWindowLayout.isMaintenanceWindowEnabled()
|
||||
? maintenanceWindowLayout.getMaintenanceDuration() : null;
|
||||
|
||||
final String maintenanceTimeZone = maintenanceWindowLayout.isMaintenanceWindowEnabled()
|
||||
? maintenanceWindowLayout.getMaintenanceTimeZone() : null;
|
||||
|
||||
final Map<Long, List<TargetIdName>> saveAssignedList = Maps.newHashMapWithExpectedSize(itemIds.size());
|
||||
|
||||
int successAssignmentCount = 0;
|
||||
@@ -185,13 +240,18 @@ public class ManangementConfirmationWindowLayout extends AbstractConfirmationWin
|
||||
saveAssignedList.put(distId, targetIdSetList);
|
||||
}
|
||||
|
||||
final String maintenanceSchedule = maintenanceWindowLayout.getMaintenanceSchedule();
|
||||
final String maintenanceDuration = maintenanceWindowLayout.getMaintenanceDuration();
|
||||
final String maintenanceTimeZone = maintenanceWindowLayout.getMaintenanceTimeZone();
|
||||
|
||||
for (final Map.Entry<Long, List<TargetIdName>> mapEntry : saveAssignedList.entrySet()) {
|
||||
tempIdList = saveAssignedList.get(mapEntry.getKey());
|
||||
final DistributionSetAssignmentResult distributionSetAssignmentResult = deploymentManagement
|
||||
.assignDistributionSet(mapEntry.getKey(),
|
||||
tempIdList.stream()
|
||||
.map(t -> new TargetWithActionType(t.getControllerId(), actionType, forcedTimeStamp,
|
||||
maintenanceSchedule, maintenanceDuration, maintenanceTimeZone))
|
||||
tempIdList.stream().map(t -> maintenanceWindowLayout.isEnabled()
|
||||
? new TargetWithActionType(t.getControllerId(), actionType, forcedTimeStamp,
|
||||
maintenanceSchedule, maintenanceDuration, maintenanceTimeZone)
|
||||
: new TargetWithActionType(t.getControllerId(), actionType, forcedTimeStamp))
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
if (distributionSetAssignmentResult.getAssigned() > 0) {
|
||||
@@ -210,6 +270,24 @@ public class ManangementConfirmationWindowLayout extends AbstractConfirmationWin
|
||||
eventBus.publish(this, SaveActionWindowEvent.SAVED_ASSIGNMENTS);
|
||||
}
|
||||
|
||||
// Exception squid:S1166 - the user is notified when the maintenance
|
||||
// schedule validation fails
|
||||
@SuppressWarnings("squid:S1166")
|
||||
private boolean isMaintenanceWindowValid() {
|
||||
if (maintenanceWindowLayout.isEnabled()) {
|
||||
try {
|
||||
MaintenanceScheduleHelper.validateMaintenanceSchedule(maintenanceWindowLayout.getMaintenanceSchedule(),
|
||||
maintenanceWindowLayout.getMaintenanceDuration(),
|
||||
maintenanceWindowLayout.getMaintenanceTimeZone());
|
||||
} catch (final InvalidMaintenanceScheduleException e) {
|
||||
uiNotification.displayValidationError(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void addMessage(final int successAssignmentCount, final int duplicateAssignmentCount) {
|
||||
if (successAssignmentCount > 0) {
|
||||
addToConsolitatedMsg(getAssigmentSuccessMessage(successAssignmentCount));
|
||||
@@ -239,12 +317,12 @@ public class ManangementConfirmationWindowLayout extends AbstractConfirmationWin
|
||||
|
||||
private String getAssigmentSuccessMessage(final int assignedCount) {
|
||||
return FontAwesome.TASKS.getHtml() + SPUILabelDefinitions.HTML_SPACE
|
||||
+ i18n.getMessage("message.target.assignment", new Object[] { assignedCount });
|
||||
+ i18n.getMessage("message.target.assignment", assignedCount);
|
||||
}
|
||||
|
||||
private String getDuplicateAssignmentMessage(final int alreadyAssignedCount) {
|
||||
return FontAwesome.TASKS.getHtml() + SPUILabelDefinitions.HTML_SPACE
|
||||
+ i18n.getMessage("message.target.alreadyAssigned", new Object[] { alreadyAssignedCount });
|
||||
+ i18n.getMessage("message.target.alreadyAssigned", alreadyAssignedCount);
|
||||
}
|
||||
|
||||
private void discardAllAssignments(final ConfirmationTab tab) {
|
||||
@@ -294,6 +294,36 @@ public final class UIComponentIdProvider {
|
||||
*/
|
||||
public static final String ACTION_BUTTON_LAYOUT = "ActionButtonLayout";
|
||||
|
||||
/**
|
||||
* Id for maintenance window layout
|
||||
*/
|
||||
public static final String MAINTENANCE_WINDOW_LAYOUT_ID = "maintenance.window.layout";
|
||||
|
||||
/**
|
||||
* Id for maintenance window - enabled checkbox
|
||||
*/
|
||||
public static final String MAINTENANCE_WINDOW_ENABLED_ID = "maintenance.window.enabled";
|
||||
|
||||
/**
|
||||
* Id for maintenance window - field schedule
|
||||
*/
|
||||
public static final String MAINTENANCE_WINDOW_SCHEDULE_ID = "maintenance.window.schedule";
|
||||
|
||||
/**
|
||||
* Id for maintenance window - field duration
|
||||
*/
|
||||
public static final String MAINTENANCE_WINDOW_DURATION_ID = "maintenance.window.duration";
|
||||
|
||||
/**
|
||||
* Id for maintenance window - field time zone
|
||||
*/
|
||||
public static final String MAINTENANCE_WINDOW_TIME_ZONE_ID = "maintenance.window.time.zone";
|
||||
|
||||
/**
|
||||
* Id for maintenance window - label schedule translator
|
||||
*/
|
||||
public static final String MAINTENANCE_WINDOW_SCHEDULE_TRANSLATOR_ID = "maintenance.window.schedule.translator";
|
||||
|
||||
/**
|
||||
* Delete button wrapper id.
|
||||
*/
|
||||
|
||||
@@ -83,39 +83,36 @@
|
||||
@include sp-button-icon-only-href;
|
||||
}
|
||||
|
||||
.v-slot-dist-window-maintenance-window-enable {
|
||||
padding-left: 9px;
|
||||
padding-top: 5px;
|
||||
padding-right: 5px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.dist-window-maintenance-window-layout {
|
||||
vertical-align: middle;
|
||||
horizontal-align: middle;
|
||||
padding-left: 14px;
|
||||
|
||||
.v-slot {
|
||||
vertical-align: middle;
|
||||
padding-left: 8px;
|
||||
padding-right: 5px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.v-caption {
|
||||
vertical-align: middle;
|
||||
margin-top: 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.v-checkbox {
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.v-filterselect {
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
width: 160px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.v-textfield {
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
width: 160px;
|
||||
height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,10 +139,6 @@
|
||||
|
||||
.v-caption {
|
||||
vertical-align: middle;
|
||||
margin-top: 0;
|
||||
font-size: 12px;
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.v-radiobutton {
|
||||
|
||||
@@ -139,10 +139,10 @@ caption.metadata.delete.action.confirmbox = Confirm Metadata Delete Action
|
||||
caption.confirm.assign.consequences = Auto assign consequences
|
||||
caption.auto.assignment.ds = Auto assignment
|
||||
|
||||
caption.maintenancewindow.enable = Maintenance Schedule
|
||||
caption.maintenancewindow.schedule = Schedule (Cron Expression)
|
||||
caption.maintenancewindow.duration = Duration (hh:mm:ss)
|
||||
caption.maintenancewindow.timezone = Time Zone
|
||||
caption.maintenancewindow.enabled = Use maintenance window
|
||||
caption.maintenancewindow.schedule = Schedule
|
||||
caption.maintenancewindow.duration = Duration
|
||||
caption.maintenancewindow.timezone = Time Zone (from GMT)
|
||||
|
||||
# Labels prefix with - label
|
||||
label.dist.details.type = Type :
|
||||
@@ -367,6 +367,8 @@ message.bulk.upload.tag.assignment.failed = Tag {0} assignment failed as tag no
|
||||
message.bulk.upload.tag.assignments.failed= Few tag assignments failed as tags no longer exists
|
||||
message.confirm.assign.consequences.none = This auto assignment will not have any effect on the currently available targets. In future added targets might match the filter and will receive the selected distribution set automatically.
|
||||
message.confirm.assign.consequences.text = When you confirm this auto assignment, {0} targets which match the filter will immediately get assigned with the selected distribution set.
|
||||
message.maintenancewindow.schedule.validation.error = Please enter a valid Cron expression
|
||||
message.maintenancewindow.duration.validation.error = Please enter the duration in the format hh:mm:ss, error is at {0} position
|
||||
|
||||
# action info
|
||||
action.target.table.selectall = Select all (Ctrl+A)
|
||||
|
||||
Reference in New Issue
Block a user