Migration of Action History to vaadin grid (#420)

* Migration of Action History to vaadin Grid
Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>
This commit is contained in:
Marcel Mager
2017-03-24 14:03:39 +01:00
committed by Kai Zimmermann
parent 081c3cccbf
commit c8db41ff85
65 changed files with 4713 additions and 1585 deletions

View File

@@ -8,8 +8,6 @@
*/
package org.eclipse.hawkbit.ui;
import java.io.IOException;
import java.io.InputStream;
import java.util.Locale;
import java.util.Set;
@@ -28,7 +26,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.Resource;
import org.vaadin.spring.events.EventBus;
import com.vaadin.annotations.Title;
@@ -44,7 +41,6 @@ import com.vaadin.spring.navigator.SpringViewProvider;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Component;
import com.vaadin.ui.CssLayout;
import com.vaadin.ui.CustomLayout;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.Panel;
@@ -84,6 +80,8 @@ public class HawkbitUI extends DefaultHawkbitUI implements DetachListener {
@Autowired
private NotificationUnreadButton notificationUnreadButton;
private Label viewTitle;
/**
* Constructor taking the push strategy.
*
@@ -120,47 +118,22 @@ public class HawkbitUI extends DefaultHawkbitUI implements DetachListener {
rootLayout.setSizeFull();
dashboardMenu.init();
dashboardMenu.setResponsive(Boolean.TRUE);
dashboardMenu.setResponsive(true);
final VerticalLayout contentVerticalLayout = new VerticalLayout();
contentVerticalLayout.addComponent(buildHeader());
contentVerticalLayout.setSizeFull();
contentVerticalLayout.setStyleName("main-content");
contentVerticalLayout.addComponent(buildHeader());
contentVerticalLayout.addComponent(buildViewTitle());
final Panel content = buildContent();
contentVerticalLayout.addComponent(content);
contentVerticalLayout.setExpandRatio(content, 1);
rootLayout.addComponent(dashboardMenu);
rootLayout.addComponent(contentVerticalLayout);
final HorizontalLayout viewHeadercontent = new HorizontalLayout();
contentVerticalLayout.addComponent(viewHeadercontent);
viewHeadercontent.setWidth("100%");
viewHeadercontent.setHeight("43px");
viewHeadercontent.addStyleName("view-header-layout");
final Label viewHeader = new Label();
viewHeader.setWidth("100%");
viewHeader.setStyleName("header-content");
viewHeadercontent.addComponent(viewHeader);
viewHeadercontent.addComponent(notificationUnreadButton);
viewHeadercontent.setComponentAlignment(notificationUnreadButton, Alignment.MIDDLE_RIGHT);
final Panel content = new Panel();
content.setSizeFull();
content.setStyleName("view-content");
contentVerticalLayout.addComponent(content);
rootLayout.setExpandRatio(contentVerticalLayout, 1.0F);
contentVerticalLayout.setStyleName("main-content");
contentVerticalLayout.setExpandRatio(content, 1.0F);
rootLayout.setExpandRatio(contentVerticalLayout, 1);
setContent(rootLayout);
final Resource resource = context
.getResource("classpath:/VAADIN/themes/" + UI.getCurrent().getTheme() + "/layouts/footer.html");
try (InputStream resourceStream = resource.getInputStream()) {
final CustomLayout customLayout = new CustomLayout(resourceStream);
customLayout.setSizeUndefined();
contentVerticalLayout.addComponent(customLayout);
} catch (final IOException ex) {
LOG.error("Footer file cannot be loaded", ex);
}
final Navigator navigator = new Navigator(this, content);
navigator.addViewChangeListener(new ViewChangeListener() {
@@ -176,10 +149,10 @@ public class HawkbitUI extends DefaultHawkbitUI implements DetachListener {
final DashboardMenuItem view = dashboardMenu.getByViewName(event.getViewName());
dashboardMenu.postViewChange(new PostViewChangeEvent(view));
if (view == null) {
viewHeader.setCaption(null);
viewTitle.setCaption(null);
return;
}
viewHeader.setCaption(view.getDashboardCaptionLong());
viewTitle.setCaption(view.getDashboardCaptionLong());
notificationUnreadButton.setCurrentView(event.getNewView());
}
});
@@ -200,6 +173,28 @@ public class HawkbitUI extends DefaultHawkbitUI implements DetachListener {
LOG.info("Current locale of the application is : {}", HawkbitCommonUtil.getLocale());
}
private Panel buildContent() {
final Panel content = new Panel();
content.setSizeFull();
content.setStyleName("view-content");
return content;
}
private HorizontalLayout buildViewTitle() {
final HorizontalLayout viewHeadercontent = new HorizontalLayout();
viewHeadercontent.setWidth("100%");
viewHeadercontent.addStyleName("view-header-layout");
viewTitle = new Label();
viewTitle.setWidth("100%");
viewTitle.setStyleName("header-content");
viewHeadercontent.addComponent(viewTitle);
viewHeadercontent.addComponent(notificationUnreadButton);
viewHeadercontent.setComponentAlignment(notificationUnreadButton, Alignment.MIDDLE_RIGHT);
return viewHeadercontent;
}
private Component buildHeader() {
final CssLayout cssLayout = new CssLayout();
cssLayout.setStyleName("view-header");

View File

@@ -467,12 +467,12 @@ public class ArtifactDetailsLayout extends VerticalLayout {
}
private void showMinIcon() {
maxMinButton.togleIcon(FontAwesome.COMPRESS);
maxMinButton.toggleIcon(FontAwesome.COMPRESS);
maxMinButton.setData(Boolean.TRUE);
}
private void showMaxIcon() {
maxMinButton.togleIcon(FontAwesome.EXPAND);
maxMinButton.toggleIcon(FontAwesome.EXPAND);
maxMinButton.setData(Boolean.FALSE);
}

View File

@@ -8,40 +8,70 @@
*/
package org.eclipse.hawkbit.ui.common.grid;
import java.util.Locale;
import org.apache.commons.lang3.ArrayUtils;
import org.eclipse.hawkbit.ui.SpPermissionChecker;
import org.eclipse.hawkbit.ui.components.RefreshableContainer;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
import org.eclipse.hawkbit.ui.utils.SPDateTimeUtil;
import org.eclipse.hawkbit.ui.utils.SPUIDefinitions;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
import org.vaadin.addons.lazyquerycontainer.LazyQueryContainer;
import org.vaadin.spring.events.EventBus;
import org.vaadin.spring.events.EventBus.UIEventBus;
import com.vaadin.data.Container;
import com.vaadin.data.Container.Indexed;
import com.vaadin.data.util.GeneratedPropertyContainer;
import com.vaadin.data.util.converter.Converter;
import com.vaadin.ui.Grid;
/**
* Abstract table class.
* Abstract grid that offers various capabilities (aka support) to offer
* convenient enhancements to the vaadin standard grid.
*
* @param <T>
* The container-type used by the grid
*/
public abstract class AbstractGrid extends Grid implements RefreshableContainer {
private static final long serialVersionUID = 4856562746502217630L;
public abstract class AbstractGrid<T extends Indexed> extends Grid implements RefreshableContainer {
private static final long serialVersionUID = 1L;
protected final VaadinMessageSource i18n;
protected final transient EventBus.UIEventBus eventBus;
protected final SpPermissionChecker permissionChecker;
protected AbstractGrid(final VaadinMessageSource i18n, final UIEventBus eventBus, final SpPermissionChecker permissionChecker) {
private transient AbstractMaximizeSupport maximizeSupport;
private transient AbstractGeneratedPropertySupport generatedPropertySupport;
private transient SingleSelectionSupport singleSelectionSupport;
private transient DetailsSupport detailsSupport;
/**
* Constructor.
*
* @param i18n
* @param eventBus
* @param permissionChecker
*/
protected AbstractGrid(final VaadinMessageSource i18n, final UIEventBus eventBus,
final SpPermissionChecker permissionChecker) {
this.i18n = i18n;
this.eventBus = eventBus;
this.permissionChecker = permissionChecker;
}
/**
* Initializes the grid.
* <p>
*
* <b>NOTE:</b> Sub-classes should configure the grid before calling this
* method (this means: set all support-classes needed, and then call init).
*/
protected void init() {
setSizeFull();
setImmediate(true);
setId(getGridId());
setSelectionMode(SelectionMode.NONE);
if (!hasSingleSelectionSupport()) {
setSelectionMode(SelectionMode.NONE);
}
setColumnReorderingAllowed(true);
addNewContainerDS();
eventBus.subscribe(this);
@@ -52,56 +82,593 @@ public abstract class AbstractGrid extends Grid implements RefreshableContainer
*/
@Override
public void refreshContainer() {
final Container container = getContainerDataSource();
if (!(container instanceof LazyQueryContainer)) {
final Indexed container = getContainerDataSource();
if (hasGeneratedPropertySupport()
&& getGeneratedPropertySupport().getRawContainer() instanceof LazyQueryContainer) {
((LazyQueryContainer) getGeneratedPropertySupport().getRawContainer()).refresh();
return;
}
((LazyQueryContainer) container).refresh();
if (container instanceof LazyQueryContainer) {
((LazyQueryContainer) container).refresh();
}
}
private void addNewContainerDS() {
final Container container = createContainer();
setContainerDataSource((Indexed) container);
/**
* Creates a new container instance by calling the required
* template-methods.
* <p>
* A new container is created on initialization as well as when container
* content fundamentally changes (e.g. if container content depends on a
* selection as common in master-details relations)
*/
protected void addNewContainerDS() {
final T container = createContainer();
Indexed indexedContainer = container;
if (hasGeneratedPropertySupport()) {
indexedContainer = getGeneratedPropertySupport().decorate(container);
setContainerDataSource(indexedContainer);
getGeneratedPropertySupport().addGeneratedContainerProperties();
} else {
setContainerDataSource(indexedContainer);
}
addContainerProperties();
setColumnExpandRatio();
setColumnProperties();
setColumnHeaderNames();
setColumnsHidable();
addColumnRenderes();
setColumnExpandRatio();
setHiddenColumns();
final CellDescriptionGenerator cellDescriptionGenerator = getDescriptionGenerator();
if (getDescriptionGenerator() != null) {
setCellDescriptionGenerator(cellDescriptionGenerator);
}
// Allow column hiding
for (final Column c : getColumns()) {
c.setHidable(true);
}
setHiddenColumns();
int size = 0;
if (container != null) {
size = container.size();
}
if (size == 0) {
if (indexedContainer != null && indexedContainer.size() == 0) {
setData(SPUIDefinitions.NO_DATA);
}
}
protected abstract Container createContainer();
/**
* Sets the standard behavior of columns to be hidable. If implementors
* needs other behavior they have to concern about it.
*/
protected void setColumnsHidable() {
// Allow column hiding
for (final Column c : getColumns()) {
c.setHidable(true);
}
}
/**
* Enables maximize-support for the grid by setting a MaximizeSupport
* implementation.
*
* @param maximizeSupport
* encapsulates behavior for minimize and maximize.
*/
protected void setMaximizeSupport(final AbstractMaximizeSupport maximizeSupport) {
this.maximizeSupport = maximizeSupport;
}
/**
* Gets the MaximizeSupport implementation describing behavior for minimize
* and maximize.
*
* @return maximizeSupport that encapsulates behavior for minimize and
* maximize.
*/
protected AbstractMaximizeSupport getMaximizeSupport() {
return maximizeSupport;
}
/**
* Checks whether maximize-support is enabled.
*
* @return <code>true</code> if maximize-support is enabled, otherwise
* <code>false</code>
*/
protected boolean hasMaximizeSupport() {
return maximizeSupport != null;
}
/**
* Enables support for generated properties. This implies that the
* standard-container has to be decorated and the generators have to be
* registered for the generated (aka virtual) properties.
*
* @param generatedPropertySupport
* that encapsulates behavior for generated properties
*/
protected void setGeneratedPropertySupport(final AbstractGeneratedPropertySupport generatedPropertySupport) {
this.generatedPropertySupport = generatedPropertySupport;
}
/**
* Gets the GeneratedPropertySupport implementation describing generated
* properties by registering their generators and attaching them to a
* wrapper-container.
*
* @return generatedPropertySupport that encapsulates registration of
* generated properties.
*/
protected AbstractGeneratedPropertySupport getGeneratedPropertySupport() {
return generatedPropertySupport;
}
/**
* Checks whether support for generated properties is enabled.
*
* @return <code>true</code> if support for generated properties is enabled,
* otherwise <code>false</code>
*/
protected boolean hasGeneratedPropertySupport() {
return generatedPropertySupport != null;
}
/**
* Enables single-selection-support for the grid by setting
* SingleSelectionSupport configuration.
*
* @param singleSelectionSupport
* encapsulates behavior for single-selection and offers some
* convenient functionality.
*/
protected void setSingleSelectionSupport(final SingleSelectionSupport singleSelectionSupport) {
this.singleSelectionSupport = singleSelectionSupport;
}
/**
* Gets the SingleSelectionSupport implementation configuring
* single-selection.
*
* @return singleSelectionSupport that configures single-selection.
*/
protected SingleSelectionSupport getSingleSelectionSupport() {
return singleSelectionSupport;
}
/**
* Checks whether single-selection-support is enabled.
*
* @return <code>true</code> if single-selection-support is enabled,
* otherwise <code>false</code>
*/
protected boolean hasSingleSelectionSupport() {
return singleSelectionSupport != null;
}
/**
* Enables details-support for the grid by setting DetailsSupport
* configuration. If details-support is enabled, the grid handles
* details-data that depends on a master-selection.
*
* @param detailsSupport
* encapsulates behavior for changes of master-selection.
*/
protected void setDetailsSupport(final DetailsSupport detailsSupport) {
this.detailsSupport = detailsSupport;
}
/**
* Gets the DetailsSupport implementation configuring master-details
* relation.
*
* @return detailsSupport that configures master-details relation.
*/
public DetailsSupport getDetailsSupport() {
return detailsSupport;
}
/**
* Checks whether details-support is enabled.
*
* @return <code>true</code> if details-support is enabled, otherwise
* <code>false</code>
*/
public boolean hasDetailsSupport() {
return detailsSupport != null;
}
/**
* Template method invoked by {@link this#addNewContainerDS()} for creating
* a container instance.
*
* @return new container instance used by the grid.
*/
protected abstract T createContainer();
/**
* Template method invoked by {@link this#addNewContainerDS()} for adding
* properties to the container (usually by invoing
* {@link Container#addContainerProperty(Object, Class, Object))})
*/
protected abstract void addContainerProperties();
/**
* Template method invoked by {@link this#addNewContainerDS()} for setting
* the expand ratio of the columns.
*/
protected abstract void setColumnExpandRatio();
/**
* Template method invoked by {@link this#addNewContainerDS()} for setting
* the column names.
*/
protected abstract void setColumnHeaderNames();
protected abstract String getGridId();
/**
* Template method invoked by {@link this#addNewContainerDS()} for setting
* the column properties to the grid.
*/
protected abstract void setColumnProperties();
/**
* Template method invoked by {@link this#addNewContainerDS()} for adding
* special column renderers if needed.
*/
protected abstract void addColumnRenderes();
/**
* Template method invoked by {@link this#addNewContainerDS()} that hides
* columns. If a column is hidable and hidden, it can be made visible via
* grid column menu.
*/
protected abstract void setHiddenColumns();
/**
* Template method invoked by {@link this#addNewContainerDS()} for adding a
* CellDescriptionGenerator to the grid.
*/
protected abstract CellDescriptionGenerator getDescriptionGenerator();
/**
* Gets id of the grid.
*
* @return id of the grid
*/
protected abstract String getGridId();
/**
* Resets the default row of the header. This means the current default row
* is removed and replaced with a newly created one.
*
* @return the new and clean header row.
*/
protected HeaderRow resetHeaderDefaultRow() {
getHeader().removeRow(getHeader().getDefaultRow());
final HeaderRow newHeaderRow = getHeader().appendRow();
getHeader().setDefaultRow(newHeaderRow);
return newHeaderRow;
}
/**
* Support for master-details relation for grid. This means that grid
* content (=details) is updated as soon as master-data changes.
*/
public class DetailsSupport {
private Long master;
/**
* Set selected master-data as member of this grid-support (as all
* presented grid-data is related to this master-data) and re-calculate
* grid-container-content.
*
* @param master
* id of selected action
*/
public void populateMasterDataAndRecalculateContainer(final Long master) {
this.master = master;
recalculateContainer();
populateSelection();
}
/**
* Set selected master-data as member of this grid-support (as all
* presented grid-data is related to this master-data) and re-create
* grid-container.
*
* @param master
* id of selected action
*/
public void populateMasterDataAndRecreateContainer(final Long master) {
this.master = master;
recreateContainer();
populateSelection();
}
/**
* Propagates the selection if needed.
*
*/
public void populateSelection() {
if (!hasSingleSelectionSupport()) {
return;
}
if (master == null) {
getSingleSelectionSupport().clearSelection();
return;
}
getSingleSelectionSupport().selectFirstRow();
}
/**
* Gets the master-data id.
*
* @return master-data id
*/
public Long getMasterDataId() {
return master;
}
/**
* Invalidates container-data (but reused container) and refreshes it
* with new details-data for the new selected master-data.
*/
private void recalculateContainer() {
clearSortOrder();
refreshContainer();
}
/**
* Invalidates container and replace it with a fresh instance for the
* new selected master-data.
*/
private void recreateContainer() {
removeAllColumns();
clearSortOrder();
addNewContainerDS();
}
}
/**
* Via implementations of this support capability an expand-mode is provided
* that maximizes the grid size.
*/
protected abstract class AbstractMaximizeSupport {
/**
* Renews the content for maximized layout.
*/
public void createMaximizedContent() {
setMaximizedColumnProperties();
setMaximizedHiddenColumns();
setMaximizedHeaders();
setMaximizedColumnExpandRatio();
}
/**
* Renews the content for minimized layout.
*/
public void createMinimizedContent() {
setColumnProperties();
setHiddenColumns();
setColumnExpandRatio();
}
/**
* Sets the column properties for maximized-state.
*/
protected abstract void setMaximizedColumnProperties();
/**
* Sets the hidden columns for maximized-state.
*/
protected abstract void setMaximizedHiddenColumns();
/**
* Sets additional headers for maximized-state.
*/
protected abstract void setMaximizedHeaders();
/**
* Sets column expand ratio for maximized-state.
*/
protected abstract void setMaximizedColumnExpandRatio();
}
/**
* Grids that are used in conjunction with
* {@link GeneratedPropertyContainer}, might use
* {@link AbstractGeneratedPropertySupport} to get type-save access to the
* raw container as well as the decorated container.
*/
protected abstract class AbstractGeneratedPropertySupport {
/**
* Gives type-save access to the wrapper container the grid works on.
* This wrapper container attaches generated properties that are not
* part of the raw container that encapsulates the database access.
*
* @return decorated container that includes the generated properties as
* well as the native properties.
*/
public abstract GeneratedPropertyContainer getDecoratedContainer();
/**
* Gives type-save access to the wrapped container that binds to the
* database. The grid does not work directly with this container but
* with a container that wraps and decorates it with generated
* properties.
*
* @return raw container that gives access to the native properties.
*/
public abstract T getRawContainer();
/**
* Adds the generated properties to the decorated container. Each
* generated property has to be associated with a property generator
* that is capable to calculate the value of the generated (aka virtual)
* property.
*
* @return decorated container that includes the generated properties
*/
protected abstract GeneratedPropertyContainer addGeneratedContainerProperties();
/**
* Decorates the raw-container by wrapping it.
*
* @param container
* raw-container to be wrapped.
* @return decorated container.
*/
protected GeneratedPropertyContainer decorate(final T container) {
return new GeneratedPropertyContainer(container);
}
}
/**
* Support for single selection on the grid.
*/
protected class SingleSelectionSupport {
public SingleSelectionSupport() {
enable();
}
public final void enable() {
setSelectionMode(SelectionMode.SINGLE);
}
public final void disable() {
setSelectionMode(SelectionMode.NONE);
}
/**
* Selects the first row if available and enabled.
*/
public void selectFirstRow() {
if (!isSingleSelectionModel()) {
return;
}
final Indexed container = getContainerDataSource();
final int size = container.size();
if (size > 0) {
refreshRows(getContainerDataSource().firstItemId());
getSingleSelectionModel().select(getContainerDataSource().firstItemId());
} else {
getSingleSelectionModel().select(null);
}
}
private boolean isSingleSelectionModel() {
return getSelectionModel() instanceof SelectionModel.Single;
}
/**
* Clears the selection.
*/
public void clearSelection() {
if (!isSingleSelectionModel()) {
return;
}
getSingleSelectionModel().select(null);
}
private SelectionModel.Single getSingleSelectionModel() {
return (SelectionModel.Single) getSelectionModel();
}
}
/**
* CellStyleGenerator that concerns about alignment in the grid cells.
*/
protected static class AlignCellStyleGenerator implements CellStyleGenerator {
private static final long serialVersionUID = 5573570647129792429L;
private final String[] left;
private final String[] center;
private final String[] right;
/**
* Constructor.
*
* @param left
* list of propertyIds that should be left-aligned
* @param center
* list of propertyIds that should be center-aligned
* @param right
* list of propertyIds that should be right-aligned
*/
public AlignCellStyleGenerator(final String[] left, final String[] center, final String[] right) {
this.left = left;
this.center = center;
this.right = right;
}
@Override
public String getStyle(final CellReference cellReference) {
if (ArrayUtils.contains(center, cellReference.getPropertyId())) {
return "centeralign";
} else if (ArrayUtils.contains(right, cellReference.getPropertyId())) {
return "rightalign";
} else if (ArrayUtils.contains(left, cellReference.getPropertyId())) {
return "leftalign";
}
return null;
}
}
/**
* Adds a tooltip to the 'Date and time' column in detailed format.
*/
public static class ModifiedTimeTooltipGenerator implements CellDescriptionGenerator {
private static final long serialVersionUID = -6617911967167729195L;
private final String datePropertyId;
/**
* Constructor.
*
* @param datePropertyId
*/
public ModifiedTimeTooltipGenerator(final String datePropertyId) {
this.datePropertyId = datePropertyId;
}
@Override
public String getDescription(final CellReference cell) {
if (!datePropertyId.equals(cell.getPropertyId())) {
return null;
}
final Long timestamp = (Long) cell.getItem().getItemProperty(datePropertyId).getValue();
return SPDateTimeUtil.getFormattedDate(timestamp);
}
}
/**
* Converter that gets time-data as input of type <code>Long</code> and
* converts to a formatted date string.
*/
public class LongToFormattedDateStringConverter implements Converter<String, Long> {
private static final long serialVersionUID = 1247513913478717845L;
@Override
public Long convertToModel(final String value, final Class<? extends Long> targetType, final Locale locale) {
// not needed
return null;
}
@Override
public String convertToPresentation(final Long value, final Class<? extends String> targetType,
final Locale locale) {
return SPDateTimeUtil.getFormattedDate(value, SPUIDefinitions.LAST_QUERY_DATE_FORMAT_SHORT);
}
@Override
public Class<Long> getModelType() {
return Long.class;
}
@Override
public Class<String> getPresentationType() {
return String.class;
}
}
}

View File

@@ -0,0 +1,201 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ui.common.grid;
import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
import org.vaadin.spring.events.EventBus;
import org.vaadin.spring.events.EventBus.UIEventBus;
import com.vaadin.ui.AbstractOrderedLayout;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Grid;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.Layout;
import com.vaadin.ui.VerticalLayout;
/**
* Abstract grid layout class which builds layout with grid {@link AbstractGrid}
* and grid header {@link DefaultGridHeader}.
*/
public abstract class AbstractGridComponentLayout extends VerticalLayout {
private static final long serialVersionUID = -3766179797384539821L;
protected final transient EventBus.UIEventBus eventBus;
protected final VaadinMessageSource i18n;
private AbstractOrderedLayout gridHeader;
private Grid grid;
private transient AbstractFooterSupport footerSupport;
/**
* Constructor.
*
* @param i18n
* @param deploymentManagement
* @param eventBus
* @param notification
* @param managementUIState
*/
public AbstractGridComponentLayout(final VaadinMessageSource i18n, final UIEventBus eventBus) {
super();
this.i18n = i18n;
this.eventBus = eventBus;
}
/**
* Initializes this layout that presents a header and a grid.
*/
protected void init() {
this.gridHeader = createGridHeader();
this.grid = createGrid();
buildLayout();
setSizeFull();
setImmediate(true);
eventBus.subscribe(this);
}
/**
* Layouts header, grid and optional footer.
*/
protected void buildLayout() {
setSizeFull();
setSpacing(true);
setMargin(false);
setStyleName("group");
final VerticalLayout gridHeaderLayout = new VerticalLayout();
gridHeaderLayout.setSizeFull();
gridHeaderLayout.setSpacing(false);
gridHeaderLayout.setMargin(false);
gridHeaderLayout.setStyleName("table-layout");
gridHeaderLayout.addComponent(gridHeader);
gridHeaderLayout.setComponentAlignment(gridHeader, Alignment.TOP_CENTER);
gridHeaderLayout.addComponent(grid);
gridHeaderLayout.setComponentAlignment(grid, Alignment.TOP_CENTER);
gridHeaderLayout.setExpandRatio(grid, 1.0F);
addComponent(gridHeaderLayout);
setComponentAlignment(gridHeaderLayout, Alignment.TOP_CENTER);
setExpandRatio(gridHeaderLayout, 1.0F);
if (hasFooterSupport()) {
final Layout footerLayout = getFooterSupport().createFooterMessageComponent();
addComponent(footerLayout);
setComponentAlignment(footerLayout, Alignment.BOTTOM_CENTER);
}
}
/**
* Registers the selection of this grid as master for another grid that
* displays the details.
*
* @param details
* the details of another grid the selection of this grid should
* be registered for as master.
*/
public void registerDetails(final AbstractGrid<?>.DetailsSupport details) {
grid.addSelectionListener(event -> {
final Long masterId = (Long) event.getSelected().stream().findFirst().orElse(null);
details.populateMasterDataAndRecalculateContainer(masterId);
});
}
/**
* Gets the grid instance displayed and owned by the layout.
*
* @return grid instance displayed and owned by the layout.
*/
public Grid getGrid() {
return grid;
}
/**
* Gets the grid-header instance displayed and owned by the layout.
*
* @return grid-header instance displayed and owned by the layout.
*/
public AbstractOrderedLayout getHeader() {
return gridHeader;
}
/**
* Creates the grid-header instance the layout is responsible for.
*
* @return newly created grid-header instance displayed and owned by the
* layout.
*/
public abstract AbstractOrderedLayout createGridHeader();
/**
* Creates the grid instance the layout is responsible for.
*
* @return newly created grid instance displayed and owned by the layout.
*/
public abstract Grid createGrid();
/**
* Enables footer-support for the grid by setting a FooterSupport
* implementation.
*
* @param footerSupport
* encapsulates footer layout.
*/
public void setFooterSupport(final AbstractFooterSupport footerSupport) {
this.footerSupport = footerSupport;
}
/**
* Gets the FooterSupport implementation describing footer layout.
*
* @return footerSupport that encapsulates footer layout.
*/
public AbstractFooterSupport getFooterSupport() {
return footerSupport;
}
/**
* Checks whether footer-support is enabled.
*
* @return <code>true</code> if footer-support is enabled, otherwise
* <code>false</code>
*/
public boolean hasFooterSupport() {
return footerSupport != null;
}
/**
* If footer support is enabled, the footer is placed below the component
*/
public abstract class AbstractFooterSupport {
/**
* Creates a sub-layout for the footer.
*
* @return the footer sub-layout.
*/
private Layout createFooterMessageComponent() {
final HorizontalLayout footerLayout = new HorizontalLayout();
footerLayout.addComponent(getFooterMessageLabel());
footerLayout.setStyleName(SPUIStyleDefinitions.FOOTER_LAYOUT);
footerLayout.setWidth(100, Unit.PERCENTAGE);
return footerLayout;
}
/**
* Get the count message label.
*
* @return count message
*/
protected abstract Label getFooterMessageLabel();
}
}

View File

@@ -152,7 +152,7 @@ public abstract class AbstractGridHeader extends VerticalLayout {
private void openSearchTextField() {
searchResetIcon.addStyleName(SPUIDefinitions.FILTER_RESET_ICON);
searchResetIcon.togleIcon(FontAwesome.TIMES);
searchResetIcon.toggleIcon(FontAwesome.TIMES);
searchResetIcon.setData(Boolean.TRUE);
searchField.removeStyleName(SPUIDefinitions.FILTER_BOX_HIDE);
searchField.setVisible(true);
@@ -164,7 +164,7 @@ public abstract class AbstractGridHeader extends VerticalLayout {
searchField.addStyleName(SPUIDefinitions.FILTER_BOX_HIDE);
searchField.setVisible(false);
searchResetIcon.removeStyleName(SPUIDefinitions.FILTER_RESET_ICON);
searchResetIcon.togleIcon(FontAwesome.SEARCH);
searchResetIcon.toggleIcon(FontAwesome.SEARCH);
searchResetIcon.setData(Boolean.FALSE);
resetSearchText();

View File

@@ -1,96 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ui.common.grid;
import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions;
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.VerticalLayout;
/**
*
* Abstract grid layout class which builds layout with grid {@link AbstractGrid}
* and table header {@link AbstractGridHeader}.
*
*/
public abstract class AbstractGridLayout extends VerticalLayout {
private static final long serialVersionUID = 8611248179949245460L;
private final AbstractGridHeader tableHeader;
protected final AbstractGrid grid;
protected AbstractGridLayout(final AbstractGridHeader tableHeader, final AbstractGrid grid) {
this.tableHeader = tableHeader;
this.grid = grid;
}
protected void buildLayout() {
setSizeFull();
setSpacing(true);
setMargin(false);
setStyleName("group");
final VerticalLayout tableHeaderLayout = new VerticalLayout();
tableHeaderLayout.setSizeFull();
tableHeaderLayout.setSpacing(false);
tableHeaderLayout.setMargin(false);
tableHeaderLayout.setStyleName("table-layout");
tableHeaderLayout.addComponent(tableHeader);
tableHeaderLayout.setComponentAlignment(tableHeader, Alignment.TOP_CENTER);
tableHeaderLayout.addComponent(grid);
tableHeaderLayout.setComponentAlignment(grid, Alignment.TOP_CENTER);
tableHeaderLayout.setExpandRatio(grid, 1.0F);
addComponent(tableHeaderLayout);
setComponentAlignment(tableHeaderLayout, Alignment.TOP_CENTER);
setExpandRatio(tableHeaderLayout, 1.0F);
if (hasCountMessage()) {
final HorizontalLayout rolloutGroupTargetsCountLayout = createCountMessageComponent();
addComponent(rolloutGroupTargetsCountLayout);
setComponentAlignment(rolloutGroupTargetsCountLayout, Alignment.BOTTOM_CENTER);
}
}
private HorizontalLayout createCountMessageComponent() {
final HorizontalLayout rolloutGroupTargetsCountLayout = new HorizontalLayout();
final Label countMessageLabel = getCountMessageLabel();
countMessageLabel.setId(UIComponentIdProvider.ROLLOUT_GROUP_TARGET_LABEL);
rolloutGroupTargetsCountLayout.addComponent(getCountMessageLabel());
rolloutGroupTargetsCountLayout.setStyleName(SPUIStyleDefinitions.FOOTER_LAYOUT);
rolloutGroupTargetsCountLayout.setWidth("100%");
return rolloutGroupTargetsCountLayout;
}
/**
* Only in rollout group targets view count message is displayed.
*
* @return true if count message has to be displayed
*/
protected abstract boolean hasCountMessage();
/**
* Get the count message label.
*
* @return count message
*/
protected abstract Label getCountMessageLabel();
public AbstractGrid getGrid() {
return grid;
}
}

View File

@@ -0,0 +1,243 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ui.common.grid;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.hawkbit.ui.common.builder.LabelBuilder;
import org.eclipse.hawkbit.ui.components.SPUIButton;
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleSmallNoBorder;
import org.eclipse.hawkbit.ui.management.state.ManagementUIState;
import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions;
import com.vaadin.server.FontAwesome;
import com.vaadin.shared.ui.label.ContentMode;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.VerticalLayout;
/**
* Abstract grid header placed on top of a grid.
*/
public class DefaultGridHeader extends VerticalLayout {
private static final long serialVersionUID = 1921798400953670917L;
private final ManagementUIState managementUIState;
private final String titleText;
private Label title;
private HorizontalLayout titleLayout;
private transient AbstractHeaderMaximizeSupport maximizeSupport;
/**
* Constructor.
*
* @param managementUIState
*/
public DefaultGridHeader(final ManagementUIState managementUIState) {
this(managementUIState, StringUtils.EMPTY);
}
/**
* Constructor.
*
* @param managementUIState
* @param titleText
*/
public DefaultGridHeader(final ManagementUIState managementUIState, final String titleText) {
this.managementUIState = managementUIState;
this.titleText = titleText;
}
/**
* Initializes the header.
*
* @return this DefaultGridHeader in order to allow method chaining
*/
public DefaultGridHeader init() {
buildTitleLabel();
buildTitleLayout();
buildComponent();
return this;
}
/**
* Builds the title label.
*
* @return title-label
*/
protected Label buildTitleLabel() {
// create default title - even shown when no data is available
title = new LabelBuilder().name(titleText).buildCaptionLabel();
title.setImmediate(true);
title.setContentMode(ContentMode.HTML);
return title;
}
/**
* Builds the title layout.
*
* @return title-layout
*/
protected HorizontalLayout buildTitleLayout() {
titleLayout = new HorizontalLayout();
titleLayout.addStyleName(SPUIStyleDefinitions.WIDGET_TITLE);
titleLayout.setSpacing(false);
titleLayout.setMargin(false);
titleLayout.setSizeFull();
titleLayout.addComponent(title);
titleLayout.setComponentAlignment(title, Alignment.TOP_LEFT);
titleLayout.setExpandRatio(title, 0.8F);
if (hasHeaderMaximizeSupport()) {
titleLayout.addComponents(getHeaderMaximizeSupport().maxMinButton);
titleLayout.setComponentAlignment(getHeaderMaximizeSupport().maxMinButton, Alignment.TOP_RIGHT);
titleLayout.setExpandRatio(getHeaderMaximizeSupport().maxMinButton, 0.2F);
}
return titleLayout;
}
/**
* Builds the layout for the header component.
*/
protected void buildComponent() {
addComponent(titleLayout);
setComponentAlignment(titleLayout, Alignment.TOP_LEFT);
setWidth(100, Unit.PERCENTAGE);
setImmediate(true);
addStyleName("action-history-header");
addStyleName("bordered-layout");
addStyleName("no-border-bottom");
}
/**
* Enables maximize-support for the header by setting a
* HeaderMaximizeSupport implementation.
*
* @param maximizeSupport
* encapsulates layout of min-max-button and behavior for
* minimize and maximize.
*/
public void setHeaderMaximizeSupport(final AbstractHeaderMaximizeSupport maximizeSupport) {
this.maximizeSupport = maximizeSupport;
}
/**
* Gets the HeaderMaximizeSupport implementation describing behavior for
* minimize and maximize.
*
* @return maximizeSupport that encapsulates behavior for minimize and
* maximize.
*/
public AbstractHeaderMaximizeSupport getHeaderMaximizeSupport() {
return maximizeSupport;
}
/**
* Checks whether maximize-support is enabled.
*
* @return <code>true</code> if maximize-support is enabled, otherwise
* <code>false</code>
*/
public boolean hasHeaderMaximizeSupport() {
return maximizeSupport != null;
}
/**
* Updates the title of the header.
*
* @param newTitle
*/
public void updateTitle(final String newTitle) {
title.setValue(newTitle);
}
/**
* The Implemented capability offers a button that triggers minimization and
* maximization.
*/
public abstract class AbstractHeaderMaximizeSupport {
private final SPUIButton maxMinButton;
/**
* Constructor.
*
* @param maximizeButtonId
*/
protected AbstractHeaderMaximizeSupport(final String maximizeButtonId) {
maxMinButton = createMinMaxButton(maximizeButtonId);
// listener for maximizing action history
maxMinButton.addClickListener(event -> maxMinButtonClicked());
}
/**
* Invoked when min-max-button is pressed.
*/
private void maxMinButtonClicked() {
final Boolean flag = (Boolean) maxMinButton.getData();
if (flag == null || Boolean.FALSE.equals(flag)) {
// Clicked on max Icon
showMinIcon();
maximize();
managementUIState.setActionHistoryMaximized(true);
} else {
// Clicked on min icon
showMaxIcon();
minimize();
managementUIState.setActionHistoryMaximized(false);
}
}
/**
* Additional actions for maximize operation might be performed by this
* method.
*/
protected abstract void maximize();
/**
* Additional actions for minimize operation might be performed by this
* method.
*/
protected abstract void minimize();
/**
* Creates a min-max-button instance.
*
* @param buttonId
* the button id for the min-max-button
* @return newly cretaed min-max-button
*/
protected SPUIButton createMinMaxButton(final String buttonId) {
return (SPUIButton) SPUIComponentProvider.getButton(buttonId, "", "Maximize", null, true,
FontAwesome.EXPAND, SPUIButtonStyleSmallNoBorder.class);
}
/**
* Styles min-max-button icon with minimize decoration
*/
public void showMinIcon() {
maxMinButton.toggleIcon(FontAwesome.COMPRESS);
maxMinButton.setDescription("Minimize");
maxMinButton.setData(Boolean.TRUE);
}
/**
* Styles min-max-button icon with maximize decoration
*/
public void showMaxIcon() {
maxMinButton.toggleIcon(FontAwesome.EXPAND);
maxMinButton.setDescription("Maximize");
maxMinButton.setData(Boolean.FALSE);
}
}
}

View File

@@ -258,7 +258,7 @@ public abstract class AbstractTableHeader extends VerticalLayout {
private void openSearchTextField() {
searchResetIcon.addStyleName(SPUIDefinitions.FILTER_RESET_ICON);
searchResetIcon.togleIcon(FontAwesome.TIMES);
searchResetIcon.toggleIcon(FontAwesome.TIMES);
searchResetIcon.setData(Boolean.TRUE);
searchField.removeStyleName(SPUIDefinitions.FILTER_BOX_HIDE);
searchField.focus();
@@ -268,7 +268,7 @@ public abstract class AbstractTableHeader extends VerticalLayout {
searchField.setValue("");
searchField.addStyleName(SPUIDefinitions.FILTER_BOX_HIDE);
searchResetIcon.removeStyleName(SPUIDefinitions.FILTER_RESET_ICON);
searchResetIcon.togleIcon(FontAwesome.SEARCH);
searchResetIcon.toggleIcon(FontAwesome.SEARCH);
searchResetIcon.setData(Boolean.FALSE);
resetSearchText();
}
@@ -297,12 +297,12 @@ public abstract class AbstractTableHeader extends VerticalLayout {
}
private void showMinIcon() {
maxMinIcon.togleIcon(FontAwesome.COMPRESS);
maxMinIcon.toggleIcon(FontAwesome.COMPRESS);
maxMinIcon.setData(Boolean.TRUE);
}
private void showMaxIcon() {
maxMinIcon.togleIcon(FontAwesome.EXPAND);
maxMinIcon.toggleIcon(FontAwesome.EXPAND);
maxMinIcon.setData(Boolean.FALSE);
}

View File

@@ -37,7 +37,7 @@ public class BaseUIEntityEvent<T extends BaseEntity> {
/**
* Base entity event
*
*
* @param eventType
* the event type
* @param entity
@@ -55,7 +55,7 @@ public class BaseUIEntityEvent<T extends BaseEntity> {
/**
* Base entity event
*
*
* @param eventType
* the event type
* @param entityIds
@@ -74,6 +74,10 @@ public class BaseUIEntityEvent<T extends BaseEntity> {
return entity;
}
public Collection<Long> getEntityIds() {
return entityIds;
}
public BaseEntityEventType getEventType() {
return eventType;
}
@@ -81,7 +85,7 @@ public class BaseUIEntityEvent<T extends BaseEntity> {
/**
* Checks if the remote event is the same as this UI event. Then maybe you
* can skip the remote event because it is already executed.
*
*
* @param tenantAwareEvent
* the remote event
* @return {@code true} match ; {@code false} not match

View File

@@ -93,7 +93,7 @@ public class NotificationUnreadButton extends Button {
notificationsWindow.setClosable(true);
notificationsWindow.setResizable(false);
notificationsWindow.setDraggable(false);
notificationsWindow.setId(UIComponentIdProvider.NOTIFICATION_UNREAD_POPUP_id);
notificationsWindow.setId(UIComponentIdProvider.NOTIFICATION_UNREAD_POPUP_ID);
notificationsWindow.addCloseListener(event -> refreshCaption());
notificationsWindow.addBlurListener(this::closeWindow);
}

View File

@@ -27,12 +27,12 @@ public class SPUIButton extends Button {
}
/**
* Toogle Icon on action.
*
* Toggle Icon on action.
*
* @param icon
* as Resource
*/
public void togleIcon(final Resource icon) {
public void toggleIcon(final Resource icon) {
setIcon(icon);
}
}

View File

@@ -0,0 +1,37 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ui.customrenderers.client;
import org.eclipse.hawkbit.ui.customrenderers.client.renderers.FontIconData;
import com.google.web.bindery.event.shared.HandlerRegistration;
import com.vaadin.client.connectors.ClickableRendererConnector;
import com.vaadin.client.renderers.ClickableRenderer.RendererClickHandler;
import com.vaadin.shared.ui.Connect;
import elemental.json.JsonObject;
/**
* A connector for {@link GridButtonRenderer }.
*
*/
@Connect(org.eclipse.hawkbit.ui.customrenderers.renderers.GridButtonRenderer.class)
public class GridButtonRendererConnector extends ClickableRendererConnector<FontIconData> {
private static final long serialVersionUID = 7987417436367399331L;
@Override
public org.eclipse.hawkbit.ui.customrenderers.client.renderers.GridButtonRenderer getRenderer() {
return (org.eclipse.hawkbit.ui.customrenderers.client.renderers.GridButtonRenderer) super.getRenderer();
}
@Override
protected HandlerRegistration addClickHandler(final RendererClickHandler<JsonObject> handler) {
return getRenderer().addClickHandler(handler);
}
}

View File

@@ -0,0 +1,120 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ui.customrenderers.client.renderers;
import java.io.Serializable;
/**
* Data class that transports the icon meta-data to client-side renderer.
*/
public class FontIconData implements Serializable {
/** serialVersionUID. */
private static final long serialVersionUID = 5823318280700107049L;
private String fontIconHtml;
private String style;
private String title;
private String id;
private boolean disabled;
/**
* Gets the html representing the icon.
*
* @return the html representing the icon
*/
public String getFontIconHtml() {
return fontIconHtml;
}
/**
* Sets the html representing the icon.
*
* @param fontIconHtml
* html representing the icon
*/
public void setFontIconHtml(String fontIconHtml) {
this.fontIconHtml = fontIconHtml;
}
/**
* Gets the style.
*
* @return the style
*/
public String getStyle() {
return style;
}
/**
* Sets the style.
*
* @param style
* icon style
*/
public void setStyle(String style) {
this.style = style;
}
/**
* Gets the title shown as tooltip.
*
* @return the title shown as tooltip.
*/
public String getTitle() {
return title;
}
/**
* Sets the title shown as tooltip.
*
* @param title
* shown as tooltip.
*/
public void setTitle(String title) {
this.title = title;
}
/**
* Gets the id for direct access.
*
* @return the id for direct access.
*/
public String getId() {
return id;
}
/**
* Sets the id for direct access.
*
* @param id
* for direct access.
*/
public void setId(String id) {
this.id = id;
}
/**
* Gets the disabled-state of the icon.
*
* @return the disabled-state of the icon.
*/
public boolean isDisabled() {
return disabled;
}
/**
* Sets the disabled-state of the icon.
*
* @param disabled
* disabled-state of the icon.
*/
public void setDisabled(boolean disabled) {
this.disabled = disabled;
}
}

View File

@@ -0,0 +1,61 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ui.customrenderers.client.renderers;
import com.google.gwt.core.shared.GWT;
import com.google.gwt.user.client.ui.Button;
import com.vaadin.client.renderers.ClickableRenderer;
import com.vaadin.client.ui.VButton;
import com.vaadin.client.widget.grid.RendererCellReference;
/**
* Renders button with provided HTML content. Used to display button with icons.
*/
public class GridButtonRenderer extends ClickableRenderer<FontIconData, Button> {
@Override
public Button createWidget() {
Button b = GWT.create(Button.class);
b.addClickHandler(this);
b.setStylePrimaryName("v-nativebutton");
return b;
}
@Override
public void render(final RendererCellReference cell, final FontIconData iconMetadata, final Button button) {
if (iconMetadata.getFontIconHtml() != null) {
button.setHTML(iconMetadata.getFontIconHtml());
}
applyStyles(button, iconMetadata.isDisabled(), iconMetadata.getStyle());
button.getElement().setId(iconMetadata.getId());
button.getElement().setTitle(iconMetadata.getTitle());
button.setEnabled(!iconMetadata.isDisabled());
// this is to allow the button to disappear, if the text is null
button.setVisible(iconMetadata.getFontIconHtml() != null);
}
private static void applyStyles(final Button button, final boolean buttonDisabled, final String additionalStyle) {
button.setStyleName(VButton.CLASSNAME);
button.addStyleName(getStyle("tiny"));
button.addStyleName(getStyle("borderless"));
button.addStyleName(getStyle("button-no-border"));
button.addStyleName(getStyle("action-type-padding"));
button.addStyleName(getStyle(additionalStyle));
if (buttonDisabled) {
button.addStyleName("v-disabled");
}
}
private static String getStyle(final String style) {
return new StringBuilder(style).append(" ").append(VButton.CLASSNAME).append("-").append(style).toString();
}
}

View File

@@ -17,7 +17,7 @@ import com.vaadin.client.ui.VLabel;
import com.vaadin.client.widget.grid.RendererCellReference;
/**
*
*
* Renders label with provided value and style.
*
*/
@@ -33,6 +33,7 @@ public class HtmlLabelRenderer extends WidgetRenderer<String, VLabel> {
Map<String, String> map = formatInput(input);
String value = map.containsKey("value") ? map.get("value") : null;
String style = map.containsKey("style") ? map.get("style") : null;
String title = map.containsKey("title") ? map.get("title") : null;
String id = map.containsKey("id") ? map.get("id") : null;
if (value != null) {
@@ -42,6 +43,7 @@ public class HtmlLabelRenderer extends WidgetRenderer<String, VLabel> {
}
applyStyle(label, style);
label.getElement().setId(id);
label.getElement().setTitle(title);
}
private void applyStyle(VLabel label, String style) {
@@ -57,7 +59,7 @@ public class HtmlLabelRenderer extends WidgetRenderer<String, VLabel> {
return new StringBuilder(style).append(" ").append(VLabel.CLASSNAME).append("-").append(style).toString();
}
private Map<String, String> formatInput(String input) {
private static Map<String, String> formatInput(String input) {
Map<String, String> details = new HashMap<>();
String[] tempData = input.split(",");
for (String statusWithCount : tempData) {

View File

@@ -0,0 +1,101 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ui.customrenderers.renderers;
import java.io.Serializable;
import java.util.Locale;
import org.eclipse.hawkbit.ui.customrenderers.client.renderers.FontIconData;
import org.eclipse.hawkbit.ui.rollout.StatusFontIcon;
import com.vaadin.data.util.converter.Converter;
/**
* Converter that adapts to a model and converts to a grid-button presentation.
*
* @param <T>
* The type the converter adapts to
*/
public abstract class AbstractGridButtonConverter<T> implements Converter<FontIconData, T> {
private static final long serialVersionUID = 1L;
private GridButtonAdapter<T> adapter;
@Override
public T convertToModel(final FontIconData meta, final Class<? extends T> targetType, final Locale locale) {
// not needed
return null;
}
@Override
public FontIconData convertToPresentation(final T status, final Class<? extends FontIconData> targetType,
final Locale locale) {
if (adapter == null) {
throw new IllegalStateException(
"Adapter must be set before usage! Convertion without adapter is not possible!");
}
return createFontIconData(adapter.adapt(status));
}
/**
* Creates a data transport object for the icon meta data.
*
* @param meta
* icon metadata
* @return icon metadata transport object
*/
private static FontIconData createFontIconData(StatusFontIcon meta) {
FontIconData result = new FontIconData();
result.setFontIconHtml(meta.getFontIcon().getHtml());
result.setTitle(meta.getTitle());
result.setStyle(meta.getStyle());
result.setId(meta.getId());
result.setDisabled(meta.isDisabled());
return result;
}
@Override
public Class<FontIconData> getPresentationType() {
return FontIconData.class;
}
/**
* Adds an appropriate adapter to the converter. The adapter is mandatory
* for the converter.
*
* @param adapter
* label-adapter that converts from model to label-presentation.
* @return self for method-chaining
*/
public AbstractGridButtonConverter<T> addAdapter(GridButtonAdapter<T> adapter) {
this.adapter = adapter;
return this;
}
/**
* Adapts from model data to presentation data.
*
* @param <T>
* The type to adapt to
*/
@FunctionalInterface
public interface GridButtonAdapter<T> extends Serializable {
/**
* @param status
* model data
* @return meta representation that is used as input for label
* generation.
*/
StatusFontIcon adapt(final T status);
}
}

View File

@@ -0,0 +1,147 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ui.customrenderers.renderers;
import java.io.Serializable;
import java.util.Locale;
import org.eclipse.hawkbit.ui.rollout.StatusFontIcon;
import com.google.common.base.Strings;
import com.vaadin.data.util.converter.Converter;
/**
* Converter that adapts to a model and converts to a label presentation.
*
* @param <T>
* The type the converter adapts to
*/
public abstract class AbstractHtmlLabelConverter<T> implements Converter<String, T> {
private static final long serialVersionUID = 1L;
private LabelAdapter<T> adapter;
@Override
public T convertToModel(final String value, final Class<? extends T> targetType, final Locale locale) {
// not needed
return null;
}
@Override
public String convertToPresentation(final T status, final Class<? extends String> targetType, final Locale locale) {
return convert(status);
}
@Override
public Class<String> getPresentationType() {
return String.class;
}
/**
* Adds an appropriate adapter to the converter. The adapter is mandatory
* for the converter.
*
* @param adapter
* label-adapter that converts from model to label-presentation.
* @return self for method-chaining
*/
public AbstractHtmlLabelConverter<T> addAdapter(LabelAdapter<T> adapter) {
this.adapter = adapter;
return this;
}
/**
* Converts the model data to a string representation of the presentation
* label that is used on client-side to style the label.
*
* @param status
* model data
* @return string representation of label
*/
private String convert(final T status) {
if (adapter == null) {
throw new IllegalStateException(
"Adapter must be set before usage! Convertion without adapter is not possible!");
}
final StatusFontIcon statusProps = adapter.adapt(status);
// fail fast
if (statusProps == null) {
return "";
}
final String codePoint = getCodePoint(statusProps);
final String title = statusProps.getTitle();
return getStatusLabelDetailsInString(codePoint, statusProps.getStyle(), title, statusProps.getId(),
statusProps.isDisabled());
}
/**
* Creates a key:value map represented by a comma-separated list.
*
* @param fontIcon
* the font representing the icon
* @param style
* the style
* @param title
* the title shown as tooltip
* @param id
* the id for direct access
* @param disabled
* disabled-state of the icon
* @return string representation of key:value map
*/
private static String getStatusLabelDetailsInString(final String value, final String style, final String title,
final String id, boolean disabled) {
final StringBuilder val = new StringBuilder();
if (!Strings.isNullOrEmpty(value)) {
val.append("value:").append(value).append(",");
}
if (!Strings.isNullOrEmpty(style)) {
val.append("style:").append(style).append(",");
}
if (!Strings.isNullOrEmpty(title)) {
val.append("title:").append(title).append(",");
}
if (disabled) {
val.append("disabled:true").append(",");
}
return val.append("id:").append(id).toString();
}
/**
* Retrieves the codepoint from the font-icon.
*
* @param statusFontIcon
* the label-metadata that holds the font-icon
*
*/
private static String getCodePoint(final StatusFontIcon statusFontIcon) {
return statusFontIcon.getFontIcon() != null ? Integer.toString(statusFontIcon.getFontIcon().getCodepoint())
: null;
}
/**
* Adapts from model data to presentation data.
*
* @param <T>
* The type to adapt to
*/
@FunctionalInterface
public interface LabelAdapter<T> extends Serializable {
/**
* @param status
* model data
* @return meta representation that is used as input for label
* generation.
*/
StatusFontIcon adapt(final T status);
}
}

View File

@@ -0,0 +1,60 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ui.customrenderers.renderers;
import org.eclipse.hawkbit.ui.customrenderers.client.renderers.FontIconData;
import com.vaadin.ui.renderers.ClickableRenderer;
import elemental.json.JsonValue;
/**
* Renders buttons for a grid with provided HTML content based on meta-data.
* Used to display button with icons.
*/
public class GridButtonRenderer extends ClickableRenderer<FontIconData> {
private static final long serialVersionUID = 1L;
/**
* Constructor.
*/
public GridButtonRenderer() {
super(FontIconData.class, null);
}
/**
* Creates a new custom object renderer and adds the given click listener to
* it.
*
* @param listener
* the click listener to register
*/
public GridButtonRenderer(final RendererClickListener listener) {
this();
addClickListener(listener);
}
/**
* Initialize custom object renderer with the given type.
*
* @param presentationType
* Class<CustomObject>
*/
public GridButtonRenderer(final Class<FontIconData> presentationType) {
super(presentationType);
}
@Override
public JsonValue encode(final FontIconData resource) {
return super.encode(resource, FontIconData.class);
}
}

View File

@@ -11,7 +11,7 @@ package org.eclipse.hawkbit.ui.customrenderers.renderers;
import com.vaadin.ui.Grid.AbstractRenderer;
/**
*
*
* Renders label with provided value and style.
*
*/

View File

@@ -154,7 +154,7 @@ public class TargetFilterHeader extends VerticalLayout {
private void openSearchTextField() {
searchResetIcon.addStyleName(SPUIDefinitions.FILTER_RESET_ICON);
searchResetIcon.togleIcon(FontAwesome.TIMES);
searchResetIcon.toggleIcon(FontAwesome.TIMES);
searchResetIcon.setData(Boolean.TRUE);
searchField.removeStyleName(SPUIDefinitions.FILTER_BOX_HIDE);
searchField.setVisible(true);
@@ -165,7 +165,7 @@ public class TargetFilterHeader extends VerticalLayout {
searchField.setValue("");
searchField.addStyleName(SPUIDefinitions.FILTER_BOX_HIDE);
searchResetIcon.removeStyleName(SPUIDefinitions.FILTER_RESET_ICON);
searchResetIcon.togleIcon(FontAwesome.SEARCH);
searchResetIcon.toggleIcon(FontAwesome.SEARCH);
searchResetIcon.setData(Boolean.FALSE);
resetSearchText();

View File

@@ -28,7 +28,11 @@ import org.eclipse.hawkbit.ui.components.AbstractNotificationView;
import org.eclipse.hawkbit.ui.components.NotificationUnreadButton;
import org.eclipse.hawkbit.ui.components.RefreshableContainer;
import org.eclipse.hawkbit.ui.dd.criteria.ManagementViewClientCriterion;
import org.eclipse.hawkbit.ui.management.actionhistory.ActionHistoryComponent;
import org.eclipse.hawkbit.ui.management.actionhistory.ActionHistoryLayout;
import org.eclipse.hawkbit.ui.management.actionhistory.ActionStatusGrid;
import org.eclipse.hawkbit.ui.management.actionhistory.ActionStatusLayout;
import org.eclipse.hawkbit.ui.management.actionhistory.ActionStatusMsgGrid;
import org.eclipse.hawkbit.ui.management.actionhistory.ActionStatusMsgLayout;
import org.eclipse.hawkbit.ui.management.dstable.DistributionTableLayout;
import org.eclipse.hawkbit.ui.management.dstag.DistributionTagLayout;
import org.eclipse.hawkbit.ui.management.event.DistributionTableEvent;
@@ -89,7 +93,11 @@ public class DeploymentView extends AbstractNotificationView implements BrowserW
private final ManagementUIState managementUIState;
private final ActionHistoryComponent actionHistoryComponent;
private final ActionHistoryLayout actionHistoryLayout;
private final ActionStatusLayout actionStatusLayout;
private final ActionStatusMsgLayout actionStatusMsgLayout;
private final TargetTagFilterLayout targetTagFilterLayout;
@@ -97,7 +105,7 @@ public class DeploymentView extends AbstractNotificationView implements BrowserW
private final DistributionTagLayout distributionTagLayout;
private final DistributionTableLayout distributionTableLayoutNew;
private final DistributionTableLayout distributionTableLayout;
private final DeleteActionsLayout deleteAndActionsLayout;
@@ -121,8 +129,10 @@ public class DeploymentView extends AbstractNotificationView implements BrowserW
this.i18n = i18n;
this.uiNotification = uiNotification;
this.managementUIState = managementUIState;
this.actionHistoryComponent = new ActionHistoryComponent(i18n, deploymentManagement, eventBus, uiNotification,
this.actionHistoryLayout = new ActionHistoryLayout(i18n, deploymentManagement, eventBus, uiNotification,
managementUIState);
this.actionStatusLayout = new ActionStatusLayout(i18n, eventBus, managementUIState);
this.actionStatusMsgLayout = new ActionStatusMsgLayout(i18n, eventBus, managementUIState);
final CreateUpdateTargetTagLayoutWindow createUpdateTargetTagLayout = new CreateUpdateTargetTagLayoutWindow(
i18n, tagManagement, entityFactory, eventBus, permChecker, uiNotification);
this.targetTagFilterLayout = new TargetTagFilterLayout(i18n, createUpdateTargetTagLayout, managementUIState,
@@ -139,7 +149,7 @@ public class DeploymentView extends AbstractNotificationView implements BrowserW
this.distributionTagLayout = new DistributionTagLayout(eventbus, managementUIState, i18n, permChecker, eventBus,
tagManagement, entityFactory, uiNotification, distFilterParameters, distributionSetManagement,
managementViewClientCriterion);
this.distributionTableLayoutNew = new DistributionTableLayout(i18n, eventBus, permChecker, managementUIState,
this.distributionTableLayout = new DistributionTableLayout(i18n, eventBus, permChecker, managementUIState,
distributionSetManagement, managementViewClientCriterion, entityFactory, uiNotification, tagManagement,
systemManagement, targetManagement, deploymentManagement);
this.deleteAndActionsLayout = new DeleteActionsLayout(i18n, permChecker, eventBus, uiNotification,
@@ -147,6 +157,9 @@ public class DeploymentView extends AbstractNotificationView implements BrowserW
deploymentManagement, distributionSetManagement);
this.deploymentViewMenuItem = deploymentViewMenuItem;
actionHistoryLayout.registerDetails(((ActionStatusGrid) actionStatusLayout.getGrid()).getDetailsSupport());
actionStatusLayout.registerDetails(((ActionStatusMsgGrid) actionStatusMsgLayout.getGrid()).getDetailsSupport());
}
@PostConstruct
@@ -238,11 +251,13 @@ public class DeploymentView extends AbstractNotificationView implements BrowserW
mainLayout.setRows(2);
mainLayout.addComponent(targetTagFilterLayout, 0, 0);
mainLayout.addComponent(targetTableLayout, 1, 0);
mainLayout.addComponent(actionHistoryComponent, 4, 0);
mainLayout.addComponent(distributionTableLayoutNew, 2, 0);
mainLayout.addComponent(distributionTableLayout, 2, 0);
mainLayout.addComponent(distributionTagLayout, 3, 0);
mainLayout.addComponent(actionHistoryLayout, 4, 0);
mainLayout.setColumnExpandRatio(0, 0F);
mainLayout.setColumnExpandRatio(1, 0.275F);
mainLayout.setColumnExpandRatio(2, 0.275F);
mainLayout.setColumnExpandRatio(3, 0F);
mainLayout.setColumnExpandRatio(4, 0.45F);
if (showFooterLayout()) {
mainLayout.addComponent(deleteAndActionsLayout, 1, 1, 2, 1);
@@ -253,7 +268,7 @@ public class DeploymentView extends AbstractNotificationView implements BrowserW
private void displayDistributionWidgetsOnly() {
mainLayout.setColumns(2);
mainLayout.setRows(2);
mainLayout.addComponent(distributionTableLayoutNew, 0, 0);
mainLayout.addComponent(distributionTableLayout, 0, 0);
mainLayout.addComponent(distributionTagLayout, 1, 0);
mainLayout.setColumnExpandRatio(0, 1F);
if (showFooterLayout()) {
@@ -280,7 +295,7 @@ public class DeploymentView extends AbstractNotificationView implements BrowserW
mainLayout.setRows(2);
mainLayout.addComponent(targetTagFilterLayout, 0, 0);
mainLayout.addComponent(targetTableLayout, 1, 0);
mainLayout.addComponent(actionHistoryComponent, 2, 0);
mainLayout.addComponent(actionHistoryLayout, 2, 0);
mainLayout.setColumnExpandRatio(1, 0.4F);
mainLayout.setColumnExpandRatio(2, 0.6F);
if (showFooterLayout()) {
@@ -291,10 +306,10 @@ public class DeploymentView extends AbstractNotificationView implements BrowserW
private void maximizeTargetTable() {
if (permChecker.hasReadDistributionPermission()) {
mainLayout.removeComponent(distributionTableLayoutNew);
mainLayout.removeComponent(distributionTableLayout);
mainLayout.removeComponent(distributionTagLayout);
}
mainLayout.removeComponent(actionHistoryComponent);
mainLayout.removeComponent(actionHistoryLayout);
mainLayout.removeComponent(deleteAndActionsLayout);
mainLayout.setColumnExpandRatio(1, 1F);
mainLayout.setColumnExpandRatio(2, 0F);
@@ -306,7 +321,7 @@ public class DeploymentView extends AbstractNotificationView implements BrowserW
if (permChecker.hasTargetReadPermission()) {
mainLayout.removeComponent(targetTagFilterLayout);
mainLayout.removeComponent(targetTableLayout);
mainLayout.removeComponent(actionHistoryComponent);
mainLayout.removeComponent(actionHistoryLayout);
}
mainLayout.removeComponent(deleteAndActionsLayout);
mainLayout.setColumnExpandRatio(0, 0F);
@@ -316,20 +331,16 @@ public class DeploymentView extends AbstractNotificationView implements BrowserW
}
private void maximizeActionHistory() {
mainLayout.setSpacing(false);
mainLayout.removeComponent(targetTagFilterLayout);
mainLayout.removeComponent(targetTableLayout);
if (permChecker.hasReadDistributionPermission()) {
mainLayout.removeComponent(distributionTableLayoutNew);
mainLayout.removeComponent(distributionTagLayout);
}
mainLayout.setColumnExpandRatio(0, 0F);
mainLayout.setColumnExpandRatio(1, 0F);
mainLayout.setColumnExpandRatio(2, 0F);
mainLayout.setColumnExpandRatio(3, 0F);
mainLayout.setColumnExpandRatio(4, 1F);
mainLayout.removeComponent(deleteAndActionsLayout);
mainLayout.setComponentAlignment(actionHistoryComponent, Alignment.TOP_LEFT);
mainLayout.removeAllComponents();
mainLayout.setColumns(3);
mainLayout.setRows(1);
mainLayout.addComponent(actionHistoryLayout, 0, 0);
mainLayout.addComponent(actionStatusLayout, 1, 0);
mainLayout.addComponent(actionStatusMsgLayout, 2, 0);
mainLayout.setColumnExpandRatio(0, 0.55F);
mainLayout.setColumnExpandRatio(1, 0.18F);
mainLayout.setColumnExpandRatio(2, 0.27F);
mainLayout.setComponentAlignment(actionHistoryLayout, Alignment.TOP_LEFT);
}
private void minimizeTargetTable() {
@@ -342,7 +353,6 @@ public class DeploymentView extends AbstractNotificationView implements BrowserW
private void minimizeActionHistory() {
layoutWidgets();
mainLayout.setSpacing(true);
}
private void checkNoDataAvaialble() {
@@ -362,7 +372,7 @@ public class DeploymentView extends AbstractNotificationView implements BrowserW
targetTagFilterLayout.setVisible(false);
targetTableLayout.setShowFilterButtonVisible(true);
distributionTagLayout.setVisible(false);
distributionTableLayoutNew.setShowFilterButtonVisible(true);
distributionTableLayout.setShowFilterButtonVisible(true);
} else {
if (!managementUIState.isTargetTagFilterClosed()) {
targetTagFilterLayout.setVisible(true);
@@ -371,7 +381,7 @@ public class DeploymentView extends AbstractNotificationView implements BrowserW
}
if (!managementUIState.isDistTagFilterClosed()) {
distributionTagLayout.setVisible(true);
distributionTableLayoutNew.setShowFilterButtonVisible(false);
distributionTableLayout.setShowFilterButtonVisible(false);
}
}
}
@@ -383,8 +393,8 @@ public class DeploymentView extends AbstractNotificationView implements BrowserW
supportedEvents.put(TargetCreatedEventContainer.class, targetTableLayout.getTable());
supportedEvents.put(TargetDeletedEventContainer.class, targetTableLayout.getTable());
supportedEvents.put(DistributionCreatedEventContainer.class, distributionTableLayoutNew.getTable());
supportedEvents.put(DistributionDeletedEventContainer.class, distributionTableLayoutNew.getTable());
supportedEvents.put(DistributionCreatedEventContainer.class, distributionTableLayout.getTable());
supportedEvents.put(DistributionDeletedEventContainer.class, distributionTableLayout.getTable());
supportedEvents.put(TargetTagCreatedEventContainer.class, targetTagFilterLayout);
supportedEvents.put(TargetTagDeletedEventContainer.class, targetTagFilterLayout);

View File

@@ -0,0 +1,179 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ui.management.actionhistory;
import static org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil.isNotNullOrEmpty;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.ArrayUtils;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.ui.management.actionhistory.ProxyAction.IsActiveDecoration;
import org.eclipse.hawkbit.ui.utils.SPUIDefinitions;
import org.eclipse.hawkbit.ui.utils.SpringContextHelper;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.vaadin.addons.lazyquerycontainer.AbstractBeanQuery;
import org.vaadin.addons.lazyquerycontainer.QueryDefinition;
/**
* Simple implementation of generics bean query which dynamically loads
* {@link ProxyAction} batch of beans.
*
*/
public class ActionBeanQuery extends AbstractBeanQuery<ProxyAction> {
private static final long serialVersionUID = 3596912494728552516L;
private Sort sort = new Sort(Direction.DESC, ProxyAction.PXY_ACTION_ID);
private transient DeploymentManagement deploymentManagement;
private String currentSelectedConrollerId;
private transient Slice<Action> firstPageActions;
/**
* Parametric Constructor.
*
* @param definition
* QueryDefinition contains the query properties.
* @param queryConfig
* Implementation specific configuration.
* @param sortPropertyIds
* The properties participating in sort.
* @param sortStates
* The ascending or descending state of sort properties.
*/
public ActionBeanQuery(final QueryDefinition definition, final Map<String, Object> queryConfig,
final Object[] sortPropertyIds, final boolean[] sortStates) {
super(definition, queryConfig, sortPropertyIds, sortStates);
if (isNotNullOrEmpty(queryConfig)) {
currentSelectedConrollerId = (String) queryConfig.get(SPUIDefinitions.ACTIONS_BY_TARGET);
}
if (ArrayUtils.isEmpty(sortStates)) {
return;
}
// Initialize sort
sort = new Sort(sortStates[0] ? Direction.ASC : Direction.DESC, (String) sortPropertyIds[0]);
// Add sort
for (int distId = 1; distId < sortPropertyIds.length; distId++) {
sort.and(new Sort(sortStates[distId] ? Direction.ASC : Direction.DESC, (String) sortPropertyIds[distId]));
}
}
@Override
protected ProxyAction constructBean() {
return new ProxyAction();
}
@Override
protected List<ProxyAction> loadBeans(final int startIndex, final int count) {
Slice<Action> actionBeans;
if (startIndex == 0) {
if (firstPageActions == null) {
firstPageActions = getDeploymentManagement().findActionsByTarget(currentSelectedConrollerId,
new PageRequest(0, SPUIDefinitions.PAGE_SIZE, sort));
}
actionBeans = firstPageActions;
} else {
actionBeans = getDeploymentManagement().findActionsByTarget(currentSelectedConrollerId,
new PageRequest(startIndex / SPUIDefinitions.PAGE_SIZE, SPUIDefinitions.PAGE_SIZE, sort));
}
return createProxyActions(actionBeans);
}
/**
* Creates a list of {@link ProxyAction}s for presentation layer from slice
* of {@link Action}s.
*
* @param actionBeans
* slice of {@link Action}s
* @return list of {@link ProxyAction}s
*/
private static List<ProxyAction> createProxyActions(final Slice<Action> actionBeans) {
final List<ProxyAction> proxyActions = new ArrayList<>();
for (final Action action : actionBeans) {
final ProxyAction proxyAction = new ProxyAction();
final String dsNameVersion = action.getDistributionSet().getName() + ":"
+ action.getDistributionSet().getVersion();
proxyAction.setActive(action.isActive());
proxyAction.setIsActiveDecoration(buildIsActiveDecoration(action));
proxyAction.setDsNameVersion(dsNameVersion);
proxyAction.setAction(action);
proxyAction.setId(action.getId());
proxyAction.setLastModifiedAt(action.getLastModifiedAt());
proxyAction.setRolloutName(action.getRollout() != null ? action.getRollout().getName() : "");
proxyAction.setStatus(action.getStatus());
proxyActions.add(proxyAction);
}
return proxyActions;
}
/**
* Generates a virtual IsActiveDecoration for the presentation layer that is
* calculated from {@link Action#isActive()} and
* {@link Action#getActionStatus()}.
*
* @param action
* the action combined IsActiveDecoration is calculated from
* @return IsActiveDecoration combined decoration for the presentation
* layer.
*/
private static IsActiveDecoration buildIsActiveDecoration(final Action action) {
final Action.Status status = action.getStatus();
if (status == Action.Status.SCHEDULED) {
return IsActiveDecoration.SCHEDULED;
} else if (status == Action.Status.ERROR) {
return IsActiveDecoration.IN_ACTIVE_ERROR;
}
return action.isActive() ? IsActiveDecoration.ACTIVE : IsActiveDecoration.IN_ACTIVE;
}
@Override
protected void saveBeans(final List<ProxyAction> addedBeans, final List<ProxyAction> modifiedBeans,
final List<ProxyAction> removedBeans) {
// CRUD operations on Target will be done through repository methods
}
@Override
public int size() {
long size = 0;
if (currentSelectedConrollerId != null) {
size = getDeploymentManagement().countActionsByTarget(currentSelectedConrollerId);
}
if (size > Integer.MAX_VALUE) {
return Integer.MAX_VALUE;
}
return (int) size;
}
/**
* Lazy gets deploymentManagement.
*
* @return the deploymentManagement
*/
public DeploymentManagement getDeploymentManagement() {
if (null == deploymentManagement) {
deploymentManagement = SpringContextHelper.getBean(DeploymentManagement.class);
}
return deploymentManagement;
}
}

View File

@@ -1,85 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ui.management.actionhistory;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.ui.common.table.BaseEntityEventType;
import org.eclipse.hawkbit.ui.management.event.TargetTableEvent;
import org.eclipse.hawkbit.ui.management.state.ManagementUIState;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
import org.eclipse.hawkbit.ui.utils.SPUIDefinitions;
import org.eclipse.hawkbit.ui.utils.UINotification;
import org.vaadin.spring.events.EventBus.UIEventBus;
import org.vaadin.spring.events.EventScope;
import org.vaadin.spring.events.annotation.EventBusListenerMethod;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
/**
*
*
*/
public class ActionHistoryComponent extends VerticalLayout {
private static final long serialVersionUID = -3766179797384539821L;
private final ActionHistoryHeader actionHistoryHeader;
private final ActionHistoryTable actionHistoryTable;
public ActionHistoryComponent(final VaadinMessageSource i18n, final DeploymentManagement deploymentManagement,
final UIEventBus eventBus, final UINotification notification, final ManagementUIState managementUIState) {
this.actionHistoryHeader = new ActionHistoryHeader(eventBus, managementUIState);
this.actionHistoryTable = new ActionHistoryTable(i18n, deploymentManagement, eventBus, notification,
managementUIState);
buildLayout();
setSizeFull();
setImmediate(true);
eventBus.subscribe(this);
}
@EventBusListenerMethod(scope = EventScope.UI)
void onEvent(final TargetTableEvent targetUIEvent) {
if (BaseEntityEventType.SELECTED_ENTITY == targetUIEvent.getEventType()) {
setData(SPUIDefinitions.DATA_AVAILABLE);
UI.getCurrent().access(() -> populateActionHistoryDetails(targetUIEvent.getEntity()));
}
}
private void buildLayout() {
setSizeFull();
setSpacing(false);
setMargin(false);
addStyleName("table-layout");
addComponents(actionHistoryHeader, actionHistoryTable);
setComponentAlignment(actionHistoryHeader, Alignment.TOP_CENTER);
setComponentAlignment(actionHistoryTable, Alignment.TOP_CENTER);
setExpandRatio(actionHistoryTable, 1.0F);
}
/**
* populate action header and table for the target.
*
* @param target
* the target
*/
public void populateActionHistoryDetails(final Target target) {
if (null != target) {
actionHistoryHeader.populateHeader(target.getName());
actionHistoryTable.setAlreadyHasMessages(false);
actionHistoryTable.populateTableData(target);
} else {
actionHistoryHeader.updateActionHistoryHeader(" ");
actionHistoryTable.setAlreadyHasMessages(false);
actionHistoryTable.clearContainerData();
}
}
}

View File

@@ -0,0 +1,792 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ui.management.actionhistory;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.ui.common.ConfirmationDialog;
import org.eclipse.hawkbit.ui.common.grid.AbstractGrid;
import org.eclipse.hawkbit.ui.common.table.BaseEntityEventType;
import org.eclipse.hawkbit.ui.customrenderers.renderers.AbstractGridButtonConverter;
import org.eclipse.hawkbit.ui.customrenderers.renderers.AbstractHtmlLabelConverter;
import org.eclipse.hawkbit.ui.customrenderers.renderers.GridButtonRenderer;
import org.eclipse.hawkbit.ui.customrenderers.renderers.HtmlLabelRenderer;
import org.eclipse.hawkbit.ui.management.actionhistory.ProxyAction.IsActiveDecoration;
import org.eclipse.hawkbit.ui.management.event.ManagementUIEvent;
import org.eclipse.hawkbit.ui.management.event.PinUnpinEvent;
import org.eclipse.hawkbit.ui.management.event.TargetTableEvent;
import org.eclipse.hawkbit.ui.management.state.ManagementUIState;
import org.eclipse.hawkbit.ui.rollout.StatusFontIcon;
import org.eclipse.hawkbit.ui.utils.SPDateTimeUtil;
import org.eclipse.hawkbit.ui.utils.SPUIDefinitions;
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
import org.eclipse.hawkbit.ui.utils.UINotification;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.vaadin.addons.lazyquerycontainer.BeanQueryFactory;
import org.vaadin.addons.lazyquerycontainer.LazyQueryContainer;
import org.vaadin.addons.lazyquerycontainer.LazyQueryDefinition;
import org.vaadin.spring.events.EventBus.UIEventBus;
import org.vaadin.spring.events.EventScope;
import org.vaadin.spring.events.annotation.EventBusListenerMethod;
import com.google.common.collect.Maps;
import com.vaadin.data.Item;
import com.vaadin.data.util.GeneratedPropertyContainer;
import com.vaadin.data.util.PropertyValueGenerator;
import com.vaadin.server.FontAwesome;
import com.vaadin.ui.UI;
/**
* This grid presents the action history for a selected target.
*/
public class ActionHistoryGrid extends AbstractGrid<LazyQueryContainer> {
private static final long serialVersionUID = 4324796883957831443L;
private static final Logger LOG = LoggerFactory.getLogger(ActionHistoryGrid.class);
private static final String BUTTON_CANCEL = "button.cancel";
private static final String BUTTON_OK = "button.ok";
private static final double FIXED_PIX_MIN = 29F;
private static final double FIXED_PIX_MAX = 32F;
private static final String STATUS_ICON_GREEN = "statusIconGreen";
private static final String STATUS_ICON_RED = "statusIconRed";
private static final String STATUS_ICON_ORANGE = "statusIconOrange";
private static final String STATUS_ICON_PENDING = "statusIconPending";
private static final String STATUS_ICON_NEUTRAL = "statusIconNeutral";
private static final String STATUS_ICON_ACTIVE = "statusIconActive";
private static final String STATUS_ICON_FORCED = "statusIconForced";
private static final String VIRT_PROP_FORCED = "forced";
private static final String VIRT_PROP_TIMEFORCED = "timeForced";
private static final String VIRT_PROP_ACTION_CANCEL = "cancel-aciton";
private static final String VIRT_PROP_ACTION_FORCE = "force-action";
private static final String VIRT_PROP_ACTION_FORCE_QUIT = "force-quit-action";
private static final Object[] maxColumnOrder = new Object[] { ProxyAction.PXY_ACTION_IS_ACTIVE_DECO,
ProxyAction.PXY_ACTION_ID, ProxyAction.PXY_ACTION_DS_NAME_VERSION, ProxyAction.PXY_ACTION_LAST_MODIFIED_AT,
ProxyAction.PXY_ACTION_STATUS, ProxyAction.PXY_ACTION_ROLLOUT_NAME, VIRT_PROP_FORCED, VIRT_PROP_TIMEFORCED,
VIRT_PROP_ACTION_CANCEL, VIRT_PROP_ACTION_FORCE, VIRT_PROP_ACTION_FORCE_QUIT };
private static final Object[] minColumnOrder = new Object[] { ProxyAction.PXY_ACTION_IS_ACTIVE_DECO,
ProxyAction.PXY_ACTION_DS_NAME_VERSION, ProxyAction.PXY_ACTION_LAST_MODIFIED_AT,
ProxyAction.PXY_ACTION_STATUS, VIRT_PROP_FORCED, VIRT_PROP_TIMEFORCED, VIRT_PROP_ACTION_CANCEL,
VIRT_PROP_ACTION_FORCE, VIRT_PROP_ACTION_FORCE_QUIT };
private static final String[] leftAlignedColumns = new String[] { VIRT_PROP_TIMEFORCED };
private static final String[] centerAlignedColumns = new String[] { ProxyAction.PXY_ACTION_IS_ACTIVE_DECO,
ProxyAction.PXY_ACTION_STATUS };
private static final String[] rightAlignedColumns = new String[] { VIRT_PROP_FORCED, ProxyAction.PXY_ACTION_ID };
private final transient DeploymentManagement deploymentManagement;
private final UINotification notification;
private final ManagementUIState managementUIState;
private Target selectedTarget;
private final AlignCellStyleGenerator alignGenerator;
private final ModifiedTimeTooltipGenerator modTimetooltipGenerator;
private final Map<Action.Status, StatusFontIcon> states;
private final Map<IsActiveDecoration, StatusFontIcon> activeStates;
private final BeanQueryFactory<ActionBeanQuery> targetQF = new BeanQueryFactory<>(ActionBeanQuery.class);
boolean forceClientRefreshToggle = true;
/**
* Constructor.
*
* @param i18n
* @param deploymentManagement
* @param eventBus
* @param notification
* @param managementUIState
*/
protected ActionHistoryGrid(final VaadinMessageSource i18n, final DeploymentManagement deploymentManagement,
final UIEventBus eventBus, final UINotification notification, final ManagementUIState managementUIState) {
super(i18n, eventBus, null);
this.deploymentManagement = deploymentManagement;
this.notification = notification;
this.managementUIState = managementUIState;
setMaximizeSupport(new ActionHistoryMaximizeSupport());
setSingleSelectionSupport(new SingleSelectionSupport());
if (!managementUIState.isActionHistoryMaximized()) {
getSingleSelectionSupport().disable();
}
setGeneratedPropertySupport(new ActionHistoryGeneratedPropertySupport());
setDetailsSupport(new DetailsSupport());
final LabelConfig conf = new LabelConfig();
states = conf.createStatusLabelConfig(i18n, UIComponentIdProvider.ACTION_HISTORY_TABLE_STATUS_LABEL_ID);
activeStates = conf
.createActiveStatusLabelConfig(UIComponentIdProvider.ACTION_HISTORY_TABLE_ACTIVESTATE_LABEL_ID);
alignGenerator = new AlignCellStyleGenerator(leftAlignedColumns, centerAlignedColumns, rightAlignedColumns);
modTimetooltipGenerator = new ModifiedTimeTooltipGenerator(ProxyAction.PXY_ACTION_LAST_MODIFIED_AT);
init();
}
@EventBusListenerMethod(scope = EventScope.UI)
void onEvent(final ManagementUIEvent mgmtUIEvent) {
if (mgmtUIEvent == ManagementUIEvent.MAX_ACTION_HISTORY) {
UI.getCurrent().access(this::createMaximizedContent);
}
if (mgmtUIEvent == ManagementUIEvent.MIN_ACTION_HISTORY) {
UI.getCurrent().access(this::createMinimizedContent);
}
}
@Override
protected void init() {
super.init();
restorePreviousState();
}
/**
* Set target as member of this grid (as all presented grid-data is related
* to this target) and recalculate grid-data for this target.
*
* @param selectedTarget
* reference of target
*/
public void populateSelectedTarget(final Target selectedTarget) {
this.selectedTarget = selectedTarget;
getDetailsSupport()
.populateMasterDataAndRecalculateContainer(selectedTarget != null ? selectedTarget.getId() : null);
}
@Override
protected LazyQueryContainer createContainer() {
configureQueryFactory();
return new LazyQueryContainer(
new LazyQueryDefinition(true, SPUIDefinitions.PAGE_SIZE, ProxyAction.PXY_ACTION_ID), targetQF);
}
@Override
public void refreshContainer() {
configureQueryFactory();
super.refreshContainer();
}
protected void configureQueryFactory() {
// ADD all the filters to the query config
final Map<String, Object> queryConfig = Maps.newHashMapWithExpectedSize(1);
queryConfig.put(SPUIDefinitions.ACTIONS_BY_TARGET,
selectedTarget != null ? selectedTarget.getControllerId() : null);
// Create ActionBeanQuery factory with the query config.
targetQF.setQueryConfiguration(queryConfig);
}
@Override
protected void addContainerProperties() {
final LazyQueryContainer rawCont = getGeneratedPropertySupport().getRawContainer();
rawCont.addContainerProperty(ProxyAction.PXY_ACTION_IS_ACTIVE_DECO, IsActiveDecoration.class, null, true,
false);
rawCont.addContainerProperty(ProxyAction.PXY_ACTION, Action.class, null, true, false);
rawCont.addContainerProperty(ProxyAction.PXY_ACTION_DS_NAME_VERSION, String.class, null, true, false);
rawCont.addContainerProperty(ProxyAction.PXY_ACTION_LAST_MODIFIED_AT, Long.class, null, true, true);
rawCont.addContainerProperty(ProxyAction.PXY_ACTION_STATUS, Action.Status.class, null, true, false);
rawCont.addContainerProperty(ProxyAction.PXY_ACTION_ID, Long.class, null, true, true);
rawCont.addContainerProperty(ProxyAction.PXY_ACTION_ROLLOUT_NAME, String.class, null, true, true);
}
@Override
protected String getGridId() {
return UIComponentIdProvider.ACTION_HISTORY_GRID_ID;
}
@Override
protected void addColumnRenderes() {
getColumn(ProxyAction.PXY_ACTION_LAST_MODIFIED_AT).setConverter(new LongToFormattedDateStringConverter());
getColumn(ProxyAction.PXY_ACTION_STATUS).setRenderer(new HtmlLabelRenderer(),
new HtmlStatusLabelConverter(this::createStatusLabelMetadata));
getColumn(ProxyAction.PXY_ACTION_IS_ACTIVE_DECO).setRenderer(new HtmlLabelRenderer(),
new HtmlIsActiveLabelConverter(this::createIsActiveLabelMetadata));
getColumn(VIRT_PROP_FORCED).setRenderer(new HtmlLabelRenderer(),
new HtmlVirtPropLabelConverter(ActionHistoryGrid::createForcedLabelMetadata));
getColumn(VIRT_PROP_TIMEFORCED).setRenderer(new HtmlLabelRenderer(),
new HtmlVirtPropLabelConverter(this::createTimeForcedLabelMetadata));
getColumn(VIRT_PROP_ACTION_CANCEL).setRenderer(
new GridButtonRenderer(clickEvent -> confirmAndCancelAction((Long) clickEvent.getItemId())),
new ActionGridButtonConverter(this::createCancelButtonMetadata));
getColumn(VIRT_PROP_ACTION_FORCE).setRenderer(
new GridButtonRenderer(clickEvent -> confirmAndForceAction((Long) clickEvent.getItemId())),
new ActionGridButtonConverter(this::createForceButtonMetadata));
getColumn(VIRT_PROP_ACTION_FORCE_QUIT).setRenderer(
new GridButtonRenderer(clickEvent -> confirmAndForceQuitAction((Long) clickEvent.getItemId())),
new ActionGridButtonConverter(this::createForceQuitButtonMetadata));
}
private StatusFontIcon createCancelButtonMetadata(final Action action) {
final boolean isDisabled = !action.isActive() || action.isCancelingOrCanceled();
return new StatusFontIcon(FontAwesome.TIMES, STATUS_ICON_NEUTRAL, i18n.getMessage("message.cancel.action"),
UIComponentIdProvider.ACTION_HISTORY_TABLE_CANCEL_ID, isDisabled);
}
private StatusFontIcon createForceButtonMetadata(final Action action) {
final boolean isDisabled = !action.isActive() || action.isForce() || action.isCancelingOrCanceled();
return new StatusFontIcon(FontAwesome.BOLT, STATUS_ICON_NEUTRAL, i18n.getMessage("message.force.action"),
UIComponentIdProvider.ACTION_HISTORY_TABLE_FORCE_ID, isDisabled);
}
private StatusFontIcon createForceQuitButtonMetadata(final Action action) {
final boolean isDisabled = !action.isActive() || !action.isCancelingOrCanceled();
return new StatusFontIcon(FontAwesome.TIMES, STATUS_ICON_RED, i18n.getMessage("message.forcequit.action"),
UIComponentIdProvider.ACTION_HISTORY_TABLE_FORCE_QUIT_ID, isDisabled);
}
private StatusFontIcon createStatusLabelMetadata(final Action.Status status) {
return states.get(status);
}
private StatusFontIcon createIsActiveLabelMetadata(final IsActiveDecoration isActiveDeco) {
return activeStates.get(isActiveDeco);
}
private static StatusFontIcon createForcedLabelMetadata(final Action action) {
StatusFontIcon result = null;
if (ActionType.FORCED.equals(action.getActionType()) || ActionType.TIMEFORCED.equals(action.getActionType())) {
result = new StatusFontIcon(FontAwesome.BOLT, STATUS_ICON_FORCED, "Forced",
UIComponentIdProvider.ACTION_HISTORY_TABLE_FORCED_LABEL_ID);
}
return result;
}
private StatusFontIcon createTimeForcedLabelMetadata(final Action action) {
StatusFontIcon result = null;
if (ActionType.TIMEFORCED.equals(action.getActionType())) {
final long currentTimeMillis = System.currentTimeMillis();
String style;
String title;
if (action.isHitAutoForceTime(currentTimeMillis)) {
style = STATUS_ICON_GREEN;
title = "auto forced since "
+ SPDateTimeUtil.getDurationFormattedString(action.getForcedTime(), currentTimeMillis, i18n);
} else {
style = STATUS_ICON_PENDING;
title = "auto forcing in "
+ SPDateTimeUtil.getDurationFormattedString(currentTimeMillis, action.getForcedTime(), i18n);
}
result = new StatusFontIcon(FontAwesome.HISTORY, style, title,
UIComponentIdProvider.ACTION_HISTORY_TABLE_TIMEFORCED_LABEL_ID);
}
return result;
}
/**
* Show confirmation window and if ok then only, force the action.
*
* @param actionId
* as Id if the action needs to be forced.
*/
private void confirmAndForceAction(final Long actionId) {
/* Display the confirmation */
final ConfirmationDialog confirmDialog = new ConfirmationDialog(
i18n.getMessage("caption.force.action.confirmbox"), i18n.getMessage("message.force.action.confirm"),
i18n.getMessage(BUTTON_OK), i18n.getMessage(BUTTON_CANCEL), ok -> {
if (!ok) {
return;
}
deploymentManagement.forceTargetAction(actionId);
populateAndUpdateTargetDetails(selectedTarget);
notification.displaySuccess(i18n.getMessage("message.force.action.success"));
});
UI.getCurrent().addWindow(confirmDialog.getWindow());
confirmDialog.getWindow().bringToFront();
}
/**
* Show confirmation window and if ok then only, force quit action.
*
* @param actionId
* as Id if the action needs to be forced.
*/
private void confirmAndForceQuitAction(final Long actionId) {
/* Display the confirmation */
final ConfirmationDialog confirmDialog = new ConfirmationDialog(
i18n.getMessage("caption.forcequit.action.confirmbox"),
i18n.getMessage("message.forcequit.action.confirm"), i18n.getMessage(BUTTON_OK),
i18n.getMessage(BUTTON_CANCEL), ok -> {
if (!ok) {
return;
}
final boolean cancelResult = forceQuitActiveAction(actionId);
if (cancelResult) {
populateAndUpdateTargetDetails(selectedTarget);
notification.displaySuccess(i18n.getMessage("message.forcequit.action.success"));
} else {
notification.displayValidationError(i18n.getMessage("message.forcequit.action.failed"));
}
}, FontAwesome.WARNING);
UI.getCurrent().addWindow(confirmDialog.getWindow());
confirmDialog.getWindow().bringToFront();
}
/**
* Show confirmation window and if ok then only, cancel the action.
*
* @param actionId
* as Id if the action needs to be cancelled.
*/
private void confirmAndCancelAction(final Long actionId) {
if (actionId == null) {
return;
}
final ConfirmationDialog confirmDialog = new ConfirmationDialog(
i18n.getMessage("caption.cancel.action.confirmbox"), i18n.getMessage("message.cancel.action.confirm"),
i18n.getMessage(BUTTON_OK), i18n.getMessage(BUTTON_CANCEL), ok -> {
if (!ok) {
return;
}
final boolean cancelResult = cancelActiveAction(actionId);
if (cancelResult) {
populateAndUpdateTargetDetails(selectedTarget);
notification.displaySuccess(i18n.getMessage("message.cancel.action.success"));
} else {
notification.displayValidationError(i18n.getMessage("message.cancel.action.failed"));
}
});
UI.getCurrent().addWindow(confirmDialog.getWindow());
confirmDialog.getWindow().bringToFront();
}
private void populateAndUpdateTargetDetails(final Target target) {
// show the updated target action history details
populateSelectedTarget(target);
// update the target table and its pinning details
updateTargetAndDsTable();
}
private void updateTargetAndDsTable() {
eventBus.publish(this, new TargetTableEvent(BaseEntityEventType.UPDATED_ENTITY, selectedTarget));
updateDistributionTableStyle();
}
/**
* Update the colors of Assigned and installed distribution set in Target
* Pinning.
*/
private void updateDistributionTableStyle() {
managementUIState.getDistributionTableFilters().getPinnedTarget().ifPresent((pinnedTarget) -> {
if (pinnedTarget.getTargetId().equals(selectedTarget.getId())) {
eventBus.publish(this, PinUnpinEvent.PIN_TARGET);
}
});
if (!managementUIState.getDistributionTableFilters().getPinnedTarget().isPresent()) {
return;
}
}
// service call to cancel the active action
private boolean cancelActiveAction(final Long actionId) {
if (actionId != null) {
try {
deploymentManagement.cancelAction(actionId);
return true;
} catch (final CancelActionNotAllowedException e) {
LOG.info("Cancel action not allowed exception :{}", e);
return false;
}
}
return false;
}
// service call to cancel the active action
private boolean forceQuitActiveAction(final Long actionId) {
if (actionId != null) {
try {
deploymentManagement.forceQuitAction(actionId);
return true;
} catch (final CancelActionNotAllowedException e) {
LOG.info("Force Cancel action not allowed exception :{}", e);
return false;
}
}
return false;
}
@Override
protected void setHiddenColumns() {
getColumn(VIRT_PROP_FORCED).setHidable(false);
getColumn(VIRT_PROP_TIMEFORCED).setHidable(false);
getColumn(VIRT_PROP_ACTION_CANCEL).setHidable(false);
getColumn(VIRT_PROP_ACTION_FORCE).setHidable(false);
getColumn(VIRT_PROP_ACTION_FORCE_QUIT).setHidable(false);
}
@Override
protected CellDescriptionGenerator getDescriptionGenerator() {
return modTimetooltipGenerator;
}
@Override
protected void setColumnHeaderNames() {
final HeaderRow newHeaderRow = resetHeaderDefaultRow();
getColumn(ProxyAction.PXY_ACTION_IS_ACTIVE_DECO).setHeaderCaption(SPUIDefinitions.ACTION_HIS_TBL_ACTIVE);
getColumn(ProxyAction.PXY_ACTION_DS_NAME_VERSION).setHeaderCaption(SPUIDefinitions.ACTION_HIS_TBL_DIST);
getColumn(ProxyAction.PXY_ACTION_LAST_MODIFIED_AT).setHeaderCaption(SPUIDefinitions.ACTION_HIS_TBL_DATETIME);
getColumn(ProxyAction.PXY_ACTION_STATUS).setHeaderCaption(SPUIDefinitions.ACTION_HIS_TBL_STATUS);
getColumn(VIRT_PROP_FORCED).setHeaderCaption(String.valueOf(forceClientRefreshToggle));
forceClientRefreshToggle = !forceClientRefreshToggle;
newHeaderRow.join(VIRT_PROP_FORCED, VIRT_PROP_TIMEFORCED).setText(SPUIDefinitions.ACTION_HIS_TBL_FORCED);
newHeaderRow.join(VIRT_PROP_ACTION_CANCEL, VIRT_PROP_ACTION_FORCE, VIRT_PROP_ACTION_FORCE_QUIT)
.setText(SPUIDefinitions.ACTIONS_COLUMN);
}
@Override
protected void setColumnExpandRatio() {
setColumnsSize(50.0, 50.0, ProxyAction.PXY_ACTION_IS_ACTIVE_DECO);
setColumnsSize(107.0, 500.0, ProxyAction.PXY_ACTION_DS_NAME_VERSION);
setColumnsSize(100.0, 120.0, ProxyAction.PXY_ACTION_LAST_MODIFIED_AT);
setColumnsSize(53.0, 55.0, ProxyAction.PXY_ACTION_STATUS);
setColumnsSize(FIXED_PIX_MIN, FIXED_PIX_MIN, VIRT_PROP_FORCED, VIRT_PROP_TIMEFORCED, VIRT_PROP_ACTION_CANCEL,
VIRT_PROP_ACTION_FORCE, VIRT_PROP_ACTION_FORCE_QUIT);
}
/**
* Conveniently sets min- and max-width for a bunch of columns.
*
* @param min
* minimum width
* @param max
* maximum width
* @param columnPropertyIds
* all the columns the min and max should be set for.
*/
private void setColumnsSize(final double min, final double max, final String... columnPropertyIds) {
for (final String columnPropertyId : columnPropertyIds) {
getColumn(columnPropertyId).setMinimumWidth(min);
getColumn(columnPropertyId).setMaximumWidth(max);
}
}
/**
* Creates the grid content for maximized-state.
*/
private void createMaximizedContent() {
getSingleSelectionSupport().enable();
getDetailsSupport().populateSelection();
getMaximizeSupport().createMaximizedContent();
recalculateColumnWidths();
}
/**
* Creates the grid content for normal (minimized) state.
*/
private void createMinimizedContent() {
getSingleSelectionSupport().disable();
getMaximizeSupport().createMinimizedContent();
recalculateColumnWidths();
}
@Override
protected void setColumnProperties() {
clearSortOrder();
setColumns(minColumnOrder);
alignColumns();
}
/**
* Restores the maximized state if the action history was left in
* maximized-state and is now re-entered.
*/
private void restorePreviousState() {
if (managementUIState.isActionHistoryMaximized()) {
createMaximizedContent();
}
}
/**
* Sets the alignment cell-style-generator that handles the alignment for
* the grid cells.
*/
private void alignColumns() {
setCellStyleGenerator(alignGenerator);
}
/**
* Adds support for virtual properties (aka generated properties)
*/
class ActionHistoryGeneratedPropertySupport extends AbstractGeneratedPropertySupport {
@Override
public GeneratedPropertyContainer getDecoratedContainer() {
return (GeneratedPropertyContainer) getContainerDataSource();
}
@Override
public LazyQueryContainer getRawContainer() {
return (LazyQueryContainer) (getDecoratedContainer()).getWrappedContainer();
}
@Override
protected GeneratedPropertyContainer addGeneratedContainerProperties() {
final GeneratedPropertyContainer decoratedContainer = getDecoratedContainer();
decoratedContainer.addGeneratedProperty(VIRT_PROP_FORCED, new GenericPropertyValueGenerator());
decoratedContainer.addGeneratedProperty(VIRT_PROP_TIMEFORCED, new GenericPropertyValueGenerator());
decoratedContainer.addGeneratedProperty(VIRT_PROP_ACTION_CANCEL, new GenericPropertyValueGenerator());
decoratedContainer.addGeneratedProperty(VIRT_PROP_ACTION_FORCE, new GenericPropertyValueGenerator());
decoratedContainer.addGeneratedProperty(VIRT_PROP_ACTION_FORCE_QUIT, new GenericPropertyValueGenerator());
return decoratedContainer;
}
}
/**
* Adds support to maximize the grid.
*/
class ActionHistoryMaximizeSupport extends AbstractMaximizeSupport {
/**
* Sets the property-ids available in maximized-state.
*/
@Override
protected void setMaximizedColumnProperties() {
clearSortOrder();
setColumns(maxColumnOrder);
alignColumns();
}
@Override
protected void setMaximizedHiddenColumns() {
getColumn(ProxyAction.PXY_ACTION_ID).setHidden(false);
getColumn(ProxyAction.PXY_ACTION_ID).setHidable(true);
getColumn(ProxyAction.PXY_ACTION_ROLLOUT_NAME).setHidden(false);
getColumn(ProxyAction.PXY_ACTION_ROLLOUT_NAME).setHidable(true);
}
/**
* Sets additional headers for the maximized-state.
*/
@Override
protected void setMaximizedHeaders() {
getColumn(ProxyAction.PXY_ACTION_ID).setHeaderCaption(SPUIDefinitions.ACTION_HIS_TBL_ACTION_ID);
getColumn(ProxyAction.PXY_ACTION_ROLLOUT_NAME)
.setHeaderCaption(SPUIDefinitions.ACTION_HIS_TBL_ROLLOUT_NAME);
}
/**
* Sets the expand ratio for the maximized-state.
*/
@Override
protected void setMaximizedColumnExpandRatio() {
/* set messages column can expand the rest of the available space */
setColumnsSize(50.0, 50.0, ProxyAction.PXY_ACTION_IS_ACTIVE_DECO);
setColumnsSize(FIXED_PIX_MIN, 100.0, ProxyAction.PXY_ACTION_ID);
setColumnsSize(107.0, 500.0, ProxyAction.PXY_ACTION_DS_NAME_VERSION);
setColumnsSize(100.0, 150.0, ProxyAction.PXY_ACTION_LAST_MODIFIED_AT);
setColumnsSize(53.0, 55.0, ProxyAction.PXY_ACTION_STATUS);
setColumnsSize(FIXED_PIX_MIN, FIXED_PIX_MAX, VIRT_PROP_FORCED, VIRT_PROP_TIMEFORCED,
VIRT_PROP_ACTION_CANCEL, VIRT_PROP_ACTION_FORCE, VIRT_PROP_ACTION_FORCE_QUIT);
setColumnsSize(FIXED_PIX_MIN, 500.0, ProxyAction.PXY_ACTION_ROLLOUT_NAME);
}
}
/**
* Concrete html-label converter that handles IsActiveDecoration enum.
*/
class HtmlIsActiveLabelConverter extends AbstractHtmlLabelConverter<IsActiveDecoration> {
private static final long serialVersionUID = 1L;
/**
* Constructor that sets the appropriate adapter.
*
* @param adapter
* adapts <code>IsActiveDecoration</code> to
* <code>String</code>
*/
public HtmlIsActiveLabelConverter(final LabelAdapter<IsActiveDecoration> adapter) {
addAdapter(adapter);
}
@Override
public Class<IsActiveDecoration> getModelType() {
return IsActiveDecoration.class;
}
}
/**
* Concrete html-label converter that handles Booleans. The converter needs
* an appropriate adapter that must be set via
* {@link AbstractHtmlLabelConverter#addAdapter()} to do the work.
*/
class HtmlBooleanLabelConverter extends AbstractHtmlLabelConverter<Boolean> {
private static final long serialVersionUID = 1L;
@Override
public Class<Boolean> getModelType() {
return Boolean.class;
}
}
/**
* Concrete html-label converter that handles Actions.
*/
class HtmlVirtPropLabelConverter extends AbstractHtmlLabelConverter<Action> {
private static final long serialVersionUID = 1L;
/**
* Constructor that sets the appropriate adapter.
*
* @param adapter
* adapts <code>Action</code> to <code>String</code>
*/
public HtmlVirtPropLabelConverter(final LabelAdapter<Action> adapter) {
addAdapter(adapter);
}
@Override
public Class<Action> getModelType() {
return Action.class;
}
}
/**
* Concrete grid-button converter that handles Actions.
*/
class ActionGridButtonConverter extends AbstractGridButtonConverter<Action> {
private static final long serialVersionUID = 1L;
/**
* Constructor that sets the appropriate adapter.
*
* @param adapter
* adapts <code>Action</code> to <code>StatusFontIcon</code>
*/
public ActionGridButtonConverter(final GridButtonAdapter<Action> adapter) {
addAdapter(adapter);
}
@Override
public Class<Action> getModelType() {
return Action.class;
}
}
/**
* Generator class responsible to retrieve an Action from the grid data in
* order to generate a virtual property.
*/
class GenericPropertyValueGenerator extends PropertyValueGenerator<Action> {
private static final long serialVersionUID = 1L;
@Override
public Action getValue(final Item item, final Object itemId, final Object propertyId) {
return (Action) item.getItemProperty(ProxyAction.PXY_ACTION).getValue();
}
@Override
public Class<Action> getType() {
return Action.class;
}
}
/**
* Generator class responsible to generate a button property value for the
* grid.
*/
class ButtonPropertyValueGenerator extends PropertyValueGenerator<String> {
private static final long serialVersionUID = 1L;
private final String buttonLabel;
public ButtonPropertyValueGenerator(final String buttonLabel) {
this.buttonLabel = buttonLabel;
}
@Override
public String getValue(final Item item, final Object itemId, final Object propertyId) {
return buttonLabel;
}
@Override
public Class<String> getType() {
return String.class;
}
}
/**
* Configuration that holds the styling properties for label- and
* button-renderers that are included in grid-cells.
*/
public static class LabelConfig {
/**
* Initializes a map with all available status label metadata.
*
* @param i18n
* @param statusLabelId
* @return the configured map
*/
public Map<Action.Status, StatusFontIcon> createStatusLabelConfig(final VaadinMessageSource i18n,
final String statusLabelId) {
final HashMap<Action.Status, StatusFontIcon> stateMap = Maps.newHashMapWithExpectedSize(9);
stateMap.put(Action.Status.FINISHED, new StatusFontIcon(FontAwesome.CHECK_CIRCLE, STATUS_ICON_GREEN,
i18n.getMessage("label.finished"), statusLabelId));
stateMap.put(Action.Status.ERROR, new StatusFontIcon(FontAwesome.EXCLAMATION_CIRCLE, STATUS_ICON_RED,
i18n.getMessage("label.error"), statusLabelId));
stateMap.put(Action.Status.WARNING, new StatusFontIcon(FontAwesome.EXCLAMATION_CIRCLE, STATUS_ICON_ORANGE,
i18n.getMessage("label.warning"), statusLabelId));
stateMap.put(Action.Status.RUNNING, new StatusFontIcon(FontAwesome.ADJUST, STATUS_ICON_PENDING,
i18n.getMessage("label.running"), statusLabelId));
stateMap.put(Action.Status.CANCELING, new StatusFontIcon(FontAwesome.TIMES_CIRCLE, STATUS_ICON_PENDING,
i18n.getMessage("label.cancelling"), statusLabelId));
stateMap.put(Action.Status.CANCELED, new StatusFontIcon(FontAwesome.TIMES_CIRCLE, STATUS_ICON_GREEN,
i18n.getMessage("label.cancelled"), statusLabelId));
stateMap.put(Action.Status.RETRIEVED, new StatusFontIcon(FontAwesome.CIRCLE_O, STATUS_ICON_PENDING,
i18n.getMessage("label.retrieved"), statusLabelId));
stateMap.put(Action.Status.DOWNLOAD, new StatusFontIcon(FontAwesome.CLOUD_DOWNLOAD, STATUS_ICON_PENDING,
i18n.getMessage("label.download"), statusLabelId));
stateMap.put(Action.Status.SCHEDULED, new StatusFontIcon(FontAwesome.HOURGLASS_1, STATUS_ICON_PENDING,
i18n.getMessage("label.scheduled"), statusLabelId));
return stateMap;
}
/**
* Initializes a map with all available active-state metadata.
*
* @param activeStateId
* @return the configured map
*/
public Map<IsActiveDecoration, StatusFontIcon> createActiveStatusLabelConfig(final String activeStateId) {
final HashMap<IsActiveDecoration, StatusFontIcon> activeStateMap = Maps.newHashMapWithExpectedSize(4);
activeStateMap.put(IsActiveDecoration.SCHEDULED,
new StatusFontIcon(FontAwesome.HOURGLASS_1, STATUS_ICON_PENDING, "Scheduled", activeStateId));
activeStateMap.put(IsActiveDecoration.ACTIVE,
new StatusFontIcon(null, STATUS_ICON_ACTIVE, "Active", activeStateId));
activeStateMap.put(IsActiveDecoration.IN_ACTIVE,
new StatusFontIcon(FontAwesome.CHECK_CIRCLE, STATUS_ICON_NEUTRAL, "In-active", activeStateId));
activeStateMap.put(IsActiveDecoration.IN_ACTIVE_ERROR,
new StatusFontIcon(FontAwesome.CHECK_CIRCLE, STATUS_ICON_RED, "In-active", activeStateId));
return activeStateMap;
}
}
}

View File

@@ -1,149 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ui.management.actionhistory;
import org.eclipse.hawkbit.ui.common.builder.LabelBuilder;
import org.eclipse.hawkbit.ui.components.SPUIButton;
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleSmallNoBorder;
import org.eclipse.hawkbit.ui.management.event.ManagementUIEvent;
import org.eclipse.hawkbit.ui.management.state.ManagementUIState;
import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil;
import org.eclipse.hawkbit.ui.utils.SPUIDefinitions;
import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions;
import org.vaadin.spring.events.EventBus;
import org.vaadin.spring.events.EventBus.UIEventBus;
import com.vaadin.server.FontAwesome;
import com.vaadin.shared.ui.label.ContentMode;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.VerticalLayout;
/**
*
*
*/
public class ActionHistoryHeader extends VerticalLayout {
private static final long serialVersionUID = -6276188234115774351L;
private final transient EventBus.UIEventBus eventBus;
private final ManagementUIState managementUIState;
private Label titleOfActionHistory;
private SPUIButton maxMinButton;
ActionHistoryHeader(final UIEventBus eventBus, final ManagementUIState managementUIState) {
this.eventBus = eventBus;
this.managementUIState = managementUIState;
buildComponent();
buildLayout();
restorePreviousState();
}
private void buildComponent() {
// create default title - it will be shown even when no data is
// available
titleOfActionHistory = new LabelBuilder().name(HawkbitCommonUtil.getArtifactoryDetailsLabelId(""))
.buildCaptionLabel();
titleOfActionHistory.setImmediate(true);
titleOfActionHistory.setContentMode(ContentMode.HTML);
maxMinButton = (SPUIButton) SPUIComponentProvider.getButton(SPUIDefinitions.EXPAND_ACTION_HISTORY, "", "", null,
true, FontAwesome.EXPAND, SPUIButtonStyleSmallNoBorder.class);
// listener for maximizing action history
maxMinButton.addClickListener(event -> maxMinButtonClicked());
}
private void buildLayout() {
final HorizontalLayout titleMaxIconsLayout = new HorizontalLayout();
titleMaxIconsLayout.addStyleName(SPUIStyleDefinitions.WIDGET_TITLE);
titleMaxIconsLayout.setSpacing(false);
titleMaxIconsLayout.setMargin(false);
titleMaxIconsLayout.setSizeFull();
titleMaxIconsLayout.addComponents(titleOfActionHistory, maxMinButton);
titleMaxIconsLayout.setComponentAlignment(titleOfActionHistory, Alignment.TOP_LEFT);
titleMaxIconsLayout.setComponentAlignment(maxMinButton, Alignment.TOP_RIGHT);
titleMaxIconsLayout.setExpandRatio(titleOfActionHistory, 0.8f);
titleMaxIconsLayout.setExpandRatio(maxMinButton, 0.2f);
// Note: here the only purpose of adding drop hints to the layout is to
// maintain consistent
// height for all widgets headers.
addComponent(titleMaxIconsLayout);
setComponentAlignment(titleMaxIconsLayout, Alignment.TOP_LEFT);
setWidth(100, Unit.PERCENTAGE);
setImmediate(true);
addStyleName("action-history-header");
addStyleName("bordered-layout");
addStyleName("no-border-bottom");
}
/**
* Populate Header Data for Target.
*
* @param targetName
* name of the target
*/
public void populateHeader(final String targetName) {
updateActionHistoryHeader(targetName);
}
private void maxMinButtonClicked() {
final Boolean flag = (Boolean) maxMinButton.getData();
if (flag == null || Boolean.FALSE.equals(flag)) {
// Clicked on max Icon
maximizedTableView();
managementUIState.setActionHistoryMaximized(Boolean.TRUE);
} else {
// Clicked on min icon
minimizeTableView();
managementUIState.setActionHistoryMaximized(Boolean.FALSE);
}
}
private void maximizedTableView() {
showMinIcon();
eventBus.publish(this, ManagementUIEvent.MAX_ACTION_HISTORY);
}
private void minimizeTableView() {
showMaxIcon();
eventBus.publish(this, ManagementUIEvent.MIN_ACTION_HISTORY);
}
/**
* Updates header with target name.
*
* @param targetName
* name of the target
*/
public void updateActionHistoryHeader(final String targetName) {
titleOfActionHistory.setValue(HawkbitCommonUtil.getActionHistoryLabelId(targetName));
}
private void showMinIcon() {
maxMinButton.togleIcon(FontAwesome.COMPRESS);
maxMinButton.setData(Boolean.TRUE);
}
private void showMaxIcon() {
maxMinButton.togleIcon(FontAwesome.EXPAND);
maxMinButton.setData(Boolean.FALSE);
}
private void restorePreviousState() {
if (managementUIState.isActionHistoryMaximized()) {
showMinIcon();
}
}
}

View File

@@ -0,0 +1,212 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ui.management.actionhistory;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.ui.common.grid.AbstractGrid;
import org.eclipse.hawkbit.ui.common.grid.AbstractGridComponentLayout;
import org.eclipse.hawkbit.ui.common.grid.DefaultGridHeader;
import org.eclipse.hawkbit.ui.common.grid.DefaultGridHeader.AbstractHeaderMaximizeSupport;
import org.eclipse.hawkbit.ui.common.table.BaseEntityEventType;
import org.eclipse.hawkbit.ui.management.event.ManagementUIEvent;
import org.eclipse.hawkbit.ui.management.event.TargetTableEvent;
import org.eclipse.hawkbit.ui.management.state.ManagementUIState;
import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil;
import org.eclipse.hawkbit.ui.utils.SPUIDefinitions;
import org.eclipse.hawkbit.ui.utils.UINotification;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
import org.vaadin.spring.events.EventBus.UIEventBus;
import org.vaadin.spring.events.EventScope;
import org.vaadin.spring.events.annotation.EventBusListenerMethod;
import com.vaadin.ui.UI;
/**
* Layout responsible for action-history-grid and the corresponding header.
*/
public class ActionHistoryLayout extends AbstractGridComponentLayout {
private static final long serialVersionUID = -3766179797384539821L;
private final transient DeploymentManagement deploymentManagement;
private final UINotification notification;
private final ManagementUIState managementUIState;
private transient AbstractGrid<?>.DetailsSupport details;
private Long masterForDetails;
/**
* Constructor.
*
* @param i18n
* @param deploymentManagement
* @param eventBus
* @param notification
* @param managementUIState
*/
public ActionHistoryLayout(final VaadinMessageSource i18n, final DeploymentManagement deploymentManagement,
final UIEventBus eventBus, final UINotification notification, final ManagementUIState managementUIState) {
super(i18n, eventBus);
this.deploymentManagement = deploymentManagement;
this.notification = notification;
this.managementUIState = managementUIState;
init();
}
@Override
public ActionHistoryHeader createGridHeader() {
return new ActionHistoryHeader(managementUIState).init();
}
@Override
public ActionHistoryGrid createGrid() {
return new ActionHistoryGrid(i18n, deploymentManagement, eventBus, notification, managementUIState);
}
@EventBusListenerMethod(scope = EventScope.UI)
void onEvent(final TargetTableEvent targetUIEvent) {
if (BaseEntityEventType.SELECTED_ENTITY == targetUIEvent.getEventType()) {
setData(SPUIDefinitions.DATA_AVAILABLE);
UI.getCurrent().access(() -> populateActionHistoryDetails(targetUIEvent.getEntity()));
} else if (BaseEntityEventType.REMOVE_ENTITY == targetUIEvent.getEventType()
&& targetUIEvent.getEntityIds().contains(managementUIState.getLastSelectedTargetId())) {
setData(SPUIDefinitions.NO_DATA);
UI.getCurrent().access(this::populateActionHistoryDetails);
}
}
/**
* Override default registration for selection propagation in order to
* interrupt update cascade in minimized state to prevent updates on
* invisible action-status-grid and message-grid.
* <p>
* The master selection is stored and propagation is performed as soon as
* the state changes to maximize and hence the dependent grids are updated.
*/
@Override
public void registerDetails(final AbstractGrid<?>.DetailsSupport details) {
this.details = details;
getGrid().addSelectionListener(event -> {
masterForDetails = (Long) event.getSelected().stream().findFirst().orElse(null);
if (managementUIState.isActionHistoryMaximized()) {
details.populateMasterDataAndRecalculateContainer(masterForDetails);
}
});
}
/**
* Populate action header and table for the target.
*
* @param target
* the target
*/
public void populateActionHistoryDetails(final Target target) {
if (null != target) {
((ActionHistoryHeader) getHeader()).updateActionHistoryHeader(target.getName());
((ActionHistoryGrid) getGrid()).populateSelectedTarget(target);
} else {
((ActionHistoryHeader) getHeader()).updateActionHistoryHeader(" ");
}
}
/**
* Populate empty action header and empty table for empty selection.
*/
public void populateActionHistoryDetails() {
((ActionHistoryHeader) getHeader()).updateActionHistoryHeader(" ");
((ActionHistoryGrid) getGrid()).populateSelectedTarget(null);
}
/**
* Header for ActionHistory with maximize-support.
*/
class ActionHistoryHeader extends DefaultGridHeader {
private static final long serialVersionUID = 1L;
/**
* Constructor.
*
* @param managementUIState
*/
ActionHistoryHeader(final ManagementUIState managementUIState) {
super(managementUIState);
this.setHeaderMaximizeSupport(
new ActionHistoryHeaderMaxSupport(this, SPUIDefinitions.EXPAND_ACTION_HISTORY));
}
/**
* Initializes the header.
*/
@Override
public ActionHistoryHeader init() {
super.init();
restorePreviousState();
return this;
}
/**
* Updates header with target name.
*
* @param targetName
* name of the target
*/
public void updateActionHistoryHeader(final String targetName) {
updateTitle(HawkbitCommonUtil.getActionHistoryLabelId(targetName));
}
/**
* Restores the previous min-max state.
*/
private void restorePreviousState() {
if (hasHeaderMaximizeSupport() && managementUIState.isActionHistoryMaximized()) {
getHeaderMaximizeSupport().showMinIcon();
}
}
}
/**
* Min-max support for header.
*/
class ActionHistoryHeaderMaxSupport extends AbstractHeaderMaximizeSupport {
private final DefaultGridHeader abstractGridHeader;
/**
* Constructor.
*
* @param abstractGridHeader
* @param maximizeButtonId
*/
protected ActionHistoryHeaderMaxSupport(final DefaultGridHeader abstractGridHeader,
final String maximizeButtonId) {
abstractGridHeader.super(maximizeButtonId);
this.abstractGridHeader = abstractGridHeader;
}
@Override
protected void maximize() {
details.populateMasterDataAndRecreateContainer(masterForDetails);
eventBus.publish(this, ManagementUIEvent.MAX_ACTION_HISTORY);
}
@Override
protected void minimize() {
eventBus.publish(this, ManagementUIEvent.MIN_ACTION_HISTORY);
}
/**
* Gets the grid header the maximize support is for.
*
* @return grid header
*/
protected DefaultGridHeader getGridHeader() {
return abstractGridHeader;
}
}
}

View File

@@ -1,844 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ui.management.actionhistory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.StringJoiner;
import org.eclipse.hawkbit.repository.ActionStatusFields;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.ActionWithStatusCount;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.ui.common.ConfirmationDialog;
import org.eclipse.hawkbit.ui.common.builder.LabelBuilder;
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.decorators.SPUIButtonStyleSmallNoBorder;
import org.eclipse.hawkbit.ui.management.event.ManagementUIEvent;
import org.eclipse.hawkbit.ui.management.event.PinUnpinEvent;
import org.eclipse.hawkbit.ui.management.event.TargetTableEvent;
import org.eclipse.hawkbit.ui.management.state.ManagementUIState;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
import org.eclipse.hawkbit.ui.utils.SPDateTimeUtil;
import org.eclipse.hawkbit.ui.utils.SPUIDefinitions;
import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions;
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
import org.eclipse.hawkbit.ui.utils.UINotification;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.vaadin.spring.events.EventBus;
import org.vaadin.spring.events.EventBus.UIEventBus;
import org.vaadin.spring.events.EventScope;
import org.vaadin.spring.events.annotation.EventBusListenerMethod;
import com.vaadin.data.Container;
import com.vaadin.data.Item;
import com.vaadin.data.util.HierarchicalContainer;
import com.vaadin.server.FontAwesome;
import com.vaadin.shared.ui.label.ContentMode;
import com.vaadin.ui.Button;
import com.vaadin.ui.Component;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.Table;
import com.vaadin.ui.TextArea;
import com.vaadin.ui.TreeTable;
import com.vaadin.ui.UI;
import com.vaadin.ui.themes.ValoTheme;
/**
* Table for {@link Target#getActions()} history.
*
*/
public class ActionHistoryTable extends TreeTable {
private static final long serialVersionUID = -1631514704696786653L;
private static final Logger LOG = LoggerFactory.getLogger(ActionHistoryTable.class);
private static final String BUTTON_CANCEL = "button.cancel";
private static final String BUTTON_OK = "button.ok";
private final VaadinMessageSource i18n;
private final transient DeploymentManagement deploymentManagement;
private final transient EventBus.UIEventBus eventBus;
private final UINotification notification;
private final ManagementUIState managementUIState;
private Container hierarchicalContainer;
private boolean alreadyHasMessages;
private Target target;
ActionHistoryTable(final VaadinMessageSource i18n, final DeploymentManagement deploymentManagement, final UIEventBus eventBus,
final UINotification notification, final ManagementUIState managementUIState) {
this.i18n = i18n;
this.deploymentManagement = deploymentManagement;
this.eventBus = eventBus;
this.notification = notification;
this.managementUIState = managementUIState;
initializeTableSettings();
buildComponent();
restorePreviousState();
setVisibleColumns(getVisbleColumns().toArray());
eventBus.subscribe(this);
setPageLength(SPUIDefinitions.PAGE_SIZE);
}
@EventBusListenerMethod(scope = EventScope.UI)
void onEvent(final ManagementUIEvent mgmtUIEvent) {
if (mgmtUIEvent == ManagementUIEvent.MAX_ACTION_HISTORY) {
UI.getCurrent().access(this::createTableContentForMax);
}
if (mgmtUIEvent == ManagementUIEvent.MIN_ACTION_HISTORY) {
UI.getCurrent().access(this::normalActionHistoryTable);
}
}
/*
* this method is called when the target history component is initialized
* without any data
*/
private void buildComponent() {
// create an empty container
createContainer();
setContainerDataSource(hierarchicalContainer);
addGeneratedColumns();
setColumnExpandRatioForMinimisedTable();
}
private void setColumnExpandRatioForMinimisedTable() {
setColumnExpandRatio(SPUIDefinitions.ACTION_HIS_TBL_ACTION_ID, 0.1F);
setColumnExpandRatio(SPUIDefinitions.ACTION_HIS_TBL_DIST, 0.3F);
setColumnExpandRatio(SPUIDefinitions.ACTION_HIS_TBL_STATUS, 0.15F);
setColumnExpandRatio(SPUIDefinitions.ACTION_HIS_TBL_DATETIME, 0.3F);
setColumnExpandRatio(SPUIDefinitions.ACTION_HIS_TBL_FORCED, 0.15F);
setColumnExpandRatio(SPUIDefinitions.ACTIONS_COLUMN, 0.2F);
}
private void initializeTableSettings() {
setId(UIComponentIdProvider.ACTION_HISTORY_TABLE_ID);
setSelectable(false);
setMultiSelect(false);
setSortEnabled(true);
setColumnReorderingAllowed(true);
setHeight(100.0F, Unit.PERCENTAGE);
setWidth(100.0F, Unit.PERCENTAGE);
setImmediate(true);
setStyleName("sp-table");
addStyleName(ValoTheme.TABLE_NO_VERTICAL_LINES);
addStyleName(ValoTheme.TABLE_SMALL);
setColumnAlignment(SPUIDefinitions.ACTION_HIS_TBL_FORCED, Align.CENTER);
setColumnAlignment(SPUIDefinitions.ACTION_HIS_TBL_STATUS, Align.CENTER);
setColumnAlignment(SPUIDefinitions.ACTIONS_COLUMN, Align.CENTER);
// listeners for child
addExpandListener(event -> {
expandParentActionRow(event.getItemId());
managementUIState.getExpandParentActionRowId().add(event.getItemId());
});
addCollapseListener(event -> {
collapseParentActionRow(event.getItemId());
managementUIState.getExpandParentActionRowId().remove(event.getItemId());
});
}
/**
* Create a empty HierarchicalContainer.
*/
public void createContainer() {
/* Create HierarchicalContainer container */
hierarchicalContainer = new HierarchicalContainer();
hierarchicalContainer.addContainerProperty(SPUIDefinitions.ACTION_HIS_TBL_ACTIVE_HIDDEN, String.class, null);
hierarchicalContainer.addContainerProperty(SPUIDefinitions.ACTION_HIS_TBL_FORCED, Action.class, null);
hierarchicalContainer.addContainerProperty(SPUIDefinitions.ACTION_HIS_TBL_ACTION_ID_HIDDEN, Long.class, null);
hierarchicalContainer.addContainerProperty(SPUIDefinitions.ACTION_HIS_TBL_ACTION_ID, String.class, null);
hierarchicalContainer.addContainerProperty(SPUIDefinitions.ACTION_HIS_TBL_DIST, String.class, null);
hierarchicalContainer.addContainerProperty(SPUIDefinitions.ACTION_HIS_TBL_DATETIME, String.class, null);
hierarchicalContainer.addContainerProperty(SPUIDefinitions.ACTION_HIS_TBL_STATUS_HIDDEN, Action.Status.class,
null);
hierarchicalContainer.addContainerProperty(SPUIDefinitions.ACTION_HIS_TBL_MSGS_HIDDEN, List.class, null);
hierarchicalContainer.addContainerProperty(SPUIDefinitions.ACTION_HIS_TBL_ROLLOUT_NAME, String.class, null);
}
private List<Object> getVisbleColumns() {
final List<Object> visibleColumnIds = new ArrayList<>();
visibleColumnIds.add(SPUIDefinitions.ACTION_HIS_TBL_ACTIVE);
visibleColumnIds.add(SPUIDefinitions.ACTION_HIS_TBL_DIST);
visibleColumnIds.add(SPUIDefinitions.ACTION_HIS_TBL_DATETIME);
visibleColumnIds.add(SPUIDefinitions.ACTION_HIS_TBL_STATUS);
visibleColumnIds.add(SPUIDefinitions.ACTION_HIS_TBL_FORCED);
visibleColumnIds.add(SPUIDefinitions.ACTIONS_COLUMN);
if (managementUIState.isActionHistoryMaximized()) {
visibleColumnIds.add(SPUIDefinitions.ACTION_HIS_TBL_ROLLOUT_NAME);
visibleColumnIds.add(SPUIDefinitions.ACTION_HIS_TBL_MSGS);
visibleColumnIds.add(1, SPUIDefinitions.ACTION_HIS_TBL_ACTION_ID);
}
return visibleColumnIds;
}
/**
* fetch the target details using controller id, and set it globally.
*
* @param selectedTarget
* reference of target
*/
public void populateTableData(final Target selectedTarget) {
target = selectedTarget;
refreshContainer();
}
/* re -create the container and get the data and set it to the table */
private void refreshContainer() {
getcontainerData();
// to expand parent row , if already expanded.
expandParentRow();
}
private void getcontainerData() {
hierarchicalContainer.removeAllItems();
if (target != null) {
/* service method to create action history for target */
final List<ActionWithStatusCount> actionHistory = deploymentManagement
.findActionsWithStatusCountByTargetOrderByIdDesc(target.getControllerId());
addDetailsToContainer(actionHistory);
}
}
/**
* Populate Container for Action.
*
* @param isActiveActions
* as flag
* @param reversedActions
* as action
* @param startIdx
* as sort
* @param target2
* @param actionHistoryMode
* as either {@link ActionHistoryMode.NORMAL} or
* {@link ActionHistoryMode.MAXIMIZED}
*/
@SuppressWarnings("unchecked")
private void addDetailsToContainer(final List<ActionWithStatusCount> actions) {
for (final ActionWithStatusCount actionWithStatusCount : actions) {
final Action action = actionWithStatusCount.getAction();
final Item item = hierarchicalContainer.addItem(actionWithStatusCount.getAction().getId());
item.getItemProperty(SPUIDefinitions.ACTION_HIS_TBL_STATUS_HIDDEN)
.setValue(actionWithStatusCount.getAction().getStatus());
/*
* add action id.
*/
item.getItemProperty(SPUIDefinitions.ACTION_HIS_TBL_ACTION_ID)
.setValue(actionWithStatusCount.getAction().getId().toString());
/*
* add active/inactive status to the item which will be used in
* Column generator to generate respective icon
*/
item.getItemProperty(SPUIDefinitions.ACTION_HIS_TBL_ACTIVE_HIDDEN).setValue(
actionWithStatusCount.getAction().isActive() ? SPUIDefinitions.ACTIVE : SPUIDefinitions.IN_ACTIVE);
/*
* add action Id to the item which will be used for fetching child
* items ( previous action status ) during expand
*/
item.getItemProperty(SPUIDefinitions.ACTION_HIS_TBL_ACTION_ID_HIDDEN)
.setValue(actionWithStatusCount.getAction().getId());
/*
* add distribution name to the item which will be displayed in the
* table. The name should not exceed certain limit.
*/
item.getItemProperty(SPUIDefinitions.ACTION_HIS_TBL_DIST)
.setValue(actionWithStatusCount.getDsName() + ":" + actionWithStatusCount.getDsVersion());
item.getItemProperty(SPUIDefinitions.ACTION_HIS_TBL_FORCED).setValue(action);
/* Default no child */
((Hierarchical) hierarchicalContainer).setChildrenAllowed(actionWithStatusCount.getAction().getId(), false);
item.getItemProperty(SPUIDefinitions.ACTION_HIS_TBL_DATETIME)
.setValue(SPDateTimeUtil.getFormattedDate(actionWithStatusCount.getAction().getLastModifiedAt()));
item.getItemProperty(SPUIDefinitions.ACTION_HIS_TBL_ROLLOUT_NAME)
.setValue(actionWithStatusCount.getRolloutName());
if (actionWithStatusCount.getActionStatusCount() > 0) {
((Hierarchical) hierarchicalContainer).setChildrenAllowed(actionWithStatusCount.getAction().getId(),
true);
}
}
}
private void addGeneratedColumns() {
addGeneratedColumn(SPUIDefinitions.ACTION_HIS_TBL_ACTIVE, new Table.ColumnGenerator() {
/* Serial Verion Id */
private static final long serialVersionUID = -8673604389011758339L;
@Override
public Component generateCell(final Table source, final Object itemId, final Object columnId) {
return getActiveColumn(itemId);
}
});
addGeneratedColumn(SPUIDefinitions.ACTION_HIS_TBL_STATUS, new Table.ColumnGenerator() {
/* Serial Verion Id */
private static final long serialVersionUID = 1L;
@Override
public Component generateCell(final Table source, final Object itemId, final Object columnId) {
return getStatusColumn(itemId);
}
});
// forced
addGeneratedColumn(SPUIDefinitions.ACTION_HIS_TBL_FORCED, new Table.ColumnGenerator() {
/* Serial Verion Id */
private static final long serialVersionUID = 1L;
@Override
public Component generateCell(final Table source, final Object itemId, final Object columnId) {
return getForcedColumn(itemId);
}
});
addGeneratedColumn(SPUIDefinitions.ACTIONS_COLUMN, new Table.ColumnGenerator() {
private static final long serialVersionUID = 1L;
@Override
public Component generateCell(final Table source, final Object itemId, final Object columnId) {
return createActionBarColumn(itemId);
}
});
}
private Component getForcedColumn(final Object itemId) {
final Action actionWithActiveStatus = (Action) hierarchicalContainer.getItem(itemId)
.getItemProperty(SPUIDefinitions.ACTION_HIS_TBL_FORCED).getValue();
final Label actionLabel = new LabelBuilder().name("").buildCaptionLabel();
actionLabel.setContentMode(ContentMode.HTML);
actionLabel.setStyleName("action-history-table-col-forced-label");
if (actionWithActiveStatus != null && actionWithActiveStatus.getActionType() == ActionType.FORCED) {
actionLabel.setValue(FontAwesome.BOLT.getHtml());
// setted Id for Forced.
actionLabel.setId("action.history.table.forcedId");
} else if (actionWithActiveStatus != null && actionWithActiveStatus.getActionType() == ActionType.TIMEFORCED) {
return actionLabelWithTimeForceIcon(actionWithActiveStatus, actionLabel);
}
return actionLabel;
}
private Component getActiveColumn(final Object itemId) {
final Action.Status status = (Action.Status) hierarchicalContainer.getItem(itemId)
.getItemProperty(SPUIDefinitions.ACTION_HIS_TBL_STATUS_HIDDEN).getValue();
String activeValue;
if (status == Action.Status.SCHEDULED) {
activeValue = Action.Status.SCHEDULED.toString().toLowerCase();
} else {
activeValue = (String) hierarchicalContainer.getItem(itemId)
.getItemProperty(SPUIDefinitions.ACTION_HIS_TBL_ACTIVE_HIDDEN).getValue();
}
final String distName = (String) hierarchicalContainer.getItem(itemId)
.getItemProperty(SPUIDefinitions.ACTION_HIS_TBL_DIST).getValue();
final Label activeStatusIcon = createActiveStatusLabel(activeValue,
(Action.Status) hierarchicalContainer.getItem(itemId)
.getItemProperty(SPUIDefinitions.ACTION_HIS_TBL_STATUS_HIDDEN)
.getValue() == Action.Status.ERROR);
activeStatusIcon.setId(new StringJoiner(".").add(distName).add(itemId.toString())
.add(SPUIDefinitions.ACTION_HIS_TBL_ACTIVE).add(activeValue).toString());
return activeStatusIcon;
}
private HorizontalLayout createActionBarColumn(final Object itemId) {
final HorizontalLayout actionBar = new HorizontalLayout();
final Item item = hierarchicalContainer.getItem(itemId);
final Long actionId = (Long) item.getItemProperty(SPUIDefinitions.ACTION_HIS_TBL_ACTION_ID_HIDDEN).getValue();
final String activeValue = (String) item.getItemProperty(SPUIDefinitions.ACTION_HIS_TBL_ACTIVE_HIDDEN)
.getValue();
final Action actionWithActiveStatus = (Action) item.getItemProperty(SPUIDefinitions.ACTION_HIS_TBL_FORCED)
.getValue();
if (actionWithActiveStatus == null) {
return null;
}
final boolean isActionActive = target != null && SPUIDefinitions.ACTIVE.equals(activeValue);
final Button actionCancel = SPUIComponentProvider.getButton(
UIComponentIdProvider.ACTION_HISTORY_TABLE_CANCEL_ID, "", i18n.getMessage("message.cancel.action"),
ValoTheme.BUTTON_TINY, true, FontAwesome.TIMES, SPUIButtonStyleSmallNoBorder.class);
actionCancel.setEnabled(isActionActive && !actionWithActiveStatus.isCancelingOrCanceled());
actionCancel.addClickListener(event -> confirmAndCancelAction(actionId));
final Button actionForce = SPUIComponentProvider.getButton(UIComponentIdProvider.ACTION_HISTORY_TABLE_FORCE_ID,
"", i18n.getMessage("message.force.action"), ValoTheme.BUTTON_TINY, true, FontAwesome.BOLT,
SPUIButtonStyleSmallNoBorder.class);
actionForce.setEnabled(
isActionActive && !actionWithActiveStatus.isForce() && !actionWithActiveStatus.isCancelingOrCanceled());
actionForce.addClickListener(event -> confirmAndForceAction(actionId));
final Button actionForceQuit = SPUIComponentProvider.getButton(
UIComponentIdProvider.ACTION_HISTORY_TABLE_FORCE_QUIT_ID, "", i18n.getMessage("message.forcequit.action"),
ValoTheme.BUTTON_TINY + " redicon", true, FontAwesome.TIMES, SPUIButtonStyleSmallNoBorder.class);
actionForceQuit.setEnabled(isActionActive && actionWithActiveStatus.isCancelingOrCanceled());
actionForceQuit.addClickListener(event -> confirmAndForceQuitAction(actionId));
actionBar.addComponents(actionCancel, actionForce, actionForceQuit);
return actionBar;
}
private Component getStatusColumn(final Object itemId) {
final Action.Status status = (Action.Status) hierarchicalContainer.getItem(itemId)
.getItemProperty(SPUIDefinitions.ACTION_HIS_TBL_STATUS_HIDDEN).getValue();
return getStatusIcon(status);
}
/**
* Load the rows of previous status history of the selected action row and
* add it next to the selected action row.
*
* @param parentRowIdx
* index of the selected action row.
*/
@SuppressWarnings("unchecked")
private void expandParentActionRow(final Object parentRowIdx) {
/* Get the item for which the expand is made */
final Item item = hierarchicalContainer.getItem(parentRowIdx);
if (null != item) {
final Long actionId = (Long) item.getItemProperty(SPUIDefinitions.ACTION_HIS_TBL_ACTION_ID_HIDDEN)
.getValue();
final Optional<Action> action = deploymentManagement.findActionWithDetails(actionId);
if (!action.isPresent()) {
return;
}
final Pageable pageReq = new PageRequest(0, 1000,
new Sort(Direction.DESC, ActionStatusFields.ID.getFieldName()));
final Page<ActionStatus> actionStatusList;
if (managementUIState.isActionHistoryMaximized()) {
actionStatusList = deploymentManagement.findActionStatusByActionWithMessages(pageReq, actionId);
} else {
actionStatusList = deploymentManagement.findActionStatusByAction(pageReq, actionId);
}
final List<ActionStatus> content = actionStatusList.getContent();
/*
* Since the recent action status and messages are already
* displaying with parent item, check if more than one action status
* available.
*/
int childIdx = 1;
for (final ActionStatus actionStatus : content) {
final String childId = parentRowIdx + " -> " + childIdx;
final Item childItem = hierarchicalContainer.addItem(childId);
if (null != childItem) {
/*
* For better UI, no need to display active/inactive icon
* for child items.
*/
childItem.getItemProperty(SPUIDefinitions.ACTION_HIS_TBL_ACTIVE_HIDDEN).setValue("");
childItem.getItemProperty(SPUIDefinitions.ACTION_HIS_TBL_DIST)
.setValue(action.get().getDistributionSet().getName() + ":"
+ action.get().getDistributionSet().getVersion());
childItem.getItemProperty(SPUIDefinitions.ACTION_HIS_TBL_DATETIME)
.setValue(SPDateTimeUtil.getFormattedDate(actionStatus.getCreatedAt()));
childItem.getItemProperty(SPUIDefinitions.ACTION_HIS_TBL_STATUS_HIDDEN)
.setValue(actionStatus.getStatus());
showOrHideMessage(childItem, actionStatus);
/* No further child items allowed for the child items */
((Hierarchical) hierarchicalContainer).setChildrenAllowed(childId, false);
/* Assign this childItem to the parent */
((Hierarchical) hierarchicalContainer).setParent(childId, parentRowIdx);
childIdx++;
}
}
}
}
/**
* Hide the rows of previous status history of the selected action row.
*
* @param parentRowIdx
* index of the selected action row.
*/
public void collapseParentActionRow(final Object parentRowIdx) {
/* Remove all child items for the clear the memory. */
final Collection<?> children = ((Hierarchical) hierarchicalContainer).getChildren(parentRowIdx);
if (children != null && !children.isEmpty()) {
String ids = children.toString().substring(1);
ids = ids.substring(0, ids.length() - 1);
for (final String childId : ids.split(", ")) {
((Hierarchical) hierarchicalContainer).removeItem(childId);
}
}
}
/**
* Get status icon.
*
* @param status
* as Status
* @return Label as UI
*/
private Label getStatusIcon(final Action.Status status) {
final Label label = new LabelBuilder().name("").buildLabel();
label.setContentMode(ContentMode.HTML);
final ActionStatusIconMapper mapping = ActionStatusIconMapper.MAPPINGS.get(status);
if (mapping == null) {
label.setDescription("");
label.setValue("");
return label;
}
label.setDescription(i18n.getMessage(mapping.getDescriptionI18N()));
label.setStyleName(mapping.getStyleName());
label.setValue(mapping.getIcon().getHtml());
return label;
}
// to show forced Icon
private Component actionLabelWithTimeForceIcon(final Action actionWithActiveStatus, final Label actionLabel) {
final long currentTimeMillis = System.currentTimeMillis();
final HorizontalLayout hLayout = new HorizontalLayout();
final Label autoForceLabel = new LabelBuilder().name("").id("action.history.table.timedforceId").buildLabel();
actionLabel.setValue(FontAwesome.BOLT.getHtml());
autoForceLabel.setContentMode(ContentMode.HTML);
autoForceLabel.setValue(FontAwesome.HISTORY.getHtml());
hLayout.addComponent(actionLabel);
hLayout.addComponent(autoForceLabel);
if (actionWithActiveStatus.isHitAutoForceTime(currentTimeMillis)) {
autoForceLabel.setDescription("autoforced");
autoForceLabel.setStyleName(SPUIStyleDefinitions.STATUS_ICON_GREEN);
autoForceLabel.setDescription("auto forced since " + SPDateTimeUtil
.getDurationFormattedString(actionWithActiveStatus.getForcedTime(), currentTimeMillis, i18n));
} else {
autoForceLabel.setDescription("auto forcing in " + SPDateTimeUtil
.getDurationFormattedString(currentTimeMillis, actionWithActiveStatus.getForcedTime(), i18n));
autoForceLabel.setStyleName("statusIconPending");
autoForceLabel.setValue(FontAwesome.HISTORY.getHtml());
}
return hLayout;
}
/**
* Create Status Label.
*
* @param activeValue
* as String
* @return Labeal as UI
*/
private static Label createActiveStatusLabel(final String activeValue, final boolean endedWithError) {
final Label label = new LabelBuilder().name("").buildLabel();
label.setContentMode(ContentMode.HTML);
if (SPUIDefinitions.SCHEDULED.equals(activeValue)) {
label.setDescription("Scheduled");
label.setValue(FontAwesome.HOURGLASS_1.getHtml());
} else if (SPUIDefinitions.ACTIVE.equals(activeValue)) {
label.setDescription("Active");
label.setStyleName("statusIconActive");
} else if (SPUIDefinitions.IN_ACTIVE.equals(activeValue)) {
if (endedWithError) {
label.setStyleName(SPUIStyleDefinitions.STATUS_ICON_RED);
} else {
label.setStyleName("statusIconNeutral");
}
label.setDescription("In-active");
label.setValue(FontAwesome.CHECK_CIRCLE.getHtml());
} else {
label.setValue("");
}
return label;
}
/**
* Creates full sized window for action history with message.
*/
private void createTableContentForMax() {
setColumnCollapsingAllowed(true);
if (!alreadyHasMessages) {
/*
* check to avoid DB call for fetching the messages again and again
* if already available in container.
*/
getcontainerData();
// to expand parent row , if already expanded.
expandParentRow();
alreadyHasMessages = true;
}
addGeneratedColumn(SPUIDefinitions.ACTION_HIS_TBL_MSGS, new Table.ColumnGenerator() {
private static final long serialVersionUID = 1L;
@SuppressWarnings("unchecked")
@Override
public Component generateCell(final Table source, final Object itemId, final Object columnId) {
final List<String> messages = (List<String>) hierarchicalContainer.getItem(itemId)
.getItemProperty(SPUIDefinitions.ACTION_HIS_TBL_MSGS_HIDDEN).getValue();
return createMessagesBlock(messages);
}
});
setVisibleColumns(getVisbleColumns().toArray());
setColumnExpantRatioOnTableMaximize();
}
private void setColumnExpantRatioOnTableMaximize() {
/* set messages column can expand the rest of the available space */
setColumnExpandRatio(SPUIDefinitions.ACTION_HIS_TBL_ACTIVE, 0.1F);
setColumnExpandRatio(SPUIDefinitions.ACTION_HIS_TBL_ACTION_ID, 0.1F);
setColumnExpandRatio(SPUIDefinitions.ACTION_HIS_TBL_STATUS, 0.1F);
setColumnExpandRatio(SPUIDefinitions.ACTION_HIS_TBL_DIST, 0.2F);
setColumnExpandRatio(SPUIDefinitions.ACTION_HIS_TBL_FORCED, 0.1F);
setColumnExpandRatio(SPUIDefinitions.ACTION_HIS_TBL_ROLLOUT_NAME, 0.1F);
setColumnExpandRatio(SPUIDefinitions.ACTION_HIS_TBL_MSGS, 0.35F);
setColumnExpandRatio(SPUIDefinitions.ACTION_HIS_TBL_DATETIME, 0.15F);
setColumnExpandRatio(SPUIDefinitions.ACTIONS_COLUMN, 0.2F);
}
/**
* create Message block for Actions.
*
* @param messages
* as List of msg
* @return Component as UI
*/
protected Component createMessagesBlock(final List<String> messages) {
final TextArea textArea = new TextArea();
textArea.addStyleName(ValoTheme.TEXTAREA_BORDERLESS);
textArea.addStyleName(ValoTheme.TEXTAREA_TINY);
textArea.addStyleName("inline-icon");
textArea.setSizeFull();
int index = 1;
final StringBuilder updateStatusMessages = new StringBuilder();
if (messages != null && !messages.isEmpty()) {
/* Messages are available */
for (final String msg : messages) {
updateStatusMessages.append('[').append(index).append("]: ").append(msg).append('\n');
index++;
}
} else {
/* Messages are not available */
updateStatusMessages.append(i18n.getMessage("message.no.available"));
}
textArea.setValue(updateStatusMessages.toString());
textArea.setReadOnly(Boolean.TRUE);
return textArea;
}
/**
* Show or hide message.
*
* @param actionHistoryMode
* {@link ActionHisTableType}
* @param childItem
* {@link Item}
* @param actionStatus
* {@link ActionHisTableType}
*/
@SuppressWarnings("unchecked")
private void showOrHideMessage(final Item childItem, final ActionStatus actionStatus) {
if (managementUIState.isActionHistoryMaximized()) {
childItem.getItemProperty(SPUIDefinitions.ACTION_HIS_TBL_MSGS_HIDDEN).setValue(actionStatus.getMessages());
}
}
/**
* View History Table in normal window mode.
*/
private void normalActionHistoryTable() {
setColumnCollapsingAllowed(false);
managementUIState.setActionHistoryMaximized(false);
removeGeneratedColumn(SPUIDefinitions.ACTION_HIS_TBL_MSGS);
setVisibleColumns(getVisbleColumns().toArray());
setColumnExpandRatioForMinimisedTable();
}
/**
* Show confirmation window and if ok then only, force the action.
*
* @param actionId
* as Id if the action needs to be forced.
*/
private void confirmAndForceAction(final Long actionId) {
/* Display the confirmation */
final ConfirmationDialog confirmDialog = new ConfirmationDialog(i18n.getMessage("caption.force.action.confirmbox"),
i18n.getMessage("message.force.action.confirm"), i18n.getMessage(BUTTON_OK), i18n.getMessage(BUTTON_CANCEL), ok -> {
if (!ok) {
return;
}
deploymentManagement.forceTargetAction(actionId);
populateAndupdateTargetDetails(target);
notification.displaySuccess(i18n.getMessage("message.force.action.success"));
});
UI.getCurrent().addWindow(confirmDialog.getWindow());
confirmDialog.getWindow().bringToFront();
}
private void confirmAndForceQuitAction(final Long actionId) {
/* Display the confirmation */
final ConfirmationDialog confirmDialog = new ConfirmationDialog(i18n.getMessage("caption.forcequit.action.confirmbox"),
i18n.getMessage("message.forcequit.action.confirm"), i18n.getMessage(BUTTON_OK), i18n.getMessage(BUTTON_CANCEL), ok -> {
if (!ok) {
return;
}
final boolean cancelResult = forceQuitActiveAction(actionId);
if (cancelResult) {
populateAndupdateTargetDetails(target);
notification.displaySuccess(i18n.getMessage("message.forcequit.action.success"));
} else {
notification.displayValidationError(i18n.getMessage("message.forcequit.action.failed"));
}
}, FontAwesome.WARNING);
UI.getCurrent().addWindow(confirmDialog.getWindow());
confirmDialog.getWindow().bringToFront();
}
/**
* Show confirmation window and if ok then only, cancel the action.
*
* @param actionId
* as Id if the action needs to be cancelled.
*/
private void confirmAndCancelAction(final Long actionId) {
if (actionId == null) {
return;
}
final ConfirmationDialog confirmDialog = new ConfirmationDialog(i18n.getMessage("caption.cancel.action.confirmbox"),
i18n.getMessage("message.cancel.action.confirm"), i18n.getMessage(BUTTON_OK), i18n.getMessage(BUTTON_CANCEL), ok -> {
if (!ok) {
return;
}
final boolean cancelResult = cancelActiveAction(actionId);
if (cancelResult) {
populateAndupdateTargetDetails(target);
notification.displaySuccess(i18n.getMessage("message.cancel.action.success"));
} else {
notification.displayValidationError(i18n.getMessage("message.cancel.action.failed"));
}
});
UI.getCurrent().addWindow(confirmDialog.getWindow());
confirmDialog.getWindow().bringToFront();
}
private void populateAndupdateTargetDetails(final Target target) {
// show the updated target action history details
populateTableData(target);
// update the target table and its pinning details
updateTargetAndDsTable();
}
// service call to cancel the active action
private boolean cancelActiveAction(final Long actionId) {
if (actionId != null) {
try {
deploymentManagement.cancelAction(actionId);
return true;
} catch (final CancelActionNotAllowedException e) {
LOG.info("Cancel action not allowed exception :{}", e);
return false;
}
}
return false;
}
// service call to cancel the active action
private boolean forceQuitActiveAction(final Long actionId) {
if (actionId != null) {
try {
deploymentManagement.forceQuitAction(actionId);
return true;
} catch (final CancelActionNotAllowedException e) {
LOG.info("Force Cancel action not allowed exception :{}", e);
return false;
}
}
return false;
}
private void updateTargetAndDsTable() {
eventBus.publish(this, new TargetTableEvent(BaseEntityEventType.UPDATED_ENTITY, target));
updateDistributionTableStyle();
}
/**
* Update the colors of Assigned and installed distribution set in Target
* Pinning.
*/
private void updateDistributionTableStyle() {
if (managementUIState.getDistributionTableFilters().getPinnedTarget().map(TargetIdName::getTargetId)
.map(target.getId()::equals).orElse(false)) {
eventBus.publish(this, PinUnpinEvent.PIN_TARGET);
}
}
/**
* empty the container when there is no data selected in Target Table.
*/
public void clearContainerData() {
getContainerDataSource().removeAllItems();
}
/**
* Set messages false.
*
* @param alreadyHasMessages
* the alreadyHasMessages to set
*/
public void setAlreadyHasMessages(final boolean alreadyHasMessages) {
this.alreadyHasMessages = alreadyHasMessages;
}
private void restorePreviousState() {
if (managementUIState.isActionHistoryMaximized()) {
createTableContentForMax();
}
}
private void expandParentRow() {
if (null != managementUIState.getExpandParentActionRowId()
&& !managementUIState.getExpandParentActionRowId().isEmpty()) {
for (final Object obj : managementUIState.getExpandParentActionRowId()) {
expandParentActionRow(obj);
}
}
}
}

View File

@@ -0,0 +1,153 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ui.management.actionhistory;
import static org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil.isNotNullOrEmpty;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.ui.utils.SPUIDefinitions;
import org.eclipse.hawkbit.ui.utils.SpringContextHelper;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.vaadin.addons.lazyquerycontainer.AbstractBeanQuery;
import org.vaadin.addons.lazyquerycontainer.QueryDefinition;
/**
* Simple implementation of generics bean query which dynamically loads
* {@link ProxyActionStatus} batch of beans.
*/
public class ActionStatusBeanQuery extends AbstractBeanQuery<ProxyActionStatus> {
private static final long serialVersionUID = 1L;
private Sort sort = new Sort(Direction.DESC, "id");
private transient DeploymentManagement deploymentManagement;
private Long currentSelectedActionId;
private transient Page<ActionStatus> firstPageActionStates;
/**
* Parametric Constructor.
*
* @param definition
* QueryDefinition contains the query properties.
* @param queryConfig
* Implementation specific configuration.
* @param sortPropertyIds
* The properties participating in sort.
* @param sortStates
* The ascending or descending state of sort properties.
*/
public ActionStatusBeanQuery(final QueryDefinition definition, final Map<String, Object> queryConfig,
final Object[] sortPropertyIds, final boolean[] sortStates) {
super(definition, queryConfig, sortPropertyIds, sortStates);
if (isNotNullOrEmpty(queryConfig)) {
currentSelectedActionId = (Long) queryConfig.get(SPUIDefinitions.ACTIONSTATES_BY_ACTION);
}
if (sortStates.length > 0) {
// Initialize sort
sort = new Sort(sortStates[0] ? Direction.ASC : Direction.DESC, (String) sortPropertyIds[0]);
// Add sort
for (int distId = 1; distId < sortPropertyIds.length; distId++) {
sort.and(new Sort(sortStates[distId] ? Direction.ASC : Direction.DESC,
(String) sortPropertyIds[distId]));
}
}
}
@Override
protected ProxyActionStatus constructBean() {
return new ProxyActionStatus();
}
@Override
protected List<ProxyActionStatus> loadBeans(final int startIndex, final int count) {
Page<ActionStatus> actionBeans;
if (startIndex == 0 && firstPageActionStates != null) {
actionBeans = firstPageActionStates;
} else {
actionBeans = getDeploymentManagement()
.findActionStatusByAction(
new PageRequest(startIndex / SPUIDefinitions.PAGE_SIZE, SPUIDefinitions.PAGE_SIZE, sort),
currentSelectedActionId);
}
return createProxyActionStates(actionBeans);
}
/**
* Creates a list of {@link ProxyActionStatus} for presentation layer from
* page of {@link ActionStatus}.
*
* @param actionBeans
* page of {@link ActionStatus}
* @return list of {@link ProxyActionStatus}
*/
private static List<ProxyActionStatus> createProxyActionStates(final Page<ActionStatus> actionStatusBeans) {
final List<ProxyActionStatus> proxyActionStates = new ArrayList<>();
for (final ActionStatus actionStatus : actionStatusBeans) {
final ProxyActionStatus proxyAS = new ProxyActionStatus();
proxyAS.setCreatedAt(actionStatus.getCreatedAt());
proxyAS.setStatus(actionStatus.getStatus());
proxyAS.setId(actionStatus.getId());
proxyActionStates.add(proxyAS);
}
return proxyActionStates;
}
/*
* (non-Javadoc)
*
* @see
* org.vaadin.addons.lazyquerycontainer.AbstractBeanQuery#saveBeans(java
* .util.List, java.util.List, java.util.List)
*/
@Override
protected void saveBeans(List<ProxyActionStatus> addedBeans, List<ProxyActionStatus> modifiedBeans,
List<ProxyActionStatus> removedBeans) {
// CRUD operations on Target will be done through repository methods
}
@Override
public int size() {
long size = 0;
if (currentSelectedActionId != null) {
firstPageActionStates = getDeploymentManagement().findActionStatusByAction(
new PageRequest(0, SPUIDefinitions.PAGE_SIZE, sort), currentSelectedActionId);
size = firstPageActionStates.getTotalElements();
}
if (size > Integer.MAX_VALUE) {
return Integer.MAX_VALUE;
}
return (int) size;
}
/**
* Lazy gets deploymentManagement.
*
* @return the deploymentManagement
*/
public DeploymentManagement getDeploymentManagement() {
if (null == deploymentManagement) {
deploymentManagement = SpringContextHelper.getBean(DeploymentManagement.class);
}
return deploymentManagement;
}
}

View File

@@ -0,0 +1,159 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ui.management.actionhistory;
import java.util.Map;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.ui.common.grid.AbstractGrid;
import org.eclipse.hawkbit.ui.customrenderers.renderers.HtmlLabelRenderer;
import org.eclipse.hawkbit.ui.management.actionhistory.ActionHistoryGrid.LabelConfig;
import org.eclipse.hawkbit.ui.rollout.StatusFontIcon;
import org.eclipse.hawkbit.ui.utils.SPUIDefinitions;
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
import org.vaadin.addons.lazyquerycontainer.BeanQueryFactory;
import org.vaadin.addons.lazyquerycontainer.LazyQueryContainer;
import org.vaadin.addons.lazyquerycontainer.LazyQueryDefinition;
import org.vaadin.spring.events.EventBus.UIEventBus;
import com.google.common.collect.Maps;
/**
* This grid presents the action states for a selected action.
*/
public class ActionStatusGrid extends AbstractGrid<LazyQueryContainer> {
private static final long serialVersionUID = 1L;
private static final String[] leftAlignedColumns = new String[] { ProxyActionStatus.PXY_AS_CREATED_AT };
private static final String[] centerAlignedColumns = new String[] { ProxyActionStatus.PXY_AS_STATUS };
private final AlignCellStyleGenerator alignGenerator;
private final ModifiedTimeTooltipGenerator modTimetooltipGenerator;
private final Map<Action.Status, StatusFontIcon> states;
private final BeanQueryFactory<ActionStatusBeanQuery> targetQF = new BeanQueryFactory<>(
ActionStatusBeanQuery.class);
/**
* Constructor.
*
* @param i18n
* @param eventBus
*/
protected ActionStatusGrid(final VaadinMessageSource i18n, final UIEventBus eventBus) {
super(i18n, eventBus, null);
setSingleSelectionSupport(new SingleSelectionSupport());
setDetailsSupport(new DetailsSupport());
final LabelConfig conf = new ActionHistoryGrid.LabelConfig();
states = conf.createStatusLabelConfig(i18n, UIComponentIdProvider.ACTION_STATUS_GRID_STATUS_LABEL_ID);
alignGenerator = new AlignCellStyleGenerator(leftAlignedColumns, centerAlignedColumns, null);
modTimetooltipGenerator = new ModifiedTimeTooltipGenerator(ProxyActionStatus.PXY_AS_CREATED_AT);
init();
}
@Override
protected LazyQueryContainer createContainer() {
configureQueryFactory();
return new LazyQueryContainer(
new LazyQueryDefinition(true, SPUIDefinitions.PAGE_SIZE, ProxyActionStatus.PXY_AS_ID), targetQF);
}
@Override
public void refreshContainer() {
configureQueryFactory();
super.refreshContainer();
}
protected void configureQueryFactory() {
// ADD all the filters to the query config
final Map<String, Object> queryConfig = Maps.newHashMapWithExpectedSize(1);
queryConfig.put(SPUIDefinitions.ACTIONSTATES_BY_ACTION, getDetailsSupport().getMasterDataId());
// Create ActionBeanQuery factory with the query config.
targetQF.setQueryConfiguration(queryConfig);
}
/**
* Gets type-save access to LazyQueryContainer.
*
* @return LazyQueryContainer
*/
private LazyQueryContainer getLazyQueryContainer() {
return (LazyQueryContainer) getContainerDataSource();
}
@Override
protected void addContainerProperties() {
final LazyQueryContainer lqContainer = getLazyQueryContainer();
lqContainer.addContainerProperty(ProxyActionStatus.PXY_AS_CREATED_AT, Long.class, null, true, true);
lqContainer.addContainerProperty(ProxyActionStatus.PXY_AS_STATUS, Action.Status.class, null, true, false);
}
@Override
protected void setColumnExpandRatio() {
getColumn(ProxyActionStatus.PXY_AS_STATUS).setMinimumWidth(53);
getColumn(ProxyActionStatus.PXY_AS_STATUS).setMaximumWidth(55);
getColumn(ProxyActionStatus.PXY_AS_CREATED_AT).setMinimumWidth(100);
getColumn(ProxyActionStatus.PXY_AS_CREATED_AT).setMaximumWidth(400);
}
@Override
protected void setColumnHeaderNames() {
getColumn(ProxyActionStatus.PXY_AS_STATUS).setHeaderCaption(SPUIDefinitions.ACTION_HIS_TBL_STATUS);
getColumn(ProxyActionStatus.PXY_AS_CREATED_AT).setHeaderCaption(SPUIDefinitions.ACTION_HIS_TBL_DATETIME);
}
@Override
protected String getGridId() {
return UIComponentIdProvider.ACTION_HISTORY_DETAILS_GRID_ID;
}
@Override
protected void setColumnProperties() {
clearSortOrder();
setColumns(ProxyActionStatus.PXY_AS_STATUS, ProxyActionStatus.PXY_AS_CREATED_AT);
alignColumns();
}
@Override
protected void addColumnRenderes() {
getColumn(ProxyActionStatus.PXY_AS_STATUS).setRenderer(new HtmlLabelRenderer(),
new HtmlStatusLabelConverter(this::createStatusLabelMetadata));
getColumn(ProxyActionStatus.PXY_AS_CREATED_AT).setConverter(new LongToFormattedDateStringConverter());
}
private StatusFontIcon createStatusLabelMetadata(final Action.Status status) {
return states.get(status);
}
@Override
protected void setHiddenColumns() {
getColumn(ProxyActionStatus.PXY_AS_STATUS).setHidable(false);
getColumn(ProxyActionStatus.PXY_AS_CREATED_AT).setHidable(false);
}
/**
* Sets the alignment cell-style-generator that handles the alignment for
* the grid cells.
*/
private void alignColumns() {
setCellStyleGenerator(alignGenerator);
}
@Override
protected CellDescriptionGenerator getDescriptionGenerator() {
return modTimetooltipGenerator;
}
}

View File

@@ -0,0 +1,49 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ui.management.actionhistory;
import org.eclipse.hawkbit.ui.common.grid.AbstractGridComponentLayout;
import org.eclipse.hawkbit.ui.common.grid.DefaultGridHeader;
import org.eclipse.hawkbit.ui.management.state.ManagementUIState;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
import org.vaadin.spring.events.EventBus.UIEventBus;
/**
* Layout responsible for action-states-grid and the corresponding header.
*/
public class ActionStatusLayout extends AbstractGridComponentLayout {
private static final long serialVersionUID = 1L;
protected final ManagementUIState managementUIState;
/**
* Constructor.
*
* @param i18n
* @param eventBus
* @param managementUIState
*/
public ActionStatusLayout(final VaadinMessageSource i18n, final UIEventBus eventBus,
final ManagementUIState managementUIState) {
super(i18n, eventBus);
this.managementUIState = managementUIState;
init();
}
@Override
public DefaultGridHeader createGridHeader() {
return new DefaultGridHeader(managementUIState, "Action States").init();
}
@Override
public ActionStatusGrid createGrid() {
return new ActionStatusGrid(i18n, eventBus);
}
}

View File

@@ -0,0 +1,161 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ui.management.actionhistory;
import static org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil.isNotNullOrEmpty;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.ui.utils.SPUIDefinitions;
import org.eclipse.hawkbit.ui.utils.SpringContextHelper;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.vaadin.addons.lazyquerycontainer.AbstractBeanQuery;
import org.vaadin.addons.lazyquerycontainer.QueryDefinition;
import com.google.common.base.Strings;
/**
* Simple implementation of generic bean query which dynamically loads
* {@link ProxyMessage} batch of beans.
*/
public class ActionStatusMsgBeanQuery extends AbstractBeanQuery<ProxyMessage> {
private static final long serialVersionUID = 1L;
private Sort sort = new Sort(Direction.DESC, "id");
private transient DeploymentManagement deploymentManagement;
private Long currentSelectedActionStatusId;
private String noMessageText;
private transient Page<String> firstPageMessages;
/**
* Parametric Constructor.
*
* @param definition
* QueryDefinition contains the query properties.
* @param queryConfig
* Implementation specific configuration.
* @param sortPropertyIds
* The properties participating in sort.
* @param sortStates
* The ascending or descending state of sort properties.
*/
public ActionStatusMsgBeanQuery(final QueryDefinition definition, final Map<String, Object> queryConfig,
final Object[] sortPropertyIds, final boolean[] sortStates) {
super(definition, queryConfig, sortPropertyIds, sortStates);
if (isNotNullOrEmpty(queryConfig)) {
currentSelectedActionStatusId = (Long) queryConfig.get(SPUIDefinitions.MESSAGES_BY_ACTIONSTATUS);
noMessageText = (String) queryConfig.get(SPUIDefinitions.NO_MSG_PROXY);
}
if (sortStates.length > 0) {
// Initialize sort
sort = new Sort(sortStates[0] ? Direction.ASC : Direction.DESC, (String) sortPropertyIds[0]);
// Add sort
for (int distId = 1; distId < sortPropertyIds.length; distId++) {
sort.and(new Sort(sortStates[distId] ? Direction.ASC : Direction.DESC,
(String) sortPropertyIds[distId]));
}
}
}
@Override
protected ProxyMessage constructBean() {
return new ProxyMessage();
}
@Override
protected List<ProxyMessage> loadBeans(final int startIndex, final int count) {
Page<String> actionBeans;
if (startIndex == 0 && firstPageMessages != null) {
actionBeans = firstPageMessages;
} else {
actionBeans = getDeploymentManagement().findMessagesByActionStatusId(
new PageRequest(startIndex / SPUIDefinitions.PAGE_SIZE, SPUIDefinitions.PAGE_SIZE, sort),
currentSelectedActionStatusId);
}
return createProxyMessages(actionBeans);
}
/**
* Creates a list of {@link ProxyActionStatus} for presentation layer from
* page of {@link ActionStatus}.
*
* @param actionBeans
* page of {@link ActionStatus}
* @return list of {@link ProxyActionStatus}
*/
private List<ProxyMessage> createProxyMessages(final Page<String> messages) {
final List<ProxyMessage> proxyMsgs = new ArrayList<>(messages.getNumberOfElements());
Long idx = messages.getNumber() * ((long) messages.getSize());
for (final String msg : messages) {
idx++;
final ProxyMessage proxyMsg = new ProxyMessage();
proxyMsg.setMessage(msg);
proxyMsg.setId(String.valueOf(idx));
proxyMsgs.add(proxyMsg);
}
if (messages.getTotalElements() == 1L && Strings.isNullOrEmpty(proxyMsgs.get(0).getMessage())) {
proxyMsgs.get(0).setMessage(noMessageText);
}
return proxyMsgs;
}
/*
* (non-Javadoc)
*
* @see
* org.vaadin.addons.lazyquerycontainer.AbstractBeanQuery#saveBeans(java
* .util.List, java.util.List, java.util.List)
*/
@Override
protected void saveBeans(List<ProxyMessage> addedBeans, List<ProxyMessage> modifiedBeans,
List<ProxyMessage> removedBeans) {
// CRUD operations on Target will be done through repository methods
}
@Override
public int size() {
long size = 0;
if (currentSelectedActionStatusId != null) {
firstPageMessages = getDeploymentManagement().findMessagesByActionStatusId(
new PageRequest(0, SPUIDefinitions.PAGE_SIZE, sort), currentSelectedActionStatusId);
size = firstPageMessages.getTotalElements();
}
if (size > Integer.MAX_VALUE) {
return Integer.MAX_VALUE;
}
return (int) size;
}
/**
* Lazy gets deploymentManagement.
*
* @return the deploymentManagement
*/
public DeploymentManagement getDeploymentManagement() {
if (null == deploymentManagement) {
deploymentManagement = SpringContextHelper.getBean(DeploymentManagement.class);
}
return deploymentManagement;
}
}

View File

@@ -0,0 +1,210 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ui.management.actionhistory;
import java.util.Map;
import org.eclipse.hawkbit.ui.common.grid.AbstractGrid;
import org.eclipse.hawkbit.ui.utils.SPUIDefinitions;
import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions;
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
import org.vaadin.addons.lazyquerycontainer.BeanQueryFactory;
import org.vaadin.addons.lazyquerycontainer.LazyQueryContainer;
import org.vaadin.addons.lazyquerycontainer.LazyQueryDefinition;
import org.vaadin.spring.events.EventBus.UIEventBus;
import com.google.common.collect.Maps;
import com.vaadin.data.Item;
import com.vaadin.event.ItemClickEvent;
import com.vaadin.event.ItemClickEvent.ItemClickListener;
import com.vaadin.ui.Component;
import com.vaadin.ui.TextArea;
import com.vaadin.ui.themes.ValoTheme;
/**
* This grid presents the messages for a selected action-status.
*/
public class ActionStatusMsgGrid extends AbstractGrid<LazyQueryContainer> {
private static final long serialVersionUID = 1L;
private static final String[] rightAlignedColumns = new String[] { ProxyMessage.PXY_MSG_ID };
private final String noMsgText;
private final AlignCellStyleGenerator alignGenerator;
private final BeanQueryFactory<ActionStatusMsgBeanQuery> targetQF = new BeanQueryFactory<>(
ActionStatusMsgBeanQuery.class);
/**
* Constructor.
*
* @param i18n
* @param eventBus
*/
protected ActionStatusMsgGrid(final VaadinMessageSource i18n, final UIEventBus eventBus) {
super(i18n, eventBus, null);
noMsgText = createNoMessageProxy(i18n);
setSingleSelectionSupport(new SingleSelectionSupport());
setDetailsSupport(new DetailsSupport());
alignGenerator = new AlignCellStyleGenerator(null, null, rightAlignedColumns);
addStyleName(SPUIStyleDefinitions.ACTION_HISTORY_MESSAGE_GRID);
setDetailsGenerator(new MessageDetailsGenerator());
this.addItemClickListener(new ItemClickListener() {
private static final long serialVersionUID = 1L;
@Override
public void itemClick(final ItemClickEvent event) {
final Object itemId = event.getItemId();
setDetailsVisible(itemId, !isDetailsVisible(itemId));
}
});
init();
}
@Override
protected LazyQueryContainer createContainer() {
configureQueryFactory();
return new LazyQueryContainer(new LazyQueryDefinition(true, SPUIDefinitions.PAGE_SIZE, null), targetQF);
}
@Override
public void refreshContainer() {
for (final Object itemId : getContainerDataSource().getItemIds()) {
setDetailsVisible(itemId, false);
}
configureQueryFactory();
super.refreshContainer();
}
protected void configureQueryFactory() {
// ADD all the filters to the query config
final Map<String, Object> queryConfig = Maps.newHashMapWithExpectedSize(2);
queryConfig.put(SPUIDefinitions.MESSAGES_BY_ACTIONSTATUS, getDetailsSupport().getMasterDataId());
queryConfig.put(SPUIDefinitions.NO_MSG_PROXY, noMsgText);
// Create ActionBeanQuery factory with the query config.
targetQF.setQueryConfiguration(queryConfig);
}
/**
* Gets type-save access to LazyQueryContainer.
*
* @return LazyQueryContainer
*/
private LazyQueryContainer getLazyQueryContainer() {
return (LazyQueryContainer) getContainerDataSource();
}
@Override
protected void addContainerProperties() {
getLazyQueryContainer().addContainerProperty(ProxyMessage.PXY_MSG_ID, String.class, null, true, false);
getLazyQueryContainer().addContainerProperty(ProxyMessage.PXY_MSG_VALUE, String.class, null, true, false);
}
@Override
protected void setColumnExpandRatio() {
getColumn(ProxyMessage.PXY_MSG_ID).setExpandRatio(0);
getColumn(ProxyMessage.PXY_MSG_VALUE).setExpandRatio(1);
}
@Override
protected void setColumnHeaderNames() {
getColumn(ProxyMessage.PXY_MSG_ID).setHeaderCaption("##");
getColumn(ProxyMessage.PXY_MSG_VALUE).setHeaderCaption(SPUIDefinitions.ACTION_HIS_TBL_MSGS);
}
@Override
protected String getGridId() {
return UIComponentIdProvider.ACTION_HISTORY_MESSAGE_GRID_ID;
}
@Override
protected void setColumnProperties() {
clearSortOrder();
setColumns(ProxyMessage.PXY_MSG_ID, ProxyMessage.PXY_MSG_VALUE);
setFrozenColumnCount(2);
alignColumns();
}
@Override
protected void addColumnRenderes() {
// no specific column renderers
}
@Override
protected void setHiddenColumns() {
getColumn(ProxyMessage.PXY_MSG_ID).setHidable(false);
getColumn(ProxyMessage.PXY_MSG_VALUE).setHidable(false);
}
@Override
protected CellDescriptionGenerator getDescriptionGenerator() {
return null;
}
/**
* Sets the alignment cell-style-generator that handles the alignment for
* the grid cells.
*/
private void alignColumns() {
setCellStyleGenerator(alignGenerator);
}
/**
* Creates the default text when no message is available for action-status
*
* @param i18n
* @return default text
*/
private static String createNoMessageProxy(final VaadinMessageSource i18n) {
return i18n.getMessage("message.no.available");
}
protected class MessageDetailsGenerator implements DetailsGenerator {
private static final long serialVersionUID = 1L;
@Override
public Component getDetails(final RowReference rowReference) {
// Find the bean to generate details for
final Item item = rowReference.getItem();
final String message = (String) item.getItemProperty(ProxyMessage.PXY_MSG_VALUE).getValue();
final TextArea textArea = new TextArea();
textArea.addStyleName(ValoTheme.TEXTAREA_BORDERLESS);
textArea.addStyleName(ValoTheme.TEXTAREA_TINY);
textArea.addStyleName("inline-icon");
textArea.setHeight(120, Unit.PIXELS);
textArea.setWidth(100, Unit.PERCENTAGE);
textArea.setValue(message);
textArea.setReadOnly(Boolean.TRUE);
return textArea;
}
}
/**
* CellStyleGenerator that concerns about cutting text.
*/
protected static class TextCutCellStyleGenerator implements CellStyleGenerator {
private static final long serialVersionUID = 1L;
@Override
public String getStyle(final CellReference cellReference) {
if (ProxyMessage.PXY_MSG_VALUE.equals(cellReference.getPropertyId())) {
return "text-cut";
}
return null;
}
}
}

View File

@@ -0,0 +1,48 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ui.management.actionhistory;
import org.eclipse.hawkbit.ui.common.grid.AbstractGridComponentLayout;
import org.eclipse.hawkbit.ui.common.grid.DefaultGridHeader;
import org.eclipse.hawkbit.ui.management.state.ManagementUIState;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
import org.vaadin.spring.events.EventBus.UIEventBus;
/**
* Layout responsible for messages-grid and the corresponding header.
*/
public class ActionStatusMsgLayout extends AbstractGridComponentLayout {
private static final long serialVersionUID = 1L;
protected final ManagementUIState managementUIState;
/**
* Constructor.
*
* @param i18n
* @param eventBus
* @param managementUIState
*/
public ActionStatusMsgLayout(final VaadinMessageSource i18n, final UIEventBus eventBus,
final ManagementUIState managementUIState) {
super(i18n, eventBus);
this.managementUIState = managementUIState;
init();
}
@Override
public DefaultGridHeader createGridHeader() {
return new DefaultGridHeader(managementUIState, "Messages").init();
}
@Override
public ActionStatusMsgGrid createGrid() {
return new ActionStatusMsgGrid(i18n, eventBus);
}
}

View File

@@ -0,0 +1,35 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ui.management.actionhistory;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.ui.customrenderers.renderers.AbstractHtmlLabelConverter;
/**
* Concrete html-label converter that handles Action.Status.
*/
public class HtmlStatusLabelConverter extends AbstractHtmlLabelConverter<Action.Status> {
private static final long serialVersionUID = 1L;
/**
* Constructor that sets the appropriate adapter.
*
* @param adapter
* adapts <code>Action.Status</code> to <code>String</code>
*/
public HtmlStatusLabelConverter(LabelAdapter<Action.Status> adapter) {
this.addAdapter(adapter);
}
@Override
public Class<Action.Status> getModelType() {
return Action.Status.class;
}
}

View File

@@ -0,0 +1,223 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ui.management.actionhistory;
import java.io.Serializable;
import org.eclipse.hawkbit.repository.model.Action;
/**
* Proxy for {@link Action}
*/
public class ProxyAction implements Serializable {
private static final long serialVersionUID = 1L;
public static final String PXY_ACTION_STATUS = "status";
public static final String PXY_ACTION_IS_ACTIVE = "isActive";
public static final String PXY_ACTION_IS_ACTIVE_DECO = "isActiveDecoration";
public static final String PXY_ACTION_ID = "id";
public static final String PXY_ACTION_DS_NAME_VERSION = "dsNameVersion";
public static final String PXY_ACTION = "action";
public static final String PXY_ACTION_LAST_MODIFIED_AT = "lastModifiedAt";
public static final String PXY_ACTION_ROLLOUT_NAME = "rolloutName";
private Action.Status status;
private boolean isActive;
private IsActiveDecoration isActiveDecoration;
private Long id;
private String dsNameVersion;
private Action action;
private Long lastModifiedAt;
private String rolloutName;
/**
* Get id for the entry.
*
* @return id for the entry.
*/
public Long getId() {
return id;
}
/**
* Set the id for the entry.
*
* @param id
* of the action entry.
*/
public void setId(Long id) {
this.id = id;
}
/**
* Gets the status literal.
*
* @return status literal
*/
public Action.Status getStatus() {
return status;
}
/**
* Sets the status literal.
*
* @param status
* literal
*/
public void setStatus(Action.Status status) {
this.status = status;
}
/**
* Flag that indicates if the action is active.
*
* @return <code>true</code> if the action is active, otherwise
* <code>false</code>
*/
public boolean isActive() {
return isActive;
}
/**
* Sets the flag that indicates if the action is active.
*
* @param isActive
* <code>true</code> if the action is active, otherwise
* <code>false</code>
*/
public void setActive(boolean isActive) {
this.isActive = isActive;
}
/**
* Gets a pre-calculated literal combining <code>ProxyAction#isActive</code>
* and <code>ProxyAction#getStatus</code> states.
*
* @return pre-calculated literal
*/
public IsActiveDecoration getIsActiveDecoration() {
return isActiveDecoration;
}
/**
* Sets the pre-calculated literal combining
* <code>ProxyAction#isActive</code> and <code>ProxyAction#getStatus</code>
* states.
*
* @param isActiveDecoration
* pre-calculated literal
*/
public void setIsActiveDecoration(IsActiveDecoration isActiveDecoration) {
this.isActiveDecoration = isActiveDecoration;
}
/**
* Pre-calculated value that is set up by distribution set name and version.
*
* @return pre-calculated value combining name and version
*/
public String getDsNameVersion() {
return dsNameVersion;
}
/**
* Sets the pre-calculated value combining distribution set name and
* version.
*
* @param dsNameVersion
* combined value
*/
public void setDsNameVersion(String dsNameVersion) {
this.dsNameVersion = dsNameVersion;
}
/**
* Gets the action to be evaluated by generators of virtual properties.
*
* @return action
*/
public Action getAction() {
return action;
}
/**
* Sets the action to be evaluated by generators of virtual properties.
*
* @param action
*/
public void setAction(Action action) {
this.action = action;
}
/**
* Get raw long-value for lastModifiedAt-date.
*
* @return raw long-value for lastModifiedAt-date
*/
public Long getLastModifiedAt() {
return lastModifiedAt;
}
/**
* Set raw long-value for lastModifiedAt-date.
*
* @param lastModifiedAt
* raw long-value for lastModifiedAt-date
*/
public void setLastModifiedAt(Long lastModifiedAt) {
this.lastModifiedAt = lastModifiedAt;
}
/**
* Gets the rolloutName.
*
* @return rolloutName
*/
public String getRolloutName() {
return rolloutName;
}
/**
* Sets the rolloutName.
*
* @param rolloutName
*/
public void setRolloutName(String rolloutName) {
this.rolloutName = rolloutName;
}
/**
* Pre-calculated decoration value combining
* <code>ProxyAction#isActive</code> and <code>ProxyAction#getStatus</code>
* states.
*/
public enum IsActiveDecoration {
/**
* Active label decoration type for {@code ProxyAction#isActive()==true}
*/
ACTIVE,
/**
* Active label decoration type for
* {@code ProxyAction#isActive()==false}
*/
IN_ACTIVE,
/**
* Active label decoration type for {@code ProxyAction#isActive()==true}
* AND {@code ProxyAction#getStatus()==Action.Status.ERROR}
*/
IN_ACTIVE_ERROR,
/**
* {@code ProxyAction#getStatus()==Action.Status.SCHEDULED}
*/
SCHEDULED,
}
}

View File

@@ -0,0 +1,85 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ui.management.actionhistory;
import java.io.Serializable;
import org.eclipse.hawkbit.repository.model.Action;
/**
* Proxy for {@link ActionStatus}
*/
public class ProxyActionStatus implements Serializable {
private static final long serialVersionUID = 1L;
public static final String PXY_AS_STATUS = "status";
public static final String PXY_AS_CREATED_AT = "createdAt";
public static final String PXY_AS_ID = "id";
private Long id;
private Action.Status status;
private Long createdAt;
/**
* Get id for the entry.
*
* @return id for the entry.
*/
public Long getId() {
return id;
}
/**
* Set the id for the entry.
*
* @param id
* of the status entry.
*/
public void setId(Long id) {
this.id = id;
}
/**
* Gets the status literal.
*
* @return status literal
*/
public Action.Status getStatus() {
return status;
}
/**
* Sets the status literal.
*
* @param status
* literal
*/
public void setStatus(Action.Status status) {
this.status = status;
}
/**
* Get raw long-value for createdAt-date.
*
* @return raw long-value for createdAt-date
*/
public Long getCreatedAt() {
return createdAt;
}
/**
* Set raw long-value for createdAt-date.
*
* @param createdAt
* raw long-value for createdAt-date
*/
public void setCreatedAt(Long createdAt) {
this.createdAt = createdAt;
}
}

View File

@@ -0,0 +1,64 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ui.management.actionhistory;
import java.io.Serializable;
import org.eclipse.hawkbit.repository.model.ActionStatus;
/**
* Proxy for an entry of {@link ActionStatus#getMessages()}
*/
public class ProxyMessage implements Serializable {
private static final long serialVersionUID = 1L;
public static final String PXY_MSG_ID = "id";
public static final String PXY_MSG_VALUE = "message";
private String id;
private String message;
/**
* Get id for the entry.
*
* @return id for the entry.
*/
public String getId() {
return id;
}
/**
* Set the id for the entry.
*
* @param id
* of the message entry.
*/
public void setId(String id) {
this.id = id;
}
/**
* Get message value.
*
* @return message value
*/
public String getMessage() {
return message;
}
/**
* Set message value.
*
* @param message
* value
*/
public void setMessage(String message) {
this.message = message;
}
}

View File

@@ -25,15 +25,14 @@ import org.eclipse.hawkbit.ui.UiProperties;
import org.eclipse.hawkbit.ui.common.UserDetailsFormatter;
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
import org.eclipse.hawkbit.ui.menu.DashboardEvent.PostViewChangeEvent;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
import org.springframework.beans.factory.annotation.Autowired;
import com.vaadin.server.FontAwesome;
import com.vaadin.server.Page;
import com.vaadin.server.Resource;
import com.vaadin.server.ThemeResource;
import com.vaadin.shared.ui.label.ContentMode;
import com.vaadin.spring.annotation.SpringComponent;
import com.vaadin.spring.annotation.UIScope;
import com.vaadin.ui.Alignment;
@@ -43,7 +42,6 @@ import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.Component;
import com.vaadin.ui.CssLayout;
import com.vaadin.ui.CustomComponent;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.Link;
import com.vaadin.ui.MenuBar;
@@ -82,8 +80,9 @@ public final class DashboardMenu extends CustomComponent {
private boolean accessibleViewsEmpty;
@Autowired
DashboardMenu(final VaadinMessageSource i18n, final UiProperties uiProperties, final HawkbitServerProperties serverProperties,
final PermissionService permissionService, final List<DashboardMenuItem> dashboardVaadinViews) {
DashboardMenu(final VaadinMessageSource i18n, final UiProperties uiProperties,
final HawkbitServerProperties serverProperties, final PermissionService permissionService,
final List<DashboardMenuItem> dashboardVaadinViews) {
this.i18n = i18n;
this.uiProperties = uiProperties;
this.serverProperties = serverProperties;
@@ -109,7 +108,6 @@ public final class DashboardMenu extends CustomComponent {
final VerticalLayout dashboardMenuLayout = new VerticalLayout();
dashboardMenuLayout.setSizeFull();
final VerticalLayout menuContent = getMenuLayout();
menuContent.addComponent(buildTitle());
menuContent.addComponent(buildUserMenu());
menuContent.addComponent(buildToggleButton());
@@ -137,15 +135,6 @@ public final class DashboardMenu extends CustomComponent {
return menuContent;
}
private Component buildTitle() {
final Label logo = new Label("<strong>" + i18n.getMessage("menu.title") + "</strong>", ContentMode.HTML);
logo.setSizeUndefined();
final HorizontalLayout logoWrapper = new HorizontalLayout(logo);
logoWrapper.setComponentAlignment(logo, Alignment.TOP_CENTER);
logoWrapper.addStyleName("valo-menu-title");
return logoWrapper;
}
private VerticalLayout buildLinksAndVersion() {
final VerticalLayout links = new VerticalLayout();
links.setSpacing(true);
@@ -174,8 +163,8 @@ public final class DashboardMenu extends CustomComponent {
if (!uiProperties.getLinks().getSupport().isEmpty()) {
final Link supportLink = SPUIComponentProvider.getLink(UIComponentIdProvider.LINK_SUPPORT,
i18n.getMessage("link.support.name"), uiProperties.getLinks().getSupport(), FontAwesome.ENVELOPE_O, "",
linkStyle);
i18n.getMessage("link.support.name"), uiProperties.getLinks().getSupport(), FontAwesome.ENVELOPE_O,
"", linkStyle);
supportLink.setDescription(i18n.getMessage("link.support.name"));
supportLink.setSizeFull();
links.addComponent(supportLink);

View File

@@ -8,35 +8,140 @@
*/
package org.eclipse.hawkbit.ui.rollout;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import java.io.Serializable;
import com.google.gwt.i18n.client.AutoDirectionHandler.Target;
import com.vaadin.server.FontAwesome;
/**
*
* Helper class which holds the details of font icon to be displayed for
* {@link Rollout}/ {@link RolloutGroup} / Rollout {@link Target}
*
*
* Helper class which holds the details of font icon to be displayed as
* label/button in grid:
* <p>
* <code>RolloutListGrid</code> / <code>RolloutGroupListGrid</code> /
* <code>RolloutGroupTargetsListGrid</code> / <code>ActionHistoryGrid</code>
*/
public class StatusFontIcon {
final FontAwesome fontIcon;
final String style;
public class StatusFontIcon implements Serializable {
/** serialVersionUID. */
private static final long serialVersionUID = 1L;
private FontAwesome fontIcon;
private String style;
private String title;
private String id;
private boolean disabled;
public StatusFontIcon(final FontAwesome fontIcon, final String style) {
super();
this.fontIcon = fontIcon;
this.style = style;
/**
* NOTE: This constructor is used for (de-)serialization only!!!
*/
public StatusFontIcon() {
// empty
}
/**
* Constructor to create icon metadata object.
*
* @param fontIcon
* the font representing the icon
* @param style
* the style
*/
public StatusFontIcon(final FontAwesome fontIcon, final String style) {
this(fontIcon, style, "", "", false);
}
/**
* Constructor to create icon metadata object.
*
* @param fontIcon
* the font representing the icon
* @param style
* the style
* @param title
* the title shown as tooltip
*/
public StatusFontIcon(final FontAwesome fontIcon, final String style, final String title) {
this(fontIcon, style, title, "", false);
}
/**
* Constructor to create icon metadata object.
*
* @param fontIcon
* the font representing the icon
* @param style
* the style
* @param title
* the title shown as tooltip
* @param id
* the id for direct access
*/
public StatusFontIcon(final FontAwesome fontIcon, final String style, final String title, final String id) {
this(fontIcon, style, title, id, false);
}
/**
* Constructor to create icon metadata object.
*
* @param fontIcon
* the font representing the icon
* @param style
* the style
* @param title
* the title shown as tooltip
* @param id
* the id for direct access
* @param disabled
* disabled-state of the icon
*/
public StatusFontIcon(final FontAwesome fontIcon, final String style, final String title, final String id,
final boolean disabled) {
this.fontIcon = fontIcon;
this.style = style;
this.title = title;
this.id = id;
this.disabled = disabled;
}
/**
* Gets the font representing the icon.
*
* @return the font representing the icon
*/
public FontAwesome getFontIcon() {
return fontIcon;
}
/**
* Gets the style.
*
* @return the style
*/
public String getStyle() {
return style;
}
/**
* Gets the title shown as tooltip.
*
* @return the title shown as tooltip.
*/
public String getTitle() {
return title;
}
/**
* Gets the id for direct access.
*
* @return the id for direct access.
*/
public String getId() {
return id;
}
/**
* Gets the disabled-state of the icon.
*
* @return the disabled-state of the icon.
*/
public boolean isDisabled() {
return disabled;
}
}

View File

@@ -70,7 +70,7 @@ import com.vaadin.ui.renderers.HtmlRenderer;
/**
* Rollout list grid component.
*/
public class RolloutListGrid extends AbstractGrid {
public class RolloutListGrid extends AbstractGrid<LazyQueryContainer> {
private static final long serialVersionUID = 1L;
@@ -132,6 +132,8 @@ public class RolloutListGrid extends AbstractGrid {
uiNotification, uiProperties, entityFactory, i18n, eventBus, targetFilterQueryManagement);
this.uiNotification = uiNotification;
this.rolloutUIState = rolloutUIState;
init();
}
/**
@@ -211,7 +213,7 @@ public class RolloutListGrid extends AbstractGrid {
}
@Override
protected Container createContainer() {
protected LazyQueryContainer createContainer() {
final BeanQueryFactory<RolloutBeanQuery> rolloutQf = new BeanQueryFactory<>(RolloutBeanQuery.class);
return new LazyQueryContainer(
new LazyQueryDefinition(true, SPUIDefinitions.PAGE_SIZE, SPUILabelDefinitions.VAR_ID), rolloutQf);

View File

@@ -14,43 +14,61 @@ import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.ui.SpPermissionChecker;
import org.eclipse.hawkbit.ui.UiProperties;
import org.eclipse.hawkbit.ui.common.grid.AbstractGridLayout;
import org.eclipse.hawkbit.ui.common.grid.AbstractGrid;
import org.eclipse.hawkbit.ui.common.grid.AbstractGridComponentLayout;
import org.eclipse.hawkbit.ui.rollout.state.RolloutUIState;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
import org.eclipse.hawkbit.ui.utils.UINotification;
import org.vaadin.addons.lazyquerycontainer.LazyQueryContainer;
import org.vaadin.spring.events.EventBus.UIEventBus;
import com.vaadin.ui.Label;
import com.vaadin.ui.AbstractOrderedLayout;
/**
* Rollout list view.
*/
public class RolloutListView extends AbstractGridLayout {
public class RolloutListView extends AbstractGridComponentLayout {
private static final long serialVersionUID = -2703552177439393208L;
private final transient RolloutManagement rolloutManagement;
private final transient TargetManagement targetManagement;
private final transient EntityFactory entityFactory;
private final transient TargetFilterQueryManagement targetFilterQueryManagement;
private final SpPermissionChecker permissionChecker;
private final RolloutUIState rolloutUIState;
private final UINotification uiNotification;
private final UiProperties uiProperties;
public RolloutListView(final SpPermissionChecker permissionChecker, final RolloutUIState rolloutUIState,
final UIEventBus eventBus, final RolloutManagement rolloutManagement,
final TargetManagement targetManagement, final UINotification uiNotification,
final UiProperties uiProperties, final EntityFactory entityFactory, final VaadinMessageSource i18n,
final TargetFilterQueryManagement targetFilterQueryManagement) {
super(new RolloutListHeader(permissionChecker, rolloutUIState, eventBus, rolloutManagement, targetManagement,
uiNotification, uiProperties, entityFactory, i18n, targetFilterQueryManagement),
new RolloutListGrid(i18n, eventBus, rolloutManagement, uiNotification, rolloutUIState,
permissionChecker, targetManagement, entityFactory, uiProperties, targetFilterQueryManagement));
super(i18n, eventBus);
this.permissionChecker = permissionChecker;
this.rolloutUIState = rolloutUIState;
this.rolloutManagement = rolloutManagement;
this.targetManagement = targetManagement;
this.uiNotification = uiNotification;
this.uiProperties = uiProperties;
this.entityFactory = entityFactory;
this.targetFilterQueryManagement = targetFilterQueryManagement;
buildLayout();
init();
}
@Override
protected boolean hasCountMessage() {
return false;
public AbstractOrderedLayout createGridHeader() {
return new RolloutListHeader(permissionChecker, rolloutUIState, eventBus, rolloutManagement, targetManagement,
uiNotification, uiProperties, entityFactory, i18n, targetFilterQueryManagement);
}
@Override
protected Label getCountMessageLabel() {
return null;
public AbstractGrid<LazyQueryContainer> createGrid() {
return new RolloutListGrid(i18n, eventBus, rolloutManagement, uiNotification, rolloutUIState, permissionChecker,
targetManagement, entityFactory, uiProperties, targetFilterQueryManagement);
}
}

View File

@@ -53,7 +53,7 @@ import com.vaadin.ui.renderers.HtmlRenderer;
/**
* Rollout group list grid component.
*/
public class RolloutGroupListGrid extends AbstractGrid {
public class RolloutGroupListGrid extends AbstractGrid<LazyQueryContainer> {
private static final long serialVersionUID = 4060904914954370524L;
private static final String ROLLOUT_RENDERER_DATA = "rolloutRendererData";
@@ -98,6 +98,8 @@ public class RolloutGroupListGrid extends AbstractGrid {
super(i18n, eventBus, permissionChecker);
this.rolloutGroupManagement = rolloutGroupManagement;
this.rolloutUIState = rolloutUIState;
init();
}
@EventBusListenerMethod(scope = EventScope.UI)
@@ -131,7 +133,7 @@ public class RolloutGroupListGrid extends AbstractGrid {
}
@Override
protected Container createContainer() {
protected LazyQueryContainer createContainer() {
final BeanQueryFactory<RolloutGroupBeanQuery> rolloutQf = new BeanQueryFactory<>(RolloutGroupBeanQuery.class);
return new LazyQueryContainer(
new LazyQueryDefinition(true, SPUIDefinitions.PAGE_SIZE, SPUILabelDefinitions.VAR_ID), rolloutQf);

View File

@@ -10,9 +10,11 @@ package org.eclipse.hawkbit.ui.rollout.rolloutgroup;
import org.eclipse.hawkbit.repository.RolloutGroupManagement;
import org.eclipse.hawkbit.ui.SpPermissionChecker;
import org.eclipse.hawkbit.ui.common.grid.AbstractGridLayout;
import org.eclipse.hawkbit.ui.common.grid.AbstractGrid;
import org.eclipse.hawkbit.ui.common.grid.AbstractGridComponentLayout;
import org.eclipse.hawkbit.ui.rollout.state.RolloutUIState;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
import org.vaadin.addons.lazyquerycontainer.LazyQueryContainer;
import org.vaadin.spring.events.EventBus.UIEventBus;
import com.vaadin.ui.Label;
@@ -20,10 +22,14 @@ import com.vaadin.ui.Label;
/**
* Groups List View.
*/
public class RolloutGroupsListView extends AbstractGridLayout {
public class RolloutGroupsListView extends AbstractGridComponentLayout {
private static final long serialVersionUID = 7252345838154270259L;
private final SpPermissionChecker permissionChecker;
private final RolloutUIState rolloutUIState;
private final transient RolloutGroupManagement rolloutGroupManagement;
/**
* Constructor for RolloutGroupsListView
*
@@ -41,20 +47,21 @@ public class RolloutGroupsListView extends AbstractGridLayout {
public RolloutGroupsListView(final VaadinMessageSource i18n, final UIEventBus eventBus,
final RolloutGroupManagement rolloutGroupManagement, final RolloutUIState rolloutUIState,
final SpPermissionChecker permissionChecker) {
super(new RolloutGroupsListHeader(eventBus, rolloutUIState, i18n),
new RolloutGroupListGrid(i18n, eventBus, rolloutGroupManagement, rolloutUIState, permissionChecker));
buildLayout();
super(i18n, eventBus);
this.permissionChecker = permissionChecker;
this.rolloutUIState = rolloutUIState;
this.rolloutGroupManagement = rolloutGroupManagement;
init();
}
@Override
protected boolean hasCountMessage() {
return false;
public RolloutGroupsListHeader createGridHeader() {
return new RolloutGroupsListHeader(eventBus, rolloutUIState, i18n);
}
@Override
protected Label getCountMessageLabel() {
return null;
public AbstractGrid<LazyQueryContainer> createGrid() {
return new RolloutGroupListGrid(i18n, eventBus, rolloutGroupManagement, rolloutUIState, permissionChecker);
}
}

View File

@@ -42,7 +42,7 @@ import com.vaadin.server.FontAwesome;
/**
* Grid component with targets of rollout group.
*/
public class RolloutGroupTargetsListGrid extends AbstractGrid {
public class RolloutGroupTargetsListGrid extends AbstractGrid<LazyQueryContainer> {
private static final long serialVersionUID = -2244756637458984597L;
@@ -85,6 +85,8 @@ public class RolloutGroupTargetsListGrid extends AbstractGrid {
final RolloutUIState rolloutUIState) {
super(i18n, eventBus, null);
this.rolloutUIState = rolloutUIState;
init();
}
@EventBusListenerMethod(scope = EventScope.UI)
@@ -97,7 +99,7 @@ public class RolloutGroupTargetsListGrid extends AbstractGrid {
}
@Override
protected Container createContainer() {
protected LazyQueryContainer createContainer() {
final BeanQueryFactory<RolloutGroupTargetsBeanQuery> rolloutgrouBeanQueryFactory = new BeanQueryFactory<>(
RolloutGroupTargetsBeanQuery.class);
return new LazyQueryContainer(
@@ -205,7 +207,7 @@ public class RolloutGroupTargetsListGrid extends AbstractGrid {
}
/**
*
*
* Converts {@link Status} into string with status icon details.
*
*/

View File

@@ -8,43 +8,50 @@
*/
package org.eclipse.hawkbit.ui.rollout.rolloutgrouptargets;
import org.eclipse.hawkbit.ui.common.grid.AbstractGridLayout;
import org.eclipse.hawkbit.ui.common.grid.AbstractGridComponentLayout;
import org.eclipse.hawkbit.ui.rollout.state.RolloutUIState;
import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider;
import org.eclipse.hawkbit.ui.utils.VaadinMessageSource;
import org.vaadin.spring.events.EventBus.UIEventBus;
import com.vaadin.ui.AbstractOrderedLayout;
import com.vaadin.ui.Label;
/**
* Rollout Group Targets List View.
*/
public class RolloutGroupTargetsListView extends AbstractGridLayout {
public class RolloutGroupTargetsListView extends AbstractGridComponentLayout {
private static final long serialVersionUID = 26089134783467012L;
private final RolloutGroupTargetsCountLabelMessage rolloutGroupTargetsCountLabelMessage;
private final RolloutUIState rolloutUIState;
public RolloutGroupTargetsListView(final UIEventBus eventBus, final VaadinMessageSource i18n,
final RolloutUIState rolloutUIState) {
super(new RolloutGroupTargetsListHeader(eventBus, i18n, rolloutUIState),
new RolloutGroupTargetsListGrid(i18n, eventBus, rolloutUIState));
this.rolloutGroupTargetsCountLabelMessage = new RolloutGroupTargetsCountLabelMessage(rolloutUIState,
(RolloutGroupTargetsListGrid) grid, i18n, eventBus);
buildLayout();
super(i18n, eventBus);
this.rolloutUIState = rolloutUIState;
this.setFooterSupport(new RolloutTargetsCountFooterSupport());
init();
}
@Override
protected boolean hasCountMessage() {
return true;
public AbstractOrderedLayout createGridHeader() {
return new RolloutGroupTargetsListHeader(eventBus, i18n, rolloutUIState);
}
@Override
protected Label getCountMessageLabel() {
return rolloutGroupTargetsCountLabelMessage;
public RolloutGroupTargetsListGrid createGrid() {
return new RolloutGroupTargetsListGrid(i18n, eventBus, rolloutUIState);
}
class RolloutTargetsCountFooterSupport extends AbstractFooterSupport {
private static final long serialVersionUID = 3300400541786329735L;
@Override
protected Label getFooterMessageLabel() {
final RolloutGroupTargetsCountLabelMessage countMessageLabel = new RolloutGroupTargetsCountLabelMessage(
rolloutUIState, (RolloutGroupTargetsListGrid) getGrid(), i18n, eventBus);
countMessageLabel.setId(UIComponentIdProvider.ROLLOUT_GROUP_TARGET_LABEL);
return countMessageLabel;
}
}
}

View File

@@ -81,6 +81,18 @@ public final class SPDateTimeUtil {
return formatDate(lastQueryDate, null);
}
/**
* Get formatted date with browser time zone.
*
* @param lastQueryDate
* @param datePattern
* pattern how to format the date (cp. {@code SimpleDateFormat})
* @return String formatted date
*/
public static String getFormattedDate(final Long lastQueryDate, final String datePattern) {
return formatDate(lastQueryDate, null, datePattern);
}
/**
* Get formatted date 'created at' by entity.
*
@@ -109,20 +121,40 @@ public final class SPDateTimeUtil {
return formatDate(baseEntity.getLastModifiedAt(), StringUtils.EMPTY);
}
private static String formatDate(final Long lastQueryDate, final String defaultString) {
/**
* Get formatted date 'last modified at' by entity.
*
* @param baseEntity
* the entity
* @param datePattern
* pattern how to format the date (cp. {@code SimpleDateFormat})
* @return String formatted date
*/
public static String formatLastModifiedAt(final BaseEntity baseEntity, final String datePattern) {
if (baseEntity == null) {
return StringUtils.EMPTY;
}
return formatDate(baseEntity.getLastModifiedAt(), StringUtils.EMPTY, datePattern);
}
private static String formatDate(final Long lastQueryDate, final String defaultString, final String datePattern) {
if (lastQueryDate != null) {
final SimpleDateFormat format = new SimpleDateFormat(SPUIDefinitions.LAST_QUERY_DATE_FORMAT);
final SimpleDateFormat format = new SimpleDateFormat(datePattern);
format.setTimeZone(getBrowserTimeZone());
return format.format(new Date(lastQueryDate));
}
return defaultString;
}
private static String formatDate(final Long lastQueryDate, final String defaultString) {
return formatDate(lastQueryDate, defaultString, SPUIDefinitions.LAST_QUERY_DATE_FORMAT);
}
/**
* Creates a formatted string of a duration in format '1 year 2 months 3
* days 4 hours 5 minutes 6 seconds' zero values will be ignored in the
* formatted string.
*
*
* @param startMillis
* the start milliseconds of the duration
* @param endMillis
@@ -158,34 +190,34 @@ public final class SPDateTimeUtil {
/**
* Enum to get the i18n key for single or plural calendar labels.
*
*
*
*
*
*/
private enum CalendarI18N {
/**
*
*
*/
YEAR("calendar.year", "calendar.years"),
/**
*
*
*/
MONTH("calendar.month", "calendar.months"),
/**
*
*
*/
DAY("calendar.days", "calendar.days"),
/**
*
*
*/
HOUR("calendar.hour", "calendar.hours"),
/**
*
*
*/
MINUTE("calendar.minute", "calendar.minutes"),
/**
*
*
*/
SECOND("calendar.second", "calendar.seconds");
@@ -193,7 +225,7 @@ public final class SPDateTimeUtil {
private final String plural;
/**
*
*
*/
CalendarI18N(final String single, final String plural) {
this.single = single;

View File

@@ -53,7 +53,7 @@ public final class SPUIDefinitions {
/**
* Action history active hidden column. This is using to generate active
* icons under active coloumn.
* icons under active column.
*/
public static final String ACTION_HIS_TBL_ACTIVE_HIDDEN = "Active_Hidden";
@@ -108,6 +108,11 @@ public final class SPUIDefinitions {
*/
public static final String ACTION_HIS_TBL_FORCED = "Forced";
/**
* Action history action type column.
*/
public static final String ACTION_HIS_TBL_TIMEFORCED = "Time-Forced";
/**
* Action history helping constant.
*/
@@ -244,6 +249,22 @@ public final class SPUIDefinitions {
* Text field style.
*/
public static final String TEXT_STYLE = "text-style";
/**
* Show actions for a target.
*/
public static final String ACTIONS_BY_TARGET = "ActionsByTarget";
/**
* Show action-states for a action.
*/
public static final String ACTIONSTATES_BY_ACTION = "ActionStatesByAction";
/**
* Show messages for a action-status.
*/
public static final String MESSAGES_BY_ACTIONSTATUS = "MessagesByActionStatus";
/**
* Key for no-message MessageProxy.
*/
public static final String NO_MSG_PROXY = "NoMessageProxy";
/**
* Style to highlight row in orange color.
@@ -353,6 +374,10 @@ public final class SPUIDefinitions {
* Target last query date format .
*/
public static final String LAST_QUERY_DATE_FORMAT = "EEE MMM d HH:mm:ss z yyyy";
/**
* Target last query date format .
*/
public static final String LAST_QUERY_DATE_FORMAT_SHORT = "MMM d HH:mm z ''yy";
/**
* Item Id used in drop comparisons.
*/

View File

@@ -25,6 +25,11 @@ public final class SPUIStyleDefinitions {
public static final String SP_TEXTFIELD_LAYOUT_ERROR_HIGHTLIGHT = "v-textfield-error";
/**
* Action history message grid style.
*/
public static final String ACTION_HISTORY_MESSAGE_GRID = "action-history-message-grid";
/**
* Style for accordion tab button.
*/

View File

@@ -6,6 +6,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ui.utils;
/**
@@ -52,17 +53,14 @@ public final class UIComponentIdProvider {
* ID-Targ.PIN.
*/
public static final String TARGET_PIN_ICON = "target.pin.icon.";
/**
* Target search text id.
*/
public static final String TARGET_TEXT_FIELD = "target.search.textfield";
/**
* ID for target filter search
*/
public static final String TARGET_FILTER_SEARCH_TEXT = "target.filter.search.text.Id";
/**
* ID for add target filter icon
*/
@@ -121,7 +119,6 @@ public final class UIComponentIdProvider {
* ID-Dist.PIN.
*/
public static final String DIST_PIN_BUTTON = "dist.pin.button";
/**
* ID for distribution set tag icon
*/
@@ -134,27 +131,22 @@ public final class UIComponentIdProvider {
* ID - soft.module.version.
*/
public static final String SOFT_MODULE_VERSION = "soft.module.version";
/**
* ID - soft.module.vendor.
*/
public static final String SOFT_MODULE_VENDOR = "soft.module.vendor";
/**
* ID - Save Assign.
*/
public static final String SAVE_ASSIGNMENT = "save.actions.popup.assign";
/**
* ID - Discard Assign.
*/
public static final String DISCARD_ASSIGNMENT = "discard.actions.popup.assign";
/**
* ID - Delete Distribution SetType Save.
*/
public static final String SAVE_DELETE_DIST_SET_TYPE = "save.actions.popup.delete.dist.set.type";
/**
* ID - Discard Distribution SetType.
*/
@@ -163,17 +155,14 @@ public final class UIComponentIdProvider {
* ID Delete Software Module Type save.
*/
public static final String SAVE_DELETE_SW_MODULE_TYPE = "save.actions.popup.delete.sw.module.type";
/**
* ID - Discard SW Module Type.
*/
public static final String DISCARD_SW_MODULE_TYPE = "save.actions.popup.discard.sw.module.type";
/**
* Action history table cancel Id.
*/
public static final String ACTION_DETAILS_SOFT_ID = "action.details.soft.group";
/**
* Start type of rollout manual radio button
*/
@@ -231,24 +220,53 @@ public final class UIComponentIdProvider {
*/
public static final String PENDING_ACTION_BUTTON = "pending.action.button";
/**
* Action history table Id.
* Action history grid Id.
*/
public static final String ACTION_HISTORY_TABLE_ID = "action.history.tableId";
public static final String ACTION_HISTORY_GRID_ID = "action.history.gridId";
/**
* Action history table cancel Id.
* Action history details grid Id.
*/
public static final String ACTION_HISTORY_DETAILS_GRID_ID = "action.history.details.gridId";
/**
* Action history message grid Id.
*/
public static final String ACTION_HISTORY_MESSAGE_GRID_ID = "action.history.message.gridId";
/**
* Action history table cancel button Id.
*/
public static final String ACTION_HISTORY_TABLE_CANCEL_ID = "action.history.table.action.cancel";
/**
* Action history table force Id.
* Action history table force button Id.
*/
public static final String ACTION_HISTORY_TABLE_FORCE_ID = "action.history.table.action.force";
/**
* Action history table force quit Id.
* Action history table force quit button Id.
*/
public static final String ACTION_HISTORY_TABLE_FORCE_QUIT_ID = "action.history.table.action.force.quit";
/**
* Action history table forced label Id.
*/
public static final String ACTION_HISTORY_TABLE_FORCED_LABEL_ID = "action.history.table.forcedId";
/**
* Action history table time-forced label Id.
*/
public static final String ACTION_HISTORY_TABLE_TIMEFORCED_LABEL_ID = "action.history.table.timedforceId";
/**
* Action history table status label Id.
*/
public static final String ACTION_HISTORY_TABLE_STATUS_LABEL_ID = "action.history.table.statusId";
/**
* Action history table active-state label Id.
*/
public static final String ACTION_HISTORY_TABLE_ACTIVESTATE_LABEL_ID = "action.history.table.activeStateId";
/**
* Action status grid status label Id.
*/
public static final String ACTION_STATUS_GRID_STATUS_LABEL_ID = "action.status.grid.statusId";
/**
* ID for option group save timeforced
@@ -985,7 +1003,7 @@ public final class UIComponentIdProvider {
/**
* Id of the unread notification popup
*/
public static final String NOTIFICATION_UNREAD_POPUP_id = "notification.unread.popup";
public static final String NOTIFICATION_UNREAD_POPUP_ID = "notification.unread.popup";
/**
* Id of the unread notification icon in the menu

View File

@@ -18,9 +18,21 @@
padding-bottom: 25px;
padding-top: 5px;
}
//Color of forced label
.action-history-table-col-forced-label {
color: $hawkbit-primary-color;
.action-history-message-grid {
.v-grid-cell {
border: none !important;
cursor: move;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
-icab-text-overflow: ellipsis;
-khtml-text-overflow: ellipsis;
-moz-text-overflow: ellipsis;
-webkit-text-overflow: ellipsis;
font-size: $v-font-size--small !important;
box-shadow: none !important;
}
}
}

View File

@@ -23,8 +23,9 @@
}
.view-header-layout{
width: 100%;
width: 100% !important;
padding-right: 20px;
padding-bottom: 0.3em
}
.v-ui {
@@ -51,10 +52,10 @@
padding-right: 20px;
padding-left: 20px;
min-width: 1000px;
min-height: 400px;
min-height: 500px;
border: 0px;
background-color: transparent;
box-shadow: none;
box-shadow: none
}
//View background styles
@@ -65,7 +66,7 @@
background-position: bottom;
background-repeat: no-repeat;
border-left: 0 solid #fff;
padding-top: 80px;
padding-top: 70px;
overflow: auto;
}
@@ -76,7 +77,7 @@
font-weight: bold;
height: 32px;
line-height: 50px;
padding-bottom: 5px;
padding-bottom: 0px;
padding-left: 15px;
padding-top: 0;
position: absolute;
@@ -103,7 +104,7 @@
background: $logo-image no-repeat right top;
background-position: $logo-position;
content: "";
height: 80px;
height: 70px;
position: absolute;
top: 0;
width: 100%;
@@ -316,5 +317,11 @@
.form-lastrow {
padding-bottom: 12px !important;
}
.text-cut {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
}

View File

@@ -0,0 +1,33 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
@mixin grid {
.v-grid-cell.centeralign {
text-align: center;
}
.v-grid-cell.leftalign {
text-align: left;
}
.v-grid-cell.rightalign {
text-align: right;
}
.v-grid-cell {
font-size: $v-font-size--small !important
}
.v-grid-cell.frozen{
box-shadow: none !important;
}
.v-grid-cell.frozen + th {
border-left: $v-grid-border-size solid $widget-border-color ;
}
}

View File

@@ -92,7 +92,7 @@
}
//Pin style - when hovered on pin and when row is selected
tr.v-selected .v-button-targetStatusPinToggle:hover:after {
widget-bg .v-button-targetStatusPinToggle:hover:after {
background-color: $widget-bg;
color: $hawkbit-primary-color !important;
}
@@ -195,56 +195,97 @@
content: "\f08d";
color: $widget-bg;
}
.statusIconPending {
// pending status label not selected
tr:not(.v-selected) .statusIconForced:after {
color: $hawkbit-primary-color;
}
.statusIconActive {
// pending status label selected
tr.v-selected .statusIconForced:after {
color: $widget-bg;
}
// pending status label not selected
tr:not(.v-selected) .statusIconPending:after {
color: $hawkbit-primary-color;
}
// pending status label selected
tr.v-selected .statusIconPending:after {
color: $widget-bg;
}
// active status label not selected
tr:not(.v-grid-row-selected) .statusIconActive {
@include valo-spinner($size:16px,$speed:1200ms);
color: $hawkbit-primary-color;
}
.statusIconNeutral {
color: $hawkbit-primary-color;
// active status label selected
tr.v-grid-row-selected .statusIconActive {
@include valo-spinner($size:16px,$speed:1200ms,$color: $grey-light);
color: $grey-light;
}
// neutral status label not selected
tr:not(.v-selected) .statusIconNeutral:after {
color: $hawkbit-primary-color;
}
// neutral status label selected
tr.v-selected .statusIconNeutral:after {
color: $widget-bg;
}
// red status label not selected
tr:not(.v-selected) .borderless.statusIconRed:after {
color: $hawkbit-primary-color;
content: '';
}
// red status label not selected
tr.v-selected .borderless.statusIconRed:after {
color: $widget-bg;
content: '';
}
.redSpinner{
@include valo-spinner($size: $v-font-size--small,$color: $red-color);
pointer-events: auto !important;
}
.greySpinner{
@include valo-spinner($size: $v-font-size--small,$color: $grey-color);
pointer-events: auto !important;
}
.greySpinner{
@include valo-spinner($size: $v-font-size--small,$color: $grey-color);
pointer-events: auto !important;
}
.yellowSpinner{
@include valo-spinner($size: $v-font-size--small,$color: $signal-yellow-color);
pointer-events: auto !important;
}
.blueSpinner{
@include valo-spinner($size: $v-font-size--small,$color: $signal-light-blue-color);
pointer-events: auto !important;
}
.yellowSpinner{
@include valo-spinner($size: $v-font-size--small,$color: $signal-yellow-color);
pointer-events: auto !important;
}
.blueSpinner{
@include valo-spinner($size: $v-font-size--small,$color: $signal-light-blue-color);
pointer-events: auto !important;
}
// Disabled row style when distribution is incomplete
.v-table-row-incomplete-distribution {
color: $disabled-row-color-grey !important;
}
.v-table-row-incomplete-distribution {
color: $disabled-row-color-grey !important;
}
.v-link {
text-decoration: none;
padding-right: 10px;
font-weight: 300;
}
.v-link {
text-decoration: none;
padding-right: 10px;
font-weight: 300;
}
.v-tooltip{
max-width:43em;
}
.v-tooltip{
max-width:43em;
}
.breadcrumbPaddingLeft{
.breadcrumbPaddingLeft{
padding-left: 3px !important;
}
}
}

View File

@@ -7,97 +7,76 @@
* http://www.eclipse.org/legal/epl-v10.html
*/
@mixin rollout {
.rollout-option-group {
font-size: 12px;
font-weight: 400;
margin-left: 8px;
}
.rollout-action-type-layout {
.v-caption-padding-right-style {
padding-right: 0px !important;
}
}
.v-context-menu .v-context-menu-item-basic-icon-container {
height: 0px !important;
width: 0px !important;
}
.v-context-menu .v-context-menu-item-basic {
background-color: #feffff !important;
border-radius: 4px;
font-family: $app-font-family;
font-size: $app-text-font-size;
font-weight: normal;
font-style: normal;
}
.v-context-menu {
background-color: #feffff !important;
border-radius: 4px;
}
.v-context-menu .v-context-menu-item-basic:focus, .v-context-menu .v-context-menu-item-basic-submenu:focus, .v-context-menu .v-context-menu-item-basic-open {
@include valo-gradient($color: $hawkbit-primary-color);
background-color: $hawkbit-primary-color !important;
color: #e8eef3;
height: 30px;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.05);
}
.disable-action-type-layout {
opacity: 0.5;
}
.action-type-padding {
padding: 0 0px !important;
}
.rollout-caption-links {
font-weight: 400;
height: 25px;
padding: 0px 4px;
}
.rollout-target-count-message {
color: $info-message-color-grey;
}
.rollout-table {
.v-table-cell-wrapper {
cursor: default;
}
}
.v-grid-cell.centeralign {
text-align: center;
}
.v-grid-cell {
font-size: $v-font-size--small !important;
height: 34px !important;
}
.v-grid-row {
height: 34px !important;
}
.v-grid-cell.frozen {
box-shadow: none !important;
}
.v-grid-cell.frozen + th {
border-left: $v-grid-border-size solid $widget-border-color;
}
.v-button-boldhide {
text-decoration: none;
}
.groups-pie-chart {
.rollout-option-group{
font-size:12px;
font-weight:400;
margin-left:8px;
}
.rollout-action-type-layout {
.v-caption-padding-right-style{
padding-right:0px !important;
}
}
.v-context-menu .v-context-menu-item-basic-icon-container{
height:0px !important;
width:0px !important;
}
.v-context-menu .v-context-menu-item-basic{
background-color: #feffff !important;
border-radius: 4px;
font-family : $app-font-family;
font-size : $app-text-font-size;
font-weight : normal;
font-style : normal;
}
.v-context-menu{
background-color: #feffff !important;
border-radius: 4px;
}
.v-context-menu .v-context-menu-item-basic:focus, .v-context-menu .v-context-menu-item-basic-submenu:focus, .v-context-menu .v-context-menu-item-basic-open {
@include valo-gradient($color: $hawkbit-primary-color);
background-color: $hawkbit-primary-color !important;
color: #e8eef3;
height: 30px;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.05);
}
.disable-action-type-layout{
opacity: 0.5;
}
.action-type-padding{
padding: 0 0px !important;
}
.rollout-caption-links{
font-weight: 400;
height: 25px ;
padding: 0px 4px ;
}
.rollout-target-count-message{
color: $info-message-color-grey;
}
.rollout-table{
.v-table-cell-wrapper {
cursor: default;
}
}
.v-button-boldhide{
text-decoration:none;
}
.groups-pie-chart {
float: right;
svg {
@@ -209,5 +188,6 @@
.v-slot:nth-child(16n+15) .rollout-group-count { border-left-color: #BBDE8F }
.v-slot:nth-child(16n+16) .rollout-group-count { border-left-color: #7FB0A4 }
}
}

View File

@@ -36,7 +36,6 @@
}
.v-table-resizer {
height: 37px !important;
margin-left: -8px !important;
}

View File

@@ -38,10 +38,13 @@ $v-grid-cell-horizontal-border: 0px ;
$v-grid-cell-focused-border: 0px ;
$v-grid-cell-padding-horizontal:6px;
$v-table-row-height: 31px;
$v-animations-enabled: true;
@import '../valo/valo';
@import 'customstyles/table';
@import 'customstyles/grid';
@import 'customstyles/filter-status';
@import 'customstyles/colorpicker';
@import 'customstyles/tags';
@@ -79,6 +82,7 @@ $v-included-components: remove($v-included-components, form);
@include valo;
@include generic-styles;
@include table;
@include grid;
@include filter-status;
@include colorpicker;
@include tags;

View File

@@ -213,7 +213,6 @@ textfield.vendor = Vendor
textfield.description = Description
textfield.customfiltername = Filter name
textfield.value = Value
ui.version = Powered by Bosch IoT Software Provisioning
prompt.target.id = Controller ID