Jakub Niedermertl has uploaded a new change for review. Change subject: webadmin: VM Icons - frontend part ......................................................................
webadmin: VM Icons - frontend part http://www.ovirt.org/Features/VM_Icons Frontend part of introduction of VM icons - customizable image of VM and template in userportal. It replaces current system of providing operating system icons for browser and allows users to customize icons.a * IconEditorWidget - encapsulated editor of vm icons communication with model using IconWithDefault class. * 'New VM', 'Edit VM', 'Edit Template', 'New Pool' and 'Edit Pool' dialogs contains 'Icon' tab. * Icons are downloaded on request in async IconVmBaseToUnitBuilder and cached in IconCache class. (This patch does not contain changes in userportal.) Change-Id: Id56aecccef6aab6604de32e27497991ab3713d8d Bug-Url: https://bugzilla.redhat.com/1103175 Signed-off-by: Jakub Niedermertl <jnied...@redhat.com> --- M frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/CommonApplicationConstants.java M frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/CommonApplicationTemplates.java M frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/gin/CommonGinUiBinderWidgets.java M frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/HasAccess.java A frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/editor/CompositeHandlerRegistration.java A frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/editor/IconEditorWidget.java A frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/editor/IconEditorWidget.ui.xml M frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/popup/AbstractVmPopupWidget.java M frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/popup/AbstractVmPopupWidget.ui.xml M frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/popup/instancetypes/InstanceTypesPopupWidget.java M frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/Cloner.java A frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/IconCache.java M frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/Linq.java M frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/builders/vm/CoreUnitToVmBaseBuilder.java M frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/builders/vm/CoreVmBaseToUnitBuilder.java A frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/builders/vm/IconVmBaseToUnitBuilder.java M frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/dataprovider/AsyncDataProvider.java M frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/TabName.java M frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/pools/PoolListModel.java M frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/templates/TemplateListModel.java M frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/templates/VmBaseListModel.java M frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/ExistingVmModelBehavior.java A frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/IconWithDefault.java M frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/NewTemplateVmModelBehavior.java M frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/NewVmModelBehavior.java M frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/PoolModelBehaviorBase.java M frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/TemplateVmModelBehavior.java M frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/UnitVmModel.java M frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/VmListModel.java M frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/VmModelBehaviorBase.java M frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/VmNextRunConfigurationModel.java M frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/instancetypes/InstanceTypeModelBehaviorBase.java A frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/validation/IconValidation.java A frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/validation/IconWithDefaultValidation.java M frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/validation/ValidationResult.java M frontend/webadmin/modules/uicompat/src/main/java/org/ovirt/engine/ui/uicompat/UIConstants.java M frontend/webadmin/modules/uicompat/src/main/java/org/ovirt/engine/ui/uicompat/UIMessages.java 37 files changed, 1,140 insertions(+), 47 deletions(-) git pull ssh://gerrit.ovirt.org:29418/ovirt-engine refs/changes/01/38601/1 diff --git a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/CommonApplicationConstants.java b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/CommonApplicationConstants.java index 27c7e9d..4df4689 100644 --- a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/CommonApplicationConstants.java +++ b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/CommonApplicationConstants.java @@ -760,6 +760,27 @@ @DefaultStringValue("Clone Name") String clonedVmName(); + @DefaultStringValue("Icon") + String iconTabVmPopup(); + + @DefaultStringValue("New Icon") + String newIconVmPopup(); + + @DefaultStringValue("Current Icon") + String currentIconVmPopup(); + + @DefaultStringValue("Upload") + String uploadIconVmPopup(); + + @DefaultStringValue("Use default") + String useDefaultIconVmPopup(); + + @DefaultStringValue("Discard changes") + String discardChangesIconVmPopup(); + + @DefaultStringValue("Icons limitations: max dimensions: width 150px, heigth 120px; max size 24kB; supported formats: jpg, png, gif") + String iconLimitationsIconVmPopup(); + // Permissions @DefaultStringValue("Inherited Permission") String inheritedPermission(); diff --git a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/CommonApplicationTemplates.java b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/CommonApplicationTemplates.java index fb94750..f054bcd 100644 --- a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/CommonApplicationTemplates.java +++ b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/CommonApplicationTemplates.java @@ -47,9 +47,15 @@ @Template("<ul style='margin-top:0'>{0}</ul>") SafeHtml unsignedList(SafeHtml list); + @Template("<ul>{0}</ul>") + SafeHtml unorderedList(SafeHtml items); + @Template("<li>{0}</li>") SafeHtml listItem(SafeHtml item); + @Template("<li>{0}</li>") + SafeHtml listItem(String item); + @Template("{0} <sub>{1}</sub>") SafeHtml sub(String main, String sub); diff --git a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/gin/CommonGinUiBinderWidgets.java b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/gin/CommonGinUiBinderWidgets.java index 0d0c7ed..19f68cb 100644 --- a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/gin/CommonGinUiBinderWidgets.java +++ b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/gin/CommonGinUiBinderWidgets.java @@ -1,25 +1,29 @@ package org.ovirt.engine.ui.common.gin; import com.google.gwt.inject.client.Ginjector; +import org.ovirt.engine.ui.common.widget.editor.IconEditorWidget; import org.ovirt.engine.ui.common.widget.uicommon.popup.vm.SerialNumberPolicyWidget; /** * Ginjector extension containing views that make use of the GWTP GinUiBinder. * * Every view that makes use of GIN dependency injection and is to be embedded in a .ui.xml file --ex: + * <pre> * <code> - * <w:MyWidget /> + * <w:MyWidget /> * * public class MyWidget { - * @Inject + * {@literal @}Inject * public MyWidget(ApplicationResources resources, MyOtherDependency dep) { * // ... * } * } * </code> + * </pre> * * Must be registered in this interface. */ public interface CommonGinUiBinderWidgets extends Ginjector { SerialNumberPolicyWidget getSerialNumberPolicyWidget(); + IconEditorWidget getIconEditorWidget(); } diff --git a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/HasAccess.java b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/HasAccess.java index df4eb9b..71512b7 100644 --- a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/HasAccess.java +++ b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/HasAccess.java @@ -2,6 +2,10 @@ /** * Widgets that implement this interface have a user access policy associated with them. + * <p> + * Inaccessible widgets should be hidden. Usually using + * {@link com.google.gwt.user.client.ui.Widget#setVisible(boolean)} + * </p> */ public interface HasAccess { diff --git a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/editor/CompositeHandlerRegistration.java b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/editor/CompositeHandlerRegistration.java new file mode 100644 index 0000000..fcc8b6d --- /dev/null +++ b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/editor/CompositeHandlerRegistration.java @@ -0,0 +1,26 @@ +package org.ovirt.engine.ui.common.widget.editor; + +import com.google.gwt.event.shared.HandlerRegistration; + +import java.util.Arrays; +import java.util.List; + +public class CompositeHandlerRegistration implements HandlerRegistration { + + private final List<HandlerRegistration> registrations; + + private CompositeHandlerRegistration(List<HandlerRegistration> registrations) { + this.registrations = registrations; + } + + public static CompositeHandlerRegistration of(HandlerRegistration... registrations) { + return new CompositeHandlerRegistration(Arrays.asList(registrations)); + } + + @Override + public void removeHandler() { + for(HandlerRegistration registration : registrations) { + registration.removeHandler(); + } + } +} diff --git a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/editor/IconEditorWidget.java b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/editor/IconEditorWidget.java new file mode 100644 index 0000000..fb61230 --- /dev/null +++ b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/editor/IconEditorWidget.java @@ -0,0 +1,366 @@ +package org.ovirt.engine.ui.common.widget.editor; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.Element; +import com.google.gwt.editor.client.LeafValueEditor; +import com.google.gwt.event.dom.client.ChangeEvent; +import com.google.gwt.event.dom.client.ChangeHandler; +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.KeyCodes; +import com.google.gwt.event.dom.client.KeyDownHandler; +import com.google.gwt.event.dom.client.KeyPressEvent; +import com.google.gwt.event.dom.client.KeyPressHandler; +import com.google.gwt.event.dom.client.KeyUpHandler; +import com.google.gwt.event.logical.shared.HasValueChangeHandlers; +import com.google.gwt.event.logical.shared.ValueChangeEvent; +import com.google.gwt.event.logical.shared.ValueChangeHandler; +import com.google.gwt.event.shared.HandlerRegistration; +import com.google.gwt.resources.client.CssResource; +import com.google.gwt.safehtml.shared.SafeHtml; +import com.google.gwt.safehtml.shared.SafeHtmlBuilder; +import com.google.gwt.safehtml.shared.SafeHtmlUtils; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.uibinder.client.UiHandler; +import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.FileUpload; +import com.google.gwt.user.client.ui.HTML; +import com.google.gwt.user.client.ui.HTMLPanel; +import com.google.gwt.user.client.ui.Image; +import com.google.gwt.user.client.ui.UIObject; +import com.google.gwt.user.client.ui.Widget; +import org.ovirt.engine.ui.common.CommonApplicationConstants; +import org.ovirt.engine.ui.common.CommonApplicationResources; +import org.ovirt.engine.ui.common.CommonApplicationTemplates; +import org.ovirt.engine.ui.common.editor.UiCommonEditor; +import org.ovirt.engine.ui.common.widget.AbstractValidatedWidget; +import org.ovirt.engine.ui.common.widget.dialog.InfoIcon; +import org.ovirt.engine.ui.uicommonweb.models.vms.IconWithDefault; +import org.ovirt.engine.ui.uicommonweb.validation.IconValidation; +import org.ovirt.engine.ui.uicommonweb.validation.ValidationResult; + +import javax.inject.Inject; +import java.util.List; + +/** + * Icon editor. It allows to set custom icon to VM-like entities. + */ +public class IconEditorWidget extends AbstractValidatedWidget + implements LeafValueEditor<IconWithDefault>, + HasValueChangeHandlers<IconWithDefault>, + UiCommonEditor<IconWithDefault> { + + interface ViewUiBinder extends UiBinder<HTMLPanel, IconEditorWidget> { + ViewUiBinder uiBinder = GWT.create(ViewUiBinder.class); + } + + protected interface Style extends CssResource { + String grey(); + String iconImageDisabled(); + } + + @UiField + protected Style style; + + @UiField + protected Image image; + + @UiField + protected Button uploadButton; + + @UiField(provided = true) + protected InfoIcon uploadInfoIcon; + + @UiField + protected FileUpload fileUpload; + + @UiField + protected Button defaultButton; + + @UiField + protected HTML errorMessageHtml; + + protected final CommonApplicationTemplates templates; + + /** + * current value, the visible image <br/> + * in dataUri format + */ + private String icon; + + /** + * default value (given by OS of VM) <br/> + * in dataUri format + */ + private String defaultIcon; + + /** + * relates to {@link com.google.gwt.user.client.ui.HasEnabled} implementation + */ + private boolean enabled; + + /** + * relates to {@link org.ovirt.engine.ui.common.widget.HasAccess} implementation + */ + private boolean accessible; + + @Inject + public IconEditorWidget(CommonApplicationConstants constants, + CommonApplicationTemplates templates, + CommonApplicationResources resources) { + this.templates = templates; + uploadInfoIcon = new InfoIcon( + SafeHtmlUtils.fromTrustedString(constants.iconLimitationsIconVmPopup()), resources); + initWidget(ViewUiBinder.uiBinder.createAndBindUi(this)); + fileUpload.getElement().setAttribute("accept", "image/gif,image/jpeg,image/png"); //$NON-NLS-1$ //$NON-NLS-2$ + fileUpload.addChangeHandler(new ChangeHandler() { + @Override + public void onChange(ChangeEvent event) { + IconEditorWidget.this.readUploadedIconFile(); + } + }); + fileUpload.getElement().setTabIndex(-1); // can be moved to *.ui.xml file in form of attribute `tabIndex="-1"` since GWT 2.7.0 + + KeyPressHandler preventEnterKeyPressHandler = createPreventEnterKeyPressHandler(); + uploadButton.addKeyPressHandler(preventEnterKeyPressHandler); + defaultButton.addKeyPressHandler(preventEnterKeyPressHandler); + + setEnabled(true); + setAccessible(true); + } + + private KeyPressHandler createPreventEnterKeyPressHandler() { + return new KeyPressHandler() { + @Override + public void onKeyPress(KeyPressEvent event) { + if (!event.isAnyModifierKeyDown() + && event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER + && event.getUnicodeCharCode() == 0) { + event.preventDefault(); + } + } + }; + } + + @Override + public void setValue(IconWithDefault value) { + final IconWithDefault oldPair = getValue(); + if (value == null) { + defaultIcon = null; + setIcon(null); + } else { + defaultIcon = value.getDefaultIcon(); + setIcon(value.getIcon()); + } + final IconWithDefault newPair = getValue(); + ValueChangeEvent.fireIfNotEqual(this, oldPair, newPair); + } + + @Override + public IconWithDefault getValue() { + if (icon == null || defaultButton == null) { + return null; + } + return new IconWithDefault(icon, defaultIcon); + } + + @Override + protected Widget getValidatedWidget() { + return this; + } + + @UiHandler("uploadButton") + void onUploadIconButton(ClickEvent event) { + clickElement(fileUpload.getElement()); + } + + @UiHandler("defaultButton") + void onDefaultIconButton(ClickEvent event) { + setIconAndFireChangeEvent(defaultIcon); + } + + /** + * There is FileUpload#click() method since GWT 2.7.0. + */ + native void clickElement(Element element) /*-{ + element.click(); + }-*/; + + protected void setIcon(String icon) { + this.icon = icon; + if (icon != null) { + final ValidationResult validation = (new IconValidation()).validate(icon); + updateErrorIconLabel(validation); + image.getElement().setAttribute("src", icon); //$NON-NLS-1$ + } else { + updateErrorIconLabel(ValidationResult.ok()); + image.getElement().setAttribute("src", ""); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + protected void setIconAndFireChangeEvent(String icon) { + final IconWithDefault oldPair = getValue(); + setIcon(icon); + final IconWithDefault newPair = getValue(); + ValueChangeEvent.fireIfNotEqual(this, oldPair, newPair); + } + + private void updateErrorIconLabel(ValidationResult validation) { + if (!validation.getSuccess() && validation.getReasons().isEmpty()) { + throw new IllegalArgumentException("Unsuccessful validation without any reason not allowed."); //$NON-NLS-1$ + } + updateErrorIconLabel(validation.getReasons()); + } + + private void updateErrorIconLabel(List<String> reasons) { + if (reasons.isEmpty()) { + errorMessageHtml.setHTML(SafeHtmlUtils.EMPTY_SAFE_HTML); + } else { + final SafeHtml htmlReasons = toUnorderedList(reasons); + errorMessageHtml.setHTML(htmlReasons); + } + } + + private SafeHtml toUnorderedList(List<String> stringItems) { + SafeHtmlBuilder builder = new SafeHtmlBuilder(); + for (String stringItem : stringItems) { + builder.append(templates.listItem(stringItem)); + } + return templates.unorderedList(builder.toSafeHtml()); + } + + native void readUploadedIconFile() /*-{ + var inputFileElement = th...@org.ovirt.engine.ui.common.widget.editor.IconEditorWidget::fileuplo...@com.google.gwt.user.client.ui.FileUpload::getElement()(); + var self = this; + var javaCallback = $entry(function (dataUri) { + return se...@org.ovirt.engine.ui.common.widget.editor.IconEditorWidget::setIconAndFireChangeEvent(Ljava/lang/String;)(dataUri); + }); + if (inputFileElement.files.length > 0) { + var file = inputFileElement.files[0]; + var fileReader = new FileReader(); + fileReader.onload = onFileRead; + fileReader.readAsDataURL(file); + } + + function onFileRead(event) { + var iconDataUri = event.target.result; + javaCallback(iconDataUri); + } + }-*/; + + + /* + * see com.google.gwt.user.client.ui.ValueListBox.addValueChangeHandler() + */ + @Override + public HandlerRegistration addValueChangeHandler(ValueChangeHandler<IconWithDefault> handler) { + return this.addHandler(handler, ValueChangeEvent.getType()); + } + + @Override + public void markAsValid() { + super.markAsValid(); + getValidatedWidgetStyle().setBorderColor("transparent"); //$NON-NLS-1$ + } + + @Override + public void markAsInvalid(List<String> validationHints) { + super.markAsInvalid(validationHints); + updateErrorIconLabel(validationHints); + } + + @Override + public LeafValueEditor<IconWithDefault> getActualEditor() { + return this; + } + + @Override + public int getTabIndex() { + return uploadButton.getTabIndex(); + } + + @Override + public void setAccessKey(char key) { + uploadButton.setAccessKey(key); + } + + @Override + public void setFocus(boolean focused) { + uploadButton.setFocus(focused); + } + + @Override + public void setTabIndex(int index) { + uploadButton.setTabIndex(index); + defaultButton.setTabIndex(index); + } + + @Override + public void disable(String disabilityHint) { + setEnabled(false, disabilityHint); + } + + @Override + public boolean isEnabled() { + return enabled; + } + + @Override + public void setEnabled(boolean enabled) { + setEnabled(enabled, ""); //$NON-NLS-1$ + } + + protected void setEnabled(boolean enabled, String hint) { + this.enabled = enabled; + uploadButton.setEnabled(enabled); + uploadButton.setTitle(hint); + defaultButton.setEnabled(enabled); + defaultButton.setTitle(hint); + ensureStyleNamePresent(errorMessageHtml, !enabled, style.grey()); + errorMessageHtml.setTitle(hint); + ensureStyleNamePresent(image, !enabled, style.iconImageDisabled()); + image.setTitle(hint); + } + + private static void ensureStyleNamePresent(UIObject object, boolean styleNameExists, String styleName) { + if (styleNameExists) { + object.addStyleName(styleName); + } else { + object.removeStyleName(styleName); + } + } + + @Override + public boolean isAccessible() { + return accessible; + } + + @Override + public void setAccessible(boolean accessible) { + this.accessible = accessible; + setVisible(accessible); + } + + @Override + public HandlerRegistration addKeyDownHandler(KeyDownHandler handler) { + return CompositeHandlerRegistration.of( + uploadButton.addKeyDownHandler(handler), + defaultButton.addKeyDownHandler(handler)); + } + + @Override + public HandlerRegistration addKeyPressHandler(KeyPressHandler handler) { + return CompositeHandlerRegistration.of( + uploadButton.addKeyPressHandler(handler), + defaultButton.addKeyPressHandler(handler)); + } + + @Override + public HandlerRegistration addKeyUpHandler(KeyUpHandler handler) { + return CompositeHandlerRegistration.of( + uploadButton.addKeyUpHandler(handler), + defaultButton.addKeyUpHandler(handler)); + } + + +} diff --git a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/editor/IconEditorWidget.ui.xml b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/editor/IconEditorWidget.ui.xml new file mode 100644 index 0000000..ec7142f --- /dev/null +++ b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/editor/IconEditorWidget.ui.xml @@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent"> +<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder" + xmlns:g="urn:import:com.google.gwt.user.client.ui" + xmlns:d="urn:import:org.ovirt.engine.ui.common.widget.dialog"> + + <ui:with field='resources' type='org.ovirt.engine.ui.common.CommonApplicationResources'/> + <ui:with field='constants' type='org.ovirt.engine.ui.common.CommonApplicationConstants'/> + + <ui:style type="org.ovirt.engine.ui.common.widget.editor.IconEditorWidget.Style"> + + .iconImage { + display: block; + height: 120px; + width: 150px; + border: thin solid rgb(211, 211, 211); + } + + .iconImageDisabled { + opacity: 0.4; + } + + .horizontal-spacing > tbody > tr > td { + padding-left: 10px; + } + + .inline-block { + display: inline-block + } + + .iconButton { + margin: 0px 0px 10px; + min-width: 110px; + } + + .iconButton:focus { + outline: thin black dotted; + } + + .hidden { + display: none; + } + + .grey { + color: grey; + } + + .iconInfoIcon { + margin: 0px 7px; + } + + .iconErrorHtml { + position: relative; + bottom: 0px; + left: 0px; + margin: 10px; + } + + .iconErrorHtml ul { + padding-left: 13px; + } + + .no-border { + border: none; + } + + </ui:style> + + + <g:HTMLPanel addStyleNames="{style.no-border}"> + <g:HorizontalPanel addStyleNames="{style.horizontal-spacing}"> + <g:Image ui:field="image" addStyleNames="{style.iconImage}"/> + <g:VerticalPanel> + <g:FlowPanel> + <g:Button ui:field="uploadButton" text="{constants.uploadIconVmPopup}" + addStyleNames="{style.inline-block} {style.iconButton}"/> + <d:InfoIcon ui:field="uploadInfoIcon" + addStyleNames="{style.inline-block} {style.iconInfoIcon}"/> + <g:HTMLPanel addStyleNames="{style.hidden}"> + <g:FileUpload ui:field="fileUpload"/> + </g:HTMLPanel> + </g:FlowPanel> + <g:Button ui:field="defaultButton" text="{constants.useDefaultIconVmPopup}" + addStyleNames="{style.iconButton}"/> + </g:VerticalPanel> + </g:HorizontalPanel> + <g:HTML ui:field="errorMessageHtml" addStyleNames="{style.iconErrorHtml}"/> + </g:HTMLPanel> + +</ui:UiBinder> diff --git a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/popup/AbstractVmPopupWidget.java b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/popup/AbstractVmPopupWidget.java index 2732ef2..c17661f 100644 --- a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/popup/AbstractVmPopupWidget.java +++ b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/popup/AbstractVmPopupWidget.java @@ -28,6 +28,7 @@ import com.google.gwt.user.client.ui.RadioButton; import com.google.gwt.user.client.ui.ValueLabel; import com.google.gwt.user.client.ui.Widget; + import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -72,6 +73,7 @@ import org.ovirt.engine.ui.common.widget.dialog.tab.DialogTab; import org.ovirt.engine.ui.common.widget.dialog.tab.DialogTabPanel; import org.ovirt.engine.ui.common.widget.editor.EntityModelCellTable; +import org.ovirt.engine.ui.common.widget.editor.IconEditorWidget; import org.ovirt.engine.ui.common.widget.editor.ListModelListBoxEditor; import org.ovirt.engine.ui.common.widget.editor.ListModelListBoxOnlyEditor; import org.ovirt.engine.ui.common.widget.editor.ListModelTypeAheadChangeableListBoxEditor; @@ -871,6 +873,15 @@ @Ignore protected DialogTabPanel mainTabPanel; + // ==Icon Tab== + @UiField + @Ignore + protected DialogTab iconTab; + + @UiField + @Path("icon.entity") + protected IconEditorWidget iconEditorWidget; + private UnitVmModel unitVmModel; private final Driver driver = GWT.create(Driver.class); @@ -1000,6 +1011,7 @@ getTabNameMapping().put(TabName.POOL_TAB, this.poolTab); getTabNameMapping().put(TabName.RESOURCE_ALLOCATION_TAB, this.resourceAllocationTab); getTabNameMapping().put(TabName.SYSTEM_TAB, this.systemTab); + getTabNameMapping().put(TabName.ICON_TAB, this.iconTab); } private void initDetachableFields() { @@ -1508,6 +1520,9 @@ numOfSocketsEditorWithDetachable.setLabel(constants.numOfSockets()); emulatedMachine.setLabel(constants.emulatedMachineLabel()); customCpu.setLabel(constants.cpuModelLabel()); + + // Icon tab + iconTab.setLabel(constants.iconTabVmPopup()); } protected void applyStyles() { @@ -2012,6 +2027,10 @@ // ==Custom Properties Tab== nextTabIndex = customPropertiesTab.setTabIndexes(nextTabIndex); + // ==Icon Tab== + nextTabIndex = iconTab.setTabIndexes(nextTabIndex); + iconEditorWidget.setTabIndex(nextTabIndex++); + return nextTabIndex; } @@ -2072,7 +2091,8 @@ rngDeviceTab, highAvailabilityTab, poolTab, - systemTab); + systemTab, + iconTab); } protected List<Widget> adancedFieldsFromGeneralTab() { @@ -2128,7 +2148,8 @@ highAvailabilityTab, resourceAllocationTab, customPropertiesTab, - rngDeviceTab + rngDeviceTab, + iconTab ); } @@ -2160,7 +2181,8 @@ bootOptionsTab, customPropertiesTab, systemTab, - rngDeviceTab + rngDeviceTab, + iconTab ); } diff --git a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/popup/AbstractVmPopupWidget.ui.xml b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/popup/AbstractVmPopupWidget.ui.xml index 00894e6..724fba3 100644 --- a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/popup/AbstractVmPopupWidget.ui.xml +++ b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/popup/AbstractVmPopupWidget.ui.xml @@ -426,6 +426,42 @@ .overrideMigrationDowntimeContentEditor { width: 20px; } + + .iconImage { + height: 120px; + width: 150px; + border: thin solid rgb(211, 211, 211); + display: block; + } + + .horizontal-spacing > tbody > tr > td { + margin-left: 10px; + } + + .inline-block { + display: inline-block + } + + .iconButton { + margin: 0px 0px 5px; + min-width: 110px; + } + + .hidden { + display: none; + } + + .iconInfoIcon { + margin: 0px 7px; + } + + .iconErrorLabel { + position: relative; + bottom: 0px; + left: 0px; + margin: 10px; + } + </ui:style> <t:DialogTabPanel width="100%" height="100%" ui:field="mainTabPanel"> @@ -787,6 +823,13 @@ </t:content> </t:DialogTab> </t:tab> + <t:tab> + <t:DialogTab ui:field="iconTab"> + <t:content> + <e:IconEditorWidget ui:field="iconEditorWidget" /> + </t:content> + </t:DialogTab> + </t:tab> </t:DialogTabPanel> </ui:UiBinder> diff --git a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/popup/instancetypes/InstanceTypesPopupWidget.java b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/popup/instancetypes/InstanceTypesPopupWidget.java index 8b7beea..3c44d1a 100644 --- a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/popup/instancetypes/InstanceTypesPopupWidget.java +++ b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/popup/instancetypes/InstanceTypesPopupWidget.java @@ -70,6 +70,7 @@ putOne(timeZoneEditorWithInfo, hiddenField()). putOne(startRunningOnPanel, hiddenField()). putOne(spiceCopyPasteEnabledEditor, hiddenField()). - putOne(spiceFileTransferEnabledEditor, hiddenField()); + putOne(spiceFileTransferEnabledEditor, hiddenField()). + putOne(iconTab, hiddenField()); } } diff --git a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/Cloner.java b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/Cloner.java index 45d3365..0ee65be 100644 --- a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/Cloner.java +++ b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/Cloner.java @@ -381,7 +381,6 @@ obj.setVmPoolType(instance.getVmPoolType()); obj.setVdsGroupId(instance.getVdsGroupId()); - obj.setVmPoolType(instance.getVmPoolType()); obj.setParameters(instance.getParameters()); obj.setDefaultEndTime(instance.getDefaultEndTime()); obj.setDefaultStartTime(instance.getDefaultStartTime()); @@ -465,6 +464,7 @@ obj.setPredefinedProperties(instance.getPredefinedProperties()); obj.setUserDefinedProperties(instance.getUserDefinedProperties()); obj.setCustomProperties(instance.getCustomProperties()); + obj.setIconId(instance.getIconId()); return obj; } @@ -514,6 +514,7 @@ obj.setPredefinedProperties(instance.getPredefinedProperties()); obj.setUserDefinedProperties(instance.getUserDefinedProperties()); obj.setCustomProperties(instance.getCustomProperties()); + obj.setIconId(instance.getIconId()); return obj; } diff --git a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/IconCache.java b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/IconCache.java new file mode 100644 index 0000000..5918c94 --- /dev/null +++ b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/IconCache.java @@ -0,0 +1,162 @@ +package org.ovirt.engine.ui.uicommonweb; + +import org.ovirt.engine.core.common.queries.GetVmIconsParameters; +import org.ovirt.engine.core.common.queries.VdcQueryReturnValue; +import org.ovirt.engine.core.common.queries.VdcQueryType; +import org.ovirt.engine.core.compat.Guid; +import org.ovirt.engine.ui.frontend.AsyncQuery; +import org.ovirt.engine.ui.frontend.Frontend; +import org.ovirt.engine.ui.frontend.INewAsyncCallback; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Bidirectional map Guid <-> String (icon id <-> dataUri icon) + */ +public class IconCache { + + public static final Guid DEFAULT_ICON_ID = Guid.Empty; + private static IconCache instance; + private GuidIconBiDiMap cache = new GuidIconBiDiMap(); + + private IconCache() { + } + + public static IconCache getInstance() { + if (instance == null) { + instance = new IconCache(); + } + return instance; + } + + + /** + * @param iconIds id of requested icon, should not be null, nor the item should be null + * @param callback callback that receives the icon; The callback can be called both synchronously + * (in case the icon is cached locally) and asynchronously (if a query to server + * needs to be done) + */ + public void getOrFetchIcons(final List<Guid> iconIds, final IconsCallback callback) { + assertNotNull(iconIds); + final Map<Guid, String> localResult = getIcons(iconIds); + if (localResult != null) { + callback.onSuccess(localResult); + } else { + Frontend.getInstance().runQuery(VdcQueryType.GetVmIcons, GetVmIconsParameters.getIconsParameters(iconIds), + new AsyncQuery(null, new INewAsyncCallback() { + @Override + public void onSuccess(Object nothing, Object returnValue) { + Map<Guid, String> idToIconMap = + ((VdcQueryReturnValue) returnValue).getReturnValue(); + IconCache.this.cache.putAll(idToIconMap); + final Map<Guid, String> result = IconCache.this.getIcons(iconIds); + callback.onSuccess(result); + } + }) + ); + } + + } + + /** + * @return cached icon or null if icon of given id is not cached + */ + public String getIcon(Guid iconId) { + return cache.getIcon(iconId); + } + + /** + * Sugar for {@link #getOrFetchIcons(List, IconCache.IconsCallback)} + */ + public void getOrFetchIcon(final Guid iconId, final IconCallback callback) { + getOrFetchIcons(Collections.singletonList(iconId), new IconsCallback() { + @Override + public void onSuccess(Map<Guid, String> idToIconMap) { + final String icon = idToIconMap.get(iconId); + callback.onSuccess(icon); + } + }); + } + + private void assertNotNull(List list) { + if (list == null) { + throw new IllegalArgumentException("Argument should not be null."); //$NON-NLS-1$ + } + for (Object item : list) { + if (item == null) { + throw new IllegalArgumentException("Argument should not contain null."); //$NON-NLS-1$ + } + } + } + + /** + * If the nullableId is {@code null} it return id of default icon, + * otherwise it behaves like identity function. + * @return never null + */ + public static Guid defaultIdIfNull(Guid nullableId) { + if (nullableId == null) { + return DEFAULT_ICON_ID; + } + return nullableId; + } + + /** + * @param iconIds requested icon ids + * @return icon ids -> icon data uri + */ + private Map<Guid, String> getIcons(List<Guid> iconIds) { + Map<Guid, String> result = new HashMap<>(); + for (Guid iconId : iconIds) { + final String cachedIcon = cache.getIcon(iconId); + if (cachedIcon == null) { + return null; + } + result.put(iconId, cachedIcon); + } + return result; + } + + public Guid getId(String icon) { + return cache.getId(icon); + } + + public static interface IconsCallback { + public void onSuccess(Map<Guid, String> idToIconMap); + } + + public static interface IconCallback { + public void onSuccess(String icon); + } + + private static class GuidIconBiDiMap { + + private Map<Guid, String> map = new HashMap<>(); + private Map<String, Guid> reverseMap = new HashMap<>(); + + public String getIcon(Guid id) { + return map.get(id); + } + + public Guid getId(String icon) { + return reverseMap.get(icon); + } + + public void put(Guid id, String icon) { + if (id == null || icon == null) { + throw new IllegalArgumentException("Neither 'id' nor 'icon' can be null."); //$NON-NLS-1$ + } + map.put(id, icon); + reverseMap.put(icon, id); + } + + public void putAll(Map<Guid, String> idToIconMap) { + for (Map.Entry<Guid, String> entry : idToIconMap.entrySet()) { + put(entry.getKey(), entry.getValue()); + } + } + } +} diff --git a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/Linq.java b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/Linq.java index a6d2ae0..b354daf 100644 --- a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/Linq.java +++ b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/Linq.java @@ -798,6 +798,10 @@ return result; } + public static <T> List<T> concatTypesafe(List<T>... lists) { + return concat(lists); + } + public static <T> ArrayList<T> union(ArrayList<ArrayList<T>> lists) { HashSet<T> set = new HashSet<T>(); diff --git a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/builders/vm/CoreUnitToVmBaseBuilder.java b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/builders/vm/CoreUnitToVmBaseBuilder.java index a08a737..0f00d98 100644 --- a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/builders/vm/CoreUnitToVmBaseBuilder.java +++ b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/builders/vm/CoreUnitToVmBaseBuilder.java @@ -1,6 +1,7 @@ package org.ovirt.engine.ui.uicommonweb.builders.vm; import org.ovirt.engine.core.common.businessentities.VmBase; +import org.ovirt.engine.ui.uicommonweb.IconCache; import org.ovirt.engine.ui.uicommonweb.models.vms.UnitVmModel; /** @@ -26,6 +27,7 @@ vm.setIsoPath(model.getCdImage().getIsChangable() ? model.getCdImage().getSelectedItem() : ""); //$NON-NLS-1$ vm.setDeleteProtected(model.getIsDeleteProtected().getEntity()); vm.setOsId(model.getOSType().getSelectedItem()); + vm.setIconId(IconCache.getInstance().getId(model.getIcon().getEntity().getIcon())); vm.setVncKeyboardLayout(model.getVncKeyboardLayout().getSelectedItem()); vm.setSerialNumberPolicy(model.getSerialNumberPolicy().getSelectedSerialNumberPolicy()); vm.setCustomSerialNumber(model.getSerialNumberPolicy().getCustomSerialNumber().getEntity()); diff --git a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/builders/vm/CoreVmBaseToUnitBuilder.java b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/builders/vm/CoreVmBaseToUnitBuilder.java index 8410bd8..77fa6d1 100644 --- a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/builders/vm/CoreVmBaseToUnitBuilder.java +++ b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/builders/vm/CoreVmBaseToUnitBuilder.java @@ -8,7 +8,8 @@ public CoreVmBaseToUnitBuilder() { super( new KernelParamsVmBaseToUnitBuilder(), - new SerialNumberPolicyVmBaseToUnitBuilder() + new SerialNumberPolicyVmBaseToUnitBuilder(), + new IconVmBaseToUnitBuilder() ); } diff --git a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/builders/vm/IconVmBaseToUnitBuilder.java b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/builders/vm/IconVmBaseToUnitBuilder.java new file mode 100644 index 0000000..8da2160 --- /dev/null +++ b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/builders/vm/IconVmBaseToUnitBuilder.java @@ -0,0 +1,32 @@ +package org.ovirt.engine.ui.uicommonweb.builders.vm; + +import org.ovirt.engine.core.common.businessentities.VmBase; +import org.ovirt.engine.core.compat.Guid; +import org.ovirt.engine.ui.uicommonweb.IconCache; +import org.ovirt.engine.ui.uicommonweb.builders.Builder; +import org.ovirt.engine.ui.uicommonweb.builders.BuilderList; +import org.ovirt.engine.ui.uicommonweb.dataprovider.AsyncDataProvider; +import org.ovirt.engine.ui.uicommonweb.models.vms.IconWithDefault; +import org.ovirt.engine.ui.uicommonweb.models.vms.UnitVmModel; + +import java.util.Arrays; +import java.util.Map; + +public class IconVmBaseToUnitBuilder implements Builder<VmBase, UnitVmModel> { + + @Override + public void build(final VmBase source, final UnitVmModel destination, final BuilderList<VmBase, UnitVmModel> rest) { + final Guid defaultIconId = AsyncDataProvider.getInstance().getDefaultIconId(source.getOsId()); + final Guid iconId = IconCache.defaultIdIfNull(source.getIconId()); + IconCache.getInstance().getOrFetchIcons(Arrays.asList(iconId, defaultIconId), new IconCache.IconsCallback() { + @Override + public void onSuccess(Map<Guid, String> idToIconMap) { + destination.getIcon().setEntity(new IconWithDefault( + idToIconMap.get(iconId), + idToIconMap.get(defaultIconId) + )); + rest.head().build(source, destination, rest.tail()); + } + }); + } +} diff --git a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/dataprovider/AsyncDataProvider.java b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/dataprovider/AsyncDataProvider.java index 1bcdc19..2e1d99c 100644 --- a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/dataprovider/AsyncDataProvider.java +++ b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/dataprovider/AsyncDataProvider.java @@ -119,6 +119,7 @@ import org.ovirt.engine.core.common.queries.GetTagsByVdsIdParameters; import org.ovirt.engine.core.common.queries.GetTagsByVmIdParameters; import org.ovirt.engine.core.common.queries.GetVmChangedFieldsForNextRunParameters; +import org.ovirt.engine.core.common.queries.GetVmIconsParameters; import org.ovirt.engine.core.common.queries.GetVmTemplateParameters; import org.ovirt.engine.core.common.queries.IdQueryParameters; import org.ovirt.engine.core.common.queries.InterfaceAndIdQueryParameters; @@ -160,6 +161,7 @@ import org.ovirt.engine.ui.frontend.Frontend; import org.ovirt.engine.ui.frontend.IAsyncConverter; import org.ovirt.engine.ui.frontend.INewAsyncCallback; +import org.ovirt.engine.ui.uicommonweb.IconCache; import org.ovirt.engine.ui.uicommonweb.Linq; import org.ovirt.engine.ui.uicommonweb.models.ApplicationModeHelper; import org.ovirt.engine.ui.uicommonweb.models.EntityModel; @@ -211,6 +213,9 @@ // cached OS names private HashMap<Integer, String> osNames; + + // OS default icons + private Map<Integer, Guid> osIdToDefaultIconIdMap; // cached list of os ids private List<Integer> osIds; @@ -285,6 +290,7 @@ } })); initOsNames(); + initOsDefaultIconIds(); initUniqueOsNames(); initLinuxOsTypes(); initWindowsOsTypes(); @@ -3435,6 +3441,17 @@ Frontend.getInstance().runQuery(VdcQueryType.OsRepository, new OsQueryParameters(OsRepositoryVerb.GetOsNames), callback); } + private void initOsDefaultIconIds() { + AsyncQuery callback = new AsyncQuery(); + callback.asyncCallback = new INewAsyncCallback() { + @Override + public void onSuccess(Object model, Object returnValue) { + osIdToDefaultIconIdMap = ((VdcQueryReturnValue) returnValue).getReturnValue(); + } + }; + Frontend.getInstance().runQuery(VdcQueryType.GetVmIcons, GetVmIconsParameters.getDefaultIconsParameters(), callback); + } + private void initOsIds() { osIds = new ArrayList<Integer>(osNames.keySet()); Collections.sort(osIds, new Comparator<Integer>() { @@ -3469,6 +3486,18 @@ return osNames.get(osId); } + /** + * If <code>osId == null</code> then {@link IconCache#DEFAULT_ICON_ID} is returned + * otherwise Guid of default icon for given OS is returned. + */ + public Guid getDefaultIconId(Integer osId) { + if (osId == null) { + return IconCache.DEFAULT_ICON_ID; + } + final Guid defaultIconIdFromDb = osIdToDefaultIconIdMap.get(osId); + return defaultIconIdFromDb != null ? defaultIconIdFromDb : IconCache.DEFAULT_ICON_ID; + } + public boolean hasSpiceSupport(int osId, Version version) { for (Pair<GraphicsType, DisplayType> graphicsDisplayPair : getGraphicsAndDisplays(osId, version)) { if (graphicsDisplayPair.getFirst() == GraphicsType.SPICE) { diff --git a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/TabName.java b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/TabName.java index ccbd4d9..02e81b1 100644 --- a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/TabName.java +++ b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/TabName.java @@ -24,6 +24,7 @@ RESOURCE_ALLOCATION_TAB, SYSTEM_TAB, TAB_RNG, - FIRST_RUN; + FIRST_RUN, + ICON_TAB; } diff --git a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/pools/PoolListModel.java b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/pools/PoolListModel.java index 35fa9a3..8da8fb1 100644 --- a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/pools/PoolListModel.java +++ b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/pools/PoolListModel.java @@ -467,6 +467,9 @@ if (model.getIsNew()) { + if (model.getIcon().getEntity().isCustom()) { + param.setVmIcon(model.getIcon().getEntity().getIcon()); + } Frontend.getInstance().runMultipleAction(VdcActionType.AddVmPoolWithVms, new ArrayList<VdcActionParametersBase>(Arrays.asList(new VdcActionParametersBase[] { param })), new IFrontendMultipleActionAsyncCallback() { diff --git a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/templates/TemplateListModel.java b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/templates/TemplateListModel.java index 2939b97..8b98919 100644 --- a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/templates/TemplateListModel.java +++ b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/templates/TemplateListModel.java @@ -30,6 +30,7 @@ import org.ovirt.engine.ui.frontend.Frontend; import org.ovirt.engine.ui.frontend.INewAsyncCallback; import org.ovirt.engine.ui.uicommonweb.Cloner; +import org.ovirt.engine.ui.uicommonweb.IconCache; import org.ovirt.engine.ui.uicommonweb.Linq; import org.ovirt.engine.ui.uicommonweb.UICommand; import org.ovirt.engine.ui.uicommonweb.builders.BuilderExecutor; @@ -633,6 +634,11 @@ VmTemplate selectedItem = ((TemplateVmModelBehavior) model.getBehavior()).getVmTemplate(); final VmTemplate template = (VmTemplate) Cloner.clone(selectedItem); + final String iconForParameters = IconCache.getInstance().getIcon(selectedItem.getIconId()).equals( + model.getIcon().getEntity().getIcon()) + ? null + : model.getIcon().getEntity().getIcon(); + String name = model.getName().getEntity(); // Check name unicitate. @@ -654,6 +660,7 @@ template.setVmInit(model.getVmInitModel().buildCloudInitParameters(model)); UpdateVmTemplateParameters parameters = new UpdateVmTemplateParameters(template); + parameters.setVmIcon(iconForParameters); parameters.setConsoleEnabled(model.getIsConsoleDeviceEnabled().getEntity()); setVmWatchdogToParams(model, parameters); BuilderExecutor.build(model, parameters, new UnitToGraphicsDeviceParamsBuilder()); diff --git a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/templates/VmBaseListModel.java b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/templates/VmBaseListModel.java index 5953988..af3fda0 100644 --- a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/templates/VmBaseListModel.java +++ b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/templates/VmBaseListModel.java @@ -378,6 +378,7 @@ parameters.setCopyTemplatePermissions(model.getCopyPermissions().getEntity()); parameters.setSoundDeviceEnabled(model.getIsSoundcardEnabled().getEntity()); parameters.setVirtioScsiEnabled(model.getIsVirtioScsiEnabled().getEntity()); + parameters.setVmIcon(model.getIcon().getEntity().getIcon()); setVmWatchdogToParams(model, parameters); setRngDeviceToParams(model, parameters); BuilderExecutor.build(model, parameters, new UnitToGraphicsDeviceParamsBuilder()); diff --git a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/ExistingVmModelBehavior.java b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/ExistingVmModelBehavior.java index 410cb26..0592929 100644 --- a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/ExistingVmModelBehavior.java +++ b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/ExistingVmModelBehavior.java @@ -302,6 +302,7 @@ @Override public void oSType_SelectedItemChanged() { + super.oSType_SelectedItemChanged(); Integer osType = getModel().getOSType().getSelectedItem(); if (osType != null) { diff --git a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/IconWithDefault.java b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/IconWithDefault.java new file mode 100644 index 0000000..598de12 --- /dev/null +++ b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/IconWithDefault.java @@ -0,0 +1,69 @@ +package org.ovirt.engine.ui.uicommonweb.models.vms; + +/** + * Bean holding two icons in dataUri format. + * <br/> + * {@link #icon} - the visible, selected icon + * <br/> + * {@link #defaultIcon} - icon of currently selected os + */ +public class IconWithDefault { + + /** + * Icon - this value changes over time during model-view communication + */ + private final String icon; + + /** + * Default (VM OS based) icon. It allows user to reset custom icon settings. + */ + private final String defaultIcon; + + public IconWithDefault(String icon, String defaultIcon) { + if (icon == null || defaultIcon == null) { + throw new IllegalArgumentException("Both arguments should not be null."); //$NON-NLS-1$ + } + this.icon = icon; + this.defaultIcon = defaultIcon; + } + + public String getIcon() { + return icon; + } + + public String getDefaultIcon() { + return defaultIcon; + } + + public boolean isCustom() { + return !getIcon().equals(getDefaultIcon()); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof IconWithDefault)) return false; + + IconWithDefault that = (IconWithDefault) o; + + if (defaultIcon != null ? !defaultIcon.equals(that.defaultIcon) : that.defaultIcon != null) return false; + if (icon != null ? !icon.equals(that.icon) : that.icon != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = icon != null ? icon.hashCode() : 0; + result = 31 * result + (defaultIcon != null ? defaultIcon.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "IconWithDefault{" + //$NON-NLS-1$ + "icon='" + (icon == null ? "null" : (icon.substring(0, 30) + "…'")) + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + ", defaultIcon='" + ((defaultIcon == null) ? "null" : defaultIcon.substring(0, 30)) + "'" + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + '}'; + } +} diff --git a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/NewTemplateVmModelBehavior.java b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/NewTemplateVmModelBehavior.java index 688bb5f..16c4274 100644 --- a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/NewTemplateVmModelBehavior.java +++ b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/NewTemplateVmModelBehavior.java @@ -288,11 +288,6 @@ } @Override - public void oSType_SelectedItemChanged() - { - } - - @Override public void updateMinAllocatedMemory() { } diff --git a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/NewVmModelBehavior.java b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/NewVmModelBehavior.java index 1ef19db..cdf76cc 100644 --- a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/NewVmModelBehavior.java +++ b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/NewVmModelBehavior.java @@ -34,7 +34,6 @@ public void initialize(SystemTreeItemModel systemTreeSelectedItem) { super.initialize(systemTreeSelectedItem); - getModel().getIsSoundcardEnabled().setIsChangable(true); getModel().getVmType().setIsChangable(true); getModel().getVmId().setIsAvailable(true); @@ -115,8 +114,7 @@ updateTimeZone(template.getTimeZone()); - if (!template.getId().equals(Guid.Empty)) - { + if (!template.getId().equals(Guid.Empty)) { getModel().getStorageDomain().setIsChangable(true); getModel().getProvisioning().setIsChangable(true); @@ -125,9 +123,7 @@ getModel().getAllowConsoleReconnect().setEntity(template.isAllowConsoleReconnect()); initDisks(); updateRngDevice(template.getId()); - } - else - { + } else { getModel().getStorageDomain().setIsChangable(false); getModel().getProvisioning().setIsChangable(false); @@ -220,6 +216,7 @@ @Override public void oSType_SelectedItemChanged() { + super.oSType_SelectedItemChanged(); VmTemplate template = getModel().getTemplateWithVersion().getSelectedItem() == null ? null : getModel().getTemplateWithVersion().getSelectedItem().getTemplateVersion(); diff --git a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/PoolModelBehaviorBase.java b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/PoolModelBehaviorBase.java index ba1fd21..5bffdf9 100644 --- a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/PoolModelBehaviorBase.java +++ b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/PoolModelBehaviorBase.java @@ -203,6 +203,7 @@ @Override public void oSType_SelectedItemChanged() { + super.oSType_SelectedItemChanged(); VmTemplate template = getModel().getTemplateWithVersion().getSelectedItem() == null ? null : getModel().getTemplateWithVersion().getSelectedItem().getTemplateVersion(); diff --git a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/TemplateVmModelBehavior.java b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/TemplateVmModelBehavior.java index 6b86ccf..79a520a 100644 --- a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/TemplateVmModelBehavior.java +++ b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/TemplateVmModelBehavior.java @@ -165,11 +165,6 @@ } @Override - public void oSType_SelectedItemChanged() - { - } - - @Override public void updateMinAllocatedMemory() { } diff --git a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/UnitVmModel.java b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/UnitVmModel.java index 430a13f..a8ee1fd 100644 --- a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/UnitVmModel.java +++ b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/UnitVmModel.java @@ -44,6 +44,7 @@ import org.ovirt.engine.ui.frontend.AsyncQuery; import org.ovirt.engine.ui.frontend.INewAsyncCallback; import org.ovirt.engine.ui.uicommonweb.ICommandTarget; +import org.ovirt.engine.ui.uicommonweb.IconCache; import org.ovirt.engine.ui.uicommonweb.Linq; import org.ovirt.engine.ui.uicommonweb.TypeResolver; import org.ovirt.engine.ui.uicommonweb.UICommand; @@ -68,6 +69,7 @@ import org.ovirt.engine.ui.uicommonweb.validation.I18NExtraNameOrNoneValidation; import org.ovirt.engine.ui.uicommonweb.validation.I18NNameValidation; import org.ovirt.engine.ui.uicommonweb.validation.IValidation; +import org.ovirt.engine.ui.uicommonweb.validation.IconWithDefaultValidation; import org.ovirt.engine.ui.uicommonweb.validation.IntegerValidation; import org.ovirt.engine.ui.uicommonweb.validation.LengthValidation; import org.ovirt.engine.ui.uicommonweb.validation.NoTrimmingWhitespacesValidation; @@ -202,6 +204,19 @@ } /** + * VM icon + */ + private NotChangableForVmInPoolEntityModel<IconWithDefault> icon; + + public EntityModel<IconWithDefault> getIcon() { + return icon; + } + + public void setIcon(NotChangableForVmInPoolEntityModel<IconWithDefault> icon) { + this.icon = icon; + } + + /** * Note: We assume that this method is called only once, on the creation stage of the model. if this assumption is * changed (i.e the VM can attached/detached from a pool after the model is created), this method should be modified */ @@ -285,6 +300,9 @@ // ==Custom Properties Tab== getCustomProperties().setIsChangable(false); getCustomPropertySheet().setIsChangable(false); + + // ==Icon Tab== + getIcon().setIsChangable(false); vmAttachedToPool = true; } @@ -1717,6 +1735,7 @@ getAutoConverge().setItems(Arrays.asList(null, true, false)); setMigrateCompressed(new NotChangableForVmInPoolListModel<Boolean>()); getMigrateCompressed().setItems(Arrays.asList(null, true, false)); + setIcon(new NotChangableForVmInPoolEntityModel<IconWithDefault>()); } public void initialize(SystemTreeItemModel SystemTreeSelectedItem) @@ -2228,7 +2247,29 @@ updateWatchdogModels(osType); vmInitEnabledChanged(); - getInstanceImages().updateActionsAvailability(); + updateIconAccordingToOs(); + } + + private void updateIconAccordingToOs() { + final Integer osId = getOSType().getSelectedItem(); + final Guid defaultIconId = AsyncDataProvider.getInstance().getDefaultIconId(osId); + IconCache.getInstance().getOrFetchIcon(defaultIconId, new IconCache.IconCallback() { + @Override + public void onSuccess(String currentOsDefaultIcon) { + String iconToShow = getIconToShow(currentOsDefaultIcon); + IconWithDefault newIconPair = new IconWithDefault(iconToShow, currentOsDefaultIcon); + getIcon().setEntity(newIconPair); + } + }); + } + + protected String getIconToShow(String currentOsDefaultIcon) { + if (getIcon().getEntity() == null) { + return currentOsDefaultIcon; + } + return getIcon().getEntity().isCustom() + ? getIcon().getEntity().getIcon() + : currentOsDefaultIcon; } private void updateWatchdogModels() { @@ -2714,6 +2755,9 @@ boolean vmInitIsValid = getVmInitModel().validate(); setValidTab(TabName.FIRST_RUN, vmInitIsValid); + getIcon().validateEntity(new IValidation[]{new IconWithDefaultValidation()}); + setValidTab(TabName.ICON_TAB, getIcon().getIsValid()); + boolean isValid = hwPartValid && vmInitIsValid && allTabsValid(); getValid().setEntity(isValid); ValidationCompleteEvent.fire(getEventBus(), this); @@ -2802,6 +2846,7 @@ setValidTab(TabName.CONSOLE_TAB, true); setValidTab(TabName.INITIAL_RUN_TAB, true); setValidTab(TabName.GENERAL_TAB, true); + setValidTab(TabName.ICON_TAB, true); getValid().setEntity(true); } diff --git a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/VmListModel.java b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/VmListModel.java index 0a17523..01fd790 100644 --- a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/VmListModel.java +++ b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/VmListModel.java @@ -61,6 +61,7 @@ import org.ovirt.engine.ui.uicommonweb.Cloner; import org.ovirt.engine.ui.uicommonweb.ConsoleOptionsFrontendPersister.ConsoleContext; import org.ovirt.engine.ui.uicommonweb.ErrorPopupManager; +import org.ovirt.engine.ui.uicommonweb.IconCache; import org.ovirt.engine.ui.uicommonweb.Linq; import org.ovirt.engine.ui.uicommonweb.TagsEqualityComparer; import org.ovirt.engine.ui.uicommonweb.TypeResolver; @@ -1395,15 +1396,15 @@ } protected static VM buildVmOnNewTemplate(UnitVmModel model, VM vm) { - VM tempVar = new VM(); - tempVar.setId(vm.getId()); - BuilderExecutor.build(model, tempVar.getStaticData(), new CommonUnitToVmBaseBuilder()); - BuilderExecutor.build(vm.getStaticData(), tempVar.getStaticData(), + VM resultVm = new VM(); + resultVm.setId(vm.getId()); + BuilderExecutor.build(model, resultVm.getStaticData(), new CommonUnitToVmBaseBuilder()); + BuilderExecutor.build(vm.getStaticData(), resultVm.getStaticData(), new KernelParamsVmBaseToVmBaseBuilder(), new DedicatedVmForVdsVmBaseToVmBaseBuilder(), new MigrationOptionsVmBaseToVmBaseBuilder(), new UsbPolicyVmBaseToVmBaseBuilder()); - return tempVar; + return resultVm; } private void migrate() @@ -1865,8 +1866,9 @@ getcurrentVm().setCreatedByUserId(selectedItem.getCreatedByUserId()); getcurrentVm().setUseLatestVersion(model.getTemplateWithVersion().getSelectedItem().isLatest()); + final String iconForCommandParams = getIconIfChanged(model, getcurrentVm()); if (selectedItem.isRunningOrPaused()) { - AsyncDataProvider.getInstance().getVmChangedFieldsForNextRun(editedVm, getcurrentVm(), getUpdateVmParameters(false), new AsyncQuery(this, + AsyncDataProvider.getInstance().getVmChangedFieldsForNextRun(editedVm, getcurrentVm(), getUpdateVmParameters(false, null), new AsyncQuery(this, new INewAsyncCallback() { @Override public void onSuccess(Object thisModel, Object returnValue) { @@ -1879,6 +1881,7 @@ confirmModel.setChangedFields(changedFields); confirmModel.setCpuPluggable(selectedItem.getCpuPerSocket() == getcurrentVm().getCpuPerSocket() && selectedItem.getNumOfSockets() != getcurrentVm().getNumOfSockets()); + confirmModel.setIcon(iconForCommandParams); confirmModel.getCommands().add(new UICommand("updateExistingVm", VmListModel.this) //$NON-NLS-1$ .setTitle(ConstantsManager.getInstance().getConstants().ok()) @@ -1889,17 +1892,23 @@ setConfirmWindow(confirmModel); } else { - updateExistingVm(false); + updateExistingVm(false, iconForCommandParams); } } })); } else { - updateExistingVm(false); + updateExistingVm(false, iconForCommandParams); } } - private void updateExistingVm(final boolean applyCpuChangesLater) { + private String getIconIfChanged(UnitVmModel model, VM vmOriginalState) { + final String newIcon = model.getIcon().getEntity().getIcon(); + final String originalIcon = IconCache.getInstance().getIcon(vmOriginalState.getStaticData().getIconId()); + return newIcon.equals(originalIcon) ? null : newIcon; + } + + private void updateExistingVm(final boolean applyCpuChangesLater, final String vmIcon) { final UnitVmModel model = (UnitVmModel) getWindow(); if (model.getProgress() != null) @@ -1929,7 +1938,7 @@ if (returnValueBase != null && returnValueBase.getSucceeded()) { VM vm = vmListModel.getcurrentVm(); - VmManagementParametersBase updateVmParams = vmListModel.getUpdateVmParameters(applyCpuChangesLater); + VmManagementParametersBase updateVmParams = vmListModel.getUpdateVmParameters(applyCpuChangesLater, vmIcon); Frontend.getInstance().runAction(VdcActionType.UpdateVm, updateVmParams, new UnitVmModelNetworkAsyncCallback(model, defaultNetworkCreatingManager, vm.getId()), vmListModel); } @@ -1945,12 +1954,12 @@ else { model.startProgress(null); - VmManagementParametersBase updateVmParams = getUpdateVmParameters(applyCpuChangesLater); + VmManagementParametersBase updateVmParams = getUpdateVmParameters(applyCpuChangesLater, vmIcon); Frontend.getInstance().runAction(VdcActionType.UpdateVm, updateVmParams, new UnitVmModelNetworkAsyncCallback(model, defaultNetworkCreatingManager, getcurrentVm().getId()), this); } } - public VmManagementParametersBase getUpdateVmParameters(boolean applyCpuChangesLater) { + public VmManagementParametersBase getUpdateVmParameters(boolean applyCpuChangesLater, String vmIcon) { UnitVmModel model = (UnitVmModel) getWindow(); VmManagementParametersBase updateVmParams = new VmManagementParametersBase(getcurrentVm()); @@ -1961,6 +1970,7 @@ updateVmParams.setVirtioScsiEnabled(model.getIsVirtioScsiEnabled().getEntity()); updateVmParams.setApplyChangesLater(applyCpuChangesLater); updateVmParams.setUpdateNuma(model.isNumaChanged()); + updateVmParams.setVmIcon(vmIcon); setRngDeviceToParams(model, updateVmParams); BuilderExecutor.build(model, updateVmParams, new UnitToGraphicsDeviceParamsBuilder()); @@ -2327,7 +2337,7 @@ } else if ("updateExistingVm".equals(command.getName())) { // $NON-NLS-1$ VmNextRunConfigurationModel model = (VmNextRunConfigurationModel) getConfirmWindow(); - updateExistingVm(model.getApplyCpuLater().getEntity()); + updateExistingVm(model.getApplyCpuLater().getEntity(), model.getIcon()); cancelConfirmation(); } else if (CMD_CONFIGURE_VMS_TO_IMPORT.equals(command.getName())) { diff --git a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/VmModelBehaviorBase.java b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/VmModelBehaviorBase.java index e2b6323..5ff46c5 100644 --- a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/VmModelBehaviorBase.java +++ b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/VmModelBehaviorBase.java @@ -186,7 +186,8 @@ public abstract void provisioning_SelectedItemChanged(); - public abstract void oSType_SelectedItemChanged(); + public void oSType_SelectedItemChanged() { + } public abstract void updateMinAllocatedMemory(); diff --git a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/VmNextRunConfigurationModel.java b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/VmNextRunConfigurationModel.java index 0f6091a..1b9a1e2 100644 --- a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/VmNextRunConfigurationModel.java +++ b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/VmNextRunConfigurationModel.java @@ -10,6 +10,15 @@ private EntityModel<Boolean> applyCpuLater; private boolean cpuPluggable; private List<String> changedFields; + private String icon; + + public String getIcon() { + return icon; + } + + public void setIcon(String icon) { + this.icon = icon; + } public VmNextRunConfigurationModel() { setApplyCpuLater(new EntityModel<Boolean>(false)); diff --git a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/instancetypes/InstanceTypeModelBehaviorBase.java b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/instancetypes/InstanceTypeModelBehaviorBase.java index 5b8fb76..439bb2f 100644 --- a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/instancetypes/InstanceTypeModelBehaviorBase.java +++ b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/instancetypes/InstanceTypeModelBehaviorBase.java @@ -94,11 +94,6 @@ } @Override - public void oSType_SelectedItemChanged() { - - } - - @Override public void updateMinAllocatedMemory() { } diff --git a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/validation/IconValidation.java b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/validation/IconValidation.java new file mode 100644 index 0000000..036265e --- /dev/null +++ b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/validation/IconValidation.java @@ -0,0 +1,112 @@ +package org.ovirt.engine.ui.uicommonweb.validation; + +import com.google.gwt.user.client.ui.Image; +import org.ovirt.engine.ui.uicommonweb.Linq; +import org.ovirt.engine.ui.uicompat.ConstantsManager; + +import java.util.List; + +/** + * It validates icons size, dimensions and file format. + */ +public class IconValidation implements IValidation { + + @Override + public ValidationResult validate(Object iconObject) { + if (iconObject instanceof String) { + final String iconString = (String) iconObject; + return validate(iconString); + } + throw new IllegalArgumentException( + "Illegal argument type: " + iconObject == null ? "null" : iconObject.getClass().toString()); //$NON-NLS-1$ //$NON-NLS-2$ + } + + private ValidationResult validate(String icon) { + ValidationResult typeValidation = validateType(icon); + if (!typeValidation.getSuccess()) { + return typeValidation; + } + ValidationResult dimensionsValidation = validateDimensions(icon); + ValidationResult sizeValidation = validateSize(icon); + if (dimensionsValidation.getSuccess() && sizeValidation.getSuccess()) { + return ValidationResult.ok(); + } + final List<String> reasons = Linq.concatTypesafe(dimensionsValidation.getReasons(), sizeValidation.getReasons()); + return new ValidationResult(false, reasons); + } + + /** + * Max width 150px, max height 120px + */ + private ValidationResult validateDimensions(String icon) { + final int maxWidth = 150; + final int maxHeight = 120; + final Image image = new Image(icon); + boolean valid = image.getWidth() <= maxWidth + && image.getHeight() <= maxHeight; + if (valid) { + return ValidationResult.ok(); + } + return ValidationResult.fail( + ConstantsManager.getInstance().getMessages().iconDimensionsTooLarge( + image.getWidth(), image.getWidth(), maxWidth, maxHeight)); + } + + /** + * The dataUri string has to fit in 32kB. + * Ratio base64encoded/raw data is approx. 4/3. + */ + private ValidationResult validateSize(String icon) { + final int maxEncodedSize = 32 * 1024; + final int maxRawSize = new Double(maxEncodedSize * (3d/4d) / 1024).intValue(); // just estimate for users, in kB + boolean valid = maxEncodedSize > icon.length(); + if (valid) { + return ValidationResult.ok(); + } + return ValidationResult.fail( + ConstantsManager.getInstance().getMessages().iconFileTooLarge(maxRawSize)); + } + + /** + * Magic numbers + * <pre> + * png 89 50 4e 47 0d 0a 1a 0a + * jpg ff d8 ff + * gif 'GIF87a' or 'GIF89a' + * </pre> + */ + private ValidationResult validateType(String icon) { + final String iconBase64Data = dataUriToBase64Data(icon); + final String iconRawData = atob(iconBase64Data); + final String[] magicNumbers = new String[] { + "\u0089\u0050\u004e\u0047\r\n\u001a\n", //$NON-NLS-1$ // png + "\u00ff\u00d8\u00ff", //$NON-NLS-1$ // png + "GIF87a", //$NON-NLS-1$ // gif + "GIF89a" //$NON-NLS-1$ // gif + }; + for (String magicNumber : magicNumbers) { + if (iconRawData.startsWith(magicNumber)) { + return ValidationResult.ok(); + } + } + return ValidationResult.fail(ConstantsManager.getInstance().getMessages().invalidIconFormat("png, jpg, gif")); //$NON-NLS-1$ + } + + private native String atob(String encodedString) /*-{ + return atob(encodedString); + }-*/; + + + /** + * Datauri format: + * <pre> + * data:[<MIME-type>][;charset=<encoding>][;base64],<data> + * </pre> + * @param dataUri + * @return base46 part of datauri + */ + private String dataUriToBase64Data(String dataUri) { + final int commaIndex = dataUri.indexOf(","); //$NON-NLS-1$ + return dataUri.substring(commaIndex + 1); + } +} diff --git a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/validation/IconWithDefaultValidation.java b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/validation/IconWithDefaultValidation.java new file mode 100644 index 0000000..e73b4ed --- /dev/null +++ b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/validation/IconWithDefaultValidation.java @@ -0,0 +1,20 @@ +package org.ovirt.engine.ui.uicommonweb.validation; + +import org.ovirt.engine.ui.uicommonweb.models.vms.IconWithDefault; + +public class IconWithDefaultValidation implements IValidation { + + @Override + public ValidationResult validate(Object value) { + if (value instanceof IconWithDefault) { + final IconWithDefault iconWithDefault = (IconWithDefault) value; + return validate(iconWithDefault); + } + throw new IllegalArgumentException( + "Illegal argument type: " + value == null ? "null" : value.getClass().toString()); //$NON-NLS-1$ //$NON-NLS-2$ + } + + private ValidationResult validate(IconWithDefault iconWithDefault) { + return new IconValidation().validate(iconWithDefault.getIcon()); + } +} diff --git a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/validation/ValidationResult.java b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/validation/ValidationResult.java index 99ded31..fef57e7 100644 --- a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/validation/ValidationResult.java +++ b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/validation/ValidationResult.java @@ -33,7 +33,7 @@ public ValidationResult() { - this(true, new ArrayList<String>()); + this(true, new ArrayList<String>(0)); } public ValidationResult(boolean success, List<String> reasons) { @@ -48,4 +48,12 @@ public static ValidationResult fail(String... reasons) { return new ValidationResult(false, Arrays.asList(reasons)); } + + @Override + public String toString() { + return "ValidationResult{" + //$NON-NLS-1$ + "success=" + privateSuccess + //$NON-NLS-1$ + ", reasons=" + privateReasons + //$NON-NLS-1$ + "}"; //$NON-NLS-1$ + } } diff --git a/frontend/webadmin/modules/uicompat/src/main/java/org/ovirt/engine/ui/uicompat/UIConstants.java b/frontend/webadmin/modules/uicompat/src/main/java/org/ovirt/engine/ui/uicompat/UIConstants.java index 38c2bbe..46bed21 100644 --- a/frontend/webadmin/modules/uicompat/src/main/java/org/ovirt/engine/ui/uicompat/UIConstants.java +++ b/frontend/webadmin/modules/uicompat/src/main/java/org/ovirt/engine/ui/uicompat/UIConstants.java @@ -985,7 +985,7 @@ String diskExistsOnAllActiveStorageDomainsMsg(); @DefaultStringValue("The Template that the VM is based on does not exist on any active Storage Domain") - String noActiveStorageDomainWithTemplateMsg();; + String noActiveStorageDomainWithTemplateMsg(); @DefaultStringValue("Field value should follow: <parameter=value;parameter=value;...>") String fieldValueShouldFollowMsg(); diff --git a/frontend/webadmin/modules/uicompat/src/main/java/org/ovirt/engine/ui/uicompat/UIMessages.java b/frontend/webadmin/modules/uicompat/src/main/java/org/ovirt/engine/ui/uicompat/UIMessages.java index 2b6fdf1..4bc51b3 100644 --- a/frontend/webadmin/modules/uicompat/src/main/java/org/ovirt/engine/ui/uicompat/UIMessages.java +++ b/frontend/webadmin/modules/uicompat/src/main/java/org/ovirt/engine/ui/uicompat/UIMessages.java @@ -417,7 +417,16 @@ @DefaultMessage("Force {0} session") String geoRepForceTitle(String action); + + @DefaultMessage("Icon dimensions are too large: {0}x{1}, maximum allowed: {2}x{3}") + String iconDimensionsTooLarge(int width, int height, int maxWidht, int maxHeight); + @DefaultMessage("Icon file is too large. Maximum size is {0}kB.") + String iconFileTooLarge(int maxSize); + + @DefaultMessage("Unknown file format. Supported formats are {0}.") + String invalidIconFormat(String s); + @DefaultMessage("Value of cluster configuration parameter {0} is empty") String clusterSnapshotOptionValueEmpty(String option); -- To view, visit https://gerrit.ovirt.org/38601 To unsubscribe, visit https://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: Id56aecccef6aab6604de32e27497991ab3713d8d Gerrit-PatchSet: 1 Gerrit-Project: ovirt-engine Gerrit-Branch: master Gerrit-Owner: Jakub Niedermertl <jnied...@redhat.com> _______________________________________________ Engine-patches mailing list Engine-patches@ovirt.org http://lists.ovirt.org/mailman/listinfo/engine-patches