diff --git a/hawkbit-autoconfigure/pom.xml b/hawkbit-autoconfigure/pom.xml index 1cd89aa38..a2c0ede90 100644 --- a/hawkbit-autoconfigure/pom.xml +++ b/hawkbit-autoconfigure/pom.xml @@ -91,5 +91,14 @@ protostuff-runtime true + + org.vaadin.spring.extensions + vaadin-spring-ext-security + + + javax.servlet + javax.servlet-api + provided + diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/mgmt/ui/MgmtUiAutoConfiguration.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/mgmt/ui/MgmtUiAutoConfiguration.java index a14657b32..3c3a210d9 100644 --- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/mgmt/ui/MgmtUiAutoConfiguration.java +++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/mgmt/ui/MgmtUiAutoConfiguration.java @@ -12,10 +12,13 @@ import java.util.concurrent.ScheduledExecutorService; import org.eclipse.hawkbit.DistributedResourceBundleMessageSource; import org.eclipse.hawkbit.ui.MgmtUiConfiguration; +import org.eclipse.hawkbit.ui.SpPermissionChecker; import org.eclipse.hawkbit.ui.UiProperties; import org.eclipse.hawkbit.ui.push.DelayedEventBusPushStrategy; import org.eclipse.hawkbit.ui.push.EventPushStrategy; +import org.eclipse.hawkbit.ui.push.HawkbitEventPermissionChecker; import org.eclipse.hawkbit.ui.push.HawkbitEventProvider; +import org.eclipse.hawkbit.ui.push.UIEventPermissionChecker; import org.eclipse.hawkbit.ui.push.UIEventProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -26,7 +29,7 @@ import org.springframework.context.annotation.Import; import org.vaadin.spring.annotation.EnableVaadinExtensions; import org.vaadin.spring.events.EventBus.UIEventBus; import org.vaadin.spring.events.annotation.EnableEventBus; -import org.vaadin.spring.security.annotation.EnableVaadinSecurity; +import org.vaadin.spring.security.annotation.EnableVaadinManagedSecurity; import com.vaadin.spring.annotation.UIScope; @@ -34,7 +37,7 @@ import com.vaadin.spring.annotation.UIScope; * The Management UI auto configuration. */ @Configuration -@EnableVaadinSecurity +@EnableVaadinManagedSecurity @EnableVaadinExtensions @EnableEventBus @ConditionalOnClass(MgmtUiConfiguration.class) @@ -68,6 +71,18 @@ public class MgmtUiAutoConfiguration { return new HawkbitEventProvider(); } + /** + * A event permission checker bean which verifies supported events for the + * UI. + * + * @return the permission checker bean + */ + @Bean + @ConditionalOnMissingBean + UIEventPermissionChecker eventPermissionChecker(final SpPermissionChecker permChecker) { + return new HawkbitEventPermissionChecker(permChecker); + } + /** * The UI scoped event push strategy. Session scope is necessary, that every * UI has an own strategy. @@ -80,6 +95,8 @@ public class MgmtUiAutoConfiguration { * the ui event bus * @param eventProvider * the event provider + * @param eventPermissionChecker + * the event permission checker * @param uiProperties * the ui properties * @return the push strategy bean @@ -89,10 +106,12 @@ public class MgmtUiAutoConfiguration { @UIScope EventPushStrategy eventPushStrategy(final ConfigurableApplicationContext applicationContext, final ScheduledExecutorService executorService, final UIEventBus eventBus, - final UIEventProvider eventProvider, final UiProperties uiProperties) { + final UIEventProvider eventProvider, final UIEventPermissionChecker eventPermissionChecker, + final UiProperties uiProperties) { final DelayedEventBusPushStrategy delayedEventBusPushStrategy = new DelayedEventBusPushStrategy(executorService, - eventBus, eventProvider, uiProperties.getEvent().getPush().getDelay()); + eventBus, eventProvider, eventPermissionChecker, uiProperties.getEvent().getPush().getDelay()); applicationContext.addApplicationListener(delayedEventBusPushStrategy); + return delayedEventBusPushStrategy; } diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/mgmt/ui/RedirectController.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/mgmt/ui/RedirectController.java index 3871dad01..9fd9049e3 100644 --- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/mgmt/ui/RedirectController.java +++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/mgmt/ui/RedirectController.java @@ -9,7 +9,7 @@ package org.eclipse.hawkbit.autoconfigure.mgmt.ui; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.servlet.ModelAndView; /** @@ -23,7 +23,7 @@ public class RedirectController { /** * @return redirect to the Management UI */ - @RequestMapping("/") + @GetMapping("/") public ModelAndView home() { return new ModelAndView("redirect:/UI/"); } diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/InMemoryUserManagementAutoConfiguration.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/InMemoryUserManagementAutoConfiguration.java index 6ecf9c1f6..4e5c68237 100644 --- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/InMemoryUserManagementAutoConfiguration.java +++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/InMemoryUserManagementAutoConfiguration.java @@ -32,6 +32,7 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.vaadin.spring.security.config.AuthenticationManagerConfigurer; /** * Auto-configuration for the in-memory-user-management. @@ -40,7 +41,8 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException; @Configuration @ConditionalOnMissingBean(UserDetailsService.class) @EnableConfigurationProperties({ MultiUserProperties.class }) -public class InMemoryUserManagementAutoConfiguration extends GlobalAuthenticationConfigurerAdapter { +public class InMemoryUserManagementAutoConfiguration extends GlobalAuthenticationConfigurerAdapter + implements AuthenticationManagerConfigurer { private static final String DEFAULT_TENANT = "DEFAULT"; diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityManagedConfiguration.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityManagedConfiguration.java index 655a8bc1c..1bb4ae651 100644 --- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityManagedConfiguration.java +++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityManagedConfiguration.java @@ -13,7 +13,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import javax.annotation.PostConstruct; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; @@ -26,7 +25,7 @@ import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants; import org.eclipse.hawkbit.ddi.rest.resource.DdiApiConfiguration; import org.eclipse.hawkbit.im.authentication.SpPermission; import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions; -import org.eclipse.hawkbit.im.authentication.TenantUserPasswordAuthenticationToken; +import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails; import org.eclipse.hawkbit.im.authentication.UserAuthenticationFilter; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; import org.eclipse.hawkbit.mgmt.rest.resource.MgmtApiConfiguration; @@ -62,7 +61,7 @@ import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.http.HttpStatus; import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.authentication.InsufficientAuthenticationException; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; @@ -97,11 +96,12 @@ import org.springframework.util.StringUtils; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; -import org.vaadin.spring.security.VaadinSecurityContext; -import org.vaadin.spring.security.annotation.EnableVaadinSecurity; +import org.vaadin.spring.http.HttpService; +import org.vaadin.spring.security.annotation.EnableVaadinSharedSecurity; +import org.vaadin.spring.security.config.VaadinSharedSecurityConfiguration; +import org.vaadin.spring.security.shared.VaadinAuthenticationSuccessHandler; +import org.vaadin.spring.security.shared.VaadinUrlAuthenticationSuccessHandler; import org.vaadin.spring.security.web.VaadinRedirectStrategy; -import org.vaadin.spring.security.web.authentication.VaadinAuthenticationSuccessHandler; -import org.vaadin.spring.security.web.authentication.VaadinUrlAuthenticationSuccessHandler; /** * All configurations related to HawkBit's authentication and authorization @@ -530,11 +530,10 @@ public class SecurityManagedConfiguration { // Only get the first client registration. Testing against every // client could increase the // attack vector - ClientRegistration clientRegistration = null; - for (final ClientRegistration cr : clientRegistrationRepository) { - clientRegistration = cr; - break; - } + final ClientRegistration clientRegistration = clientRegistrationRepository != null + && clientRegistrationRepository.iterator().hasNext() + ? clientRegistrationRepository.iterator().next() + : null; Assert.notNull(clientRegistration, "There must be a valid client registration"); httpSec.oauth2ResourceServer().jwt().jwkSetUri(clientRegistration.getProviderDetails().getJwkSetUri()); @@ -596,18 +595,13 @@ public class SecurityManagedConfiguration { */ @Configuration @Order(400) - @EnableVaadinSecurity + @EnableVaadinSharedSecurity @ConditionalOnClass(MgmtUiConfiguration.class) public static class UISecurityConfigurationAdapter extends WebSecurityConfigurerAdapter { - @Autowired - private VaadinSecurityContext vaadinSecurityContext; - @Autowired private HawkbitSecurityProperties hawkbitSecurityProperties; - private final VaadinUrlAuthenticationSuccessHandler handler; - @Autowired(required = false) private OAuth2UserService oidcUserService; @@ -620,14 +614,6 @@ public class SecurityManagedConfiguration { @Autowired private LogoutSuccessHandler logoutSuccessHandler; - - public UISecurityConfigurationAdapter(final VaadinRedirectStrategy redirectStrategy) { - handler = new TenantMetadataSavedRequestAwareVaadinAuthenticationSuccessHandler(); - handler.setRedirectStrategy(redirectStrategy); - handler.setDefaultTargetUrl("/UI/"); - handler.setTargetUrlParameter("r"); - } - /** * Filter to protect the hawkBit management UI against to many requests. * @@ -651,17 +637,8 @@ public class SecurityManagedConfiguration { return filterRegBean; } - /** - * post construct for setting the authentication success handler for the - * vaadin security context. - */ - @PostConstruct - public void afterPropertiesSet() { - this.vaadinSecurityContext.addAuthenticationSuccessHandler(handler); - } - @Override - @Bean(name = "authenticationManager") + @Bean(name = VaadinSharedSecurityConfiguration.AUTHENTICATION_MANAGER_BEAN) public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @@ -669,8 +646,13 @@ public class SecurityManagedConfiguration { /** * @return the vaadin success authentication handler */ - @Bean - public VaadinAuthenticationSuccessHandler redirectSaveHandler() { + @Bean(name = VaadinSharedSecurityConfiguration.VAADIN_AUTHENTICATION_SUCCESS_HANDLER_BEAN) + public VaadinAuthenticationSuccessHandler redirectSaveHandler(final HttpService httpService, + final VaadinRedirectStrategy redirectStrategy) { + final VaadinUrlAuthenticationSuccessHandler handler = new TenantMetadataSavedRequestAwareVaadinAuthenticationSuccessHandler( + httpService, redirectStrategy, "/UI/"); + handler.setTargetUrlParameter("r"); + return handler; } @@ -717,8 +699,8 @@ public class SecurityManagedConfiguration { } // UI - httpSec.authorizeRequests().antMatchers("/UI/login/**", "/UI/UIDL/**").permitAll() - .anyRequest().authenticated(); + httpSec.authorizeRequests().antMatchers("/UI/login/**", "/UI/UIDL/**").permitAll().anyRequest() + .authenticated(); if (enableOidc) { // OIDC @@ -737,7 +719,7 @@ public class SecurityManagedConfiguration { @Override public void configure(final WebSecurity webSecurity) throws Exception { - // Not security for static content + // No security for static content webSecurity.ignoring().antMatchers("/documentation/**", "/VAADIN/**", "/*.*", "/docs/**"); } } @@ -756,30 +738,31 @@ class TenantMetadataSavedRequestAwareVaadinAuthenticationSuccessHandler extends @Autowired private SystemSecurityContext systemSecurityContext; + public TenantMetadataSavedRequestAwareVaadinAuthenticationSuccessHandler(final HttpService http, + final VaadinRedirectStrategy redirectStrategy, final String defaultTargetUrl) { + super(http, redirectStrategy, defaultTargetUrl); + } + @Override public void onAuthenticationSuccess(final Authentication authentication) throws Exception { - - if (authentication.getClass().equals(TenantUserPasswordAuthenticationToken.class)) { - systemSecurityContext.runAsSystemAsTenant(systemManagement::getTenantMetadata, - ((TenantUserPasswordAuthenticationToken) authentication).getTenant().toString()); - } else if (authentication.getClass().equals(UsernamePasswordAuthenticationToken.class)) { - // TODO: vaadin4spring-ext-security does not give us the - // fullyAuthenticatedToken - // in the GenericVaadinSecurity class. Only the token which has been - // created in the - // LoginView. This needs to be changed with the update of - // vaadin4spring 0.0.7 because it - // has been fixed. - final String defaultTenant = "DEFAULT"; - systemSecurityContext.runAsSystemAsTenant(systemManagement::getTenantMetadata, defaultTenant); - } + systemSecurityContext.runAsSystemAsTenant(systemManagement::getTenantMetadata, getTenantFrom(authentication)); super.onAuthenticationSuccess(authentication); } + + private static String getTenantFrom(final Authentication authentication) { + final Object details = authentication.getDetails(); + if (details instanceof TenantAwareAuthenticationDetails) { + return ((TenantAwareAuthenticationDetails) details).getTenant(); + } + + throw new InsufficientAuthenticationException("Authentication details/tenant info are not specified!"); + } } /** - * Servletfilter to create metadata after successful authentication over RESTful. + * Servletfilter to create metadata after successful authentication over + * RESTful. */ class AuthenticationSuccessTenantMetadataCreationFilter implements Filter { diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleManagement.java index 00b4d4c7d..5e83a0424 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleManagement.java @@ -21,9 +21,9 @@ import org.eclipse.hawkbit.repository.builder.SoftwareModuleCreate; import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataCreate; import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataUpdate; import org.eclipse.hawkbit.repository.builder.SoftwareModuleUpdate; +import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException; import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; -import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException; import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException; import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException; import org.eclipse.hawkbit.repository.model.AssignedSoftwareModule; @@ -135,6 +135,21 @@ public interface SoftwareModuleManagement @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) Page findByAssignedTo(@NotNull Pageable pageable, long setId); + /** + * Returns count of all modules assigned to given {@link DistributionSet}. + * + * @param setId + * to search for + * + * @return count of {@link SoftwareModule}s that are assigned to given + * {@link DistributionSet}. + * + * @throws EntityNotFoundException + * if distribution set with given ID does not exist + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) + long countByAssignedTo(long setId); + /** * Filter {@link SoftwareModule}s with given * {@link SoftwareModule#getName()} or {@link SoftwareModule#getVersion()} diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetTagManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetTagManagement.java index 6376f06b4..a4fc90e62 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetTagManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetTagManagement.java @@ -155,6 +155,16 @@ public interface TargetTagManagement { @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) Optional get(long id); + /** + * Finds {@link TargetTag} by given ids. + * + * @param ids + * the ids to for + * @return the found {@link TargetTag}s + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) + List get(@NotEmpty Collection ids); + /** * updates the {@link TargetTag}. * diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/AbstractActionEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/AbstractActionEvent.java index 386016337..583e78a09 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/AbstractActionEvent.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/AbstractActionEvent.java @@ -16,6 +16,7 @@ import org.eclipse.hawkbit.repository.model.Action; public abstract class AbstractActionEvent extends RemoteEntityEvent { private static final long serialVersionUID = 1L; + private Long targetId; private Long rolloutId; private Long rolloutGroupId; @@ -31,6 +32,8 @@ public abstract class AbstractActionEvent extends RemoteEntityEvent { * * @param action * the created action + * @param targetId + * targetId identifier (optional) * @param rolloutId * rollout identifier (optional) * @param rolloutGroupId @@ -38,13 +41,18 @@ public abstract class AbstractActionEvent extends RemoteEntityEvent { * @param applicationId * the origin application id */ - public AbstractActionEvent(final Action action, final Long rolloutId, final Long rolloutGroupId, - final String applicationId) { + public AbstractActionEvent(final Action action, final Long targetId, final Long rolloutId, + final Long rolloutGroupId, final String applicationId) { super(action, applicationId); + this.targetId = targetId; this.rolloutId = rolloutId; this.rolloutGroupId = rolloutGroupId; } + public Long getTargetId() { + return targetId; + } + public Long getRolloutId() { return rolloutId; } diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/ActionCreatedEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/ActionCreatedEvent.java index 859e90615..681fd987f 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/ActionCreatedEvent.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/ActionCreatedEvent.java @@ -29,6 +29,8 @@ public class ActionCreatedEvent extends AbstractActionEvent implements EntityCre * * @param action * the created action + * @param targetId + * targetId identifier (optional) * @param rolloutId * rollout identifier (optional) * @param rolloutGroupId @@ -36,9 +38,9 @@ public class ActionCreatedEvent extends AbstractActionEvent implements EntityCre * @param applicationId * the origin application id */ - public ActionCreatedEvent(final Action action, final Long rolloutId, final Long rolloutGroupId, + public ActionCreatedEvent(final Action action, final Long targetId, final Long rolloutId, final Long rolloutGroupId, final String applicationId) { - super(action, rolloutId, rolloutGroupId, applicationId); + super(action, targetId, rolloutId, rolloutGroupId, applicationId); } } diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/ActionUpdatedEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/ActionUpdatedEvent.java index e3a9fae93..6ebfda76a 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/ActionUpdatedEvent.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/ActionUpdatedEvent.java @@ -29,6 +29,8 @@ public class ActionUpdatedEvent extends AbstractActionEvent implements EntityUpd * * @param action * the updated action + * @param targetId + * targetId identifier (optional) * @param rolloutId * rollout identifier (optional) * @param rolloutGroupId @@ -36,9 +38,9 @@ public class ActionUpdatedEvent extends AbstractActionEvent implements EntityUpd * @param applicationId * the origin application id */ - public ActionUpdatedEvent(final Action action, final Long rolloutId, final Long rolloutGroupId, + public ActionUpdatedEvent(final Action action, final Long targetId, final Long rolloutId, final Long rolloutGroupId, final String applicationId) { - super(action, rolloutId, rolloutGroupId, applicationId); + super(action, targetId, rolloutId, rolloutGroupId, applicationId); } } diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/SoftwareModuleTypeUpdatedEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/SoftwareModuleTypeUpdatedEvent.java index fc3a6c207..ca4f07b71 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/SoftwareModuleTypeUpdatedEvent.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/SoftwareModuleTypeUpdatedEvent.java @@ -8,13 +8,15 @@ */ package org.eclipse.hawkbit.repository.event.remote.entity; +import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; /** * Defines the remote event for updating a {@link SoftwareModuleType}. * */ -public class SoftwareModuleTypeUpdatedEvent extends RemoteEntityEvent { +public class SoftwareModuleTypeUpdatedEvent extends RemoteEntityEvent + implements EntityUpdatedEvent { private static final long serialVersionUID = 1L; diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/AssignedSoftwareModule.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/AssignedSoftwareModule.java index 9215fe46f..94e5a72af 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/AssignedSoftwareModule.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/AssignedSoftwareModule.java @@ -10,13 +10,15 @@ package org.eclipse.hawkbit.repository.model; import java.io.Serializable; +import org.springframework.hateoas.Identifiable; + /** * Use to display software modules for the selected distribution. * */ -public class AssignedSoftwareModule implements Serializable { +public class AssignedSoftwareModule implements Serializable, Identifiable { - private static final long serialVersionUID = 6144585781451168439L; + private static final long serialVersionUID = 1L; private final SoftwareModule softwareModule; @@ -89,4 +91,9 @@ public class AssignedSoftwareModule implements Serializable { return true; } + @Override + public Long getId() { + return softwareModule.getId(); + } + } diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetType.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetType.java index 6f011be68..1b36d8927 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetType.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetType.java @@ -18,22 +18,7 @@ import java.util.Set; * assigned to a {@link Target}. * */ -public interface DistributionSetType extends NamedEntity { - /** - * Maximum length of key. - */ - int KEY_MAX_SIZE = 64; - - /** - * Maximum length of colour in Management UI. - */ - int COLOUR_MAX_SIZE = 16; - - /** - * @return true if the type is deleted and only kept for - * history purposes. - */ - boolean isDeleted(); +public interface DistributionSetType extends Type { /** * @return immutable set of {@link SoftwareModuleType}s that need to be in a @@ -60,6 +45,18 @@ public interface DistributionSetType extends NamedEntity { return containsMandatoryModuleType(softwareModuleType) || containsOptionalModuleType(softwareModuleType); } + /** + * Checks if the given {@link SoftwareModuleType} is in this + * {@link DistributionSetType}. + * + * @param softwareModuleTypeId + * search for by {@link SoftwareModuleType#getId()} + * @return true if found + */ + default boolean containsModuleType(final Long softwareModuleTypeId) { + return containsMandatoryModuleType(softwareModuleTypeId) || containsOptionalModuleType(softwareModuleTypeId); + } + /** * Checks if the given {@link SoftwareModuleType} is in * {@link #getMandatoryModuleTypes()}. @@ -118,11 +115,6 @@ public interface DistributionSetType extends NamedEntity { */ boolean areModuleEntriesIdentical(DistributionSetType dsType); - /** - * @return business key of this {@link DistributionSetType}. - */ - String getKey(); - /** * @param distributionSet * to check for completeness @@ -130,10 +122,4 @@ public interface DistributionSetType extends NamedEntity { * in the system. */ boolean checkComplete(DistributionSet distributionSet); - - /** - * @return get color code to by used in management UI views. - */ - String getColour(); - } diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModuleType.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModuleType.java index a8ce522e1..12e7a1444 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModuleType.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModuleType.java @@ -14,37 +14,11 @@ package org.eclipse.hawkbit.repository.model; * specific information. * */ -public interface SoftwareModuleType extends NamedEntity { - /** - * Maximum length of key. - */ - int KEY_MAX_SIZE = 64; - - /** - * Maximum length of colour in Management UI. - */ - int COLOUR_MAX_SIZE = 16; - - /** - * @return business key of this {@link SoftwareModuleType}. - */ - String getKey(); +public interface SoftwareModuleType extends Type { /** * @return maximum assignments of an {@link SoftwareModule} of this type to * a {@link DistributionSet}. */ int getMaxAssignments(); - - /** - * @return true if the type is deleted and only kept for - * history purposes. - */ - boolean isDeleted(); - - /** - * @return get color code to by used in management UI views. - */ - String getColour(); - } diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetWithActionStatus.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetWithActionStatus.java index 9193b47ef..41f7ccbc2 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetWithActionStatus.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetWithActionStatus.java @@ -9,13 +9,14 @@ package org.eclipse.hawkbit.repository.model; import org.eclipse.hawkbit.repository.model.Action.Status; +import org.springframework.hateoas.Identifiable; /** * * Target with action status. * */ -public class TargetWithActionStatus { +public class TargetWithActionStatus implements Identifiable { private Target target; @@ -46,4 +47,9 @@ public class TargetWithActionStatus { this.status = status; } + @Override + public Long getId() { + return target.getId(); + } + } diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Type.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Type.java new file mode 100644 index 000000000..90c0134b2 --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Type.java @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2019 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.repository.model; + +/** + * {@link Type} is an abstract definition for {@link DistributionSetType}s and + * {@link SoftwareModuleType}s + */ +public interface Type extends NamedEntity { + /** + * Maximum length of key. + */ + int KEY_MAX_SIZE = 64; + + /** + * Maximum length of color in Management UI. + */ + int COLOUR_MAX_SIZE = 16; + + /** + * @return business key. + */ + String getKey(); + + /** + * @return true if the type is deleted and only kept for + * history purposes. + */ + boolean isDeleted(); + + /** + * @return get color code to be used in management UI views. + */ + String getColour(); + +} diff --git a/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/builder/AbstractDistributionSetTypeUpdateCreate.java b/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/builder/AbstractDistributionSetTypeUpdateCreate.java index 0d81ec878..dd6c8d459 100644 --- a/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/builder/AbstractDistributionSetTypeUpdateCreate.java +++ b/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/builder/AbstractDistributionSetTypeUpdateCreate.java @@ -41,18 +41,10 @@ public abstract class AbstractDistributionSetTypeUpdateCreate extends Abstrac } public Optional> getMandatory() { - if (CollectionUtils.isEmpty(mandatory)) { - return Optional.empty(); - } - return Optional.ofNullable(mandatory); } public Optional> getOptional() { - if (CollectionUtils.isEmpty(optional)) { - return Optional.empty(); - } - return Optional.ofNullable(optional); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/DefaultRolloutApprovalStrategy.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/DefaultRolloutApprovalStrategy.java index 6fe288128..485d168fa 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/DefaultRolloutApprovalStrategy.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/DefaultRolloutApprovalStrategy.java @@ -8,16 +8,22 @@ */ package org.eclipse.hawkbit.repository.jpa; +import java.util.Collection; + import org.eclipse.hawkbit.im.authentication.SpPermission; import org.eclipse.hawkbit.im.authentication.UserPrincipal; import org.eclipse.hawkbit.repository.RolloutApprovalStrategy; import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.model.Rollout; +import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus; import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.util.StringUtils; /** * Default implementation of {@link RolloutApprovalStrategy}. Decides whether @@ -33,38 +39,50 @@ public class DefaultRolloutApprovalStrategy implements RolloutApprovalStrategy { private final SystemSecurityContext systemSecurityContext; - DefaultRolloutApprovalStrategy(UserDetailsService userDetailsService, - TenantConfigurationManagement tenantConfigurationManagement, - SystemSecurityContext systemSecurityContext) { + DefaultRolloutApprovalStrategy(final UserDetailsService userDetailsService, + final TenantConfigurationManagement tenantConfigurationManagement, + final SystemSecurityContext systemSecurityContext) { this.userDetailsService = userDetailsService; this.tenantConfigurationManagement = tenantConfigurationManagement; this.systemSecurityContext = systemSecurityContext; } /** - * Returns true, if rollout approval is enabled and rollout creator doesn't have - * approval role. + * Returns true, if rollout approval is enabled and rollout creator doesn't + * have approval role. */ @Override public boolean isApprovalNeeded(final Rollout rollout) { - final UserDetails userDetails = this.getActor(rollout); - final boolean approvalEnabled = this.tenantConfigurationManagement - .getConfigurationValue(TenantConfigurationKey.ROLLOUT_APPROVAL_ENABLED, Boolean.class).getValue(); - return approvalEnabled && userDetails.getAuthorities().stream() - .noneMatch(authority -> SpPermission.APPROVE_ROLLOUT.equals(authority.getAuthority())); + return isApprovalEnabled() && hasNoApproveRolloutPermission(getActor(rollout).getAuthorities()); } + private boolean isApprovalEnabled() { + return systemSecurityContext.runAsSystem(() -> tenantConfigurationManagement + .getConfigurationValue(TenantConfigurationKey.ROLLOUT_APPROVAL_ENABLED, Boolean.class).getValue()); + } - private UserDetails getActor(Rollout rollout) { - final String actor = rollout.getLastModifiedBy() != null ? rollout.getLastModifiedBy() : rollout.getCreatedBy(); - return systemSecurityContext.runAsSystem(() -> { - UserPrincipal userPrincipal = (UserPrincipal) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); - if(userPrincipal.getUsername().equals(actor)) { - return userPrincipal; - } else { - return this.userDetailsService.loadUserByUsername(actor); + private UserDetails getActor(final Rollout rollout) { + // rollout state transition from CREATING to CREATED is managed by + // scheduler under SYSTEM user context, thus we get the + // user based on the properties of initially created rollout entity + if (RolloutStatus.CREATING == rollout.getStatus()) { + final String actor = rollout.getLastModifiedBy() != null ? rollout.getLastModifiedBy() + : rollout.getCreatedBy(); + if (!StringUtils.isEmpty(actor)) { + return systemSecurityContext.runAsSystem(() -> userDetailsService.loadUserByUsername(actor)); } - }); + } + + return (UserPrincipal) getCurrentAuthentication().getPrincipal(); + } + + private static Authentication getCurrentAuthentication() { + return SecurityContextHolder.getContext().getAuthentication(); + } + + private static boolean hasNoApproveRolloutPermission(final Collection authorities) { + return authorities.stream() + .noneMatch(authority -> SpPermission.APPROVE_ROLLOUT.equals(authority.getAuthority())); } /*** @@ -74,12 +92,12 @@ public class DefaultRolloutApprovalStrategy implements RolloutApprovalStrategy { * rollout to create approval task for. */ @Override - public void onApprovalRequired(Rollout rollout) { + public void onApprovalRequired(final Rollout rollout) { // do nothing per default, can be extended by further implementations. } @Override - public String getApprovalUser(Rollout rollout) { - return SecurityContextHolder.getContext().getAuthentication().getName(); + public String getApprovalUser(final Rollout rollout) { + return getCurrentAuthentication().getName(); } } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetManagement.java index 12174e745..abc9465fd 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetManagement.java @@ -630,7 +630,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { specList.add(spec); } - if (isDSWithNoTagSelected(distributionSetFilter) || isTagsSelected(distributionSetFilter)) { + if (hasTagsFilterActive(distributionSetFilter)) { spec = DistributionSetSpecification.hasTags(distributionSetFilter.getTagNames(), distributionSetFilter.getSelectDSWithNoTag()); specList.add(spec); @@ -666,12 +666,11 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { } } - private static Boolean isDSWithNoTagSelected(final DistributionSetFilter distributionSetFilter) { - return distributionSetFilter.getSelectDSWithNoTag() != null && distributionSetFilter.getSelectDSWithNoTag(); - } + private static boolean hasTagsFilterActive(final DistributionSetFilter distributionSetFilter) { + final boolean isNoTagActive = Boolean.TRUE.equals(distributionSetFilter.getSelectDSWithNoTag()); + final boolean isAtLeastOneTagActive = !CollectionUtils.isEmpty(distributionSetFilter.getTagNames()); - private static Boolean isTagsSelected(final DistributionSetFilter distributionSetFilter) { - return !CollectionUtils.isEmpty(distributionSetFilter.getTagNames()); + return isNoTagActive || isAtLeastOneTagActive; } /** diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetTypeManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetTypeManagement.java index 48f34deaa..390ac3c12 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetTypeManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetTypeManagement.java @@ -13,7 +13,11 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.function.LongFunction; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.eclipse.hawkbit.repository.DistributionSetTypeFields; import org.eclipse.hawkbit.repository.DistributionSetTypeManagement; @@ -21,9 +25,9 @@ import org.eclipse.hawkbit.repository.QuotaManagement; import org.eclipse.hawkbit.repository.builder.DistributionSetTypeCreate; import org.eclipse.hawkbit.repository.builder.DistributionSetTypeUpdate; import org.eclipse.hawkbit.repository.builder.GenericDistributionSetTypeUpdate; +import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException; -import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException; import org.eclipse.hawkbit.repository.jpa.builder.JpaDistributionSetTypeCreate; import org.eclipse.hawkbit.repository.jpa.configuration.Constants; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType; @@ -95,18 +99,51 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana update.getDescription().ifPresent(type::setDescription); update.getColour().ifPresent(type::setColour); - if (hasModules(update)) { + if (hasModuleChanges(update)) { checkDistributionSetTypeSoftwareModuleTypesIsAllowedToModify(update.getId()); - update.getMandatory().ifPresent( - mand -> softwareModuleTypeRepository.findAllById(mand).forEach(type::addMandatoryModuleType)); - update.getOptional().ifPresent( - opt -> softwareModuleTypeRepository.findAllById(opt).forEach(type::addOptionalModuleType)); + final Collection currentMandatorySmTypeIds = type.getMandatoryModuleTypes().stream() + .map(SoftwareModuleType::getId).collect(Collectors.toSet()); + final Collection currentOptionalSmTypeIds = type.getOptionalModuleTypes().stream() + .map(SoftwareModuleType::getId).collect(Collectors.toSet()); + final Collection currentSmTypeIds = Stream + .concat(currentMandatorySmTypeIds.stream(), currentOptionalSmTypeIds.stream()) + .collect(Collectors.toSet()); + + final Collection updatedMandatorySmTypeIds = update.getMandatory().orElse(currentMandatorySmTypeIds); + final Collection updatedOptionalSmTypeIds = update.getOptional().orElse(currentOptionalSmTypeIds); + final Collection updatedSmTypeIds = Stream + .concat(updatedMandatorySmTypeIds.stream(), updatedOptionalSmTypeIds.stream()) + .collect(Collectors.toSet()); + + addModuleTypes(currentMandatorySmTypeIds, updatedMandatorySmTypeIds, type::addMandatoryModuleType); + addModuleTypes(currentOptionalSmTypeIds, updatedOptionalSmTypeIds, type::addOptionalModuleType); + + removeModuleTypes(currentSmTypeIds, updatedSmTypeIds, type::removeModuleType); } return distributionSetTypeRepository.save(type); } + private void addModuleTypes(final Collection currentSmTypeIds, final Collection updatedSmTypeIds, + final Function addModuleTypeCallback) { + final Set smTypeIdsToAdd = updatedSmTypeIds.stream().filter(id -> !currentSmTypeIds.contains(id)) + .collect(Collectors.toSet()); + if (!CollectionUtils.isEmpty(smTypeIdsToAdd)) { + softwareModuleTypeRepository.findAllById(smTypeIdsToAdd).forEach(addModuleTypeCallback::apply); + } + } + + private static void removeModuleTypes(final Collection currentSmTypeIds, + final Collection updatedSmTypeIds, + final LongFunction removeModuleTypeCallback) { + final Set smTypeIdsToRemove = currentSmTypeIds.stream().filter(id -> !updatedSmTypeIds.contains(id)) + .collect(Collectors.toSet()); + if (!CollectionUtils.isEmpty(smTypeIdsToRemove)) { + smTypeIdsToRemove.forEach(removeModuleTypeCallback::apply); + } + } + @Override @Transactional @Retryable(include = { @@ -258,7 +295,7 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana .orElseThrow(() -> new EntityNotFoundException(DistributionSetType.class, setId)); } - private static boolean hasModules(final GenericDistributionSetTypeUpdate update) { + private static boolean hasModuleChanges(final GenericDistributionSetTypeUpdate update) { return update.getOptional().isPresent() || update.getMandatory().isPresent(); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutManagement.java index 26a31d931..6d2134542 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutManagement.java @@ -44,8 +44,6 @@ import org.eclipse.hawkbit.repository.event.remote.entity.RolloutGroupCreatedEve import org.eclipse.hawkbit.repository.event.remote.entity.RolloutUpdatedEvent; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException; -import org.eclipse.hawkbit.repository.exception.MultiAssignmentIsNotEnabledException; -import org.eclipse.hawkbit.repository.exception.NoWeightProvidedInMultiAssignmentModeException; import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException; import org.eclipse.hawkbit.repository.jpa.configuration.Constants; import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor; @@ -61,7 +59,6 @@ import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder; import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper; import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper; import org.eclipse.hawkbit.repository.jpa.utils.WeightValidationHelper; -import org.eclipse.hawkbit.repository.jpa.utils.TenantConfigHelper; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action.ActionType; import org.eclipse.hawkbit.repository.model.Action.Status; @@ -1019,7 +1016,9 @@ public class JpaRolloutManagement extends AbstractRolloutManagement { update.getActionType().ifPresent(rollout::setActionType); update.getForcedTime().ifPresent(rollout::setForcedTime); update.getWeight().ifPresent(rollout::setWeight); - update.getStartAt().ifPresent(rollout::setStartAt); + // reseting back to manual start is done by setting start at time to + // null + rollout.setStartAt(update.getStartAt().orElse(null)); update.getSet().ifPresent(setId -> { final DistributionSet set = distributionSetManagement.get(setId) .orElseThrow(() -> new EntityNotFoundException(DistributionSet.class, setId)); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSoftwareModuleManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSoftwareModuleManagement.java index 860265f5b..ea85e11f6 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSoftwareModuleManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSoftwareModuleManagement.java @@ -21,8 +21,11 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import javax.persistence.EntityManager; +import javax.persistence.Tuple; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Expression; +import javax.persistence.criteria.JoinType; import javax.persistence.criteria.ListJoin; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; @@ -344,70 +347,43 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement { @Override public Slice findAllOrderBySetAssignmentAndModuleNameAscModuleVersionAsc( - final Pageable pageable, final long orderByDistributionId, final String searchText, final Long typeId) { + final Pageable pageable, final long dsId, final String searchText, final Long smTypeId) { + final CriteriaBuilder cb = entityManager.getCriteriaBuilder(); + final CriteriaQuery query = cb.createTupleQuery(); + final Root smRoot = query.from(JpaSoftwareModule.class); + + final ListJoin assignedDsList = smRoot + .join(JpaSoftwareModule_.assignedTo, JoinType.LEFT); + + final Expression assignedCaseMax = cb.max( + cb. selectCase(assignedDsList.get(JpaDistributionSet_.id)).when(dsId, 1).otherwise(0)); + + query.multiselect(smRoot.alias("sm"), assignedCaseMax.alias("assigned")); + + final Predicate[] specPredicate = specificationsToPredicate(buildSpecificationList(searchText, smTypeId), + smRoot, query, cb); + + if (specPredicate.length > 0) { + query.where(specPredicate); + } + + query.groupBy(smRoot); + + query.orderBy(cb.desc(assignedCaseMax), cb.asc(smRoot.get(JpaSoftwareModule_.name)), + cb.asc(smRoot.get(JpaSoftwareModule_.version))); + + final int pageSize = pageable.getPageSize(); + final List smWithAssignedFlagList = entityManager.createQuery(query) + .setFirstResult((int) pageable.getOffset()).setMaxResults(pageSize).getResultList(); + final boolean hasNext = smWithAssignedFlagList.size() > pageSize; final List resultList = new ArrayList<>(); - final int pageSize = pageable.getPageSize(); - final CriteriaBuilder cb = entityManager.getCriteriaBuilder(); - // get the assigned software modules - final CriteriaQuery assignedQuery = cb.createQuery(JpaSoftwareModule.class); - final Root assignedRoot = assignedQuery.from(JpaSoftwareModule.class); - assignedQuery.distinct(true); - final ListJoin assignedDsJoin = assignedRoot - .join(JpaSoftwareModule_.assignedTo); - // build the specifications and then to predicates necessary by the - // given filters - final Predicate[] specPredicate = specificationsToPredicate(buildSpecificationList(searchText, typeId), - assignedRoot, assignedQuery, cb, - cb.equal(assignedDsJoin.get(JpaDistributionSet_.id), orderByDistributionId)); - // if we have some predicates then add it to the where clause of the - // multi select - assignedQuery.where(specPredicate); - assignedQuery.orderBy(cb.asc(assignedRoot.get(JpaSoftwareModule_.name)), - cb.asc(assignedRoot.get(JpaSoftwareModule_.version))); - // don't page the assigned query on database, we need all assigned - // software modules to filter - // them out in the unassigned query - final List assignedSoftwareModules = entityManager.createQuery(assignedQuery) - .getResultList(); - // map result - if (pageable.getOffset() < assignedSoftwareModules.size()) { - assignedSoftwareModules - .subList((int) pageable.getOffset(), - Math.min(assignedSoftwareModules.size(), pageable.getPageSize())) - .forEach(sw -> resultList.add(new AssignedSoftwareModule(sw, true))); - } + smWithAssignedFlagList.forEach(smWithAssignedFlag -> resultList + .add(new AssignedSoftwareModule(smWithAssignedFlag.get("sm", JpaSoftwareModule.class), + smWithAssignedFlag.get("assigned", Number.class).longValue() == 1))); - if (assignedSoftwareModules.size() >= pageSize) { - return new SliceImpl<>(resultList); - } - - // get the unassigned software modules - final CriteriaQuery unassignedQuery = cb.createQuery(JpaSoftwareModule.class); - unassignedQuery.distinct(true); - final Root unassignedRoot = unassignedQuery.from(JpaSoftwareModule.class); - - Predicate[] unassignedSpec; - if (!assignedSoftwareModules.isEmpty()) { - unassignedSpec = specificationsToPredicate(buildSpecificationList(searchText, typeId), unassignedRoot, - unassignedQuery, cb, cb.not(unassignedRoot.get(JpaSoftwareModule_.id).in( - assignedSoftwareModules.stream().map(SoftwareModule::getId).collect(Collectors.toList())))); - } else { - unassignedSpec = specificationsToPredicate(buildSpecificationList(searchText, typeId), unassignedRoot, - unassignedQuery, cb); - } - - unassignedQuery.where(unassignedSpec); - unassignedQuery.orderBy(cb.asc(unassignedRoot.get(JpaSoftwareModule_.name)), - cb.asc(unassignedRoot.get(JpaSoftwareModule_.version))); - final List unassignedSoftwareModules = entityManager.createQuery(unassignedQuery) - .setFirstResult((int) Math.max(0, pageable.getOffset() - assignedSoftwareModules.size())) - .setMaxResults(pageSize).getResultList(); - // map result - unassignedSoftwareModules.forEach(sw -> resultList.add(new AssignedSoftwareModule(sw, false))); - - return new SliceImpl<>(resultList); + return new SliceImpl<>(Collections.unmodifiableList(resultList), pageable, hasNext); } private List> buildSpecificationList(final String searchText, final Long typeId) { @@ -464,6 +440,15 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement { return softwareModuleRepository.findByAssignedToId(pageable, setId); } + @Override + public long countByAssignedTo(final long setId) { + if (!distributionSetRepository.existsById(setId)) { + throw new EntityNotFoundException(DistributionSet.class, setId); + } + + return softwareModuleRepository.countByAssignedToId(setId); + } + @Override @Transactional @Retryable(include = { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetManagement.java index 4cb23df05..8e1450480 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetManagement.java @@ -13,6 +13,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -22,6 +23,7 @@ import javax.persistence.EntityManager; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Expression; +import javax.persistence.criteria.MapJoin; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; @@ -293,8 +295,7 @@ public class JpaTargetManagement implements TargetManagement { public Optional getMetaDataByControllerId(final String controllerId, final String key) { final Long targetId = getByControllerIdAndThrowIfNotFound(controllerId).getId(); - return targetMetadataRepository.findById(new TargetMetadataCompositeKey(targetId, key)) - .map(t -> t); + return targetMetadataRepository.findById(new TargetMetadataCompositeKey(targetId, key)).map(t -> t); } @Override @@ -457,7 +458,7 @@ public class JpaTargetManagement implements TargetManagement { if ((filterParams.getFilterByStatus() != null) && !filterParams.getFilterByStatus().isEmpty()) { specList.add(TargetSpecifications.hasTargetUpdateStatus(filterParams.getFilterByStatus())); } - if (filterParams.getOverdueState() != null) { + if (filterParams.getOverdueState() != null && filterParams.getOverdueState()) { specList.add(TargetSpecifications.isOverdue(TimestampCalculator.calculateOverdueTimestamp())); } if (filterParams.getFilterByDistributionId() != null) { @@ -478,8 +479,11 @@ public class JpaTargetManagement implements TargetManagement { } private static boolean hasTagsFilterActive(final FilterParams filterParams) { - return ((filterParams.getSelectTargetWithNoTag() != null) && filterParams.getSelectTargetWithNoTag()) - || ((filterParams.getFilterByTagNames() != null) && (filterParams.getFilterByTagNames().length > 0)); + final boolean isNoTagActive = Boolean.TRUE.equals(filterParams.getSelectTargetWithNoTag()); + final boolean isAtLeastOneTagActive = filterParams.getFilterByTagNames() != null + && filterParams.getFilterByTagNames().length > 0; + + return isNoTagActive || isAtLeastOneTagActive; } private Slice findByCriteriaAPI(final Pageable pageable, final List> specList) { @@ -611,16 +615,10 @@ public class JpaTargetManagement implements TargetManagement { } // add the order to the multi select first based on the selectCase query.orderBy(cb.asc(selectCase), cb.desc(targetRoot.get(JpaTarget_.id))); - // the result is a Object[] due the fact that the selectCase is an extra - // column, so it cannot - // be mapped directly to a Target entity because the selectCase is not a - // attribute of the - // Target entity, the the Object array contains the Target on the first - // index of the array and - // the 2nd contains the selectCase int value. + final int pageSize = pageable.getPageSize(); final List resultList = entityManager.createQuery(query).setFirstResult((int) pageable.getOffset()) - .setMaxResults(pageSize + 1).getResultList(); + .setMaxResults(pageSize).getResultList(); final boolean hasNext = resultList.size() > pageSize; return new SliceImpl<>(Collections.unmodifiableList(resultList), pageable, hasNext); } @@ -785,9 +783,20 @@ public class JpaTargetManagement implements TargetManagement { @Override public Map getControllerAttributes(final String controllerId) { - final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId); + final CriteriaBuilder cb = entityManager.getCriteriaBuilder(); + final CriteriaQuery query = cb.createQuery(Object[].class); - return target.getControllerAttributes(); + final Root targetRoot = query.from(JpaTarget.class); + query.where(cb.equal(targetRoot.get(JpaTarget_.controllerId), controllerId)); + + final MapJoin attributes = targetRoot.join(JpaTarget_.controllerAttributes); + query.multiselect(attributes.key(), attributes.value()); + query.orderBy(cb.asc(attributes.key())); + + final List attr = entityManager.createQuery(query).getResultList(); + + return attr.stream().collect(Collectors.toMap(entry -> (String) entry[0], entry -> (String) entry[1], + (v1, v2) -> v1, LinkedHashMap::new)); } @Override diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetTagManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetTagManagement.java index eb5fa5265..3bad1eab3 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetTagManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetTagManagement.java @@ -142,6 +142,11 @@ public class JpaTargetTagManagement implements TargetTagManagement { return targetTagRepository.findById(id).map(tt -> (TargetTag) tt); } + @Override + public List get(final Collection ids) { + return Collections.unmodifiableList(targetTagRepository.findAllById(ids)); + } + @Override public Page findAll(final Pageable pageable) { return convertTPage(targetTagRepository.findAll(pageable), pageable); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetTagRepository.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetTagRepository.java index b242865af..4570ddb0c 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetTagRepository.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetTagRepository.java @@ -81,4 +81,9 @@ public interface TargetTagRepository @Transactional @Query("DELETE FROM JpaTargetTag t WHERE t.tenant = :tenant") void deleteByTenant(@Param("tenant") String tenant); + + @Override + // Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477 + @Query("SELECT t FROM JpaTargetTag t WHERE t.id IN ?1") + List findAllById(Iterable ids); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaAction.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaAction.java index 3b86ba46a..6879f0a66 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaAction.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaAction.java @@ -236,15 +236,17 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio @Override public void fireCreateEvent(final DescriptorEvent descriptorEvent) { EventPublisherHolder.getInstance().getEventPublisher() - .publishEvent(new ActionCreatedEvent(this, BaseEntity.getIdOrNull(rollout), - BaseEntity.getIdOrNull(rolloutGroup), EventPublisherHolder.getInstance().getApplicationId())); + .publishEvent(new ActionCreatedEvent(this, BaseEntity.getIdOrNull(target), + BaseEntity.getIdOrNull(rollout), BaseEntity.getIdOrNull(rolloutGroup), + EventPublisherHolder.getInstance().getApplicationId())); } @Override public void fireUpdateEvent(final DescriptorEvent descriptorEvent) { EventPublisherHolder.getInstance().getEventPublisher() - .publishEvent(new ActionUpdatedEvent(this, BaseEntity.getIdOrNull(rollout), - BaseEntity.getIdOrNull(rolloutGroup), EventPublisherHolder.getInstance().getApplicationId())); + .publishEvent(new ActionUpdatedEvent(this, BaseEntity.getIdOrNull(target), + BaseEntity.getIdOrNull(rollout), BaseEntity.getIdOrNull(rolloutGroup), + EventPublisherHolder.getInstance().getApplicationId())); } @Override diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/DistributionSetSpecification.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/DistributionSetSpecification.java index 828554271..953b304f4 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/DistributionSetSpecification.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/DistributionSetSpecification.java @@ -8,13 +8,16 @@ */ package org.eclipse.hawkbit.repository.jpa.specifications; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.JoinType; import javax.persistence.criteria.ListJoin; import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; import javax.persistence.criteria.SetJoin; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; @@ -149,27 +152,36 @@ public final class DistributionSetSpecification { public static Specification hasTags(final Collection tagNames, final Boolean selectDSWithNoTag) { return (targetRoot, query, cb) -> { - final SetJoin tags = targetRoot.join(JpaDistributionSet_.tags, - JoinType.LEFT); - final Predicate predicate = getPredicate(tags, tagNames, selectDSWithNoTag, cb); + final Predicate predicate = getHasTagsPredicate(targetRoot, cb, selectDSWithNoTag, tagNames); query.distinct(true); return predicate; }; } - private static Predicate getPredicate(final SetJoin tags, - final Collection tagNames, final Boolean selectDSWithNoTag, final CriteriaBuilder cb) { - tags.get(JpaDistributionSetTag_.name); + private static Predicate getHasTagsPredicate(final Root targetRoot, final CriteriaBuilder cb, + final Boolean selectDSWithNoTag, final Collection tagNames) { + final SetJoin tags = targetRoot.join(JpaDistributionSet_.tags, + JoinType.LEFT); final Path exp = tags.get(JpaDistributionSetTag_.name); - if (selectDSWithNoTag != null && selectDSWithNoTag) { - if (!CollectionUtils.isEmpty(tagNames)) { - return cb.or(exp.isNull(), exp.in(tagNames)); - } else { - return exp.isNull(); - } - } else { - return exp.in(tagNames); + + final List hasTagsPredicates = new ArrayList<>(); + if (isNoTagActive(selectDSWithNoTag)) { + hasTagsPredicates.add(exp.isNull()); } + if (isAtLeastOneTagActive(tagNames)) { + hasTagsPredicates.add(exp.in(tagNames)); + } + + return hasTagsPredicates.stream().reduce(cb::or).orElseThrow( + () -> new RuntimeException("Neither NO_TAG, nor TAG distribution set tag filter was provided!")); + } + + private static boolean isNoTagActive(final Boolean selectDSWithNoTag) { + return Boolean.TRUE.equals(selectDSWithNoTag); + } + + private static boolean isAtLeastOneTagActive(final Collection tagNames) { + return !CollectionUtils.isEmpty(tagNames); } /** diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TargetSpecifications.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TargetSpecifications.java index 52fd2b798..5e92e5d41 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TargetSpecifications.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TargetSpecifications.java @@ -8,17 +8,18 @@ */ package org.eclipse.hawkbit.repository.jpa.specifications; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.JoinType; import javax.persistence.criteria.ListJoin; +import javax.persistence.criteria.MapJoin; import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import javax.persistence.criteria.SetJoin; -import javax.persistence.criteria.MapJoin; import javax.validation.constraints.NotNull; import org.eclipse.hawkbit.repository.jpa.model.JpaAction; @@ -226,25 +227,35 @@ public final class TargetSpecifications { */ public static Specification hasTags(final String[] tagNames, final Boolean selectTargetWithNoTag) { return (targetRoot, query, cb) -> { - final Predicate predicate = getPredicate(targetRoot, cb, selectTargetWithNoTag, tagNames); + final Predicate predicate = getHasTagsPredicate(targetRoot, cb, selectTargetWithNoTag, tagNames); query.distinct(true); return predicate; }; } - private static Predicate getPredicate(final Root targetRoot, final CriteriaBuilder cb, + private static Predicate getHasTagsPredicate(final Root targetRoot, final CriteriaBuilder cb, final Boolean selectTargetWithNoTag, final String[] tagNames) { final SetJoin tags = targetRoot.join(JpaTarget_.tags, JoinType.LEFT); final Path exp = tags.get(JpaTargetTag_.name); - if (selectTargetWithNoTag) { - if (tagNames != null) { - return cb.or(exp.isNull(), exp.in(tagNames)); - } else { - return exp.isNull(); - } - } else { - return exp.in(tagNames); + + final List hasTagsPredicates = new ArrayList<>(); + if (isNoTagActive(selectTargetWithNoTag)) { + hasTagsPredicates.add(exp.isNull()); } + if (isAtLeastOneTagActive(tagNames)) { + hasTagsPredicates.add(exp.in(tagNames)); + } + + return hasTagsPredicates.stream().reduce(cb::or) + .orElseThrow(() -> new RuntimeException("Neither NO_TAG, nor TAG target tag filter was provided!")); + } + + private static boolean isNoTagActive(final Boolean selectTargetWithNoTag) { + return Boolean.TRUE.equals(selectTargetWithNoTag); + } + + private static boolean isAtLeastOneTagActive(final String[] tagNames) { + return tagNames != null && tagNames.length > 0; } /** diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/event/remote/entity/AbstractRemoteEntityEventTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/event/remote/entity/AbstractRemoteEntityEventTest.java index f43afc8df..7cf096045 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/event/remote/entity/AbstractRemoteEntityEventTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/event/remote/entity/AbstractRemoteEntityEventTest.java @@ -12,6 +12,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; import java.lang.reflect.Constructor; +import java.util.Arrays; import org.eclipse.hawkbit.repository.event.remote.AbstractRemoteEventTest; @@ -35,25 +36,32 @@ public abstract class AbstractRemoteEntityEventTest extends AbstractRemoteEve protected RemoteEntityEvent createRemoteEvent(final E baseEntity, final Class> eventType) { - Constructor constructor = null; - for (final Constructor constructors : eventType.getDeclaredConstructors()) { - if (constructors.getParameterCount() == 2) { - constructor = constructors; - } - } - - if (constructor == null) { - throw new IllegalArgumentException("No suitable constructor foundes"); - } - + final int constructorParamCount = getConstructorParamCount(); + final Constructor eventConstructor = findEventConstructorByParamCount(eventType, constructorParamCount); + final Object[] eventConstructorParams = getConstructorParams(baseEntity); try { - return (RemoteEntityEvent) constructor.newInstance(baseEntity, "Node"); + return (RemoteEntityEvent) eventConstructor.newInstance(eventConstructorParams); } catch (final ReflectiveOperationException e) { fail("Exception should not happen " + e.getMessage()); } return null; } + protected int getConstructorParamCount() { + return 2; + } + + protected Constructor findEventConstructorByParamCount(final Class> eventType, + final int paramCount) { + return Arrays.stream(eventType.getDeclaredConstructors()) + .filter(constructor -> constructor.getParameterCount() == paramCount).findAny() + .orElseThrow(() -> new IllegalArgumentException("No suitable constructor founded")); + } + + protected Object[] getConstructorParams(final E baseEntity) { + return new Object[] { baseEntity, "Node" }; + } + protected RemoteEntityEvent assertEntity(final E baseEntity, final RemoteEntityEvent event) { assertThat(event.getEntity()).isSameAs(baseEntity); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/event/remote/entity/ActionEventTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/event/remote/entity/ActionEventTest.java index b782a32e6..57c8953a7 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/event/remote/entity/ActionEventTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/event/remote/entity/ActionEventTest.java @@ -9,9 +9,6 @@ package org.eclipse.hawkbit.repository.event.remote.entity; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.fail; - -import java.lang.reflect.Constructor; import org.eclipse.hawkbit.repository.jpa.model.JpaAction; import org.eclipse.hawkbit.repository.model.Action; @@ -45,26 +42,13 @@ public class ActionEventTest extends AbstractRemoteEntityEventTest { } @Override - protected RemoteEntityEvent createRemoteEvent(final Action baseEntity, - final Class> eventType) { + protected int getConstructorParamCount() { + return 5; + } - Constructor constructor = null; - for (final Constructor constructors : eventType.getDeclaredConstructors()) { - if (constructors.getParameterCount() == 4) { - constructor = constructors; - } - } - - if (constructor == null) { - throw new IllegalArgumentException("No suitable constructor foundes"); - } - - try { - return (RemoteEntityEvent) constructor.newInstance(baseEntity, 1L, 2L, "Node"); - } catch (final ReflectiveOperationException e) { - fail("Exception should not happen " + e.getMessage()); - } - return null; + @Override + protected Object[] getConstructorParams(final Action baseEntity) { + return new Object[] { baseEntity, 1L, 1L, 2L, "Node" }; } @Override @@ -72,15 +56,19 @@ public class ActionEventTest extends AbstractRemoteEntityEventTest { final AbstractActionEvent event = (AbstractActionEvent) e; assertThat(event.getEntity()).isSameAs(baseEntity); + assertThat(event.getTargetId()).isEqualTo(1L); assertThat(event.getRolloutId()).isEqualTo(1L); + assertThat(event.getRolloutGroupId()).isEqualTo(2L); AbstractActionEvent underTestCreatedEvent = createProtoStuffEvent(event); assertThat(underTestCreatedEvent.getEntity()).isEqualTo(baseEntity); + assertThat(underTestCreatedEvent.getTargetId()).isEqualTo(1L); assertThat(underTestCreatedEvent.getRolloutId()).isEqualTo(1L); assertThat(underTestCreatedEvent.getRolloutGroupId()).isEqualTo(2L); underTestCreatedEvent = createJacksonEvent(event); assertThat(underTestCreatedEvent.getEntity()).isEqualTo(baseEntity); + assertThat(underTestCreatedEvent.getTargetId()).isEqualTo(1L); assertThat(underTestCreatedEvent.getRolloutId()).isEqualTo(1L); assertThat(underTestCreatedEvent.getRolloutGroupId()).isEqualTo(2L); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/event/remote/entity/RolloutGroupEventTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/event/remote/entity/RolloutGroupEventTest.java index f155aaeb3..7a7e64977 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/event/remote/entity/RolloutGroupEventTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/event/remote/entity/RolloutGroupEventTest.java @@ -9,9 +9,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.fail; -import java.lang.reflect.Constructor; import java.util.UUID; import org.eclipse.hawkbit.repository.model.DistributionSet; @@ -47,26 +45,13 @@ public class RolloutGroupEventTest extends AbstractRemoteEntityEventTest createRemoteEvent(final RolloutGroup baseEntity, - final Class> eventType) { + protected int getConstructorParamCount() { + return 3; + } - Constructor constructor = null; - for (final Constructor constructors : eventType.getDeclaredConstructors()) { - if (constructors.getParameterCount() == 3) { - constructor = constructors; - } - } - - if (constructor == null) { - throw new IllegalArgumentException("No suitable constructor foundes"); - } - - try { - return (RemoteEntityEvent) constructor.newInstance(baseEntity, 1L, "Node"); - } catch (final ReflectiveOperationException e) { - fail("Exception should not happen " + e.getMessage()); - } - return null; + @Override + protected Object[] getConstructorParams(final RolloutGroup baseEntity) { + return new Object[] { baseEntity, 1L, "Node" }; } @Override @@ -91,8 +76,8 @@ public class RolloutGroupEventTest extends AbstractRemoteEntityEventTest${project.version} - - - - com.vaadin - vaadin-client - - - com.vaadin.external.gwt - gwt-elemental - - - \ No newline at end of file diff --git a/hawkbit-ui/pom.xml b/hawkbit-ui/pom.xml index b0dbc3ac0..c19f550f2 100644 --- a/hawkbit-ui/pom.xml +++ b/hawkbit-ui/pom.xml @@ -25,7 +25,7 @@ vaadin-maven-plugin ${vaadin.plugin.version} - -Xmx1g -Xss1024k + -Xmx2g -Xss1024k src/main/resources/VAADIN/widgetsets src/main/resources/VAADIN/widgetsets @@ -78,24 +78,6 @@ - - org.apache.maven.plugins - maven-jar-plugin - - - true - - true - true - - - CustomRenderers - 1 - org.eclipse.hawkbit.ui.customrenderers.CustomRendererWidgetSet - - - - @@ -205,14 +187,6 @@ com.vaadin vaadin-themes - - org.vaadin.addons.lazyquerycontainer - vaadin-lazyquerycontainer - - - org.vaadin.addons - flexibleoptiongroup - org.vaadin.alump.distributionbar dbar-addon @@ -233,6 +207,10 @@ com.cronutils cron-utils + + com.github.ben-manes.caffeine + caffeine + @@ -246,11 +224,6 @@ spring-boot-starter-test test - - com.google.gwt.gwtmockito - gwtmockito - test - io.qameta.allure allure-junit4 diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/AbstractHawkbitUI.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/AbstractHawkbitUI.java index 619f92d15..92b0664a4 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/AbstractHawkbitUI.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/AbstractHawkbitUI.java @@ -14,6 +14,7 @@ import org.eclipse.hawkbit.ui.menu.DashboardEvent.PostViewChangeEvent; import org.eclipse.hawkbit.ui.menu.DashboardMenu; import org.eclipse.hawkbit.ui.menu.DashboardMenuItem; import org.eclipse.hawkbit.ui.push.EventPushStrategy; +import org.eclipse.hawkbit.ui.push.UIEventProvider; import org.eclipse.hawkbit.ui.themes.HawkbitTheme; import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil; import org.eclipse.hawkbit.ui.utils.SPDateTimeUtil; @@ -22,7 +23,6 @@ import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; -import org.vaadin.spring.events.EventBus; import org.vaadin.spring.events.EventBus.UIEventBus; import com.vaadin.annotations.Theme; @@ -35,7 +35,6 @@ import com.vaadin.navigator.ViewProvider; import com.vaadin.server.ClientConnector.DetachListener; import com.vaadin.server.Responsive; import com.vaadin.server.VaadinRequest; -import com.vaadin.spring.navigator.SpringNavigator; import com.vaadin.spring.navigator.SpringViewProvider; import com.vaadin.ui.Alignment; import com.vaadin.ui.Component; @@ -61,32 +60,27 @@ public abstract class AbstractHawkbitUI extends UI implements DetachListener { private static final String EMPTY_VIEW = ""; - private transient EventPushStrategy pushStrategy; - - protected final transient EventBus.UIEventBus eventBus; - - private final SpringViewProvider viewProvider; - - private final transient ApplicationContext context; - - private final DashboardMenu dashboardMenu; - - private final ErrorView errorview; - - private final NotificationUnreadButton notificationUnreadButton; + private final VaadinMessageSource i18n; + private final UiProperties uiProperties; private Label viewTitle; - private final UiProperties uiProperties; + private final DashboardMenu dashboardMenu; + private final ErrorView errorview; + private final NotificationUnreadButton notificationUnreadButton; - private final VaadinMessageSource i18n; + private final SpringViewProvider viewProvider; + private final transient ApplicationContext context; + private final transient EventPushStrategy pushStrategy; + + private final transient HawkbitEntityEventListener entityEventsListener; protected AbstractHawkbitUI(final EventPushStrategy pushStrategy, final UIEventBus eventBus, - final SpringViewProvider viewProvider, final ApplicationContext context, final DashboardMenu dashboardMenu, - final ErrorView errorview, final NotificationUnreadButton notificationUnreadButton, - final UiProperties uiProperties, final VaadinMessageSource i18n) { + final UIEventProvider eventProvider, final SpringViewProvider viewProvider, + final ApplicationContext context, final DashboardMenu dashboardMenu, final ErrorView errorview, + final NotificationUnreadButton notificationUnreadButton, final UiProperties uiProperties, + final VaadinMessageSource i18n) { this.pushStrategy = pushStrategy; - this.eventBus = eventBus; this.viewProvider = viewProvider; this.context = context; this.dashboardMenu = dashboardMenu; @@ -94,12 +88,16 @@ public abstract class AbstractHawkbitUI extends UI implements DetachListener { this.notificationUnreadButton = notificationUnreadButton; this.uiProperties = uiProperties; this.i18n = i18n; + + this.entityEventsListener = new HawkbitEntityEventListener(eventBus, eventProvider, notificationUnreadButton); } @Override public void detach(final DetachEvent event) { - LOG.info("ManagementUI is detached uiid - {}", getUIId()); - eventBus.unsubscribe(this); + LOG.debug("ManagementUI is detached uiid - {}", getUIId()); + + entityEventsListener.unsubscribeListeners(); + if (pushStrategy != null) { pushStrategy.clean(); } @@ -107,7 +105,7 @@ public abstract class AbstractHawkbitUI extends UI implements DetachListener { @Override protected void init(final VaadinRequest vaadinRequest) { - LOG.info("ManagementUI init starts uiid - {}", getUI().getUIId()); + LOG.debug("ManagementUI init starts uiid - {}", getUI().getUIId()); if (pushStrategy != null) { pushStrategy.init(getUI()); } @@ -119,6 +117,8 @@ public abstract class AbstractHawkbitUI extends UI implements DetachListener { setResponsive(Boolean.TRUE); final HorizontalLayout rootLayout = new HorizontalLayout(); + rootLayout.setMargin(false); + rootLayout.setSpacing(false); rootLayout.setSizeFull(); HawkbitCommonUtil.initLocalization(this, uiProperties.getLocalization(), i18n); @@ -128,6 +128,8 @@ public abstract class AbstractHawkbitUI extends UI implements DetachListener { dashboardMenu.setResponsive(true); final VerticalLayout contentVerticalLayout = new VerticalLayout(); + contentVerticalLayout.setMargin(false); + contentVerticalLayout.setSpacing(false); contentVerticalLayout.setSizeFull(); contentVerticalLayout.setStyleName("main-content"); contentVerticalLayout.addComponent(buildHeader()); @@ -135,11 +137,11 @@ public abstract class AbstractHawkbitUI extends UI implements DetachListener { final Panel content = buildContent(); contentVerticalLayout.addComponent(content); - contentVerticalLayout.setExpandRatio(content, 1); + contentVerticalLayout.setExpandRatio(content, 1.0F); rootLayout.addComponent(dashboardMenu); rootLayout.addComponent(contentVerticalLayout); - rootLayout.setExpandRatio(contentVerticalLayout, 1); + rootLayout.setExpandRatio(contentVerticalLayout, 1.0F); setContent(rootLayout); final Navigator navigator = new Navigator(this, content); @@ -160,23 +162,22 @@ public abstract class AbstractHawkbitUI extends UI implements DetachListener { return; } viewTitle.setCaption(view.getDashboardCaptionLong()); - notificationUnreadButton.setCurrentView(event.getNewView()); } }); navigator.setErrorView(errorview); + navigator.addView(EMPTY_VIEW, new Navigator.EmptyView()); navigator.addProvider(new ManagementViewProvider()); setNavigator(navigator); - navigator.addView(EMPTY_VIEW, new Navigator.EmptyView()); if (UI.getCurrent().getErrorHandler() == null) { UI.getCurrent().setErrorHandler(new HawkbitUIErrorHandler()); } - LOG.info("Current locale of the application is : {}", getLocale()); + LOG.debug("Current locale of the application is : {}", getLocale()); } - private Panel buildContent() { + private static Panel buildContent() { final Panel content = new Panel(); content.setSizeFull(); content.setStyleName("view-content"); @@ -185,6 +186,8 @@ public abstract class AbstractHawkbitUI extends UI implements DetachListener { private HorizontalLayout buildViewTitle() { final HorizontalLayout viewHeadercontent = new HorizontalLayout(); + viewHeadercontent.setMargin(false); + viewHeadercontent.setSpacing(false); viewHeadercontent.setWidth("100%"); viewHeadercontent.addStyleName("view-header-layout"); @@ -198,14 +201,13 @@ public abstract class AbstractHawkbitUI extends UI implements DetachListener { return viewHeadercontent; } - private Component buildHeader() { + private static Component buildHeader() { final CssLayout cssLayout = new CssLayout(); cssLayout.setStyleName("view-header"); return cssLayout; } - private class ManagementViewProvider extends SpringNavigator implements ViewProvider { - + private class ManagementViewProvider implements ViewProvider { private static final long serialVersionUID = 1L; @Override diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/AppWidgetSet.gwt.xml b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/AppWidgetSet.gwt.xml index ad1e153c2..f17be5817 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/AppWidgetSet.gwt.xml +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/AppWidgetSet.gwt.xml @@ -13,17 +13,12 @@ - - - - - - - - - + + diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/AsyncVaadinServletConfiguration.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/AsyncVaadinServletConfiguration.java index f69e442a8..88c7f43a7 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/AsyncVaadinServletConfiguration.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/AsyncVaadinServletConfiguration.java @@ -34,12 +34,30 @@ import com.vaadin.spring.server.SpringVaadinServlet; @Import(VaadinServletConfiguration.class) public class AsyncVaadinServletConfiguration extends VaadinServletConfiguration { + /** + * Localized system message provider bean. + * + * @param uiProperties + * UiProperties + * @param i18n + * VaadinMessageSource + * + * @return Localized system message provider + */ @Bean public LocalizedSystemMessagesProvider localizedSystemMessagesProvider(final UiProperties uiProperties, final VaadinMessageSource i18n) { return new LocalizedSystemMessagesProvider(uiProperties, i18n); } + /** + * Vaadin servlet bean. + * + * @param localizedSystemMessagesProvider + * LocalizedSystemMessagesProvider + * + * @return Vaadin servlet service + */ @Bean public VaadinServlet vaadinServlet(final LocalizedSystemMessagesProvider localizedSystemMessagesProvider) { return new SpringVaadinServlet() { diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/ErrorView.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/ErrorView.java index 0ad65009d..bdd3cae62 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/ErrorView.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/ErrorView.java @@ -11,19 +11,16 @@ package org.eclipse.hawkbit.ui; import org.eclipse.hawkbit.ui.menu.DashboardMenu; import org.eclipse.hawkbit.ui.menu.DashboardMenuItem; import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions; +import org.eclipse.hawkbit.ui.utils.UINotification; import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; import org.springframework.beans.factory.annotation.Autowired; import com.vaadin.navigator.Navigator; import com.vaadin.navigator.View; import com.vaadin.navigator.ViewChangeListener; -import com.vaadin.shared.Position; import com.vaadin.spring.annotation.SpringComponent; import com.vaadin.spring.annotation.UIScope; import com.vaadin.ui.Label; -import com.vaadin.ui.Notification; -import com.vaadin.ui.Notification.Type; -import com.vaadin.ui.UI; import com.vaadin.ui.VerticalLayout; /** @@ -49,6 +46,8 @@ public class ErrorView extends VerticalLayout implements View { this.i18n = i18n; this.dashboardMenu = dashboardMenu; setMargin(true); + setSpacing(false); + message = new Label(); addComponent(message); } @@ -61,11 +60,9 @@ public class ErrorView extends VerticalLayout implements View { return; } if (dashboardMenu.isAccessDenied(event.getViewName())) { - final Notification nt = new Notification("Access denied", - i18n.getMessage("message.accessdenied.view", event.getViewName()), Type.ERROR_MESSAGE, false); - nt.setStyleName(SPUIStyleDefinitions.SP_NOTIFICATION_ERROR_MESSAGE_STYLE); - nt.setPosition(Position.BOTTOM_RIGHT); - nt.show(UI.getCurrent().getPage()); + UINotification.showNotification(SPUIStyleDefinitions.SP_NOTIFICATION_ERROR_MESSAGE_STYLE, + i18n.getMessage("message.accessdenied"), + i18n.getMessage("message.accessdenied.view", event.getViewName()), null, true); message.setValue(i18n.getMessage("message.accessdenied.view", event.getViewName())); } } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/HawkbitEntityEventListener.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/HawkbitEntityEventListener.java new file mode 100644 index 000000000..57999a22e --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/HawkbitEntityEventListener.java @@ -0,0 +1,176 @@ +/** + * Copyright (c) 2020 Bosch.IO 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; + +import static java.util.concurrent.TimeUnit.SECONDS; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.hawkbit.ui.common.event.EntityModifiedEventPayload; +import org.eclipse.hawkbit.ui.common.event.EntityModifiedEventPayloadIdentifier; +import org.eclipse.hawkbit.ui.common.event.EventTopics; +import org.eclipse.hawkbit.ui.components.NotificationUnreadButton; +import org.eclipse.hawkbit.ui.push.UIEventProvider; +import org.springframework.util.CollectionUtils; +import org.vaadin.spring.events.Event; +import org.vaadin.spring.events.EventBus.UIEventBus; +import org.vaadin.spring.events.EventBusListenerMethodFilter; +import org.vaadin.spring.events.EventScope; +import org.vaadin.spring.events.annotation.EventBusListenerMethod; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.vaadin.ui.UI; + +/** + * Listener for internal and remote entity modified events. Keeps a cache of the + * events coming from UI in order to suppress the corresponding remote events. + */ +public class HawkbitEntityEventListener { + private final UIEventBus eventBus; + private final UIEventProvider eventProvider; + private final NotificationUnreadButton notificationUnreadButton; + + private final Cache> uiOriginatedEventsCache; + private final List eventListeners; + + HawkbitEntityEventListener(final UIEventBus eventBus, final UIEventProvider eventProvider, + final NotificationUnreadButton notificationUnreadButton) { + this.eventBus = eventBus; + this.eventProvider = eventProvider; + this.notificationUnreadButton = notificationUnreadButton; + + this.uiOriginatedEventsCache = Caffeine.newBuilder().expireAfterWrite(10, SECONDS).build(); + + this.eventListeners = new ArrayList<>(); + registerEventListeners(); + } + + private void registerEventListeners() { + eventListeners.add(new EntityModifiedListener()); + eventListeners.add(new RemoteEventListener()); + } + + private class EntityModifiedListener { + + /** + * Constructor for EntityModifiedListener + */ + public EntityModifiedListener() { + eventBus.subscribe(this, EventTopics.ENTITY_MODIFIED); + } + + @EventBusListenerMethod(scope = EventScope.UI, filter = IgnoreRemoteEventsFilter.class) + private void onEntityModifiedEvent(final EntityModifiedEventPayload eventPayload) { + // parentId is ignored here because entityIds should be unique + uiOriginatedEventsCache.asMap().merge(EntityModifiedEventPayloadIdentifier.of(eventPayload), + eventPayload.getEntityIds(), (oldEntityIds, newEntityIds) -> Stream + .concat(oldEntityIds.stream(), newEntityIds.stream()).collect(Collectors.toList())); + } + } + + /** + * Ignore remote events filter + */ + public static class IgnoreRemoteEventsFilter implements EventBusListenerMethodFilter { + + @Override + public boolean filter(final Event event) { + return !event.getSource().equals(UI.getCurrent()); + } + } + + private class RemoteEventListener { + + /** + * Constructor for RemoteEventListener + */ + public RemoteEventListener() { + eventBus.subscribe(this, EventTopics.REMOTE_EVENT_RECEIVED); + } + + @EventBusListenerMethod(scope = EventScope.UI) + private void onRemoteEventReceived(final EntityModifiedEventPayload eventPayload) { + if (eventPayload.getEntityType() == null || eventPayload.getEntityModifiedEventType() == null + || CollectionUtils.isEmpty(eventPayload.getEntityIds())) { + return; + } + + getEventPayloadIdentifierFromProvider(eventPayload).ifPresent(eventPayloadIdentifier -> { + final Collection remotelyModifiedEntityIds = getRemotelyModifiedEntityIds(eventPayloadIdentifier, + eventPayload.getEntityIds()); + + if (!remotelyModifiedEntityIds.isEmpty()) { + final EntityModifiedEventPayload remoteEventPayload = EntityModifiedEventPayload + .of(eventPayloadIdentifier, eventPayload.getParentId(), remotelyModifiedEntityIds); + + if (eventPayloadIdentifier.shouldBeDeffered()) { + notificationUnreadButton.incrementUnreadNotification( + eventPayloadIdentifier.getEventTypeMessageKey(), remoteEventPayload); + } else { + eventBus.publish(EventTopics.ENTITY_MODIFIED, UI.getCurrent(), remoteEventPayload); + } + } + }); + } + + private Optional getEventPayloadIdentifierFromProvider( + final EntityModifiedEventPayload eventPayload) { + return eventProvider.getEvents().values().stream().filter(providedIdentifier -> providedIdentifier + .equals(EntityModifiedEventPayloadIdentifier.of(eventPayload))).findAny(); + } + + private Collection getRemotelyModifiedEntityIds( + final EntityModifiedEventPayloadIdentifier eventPayloadIdentifier, + final Collection remoteEventEntityIds) { + final Collection cachedEventEntityIds = uiOriginatedEventsCache.getIfPresent(eventPayloadIdentifier); + + if (CollectionUtils.isEmpty(cachedEventEntityIds)) { + return remoteEventEntityIds; + } + + final Collection commonEntityIds = getCommonEntityIds(cachedEventEntityIds, remoteEventEntityIds); + if (!commonEntityIds.isEmpty()) { + updateCache(eventPayloadIdentifier, cachedEventEntityIds, commonEntityIds); + remoteEventEntityIds.removeAll(commonEntityIds); + } + + return remoteEventEntityIds; + } + + private Collection getCommonEntityIds(final Collection cachedEventEntityIds, + final Collection remoteEventEntityIds) { + final List commonEntityIds = new ArrayList<>(cachedEventEntityIds); + commonEntityIds.retainAll(remoteEventEntityIds); + + return commonEntityIds; + } + + private void updateCache(final EntityModifiedEventPayloadIdentifier eventPayloadIdentifier, + final Collection cachedEventEntityIds, final Collection commonEntityIds) { + final List updatedCachedEventEntityIds = new ArrayList<>(cachedEventEntityIds); + updatedCachedEventEntityIds.removeAll(commonEntityIds); + + if (updatedCachedEventEntityIds.isEmpty()) { + uiOriginatedEventsCache.invalidate(eventPayloadIdentifier); + } else { + uiOriginatedEventsCache.put(eventPayloadIdentifier, updatedCachedEventEntityIds); + } + } + } + + void unsubscribeListeners() { + eventListeners.forEach(eventBus::unsubscribe); + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/LocalizedSystemMessagesProvider.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/LocalizedSystemMessagesProvider.java index c45c8d48d..c8b1986ab 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/LocalizedSystemMessagesProvider.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/LocalizedSystemMessagesProvider.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2019 Bosch Software Innovations GmbH and others. + * Copyright (c) 2020 Bosch.IO 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 @@ -30,6 +30,8 @@ public class LocalizedSystemMessagesProvider implements SystemMessagesProvider { private final UiProperties uiProperties; /** + * Constructor for LocalizedSystemMessagesProvider + * * @param uiProperties * Properties to determine the available Locales * @param i18n diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/SpPermissionChecker.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/SpPermissionChecker.java index 15f290a71..9093c7d2d 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/SpPermissionChecker.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/SpPermissionChecker.java @@ -80,6 +80,15 @@ public class SpPermissionChecker implements Serializable { return permissionService.hasPermission(SpPermission.READ_REPOSITORY); } + /** + * Has the download Repository Artifact Permission. + * + * @return DOWNLOAD_REPOSITORY_ARTIFACT boolean value + */ + public boolean hasDownloadRepositoryPermission() { + return permissionService.hasPermission(SpPermission.DOWNLOAD_REPOSITORY_ARTIFACT); + } + /** * Gets the create Repository Permission. * diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/UiProperties.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/UiProperties.java index 2b2e8595a..c342b077f 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/UiProperties.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/UiProperties.java @@ -32,24 +32,40 @@ public class UiProperties implements Serializable { private final Links links = new Links(); - private final Login login = new Login(); - private final Demo demo = new Demo(); private final Event event = new Event(); + /** + * @return True if menu item has gravatar else false + */ public boolean isGravatar() { return gravatar; } + /** + * Sets the gravatar + * + * @param gravatar + * Menu icon + */ public void setGravatar(final boolean gravatar) { this.gravatar = gravatar; } + /** + * @return Fixed time zone if unknown then GMT + */ public String getFixedTimeZone() { return fixedTimeZone; } + /** + * Sets the fixed time zone + * + * @param fixedTimeZone + * Date time zone + */ public void setFixedTimeZone(final String fixedTimeZone) { this.fixedTimeZone = fixedTimeZone; } @@ -70,18 +86,36 @@ public class UiProperties implements Serializable { */ private List availableLocals = Collections.singletonList(Locale.ENGLISH); + /** + * @return Default locale + */ public Locale getDefaultLocal() { return defaultLocal; } + /** + * @return List of available locale + */ public List getAvailableLocals() { return availableLocals; } + /** + * Sets the default locale + * + * @param defaultLocal + * Locale + */ public void setDefaultLocal(final Locale defaultLocal) { this.defaultLocal = defaultLocal; } + /** + * Sets the all available locale + * + * @param availableLocals + * List of locale + */ public void setAvailableLocals(final List availableLocals) { this.availableLocals = availableLocals; } @@ -112,34 +146,70 @@ public class UiProperties implements Serializable { private String disclaimer = ""; + /** + * @return password + */ public String getPassword() { return password; } + /** + * @return tenant + */ public String getTenant() { return tenant; } + /** + * @return username + */ public String getUser() { return user; } + /** + * Sets the login password + * + * @param password + * Password value + */ public void setPassword(final String password) { this.password = password; } + /** + * Sets the tenant + * + * @param tenant + * Tenant value + */ public void setTenant(final String tenant) { this.tenant = tenant; } + /** + * Sets the login user + * + * @param user + * username + */ public void setUser(final String user) { this.user = user; } + /** + * @return disclaimer + */ public String getDisclaimer() { return disclaimer; } + /** + * Sets the disclaimer + * + * @param disclaimer + * Disclaimer value + */ public void setDisclaimer(final String disclaimer) { this.disclaimer = disclaimer; } @@ -211,6 +281,9 @@ public class UiProperties implements Serializable { private String provisioningStateMachine = ""; + /** + * @return Link to documentation of deployment view + */ public String getDeploymentView() { return deploymentView; } @@ -219,26 +292,44 @@ public class UiProperties implements Serializable { return distributionView; } + /** + * @return Link to documentation of rollout view + */ public String getRolloutView() { return rolloutView; } + /** + * @return Link to documentation of root + */ public String getRoot() { return root; } + /** + * @return Link to documentation of security + */ public String getSecurity() { return security; } + /** + * @return Link to documentation of rollout + */ public String getRollout() { return rollout; } + /** + * @return Link to documentation of system config + */ public String getSystemConfigurationView() { return systemConfigurationView; } + /** + * @return Link to documentation of target filter + */ public String getTargetfilterView() { return targetfilterView; } @@ -247,10 +338,16 @@ public class UiProperties implements Serializable { return uploadView; } + /** + * @return Link to documentation of maintenance window + */ public String getMaintenanceWindowView() { return maintenanceWindowView; } + /** + * @return Link to documentation of provisioning state machine + */ public String getProvisioningStateMachine() { return provisioningStateMachine; } @@ -267,14 +364,32 @@ public class UiProperties implements Serializable { this.rolloutView = rolloutView; } + /** + * Sets the root documentation link + * + * @param root + * link + */ public void setRoot(final String root) { this.root = root; } + /** + * Sets the security documentation link + * + * @param security + * link + */ public void setSecurity(final String security) { this.security = security; } + /** + * Sets the rollout documentation link + * + * @param rollout + * link + */ public void setRollout(final String rollout) { this.rollout = rollout; } @@ -318,70 +433,66 @@ public class UiProperties implements Serializable { */ private String userManagement = ""; + /** + * @return Link to documentation + */ public Documentation getDocumentation() { return documentation; } + /** + * @return Link to request a system account, access + */ public String getRequestAccount() { return requestAccount; } + /** + * @return Link to product support + */ public String getSupport() { return support; } + /** + * @return Link to user management + */ public String getUserManagement() { return userManagement; } + /** + * Sets the link to request a system account, access + * + * @param requestAccount + * Link + */ public void setRequestAccount(final String requestAccount) { this.requestAccount = requestAccount; } + /** + * Sets the link to product support + * + * @param support + * Link + */ public void setSupport(final String support) { this.support = support; } + /** + * Sets the link to user management + * + * @param userManagement + * Link + */ public void setUserManagement(final String userManagement) { this.userManagement = userManagement; } } - /** - * Configuration of login view. - * - */ - public static class Login implements Serializable { - private static final long serialVersionUID = 1L; - - /** - * Cookie configuration for login credential cookie. - * - */ - public static class Cookie implements Serializable { - private static final long serialVersionUID = 1L; - /** - * Secure cookie enabled. - */ - private boolean secure = true; - - public boolean isSecure() { - return secure; - } - - public void setSecure(final boolean secure) { - this.secure = secure; - } - } - - private final Cookie cookie = new Cookie(); - - public Cookie getCookie() { - return cookie; - } - } - /** * Configuration of the UI event bus. */ @@ -417,22 +528,30 @@ public class UiProperties implements Serializable { } } + /** + * @return Demo account details + */ public Demo getDemo() { return demo; } + /** + * @return Document links + */ public Links getLinks() { return links; } - public Login getLogin() { - return login; - } - + /** + * @return Events + */ public Event getEvent() { return event; } + /** + * @return Localization + */ public Localization getLocalization() { return localization; } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/state/ArtifactUploadState.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/ArtifactUploadState.java similarity index 68% rename from hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/state/ArtifactUploadState.java rename to hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/ArtifactUploadState.java index 5b960cb55..e1663ffd9 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/state/ArtifactUploadState.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/ArtifactUploadState.java @@ -6,28 +6,26 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.hawkbit.ui.artifacts.state; +package org.eclipse.hawkbit.ui.artifacts; import java.io.File; import java.io.Serializable; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; import java.util.Map; -import java.util.Optional; import java.util.Set; import org.apache.commons.io.FileUtils; -import org.apache.commons.lang3.StringUtils; import org.eclipse.hawkbit.repository.model.SoftwareModule; +import org.eclipse.hawkbit.ui.artifacts.details.ArtifactDetailsGridLayoutUiState; import org.eclipse.hawkbit.ui.artifacts.upload.FileUploadId; import org.eclipse.hawkbit.ui.artifacts.upload.FileUploadProgress; import org.eclipse.hawkbit.ui.artifacts.upload.FileUploadProgress.FileUploadStatus; -import org.eclipse.hawkbit.ui.common.ManagementEntityState; +import org.eclipse.hawkbit.ui.common.state.GridLayoutUiState; +import org.eclipse.hawkbit.ui.common.state.TypeFilterLayoutUiState; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.StringUtils; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -39,29 +37,14 @@ import com.vaadin.spring.annotation.VaadinSessionScope; */ @VaadinSessionScope @SpringComponent -public class ArtifactUploadState implements ManagementEntityState, Serializable { - +public class ArtifactUploadState implements Serializable { private static final long serialVersionUID = 1L; private static final Logger LOG = LoggerFactory.getLogger(ArtifactUploadState.class); - private final SoftwareModuleFilters softwareModuleFilters; - - private final Map deleteSofwareModules = new HashMap<>(); - - private transient Optional selectedBaseSwModuleId = Optional.empty(); - - private Set selectedSoftwareModules = Collections.emptySet(); - - private boolean swTypeFilterClosed; - - private boolean swModuleTableMaximized; - - private boolean artifactDetailsMaximized; - - private final Set selectedDeleteSWModuleTypes = new HashSet<>(); - - private boolean noDataAvilableSoftwareModule; + private final TypeFilterLayoutUiState smTypeFilterLayoutUiState; + private final GridLayoutUiState smGridLayoutUiState; + private final ArtifactDetailsGridLayoutUiState artifactDetailsGridLayoutUiState; private boolean statusPopupMinimized; @@ -71,125 +54,163 @@ public class ArtifactUploadState implements ManagementEntityState, Serializable */ private Map overallFilesInUploadProcess; - @Autowired - ArtifactUploadState(final SoftwareModuleFilters softwareModuleFilters) { - this.softwareModuleFilters = softwareModuleFilters; + /** + * Constructor for ArtifactUploadState + */ + public ArtifactUploadState() { + this.smTypeFilterLayoutUiState = new TypeFilterLayoutUiState(); + this.smGridLayoutUiState = new GridLayoutUiState(); + this.artifactDetailsGridLayoutUiState = new ArtifactDetailsGridLayoutUiState(); } + /** + * Minimize or maximize the status popup view + * + * @param statusPopupMinimized + * boolean + */ public void setStatusPopupMinimized(final boolean statusPopupMinimized) { this.statusPopupMinimized = statusPopupMinimized; } + /** + * Checks if the status popup view is in minimized state + * + * @return boolean + */ public boolean isStatusPopupMinimized() { return statusPopupMinimized; } - public SoftwareModuleFilters getSoftwareModuleFilters() { - return softwareModuleFilters; + /** + * Get the Software module type filter UI state + * + * @return TypeFilterLayoutUiState + */ + public TypeFilterLayoutUiState getSmTypeFilterLayoutUiState() { + return smTypeFilterLayoutUiState; } - public Map getDeleteSoftwareModules() { - return deleteSofwareModules; + /** + * Get the Software module grid UI state + * + * @return GridLayoutUiState + */ + public GridLayoutUiState getSmGridLayoutUiState() { + return smGridLayoutUiState; } - public Optional getSelectedBaseSwModuleId() { - return selectedBaseSwModuleId; - } - - public Set getSelectedSoftwareModules() { - return selectedSoftwareModules; - } - - @Override - public void setLastSelectedEntityId(final Long value) { - this.selectedBaseSwModuleId = Optional.ofNullable(value); - } - - @Override - public void setSelectedEnitities(final Set values) { - this.selectedSoftwareModules = values; - } - - public boolean isSwTypeFilterClosed() { - return swTypeFilterClosed; - } - - public void setSwTypeFilterClosed(final boolean swTypeFilterClosed) { - this.swTypeFilterClosed = swTypeFilterClosed; - } - - public boolean isSwModuleTableMaximized() { - return swModuleTableMaximized; - } - - public void setSwModuleTableMaximized(final boolean swModuleTableMaximized) { - this.swModuleTableMaximized = swModuleTableMaximized; - } - - public Set getSelectedDeleteSWModuleTypes() { - return selectedDeleteSWModuleTypes; - } - - public boolean isArtifactDetailsMaximized() { - return artifactDetailsMaximized; - } - - public void setArtifactDetailsMaximized(final boolean artifactDetailsMaximized) { - this.artifactDetailsMaximized = artifactDetailsMaximized; - } - - public boolean isNoDataAvilableSoftwareModule() { - return noDataAvilableSoftwareModule; - } - - public void setNoDataAvilableSoftwareModule(final boolean noDataAvilableSoftwareModule) { - this.noDataAvilableSoftwareModule = noDataAvilableSoftwareModule; - } - - public boolean isMoreThanOneSoftwareModulesSelected() { - return getSelectedSoftwareModules().size() > 1; - } - - public boolean isNoSoftwareModuleSelected() { - return !getSelectedBaseSwModuleId().isPresent(); + /** + * Get the Artifact details grid UI state + * + * @return ArtifactDetailsGridLayoutUiState + */ + public ArtifactDetailsGridLayoutUiState getArtifactDetailsGridLayoutUiState() { + return artifactDetailsGridLayoutUiState; } + /** + * Remove all the files from the upload process list + * + * @param filesToRemove + * Collection of fie upload ID + */ public void removeFilesFromOverallUploadProcessList(final Collection filesToRemove) { getOverallFilesInUploadProcessMap().keySet().removeAll(filesToRemove); } + /** + * Get all the IDs of uploaded files from the upload process + * + * @return List of IDs of uploaded files + */ public Set getAllFileUploadIdsFromOverallUploadProcessList() { return Collections.unmodifiableSet(getOverallFilesInUploadProcessMap().keySet()); } + /** + * Get file upload progress values from process list + * + * @return List of FileUploadProgress + */ public Collection getAllFileUploadProgressValuesFromOverallUploadProcessList() { return Collections.unmodifiableCollection(getOverallFilesInUploadProcessMap().values()); } + /** + * Get all the IDs of files from the failed upload + * + * @return List of IDs of uploaded files + */ public Set getFilesInFailedState() { return Collections.unmodifiableSet(getFailedUploads()); } + /** + * Get upload progress of the file + * + * @param fileUploadId + * FileUploadId + * + * @return FileUploadProgress + */ public FileUploadProgress getFileUploadProgress(final FileUploadId fileUploadId) { return getOverallFilesInUploadProcessMap().get(fileUploadId); } + /** + * Get all files that were selected for upload + * + * @param fileUploadId + * FileUploadId + * @param fileUploadProgress + * FileUploadProgress + */ public void updateFileUploadProgress(final FileUploadId fileUploadId, final FileUploadProgress fileUploadProgress) { getOverallFilesInUploadProcessMap().put(fileUploadId, fileUploadProgress); } + /** + * Check upload state of the file + * + * @param fileUploadId + * FileUploadId + * + * @return boolean + */ public boolean isFileInUploadState(final FileUploadId fileUploadId) { return getOverallFilesInUploadProcessMap().containsKey(fileUploadId); } + /** + * Check upload state of the file link to the related software module + * + * @param filename + * Name of the file + * + * @param softwareModule + * the {@link SoftwareModule} for which the file is uploaded + * + * @return boolean + */ public boolean isFileInUploadState(final String filename, final SoftwareModule softwareModule) { return isFileInUploadState(new FileUploadId(filename, softwareModule)); } + /** + * Check if at least one file upload is in progress + * + * @return boolean + */ public boolean isAtLeastOneUploadInProgress() { return getInProgressCount() > 0; } + /** + * Check if all file uploads are finished + * + * @return boolean + */ public boolean areAllUploadsFinished() { return getInProgressCount() == 0; } @@ -223,7 +244,10 @@ public class ArtifactUploadState implements ManagementEntityState, Serializable return buffer.toString(); } - void clearFileStates() { + /** + * Removes all of the data from this Upload process collection + */ + public void clearFileStates() { getOverallFilesInUploadProcessMap().clear(); } @@ -234,7 +258,7 @@ public class ArtifactUploadState implements ManagementEntityState, Serializable LOG.debug("Cleaning up temp data..."); // delete file system zombies for (final FileUploadProgress fileUploadProgress : getAllFileUploadProgressValuesFromOverallUploadProcessList()) { - if (!StringUtils.isBlank(fileUploadProgress.getFilePath())) { + if (StringUtils.hasText(fileUploadProgress.getFilePath())) { final boolean deleted = FileUtils.deleteQuietly(new File(fileUploadProgress.getFilePath())); if (!deleted) { LOG.warn("TempFile was not deleted: {}", fileUploadProgress.getFilePath()); @@ -249,6 +273,7 @@ public class ArtifactUploadState implements ManagementEntityState, Serializable * * @param softwareModuleId * id of the software module + * * @return boolean */ public boolean isUploadInProgressForSelectedSoftwareModule(final Long softwareModuleId) { @@ -290,5 +315,4 @@ public class ArtifactUploadState implements ManagementEntityState, Serializable } return succeededFileUploads; } - } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/UploadArtifactView.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/UploadArtifactView.java index c5aae1a3a..f3c722752 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/UploadArtifactView.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/UploadArtifactView.java @@ -8,6 +8,9 @@ */ package org.eclipse.hawkbit.ui.artifacts; +import java.util.EnumMap; +import java.util.Map; + import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.servlet.MultipartConfigElement; @@ -18,34 +21,29 @@ import org.eclipse.hawkbit.repository.SoftwareModuleManagement; import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement; import org.eclipse.hawkbit.ui.AbstractHawkbitUI; import org.eclipse.hawkbit.ui.SpPermissionChecker; -import org.eclipse.hawkbit.ui.artifacts.details.ArtifactDetailsLayout; -import org.eclipse.hawkbit.ui.artifacts.event.ArtifactDetailsEvent; -import org.eclipse.hawkbit.ui.artifacts.event.SoftwareModuleEvent; -import org.eclipse.hawkbit.ui.artifacts.smtable.SoftwareModuleTableLayout; -import org.eclipse.hawkbit.ui.artifacts.smtype.filter.SMTypeFilterButtons; +import org.eclipse.hawkbit.ui.artifacts.details.ArtifactDetailsGridLayout; +import org.eclipse.hawkbit.ui.artifacts.smtable.SoftwareModuleGridLayout; import org.eclipse.hawkbit.ui.artifacts.smtype.filter.SMTypeFilterLayout; -import org.eclipse.hawkbit.ui.artifacts.state.ArtifactUploadState; -import org.eclipse.hawkbit.ui.artifacts.upload.UploadDropAreaLayout; -import org.eclipse.hawkbit.ui.common.table.BaseEntityEventType; -import org.eclipse.hawkbit.ui.dd.criteria.UploadViewClientCriterion; +import org.eclipse.hawkbit.ui.common.event.EventLayout; +import org.eclipse.hawkbit.ui.common.event.EventView; +import org.eclipse.hawkbit.ui.common.event.EventViewAware; +import org.eclipse.hawkbit.ui.common.layout.listener.LayoutResizeListener; +import org.eclipse.hawkbit.ui.common.layout.listener.LayoutResizeListener.ResizeHandler; +import org.eclipse.hawkbit.ui.common.layout.listener.LayoutVisibilityListener; +import org.eclipse.hawkbit.ui.common.layout.listener.LayoutVisibilityListener.VisibilityHandler; import org.eclipse.hawkbit.ui.utils.SPUIDefinitions; import org.eclipse.hawkbit.ui.utils.UINotification; import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; import org.springframework.beans.factory.annotation.Autowired; -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.navigator.View; -import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent; import com.vaadin.server.Page; import com.vaadin.server.Page.BrowserWindowResizeEvent; import com.vaadin.server.Page.BrowserWindowResizeListener; import com.vaadin.spring.annotation.SpringView; import com.vaadin.spring.annotation.UIScope; -import com.vaadin.ui.Alignment; -import com.vaadin.ui.GridLayout; +import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.VerticalLayout; /** @@ -54,202 +52,208 @@ import com.vaadin.ui.VerticalLayout; @UIScope @SpringView(name = UploadArtifactView.VIEW_NAME, ui = AbstractHawkbitUI.class) public class UploadArtifactView extends VerticalLayout implements View, BrowserWindowResizeListener { - private static final long serialVersionUID = 1L; public static final String VIEW_NAME = "spUpload"; - private final transient EventBus.UIEventBus eventBus; - private final SpPermissionChecker permChecker; - - private final VaadinMessageSource i18n; - - private final UINotification uiNotification; - private final ArtifactUploadState artifactUploadState; - private final SMTypeFilterLayout filterByTypeLayout; + private final SMTypeFilterLayout smTypeFilterLayout; + private final SoftwareModuleGridLayout smGridLayout; + private final ArtifactDetailsGridLayout artifactDetailsGridLayout; - private final SoftwareModuleTableLayout smTableLayout; + private HorizontalLayout mainLayout; - private final ArtifactDetailsLayout artifactDetailsLayout; - - private final UploadDropAreaLayout dropAreaLayout; - - private VerticalLayout detailAndUploadLayout; - - private GridLayout mainLayout; + private final transient LayoutVisibilityListener layoutVisibilityListener; + private final transient LayoutResizeListener layoutResizeListener; @Autowired UploadArtifactView(final UIEventBus eventBus, final SpPermissionChecker permChecker, final VaadinMessageSource i18n, final UINotification uiNotification, final ArtifactUploadState artifactUploadState, final EntityFactory entityFactory, final SoftwareModuleManagement softwareModuleManagement, final SoftwareModuleTypeManagement softwareModuleTypeManagement, - final UploadViewClientCriterion uploadViewClientCriterion, final MultipartConfigElement multipartConfigElement, final ArtifactManagement artifactManagement) { - this.eventBus = eventBus; this.permChecker = permChecker; - this.i18n = i18n; - this.uiNotification = uiNotification; this.artifactUploadState = artifactUploadState; - this.smTableLayout = new SoftwareModuleTableLayout(i18n, permChecker, artifactUploadState, uiNotification, - eventBus, softwareModuleManagement, softwareModuleTypeManagement, entityFactory, - uploadViewClientCriterion); - this.artifactDetailsLayout = new ArtifactDetailsLayout(i18n, eventBus, artifactUploadState, uiNotification, - artifactManagement, softwareModuleManagement); - final SMTypeFilterButtons smTypeFilterButtons = new SMTypeFilterButtons(eventBus, artifactUploadState, - uploadViewClientCriterion, softwareModuleTypeManagement, i18n, entityFactory, permChecker, - uiNotification); - this.filterByTypeLayout = new SMTypeFilterLayout(artifactUploadState, i18n, permChecker, eventBus, - entityFactory, uiNotification, softwareModuleTypeManagement, smTypeFilterButtons); - this.dropAreaLayout = new UploadDropAreaLayout(i18n, eventBus, uiNotification, artifactUploadState, - multipartConfigElement, softwareModuleManagement, artifactManagement); + + if (permChecker.hasReadRepositoryPermission()) { + this.smTypeFilterLayout = new SMTypeFilterLayout(i18n, permChecker, eventBus, entityFactory, uiNotification, + softwareModuleTypeManagement, artifactUploadState.getSmTypeFilterLayoutUiState()); + this.smGridLayout = new SoftwareModuleGridLayout(i18n, permChecker, uiNotification, eventBus, + softwareModuleManagement, softwareModuleTypeManagement, entityFactory, + artifactUploadState.getSmTypeFilterLayoutUiState(), artifactUploadState.getSmGridLayoutUiState()); + this.artifactDetailsGridLayout = new ArtifactDetailsGridLayout(i18n, eventBus, permChecker, uiNotification, + artifactUploadState, artifactUploadState.getArtifactDetailsGridLayoutUiState(), artifactManagement, + softwareModuleManagement, multipartConfigElement); + + final Map layoutVisibilityHandlers = new EnumMap<>(EventLayout.class); + layoutVisibilityHandlers.put(EventLayout.SM_TYPE_FILTER, + new VisibilityHandler(this::showSmTypeLayout, this::hideSmTypeLayout)); + this.layoutVisibilityListener = new LayoutVisibilityListener(eventBus, new EventViewAware(EventView.UPLOAD), + layoutVisibilityHandlers); + + final Map layoutResizeHandlers = new EnumMap<>(EventLayout.class); + layoutResizeHandlers.put(EventLayout.SM_LIST, + new ResizeHandler(this::maximizeSmGridLayout, this::minimizeSmGridLayout)); + layoutResizeHandlers.put(EventLayout.ARTIFACT_LIST, + new ResizeHandler(this::maximizeArtifactGridLayout, this::minimizeArtifactGridLayout)); + this.layoutResizeListener = new LayoutResizeListener(eventBus, new EventViewAware(EventView.UPLOAD), + layoutResizeHandlers); + } else { + this.smTypeFilterLayout = null; + this.smGridLayout = null; + this.artifactDetailsGridLayout = null; + this.layoutVisibilityListener = null; + this.layoutResizeListener = null; + } } @PostConstruct void init() { - buildLayout(); - restoreState(); - checkNoDataAvaialble(); - eventBus.subscribe(this); - Page.getCurrent().addBrowserWindowResizeListener(this); - showOrHideFilterButtons(Page.getCurrent().getBrowserWindowWidth()); - } - - @PreDestroy - void destroy() { - eventBus.unsubscribe(this); - } - - @EventBusListenerMethod(scope = EventScope.UI) - void onEvent(final SoftwareModuleEvent event) { - if (BaseEntityEventType.MINIMIZED == event.getEventType()) { - minimizeSwTable(); - } else if (BaseEntityEventType.MAXIMIZED == event.getEventType()) { - maximizeSwTable(); - } - } - - @EventBusListenerMethod(scope = EventScope.UI) - void onEvent(final ArtifactDetailsEvent event) { - if (event == ArtifactDetailsEvent.MINIMIZED) { - minimizeArtifactoryDetails(); - } else if (event == ArtifactDetailsEvent.MAXIMIZED) { - maximizeArtifactoryDetails(); - } - } - - private void restoreState() { - if (artifactUploadState.isSwModuleTableMaximized()) { - maximizeSwTable(); - } - if (artifactUploadState.isArtifactDetailsMaximized()) { - maximizeArtifactoryDetails(); + if (permChecker.hasReadRepositoryPermission()) { + buildLayout(); + restoreState(); + Page.getCurrent().addBrowserWindowResizeListener(this); } } private void buildLayout() { - if (permChecker.hasReadRepositoryPermission() || permChecker.hasCreateRepositoryPermission()) { - setSizeFull(); - createMainLayout(); - addComponents(mainLayout); - setExpandRatio(mainLayout, 1); - } + setMargin(false); + setSpacing(false); + setSizeFull(); + + createMainLayout(); + + addComponent(mainLayout); + setExpandRatio(mainLayout, 1.0F); } - private VerticalLayout createDetailsAndUploadLayout() { - detailAndUploadLayout = new VerticalLayout(); - detailAndUploadLayout.addComponent(artifactDetailsLayout); - detailAndUploadLayout.setComponentAlignment(artifactDetailsLayout, Alignment.TOP_CENTER); - detailAndUploadLayout.setExpandRatio(artifactDetailsLayout, 1.0F); - - if (permChecker.hasCreateRepositoryPermission()) { - detailAndUploadLayout.addComponent(dropAreaLayout.getDropAreaWrapper()); - } - - detailAndUploadLayout.setSizeFull(); - detailAndUploadLayout.addStyleName("group"); - detailAndUploadLayout.setSpacing(true); - return detailAndUploadLayout; - } - - private GridLayout createMainLayout() { - createDetailsAndUploadLayout(); - mainLayout = new GridLayout(3, 1); + private void createMainLayout() { + mainLayout = new HorizontalLayout(); mainLayout.setSizeFull(); + mainLayout.setMargin(false); mainLayout.setSpacing(true); - mainLayout.setStyleName("fullSize"); - mainLayout.addComponent(filterByTypeLayout, 0, 0); - mainLayout.addComponent(smTableLayout, 1, 0); - mainLayout.addComponent(detailAndUploadLayout, 2, 0); - mainLayout.setRowExpandRatio(0, 1.0F); - mainLayout.setColumnExpandRatio(1, 0.5F); - mainLayout.setColumnExpandRatio(2, 0.5F); + mainLayout.addComponent(smTypeFilterLayout); + mainLayout.addComponent(smGridLayout); + mainLayout.addComponent(artifactDetailsGridLayout); - return mainLayout; + mainLayout.setExpandRatio(smTypeFilterLayout, 0F); + mainLayout.setExpandRatio(smGridLayout, 0.5F); + mainLayout.setExpandRatio(artifactDetailsGridLayout, 0.5F); } - private void minimizeSwTable() { - mainLayout.addComponent(detailAndUploadLayout, 2, 0); - addOtherComponents(); - } - - private void maximizeSwTable() { - mainLayout.removeComponent(detailAndUploadLayout); - mainLayout.setColumnExpandRatio(1, 1F); - mainLayout.setColumnExpandRatio(2, 0F); - } - - private void minimizeArtifactoryDetails() { - mainLayout.setSpacing(true); - detailAndUploadLayout.addComponent(dropAreaLayout.getDropAreaWrapper()); - mainLayout.addComponent(filterByTypeLayout, 0, 0); - mainLayout.addComponent(smTableLayout, 1, 0); - addOtherComponents(); - } - - private void maximizeArtifactoryDetails() { - mainLayout.setSpacing(false); - mainLayout.removeComponent(filterByTypeLayout); - mainLayout.removeComponent(smTableLayout); - detailAndUploadLayout.removeComponent(dropAreaLayout.getDropAreaWrapper()); - mainLayout.setColumnExpandRatio(1, 0F); - mainLayout.setColumnExpandRatio(2, 1F); - } - - private void addOtherComponents() { - mainLayout.setColumnExpandRatio(1, 0.5F); - mainLayout.setColumnExpandRatio(2, 0.5F); - } - - private void checkNoDataAvaialble() { - if (artifactUploadState.isNoDataAvilableSoftwareModule()) { - uiNotification.displayValidationError(i18n.getMessage("message.no.data")); + private void restoreState() { + if (artifactUploadState.getSmTypeFilterLayoutUiState().isHidden() + || artifactUploadState.getArtifactDetailsGridLayoutUiState().isMaximized()) { + hideSmTypeLayout(); + } else { + showSmTypeLayout(); } + smTypeFilterLayout.restoreState(); + + if (artifactUploadState.getSmGridLayoutUiState().isMaximized()) { + maximizeSmGridLayout(); + } + smGridLayout.restoreState(); + + if (artifactUploadState.getArtifactDetailsGridLayoutUiState().isMaximized()) { + maximizeArtifactGridLayout(); + } + artifactDetailsGridLayout.restoreState(); } + private void showSmTypeLayout() { + smTypeFilterLayout.setVisible(true); + smGridLayout.hideSmTypeHeaderIcon(); + } + + private void hideSmTypeLayout() { + smTypeFilterLayout.setVisible(false); + smGridLayout.showSmTypeHeaderIcon(); + } + + private void maximizeSmGridLayout() { + artifactDetailsGridLayout.setVisible(false); + + mainLayout.setExpandRatio(smTypeFilterLayout, 0F); + mainLayout.setExpandRatio(smGridLayout, 1.0F); + mainLayout.setExpandRatio(artifactDetailsGridLayout, 0F); + + smGridLayout.maximize(); + } + + private void minimizeSmGridLayout() { + artifactDetailsGridLayout.setVisible(true); + + mainLayout.setExpandRatio(smTypeFilterLayout, 0F); + mainLayout.setExpandRatio(smGridLayout, 0.5F); + mainLayout.setExpandRatio(artifactDetailsGridLayout, 0.5F); + + smGridLayout.minimize(); + } + + private void maximizeArtifactGridLayout() { + smTypeFilterLayout.setVisible(false); + smGridLayout.setVisible(false); + + mainLayout.setExpandRatio(smTypeFilterLayout, 0F); + mainLayout.setExpandRatio(smGridLayout, 0F); + mainLayout.setExpandRatio(artifactDetailsGridLayout, 1.0F); + + artifactDetailsGridLayout.maximize(); + } + + private void minimizeArtifactGridLayout() { + if (!artifactUploadState.getSmTypeFilterLayoutUiState().isHidden()) { + smTypeFilterLayout.setVisible(true); + } + smGridLayout.setVisible(true); + + mainLayout.setExpandRatio(smTypeFilterLayout, 0F); + mainLayout.setExpandRatio(smGridLayout, 0.5F); + mainLayout.setExpandRatio(artifactDetailsGridLayout, 0.5F); + + artifactDetailsGridLayout.minimize(); + } + + /** + * Show or hide the filter button based on the event width + * + * @param event + * BrowserWindowResizeEvent + */ @Override public void browserWindowResized(final BrowserWindowResizeEvent event) { showOrHideFilterButtons(event.getWidth()); } private void showOrHideFilterButtons(final int browserWidth) { + if (artifactUploadState.getArtifactDetailsGridLayoutUiState().isMaximized()) { + return; + } + if (browserWidth < SPUIDefinitions.REQ_MIN_BROWSER_WIDTH) { - filterByTypeLayout.setVisible(false); - smTableLayout.setShowFilterButtonVisible(true); - } else if (!artifactUploadState.isSwTypeFilterClosed()) { - filterByTypeLayout.setVisible(true); - smTableLayout.setShowFilterButtonVisible(false); + if (!artifactUploadState.getSmTypeFilterLayoutUiState().isHidden()) { + hideSmTypeLayout(); + } + } else { + if (artifactUploadState.getSmTypeFilterLayoutUiState().isHidden()) { + showSmTypeLayout(); + } } } - @Override - public void enter(final ViewChangeEvent event) { - smTableLayout.getSoftwareModuleTable() - .selectEntity(artifactUploadState.getSelectedBaseSwModuleId().orElse(null)); - dropAreaLayout.getUploadButtonLayout().restoreState(); - } + @PreDestroy + void destroy() { + if (permChecker.hasReadRepositoryPermission()) { + layoutVisibilityListener.unsubscribe(); + layoutResizeListener.unsubscribe(); + smTypeFilterLayout.unsubscribeListener(); + smGridLayout.unsubscribeListener(); + artifactDetailsGridLayout.unsubscribeListener(); + } + } } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/UploadArtifactViewMenuItem.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/UploadArtifactViewMenuItem.java index dae2cedc6..9f943d4b2 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/UploadArtifactViewMenuItem.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/UploadArtifactViewMenuItem.java @@ -12,12 +12,12 @@ import java.util.Arrays; import java.util.List; import org.eclipse.hawkbit.im.authentication.SpPermission; -import org.eclipse.hawkbit.ui.management.AbstractDashboardMenuItemNotification; +import org.eclipse.hawkbit.ui.menu.AbstractDashboardMenuItemNotification; import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.annotation.Order; -import com.vaadin.server.FontAwesome; +import com.vaadin.icons.VaadinIcons; import com.vaadin.server.Resource; import com.vaadin.spring.annotation.SpringComponent; import com.vaadin.spring.annotation.UIScope; @@ -31,7 +31,6 @@ import com.vaadin.spring.annotation.UIScope; @UIScope @Order(500) public class UploadArtifactViewMenuItem extends AbstractDashboardMenuItemNotification { - private static final long serialVersionUID = 1L; @Autowired @@ -46,17 +45,17 @@ public class UploadArtifactViewMenuItem extends AbstractDashboardMenuItemNotific @Override public Resource getDashboardIcon() { - return FontAwesome.UPLOAD; + return VaadinIcons.UPLOAD; } @Override public String getDashboardCaption() { - return getI18n().getMessage("dashboard.upload.caption"); + return i18n.getMessage("dashboard.upload.caption"); } @Override public String getDashboardCaptionLong() { - return getI18n().getMessage("dashboard.upload.caption-long"); + return i18n.getMessage("dashboard.upload.caption-long"); } @Override diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactBeanQuery.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactBeanQuery.java deleted file mode 100644 index 2b605d4c6..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactBeanQuery.java +++ /dev/null @@ -1,113 +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.artifacts.details; - -import java.util.List; -import java.util.Map; - -import org.apache.commons.lang3.ArrayUtils; -import org.eclipse.hawkbit.repository.ArtifactManagement; -import org.eclipse.hawkbit.repository.OffsetBasedPageRequest; -import org.eclipse.hawkbit.repository.model.Artifact; -import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil; -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.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 artifact - * beans. - */ -public class ArtifactBeanQuery extends AbstractBeanQuery { - private static final long serialVersionUID = 1L; - private Sort sort = new Sort(Direction.DESC, "filename"); - private transient ArtifactManagement artifactManagement; - private transient Page firstPagetArtifacts; - private Long baseSwModuleId; - - /** - * Parametric Constructor. - * - * @param definition - * as Def - * @param queryConfig - * as Config - * @param sortIds - * as sort - * @param sortStates - * as Sort status - */ - public ArtifactBeanQuery(final QueryDefinition definition, final Map queryConfig, - final Object[] sortIds, final boolean[] sortStates) { - - super(definition, queryConfig, sortIds, sortStates); - - if (HawkbitCommonUtil.isNotNullOrEmpty(queryConfig)) { - baseSwModuleId = (Long) queryConfig.get(SPUIDefinitions.BY_BASE_SOFTWARE_MODULE); - } - - if (!ArrayUtils.isEmpty(sortStates)) { - sort = new Sort(sortStates[0] ? Direction.ASC : Direction.DESC, (String) sortIds[0]); - - for (int targetId = 1; targetId < sortIds.length; targetId++) { - sort.and(new Sort(sortStates[targetId] ? Direction.ASC : Direction.DESC, (String) sortIds[targetId])); - } - } - } - - @Override - protected Artifact constructBean() { - return null; - } - - @Override - protected List loadBeans(final int startIndex, final int count) { - Page artifactBeans; - if (startIndex == 0 && firstPagetArtifacts != null) { - artifactBeans = firstPagetArtifacts; - } else { - artifactBeans = getArtifactManagement() - .findBySoftwareModule(new OffsetBasedPageRequest(startIndex, count, sort), baseSwModuleId); - } - - return artifactBeans.getContent(); - } - - @Override - protected void saveBeans(final List addedTargets, final List modifiedTargets, - final List removedTargets) { - // CRUD operations on Target will be done through repository methods - } - - @Override - public int size() { - long size = 0; - if (baseSwModuleId != null) { - firstPagetArtifacts = getArtifactManagement().findBySoftwareModule( - new OffsetBasedPageRequest(0, SPUIDefinitions.PAGE_SIZE, sort), baseSwModuleId); - size = firstPagetArtifacts.getTotalElements(); - } - if (size > Integer.MAX_VALUE) { - size = Integer.MAX_VALUE; - } - - return (int) size; - } - - private ArtifactManagement getArtifactManagement() { - if (artifactManagement == null) { - artifactManagement = SpringContextHelper.getBean(ArtifactManagement.class); - } - return artifactManagement; - } -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactDetailsGrid.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactDetailsGrid.java new file mode 100644 index 000000000..dd0b74639 --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactDetailsGrid.java @@ -0,0 +1,238 @@ +/** + * Copyright (c) 2020 Bosch.IO 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.artifacts.details; + +import java.util.Arrays; +import java.util.Collection; +import java.util.stream.Collectors; + +import org.eclipse.hawkbit.artifact.repository.model.AbstractDbArtifact; +import org.eclipse.hawkbit.repository.ArtifactManagement; +import org.eclipse.hawkbit.ui.SpPermissionChecker; +import org.eclipse.hawkbit.ui.common.builder.GridComponentBuilder; +import org.eclipse.hawkbit.ui.common.data.mappers.ArtifactToProxyArtifactMapper; +import org.eclipse.hawkbit.ui.common.data.providers.ArtifactDataProvider; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyArtifact; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyIdentifiableEntity; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxySoftwareModule; +import org.eclipse.hawkbit.ui.common.event.EntityModifiedEventPayload; +import org.eclipse.hawkbit.ui.common.event.EntityModifiedEventPayload.EntityModifiedEventType; +import org.eclipse.hawkbit.ui.common.event.EventTopics; +import org.eclipse.hawkbit.ui.common.event.FilterType; +import org.eclipse.hawkbit.ui.common.grid.AbstractGrid; +import org.eclipse.hawkbit.ui.common.grid.support.DeleteSupport; +import org.eclipse.hawkbit.ui.common.grid.support.FilterSupport; +import org.eclipse.hawkbit.ui.common.grid.support.MasterEntitySupport; +import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions; +import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; +import org.eclipse.hawkbit.ui.utils.UIMessageIdProvider; +import org.eclipse.hawkbit.ui.utils.UINotification; +import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; +import org.vaadin.spring.events.EventBus.UIEventBus; + +import com.vaadin.icons.VaadinIcons; +import com.vaadin.server.FileDownloader; +import com.vaadin.server.StreamResource; +import com.vaadin.ui.Button; + +/** + * Artifact Details grid which is shown on the Upload View. + */ +public class ArtifactDetailsGrid extends AbstractGrid { + private static final long serialVersionUID = 1L; + + private static final String ARTIFACT_NAME_ID = "artifactName"; + private static final String ARTIFACT_SIZE_ID = "artifactSize"; + private static final String ARTIFACT_MODIFIED_DATE_ID = "artifactModifiedDate"; + private static final String ARTIFACT_SHA1_ID = "artifactSha1"; + private static final String ARTIFACT_MD5_ID = "artifactMd5"; + private static final String ARTIFACT_SHA256_ID = "artifactSha256"; + private static final String ARTIFACT_DOWNLOAD_BUTTON_ID = "artifactDownloadButton"; + private static final String ARTIFACT_DELETE_BUTTON_ID = "artifactDeleteButton"; + + private final UINotification notification; + private final transient ArtifactManagement artifactManagement; + + private final transient DeleteSupport artifactDeleteSupport; + private final transient MasterEntitySupport masterEntitySupport; + + /** + * Constructor + * + * @param eventBus + * UIEventBus + * @param i18n + * VaadinMessageSource + * @param permissionChecker + * SpPermissionChecker + * @param notification + * UINotification + * @param artifactManagement + * ArtifactManagement + */ + public ArtifactDetailsGrid(final UIEventBus eventBus, final VaadinMessageSource i18n, + final SpPermissionChecker permissionChecker, final UINotification notification, + final ArtifactManagement artifactManagement) { + super(i18n, eventBus, permissionChecker); + + this.notification = notification; + this.artifactManagement = artifactManagement; + + this.artifactDeleteSupport = new DeleteSupport<>(this, i18n, notification, + i18n.getMessage("artifact.details.header"), ProxyArtifact::getFilename, this::artifactsDeletionCallback, + UIComponentIdProvider.ARTIFACT_DELETE_CONFIRMATION_DIALOG); + + setFilterSupport( + new FilterSupport<>(new ArtifactDataProvider(artifactManagement, new ArtifactToProxyArtifactMapper()))); + initFilterMappings(); + + this.masterEntitySupport = new MasterEntitySupport<>(getFilterSupport()); + + init(); + } + + private void initFilterMappings() { + getFilterSupport(). addMapping(FilterType.MASTER, + (filter, masterFilter) -> getFilterSupport().setFilter(masterFilter)); + } + + /** + * Initial method of grid and set style name + */ + @Override + public void init() { + super.init(); + + addStyleName("grid-row-border"); + } + + private boolean artifactsDeletionCallback(final Collection artifactsToBeDeleted) { + final Collection artifactToBeDeletedIds = artifactsToBeDeleted.stream() + .map(ProxyIdentifiableEntity::getId).collect(Collectors.toList()); + artifactToBeDeletedIds.forEach(artifactManagement::delete); + + eventBus.publish(EventTopics.ENTITY_MODIFIED, this, + new EntityModifiedEventPayload(EntityModifiedEventType.ENTITY_UPDATED, ProxySoftwareModule.class, + getMasterEntitySupport().getMasterId())); + + return true; + } + + /** + * @return ID for artifact details table + */ + @Override + public String getGridId() { + return UIComponentIdProvider.UPLOAD_ARTIFACT_DETAILS_TABLE; + } + + /** + * setting up the columns with their required definition + */ + @Override + public void addColumns() { + addFilenameColumn().setExpandRatio(2); + + addSizeColumn(); + + addModifiedDateColumn(); + + GridComponentBuilder.joinToActionColumn(i18n, getDefaultHeaderRow(), + Arrays.asList(addDownloadColumn(), addDeleteColumn())); + } + + private Column addFilenameColumn() { + return GridComponentBuilder.addColumn(this, ProxyArtifact::getFilename).setId(ARTIFACT_NAME_ID) + .setCaption(i18n.getMessage("artifact.filename.caption")); + } + + private Column addSizeColumn() { + return GridComponentBuilder.addColumn(this, ProxyArtifact::getSize).setId(ARTIFACT_SIZE_ID) + .setCaption(i18n.getMessage("artifact.filesize.bytes.caption")); + } + + protected Column addModifiedDateColumn() { + return GridComponentBuilder.addColumn(this, ProxyArtifact::getModifiedDate).setId(ARTIFACT_MODIFIED_DATE_ID) + .setCaption(i18n.getMessage("upload.last.modified.date")); + } + + private Column addDownloadColumn() { + return GridComponentBuilder.addIconColumn(this, this::buildDownloadButton, ARTIFACT_DOWNLOAD_BUTTON_ID, + i18n.getMessage("header.action.download")); + } + + private Button buildDownloadButton(final ProxyArtifact artifact) { + final Button downloadButton = GridComponentBuilder.buildActionButton(i18n, clickEvent -> { + }, VaadinIcons.DOWNLOAD, UIMessageIdProvider.TOOLTIP_ARTIFACT_DOWNLOAD, + SPUIStyleDefinitions.STATUS_ICON_NEUTRAL, + UIComponentIdProvider.ARTIFACT_FILE_DOWNLOAD_ICON + "." + artifact.getId(), + permissionChecker.hasDownloadRepositoryPermission()); + + attachFileDownloader(artifact, downloadButton); + + return downloadButton; + } + + private void attachFileDownloader(final ProxyArtifact artifact, final Button downloadButton) { + final StreamResource artifactStreamResource = new StreamResource(() -> artifactManagement + .loadArtifactBinary(artifact.getSha1Hash()).map(AbstractDbArtifact::getFileInputStream).orElse(null), + artifact.getFilename()); + + final FileDownloader fileDownloader = new FileDownloader(artifactStreamResource); + fileDownloader.setErrorHandler(event -> notification + .displayValidationError(i18n.getMessage(UIMessageIdProvider.ARTIFACT_DOWNLOAD_FAILURE_MSG))); + + fileDownloader.extend(downloadButton); + } + + private Column addDeleteColumn() { + return GridComponentBuilder.addDeleteColumn(this, i18n, ARTIFACT_DELETE_BUTTON_ID, artifactDeleteSupport, + UIComponentIdProvider.ARTIFACT_DELET_ICON, e -> permissionChecker.hasDeleteRepositoryPermission()); + } + + @Override + protected void addMaxColumns() { + addFilenameColumn().setExpandRatio(2); + + addSizeColumn(); + + addSha1Column(); + addMd5Column(); + addSha256Column(); + + addModifiedDateColumn(); + + GridComponentBuilder.joinToActionColumn(i18n, getDefaultHeaderRow(), + Arrays.asList(addDownloadColumn(), addDeleteColumn())); + + getColumns().forEach(column -> column.setHidable(true)); + } + + private Column addSha1Column() { + return GridComponentBuilder.addColumn(this, ProxyArtifact::getSha1Hash).setId(ARTIFACT_SHA1_ID) + .setCaption(i18n.getMessage("upload.sha1")); + } + + private Column addMd5Column() { + return GridComponentBuilder.addColumn(this, ProxyArtifact::getMd5Hash).setId(ARTIFACT_MD5_ID) + .setCaption(i18n.getMessage("upload.md5")); + } + + private Column addSha256Column() { + return GridComponentBuilder.addColumn(this, ProxyArtifact::getSha256Hash).setId(ARTIFACT_SHA256_ID) + .setCaption(i18n.getMessage("upload.sha256")); + } + + /** + * @return software module entity + */ + public MasterEntitySupport getMasterEntitySupport() { + return masterEntitySupport; + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactDetailsGridHeader.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactDetailsGridHeader.java new file mode 100644 index 000000000..588be81f3 --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactDetailsGridHeader.java @@ -0,0 +1,95 @@ +/** + * Copyright (c) 2020 Bosch.IO 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.artifacts.details; + +import org.eclipse.hawkbit.ui.common.data.proxies.ProxySoftwareModule; +import org.eclipse.hawkbit.ui.common.event.CommandTopics; +import org.eclipse.hawkbit.ui.common.event.EventLayout; +import org.eclipse.hawkbit.ui.common.event.EventView; +import org.eclipse.hawkbit.ui.common.event.LayoutResizeEventPayload; +import org.eclipse.hawkbit.ui.common.event.LayoutResizeEventPayload.ResizeType; +import org.eclipse.hawkbit.ui.common.grid.header.AbstractMasterAwareGridHeader; +import org.eclipse.hawkbit.ui.common.grid.header.support.ResizeHeaderSupport; +import org.eclipse.hawkbit.ui.utils.SPUIDefinitions; +import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; +import org.eclipse.hawkbit.ui.utils.UIMessageIdProvider; +import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; +import org.vaadin.spring.events.EventBus.UIEventBus; + +/** + * Header for ArtifactDetails with maximize-support. + */ +public class ArtifactDetailsGridHeader extends AbstractMasterAwareGridHeader { + private static final long serialVersionUID = 1L; + + private final ArtifactDetailsGridLayoutUiState artifactDetailsGridLayoutUiState; + + private final transient ResizeHeaderSupport resizeHeaderSupport; + + /** + * Constructor + * + * @param i18n + * VaadinMessageSource + * @param eventBus + * UIEventBus + * @param artifactDetailsGridLayoutUiState + * ArtifactDetailsGridLayoutUiState + */ + public ArtifactDetailsGridHeader(final VaadinMessageSource i18n, final UIEventBus eventBus, + final ArtifactDetailsGridLayoutUiState artifactDetailsGridLayoutUiState) { + super(i18n, null, eventBus); + + this.artifactDetailsGridLayoutUiState = artifactDetailsGridLayoutUiState; + + this.resizeHeaderSupport = new ResizeHeaderSupport(i18n, SPUIDefinitions.EXPAND_ARTIFACT_DETAILS, + this::maximizeTable, this::minimizeTable, this::onLoadIsTableMaximized); + addHeaderSupport(resizeHeaderSupport); + + buildHeader(); + } + + @Override + protected String getEntityDetailsCaptionMsgKey() { + return UIMessageIdProvider.CAPTION_ARTIFACT_DETAILS; + } + + @Override + protected String getMasterEntityDetailsCaptionId() { + return UIComponentIdProvider.ARTIFACT_DETAILS_HEADER_LABEL_ID; + } + + @Override + protected String getMasterEntityName(final ProxySoftwareModule masterEntity) { + return masterEntity.getNameAndVersion(); + } + + @Override + protected String getEntityDetailsCaptionOfMsgKey() { + return UIMessageIdProvider.CAPTION_ARTIFACT_DETAILS_OF; + } + + private void maximizeTable() { + eventBus.publish(CommandTopics.RESIZE_LAYOUT, this, + new LayoutResizeEventPayload(ResizeType.MAXIMIZE, EventLayout.ARTIFACT_LIST, EventView.UPLOAD)); + + artifactDetailsGridLayoutUiState.setMaximized(true); + } + + private void minimizeTable() { + eventBus.publish(CommandTopics.RESIZE_LAYOUT, this, + new LayoutResizeEventPayload(ResizeType.MINIMIZE, EventLayout.ARTIFACT_LIST, EventView.UPLOAD)); + + artifactDetailsGridLayoutUiState.setMaximized(false); + } + + private Boolean onLoadIsTableMaximized() { + return artifactDetailsGridLayoutUiState.isMaximized(); + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactDetailsGridLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactDetailsGridLayout.java new file mode 100644 index 000000000..7b9357cb6 --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactDetailsGridLayout.java @@ -0,0 +1,148 @@ +/** + * 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.artifacts.details; + +import java.util.Arrays; +import java.util.List; + +import javax.servlet.MultipartConfigElement; + +import org.eclipse.hawkbit.repository.ArtifactManagement; +import org.eclipse.hawkbit.repository.SoftwareModuleManagement; +import org.eclipse.hawkbit.ui.SpPermissionChecker; +import org.eclipse.hawkbit.ui.artifacts.ArtifactUploadState; +import org.eclipse.hawkbit.ui.artifacts.upload.FileUploadProgress; +import org.eclipse.hawkbit.ui.artifacts.upload.UploadDropAreaLayout; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxySoftwareModule; +import org.eclipse.hawkbit.ui.common.event.EventLayout; +import org.eclipse.hawkbit.ui.common.event.EventLayoutViewAware; +import org.eclipse.hawkbit.ui.common.event.EventTopics; +import org.eclipse.hawkbit.ui.common.event.EventView; +import org.eclipse.hawkbit.ui.common.layout.AbstractGridComponentLayout; +import org.eclipse.hawkbit.ui.common.layout.MasterEntityAwareComponent; +import org.eclipse.hawkbit.ui.common.layout.listener.GenericEventListener; +import org.eclipse.hawkbit.ui.common.layout.listener.SelectionChangedListener; +import org.eclipse.hawkbit.ui.utils.UINotification; +import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; +import org.vaadin.spring.events.EventBus.UIEventBus; + +/** + * Display the details of the artifacts for a selected software module. + */ +public class ArtifactDetailsGridLayout extends AbstractGridComponentLayout { + private static final long serialVersionUID = 1L; + + private final ArtifactDetailsGridHeader artifactDetailsHeader; + private final ArtifactDetailsGrid artifactDetailsGrid; + private final UploadDropAreaLayout uploadDropAreaLayout; + + private final transient SelectionChangedListener selectionChangedListener; + private final transient GenericEventListener fileUploadChangedListener; + + /** + * Constructor for ArtifactDetailsLayout + * + * @param i18n + * VaadinMessageSource + * @param eventBus + * UIEventBus + * @param permChecker + * SpPermissionChecker + * @param notification + * UINotification + * @param artifactUploadState + * ArtifactUploadState + * @param artifactDetailsGridLayoutUiState + * ArtifactDetailsGridLayoutUiState + * @param artifactManagement + * ArtifactManagement + * @param softwareManagement + * SoftwareModuleManagement + * @param multipartConfigElement + * MultipartConfigElement + */ + public ArtifactDetailsGridLayout(final VaadinMessageSource i18n, final UIEventBus eventBus, + final SpPermissionChecker permChecker, final UINotification notification, + final ArtifactUploadState artifactUploadState, + final ArtifactDetailsGridLayoutUiState artifactDetailsGridLayoutUiState, + final ArtifactManagement artifactManagement, final SoftwareModuleManagement softwareManagement, + final MultipartConfigElement multipartConfigElement) { + this.artifactDetailsHeader = new ArtifactDetailsGridHeader(i18n, eventBus, artifactDetailsGridLayoutUiState); + this.artifactDetailsGrid = new ArtifactDetailsGrid(eventBus, i18n, permChecker, notification, + artifactManagement); + + if (permChecker.hasCreateRepositoryPermission()) { + this.uploadDropAreaLayout = new UploadDropAreaLayout(i18n, eventBus, notification, artifactUploadState, + multipartConfigElement, softwareManagement, artifactManagement); + + buildLayout(artifactDetailsHeader, artifactDetailsGrid, uploadDropAreaLayout); + } else { + this.uploadDropAreaLayout = null; + + buildLayout(artifactDetailsHeader, artifactDetailsGrid); + } + + final EventLayoutViewAware masterLayoutView = new EventLayoutViewAware(EventLayout.SM_LIST, EventView.UPLOAD); + this.selectionChangedListener = new SelectionChangedListener<>(eventBus, masterLayoutView, + getMasterEntityAwareComponents()); + this.fileUploadChangedListener = new GenericEventListener<>(eventBus, EventTopics.FILE_UPLOAD_CHANGED, + this::onUploadChanged); + } + + private List> getMasterEntityAwareComponents() { + return Arrays.asList(artifactDetailsHeader, artifactDetailsGrid.getMasterEntitySupport(), uploadDropAreaLayout); + } + + /** + * Checks progress on file upload + * + * @param fileUploadProgress + * FileUploadProgress + */ + public void onUploadChanged(final FileUploadProgress fileUploadProgress) { + if (uploadDropAreaLayout != null) { + uploadDropAreaLayout.onUploadChanged(fileUploadProgress); + } + } + + /** + * Maximize the artifact grid + */ + public void maximize() { + artifactDetailsGrid.createMaximizedContent(); + hideDetailsLayout(); + } + + /** + * Minimize the artifact grid + */ + public void minimize() { + artifactDetailsGrid.createMinimizedContent(); + showDetailsLayout(); + } + + /** + * Is called when view is shown to the user + */ + public void restoreState() { + artifactDetailsHeader.restoreState(); + + if (uploadDropAreaLayout != null) { + uploadDropAreaLayout.restoreState(); + } + } + + /** + * Unsubscribe the even listeners for selection change and fileupload + */ + public void unsubscribeListener() { + selectionChangedListener.unsubscribe(); + fileUploadChangedListener.unsubscribe(); + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactDetailsGridLayoutUiState.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactDetailsGridLayoutUiState.java new file mode 100644 index 000000000..0eef4d8c5 --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactDetailsGridLayoutUiState.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2020 Bosch.IO 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.artifacts.details; + +import java.io.Serializable; + +/** + * Display and set the current state of the artifacts detail layout + * + */ +public class ArtifactDetailsGridLayoutUiState implements Serializable { + private static final long serialVersionUID = 1L; + + private boolean maximized; + + /** + * @return current state artifact detail grid + */ + public boolean isMaximized() { + return maximized; + } + + /** + * @param maximized set artifact detail grid to true or false + */ + public void setMaximized(final boolean maximized) { + this.maximized = maximized; + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactDetailsLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactDetailsLayout.java deleted file mode 100644 index b613f6b24..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactDetailsLayout.java +++ /dev/null @@ -1,606 +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.artifacts.details; - -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -import org.eclipse.hawkbit.artifact.repository.model.AbstractDbArtifact; -import org.eclipse.hawkbit.repository.ArtifactManagement; -import org.eclipse.hawkbit.repository.SoftwareModuleManagement; -import org.eclipse.hawkbit.repository.model.Artifact; -import org.eclipse.hawkbit.repository.model.SoftwareModule; -import org.eclipse.hawkbit.ui.artifacts.event.ArtifactDetailsEvent; -import org.eclipse.hawkbit.ui.artifacts.event.SoftwareModuleEvent; -import org.eclipse.hawkbit.ui.artifacts.event.SoftwareModuleEvent.SoftwareModuleEventType; -import org.eclipse.hawkbit.ui.artifacts.state.ArtifactUploadState; -import org.eclipse.hawkbit.ui.common.ConfirmationDialog; -import org.eclipse.hawkbit.ui.common.table.BaseEntityEventType; -import org.eclipse.hawkbit.ui.components.SPUIButton; -import org.eclipse.hawkbit.ui.components.SPUIComponentProvider; -import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleNoBorder; -import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil; -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.UIMessageIdProvider; -import org.eclipse.hawkbit.ui.utils.UINotification; -import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; -import org.springframework.util.StringUtils; -import org.vaadin.addons.lazyquerycontainer.BeanQueryFactory; -import org.vaadin.addons.lazyquerycontainer.LazyQueryContainer; -import org.vaadin.addons.lazyquerycontainer.LazyQueryDefinition; -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.google.common.collect.Maps; -import com.vaadin.data.Container; -import com.vaadin.server.ErrorHandler; -import com.vaadin.server.FileDownloader; -import com.vaadin.server.FontAwesome; -import com.vaadin.server.StreamResource; -import com.vaadin.ui.Alignment; -import com.vaadin.ui.Button; -import com.vaadin.ui.HorizontalLayout; -import com.vaadin.ui.Label; -import com.vaadin.ui.Table; -import com.vaadin.ui.Table.ColumnGenerator; -import com.vaadin.ui.Table.TableDragMode; -import com.vaadin.ui.UI; -import com.vaadin.ui.VerticalLayout; -import com.vaadin.ui.themes.ValoTheme; - -/** - * Display the details of the artifacts for a selected software module. - */ -public class ArtifactDetailsLayout extends VerticalLayout { - - private static final long serialVersionUID = 1L; - - private static final String PROVIDED_FILE_NAME = "filename"; - - private static final String LAST_MODIFIED_DATE = "lastModifiedAt"; - - private static final String CREATE_MODIFIED_DATE_UPLOAD = "Created/Modified Date"; - - private static final String ACTION = "action"; - - private static final String CREATED_DATE = "createdAt"; - - private static final String SIZE = "size"; - - private static final String SHA1HASH = "sha1Hash"; - - private static final String MD5HASH = "md5Hash"; - - private final VaadinMessageSource i18n; - - private final transient EventBus.UIEventBus eventBus; - - private final ArtifactUploadState artifactUploadState; - - private final UINotification uINotification; - - private Label prefixTitleOfArtifactDetails; - - private Label titleOfArtifactDetails; - - private HorizontalLayout headerCaptionLayout; - - private SPUIButton maxMinButton; - - private Table artifactDetailsTable; - - private Table maxArtifactDetailsTable; - - private boolean fullWindowMode; - - private boolean readOnly; - - private final transient ArtifactManagement artifactManagement; - - private final transient SoftwareModuleManagement softwareModuleManagement; - - /** - * Constructor for ArtifactDetailsLayout - * - * @param i18n - * VaadinMessageSource - * @param eventBus - * UIEventBus - * @param artifactUploadState - * ArtifactUploadState - * @param uINotification - * UINotification - * @param artifactManagement - * ArtifactManagement - * @param softwareManagement - * SoftwareManagement - */ - public ArtifactDetailsLayout(final VaadinMessageSource i18n, final UIEventBus eventBus, - final ArtifactUploadState artifactUploadState, final UINotification uINotification, - final ArtifactManagement artifactManagement, final SoftwareModuleManagement softwareManagement) { - this.i18n = i18n; - this.eventBus = eventBus; - this.artifactUploadState = artifactUploadState; - this.uINotification = uINotification; - this.artifactManagement = artifactManagement; - this.softwareModuleManagement = softwareManagement; - - final Optional selectedSoftwareModule = findSelectedSoftwareModule(); - - String labelSoftwareModule = ""; - if (selectedSoftwareModule.isPresent()) { - labelSoftwareModule = HawkbitCommonUtil.getFormattedNameVersion(selectedSoftwareModule.get().getName(), - selectedSoftwareModule.get().getVersion()); - } - createComponents(); - buildLayout(); - eventBus.subscribe(this); - - if (selectedSoftwareModule.isPresent()) { - populateArtifactDetails(selectedSoftwareModule.get().getId(), labelSoftwareModule); - } - - if (isMaximized()) { - maximizedArtifactDetailsView(); - } - } - - private Optional findSelectedSoftwareModule() { - final Optional selectedBaseSwModuleId = artifactUploadState.getSelectedBaseSwModuleId(); - - if (selectedBaseSwModuleId.isPresent()) { - return softwareModuleManagement.get(selectedBaseSwModuleId.get()); - } - return Optional.empty(); - } - - private void createComponents() { - prefixTitleOfArtifactDetails = new Label(); - prefixTitleOfArtifactDetails.addStyleName(ValoTheme.LABEL_SMALL); - prefixTitleOfArtifactDetails.addStyleName(ValoTheme.LABEL_BOLD); - prefixTitleOfArtifactDetails.setSizeUndefined(); - - titleOfArtifactDetails = new Label(); - titleOfArtifactDetails.setId(UIComponentIdProvider.ARTIFACT_DETAILS_HEADER_LABEL_ID); - titleOfArtifactDetails.setSizeFull(); - titleOfArtifactDetails.setImmediate(true); - titleOfArtifactDetails.setWidth("100%"); - titleOfArtifactDetails.addStyleName(ValoTheme.LABEL_SMALL); - titleOfArtifactDetails.addStyleName("text-bold"); - titleOfArtifactDetails.addStyleName("text-cut"); - titleOfArtifactDetails.addStyleName("header-caption-right"); - - headerCaptionLayout = new HorizontalLayout(); - headerCaptionLayout.setMargin(false); - headerCaptionLayout.setSpacing(true); - headerCaptionLayout.setSizeFull(); - headerCaptionLayout.addStyleName("header-caption"); - - headerCaptionLayout.addComponent(prefixTitleOfArtifactDetails); - headerCaptionLayout.setComponentAlignment(prefixTitleOfArtifactDetails, Alignment.TOP_LEFT); - headerCaptionLayout.setExpandRatio(prefixTitleOfArtifactDetails, 0.0F); - - headerCaptionLayout.addComponent(titleOfArtifactDetails); - headerCaptionLayout.setComponentAlignment(titleOfArtifactDetails, Alignment.TOP_LEFT); - headerCaptionLayout.setExpandRatio(titleOfArtifactDetails, 1.0F); - - maxMinButton = createMaxMinButton(); - - artifactDetailsTable = createArtifactDetailsTable(); - - artifactDetailsTable.setContainerDataSource(createArtifactLazyQueryContainer()); - addGeneratedColumn(artifactDetailsTable); - if (!readOnly) { - addGeneratedColumnButton(artifactDetailsTable); - } - setTableColumnDetails(artifactDetailsTable); - } - - private SPUIButton createMaxMinButton() { - final SPUIButton button = (SPUIButton) SPUIComponentProvider.getButton(SPUIDefinitions.EXPAND_ACTION_HISTORY, - "", i18n.getMessage(UIMessageIdProvider.TOOLTIP_MAXIMIZE), null, true, FontAwesome.EXPAND, - SPUIButtonStyleNoBorder.class); - button.addClickListener(event -> maxArtifactDetails()); - return button; - - } - - private void buildLayout() { - final HorizontalLayout header = new HorizontalLayout(); - header.addStyleName("artifact-details-header"); - header.addStyleName("bordered-layout"); - header.addStyleName("no-border-bottom"); - header.setSpacing(false); - header.setMargin(false); - header.setSizeFull(); - header.setHeightUndefined(); - header.setImmediate(true); - header.addComponents(headerCaptionLayout, maxMinButton); - header.setComponentAlignment(headerCaptionLayout, Alignment.TOP_LEFT); - header.setComponentAlignment(maxMinButton, Alignment.TOP_RIGHT); - header.setExpandRatio(headerCaptionLayout, 1.0F); - - setSizeFull(); - setImmediate(true); - addStyleName("artifact-table"); - addStyleName("table-layout"); - addComponent(header); - setComponentAlignment(header, Alignment.MIDDLE_CENTER); - addComponent(artifactDetailsTable); - setComponentAlignment(artifactDetailsTable, Alignment.MIDDLE_CENTER); - setExpandRatio(artifactDetailsTable, 1.0F); - } - - private Container createArtifactLazyQueryContainer() { - return getArtifactLazyQueryContainer(Collections.emptyMap()); - } - - private LazyQueryContainer getArtifactLazyQueryContainer(final Map queryConfig) { - - final BeanQueryFactory artifactQF = new BeanQueryFactory<>(ArtifactBeanQuery.class); - artifactQF.setQueryConfiguration(queryConfig); - final LazyQueryContainer artifactCont = new LazyQueryContainer(new LazyQueryDefinition(true, 10, "id"), - artifactQF); - addArtifactTableProperties(artifactCont); - return artifactCont; - } - - private void addArtifactTableProperties(final LazyQueryContainer artifactCont) { - artifactCont.addContainerProperty(PROVIDED_FILE_NAME, Label.class, "", false, false); - artifactCont.addContainerProperty(SIZE, Long.class, null, false, false); - artifactCont.addContainerProperty(SHA1HASH, String.class, null, false, false); - artifactCont.addContainerProperty(MD5HASH, String.class, null, false, false); - artifactCont.addContainerProperty(CREATED_DATE, Date.class, null, false, false); - artifactCont.addContainerProperty(LAST_MODIFIED_DATE, Date.class, null, false, false); - if (!readOnly) { - artifactCont.addContainerProperty(ACTION, Label.class, null, false, false); - } - } - - private static void addGeneratedColumn(final Table table) { - table.addGeneratedColumn(CREATE_MODIFIED_DATE_UPLOAD, new ColumnGenerator() { - private static final long serialVersionUID = 1L; - - @Override - public String generateCell(final Table source, final Object itemId, final Object columnId) { - final Long createdDate = (Long) table.getContainerDataSource().getItem(itemId) - .getItemProperty(CREATED_DATE).getValue(); - final Long modifiedDATE = (Long) table.getContainerDataSource().getItem(itemId) - .getItemProperty(LAST_MODIFIED_DATE).getValue(); - if (modifiedDATE != null) { - return SPDateTimeUtil.getFormattedDate(modifiedDATE); - } - return SPDateTimeUtil.getFormattedDate(createdDate); - } - }); - } - - private void addGeneratedColumnButton(final Table table) { - table.addGeneratedColumn(ACTION, new ColumnGenerator() { - private static final long serialVersionUID = 1L; - - @Override - public HorizontalLayout generateCell(final Table source, final Object itemId, final Object columnId) { - HorizontalLayout actioncellLayout = new HorizontalLayout(); - actioncellLayout.addComponent(getArtifactDeleteButton(table, itemId)); - actioncellLayout.addComponent(getArtifactDownloadButton(table, itemId)); - actioncellLayout.setImmediate(true); - return actioncellLayout; - } - }); - } - - private Button getArtifactDeleteButton(final Table table, final Object itemId) { - final String fileName = (String) table.getContainerDataSource().getItem(itemId) - .getItemProperty(PROVIDED_FILE_NAME).getValue(); - final Button deleteIcon = SPUIComponentProvider.getButton( - fileName + "-" + UIComponentIdProvider.UPLOAD_FILE_DELETE_ICON, "", - i18n.getMessage(UIMessageIdProvider.CAPTION_DISCARD), ValoTheme.BUTTON_TINY + " " + "blueicon", - true, FontAwesome.TRASH_O, SPUIButtonStyleNoBorder.class); - deleteIcon.setData(itemId); - deleteIcon.addClickListener(event -> confirmAndDeleteArtifact((Long) itemId, fileName)); - return deleteIcon; - } - - private Button getArtifactDownloadButton(final Table table, final Object itemId) { - final String fileName = (String) table.getContainerDataSource().getItem(itemId) - .getItemProperty(PROVIDED_FILE_NAME).getValue(); - final Button downloadIcon = SPUIComponentProvider.getButton( - fileName + "-" + UIComponentIdProvider.ARTIFACT_FILE_DOWNLOAD_ICON, "", - i18n.getMessage(UIMessageIdProvider.TOOLTIP_ARTIFACT_DOWNLOAD), ValoTheme.BUTTON_TINY + " " + "blueicon", - true, FontAwesome.DOWNLOAD, SPUIButtonStyleNoBorder.class); - downloadIcon.setData(itemId); - FileDownloader fileDownloader = new FileDownloader(createStreamResource((Long) itemId)); - fileDownloader.extend(downloadIcon); - fileDownloader.setErrorHandler(new ErrorHandler() { - - /** - * Error handler for file downloader - */ - private static final long serialVersionUID = 4030230501114422570L; - - @Override - public void error(com.vaadin.server.ErrorEvent event) { - uINotification.displayValidationError(i18n.getMessage(UIMessageIdProvider.ARTIFACT_DOWNLOAD_FAILURE_MSG)); - } - }); - return downloadIcon; - } - - private StreamResource createStreamResource(final Long id) { - - Optional artifact = this.artifactManagement.get(id); - if (artifact.isPresent()) { - Optional file = artifactManagement.loadArtifactBinary(artifact.get().getSha1Hash()); - return new StreamResource(new StreamResource.StreamSource() { - private static final long serialVersionUID = 1L; - - @Override - public InputStream getStream() { - if (file.isPresent()) { - return file.get().getFileInputStream(); - } - return null; - } - }, artifact.get().getFilename()); - } - return null; - } - - private void confirmAndDeleteArtifact(final Long id, final String fileName) { - final ConfirmationDialog confirmDialog = new ConfirmationDialog( - i18n.getMessage("caption.delete.artifact.confirmbox"), - i18n.getMessage("message.delete.artifact", fileName), i18n.getMessage(UIMessageIdProvider.BUTTON_OK), - i18n.getMessage(UIMessageIdProvider.BUTTON_CANCEL), ok -> { - if (ok) { - artifactManagement.delete(id); - uINotification.displaySuccess(i18n.getMessage("message.artifact.deleted", fileName)); - final Optional softwareModule = findSelectedSoftwareModule(); - populateArtifactDetails(softwareModule.orElse(null)); - } - }); - UI.getCurrent().addWindow(confirmDialog.getWindow()); - confirmDialog.getWindow().bringToFront(); - } - - private void setTableColumnDetails(final Table table) { - - table.setColumnHeader(PROVIDED_FILE_NAME, i18n.getMessage(UIMessageIdProvider.CAPTION_ARTIFACT_FILENAME)); - table.setColumnHeader(SIZE, i18n.getMessage(UIMessageIdProvider.CAPTION_ARTIFACT_FILESIZE_BYTES)); - if (fullWindowMode) { - table.setColumnHeader(SHA1HASH, i18n.getMessage("upload.sha1")); - table.setColumnHeader(MD5HASH, i18n.getMessage("upload.md5")); - } - table.setColumnHeader(CREATE_MODIFIED_DATE_UPLOAD, i18n.getMessage("upload.last.modified.date")); - if (!readOnly) { - table.setColumnHeader(ACTION, i18n.getMessage(UIMessageIdProvider.MESSAGE_UPLOAD_ACTION)); - } - - table.setColumnExpandRatio(PROVIDED_FILE_NAME, 3.5F); - table.setColumnExpandRatio(SIZE, 2F); - if (fullWindowMode) { - table.setColumnExpandRatio(SHA1HASH, 2.8F); - table.setColumnExpandRatio(MD5HASH, 2.4F); - } - table.setColumnExpandRatio(CREATE_MODIFIED_DATE_UPLOAD, 3F); - if (!readOnly) { - table.setColumnExpandRatio(ACTION, 2.5F); - } - - table.setVisibleColumns(getVisbleColumns().toArray()); - } - - private List getVisbleColumns() { - final List visibileColumn = new ArrayList<>(); - visibileColumn.add(PROVIDED_FILE_NAME); - visibileColumn.add(SIZE); - if (fullWindowMode) { - visibileColumn.add(SHA1HASH); - visibileColumn.add(MD5HASH); - } - visibileColumn.add(CREATE_MODIFIED_DATE_UPLOAD); - if (!readOnly) { - visibileColumn.add(ACTION); - } - return visibileColumn; - } - - private static Table createArtifactDetailsTable() { - final Table detailsTable = new Table(); - detailsTable.addStyleName("sp-table"); - - detailsTable.setImmediate(true); - detailsTable.setSizeFull(); - - detailsTable.setId(UIComponentIdProvider.UPLOAD_ARTIFACT_DETAILS_TABLE); - detailsTable.addStyleName(ValoTheme.TABLE_NO_VERTICAL_LINES); - detailsTable.addStyleName(ValoTheme.TABLE_SMALL); - return detailsTable; - } - - /** - * will be used by button click listener of action history expand icon. - */ - private void maxArtifactDetails() { - final Boolean flag = (Boolean) maxMinButton.getData(); - if (flag == null || Boolean.FALSE.equals(flag)) { - // Clicked on max Button - maximizedArtifactDetailsView(); - } else { - minimizedArtifactDetailsView(); - } - } - - private void minimizedArtifactDetailsView() { - fullWindowMode = Boolean.FALSE; - showMaxIcon(); - setTableColumnDetails(artifactDetailsTable); - createArtifactDetailsMinView(); - - } - - private void maximizedArtifactDetailsView() { - fullWindowMode = Boolean.TRUE; - showMinIcon(); - setTableColumnDetails(artifactDetailsTable); - createArtifactDetailsMaxView(); - } - - /** - * Create Max artifact details Table. - */ - public void createMaxArtifactDetailsTable() { - maxArtifactDetailsTable = createArtifactDetailsTable(); - maxArtifactDetailsTable.setId(UIComponentIdProvider.UPLOAD_ARTIFACT_DETAILS_TABLE_MAX); - maxArtifactDetailsTable.setContainerDataSource(artifactDetailsTable.getContainerDataSource()); - addGeneratedColumn(maxArtifactDetailsTable); - if (!readOnly) { - addGeneratedColumnButton(maxArtifactDetailsTable); - } - setTableColumnDetails(maxArtifactDetailsTable); - } - - private void createArtifactDetailsMaxView() { - - artifactDetailsTable.setValue(null); - artifactDetailsTable.setSelectable(false); - artifactDetailsTable.setMultiSelect(false); - artifactDetailsTable.setDragMode(TableDragMode.NONE); - artifactDetailsTable.setColumnCollapsingAllowed(true); - artifactUploadState.setArtifactDetailsMaximized(Boolean.TRUE); - eventBus.publish(this, ArtifactDetailsEvent.MAXIMIZED); - } - - private void createArtifactDetailsMinView() { - artifactUploadState.setArtifactDetailsMaximized(Boolean.FALSE); - artifactDetailsTable.setColumnCollapsingAllowed(false); - eventBus.publish(this, ArtifactDetailsEvent.MINIMIZED); - } - - /** - * Populate artifact details. - * - * @param softwareModule - * software module - */ - public void populateArtifactDetails(final SoftwareModule softwareModule) { - if (softwareModule == null) { - populateArtifactDetails(null, null); - } else { - populateArtifactDetails(softwareModule.getId(), - HawkbitCommonUtil.getFormattedNameVersion(softwareModule.getName(), softwareModule.getVersion())); - } - } - - private void populateArtifactDetails(final Long baseSwModuleId, final String swModuleName) { - if (!readOnly) { - - if (StringUtils.hasText(swModuleName)) { - prefixTitleOfArtifactDetails.setValue(i18n.getMessage(UIMessageIdProvider.CAPTION_ARTIFACT_DETAILS_OF)); - titleOfArtifactDetails.setValue(swModuleName); - } else { - setTitleOfLayoutHeader(); - } - - } - final Map queryConfiguration; - if (baseSwModuleId != null) { - queryConfiguration = Maps.newHashMapWithExpectedSize(1); - queryConfiguration.put(SPUIDefinitions.BY_BASE_SOFTWARE_MODULE, baseSwModuleId); - } else { - queryConfiguration = Collections.emptyMap(); - } - final LazyQueryContainer artifactContainer = getArtifactLazyQueryContainer(queryConfiguration); - artifactDetailsTable.setContainerDataSource(artifactContainer); - if (fullWindowMode && maxArtifactDetailsTable != null) { - maxArtifactDetailsTable.setContainerDataSource(artifactContainer); - } - setTableColumnDetails(artifactDetailsTable); - } - - /** - * Set title of artifact details header layout. - */ - private void setTitleOfLayoutHeader() { - prefixTitleOfArtifactDetails.setValue(i18n.getMessage(UIMessageIdProvider.CAPTION_ARTIFACT_DETAILS)); - titleOfArtifactDetails.setValue(""); - } - - @EventBusListenerMethod(scope = EventScope.UI) - void onEvent(final SoftwareModuleEvent softwareModuleEvent) { - if (softwareModuleEvent.getEventType() == BaseEntityEventType.SELECTED_ENTITY) { - UI.getCurrent().access(() -> { - if (softwareModuleEvent.getEntity() != null) { - populateArtifactDetails(softwareModuleEvent.getEntity()); - } else { - populateArtifactDetails(null, null); - } - }); - } - if (isArtifactChangedEvent(softwareModuleEvent) && areEntityIdsNotEmpty(softwareModuleEvent)) { - UI.getCurrent().access(() -> findSelectedSoftwareModule().ifPresent(selectedSoftwareModule -> { - if (hasSelectedSoftwareModuleChanged(softwareModuleEvent.getEntityIds(), selectedSoftwareModule)) { - populateArtifactDetails(selectedSoftwareModule); - } - })); - } - } - - private static boolean areEntityIdsNotEmpty(final SoftwareModuleEvent softwareModuleEvent) { - return softwareModuleEvent.getEntityIds() != null && !softwareModuleEvent.getEntityIds().isEmpty(); - } - - private static boolean isArtifactChangedEvent(final SoftwareModuleEvent softwareModuleEvent) { - return softwareModuleEvent.getSoftwareModuleEventType() == SoftwareModuleEventType.ARTIFACTS_CHANGED; - } - - private static boolean hasSelectedSoftwareModuleChanged(final Collection changedSoftwareModuleIds, - final SoftwareModule selectedSoftwareModule) { - return changedSoftwareModuleIds.stream().anyMatch(smId -> selectedSoftwareModule.getId().equals(smId)); - } - - public Table getArtifactDetailsTable() { - return artifactDetailsTable; - } - - public Table getMaxArtifactDetailsTable() { - return maxArtifactDetailsTable; - } - - public void setFullWindowMode(final boolean fullWindowMode) { - this.fullWindowMode = fullWindowMode; - } - - private void showMinIcon() { - maxMinButton.toggleIcon(FontAwesome.COMPRESS); - maxMinButton.setData(Boolean.TRUE); - maxMinButton.setDescription(i18n.getMessage(UIMessageIdProvider.TOOLTIP_MINIMIZE)); - } - - private void showMaxIcon() { - maxMinButton.toggleIcon(FontAwesome.EXPAND); - maxMinButton.setData(Boolean.FALSE); - maxMinButton.setDescription(i18n.getMessage(UIMessageIdProvider.TOOLTIP_MAXIMIZE)); - } - - private boolean isMaximized() { - return artifactUploadState.isArtifactDetailsMaximized(); - } - -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/event/RefreshSoftwareModuleByFilterEvent.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/event/RefreshSoftwareModuleByFilterEvent.java deleted file mode 100644 index 88afbe741..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/event/RefreshSoftwareModuleByFilterEvent.java +++ /dev/null @@ -1,20 +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.artifacts.event; - -import org.eclipse.hawkbit.ui.common.table.BaseUIEvent; - -/** - * Software module filter event. Is thrown when there is a filter action on a - * software module table on the Distribution or Upload View. It is possible to - * filter by text or type. - */ -public class RefreshSoftwareModuleByFilterEvent extends BaseUIEvent { - -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/event/SoftwareModuleEvent.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/event/SoftwareModuleEvent.java deleted file mode 100644 index 8b8c6791f..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/event/SoftwareModuleEvent.java +++ /dev/null @@ -1,99 +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.artifacts.event; - -import java.util.Arrays; -import java.util.Collection; - -import org.eclipse.hawkbit.repository.model.SoftwareModule; -import org.eclipse.hawkbit.ui.common.table.BaseEntityEventType; -import org.eclipse.hawkbit.ui.common.table.BaseUIEntityEvent; - -/** - * TenantAwareEvent to represent software add, update or delete. - * - */ -public class SoftwareModuleEvent extends BaseUIEntityEvent { - - /** - * Software module events in the Upload UI. - * - */ - public enum SoftwareModuleEventType { - ARTIFACTS_CHANGED, ASSIGN_SOFTWARE_MODULE - } - - private SoftwareModuleEventType softwareModuleEventType; - - /** - * Creates software module event. - * - * @param entityEventType - * the event type - */ - public SoftwareModuleEvent(final BaseEntityEventType entityEventType) { - super(entityEventType, null); - } - - /** - * Creates software module event. - * - * @param entityEventType - * the event type - * @param softwareModule - * the module - */ - public SoftwareModuleEvent(final BaseEntityEventType entityEventType, final SoftwareModule softwareModule) { - super(entityEventType, softwareModule); - } - - /** - * Constructor - * - * @param eventType - * the event type - * @param entityIds - * the entity ids - */ - public SoftwareModuleEvent(final BaseEntityEventType eventType, final Collection entityIds) { - super(eventType, entityIds, SoftwareModule.class); - } - - /** - * Creates software module event. - * - * @param softwareModuleEventType - * the event type - * @param softwareModule - * the module - */ - public SoftwareModuleEvent(final SoftwareModuleEventType softwareModuleEventType, - final SoftwareModule softwareModule) { - super(null, softwareModule); - this.softwareModuleEventType = softwareModuleEventType; - } - - /** - * Creates software module event. - * - * @param softwareModuleEventType - * the event type - * @param softwareModuleId - * the id of the {@link SoftwareModule} - */ - public SoftwareModuleEvent(final SoftwareModuleEventType softwareModuleEventType, final long softwareModuleId) { - super(null, Arrays.asList(softwareModuleId), SoftwareModule.class); - this.softwareModuleEventType = softwareModuleEventType; - } - - public SoftwareModuleEventType getSoftwareModuleEventType() { - return softwareModuleEventType; - } - -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/event/SoftwareModuleTypeEvent.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/event/SoftwareModuleTypeEvent.java deleted file mode 100644 index 25dd93a73..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/event/SoftwareModuleTypeEvent.java +++ /dev/null @@ -1,51 +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.artifacts.event; - -import org.eclipse.hawkbit.repository.model.SoftwareModuleType; - -/** - * TenantAwareEvent to represent software module type add, update or delete. - */ -public class SoftwareModuleTypeEvent { - - /** - * Software module type events in the Upload UI. - */ - public enum SoftwareModuleTypeEnum { - ADD_SOFTWARE_MODULE_TYPE, UPDATE_SOFTWARE_MODULE_TYPE - } - - private SoftwareModuleType softwareModuleType; - - private final SoftwareModuleTypeEnum softwareModuleTypeEnum; - - /** - * @param softwareModuleTypeEnum - * @param softwareModuleType - */ - public SoftwareModuleTypeEvent(final SoftwareModuleTypeEnum softwareModuleTypeEnum, - final SoftwareModuleType softwareModuleType) { - this.softwareModuleTypeEnum = softwareModuleTypeEnum; - this.softwareModuleType = softwareModuleType; - } - - public SoftwareModuleTypeEnum getSoftwareModuleTypeEnum() { - return softwareModuleTypeEnum; - } - - public SoftwareModuleType getSoftwareModuleType() { - return softwareModuleType; - } - - public void setSoftwareModuleType(final SoftwareModuleType softwareModuleType) { - this.softwareModuleType = softwareModuleType; - } - -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/event/UploadArtifactUIEvent.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/event/UploadArtifactUIEvent.java deleted file mode 100644 index 8f4f526c6..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/event/UploadArtifactUIEvent.java +++ /dev/null @@ -1,19 +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.artifacts.event; - -/** - * TenantAwareEvent generated in Upload artifact UI. - * - * - * - */ -public enum UploadArtifactUIEvent { - HIDE_DROP_HINTS, HIDE_FILTER_BY_TYPE, SHOW_FILTER_BY_TYPE, DISCARD_DELETE_SOFTWARE, DISCARD_ALL_DELETE_SOFTWARE, DELETED_ALL_SOFTWARE, DISCARD_DELETE_SOFTWARE_TYPE, DISCARD_ALL_DELETE_SOFTWARE_TYPE, DELETED_ALL_SOFTWARE_TYPE -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/AddSmWindowController.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/AddSmWindowController.java new file mode 100644 index 000000000..0f6b5369c --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/AddSmWindowController.java @@ -0,0 +1,146 @@ +/** + * Copyright (c) 2020 Bosch.IO 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.artifacts.smtable; + +import javax.validation.ConstraintViolationException; + +import org.eclipse.hawkbit.repository.EntityFactory; +import org.eclipse.hawkbit.repository.SoftwareModuleManagement; +import org.eclipse.hawkbit.repository.builder.SoftwareModuleCreate; +import org.eclipse.hawkbit.repository.model.SoftwareModule; +import org.eclipse.hawkbit.ui.common.AbstractEntityWindowController; +import org.eclipse.hawkbit.ui.common.AbstractEntityWindowLayout; +import org.eclipse.hawkbit.ui.common.data.mappers.SoftwareModuleToProxyMapper; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxySoftwareModule; +import org.eclipse.hawkbit.ui.common.event.CommandTopics; +import org.eclipse.hawkbit.ui.common.event.EntityModifiedEventPayload; +import org.eclipse.hawkbit.ui.common.event.EntityModifiedEventPayload.EntityModifiedEventType; +import org.eclipse.hawkbit.ui.common.event.EventLayout; +import org.eclipse.hawkbit.ui.common.event.EventTopics; +import org.eclipse.hawkbit.ui.common.event.EventView; +import org.eclipse.hawkbit.ui.common.event.SelectionChangedEventPayload; +import org.eclipse.hawkbit.ui.common.event.SelectionChangedEventPayload.SelectionChangedEventType; +import org.eclipse.hawkbit.ui.utils.UINotification; +import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.StringUtils; +import org.vaadin.spring.events.EventBus.UIEventBus; + +/** + * Controller for populating and saving data in Add Software Module Window. + */ +public class AddSmWindowController extends AbstractEntityWindowController { + private static final Logger LOG = LoggerFactory.getLogger(AddSmWindowController.class); + + private final VaadinMessageSource i18n; + private final EntityFactory entityFactory; + private final UIEventBus eventBus; + private final UINotification uiNotification; + + private final SoftwareModuleManagement smManagement; + + private final SmWindowLayout layout; + + private final EventView view; + + /** + * Constructor + * + * @param i18n + * VaadinMessageSource + * @param entityFactory + * EntityFactory + * @param eventBus + * UIEventBus + * @param uiNotification + * UINotification + * @param smManagement + * SoftwareModuleManagement + * @param layout + * SoftwareModuleWindowLayout + * @param view + * EventView + */ + public AddSmWindowController(final VaadinMessageSource i18n, final EntityFactory entityFactory, + final UIEventBus eventBus, final UINotification uiNotification, final SoftwareModuleManagement smManagement, + final SmWindowLayout layout, final EventView view) { + this.i18n = i18n; + this.entityFactory = entityFactory; + this.eventBus = eventBus; + this.uiNotification = uiNotification; + + this.smManagement = smManagement; + + this.layout = layout; + + this.view = view; + } + + /** + * @return software module layout + */ + @Override + public AbstractEntityWindowLayout getLayout() { + return layout; + } + + @Override + protected ProxySoftwareModule buildEntityFromProxy(final ProxySoftwareModule proxyEntity) { + // We ignore the method parameter, because we are interested in the + // empty object, that we can populate with defaults + return new ProxySoftwareModule(); + } + + @Override + protected void persistEntity(final ProxySoftwareModule entity) { + final SoftwareModuleCreate smCreate = entityFactory.softwareModule().create() + .type(entity.getTypeInfo().getKey()).name(entity.getName()).version(entity.getVersion()) + .vendor(entity.getVendor()).description(entity.getDescription()); + + final SoftwareModule newSoftwareModule; + try { + newSoftwareModule = smManagement.create(smCreate); + } catch (final ConstraintViolationException ex) { + LOG.trace("Create of software module failed in UI: {}", ex.getMessage()); + uiNotification.displayValidationError( + i18n.getMessage("message.save.fail", entity.getName() + ":" + entity.getVersion())); + return; + } + + uiNotification.displaySuccess(i18n.getMessage("message.save.success", + newSoftwareModule.getName() + ":" + newSoftwareModule.getVersion())); + eventBus.publish(EventTopics.ENTITY_MODIFIED, this, new EntityModifiedEventPayload( + EntityModifiedEventType.ENTITY_ADDED, ProxySoftwareModule.class, newSoftwareModule.getId())); + + final ProxySoftwareModule addedItem = new SoftwareModuleToProxyMapper().map(newSoftwareModule); + eventBus.publish(CommandTopics.SELECT_GRID_ENTITY, this, new SelectionChangedEventPayload<>( + SelectionChangedEventType.ENTITY_SELECTED, addedItem, EventLayout.SM_LIST, view)); + } + + @Override + protected boolean isEntityValid(final ProxySoftwareModule entity) { + if (!StringUtils.hasText(entity.getName()) || !StringUtils.hasText(entity.getVersion()) + || entity.getTypeInfo() == null) { + uiNotification.displayValidationError(i18n.getMessage("message.error.missing.nameorversionortype")); + return false; + } + + final String trimmedName = StringUtils.trimWhitespace(entity.getName()); + final String trimmedVersion = StringUtils.trimWhitespace(entity.getVersion()); + final Long typeId = entity.getTypeInfo().getId(); + if (smManagement.getByNameAndVersionAndType(trimmedName, trimmedVersion, typeId).isPresent()) { + uiNotification.displayValidationError( + i18n.getMessage("message.duplicate.softwaremodule", trimmedName, trimmedVersion)); + return false; + } + + return true; + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/BaseSwModuleBeanQuery.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/BaseSwModuleBeanQuery.java deleted file mode 100644 index 53592acb6..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/BaseSwModuleBeanQuery.java +++ /dev/null @@ -1,133 +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.artifacts.smtable; - -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; - -import org.eclipse.hawkbit.repository.OffsetBasedPageRequest; -import org.eclipse.hawkbit.repository.SoftwareModuleManagement; -import org.eclipse.hawkbit.repository.model.SoftwareModule; -import org.eclipse.hawkbit.repository.model.SoftwareModuleType; -import org.eclipse.hawkbit.ui.common.UserDetailsFormatter; -import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil; -import org.eclipse.hawkbit.ui.utils.SPDateTimeUtil; -import org.eclipse.hawkbit.ui.utils.SPUIDefinitions; -import org.eclipse.hawkbit.ui.utils.SpringContextHelper; -import org.springframework.data.domain.Slice; -import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Direction; -import org.springframework.util.StringUtils; -import org.vaadin.addons.lazyquerycontainer.AbstractBeanQuery; -import org.vaadin.addons.lazyquerycontainer.QueryDefinition; - -/** - * Simple implementation of generics bean query which dynamically loads a batch - * of beans. - * - */ -public class BaseSwModuleBeanQuery extends AbstractBeanQuery { - private static final long serialVersionUID = 4362142538539335466L; - private transient SoftwareModuleManagement softwareManagementService; - private Long type; - private String searchText; - private final Sort sort = new Sort(Direction.ASC, "name", "version"); - - /** - * Parametric Constructor. - * - * @param definition - * as Def - * @param queryConfig - * as Config - * @param sortIds - * as sort - * @param sortStates - * as Sort status - */ - public BaseSwModuleBeanQuery(final QueryDefinition definition, final Map queryConfig, - final Object[] sortIds, final boolean[] sortStates) { - super(definition, queryConfig, sortIds, sortStates); - if (HawkbitCommonUtil.isNotNullOrEmpty(queryConfig)) { - type = Optional.ofNullable((SoftwareModuleType) queryConfig.get(SPUIDefinitions.BY_SOFTWARE_MODULE_TYPE)) - .map(SoftwareModuleType::getId).orElse(null); - searchText = (String) queryConfig.get(SPUIDefinitions.FILTER_BY_TEXT); - if (!StringUtils.isEmpty(searchText)) { - searchText = String.format("%%%s%%", searchText); - } - } - } - - @Override - protected ProxyBaseSoftwareModuleItem constructBean() { - return new ProxyBaseSoftwareModuleItem(); - } - - @Override - protected List loadBeans(final int startIndex, final int count) { - final Slice swModuleBeans; - - if (type == null && StringUtils.isEmpty(searchText)) { - swModuleBeans = getSoftwareManagementService().findAll(new OffsetBasedPageRequest(startIndex, count, sort)); - } else { - swModuleBeans = getSoftwareManagementService() - .findByTextAndType(new OffsetBasedPageRequest(startIndex, count, sort), searchText, type); - } - - return swModuleBeans.getContent().stream().map(this::getProxyBean).collect(Collectors.toList()); - } - - private ProxyBaseSoftwareModuleItem getProxyBean(final SoftwareModule bean) { - final ProxyBaseSoftwareModuleItem proxy = new ProxyBaseSoftwareModuleItem(); - proxy.setSwId(bean.getId()); - final String swNameVersion = HawkbitCommonUtil.concatStrings(":", bean.getName(), bean.getVersion()); - proxy.setNameAndVersion(swNameVersion); - proxy.setCreatedDate(SPDateTimeUtil.getFormattedDate(bean.getCreatedAt())); - proxy.setLastModifiedDate(SPDateTimeUtil.getFormattedDate(bean.getLastModifiedAt())); - proxy.setName(bean.getName()); - proxy.setVersion(bean.getVersion()); - proxy.setVendor(bean.getVendor()); - proxy.setDescription(bean.getDescription()); - proxy.setCreatedByUser(UserDetailsFormatter.loadAndFormatCreatedBy(bean)); - proxy.setModifiedByUser(UserDetailsFormatter.loadAndFormatLastModifiedBy(bean)); - return proxy; - } - - @Override - public int size() { - long size; - if (type == null && StringUtils.isEmpty(searchText)) { - size = getSoftwareManagementService().count(); - } else { - size = getSoftwareManagementService().countByTextAndType(searchText, type); - } - - if (size > Integer.MAX_VALUE) { - size = Integer.MAX_VALUE; - } - return (int) size; - - } - - @Override - protected void saveBeans(final List addedBeans, - final List modifiedBeans, - final List removedBeans) { - // save of the entity not required from this method - } - - private SoftwareModuleManagement getSoftwareManagementService() { - if (softwareManagementService == null) { - softwareManagementService = SpringContextHelper.getBean(SoftwareModuleManagement.class); - } - return softwareManagementService; - } -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/ProxyBaseSoftwareModuleItem.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/ProxyBaseSoftwareModuleItem.java deleted file mode 100644 index 5dd5bc521..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/ProxyBaseSoftwareModuleItem.java +++ /dev/null @@ -1,140 +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.artifacts.smtable; - -import java.security.SecureRandom; - -/** - * - * Proxy for software module to display details in Software modules table. - * - * - * - */ -public class ProxyBaseSoftwareModuleItem { - - private static final long serialVersionUID = -1555306616599140635L; - - private static final SecureRandom RANDOM_OBJ = new SecureRandom(); - - private String nameAndVersion; - - private Long swId; - - private boolean assigned; - - private String createdDate; - - private String lastModifiedDate; - - private String createdByUser; - - private String modifiedByUser; - - private String name; - private String version; - private String vendor; - private String description; - - /** - * Default constructor. - */ - public ProxyBaseSoftwareModuleItem() { - swId = RANDOM_OBJ.nextLong(); - } - - public String getName() { - return name; - } - - public void setName(final String name) { - this.name = name; - } - - public String getVersion() { - return version; - } - - public void setVersion(final String version) { - this.version = version; - } - - public String getVendor() { - return vendor; - } - - public void setVendor(final String vendor) { - this.vendor = vendor; - } - - public String getDescription() { - return description; - } - - public void setDescription(final String description) { - this.description = description; - } - - public String getCreatedByUser() { - return createdByUser; - } - - public Long getSwId() { - return swId; - } - - public void setCreatedByUser(final String createdByUser) { - this.createdByUser = createdByUser; - } - - public void setSwId(final Long swId) { - this.swId = swId; - } - - public String getModifiedByUser() { - return modifiedByUser; - } - - public void setModifiedByUser(final String modifiedByUser) { - this.modifiedByUser = modifiedByUser; - } - - public String getCreatedDate() { - return createdDate; - } - - public void setCreatedDate(final String createdDate) { - this.createdDate = createdDate; - } - - public String getLastModifiedDate() { - return lastModifiedDate; - } - - public void setLastModifiedDate(final String lastModifiedDate) { - this.lastModifiedDate = lastModifiedDate; - } - - public String getNameAndVersion() { - return nameAndVersion; - } - - public void setNameAndVersion(final String nameAndVersion) { - this.nameAndVersion = nameAndVersion; - } - - public boolean isAssigned() { - return assigned; - } - - public void setAssigned(final boolean assigned) { - this.assigned = assigned; - } - -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SmMetaDataAddUpdateWindowLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SmMetaDataAddUpdateWindowLayout.java new file mode 100644 index 000000000..b80748c27 --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SmMetaDataAddUpdateWindowLayout.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2020 Bosch.IO 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.artifacts.smtable; + +import org.eclipse.hawkbit.ui.common.detailslayout.MetaDataAddUpdateWindowLayout; +import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; + +import com.vaadin.ui.CheckBox; +import com.vaadin.ui.ComponentContainer; + +/** + * Class for sm metadata add/update window layout. + */ +public class SmMetaDataAddUpdateWindowLayout extends MetaDataAddUpdateWindowLayout { + protected final CheckBox isVisibleForTarget; + + /** + * Constructor for AbstractTagWindowLayout + * + * @param i18n + * I18N + */ + public SmMetaDataAddUpdateWindowLayout(final VaadinMessageSource i18n) { + super(i18n); + + this.isVisibleForTarget = metaDataComponentBuilder.createVisibleForTargetsField(binder); + } + + /** + * @return form layout checkbox container for software module + */ + @Override + public ComponentContainer getRootComponent() { + final ComponentContainer formLayout = super.getRootComponent(); + + formLayout.addComponent(isVisibleForTarget); + + return formLayout; + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SmMetaDataWindowBuilder.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SmMetaDataWindowBuilder.java new file mode 100644 index 000000000..25803455a --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SmMetaDataWindowBuilder.java @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2020 Bosch.IO 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.artifacts.smtable; + +import org.eclipse.hawkbit.repository.EntityFactory; +import org.eclipse.hawkbit.repository.SoftwareModuleManagement; +import org.eclipse.hawkbit.ui.SpPermissionChecker; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyMetaData; +import org.eclipse.hawkbit.ui.common.detailslayout.AbstractMetaDataWindowBuilder; +import org.eclipse.hawkbit.ui.utils.UINotification; +import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; +import org.vaadin.spring.events.EventBus.UIEventBus; + +import com.vaadin.ui.Window; + +/** + * Builder for Software module meta data windows + */ +public class SmMetaDataWindowBuilder extends AbstractMetaDataWindowBuilder { + private final EntityFactory entityFactory; + private final UIEventBus eventBus; + private final UINotification uiNotification; + private final SpPermissionChecker permChecker; + + private final SoftwareModuleManagement smManagement; + + /** + * Constructor for SmMetaDataWindowBuilder + * + * @param i18n + * VaadinMessageSource + * @param entityFactory + * EntityFactory + * @param eventBus + * UIEventBus + * @param uiNotification + * UINotification + * @param permChecker + * SpPermissionChecker + * @param smManagement + * SoftwareModuleManagement + */ + public SmMetaDataWindowBuilder(final VaadinMessageSource i18n, final EntityFactory entityFactory, + final UIEventBus eventBus, final UINotification uiNotification, final SpPermissionChecker permChecker, + final SoftwareModuleManagement smManagement) { + super(i18n); + + this.entityFactory = entityFactory; + this.eventBus = eventBus; + this.uiNotification = uiNotification; + this.permChecker = permChecker; + + this.smManagement = smManagement; + } + + /** + * Get software module window without proxy meta data + * + * @param smId + * software module ID + * @param name + * Selected entity name + * + * @return software module window + */ + public Window getWindowForShowSmMetaData(final Long smId, final String name) { + return getWindowForShowSmMetaData(smId, name, null); + } + + /** + * Get software module window with proxy meta data + * + * @param smId + * software module ID + * @param name + * Selected entity name + * @param proxyMetaData + * ProxyMetaData + * + * @return software module window + */ + public Window getWindowForShowSmMetaData(final Long smId, final String name, final ProxyMetaData proxyMetaData) { + return getWindowForShowMetaData( + new SmMetaDataWindowLayout(i18n, eventBus, permChecker, uiNotification, entityFactory, smManagement), + smId, name, proxyMetaData); + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SmMetaDataWindowLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SmMetaDataWindowLayout.java new file mode 100644 index 000000000..2c6d9a8ca --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SmMetaDataWindowLayout.java @@ -0,0 +1,143 @@ +/** + * Copyright (c) 2020 Bosch.IO 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.artifacts.smtable; + +import org.eclipse.hawkbit.repository.EntityFactory; +import org.eclipse.hawkbit.repository.SoftwareModuleManagement; +import org.eclipse.hawkbit.repository.model.MetaData; +import org.eclipse.hawkbit.ui.SpPermissionChecker; +import org.eclipse.hawkbit.ui.common.data.providers.SmMetaDataDataProvider; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyMetaData; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxySoftwareModule; +import org.eclipse.hawkbit.ui.common.detailslayout.AbstractMetaDataWindowLayout; +import org.eclipse.hawkbit.ui.common.detailslayout.AddMetaDataWindowController; +import org.eclipse.hawkbit.ui.common.detailslayout.MetaDataAddUpdateWindowLayout; +import org.eclipse.hawkbit.ui.common.detailslayout.MetaDataWindowGrid; +import org.eclipse.hawkbit.ui.common.detailslayout.UpdateMetaDataWindowController; +import org.eclipse.hawkbit.ui.common.event.EntityModifiedEventPayload; +import org.eclipse.hawkbit.ui.common.event.EntityModifiedEventPayload.EntityModifiedEventType; +import org.eclipse.hawkbit.ui.common.event.EventTopics; +import org.eclipse.hawkbit.ui.utils.UINotification; +import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; +import org.vaadin.spring.events.EventBus.UIEventBus; + +/** + * Class for metadata add/update window layout. + */ +public class SmMetaDataWindowLayout extends AbstractMetaDataWindowLayout { + private static final long serialVersionUID = 1L; + + private final transient SoftwareModuleManagement smManagement; + private final transient EntityFactory entityFactory; + + private final MetaDataWindowGrid smMetaDataWindowGrid; + + private final transient SmMetaDataAddUpdateWindowLayout smMetaDataAddUpdateWindowLayout; + private final transient AddMetaDataWindowController addSmMetaDataWindowController; + private final transient UpdateMetaDataWindowController updateSmMetaDataWindowController; + + /** + * Constructor for AbstractTagWindowLayout + * + * @param i18n + * VaadinMessageSource + * @param eventBus + * UIEventBus + * @param permChecker + * SpPermissionChecker + * @param uiNotification + * UINotification + * @param entityFactory + * EntityFactory + * @param smManagement + * SoftwareModuleManagement + */ + public SmMetaDataWindowLayout(final VaadinMessageSource i18n, final UIEventBus eventBus, + final SpPermissionChecker permChecker, final UINotification uiNotification, + final EntityFactory entityFactory, final SoftwareModuleManagement smManagement) { + super(i18n, eventBus, uiNotification, permChecker); + + this.smManagement = smManagement; + this.entityFactory = entityFactory; + + this.smMetaDataWindowGrid = new MetaDataWindowGrid<>(i18n, eventBus, permChecker, uiNotification, + new SmMetaDataDataProvider(smManagement), this::deleteMetaData); + + this.smMetaDataAddUpdateWindowLayout = new SmMetaDataAddUpdateWindowLayout(i18n); + this.addSmMetaDataWindowController = new AddMetaDataWindowController(i18n, uiNotification, + smMetaDataAddUpdateWindowLayout, this::createMetaData, this::isDuplicate); + this.updateSmMetaDataWindowController = new UpdateMetaDataWindowController(i18n, uiNotification, + smMetaDataAddUpdateWindowLayout, this::updateMetaData); + + buildLayout(); + addGridSelectionListener(); + } + + @Override + protected String getEntityType() { + return i18n.getMessage("caption.software.module"); + } + + @Override + protected MetaData doCreateMetaData(final ProxyMetaData entity) { + return smManagement.createMetaData(entityFactory.softwareModuleMetadata().create(masterEntityFilter) + .key(entity.getKey()).value(entity.getValue()).targetVisible(entity.isVisibleForTargets())); + } + + @Override + protected MetaData doUpdateMetaData(final ProxyMetaData entity) { + return smManagement + .updateMetaData(entityFactory.softwareModuleMetadata().update(masterEntityFilter, entity.getKey()) + .value(entity.getValue()).targetVisible(entity.isVisibleForTargets())); + } + + @Override + protected void doDeleteMetaDataByKey(final String metaDataKey) { + smManagement.deleteMetaData(masterEntityFilter, metaDataKey); + } + + private boolean isDuplicate(final String metaDataKey) { + return smManagement.getMetaDataBySoftwareModuleId(masterEntityFilter, metaDataKey).isPresent(); + } + + @Override + protected MetaDataWindowGrid getMetaDataWindowGrid() { + return smMetaDataWindowGrid; + } + + /** + * @return add widow controller for software module + */ + @Override + public AddMetaDataWindowController getAddMetaDataWindowController() { + return addSmMetaDataWindowController; + } + + /** + * @return update widow controller for software module + */ + @Override + public UpdateMetaDataWindowController getUpdateMetaDataWindowController() { + return updateSmMetaDataWindowController; + } + + /** + * @return add and update widow layout for software module + */ + @Override + public MetaDataAddUpdateWindowLayout getMetaDataAddUpdateWindowLayout() { + return smMetaDataAddUpdateWindowLayout; + } + + @Override + protected void publishEntityModifiedEvent() { + eventBus.publish(EventTopics.ENTITY_MODIFIED, this, new EntityModifiedEventPayload( + EntityModifiedEventType.ENTITY_UPDATED, ProxySoftwareModule.class, masterEntityFilter)); + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SmWindowBuilder.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SmWindowBuilder.java new file mode 100644 index 000000000..91aead49b --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SmWindowBuilder.java @@ -0,0 +1,96 @@ +/** + * Copyright (c) 2020 Bosch.IO 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.artifacts.smtable; + +import org.eclipse.hawkbit.repository.EntityFactory; +import org.eclipse.hawkbit.repository.SoftwareModuleManagement; +import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement; +import org.eclipse.hawkbit.ui.common.AbstractEntityWindowBuilder; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxySoftwareModule; +import org.eclipse.hawkbit.ui.common.event.EventView; +import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; +import org.eclipse.hawkbit.ui.utils.UINotification; +import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; +import org.vaadin.spring.events.EventBus.UIEventBus; + +import com.vaadin.ui.Window; + +/** + * Builder for Software module windows + */ +public class SmWindowBuilder extends AbstractEntityWindowBuilder { + private final EntityFactory entityFactory; + private final UIEventBus eventBus; + private final UINotification uiNotification; + + private final SoftwareModuleManagement smManagement; + private final SoftwareModuleTypeManagement smTypeManagement; + + private final EventView view; + + /** + * Constructor for SmWindowBuilder + * + * @param i18n + * VaadinMessageSource + * @param entityFactory + * EntityFactory + * @param eventBus + * UIEventBus + * @param uiNotification + * UINotification + * @param smManagement + * SoftwareModuleManagement + * @param smTypeManagement + * SoftwareModuleTypeManagement + * @param view + * EventView + */ + public SmWindowBuilder(final VaadinMessageSource i18n, final EntityFactory entityFactory, final UIEventBus eventBus, + final UINotification uiNotification, final SoftwareModuleManagement smManagement, + final SoftwareModuleTypeManagement smTypeManagement, final EventView view) { + super(i18n); + + this.entityFactory = entityFactory; + this.eventBus = eventBus; + this.uiNotification = uiNotification; + + this.smManagement = smManagement; + this.smTypeManagement = smTypeManagement; + + this.view = view; + } + + @Override + protected String getWindowId() { + return UIComponentIdProvider.CREATE_POPUP_ID; + } + + /** + * @return add window for software module + */ + @Override + public Window getWindowForAdd() { + return getWindowForNewEntity(new AddSmWindowController(i18n, entityFactory, eventBus, uiNotification, + smManagement, new SmWindowLayout(i18n, smTypeManagement), view)); + + } + + /** + * @param proxySm + * ProxySoftwareModule + * + * @return update window for software module + */ + @Override + public Window getWindowForUpdate(final ProxySoftwareModule proxySm) { + return getWindowForEntity(proxySm, new UpdateSmWindowController(i18n, entityFactory, eventBus, uiNotification, + smManagement, new SmWindowLayout(i18n, smTypeManagement))); + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SmWindowLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SmWindowLayout.java new file mode 100644 index 000000000..0c78afbd7 --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SmWindowLayout.java @@ -0,0 +1,105 @@ +/** + * Copyright (c) 2020 Bosch.IO 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.artifacts.smtable; + +import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement; +import org.eclipse.hawkbit.repository.model.SoftwareModuleType; +import org.eclipse.hawkbit.ui.common.AbstractEntityWindowLayout; +import org.eclipse.hawkbit.ui.common.data.mappers.TypeToTypeInfoMapper; +import org.eclipse.hawkbit.ui.common.data.providers.SoftwareModuleTypeDataProvider; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxySoftwareModule; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyTypeInfo; +import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; + +import com.vaadin.ui.ComboBox; +import com.vaadin.ui.ComponentContainer; +import com.vaadin.ui.FormLayout; +import com.vaadin.ui.TextArea; +import com.vaadin.ui.TextField; + +/** + * Target add/update window layout. + */ +public class SmWindowLayout extends AbstractEntityWindowLayout { + private final SmWindowLayoutComponentBuilder smComponentBuilder; + + private final ComboBox smTypeSelect; + private final TextField smName; + private final TextField smVersion; + private final TextField smVendor; + private final TextArea smDescription; + + /** + * Constructor for AbstractTagWindowLayout + * + * @param i18n + * VaadinMessageSource + * @param smTypeManagement + * SoftwareModuleTypeManagement + */ + public SmWindowLayout(final VaadinMessageSource i18n, final SoftwareModuleTypeManagement smTypeManagement) { + super(); + + final SoftwareModuleTypeDataProvider smTypeDataProvider = new SoftwareModuleTypeDataProvider<>( + smTypeManagement, new TypeToTypeInfoMapper()); + this.smComponentBuilder = new SmWindowLayoutComponentBuilder(i18n, smTypeDataProvider); + + this.smTypeSelect = smComponentBuilder.createSoftwareModuleTypeCombo(binder); + this.smName = smComponentBuilder.createNameField(binder); + this.smVersion = smComponentBuilder.createVersionField(binder); + this.smVendor = smComponentBuilder.createVendorField(binder); + this.smDescription = smComponentBuilder.createDescription(binder); + } + + /** + * @return software module window layout + */ + @Override + public ComponentContainer getRootComponent() { + final FormLayout smWindowLayout = new FormLayout(); + + smWindowLayout.setSpacing(true); + smWindowLayout.setMargin(true); + smWindowLayout.setSizeUndefined(); + + smWindowLayout.addComponent(smTypeSelect); + + smWindowLayout.addComponent(smName); + smName.focus(); + + smWindowLayout.addComponent(smVersion); + + smWindowLayout.addComponent(smVendor); + + smWindowLayout.addComponent(smDescription); + + return smWindowLayout; + } + + /** + * Disable the software module type + */ + public void disableSmTypeSelect() { + smTypeSelect.setEnabled(false); + } + + /** + * Disable the software module name + */ + public void disableNameField() { + smName.setEnabled(false); + } + + /** + * Disable the software module version + */ + public void disableVersionField() { + smVersion.setEnabled(false); + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SmWindowLayoutComponentBuilder.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SmWindowLayoutComponentBuilder.java new file mode 100644 index 000000000..21f63022e --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SmWindowLayoutComponentBuilder.java @@ -0,0 +1,116 @@ +/** + * Copyright (c) 2020 Bosch.IO 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.artifacts.smtable; + +import org.eclipse.hawkbit.repository.model.SoftwareModule; +import org.eclipse.hawkbit.ui.common.builder.FormComponentBuilder; +import org.eclipse.hawkbit.ui.common.builder.TextFieldBuilder; +import org.eclipse.hawkbit.ui.common.data.providers.SoftwareModuleTypeDataProvider; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxySoftwareModule; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyTypeInfo; +import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; +import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; + +import com.vaadin.data.Binder; +import com.vaadin.ui.ComboBox; +import com.vaadin.ui.TextArea; +import com.vaadin.ui.TextField; + +/** + * Builder for software module window layout + */ +public class SmWindowLayoutComponentBuilder { + + public static final String TEXTFIELD_VENDOR = "textfield.vendor"; + + private final VaadinMessageSource i18n; + private final SoftwareModuleTypeDataProvider smTypeDataProvider; + + /** + * Constructor for SmWindowLayoutComponentBuilder + * + * @param i18n + * VaadinMessageSource + * @param smTypeDataProvider + * SoftwareModuleTypeDataProvider + */ + public SmWindowLayoutComponentBuilder(final VaadinMessageSource i18n, + final SoftwareModuleTypeDataProvider smTypeDataProvider) { + this.i18n = i18n; + this.smTypeDataProvider = smTypeDataProvider; + } + + /** + * Create combo box options for software module types + * + * @param binder + * binder the input will be bound to + * + * @return input component + */ + public ComboBox createSoftwareModuleTypeCombo(final Binder binder) { + return FormComponentBuilder + .createTypeCombo(binder, smTypeDataProvider, i18n, UIComponentIdProvider.SW_MODULE_TYPE).getComponent(); + } + + /** + * create name field + * + * @param binder + * binder the input will be bound to + * @return input component + */ + public TextField createNameField(final Binder binder) { + return FormComponentBuilder.createNameInput(binder, i18n, UIComponentIdProvider.SOFT_MODULE_NAME) + .getComponent(); + } + + /** + * create version field + * + * @param binder + * binder the input will be bound to + * @return input component + */ + public TextField createVersionField(final Binder binder) { + return FormComponentBuilder.createVersionInput(binder, i18n, UIComponentIdProvider.SOFT_MODULE_VERSION) + .getComponent(); + } + + /** + * Create vendor field + * + * @param binder + * binder the input will be bound to + * + * @return input component + */ + public TextField createVendorField(final Binder binder) { + final TextField smVendor = new TextFieldBuilder(SoftwareModule.VENDOR_MAX_SIZE) + .id(UIComponentIdProvider.SOFT_MODULE_VENDOR).caption(i18n.getMessage(TEXTFIELD_VENDOR)) + .prompt(i18n.getMessage(TEXTFIELD_VENDOR)).buildTextComponent(); + smVendor.setSizeUndefined(); + + binder.forField(smVendor).bind(ProxySoftwareModule::getVendor, ProxySoftwareModule::setVendor); + + return smVendor; + } + + /** + * create description field + * + * @param binder + * binder the input will be bound to + * @return input component + */ + public TextArea createDescription(final Binder binder) { + return FormComponentBuilder + .createDescriptionInput(binder, i18n, UIComponentIdProvider.ADD_SW_MODULE_DESCRIPTION).getComponent(); + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleAddUpdateWindow.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleAddUpdateWindow.java deleted file mode 100644 index f5bab1e8d..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleAddUpdateWindow.java +++ /dev/null @@ -1,317 +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.artifacts.smtable; - -import java.util.Optional; - -import javax.validation.ConstraintViolationException; - -import org.eclipse.hawkbit.repository.EntityFactory; -import org.eclipse.hawkbit.repository.SoftwareModuleManagement; -import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement; -import org.eclipse.hawkbit.repository.builder.SoftwareModuleCreate; -import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; -import org.eclipse.hawkbit.repository.model.SoftwareModule; -import org.eclipse.hawkbit.repository.model.SoftwareModuleType; -import org.eclipse.hawkbit.ui.artifacts.event.SoftwareModuleEvent; -import org.eclipse.hawkbit.ui.common.CommonDialogWindow; -import org.eclipse.hawkbit.ui.common.CommonDialogWindow.SaveDialogCloseListener; -import org.eclipse.hawkbit.ui.common.SoftwareModuleTypeBeanQuery; -import org.eclipse.hawkbit.ui.common.builder.LabelBuilder; -import org.eclipse.hawkbit.ui.common.builder.TextAreaBuilder; -import org.eclipse.hawkbit.ui.common.builder.TextFieldBuilder; -import org.eclipse.hawkbit.ui.common.builder.WindowBuilder; -import org.eclipse.hawkbit.ui.common.table.AbstractTable; -import org.eclipse.hawkbit.ui.common.table.BaseEntityEventType; -import org.eclipse.hawkbit.ui.components.SPUIComponentProvider; -import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil; -import org.eclipse.hawkbit.ui.utils.SPUIDefinitions; -import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions; -import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; -import org.eclipse.hawkbit.ui.utils.UIMessageIdProvider; -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.spring.events.EventBus; -import org.vaadin.spring.events.EventBus.UIEventBus; - -import com.google.common.collect.Sets; -import com.vaadin.ui.ComboBox; -import com.vaadin.ui.CustomComponent; -import com.vaadin.ui.FormLayout; -import com.vaadin.ui.Label; -import com.vaadin.ui.TextArea; -import com.vaadin.ui.TextField; -import com.vaadin.ui.themes.ValoTheme; - -/** - * Generates window for Software module add or update. - */ -public class SoftwareModuleAddUpdateWindow extends CustomComponent { - - private static final Logger LOGGER = LoggerFactory.getLogger(SoftwareModuleAddUpdateWindow.class.getName()); - - private static final long serialVersionUID = 1L; - private final VaadinMessageSource i18n; - private final UINotification uiNotifcation; - private final transient EventBus.UIEventBus eventBus; - private final transient SoftwareModuleManagement softwareModuleManagement; - private final transient SoftwareModuleTypeManagement softwareModuleTypeManagement; - private final transient EntityFactory entityFactory; - private final AbstractTable softwareModuleTable; - private TextField nameTextField; - private TextField versionTextField; - private TextField vendorTextField; - private ComboBox typeComboBox; - private TextArea descTextArea; - private Boolean editSwModule = Boolean.FALSE; - private Long baseSwModuleId; - private FormLayout formLayout; - private Label softwareModuleType; - - /** - * Constructor for SoftwareModuleAddUpdateWindow - * - * @param i18n - * I18N - * @param uiNotifcation - * UINotification - * @param eventBus - * UIEventBus - * @param softwareModuleManagement - * management for {@link SoftwareModule}s - * @param softwareModuleTypeManagement - * management for {@link SoftwareModuleType}s - * @param entityFactory - * EntityFactory - */ - public SoftwareModuleAddUpdateWindow(final VaadinMessageSource i18n, final UINotification uiNotifcation, - final UIEventBus eventBus, final SoftwareModuleManagement softwareModuleManagement, - final SoftwareModuleTypeManagement softwareModuleTypeManagement, final EntityFactory entityFactory, - final AbstractTable softwareModuleTable) { - this.i18n = i18n; - this.uiNotifcation = uiNotifcation; - this.eventBus = eventBus; - this.softwareModuleManagement = softwareModuleManagement; - this.softwareModuleTypeManagement = softwareModuleTypeManagement; - this.entityFactory = entityFactory; - this.softwareModuleTable = softwareModuleTable; - - createRequiredComponents(); - } - - private void createRequiredComponents() { - - nameTextField = createTextField("textfield.name", UIComponentIdProvider.SOFT_MODULE_NAME, - SoftwareModule.NAME_MAX_SIZE); - - versionTextField = createTextField("textfield.version", UIComponentIdProvider.SOFT_MODULE_VERSION, - SoftwareModule.VERSION_MAX_SIZE); - - vendorTextField = new TextFieldBuilder(SoftwareModule.VENDOR_MAX_SIZE) - .caption(i18n.getMessage("textfield.vendor")).id(UIComponentIdProvider.SOFT_MODULE_VENDOR) - .buildTextComponent(); - - descTextArea = new TextAreaBuilder(SoftwareModule.DESCRIPTION_MAX_SIZE) - .caption(i18n.getMessage("textfield.description")).style("text-area-style") - .id(UIComponentIdProvider.ADD_SW_MODULE_DESCRIPTION).buildTextComponent(); - - typeComboBox = SPUIComponentProvider.getComboBox( - i18n.getMessage(UIMessageIdProvider.CAPTION_ARTIFACT_SOFTWARE_MODULE_TYPE), "", null, null, true, null, - i18n.getMessage(UIMessageIdProvider.CAPTION_ARTIFACT_SOFTWARE_MODULE_TYPE)); - typeComboBox.setId(UIComponentIdProvider.SW_MODULE_TYPE); - typeComboBox.setStyleName(SPUIDefinitions.COMBO_BOX_SPECIFIC_STYLE + " " + ValoTheme.COMBOBOX_TINY); - typeComboBox.setNewItemsAllowed(Boolean.FALSE); - typeComboBox.setImmediate(Boolean.TRUE); - } - - private TextField createTextField(final String in18Key, final String id, final int maxLength) { - return new TextFieldBuilder(maxLength).caption(i18n.getMessage(in18Key)).required(true, i18n).id(id) - .buildTextComponent(); - } - - /** - * Creates window for new software module. - * - * @return reference of {@link com.vaadin.ui.Window} to add new software - * module. - */ - public CommonDialogWindow createAddSoftwareModuleWindow() { - return createUpdateSoftwareModuleWindow(null); - } - - /** - * Creates window for update software module. - * - * @param baseSwModuleId - * id of the software module to edit. - * @return reference of {@link com.vaadin.ui.Window} to update software - * module. - */ - public CommonDialogWindow createUpdateSoftwareModuleWindow(final Long baseSwModuleId) { - this.baseSwModuleId = baseSwModuleId; - resetComponents(); - populateTypeNameCombo(); - populateValuesOfSwModule(); - return createWindow(); - } - - private void resetComponents() { - vendorTextField.clear(); - nameTextField.clear(); - versionTextField.clear(); - descTextArea.clear(); - if (!editSwModule) { - typeComboBox.clear(); - } - editSwModule = Boolean.FALSE; - } - - private void populateTypeNameCombo() { - typeComboBox.setContainerDataSource( - HawkbitCommonUtil.createLazyQueryContainer(new BeanQueryFactory<>(SoftwareModuleTypeBeanQuery.class))); - typeComboBox.setItemCaptionPropertyId(SPUILabelDefinitions.VAR_NAME); - } - - /** - * fill the data of a softwareModule in the content of the window - */ - private void populateValuesOfSwModule() { - if (baseSwModuleId == null) { - return; - } - editSwModule = Boolean.TRUE; - softwareModuleManagement.get(baseSwModuleId).ifPresent(swModule -> { - nameTextField.setValue(swModule.getName()); - versionTextField.setValue(swModule.getVersion()); - vendorTextField.setValue(swModule.getVendor()); - descTextArea.setValue(swModule.getDescription()); - softwareModuleType = new LabelBuilder().name(swModule.getType().getName()) - .caption(i18n.getMessage(UIMessageIdProvider.CAPTION_ARTIFACT_SOFTWARE_MODULE_TYPE)).buildLabel(); - }); - } - - private CommonDialogWindow createWindow() { - final Label madatoryStarLabel = new Label("*"); - madatoryStarLabel.setStyleName("v-caption v-required-field-indicator"); - madatoryStarLabel.setWidth(null); - addStyleName("lay-color"); - setSizeUndefined(); - - formLayout = new FormLayout(); - formLayout.setCaption(null); - if (editSwModule) { - formLayout.addComponent(softwareModuleType); - } else { - formLayout.addComponent(typeComboBox); - typeComboBox.focus(); - } - - formLayout.addComponent(nameTextField); - formLayout.addComponent(versionTextField); - formLayout.addComponent(vendorTextField); - formLayout.addComponent(descTextArea); - - setCompositionRoot(formLayout); - - final CommonDialogWindow window = new WindowBuilder(SPUIDefinitions.CREATE_UPDATE_WINDOW) - .caption(i18n.getMessage("caption.create.new", i18n.getMessage("caption.software.module"))) - .id(UIComponentIdProvider.SW_MODULE_CREATE_DIALOG).content(this).layout(formLayout).i18n(i18n) - .saveDialogCloseListener(new SaveOnDialogCloseListener()).buildCommonDialogWindow(); - nameTextField.setEnabled(!editSwModule); - versionTextField.setEnabled(!editSwModule); - - return window; - } - - public FormLayout getFormLayout() { - return formLayout; - } - - /** - * Save or update the sw module. - */ - private final class SaveOnDialogCloseListener implements SaveDialogCloseListener { - @Override - public boolean canWindowSaveOrUpdate() { - return editSwModule || !isDuplicate(); - } - - @Override - public void saveOrUpdate() { - if (editSwModule) { - updateSwModule(); - return; - } - addNewBaseSoftware(); - } - - /** - * updates a softwareModule - */ - private void updateSwModule() { - final SoftwareModule newSWModule = softwareModuleManagement.update(entityFactory.softwareModule() - .update(baseSwModuleId).description(descTextArea.getValue()).vendor(vendorTextField.getValue())); - if (newSWModule != null) { - uiNotifcation.displaySuccess(i18n.getMessage("message.save.success", - newSWModule.getName() + ":" + newSWModule.getVersion())); - - eventBus.publish(this, new SoftwareModuleEvent(BaseEntityEventType.UPDATED_ENTITY, newSWModule)); - } - } - - private void addNewBaseSoftware() { - final String name = nameTextField.getValue(); - final String version = versionTextField.getValue(); - final String vendor = vendorTextField.getValue(); - final String description = descTextArea.getValue(); - final String type = typeComboBox.getValue() != null ? typeComboBox.getValue().toString() : null; - - final SoftwareModuleType softwareModuleTypeByName = softwareModuleTypeManagement.getByName(type) - .orElseThrow(() -> new EntityNotFoundException(SoftwareModuleType.class, type)); - final SoftwareModuleCreate softwareModule = entityFactory.softwareModule().create() - .type(softwareModuleTypeByName).name(name).version(version).description(description).vendor(vendor); - final SoftwareModule newSoftwareModule; - try { - newSoftwareModule = softwareModuleManagement.create(softwareModule); - } catch (ConstraintViolationException ex) { - String message = "This SoftwareModuleName is not valid! " - + "Please choose a SoftwareModuleName without the characters /, ?, #, blank, and quota chars" - + ex.getMessage(); - LOGGER.warn(message, ex); - uiNotifcation.displayValidationError(i18n.getMessage("message.save.fail", name + ":" + version)); - return; - } - eventBus.publish(this, new SoftwareModuleEvent(BaseEntityEventType.ADD_ENTITY, newSoftwareModule)); - uiNotifcation.displaySuccess(i18n.getMessage("message.save.success", - newSoftwareModule.getName() + ":" + newSoftwareModule.getVersion())); - softwareModuleTable.setValue(Sets.newHashSet(newSoftwareModule.getId())); - } - - private boolean isDuplicate() { - final String name = nameTextField.getValue(); - final String version = versionTextField.getValue(); - final String type = typeComboBox.getValue() != null ? typeComboBox.getValue().toString() : null; - - final Optional moduleType = softwareModuleTypeManagement.getByName(type) - .map(SoftwareModuleType::getId); - if (moduleType.isPresent() && softwareModuleManagement - .getByNameAndVersionAndType(name, version, moduleType.get()).isPresent()) { - uiNotifcation - .displayValidationError(i18n.getMessage("message.duplicate.softwaremodule", name, version)); - return true; - } - return false; - } - - } - -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleDetails.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleDetails.java deleted file mode 100644 index ded36197c..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleDetails.java +++ /dev/null @@ -1,55 +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.artifacts.smtable; - -import org.eclipse.hawkbit.repository.SoftwareModuleManagement; -import org.eclipse.hawkbit.repository.model.SoftwareModule; -import org.eclipse.hawkbit.ui.SpPermissionChecker; -import org.eclipse.hawkbit.ui.artifacts.state.ArtifactUploadState; -import org.eclipse.hawkbit.ui.common.detailslayout.AbstractSoftwareModuleDetails; -import org.eclipse.hawkbit.ui.distributions.smtable.SwMetadataPopupLayout; -import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; -import org.vaadin.spring.events.EventBus.UIEventBus; - -/** - * Software module details. - */ -public class SoftwareModuleDetails extends AbstractSoftwareModuleDetails { - - private static final long serialVersionUID = 1L; - - private final ArtifactUploadState artifactUploadState; - - SoftwareModuleDetails(final VaadinMessageSource i18n, final UIEventBus eventBus, - final SpPermissionChecker permissionChecker, - final SoftwareModuleAddUpdateWindow softwareModuleAddUpdateWindow, - final ArtifactUploadState artifactUploadState, final SoftwareModuleManagement softwareManagement, - final SwMetadataPopupLayout swMetadataPopupLayout) { - super(i18n, eventBus, permissionChecker, null, softwareManagement, swMetadataPopupLayout, - softwareModuleAddUpdateWindow); - this.artifactUploadState = artifactUploadState; - restoreState(); - } - - @Override - protected boolean onLoadIsTableMaximized() { - return artifactUploadState.isSwModuleTableMaximized(); - } - - @Override - protected String getTabSheetId() { - return null; - } - - @Override - protected boolean isSoftwareModuleSelected(final SoftwareModule softwareModule) { - return compareSoftwareModulesById(softwareModule, artifactUploadState.getSelectedBaseSwModuleId().orElse(null)); - } - -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleGrid.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleGrid.java new file mode 100644 index 000000000..618611786 --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleGrid.java @@ -0,0 +1,305 @@ +/** + * Copyright (c) 2020 Bosch.IO 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.artifacts.smtable; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +import org.eclipse.hawkbit.repository.SoftwareModuleManagement; +import org.eclipse.hawkbit.ui.SpPermissionChecker; +import org.eclipse.hawkbit.ui.artifacts.upload.FileUploadProgress; +import org.eclipse.hawkbit.ui.common.builder.GridComponentBuilder; +import org.eclipse.hawkbit.ui.common.data.filters.SwFilterParams; +import org.eclipse.hawkbit.ui.common.data.mappers.AssignedSoftwareModuleToProxyMapper; +import org.eclipse.hawkbit.ui.common.data.mappers.SoftwareModuleToProxyMapper; +import org.eclipse.hawkbit.ui.common.data.providers.SoftwareModuleDataProvider; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyDistributionSet; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyIdentifiableEntity; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxySoftwareModule; +import org.eclipse.hawkbit.ui.common.event.EntityModifiedEventPayload; +import org.eclipse.hawkbit.ui.common.event.EntityModifiedEventPayload.EntityModifiedEventType; +import org.eclipse.hawkbit.ui.common.event.EventLayout; +import org.eclipse.hawkbit.ui.common.event.EventTopics; +import org.eclipse.hawkbit.ui.common.event.EventView; +import org.eclipse.hawkbit.ui.common.event.FilterType; +import org.eclipse.hawkbit.ui.common.grid.AbstractGrid; +import org.eclipse.hawkbit.ui.common.grid.support.DeleteSupport; +import org.eclipse.hawkbit.ui.common.grid.support.DragAndDropSupport; +import org.eclipse.hawkbit.ui.common.grid.support.FilterSupport; +import org.eclipse.hawkbit.ui.common.grid.support.MasterEntitySupport; +import org.eclipse.hawkbit.ui.common.grid.support.SelectionSupport; +import org.eclipse.hawkbit.ui.common.state.GridLayoutUiState; +import org.eclipse.hawkbit.ui.common.state.TypeFilterLayoutUiState; +import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; +import org.eclipse.hawkbit.ui.utils.UINotification; +import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; +import org.vaadin.spring.events.EventBus.UIEventBus; + +import com.vaadin.ui.Button; + +/** + * Software Module grid. + */ +public class SoftwareModuleGrid extends AbstractGrid { + private static final long serialVersionUID = 1L; + + private static final String SM_NAME_ID = "smName"; + private static final String SM_VERSION_ID = "smVersion"; + private static final String SM_DESC_ID = "smDescription"; + private static final String SM_VENDOR_ID = "smVendor"; + private static final String SM_DELETE_BUTTON_ID = "smDeleteButton"; + + private final UINotification notification; + + private final TypeFilterLayoutUiState smTypeFilterLayoutUiState; + private final GridLayoutUiState smGridLayoutUiState; + + private final transient SoftwareModuleManagement softwareModuleManagement; + private final transient SoftwareModuleToProxyMapper softwareModuleToProxyMapper; + + private final transient DeleteSupport swModuleDeleteSupport; + private transient MasterEntitySupport masterEntitySupport; + + private final Map numberOfArtifactUploadsForSm; + + /** + * Constructor for SoftwareModuleGrid + * + * @param eventBus + * UIEventBus + * @param i18n + * VaadinMessageSource + * @param permissionChecker + * SpPermissionChecker + * @param notification + * UINotification + * @param smTypeFilterLayoutUiState + * TypeFilterLayoutUiState + * @param smGridLayoutUiState + * GridLayoutUiState + * @param softwareModuleManagement + * SoftwareModuleManagement + * @param view + * EventView + */ + public SoftwareModuleGrid(final UIEventBus eventBus, final VaadinMessageSource i18n, + final SpPermissionChecker permissionChecker, final UINotification notification, + final TypeFilterLayoutUiState smTypeFilterLayoutUiState, final GridLayoutUiState smGridLayoutUiState, + final SoftwareModuleManagement softwareModuleManagement, final EventView view) { + super(i18n, eventBus, permissionChecker); + + this.smTypeFilterLayoutUiState = smTypeFilterLayoutUiState; + this.smGridLayoutUiState = smGridLayoutUiState; + this.notification = notification; + this.softwareModuleManagement = softwareModuleManagement; + this.softwareModuleToProxyMapper = new SoftwareModuleToProxyMapper(); + + setSelectionSupport(new SelectionSupport(this, eventBus, EventLayout.SM_LIST, view, + this::mapIdToProxyEntity, this::getSelectedEntityIdFromUiState, this::setSelectedEntityIdToUiState)); + if (smGridLayoutUiState.isMaximized()) { + getSelectionSupport().disableSelection(); + } else { + getSelectionSupport().enableMultiSelection(); + } + + this.swModuleDeleteSupport = new DeleteSupport<>(this, i18n, notification, + i18n.getMessage("caption.software.module"), ProxySoftwareModule::getNameAndVersion, + this::deleteSoftwareModules, UIComponentIdProvider.SM_DELETE_CONFIRMATION_DIALOG); + + setFilterSupport(new FilterSupport<>( + new SoftwareModuleDataProvider(softwareModuleManagement, + new AssignedSoftwareModuleToProxyMapper(softwareModuleToProxyMapper)), + getSelectionSupport()::deselectAll)); + initFilterMappings(); + getFilterSupport().setFilter(new SwFilterParams()); + + this.numberOfArtifactUploadsForSm = new HashMap<>(); + } + + private void initFilterMappings() { + getFilterSupport().addMapping(FilterType.SEARCH, SwFilterParams::setSearchText, + smGridLayoutUiState.getSearchFilter()); + getFilterSupport().addMapping(FilterType.TYPE, SwFilterParams::setSoftwareModuleTypeId, + smTypeFilterLayoutUiState.getClickedTypeId()); + } + + /** + * Initial method of grid and set style name + */ + @Override + public void init() { + super.init(); + + addStyleName("grid-row-border"); + } + + /** + * Map SoftwareModule to Proxy type + * + * @param entityId + * EntityId Long type + * + * @return ProxySoftwareModule + */ + public Optional mapIdToProxyEntity(final long entityId) { + return softwareModuleManagement.get(entityId).map(softwareModuleToProxyMapper::map); + } + + private Long getSelectedEntityIdFromUiState() { + return smGridLayoutUiState.getSelectedEntityId(); + } + + private void setSelectedEntityIdToUiState(final Long entityId) { + smGridLayoutUiState.setSelectedEntityId(entityId); + } + + private boolean deleteSoftwareModules(final Collection swModulesToBeDeleted) { + final Collection swModuleToBeDeletedIds = swModulesToBeDeleted.stream() + .map(ProxyIdentifiableEntity::getId).collect(Collectors.toList()); + if (isUploadInProgressForSoftwareModule(swModuleToBeDeletedIds)) { + notification.displayValidationError(i18n.getMessage("message.error.swModule.notDeleted")); + + return false; + } + + softwareModuleManagement.delete(swModuleToBeDeletedIds); + + eventBus.publish(EventTopics.ENTITY_MODIFIED, this, new EntityModifiedEventPayload( + EntityModifiedEventType.ENTITY_REMOVED, ProxySoftwareModule.class, swModuleToBeDeletedIds)); + + return true; + } + + private boolean isUploadInProgressForSoftwareModule(final Collection swModuleToBeDeletedIds) { + return swModuleToBeDeletedIds.stream().anyMatch( + smId -> numberOfArtifactUploadsForSm.containsKey(smId) && numberOfArtifactUploadsForSm.get(smId) > 0); + } + + /** + * Artifacts upload process + * + * @param fileUploadProgress + * FileUploadProgress + */ + public void onUploadChanged(final FileUploadProgress fileUploadProgress) { + final FileUploadProgress.FileUploadStatus uploadProgressEventType = fileUploadProgress.getFileUploadStatus(); + final Long fileUploadSmId = fileUploadProgress.getFileUploadId().getSoftwareModuleId(); + + if (fileUploadSmId == null) { + return; + } + + if (FileUploadProgress.FileUploadStatus.UPLOAD_STARTED == uploadProgressEventType) { + numberOfArtifactUploadsForSm.merge(fileUploadSmId, 1, Integer::sum); + } + + if (FileUploadProgress.FileUploadStatus.UPLOAD_FINISHED == uploadProgressEventType) { + numberOfArtifactUploadsForSm.computeIfPresent(fileUploadSmId, (smId, oldCount) -> { + final Integer newCount = oldCount - 1; + return newCount.equals(0) ? null : newCount; + }); + } + } + + /** + * Drag and drop for grid elements + */ + public void addDragAndDropSupport() { + setDragAndDropSupportSupport( + new DragAndDropSupport<>(this, i18n, notification, Collections.emptyMap(), eventBus)); + if (!smGridLayoutUiState.isMaximized()) { + getDragAndDropSupportSupport().addDragSource(); + } + } + + /** + * Add master filter type + */ + public void addMasterSupport() { + getFilterSupport().addMapping(FilterType.MASTER, SwFilterParams::setLastSelectedDistributionId); + + masterEntitySupport = new MasterEntitySupport<>(getFilterSupport()); + + initMasterDsStyleGenerator(); + } + + private void initMasterDsStyleGenerator() { + setStyleGenerator(sm -> { + if (masterEntitySupport.getMasterId() == null || !sm.isAssigned()) { + return null; + } + + return String.join("-", UIComponentIdProvider.SM_TYPE_COLOR_CLASS, + String.valueOf(sm.getTypeInfo().getId())); + }); + } + + /** + * @return gridId of software module grid + */ + @Override + public String getGridId() { + return UIComponentIdProvider.SOFTWARE_MODULE_TABLE; + } + + /** + * Add columns to Grid + */ + @Override + public void addColumns() { + addNameColumn().setExpandRatio(2); + addVersionColumn(); + addDeleteColumn(); + } + + private Column addNameColumn() { + return GridComponentBuilder.addNameColumn(this, i18n, SM_NAME_ID); + } + + private Column addVersionColumn() { + return GridComponentBuilder.addVersionColumn(this, i18n, ProxySoftwareModule::getVersion, SM_VERSION_ID); + } + + private Column addDeleteColumn() { + return GridComponentBuilder.addDeleteColumn(this, i18n, SM_DELETE_BUTTON_ID, swModuleDeleteSupport, + UIComponentIdProvider.SM_DELET_ICON, e -> permissionChecker.hasDeleteRepositoryPermission()); + } + + @Override + protected void addMaxColumns() { + addNameColumn().setExpandRatio(7); + addVersionColumn(); + addDescriptionColumn().setExpandRatio(5); + addVendorColumn(); + GridComponentBuilder.addCreatedAndModifiedColumns(this, i18n); + addDeleteColumn(); + + getColumns().forEach(column -> column.setHidable(true)); + } + + private Column addDescriptionColumn() { + return GridComponentBuilder.addDescriptionColumn(this, i18n, SM_DESC_ID); + } + + private Column addVendorColumn() { + return GridComponentBuilder.addColumn(this, ProxySoftwareModule::getVendor).setId(SM_VENDOR_ID) + .setCaption(i18n.getMessage("header.vendor")); + } + + /** + * @return ProxyDistributionSet of master entity type + */ + public MasterEntitySupport getMasterEntitySupport() { + return masterEntitySupport; + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleGridHeader.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleGridHeader.java new file mode 100644 index 000000000..997951f83 --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleGridHeader.java @@ -0,0 +1,108 @@ +/** + * 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.artifacts.smtable; + +import org.eclipse.hawkbit.ui.SpPermissionChecker; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyIdentifiableEntity; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxySoftwareModule; +import org.eclipse.hawkbit.ui.common.event.EventLayout; +import org.eclipse.hawkbit.ui.common.event.EventView; +import org.eclipse.hawkbit.ui.common.grid.header.AbstractEntityGridHeader; +import org.eclipse.hawkbit.ui.common.state.GridLayoutUiState; +import org.eclipse.hawkbit.ui.common.state.HidableLayoutUiState; +import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; +import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; +import org.vaadin.spring.events.EventBus.UIEventBus; + +/** + * Header of Software module table. + */ +public class SoftwareModuleGridHeader extends AbstractEntityGridHeader { + private static final long serialVersionUID = 1L; + + private static final String SWM_TABLE_HEADER = "upload.swModuleTable.header"; + private static final String SWM_CAPTION = "caption.software.module"; + + /** + * Constructor for SoftwareModuleGridHeader + * + * @param i18n + * VaadinMessageSource + * @param permChecker + * SpPermissionChecker + * @param eventBus + * UIEventBus + * @param smTypeFilterLayoutUiState + * HidableLayoutUiState + * @param smGridLayoutUiState + * GridLayoutUiState + * @param smWindowBuilder + * SmWindowBuilder + * @param view + * EventView + */ + public SoftwareModuleGridHeader(final VaadinMessageSource i18n, final SpPermissionChecker permChecker, + final UIEventBus eventBus, final HidableLayoutUiState smTypeFilterLayoutUiState, + final GridLayoutUiState smGridLayoutUiState, final SmWindowBuilder smWindowBuilder, final EventView view) { + super(i18n, permChecker, eventBus, smTypeFilterLayoutUiState, smGridLayoutUiState, EventLayout.SM_TYPE_FILTER, + view); + + addAddHeaderSupport(smWindowBuilder); + } + + @Override + protected String getCaptionMsg() { + return SWM_TABLE_HEADER; + } + + @Override + protected String getSearchFieldId() { + return UIComponentIdProvider.SW_MODULE_SEARCH_TEXT_FIELD; + } + + @Override + protected String getSearchResetIconId() { + return UIComponentIdProvider.SW_MODULE_SEARCH_RESET_ICON; + } + + @Override + protected Class getEntityType() { + return ProxySoftwareModule.class; + } + + @Override + protected String getFilterButtonsIconId() { + return UIComponentIdProvider.SHOW_SM_TYPE_ICON; + } + + @Override + protected String getMaxMinIconId() { + return UIComponentIdProvider.SW_MAX_MIN_TABLE_ICON; + } + + @Override + protected EventLayout getLayout() { + return EventLayout.SM_LIST; + } + + @Override + protected boolean hasCreatePermission() { + return permChecker.hasCreateRepositoryPermission(); + } + + @Override + protected String getAddIconId() { + return UIComponentIdProvider.SW_MODULE_ADD_BUTTON; + } + + @Override + protected String getAddWindowCaptionMsg() { + return SWM_CAPTION; + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleGridLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleGridLayout.java new file mode 100644 index 000000000..ad5cbf95c --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleGridLayout.java @@ -0,0 +1,191 @@ +/** + * 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.artifacts.smtable; + +import java.util.Arrays; +import java.util.List; + +import org.eclipse.hawkbit.repository.EntityFactory; +import org.eclipse.hawkbit.repository.SoftwareModuleManagement; +import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement; +import org.eclipse.hawkbit.ui.SpPermissionChecker; +import org.eclipse.hawkbit.ui.artifacts.upload.FileUploadProgress; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxySoftwareModule; +import org.eclipse.hawkbit.ui.common.detailslayout.SoftwareModuleDetails; +import org.eclipse.hawkbit.ui.common.detailslayout.SoftwareModuleDetailsHeader; +import org.eclipse.hawkbit.ui.common.event.EventLayout; +import org.eclipse.hawkbit.ui.common.event.EventLayoutViewAware; +import org.eclipse.hawkbit.ui.common.event.EventTopics; +import org.eclipse.hawkbit.ui.common.event.EventView; +import org.eclipse.hawkbit.ui.common.event.EventViewAware; +import org.eclipse.hawkbit.ui.common.layout.AbstractGridComponentLayout; +import org.eclipse.hawkbit.ui.common.layout.MasterEntityAwareComponent; +import org.eclipse.hawkbit.ui.common.layout.listener.EntityModifiedListener; +import org.eclipse.hawkbit.ui.common.layout.listener.EntityModifiedListener.EntityModifiedAwareSupport; +import org.eclipse.hawkbit.ui.common.layout.listener.FilterChangedListener; +import org.eclipse.hawkbit.ui.common.layout.listener.GenericEventListener; +import org.eclipse.hawkbit.ui.common.layout.listener.SelectGridEntityListener; +import org.eclipse.hawkbit.ui.common.layout.listener.SelectionChangedListener; +import org.eclipse.hawkbit.ui.common.layout.listener.support.EntityModifiedGridRefreshAwareSupport; +import org.eclipse.hawkbit.ui.common.layout.listener.support.EntityModifiedSelectionAwareSupport; +import org.eclipse.hawkbit.ui.common.state.GridLayoutUiState; +import org.eclipse.hawkbit.ui.common.state.TypeFilterLayoutUiState; +import org.eclipse.hawkbit.ui.utils.UINotification; +import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; +import org.vaadin.spring.events.EventBus.UIEventBus; + +/** + * Software module table layout. (Upload Management) + */ +public class SoftwareModuleGridLayout extends AbstractGridComponentLayout { + private static final long serialVersionUID = 1L; + + private final SoftwareModuleGridHeader softwareModuleGridHeader; + private final SoftwareModuleGrid softwareModuleGrid; + private final SoftwareModuleDetailsHeader softwareModuleDetailsHeader; + private final SoftwareModuleDetails softwareModuleDetails; + + private final transient FilterChangedListener smFilterListener; + private final transient SelectionChangedListener masterSmChangedListener; + private final transient SelectGridEntityListener selectSmListener; + private final transient EntityModifiedListener smModifiedListener; + private final transient GenericEventListener fileUploadChangedListener; + + /** + * Constructor for SoftwareModuleGridLayout + * + * @param i18n + * VaadinMessageSource + * @param permChecker + * SpPermissionChecker + * @param uiNotification + * UINotification + * @param eventBus + * UIEventBus + * @param softwareModuleManagement + * SoftwareModuleManagement + * @param softwareModuleTypeManagement + * SoftwareModuleTypeManagement + * @param entityFactory + * EntityFactory + * @param smTypeFilterLayoutUiState + * TypeFilterLayoutUiState + * @param smGridLayoutUiState + * GridLayoutUiState + */ + public SoftwareModuleGridLayout(final VaadinMessageSource i18n, final SpPermissionChecker permChecker, + final UINotification uiNotification, final UIEventBus eventBus, + final SoftwareModuleManagement softwareModuleManagement, + final SoftwareModuleTypeManagement softwareModuleTypeManagement, final EntityFactory entityFactory, + final TypeFilterLayoutUiState smTypeFilterLayoutUiState, final GridLayoutUiState smGridLayoutUiState) { + super(); + + final SmWindowBuilder smWindowBuilder = new SmWindowBuilder(i18n, entityFactory, eventBus, uiNotification, + softwareModuleManagement, softwareModuleTypeManagement, EventView.UPLOAD); + final SmMetaDataWindowBuilder smMetaDataWindowBuilder = new SmMetaDataWindowBuilder(i18n, entityFactory, + eventBus, uiNotification, permChecker, softwareModuleManagement); + + this.softwareModuleGridHeader = new SoftwareModuleGridHeader(i18n, permChecker, eventBus, + smTypeFilterLayoutUiState, smGridLayoutUiState, smWindowBuilder, EventView.UPLOAD); + this.softwareModuleGridHeader.buildHeader(); + this.softwareModuleGrid = new SoftwareModuleGrid(eventBus, i18n, permChecker, uiNotification, + smTypeFilterLayoutUiState, smGridLayoutUiState, softwareModuleManagement, EventView.UPLOAD); + this.softwareModuleGrid.init(); + + this.softwareModuleDetailsHeader = new SoftwareModuleDetailsHeader(i18n, permChecker, eventBus, uiNotification, + smWindowBuilder, smMetaDataWindowBuilder); + this.softwareModuleDetailsHeader.buildHeader(); + this.softwareModuleDetails = new SoftwareModuleDetails(i18n, eventBus, softwareModuleManagement, + softwareModuleTypeManagement, smMetaDataWindowBuilder); + this.softwareModuleDetails.buildDetails(); + + this.smFilterListener = new FilterChangedListener<>(eventBus, ProxySoftwareModule.class, + new EventViewAware(EventView.UPLOAD), softwareModuleGrid.getFilterSupport()); + this.masterSmChangedListener = new SelectionChangedListener<>(eventBus, + new EventLayoutViewAware(EventLayout.SM_LIST, EventView.UPLOAD), getMasterSmAwareComponents()); + this.selectSmListener = new SelectGridEntityListener<>(eventBus, + new EventLayoutViewAware(EventLayout.SM_LIST, EventView.UPLOAD), + softwareModuleGrid.getSelectionSupport()); + this.smModifiedListener = new EntityModifiedListener.Builder<>(eventBus, ProxySoftwareModule.class) + .entityModifiedAwareSupports(getSmModifiedAwareSupports()).build(); + this.fileUploadChangedListener = new GenericEventListener<>(eventBus, EventTopics.FILE_UPLOAD_CHANGED, + this::onUploadChanged); + + buildLayout(softwareModuleGridHeader, softwareModuleGrid, softwareModuleDetailsHeader, softwareModuleDetails); + } + + private List> getMasterSmAwareComponents() { + return Arrays.asList(softwareModuleDetailsHeader, softwareModuleDetails); + } + + private List getSmModifiedAwareSupports() { + return Arrays.asList(EntityModifiedGridRefreshAwareSupport.of(softwareModuleGrid::refreshAll), + EntityModifiedSelectionAwareSupport.of(softwareModuleGrid.getSelectionSupport(), + softwareModuleGrid::mapIdToProxyEntity)); + } + + /** + * Verifies when file upload is in progress + * + * @param fileUploadProgress + * FileUploadProgress + */ + public void onUploadChanged(final FileUploadProgress fileUploadProgress) { + softwareModuleGrid.onUploadChanged(fileUploadProgress); + } + + /** + * Show software module grid header + */ + public void showSmTypeHeaderIcon() { + softwareModuleGridHeader.showFilterIcon(); + } + + /** + * Hide software module grid header + */ + public void hideSmTypeHeaderIcon() { + softwareModuleGridHeader.hideFilterIcon(); + } + + /** + * Maximize the software module grid + */ + public void maximize() { + softwareModuleGrid.createMaximizedContent(); + hideDetailsLayout(); + } + + /** + * Minimize the software module grid + */ + public void minimize() { + softwareModuleGrid.createMinimizedContent(); + showDetailsLayout(); + } + + /** + * Is called when view is shown to the user + */ + public void restoreState() { + softwareModuleGridHeader.restoreState(); + softwareModuleGrid.restoreState(); + } + + /** + * Unsubscribe all the events listeners + */ + public void unsubscribeListener() { + smFilterListener.unsubscribe(); + masterSmChangedListener.unsubscribe(); + selectSmListener.unsubscribe(); + smModifiedListener.unsubscribe(); + fileUploadChangedListener.unsubscribe(); + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleTable.java deleted file mode 100644 index 624397235..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleTable.java +++ /dev/null @@ -1,255 +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.artifacts.smtable; - -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; - -import org.eclipse.hawkbit.repository.SoftwareModuleManagement; -import org.eclipse.hawkbit.repository.model.SoftwareModule; -import org.eclipse.hawkbit.ui.SpPermissionChecker; -import org.eclipse.hawkbit.ui.artifacts.event.RefreshSoftwareModuleByFilterEvent; -import org.eclipse.hawkbit.ui.artifacts.event.SoftwareModuleEvent; -import org.eclipse.hawkbit.ui.artifacts.event.UploadArtifactUIEvent; -import org.eclipse.hawkbit.ui.artifacts.state.ArtifactUploadState; -import org.eclipse.hawkbit.ui.common.table.AbstractNamedVersionTable; -import org.eclipse.hawkbit.ui.common.table.BaseEntityEventType; -import org.eclipse.hawkbit.ui.dd.criteria.UploadViewClientCriterion; -import org.eclipse.hawkbit.ui.push.SoftwareModuleUpdatedEventContainer; -import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil; -import org.eclipse.hawkbit.ui.utils.SPUIDefinitions; -import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions; -import org.eclipse.hawkbit.ui.utils.TableColumn; -import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; -import org.eclipse.hawkbit.ui.utils.UINotification; -import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; -import org.eclipse.hawkbit.ui.view.filter.OnlyEventsFromUploadArtifactViewFilter; -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.Container; -import com.vaadin.data.Item; -import com.vaadin.event.dd.DragAndDropEvent; -import com.vaadin.event.dd.acceptcriteria.AcceptCriterion; -import com.vaadin.ui.UI; - -/** - * The Software module table. - */ -public class SoftwareModuleTable extends AbstractNamedVersionTable { - - private static final long serialVersionUID = 1L; - - private final ArtifactUploadState artifactUploadState; - - private final transient SoftwareModuleManagement softwareModuleManagement; - - private final UploadViewClientCriterion uploadViewClientCriterion; - - SoftwareModuleTable(final UIEventBus eventBus, final VaadinMessageSource i18n, final UINotification uiNotification, - final ArtifactUploadState artifactUploadState, final SoftwareModuleManagement softwareManagement, - final UploadViewClientCriterion uploadViewClientCriterion, final SpPermissionChecker permChecker) { - super(eventBus, i18n, uiNotification, permChecker); - this.artifactUploadState = artifactUploadState; - this.softwareModuleManagement = softwareManagement; - this.uploadViewClientCriterion = uploadViewClientCriterion; - - addNewContainerDS(); - setColumnProperties(); - setDataAvailable(getContainerDataSource().size() != 0); - } - - @EventBusListenerMethod(scope = EventScope.UI, filter = OnlyEventsFromUploadArtifactViewFilter.class) - void onEvent(final RefreshSoftwareModuleByFilterEvent filterEvent) { - UI.getCurrent().access(this::refreshFilter); - } - - @Override - protected String getTableId() { - return UIComponentIdProvider.UPLOAD_SOFTWARE_MODULE_TABLE; - } - - @Override - protected Container createContainer() { - final Map queryConfiguration = prepareQueryConfigFilters(); - - final BeanQueryFactory swQF = new BeanQueryFactory<>(BaseSwModuleBeanQuery.class); - swQF.setQueryConfiguration(queryConfiguration); - - return new LazyQueryContainer( - new LazyQueryDefinition(true, SPUIDefinitions.PAGE_SIZE, SPUILabelDefinitions.VAR_SWM_ID), swQF); - } - - private Map prepareQueryConfigFilters() { - final Map queryConfig = Maps.newHashMapWithExpectedSize(2); - artifactUploadState.getSoftwareModuleFilters().getSearchText() - .ifPresent(value -> queryConfig.put(SPUIDefinitions.FILTER_BY_TEXT, value)); - artifactUploadState.getSoftwareModuleFilters().getSoftwareModuleType() - .ifPresent(type -> queryConfig.put(SPUIDefinitions.BY_SOFTWARE_MODULE_TYPE, type)); - return queryConfig; - } - - @Override - protected void addContainerProperties(final Container container) { - final LazyQueryContainer lqc = (LazyQueryContainer) container; - lqc.addContainerProperty("nameAndVersion", String.class, null, false, false); - lqc.addContainerProperty(SPUILabelDefinitions.VAR_ID, Long.class, null, false, false); - lqc.addContainerProperty(SPUILabelDefinitions.VAR_DESC, String.class, "", false, true); - lqc.addContainerProperty(SPUILabelDefinitions.VAR_VERSION, String.class, null, false, false); - lqc.addContainerProperty(SPUILabelDefinitions.VAR_NAME, String.class, null, false, true); - lqc.addContainerProperty(SPUILabelDefinitions.VAR_VENDOR, String.class, null, false, true); - lqc.addContainerProperty(SPUILabelDefinitions.VAR_CREATED_BY, String.class, null, false, true); - lqc.addContainerProperty(SPUILabelDefinitions.VAR_LAST_MODIFIED_BY, String.class, null, false, true); - lqc.addContainerProperty(SPUILabelDefinitions.VAR_CREATED_DATE, String.class, null, false, true); - lqc.addContainerProperty(SPUILabelDefinitions.VAR_LAST_MODIFIED_DATE, String.class, null, false, true); - } - - @Override - protected Object getItemIdToSelect() { - return artifactUploadState.getSelectedSoftwareModules().isEmpty() ? null - : artifactUploadState.getSelectedSoftwareModules(); - } - - @Override - protected boolean isMaximized() { - return artifactUploadState.isSwModuleTableMaximized(); - } - - @Override - protected Optional findEntityByTableValue(final Long entityTableId) { - return softwareModuleManagement.get(entityTableId); - } - - @Override - protected ArtifactUploadState getManagementEntityState() { - return artifactUploadState; - } - - @Override - protected void publishSelectedEntityEvent(final SoftwareModule lastSoftwareModule) { - getEventBus().publish(this, new SoftwareModuleEvent(BaseEntityEventType.SELECTED_ENTITY, lastSoftwareModule)); - } - - @EventBusListenerMethod(scope = EventScope.UI) - void onEvent(final SoftwareModuleEvent event) { - onBaseEntityEvent(event); - } - - @EventBusListenerMethod(scope = EventScope.UI) - void onEvent(final UploadArtifactUIEvent event) { - if (event == UploadArtifactUIEvent.DELETED_ALL_SOFTWARE) { - UI.getCurrent().access(this::refreshFilter); - } - } - - @EventBusListenerMethod(scope = EventScope.UI) - void onSoftwareModuleUpdateEvents(final SoftwareModuleUpdatedEventContainer eventContainer) { - @SuppressWarnings("unchecked") - final List visibleItemIds = (List) getVisibleItemIds(); - eventContainer.getEvents().stream().filter(event -> visibleItemIds.contains(event.getEntityId())) - .filter(Objects::nonNull).forEach(event -> updateSoftwareModuleInTable(event.getEntity())); - } - - private void updateSoftwareModuleInTable(final SoftwareModule editedSm) { - final Item item = getContainerDataSource().getItem(editedSm.getId()); - updateEntity(editedSm, item); - } - - @SuppressWarnings("unchecked") - @Override - protected void updateEntity(final SoftwareModule baseEntity, final Item item) { - final String swNameVersion = HawkbitCommonUtil.concatStrings(":", baseEntity.getName(), - baseEntity.getVersion()); - item.getItemProperty(SPUILabelDefinitions.NAME_VERSION).setValue(swNameVersion); - item.getItemProperty("swId").setValue(baseEntity.getId()); - item.getItemProperty(SPUILabelDefinitions.VAR_VENDOR).setValue(baseEntity.getVendor()); - super.updateEntity(baseEntity, item); - } - - @Override - protected List getTableVisibleColumns() { - final List columnList = super.getTableVisibleColumns(); - if (isMaximized()) { - columnList - .add(new TableColumn(SPUILabelDefinitions.VAR_VENDOR, getI18n().getMessage("header.vendor"), 0.1F)); - } - return columnList; - } - - @Override - protected AcceptCriterion getDropAcceptCriterion() { - return uploadViewClientCriterion; - } - - @Override - protected boolean isDropValid(final DragAndDropEvent dragEvent) { - return false; - } - - @Override - protected void setDataAvailable(final boolean available) { - artifactUploadState.setNoDataAvilableSoftwareModule(!available); - } - - @Override - protected void handleOkDelete(final List entitiesToDelete) { - if (isUploadInProgressForSoftwareModule(entitiesToDelete)) { - getNotification().displayValidationError(getI18n().getMessage("message.error.swModule.notDeleted")); - return; - } - softwareModuleManagement.delete(entitiesToDelete); - getEventBus().publish(this, new SoftwareModuleEvent(BaseEntityEventType.REMOVE_ENTITY, entitiesToDelete)); - getNotification().displaySuccess(getI18n().getMessage("message.delete.success", - entitiesToDelete.size() + " " + getI18n().getMessage("caption.software.module") + "(s)")); - - artifactUploadState.getSelectedSoftwareModules().clear(); - getEventBus().publish(this, UploadArtifactUIEvent.DELETED_ALL_SOFTWARE); - } - - private boolean isUploadInProgressForSoftwareModule(final List entitiesToDelete) { - for (final Long id : entitiesToDelete) { - if (artifactUploadState.isUploadInProgressForSelectedSoftwareModule(id)) { - return true; - } - } - return false; - } - - @Override - protected String getEntityType() { - return getI18n().getMessage("upload.swModuleTable.header"); - } - - @Override - protected Set getSelectedEntities() { - return artifactUploadState.getSelectedSoftwareModules(); - } - - @Override - protected String getEntityId(final Object itemId) { - final String entityId = String.valueOf( - getContainerDataSource().getItem(itemId).getItemProperty(SPUILabelDefinitions.VAR_SWM_ID).getValue()); - return "softwareModule." + entityId; - } - - @Override - protected String getDeletedEntityName(final Long entityId) { - final Optional softwareModule = softwareModuleManagement.get(entityId); - return softwareModule.map(module -> module.getName() + ":" + module.getVersion()).orElse(""); - } - -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleTableHeader.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleTableHeader.java deleted file mode 100644 index 57856c6dd..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleTableHeader.java +++ /dev/null @@ -1,97 +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.artifacts.smtable; - -import org.eclipse.hawkbit.ui.SpPermissionChecker; -import org.eclipse.hawkbit.ui.artifacts.event.RefreshSoftwareModuleByFilterEvent; -import org.eclipse.hawkbit.ui.artifacts.event.SoftwareModuleEvent; -import org.eclipse.hawkbit.ui.artifacts.event.UploadArtifactUIEvent; -import org.eclipse.hawkbit.ui.artifacts.state.ArtifactUploadState; -import org.eclipse.hawkbit.ui.common.table.AbstractSoftwareModuleTableHeader; -import org.eclipse.hawkbit.ui.common.table.BaseEntityEventType; -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; - -/** - * Header of Software module table. - */ -public class SoftwareModuleTableHeader extends AbstractSoftwareModuleTableHeader { - - private static final long serialVersionUID = 1L; - - SoftwareModuleTableHeader(final VaadinMessageSource i18n, final SpPermissionChecker permChecker, - final UIEventBus eventbus, final ArtifactUploadState artifactUploadState, - final SoftwareModuleAddUpdateWindow softwareModuleAddUpdateWindow) { - super(i18n, permChecker, eventbus, null, null, artifactUploadState, softwareModuleAddUpdateWindow); - } - - @EventBusListenerMethod(scope = EventScope.UI) - void onEvent(final UploadArtifactUIEvent event) { - if (event == UploadArtifactUIEvent.HIDE_FILTER_BY_TYPE) { - setFilterButtonsIconVisible(true); - } - } - - @Override - protected String onLoadSearchBoxValue() { - return getArtifactUploadState().getSoftwareModuleFilters().getSearchText().orElse(null); - } - - @Override - protected void showFilterButtonsLayout() { - getArtifactUploadState().setSwTypeFilterClosed(false); - eventBus.publish(this, UploadArtifactUIEvent.SHOW_FILTER_BY_TYPE); - - } - - @Override - protected void resetSearchText() { - if (getArtifactUploadState().getSoftwareModuleFilters().getSearchText().isPresent()) { - getArtifactUploadState().getSoftwareModuleFilters().setSearchText(null); - eventBus.publish(this, new RefreshSoftwareModuleByFilterEvent()); - } - } - - @Override - public void maximizeTable() { - getArtifactUploadState().setSwModuleTableMaximized(Boolean.TRUE); - eventBus.publish(this, new SoftwareModuleEvent(BaseEntityEventType.MAXIMIZED)); - } - - @Override - public void minimizeTable() { - getArtifactUploadState().setSwModuleTableMaximized(Boolean.FALSE); - eventBus.publish(this, new SoftwareModuleEvent(BaseEntityEventType.MINIMIZED)); - } - - @Override - public Boolean onLoadIsTableMaximized() { - return getArtifactUploadState().isSwModuleTableMaximized(); - } - - @Override - public Boolean onLoadIsShowFilterButtonDisplayed() { - return getArtifactUploadState().isSwTypeFilterClosed(); - } - - @Override - protected void searchBy(final String newSearchText) { - getArtifactUploadState().getSoftwareModuleFilters().setSearchText(newSearchText); - eventBus.publish(this, new RefreshSoftwareModuleByFilterEvent()); - } - - @Override - protected boolean isDropHintRequired() { - /* No dropping on software module table header in Upload View */ - return false; - } - -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleTableLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleTableLayout.java deleted file mode 100644 index 92d50c55d..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleTableLayout.java +++ /dev/null @@ -1,59 +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.artifacts.smtable; - -import org.eclipse.hawkbit.repository.EntityFactory; -import org.eclipse.hawkbit.repository.SoftwareModuleManagement; -import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement; -import org.eclipse.hawkbit.ui.SpPermissionChecker; -import org.eclipse.hawkbit.ui.artifacts.state.ArtifactUploadState; -import org.eclipse.hawkbit.ui.common.table.AbstractTableLayout; -import org.eclipse.hawkbit.ui.dd.criteria.UploadViewClientCriterion; -import org.eclipse.hawkbit.ui.distributions.smtable.SwMetadataPopupLayout; -import org.eclipse.hawkbit.ui.utils.UINotification; -import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; -import org.vaadin.spring.events.EventBus.UIEventBus; - -/** - * Software module table layout. (Upload Management) - */ -public class SoftwareModuleTableLayout extends AbstractTableLayout { - - private static final long serialVersionUID = 1L; - - private final SoftwareModuleTable softwareModuleTable; - - public SoftwareModuleTableLayout(final VaadinMessageSource i18n, final SpPermissionChecker permChecker, - final ArtifactUploadState artifactUploadState, final UINotification uiNotification, - final UIEventBus eventBus, final SoftwareModuleManagement softwareModuleManagement, - final SoftwareModuleTypeManagement softwareModuleTypeManagement, final EntityFactory entityFactory, - final UploadViewClientCriterion uploadViewClientCriterion) { - - final SwMetadataPopupLayout swMetadataPopupLayout = new SwMetadataPopupLayout(i18n, uiNotification, eventBus, - softwareModuleManagement, entityFactory, permChecker); - this.softwareModuleTable = new SoftwareModuleTable(eventBus, i18n, uiNotification, artifactUploadState, - softwareModuleManagement, uploadViewClientCriterion, permChecker); - - final SoftwareModuleAddUpdateWindow softwareModuleAddUpdateWindow = new SoftwareModuleAddUpdateWindow(i18n, - uiNotification, eventBus, softwareModuleManagement, softwareModuleTypeManagement, entityFactory, - softwareModuleTable); - - super.init(i18n, - new SoftwareModuleTableHeader(i18n, permChecker, eventBus, artifactUploadState, - softwareModuleAddUpdateWindow), - softwareModuleTable, - new SoftwareModuleDetails(i18n, eventBus, permChecker, softwareModuleAddUpdateWindow, - artifactUploadState, softwareModuleManagement, swMetadataPopupLayout)); - } - - public SoftwareModuleTable getSoftwareModuleTable() { - return softwareModuleTable; - } - -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/UpdateSmWindowController.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/UpdateSmWindowController.java new file mode 100644 index 000000000..70bae8341 --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/UpdateSmWindowController.java @@ -0,0 +1,133 @@ +/** + * Copyright (c) 2020 Bosch.IO 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.artifacts.smtable; + +import org.eclipse.hawkbit.repository.EntityFactory; +import org.eclipse.hawkbit.repository.SoftwareModuleManagement; +import org.eclipse.hawkbit.repository.builder.SoftwareModuleUpdate; +import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; +import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException; +import org.eclipse.hawkbit.repository.model.SoftwareModule; +import org.eclipse.hawkbit.ui.common.AbstractEntityWindowController; +import org.eclipse.hawkbit.ui.common.AbstractEntityWindowLayout; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxySoftwareModule; +import org.eclipse.hawkbit.ui.common.event.EntityModifiedEventPayload; +import org.eclipse.hawkbit.ui.common.event.EntityModifiedEventPayload.EntityModifiedEventType; +import org.eclipse.hawkbit.ui.common.event.EventTopics; +import org.eclipse.hawkbit.ui.utils.UINotification; +import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.StringUtils; +import org.vaadin.spring.events.EventBus.UIEventBus; + +/** + * Controller for update software module window + */ +public class UpdateSmWindowController extends AbstractEntityWindowController { + private static final Logger LOG = LoggerFactory.getLogger(UpdateSmWindowController.class); + + private final VaadinMessageSource i18n; + private final EntityFactory entityFactory; + private final UIEventBus eventBus; + private final UINotification uiNotification; + + private final SoftwareModuleManagement smManagement; + + private final SmWindowLayout layout; + + /** + * Constructor for UpdateSmWindowController + * + * @param i18n + * VaadinMessageSource + * @param entityFactory + * EntityFactory + * @param eventBus + * UIEventBus + * @param uiNotification + * UINotification + * @param smManagement + * SoftwareModuleManagement + * @param layout + * SmWindowLayout + */ + public UpdateSmWindowController(final VaadinMessageSource i18n, final EntityFactory entityFactory, + final UIEventBus eventBus, final UINotification uiNotification, final SoftwareModuleManagement smManagement, + final SmWindowLayout layout) { + this.i18n = i18n; + this.entityFactory = entityFactory; + this.eventBus = eventBus; + this.uiNotification = uiNotification; + + this.smManagement = smManagement; + + this.layout = layout; + } + + /** + * @return Software module layout + */ + @Override + public AbstractEntityWindowLayout getLayout() { + return layout; + } + + @Override + protected ProxySoftwareModule buildEntityFromProxy(final ProxySoftwareModule proxyEntity) { + final ProxySoftwareModule sm = new ProxySoftwareModule(); + + sm.setId(proxyEntity.getId()); + sm.setTypeInfo(proxyEntity.getTypeInfo()); + sm.setName(proxyEntity.getName()); + sm.setVersion(proxyEntity.getVersion()); + sm.setVendor(proxyEntity.getVendor()); + sm.setDescription(proxyEntity.getDescription()); + + return sm; + } + + @Override + protected void adaptLayout(final ProxySoftwareModule proxyEntity) { + layout.disableSmTypeSelect(); + layout.disableNameField(); + layout.disableVersionField(); + } + + @Override + protected void persistEntity(final ProxySoftwareModule entity) { + final SoftwareModuleUpdate smUpdate = entityFactory.softwareModule().update(entity.getId()) + .vendor(entity.getVendor()).description(entity.getDescription()); + + try { + final SoftwareModule updatedSm = smManagement.update(smUpdate); + + uiNotification.displaySuccess( + i18n.getMessage("message.update.success", updatedSm.getName() + ":" + updatedSm.getVersion())); + eventBus.publish(EventTopics.ENTITY_MODIFIED, this, new EntityModifiedEventPayload( + EntityModifiedEventType.ENTITY_UPDATED, ProxySoftwareModule.class, updatedSm.getId())); + } catch (final EntityNotFoundException | EntityReadOnlyException e) { + LOG.trace("Update of software module failed in UI: {}", e.getMessage()); + final String entityType = i18n.getMessage("caption.software.module"); + uiNotification + .displayWarning(i18n.getMessage("message.deleted.or.notAllowed", entityType, entity.getName())); + } + } + + @Override + protected boolean isEntityValid(final ProxySoftwareModule entity) { + if (!StringUtils.hasText(entity.getName()) || !StringUtils.hasText(entity.getVersion()) + || entity.getTypeInfo() == null) { + uiNotification.displayValidationError(i18n.getMessage("message.error.missing.nameorversionortype")); + return false; + } + + return true; + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/AbstractSoftwareModuleTypeLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/AbstractSoftwareModuleTypeLayout.java deleted file mode 100644 index da9b09830..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/AbstractSoftwareModuleTypeLayout.java +++ /dev/null @@ -1,196 +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.artifacts.smtype; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - -import org.eclipse.hawkbit.repository.EntityFactory; -import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement; -import org.eclipse.hawkbit.repository.model.SoftwareModuleType; -import org.eclipse.hawkbit.ui.SpPermissionChecker; -import org.eclipse.hawkbit.ui.common.builder.LabelBuilder; -import org.eclipse.hawkbit.ui.layouts.AbstractTypeLayout; -import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; -import org.eclipse.hawkbit.ui.utils.UINotification; -import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; -import org.vaadin.spring.events.EventBus.UIEventBus; - -import com.vaadin.ui.Label; -import com.vaadin.ui.OptionGroup; -import com.vaadin.ui.themes.ValoTheme; - -/** - * General Layout for the software module types pop-up window which is provided - * on the Distribution and Upload view when creating, updating or deleting a - * software module type - * - */ -public abstract class AbstractSoftwareModuleTypeLayout extends AbstractTypeLayout { - - private static final long serialVersionUID = 1L; - - private final transient SoftwareModuleTypeManagement softwareModuleTypeManagement; - - private String singleAssignStr; - - private String multiAssignStr; - - private Label singleAssign; - - private Label multiAssign; - - private OptionGroup assignOptiongroup; - - /** - * Constructor - * - * @param i18n - * VaadinMessageSource - * @param entityFactory - * EntityFactory - * @param eventBus - * UIEventBus - * @param permChecker - * SpPermissionChecker - * @param uiNotification - * UINotification - * @param softwareModuleTypeManagement - * SoftwareModuleTypeManagement - */ - public AbstractSoftwareModuleTypeLayout(final VaadinMessageSource i18n, final EntityFactory entityFactory, - final UIEventBus eventBus, final SpPermissionChecker permChecker, final UINotification uiNotification, - final SoftwareModuleTypeManagement softwareModuleTypeManagement) { - super(i18n, entityFactory, eventBus, permChecker, uiNotification); - this.softwareModuleTypeManagement = softwareModuleTypeManagement; - } - - @Override - protected int getTagNameSize() { - return SoftwareModuleType.NAME_MAX_SIZE; - } - - @Override - protected int getTagDescSize() { - return SoftwareModuleType.DESCRIPTION_MAX_SIZE; - } - - @Override - protected int getTypeKeySize() { - return SoftwareModuleType.KEY_MAX_SIZE; - } - - @Override - protected String getTagNameId() { - return UIComponentIdProvider.NEW_SOFTWARE_TYPE_NAME; - } - - @Override - protected String getTagDescId() { - return UIComponentIdProvider.NEW_SOFTWARE_TYPE_DESC; - } - - @Override - protected String getTypeKeyId() { - return UIComponentIdProvider.NEW_SOFTWARE_TYPE_KEY; - } - - @Override - protected void createRequiredComponents() { - super.createRequiredComponents(); - singleAssignStr = getI18n().getMessage("label.singleAssign.type"); - multiAssignStr = getI18n().getMessage("label.multiAssign.type"); - singleAssign = new LabelBuilder().name(singleAssignStr).buildLabel(); - multiAssign = new LabelBuilder().name(multiAssignStr).buildLabel(); - singleMultiOptionGroup(); - } - - @Override - protected void buildLayout() { - super.buildLayout(); - getFormLayout().addComponent(assignOptiongroup); - } - - @Override - protected Optional findEntityByKey() { - return softwareModuleTypeManagement.getByKey(getTypeKey().getValue()); - } - - @Override - protected Optional findEntityByName() { - return softwareModuleTypeManagement.getByName(getTagName().getValue()); - } - - @Override - protected String getDuplicateKeyErrorMessage(final SoftwareModuleType existingType) { - return getI18n().getMessage("message.type.key.swmodule.duplicate.check", existingType.getKey()); - } - - public SoftwareModuleTypeManagement getSoftwareModuleTypeManagement() { - return softwareModuleTypeManagement; - } - - public String getSingleAssignStr() { - return singleAssignStr; - } - - public void setSingleAssignStr(final String singleAssignStr) { - this.singleAssignStr = singleAssignStr; - } - - public String getMultiAssignStr() { - return multiAssignStr; - } - - public void setMultiAssignStr(final String multiAssignStr) { - this.multiAssignStr = multiAssignStr; - } - - public Label getSingleAssign() { - return singleAssign; - } - - public void setSingleAssign(final Label singleAssign) { - this.singleAssign = singleAssign; - } - - public Label getMultiAssign() { - return multiAssign; - } - - public void setMultiAssign(final Label multiAssign) { - this.multiAssign = multiAssign; - } - - public OptionGroup getAssignOptiongroup() { - return assignOptiongroup; - } - - public void setAssignOptiongroup(final OptionGroup assignOptiongroup) { - this.assignOptiongroup = assignOptiongroup; - } - - private void singleMultiOptionGroup() { - final List optionValues = new ArrayList<>(); - optionValues.add(singleAssign.getValue()); - optionValues.add(multiAssign.getValue()); - assignOptionGroupByValues(optionValues); - } - - private void assignOptionGroupByValues(final List tagOptions) { - assignOptiongroup = new OptionGroup("", tagOptions); - assignOptiongroup.setStyleName(ValoTheme.OPTIONGROUP_SMALL); - assignOptiongroup.addStyleName("custom-option-group"); - assignOptiongroup.setNullSelectionAllowed(false); - assignOptiongroup.setId(UIComponentIdProvider.ASSIGN_OPTION_GROUP_SOFTWARE_MODULE_TYPE_ID); - assignOptiongroup.select(tagOptions.get(0)); - } - -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/AddSmTypeWindowController.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/AddSmTypeWindowController.java new file mode 100644 index 000000000..5dccbc604 --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/AddSmTypeWindowController.java @@ -0,0 +1,121 @@ +/** + * Copyright (c) 2020 Bosch.IO 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.artifacts.smtype; + +import org.eclipse.hawkbit.repository.EntityFactory; +import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement; +import org.eclipse.hawkbit.repository.model.SoftwareModuleType; +import org.eclipse.hawkbit.ui.common.AbstractEntityWindowController; +import org.eclipse.hawkbit.ui.common.AbstractEntityWindowLayout; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxySoftwareModule; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyType; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyType.SmTypeAssign; +import org.eclipse.hawkbit.ui.common.event.EntityModifiedEventPayload; +import org.eclipse.hawkbit.ui.common.event.EntityModifiedEventPayload.EntityModifiedEventType; +import org.eclipse.hawkbit.ui.common.event.EventTopics; +import org.eclipse.hawkbit.ui.utils.UINotification; +import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; +import org.springframework.util.StringUtils; +import org.vaadin.spring.events.EventBus.UIEventBus; + +/** + * Controller for Add software module type window + */ +public class AddSmTypeWindowController extends AbstractEntityWindowController { + private final VaadinMessageSource i18n; + private final EntityFactory entityFactory; + private final UIEventBus eventBus; + private final UINotification uiNotification; + + private final SoftwareModuleTypeManagement smTypeManagement; + + private final SmTypeWindowLayout layout; + + /** + * Constructor for AddSmTypeWindowController + * + * @param i18n + * VaadinMessageSource + * @param entityFactory + * EntityFactory + * @param eventBus + * UIEventBus + * @param uiNotification + * UINotification + * @param smTypeManagement + * SoftwareModuleTypeManagement + * @param layout + * SmTypeWindowLayout + */ + public AddSmTypeWindowController(final VaadinMessageSource i18n, final EntityFactory entityFactory, + final UIEventBus eventBus, final UINotification uiNotification, + final SoftwareModuleTypeManagement smTypeManagement, final SmTypeWindowLayout layout) { + this.i18n = i18n; + this.entityFactory = entityFactory; + this.eventBus = eventBus; + this.uiNotification = uiNotification; + + this.smTypeManagement = smTypeManagement; + + this.layout = layout; + } + + /** + * Getter for Software module type Window Layout + * + * @return AbstractEntityWindowLayout + */ + @Override + public AbstractEntityWindowLayout getLayout() { + return layout; + } + + @Override + protected ProxyType buildEntityFromProxy(final ProxyType proxyEntity) { + // We ignore the method parameter, because we are interested in the + // empty object, that we can populate with defaults + return new ProxyType(); + } + + @Override + protected void persistEntity(final ProxyType entity) { + final int assignNumber = entity.getSmTypeAssign() == SmTypeAssign.SINGLE ? 1 : Integer.MAX_VALUE; + + final SoftwareModuleType newSmType = smTypeManagement + .create(entityFactory.softwareModuleType().create().key(entity.getKey()).name(entity.getName()) + .description(entity.getDescription()).colour(entity.getColour()).maxAssignments(assignNumber)); + + uiNotification.displaySuccess(i18n.getMessage("message.save.success", newSmType.getName())); + eventBus.publish(EventTopics.ENTITY_MODIFIED, this, new EntityModifiedEventPayload( + EntityModifiedEventType.ENTITY_ADDED, ProxySoftwareModule.class, ProxyType.class, newSmType.getId())); + } + + @Override + protected boolean isEntityValid(final ProxyType entity) { + if (!StringUtils.hasText(entity.getName()) || !StringUtils.hasText(entity.getKey()) + || entity.getSmTypeAssign() == null) { + uiNotification.displayValidationError(i18n.getMessage("message.error.missing.typenameorkeyorsmtype")); + return false; + } + + final String trimmedName = StringUtils.trimWhitespace(entity.getName()); + final String trimmedKey = StringUtils.trimWhitespace(entity.getKey()); + if (smTypeManagement.getByName(trimmedName).isPresent()) { + uiNotification.displayValidationError(i18n.getMessage("message.type.duplicate.check", trimmedName)); + return false; + } + if (smTypeManagement.getByKey(trimmedKey).isPresent()) { + uiNotification + .displayValidationError(i18n.getMessage("message.type.key.swmodule.duplicate.check", trimmedKey)); + return false; + } + + return true; + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/CreateSoftwareModuleTypeLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/CreateSoftwareModuleTypeLayout.java deleted file mode 100644 index 435b894c5..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/CreateSoftwareModuleTypeLayout.java +++ /dev/null @@ -1,92 +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.artifacts.smtype; - -import org.eclipse.hawkbit.repository.EntityFactory; -import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement; -import org.eclipse.hawkbit.repository.model.SoftwareModuleType; -import org.eclipse.hawkbit.ui.SpPermissionChecker; -import org.eclipse.hawkbit.ui.artifacts.event.SoftwareModuleTypeEvent; -import org.eclipse.hawkbit.ui.artifacts.event.SoftwareModuleTypeEvent.SoftwareModuleTypeEnum; -import org.eclipse.hawkbit.ui.colorpicker.ColorPickerHelper; -import org.eclipse.hawkbit.ui.utils.UINotification; -import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; -import org.vaadin.spring.events.EventBus.UIEventBus; - -/** - * Layout for the pop-up window which is created when creating a software module - * type on the Upload or Distribution View. - */ -public class CreateSoftwareModuleTypeLayout extends AbstractSoftwareModuleTypeLayout { - - private static final long serialVersionUID = 1L; - - /** - * Constructor for CreateUpdateSoftwareTypeLayout - * - * @param i18n - * I18N - * @param entityFactory - * EntityFactory - * @param eventBus - * UIEventBus - * @param permChecker - * SpPermissionChecker - * @param uiNotification - * UINotification - * @param softwareModuleTypeManagement - * management for {@link SoftwareModuleType}s - */ - public CreateSoftwareModuleTypeLayout(final VaadinMessageSource i18n, final EntityFactory entityFactory, - final UIEventBus eventBus, final SpPermissionChecker permChecker, final UINotification uiNotification, - final SoftwareModuleTypeManagement softwareModuleTypeManagement) { - super(i18n, entityFactory, eventBus, permChecker, uiNotification, softwareModuleTypeManagement); - } - - @Override - protected String getWindowCaption() { - return getI18n().getMessage("caption.create.new", getI18n().getMessage("caption.type")); - } - - @Override - protected void saveEntity() { - createNewSWModuleType(); - } - - private void createNewSWModuleType() { - int assignNumber = 0; - final String colorPicked = ColorPickerHelper.getColorPickedString(getColorPickerLayout().getSelPreview()); - final String typeNameValue = getTagName().getValue(); - final String typeKeyValue = getTypeKey().getValue(); - final String typeDescValue = getTagDesc().getValue(); - final String assignValue = (String) getAssignOptiongroup().getValue(); - if (assignValue != null && assignValue.equalsIgnoreCase(getSingleAssignStr())) { - assignNumber = 1; - } else if (assignValue != null && assignValue.equalsIgnoreCase(getMultiAssignStr())) { - assignNumber = Integer.MAX_VALUE; - } - - if (typeNameValue != null && typeKeyValue != null) { - final SoftwareModuleType newSWType = getSoftwareModuleTypeManagement() - .create(getEntityFactory().softwareModuleType().create().key(typeKeyValue).name(typeNameValue) - .description(typeDescValue).colour(colorPicked).maxAssignments(assignNumber)); - getUiNotification().displaySuccess(getI18n().getMessage("message.save.success", newSWType.getName())); - getEventBus().publish(this, - new SoftwareModuleTypeEvent(SoftwareModuleTypeEnum.ADD_SOFTWARE_MODULE_TYPE, newSWType)); - } else { - getUiNotification().displayValidationError(getI18n().getMessage("message.error.missing.typenameorkey")); - } - } - - @Override - protected boolean isUpdateAction() { - return false; - } - -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/SmTypeWindowBuilder.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/SmTypeWindowBuilder.java new file mode 100644 index 000000000..94118f4df --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/SmTypeWindowBuilder.java @@ -0,0 +1,88 @@ +/** + * Copyright (c) 2020 Bosch.IO 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.artifacts.smtype; + +import org.eclipse.hawkbit.repository.EntityFactory; +import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement; +import org.eclipse.hawkbit.ui.common.AbstractEntityWindowBuilder; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyType; +import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; +import org.eclipse.hawkbit.ui.utils.UINotification; +import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; +import org.vaadin.spring.events.EventBus.UIEventBus; + +import com.vaadin.ui.Window; + +/** + * Builder for software module type window + */ +public class SmTypeWindowBuilder extends AbstractEntityWindowBuilder { + private final EntityFactory entityFactory; + private final UIEventBus eventBus; + private final UINotification uiNotification; + + private final SoftwareModuleTypeManagement smTypeManagement; + + /** + * Constructor for SmTypeWindowBuilder + * + * @param i18n + * VaadinMessageSource + * @param entityFactory + * EntityFactory + * @param eventBus + * UIEventBus + * @param uiNotification + * UINotification + * @param smTypeManagement + * SoftwareModuleTypeManagement + */ + public SmTypeWindowBuilder(final VaadinMessageSource i18n, final EntityFactory entityFactory, + final UIEventBus eventBus, final UINotification uiNotification, + final SoftwareModuleTypeManagement smTypeManagement) { + super(i18n); + + this.entityFactory = entityFactory; + this.eventBus = eventBus; + this.uiNotification = uiNotification; + + this.smTypeManagement = smTypeManagement; + } + + @Override + protected String getWindowId() { + return UIComponentIdProvider.TAG_POPUP_ID; + } + + /** + * Add window for software module type + * + * @return Window of Software module type + */ + @Override + public Window getWindowForAdd() { + return getWindowForNewEntity(new AddSmTypeWindowController(i18n, entityFactory, eventBus, uiNotification, + smTypeManagement, new SmTypeWindowLayout(i18n, uiNotification))); + + } + + /** + * Update window for software module type + * + * @param proxyType + * ProxyType + * + * @return Window of Software module type + */ + @Override + public Window getWindowForUpdate(final ProxyType proxyType) { + return getWindowForEntity(proxyType, new UpdateSmTypeWindowController(i18n, entityFactory, eventBus, + uiNotification, smTypeManagement, new SmTypeWindowLayout(i18n, uiNotification))); + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/SmTypeWindowLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/SmTypeWindowLayout.java new file mode 100644 index 000000000..211b165b1 --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/SmTypeWindowLayout.java @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2020 Bosch.IO 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.artifacts.smtype; + +import org.eclipse.hawkbit.ui.common.builder.FormComponentBuilder; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyType; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyType.SmTypeAssign; +import org.eclipse.hawkbit.ui.management.tag.TagWindowLayout; +import org.eclipse.hawkbit.ui.utils.UINotification; +import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; + +import com.vaadin.ui.FormLayout; +import com.vaadin.ui.RadioButtonGroup; +import com.vaadin.ui.TextField; + +/** + * Software module type Window Layout view + */ +public class SmTypeWindowLayout extends TagWindowLayout { + private final SmTypeWindowLayoutComponentBuilder smTypeComponentBuilder; + + private final TextField typeKey; + private final RadioButtonGroup smTypeAssignOptionGroup; + + /** + * Constructor for SmTypeWindowLayout + * + * @param i18n + * VaadinMessageSource + * @param uiNotification + * UINotification + */ + public SmTypeWindowLayout(final VaadinMessageSource i18n, final UINotification uiNotification) { + super(i18n, uiNotification); + + this.smTypeComponentBuilder = new SmTypeWindowLayoutComponentBuilder(i18n); + + this.typeKey = FormComponentBuilder.createTypeKeyField(binder, i18n); + this.smTypeAssignOptionGroup = smTypeComponentBuilder.createSmTypeAssignOptionGroup(binder); + + this.colorPickerComponent.getColorPickerBtn().setCaption(i18n.getMessage("label.choose.type.color")); + } + + @Override + protected FormLayout buildFormLayout() { + final FormLayout formLayout = super.buildFormLayout(); + + formLayout.addComponent(typeKey, formLayout.getComponentCount() - 1); + formLayout.addComponent(smTypeAssignOptionGroup, formLayout.getComponentCount() - 1); + + return formLayout; + } + + /** + * Disable the software module type key text field + */ + public void disableTypeKey() { + typeKey.setEnabled(false); + } + + /** + * Disable the software module type assign option + */ + public void disableTypeAssignOptionGroup() { + smTypeAssignOptionGroup.setEnabled(false); + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/SmTypeWindowLayoutComponentBuilder.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/SmTypeWindowLayoutComponentBuilder.java new file mode 100644 index 000000000..77622a52e --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/SmTypeWindowLayoutComponentBuilder.java @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2020 Bosch.IO 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.artifacts.smtype; + +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyType; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyType.SmTypeAssign; +import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; +import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; + +import com.vaadin.data.Binder; +import com.vaadin.ui.RadioButtonGroup; + +/** + * Builder for software module type window layout component + */ +public class SmTypeWindowLayoutComponentBuilder { + + public static final String TEXTFIELD_KEY = "textfield.key"; + + private final VaadinMessageSource i18n; + + /** + * Constructor for SmTypeWindowLayoutComponentBuilder + * + * @param i18n + * VaadinMessageSource + */ + public SmTypeWindowLayoutComponentBuilder(final VaadinMessageSource i18n) { + this.i18n = i18n; + } + + /** + * Create software module type assignment group + * + * @param binder + * Vaadin binder of ProxyType + * + * @return RadioButtonGroup of software module type assignment + */ + public RadioButtonGroup createSmTypeAssignOptionGroup(final Binder binder) { + final RadioButtonGroup smTypeAssignOptionGroup = new RadioButtonGroup<>(); + smTypeAssignOptionGroup.setId(UIComponentIdProvider.ASSIGN_OPTION_GROUP_SOFTWARE_MODULE_TYPE_ID); + + smTypeAssignOptionGroup.setItemCaptionGenerator(item -> { + switch (item) { + case SINGLE: + return i18n.getMessage("label.singleAssign.type"); + case MULTI: + return i18n.getMessage("label.multiAssign.type"); + default: + return null; + } + }); + smTypeAssignOptionGroup.setItemDescriptionGenerator(item -> { + switch (item) { + case SINGLE: + return i18n.getMessage("label.singleAssign.type.desc"); + case MULTI: + return i18n.getMessage("label.multiAssign.type.desc"); + default: + return null; + } + }); + + binder.forField(smTypeAssignOptionGroup).bind(ProxyType::getSmTypeAssign, ProxyType::setSmTypeAssign); + smTypeAssignOptionGroup.setItems(SmTypeAssign.values()); + + return smTypeAssignOptionGroup; + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/UpdateSmTypeWindowController.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/UpdateSmTypeWindowController.java new file mode 100644 index 000000000..cc7063b68 --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/UpdateSmTypeWindowController.java @@ -0,0 +1,162 @@ +/** + * Copyright (c) 2020 Bosch.IO 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.artifacts.smtype; + +import org.eclipse.hawkbit.repository.EntityFactory; +import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement; +import org.eclipse.hawkbit.repository.builder.SoftwareModuleTypeUpdate; +import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; +import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException; +import org.eclipse.hawkbit.repository.model.SoftwareModuleType; +import org.eclipse.hawkbit.ui.common.AbstractEntityWindowController; +import org.eclipse.hawkbit.ui.common.AbstractEntityWindowLayout; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxySoftwareModule; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyType; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyType.SmTypeAssign; +import org.eclipse.hawkbit.ui.common.event.EntityModifiedEventPayload; +import org.eclipse.hawkbit.ui.common.event.EntityModifiedEventPayload.EntityModifiedEventType; +import org.eclipse.hawkbit.ui.common.event.EventTopics; +import org.eclipse.hawkbit.ui.utils.UINotification; +import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.StringUtils; +import org.vaadin.spring.events.EventBus.UIEventBus; + +/** + * Controller for update software module type window + */ +public class UpdateSmTypeWindowController extends AbstractEntityWindowController { + private static final Logger LOG = LoggerFactory.getLogger(UpdateSmTypeWindowController.class); + + private final VaadinMessageSource i18n; + private final EntityFactory entityFactory; + private final UIEventBus eventBus; + private final UINotification uiNotification; + + private final SoftwareModuleTypeManagement smTypeManagement; + + private final SmTypeWindowLayout layout; + + private String nameBeforeEdit; + private String keyBeforeEdit; + + /** + * Constructor for UpdateSmTypeWindowController + * + * @param i18n + * VaadinMessageSource + * @param entityFactory + * EntityFactory + * @param eventBus + * UIEventBus + * @param uiNotification + * UINotification + * @param smTypeManagement + * SoftwareModuleTypeManagement + * @param layout + * SmTypeWindowLayout + */ + public UpdateSmTypeWindowController(final VaadinMessageSource i18n, final EntityFactory entityFactory, + final UIEventBus eventBus, final UINotification uiNotification, + final SoftwareModuleTypeManagement smTypeManagement, final SmTypeWindowLayout layout) { + this.i18n = i18n; + this.entityFactory = entityFactory; + this.eventBus = eventBus; + this.uiNotification = uiNotification; + + this.smTypeManagement = smTypeManagement; + + this.layout = layout; + } + + /** + * Getter for Software module type Window Layout + * + * @return AbstractEntityWindowLayout + */ + @Override + public AbstractEntityWindowLayout getLayout() { + return layout; + } + + @Override + protected ProxyType buildEntityFromProxy(final ProxyType proxyEntity) { + final ProxyType smType = new ProxyType(); + + smType.setId(proxyEntity.getId()); + smType.setName(proxyEntity.getName()); + smType.setDescription(proxyEntity.getDescription()); + smType.setColour(proxyEntity.getColour()); + smType.setKey(proxyEntity.getKey()); + smType.setSmTypeAssign(getSmTypeAssignById(proxyEntity.getId())); + + nameBeforeEdit = proxyEntity.getName(); + keyBeforeEdit = proxyEntity.getKey(); + + return smType; + } + + private SmTypeAssign getSmTypeAssignById(final Long id) { + return smTypeManagement.get(id) + .map(smType -> smType.getMaxAssignments() == 1 ? SmTypeAssign.SINGLE : SmTypeAssign.MULTI) + .orElse(SmTypeAssign.SINGLE); + } + + @Override + protected void adaptLayout(final ProxyType proxyEntity) { + layout.disableTagName(); + layout.disableTypeKey(); + layout.disableTypeAssignOptionGroup(); + } + + @Override + protected void persistEntity(final ProxyType entity) { + final SoftwareModuleTypeUpdate smTypeUpdate = entityFactory.softwareModuleType().update(entity.getId()) + .description(entity.getDescription()).colour(entity.getColour()); + + try { + final SoftwareModuleType updatedSmType = smTypeManagement.update(smTypeUpdate); + + uiNotification.displaySuccess(i18n.getMessage("message.update.success", updatedSmType.getName())); + eventBus.publish(EventTopics.ENTITY_MODIFIED, this, + new EntityModifiedEventPayload(EntityModifiedEventType.ENTITY_UPDATED, ProxySoftwareModule.class, + ProxyType.class, updatedSmType.getId())); + } catch (final EntityNotFoundException | EntityReadOnlyException e) { + LOG.trace("Update of software module type failed in UI: {}", e.getMessage()); + + final String entityType = i18n.getMessage("caption.entity.software.module.type"); + uiNotification + .displayWarning(i18n.getMessage("message.deleted.or.notAllowed", entityType, entity.getName())); + } + } + + @Override + protected boolean isEntityValid(final ProxyType entity) { + if (!StringUtils.hasText(entity.getName()) || !StringUtils.hasText(entity.getKey()) + || entity.getSmTypeAssign() == null) { + uiNotification.displayValidationError(i18n.getMessage("message.error.missing.typenameorkeyorsmtype")); + return false; + } + + final String trimmedName = StringUtils.trimWhitespace(entity.getName()); + final String trimmedKey = StringUtils.trimWhitespace(entity.getKey()); + if (!nameBeforeEdit.equals(trimmedName) && smTypeManagement.getByName(trimmedName).isPresent()) { + uiNotification.displayValidationError(i18n.getMessage("message.type.duplicate.check", trimmedName)); + return false; + } + if (!keyBeforeEdit.equals(trimmedKey) && smTypeManagement.getByKey(trimmedKey).isPresent()) { + uiNotification + .displayValidationError(i18n.getMessage("message.type.key.swmodule.duplicate.check", trimmedKey)); + return false; + } + + return true; + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/UpdateSoftwareModuleTypeLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/UpdateSoftwareModuleTypeLayout.java deleted file mode 100644 index 22b4ee633..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/UpdateSoftwareModuleTypeLayout.java +++ /dev/null @@ -1,132 +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.artifacts.smtype; - -import org.eclipse.hawkbit.repository.EntityFactory; -import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement; -import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; -import org.eclipse.hawkbit.repository.model.DistributionSetType; -import org.eclipse.hawkbit.repository.model.SoftwareModuleType; -import org.eclipse.hawkbit.ui.SpPermissionChecker; -import org.eclipse.hawkbit.ui.artifacts.event.SoftwareModuleTypeEvent; -import org.eclipse.hawkbit.ui.artifacts.event.SoftwareModuleTypeEvent.SoftwareModuleTypeEnum; -import org.eclipse.hawkbit.ui.colorpicker.ColorPickerHelper; -import org.eclipse.hawkbit.ui.layouts.UpdateTag; -import org.eclipse.hawkbit.ui.utils.UINotification; -import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; -import org.vaadin.spring.events.EventBus.UIEventBus; - -import com.vaadin.ui.Window.CloseListener; - -/** - * Layout for the pop-up window which is created when updating a Software Module - * Type on the Upload View. - * - */ -public class UpdateSoftwareModuleTypeLayout extends AbstractSoftwareModuleTypeLayout implements UpdateTag { - - private static final long serialVersionUID = 1L; - - private final String selectedTypeName; - - private final CloseListener closeListener; - - /** - * Constructor for initializing the pop-up window for updating a software - * module type. The form fields are filled with the current data of the - * selected software module type. - * - * @param i18n - * VaadinMessageSource - * @param entityFactory - * EntityFactory - * @param eventBus - * UIEventBus - * @param permChecker - * SpPermissionChecker - * @param uiNotification - * UINotification - * @param softwareModuleTypeManagement - * SoftwareModuleTypeManagement - * @param selectedTypeName - * The name of the selected software module type to update - * @param closeListener - * CloseListener which describes the action to do when closing - * the window - */ - public UpdateSoftwareModuleTypeLayout(final VaadinMessageSource i18n, final EntityFactory entityFactory, - final UIEventBus eventBus, final SpPermissionChecker permChecker, final UINotification uiNotification, - final SoftwareModuleTypeManagement softwareModuleTypeManagement, final String selectedTypeName, - final CloseListener closeListener) { - super(i18n, entityFactory, eventBus, permChecker, uiNotification, softwareModuleTypeManagement); - this.selectedTypeName = selectedTypeName; - this.closeListener = closeListener; - initUpdatePopup(); - } - - private void initUpdatePopup() { - setTagDetails(selectedTypeName); - getWindow().addCloseListener(closeListener); - } - - @Override - protected String getWindowCaption() { - return getI18n().getMessage("caption.update", getI18n().getMessage("caption.type")); - } - - @Override - protected void saveEntity() { - updateSWModuleType(findEntityByName() - .orElseThrow(() -> new EntityNotFoundException(DistributionSetType.class, getTagName().getValue()))); - } - - @Override - protected void buildLayout() { - super.buildLayout(); - disableFields(); - } - - @Override - protected void disableFields() { - getTagName().setEnabled(false); - getTypeKey().setEnabled(false); - getAssignOptiongroup().setEnabled(false); - } - - @Override - public void setTagDetails(final String selectedEntity) { - getSoftwareModuleTypeManagement().getByName(selectedEntity).ifPresent(selectedTypeTag -> { - getTagName().setValue(selectedTypeTag.getName()); - getTagDesc().setValue(selectedTypeTag.getDescription()); - getTypeKey().setValue(selectedTypeTag.getKey()); - if (selectedTypeTag.getMaxAssignments() == 1) { - getAssignOptiongroup().setValue(getSingleAssignStr()); - } else { - getAssignOptiongroup().setValue(getMultiAssignStr()); - } - setColorPickerComponentsColor(selectedTypeTag.getColour()); - }); - disableFields(); - } - - private void updateSWModuleType(final SoftwareModuleType existingType) { - getSoftwareModuleTypeManagement().update(getEntityFactory().softwareModuleType().update(existingType.getId()) - .description(getTagDesc().getValue()) - .colour(ColorPickerHelper.getColorPickedString(getColorPickerLayout().getSelPreview()))); - getUiNotification().displaySuccess(getI18n().getMessage("message.update.success", existingType.getName())); - getEventBus().publish(this, - new SoftwareModuleTypeEvent(SoftwareModuleTypeEnum.UPDATE_SOFTWARE_MODULE_TYPE, existingType)); - } - - @Override - protected boolean isUpdateAction() { - return true; - } - -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/filter/SMTypeFilterButtonClick.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/filter/SMTypeFilterButtonClick.java deleted file mode 100644 index b1ac11259..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/filter/SMTypeFilterButtonClick.java +++ /dev/null @@ -1,55 +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.artifacts.smtype.filter; - -import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement; -import org.eclipse.hawkbit.ui.artifacts.event.RefreshSoftwareModuleByFilterEvent; -import org.eclipse.hawkbit.ui.artifacts.state.ArtifactUploadState; -import org.eclipse.hawkbit.ui.common.filterlayout.AbstractFilterSingleButtonClick; -import org.vaadin.spring.events.EventBus; -import org.vaadin.spring.events.EventBus.UIEventBus; - -import com.vaadin.ui.Button; - -/** - * Single button click behavior of filter buttons layout for software module - * table on the Upload view. - */ -public class SMTypeFilterButtonClick extends AbstractFilterSingleButtonClick { - - private static final long serialVersionUID = 1L; - - private final transient EventBus.UIEventBus eventBus; - - private final ArtifactUploadState artifactUploadState; - - private final transient SoftwareModuleTypeManagement softwareModuleTypeManagement; - - SMTypeFilterButtonClick(final UIEventBus eventBus, final ArtifactUploadState artifactUploadState, - final SoftwareModuleTypeManagement softwareModuleTypeManagement) { - this.eventBus = eventBus; - this.artifactUploadState = artifactUploadState; - this.softwareModuleTypeManagement = softwareModuleTypeManagement; - } - - @Override - protected void filterUnClicked(final Button clickedButton) { - artifactUploadState.getSoftwareModuleFilters().setSoftwareModuleType(null); - eventBus.publish(this, new RefreshSoftwareModuleByFilterEvent()); - } - - @Override - protected void filterClicked(final Button clickedButton) { - softwareModuleTypeManagement.getByName(clickedButton.getData().toString()).ifPresent(softwareModuleType -> { - artifactUploadState.getSoftwareModuleFilters().setSoftwareModuleType(softwareModuleType); - eventBus.publish(this, new RefreshSoftwareModuleByFilterEvent()); - }); - } - -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/filter/SMTypeFilterButtons.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/filter/SMTypeFilterButtons.java index e2fd1b1f3..5d3ee04ec 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/filter/SMTypeFilterButtons.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/filter/SMTypeFilterButtons.java @@ -8,190 +8,115 @@ */ package org.eclipse.hawkbit.ui.artifacts.smtype.filter; -import java.util.EnumSet; -import java.util.Optional; - -import org.eclipse.hawkbit.repository.EntityFactory; import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement; -import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.ui.SpPermissionChecker; -import org.eclipse.hawkbit.ui.artifacts.event.SoftwareModuleTypeEvent; -import org.eclipse.hawkbit.ui.artifacts.event.SoftwareModuleTypeEvent.SoftwareModuleTypeEnum; -import org.eclipse.hawkbit.ui.artifacts.event.UploadArtifactUIEvent; -import org.eclipse.hawkbit.ui.artifacts.smtype.UpdateSoftwareModuleTypeLayout; -import org.eclipse.hawkbit.ui.artifacts.state.ArtifactUploadState; -import org.eclipse.hawkbit.ui.common.SoftwareModuleTypeBeanQuery; -import org.eclipse.hawkbit.ui.common.event.FilterHeaderEvent.FilterHeaderEnum; -import org.eclipse.hawkbit.ui.common.event.SoftwareModuleTypeFilterHeaderEvent; -import org.eclipse.hawkbit.ui.common.filterlayout.AbstractFilterButtons; -import org.eclipse.hawkbit.ui.dd.criteria.UploadViewClientCriterion; -import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil; +import org.eclipse.hawkbit.ui.artifacts.smtype.SmTypeWindowBuilder; +import org.eclipse.hawkbit.ui.common.data.mappers.TypeToProxyTypeMapper; +import org.eclipse.hawkbit.ui.common.data.providers.SoftwareModuleTypeDataProvider; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyIdentifiableEntity; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxySoftwareModule; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyType; +import org.eclipse.hawkbit.ui.common.event.EventView; +import org.eclipse.hawkbit.ui.common.filterlayout.AbstractTypeFilterButtons; +import org.eclipse.hawkbit.ui.common.state.TypeFilterLayoutUiState; import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.UINotification; import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; -import org.vaadin.addons.lazyquerycontainer.BeanQueryFactory; -import org.vaadin.addons.lazyquerycontainer.LazyQueryContainer; import org.vaadin.spring.events.EventBus.UIEventBus; -import org.vaadin.spring.events.EventScope; -import org.vaadin.spring.events.annotation.EventBusListenerMethod; -import com.vaadin.event.dd.DragAndDropEvent; -import com.vaadin.event.dd.DropHandler; -import com.vaadin.event.dd.acceptcriteria.AcceptCriterion; -import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Window; /** * Software module type filter buttons. * */ -public class SMTypeFilterButtons extends AbstractFilterButtons { - +public class SMTypeFilterButtons extends AbstractTypeFilterButtons { private static final long serialVersionUID = 1L; - private final ArtifactUploadState artifactUploadState; - - private final UploadViewClientCriterion uploadViewClientCriterion; - private final transient SoftwareModuleTypeManagement softwareModuleTypeManagement; + private final transient SmTypeWindowBuilder smTypeWindowBuilder; - private final transient EntityFactory entityFactory; - - private final SpPermissionChecker permChecker; - - private final UINotification uiNotification; + private final EventView view; /** * Constructor * * @param eventBus * UIEventBus - * @param artifactUploadState - * ArtifactUploadState - * @param uploadViewClientCriterion - * UploadViewClientCriterion * @param softwareModuleTypeManagement * SoftwareModuleTypeManagement * @param i18n * VaadinMessageSource - * @param entityFactory - * EntityFactory * @param permChecker * SpPermissionChecker * @param uiNotification * UINotification + * @param smTypeWindowBuilder + * SmTypeWindowBuilder + * @param typeFilterLayoutUiState + * TypeFilterLayoutUiState + * @param view + * EventView */ - public SMTypeFilterButtons(final UIEventBus eventBus, final ArtifactUploadState artifactUploadState, - final UploadViewClientCriterion uploadViewClientCriterion, - final SoftwareModuleTypeManagement softwareModuleTypeManagement, final VaadinMessageSource i18n, - final EntityFactory entityFactory, final SpPermissionChecker permChecker, - final UINotification uiNotification) { - super(eventBus, new SMTypeFilterButtonClick(eventBus, artifactUploadState, softwareModuleTypeManagement), i18n); - this.artifactUploadState = artifactUploadState; - this.uploadViewClientCriterion = uploadViewClientCriterion; + public SMTypeFilterButtons(final UIEventBus eventBus, final VaadinMessageSource i18n, + final UINotification uiNotification, final SpPermissionChecker permChecker, + final SoftwareModuleTypeManagement softwareModuleTypeManagement, + final SmTypeWindowBuilder smTypeWindowBuilder, final TypeFilterLayoutUiState typeFilterLayoutUiState, + final EventView view) { + super(eventBus, i18n, uiNotification, permChecker, typeFilterLayoutUiState); + this.softwareModuleTypeManagement = softwareModuleTypeManagement; - this.entityFactory = entityFactory; - this.permChecker = permChecker; - this.uiNotification = uiNotification; - } - - @EventBusListenerMethod(scope = EventScope.UI) - void onEvent(final SoftwareModuleTypeEvent event) { - if (event.getSoftwareModuleType() != null - && EnumSet.allOf(SoftwareModuleTypeEnum.class).contains(event.getSoftwareModuleTypeEnum())) { - refreshTable(); - } - - if (isUpdate(event)) { - getEventBus().publish(this, new SoftwareModuleTypeFilterHeaderEvent(FilterHeaderEnum.SHOW_MENUBAR)); - } - } - - private static boolean isUpdate(final SoftwareModuleTypeEvent event) { - return event.getSoftwareModuleTypeEnum() == SoftwareModuleTypeEnum.UPDATE_SOFTWARE_MODULE_TYPE; - } - - @EventBusListenerMethod(scope = EventScope.UI) - void onEvent(final UploadArtifactUIEvent event) { - if (event == UploadArtifactUIEvent.DELETED_ALL_SOFTWARE_TYPE) { - refreshTable(); - } + this.smTypeWindowBuilder = smTypeWindowBuilder; + this.view = view; + + init(); + setDataProvider( + new SoftwareModuleTypeDataProvider<>(softwareModuleTypeManagement, new TypeToProxyTypeMapper<>())); } + /** + * Gets id of the software module type grid. + * + * @return id of the grid + */ @Override - protected String getButtonsTableId() { + public String getGridId() { return UIComponentIdProvider.SW_MODULE_TYPE_TABLE_ID; } @Override - protected LazyQueryContainer createButtonsLazyQueryContainer() { - return HawkbitCommonUtil.createLazyQueryContainer(new BeanQueryFactory<>(SoftwareModuleTypeBeanQuery.class)); + protected String getFilterButtonsType() { + return i18n.getMessage("caption.entity.software.module.type"); } @Override - protected boolean isClickedByDefault(final String typeName) { - return artifactUploadState.getSoftwareModuleFilters().getSoftwareModuleType() - .map(type -> type.getName().equals(typeName)).orElse(false); + protected String getFilterButtonIdPrefix() { + return UIComponentIdProvider.SOFTWARE_MODULE_TYPE_ID_PREFIXS; } @Override - protected String createButtonId(final String name) { - return UIComponentIdProvider.SM_TYPE_FILTER_BTN_ID + name; + protected boolean isDefaultType(final ProxyType type) { + // We do not have default type for software module + return false; } @Override - protected DropHandler getFilterButtonDropHandler() { - return new DropHandler() { - private static final long serialVersionUID = 1L; - - @Override - public AcceptCriterion getAcceptCriterion() { - return uploadViewClientCriterion; - } - - @Override - public void drop(final DragAndDropEvent event) { - /* Not required */ - } - }; + protected Class getFilterMasterEntityType() { + return ProxySoftwareModule.class; } @Override - protected String getButttonWrapperIdPrefix() { - return UIComponentIdProvider.UPLOAD_TYPE_BUTTON_PREFIX; + protected EventView getView() { + return view; } @Override - protected String getButtonWrapperData() { - return null; + protected void deleteType(final ProxyType typeToDelete) { + softwareModuleTypeManagement.delete(typeToDelete.getId()); } @Override - protected void addEditButtonClickListener(final ClickEvent event) { - new UpdateSoftwareModuleTypeLayout(getI18n(), entityFactory, getEventBus(), permChecker, uiNotification, - softwareModuleTypeManagement, getEntityId(event), getCloseListenerForEditAndDeleteTag( - new SoftwareModuleTypeFilterHeaderEvent(FilterHeaderEnum.SHOW_MENUBAR))); + protected Window getUpdateWindow(final ProxyType clickedFilter) { + return smTypeWindowBuilder.getWindowForUpdate(clickedFilter); } - - @Override - protected void addDeleteButtonClickListener(final ClickEvent event) { - openConfirmationWindowForDeletion(getEntityId(event), - getI18n().getMessage("caption.entity.software.module.type"), - new SoftwareModuleTypeFilterHeaderEvent(FilterHeaderEnum.SHOW_MENUBAR)); - } - - @Override - protected void deleteEntity(final String entityToDelete) { - final Optional swmTypeToDelete = softwareModuleTypeManagement.getByName(entityToDelete); - swmTypeToDelete.ifPresent(tag -> { - if (artifactUploadState.getSoftwareModuleFilters().getSoftwareModuleType().equals(swmTypeToDelete)) { - uiNotification.displayValidationError(getI18n().getMessage("message.tag.delete", entityToDelete)); - removeUpdateAndDeleteColumn(); - } else { - softwareModuleTypeManagement.delete(swmTypeToDelete.get().getId()); - getEventBus().publish(this, UploadArtifactUIEvent.DELETED_ALL_SOFTWARE_TYPE); - uiNotification.displaySuccess(getI18n().getMessage("message.delete.success", entityToDelete)); - } - }); - } - } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/filter/SMTypeFilterHeader.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/filter/SMTypeFilterHeader.java index 01d9c1190..0f5d6313c 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/filter/SMTypeFilterHeader.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/filter/SMTypeFilterHeader.java @@ -8,122 +8,96 @@ */ package org.eclipse.hawkbit.ui.artifacts.smtype.filter; -import org.eclipse.hawkbit.repository.EntityFactory; -import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement; import org.eclipse.hawkbit.ui.SpPermissionChecker; -import org.eclipse.hawkbit.ui.artifacts.event.UploadArtifactUIEvent; -import org.eclipse.hawkbit.ui.artifacts.smtype.CreateSoftwareModuleTypeLayout; -import org.eclipse.hawkbit.ui.artifacts.state.ArtifactUploadState; -import org.eclipse.hawkbit.ui.common.event.FilterHeaderEvent.FilterHeaderEnum; -import org.eclipse.hawkbit.ui.common.event.SoftwareModuleTypeFilterHeaderEvent; -import org.eclipse.hawkbit.ui.common.filterlayout.AbstractFilterHeader; +import org.eclipse.hawkbit.ui.artifacts.smtype.SmTypeWindowBuilder; +import org.eclipse.hawkbit.ui.common.event.EventLayout; +import org.eclipse.hawkbit.ui.common.event.EventView; +import org.eclipse.hawkbit.ui.common.grid.header.AbstractFilterHeader; +import org.eclipse.hawkbit.ui.common.state.TypeFilterLayoutUiState; import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.UIMessageIdProvider; -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.Button.ClickEvent; -import com.vaadin.ui.MenuBar.Command; +import com.vaadin.ui.Window; /** * Software module type filter buttons header. */ public class SMTypeFilterHeader extends AbstractFilterHeader { - private static final long serialVersionUID = 1L; - private final ArtifactUploadState artifactUploadState; + private final TypeFilterLayoutUiState smTypeFilterLayoutUiState; - private final transient EntityFactory entityFactory; + private final transient SmTypeWindowBuilder smTypeWindowBuilder; - private final UINotification uiNotification; + private final EventView view; - private final transient SoftwareModuleTypeManagement softwareModuleTypeManagement; + /** + * Constructor for SMTypeFilterHeader + * + * @param eventBus + * UIEventBus + * @param i18n + * VaadinMessageSource + * @param permChecker + * SpPermissionChecker + * @param smTypeWindowBuilder + * SmTypeWindowBuilder + * @param smTypeFilterLayoutUiState + * TypeFilterLayoutUiState + * @param view + * EventView + */ + public SMTypeFilterHeader(final UIEventBus eventBus, final VaadinMessageSource i18n, + final SpPermissionChecker permChecker, final SmTypeWindowBuilder smTypeWindowBuilder, + final TypeFilterLayoutUiState smTypeFilterLayoutUiState, final EventView view) { + super(i18n, permChecker, eventBus); - private final SMTypeFilterButtons smTypeFilterButtons; + this.smTypeFilterLayoutUiState = smTypeFilterLayoutUiState; + this.smTypeWindowBuilder = smTypeWindowBuilder; + this.view = view; - SMTypeFilterHeader(final VaadinMessageSource i18n, final SpPermissionChecker permChecker, final UIEventBus eventBus, - final ArtifactUploadState artifactUploadState, final EntityFactory entityFactory, - final UINotification uiNotification, final SoftwareModuleTypeManagement softwareModuleTypeManagement, - final SMTypeFilterButtons smTypeFilterButtons) { - super(permChecker, eventBus, i18n); - this.artifactUploadState = artifactUploadState; - this.entityFactory = entityFactory; - this.uiNotification = uiNotification; - this.softwareModuleTypeManagement = softwareModuleTypeManagement; - this.smTypeFilterButtons = smTypeFilterButtons; + buildHeader(); } @Override - protected String getTitle() { - return getI18n().getMessage(UIMessageIdProvider.CAPTION_FILTER_BY_TYPE); + protected String getHeaderCaptionMsgKey() { + return UIMessageIdProvider.CAPTION_FILTER_BY_TYPE; } @Override - protected boolean dropHitsRequired() { - return false; - } - - @Override - protected void hideFilterButtonLayout() { - artifactUploadState.setSwTypeFilterClosed(true); - getEventBus().publish(this, UploadArtifactUIEvent.HIDE_FILTER_BY_TYPE); - } - - @Override - protected String getConfigureFilterButtonId() { - return UIComponentIdProvider.ADD_SOFTWARE_MODULE_TYPE; - } - - @Override - protected String getHideButtonId() { - return UIComponentIdProvider.SM_SHOW_FILTER_BUTTON_ID; - } - - @Override - protected boolean isAddTagRequired() { - return true; - } - - @Override - protected Command getAddButtonCommand() { - return command -> new CreateSoftwareModuleTypeLayout(getI18n(), entityFactory, getEventBus(), getPermChecker(), - uiNotification, softwareModuleTypeManagement); - } - - @Override - protected Command getDeleteButtonCommand() { - return command -> { - smTypeFilterButtons.addDeleteColumn(); - getEventBus().publish(this, new SoftwareModuleTypeFilterHeaderEvent(FilterHeaderEnum.SHOW_CANCEL_BUTTON)); - }; - } - - @Override - protected Command getUpdateButtonCommand() { - return command -> { - smTypeFilterButtons.addUpdateColumn(); - getEventBus().publish(this, new SoftwareModuleTypeFilterHeaderEvent(FilterHeaderEnum.SHOW_CANCEL_BUTTON)); - }; - } - - @Override - protected void cancelUpdateOrDeleteTag(final ClickEvent event) { - super.cancelUpdateOrDeleteTag(event); - smTypeFilterButtons.removeUpdateAndDeleteColumn(); - } - - @EventBusListenerMethod(scope = EventScope.UI) - private void onEvent(final SoftwareModuleTypeFilterHeaderEvent event) { - processFilterHeaderEvent(event); - } - - @Override - protected String getMenuBarId() { + protected String getCrudMenuBarId() { return UIComponentIdProvider.SOFT_MODULE_TYPE_MENU_BAR_ID; } + @Override + protected Window getWindowForAdd() { + return smTypeWindowBuilder.getWindowForAdd(); + } + + @Override + protected String getAddEntityWindowCaptionMsgKey() { + return "caption.type"; + } + + @Override + protected String getCloseIconId() { + return UIComponentIdProvider.HIDE_SM_TYPES; + } + + @Override + protected void updateHiddenUiState() { + smTypeFilterLayoutUiState.setHidden(true); + } + + @Override + protected EventLayout getLayout() { + return EventLayout.SM_TYPE_FILTER; + } + + @Override + protected EventView getView() { + return view; + } } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/filter/SMTypeFilterLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/filter/SMTypeFilterLayout.java index e5ebd1844..62be0fbdb 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/filter/SMTypeFilterLayout.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/filter/SMTypeFilterLayout.java @@ -8,32 +8,46 @@ */ package org.eclipse.hawkbit.ui.artifacts.smtype.filter; +import java.util.Arrays; +import java.util.List; + import org.eclipse.hawkbit.repository.EntityFactory; import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement; import org.eclipse.hawkbit.ui.SpPermissionChecker; -import org.eclipse.hawkbit.ui.artifacts.event.UploadArtifactUIEvent; -import org.eclipse.hawkbit.ui.artifacts.state.ArtifactUploadState; +import org.eclipse.hawkbit.ui.artifacts.smtype.SmTypeWindowBuilder; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxySoftwareModule; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyType; +import org.eclipse.hawkbit.ui.common.event.EventLayout; +import org.eclipse.hawkbit.ui.common.event.EventLayoutViewAware; +import org.eclipse.hawkbit.ui.common.event.EventView; import org.eclipse.hawkbit.ui.common.filterlayout.AbstractFilterLayout; +import org.eclipse.hawkbit.ui.common.layout.listener.EntityModifiedListener; +import org.eclipse.hawkbit.ui.common.layout.listener.EntityModifiedListener.EntityModifiedAwareSupport; +import org.eclipse.hawkbit.ui.common.layout.listener.GridActionsVisibilityListener; +import org.eclipse.hawkbit.ui.common.layout.listener.support.EntityModifiedGenericSupport; +import org.eclipse.hawkbit.ui.common.layout.listener.support.EntityModifiedGridRefreshAwareSupport; +import org.eclipse.hawkbit.ui.common.state.TypeFilterLayoutUiState; 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.ComponentContainer; /** * Software module type filter buttons layout. */ public class SMTypeFilterLayout extends AbstractFilterLayout { - private static final long serialVersionUID = 1L; - private final ArtifactUploadState artifactUploadState; + private final SMTypeFilterHeader smTypeFilterHeader; + private final SMTypeFilterButtons smTypeFilterButtons; + + private final transient GridActionsVisibilityListener gridActionsVisibilityListener; + private final transient EntityModifiedListener entityModifiedListener; /** * Constructor * - * @param artifactUploadState - * ArtifactUploadState * @param i18n * VaadinMessageSource * @param permChecker @@ -46,32 +60,59 @@ public class SMTypeFilterLayout extends AbstractFilterLayout { * UINotification * @param softwareModuleTypeManagement * SoftwareModuleTypeManagement - * @param sMTypeFilterButtons - * SMTypeFilterButtons + * @param smTypeFilterLayoutUiState + * SMTypeFilterLayoutUiState */ - public SMTypeFilterLayout(final ArtifactUploadState artifactUploadState, final VaadinMessageSource i18n, - final SpPermissionChecker permChecker, final UIEventBus eventBus, final EntityFactory entityFactory, - final UINotification uiNotification, final SoftwareModuleTypeManagement softwareModuleTypeManagement, - final SMTypeFilterButtons sMTypeFilterButtons) { - super(new SMTypeFilterHeader(i18n, permChecker, eventBus, artifactUploadState, entityFactory, uiNotification, - softwareModuleTypeManagement, sMTypeFilterButtons), sMTypeFilterButtons, eventBus); - this.artifactUploadState = artifactUploadState; - restoreState(); + public SMTypeFilterLayout(final VaadinMessageSource i18n, final SpPermissionChecker permChecker, + final UIEventBus eventBus, final EntityFactory entityFactory, final UINotification uiNotification, + final SoftwareModuleTypeManagement softwareModuleTypeManagement, + final TypeFilterLayoutUiState smTypeFilterLayoutUiState) { + final SmTypeWindowBuilder smTypeWindowBuilder = new SmTypeWindowBuilder(i18n, entityFactory, eventBus, + uiNotification, softwareModuleTypeManagement); + + this.smTypeFilterHeader = new SMTypeFilterHeader(eventBus, i18n, permChecker, smTypeWindowBuilder, + smTypeFilterLayoutUiState, EventView.UPLOAD); + this.smTypeFilterButtons = new SMTypeFilterButtons(eventBus, i18n, uiNotification, permChecker, + softwareModuleTypeManagement, smTypeWindowBuilder, smTypeFilterLayoutUiState, EventView.UPLOAD); + + this.gridActionsVisibilityListener = new GridActionsVisibilityListener(eventBus, + new EventLayoutViewAware(EventLayout.SM_TYPE_FILTER, EventView.UPLOAD), + smTypeFilterButtons::hideActionColumns, smTypeFilterButtons::showEditColumn, + smTypeFilterButtons::showDeleteColumn); + this.entityModifiedListener = new EntityModifiedListener.Builder<>(eventBus, ProxyType.class) + .entityModifiedAwareSupports(getEntityModifiedAwareSupports()) + .parentEntityType(ProxySoftwareModule.class).build(); + + buildLayout(); } - @EventBusListenerMethod(scope = EventScope.UI) - void onEvent(final UploadArtifactUIEvent event) { - if (event == UploadArtifactUIEvent.HIDE_FILTER_BY_TYPE) { - setVisible(false); - } - if (event == UploadArtifactUIEvent.SHOW_FILTER_BY_TYPE) { - setVisible(true); - } + private List getEntityModifiedAwareSupports() { + return Arrays.asList(EntityModifiedGridRefreshAwareSupport.of(smTypeFilterButtons::refreshAll), + EntityModifiedGenericSupport.of(null, null, smTypeFilterButtons::resetFilterOnTypesDeleted)); } @Override - public Boolean onLoadIsTypeFilterIsClosed() { - return artifactUploadState.isSwTypeFilterClosed(); + protected SMTypeFilterHeader getFilterHeader() { + return smTypeFilterHeader; } + @Override + protected ComponentContainer getFilterContent() { + return wrapFilterContent(smTypeFilterButtons); + } + + /** + * Is called when view is shown to the user + */ + public void restoreState() { + smTypeFilterButtons.restoreState(); + } + + /** + * Unsubscribe the events listeners + */ + public void unsubscribeListener() { + gridActionsVisibilityListener.unsubscribe(); + entityModifiedListener.unsubscribe(); + } } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/state/SoftwareModuleFilters.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/state/SoftwareModuleFilters.java deleted file mode 100644 index 4ea48f7c2..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/state/SoftwareModuleFilters.java +++ /dev/null @@ -1,50 +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.artifacts.state; - -import java.io.Serializable; -import java.util.Optional; - -import org.eclipse.hawkbit.repository.model.SoftwareModuleType; - -import com.vaadin.spring.annotation.SpringComponent; -import com.vaadin.spring.annotation.VaadinSessionScope; - -/** - * Software module filters. - * - * - */ -@VaadinSessionScope -@SpringComponent -public class SoftwareModuleFilters implements Serializable { - - private static final long serialVersionUID = -5251492630546463593L; - - private SoftwareModuleType softwareModuleType; - - private String searchText; - - public Optional getSoftwareModuleType() { - return Optional.ofNullable(softwareModuleType); - } - - public void setSoftwareModuleType(final SoftwareModuleType softwareModuleType) { - this.softwareModuleType = softwareModuleType; - } - - public Optional getSearchText() { - return Optional.ofNullable(searchText); - } - - public void setSearchText(final String searchText) { - this.searchText = searchText; - } - -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/AbstractFileTransferHandler.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/AbstractFileTransferHandler.java index 4f84d6a23..e2cb74e89 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/AbstractFileTransferHandler.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/AbstractFileTransferHandler.java @@ -12,10 +12,9 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Serializable; -import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executor; import java.util.concurrent.locks.Lock; -import org.apache.commons.lang3.StringUtils; import org.eclipse.hawkbit.repository.ArtifactManagement; import org.eclipse.hawkbit.repository.RegexCharacterCollection; import org.eclipse.hawkbit.repository.RegexCharacterCollection.RegexChar; @@ -28,15 +27,18 @@ import org.eclipse.hawkbit.repository.exception.StorageQuotaExceededException; import org.eclipse.hawkbit.repository.model.Artifact; import org.eclipse.hawkbit.repository.model.ArtifactUpload; import org.eclipse.hawkbit.repository.model.SoftwareModule; -import org.eclipse.hawkbit.ui.artifacts.event.SoftwareModuleEvent; -import org.eclipse.hawkbit.ui.artifacts.event.SoftwareModuleEvent.SoftwareModuleEventType; -import org.eclipse.hawkbit.ui.artifacts.state.ArtifactUploadState; +import org.eclipse.hawkbit.ui.artifacts.ArtifactUploadState; import org.eclipse.hawkbit.ui.artifacts.upload.FileUploadProgress.FileUploadStatus; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxySoftwareModule; +import org.eclipse.hawkbit.ui.common.event.EntityModifiedEventPayload; +import org.eclipse.hawkbit.ui.common.event.EntityModifiedEventPayload.EntityModifiedEventType; +import org.eclipse.hawkbit.ui.common.event.EventTopics; import org.eclipse.hawkbit.ui.utils.SpringContextHelper; import org.eclipse.hawkbit.ui.utils.UINotification; import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.util.StringUtils; import org.vaadin.spring.events.EventBus; import org.vaadin.spring.events.EventBus.UIEventBus; @@ -101,9 +103,8 @@ public abstract class AbstractFileTransferHandler implements Serializable { protected void startTransferToRepositoryThread(final InputStream inputStream, final FileUploadId fileUploadId, final String mimeType) { - SpringContextHelper.getBean("asyncExecutor", ExecutorService.class) - .execute(new TransferArtifactToRepositoryRunnable(inputStream, fileUploadId, mimeType, UI.getCurrent(), - uploadLock)); + SpringContextHelper.getBean("uiExecutor", Executor.class).execute(new TransferArtifactToRepositoryRunnable( + inputStream, fileUploadId, mimeType, UI.getCurrent(), uploadLock)); } private void interruptUploadAndSetReason(final String failureReason) { @@ -152,7 +153,7 @@ public abstract class AbstractFileTransferHandler implements Serializable { final FileUploadProgress fileUploadProgress = new FileUploadProgress(fileUploadId, FileUploadStatus.UPLOAD_STARTED); artifactUploadState.updateFileUploadProgress(fileUploadId, fileUploadProgress); - eventBus.publish(this, fileUploadProgress); + eventBus.publish(EventTopics.FILE_UPLOAD_CHANGED, this, fileUploadProgress); } protected void publishUploadProgressEvent(final FileUploadId fileUploadId, final long bytesReceived, @@ -164,14 +165,14 @@ public abstract class AbstractFileTransferHandler implements Serializable { final FileUploadProgress fileUploadProgress = new FileUploadProgress(fileUploadId, FileUploadStatus.UPLOAD_IN_PROGRESS, bytesReceived, fileSize); artifactUploadState.updateFileUploadProgress(fileUploadId, fileUploadProgress); - eventBus.publish(this, fileUploadProgress); + eventBus.publish(EventTopics.FILE_UPLOAD_CHANGED, this, fileUploadProgress); } protected void publishUploadFinishedEvent(final FileUploadId fileUploadId) { LOG.debug("Upload finished for file {}", fileUploadId); final FileUploadProgress fileUploadProgress = new FileUploadProgress(fileUploadId, FileUploadStatus.UPLOAD_FINISHED); - eventBus.publish(this, fileUploadProgress); + eventBus.publish(EventTopics.FILE_UPLOAD_CHANGED, this, fileUploadProgress); } protected void publishUploadSucceeded(final FileUploadId fileUploadId, final long fileSize) { @@ -179,21 +180,21 @@ public abstract class AbstractFileTransferHandler implements Serializable { final FileUploadProgress fileUploadProgress = new FileUploadProgress(fileUploadId, FileUploadStatus.UPLOAD_SUCCESSFUL, fileSize, fileSize); artifactUploadState.updateFileUploadProgress(fileUploadId, fileUploadProgress); - eventBus.publish(this, fileUploadProgress); + eventBus.publish(EventTopics.FILE_UPLOAD_CHANGED, this, fileUploadProgress); } protected void publishUploadFailedEvent(final FileUploadId fileUploadId) { LOG.info("Upload failed for file {} due to reason: {}", fileUploadId, failureReason); final FileUploadProgress fileUploadProgress = new FileUploadProgress(fileUploadId, FileUploadStatus.UPLOAD_FAILED, - StringUtils.isBlank(failureReason) ? i18n.getMessage(MESSAGE_UPLOAD_FAILED) : failureReason); + StringUtils.hasText(failureReason) ? failureReason : i18n.getMessage(MESSAGE_UPLOAD_FAILED)); artifactUploadState.updateFileUploadProgress(fileUploadId, fileUploadProgress); - eventBus.publish(this, fileUploadProgress); + eventBus.publish(EventTopics.FILE_UPLOAD_CHANGED, this, fileUploadProgress); } protected void publishArtifactsChanged(final FileUploadId fileUploadId) { - eventBus.publish(this, - new SoftwareModuleEvent(SoftwareModuleEventType.ARTIFACTS_CHANGED, fileUploadId.getSoftwareModuleId())); + eventBus.publish(EventTopics.ENTITY_MODIFIED, this, new EntityModifiedEventPayload( + EntityModifiedEventType.ENTITY_UPDATED, ProxySoftwareModule.class, fileUploadId.getSoftwareModuleId())); } protected void publishUploadFailedAndFinishedEvent(final FileUploadId fileUploadId) { @@ -236,6 +237,20 @@ public abstract class AbstractFileTransferHandler implements Serializable { private final UI vaadinUi; private final Lock uploadLock; + /** + * Constructor for TransferArtifactToRepositoryRunnable + * + * @param inputStream + * InputStream + * @param fileUploadId + * FileUploadId + * @param mimeType + * String + * @param vaadinUi + * UI + * @param uploadLock + * Lock + */ public TransferArtifactToRepositoryRunnable(final InputStream inputStream, final FileUploadId fileUploadId, final String mimeType, final UI vaadinUi, final Lock uploadLock) { this.inputStream = inputStream; @@ -285,6 +300,7 @@ public abstract class AbstractFileTransferHandler implements Serializable { LOG.debug("Transfering file {} directly to repository", filename); final Artifact artifact = uploadArtifact(filename); if (isUploadInterrupted()) { + LOG.warn("Upload of {} was interrupted", filename); handleUploadFailure(artifact); publishUploadFinishedEvent(fileUploadId); return; diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/FileTransferHandlerStreamVariable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/FileTransferHandlerStreamVariable.java index 878ba82f0..87b73e844 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/FileTransferHandlerStreamVariable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/FileTransferHandlerStreamVariable.java @@ -24,8 +24,6 @@ import org.slf4j.LoggerFactory; import com.google.common.io.ByteStreams; import com.vaadin.server.StreamVariable; -import com.vaadin.ui.Upload.FinishedEvent; -import com.vaadin.ui.Upload.SucceededEvent; /** * {@link StreamVariable} implementation to read and upload a file. One instance @@ -61,12 +59,19 @@ public class FileTransferHandlerStreamVariable extends AbstractFileTransferHandl publishUploadStarted(fileUploadId); } + /** + * Checks for duplication and invalid file name during feil upload process + * + * @param event + * StreamingStartEvent + */ @Override public void streamingStarted(final StreamingStartEvent event) { assertStateConsistency(fileUploadId, event.getFileName()); if (RegexCharacterCollection.stringContainsCharacter(event.getFileName(), ILLEGAL_FILENAME_CHARACTERS)) { - LOG.debug("Filename contains illegal characters {} for upload {}", fileUploadId.getFilename(), fileUploadId); + LOG.debug("Filename contains illegal characters {} for upload {}", fileUploadId.getFilename(), + fileUploadId); interruptUploadDueToIllegalFilename(); } else if (isFileAlreadyContainedInSoftwareModule(fileUploadId, selectedSoftwareModule)) { LOG.debug("File {} already contained in Software Module {}", fileUploadId.getFilename(), @@ -75,6 +80,11 @@ public class FileTransferHandlerStreamVariable extends AbstractFileTransferHandl } } + /** + * get the output stream of uploaded file + * + * @return OutputStream + */ @Override public final OutputStream getOutputStream() { if (isUploadInterrupted()) { @@ -134,8 +144,7 @@ public class FileTransferHandlerStreamVariable extends AbstractFileTransferHandl /** * Upload finished for {@link StreamVariable} variant. Called only in good - * case. So a combination of {@link #uploadSucceeded(SucceededEvent)} and - * {@link #uploadFinished(FinishedEvent)}. + * case. * * @see com.vaadin.server.StreamVariable#streamingFinished(com.vaadin.server.StreamVariable.StreamingEndEvent) */ @@ -161,6 +170,11 @@ public class FileTransferHandlerStreamVariable extends AbstractFileTransferHandl publishUploadFailedAndFinishedEvent(fileUploadId); } + /** + * Verify if upload process is interrupted + * + * @return boolean + */ @Override public boolean isInterrupted() { return isUploadInterrupted(); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/FileTransferHandlerVaadinUpload.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/FileTransferHandlerVaadinUpload.java index f933ec16e..f007a8861 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/FileTransferHandlerVaadinUpload.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/FileTransferHandlerVaadinUpload.java @@ -59,6 +59,7 @@ public class FileTransferHandlerVaadinUpload extends AbstractFileTransferHandler FileTransferHandlerVaadinUpload(final long maxSize, final SoftwareModuleManagement softwareManagement, final ArtifactManagement artifactManagement, final VaadinMessageSource i18n, final Lock uploadLock) { super(artifactManagement, i18n, uploadLock); + this.maxSize = maxSize; this.softwareModuleManagement = softwareManagement; } @@ -91,7 +92,8 @@ public class FileTransferHandlerVaadinUpload extends AbstractFileTransferHandler interruptUploadDueToIllegalFilename(); event.getUpload().interruptUpload(); } else if (isFileAlreadyContainedInSoftwareModule(fileUploadId, softwareModule)) { - LOG.debug("File {} already contained in Software Module {}", fileUploadId.getFilename(), softwareModule); + LOG.debug("File {} already contained in Software Module {}", fileUploadId.getFilename(), + softwareModule); interruptUploadDueToDuplicateFile(); event.getUpload().interruptUpload(); } @@ -99,12 +101,9 @@ public class FileTransferHandlerVaadinUpload extends AbstractFileTransferHandler } private SoftwareModule getSelectedSoftwareModule() { - if (getUploadState().isMoreThanOneSoftwareModulesSelected()) { - throw new IllegalStateException("More than one SoftwareModul selected but only one is allowed"); - } - final long selectedId = getUploadState().getSelectedBaseSwModuleId() - .orElseThrow(() -> new IllegalStateException("No SoftwareModul selected")); - return softwareModuleManagement.get(selectedId) + final Long lastSelectedSmId = getUploadState().getSmGridLayoutUiState().getSelectedEntityId(); + + return softwareModuleManagement.get(lastSelectedSmId) .orElseThrow(() -> new IllegalStateException("SoftwareModul with unknown ID selected")); } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/FileUploadId.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/FileUploadId.java index fd23e398f..339075b14 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/FileUploadId.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/FileUploadId.java @@ -101,18 +101,38 @@ public class FileUploadId implements Serializable { return id; } + /** + * Getter for the uploaded file name + * + * @return String + */ public String getFilename() { return filename; } + /** + * Getter for the software module name + * + * @return String + */ public String getSoftwareModuleName() { return softwareModuleName; } + /** + * Getter for the software module version + * + * @return String + */ public String getSoftwareModuleVersion() { return softwareModuleVersion; } + /** + * Getter for the software module ID + * + * @return Long + */ public Long getSoftwareModuleId() { return softwareModuleId; } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/FileUploadProgress.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/FileUploadProgress.java index b4652dabf..ad9cb04fa 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/FileUploadProgress.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/FileUploadProgress.java @@ -137,26 +137,55 @@ public class FileUploadProgress implements Serializable { this.failureReason = failureReason; } + /** + * Getter for file upload ID + * + * @return FileUploadId + */ public FileUploadId getFileUploadId() { return fileUploadId; } + /** + * Getter for file content length + * + * @return long + */ public long getContentLength() { return contentLength; } + /** + * Getter for bytes read of the file + * + * @return long + */ public long getBytesRead() { return bytesRead; } + /** + * Getter for failed reason for upload + * @return String + */ public String getFailureReason() { return failureReason; } + /** + * Getter for file path + * + * @return String + */ public String getFilePath() { return filePath; } + /** + * Get Status of a file upload + * + * @return FileUploadStatus + */ public FileUploadStatus getFileUploadStatus() { return fileUploadStatus; } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadDropAreaLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadDropAreaLayout.java index a6b89b7f5..f6124d6f5 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadDropAreaLayout.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadDropAreaLayout.java @@ -8,70 +8,54 @@ */ package org.eclipse.hawkbit.ui.artifacts.upload; +import java.util.Collection; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + import javax.servlet.MultipartConfigElement; import org.eclipse.hawkbit.repository.ArtifactManagement; import org.eclipse.hawkbit.repository.SoftwareModuleManagement; import org.eclipse.hawkbit.repository.model.SoftwareModule; -import org.eclipse.hawkbit.ui.artifacts.event.SoftwareModuleEvent; -import org.eclipse.hawkbit.ui.artifacts.state.ArtifactUploadState; -import org.eclipse.hawkbit.ui.common.table.BaseEntityEventType; -import org.eclipse.hawkbit.ui.dd.criteria.ServerItemIdClientCriterion; -import org.eclipse.hawkbit.ui.dd.criteria.ServerItemIdClientCriterion.Mode; +import org.eclipse.hawkbit.ui.artifacts.ArtifactUploadState; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxySoftwareModule; +import org.eclipse.hawkbit.ui.common.layout.MasterEntityAwareComponent; import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.UIMessageIdProvider; import org.eclipse.hawkbit.ui.utils.UINotification; import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; 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.event.dd.DragAndDropEvent; -import com.vaadin.event.dd.DropHandler; -import com.vaadin.event.dd.acceptcriteria.AcceptCriterion; -import com.vaadin.event.dd.acceptcriteria.Not; -import com.vaadin.server.FontAwesome; -import com.vaadin.shared.ui.label.ContentMode; -import com.vaadin.ui.AbstractComponent; -import com.vaadin.ui.Alignment; -import com.vaadin.ui.DragAndDropWrapper; -import com.vaadin.ui.DragAndDropWrapper.WrapperTransferable; +import com.vaadin.icons.VaadinIcons; +import com.vaadin.shared.ui.ContentMode; +import com.vaadin.ui.CustomComponent; import com.vaadin.ui.Html5File; import com.vaadin.ui.Label; -import com.vaadin.ui.UI; import com.vaadin.ui.VerticalLayout; - -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; +import com.vaadin.ui.dnd.FileDropHandler; +import com.vaadin.ui.dnd.FileDropTarget; +import com.vaadin.ui.dnd.event.FileDropEvent; /** * Container for drag and drop area in the upload view. */ -public class UploadDropAreaLayout extends AbstractComponent { - +public class UploadDropAreaLayout extends CustomComponent implements MasterEntityAwareComponent { private static final long serialVersionUID = 1L; - private static AcceptCriterion acceptAllExceptBlacklisted = new Not(new ServerItemIdClientCriterion(Mode.PREFIX, - UIComponentIdProvider.UPLOAD_SOFTWARE_MODULE_TABLE, UIComponentIdProvider.UPLOAD_TYPE_BUTTON_PREFIX)); - - private DragAndDropWrapper dropAreaWrapper; - private final VaadinMessageSource i18n; - private final UINotification uiNotification; private final ArtifactUploadState artifactUploadState; - private final transient MultipartConfigElement multipartConfigElement; - + private final transient ArtifactManagement artifactManagement; private final transient SoftwareModuleManagement softwareManagement; - private final transient ArtifactManagement artifactManagement; + private final transient MultipartConfigElement multipartConfigElement; + private final transient Lock uploadLock = new ReentrantLock(); private final UploadProgressButtonLayout uploadButtonLayout; - - private final transient Lock uploadLock = new ReentrantLock(); + private VerticalLayout dropAreaLayout; /** * Creates a new {@link UploadDropAreaLayout} instance. @@ -103,84 +87,112 @@ public class UploadDropAreaLayout extends AbstractComponent { this.multipartConfigElement = multipartConfigElement; this.softwareManagement = softwareManagement; this.artifactManagement = artifactManagement; + this.uploadButtonLayout = new UploadProgressButtonLayout(i18n, eventBus, artifactUploadState, multipartConfigElement, artifactManagement, softwareManagement, uploadLock); buildLayout(); - - eventBus.subscribe(this); - } - - @EventBusListenerMethod(scope = EventScope.UI) - void onEvent(final SoftwareModuleEvent event) { - final BaseEntityEventType eventType = event.getEventType(); - if (eventType == BaseEntityEventType.SELECTED_ENTITY) { - UI.getCurrent().access(() -> { - if (artifactUploadState.isNoSoftwareModuleSelected() - || artifactUploadState.isMoreThanOneSoftwareModulesSelected()) { - dropAreaWrapper.setEnabled(false); - } else if (artifactUploadState.areAllUploadsFinished()) { - dropAreaWrapper.setEnabled(true); - } - }); - } } private void buildLayout() { - dropAreaWrapper = new DragAndDropWrapper(createDropAreaLayout()); - dropAreaWrapper.setDropHandler(new DropAreaHandler()); - } + dropAreaLayout = new VerticalLayout(); + dropAreaLayout.setId(UIComponentIdProvider.UPLOAD_ARTIFACT_FILE_DROP_LAYOUT); + dropAreaLayout.setMargin(false); + dropAreaLayout.setSpacing(false); + dropAreaLayout.addStyleName("upload-drop-area-layout-info"); + dropAreaLayout.setEnabled(false); + dropAreaLayout.setHeightUndefined(); - private VerticalLayout createDropAreaLayout() { - final VerticalLayout dropAreaLayout = new VerticalLayout(); - final Label dropHereLabel = new Label(i18n.getMessage(UIMessageIdProvider.LABEL_DROP_AREA_UPLOAD)); - dropHereLabel.setWidth(null); - - final Label dropIcon = new Label(FontAwesome.ARROW_DOWN.getHtml(), ContentMode.HTML); + final Label dropIcon = new Label(VaadinIcons.ARROW_DOWN.getHtml(), ContentMode.HTML); dropIcon.addStyleName("drop-icon"); dropIcon.setWidth(null); - dropAreaLayout.addComponent(dropIcon); - dropAreaLayout.setComponentAlignment(dropIcon, Alignment.TOP_CENTER); + + final Label dropHereLabel = new Label(i18n.getMessage(UIMessageIdProvider.LABEL_DROP_AREA_UPLOAD)); + dropHereLabel.setWidth(null); dropAreaLayout.addComponent(dropHereLabel); - dropAreaLayout.setComponentAlignment(dropHereLabel, Alignment.TOP_CENTER); uploadButtonLayout.setWidth(null); uploadButtonLayout.addStyleName("upload-button"); dropAreaLayout.addComponent(uploadButtonLayout); - dropAreaLayout.setComponentAlignment(uploadButtonLayout, Alignment.BOTTOM_CENTER); - dropAreaLayout.setSizeFull(); - dropAreaLayout.setStyleName("upload-drop-area-layout-info"); - dropAreaLayout.setSpacing(false); + new FileDropTarget<>(dropAreaLayout, new UploadFileDropHandler()); + + setCompositionRoot(dropAreaLayout); + } + + /** + * Update the upload view on file drop + * + * @param masterEntity + * ProxySoftwareModule + */ + @Override + public void masterEntityChanged(final ProxySoftwareModule masterEntity) { + final Long masterEntityId = masterEntity != null ? masterEntity.getId() : null; + + dropAreaLayout.setEnabled(masterEntityId != null); + uploadButtonLayout.updateMasterEntityFilter(masterEntityId); + } + + /** + * Checks progress on file upload + * + * @param fileUploadProgress + * FileUploadProgress + */ + public void onUploadChanged(final FileUploadProgress fileUploadProgress) { + uploadButtonLayout.onUploadChanged(fileUploadProgress); + } + + /** + * Is called when view is shown to the user + */ + public void restoreState() { + uploadButtonLayout.restoreState(); + } + + /** + * Get file drop area layout + * + * @return VerticalLayout + */ + public VerticalLayout getDropAreaLayout() { return dropAreaLayout; } - public DragAndDropWrapper getDropAreaWrapper() { - return dropAreaWrapper; + /** + * Get File upload button layout + * + * @return UploadProgressButtonLayout + */ + public UploadProgressButtonLayout getUploadButtonLayout() { + return uploadButtonLayout; } - private class DropAreaHandler implements DropHandler { + private class UploadFileDropHandler implements FileDropHandler { private static final long serialVersionUID = 1L; + /** + * Validates the file drop events and triggers the upload + * + * @param event + * FileDropEvent + */ @Override - public AcceptCriterion getAcceptCriterion() { - return acceptAllExceptBlacklisted; - } - - @Override - public void drop(final DragAndDropEvent event) { + public void drop(final FileDropEvent event) { if (validate(event)) { - final Html5File[] files = ((WrapperTransferable) event.getTransferable()).getFiles(); // selected software module at the time of file drop is // considered for upload - artifactUploadState.getSelectedBaseSwModuleId() - .ifPresent(selectedSwId -> uploadFilesForSoftwareModule(files, selectedSwId)); + final Long lastSelectedSmId = artifactUploadState.getSmGridLayoutUiState().getSelectedEntityId(); + if (lastSelectedSmId != null) { + uploadFilesForSoftwareModule(event.getFiles(), lastSelectedSmId); + } } } - private void uploadFilesForSoftwareModule(final Html5File[] files, final Long softwareModuleId) { + private void uploadFilesForSoftwareModule(final Collection files, final Long softwareModuleId) { final SoftwareModule softwareModule = softwareManagement.get(softwareModuleId).orElse(null); boolean duplicateFound = false; @@ -199,7 +211,7 @@ public class UploadDropAreaLayout extends AbstractComponent { } } - private boolean validate(final DragAndDropEvent event) { + private boolean validate(final FileDropEvent event) { // check if drop is valid.If valid ,check if software module is // selected. if (!isFilesDropped(event)) { @@ -209,29 +221,19 @@ public class UploadDropAreaLayout extends AbstractComponent { return validateSoftwareModuleSelection(); } - private boolean isFilesDropped(final DragAndDropEvent event) { - if (event.getTransferable() instanceof WrapperTransferable) { - final Html5File[] files = ((WrapperTransferable) event.getTransferable()).getFiles(); - return files != null; - } - return false; + private boolean isFilesDropped(final FileDropEvent event) { + return event.getFiles() != null; } private boolean validateSoftwareModuleSelection() { - if (artifactUploadState.isNoSoftwareModuleSelected()) { + final Long lastSelectedSmId = artifactUploadState.getSmGridLayoutUiState().getSelectedEntityId(); + + if (lastSelectedSmId == null) { uiNotification.displayValidationError(i18n.getMessage("message.error.noSwModuleSelected")); return false; } - if (artifactUploadState.isMoreThanOneSoftwareModulesSelected()) { - uiNotification.displayValidationError(i18n.getMessage("message.error.multiSwModuleSelected")); - return false; - } + return true; } } - - public UploadProgressButtonLayout getUploadButtonLayout() { - return uploadButtonLayout; - } - } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadFixed.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadFixed.java index 576dac6a9..56e4ab9b2 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadFixed.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadFixed.java @@ -27,6 +27,9 @@ public class UploadFixed extends Upload { private boolean uploadInterrupted; + /** + * Stops the file upload + */ @Override public void interruptUpload() { super.interruptUpload(); @@ -42,6 +45,12 @@ public class UploadFixed extends Upload { private static final long serialVersionUID = 1L; private final StreamVariable originalStreamVariable; + /** + * Constructor for StreamVariableFixed + * + * @param originalStreamVariable + * StreamVariable + */ public StreamVariableFixed(final StreamVariable originalStreamVariable) { this.originalStreamVariable = originalStreamVariable; } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadProgressButtonLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadProgressButtonLayout.java index 9848e4696..f0b5bbe44 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadProgressButtonLayout.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadProgressButtonLayout.java @@ -8,56 +8,47 @@ */ package org.eclipse.hawkbit.ui.artifacts.upload; +import java.util.concurrent.locks.Lock; + import javax.servlet.MultipartConfigElement; import org.eclipse.hawkbit.repository.ArtifactManagement; import org.eclipse.hawkbit.repository.SoftwareModuleManagement; import org.eclipse.hawkbit.repository.model.SoftwareModule; -import org.eclipse.hawkbit.ui.artifacts.event.SoftwareModuleEvent; -import org.eclipse.hawkbit.ui.artifacts.state.ArtifactUploadState; -import org.eclipse.hawkbit.ui.common.table.BaseEntityEventType; +import org.eclipse.hawkbit.ui.artifacts.ArtifactUploadState; import org.eclipse.hawkbit.ui.components.SPUIComponentProvider; import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleNoBorder; import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions; import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; 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.Button; import com.vaadin.ui.UI; +import com.vaadin.ui.Upload; import com.vaadin.ui.VerticalLayout; -import java.util.concurrent.locks.Lock; - /** * Container for upload and progress button. */ public class UploadProgressButtonLayout extends VerticalLayout { - private static final long serialVersionUID = 1L; - private final UploadProgressInfoWindow uploadInfoWindow; - private final VaadinMessageSource i18n; - private final transient MultipartConfigElement multipartConfigElement; - - private final UI ui; - - private Button uploadProgressButton; - - private final transient SoftwareModuleManagement softwareModuleManagement; - - private final UploadFixed upload; - - private final transient ArtifactManagement artifactManagement; - private final ArtifactUploadState artifactUploadState; + private final transient ArtifactManagement artifactManagement; + private final transient SoftwareModuleManagement softwareModuleManagement; + + private final transient MultipartConfigElement multipartConfigElement; + private final Upload upload; private final transient Lock uploadLock; + private final UploadProgressInfoWindow uploadInfoWindow; + + private Button uploadProgressButton; + /** * Creates a new {@link UploadProgressButtonLayout} instance. * @@ -76,14 +67,16 @@ public class UploadProgressButtonLayout extends VerticalLayout { * the {@link ArtifactManagement} for storing the uploaded * artifacts * @param uploadLock - * A common upload lock that enforced sequential upload within an UI instance + * A common upload lock that enforced sequential upload within an + * UI instance */ public UploadProgressButtonLayout(final VaadinMessageSource i18n, final UIEventBus eventBus, final ArtifactUploadState artifactUploadState, final MultipartConfigElement multipartConfigElement, - final ArtifactManagement artifactManagement, final SoftwareModuleManagement softwareManagement, final Lock uploadLock) { + final ArtifactManagement artifactManagement, final SoftwareModuleManagement softwareManagement, + final Lock uploadLock) { this.artifactUploadState = artifactUploadState; this.artifactManagement = artifactManagement; - this.uploadInfoWindow = new UploadProgressInfoWindow(eventBus, artifactUploadState, i18n); + this.uploadInfoWindow = new UploadProgressInfoWindow(i18n, artifactUploadState); this.uploadInfoWindow.addCloseListener(event -> { // ensure that the progress button is hidden when the progress // window is closed and no more uploads running @@ -99,25 +92,34 @@ public class UploadProgressButtonLayout extends VerticalLayout { createComponents(); buildLayout(); - restoreState(); - ui = UI.getCurrent(); - - eventBus.subscribe(this); } - @EventBusListenerMethod(scope = EventScope.UI) - void onEvent(final FileUploadProgress fileUploadProgress) { + /** + * Perform specific tasks based on the file upload status + * + * @param fileUploadProgress + * FileUploadProgress + */ + public void onUploadChanged(final FileUploadProgress fileUploadProgress) { final FileUploadProgress.FileUploadStatus uploadProgressEventType = fileUploadProgress.getFileUploadStatus(); + switch (uploadProgressEventType) { case UPLOAD_STARTED: - ui.access(this::onStartOfUpload); + UI.getCurrent().access(() -> { + onStartOfUpload(); + uploadInfoWindow.onUploadStarted(fileUploadProgress); + }); break; case UPLOAD_FAILED: case UPLOAD_SUCCESSFUL: case UPLOAD_IN_PROGRESS: + UI.getCurrent().access(() -> uploadInfoWindow.updateUploadProgressInfoRowObject(fileUploadProgress)); break; case UPLOAD_FINISHED: - ui.access(this::onUploadFinished); + UI.getCurrent().access(() -> { + onUploadFinished(); + uploadInfoWindow.onUploadFinished(); + }); break; default: throw new IllegalArgumentException("Enum " + FileUploadProgress.FileUploadStatus.class.getSimpleName() @@ -125,35 +127,28 @@ public class UploadProgressButtonLayout extends VerticalLayout { } } - @EventBusListenerMethod(scope = EventScope.UI) - void onEvent(final SoftwareModuleEvent event) { - final BaseEntityEventType eventType = event.getEventType(); - if (eventType == BaseEntityEventType.SELECTED_ENTITY) { - ui.access(() -> { - if (artifactUploadState.isNoSoftwareModuleSelected() - || artifactUploadState.isMoreThanOneSoftwareModulesSelected()) { - upload.setEnabled(false); - } else if (artifactUploadState.areAllUploadsFinished()) { - upload.setEnabled(true); - } - }); - } + /** + * Enable the upload view after upload is finished + * + * @param masterEntityId + * Long + */ + public void updateMasterEntityFilter(final Long masterEntityId) { + upload.setEnabled(masterEntityId != null && artifactUploadState.areAllUploadsFinished()); } private void createComponents() { uploadProgressButton = SPUIComponentProvider.getButton(UIComponentIdProvider.UPLOAD_STATUS_BUTTON, "", "", "", false, null, SPUIButtonStyleNoBorder.class); uploadProgressButton.addStyleName(SPUIStyleDefinitions.UPLOAD_PROGRESS_INDICATOR_STYLE); - uploadProgressButton.setIcon(null); - uploadProgressButton.setHtmlContentAllowed(true); - uploadProgressButton.addClickListener(event -> onClickOfUploadProgressButton()); + uploadProgressButton.addClickListener(event -> showUploadInfoWindow()); } private void buildLayout() { final FileTransferHandlerVaadinUpload uploadHandler = new FileTransferHandlerVaadinUpload( - multipartConfigElement.getMaxFileSize(), softwareModuleManagement, artifactManagement, i18n, uploadLock); + multipartConfigElement.getMaxFileSize(), softwareModuleManagement, artifactManagement, i18n, + uploadLock); upload.setButtonCaption(i18n.getMessage("upload.file")); - upload.setImmediate(true); upload.setReceiver(uploadHandler); upload.addSucceededListener(uploadHandler); upload.addFailedListener(uploadHandler); @@ -163,7 +158,8 @@ public class UploadProgressButtonLayout extends VerticalLayout { upload.setId(UIComponentIdProvider.UPLOAD_BUTTON); addComponent(upload); - setSpacing(true); + setSizeFull(); + setMargin(false); } /** @@ -194,7 +190,10 @@ public class UploadProgressButtonLayout extends VerticalLayout { } } - private void onClickOfUploadProgressButton() { + /** + * Maximize the file upload view + */ + public void showUploadInfoWindow() { uploadInfoWindow.maximizeWindow(); } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadProgressGrid.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadProgressGrid.java new file mode 100644 index 000000000..a421ad190 --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadProgressGrid.java @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2020 Bosch.IO 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.artifacts.upload; + +import org.eclipse.hawkbit.ui.common.builder.GridComponentBuilder; +import org.eclipse.hawkbit.ui.common.builder.StatusIconBuilder.ProgressStatusIconSupplier; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyUploadProgress; +import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil; +import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions; +import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; +import org.eclipse.hawkbit.ui.utils.UIMessageIdProvider; +import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; + +import com.vaadin.ui.Grid; +import com.vaadin.ui.renderers.ProgressBarRenderer; + +/** + * Grid for Upload Progress pop up info window. + */ +public class UploadProgressGrid extends Grid { + private static final long serialVersionUID = 1L; + + private final VaadinMessageSource i18n; + + private static final String UPLOAD_PROGRESS_STATUS_ID = "uploadProgressStatus"; + private static final String UPLOAD_PROGRESS_BAR_ID = "uploadProgressBar"; + private static final String UPLOAD_PROGRESS_FILENAME_ID = "uploadProgressFileName"; + private static final String UPLOAD_PROGRESS_SM_ID = "uploadProgressSm"; + private static final String UPLOAD_PROGRESS_REASON_ID = "uploadProgressReason"; + + private final ProgressStatusIconSupplier progressStatusIconSupplier; + + /** + * Constructor for UploadProgressGrid + * + * @param i18n + * VaadinMessageSource + */ + public UploadProgressGrid(final VaadinMessageSource i18n) { + this.i18n = i18n; + + progressStatusIconSupplier = new ProgressStatusIconSupplier<>(i18n, ProxyUploadProgress::getStatus, + UIComponentIdProvider.UPLOAD_STATUS_LABEL_ID); + init(); + } + + private void init() { + setId(UIComponentIdProvider.UPLOAD_STATUS_POPUP_GRID); + addStyleName(SPUIStyleDefinitions.UPLOAD_STATUS_GRID); + setSelectionMode(SelectionMode.NONE); + setSizeFull(); + + addColumns(); + } + + private void addColumns() { + GridComponentBuilder.addIconColumn(this, progressStatusIconSupplier::getLabel, UPLOAD_PROGRESS_STATUS_ID, + i18n.getMessage(UIMessageIdProvider.CAPTION_ARTIFACT_UPLOAD_STATUS)); + + addColumn(ProxyUploadProgress::getProgress, new ProgressBarRenderer()).setId(UPLOAD_PROGRESS_BAR_ID) + .setCaption(i18n.getMessage(UIMessageIdProvider.CAPTION_ARTIFACT_UPLOAD_PROGRESS)).setExpandRatio(1); + + GridComponentBuilder.addColumn(this, uploadProgress -> uploadProgress.getFileUploadId().getFilename()) + .setId(UPLOAD_PROGRESS_FILENAME_ID) + .setCaption(i18n.getMessage(UIMessageIdProvider.CAPTION_ARTIFACT_FILENAME)); + + GridComponentBuilder + .addColumn(this, + uploadProgress -> HawkbitCommonUtil.getFormattedNameVersion( + uploadProgress.getFileUploadId().getSoftwareModuleName(), + uploadProgress.getFileUploadId().getSoftwareModuleVersion())) + .setId(UPLOAD_PROGRESS_SM_ID).setCaption(i18n.getMessage(UIMessageIdProvider.CAPTION_SOFTWARE_MODULE)); + + GridComponentBuilder.addColumn(this, ProxyUploadProgress::getReason).setId(UPLOAD_PROGRESS_REASON_ID) + .setCaption(i18n.getMessage(UIMessageIdProvider.CAPTION_ARTIFACT_UPLOAD_REASON)); + + getColumns().forEach(col -> col.setSortable(false)); + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadProgressInfoWindow.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadProgressInfoWindow.java index 10f94ceab..356d897fe 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadProgressInfoWindow.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadProgressInfoWindow.java @@ -8,76 +8,52 @@ */ package org.eclipse.hawkbit.ui.artifacts.upload; -import org.eclipse.hawkbit.ui.artifacts.state.ArtifactUploadState; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.hawkbit.ui.artifacts.ArtifactUploadState; import org.eclipse.hawkbit.ui.artifacts.upload.FileUploadProgress.FileUploadStatus; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyUploadProgress; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyUploadProgress.ProgressSatus; import org.eclipse.hawkbit.ui.components.SPUIComponentProvider; import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleNoBorder; -import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil; import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions; import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.UIMessageIdProvider; import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; import org.springframework.util.StringUtils; -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.Indexed; -import com.vaadin.data.Item; -import com.vaadin.data.util.IndexedContainer; -import com.vaadin.server.FontAwesome; +import com.vaadin.icons.VaadinIcons; import com.vaadin.shared.ui.window.WindowMode; import com.vaadin.ui.Button; -import com.vaadin.ui.Grid; -import com.vaadin.ui.Grid.SelectionMode; import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.Label; import com.vaadin.ui.UI; import com.vaadin.ui.VerticalLayout; import com.vaadin.ui.Window; -import com.vaadin.ui.renderers.HtmlRenderer; -import com.vaadin.ui.renderers.ProgressBarRenderer; import com.vaadin.ui.themes.ValoTheme; -import elemental.json.JsonValue; - /** * Window that shows the progress of all uploads. */ public class UploadProgressInfoWindow extends Window { - private static final long serialVersionUID = 1L; - private static final String COLUMN_PROGRESS = UIMessageIdProvider.CAPTION_ARTIFACT_UPLOAD_PROGRESS; - private static final String COLUMN_FILE_NAME = UIMessageIdProvider.CAPTION_ARTIFACT_FILENAME; - private static final String COLUMN_STATUS = UIMessageIdProvider.CAPTION_ARTIFACT_UPLOAD_STATUS; - private static final String COLUMN_REASON = UIMessageIdProvider.CAPTION_ARTIFACT_UPLOAD_REASON; - private static final String COLUMN_SOFTWARE_MODULE = UIMessageIdProvider.CAPTION_SOFTWARE_MODULE; - - private static final String STATUS_INPROGRESS = "InProgress"; - private static final String STATUS_FINISHED = "Finished"; - private static final String STATUS_FAILED = "Failed"; - - private final ArtifactUploadState artifactUploadState; - private final VaadinMessageSource i18n; - private final Grid grid; - - private final IndexedContainer uploads; + private final ArtifactUploadState artifactUploadState; + private final UploadProgressGrid uploadProgressGrid; private final VerticalLayout mainLayout; - private final UI ui; - private Label windowCaption; - private Button closeButton; - UploadProgressInfoWindow(final UIEventBus eventBus, final ArtifactUploadState artifactUploadState, - final VaadinMessageSource i18n) { - this.artifactUploadState = artifactUploadState; + private final List uploads; + + UploadProgressInfoWindow(final VaadinMessageSource i18n, final ArtifactUploadState artifactUploadState) { this.i18n = i18n; + this.artifactUploadState = artifactUploadState; setPopupProperties(); createStatusPopupHeaderComponents(); @@ -87,44 +63,84 @@ public class UploadProgressInfoWindow extends Window { mainLayout.setSizeUndefined(); setPopupSizeInMinMode(); - uploads = getGridContainer(); - grid = createGrid(); - setGridColumnProperties(); + uploads = new ArrayList<>(); + uploadProgressGrid = new UploadProgressGrid(i18n); + uploadProgressGrid.setItems(uploads); - mainLayout.addComponents(getCaptionLayout(), grid); - mainLayout.setExpandRatio(grid, 1.0F); + mainLayout.addComponents(getCaptionLayout(), uploadProgressGrid); + mainLayout.setExpandRatio(uploadProgressGrid, 1.0F); setContent(mainLayout); - eventBus.subscribe(this); - ui = UI.getCurrent(); } - @EventBusListenerMethod(scope = EventScope.UI) - void onEvent(final FileUploadProgress fileUploadProgress) { - switch (fileUploadProgress.getFileUploadStatus()) { - case UPLOAD_STARTED: - ui.access(() -> onUploadStarted(fileUploadProgress)); - break; - case UPLOAD_IN_PROGRESS: - case UPLOAD_FAILED: - case UPLOAD_SUCCESSFUL: - ui.access(() -> updateUploadProgressInfoRowObject(fileUploadProgress)); - break; - case UPLOAD_FINISHED: - ui.access(this::onUploadFinished); - break; - default: - break; + /** + * Updates the status of each file uploaded in the grid view + * + * @param fileUploadProgress + * FileUploadProgress + */ + public void updateUploadProgressInfoRowObject(final FileUploadProgress fileUploadProgress) { + final FileUploadId fileUploadId = fileUploadProgress.getFileUploadId(); + final ProxyUploadProgress gridUploadItem = uploads.stream() + .filter(upload -> upload.getFileUploadId().equals(fileUploadId)).findAny() + .orElse(new ProxyUploadProgress()); + + gridUploadItem.setStatus(getStatusRepresentaion(fileUploadProgress.getFileUploadStatus())); + gridUploadItem.setReason(getFailureReason(fileUploadId)); + + final long bytesRead = fileUploadProgress.getBytesRead(); + final long fileSize = fileUploadProgress.getContentLength(); + if (bytesRead > 0 && fileSize > 0) { + gridUploadItem.setProgress((double) bytesRead / (double) fileSize); + } + + if (gridUploadItem.getFileUploadId() == null) { + gridUploadItem.setFileUploadId(fileUploadId); + uploads.add(gridUploadItem); + } + + uploadProgressGrid.getDataProvider().refreshItem(gridUploadItem); + } + + private static ProgressSatus getStatusRepresentaion(final FileUploadStatus uploadStatus) { + if (uploadStatus == FileUploadStatus.UPLOAD_FAILED) { + return ProgressSatus.FAILED; + } else if (uploadStatus == FileUploadStatus.UPLOAD_SUCCESSFUL) { + return ProgressSatus.FINISHED; + } else { + return ProgressSatus.INPROGRESS; } } - private void onUploadStarted(final FileUploadProgress fileUploadProgress) { + /** + * Returns the failure reason for the provided fileUploadId or an empty + * string but never null. + * + * @param fileUploadId + * @return the failure reason or an empty String. + */ + private String getFailureReason(final FileUploadId fileUploadId) { + String failureReason = ""; + if (artifactUploadState.getFileUploadProgress(fileUploadId) != null) { + failureReason = artifactUploadState.getFileUploadProgress(fileUploadId).getFailureReason(); + } + if (StringUtils.isEmpty(failureReason)) { + return ""; + } + return failureReason; + } + + /** + * Starts the file upload process and maximize the upload view + * + * @param fileUploadProgress + * FileUploadProgress + */ + public void onUploadStarted(final FileUploadProgress fileUploadProgress) { updateUploadProgressInfoRowObject(fileUploadProgress); if (isWindowNotAlreadyAttached()) { maximizeWindow(); } - - grid.scrollTo(fileUploadProgress.getFileUploadId()); } private boolean isWindowNotAlreadyAttached() { @@ -132,8 +148,7 @@ public class UploadProgressInfoWindow extends Window { } private void restoreState() { - final Indexed container = grid.getContainerDataSource(); - container.removeAllItems(); + uploads.clear(); for (final FileUploadProgress fileUploadProgress : artifactUploadState .getAllFileUploadProgressValuesFromOverallUploadProcessList()) { updateUploadProgressInfoRowObject(fileUploadProgress); @@ -143,48 +158,13 @@ public class UploadProgressInfoWindow extends Window { private void setPopupProperties() { setId(UIComponentIdProvider.UPLOAD_STATUS_POPUP_ID); addStyleName(SPUIStyleDefinitions.UPLOAD_INFO); - setImmediate(true); + setResizable(false); setDraggable(true); setClosable(false); setModal(true); } - private void setGridColumnProperties() { - grid.getColumn(COLUMN_STATUS).setRenderer(new StatusRenderer()); - grid.getColumn(COLUMN_PROGRESS).setRenderer(new ProgressBarRenderer()); - grid.setColumnOrder(COLUMN_STATUS, COLUMN_PROGRESS, COLUMN_FILE_NAME, COLUMN_SOFTWARE_MODULE, - COLUMN_REASON); - setColumnWidth(); - grid.getColumn(COLUMN_STATUS).setHeaderCaption(i18n.getMessage(COLUMN_STATUS)); - grid.getColumn(COLUMN_PROGRESS).setHeaderCaption(i18n.getMessage(COLUMN_PROGRESS)); - grid.getColumn(COLUMN_FILE_NAME).setHeaderCaption(i18n.getMessage(COLUMN_FILE_NAME)); - grid.getColumn(COLUMN_SOFTWARE_MODULE).setHeaderCaption(i18n.getMessage(COLUMN_SOFTWARE_MODULE)); - grid.getColumn(COLUMN_REASON).setHeaderCaption(i18n.getMessage(COLUMN_REASON)); - grid.setFrozenColumnCount(5); - } - - private Grid createGrid() { - final Grid statusGrid = new Grid(uploads); - statusGrid.addStyleName(SPUIStyleDefinitions.UPLOAD_STATUS_GRID); - statusGrid.setId(UIComponentIdProvider.UPLOAD_STATUS_POPUP_GRID); - statusGrid.setSelectionMode(SelectionMode.NONE); - statusGrid.setHeaderVisible(true); - statusGrid.setImmediate(true); - statusGrid.setSizeFull(); - return statusGrid; - } - - private static IndexedContainer getGridContainer() { - final IndexedContainer uploadContainer = new IndexedContainer(); - uploadContainer.addContainerProperty(COLUMN_STATUS, String.class, "Active"); - uploadContainer.addContainerProperty(COLUMN_FILE_NAME, String.class, null); - uploadContainer.addContainerProperty(COLUMN_PROGRESS, Double.class, 0D); - uploadContainer.addContainerProperty(COLUMN_REASON, String.class, ""); - uploadContainer.addContainerProperty(COLUMN_SOFTWARE_MODULE, String.class, ""); - return uploadContainer; - } - private HorizontalLayout getCaptionLayout() { final HorizontalLayout captionLayout = new HorizontalLayout(); captionLayout.setSizeFull(); @@ -200,43 +180,6 @@ public class UploadProgressInfoWindow extends Window { closeButton = getCloseButton(); } - private void setColumnWidth() { - grid.getColumn(COLUMN_STATUS).setWidth(60); - grid.getColumn(COLUMN_PROGRESS).setWidth(150); - grid.getColumn(COLUMN_FILE_NAME).setWidth(200); - grid.getColumn(COLUMN_REASON).setWidth(290); - grid.getColumn(COLUMN_SOFTWARE_MODULE).setWidth(200); - } - - private static class StatusRenderer extends HtmlRenderer { - - private static final long serialVersionUID = 1L; - - @Override - public JsonValue encode(final String value) { - if (value == null) { - return super.encode(getNullRepresentation()); - } - - final String result; - switch (value) { - case STATUS_FINISHED: - result = "
" + FontAwesome.CHECK_CIRCLE.getHtml() + "
"; - break; - case STATUS_FAILED: - result = "
" + FontAwesome.EXCLAMATION_CIRCLE.getHtml() + "
"; - break; - case STATUS_INPROGRESS: - result = "
"; - break; - default: - throw new IllegalArgumentException("Argument " + value + " wasn't expected."); - } - - return super.encode(result); - } - } - private void openWindow() { UI.getCurrent().addWindow(this); center(); @@ -260,7 +203,7 @@ public class UploadProgressInfoWindow extends Window { /** * Called for every finished (succeeded or failed) upload. */ - private void onUploadFinished() { + public void onUploadFinished() { if (artifactUploadState.areAllUploadsFinished() && artifactUploadState.isStatusPopupMinimized()) { if (artifactUploadState.getFilesInFailedState().isEmpty()) { cleanupStates(); @@ -272,7 +215,7 @@ public class UploadProgressInfoWindow extends Window { } private void cleanupStates() { - uploads.removeAllItems(); + uploads.clear(); artifactUploadState.clearUploadTempData(); } @@ -283,7 +226,7 @@ public class UploadProgressInfoWindow extends Window { private Button getCloseButton() { final Button closeBtn = SPUIComponentProvider.getButton( - UIComponentIdProvider.UPLOAD_STATUS_POPUP_CLOSE_BUTTON_ID, "", "", "", true, FontAwesome.TIMES, + UIComponentIdProvider.UPLOAD_STATUS_POPUP_CLOSE_BUTTON_ID, "", "", "", true, VaadinIcons.CLOSE, SPUIButtonStyleNoBorder.class); closeBtn.addStyleName(ValoTheme.BUTTON_BORDERLESS); closeBtn.addClickListener(event -> onClose()); @@ -301,56 +244,7 @@ public class UploadProgressInfoWindow extends Window { private void closeWindow() { setWindowMode(WindowMode.NORMAL); - setColumnWidth(); setPopupSizeInMinMode(); - this.close(); - } - - @SuppressWarnings("unchecked") - private void updateUploadProgressInfoRowObject(final FileUploadProgress fileUploadProgress) { - final FileUploadId fileUploadId = fileUploadProgress.getFileUploadId(); - Item item = uploads.getItem(fileUploadId); - if (item == null) { - item = grid.getContainerDataSource().addItem(fileUploadId); - item.getItemProperty(COLUMN_FILE_NAME).setValue(fileUploadId.getFilename()); - item.getItemProperty(COLUMN_SOFTWARE_MODULE).setValue(HawkbitCommonUtil.getFormattedNameVersion( - fileUploadId.getSoftwareModuleName(), fileUploadId.getSoftwareModuleVersion())); - } - - final String status; - final FileUploadStatus uploadStatus = fileUploadProgress.getFileUploadStatus(); - if (uploadStatus == FileUploadStatus.UPLOAD_FAILED) { - status = STATUS_FAILED; - } else if (uploadStatus == FileUploadStatus.UPLOAD_SUCCESSFUL) { - status = STATUS_FINISHED; - } else { - status = STATUS_INPROGRESS; - } - item.getItemProperty(COLUMN_STATUS).setValue(status); - item.getItemProperty(COLUMN_REASON).setValue(getFailureReason(fileUploadId)); - - final long bytesRead = fileUploadProgress.getBytesRead(); - final long fileSize = fileUploadProgress.getContentLength(); - if (bytesRead > 0 && fileSize > 0) { - item.getItemProperty(COLUMN_PROGRESS).setValue((double) bytesRead / (double) fileSize); - } - } - - /** - * Returns the failure reason for the provided fileUploadId or an empty - * string but never null. - * - * @param fileUploadId - * @return the failure reason or an empty String. - */ - private String getFailureReason(final FileUploadId fileUploadId) { - String failureReason = ""; - if (artifactUploadState.getFileUploadProgress(fileUploadId) != null) { - failureReason = artifactUploadState.getFileUploadProgress(fileUploadId).getFailureReason(); - } - if (StringUtils.isEmpty(failureReason)) { - return ""; - } - return failureReason; + close(); } } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/colorpicker/ColorPickerConstants.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/colorpicker/ColorPickerConstants.java deleted file mode 100644 index 546cd04c7..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/colorpicker/ColorPickerConstants.java +++ /dev/null @@ -1,26 +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.colorpicker; - -import com.vaadin.shared.ui.colorpicker.Color; - -/** - * Provides color constants for the ColorPickerLayout - * - */ -public final class ColorPickerConstants { - - public static final String DEFAULT_COLOR = "rgb(44,151,32)"; - - public static final Color START_COLOR = new Color(0, 146, 58); - - private ColorPickerConstants() { - - } -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/colorpicker/ColorPickerHelper.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/colorpicker/ColorPickerHelper.java deleted file mode 100644 index cc208beee..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/colorpicker/ColorPickerHelper.java +++ /dev/null @@ -1,94 +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.colorpicker; - -import org.eclipse.hawkbit.ui.management.tag.SpColorPickerPreview; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.StringUtils; - -import com.vaadin.shared.ui.colorpicker.Color; -import com.vaadin.ui.Slider.ValueOutOfBoundsException; - -/** - * Contains helper methods for the ColorPickerLayout to handle the ColorPicker - * - */ -public final class ColorPickerHelper { - - private static final Logger LOG = LoggerFactory.getLogger(ColorPickerHelper.class); - - private ColorPickerHelper() { - - } - - /** - * Get color picked value as string. - * - * @param preview - * the color picker preview - * @return String of color picked value. - */ - public static String getColorPickedString(final SpColorPickerPreview preview) { - - final Color color = preview.getColor(); - return "rgb(" + color.getRed() + "," + color.getGreen() + "," + color.getBlue() + ")"; - } - - /** - * Covert RGB code to {@link Color}. - * - * @param value - * RGB vale - * @return Color - */ - public static Color rgbToColorConverter(final String value) { - - if (StringUtils.isEmpty(value) || (!StringUtils.isEmpty(value) && !value.startsWith("rgb"))) { - throw new IllegalArgumentException( - "String to convert is empty or of invalid format - value: '" + value + "'"); - } - - // RGB color format rgb/rgba(255,255,255,0.1) - final String[] colors = value.substring(value.indexOf('(') + 1, value.length() - 1).split(","); - final int red = Integer.parseInt(colors[0]); - final int green = Integer.parseInt(colors[1]); - final int blue = Integer.parseInt(colors[2]); - if (colors.length > 3) { - final int alpha = (int) (Double.parseDouble(colors[3]) * 255D); - return new Color(red, green, blue, alpha); - } - return new Color(red, green, blue); - } - - /** - * - * Gets the selectedColor in the ColorPickerLayout and sets the slider - * values - * - * @param colorPickerLayout - * colorPickerLayout - */ - public static void setRgbSliderValues(final ColorPickerLayout colorPickerLayout) { - - try { - final double redColorValue = colorPickerLayout.getSelectedColor().getRed(); - colorPickerLayout.getRedSlider().setValue(Double.valueOf(redColorValue)); - final double blueColorValue = colorPickerLayout.getSelectedColor().getBlue(); - colorPickerLayout.getBlueSlider().setValue(Double.valueOf(blueColorValue)); - final double greenColorValue = colorPickerLayout.getSelectedColor().getGreen(); - colorPickerLayout.getGreenSlider().setValue(Double.valueOf(greenColorValue)); - } catch (final ValueOutOfBoundsException e) { - LOG.error("Unable to set RGB color value to " + colorPickerLayout.getSelectedColor().getRed() + "," - + colorPickerLayout.getSelectedColor().getGreen() + "," - + colorPickerLayout.getSelectedColor().getBlue(), e); - } - } - -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/colorpicker/ColorPickerLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/colorpicker/ColorPickerLayout.java deleted file mode 100644 index b9b67eac1..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/colorpicker/ColorPickerLayout.java +++ /dev/null @@ -1,155 +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.colorpicker; - -import java.util.HashSet; -import java.util.Set; - -import org.eclipse.hawkbit.ui.common.CoordinatesToColor; -import org.eclipse.hawkbit.ui.management.tag.SpColorPickerPreview; -import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; - -import com.vaadin.shared.ui.colorpicker.Color; -import com.vaadin.ui.AbstractColorPicker.Coordinates2Color; -import com.vaadin.ui.GridLayout; -import com.vaadin.ui.Slider; -import com.vaadin.ui.components.colorpicker.ColorPickerGradient; -import com.vaadin.ui.components.colorpicker.ColorSelector; - -/** - * - * Defines the Layout for the ColorPicker - * - */ -public class ColorPickerLayout extends GridLayout { - - private static final long serialVersionUID = 1L; - - private SpColorPickerPreview selPreview; - - private ColorPickerGradient colorSelect; - - private Set selectors; - - private Color selectedColor; - - /** RGB color converter. */ - private final Coordinates2Color rgbConverter = new CoordinatesToColor(); - - private Slider redSlider; - - private Slider greenSlider; - - private Slider blueSlider; - - public ColorPickerLayout() { - - setColumns(2); - setRows(4); - setId(UIComponentIdProvider.COLOR_PICKER_LAYOUT); - - init(); - - setStyleName("rgb-vertical-layout"); - - addComponent(redSlider, 0, 1); - addComponent(greenSlider, 0, 2); - addComponent(blueSlider, 0, 3); - - addComponent(selPreview, 1, 0); - addComponent(colorSelect, 1, 1, 1, 3); - } - - private void init() { - - selectors = new HashSet<>(); - selectedColor = getDefaultColor(); - selPreview = new SpColorPickerPreview(selectedColor); - - colorSelect = new ColorPickerGradient("rgb-gradient", rgbConverter); - colorSelect.setColor(selectedColor); - colorSelect.setWidth("220px"); - - redSlider = createRGBSlider("", "red"); - redSlider.setId(UIComponentIdProvider.COLOR_PICKER_RED_SLIDER); - greenSlider = createRGBSlider("", "green"); - blueSlider = createRGBSlider("", "blue"); - - selectors.add(colorSelect); - } - - private static Slider createRGBSlider(final String caption, final String styleName) { - final Slider slider = new Slider(caption, 0, 255); - slider.setImmediate(true); - slider.setWidth("150px"); - slider.addStyleName(styleName); - return slider; - } - - public SpColorPickerPreview getSelPreview() { - return selPreview; - } - - public void setSelPreview(final SpColorPickerPreview selPreview) { - this.selPreview = selPreview; - } - - public ColorPickerGradient getColorSelect() { - return colorSelect; - } - - public void setColorSelect(final ColorPickerGradient colorSelect) { - this.colorSelect = colorSelect; - } - - public Set getSelectors() { - return selectors; - } - - public Color getSelectedColor() { - return selectedColor; - } - - public void setSelectedColor(final Color selectedColor) { - this.selectedColor = selectedColor; - } - - public Coordinates2Color getRgbConverter() { - return rgbConverter; - } - - public Color getDefaultColor() { - return new Color(44, 151, 32); - } - - public Slider getRedSlider() { - return redSlider; - } - - public void setRedSlider(final Slider redSlider) { - this.redSlider = redSlider; - } - - public Slider getGreenSlider() { - return greenSlider; - } - - public void setGreenSlider(final Slider greenSlider) { - this.greenSlider = greenSlider; - } - - public Slider getBlueSlider() { - return blueSlider; - } - - public void setBlueSlider(final Slider blueSlider) { - this.blueSlider = blueSlider; - } - -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/AbstractEntityWindowBuilder.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/AbstractEntityWindowBuilder.java new file mode 100644 index 000000000..90af792dc --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/AbstractEntityWindowBuilder.java @@ -0,0 +1,86 @@ +/** + * Copyright (c) 2020 Bosch.IO 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; + +import org.eclipse.hawkbit.ui.common.CommonDialogWindow.SaveDialogCloseListener; +import org.eclipse.hawkbit.ui.common.builder.WindowBuilder; +import org.eclipse.hawkbit.ui.utils.SPUIDefinitions; +import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; + +import com.vaadin.ui.Component; +import com.vaadin.ui.Window; + +/** + * Builder for abstract entity window + * + * @param + * Generic type entity + */ +public abstract class AbstractEntityWindowBuilder { + protected final VaadinMessageSource i18n; + + protected AbstractEntityWindowBuilder(final VaadinMessageSource i18n) { + this.i18n = i18n; + } + + protected CommonDialogWindow getWindowForNewEntity(final AbstractEntityWindowController controller) { + return getWindowForEntity(null, controller); + } + + protected CommonDialogWindow getWindowForNewEntity(final AbstractEntityWindowController controller, + final Component windowContent) { + return getWindowForEntity(null, controller, windowContent); + } + + protected CommonDialogWindow getWindowForEntity(final T proxyEntity, + final AbstractEntityWindowController controller) { + return getWindowForEntity(proxyEntity, controller, controller.getLayout().getRootComponent()); + } + + protected CommonDialogWindow getWindowForEntity(final T proxyEntity, + final AbstractEntityWindowController controller, final Component windowContent) { + controller.populateWithData(proxyEntity); + + final CommonDialogWindow window = createWindow(windowContent, controller.getSaveDialogCloseListener()); + + controller.getLayout().addValidationListener(window::setSaveButtonEnabled); + + return window; + } + + protected CommonDialogWindow createWindow(final Component content, + final SaveDialogCloseListener saveDialogCloseListener) { + return new WindowBuilder(SPUIDefinitions.CREATE_UPDATE_WINDOW).id(getWindowId()).content(content).i18n(i18n) + .helpLink(getHelpLink()).saveDialogCloseListener(saveDialogCloseListener).buildCommonDialogWindow(); + } + + protected abstract String getWindowId(); + + /** + * Gets the add window + * + * @return window + */ + public abstract Window getWindowForAdd(); + + /** + * Gets the update window + * + * @param entity + * Generic type entity + * + * @return window + */ + public abstract Window getWindowForUpdate(final T entity); + + protected String getHelpLink() { + // can be overriden to provide help link to documentation + return null; + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/AbstractEntityWindowController.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/AbstractEntityWindowController.java new file mode 100644 index 000000000..f6380f075 --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/AbstractEntityWindowController.java @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2020 Bosch.IO 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; + +import org.eclipse.hawkbit.ui.common.CommonDialogWindow.SaveDialogCloseListener; + +/** + * Controller for abstract entity window + * + * @param + * Generic type entity + * @param + * Generic type entity + */ +public abstract class AbstractEntityWindowController { + + /** + * Populate entity with data + * + * @param proxyEntity + * Generic type entity + */ + public void populateWithData(final T proxyEntity) { + getLayout().setEntity(buildEntityFromProxy(proxyEntity)); + + adaptLayout(proxyEntity); + } + + /** + * @return layout + */ + public abstract EntityWindowLayout getLayout(); + + protected abstract E buildEntityFromProxy(final T proxyEntity); + + protected void adaptLayout(final T proxyEntity) { + // can be overriden to adapt layout components (e.g. disable/enable + // fields, adapt bindings, etc.) + } + + /** + * @return Save dialog close listener + */ + public SaveDialogCloseListener getSaveDialogCloseListener() { + return new SaveDialogCloseListener() { + @Override + public void saveOrUpdate() { + persistEntity(getLayout().getEntity()); + } + + @Override + public boolean canWindowSaveOrUpdate() { + return isEntityValid(getLayout().getEntity()); + } + + @Override + public boolean canWindowClose() { + return closeWindowAfterSave(); + } + }; + } + + protected abstract void persistEntity(final E entity); + + protected abstract boolean isEntityValid(final E entity); + + protected boolean closeWindowAfterSave() { + return true; + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/AbstractEntityWindowLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/AbstractEntityWindowLayout.java new file mode 100644 index 000000000..51a8c9a21 --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/AbstractEntityWindowLayout.java @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2020 Bosch.IO 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; + +import java.util.Optional; +import java.util.function.Consumer; + +import com.vaadin.data.Binder; + +/** + * Abstract class for entity window layout + * + * @param + * Generic type entity + */ +public abstract class AbstractEntityWindowLayout implements EntityWindowLayout { + + protected final Binder binder; + + protected Consumer validationCallback; + + protected AbstractEntityWindowLayout() { + this.binder = new Binder<>(); + } + + @Override + public void setEntity(final T proxyEntity) { + binder.setBean(proxyEntity); + } + + @Override + public T getEntity() { + return binder.getBean(); + } + + @Override + public void addValidationListener(final Consumer validationCallback) { + binder.addStatusChangeListener(event -> validationCallback.accept(event.getBinder().isValid())); + this.validationCallback = validationCallback; + } + + /** + * @return Validation callback event + */ + public Optional> getValidationCallback() { + return Optional.ofNullable(validationCallback); + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/AbstractMetadataPopupLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/AbstractMetadataPopupLayout.java deleted file mode 100644 index b9c225f26..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/AbstractMetadataPopupLayout.java +++ /dev/null @@ -1,534 +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; - -import java.util.List; -import java.util.Set; - -import org.eclipse.hawkbit.repository.model.MetaData; -import org.eclipse.hawkbit.repository.model.NamedEntity; -import org.eclipse.hawkbit.ui.SpPermissionChecker; -import org.eclipse.hawkbit.ui.common.CommonDialogWindow.SaveDialogCloseListener; -import org.eclipse.hawkbit.ui.common.builder.LabelBuilder; -import org.eclipse.hawkbit.ui.common.builder.TextAreaBuilder; -import org.eclipse.hawkbit.ui.common.builder.TextFieldBuilder; -import org.eclipse.hawkbit.ui.common.builder.WindowBuilder; -import org.eclipse.hawkbit.ui.components.SPUIComponentProvider; -import org.eclipse.hawkbit.ui.customrenderers.renderers.HtmlButtonRenderer; -import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleNoBorder; -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.UIMessageIdProvider; -import org.eclipse.hawkbit.ui.utils.UINotification; -import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; -import org.vaadin.spring.events.EventBus; -import org.vaadin.spring.events.EventBus.UIEventBus; - -import com.vaadin.data.Item; -import com.vaadin.data.util.IndexedContainer; -import com.vaadin.event.FieldEvents.TextChangeEvent; -import com.vaadin.event.SelectionEvent; -import com.vaadin.server.FontAwesome; -import com.vaadin.ui.AbstractTextField.TextChangeEventMode; -import com.vaadin.ui.Alignment; -import com.vaadin.ui.Button; -import com.vaadin.ui.CustomComponent; -import com.vaadin.ui.Grid; -import com.vaadin.ui.Grid.SelectionMode; -import com.vaadin.ui.HorizontalLayout; -import com.vaadin.ui.Label; -import com.vaadin.ui.TextArea; -import com.vaadin.ui.TextField; -import com.vaadin.ui.UI; -import com.vaadin.ui.VerticalLayout; -import com.vaadin.ui.renderers.ClickableRenderer.RendererClickEvent; - -/** - * - * Abstract pop up layout - * - * @param - * E id the entity for which metadata is displayed - * @param - * M is the metadata - * - */ -public abstract class AbstractMetadataPopupLayout extends CustomComponent { - private static final long serialVersionUID = 1L; - - private static final String DELETE_BUTTON = "DELETE_BUTTON"; - private static final int INPUT_DEBOUNCE_TIMEOUT = 250; - - protected static final String VALUE = "value"; - protected static final String KEY = "key"; - protected static final int MAX_METADATA_QUERY = 500; - - protected VaadinMessageSource i18n; - private final UINotification uiNotification; - protected transient EventBus.UIEventBus eventBus; - - private TextField keyTextField; - private TextArea valueTextArea; - private Button addIcon; - private Grid metaDataGrid; - private Label headerCaption; - private CommonDialogWindow metadataWindow; - - private E selectedEntity; - - private HorizontalLayout mainLayout; - protected SpPermissionChecker permChecker; - - protected AbstractMetadataPopupLayout(final VaadinMessageSource i18n, final UINotification uiNotification, - final UIEventBus eventBus, final SpPermissionChecker permChecker) { - this.i18n = i18n; - this.uiNotification = uiNotification; - this.eventBus = eventBus; - this.permChecker = permChecker; - - createComponents(); - buildLayout(); - } - - /** - * Save the metadata and never close the window after saving. - */ - private final class SaveOnDialogCloseListener implements SaveDialogCloseListener { - @Override - public void saveOrUpdate() { - onSave(); - } - - @Override - public boolean canWindowClose() { - return false; - } - - @Override - public boolean canWindowSaveOrUpdate() { - return true; - } - - } - - /** - * Returns metadata popup. - * - * @param entity - * entity for which metadata data is displayed - * @param metaDatakey - * metadata key to be selected - * @return {@link CommonDialogWindow} - */ - public CommonDialogWindow getWindow(final E entity, final String metaDatakey) { - selectedEntity = entity; - - metadataWindow = new WindowBuilder(SPUIDefinitions.CREATE_UPDATE_WINDOW).content(this) - .cancelButtonClickListener(event -> onCancel()).id(UIComponentIdProvider.METADATA_POPUP_ID) - .layout(mainLayout).i18n(i18n).saveDialogCloseListener(new SaveOnDialogCloseListener()) - .buildCommonDialogWindow(); - - metadataWindow.setCaptionAsHtml(false); - metadataWindow.setAssistivePrefix(i18n.getMessage("caption.metadata.popup") + " " + ""); - metadataWindow.setCaption(getElementTitle()); - metadataWindow.setAssistivePostfix(""); - - metadataWindow.setHeight(550, Unit.PIXELS); - metadataWindow.setWidth(800, Unit.PIXELS); - metadataWindow.getMainLayout().setSizeFull(); - metadataWindow.getButtonsLayout().setHeight("45px"); - setUpDetails(entity.getId(), metaDatakey); - return metadataWindow; - } - - public E getSelectedEntity() { - return selectedEntity; - } - - public void setSelectedEntity(final E selectedEntity) { - this.selectedEntity = selectedEntity; - } - - protected abstract boolean checkForDuplicate(E entity, String value); - - protected abstract M createMetadata(E entity, String key, String value); - - protected abstract M updateMetadata(E entity, String key, String value); - - protected abstract List getMetadataList(); - - protected abstract void deleteMetadata(E entity, String key); - - protected abstract boolean hasCreatePermission(); - - protected abstract boolean hasUpdatePermission(); - - protected void createComponents() { - keyTextField = createKeyTextField(); - valueTextArea = createValueTextField(); - metaDataGrid = createMetadataGrid(); - addIcon = createAddIcon(); - headerCaption = createHeaderCaption(); - } - - private void buildLayout() { - final HorizontalLayout headerLayout = new HorizontalLayout(); - headerLayout.addStyleName(SPUIStyleDefinitions.WIDGET_TITLE); - headerLayout.setSpacing(false); - headerLayout.setMargin(false); - headerLayout.setSizeFull(); - headerLayout.addComponent(headerCaption); - if (hasCreatePermission()) { - headerLayout.addComponents(addIcon); - headerLayout.setComponentAlignment(addIcon, Alignment.MIDDLE_RIGHT); - } - headerLayout.setExpandRatio(headerCaption, 1.0F); - - final HorizontalLayout headerWrapperLayout = new HorizontalLayout(); - headerWrapperLayout.addStyleName("bordered-layout" + " " + "no-border-bottom" + " " + "metadata-table-margin"); - headerWrapperLayout.addComponent(headerLayout); - headerWrapperLayout.setWidth("100%"); - headerLayout.setHeight("30px"); - - final VerticalLayout tableLayout = new VerticalLayout(); - tableLayout.setSizeFull(); - tableLayout.setHeight("100%"); - tableLayout.addComponent(headerWrapperLayout); - tableLayout.addComponent(metaDataGrid); - tableLayout.addStyleName("table-layout"); - tableLayout.setExpandRatio(metaDataGrid, 1.0F); - - final VerticalLayout metadataFieldsLayout = createMetadataFieldsLayout(); - - mainLayout = new HorizontalLayout(); - mainLayout.addComponent(tableLayout); - mainLayout.addComponent(metadataFieldsLayout); - mainLayout.setExpandRatio(tableLayout, 0.5F); - mainLayout.setExpandRatio(metadataFieldsLayout, 0.5F); - mainLayout.setSizeFull(); - mainLayout.setSpacing(true); - setCompositionRoot(mainLayout); - setSizeFull(); - } - - protected VerticalLayout createMetadataFieldsLayout() { - final VerticalLayout metadataFieldsLayout = new VerticalLayout(); - metadataFieldsLayout.setSizeFull(); - metadataFieldsLayout.setHeight("100%"); - metadataFieldsLayout.addComponent(keyTextField); - metadataFieldsLayout.addComponent(valueTextArea); - metadataFieldsLayout.setSpacing(true); - metadataFieldsLayout.setExpandRatio(valueTextArea, 1F); - return metadataFieldsLayout; - } - - private TextField createKeyTextField() { - final TextField keyField = new TextFieldBuilder(MetaData.KEY_MAX_SIZE).caption(i18n.getMessage("textfield.key")) - .required(true, i18n).id(UIComponentIdProvider.METADATA_KEY_FIELD_ID).buildTextComponent(); - keyField.addTextChangeListener(this::onKeyChange); - keyField.setTextChangeEventMode(TextChangeEventMode.LAZY); - keyField.setTextChangeTimeout(INPUT_DEBOUNCE_TIMEOUT); - keyField.setWidth("100%"); - return keyField; - } - - private TextArea createValueTextField() { - valueTextArea = new TextAreaBuilder(MetaData.VALUE_MAX_SIZE).caption(i18n.getMessage("textfield.value")) - .required(true, i18n).id(UIComponentIdProvider.METADATA_VALUE_ID).buildTextComponent(); - valueTextArea.setSizeFull(); - valueTextArea.setHeight(100, Unit.PERCENTAGE); - valueTextArea.addTextChangeListener(this::onValueChange); - valueTextArea.setTextChangeEventMode(TextChangeEventMode.LAZY); - valueTextArea.setTextChangeTimeout(INPUT_DEBOUNCE_TIMEOUT); - return valueTextArea; - } - - protected Grid createMetadataGrid() { - final Grid metadataGrid = new Grid(); - metadataGrid.addStyleName(SPUIStyleDefinitions.METADATA_GRID); - metadataGrid.setImmediate(true); - metadataGrid.setHeight("100%"); - metadataGrid.setWidth("100%"); - metadataGrid.setId(UIComponentIdProvider.METDATA_TABLE_ID); - metadataGrid.setSelectionMode(SelectionMode.SINGLE); - metadataGrid.setColumnReorderingAllowed(true); - metadataGrid.setContainerDataSource(getMetadataContainer()); - metadataGrid.getColumn(KEY).setHeaderCaption(i18n.getMessage("header.key")); - metadataGrid.getColumn(VALUE).setHeaderCaption(i18n.getMessage("header.value")); - metadataGrid.getColumn(VALUE).setHidden(true); - metadataGrid.addSelectionListener(this::onRowClick); - metadataGrid.getColumn(DELETE_BUTTON).setHeaderCaption(""); - metadataGrid.getColumn(DELETE_BUTTON).setRenderer(new HtmlButtonRenderer(this::onDelete)); - metadataGrid.getColumn(DELETE_BUTTON).setWidth(50); - metadataGrid.getColumn(KEY).setExpandRatio(1); - return metadataGrid; - } - - private void onDelete(final RendererClickEvent event) { - final Item item = metaDataGrid.getContainerDataSource().getItem(event.getItemId()); - final String key = (String) item.getItemProperty(KEY).getValue(); - - final ConfirmationDialog confirmDialog = new ConfirmationDialog( - i18n.getMessage("caption.entity.delete.action.confirmbox"), - i18n.getMessage("message.confirm.delete.metadata", key), i18n.getMessage(UIMessageIdProvider.BUTTON_OK), - i18n.getMessage(UIMessageIdProvider.BUTTON_CANCEL), ok -> { - if (ok) { - handleOkDeleteMetadata(event, key); - } - }); - UI.getCurrent().addWindow(confirmDialog.getWindow()); - confirmDialog.getWindow().bringToFront(); - } - - private void handleOkDeleteMetadata(final RendererClickEvent event, final String key) { - deleteMetadata(getSelectedEntity(), key); - uiNotification.displaySuccess(i18n.getMessage("message.metadata.deleted.successfully", key)); - final Object selectedRow = metaDataGrid.getSelectedRow(); - metaDataGrid.getContainerDataSource().removeItem(event.getItemId()); - // force grid to refresh - metaDataGrid.clearSortOrder(); - if (!metaDataGrid.getContainerDataSource().getItemIds().isEmpty()) { - if (selectedRow != null) { - if (selectedRow.equals(event.getItemId())) { - metaDataGrid.select(metaDataGrid.getContainerDataSource().getIdByIndex(0)); - } else { - metaDataGrid.select(selectedRow); - } - } - } else { - resetFields(); - } - } - - private void resetFields() { - clearFields(); - metaDataGrid.select(null); - if (hasCreatePermission()) { - enableEditing(); - addIcon.setEnabled(false); - } - } - - private Button createAddIcon() { - addIcon = SPUIComponentProvider.getButton(UIComponentIdProvider.METADTA_ADD_ICON_ID, - i18n.getMessage("button.save"), null, null, false, FontAwesome.PLUS, SPUIButtonStyleNoBorder.class); - addIcon.addClickListener(event -> onAdd()); - return addIcon; - } - - private Label createHeaderCaption() { - return new LabelBuilder().name(i18n.getMessage("caption.metadata")).buildCaptionLabel(); - } - - private static IndexedContainer getMetadataContainer() { - final IndexedContainer swcontactContainer = new IndexedContainer(); - swcontactContainer.addContainerProperty(KEY, String.class, ""); - swcontactContainer.addContainerProperty(VALUE, String.class, ""); - swcontactContainer.addContainerProperty(DELETE_BUTTON, String.class, FontAwesome.TRASH_O.getHtml()); - return swcontactContainer; - } - - protected Item popualateKeyValue(final Object metadataCompositeKey) { - if (metadataCompositeKey != null) { - final Item item = metaDataGrid.getContainerDataSource().getItem(metadataCompositeKey); - keyTextField.setValue((String) item.getItemProperty(KEY).getValue()); - valueTextArea.setValue((String) item.getItemProperty(VALUE).getValue()); - keyTextField.setEnabled(false); - if (hasUpdatePermission()) { - valueTextArea.setEnabled(true); - } - return item; - } - - return null; - } - - private void populateGrid() { - final List metadataList = getMetadataList(); - for (final M metaData : metadataList) { - addItemToGrid(metaData); - } - } - - protected Item addItemToGrid(final M metaData) { - final IndexedContainer metadataContainer = (IndexedContainer) metaDataGrid.getContainerDataSource(); - final Item item = metadataContainer.addItem(metaData.getKey()); - item.getItemProperty(VALUE).setValue(metaData.getValue()); - item.getItemProperty(KEY).setValue(metaData.getKey()); - return item; - } - - protected Item updateItemInGrid(final String key) { - final IndexedContainer metadataContainer = (IndexedContainer) metaDataGrid.getContainerDataSource(); - final Item item = metadataContainer.getItem(key); - item.getItemProperty(VALUE).setValue(valueTextArea.getValue()); - return item; - } - - private void onAdd() { - metaDataGrid.deselect(metaDataGrid.getSelectedRow()); - clearFields(); - enableEditing(); - addIcon.setEnabled(true); - } - - protected void clearFields() { - valueTextArea.clear(); - keyTextField.clear(); - } - - protected void onSave() { - final String key = keyTextField.getValue(); - final String value = valueTextArea.getValue(); - if (mandatoryCheck()) { - final E entity = selectedEntity; - if (metaDataGrid.getSelectedRow() == null) { - if (!duplicateCheck(entity)) { - final M metadata = createMetadata(entity, key, value); - uiNotification.displaySuccess(i18n.getMessage("message.metadata.saved", metadata.getKey())); - addItemToGrid(metadata); - metaDataGrid.scrollToEnd(); - metaDataGrid.select(metadata.getKey()); - addIcon.setEnabled(true); - metadataWindow.setSaveButtonEnabled(false); - if (!hasUpdatePermission()) { - valueTextArea.setEnabled(false); - } - } - } else { - final M metadata = updateMetadata(entity, key, value); - uiNotification.displaySuccess(i18n.getMessage("message.metadata.updated", metadata.getKey())); - updateItemInGrid(metadata.getKey()); - metaDataGrid.select(metadata.getKey()); - addIcon.setEnabled(true); - metadataWindow.setSaveButtonEnabled(false); - } - } - } - - private boolean mandatoryCheck() { - if (keyTextField.getValue().isEmpty()) { - uiNotification.displayValidationError(i18n.getMessage("message.key.missing")); - return false; - } - if (valueTextArea.getValue().isEmpty()) { - uiNotification.displayValidationError(i18n.getMessage("message.value.missing")); - return false; - } - return true; - } - - private boolean duplicateCheck(final E entity) { - if (!checkForDuplicate(entity, keyTextField.getValue())) { - return false; - } - - uiNotification - .displayValidationError(i18n.getMessage("message.metadata.duplicate.check", keyTextField.getValue())); - return true; - } - - protected String getElementTitle() { - return getSelectedEntity().getName(); - } - - private void onCancel() { - metadataWindow.close(); - UI.getCurrent().removeWindow(metadataWindow); - } - - private void onKeyChange(final TextChangeEvent event) { - if (hasCreatePermission() || hasUpdatePermission()) { - if (!valueTextArea.getValue().isEmpty() && !event.getText().isEmpty()) { - metadataWindow.setSaveButtonEnabled(true); - } else { - metadataWindow.setSaveButtonEnabled(false); - } - } - } - - protected void onRowClick(final SelectionEvent event) { - final Set itemsSelected = event.getSelected(); - if (!itemsSelected.isEmpty()) { - popualateKeyValue(itemsSelected.iterator().next()); - addIcon.setEnabled(true); - } else { - clearFields(); - if (hasCreatePermission()) { - enableEditing(); - addIcon.setEnabled(false); - } else { - keyTextField.setEnabled(false); - valueTextArea.setEnabled(false); - } - } - metadataWindow.setSaveButtonEnabled(false); - } - - protected void enableEditing() { - keyTextField.setEnabled(true); - valueTextArea.setEnabled(true); - } - - private void onValueChange(final TextChangeEvent event) { - if (hasCreatePermission() || hasUpdatePermission()) { - if (!keyTextField.getValue().isEmpty() && !event.getText().isEmpty()) { - metadataWindow.setSaveButtonEnabled(true); - } else { - metadataWindow.setSaveButtonEnabled(false); - } - } - } - - private void setUpDetails(final Long swId, final String metaDatakey) { - resetDetails(); - metadataWindow.clearOriginalValues(); - if (swId != null) { - metaDataGrid.getContainerDataSource().removeAllItems(); - populateGrid(); - metaDataGrid.getSelectionModel().reset(); - if (!metaDataGrid.getContainerDataSource().getItemIds().isEmpty()) { - if (metaDatakey == null) { - metaDataGrid.select(metaDataGrid.getContainerDataSource().getIdByIndex(0)); - } else { - metaDataGrid.select(metaDatakey); - } - } else if (hasCreatePermission()) { - enableEditing(); - addIcon.setEnabled(false); - } - } - } - - private void resetDetails() { - clearFields(); - disableEditing(); - metadataWindow.setSaveButtonEnabled(false); - addIcon.setEnabled(true); - } - - protected void disableEditing() { - keyTextField.setEnabled(false); - valueTextArea.setEnabled(false); - } - - protected TextArea getValueTextArea() { - return valueTextArea; - } - - protected TextField getKeyTextField() { - return keyTextField; - } - - protected CommonDialogWindow getMetadataWindow() { - return metadataWindow; - } - -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/AbstractMetadataPopupLayoutVersioned.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/AbstractMetadataPopupLayoutVersioned.java deleted file mode 100644 index d8d915c81..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/AbstractMetadataPopupLayoutVersioned.java +++ /dev/null @@ -1,44 +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; - -import org.eclipse.hawkbit.repository.model.MetaData; -import org.eclipse.hawkbit.repository.model.NamedVersionedEntity; -import org.eclipse.hawkbit.ui.SpPermissionChecker; -import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil; -import org.eclipse.hawkbit.ui.utils.UINotification; -import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; -import org.vaadin.spring.events.EventBus.UIEventBus; - -/** - * - * Abstract pop up layout - * - * @param - * E id the entity for which metadata is displayed - * @param - * M is the metadata - * - */ -public abstract class AbstractMetadataPopupLayoutVersioned - extends AbstractMetadataPopupLayout { - - private static final long serialVersionUID = 1L; - - protected AbstractMetadataPopupLayoutVersioned(final VaadinMessageSource i18n, final UINotification uiNotification, - final UIEventBus eventBus, final SpPermissionChecker permChecker) { - super(i18n, uiNotification, eventBus, permChecker); - } - - @Override - protected String getElementTitle() { - return HawkbitCommonUtil.getFormattedNameVersion(getSelectedEntity().getName(), - getSelectedEntity().getVersion()); - } -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/CommonDialogWindow.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/CommonDialogWindow.java index e06a3a441..d4514a176 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/CommonDialogWindow.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/CommonDialogWindow.java @@ -8,58 +8,27 @@ */ package org.eclipse.hawkbit.ui.common; -import static com.google.common.base.Preconditions.checkNotNull; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; - -import org.eclipse.hawkbit.ui.artifacts.smtable.SoftwareModuleAddUpdateWindow; import org.eclipse.hawkbit.ui.components.SPUIComponentProvider; +import org.eclipse.hawkbit.ui.decorators.SPUIButtonDecorator; import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleNoBorderWithIcon; -import org.eclipse.hawkbit.ui.management.targettable.TargetAddUpdateWindowLayout; import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions; import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.UIMessageIdProvider; import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; -import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; -import org.vaadin.hene.flexibleoptiongroup.FlexibleOptionGroupItemComponent; -import com.google.common.collect.Maps; -import com.vaadin.data.Container.ItemSetChangeEvent; -import com.vaadin.data.Container.ItemSetChangeListener; -import com.vaadin.data.Property.ValueChangeEvent; -import com.vaadin.data.Property.ValueChangeListener; -import com.vaadin.data.Validator; -import com.vaadin.data.validator.NullValidator; -import com.vaadin.event.FieldEvents.TextChangeEvent; -import com.vaadin.event.FieldEvents.TextChangeListener; -import com.vaadin.event.FieldEvents.TextChangeNotifier; import com.vaadin.event.ShortcutAction.KeyCode; -import com.vaadin.server.FontAwesome; +import com.vaadin.icons.VaadinIcons; import com.vaadin.ui.AbstractComponent; -import com.vaadin.ui.AbstractField; -import com.vaadin.ui.AbstractLayout; -import com.vaadin.ui.AbstractOrderedLayout; import com.vaadin.ui.Alignment; import com.vaadin.ui.Button; import com.vaadin.ui.Button.ClickEvent; import com.vaadin.ui.Button.ClickListener; -import com.vaadin.ui.CheckBox; import com.vaadin.ui.Component; -import com.vaadin.ui.Field; import com.vaadin.ui.GridLayout; import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.Label; import com.vaadin.ui.Link; -import com.vaadin.ui.TabSheet; -import com.vaadin.ui.Table; import com.vaadin.ui.VerticalLayout; import com.vaadin.ui.Window; import com.vaadin.ui.themes.ValoTheme; @@ -71,38 +40,36 @@ import com.vaadin.ui.themes.ValoTheme; * */ public class CommonDialogWindow extends Window { - private static final long serialVersionUID = 1L; - private final VerticalLayout mainLayout = new VerticalLayout(); + private final VaadinMessageSource i18n; + private final VerticalLayout mainLayout; private final String caption; - private final Component content; - private final String helpLink; - - private Button saveButton; - + private final ConfirmStyle confirmStyle; + private final Class buttonDecorator; + private Button confirmButton; private Button cancelButton; - private HorizontalLayout buttonsLayout; + private Label mandatoryLabel; private final ClickListener cancelButtonClickListener; - - private final ClickListener closeClickListener = this::onCloseEvent; - - private final transient Map orginalValues; - - private List> allComponents; - - private final VaadinMessageSource i18n; + private final ClickListener closeClickListener; private transient SaveDialogCloseListener closeListener; /** - * Constructor. - * + * Different kinds of confirm buttons + */ + public enum ConfirmStyle { + SAVE, OK + } + + /** + * Constructor + * * @param caption * the caption * @param content @@ -113,28 +80,34 @@ public class CommonDialogWindow extends Window { * the saveDialogCloseListener * @param cancelButtonClickListener * the cancelButtonClickListener - * @param layout - * the abstract layout + * @param confirmStyle + * what kind of button is used + * @param buttonDecorator + * to style the confirm and cancel buttons * @param i18n - * the i18n service + * internationalization */ public CommonDialogWindow(final String caption, final Component content, final String helpLink, final SaveDialogCloseListener closeListener, final ClickListener cancelButtonClickListener, - final AbstractLayout layout, final VaadinMessageSource i18n) { - checkNotNull(closeListener); + final ConfirmStyle confirmStyle, final Class buttonDecorator, + final VaadinMessageSource i18n) { this.caption = caption; this.content = content; this.helpLink = helpLink; this.closeListener = closeListener; this.cancelButtonClickListener = cancelButtonClickListener; - this.allComponents = getAllComponents(layout); - this.orginalValues = Maps.newHashMapWithExpectedSize(allComponents.size()); + this.confirmStyle = confirmStyle; + this.buttonDecorator = buttonDecorator; this.i18n = i18n; + + this.mainLayout = new VerticalLayout(); + this.closeClickListener = this::onCloseEvent; + init(); } private void onCloseEvent(final ClickEvent clickEvent) { - if (!clickEvent.getButton().equals(saveButton)) { + if (!clickEvent.getButton().equals(confirmButton)) { close(); return; } @@ -153,56 +126,13 @@ public class CommonDialogWindow extends Window { @Override public void close() { super.close(); - orginalValues.clear(); - removeListeners(); - allComponents.clear(); - this.saveButton.setEnabled(false); - } - - private void removeListeners() { - for (final AbstractField field : allComponents) { - removeTextListener(field); - removeValueChangeListener(field); - removeItemSetChangeistener(field); - } - } - - private void removeItemSetChangeistener(final AbstractField field) { - if (!(field instanceof Table)) { - return; - } - for (final Object listener : field.getListeners(ItemSetChangeEvent.class)) { - if (listener instanceof ChangeListener) { - ((Table) field).removeItemSetChangeListener((ChangeListener) listener); - } - } - } - - private void removeTextListener(final AbstractField field) { - if (!(field instanceof TextChangeNotifier)) { - return; - } - for (final Object listener : field.getListeners(TextChangeEvent.class)) { - if (listener instanceof ChangeListener) { - ((TextChangeNotifier) field).removeTextChangeListener((ChangeListener) listener); - } - } - } - - private void removeValueChangeListener(final AbstractField field) { - for (final Object listener : field.getListeners(ValueChangeEvent.class)) { - if (listener instanceof ChangeListener) { - field.removeValueChangeListener((ChangeListener) listener); - } - } + this.confirmButton.setEnabled(false); } private final void init() { + mainLayout.setMargin(false); + mainLayout.setSpacing(false); - if (content instanceof AbstractOrderedLayout) { - ((AbstractOrderedLayout) content).setSpacing(true); - ((AbstractOrderedLayout) content).setMargin(true); - } if (content instanceof GridLayout) { addStyleName("marginTop"); } @@ -218,206 +148,27 @@ public class CommonDialogWindow extends Window { mainLayout.addComponent(buttonLayout); mainLayout.setComponentAlignment(buttonLayout, Alignment.TOP_CENTER); + setCaptionAsHtml(false); setCaption(caption); - setCaptionAsHtml(true); setContent(mainLayout); setResizable(false); center(); setModal(true); addStyleName("fontsize"); - setOrginaleValues(); - addComponentListeners(); - } - - /** - * saves the original values in a Map so we can use them for detecting - * changes - */ - public final void setOrginaleValues() { - for (final AbstractField field : allComponents) { - Object value = field.getValue(); - - if (field instanceof Table) { - value = ((Table) field).getContainerDataSource().getItemIds(); - } - orginalValues.put(field, value); - } - saveButton.setEnabled(isSaveButtonEnabledAfterValueChange(null, null)); - } - - /** - * Clears the original values in case no value changed check is wished - */ - public final void clearOriginalValues() { - orginalValues.clear(); } protected void addCloseListenerForSaveButton() { - saveButton.addClickListener(closeClickListener); + confirmButton.addClickListener(closeClickListener); } protected void addCloseListenerForCancelButton() { cancelButton.addClickListener(closeClickListener); } - /** - * adds a listener to a component. Depending on the type of component a - * valueChange-, textChange- or itemSetChangeListener will be added. - */ - public void addComponentListeners() { - // avoid duplicate registration - removeListeners(); - - for (final AbstractField field : allComponents) { - if (field instanceof TextChangeNotifier) { - ((TextChangeNotifier) field).addTextChangeListener(new ChangeListener(field)); - } - - if (field instanceof Table) { - ((Table) field).addItemSetChangeListener(new ChangeListener(field)); - } - field.addValueChangeListener(new ChangeListener(field)); - } - } - - private boolean isSaveButtonEnabledAfterValueChange(final Component currentChangedComponent, - final Object newValue) { - return isMandatoryFieldNotEmptyAndValid(currentChangedComponent, newValue) - && isValuesChanged(currentChangedComponent, newValue); - } - - private boolean isValuesChanged(final Component currentChangedComponent, final Object newValue) { - for (final AbstractField field : allComponents) { - Object originalValue = orginalValues.get(field); - if (field instanceof CheckBox && originalValue == null) { - originalValue = Boolean.FALSE; - } - final Object currentValue = getCurrentValue(currentChangedComponent, newValue, field); - - if (!Objects.equals(originalValue, currentValue)) { - return true; - } - } - return false; - } - - private static Object getCurrentValue(final Component currentChangedComponent, final Object newValue, - final AbstractField field) { - Object currentValue = field.getValue(); - if (field instanceof Table) { - currentValue = ((Table) field).getContainerDataSource().getItemIds(); - } - - if (field.equals(currentChangedComponent)) { - currentValue = newValue; - } - return currentValue; - } - - private boolean shouldMandatoryLabelShown() { - for (final AbstractField field : allComponents) { - if (field.isRequired()) { - return true; - } - } - - return false; - } - - private boolean isMandatoryFieldNotEmptyAndValid(final Component currentChangedComponent, final Object newValue) { - - boolean valid = true; - final List> requiredComponents = allComponents.stream().filter(AbstractField::isRequired) - .filter(AbstractField::isEnabled).collect(Collectors.toList()); - - requiredComponents.addAll(allComponents.stream().filter(this::hasNullValidator).collect(Collectors.toList())); - - for (final AbstractField field : requiredComponents) { - Object value = getCurrentValue(currentChangedComponent, newValue, field); - - if (Set.class.equals(field.getType())) { - value = emptyToNull((Collection) value); - } - - if (value == null) { - return false; - } - - // We need to loop through all of components for validity testing. - // Otherwise the UI will only mark the first field with errors and - // then stop. Setting the value is necessary because not all - // required input fields have empty string validator, but emptiness - // is checked during isValid() call. Setting the value could be - // redundant, check if it could be removed in the future. - field.setValue(value); - if (!field.isValid()) { - valid = false; - } - } - - return valid; - } - - private static Object emptyToNull(final Collection c) { - return CollectionUtils.isEmpty(c) ? null : c; - } - - private boolean hasNullValidator(final Component component) { - - if (component instanceof AbstractField) { - final AbstractField fieldComponent = (AbstractField) component; - for (final Validator validator : fieldComponent.getValidators()) { - if (validator instanceof NullValidator) { - return true; - } - } - } - return false; - } - - private static List> getAllComponents(final AbstractLayout abstractLayout) { - final List> components = new ArrayList<>(); - - final Iterator iterate = abstractLayout.iterator(); - while (iterate.hasNext()) { - final Component c = iterate.next(); - if (c instanceof AbstractLayout) { - components.addAll(getAllComponents((AbstractLayout) c)); - } - - if (c instanceof AbstractField) { - components.add((AbstractField) c); - } - - if (c instanceof FlexibleOptionGroupItemComponent) { - components.add(((FlexibleOptionGroupItemComponent) c).getOwner()); - } - - if (c instanceof TabSheet) { - final TabSheet tabSheet = (TabSheet) c; - components.addAll(getAllComponentsFromTabSheet(tabSheet)); - } - } - return components; - } - - private static List> getAllComponentsFromTabSheet(final TabSheet tabSheet) { - final List> components = new ArrayList<>(); - for (final Iterator i = tabSheet.iterator(); i.hasNext();) { - final Component component = i.next(); - if (component instanceof AbstractLayout) { - components.addAll(getAllComponents((AbstractLayout) component)); - } - } - return components; - } - private HorizontalLayout createActionButtonsLayout() { buttonsLayout = new HorizontalLayout(); buttonsLayout.setSizeFull(); - buttonsLayout.setSpacing(true); - buttonsLayout.setSpacing(true); buttonsLayout.addStyleName("actionButtonsMargin"); createSaveButton(); @@ -430,25 +181,25 @@ public class CommonDialogWindow extends Window { private void createMandatoryLabel() { - if (!shouldMandatoryLabelShown()) { - return; - } - - final Label mandatoryLabel = new Label(i18n.getMessage("label.mandatory.field")); + mandatoryLabel = new Label(i18n.getMessage("label.mandatory.field")); mandatoryLabel.addStyleName(SPUIStyleDefinitions.SP_TEXTFIELD_ERROR + " " + ValoTheme.LABEL_TINY); - if (content instanceof TargetAddUpdateWindowLayout) { - ((TargetAddUpdateWindowLayout) content).getFormLayout().addComponent(mandatoryLabel); - } else if (content instanceof SoftwareModuleAddUpdateWindow) { - ((SoftwareModuleAddUpdateWindow) content).getFormLayout().addComponent(mandatoryLabel); - } - mainLayout.addComponent(mandatoryLabel); } + /** + * Hide the line that explains the mandatory decorator + * + */ + public void hideMandatoryExplanation() { + if (mandatoryLabel != null) { + mainLayout.removeComponent(mandatoryLabel); + } + } + private void createCancelButton() { cancelButton = SPUIComponentProvider.getButton(UIComponentIdProvider.CANCEL_BUTTON, - i18n.getMessage(UIMessageIdProvider.BUTTON_CANCEL), "", "", true, FontAwesome.TIMES, + i18n.getMessage(UIMessageIdProvider.BUTTON_CANCEL), "", "", true, VaadinIcons.CLOSE, SPUIButtonStyleNoBorderWithIcon.class); cancelButton.setSizeUndefined(); cancelButton.addStyleName("default-color"); @@ -463,17 +214,23 @@ public class CommonDialogWindow extends Window { } private void createSaveButton() { - saveButton = SPUIComponentProvider.getButton(UIComponentIdProvider.SAVE_BUTTON, - i18n.getMessage(UIMessageIdProvider.BUTTON_SAVE), "", "", true, FontAwesome.SAVE, - SPUIButtonStyleNoBorderWithIcon.class); - saveButton.setSizeUndefined(); - saveButton.addStyleName("default-color"); + if (confirmStyle == ConfirmStyle.SAVE) { + confirmButton = SPUIComponentProvider.getButton(UIComponentIdProvider.SAVE_BUTTON, + i18n.getMessage(UIMessageIdProvider.BUTTON_SAVE), "", "", true, VaadinIcons.HARDDRIVE, + buttonDecorator); + } else { + confirmButton = SPUIComponentProvider.getButton(UIComponentIdProvider.OK_BUTTON, + i18n.getMessage(UIMessageIdProvider.BUTTON_OK), "", ValoTheme.BUTTON_PRIMARY, false, null, + buttonDecorator); + } + confirmButton.setSizeUndefined(); + confirmButton.addStyleName("default-color"); addCloseListenerForSaveButton(); - saveButton.setEnabled(false); - saveButton.setClickShortcut(KeyCode.ENTER); - buttonsLayout.addComponent(saveButton); - buttonsLayout.setComponentAlignment(saveButton, Alignment.MIDDLE_RIGHT); - buttonsLayout.setExpandRatio(saveButton, 1.0F); + confirmButton.setEnabled(false); + confirmButton.setClickShortcut(KeyCode.ENTER); + buttonsLayout.addComponent(confirmButton); + buttonsLayout.setComponentAlignment(confirmButton, Alignment.MIDDLE_RIGHT); + buttonsLayout.setExpandRatio(confirmButton, 1.0F); } private void addHelpLink() { @@ -490,62 +247,35 @@ public class CommonDialogWindow extends Window { return this.buttonsLayout; } - private class ChangeListener implements ValueChangeListener, TextChangeListener, ItemSetChangeListener { - - private static final long serialVersionUID = 1L; - private final Field field; - - public ChangeListener(final Field field) { - this.field = field; - } - - @Override - public void textChange(final TextChangeEvent event) { - saveButton.setEnabled(isSaveButtonEnabledAfterValueChange(field, event.getText())); - } - - @Override - public void valueChange(final ValueChangeEvent event) { - saveButton.setEnabled(isSaveButtonEnabledAfterValueChange(field, field.getValue())); - } - - @Override - public void containerItemSetChange(final ItemSetChangeEvent event) { - if (!(field instanceof Table)) { - return; - } - final Table table = (Table) field; - saveButton.setEnabled( - isSaveButtonEnabledAfterValueChange(table, table.getContainerDataSource().getItemIds())); - } - } - - /** - * Adds the component manually to the allComponents-List and adds a - * ValueChangeListener to it. Necessary in Update Distribution Type as the - * CheckBox concerned is an ItemProperty... - * - * @param component - * AbstractField - */ - public void updateAllComponents(final AbstractField component) { - - allComponents.add(component); - component.addValueChangeListener(new ChangeListener(component)); - } - public VerticalLayout getMainLayout() { return mainLayout; } + /** + * Enables the save confirmation button + * + * @param enabled + * boolean + * + */ public void setSaveButtonEnabled(final boolean enabled) { - saveButton.setEnabled(enabled); + confirmButton.setEnabled(enabled); } public void setCancelButtonEnabled(final boolean enabled) { cancelButton.setEnabled(enabled); } + /** + * Sets the close listener in save dialog + * + * @param closeListener + * SaveDialogCloseListener + */ + public void setCloseListener(final SaveDialogCloseListener closeListener) { + this.closeListener = closeListener; + } + /** * Check if the safe action can executed. After a the save action the * listener checks if the dialog can closed. @@ -556,38 +286,24 @@ public class CommonDialogWindow extends Window { /** * Checks if the safe action can executed. * - * @return true = save action can executed - * false = cannot execute safe action . + * @return = save action can executed = cannot execute + * safe action . */ boolean canWindowSaveOrUpdate(); /** - * Checks if the window can closed after the safe action is executed + * Checks if the window can be closed after the save action is executed * - * @return true = window will close false = - * will not closed. + * @return = window will close = will not closed. */ default boolean canWindowClose() { return true; } /** - * Saves/Updates action. Is called if canWindowSaveOrUpdate is - * true. + * Saves/Updates action. Is called if canWindowSaveOrUpdate is . * */ void saveOrUpdate(); } - - /** - * Updates the field allComponents. All components existing on the given - * layout are added to the list of allComponents - * - * @param layout - * AbstractLayout - */ - public void updateAllComponents(final AbstractLayout layout) { - allComponents = getAllComponents(layout); - } - } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/ConfirmationDialog.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/ConfirmationDialog.java index fa27ec8cb..de247b6e4 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/ConfirmationDialog.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/ConfirmationDialog.java @@ -8,164 +8,90 @@ */ package org.eclipse.hawkbit.ui.common; -import org.eclipse.hawkbit.ui.common.confirmwindow.layout.ConfirmationTab; -import org.eclipse.hawkbit.ui.components.SPUIComponentProvider; +import java.io.Serializable; +import java.util.function.Consumer; + +import org.eclipse.hawkbit.ui.common.CommonDialogWindow.ConfirmStyle; +import org.eclipse.hawkbit.ui.common.CommonDialogWindow.SaveDialogCloseListener; +import org.eclipse.hawkbit.ui.common.builder.WindowBuilder; import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleTiny; +import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil; +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.springframework.util.StringUtils; -import com.vaadin.event.ShortcutAction.KeyCode; import com.vaadin.server.Resource; -import com.vaadin.shared.ui.label.ContentMode; -import com.vaadin.ui.Alignment; -import com.vaadin.ui.Button; -import com.vaadin.ui.Button.ClickEvent; -import com.vaadin.ui.HorizontalLayout; +import com.vaadin.server.Sizeable.Unit; +import com.vaadin.shared.ui.ContentMode; +import com.vaadin.ui.Component; import com.vaadin.ui.Label; import com.vaadin.ui.UI; import com.vaadin.ui.VerticalLayout; import com.vaadin.ui.Window; -import com.vaadin.ui.themes.ValoTheme; /** * Class for the confirmation dialog which pops up when deleting, assigning... * entities. */ -public class ConfirmationDialog implements Button.ClickListener { +public class ConfirmationDialog implements Serializable { private static final long serialVersionUID = 1L; - private transient ConfirmationDialogCallback callback; + private final transient Consumer callback; - private final Button okButton; - - private final Window window; - - private boolean isImplicitClose; + private final CommonDialogWindow window; /** * Constructor for configuring confirmation dialog. * + * @param i18n + * internationalization * @param caption * the dialog caption. * @param question * the question. - * @param okLabel - * the Ok button label. - * @param cancelLabel - * the cancel button label. * @param callback * the callback. * @param tab - * ConfirmationTab which contains more information about the action - * which has to be confirmed, e.g. maintenance window + * ConfirmationTab which contains more information about the + * action which has to be confirmed, e.g. maintenance window * @param id * the id of the confirmation window */ - public ConfirmationDialog(final String caption, final String question, final String okLabel, - final String cancelLabel, final ConfirmationDialogCallback callback, final ConfirmationTab tab, - final String id) { - this(caption, question, okLabel, cancelLabel, callback, null, id, tab); + public ConfirmationDialog(final VaadinMessageSource i18n, final String caption, final String question, + final Consumer callback, final Component tab, final String id) { + this(i18n, caption, question, callback, null, id, tab); } /** * Constructor for configuring confirmation dialog. * + * @param i18n + * internationalization * @param caption * the dialog caption. * @param question * the question. - * @param okLabel - * the Ok button label. - * @param cancelLabel - * the cancel button label. - * @param callback - * the callback. - */ - public ConfirmationDialog(final String caption, final String question, final String okLabel, - final String cancelLabel, final ConfirmationDialogCallback callback) { - this(caption, question, okLabel, cancelLabel, callback, null, null, null); - } - - /** - * Constructor for configuring confirmation dialog. - * - * @param caption - * the dialog caption. - * @param question - * the question. - * @param okLabel - * the Ok button label. - * @param cancelLabel - * the cancel button label. * @param callback * the callback. * @param id * the id of the confirmation dialog */ - public ConfirmationDialog(final String caption, final String question, final String okLabel, - final String cancelLabel, final ConfirmationDialogCallback callback, final String id) { - this(caption, question, okLabel, cancelLabel, callback, null, id, null); + public ConfirmationDialog(final VaadinMessageSource i18n, final String caption, final String question, + final Consumer callback, final String id) { + this(i18n, caption, question, callback, null, id, null); } /** * Constructor for configuring confirmation dialog. * + * @param i18n + * internationalization * @param caption * the dialog caption. * @param question * the question. - * @param okLabel - * the Ok button label. - * @param cancelLabel - * the cancel button label. - * @param callback - * the callback. - * @param id - * the id of the confirmation dialog - * @param mapCloseToCancel - * Flag indicating whether or not the close event on the enclosing - * window should be mapped to a cancel event. - */ - public ConfirmationDialog(final String caption, final String question, final String okLabel, - final String cancelLabel, final ConfirmationDialogCallback callback, final String id, - final boolean mapCloseToCancel) { - this(caption, question, okLabel, cancelLabel, callback, null, id, null, mapCloseToCancel); - } - - /** - * Constructor for configuring confirmation dialog. - * - * @param caption - * the dialog caption. - * @param question - * the question. - * @param okLabel - * the Ok button label. - * @param cancelLabel - * the cancel button label. - * @param callback - * the callback. - * @param icon - * the icon of the dialog - */ - public ConfirmationDialog(final String caption, final String question, final String okLabel, - final String cancelLabel, final ConfirmationDialogCallback callback, final Resource icon) { - this(caption, question, okLabel, cancelLabel, callback, icon, null, null); - } - - /** - * Constructor for configuring confirmation dialog. - * - * @param caption - * the dialog caption. - * @param question - * the question. - * @param okLabel - * the Ok button label. - * @param cancelLabel - * the cancel button label. * @param callback * the callback. * @param icon @@ -173,154 +99,84 @@ public class ConfirmationDialog implements Button.ClickListener { * @param id * the id of the confirmation dialog * @param tab - * ConfirmationTab which contains more information about the action - * which has to be confirmed, e.g. maintenance window + * ConfirmationTab which contains more information about the + * action which has to be confirmed, e.g. maintenance window */ - public ConfirmationDialog(final String caption, final String question, final String okLabel, - final String cancelLabel, final ConfirmationDialogCallback callback, final Resource icon, final String id, - final ConfirmationTab tab) { - this(caption, question, okLabel, cancelLabel, callback, icon, id, tab, false); + public ConfirmationDialog(final VaadinMessageSource i18n, final String caption, final String question, + final Consumer callback, final Resource icon, final String id, final Component tab) { - } + final VerticalLayout content = new VerticalLayout(); + content.setMargin(false); + content.setSpacing(false); - /** - * Constructor for configuring confirmation dialog. - * - * @param caption - * the dialog caption. - * @param question - * the question. - * @param okLabel - * the Ok button label. - * @param cancelLabel - * the cancel button label. - * @param callback - * the callback. - * @param icon - * the icon of the dialog - * @param id - * the id of the confirmation dialog - * @param tab - * ConfirmationTab which contains more information about the action - * which has to be confirmed, e.g. maintenance window - * @param mapCloseToCancel - * Flag indicating whether or not the close event on the enclosing - * window should be mapped to a cancel event. - */ - public ConfirmationDialog(final String caption, final String question, final String okLabel, - final String cancelLabel, final ConfirmationDialogCallback callback, final Resource icon, final String id, - final ConfirmationTab tab, final boolean mapCloseToCancel) { - window = new Window(caption); - if (!StringUtils.isEmpty(id)) { - window.setId(id); + if (question != null) { + content.addComponent(createConfirmationQuestion(question)); } - window.addStyleName(SPUIStyleDefinitions.CONFIRMATION_WINDOW_CAPTION); + if (tab != null) { + content.addComponent(tab); + } + final WindowBuilder windowBuilder = new WindowBuilder(SPUIDefinitions.CONFIRMATION_WINDOW).caption(caption) + .content(content).cancelButtonClickListener(e -> callback.accept(false)) + .saveDialogCloseListener(getSaveDialogCloseListener()).hideMandatoryExplanation() + .buttonDecorator(SPUIButtonStyleTiny.class).confirmStyle(ConfirmStyle.OK).i18n(i18n); + + if (!StringUtils.isEmpty(id)) { + windowBuilder.id(id); + } + this.window = windowBuilder.buildCommonDialogWindow(); + window.setSaveButtonEnabled(true); + this.callback = callback; if (icon != null) { window.setIcon(icon); } - okButton = createOkButton(okLabel); - - final Button cancelButton = createCancelButton(cancelLabel); - if (mapCloseToCancel) { - window.addCloseListener(e -> { - if (!isImplicitClose) { - cancelButton.click(); - } - }); - } - window.setModal(true); window.addStyleName(SPUIStyleDefinitions.CONFIRMBOX_WINDOW_STYLE); - if (this.callback == null) { - this.callback = callback; - } - final VerticalLayout vLayout = new VerticalLayout(); - if (question != null) { - vLayout.addComponent(createConfirmationQuestion(question)); - } - if (tab != null) { - vLayout.addComponent(tab); - } + } - final HorizontalLayout hButtonLayout = createButtonLayout(cancelButton); - hButtonLayout.addStyleName("marginTop"); - vLayout.addComponent(hButtonLayout); - vLayout.setComponentAlignment(hButtonLayout, Alignment.BOTTOM_CENTER); + private SaveDialogCloseListener getSaveDialogCloseListener() { + return new SaveDialogCloseListener() { + @Override + public void saveOrUpdate() { + if (window.getParent() != null) { + UI.getCurrent().removeWindow(window); + } + callback.accept(true); + } - window.setContent(vLayout); - window.setResizable(false); + @Override + public boolean canWindowSaveOrUpdate() { + return true; + } + }; } private static Label createConfirmationQuestion(final String question) { - final Label questionLbl = new Label(question, ContentMode.TEXT); + // ContentMode.HTML is used instead of ContentMode.PREFORMATTED here due + // to no linebreaks if an entity name is very long + final String questionHtmlSave = HawkbitCommonUtil.sanitizeHtml(question); + final Label questionLbl = new Label(questionHtmlSave, ContentMode.HTML); + questionLbl.setWidth(100, Unit.PERCENTAGE); questionLbl.addStyleName(SPUIStyleDefinitions.CONFIRMBOX_QUESTION_LABEL); return questionLbl; } - private HorizontalLayout createButtonLayout(final Button cancelButton) { - final HorizontalLayout hButtonLayout = new HorizontalLayout(); - hButtonLayout.setSpacing(true); - hButtonLayout.addComponent(okButton); - hButtonLayout.addComponent(cancelButton); - hButtonLayout.setSizeUndefined(); - hButtonLayout.setComponentAlignment(okButton, Alignment.TOP_CENTER); - hButtonLayout.setComponentAlignment(cancelButton, Alignment.TOP_CENTER); - return hButtonLayout; - } - - private Button createCancelButton(final String cancelLabel) { - final Button button = SPUIComponentProvider.getButton(UIComponentIdProvider.CANCEL_BUTTON, cancelLabel, "", - null, false, null, SPUIButtonStyleTiny.class); - button.addClickListener(this); - button.setClickShortcut(KeyCode.ESCAPE); - return button; - } - - private Button createOkButton(final String okLabel) { - final Button button = SPUIComponentProvider.getButton(UIComponentIdProvider.OK_BUTTON, okLabel, "", - ValoTheme.BUTTON_PRIMARY, false, null, SPUIButtonStyleTiny.class); - button.addClickListener(this); - button.setClickShortcut(KeyCode.ENTER); - return button; + /** + * Enables the ok save button + * + * @param enabled + * boolean + */ + public void setOkButtonEnabled(final boolean enabled) { + window.setSaveButtonEnabled(enabled); } /** - * TenantAwareEvent handler for button clicks. - * - * @param event - * the click event. + * @return confirmation window */ - @Override - public void buttonClick(final ClickEvent event) { - if (window.getParent() != null) { - isImplicitClose = true; - UI.getCurrent().removeWindow(window); - } - callback.response(event.getSource().equals(okButton)); - } - public Window getWindow() { return window; } - /** - * Interface for confirmation dialog callbacks. - */ - @FunctionalInterface - public interface ConfirmationDialogCallback { - /** - * The user response. - * - * @param ok - * True if user clicked ok. - */ - void response(boolean ok); - } - - public Button getOkButton() { - return okButton; - } - } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/CoordinatesToColor.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/CoordinatesToColor.java index 09f88d655..874108bb9 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/CoordinatesToColor.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/CoordinatesToColor.java @@ -15,8 +15,7 @@ import com.vaadin.ui.AbstractColorPicker.Coordinates2Color; * Converts 2d-coordinates to a Color. */ public class CoordinatesToColor implements Coordinates2Color { - - private static final long serialVersionUID = 9145071998551210789L; + private static final long serialVersionUID = 1L; @Override public Color calculate(final int x, final int y) { @@ -26,19 +25,19 @@ public class CoordinatesToColor implements Coordinates2Color { @Override public int[] calculate(final Color color) { final float[] hsv = color.getHSV(); - final int x = Math.round(hsv[0] * 220F); + final int x = (int) Math.round(hsv[0] * 220.0); final int y = calculateYCoordinateOfColor(hsv); return new int[] { x, y }; } private static Color calculateHSVColor(final int x, final int y) { - final float h = x / 220F; + final float h = (float) (x / 220.0); float s = 1F; float v = 1F; if (y < 110) { - s = y / 110F; + s = (float) (y / 110.0); } else if (y > 110) { - v = 1F - (y - 110F) / 110F; + v = (float) (1.0 - (y - 110.0) / 110.0); } return new Color(Color.HSVtoRGB(h, s, v)); } @@ -48,9 +47,9 @@ public class CoordinatesToColor implements Coordinates2Color { // lower half /* Assuming hsv[] array value will have in the range of 0 to 1 */ if (hsv[1] < 1F) { - y = Math.round(hsv[1] * 110F); + y = (int) Math.round(hsv[1] * 110.0); } else { - y = Math.round(110F - (hsv[1] + hsv[2]) * 110F); + y = (int) Math.round(110.0 - ((double) hsv[1] + (double) hsv[2]) * 110.0); } return y; } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/DistributionSetTypeBeanQuery.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/DistributionSetTypeBeanQuery.java deleted file mode 100644 index ea3a7057c..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/DistributionSetTypeBeanQuery.java +++ /dev/null @@ -1,87 +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; - -import java.util.List; -import java.util.Map; - -import org.eclipse.hawkbit.repository.DistributionSetTypeManagement; -import org.eclipse.hawkbit.repository.OffsetBasedPageRequest; -import org.eclipse.hawkbit.repository.model.DistributionSetType; -import org.eclipse.hawkbit.ui.utils.SpringContextHelper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -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; - -/** - * Distribution Set Type Bean query. - * - * - * - * - */ -public class DistributionSetTypeBeanQuery extends AbstractBeanQuery { - - private static final long serialVersionUID = 1329137292955215629L; - - private static final Logger LOG = LoggerFactory.getLogger(DistributionSetTypeBeanQuery.class); - - private final Sort sort = new Sort(Direction.ASC, "name"); - private transient DistributionSetTypeManagement distributionSetTypeManagement; - - /** - * Parametric constructor. - * - * @param definition - * @param queryConfig - * @param sortIds - * @param sortStates - */ - public DistributionSetTypeBeanQuery(final QueryDefinition definition, final Map queryConfig, - final Object[] sortIds, final boolean[] sortStates) { - super(definition, queryConfig, sortIds, sortStates); - } - - @Override - protected DistributionSetType constructBean() { - return null; - } - - @Override - public int size() { - - long size = getDistributionSetManagement().count(); - if (size > Integer.MAX_VALUE) { - size = Integer.MAX_VALUE; - } - return (int) size; - } - - private DistributionSetTypeManagement getDistributionSetManagement() { - if (distributionSetTypeManagement == null) { - distributionSetTypeManagement = SpringContextHelper.getBean(DistributionSetTypeManagement.class); - } - return distributionSetTypeManagement; - } - - @Override - protected List loadBeans(final int startIndex, final int count) { - return getDistributionSetManagement().findAll(new OffsetBasedPageRequest(startIndex, count, sort)).getContent(); - } - - @Override - protected void saveBeans(final List arg0, final List arg1, - final List arg2) { - LOG.info("in side of save Bean()"); - } - -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/EmptyStringValidator.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/EmptyStringValidator.java deleted file mode 100644 index af3029fc0..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/EmptyStringValidator.java +++ /dev/null @@ -1,43 +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; - -import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; - -import com.vaadin.data.validator.StringLengthValidator; - -/** - * Assures that the entered text does not contain only whitespaces. At least one - * character has to be entered. Leading and trailing whitespaces are allowed as - * they will be trimmed by the repository. - */ -public class EmptyStringValidator extends StringLengthValidator { - - private static final long serialVersionUID = 1L; - - private static final String MESSAGE_KEY = "validator.textfield.min.length"; - - /** - * Constructor for EmptyStringValidator - * - * @param i18n - * {@link VaadinMessageSource} - * @param maxLength - * max length of the textfield - */ - public EmptyStringValidator(final VaadinMessageSource i18n, final int maxLength) { - super(i18n.getMessage(MESSAGE_KEY, maxLength), 1, maxLength, false); - } - - @Override - public boolean isValidValue(final String value) { - return super.isValidValue(value != null ? value.trim() : null); - } - -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/EntityWindowLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/EntityWindowLayout.java new file mode 100644 index 000000000..e2160eb90 --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/EntityWindowLayout.java @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2020 Bosch.IO 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; + +import java.util.function.Consumer; + +import com.vaadin.ui.ComponentContainer; + +/** + * Interface for entity window layout + * + * @param + * Generic type entity + */ +public interface EntityWindowLayout { + + ComponentContainer getRootComponent(); + + void setEntity(final T proxyEntity); + + T getEntity(); + + /** + * Method to add validation listeners. + * + * @param validationCallback + * Validation callback event + */ + void addValidationListener(final Consumer validationCallback); +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/ManagementEntityState.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/ManagementEntityState.java deleted file mode 100644 index 63b2a220c..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/ManagementEntityState.java +++ /dev/null @@ -1,35 +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; - -import java.util.Set; - -/** - * Interface for all entity states UI to show the details to a entity. - */ -public interface ManagementEntityState { - - /** - * The selected entities for the detail. - * - * @param values - * the selected entities. - * - */ - void setSelectedEnitities(Set values); - - /** - * The last selected value. - * - * @param value - * the value - */ - void setLastSelectedEntityId(Long value); - -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/SoftwareModuleTypeBeanQuery.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/SoftwareModuleTypeBeanQuery.java deleted file mode 100644 index 44a61361d..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/SoftwareModuleTypeBeanQuery.java +++ /dev/null @@ -1,75 +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; - -import java.util.List; -import java.util.Map; - -import org.eclipse.hawkbit.repository.OffsetBasedPageRequest; -import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement; -import org.eclipse.hawkbit.repository.model.SoftwareModuleType; -import org.eclipse.hawkbit.ui.utils.SpringContextHelper; -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; - -/** - * - * - */ -public class SoftwareModuleTypeBeanQuery extends AbstractBeanQuery { - private static final long serialVersionUID = 7824925429198339644L; - - private final Sort sort = new Sort(Direction.ASC, "name"); - private transient SoftwareModuleTypeManagement softwareModuleTypeManagement; - - /** - * Parametric constructor. - */ - public SoftwareModuleTypeBeanQuery(final QueryDefinition definition, final Map queryConfig, - final Object[] sortIds, final boolean[] sortStates) { - super(definition, queryConfig, sortIds, sortStates); - } - - @Override - protected SoftwareModuleType constructBean() { - return null; - } - - @Override - public int size() { - long size = getSoftwareModuleTypeManagement().count(); - if (size > Integer.MAX_VALUE) { - size = Integer.MAX_VALUE; - } - return (int) size; - } - - private SoftwareModuleTypeManagement getSoftwareModuleTypeManagement() { - if (softwareModuleTypeManagement == null) { - softwareModuleTypeManagement = SpringContextHelper.getBean(SoftwareModuleTypeManagement.class); - } - return softwareModuleTypeManagement; - } - - @Override - protected List loadBeans(final int startIndex, final int count) { - - return getSoftwareModuleTypeManagement().findAll(new OffsetBasedPageRequest(startIndex, count, sort)) - .getContent(); - } - - @Override - protected void saveBeans(final List addedBeans, final List modifiedBeans, - final List removedBeans) { - // CRUD operations on Target will be done through repository methods - } - -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/UserDetailsFormatter.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/UserDetailsFormatter.java index 6aa387b02..c67202f6a 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/UserDetailsFormatter.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/UserDetailsFormatter.java @@ -173,6 +173,9 @@ public final class UserDetailsFormatter { return Optional.ofNullable(userPrincipal.getEmail()); } + /** + * @return Details of currently logged-in user + */ public static UserDetails getCurrentUser() { final SecurityContext context = (SecurityContext) VaadinService.getCurrentRequest().getWrappedSession() .getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/builder/AbstractTextFieldBuilder.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/builder/AbstractTextFieldBuilder.java index e84dd7e73..c56cd38b9 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/builder/AbstractTextFieldBuilder.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/builder/AbstractTextFieldBuilder.java @@ -8,14 +8,8 @@ */ package org.eclipse.hawkbit.ui.common.builder; -import java.util.LinkedList; -import java.util.List; - -import org.eclipse.hawkbit.ui.common.EmptyStringValidator; -import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; import org.springframework.util.StringUtils; -import com.vaadin.data.Validator; import com.vaadin.ui.AbstractTextField; /** @@ -34,18 +28,17 @@ public abstract class AbstractTextFieldBuilder { private String styleName; private String prompt; private String id; - private boolean immediate = true; - private boolean required; private boolean readOnly; private boolean enabled = true; private final int maxLengthAllowed; - private final List validators = new LinkedList<>(); protected AbstractTextFieldBuilder(final int maxLengthAllowed) { this.maxLengthAllowed = maxLengthAllowed; } /** + * Add caption to text field + * * @param caption * the caption to set * @return the builder @@ -57,6 +50,8 @@ public abstract class AbstractTextFieldBuilder { } /** + * Add style to text field + * * @param style * the style to set * @return the builder * @return the builder @@ -67,6 +62,8 @@ public abstract class AbstractTextFieldBuilder { } /** + * Add style name to text field + * * @param styleName * the styleName to set * @return the builder @@ -77,21 +74,8 @@ public abstract class AbstractTextFieldBuilder { } /** - * @param required - * the required to set - * @param i18n - * to translate error message - * @return the builder - */ - public T required(final boolean required, final VaadinMessageSource i18n) { - this.required = required; - if (required) { - validators.add(new EmptyStringValidator(i18n, maxLengthAllowed)); - } - return (T) this; - } - - /** + * Add read only to text field + * * @param readOnly * the readOnly to set * @return the builder @@ -102,6 +86,8 @@ public abstract class AbstractTextFieldBuilder { } /** + * Enable the text field + * * @param enabled * the enabled to set * @return the builder @@ -112,6 +98,8 @@ public abstract class AbstractTextFieldBuilder { } /** + * Add prompt to text field + * * @param prompt * the prompt to set * @return the builder @@ -122,16 +110,8 @@ public abstract class AbstractTextFieldBuilder { } /** - * @param immediate - * the immediate to set - * @return the builder - */ - public T immediate(final boolean immediate) { - this.immediate = immediate; - return (T) this; - } - - /** + * Add id to text field + * * @param id * the id to set * @return the builder @@ -141,17 +121,6 @@ public abstract class AbstractTextFieldBuilder { return (T) this; } - /** - * - * @param validator - * the validator to set for this field - * @return the builder - */ - public T validator(final Validator validator) { - validators.add(validator); - return (T) this; - } - /** * Build a textfield * @@ -160,8 +129,6 @@ public abstract class AbstractTextFieldBuilder { public E buildTextComponent() { final E textComponent = createTextComponent(); - textComponent.setRequired(required); - textComponent.setImmediate(immediate); textComponent.setReadOnly(readOnly); textComponent.setEnabled(enabled); @@ -177,7 +144,7 @@ public abstract class AbstractTextFieldBuilder { textComponent.addStyleName(styleName); } if (!StringUtils.isEmpty(prompt)) { - textComponent.setInputPrompt(prompt); + textComponent.setPlaceholder(prompt); } if (maxLengthAllowed > 0) { @@ -188,12 +155,6 @@ public abstract class AbstractTextFieldBuilder { textComponent.setId(id); } - if (!validators.isEmpty()) { - validators.forEach(textComponent::addValidator); - } - - textComponent.setNullRepresentation(""); - return textComponent; } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/builder/BoundComponent.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/builder/BoundComponent.java new file mode 100644 index 000000000..bc06cd525 --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/builder/BoundComponent.java @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2020 Bosch.IO 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.builder; + +import java.io.Serializable; + +import com.vaadin.data.Binder.Binding; +import com.vaadin.ui.Component; + +/** + * Holds a {@link Component} and its {@link Binding} + * + * @param + * Component type + */ +public class BoundComponent implements Serializable { + private static final long serialVersionUID = 1L; + + private final T component; + private final Binding binding; + + /** + * Constructor + * + * @param component + * component + * @param binding + * binding of the component + */ + public BoundComponent(final T component, final Binding binding) { + this.component = component; + this.binding = binding; + } + + /** + * @return The component + */ + public T getComponent() { + return component; + } + + /** + * Set to true if the component is required + * + * @param isRequired + * boolean + */ + public void setRequired(final boolean isRequired) { + binding.setAsRequiredEnabled(isRequired); + } + + /** + * @return boolean + */ + public boolean isValid() { + return !binding.validate(false).isError(); + } + + /** + * Validate binding + */ + public void validate() { + binding.validate(); + } + + /** + * unbind the component + */ + public void unbind() { + binding.unbind(); + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/builder/ComboBoxBuilder.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/builder/ComboBoxBuilder.java deleted file mode 100644 index cc4c0134f..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/builder/ComboBoxBuilder.java +++ /dev/null @@ -1,72 +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.builder; - -import org.eclipse.hawkbit.ui.components.SPUIComponentProvider; -import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions; - -import com.vaadin.data.Property; -import com.vaadin.ui.ComboBox; -import com.vaadin.ui.themes.ValoTheme; - -/** - * Builds ComboBox Elements with a commonly used properties. - */ -public class ComboBoxBuilder { - - private Property.ValueChangeListener valueChangeListener; - - private String id; - - private String prompt; - - private String caption; - - public ComboBoxBuilder setValueChangeListener(final Property.ValueChangeListener valueChangeListener) { - this.valueChangeListener = valueChangeListener; - return this; - } - - public ComboBoxBuilder setId(final String id) { - this.id = id; - return this; - } - - public ComboBoxBuilder setPrompt(final String prompt) { - this.prompt = prompt; - return this; - } - - public ComboBoxBuilder setCaption(final String caption) { - this.caption = caption; - return this; - } - - /** - * @return a new ComboBox - */ - public ComboBox buildCombBox() { - final ComboBox comboBox = SPUIComponentProvider.getComboBox(null, "", null, ValoTheme.COMBOBOX_SMALL, false, "", - prompt); - comboBox.setImmediate(true); - comboBox.setPageLength(7); - comboBox.setItemCaptionPropertyId(SPUILabelDefinitions.VAR_NAME); - comboBox.setSizeUndefined(); - if (caption != null) { - comboBox.setCaption(caption); - } - if (id != null) { - comboBox.setId(id); - } - if (valueChangeListener != null) { - comboBox.addValueChangeListener(valueChangeListener); - } - return comboBox; - } -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/builder/FormComponentBuilder.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/builder/FormComponentBuilder.java new file mode 100644 index 000000000..e003c672b --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/builder/FormComponentBuilder.java @@ -0,0 +1,414 @@ +/** + * Copyright (c) 2020 Bosch.IO 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.builder; + +import org.eclipse.hawkbit.repository.model.NamedEntity; +import org.eclipse.hawkbit.repository.model.NamedVersionedEntity; +import org.eclipse.hawkbit.repository.model.Type; +import org.eclipse.hawkbit.ui.common.data.aware.ActionTypeAware; +import org.eclipse.hawkbit.ui.common.data.aware.DescriptionAware; +import org.eclipse.hawkbit.ui.common.data.aware.DsIdAware; +import org.eclipse.hawkbit.ui.common.data.aware.NameAware; +import org.eclipse.hawkbit.ui.common.data.aware.StartOptionAware; +import org.eclipse.hawkbit.ui.common.data.aware.TargetFilterQueryAware; +import org.eclipse.hawkbit.ui.common.data.aware.TypeInfoAware; +import org.eclipse.hawkbit.ui.common.data.aware.VersionAware; +import org.eclipse.hawkbit.ui.common.data.providers.DistributionSetStatelessDataProvider; +import org.eclipse.hawkbit.ui.common.data.providers.AbstractProxyDataProvider; +import org.eclipse.hawkbit.ui.common.data.providers.TargetFilterQueryDataProvider; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyDistributionSet; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyTargetFilterQuery; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyType; +import org.eclipse.hawkbit.ui.common.data.proxies.ProxyTypeInfo; +import org.eclipse.hawkbit.ui.components.SPUIComponentProvider; +import org.eclipse.hawkbit.ui.management.miscs.ActionTypeOptionGroupAssignmentLayout; +import org.eclipse.hawkbit.ui.rollout.window.components.AutoStartOptionGroupLayout; +import org.eclipse.hawkbit.ui.utils.SPDateTimeUtil; +import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; +import org.eclipse.hawkbit.ui.utils.UIMessageIdProvider; +import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; +import org.springframework.util.StringUtils; + +import com.vaadin.data.Binder; +import com.vaadin.data.Binder.Binding; +import com.vaadin.data.Binder.BindingBuilder; +import com.vaadin.data.Validator; +import com.vaadin.data.ValueProvider; +import com.vaadin.server.Setter; +import com.vaadin.ui.CheckBox; +import com.vaadin.ui.ComboBox; +import com.vaadin.ui.TextArea; +import com.vaadin.ui.TextField; + +/** + * Builder class for from components + */ +public final class FormComponentBuilder { + public static final String TEXTFIELD_NAME = "textfield.name"; + public static final String TEXTFIELD_VERSION = "textfield.version"; + public static final String TEXTFIELD_DESCRIPTION = "textfield.description"; + + public static final String PROMPT_DISTRIBUTION_SET = "prompt.distribution.set"; + public static final String PROMPT_TARGET_FILTER = "prompt.target.filter"; + + public static final String CAPTION_TYPE = "caption.type"; + + private FormComponentBuilder() { + } + + /** + * Create an input field for a name + * + * @param + * type of the binder + * @param binder + * that is bound to the field + * @param i18n + * message source + * @param fieldId + * id of the field + * @return the TextField with its Binding + */ + public static BoundComponent createNameInput(final Binder binder, + final VaadinMessageSource i18n, final String fieldId) { + final TextField nameInput = new TextFieldBuilder(NamedEntity.NAME_MAX_SIZE).id(fieldId) + .caption(i18n.getMessage(TEXTFIELD_NAME)).prompt(i18n.getMessage(TEXTFIELD_NAME)).buildTextComponent(); + nameInput.setSizeUndefined(); + + final Binding binding = binder.forField(nameInput) + .asRequired(i18n.getMessage(UIMessageIdProvider.MESSAGE_ERROR_NAMEREQUIRED)) + .bind(NameAware::getName, NameAware::setName); + + return new BoundComponent<>(nameInput, binding); + } + + /** + * Create a required input field for a version + * + * @param + * type of the binder + * @param binder + * that is bound to the field + * @param i18n + * message source + * @param fieldId + * id of the field + * @return the TextField + */ + public static BoundComponent createVersionInput(final Binder binder, + final VaadinMessageSource i18n, final String fieldId) { + final TextField versionInput = new TextFieldBuilder(NamedVersionedEntity.VERSION_MAX_SIZE).id(fieldId) + .caption(i18n.getMessage(TEXTFIELD_VERSION)).prompt(i18n.getMessage(TEXTFIELD_VERSION)) + .buildTextComponent(); + versionInput.setSizeUndefined(); + + final Binding binding = binder.forField(versionInput) + .asRequired(i18n.getMessage(UIMessageIdProvider.MESSAGE_ERROR_VERSIONREQUIRED)) + .bind(VersionAware::getVersion, VersionAware::setVersion); + + return new BoundComponent<>(versionInput, binding); + } + + /** + * Create an optional input field for a description + * + * @param + * type of the binder + * @param binder + * that is bound to the field + * @param i18n + * message source + * @param fieldId + * id of the field + * @return the TextArea + */ + public static BoundComponent