diff --git a/java/com.sap.sailing.gwt.ui/DataMining.css b/java/com.sap.sailing.gwt.ui/DataMining.css index 9ef6e46c92d..1818050740f 100644 --- a/java/com.sap.sailing.gwt.ui/DataMining.css +++ b/java/com.sap.sailing.gwt.ui/DataMining.css @@ -220,6 +220,11 @@ input.magnifier { top: 7.5px; } +.orientationAnchor { + position: relative; + top: 7.5px; +} + .groupingProvider { padding: 3px 0px; overflow-y: auto; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java index 9cc9f2add98..2c7b5c1cd03 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java @@ -962,6 +962,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String raceTimeReachingTooltip(); String raceTimeUpwindTooltip(); String dataMining(); + String changeOrientation(); String hideToolbar(); String showSeriesLeaderboards(); String showOverallLeaderboard(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties index 5d2b596d3f7..7755f0f354c 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties @@ -954,6 +954,7 @@ raceTimeUpwindTooltip=Total time traveled upwind in this race noCustomGrouperScriptTextError=The grouper script is empty noGrouperSelectedError=No grouper type is selected dataMining=Data Mining +changeOrientation=Change Orientation hideToolbar=Hide toolbar showSeriesLeaderboards=Show series leaderboards showOverallLeaderboard=Show overall leaderboard diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties index 81e2722509f..493831ede52 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties @@ -940,6 +940,7 @@ raceTimeUpwindTooltip=Die in diesem Rennen auf der Kreuz gesegelte Zeit noCustomGrouperScriptTextError=Das Grouper-Skript leer ist noGrouperSelectedError=Kein Gruppierungs-Typ ausgewählt ist dataMining=Data Mining +changeOrientation=Ausrichtung ändern hideToolbar=Toolbar ausblenden showSeriesLeaderboards=Ranglisten der Serie anzeigen showOverallLeaderboard=Gesamtrangliste anzeigen diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/DataMiningEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/DataMiningEntryPoint.java index 4ef1f0133b2..b5a60161e28 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/DataMiningEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/DataMiningEntryPoint.java @@ -6,6 +6,8 @@ import java.util.logging.Logger; import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.Style.Unit; +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.http.client.UrlBuilder; import com.google.gwt.json.client.JSONArray; import com.google.gwt.json.client.JSONObject; @@ -13,6 +15,8 @@ import com.google.gwt.json.client.JSONParser; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.rpc.ServiceDefTarget; +import com.google.gwt.user.client.ui.AbstractImagePrototype; +import com.google.gwt.user.client.ui.Anchor; import com.google.gwt.user.client.ui.DockLayoutPanel; import com.google.gwt.user.client.ui.RootLayoutPanel; import com.google.gwt.user.client.ui.SplitLayoutPanel; @@ -55,9 +59,19 @@ public class DataMiningEntryPoint extends AbstractSailingEntryPoint { public static final ComponentResources resources = GWT.create(ComponentResources.class); private static final Logger LOG = Logger.getLogger(DataMiningEntryPoint.class.getName()); + private static final DataMiningResources dataMiningResources = GWT.create(DataMiningResources.class); + private final DataMiningServiceAsync dataMiningService = GWT.create(DataMiningService.class); private DataMiningSession session; + private QueryDefinitionProviderWithControls queryDefinitionProvider; + private CompositeResultsPresenter resultsPresenter; + private Integer resultsPresenterSouthHeight = 350; + private Integer resultsPresenterEastWidth = 750; + + private SplitLayoutPanel queryAndResultSplitPanel; + private boolean queryAndResultAreVertical = true; + @Override protected void doOnModuleLoad() { Highcharts.ensureInjectedWithMore(); @@ -78,7 +92,6 @@ public class DataMiningEntryPoint extends AbstractSailingEntryPoint { authorizedContentDecorator.setPermissionToCheck(serverInfo, ServerActions.DATA_MINING); authorizedContentDecorator.setContentWidgetFactory(new WidgetFactory() { - private QueryDefinitionProviderWithControls queryDefinitionProvider; private SimpleQueryRunner queryRunner; private final DataMiningSettingsInfoManager settingsManager = new DataMiningSettingsInfoManagerImpl( getStringMessages()); @@ -86,7 +99,7 @@ public class DataMiningEntryPoint extends AbstractSailingEntryPoint { @Override public Widget get() { DataMiningSettingsControl settingsControl = new AnchorDataMiningSettingsControl(null, null); - CompositeResultsPresenter resultsPresenter = new TabbedSailingResultsPresenter(/* parent */ null, + resultsPresenter = new TabbedSailingResultsPresenter(/* parent */ null, /* context */ null, /* drillDownCallback */ groupKey -> { queryDefinitionProvider.drillDown(groupKey, () -> { queryRunner.run(queryDefinitionProvider.getQueryDefinition()); @@ -111,6 +124,18 @@ public class DataMiningEntryPoint extends AbstractSailingEntryPoint { queryDefinitionProvider, dataMiningService, queryRunner); queryDefinitionProvider.addControl(new StoredDataMiningQueryPanel(dataProvider)); } + + Anchor orientationAnchor = new Anchor(AbstractImagePrototype.create( + dataMiningResources.orientationIcon()).getSafeHtml()); + orientationAnchor.addStyleName("orientationAnchor"); + orientationAnchor.setTitle(getStringMessages().changeOrientation()); + orientationAnchor.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + setQueryAndResultOrientation(); + } + }); + queryDefinitionProvider.addControl(orientationAnchor); /* * Running queries automatically when they've been changed is currently unnecessary, if not even * counterproductive. This removes the query runner settings to prevent that the user can enable the @@ -118,9 +143,8 @@ public class DataMiningEntryPoint extends AbstractSailingEntryPoint { */ // settingsControl.addSettingsComponent(queryRunner); - SplitLayoutPanel splitPanel = new SplitLayoutPanel(10); - splitPanel.addSouth(resultsPresenter.getEntryWidget(), 350); - splitPanel.add(queryDefinitionProvider.getEntryWidget()); + queryAndResultSplitPanel = new SplitLayoutPanel(10); + addDefinitionProviderAndResultPresenter(); if (queryIdentifier != null) { if (Storage.isLocalStorageSupported()) { @@ -153,7 +177,7 @@ public class DataMiningEntryPoint extends AbstractSailingEntryPoint { NotificationType.ERROR); } } - return splitPanel; + return queryAndResultSplitPanel; } }); @@ -165,6 +189,49 @@ public class DataMiningEntryPoint extends AbstractSailingEntryPoint { rootPanel.add(panel); } + /** + * Toggles the position of the {@link #resultsPresenter} from south to east and vice versa. + */ + public void setQueryAndResultOrientation() { + setQueryAndResultOrientation(!queryAndResultAreVertical); + } + /** + * Sets the position of the {@link #resultsPresenter}. + * @param vertical {@code boolean} {@code true} places it in the south position + * and {@code false} in the east position. + */ + public void setQueryAndResultOrientation(boolean vertical) { + if (vertical != queryAndResultAreVertical) { + // Store current size for the next orientation change + Double size = queryAndResultSplitPanel.getWidgetSize(resultsPresenter.getEntryWidget()); + if (size != null) { + if (queryAndResultAreVertical) { + resultsPresenterSouthHeight = size.intValue(); + } else { + resultsPresenterEastWidth = size.intValue(); + } + } + + queryAndResultAreVertical = vertical; + + queryAndResultSplitPanel.remove(resultsPresenter.getEntryWidget()); + // You can't add panels as long as there is a center panel so temporarily remove it + queryAndResultSplitPanel.remove(queryDefinitionProvider.getEntryWidget()); + + // Add the center panel back once the changes are made + addDefinitionProviderAndResultPresenter(); + } + } + + private void addDefinitionProviderAndResultPresenter() { + if (queryAndResultAreVertical) { + queryAndResultSplitPanel.addSouth(resultsPresenter.getEntryWidget(), resultsPresenterSouthHeight); + } else { + queryAndResultSplitPanel.addEast(resultsPresenter.getEntryWidget(), resultsPresenterEastWidth); + } + queryAndResultSplitPanel.add(queryDefinitionProvider.getEntryWidget()); + } + private void removeUrlParameter() { try { UrlBuilder builder = Window.Location.createUrlBuilder().setHost(Window.Location.getHost()) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/DataMiningResources.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/DataMiningResources.java new file mode 100644 index 00000000000..40f4ab6d6f3 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/DataMiningResources.java @@ -0,0 +1,9 @@ +package com.sap.sailing.gwt.ui.datamining; + +import com.google.gwt.resources.client.ClientBundle; +import com.google.gwt.resources.client.ImageResource; + +interface DataMiningResources extends ClientBundle { + @Source("com/sap/sailing/gwt/ui/datamining/images/change_orientation.png") + ImageResource orientationIcon(); +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/tagging/TaggingPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/tagging/TaggingPanel.java index 8d07383dd79..49ca4e32f26 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/tagging/TaggingPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/tagging/TaggingPanel.java @@ -10,6 +10,7 @@ import com.google.gwt.core.shared.GWT; import com.google.gwt.dom.client.BrowserEvents; import com.google.gwt.user.cellview.client.CellList; import com.google.gwt.user.cellview.client.HasKeyboardSelectionPolicy.KeyboardSelectionPolicy; +import com.google.gwt.user.client.Random; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.FlowPanel; @@ -38,6 +39,7 @@ import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.common.media.MediaTagConstants; import com.sap.sse.gwt.client.Notification; import com.sap.sse.gwt.client.Notification.NotificationType; +import com.sap.sse.gwt.client.Storage; import com.sap.sse.gwt.client.media.ImageDTO; import com.sap.sse.gwt.client.media.ImageResizingTaskDTO; import com.sap.sse.gwt.client.player.TimeListener; @@ -71,6 +73,9 @@ public class TaggingPanel extends ComponentWithoutSettings CREATE_TAG, EDIT_TAG } + // HTML5 Storage for notifying other instances of tag changes + private static final String LOCAL_STORAGE_UPDATE_KEY = "private-tags-changed"; + // styling private final TagPanelStyle style; @@ -109,6 +114,9 @@ public class TaggingPanel extends ComponentWithoutSettings protected final TimePoint timePointToHighlight; protected final String tagToHighlight; + // ID for inter-instance communication + private String id; + /** * This boolean prevents the timer from jumping when other users create or delete tags. The timer change event is * called by the selection change event and the other way around. When:
@@ -162,6 +170,9 @@ public class TaggingPanel extends ComponentWithoutSettings userService.addUserStatusEventHandler(this); raceTimesInfoProvider.addRaceTimesInfoProviderListener(this); + generateRandomId(); + registerStorageEventHandler(); + setCurrentState(State.VIEW); initializePanel(); } @@ -225,6 +236,38 @@ public class TaggingPanel extends ComponentWithoutSettings updateContent(); } + /** + * Registers a storage event handler that reloads the private tags if another instance has updated them. + */ + private void registerStorageEventHandler() { + Storage.addStorageEventHandler(event -> { + if (LOCAL_STORAGE_UPDATE_KEY.equals(event.getKey()) && event.getNewValue() != null + && !event.getNewValue().isEmpty() && !event.getNewValue().equals(id.toString())) { + reloadPrivateTags(); + } + }); + } + + /** + * Notifies other instances that the private tags have changed. + */ + private void firePrivateTagUpdateEvent() { + if (Storage.isSupported()) { + if (Storage.getLocalStorageIfSupported().getItem(LOCAL_STORAGE_UPDATE_KEY).equals(id)) { + // This instance fired the last update. To fire another one we have to change our id + generateRandomId(); + } + Storage.getLocalStorageIfSupported().setItem(LOCAL_STORAGE_UPDATE_KEY, id); + } + } + + /** + * Generates a new random ID + */ + private void generateRandomId() { + id = Long.toString(System.currentTimeMillis() * Random.nextInt()); + } + /** * Updates parameters required to save/revoke {@link com.sap.sailing.domain.abstractlog.race.RaceLogEvent events} * to/from {@link com.sap.sailing.domain.abstractlog.race.RaceLog RaceLog}. @@ -374,6 +417,7 @@ public class TaggingPanel extends ComponentWithoutSettings // reload private tags if added tag is private if (!visibleForPublic) { reloadPrivateTags(); + firePrivateTagUpdateEvent(); } } else { Notification.notify(stringMessages.tagNotSavedReason(result.getMessage()), @@ -421,6 +465,7 @@ public class TaggingPanel extends ComponentWithoutSettings if (!silent) { Notification.notify(stringMessages.tagRemovedSuccessfully(), NotificationType.SUCCESS); } + firePrivateTagUpdateEvent(); } else { Notification.notify(stringMessages.tagNotRemoved() + " " + result.getMessage(), NotificationType.ERROR); @@ -464,6 +509,7 @@ public class TaggingPanel extends ComponentWithoutSettings // refresh UI. if (!tagToUpdate.isVisibleForPublic() || !visibleForPublic) { reloadPrivateTags(); + firePrivateTagUpdateEvent(); } else { updateContent(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/datamining/images/change_orientation.png b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/datamining/images/change_orientation.png new file mode 100644 index 00000000000..b6b6b665ffe Binary files /dev/null and b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/datamining/images/change_orientation.png differ diff --git a/java/com.sap.sailing.mongodb.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.mongodb.test/META-INF/MANIFEST.MF index 2aa62a6ba5a..2ea264c5696 100644 --- a/java/com.sap.sailing.mongodb.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.mongodb.test/META-INF/MANIFEST.MF @@ -35,7 +35,8 @@ Require-Bundle: com.sap.sailing.domain, com.sap.sailing.domain.racelogtrackingadapter, com.sap.sailing.domain.swisstimingreplayadapter, com.sap.sse.security, - com.sap.sse.security.common;bundle-version="1.0.0" + com.sap.sse.security.common;bundle-version="1.0.0", + com.sap.sse.security.testsupport Import-Package: com.sap.sailing.domain.common, com.sap.sailing.domain.common.impl, com.sap.sailing.domain.common.racelog, diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingLeaderboardGroups.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingLeaderboardGroups.java index 58da6e553f7..60545577ed4 100755 --- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingLeaderboardGroups.java +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingLeaderboardGroups.java @@ -60,6 +60,8 @@ import com.sap.sse.common.Color; import com.sap.sse.common.Util; import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.mongodb.MongoDBService; +import com.sap.sse.security.SecurityService; +import com.sap.sse.security.testsupport.SecurityServiceMockFactory; public class TestStoringAndRetrievingLeaderboardGroups extends AbstractMongoDBTest { private MongoObjectFactory mongoObjectFactory = null; @@ -115,7 +117,14 @@ public class TestStoringAndRetrievingLeaderboardGroups extends AbstractMongoDBTe @Test public void testStoringAndRetrievingLeaderboardGroupWithOverallLeaderboardWithScoreCorrection() throws NoWindException { - RacingEventService racingEventService = new RacingEventServiceImpl(); + + final SecurityService securityService = SecurityServiceMockFactory.mockSecurityService(); + final RacingEventService racingEventService = new RacingEventServiceImpl() { + @Override + public SecurityService getSecurityService() { + return securityService; + } + }; Competitor wolfgangWithoutBoat = new CompetitorImpl(123, "$$$Dr. Wolfgang+Hunger$$$", "KYC", Color.RED, null, null, new TeamImpl("STG", Collections.singleton( new PersonImpl("$$$Dr. Wolfgang+Hunger$$$", new NationalityImpl("GER"), /* dateOfBirth */ null, "This is famous Dr. Wolfgang Hunger")), new PersonImpl("Rigo van Maas", new NationalityImpl("NED"), diff --git a/java/com.sap.sailing.server.replication.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.server.replication.test/META-INF/MANIFEST.MF index bf82f4c7820..abc223329dc 100644 --- a/java/com.sap.sailing.server.replication.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.server.replication.test/META-INF/MANIFEST.MF @@ -34,5 +34,6 @@ Require-Bundle: com.sap.sailing.server, org.mongodb.mongo-java-driver;bundle-version="3.6.4", com.sap.sse.security, com.sap.sse.security.common, - com.sun.jersey;bundle-version="1.17.0" + com.sun.jersey;bundle-version="1.17.0", + com.sap.sse.security.testsupport Automatic-Module-Name: com.sap.sailing.server.replication.test diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/InitialLoadReplicationObjectIdentityTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/InitialLoadReplicationObjectIdentityTest.java index 00c3efede88..aa951b1ad21 100755 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/InitialLoadReplicationObjectIdentityTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/InitialLoadReplicationObjectIdentityTest.java @@ -17,9 +17,11 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.concurrent.Callable; import org.junit.Before; import org.junit.Test; +import org.mockito.Mockito; import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl; import com.sap.sailing.domain.abstractlog.race.RaceLog; @@ -56,14 +58,19 @@ import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard; import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.LeaderboardGroup; import com.sap.sailing.domain.leaderboard.impl.LowPoint; +import com.sap.sailing.domain.persistence.DomainObjectFactory; +import com.sap.sailing.domain.persistence.MongoObjectFactory; import com.sap.sailing.domain.persistence.PersistenceFactory; +import com.sap.sailing.domain.persistence.media.MediaDB; import com.sap.sailing.domain.persistence.media.MediaDBFactory; import com.sap.sailing.domain.racelog.tracking.EmptySensorFixStore; +import com.sap.sailing.domain.racelog.tracking.SensorFixStore; import com.sap.sailing.domain.ranking.OneDesignRankingMetric; import com.sap.sailing.domain.test.PositionAssert; import com.sap.sailing.domain.test.TrackBasedTest; import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sailing.domain.tracking.WindStore; import com.sap.sailing.domain.tracking.WindTrack; import com.sap.sailing.domain.tracking.impl.EmptyWindStore; import com.sap.sailing.server.impl.RacingEventServiceImpl; @@ -81,21 +88,47 @@ import com.sap.sse.common.media.MimeType; import com.sap.sse.replication.ReplicationMasterDescriptor; import com.sap.sse.replication.impl.ReplicationReceiverImpl; import com.sap.sse.replication.testsupport.AbstractServerReplicationTestSetUp.ReplicationServiceTestImpl; +import com.sap.sse.security.SecurityService; +import com.sap.sse.security.testsupport.SecurityServiceMockFactory; public class InitialLoadReplicationObjectIdentityTest extends AbstractServerReplicationTest { private Pair, ReplicationMasterDescriptor> replicationDescriptorPair; + private static class RacingEventServiceWithSecurityService extends RacingEventServiceImpl { + private final SecurityService securityService; + + private RacingEventServiceWithSecurityService(final DomainObjectFactory domainObjectFactory, MongoObjectFactory mongoObjectFactory, + MediaDB mediaDB, WindStore windStore, SensorFixStore sensorFixStore, boolean restoreTrackedRaces, SecurityService securityService) { + super(domainObjectFactory, mongoObjectFactory, mediaDB, windStore, sensorFixStore, restoreTrackedRaces); + this.securityService = securityService; + } + + @Override + public SecurityService getSecurityService() { + return securityService; + } + } + /** * Drops the test DB. Sets up master and replica, starts the JMS message broker and registers the replica with the master. */ + @SuppressWarnings("unchecked") @Before @Override public void setUp() throws Exception { persistenceSetUp(/* dropDB */ true); - this.master = new RacingEventServiceImpl(PersistenceFactory.INSTANCE.getDomainObjectFactory(testSetUp.mongoDBService, DomainFactory.INSTANCE), PersistenceFactory.INSTANCE - .getMongoObjectFactory(testSetUp.mongoDBService), MediaDBFactory.INSTANCE.getMediaDB(testSetUp.mongoDBService), EmptyWindStore.INSTANCE, EmptySensorFixStore.INSTANCE, /* restoreTrackedRaces */ false); - this.replica = new RacingEventServiceImpl(PersistenceFactory.INSTANCE.getDomainObjectFactory(testSetUp.mongoDBService, DomainFactory.INSTANCE), PersistenceFactory.INSTANCE - .getMongoObjectFactory(testSetUp.mongoDBService), MediaDBFactory.INSTANCE.getMediaDB(testSetUp.mongoDBService), EmptyWindStore.INSTANCE, EmptySensorFixStore.INSTANCE, /* restoreTrackedRaces */ false); + final SecurityService securityService = SecurityServiceMockFactory.mockSecurityService(); + Mockito.when(securityService.setOwnershipCheckPermissionForObjectCreationAndRevertOnError(Mockito.any(), + Mockito.any(), Mockito.any(), Mockito.any(Callable.class))) + .thenAnswer(i -> i.getArgumentAt(3, Callable.class).call()); + this.master = new RacingEventServiceWithSecurityService(PersistenceFactory.INSTANCE.getDomainObjectFactory(testSetUp.mongoDBService, DomainFactory.INSTANCE), PersistenceFactory.INSTANCE + .getMongoObjectFactory(testSetUp.mongoDBService), + MediaDBFactory.INSTANCE.getMediaDB(testSetUp.mongoDBService), EmptyWindStore.INSTANCE, + EmptySensorFixStore.INSTANCE, /* restoreTrackedRaces */ false, securityService); + this.replica = new RacingEventServiceWithSecurityService(PersistenceFactory.INSTANCE.getDomainObjectFactory(testSetUp.mongoDBService, DomainFactory.INSTANCE), PersistenceFactory.INSTANCE + .getMongoObjectFactory(testSetUp.mongoDBService), + MediaDBFactory.INSTANCE.getMediaDB(testSetUp.mongoDBService), EmptyWindStore.INSTANCE, + EmptySensorFixStore.INSTANCE, /* restoreTrackedRaces */ false, securityService); } private void performReplicationSetup() throws Exception { diff --git a/java/com.sap.sailing.server.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.server.test/META-INF/MANIFEST.MF index 895f3906de2..cffcd3c6c76 100644 --- a/java/com.sap.sailing.server.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.server.test/META-INF/MANIFEST.MF @@ -24,7 +24,8 @@ Require-Bundle: com.sap.sailing.domain, com.sap.sailing.domain.shared.android, com.sap.sailing.domain.racelogtrackingadapter.testsupport, com.sap.sailing.server.testsupport, - com.sun.jersey;bundle-version="1.17.0" + com.sun.jersey;bundle-version="1.17.0", + com.sap.sse.security.testsupport Import-Package: com.sap.sailing.server.gateway.jaxrs, com.sap.sailing.server.gateway.jaxrs.spi, com.sap.sse.security.userstore.mongodb, diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java index 0679e3a175b..718a33704f8 100644 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java @@ -25,6 +25,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; import javax.ws.rs.core.Response; @@ -190,6 +191,7 @@ public class MasterDataImportTest { deleteAllDataFromDatabase(); } + @SuppressWarnings("unchecked") @Before public void setUp() { UserGroupImpl defaultTenant = new UserGroupImpl(new UUID(0, 1), "defaultTenant"); @@ -213,6 +215,9 @@ public class MasterDataImportTest { Mockito.doReturn(true).when(securityService).hasCurrentUserOneOfExplicitPermissions( Mockito.any(WithQualifiedObjectIdentifier.class), Matchers. anyVararg()); + Mockito.when(securityService.setOwnershipCheckPermissionForObjectCreationAndRevertOnError(Mockito.any(), + Mockito.any(), Mockito.any(), Mockito.any(Callable.class))) + .thenAnswer(i -> i.getArgumentAt(3, Callable.class).call()); Mockito.doReturn(true).when(fakeSubject).isAuthenticated(); @@ -227,7 +232,7 @@ public class MasterDataImportTest { sourceService = Mockito .spy(new RacingEventServiceImpl(dbFactory, mongoObjectFactory, MediaDBFactory.INSTANCE.getDefaultMediaDB(), EmptyWindStore.INSTANCE, EmptySensorFixStore.INSTANCE, - false)); + false)); masterDataResource = spyResource(new DummyMasterDataRessource(), sourceService); @@ -1879,7 +1884,13 @@ public class MasterDataImportTest { public void testMasterDataImportForMediaTracks() throws MalformedURLException, IOException, InterruptedException, ClassNotFoundException { // Setup source service - RacingEventService sourceService = new RacingEventServiceImpl(); + RacingEventService sourceService = new RacingEventServiceImpl() { + @Override + public SecurityService getSecurityService() { + return securityService; + } + }; + ; Set assignedRaces = new HashSet(); String regattaName1 = "49er"; String regattaName2 = "49er FX"; diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RemoveLeaderboardGroupTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RemoveLeaderboardGroupTest.java index 3e10b5d23ae..10cc1678c93 100644 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RemoveLeaderboardGroupTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RemoveLeaderboardGroupTest.java @@ -36,16 +36,25 @@ import com.sap.sailing.server.operationaltransformation.UpdateLeaderboardGroup; import com.sap.sse.common.TimePoint; import com.sap.sse.common.Util; import com.sap.sse.common.impl.MillisecondsTimePoint; +import com.sap.sse.security.SecurityService; +import com.sap.sse.security.testsupport.SecurityServiceMockFactory; import com.sap.sse.shared.media.ImageDescriptor; import com.sap.sse.shared.media.VideoDescriptor; public class RemoveLeaderboardGroupTest { private RacingEventService server; private Event pfingstbusch; - + @Before public void setUp() { - server = new RacingEventServiceImpl(); + + final SecurityService securityService = SecurityServiceMockFactory.mockSecurityService(); + server = new RacingEventServiceImpl() { + @Override + public SecurityService getSecurityService() { + return securityService; + } + }; List allEvents = new ArrayList<>(); Util.addAll(server.getAllEvents(), allEvents); for (final Event e : allEvents) { diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java index 9f7a80751f0..6c943953479 100644 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java @@ -28,6 +28,7 @@ import java.util.Map.Entry; import java.util.Optional; import java.util.Set; import java.util.UUID; +import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; @@ -3025,11 +3026,21 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes private void addOverallLeaderboardToLeaderboardGroup(LeaderboardGroup leaderboardGroup, ScoringScheme scoringScheme, int[] discardThresholds) { - Leaderboard overallLeaderboard = new LeaderboardGroupMetaLeaderboard(leaderboardGroup, scoringScheme, + final Leaderboard overallLeaderboard = new LeaderboardGroupMetaLeaderboard(leaderboardGroup, scoringScheme, new ThresholdBasedResultDiscardingRuleImpl(discardThresholds)); - leaderboardGroup.setOverallLeaderboard(overallLeaderboard); - addLeaderboard(overallLeaderboard); - updateStoredLeaderboard(overallLeaderboard); + // FIXME bug5081 ownership? + getSecurityService().setOwnershipCheckPermissionForObjectCreationAndRevertOnError( + SecuredDomainType.LEADERBOARD, Leaderboard.getTypeRelativeObjectIdentifier(overallLeaderboard.getName()), + overallLeaderboard.getDisplayName(), + new Callable() { + @Override + public Void call() throws Exception { + leaderboardGroup.setOverallLeaderboard(overallLeaderboard); + addLeaderboard(overallLeaderboard); + updateStoredLeaderboard(overallLeaderboard); + return null; + } + }); } @Override diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/QueryDefinitionProviderWithControls.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/QueryDefinitionProviderWithControls.java index f6cf540a18c..112f78ed4cb 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/QueryDefinitionProviderWithControls.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/QueryDefinitionProviderWithControls.java @@ -11,7 +11,9 @@ import java.util.function.Consumer; import com.google.gwt.dom.builder.shared.HtmlBuilderFactory; import com.google.gwt.dom.builder.shared.HtmlUListBuilder; +import com.google.gwt.dom.client.Style.Overflow; import com.google.gwt.dom.client.Style.Unit; +import com.google.gwt.dom.client.Style.WhiteSpace; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.i18n.client.LocaleInfo; @@ -72,7 +74,7 @@ public class QueryDefinitionProviderWithControls extends AbstractQueryDefinition private static final double HeaderPanelHeight = 41; private static final double InitialQueryDefinitionViewerWidth = 600; - private static final double InitialStatisticProviderWidth = 800; + private static final double InitialStatisticProviderWidth = 500; private static final double FooterPanelHeight = 60; private static final int SplitterSize = 10; @@ -108,6 +110,10 @@ public class QueryDefinitionProviderWithControls extends AbstractQueryDefinition // Creating the header panel, that contains the retriever chain provider and the controls controlsPanel = new FlowPanel(); controlsPanel.addStyleName("dataMiningMarginBase"); + // The following 2 lines restrict controlsPanel to using one line + // instead of pushing its content into the next line when there is not enough space. + controlsPanel.getElement().getStyle().setOverflow(Overflow.HIDDEN); + controlsPanel.getElement().getStyle().setWhiteSpace(WhiteSpace.NOWRAP); this.settingsControl = settingsControl; addControl(this.settingsControl.getEntryWidget()); diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/celltable/TableWrapper.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/celltable/TableWrapper.java index 7fcfc3d08ae..7b5c0106046 100755 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/celltable/TableWrapper.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/celltable/TableWrapper.java @@ -110,6 +110,7 @@ public abstract class TableWrapper, SM public void refresh(Iterable newItems) { dataProvider.getList().clear(); Util.addAll(newItems, dataProvider.getList()); + dataProvider.refresh(); dataProvider.flush(); }