java/com.google.gwt.dev/lib/gwt-dev.jar
java/com.google.gwt.servlet/lib/gwt-servlet-deps.jar
java/com.google.gwt.servlet/lib/gwt-servlet.jar
java/com.google.gwt.user/lib/gwt-user.jar
java/com.sap.sailing.dashboards.gwt/GWT Dashboards DevMode.launch
... ...
@@ -20,6 +20,7 @@
20 20
</listAttribute>
21 21
<booleanAttribute key="org.eclipse.jdt.debug.ui.CONSIDER_INHERITED_MAIN" value="true"/>
22 22
<booleanAttribute key="org.eclipse.jdt.debug.ui.INCLUDE_EXTERNAL_JARS" value="true"/>
23
+ <booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
23 24
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
24 25
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8&quot; javaProject=&quot;com.sap.sailing.gwt.ui&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#13;&#10;"/>
25 26
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#13;&#10; &lt;memento exportedEntriesOnly=&quot;false&quot; project=&quot;com.sap.sailing.dashboards.gwt&quot;/&gt;&#13;&#10;&lt;/runtimeClasspathEntry&gt;&#13;&#10;"/>
java/com.sap.sailing.dashboards.gwt/GWT Dashboards SDM.launch
... ...
@@ -22,6 +22,7 @@
22 22
<booleanAttribute key="org.eclipse.jdt.debug.ui.CONSIDER_INHERITED_MAIN" value="true"/>
23 23
<booleanAttribute key="org.eclipse.jdt.debug.ui.INCLUDE_EXTERNAL_JARS" value="true"/>
24 24
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_CLASSPATH_ONLY_JAR" value="false"/>
25
+ <booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
25 26
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
26 27
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8&quot; javaProject=&quot;com.sap.sailing.gwt.ui&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#10;"/>
27 28
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#10;&lt;memento exportedEntriesOnly=&quot;false&quot; project=&quot;com.sap.sailing.dashboards.gwt&quot;/&gt;&#10;&lt;/runtimeClasspathEntry&gt;&#10;"/>
java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/RibDashboard.gwt.xml
... ...
@@ -43,7 +43,7 @@
43 43
<inherits name="com.sap.sailing.ExpeditionConnectorCommon" />
44 44
45 45
<!-- module containing locale configuration -->
46
- <inherits name='com.sap.sailing.gwt.common.SailingLocalesAllPermutations' />
46
+ <inherits name='com.sap.sailing.gwt.common.SailingLocalesSinglePermutation' />
47 47
<inherits name="com.sap.sailing.gwt.ui.NoUI" />
48 48
49 49
<!-- Specify the app entry point class. -->
java/com.sap.sailing.gwt.ui/GWT Sailing DevMode.launch
... ...
@@ -33,6 +33,7 @@
33 33
</listAttribute>
34 34
<booleanAttribute key="org.eclipse.jdt.debug.ui.CONSIDER_INHERITED_MAIN" value="true"/>
35 35
<booleanAttribute key="org.eclipse.jdt.debug.ui.INCLUDE_EXTERNAL_JARS" value="true"/>
36
+ <booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
36 37
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
37 38
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8&quot; javaProject=&quot;com.sap.sailing.gwt.ui&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#13;&#10;"/>
38 39
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.gwt.ui/src/main/resources&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
java/com.sap.sailing.gwt.ui/GWT Sailing SDM (Home+Admin+Raceboard).launch
... ...
@@ -25,6 +25,7 @@
25 25
</listAttribute>
26 26
<booleanAttribute key="org.eclipse.jdt.debug.ui.CONSIDER_INHERITED_MAIN" value="true"/>
27 27
<booleanAttribute key="org.eclipse.jdt.debug.ui.INCLUDE_EXTERNAL_JARS" value="true"/>
28
+ <booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
28 29
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
29 30
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8&quot; javaProject=&quot;com.sap.sailing.gwt.ui&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#13;&#10;"/>
30 31
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.gwt.ui/src/main/resources&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
java/com.sap.sailing.gwt.ui/GWT Sailing SDM ManagementConsole.launch
... ...
@@ -22,6 +22,7 @@
22 22
</listAttribute>
23 23
<booleanAttribute key="org.eclipse.jdt.debug.ui.CONSIDER_INHERITED_MAIN" value="true"/>
24 24
<booleanAttribute key="org.eclipse.jdt.debug.ui.INCLUDE_EXTERNAL_JARS" value="true"/>
25
+ <booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
25 26
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
26 27
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8&quot; javaProject=&quot;com.sap.sailing.gwt.ui&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#13;&#10;"/>
27 28
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.gwt.ui/src/main/resources&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
java/com.sap.sailing.gwt.ui/GWT Sailing SDM.launch
... ...
@@ -38,6 +38,7 @@
38 38
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_ATTR_USE_ARGFILE" value="false"/>
39 39
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_SHOW_CODEDETAILS_IN_EXCEPTION_MESSAGES" value="true"/>
40 40
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_CLASSPATH_ONLY_JAR" value="false"/>
41
+ <booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
41 42
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
42 43
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8&quot; javaProject=&quot;com.sap.sailing.gwt.ui&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#13;&#10;"/>
43 44
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.gwt.ui/src/main/resources&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/AutoPlay.gwt.xml
... ...
@@ -19,7 +19,7 @@
19 19
20 20
<!-- Specify the paths for translatable code -->
21 21
<!-- module containing locale configuration -->
22
- <inherits name='com.sap.sailing.gwt.common.SailingLocalesAllPermutations' />
22
+ <inherits name='com.sap.sailing.gwt.common.SailingLocalesSinglePermutation' />
23 23
24 24
<source path="client"/>
25 25
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/SailingLocalesAllPermutations.gwt.xml
... ...
@@ -13,8 +13,8 @@
13 13
14 14
but there can be other i18n bundles.
15 15
16
- the build script switches references to *AllPermutations*
17
- to *AllPermutations* in gwt modules with the "-b" option.
16
+ the build script switches references to *SinglePermutation*
17
+ to *SinglePermutation* in gwt modules with the "-b" option.
18 18
19 19
-->
20 20
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/HomeBase.gwt.xml
... ...
@@ -24,7 +24,7 @@
24 24
<inherits name='com.google.gwt.maps.Maps' />
25 25
26 26
<!-- module containing locale configuration -->
27
- <inherits name='com.sap.sailing.gwt.common.SailingLocalesAllPermutations' />
27
+ <inherits name='com.sap.sailing.gwt.common.SailingLocalesSinglePermutation' />
28 28
29 29
<source path='client' />
30 30
<source path='shared' />
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/media/MediaPage.css
... ...
@@ -125,7 +125,6 @@
125 125
}
126 126
127 127
.popup {
128
- position: fixed!important;
129 128
z-index: 1000;
130 129
width: auto;
131 130
height: auto;
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/media/MediaPage.java
... ...
@@ -31,6 +31,7 @@ import com.sap.sailing.gwt.ui.client.StringMessages;
31 31
import com.sap.sailing.gwt.ui.client.media.GalleryImageHolder;
32 32
import com.sap.sailing.gwt.ui.client.media.VideoThumbnail;
33 33
import com.sap.sailing.gwt.ui.shared.ManageMediaModel;
34
+import com.sap.sse.common.media.MimeType;
34 35
import com.sap.sse.gwt.client.media.ImageDTO;
35 36
import com.sap.sse.gwt.client.media.VideoDTO;
36 37
import com.sap.sse.security.ui.authentication.AuthenticationContextEvent;
... ...
@@ -135,11 +136,8 @@ public class MediaPage extends Composite {
135 136
@UiHandler("mediaAddButton")
136 137
public void handleMediaAddButtonClick(ClickEvent e) {
137 138
popupHolder.clear();
138
- final DesktopMediaUploadPopup popup = new DesktopMediaUploadPopup(video -> {
139
- manageMediaModel.addVideo(video, eventDto -> updateMedia());
140
- }, image -> {
141
- manageMediaModel.addImage(image, eventDto -> updateMedia());
142
- });
139
+ final DesktopMediaUploadPopup popup = new DesktopMediaUploadPopup(
140
+ (images, videos) -> manageMediaModel.addImagesAndVideos(images, videos, eventDto -> updateMedia()));
143 141
popupHolder.add(popup);
144 142
popup.center();
145 143
}
... ...
@@ -306,7 +304,17 @@ public class MediaPage extends Composite {
306 304
private void putVideoOnDisplay(final VideoDTO video, boolean autoplay) {
307 305
videoDisplayUi = new VideoWithLowerThird(true, autoplay);
308 306
videoDisplayUi.setVideo(video);
309
- videoDisplayHolderUi.setWidget(videoDisplayUi);
307
+ try {
308
+ videoDisplayHolderUi.setWidget(videoDisplayUi);
309
+ } catch (Exception e) {
310
+ final MimeType mimeType;
311
+ if (video != null) {
312
+ mimeType = video.getMimeType();
313
+ } else {
314
+ mimeType = MimeType.unknown;
315
+ }
316
+ GWT.log("Could not setup video player video with mime type: " + mimeType, e);
317
+ }
310 318
}
311 319
312 320
private void setMediaManaged(boolean managed) {
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/uploadpopup/DesktopMediaUploadPopup.java
... ...
@@ -1,6 +1,7 @@
1 1
package com.sap.sailing.gwt.home.desktop.partials.uploadpopup;
2 2
3
-import java.util.function.Consumer;
3
+import java.util.List;
4
+import java.util.function.BiConsumer;
4 5
5 6
import com.sap.sailing.gwt.home.desktop.partials.media.MediaPageResources;
6 7
import com.sap.sailing.gwt.ui.shared.AbstractMediaUploadPopup;
... ...
@@ -9,14 +10,14 @@ import com.sap.sse.gwt.client.media.VideoDTO;
9 10
10 11
public class DesktopMediaUploadPopup extends AbstractMediaUploadPopup {
11 12
12
- public DesktopMediaUploadPopup(Consumer<VideoDTO> updateVideo, Consumer<ImageDTO> updateImage) {
13
- super(updateVideo, updateImage);
13
+ public DesktopMediaUploadPopup(BiConsumer<List<ImageDTO>, List<VideoDTO>> updateImagesAndVideos) {
14
+ super(updateImagesAndVideos);
14 15
MediaPageResources.INSTANCE.css().ensureInjected();
15 16
addStyleName(MediaPageResources.INSTANCE.css().popup());
16 17
}
17 18
18 19
@Override
19
- protected void updateFileName(String fileName) {
20
+ protected String getTitleFromFileName(String fileName) {
20 21
if (fileName == null) {
21 22
fileName = "";
22 23
}
... ...
@@ -26,7 +27,6 @@ public class DesktopMediaUploadPopup extends AbstractMediaUploadPopup {
26 27
} else {
27 28
name = fileName;
28 29
}
29
- titleTextBox.setValue(name);
30
- fileNameInput.setValue(fileName);
30
+ return name;
31 31
}
32 32
}
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/AbstractEventActivity.java
... ...
@@ -260,7 +260,10 @@ public abstract class AbstractEventActivity<PLACE extends AbstractEventPlace> ex
260 260
261 261
@Override
262 262
public boolean hasMedia() {
263
- return !showRegattaMetadata() && eventDTO.isHasMedia();
263
+ if (showRegattaMetadata()) {
264
+ return false;
265
+ }
266
+ return eventDTO.isHasMedia();
264 267
}
265 268
266 269
@Override
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/multiregatta/mediatab/MultiregattaMediaTabView.java
... ...
@@ -9,10 +9,8 @@ import com.sap.sailing.gwt.home.desktop.places.event.multiregatta.EventMultirega
9 9
import com.sap.sailing.gwt.home.desktop.places.event.multiregatta.EventMultiregattaView.Presenter;
10 10
import com.sap.sailing.gwt.home.desktop.places.event.multiregatta.MultiregattaTabView;
11 11
import com.sap.sailing.gwt.home.shared.app.ActivityCallback;
12
-import com.sap.sailing.gwt.ui.client.SailingServiceHelper;
13
-import com.sap.sailing.gwt.ui.client.StringMessages;
14 12
import com.sap.sailing.gwt.ui.client.refresh.ErrorAndBusyClientFactory;
15
-import com.sap.sailing.gwt.ui.shared.ManageMediaModel;
13
+import com.sap.sse.security.shared.HasPermissions;
16 14
17 15
/**
18 16
* Created by pgtaboada on 25.11.14.
... ...
@@ -37,9 +35,8 @@ public class MultiregattaMediaTabView extends Composite implements MultiregattaT
37 35
38 36
@Override
39 37
public TabView.State getState() {
40
- final ManageMediaModel model = new ManageMediaModel(
41
- SailingServiceHelper.createSailingServiceWriteInstance(), currentPresenter.getUserService(), currentPresenter.getEventDTO(), StringMessages.INSTANCE);
42
- return currentPresenter.hasMedia() || model.hasPermissions()
38
+ return currentPresenter.hasMedia() || currentPresenter.getUserService()
39
+ .hasPermission(currentPresenter.getEventDTO(), HasPermissions.DefaultActions.UPDATE)
43 40
? TabView.State.VISIBLE
44 41
: TabView.State.INVISIBLE;
45 42
}
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/mediatab/RegattaMediaTabView.java
... ...
@@ -9,10 +9,7 @@ import com.sap.sailing.gwt.home.desktop.places.event.regatta.EventRegattaView;
9 9
import com.sap.sailing.gwt.home.desktop.places.event.regatta.EventRegattaView.Presenter;
10 10
import com.sap.sailing.gwt.home.desktop.places.event.regatta.RegattaTabView;
11 11
import com.sap.sailing.gwt.home.shared.app.ActivityCallback;
12
-import com.sap.sailing.gwt.ui.client.SailingServiceHelper;
13
-import com.sap.sailing.gwt.ui.client.StringMessages;
14 12
import com.sap.sailing.gwt.ui.client.refresh.ErrorAndBusyClientFactory;
15
-import com.sap.sailing.gwt.ui.shared.ManageMediaModel;
16 13
17 14
/**
18 15
* Created by pgtaboada on 25.11.14.
... ...
@@ -33,11 +30,7 @@ public class RegattaMediaTabView extends Composite implements RegattaTabView<Reg
33 30
34 31
@Override
35 32
public TabView.State getState() {
36
- final ManageMediaModel model = new ManageMediaModel(
37
- SailingServiceHelper.createSailingServiceWriteInstance(), currentPresenter.getUserService(), currentPresenter.getEventDTO(), StringMessages.INSTANCE);
38
- return currentPresenter.hasMedia() || model.hasPermissions()
39
- ? TabView.State.VISIBLE
40
- : TabView.State.INVISIBLE;
33
+ return currentPresenter.hasMedia() ? TabView.State.VISIBLE : TabView.State.INVISIBLE;
41 34
}
42 35
43 36
@Override
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/uploadpopup/MobileMediaUploadPopup.java
... ...
@@ -1,6 +1,7 @@
1 1
package com.sap.sailing.gwt.home.mobile.partials.uploadpopup;
2 2
3
-import java.util.function.Consumer;
3
+import java.util.List;
4
+import java.util.function.BiConsumer;
4 5
5 6
import com.sap.sailing.gwt.home.mobile.places.event.media.MediaViewResources;
6 7
import com.sap.sailing.gwt.ui.shared.AbstractMediaUploadPopup;
... ...
@@ -9,15 +10,14 @@ import com.sap.sse.gwt.client.media.VideoDTO;
9 10
10 11
public class MobileMediaUploadPopup extends AbstractMediaUploadPopup {
11 12
12
- public MobileMediaUploadPopup(Consumer<VideoDTO> updateVideo, Consumer<ImageDTO> updateImage) {
13
- super(updateVideo, updateImage);
13
+ public MobileMediaUploadPopup(BiConsumer<List<ImageDTO>, List<VideoDTO>> updateImagesAndVideos) {
14
+ super(updateImagesAndVideos);
14 15
MediaViewResources.INSTANCE.css().ensureInjected();
15 16
addStyleName(MediaViewResources.INSTANCE.css().popup());
16 17
}
17 18
18 19
@Override
19
- protected void updateFileName(String fileName) {
20
- titleTextBox.setValue("");
21
- fileNameInput.setValue("");
20
+ protected String getTitleFromFileName(String fileName) {
21
+ return "";
22 22
}
23 23
}
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/event/media/MediaViewImpl.gss
... ...
@@ -27,10 +27,9 @@
27 27
}
28 28
29 29
.popup {
30
- position: fixed!important;
31 30
z-index: 1000;
32 31
width: 100%;
33
- height: 100%;
32
+ height: auto;
34 33
background-color: #ffffff;
35 34
}
36 35
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/event/media/MediaViewImpl.java
... ...
@@ -59,11 +59,8 @@ public class MediaViewImpl extends AbstractEventView<MediaView.Presenter> implem
59 59
MediaViewResources.INSTANCE.css().ensureInjected();
60 60
setViewContent(uiBinder.createAndBindUi(this));
61 61
MediaPageResources.INSTANCE.css().ensureInjected();
62
- mobileMediaUploadPopup = new MobileMediaUploadPopup(video -> {
63
- manageMediaModel.addVideo(video, eventDto -> updateMedia());
64
- }, image -> {
65
- manageMediaModel.addImage(image, eventDto -> updateMedia());
66
- });
62
+ mobileMediaUploadPopup = new MobileMediaUploadPopup(
63
+ (images, videos) -> manageMediaModel.addImagesAndVideos(images, videos, eventDto -> updateMedia()));
67 64
addMediaButtonUi.addClickHandler(new ClickHandler() {
68 65
@Override
69 66
public void onClick(ClickEvent event) {
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/SharedHome.gss
... ...
@@ -123,6 +123,12 @@ button.uploadButton:after {
123 123
font-family: 'Open Sans',sans-serif;
124 124
}
125 125
126
+.popup button.headerButton {
127
+ position: absolute;
128
+ right: 10px;
129
+ top: 10px;
130
+}
131
+
126 132
.subTitle {
127 133
width: 100%;
128 134
color: #333;
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/SharedHomeResources.java
... ...
@@ -18,6 +18,8 @@ public interface SharedHomeResources extends CommonIcons {
18 18
public interface LocalCss extends CssResource {
19 19
String primary();
20 20
21
+ String headerButton();
22
+
21 23
String right();
22 24
23 25
String label();
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/managementconsole/ManagementConsole.gwt.xml
... ...
@@ -32,7 +32,7 @@
32 32
<set-property name="gwt.logging.systemHandler" value="ENABLED" />
33 33
34 34
<!-- module containing locale configuration -->
35
- <inherits name='com.sap.sailing.gwt.common.SailingLocalesAllPermutations' />
35
+ <inherits name='com.sap.sailing.gwt.common.SailingLocalesSinglePermutation' />
36 36
37 37
<!-- <inherits name='com.sap.sailing.gwt.ui.SourceActions' /> -->
38 38
<inherits name='com.sap.sailing.gwt.ui.SourceAdminConsole' />
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/RegattaOverview.gwt.xml
... ...
@@ -25,7 +25,7 @@
25 25
26 26
<source path='client'/>
27 27
<!-- module containing locale configuration -->
28
- <inherits name='com.sap.sailing.gwt.common.SailingLocalesAllPermutations' />
28
+ <inherits name='com.sap.sailing.gwt.common.SailingLocalesSinglePermutation' />
29 29
30 30
<inherits name="com.sap.sailing.gwt.ui.SourceAdminConsole"/>
31 31
<inherits name='com.sap.sailing.gwt.ui.SourceClient'/>
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/AdminConsole.gwt.xml
... ...
@@ -42,7 +42,7 @@
42 42
<set-property name="gwt.logging.systemHandler" value="ENABLED" />
43 43
44 44
<!-- module containing locale configuration -->
45
- <inherits name='com.sap.sailing.gwt.common.SailingLocalesAllPermutations' />
45
+ <inherits name='com.sap.sailing.gwt.common.SailingLocalesSinglePermutation' />
46 46
47 47
<!-- Turning CSS obfuscation off -->
48 48
<!-- <set-configuration-property name="CssResource.style" value="pretty"/> -->
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractBoatCertificatesPanel.java
... ...
@@ -58,6 +58,7 @@ import com.sap.sse.gwt.client.controls.busyindicator.BusyIndicator;
58 58
import com.sap.sse.gwt.client.controls.busyindicator.SimpleBusyIndicator;
59 59
import com.sap.sse.gwt.client.controls.listedit.StringListEditorComposite;
60 60
import com.sap.sse.gwt.client.dialog.DialogUtils;
61
+import com.sap.sse.gwt.client.fileupload.FileUploadUtil;
61 62
import com.sap.sse.security.shared.HasPermissions.DefaultActions;
62 63
import com.sap.sse.security.shared.dto.SecuredDTO;
63 64
import com.sap.sse.security.ui.client.UserService;
... ...
@@ -254,7 +255,7 @@ public abstract class AbstractBoatCertificatesPanel extends SimplePanel {
254 255
255 256
private void formSubmitComplete(SubmitCompleteEvent e) {
256 257
try {
257
- final JSONObject json = (JSONObject) JSONParser.parseStrict(e.getResults());
258
+ final JSONObject json = (JSONObject) JSONParser.parseStrict(FileUploadUtil.getApplicationJsonContent(e));
258 259
if (json.get(ORCCertificateUploadConstants.CERTIFICATES) != null) {
259 260
sailingServiceWrite.getORCCertificates(e.getResults(), new AsyncCallback<Collection<ORCCertificate>>() {
260 261
@Override
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractFileImportWidget.java
... ...
@@ -26,6 +26,7 @@ import com.sap.sailing.gwt.ui.shared.TrackFileImportDeviceIdentifierDTO;
26 26
import com.sap.sse.gwt.client.ErrorReporter;
27 27
import com.sap.sse.gwt.client.Notification;
28 28
import com.sap.sse.gwt.client.Notification.NotificationType;
29
+import com.sap.sse.gwt.client.fileupload.FileUploadUtil;
29 30
30 31
public abstract class AbstractFileImportWidget extends Composite {
31 32
... ...
@@ -88,7 +89,7 @@ public abstract class AbstractFileImportWidget extends Composite {
88 89
89 90
@UiHandler("formPanelUi")
90 91
void onFileImportComplete(SubmitCompleteEvent event) {
91
- SensorDataImportResponse importResponse = SensorDataImportResponse.parse(event.getResults());
92
+ SensorDataImportResponse importResponse = SensorDataImportResponse.parse(FileUploadUtil.getApplicationJsonContent(event));
92 93
if (importResponse == null) {
93 94
Notification.notify(StringMessages.INSTANCE.unexpectedErrorDuringFileImport(), NotificationType.ERROR);
94 95
} else {
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorEditDialog.java
... ...
@@ -116,9 +116,9 @@ public abstract class CompetitorEditDialog<CompetitorType extends CompetitorDTO>
116 116
this.threeLetterIocCountryCode = createListBox(/* isMultipleSelect */ false);
117 117
DialogUtils.makeCountrySelection(this.threeLetterIocCountryCode, competitorToEdit.getThreeLetterIocCountryCode());
118 118
this.flagImageURL = new URLFieldWithFileUpload(stringMessages, "image/*");
119
- this.flagImageURL.setURL(competitorToEdit.getFlagImageURL());
119
+ this.flagImageURL.setUri(competitorToEdit.getFlagImageURL());
120 120
this.imageUrlAndUploadComposite = new URLFieldWithFileUpload(stringMessages, "image/*");
121
- this.imageUrlAndUploadComposite.setURL(competitorToEdit.getImageURL());
121
+ this.imageUrlAndUploadComposite.setUri(competitorToEdit.getImageURL());
122 122
this.yardstickLabel = new Label(stringMessages.yardstickNumber(yardstickScale));
123 123
this.yardstickNumber = createDoubleBox(competitorToEdit.getTimeOnTimeFactor() == null ? null
124 124
: convertYardstickTimeOnTime(competitorToEdit.getTimeOnTimeFactor(), yardstickScale), 10);
... ...
@@ -216,7 +216,7 @@ public abstract class CompetitorEditDialog<CompetitorType extends CompetitorDTO>
216 216
/* twoLetterIsoCountryCode */ null,
217 217
threeLetterIocCountryCode.getValue(threeLetterIocCountryCode.getSelectedIndex()),
218 218
/* countryName */ null, competitorToEdit.getIdAsString(),
219
- imageUrlAndUploadComposite.getURL(), flagImageURL.getURL(),
219
+ imageUrlAndUploadComposite.getUri(), flagImageURL.getUri(),
220 220
timeOnTimeFactor.getValue(),
221 221
timeOnDistanceAllowanceInSecondsPerNauticalMile.getValue() == null ? null :
222 222
new MillisecondsDurationImpl((long) (timeOnDistanceAllowanceInSecondsPerNauticalMile.getValue()*1000)), searchTag.getValue(), null);
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventEditDialog.java
... ...
@@ -4,6 +4,7 @@ import java.util.ArrayList;
4 4
import java.util.Collection;
5 5
import java.util.List;
6 6
7
+import com.sap.sailing.gwt.ui.client.MediaServiceAsync;
7 8
import com.sap.sailing.gwt.ui.client.SailingServiceWriteAsync;
8 9
import com.sap.sailing.gwt.ui.client.StringMessages;
9 10
import com.sap.sailing.gwt.ui.shared.EventDTO;
... ...
@@ -12,7 +13,7 @@ import com.sap.sse.gwt.client.controls.datetime.DateTimeInput.Accuracy;
12 13
13 14
public class EventEditDialog extends EventDialog {
14 15
public EventEditDialog(EventDTO event, Collection<EventDTO> otherExistingEvents, List<LeaderboardGroupDTO> availableLeaderboardGroups,
15
- SailingServiceWriteAsync sailingServiceWrite, StringMessages stringMessages, DialogCallback<EventDTO> callback) {
16
+ SailingServiceWriteAsync sailingServiceWrite, StringMessages stringMessages, DialogCallback<EventDTO> callback, MediaServiceAsync mediaService) {
16 17
super(new EventParameterValidator(stringMessages, otherExistingEvents), sailingServiceWrite, stringMessages, availableLeaderboardGroups, event.getLeaderboardGroups(), callback);
17 18
nameEntryField = createTextBox(event.getName());
18 19
nameEntryField.setVisibleLength(50);
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventListComposite.java
... ...
@@ -624,7 +624,7 @@ public class EventListComposite extends Composite {
624 624
public void ok(EventDTO updatedEvent) {
625 625
updateEvent(selectedEvent, updatedEvent);
626 626
}
627
- });
627
+ }, presenter.getMediaServiceWrite());
628 628
dialog.show();
629 629
}
630 630
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ExpeditionAllInOneImportPanel.java
... ...
@@ -37,6 +37,7 @@ import com.sap.sse.gwt.client.Notification;
37 37
import com.sap.sse.gwt.client.Notification.NotificationType;
38 38
import com.sap.sse.gwt.client.controls.busyindicator.BusyIndicator;
39 39
import com.sap.sse.gwt.client.controls.busyindicator.SimpleBusyIndicator;
40
+import com.sap.sse.gwt.client.fileupload.FileUploadUtil;
40 41
41 42
/**
42 43
* The UI form to upload data for expedition all in one import.
... ...
@@ -149,7 +150,7 @@ public class ExpeditionAllInOneImportPanel extends Composite {
149 150
formPanel.addSubmitCompleteHandler(event -> {
150 151
validation.run();
151 152
busyIndicator.setBusy(false);
152
- final ExpeditionDataImportResponse response = ExpeditionDataImportResponse.parse(event.getResults());
153
+ final ExpeditionDataImportResponse response = ExpeditionDataImportResponse.parse(FileUploadUtil.getApplicationJsonContent(event));
153 154
if (response == null) {
154 155
Notification.notify(StringMessages.INSTANCE.unexpectedErrorDuringFileImport(), NotificationType.ERROR);
155 156
} else if (response.hasEventId()) {
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ImageCreateDialog.java
... ...
@@ -11,7 +11,7 @@ import com.sap.sse.gwt.client.media.ImageResizingTaskDTO;
11 11
12 12
public class ImageCreateDialog extends ImageDialog {
13 13
14
- public ImageCreateDialog(String initialTag, SailingServiceAsync sailingService, StringMessages stringMessages, FileStorageServiceConnectionTestObservable storageServiceAvailable, DialogCallback<ImageResizingTaskDTO> dialogCallback) {
14
+ public ImageCreateDialog(String initialTag, SailingServiceAsync sailingService, StringMessages stringMessages, FileStorageServiceConnectionTestObservable storageServiceAvailable, DialogCallback<List<ImageResizingTaskDTO>> dialogCallback) {
15 15
super(new Date(), sailingService, stringMessages, storageServiceAvailable, dialogCallback);
16 16
createdAtLabel = new Label(creationDate.toString());
17 17
titleTextBox = createTextBox(null);
... ...
@@ -20,10 +20,6 @@ public class ImageCreateDialog extends ImageDialog {
20 20
subtitleTextBox.setVisibleLength(40);
21 21
copyrightTextBox = createTextBox(null);
22 22
copyrightTextBox.setVisibleLength(40);
23
- widthInPxBox = createIntegerBox(null, 10);
24
- widthInPxBox.setEnabled(false);
25
- heightInPxBox = createIntegerBox(null, 10);
26
- heightInPxBox.setEnabled(false);
27 23
List<String> tags = new ArrayList<>();
28 24
if (initialTag != null && !initialTag.isEmpty()) {
29 25
tags.add(initialTag);
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ImageDialog.java
30 26
index 6d9ebc1..36673ee
31
--- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ImageDialog.java
27
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ImageDialog.java
... ...
@@ -4,8 +4,12 @@ import java.util.ArrayList;
4 4
import java.util.Arrays;
5 5
import java.util.Collections;
6 6
import java.util.Date;
7
+import java.util.HashMap;
7 8
import java.util.List;
9
+import java.util.Map;
10
+import java.util.StringJoiner;
8 11
12
+import com.google.gwt.core.client.GWT;
9 13
import com.google.gwt.event.dom.client.ChangeEvent;
10 14
import com.google.gwt.event.dom.client.ChangeHandler;
11 15
import com.google.gwt.event.logical.shared.ValueChangeEvent;
... ...
@@ -24,12 +28,10 @@ import com.sap.sailing.gwt.ui.adminconsole.FileStorageServiceConnectionTestObser
24 28
import com.sap.sailing.gwt.ui.client.SailingService;
25 29
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
26 30
import com.sap.sailing.gwt.ui.client.StringMessages;
27
-import com.sap.sse.common.Util;
28 31
import com.sap.sse.common.Util.Pair;
29 32
import com.sap.sse.common.media.MediaTagConstants;
30 33
import com.sap.sse.gwt.adminconsole.URLFieldWithFileUpload;
31 34
import com.sap.sse.gwt.client.IconResources;
32
-import com.sap.sse.gwt.client.controls.IntegerBox;
33 35
import com.sap.sse.gwt.client.controls.busyindicator.BusyIndicator;
34 36
import com.sap.sse.gwt.client.controls.busyindicator.SimpleBusyIndicator;
35 37
import com.sap.sse.gwt.client.controls.listedit.ExpandedUiWithCheckboxes;
... ...
@@ -38,7 +40,7 @@ import com.sap.sse.gwt.client.dialog.DataEntryDialog;
38 40
import com.sap.sse.gwt.client.media.ImageDTO;
39 41
import com.sap.sse.gwt.client.media.ImageResizingTaskDTO;
40 42
41
-public abstract class ImageDialog extends DataEntryDialog<ImageResizingTaskDTO>
43
+public abstract class ImageDialog extends DataEntryDialog<List<ImageResizingTaskDTO>>
42 44
implements FileStorageServiceConnectionTestObserver {
43 45
private final SailingServiceAsync sailingService;
44 46
... ...
@@ -49,14 +51,15 @@ public abstract class ImageDialog extends DataEntryDialog<ImageResizingTaskDTO>
49 51
protected TextBox titleTextBox;
50 52
protected TextBox subtitleTextBox;
51 53
protected TextBox copyrightTextBox;
52
- protected IntegerBox widthInPxBox;
53
- protected IntegerBox heightInPxBox;
54
+ protected final VerticalPanel fileInfoVPanel;
54 55
protected final StringListInlineEditorComposite tagsListEditor;
55 56
protected Image image;
56 57
private final ExpandedUiWithCheckboxes<String> expandedUi;
57 58
private final BusyIndicator busyIndicator;
59
+ private int busyCounter;
60
+ private final HashMap<String, Pair<Integer, Integer>> imageDimensionsMap;
58 61
59
- protected static class ImageParameterValidator implements Validator<ImageResizingTaskDTO> {
62
+ protected static class ImageParameterValidator implements Validator<List<ImageResizingTaskDTO>> {
60 63
private final StringMessages stringMessages;
61 64
private List<CheckBox> doResize;
62 65
private final FileStorageServiceConnectionTestObservable storageServiceAvailable;
... ...
@@ -67,11 +70,17 @@ public abstract class ImageDialog extends DataEntryDialog<ImageResizingTaskDTO>
67 70
this.storageServiceAvailable = storageServiceAvailable;
68 71
this.doResize = new ArrayList<CheckBox>();
69 72
}
70
-
73
+
71 74
public void setCheckBoxes(List<CheckBox> doResize) {
72 75
this.doResize = doResize;
73 76
}
74 77
78
+ private enum CheckBoxStyle {
79
+ Invisible,
80
+ Normal,
81
+ Error
82
+ }
83
+
75 84
/*
76 85
* author Robin Fleige (D067799)
77 86
* will return an error message if the image is too small
... ...
@@ -80,57 +89,96 @@ public abstract class ImageDialog extends DataEntryDialog<ImageResizingTaskDTO>
80 89
* will disable the checkbox if there is no working FileStorageService
81 90
*/
82 91
@Override
83
- public String getErrorMessage(final ImageResizingTaskDTO resizingTask) {
84
- String errorMessage = null;
85
- final ImageDTO imageToValidate = resizingTask.getImage();
86
- final Integer imageWidth = imageToValidate.getWidthInPx();
87
- final Integer imageHeight = imageToValidate.getHeightInPx();
88
- if (imageToValidate.getSourceRef() == null || imageToValidate.getSourceRef().isEmpty()) {
89
- errorMessage = stringMessages.pleaseEnterNonEmptyUrlOrUploadImage();
90
- } else if (imageToValidate.getSourceRef().startsWith("http:")) {
91
- errorMessage = stringMessages.pleaseUseHttpsForImageUrls();
92
- } else if (imageWidth == null || imageHeight == null) {
93
- errorMessage = stringMessages.couldNotRetrieveImageSizeYet();
94
- } else {
95
- // check if image is too small for resizing
96
- errorMessage = "";
97
- for (MediaTagConstants mediaTag : MediaTagConstants.values()) {
98
- if (imageToValidate.hasTag(mediaTag.getName())
99
- && (imageWidth < mediaTag.getMinWidth() || imageHeight < mediaTag.getMinHeight())) {
100
- errorMessage += getImageToSmallErrorMessage(mediaTag, stringMessages) + "\n";
101
- }
102
- }
103
- if (errorMessage.equals("")) {// Check if image ratio fits for resizing
104
- errorMessage = imageRatioFits(imageToValidate);
105
- }
106
- if (errorMessage.equals("")) {// check for checkboxes and resizing
92
+ public String getErrorMessage(final List<ImageResizingTaskDTO> resizingTasks) {
93
+ StringJoiner errorJoiner = new StringJoiner("\n");
94
+ final Map<CheckBox, CheckBoxStyle> checkBoxStyleMap = new HashMap<>();
95
+ for (ImageResizingTaskDTO resizingTask : resizingTasks) {
96
+ String errorMessage = null;
97
+ final ImageDTO imageToValidate = resizingTask.getImage();
98
+ final Integer imageWidth = imageToValidate.getWidthInPx();
99
+ final Integer imageHeight = imageToValidate.getHeightInPx();
100
+ if (imageToValidate.getSourceRef() == null || imageToValidate.getSourceRef().isEmpty()) {
101
+ errorMessage = stringMessages.pleaseEnterNonEmptyUrlOrUploadImage();
102
+ } else if (imageToValidate.getSourceRef().startsWith("http:")
103
+ && !imageToValidate.getSourceRef().contains("localhost")
104
+ && !imageToValidate.getSourceRef().contains("127.0.0.1")) {
105
+ errorMessage = stringMessages.pleaseUseHttpsForImageUrls();
106
+ } else if (imageWidth == null || imageHeight == null) {
107
+ errorMessage = stringMessages.couldNotRetrieveImageSizeYet();
108
+ } else {
109
+ // check if image is too small for resizing
110
+ errorMessage = "";
107 111
for (MediaTagConstants mediaTag : MediaTagConstants.values()) {
108
- final CheckBox checkBox = getCheckBoxForTag(mediaTag.getName(), imageToValidate);
109 112
if (imageToValidate.hasTag(mediaTag.getName())
110
- && (imageWidth > mediaTag.getMaxWidth() || imageHeight > mediaTag.getMaxHeight())) {
111
- if (!resizingTask.getResizingTask().contains(mediaTag)) {
112
- errorMessage += getSizeErrorMessage(mediaTag, stringMessages) + "\n";
113
- checkBox.setStyleName(ExpandedUiWithCheckboxes.getErrorStyle());
114
- if (!errorMessage.equals("") && !storageServiceAvailable.getValue()) {
115
- checkBox.setEnabled(false);
113
+ && (imageWidth < mediaTag.getMinWidth() || imageHeight < mediaTag.getMinHeight())) {
114
+ errorMessage += getImageToSmallErrorMessage(mediaTag, stringMessages) + "\n";
115
+ }
116
+ }
117
+ if (errorMessage.equals("")) {// Check if image ratio fits for resizing
118
+ errorMessage = imageRatioFits(imageToValidate);
119
+ }
120
+ if (errorMessage.equals("")) {// check for checkboxes and resizing
121
+ for (MediaTagConstants mediaTag : MediaTagConstants.values()) {
122
+ final CheckBox checkBox = getCheckBoxForTag(mediaTag.getName(), imageToValidate);
123
+ if (imageToValidate.hasTag(mediaTag.getName())
124
+ && (imageWidth > mediaTag.getMaxWidth() || imageHeight > mediaTag.getMaxHeight())) {
125
+ // Image has tag but is not compatible
126
+ if (!resizingTask.getResizingTask().contains(mediaTag)) { // Image has tag but resizeTask does not
127
+ String fileName = ImageDialog.extractFileName(imageToValidate.getSourceRef());
128
+ errorMessage += getSizeErrorMessage(fileName, mediaTag, stringMessages) + "\r\n";
129
+ checkBoxStyleMap.put(checkBox, CheckBoxStyle.Error);
130
+ if (!errorMessage.equals("") && !storageServiceAvailable.getValue()) {
131
+ checkBox.setEnabled(false);
132
+ }
133
+ } else {
134
+ // Set checkbox to Normal if not already set to Error
135
+ checkBoxStyleMap.compute(checkBox, (k, v) -> {
136
+ if (v == CheckBoxStyle.Error) {
137
+ return CheckBoxStyle.Error;
138
+ } else {
139
+ return CheckBoxStyle.Normal;
140
+ }
141
+ });
116 142
}
117 143
} else {
118
- checkBox.setStyleName(ExpandedUiWithCheckboxes.getNormalStyle());
144
+ // Set checkbox to Invisble if not already set to Normal or Error
145
+ checkBoxStyleMap.compute(checkBox, (k, v) -> {
146
+ if (v == null || v == CheckBoxStyle.Invisible) {
147
+ return CheckBoxStyle.Invisible;
148
+ } else {
149
+ return v;
150
+ }
151
+ });
119 152
}
120
- } else {
121
- checkBox.setStyleName(ExpandedUiWithCheckboxes.getInvisibleStyle());
122
- checkBox.setValue(false);
123 153
}
124 154
}
155
+ if (!errorMessage.equals("") && !storageServiceAvailable.getValue()) {
156
+ errorMessage += stringMessages.automaticResizeNeedsStorageService() + "\n";
157
+ }
158
+ }
159
+ if (errorMessage.equals("")) {
160
+ errorMessage = null;
125 161
}
126
- if (!errorMessage.equals("") && !storageServiceAvailable.getValue()) {
127
- errorMessage += stringMessages.automaticResizeNeedsStorageService() + "\n";
162
+ if (errorMessage != null) {
163
+ errorJoiner.add(errorMessage);
128 164
}
129 165
}
130
- if (errorMessage.equals("")) {
131
- errorMessage = null;
166
+ for (Map.Entry<CheckBox, CheckBoxStyle> entry : checkBoxStyleMap.entrySet()) {
167
+ final CheckBox checkBox = entry.getKey();
168
+ switch (entry.getValue()) {
169
+ case Invisible:
170
+ checkBox.setStyleName(ExpandedUiWithCheckboxes.getInvisibleStyle());
171
+ checkBox.setValue(false);
172
+ break;
173
+ case Normal:
174
+ checkBox.setStyleName(ExpandedUiWithCheckboxes.getNormalStyle());
175
+ break;
176
+ case Error:
177
+ checkBox.setStyleName(ExpandedUiWithCheckboxes.getErrorStyle());
178
+ break;
179
+ }
132 180
}
133
- return errorMessage;
181
+ return errorJoiner.toString();
134 182
}
135 183
136 184
/**
... ...
@@ -181,8 +229,8 @@ public abstract class ImageDialog extends DataEntryDialog<ImageResizingTaskDTO>
181 229
return errorMessage;
182 230
}
183 231
184
- private String getSizeErrorMessage(MediaTagConstants mediaTag, StringMessages stringMessages) {
185
- String errorMessage = stringMessages.imageSizeError(mediaTag.getName(), mediaTag.getMinWidth(),
232
+ private String getSizeErrorMessage(String fileName, MediaTagConstants mediaTag, StringMessages stringMessages) {
233
+ String errorMessage = stringMessages.imageSizeError(fileName + " (" + mediaTag.getName() + ")", mediaTag.getMinWidth(),
186 234
mediaTag.getMaxWidth(), mediaTag.getMinHeight(), mediaTag.getMaxHeight());
187 235
return errorMessage;
188 236
}
... ...
@@ -196,47 +244,61 @@ public abstract class ImageDialog extends DataEntryDialog<ImageResizingTaskDTO>
196 244
197 245
public ImageDialog(Date creationDate, SailingServiceAsync sailingService, StringMessages stringMessages,
198 246
FileStorageServiceConnectionTestObservable storageServiceAvailable,
199
- DialogCallback<ImageResizingTaskDTO> callback) {
247
+ DialogCallback<List<ImageResizingTaskDTO>> callback) {
200 248
this(creationDate, sailingService, stringMessages, storageServiceAvailable,
201 249
new ImageParameterValidator(stringMessages, storageServiceAvailable), callback);
202 250
}
203 251
204 252
private ImageDialog(Date creationDate, SailingServiceAsync sailingService, StringMessages stringMessages,
205 253
FileStorageServiceConnectionTestObservable storageServiceAvailable, ImageParameterValidator validator,
206
- DialogCallback<ImageResizingTaskDTO> callback) {
254
+ DialogCallback<List<ImageResizingTaskDTO>> callback) {
207 255
super(stringMessages.image(), null, stringMessages.ok(), stringMessages.cancel(), validator, callback);
208 256
this.stringMessages = stringMessages;
209 257
this.sailingService = sailingService;
210 258
this.creationDate = creationDate;
259
+ fileInfoVPanel = new VerticalPanel();
211 260
getDialogBox().getWidget().setWidth("730px");
212 261
busyIndicator = new SimpleBusyIndicator();
213
- imageURLAndUploadComposite = new URLFieldWithFileUpload(stringMessages, true, true, "image/*");
214
- imageURLAndUploadComposite.addValueChangeHandler(new ValueChangeHandler<String>() {
262
+ imageURLAndUploadComposite = new URLFieldWithFileUpload(stringMessages, true, true, true, "image/*");
263
+ imageURLAndUploadComposite.addValueChangeHandler(new ValueChangeHandler<Map<String, String>>() {
215 264
@Override
216
- public void onValueChange(ValueChangeEvent<String> event) {
217
- String imageUrlAsString = event.getValue();
218
- if (imageUrlAsString == null || imageUrlAsString.isEmpty()) {
219
- widthInPxBox.setText("");
220
- heightInPxBox.setText("");
265
+ public void onValueChange(ValueChangeEvent<Map<String, String>> event) {
266
+ Map<String, String> imageUrls = event.getValue();
267
+ if (imageUrls == null || imageUrls.isEmpty()) {
268
+ fileInfoVPanel.clear();
221 269
} else {
222 270
busyIndicator.setBusy(true);
223
- ImageDialog.this.sailingService.resolveImageDimensions(imageUrlAsString,
224
- new AsyncCallback<Util.Pair<Integer, Integer>>() {
225
- @Override
226
- public void onSuccess(Pair<Integer, Integer> imageSize) {
227
- busyIndicator.setBusy(false);
228
- if (imageSize != null) {
229
- widthInPxBox.setValue(imageSize.getA());
230
- heightInPxBox.setValue(imageSize.getB());
231
- }
232
- validateAndUpdate();
233
- }
271
+ busyCounter = 0;
272
+ for (final String imageUrl : imageUrls.keySet()) {
273
+ if (!imageDimensionsMap.containsKey(imageUrl)) {
274
+ busyCounter += 1;
275
+ ImageDialog.this.sailingService.resolveImageDimensions(imageUrl,
276
+ new AsyncCallback<Pair<Integer, Integer>>() {
277
+ @Override
278
+ public void onSuccess(Pair<Integer, Integer> imageSize) {
279
+ GWT.log("resolve image dimensions - SUCCESS " + imageSize);
280
+ imageDimensionsMap.put(imageUrl, imageSize);
281
+ busyCounter -= 1;
282
+ if (busyCounter <= 0) {
283
+ busyIndicator.setBusy(false);
284
+ }
285
+ validateAndUpdate();
286
+ }
234 287
235
- @Override
236
- public void onFailure(Throwable caught) {
237
- busyIndicator.setBusy(false);
238
- }
239
- });
288
+ @Override
289
+ public void onFailure(Throwable caught) {
290
+ GWT.log("resolve image dimensions - FAILURE", caught);
291
+ busyCounter -= 1;
292
+ if (busyCounter <= 0) {
293
+ busyIndicator.setBusy(false);
294
+ }
295
+ }
296
+ });
297
+ }
298
+ }
299
+ if(busyCounter <= 0) {
300
+ busyIndicator.setBusy(false);
301
+ }
240 302
}
241 303
validateAndUpdate();
242 304
}
... ...
@@ -260,6 +322,7 @@ public abstract class ImageDialog extends DataEntryDialog<ImageResizingTaskDTO>
260 322
validateAndUpdate();
261 323
}
262 324
});
325
+ imageDimensionsMap = new HashMap<>(4);
263 326
}
264 327
265 328
/**
... ...
@@ -269,27 +332,72 @@ public abstract class ImageDialog extends DataEntryDialog<ImageResizingTaskDTO>
269 332
* For a lookout to further progressing see {@link SailingService#resizeImage(ImageResizingTaskDTO)}
270 333
*/
271 334
@Override
272
- protected ImageResizingTaskDTO getResult() {
335
+ protected List<ImageResizingTaskDTO> getResult() {
273 336
final List<String> tags = new ArrayList<String>();
274 337
for (String tag : tagsListEditor.getValue()) {
275 338
tags.add(tag);
276 339
}
277
- final List<MediaTagConstants> resizingTask = new ArrayList<MediaTagConstants>();
340
+ final List<MediaTagConstants> notCheckedMediaTags = new ArrayList<MediaTagConstants>();
341
+ for (int i = 0; i < tags.size(); i++) {
342
+ if (Arrays.asList(MediaTagConstants.values()).contains(MediaTagConstants.fromName(tags.get(i)))
343
+ && !expandedUi.getCheckBoxes().get(i).getValue()) {
344
+ notCheckedMediaTags.add(MediaTagConstants.fromName(tags.get(i)));
345
+ }
346
+ }
347
+ final List<MediaTagConstants> mediaTags = new ArrayList<MediaTagConstants>();
278 348
for (int i = 0; i < tags.size(); i++) {
279 349
if (Arrays.asList(MediaTagConstants.values()).contains(MediaTagConstants.fromName(tags.get(i)))
280 350
&& expandedUi.getCheckBoxes().get(i).getValue()) {
281
- resizingTask.add(MediaTagConstants.fromName(tags.get(i)));
351
+ mediaTags.add(MediaTagConstants.fromName(tags.get(i)));
282 352
}
283 353
}
284
- final ImageDTO image = new ImageDTO(imageURLAndUploadComposite.getURL(), creationDate);
285
- image.setTitle(titleTextBox.getValue());
286
- image.setSubtitle(subtitleTextBox.getValue());
287
- image.setCopyright(copyrightTextBox.getValue());
288
- if (widthInPxBox.getValue() != null && heightInPxBox.getValue() != null) {
289
- image.setSizeInPx(widthInPxBox.getValue(), heightInPxBox.getValue());
354
+ ArrayList<ImageResizingTaskDTO> results = new ArrayList<>(imageURLAndUploadComposite.getUris().size());
355
+ List<String> uris = imageURLAndUploadComposite.getUris();
356
+ fileInfoVPanel.clear();
357
+ for (int i = 0; i < uris.size(); i++) {
358
+ final String imageURL = uris.get(i);
359
+ final String fileName = ImageDialog.extractFileName(imageURL);
360
+ final ImageDTO image = new ImageDTO(imageURL, creationDate);
361
+ image.setTitle(titleTextBox.getValue());
362
+ image.setSubtitle(subtitleTextBox.getValue());
363
+ image.setCopyright(copyrightTextBox.getValue());
364
+ final Pair<Integer, Integer> dims = imageDimensionsMap.get(imageURL);
365
+ final Label fileInfoText;
366
+ if (dims != null) {
367
+ image.setSizeInPx(dims.getA(), dims.getB());
368
+ fileInfoText = new Label(fileName + " (" + dims.getA() + "x" + dims.getB() + ")");
369
+
370
+ } else {
371
+ fileInfoText = new Label(fileName);
372
+ }
373
+ image.setTags(tags);
374
+ final List<MediaTagConstants> resizeTags = new ArrayList<>();
375
+ for (final MediaTagConstants mediaTag : mediaTags) {
376
+ if (imageNeedsResizeForTag(image, mediaTag)) {
377
+ resizeTags.add(mediaTag);
378
+ fileInfoText.setText(fileInfoText.getText() + " - " + stringMessages.resize() + " (" + mediaTag.name() + ")");
379
+ }
380
+ }
381
+ for (final MediaTagConstants mediaTag : notCheckedMediaTags) {
382
+ if (imageNeedsResizeForTag(image, mediaTag)) {
383
+ fileInfoText.setText(fileInfoText.getText() + " - " + stringMessages.resize() + " (" + mediaTag.name() + ")");
384
+ fileInfoText.setStyleName("errorLabel");
385
+ }
386
+ }
387
+ fileInfoVPanel.add(fileInfoText);
388
+ results.add(new ImageResizingTaskDTO(image, resizeTags));
290 389
}
291
- image.setTags(tags);
292
- return new ImageResizingTaskDTO(image, resizingTask);
390
+ return results;
391
+ }
392
+
393
+ private static String extractFileName(String url) {
394
+ return url.substring(url.lastIndexOf("/") + 1);
395
+ }
396
+
397
+ private static boolean imageNeedsResizeForTag(ImageDTO image, MediaTagConstants mediaTag) {
398
+ final boolean widthExceeded = image != null && image.getWidthInPx() != null && image.getWidthInPx() > mediaTag.getMaxWidth();
399
+ final boolean heightExceeded = image != null && image.getWidthInPx() != null && image.getHeightInPx() > mediaTag.getMaxHeight();
400
+ return widthExceeded || heightExceeded;
293 401
}
294 402
295 403
@Override
... ...
@@ -300,34 +408,26 @@ public abstract class ImageDialog extends DataEntryDialog<ImageResizingTaskDTO>
300 408
if (additionalWidget != null) {
301 409
panel.add(additionalWidget);
302 410
}
303
-
304
- Grid grid = new Grid(11, 2);
305
-
411
+ Grid grid = new Grid(10, 2);
306 412
grid.setWidget(0, 0, new Label(stringMessages.createdAt() + ":"));
307 413
grid.setWidget(0, 1, createdAtLabel);
308 414
grid.setWidget(1, 0, new HTML("&nbsp;"));
309 415
grid.setWidget(1, 1, busyIndicator);
310 416
grid.setWidget(2, 0, new Label(stringMessages.imageURL() + ":"));
311 417
grid.setWidget(2, 1, imageURLAndUploadComposite);
312
- grid.setWidget(3, 0, new HTML("&nbsp;"));
313
-
314
- grid.setWidget(4, 0, new Label(stringMessages.title() + ":"));
315
- grid.setWidget(4, 1, titleTextBox);
316
- grid.setWidget(5, 0, new Label(stringMessages.subtitle() + ":"));
317
- grid.setWidget(5, 1, subtitleTextBox);
318
- grid.setWidget(6, 0, new Label(stringMessages.copyright() + ":"));
319
- grid.setWidget(6, 1, copyrightTextBox);
320
- grid.setWidget(7, 0, new Label(stringMessages.widthInPx() + ":"));
321
- grid.setWidget(7, 1, widthInPxBox);
322
- grid.setWidget(8, 0, new Label(stringMessages.heightInPx() + ":"));
323
- grid.setWidget(8, 1, heightInPxBox);
324
-
325
- grid.setWidget(9, 0, new HTML("&nbsp;"));
326
- grid.setWidget(10, 0, new Label(stringMessages.tags() + ":"));
327
- grid.setWidget(10, 1, tagsListEditor);
328
-
418
+ grid.setWidget(3, 0, new Label(stringMessages.fileUpload() + ":"));
419
+ grid.setWidget(3, 1, fileInfoVPanel);
420
+ grid.setWidget(4, 0, new HTML("&nbsp;"));
421
+ grid.setWidget(5, 0, new Label(stringMessages.title() + ":"));
422
+ grid.setWidget(5, 1, titleTextBox);
423
+ grid.setWidget(6, 0, new Label(stringMessages.subtitle() + ":"));
424
+ grid.setWidget(6, 1, subtitleTextBox);
425
+ grid.setWidget(7, 0, new Label(stringMessages.copyright() + ":"));
426
+ grid.setWidget(7, 1, copyrightTextBox);
427
+ grid.setWidget(8, 0, new HTML("&nbsp;"));
428
+ grid.setWidget(9, 0, new Label(stringMessages.tags() + ":"));
429
+ grid.setWidget(9, 1, tagsListEditor);
329 430
panel.add(grid);
330
-
331 431
return panel;
332 432
}
333 433
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ImageEditDialog.java
... ...
@@ -3,6 +3,7 @@ package com.sap.sailing.gwt.ui.adminconsole;
3 3
import java.util.ArrayList;
4 4
import java.util.List;
5 5
6
+import com.google.gwt.event.logical.shared.ValueChangeEvent;
6 7
import com.google.gwt.user.client.ui.Label;
7 8
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
8 9
import com.sap.sailing.gwt.ui.client.StringMessages;
... ...
@@ -10,22 +11,19 @@ import com.sap.sse.gwt.client.media.ImageDTO;
10 11
import com.sap.sse.gwt.client.media.ImageResizingTaskDTO;
11 12
12 13
public class ImageEditDialog extends ImageDialog {
13
- public ImageEditDialog(ImageDTO imageDTO, SailingServiceAsync sailingService, StringMessages stringMessages, FileStorageServiceConnectionTestObservable storageServiceAvailable, DialogCallback<ImageResizingTaskDTO> dialogCallback) {
14
+ public ImageEditDialog(ImageDTO imageDTO, SailingServiceAsync sailingService, StringMessages stringMessages, FileStorageServiceConnectionTestObservable storageServiceAvailable, DialogCallback<List<ImageResizingTaskDTO>> dialogCallback) {
14 15
super(imageDTO.getCreatedAtDate(), sailingService, stringMessages, storageServiceAvailable, dialogCallback);
15 16
createdAtLabel = new Label(imageDTO.getCreatedAtDate().toString());
16
- imageURLAndUploadComposite.setURL(imageDTO.getSourceRef());
17
+ imageURLAndUploadComposite.setUri(imageDTO.getSourceRef());
17 18
titleTextBox = createTextBox(imageDTO.getTitle());
18 19
titleTextBox.setVisibleLength(40);
19 20
subtitleTextBox = createTextBox(imageDTO.getSubtitle());
20 21
subtitleTextBox.setVisibleLength(40);
21 22
copyrightTextBox = createTextBox(imageDTO.getCopyright());
22 23
copyrightTextBox.setVisibleLength(40);
23
- widthInPxBox = createIntegerBox(imageDTO.getWidthInPx(), 10);
24
- widthInPxBox.setEnabled(false);
25
- heightInPxBox = createIntegerBox(imageDTO.getHeightInPx(), 10);
26
- heightInPxBox.setEnabled(false);
27 24
List<String> tags = new ArrayList<String>();
28 25
tags.addAll(imageDTO.getTags());
29 26
tagsListEditor.setValue(tags);
27
+ ValueChangeEvent.fire(imageURLAndUploadComposite, imageURLAndUploadComposite.getValue());
30 28
}
31 29
}
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ImagesListComposite.java
... ...
@@ -275,18 +275,20 @@ public class ImagesListComposite extends Composite {
275 275
276 276
private void openCreateImageDialog(String initialTag) {
277 277
ImageCreateDialog dialog = new ImageCreateDialog(initialTag, sailingServiceWrite, stringMessages,
278
- storageServiceAvailable, new DialogCallback<ImageResizingTaskDTO>() {
278
+ storageServiceAvailable, new DialogCallback<List<ImageResizingTaskDTO>>() {
279 279
@Override
280 280
public void cancel() {
281 281
}
282 282
283 283
@Override
284
- public void ok(ImageResizingTaskDTO resizingTask) {
285
- if (resizingTask.getResizingTask().size() != 0) {
286
- callResizingServiceAndUpdateTable(resizingTask, null);
287
- } else {
288
- imageListDataProvider.getList().add(resizingTask.getImage());
289
- updateTableVisibility();
284
+ public void ok(List<ImageResizingTaskDTO> resizingTasks) {
285
+ for (ImageResizingTaskDTO resizingTask : resizingTasks) {
286
+ if (resizingTask.getResizingTask().size() != 0) {
287
+ callResizingServiceAndUpdateTable(resizingTask, null);
288
+ } else {
289
+ imageListDataProvider.getList().add(resizingTask.getImage());
290
+ updateTableVisibility();
291
+ }
290 292
}
291 293
}
292 294
});
... ...
@@ -295,19 +297,21 @@ public class ImagesListComposite extends Composite {
295 297
296 298
private void openEditImageDialog(final ImageDTO selectedImage) {
297 299
ImageEditDialog dialog = new ImageEditDialog(selectedImage, sailingServiceWrite, stringMessages,
298
- storageServiceAvailable, new DialogCallback<ImageResizingTaskDTO>() {
300
+ storageServiceAvailable, new DialogCallback<List<ImageResizingTaskDTO>>() {
299 301
@Override
300 302
public void cancel() {
301 303
}
302 304
303 305
@Override
304
- public void ok(ImageResizingTaskDTO resizingTask) {
305
- if (resizingTask.getResizingTask().size() != 0) {
306
- callResizingServiceAndUpdateTable(resizingTask, selectedImage);
307
- } else {
308
- imageListDataProvider.getList().remove(selectedImage);
309
- imageListDataProvider.getList().add(resizingTask.getImage());
310
- updateTableVisibility();
306
+ public void ok(List<ImageResizingTaskDTO> resizingTasks) {
307
+ for (ImageResizingTaskDTO resizingTask : resizingTasks) {
308
+ if (resizingTask.getResizingTask().size() != 0) {
309
+ callResizingServiceAndUpdateTable(resizingTask, selectedImage);
310
+ } else {
311
+ imageListDataProvider.getList().remove(selectedImage);
312
+ imageListDataProvider.getList().add(resizingTask.getImage());
313
+ updateTableVisibility();
314
+ }
311 315
}
312 316
}
313 317
});
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaPanelSupplier.java
... ...
@@ -29,6 +29,7 @@ public class MediaPanelSupplier extends AdminConsolePanelSupplier<MediaPanel> {
29 29
@Override
30 30
public void getAsync(RunAsyncCallback callback) {
31 31
GWT.runAsync(new RunAsyncCallback() {
32
+
32 33
@Override
33 34
public void onSuccess() {
34 35
widget = init();
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SeriesEditDialog.java
... ...
@@ -198,8 +198,6 @@ public class SeriesEditDialog extends DataEntryDialog<SeriesDescriptor> {
198 198
hasCrossFleetMergedRankingCheckbox.ensureDebugId("HasCrossFleetMergedRankingCheckbox");
199 199
hasCrossFleetMergedRankingCheckbox.setValue(selectedSeries.hasCrossFleetMergedRanking());
200 200
additionalWidgetPanel.add(hasCrossFleetMergedRankingCheckbox);
201
- // TODO bug 5147: enable when cross-fleet merged ranking is really working
202
- hasCrossFleetMergedRankingCheckbox.setVisible(false);
203 201
204 202
firstColumnIsNonDiscardableCarryForwardCheckbox = createCheckbox(stringMessages.firstRaceIsNonDiscardableCarryForward());
205 203
firstColumnIsNonDiscardableCarryForwardCheckbox.ensureDebugId("StartsWithNonDiscardableCarryForwardCheckbox");
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SeriesWithFleetsCreateDialog.java
... ...
@@ -139,8 +139,6 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
139 139
140 140
hasCrossFleetMergedRankingCheckbox = createCheckbox(stringMessages.hasCrossFleetMergedRanking());
141 141
hasCrossFleetMergedRankingCheckbox.ensureDebugId("HasCrossFleetMergedRankingCheckbox");
142
- // TODO bug 5147: enable when cross-fleet merged ranking is really working
143
- hasCrossFleetMergedRankingCheckbox.setVisible(false);
144 142
145 143
maximumNumberOfDiscardsBox = createIntegerBox(null, /* visibleLength */ 3);
146 144
maximumNumberOfDiscardsBox.ensureDebugId("maximumNumberOfDiscardsBox");
... ...
@@ -208,8 +206,7 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
208 206
formGrid.setWidget(row++, 1, fleetsCanRunInParallelCheckbox);
209 207
formGrid.setWidget(row++, 1, startsWithZeroScoreCheckbox);
210 208
formGrid.setWidget(row++, 1, hasSplitFleetContiguousScoringCheckbox);
211
- // TODO bug 5147: enable when cross-fleet merged ranking is really working
212
- // formGrid.setWidget(row++, 1, hasCrossFleetMergedRankingCheckbox);
209
+ formGrid.setWidget(row++, 1, hasCrossFleetMergedRankingCheckbox);
213 210
formGrid.setWidget(row++, 1, firstColumnIsNonDiscardableCarryForwardCheckbox);
214 211
formGrid.setWidget(row++, 1, oneAlwaysStaysOneCheckbox);
215 212
formGrid.setWidget(row, 0, new Label(stringMessages.maximumNumberOfDiscards()));
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TrackFileImportWidget.java
... ...
@@ -34,5 +34,4 @@ public class TrackFileImportWidget extends AbstractFileImportWidget implements I
34 34
}
35 35
});
36 36
}
37
-
38 37
}
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/VideoCreateDialog.java
... ...
@@ -6,26 +6,23 @@ import java.util.List;
6 6
7 7
import com.google.gwt.user.client.ui.Label;
8 8
import com.sap.sailing.gwt.ui.client.StringMessages;
9
-import com.sap.sse.common.media.MimeType;
10 9
import com.sap.sse.gwt.client.media.VideoDTO;
11 10
12 11
public class VideoCreateDialog extends VideoDialog {
13 12
14
- public VideoCreateDialog(String initialTag, StringMessages stringMessages, FileStorageServiceConnectionTestObservable storageServiceAvailable, DialogCallback<VideoDTO> callback) {
15
- super(new Date(), new VideoParameterValidator(stringMessages), stringMessages, storageServiceAvailable, callback);
13
+ public VideoCreateDialog(String initialTag, StringMessages stringMessages,
14
+ FileStorageServiceConnectionTestObservable storageServiceAvailable, DialogCallback<List<VideoDTO>> callback) {
15
+ super(new Date(), new VideoParameterValidator(stringMessages), stringMessages, storageServiceAvailable,
16
+ callback);
16 17
createdAtLabel = new Label(creationDate.toString());
17
- titleTextBox = createTextBox(null);
18
- titleTextBox.setVisibleLength(50);
19 18
subtitleTextBox = createTextBox(null);
20 19
subtitleTextBox.setVisibleLength(50);
21 20
copyrightTextBox = createTextBox(null);
22 21
copyrightTextBox.setVisibleLength(50);
23
- lengthIntegerBox = createIntegerBox(null, 10);
24 22
List<String> tags = new ArrayList<>();
25
- if(initialTag != null && !initialTag.isEmpty()) {
23
+ if (initialTag != null && !initialTag.isEmpty()) {
26 24
tags.add(initialTag);
27 25
}
28 26
tagsListEditor.setValue(tags);
29
- setSelectedMimeType(MimeType.unknown);
30 27
}
31 28
}
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/VideoDialog.java
... ...
@@ -4,9 +4,16 @@ import java.util.ArrayList;
4 4
import java.util.Collections;
5 5
import java.util.Date;
6 6
import java.util.List;
7
+import java.util.Map;
8
+import java.util.Map.Entry;
9
+import java.util.StringJoiner;
7 10
11
+import com.google.gwt.core.client.GWT;
12
+import com.google.gwt.event.dom.client.ChangeEvent;
13
+import com.google.gwt.event.dom.client.ChangeHandler;
8 14
import com.google.gwt.event.logical.shared.ValueChangeEvent;
9 15
import com.google.gwt.event.logical.shared.ValueChangeHandler;
16
+import com.google.gwt.user.client.ui.FlexTable;
10 17
import com.google.gwt.user.client.ui.FocusWidget;
11 18
import com.google.gwt.user.client.ui.Grid;
12 19
import com.google.gwt.user.client.ui.Label;
... ...
@@ -22,27 +29,42 @@ import com.sap.sse.common.media.MimeType;
22 29
import com.sap.sse.gwt.adminconsole.URLFieldWithFileUpload;
23 30
import com.sap.sse.gwt.client.GWTLocaleUtil;
24 31
import com.sap.sse.gwt.client.IconResources;
25
-import com.sap.sse.gwt.client.controls.IntegerBox;
26 32
import com.sap.sse.gwt.client.controls.listedit.GenericStringListInlineEditorComposite;
27 33
import com.sap.sse.gwt.client.controls.listedit.StringListInlineEditorComposite;
28 34
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
29 35
import com.sap.sse.gwt.client.media.VideoDTO;
30 36
31
-public abstract class VideoDialog extends DataEntryDialog<VideoDTO> implements FileStorageServiceConnectionTestObserver {
37
+public abstract class VideoDialog extends DataEntryDialog<List<VideoDTO>>
38
+ implements FileStorageServiceConnectionTestObserver {
32 39
protected final StringMessages stringMessages;
33 40
protected final URLFieldWithFileUpload videoURLAndUploadComposite;
34 41
protected final Date creationDate;
35 42
protected Label createdAtLabel;
36
- protected TextBox titleTextBox;
37 43
protected TextBox subtitleTextBox;
38 44
protected TextBox copyrightTextBox;
39
- protected final ListBox mimeTypeListBox;
45
+ private Grid multiVideoGrid;
46
+ private final TextBox titleTextBox = new TextBox();
47
+ private final ListBox mimeTypeListBox = createMimeTextSelection();
40 48
protected final ListBox localeListBox;
41
- protected IntegerBox lengthIntegerBox;
42 49
protected final URLFieldWithFileUpload thumbnailURLAndUploadComposite;
43 50
protected StringListInlineEditorComposite tagsListEditor;
44
-
45
- protected static class VideoParameterValidator implements Validator<VideoDTO> {
51
+ private final List<VideoTmpData> videoTmpDatas;
52
+ private FlexTable formFlexTable;
53
+
54
+ private static class VideoTmpData {
55
+ String uri;
56
+ String fileName;
57
+ String title;
58
+ Integer lengthInSeconds;
59
+ MimeType mimeType;
60
+ @Override
61
+ public String toString() {
62
+ return "VideoTmpData [uri=" + uri + ", fileName=" + fileName + ", title=" + title + ", lengthInSeconds="
63
+ + lengthInSeconds + ", mimeType=" + mimeType + "]";
64
+ }
65
+ }
66
+
67
+ protected static class VideoParameterValidator implements Validator<List<VideoDTO>> {
46 68
private StringMessages stringMessages;
47 69
48 70
public VideoParameterValidator(StringMessages stringMessages) {
... ...
@@ -50,103 +72,224 @@ public abstract class VideoDialog extends DataEntryDialog<VideoDTO> implements F
50 72
}
51 73
52 74
@Override
53
- public String getErrorMessage(VideoDTO videoToValidate) {
54
- String errorMessage = null;
55
-
56
- if(videoToValidate.getSourceRef() == null || videoToValidate.getSourceRef().isEmpty()) {
57
- errorMessage = stringMessages.pleaseEnterNonEmptyUrl();
58
- } else if (videoToValidate.getMimeType() == null) {
59
- errorMessage = "You must select the mime type for the video.";
75
+ public String getErrorMessage(List<VideoDTO> videosToValidate) {
76
+ StringJoiner errorJoiner = new StringJoiner("\n");
77
+ for (VideoDTO videoToValidate : videosToValidate) {
78
+ String errorMessage = null;
79
+
80
+ if (videoToValidate.getSourceRef() == null || videoToValidate.getSourceRef().isEmpty()) {
81
+ errorMessage = stringMessages.pleaseEnterNonEmptyUrl();
82
+ } else if (videoToValidate.getMimeType() == null) {
83
+ errorMessage = "You must select the mime type for the video.";
84
+ }
85
+ if (errorMessage != null) {
86
+ errorJoiner.add(errorMessage);
87
+ }
60 88
}
61
- return errorMessage;
89
+ return errorJoiner.toString();
62 90
}
63 91
}
64 92
65
- public VideoDialog(Date createdAtDate, VideoParameterValidator validator, StringMessages stringMessages, FileStorageServiceConnectionTestObservable storageServiceAvailable, DialogCallback<VideoDTO> callback) {
66
- super(stringMessages.video(), null, stringMessages.ok(), stringMessages.cancel(), validator,
67
- callback);
93
+ protected void initVideoTmpDatas(String uri, String title, Integer lengthInSeconds, MimeType mimeType) {
94
+ VideoTmpData videoTmpData = new VideoTmpData();
95
+ videoTmpDatas.add(videoTmpData);
96
+ videoTmpData.uri = uri;
97
+ videoTmpData.fileName = extractFileName(uri);
98
+ videoTmpData.title = title;
99
+ videoTmpData.lengthInSeconds = lengthInSeconds;
100
+ videoTmpData.mimeType = mimeType;
101
+ renderMultiFileTable(false);
102
+ setFieldsEnabled(true);
103
+ }
104
+
105
+ private String extractFileName(String url) {
106
+ return url.substring(url.lastIndexOf("/") + 1);
107
+ }
108
+
109
+ public VideoDialog(Date createdAtDate, VideoParameterValidator validator, StringMessages stringMessages,
110
+ FileStorageServiceConnectionTestObservable storageServiceAvailable, DialogCallback<List<VideoDTO>> callback) {
111
+ super(stringMessages.video(), null, stringMessages.ok(), stringMessages.cancel(), validator, callback);
68 112
this.stringMessages = stringMessages;
69 113
this.creationDate = createdAtDate;
114
+ videoTmpDatas = new ArrayList<VideoDialog.VideoTmpData>();
70 115
getDialogBox().getWidget().setWidth("730px");
71
-
72
- mimeTypeListBox = createListBox(false);
73
- mimeTypeListBox.addItem(MimeType.unknown.name());
74
- mimeTypeListBox.addItem(MimeType.aac.name());
75
- mimeTypeListBox.addItem(MimeType.mp4.name());
76
- mimeTypeListBox.addItem(MimeType.mp4panorama.name());
77
- mimeTypeListBox.addItem(MimeType.mp4panoramaflip.name());
78
- mimeTypeListBox.addItem(MimeType.ogg.name());
79
- mimeTypeListBox.addItem(MimeType.ogv.name());
80
- mimeTypeListBox.addItem(MimeType.qt.name());
81
- mimeTypeListBox.addItem(MimeType.youtube.name());
82
- mimeTypeListBox.addItem(MimeType.vimeo.name());
83 116
localeListBox = createListBox(false);
84 117
for (String locale : GWTLocaleUtil.getAvailableLocalesAndDefault()) {
85
- localeListBox.addItem(GWTLocaleUtil.getDecoratedLanguageDisplayNameWithDefaultLocaleSupport(locale), locale == null ? "" : locale);
118
+ localeListBox.addItem(GWTLocaleUtil.getDecoratedLanguageDisplayNameWithDefaultLocaleSupport(locale),
119
+ locale == null ? "" : locale);
86 120
}
87
- videoURLAndUploadComposite = new URLFieldWithFileUpload(stringMessages, true, true, "audio/*,video/*");
88
- videoURLAndUploadComposite.addValueChangeHandler(new ValueChangeHandler<String>() {
121
+ videoURLAndUploadComposite = new URLFieldWithFileUpload(stringMessages, true, true, true, "audio/*,video/*");
122
+ videoURLAndUploadComposite.addValueChangeHandler(new ValueChangeHandler<Map<String, String>>() {
89 123
@Override
90
- public void onValueChange(ValueChangeEvent<String> event) {
124
+ public void onValueChange(ValueChangeEvent<Map<String, String>> event) {
91 125
validateAndUpdate();
126
+ videoTmpDatas.clear();
127
+ setFieldsEnabled(false);
128
+ GWT.log("value change event. " + videoURLAndUploadComposite.getValue());
129
+ formFlexTable.getRowFormatter().setVisible(5, false);
130
+ for (Entry<String, String> upload : videoURLAndUploadComposite.getValue().entrySet()) {
131
+ VideoTmpData videoTmpData = new VideoTmpData();
132
+ videoTmpDatas.add(videoTmpData);
133
+ setFieldsEnabled(true);
134
+ videoTmpData.uri = upload.getKey();
135
+ videoTmpData.fileName = upload.getValue();
136
+ videoTmpData.mimeType = MimeType.extractFromUrl(videoTmpData.uri);
137
+ final String title;
138
+ if (videoTmpData.fileName.contains(".")) {
139
+ title = videoTmpData.fileName.substring(0, videoTmpData.fileName.lastIndexOf('.'));
140
+ } else {
141
+ title = videoTmpData.fileName;
142
+ }
143
+ videoTmpData.title = title;
144
+ }
145
+ renderMultiFileTable(true);
92 146
}
93 147
});
94
- thumbnailURLAndUploadComposite = new URLFieldWithFileUpload(stringMessages, true, true, "audio/*,video/*");
148
+ thumbnailURLAndUploadComposite = new URLFieldWithFileUpload(stringMessages, false, true, true, "image/*");
95 149
tagsListEditor = new StringListInlineEditorComposite(Collections.<String> emptyList(),
96
- new GenericStringListInlineEditorComposite.ExpandedUi<String>(stringMessages, IconResources.INSTANCE.removeIcon(), /* suggestValues */
150
+ new GenericStringListInlineEditorComposite.ExpandedUi<String>(stringMessages,
151
+ IconResources.INSTANCE.removeIcon(), /* suggestValues */
97 152
MediaTagConstants.videoTagSuggestions, stringMessages.enterTagsForTheVideo(), 50));
98
- //the observer has to be registered after creating the URLFieldWithFileUpload
153
+ // the observer has to be registered after creating the URLFieldWithFileUpload
99 154
storageServiceAvailable.registerObserver(this);
100 155
}
101 156
157
+ private void renderMultiFileTable(boolean updateMediaInfo) {
158
+ GWT.log("video tmp data size = " + videoTmpDatas.size());
159
+ GWT.log("video tmp data = " + videoTmpDatas);
160
+ if (formFlexTable == null && videoTmpDatas.size() > 0) {
161
+ initFormFlexTable();
162
+ }
163
+ if (formFlexTable != null && videoTmpDatas.size() > 0) {
164
+ if (videoTmpDatas.size() == 1) {
165
+ formFlexTable.getRowFormatter().setVisible(2, true);
166
+ formFlexTable.getRowFormatter().setVisible(3, true);
167
+ formFlexTable.getRowFormatter().setVisible(4, true);
168
+ formFlexTable.getRowFormatter().setVisible(5, false);
169
+ VideoTmpData videoTmpData = videoTmpDatas.get(0);
170
+ titleTextBox.setText(videoTmpData.title);
171
+ GWT.log("video tmp data = " + videoTmpData);
172
+ titleTextBox.addChangeHandler(new ChangeHandler() {
173
+ @Override
174
+ public void onChange(ChangeEvent event) {
175
+ videoTmpData.title = titleTextBox.getValue();
176
+ }
177
+ });
178
+ mimeTypeListBox.addChangeHandler(new ChangeHandler() {
179
+ @Override
180
+ public void onChange(ChangeEvent event) {
181
+ videoTmpData.mimeType = getSelectedMimeType(mimeTypeListBox);
182
+ }
183
+ });
184
+ setSelectedMimeType(mimeTypeListBox, videoTmpData.mimeType);
185
+ } else {
186
+ formFlexTable.getRowFormatter().setVisible(2, false);
187
+ formFlexTable.getRowFormatter().setVisible(3, false);
188
+ formFlexTable.getRowFormatter().setVisible(4, false);
189
+ formFlexTable.getRowFormatter().setVisible(5, true);
190
+ Grid multiVideoGrid = new Grid(videoTmpDatas.size() + 1, 3);
191
+ multiVideoGrid.setWidth("100%");
192
+ formFlexTable.setWidget(5, 0, multiVideoGrid);
193
+ // add header
194
+ multiVideoGrid.setWidget(0, 0, new Label(stringMessages.name()));
195
+ multiVideoGrid.setWidget(0, 1, new Label(stringMessages.title()));
196
+ multiVideoGrid.setWidget(0, 2, new Label(stringMessages.mimeType()));
197
+ // process single upload items
198
+ for (int i = 0; i < videoTmpDatas.size(); i++) {
199
+ VideoTmpData videoTmpData = videoTmpDatas.get(i);
200
+ TextBox titleTextBox = new TextBox();
201
+ titleTextBox.setText(videoTmpData.title);
202
+ titleTextBox.addChangeHandler(new ChangeHandler() {
203
+ @Override
204
+ public void onChange(ChangeEvent event) {
205
+ videoTmpData.title = titleTextBox.getValue();
206
+ }
207
+ });
208
+ Label nameLabel = new Label(videoTmpData.fileName);
209
+ ListBox mimeTypeListBox = createMimeTextSelection();
210
+ mimeTypeListBox.addChangeHandler(new ChangeHandler() {
211
+ @Override
212
+ public void onChange(ChangeEvent event) {
213
+ videoTmpData.mimeType = getSelectedMimeType(mimeTypeListBox);
214
+ }
215
+ });
216
+ setSelectedMimeType(mimeTypeListBox, videoTmpData.mimeType);
217
+ multiVideoGrid.setWidget(i + 1, 0, nameLabel);
218
+ multiVideoGrid.setWidget(i + 1, 1, titleTextBox);
219
+ multiVideoGrid.setWidget(i + 1, 2, mimeTypeListBox);
220
+ }
221
+ }
222
+ }
223
+ }
224
+
102 225
@Override
103
- protected VideoDTO getResult() {
104
- VideoDTO result = new VideoDTO(videoURLAndUploadComposite.getURL(), getSelectedMimeType(), creationDate);
105
- result.setTitle(titleTextBox.getValue());
106
- result.setSubtitle(subtitleTextBox.getValue());
107
- result.setCopyright(copyrightTextBox.getValue());
108
- result.setLocale(getSelectedLocale());
109
- List<String> tags = new ArrayList<String>();
110
- for (String tag: tagsListEditor.getValue()) {
111
- tags.add(tag);
226
+ protected List<VideoDTO> getResult() {
227
+ List<VideoDTO> results = new ArrayList<VideoDTO>(videoURLAndUploadComposite.getUris().size());
228
+ for (VideoTmpData videoData : videoTmpDatas) {
229
+ VideoDTO videoDTO = new VideoDTO(videoData.uri, videoData.mimeType, creationDate);
230
+ videoDTO.setTitle(videoData.title);
231
+ videoDTO.setSubtitle(subtitleTextBox.getValue());
232
+ videoDTO.setCopyright(copyrightTextBox.getValue());
233
+ videoDTO.setLocale(getSelectedLocale());
234
+ List<String> tags = new ArrayList<String>();
235
+ for (String tag : tagsListEditor.getValue()) {
236
+ tags.add(tag);
237
+ }
238
+ videoDTO.setTags(tags);
239
+ videoDTO.setThumbnailRef(thumbnailURLAndUploadComposite.getUri());
240
+ videoDTO.setLengthInSeconds(videoData.lengthInSeconds);
241
+ results.add(videoDTO);
112 242
}
113
- result.setTags(tags);
114
- result.setThumbnailRef(thumbnailURLAndUploadComposite.getURL());
115
- return result;
243
+ return results;
116 244
}
117 245
118
- protected void setSelectedMimeType(MimeType mimeType) {
119
- for(int i = 0; i < mimeTypeListBox.getItemCount(); i++) {
120
- if(mimeTypeListBox.getItemText(i).equals(mimeType.name())) {
246
+ private ListBox createMimeTextSelection() {
247
+ ListBox mimeTypeListBox = createListBox(false);
248
+ mimeTypeListBox.addItem(MimeType.unknown.name());
249
+ mimeTypeListBox.addItem(MimeType.mp4.name());
250
+ mimeTypeListBox.addItem(MimeType.mov.name());
251
+ mimeTypeListBox.addItem(MimeType.mp4panorama.name());
252
+ mimeTypeListBox.addItem(MimeType.mp4panoramaflip.name());
253
+ mimeTypeListBox.addItem(MimeType.ogv.name());
254
+ mimeTypeListBox.addItem(MimeType.qt.name());
255
+ mimeTypeListBox.addItem(MimeType.webm.name());
256
+ mimeTypeListBox.addItem(MimeType.youtube.name());
257
+ mimeTypeListBox.addItem(MimeType.vimeo.name());
258
+ return mimeTypeListBox;
259
+ }
260
+
261
+ protected void setSelectedMimeType(ListBox mimeTypeListBox, MimeType mimeType) {
262
+ for (int i = 0; i < mimeTypeListBox.getItemCount(); i++) {
263
+ if (mimeTypeListBox.getItemText(i).equals(mimeType.name())) {
121 264
mimeTypeListBox.setSelectedIndex(i);
122 265
break;
123 266
}
124 267
}
125 268
}
126 269
127
- private MimeType getSelectedMimeType() {
270
+ private MimeType getSelectedMimeType(ListBox mimeTypeListBox) {
128 271
MimeType result = null;
129 272
int selectedIndex = mimeTypeListBox.getSelectedIndex();
130
- if(selectedIndex >= 0) {
273
+ if (selectedIndex >= 0) {
131 274
result = MimeType.valueOf(mimeTypeListBox.getSelectedValue());
132 275
}
133 276
return result;
134 277
}
135
-
278
+
136 279
protected void setSelectedLocale(String locale) {
137
- for(int i = 0; i < localeListBox.getItemCount(); i++) {
138
- if(Util.equalsWithNull(localeListBox.getValue(i), locale)) {
280
+ for (int i = 0; i < localeListBox.getItemCount(); i++) {
281
+ if (Util.equalsWithNull(localeListBox.getValue(i), locale)) {
139 282
localeListBox.setSelectedIndex(i);
140 283
return;
141 284
}
142 285
}
143 286
localeListBox.setSelectedIndex(0);
144 287
}
145
-
288
+
146 289
private String getSelectedLocale() {
147 290
return localeListBox.getSelectedValue();
148 291
}
149
-
292
+
150 293
@Override
151 294
protected Widget getAdditionalWidget() {
152 295
final VerticalPanel panel = new VerticalPanel();
... ...
@@ -155,42 +298,52 @@ public abstract class VideoDialog extends DataEntryDialog<VideoDTO> implements F
155 298
if (additionalWidget != null) {
156 299
panel.add(additionalWidget);
157 300
}
158
-
159
- Grid formGrid = new Grid(10, 2);
160
- panel.add(formGrid);
161
-
162
- formGrid.setWidget(0, 0, new Label("Created at:"));
163
- formGrid.setWidget(0, 1, createdAtLabel);
164
- formGrid.setWidget(1, 0, new Label("Video URL:"));
165
- formGrid.setWidget(1, 1, videoURLAndUploadComposite);
166
- formGrid.setWidget(2, 0, new Label("Mime Type:"));
167
- formGrid.setWidget(2, 1, mimeTypeListBox);
168
-
169
-
170
- formGrid.setWidget(3, 0, new Label(stringMessages.title() + ":"));
171
- formGrid.setWidget(3, 1, titleTextBox);
172
- formGrid.setWidget(4, 0, new Label("Subtitle:"));
173
- formGrid.setWidget(4, 1, subtitleTextBox);
174
- formGrid.setWidget(5, 0, new Label("Copyright:"));
175
- formGrid.setWidget(5, 1, copyrightTextBox);
176
- formGrid.setWidget(6, 0, new Label("Language:"));
177
- formGrid.setWidget(6, 1, localeListBox);
178
- formGrid.setWidget(7, 0, new Label("Tags:"));
179
- formGrid.setWidget(7, 1, tagsListEditor);
180
-
181
- formGrid.setWidget(8, 0, new Label("Video-Length (s):"));
182
- formGrid.setWidget(8, 1, lengthIntegerBox);
183
- formGrid.setWidget(9, 0, new Label("Thumbnail-URL:"));
184
- formGrid.setWidget(9, 1, thumbnailURLAndUploadComposite);
185
-
301
+ initFormFlexTable();
302
+ panel.add(formFlexTable);
186 303
return panel;
187 304
}
305
+
306
+ private void initFormFlexTable() {
307
+ formFlexTable = new FlexTable();
308
+ formFlexTable.setWidget(0, 0, new Label(stringMessages.createdAt() + ":"));
309
+ formFlexTable.setWidget(0, 1, createdAtLabel);
310
+ formFlexTable.setWidget(1, 0, new Label(stringMessages.videoUrl() + ":"));
311
+ formFlexTable.setWidget(1, 1, videoURLAndUploadComposite);
312
+ formFlexTable.setWidget(2, 0, new Label(stringMessages.title() + ":"));
313
+ formFlexTable.setWidget(2, 1, titleTextBox);
314
+ formFlexTable.setWidget(3, 0, new Label(stringMessages.mimeType() + ":"));
315
+ formFlexTable.setWidget(3, 1, mimeTypeListBox);
316
+ formFlexTable.setWidget(5, 0, multiVideoGrid);
317
+ formFlexTable.getFlexCellFormatter().setColSpan(5, 0, 2);
318
+ formFlexTable.getRowFormatter().setVisible(5, false);
319
+ formFlexTable.setWidget(6, 0, new Label(stringMessages.subtitle() + ":"));
320
+ formFlexTable.setWidget(6, 1, subtitleTextBox);
321
+ formFlexTable.setWidget(7, 0, new Label(stringMessages.copyright() + ":"));
322
+ formFlexTable.setWidget(7, 1, copyrightTextBox);
323
+ formFlexTable.setWidget(8, 0, new Label(stringMessages.language() + ":"));
324
+ formFlexTable.setWidget(8, 1, localeListBox);
325
+ formFlexTable.setWidget(9, 0, new Label(stringMessages.tags() + ":"));
326
+ formFlexTable.setWidget(9, 1, tagsListEditor);
327
+ formFlexTable.setWidget(10, 0, new Label(stringMessages.thumbnailUrl() + ":"));
328
+ formFlexTable.setWidget(10, 1, thumbnailURLAndUploadComposite);
329
+ setFieldsEnabled(videoTmpDatas.size() > 0);
330
+ }
331
+
332
+ private void setFieldsEnabled(boolean enabled) {
333
+ formFlexTable.getRowFormatter().setVisible(2, enabled);
334
+ formFlexTable.getRowFormatter().setVisible(3, enabled);
335
+ formFlexTable.getRowFormatter().setVisible(6, enabled);
336
+ formFlexTable.getRowFormatter().setVisible(7, enabled);
337
+ formFlexTable.getRowFormatter().setVisible(8, enabled);
338
+ formFlexTable.getRowFormatter().setVisible(9, enabled);
339
+ formFlexTable.getRowFormatter().setVisible(10, enabled);
340
+ }
188 341
189 342
@Override
190 343
protected FocusWidget getInitialFocusWidget() {
191 344
return videoURLAndUploadComposite.getInitialFocusWidget();
192 345
}
193
-
346
+
194 347
@Override
195 348
public void onFileStorageServiceTestPassed() {
196 349
videoURLAndUploadComposite.setUploadEnabled(true);
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/VideoEditDialog.java
... ...
@@ -8,22 +8,21 @@ import com.sap.sailing.gwt.ui.client.StringMessages;
8 8
import com.sap.sse.gwt.client.media.VideoDTO;
9 9
10 10
public class VideoEditDialog extends VideoDialog {
11
- public VideoEditDialog(VideoDTO video, StringMessages stringMessages, FileStorageServiceConnectionTestObservable storageServiceAvailable, DialogCallback<VideoDTO> callback) {
12
- super(video.getCreatedAtDate(), new VideoParameterValidator(stringMessages), stringMessages, storageServiceAvailable, callback);
11
+ public VideoEditDialog(VideoDTO video, StringMessages stringMessages,
12
+ FileStorageServiceConnectionTestObservable storageServiceAvailable, DialogCallback<List<VideoDTO>> callback) {
13
+ super(video.getCreatedAtDate(), new VideoParameterValidator(stringMessages), stringMessages,
14
+ storageServiceAvailable, callback);
13 15
createdAtLabel = new Label(video.getCreatedAtDate().toString());
14
- videoURLAndUploadComposite.setURL(video.getSourceRef());
15
- titleTextBox = createTextBox(video.getTitle());
16
- titleTextBox.setVisibleLength(50);
16
+ videoURLAndUploadComposite.setUri(video.getSourceRef());
17 17
subtitleTextBox = createTextBox(video.getSubtitle());
18 18
subtitleTextBox.setVisibleLength(50);
19 19
copyrightTextBox = createTextBox(video.getCopyright());
20 20
copyrightTextBox.setVisibleLength(50);
21
- lengthIntegerBox = createIntegerBox(video.getLengthInSeconds(), 10);
22 21
List<String> tags = new ArrayList<String>();
23 22
tags.addAll(video.getTags());
24 23
tagsListEditor.setValue(tags);
25
- setSelectedMimeType(video.getMimeType());
26 24
setSelectedLocale(video.getLocale());
27
- thumbnailURLAndUploadComposite.setURL(video.getThumbnailRef());
25
+ thumbnailURLAndUploadComposite.setUri(video.getThumbnailRef());
26
+ initVideoTmpDatas(video.getSourceRef(), video.getTitle(), video.getLengthInSeconds(), video.getMimeType());
28 27
}
29 28
}
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/VideosListComposite.java
... ...
@@ -249,14 +249,14 @@ public class VideosListComposite extends Composite {
249 249
250 250
private void openCreateVideoDialog(String initialTag) {
251 251
VideoCreateDialog dialog = new VideoCreateDialog(initialTag, stringMessages, storageServiceAvailable,
252
- new DialogCallback<VideoDTO>() {
252
+ new DialogCallback<List<VideoDTO>>() {
253 253
@Override
254 254
public void cancel() {
255 255
}
256 256
257 257
@Override
258
- public void ok(VideoDTO newVideo) {
259
- videoListDataProvider.getList().add(newVideo);
258
+ public void ok(List<VideoDTO> newVideos) {
259
+ videoListDataProvider.getList().addAll(newVideos);
260 260
updateTableVisisbilty();
261 261
}
262 262
});
... ...
@@ -265,15 +265,15 @@ public class VideosListComposite extends Composite {
265 265
266 266
private void openEditVideoDialog(final VideoDTO selectedVideo) {
267 267
VideoEditDialog dialog = new VideoEditDialog(selectedVideo, stringMessages, storageServiceAvailable,
268
- new DialogCallback<VideoDTO>() {
268
+ new DialogCallback<List<VideoDTO>>() {
269 269
@Override
270 270
public void cancel() {
271 271
}
272 272
273 273
@Override
274
- public void ok(VideoDTO updatedVideo) {
274
+ public void ok(List<VideoDTO> updatedVideos) {
275 275
videoListDataProvider.getList().remove(selectedVideo);
276
- videoListDataProvider.getList().add(updatedVideo);
276
+ videoListDataProvider.getList().add(updatedVideos.get(0));
277 277
updateTableVisisbilty();
278 278
}
279 279
});
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java
... ...
@@ -75,6 +75,7 @@ import com.sap.sse.gwt.client.ErrorReporter;
75 75
import com.sap.sse.gwt.client.celltable.BaseCelltable;
76 76
import com.sap.sse.gwt.client.celltable.RefreshableMultiSelectionModel;
77 77
import com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback;
78
+import com.sap.sse.gwt.client.fileupload.FileUploadUtil;
78 79
import com.sap.sse.gwt.client.panels.AbstractFilterablePanel;
79 80
import com.sap.sse.security.shared.dto.UserDTO;
80 81
import com.sap.sse.security.ui.client.UserService;
... ...
@@ -476,7 +477,7 @@ public class WindPanel extends FormPanel implements FilterablePanelProvider<Race
476 477
form.addSubmitCompleteHandler(new FormPanel.SubmitCompleteHandler() {
477 478
public void onSubmitComplete(SubmitCompleteEvent event) {
478 479
importResultPanel.clear();
479
- String windImportResultJson = event.getResults();
480
+ String windImportResultJson = FileUploadUtil.getApplicationJsonContent(event);
480 481
try {
481 482
WindImportResult windImportResult = WindImportResult.fromJson(windImportResultJson);
482 483
JsArray<RaceEntry> raceEntries = windImportResult.getRaceEntries();
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java
... ...
@@ -1121,6 +1121,12 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages,
1121 1121
String images();
1122 1122
String imagesWithCount(int number);
1123 1123
String video();
1124
+
1125
+ String videoUrl();
1126
+ String language();
1127
+ String videoLengthSeconds();
1128
+ String thumbnailUrl();
1129
+
1124 1130
String videosWithCount(int number);
1125 1131
String flagImageURL();
1126 1132
String imageURLs();
... ...
@@ -2062,6 +2068,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages,
2062 2068
String swissTimingUpdateUsername();
2063 2069
String swissTimingUpdatePassword();
2064 2070
String allowResizing();
2071
+ String resize();
2065 2072
String resizeSuccessfull();
2066 2073
String resizeUnsuccessful(String cause);
2067 2074
String automaticResizeNeedsStorageService();
... ...
@@ -2384,9 +2391,14 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages,
2384 2391
String confirmDeleteImage();
2385 2392
String confirmDeleteVideo();
2386 2393
String fileUpload();
2394
+ String uploadedFiles();
2387 2395
String or();
2388 2396
String noMediaSelected();
2389 2397
String fileTypeNotSupported();
2398
+ String fileWithDetectedMimeTypeNotSupported(String mimeType);
2399
+ String notSupportedFileTypesDetected();
2400
+ String videoAdded();
2401
+ String imageAdded();
2390 2402
String noImageOrVideoDetected();
2391 2403
String errorWhileUpdatingEvent();
2392 2404
String updateEventSuccessfully();
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties
... ...
@@ -1129,6 +1129,12 @@ image=Image
1129 1129
images=Images
1130 1130
imagesWithCount={0} image(s)
1131 1131
video=Video
1132
+
1133
+videoUrl=Video URL
1134
+language=Language
1135
+videoLengthSeconds=Video-Length (s)
1136
+thumbnailUrl=Thumbnail-URL
1137
+
1132 1138
videosWithCount={0} video(s)
1133 1139
flagImageURL=Flag image URL (image renders with 18x12px)
1134 1140
imageURLs=Image URLs
... ...
@@ -2096,6 +2102,7 @@ swissTimingUpdateURL=Update URL (e.g., for start time and course change feedback
2096 2102
swissTimingUpdateUsername=Username for sending updates
2097 2103
swissTimingUpdatePassword=Password for sending updates
2098 2104
allowResizing=Allow resizing
2105
+resize=resize
2099 2106
resizeSuccessfull=Resizing successfully finished
2100 2107
resizeUnsuccessful=Resizing not possible: {0}
2101 2108
automaticResizeNeedsStorageService=Automatic Resizing needs a working FileStorageService.
... ...
@@ -2421,9 +2428,14 @@ moreOptions=more options
2421 2428
confirmDeleteImage=Do you really want to delete the image?
2422 2429
confirmDeleteVideo=Do you really want to delete the video?
2423 2430
fileUpload=File Upload
2431
+uploadedFiles=Uploaded Files
2424 2432
or=or
2425 2433
noMediaSelected=no media selected
2426 2434
fileTypeNotSupported=File type is not supported.
2435
+fileWithDetectedMimeTypeNotSupported=File with detected mime type {0} is not supported.
2436
+notSupportedFileTypesDetected=Not all uploaded files are supported. These files were skipped.
2437
+videoAdded=Video added successfully.
2438
+imageAdded=Image added successfully.
2427 2439
noImageOrVideoDetected=No image or video detected. Nothing will be saved.
2428 2440
errorWhileUpdatingEvent=Error while updating event data.
2429 2441
updateEventSuccessfully=Updated event successfully.
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java
... ...
@@ -2,15 +2,18 @@ package com.sap.sailing.gwt.ui.client.media;
2 2
3 3
import java.util.Date;
4 4
import java.util.HashSet;
5
-import java.util.List;
5
+import java.util.Map;
6 6
import java.util.Set;
7 7
import java.util.logging.Level;
8 8
import java.util.logging.Logger;
9 9
10
+import com.google.gwt.core.client.GWT;
10 11
import com.google.gwt.dom.client.AnchorElement;
11 12
import com.google.gwt.dom.client.Document;
12 13
import com.google.gwt.dom.client.MediaElement;
13 14
import com.google.gwt.dom.client.Style.Unit;
15
+import com.google.gwt.event.dom.client.ChangeEvent;
16
+import com.google.gwt.event.dom.client.ChangeHandler;
14 17
import com.google.gwt.event.dom.client.ClickEvent;
15 18
import com.google.gwt.event.dom.client.ClickHandler;
16 19
import com.google.gwt.event.logical.shared.ValueChangeEvent;
... ...
@@ -93,7 +96,7 @@ public class NewMediaDialog extends DataEntryDialog<MediaTrack> implements FileS
93 96
} else if (media.title == null || media.title.trim().isEmpty()) {
94 97
errorMessage = stringMessages.pleaseEnterA(stringMessages.name());
95 98
} else if (media.mimeType == null) {
96
- errorMessage = stringMessages.pleaseEnterA(stringMessages.mimeType());
99
+ errorMessage = stringMessages.fileTypeNotSupported();
97 100
} else if (media.startTime == null) {
98 101
errorMessage = stringMessages.pleaseEnterA(stringMessages.startTime());
99 102
} else if (media.duration == null) {
... ...
@@ -152,18 +155,16 @@ public class NewMediaDialog extends DataEntryDialog<MediaTrack> implements FileS
152 155
this.assignedRaces = new HashSet<RegattaAndRaceIdentifier>();
153 156
this.assignedRaces.add(raceIdentifier);
154 157
this.mediaService = mediaService;
155
-
156
- urlBox = new URLFieldWithFileUpload(stringMessages, true, false, "audio/*,video/*");
157
- urlBox.addValueChangeHandler(new ValueChangeHandler<String>() {
158
+ urlBox = new URLFieldWithFileUpload(stringMessages, /* multi */ false, /* initiallyEnabled */ true, /* showUrlAfterUpload */ false, "audio/*,video/*");
159
+ urlBox.addValueChangeHandler(new ValueChangeHandler<Map<String, String>>() {
158 160
@Override
159
- public void onValueChange(ValueChangeEvent<String> event) {
161
+ public void onValueChange(ValueChangeEvent<Map<String, String>> event) {
160 162
nameBox.setValue(urlBox.getName());
161 163
mediaTrack.title = nameBox.getValue();
162 164
validateAndUpdate();
163 165
}
164 166
});
165 167
getCancelButton().addClickHandler(clickEvent-> urlBox.deleteCurrentFile());
166
-
167 168
nameBox = createTextBox(null);
168 169
nameBox.addStyleName(RESOURCES.css().nameBoxClass());
169 170
nameBox.addValueChangeHandler(new ValueChangeHandler<String>() {
... ...
@@ -232,7 +233,7 @@ public class NewMediaDialog extends DataEntryDialog<MediaTrack> implements FileS
232 233
@Override
233 234
protected void validateAndUpdate() {
234 235
super.validateAndUpdate();
235
- if (urlBox.getURL() == null || urlBox.getURL().isEmpty()) {
236
+ if (urlBox.getUri() == null || urlBox.getUri().isEmpty()) {
236 237
getOkButton().setEnabled(false);
237 238
}
238 239
}
... ...
@@ -258,10 +259,13 @@ public class NewMediaDialog extends DataEntryDialog<MediaTrack> implements FileS
258 259
}
259 260
}
260 261
}
262
+ if (mimeTypeListBox.getSelectedIndex() < 1) {
263
+ mediaTrack.mimeType = null;
264
+ }
261 265
}
262 266
263 267
protected void updateFromUrl() {
264
- String url = urlBox.getValue();
268
+ String url = urlBox.getUri();
265 269
if (url != null && !url.isEmpty()) {
266 270
boolean urlChanged = !url.equals(lastCheckedUrl);
267 271
lastCheckedUrl = url;
... ...
@@ -269,12 +273,9 @@ public class NewMediaDialog extends DataEntryDialog<MediaTrack> implements FileS
269 273
remoteMp4WasStarted = false;
270 274
remoteMp4WasFinished = false;
271 275
manuallyEditedStartTime = false;
272
-
273 276
if (mediaTrack.startTime == null) {
274 277
mediaTrack.startTime = defaultStartTime;
275 278
}
276
-
277
-
278 279
boolean isVimeoUrl = isVimeoUrl(url);
279 280
String youtubeId = YoutubeApi.getIdByUrl(url);
280 281
if (youtubeId != null) {
... ...
@@ -310,20 +311,9 @@ public class NewMediaDialog extends DataEntryDialog<MediaTrack> implements FileS
310 311
anchor.setHref(url);
311 312
//remove trailing / as well
312 313
String lastPathSegment = anchor.getPropertyString("pathname").substring(1);
313
- int dotPos = lastPathSegment.lastIndexOf('.');
314
- if (dotPos >= 0) {
315
- String fileEnding = lastPathSegment.substring(dotPos + 1).toLowerCase();
316
- List<MimeType> possibleMimeTypes = MimeType.byExtension(fileEnding);
317
- if (possibleMimeTypes.size() > 0) {
318
- mediaTrack.mimeType = possibleMimeTypes.get(0);
319
- } else {
320
- mediaTrack.mimeType = null;
321
- }
322
- if (mediaTrack.mimeType != null && MediaSubType.mp4 == mediaTrack.mimeType.getMediaSubType()) {
323
- processMp4(mediaTrack);
324
- } else {
325
- loadMediaDuration();
326
- }
314
+ MimeType mimeType = MimeType.byExtension(lastPathSegment);
315
+ if (mimeType != MimeType.unknown) {
316
+ mediaTrack.mimeType = mimeType;
327 317
} else {
328 318
mediaTrack.mimeType = null;
329 319
}
... ...
@@ -446,7 +436,7 @@ public class NewMediaDialog extends DataEntryDialog<MediaTrack> implements FileS
446 436
}
447 437
448 438
private void requestProgressPercentage(final Timer t, final Label counter) {
449
- RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, PROGRESS_STATUS_URL);
439
+ final RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, PROGRESS_STATUS_URL);
450 440
builder.setHeader(HttpRequestHeaderConstants.HEADER_FORWARD_TO_MASTER.getA(), HttpRequestHeaderConstants.HEADER_FORWARD_TO_MASTER.getB());
451 441
try {
452 442
builder.sendRequest(null, new RequestCallback() {
... ...
@@ -572,6 +562,7 @@ public class NewMediaDialog extends DataEntryDialog<MediaTrack> implements FileS
572 562
}
573 563
@Override
574 564
public void onFailure(Throwable caught) {
565
+ GWT.log("Error in backend", caught);
575 566
busyIndicator.setBusy(false);
576 567
remoteMp4WasFinished = true;
577 568
manualMimeTypeSelection(caught.getMessage(), mediaTrack, MimeType.mp4MimeTypes());
... ...
@@ -671,6 +662,14 @@ public class NewMediaDialog extends DataEntryDialog<MediaTrack> implements FileS
671 662
for (int i = 0; i < proposedMimeTypes.length; i++) {
672 663
mimeTypeListBox.addItem(proposedMimeTypes[i].name());
673 664
}
665
+ mimeTypeListBox.addChangeHandler(new ChangeHandler() {
666
+
667
+ @Override
668
+ public void onChange(ChangeEvent event) {
669
+ mediaTrack.mimeType = MimeType.byName(mimeTypeListBox.getSelectedValue());
670
+ validateAndUpdate();
671
+ }
672
+ });
674 673
fp.add(mimeTypeListBox);
675 674
infoLabel.setWidget(fp);
676 675
updateBoxByMimeType();
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/VideoJSPlayer.java
... ...
@@ -110,10 +110,6 @@ public class VideoJSPlayer extends Widget implements RequiresResize {
110 110
type = "video/vimeo";
111 111
} else if (mimeType.mediaSubType == MediaSubType.mp4) {
112 112
type = "video/mp4";
113
- } else if (mimeType == MimeType.qt) {
114
- // TODO: this is a workaround because browsers are not supporting media type video/quicktime. For most cases this should work.
115
- // Else video upload from apple devices will not be possible.
116
- type = "video/mp4";
117 113
} else if (mimeType == MimeType.mp3) {
118 114
type = "audio/mp3";
119 115
}
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/tagging/TagInputPanel.java
... ...
@@ -118,7 +118,7 @@ public class TagInputPanel extends FlowPanel {
118 118
@Override
119 119
protected TagDTO getResult() {
120 120
return new TagDTO(getTagTextBox().getValue(), getCommentTextArea().getValue(),
121
- getImageUploadPanel().getURL(), null, getVisibleForPublicCheckBox().getValue(), null, null, null);
121
+ getImageUploadPanel().getUri(), null, getVisibleForPublicCheckBox().getValue(), null, null, null);
122 122
}
123 123
124 124
@Override
... ...
@@ -228,7 +228,7 @@ public class TagInputPanel extends FlowPanel {
228 228
}
229 229
230 230
public String getImageURL() {
231
- return tagEntryFields.getImageUploadPanel().getURL();
231
+ return tagEntryFields.getImageUploadPanel().getUri();
232 232
}
233 233
234 234
protected void setTag(String tag) {
... ...
@@ -240,7 +240,7 @@ public class TagInputPanel extends FlowPanel {
240 240
}
241 241
242 242
protected void setImageURL(String imageURL) {
243
- getImageURLTextBox().setValue(imageURL);
243
+ getImageURLTextBox().setUri(imageURL);
244 244
}
245 245
246 246
protected void setVisibleForPublic(boolean visibleForPublic) {
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/AbstractMediaUploadPopup.java
... ...
@@ -1,8 +1,14 @@
1 1
package com.sap.sailing.gwt.ui.shared;
2 2
3
+import java.util.ArrayList;
4
+import java.util.Arrays;
3 5
import java.util.Collections;
4 6
import java.util.Date;
5
-import java.util.function.Consumer;
7
+import java.util.LinkedHashMap;
8
+import java.util.List;
9
+import java.util.Map;
10
+import java.util.Map.Entry;
11
+import java.util.function.BiConsumer;
6 12
import java.util.logging.Level;
7 13
import java.util.logging.Logger;
8 14
... ...
@@ -20,8 +26,6 @@ import com.google.gwt.http.client.Response;
20 26
import com.google.gwt.json.client.JSONArray;
21 27
import com.google.gwt.json.client.JSONParser;
22 28
import com.google.gwt.json.client.JSONValue;
23
-import com.google.gwt.regexp.shared.MatchResult;
24
-import com.google.gwt.regexp.shared.RegExp;
25 29
import com.google.gwt.safehtml.shared.UriUtils;
26 30
import com.google.gwt.user.client.ui.Button;
27 31
import com.google.gwt.user.client.ui.DialogBox;
... ...
@@ -42,8 +46,10 @@ import com.sap.sse.common.media.MediaType;
42 46
import com.sap.sse.common.media.MimeType;
43 47
import com.sap.sse.gwt.client.Notification;
44 48
import com.sap.sse.gwt.client.Notification.NotificationType;
49
+import com.sap.sse.gwt.client.fileupload.FileUploadUtil;
45 50
import com.sap.sse.gwt.client.media.ImageDTO;
46 51
import com.sap.sse.gwt.client.media.VideoDTO;
52
+import com.sap.sse.gwt.client.panels.HorizontalFlowPanel;
47 53
import com.sap.sse.gwt.client.shared.components.CollapsablePanel;
48 54
49 55
public abstract class AbstractMediaUploadPopup extends DialogBox {
... ...
@@ -55,42 +61,48 @@ public abstract class AbstractMediaUploadPopup extends DialogBox {
55 61
private final static String STATUS_OK = "OK";
56 62
private final static String STATUS_NOT_OK = "NOK";
57 63
private final static String EMPTY_MESSAGE = "-";
58
- private final static String YOUTUBE_REGEX = "http(?:s?):\\/\\/(?:www\\.)?youtu(?:be\\.com\\/watch\\?v=|\\.be "
59
- + "\\/)([\\w\\-\\_]*)(&(amp;)?‌​[\\w\\?‌​=]*)?";
60 64
protected final StringMessages i18n = StringMessages.INSTANCE;
61 65
protected final SharedHomeResources sharedHomeResources = SharedHomeResources.INSTANCE;
62 66
63
- private final Consumer<VideoDTO> updateVideo;
64
- private final Consumer<ImageDTO> updateImage;
67
+ private final BiConsumer<List<ImageDTO>, List<VideoDTO>> updateImagesAndVideos;
65 68
66 69
protected final FileUpload upload;
67 70
protected final Button uploadButton;
68 71
protected final FlowPanel content;
69 72
protected final TextBox fileNameInput;
70 73
protected final TextBox urlInput;
71
- protected final TextBox titleTextBox;
72
- protected final TextBox subtitleTextBox;
73
- protected final TextBox copyrightTextBox;
74
- protected final ListBox mimeTypeListBox;
74
+ protected final FlowPanel files;
75 75
private final FlowPanel fileExistingPanel;
76 76
private final Button saveButton;
77 77
private FlowPanel progressOverlay;
78
- private String uri;
78
+ private final Map<String, MediaObject> mediaObjectMap = new LinkedHashMap<>();
79
+
80
+ private static class MediaObject {
81
+ String title;
82
+ String subTitle;
83
+ String copyright;
84
+ MimeType mimeType;
85
+ }
79 86
80
- public AbstractMediaUploadPopup(Consumer<VideoDTO> updateVideo, Consumer<ImageDTO> updateImage) {
81
- this.updateVideo = updateVideo;
82
- this.updateImage = updateImage;
87
+ public AbstractMediaUploadPopup(BiConsumer<List<ImageDTO>, List<VideoDTO>> updateImagesAndVideos) {
88
+ this.updateImagesAndVideos = updateImagesAndVideos;
83 89
sharedHomeResources.sharedHomeCss().ensureInjected();
84
- addStyleName(sharedHomeResources.sharedHomeCss().popup());
85
- setTitle(i18n.upload());
86
- setText(i18n.upload());
87
- setGlassEnabled(true);
88
- setAnimationEnabled(true);
90
+ this.addStyleName(sharedHomeResources.sharedHomeCss().popup());
91
+ this.setTitle(i18n.upload());
92
+ Label headerLabel = new Label(i18n.upload());
93
+ HorizontalFlowPanel hFlow = new HorizontalFlowPanel();
94
+ hFlow.add(headerLabel);
95
+ this.setHTML(hFlow.getElement().getInnerHTML());
96
+ this.setGlassEnabled(true);
97
+ this.setAnimationEnabled(true);
98
+ this.setModal(true);
89 99
upload = new FileUpload();
90
- upload.getElement().setAttribute("accept", "image/*;capture=camera");
100
+ upload.getElement().setAttribute("accept", "image/*,video/ogg,video/mp4,video/quicktime,video/webm");
101
+ // deactivated camera first feature, because on some devices the file picker option will not be available any more.
102
+ //upload.getElement().setAttribute("capture", "camera");
103
+ upload.getElement().setAttribute("multiple", "multiple");
91 104
upload.setVisible(false);
92 105
upload.setName("file");
93
- this.setModal(false);
94 106
content = new FlowPanel();
95 107
fileNameInput = new TextBox();
96 108
urlInput = new TextBox();
... ...
@@ -144,47 +156,21 @@ public abstract class AbstractMediaUploadPopup extends DialogBox {
144 156
urlInput.addChangeHandler(new ChangeHandler() {
145 157
@Override
146 158
public void onChange(ChangeEvent event) {
147
- selectMimeTypeInBox(getMimeType(urlInput.getValue()));
159
+ files.clear();
160
+ mediaObjectMap.clear();
161
+ addUri(urlInput.getValue(), "", getMimeType(urlInput.getValue()));
148 162
checkSaveButton();
149 163
}
150 164
});
151 165
metaDataPanel.add(urlInput);
166
+ final Label uploadedFiles = new Label(i18n.uploadedFiles());
167
+ uploadedFiles.addStyleName(sharedHomeResources.sharedHomeCss().label());
168
+ metaDataPanel.add(uploadedFiles);
169
+ files = new FlowPanel();
170
+ metaDataPanel.add(files);
152 171
fileExistingPanel = new FlowPanel();
153 172
fileExistingPanel.add(new Label("-- " + i18n.noMediaSelected() + " --"));
154 173
metaDataPanel.add(fileExistingPanel);
155
- final Label detailsSubTitle = new Label(i18n.details());
156
- detailsSubTitle.addStyleName(sharedHomeResources.sharedHomeCss().subTitle());
157
- metaDataPanel.add(detailsSubTitle);
158
- final Label titleLabel = new Label(i18n.title());
159
- titleLabel.addStyleName(sharedHomeResources.sharedHomeCss().label());
160
- metaDataPanel.add(titleLabel);
161
- titleTextBox = new TextBox();
162
- titleTextBox.addStyleName(sharedHomeResources.sharedHomeCss().input());
163
- metaDataPanel.add(titleTextBox);
164
- final FlowPanel advancedContent = new FlowPanel();
165
- final CollapsablePanel collapsableAdvancedPanel = new CollapsablePanel(i18n.advanced(), true);
166
- collapsableAdvancedPanel.setContent(advancedContent);
167
- collapsableAdvancedPanel.setWidth("100%");
168
- metaDataPanel.add(collapsableAdvancedPanel);
169
- final Label subtitleLabel = new Label(i18n.subtitle());
170
- subtitleLabel.addStyleName(sharedHomeResources.sharedHomeCss().label());
171
- advancedContent.add(subtitleLabel);
172
- subtitleTextBox = new TextBox();
173
- subtitleTextBox.addStyleName(sharedHomeResources.sharedHomeCss().input());
174
- advancedContent.add(subtitleTextBox);
175
- final Label copyrightLabel = new Label(i18n.copyright());
176
- copyrightLabel.addStyleName(sharedHomeResources.sharedHomeCss().label());
177
- advancedContent.add(copyrightLabel);
178
- copyrightTextBox = new TextBox();
179
- copyrightTextBox.addStyleName(sharedHomeResources.sharedHomeCss().input());
180
- advancedContent.add(copyrightTextBox);
181
- final Label mimeTypeListLabel = new Label(i18n.mimeType());
182
- mimeTypeListLabel.addStyleName(sharedHomeResources.sharedHomeCss().label());
183
- advancedContent.add(mimeTypeListLabel);
184
- mimeTypeListBox = new ListBox();
185
- mimeTypeListBox.addStyleName(sharedHomeResources.sharedHomeCss().select());
186
- initMediaTypes();
187
- advancedContent.add(mimeTypeListBox);
188 174
final FlowPanel buttonGroup = new FlowPanel();
189 175
buttonGroup.addStyleName(sharedHomeResources.sharedHomeCss().buttonGroup());
190 176
buttonGroup.addStyleName(sharedHomeResources.sharedHomeCss().right());
... ...
@@ -192,10 +178,7 @@ public abstract class AbstractMediaUploadPopup extends DialogBox {
192 178
// Add a 'submit' button.
193 179
final Button cancelButton = new Button(i18n.cancel(), new ClickHandler() {
194 180
public void onClick(ClickEvent event) {
195
- event.stopPropagation();
196
- fileNameInput.setValue("");
197
- cleanupTempFileUpload();
198
- hide();
181
+ closePopup(event);
199 182
}
200 183
});
201 184
buttonGroup.add(cancelButton);
... ...
@@ -231,21 +214,40 @@ public abstract class AbstractMediaUploadPopup extends DialogBox {
231 214
progressSpinner.addStyleName(sharedHomeResources.sharedHomeCss().progressSpinner());
232 215
progressOverlay.add(progressSpinner);
233 216
progressOverlay.setVisible(false);
217
+ Button headerCancelButton = new Button("X", new ClickHandler() {
218
+ public void onClick(ClickEvent event) {
219
+ closePopup(event);
220
+ }
221
+ });
222
+ headerCancelButton.addStyleName(SharedHomeResources.INSTANCE.sharedHomeCss().headerButton());
223
+ content.add(headerCancelButton);
234 224
content.add(progressOverlay);
235
- add(content);
225
+ this.add(content);
226
+ }
227
+
228
+ private void closePopup(ClickEvent event) {
229
+ event.stopPropagation();
230
+ fileNameInput.setValue("");
231
+ cleanupTempFileUpload();
232
+ hide();
236 233
}
237 234
238
- private void initMediaTypes() {
235
+ abstract protected String getTitleFromFileName(String fileName);
236
+
237
+ private ListBox initMediaTypes() {
238
+ final ListBox mimeTypeListBox = new ListBox();
239 239
mimeTypeListBox.addItem(MimeType.unknown.name());
240 240
for (MimeType mimeType : MimeType.values()) {
241
- if (mimeType != MimeType.unknown) {
241
+ if (mimeType.isVideo() || mimeType.isImage()) {
242 242
mimeTypeListBox.addItem(mimeType.name());
243 243
}
244 244
}
245 245
mimeTypeListBox.setSelectedIndex(0);
246
+ mimeTypeListBox.addStyleName(sharedHomeResources.sharedHomeCss().select());
247
+ return mimeTypeListBox;
246 248
}
247 249
248
- private void selectMimeTypeInBox(MimeType mimeType) {
250
+ private void selectMimeTypeInBox(ListBox mimeTypeListBox, MimeType mimeType) {
249 251
for (int i = 0; i < mimeTypeListBox.getItemCount(); i++) {
250 252
if (mimeType.name().equals(mimeTypeListBox.getValue(i))) {
251 253
mimeTypeListBox.setSelectedIndex(i);
... ...
@@ -261,46 +263,7 @@ public abstract class AbstractMediaUploadPopup extends DialogBox {
261 263
} else {
262 264
url = urlParam.trim();
263 265
}
264
- final MimeType mimeType;
265
- if (matches(url, YOUTUBE_REGEX)) {
266
- mimeType = MimeType.youtube;
267
- } else if (isVimeoUrl(url)) {
268
- mimeType = MimeType.vimeo;
269
- } else {
270
- mimeType = detectMimeTypeFromUrl(url);
271
- }
272
- return mimeType;
273
- }
274
-
275
- private MimeType detectMimeTypeFromUrl(String url) {
276
- MimeType result = MimeType.unknown;
277
- if (url != null) {
278
- for (MimeType mimeType : MimeType.values()) {
279
- if (mimeType.endingPattern.length() > 0) {
280
- String regex = "[a-z\\-_0-9\\/\\:\\.]*\\.(" + mimeType.getEndingPattern() + ")";
281
- if (matches(url, regex)) {
282
- result = mimeType;
283
- break;
284
- }
285
- }
286
- }
287
- }
288
- return result;
289
- }
290
-
291
- private boolean matches(String matcher, String pattern) {
292
- return RegExp.compile(pattern, "i").test(matcher);
293
- }
294
-
295
- private boolean isVimeoUrl(String url) {
296
- try {
297
- RegExp urlPattern = RegExp.compile("^(.*:)//([A-Za-z0-9\\-\\.]+)(:[0-9]+)?(.*)$");
298
- MatchResult matchResult = urlPattern.exec(url);
299
- String host = matchResult.getGroup(2);
300
- return host.contains("vimeo.com");
301
- } catch (Exception e) {
302
- return false;
303
- }
266
+ return MimeType.extractFromUrl(url);
304 267
}
305 268
306 269
protected class SubmitHandler implements FormPanel.SubmitHandler {
... ...
@@ -310,15 +273,7 @@ public abstract class AbstractMediaUploadPopup extends DialogBox {
310 273
// this opportunity to perform validation.
311 274
progressOverlay.setVisible(true);
312 275
fileNameInput.setValue(i18n.loading());
313
- if (getMimeType(upload.getFilename()) == MimeType.unknown) {
314
- logger.log(Level.SEVERE, "File type is not supported.");
315
- progressOverlay.setVisible(false);
316
- fileNameInput.setValue(i18n.fileTypeNotSupported());
317
- Notification.notify(i18n.fileTypeNotSupported(), NotificationType.WARNING);
318
- event.cancel();
319
- }
320 276
}
321
-
322 277
}
323 278
324 279
private class SubmitCompleteHandler implements FormPanel.SubmitCompleteHandler {
... ...
@@ -329,28 +284,50 @@ public abstract class AbstractMediaUploadPopup extends DialogBox {
329 284
// we can get the result text here (see the FormPanel documentation for
330 285
// further explanation).
331 286
progressOverlay.setVisible(false);
332
- fileNameInput.setValue("");
333
- String result = event.getResults().trim();
334
- JSONValue resultJsonValue = parseAfterReplacingSurroundingPreElement(result);
287
+ JSONValue resultJsonValue = JSONParser.parseStrict(FileUploadUtil.getApplicationJsonContent(event));
335 288
JSONArray resultJson = resultJsonValue.isArray();
289
+ boolean uploadSuccessful = false;
290
+ boolean fileSkipped = false;
336 291
if (resultJson != null) {
337
- if (resultJson.get(0).isObject().get(FileUploadConstants.FILE_URI) != null) {
338
- cleanFormElements();
339
- String uri = resultJson.get(0).isObject().get(FileUploadConstants.FILE_URI).isString()
340
- .stringValue();
341
- String fileName = resultJson.get(0).isObject().get(FileUploadConstants.FILE_NAME).isString()
342
- .stringValue();
343
- updateUri(uri, fileName);
344
- fileNameInput.setValue(i18n.uploadSuccessful());
345
- } else {
346
- String status = resultJson.get(0).isObject().get(FileUploadConstants.STATUS).isString()
347
- .stringValue();
348
- String message = resultJson.get(0).isObject().get(FileUploadConstants.MESSAGE).isString()
349
- .stringValue();
350
- Notification.notify(i18n.fileUploadResult(status, message), NotificationType.ERROR);
351
- logger.log(Level.SEVERE, "Submit file failed. Status: " + status + ", message: " + message);
292
+ for (int i=0; i<resultJson.size(); i++) {
293
+ if (resultJson.get(i).isObject().get(FileUploadConstants.FILE_URI) != null) {
294
+ cleanFormElements();
295
+ String uri = resultJson.get(i).isObject().get(FileUploadConstants.FILE_URI).isString()
296
+ .stringValue();
297
+ String fileNameUnderscoreEncoded = resultJson.get(i).isObject().get(FileUploadConstants.FILE_NAME).isString()
298
+ .stringValue();
299
+ // special handling of double underscore in JSON. Double underscores were encoded with hex representation.
300
+ // In some cases the JSON parser of Apples Safari on mobile devices cannot parse JSON with __. See also bug5127
301
+ String fileName = fileNameUnderscoreEncoded.replace("%5f%5f", "__");
302
+ String contentType = resultJson.get(i).isObject().get(FileUploadConstants.CONTENT_TYPE).isString()
303
+ .stringValue();
304
+ MimeType mimeType = MimeType.byContentType(contentType);
305
+ if (!fileName.isEmpty()) {
306
+ if (mimeType == MimeType.unknown) {
307
+ fileSkipped = true;
308
+ logger.log(Level.WARNING, "An unsupported file (" + fileName + " - " + contentType + ") detected. File has been skipped.");
309
+ } else {
310
+ uploadSuccessful = true;
311
+ addUri(uri, fileName, mimeType);
312
+ }
313
+ }
314
+ } else {
315
+ String status = resultJson.get(i).isObject().get(FileUploadConstants.STATUS).isString()
316
+ .stringValue();
317
+ String message = resultJson.get(i).isObject().get(FileUploadConstants.MESSAGE).isString()
318
+ .stringValue();
319
+ Notification.notify(i18n.fileUploadResult(status, message), NotificationType.ERROR);
320
+ logger.log(Level.SEVERE, "Submit file failed. Status: " + status + ", message: " + message);
321
+ }
352 322
}
353 323
}
324
+ String resultMessage = "";
325
+ if (fileSkipped) {
326
+ resultMessage = i18n.notSupportedFileTypesDetected();
327
+ } else if (uploadSuccessful) {
328
+ resultMessage = i18n.uploadSuccessful();
329
+ }
330
+ fileNameInput.setValue(resultMessage);
354 331
}
355 332
356 333
}
... ...
@@ -362,7 +339,7 @@ public abstract class AbstractMediaUploadPopup extends DialogBox {
362 339
@Override
363 340
public void show() {
364 341
// reset all fields
365
- updateUri(null, null);
342
+ resetInput();
366 343
cleanFormElements();
367 344
super.show();
368 345
}
... ...
@@ -370,10 +347,6 @@ public abstract class AbstractMediaUploadPopup extends DialogBox {
370 347
private void cleanFormElements() {
371 348
fileNameInput.setValue("");
372 349
urlInput.setValue("");
373
- titleTextBox.setValue("");
374
- subtitleTextBox.setValue("");
375
- copyrightTextBox.setValue("");
376
- mimeTypeListBox.setSelectedIndex(0);
377 350
// progressOverlay.setVisible(false);
378 351
}
379 352
... ...
@@ -381,70 +354,77 @@ public abstract class AbstractMediaUploadPopup extends DialogBox {
381 354
* Finally add a new MediaTrack.
382 355
*/
383 356
private void addMedia() {
384
-
385
- final String uploadUrl;
386
- if (uri == null) {
387
- uploadUrl = "";
388
- } else if (!UriUtils.isSafeUri(uri.trim())) {
389
- logger.warning("Upload url is not valid: " + uri + ". Ignore upload url.");
390
- uploadUrl = "";
391
- } else {
392
- uploadUrl = uri.trim();
393
- }
394
- final String inputUrl;
395
- if (urlInput.getValue() == null) {
396
- inputUrl = "";
397
- } else if (!UriUtils.isSafeUri(urlInput.getValue())) {
398
- logger.warning("Upload url is not valid: " + uri + ". Ignore upload url.");
399
- inputUrl = "";
400
- } else {
401
- inputUrl = urlInput.getValue();
402
- }
403
- final String url;
404
- if (uploadUrl.isEmpty()) {
405
- url = inputUrl;
406
- } else {
407
- url = uploadUrl;
408
- }
409
-
410
- if (!url.isEmpty()) {
411
- final String mimeTypeName = mimeTypeListBox.getSelectedValue();
412
- final MimeType mimeType;
413
- if (mimeTypeName != null) {
414
- mimeType = MimeType.byName(mimeTypeListBox.getSelectedValue());
357
+ List<ImageDTO> imageList = new ArrayList<>();
358
+ List<VideoDTO> videoList = new ArrayList<>();
359
+
360
+ for (Entry<String, MediaObject> mediaObjectEntry: mediaObjectMap.entrySet()) {
361
+ String uri = mediaObjectEntry.getKey();
362
+ final String uploadUrl;
363
+ if (uri == null) {
364
+ uploadUrl = "";
365
+ } else if (!UriUtils.isSafeUri(uri.trim())) {
366
+ logger.warning("Upload url is not valid: " + uri + ". Ignore upload url.");
367
+ uploadUrl = "";
415 368
} else {
416
- mimeType = MimeType.unknown;
369
+ uploadUrl = uri.trim();
417 370
}
418
- hide();
419
- if (mimeType.mediaType == MediaType.image) {
420
- updateImage.accept(createImage(url));
421
- } else if (mimeType.mediaType == MediaType.video || mimeType.mediaType == MediaType.audio) {
422
- updateVideo.accept(createVideo(url, null, mimeType));
371
+ final String inputUrl;
372
+ if (urlInput.getValue() == null) {
373
+ inputUrl = "";
374
+ } else if (!UriUtils.isSafeUri(urlInput.getValue())) {
375
+ logger.warning("Upload url is not valid: " + uri + ". Ignore upload url.");
376
+ inputUrl = "";
423 377
} else {
424
- logger.warning("No image nor video detected. Nothing will be saved.");
425
- Notification.notify(i18n.noImageOrVideoDetected(), NotificationType.WARNING);
378
+ inputUrl = urlInput.getValue();
426 379
}
427
-
380
+ final String url;
381
+ if (uploadUrl.isEmpty()) {
382
+ url = inputUrl;
383
+ } else {
384
+ url = uploadUrl;
385
+ }
386
+ if (!url.isEmpty()) {
387
+ final MimeType mimeType = mediaObjectEntry.getValue().mimeType;
388
+ hide();
389
+ if (mimeType.mediaType == MediaType.image) {
390
+ imageList.add(createImage(url));
391
+ Notification.notify(i18n.imageAdded(), NotificationType.SUCCESS);
392
+ } else if (mimeType.mediaType == MediaType.video) {
393
+ videoList.add(createVideo(url, null, mimeType));
394
+ Notification.notify(i18n.videoAdded(), NotificationType.SUCCESS);
395
+ } else {
396
+ logger.warning("Detected MimeType is not of type video or image. File will be skipped. Found MimeType: " + mimeType);
397
+ Notification.notify(i18n.fileWithDetectedMimeTypeNotSupported(mimeType.toString()), NotificationType.WARNING);
398
+ }
399
+ } else {
400
+ Notification.notify(i18n.invalidURL(), NotificationType.ERROR);
401
+ }
402
+ }
403
+ if (!imageList.isEmpty() || !videoList.isEmpty()) {
404
+ updateImagesAndVideos.accept(imageList, videoList);
428 405
} else {
429
- Notification.notify(i18n.invalidURL(), NotificationType.ERROR);
406
+ logger.warning("No image nor video detected. Nothing will be saved.");
407
+ Notification.notify(i18n.noImageOrVideoDetected(), NotificationType.WARNING);
430 408
}
431 409
}
432 410
433 411
private ImageDTO createImage(String url) {
412
+ MediaObject mediaObject = mediaObjectMap.get(url);
434 413
final ImageDTO image = new ImageDTO(url, new Date());
435
- image.setTitle(titleTextBox.getValue());
436
- image.setSubtitle(subtitleTextBox.getValue());
437
- image.setCopyright(copyrightTextBox.getValue());
414
+ image.setTitle(mediaObject.title);
415
+ image.setSubtitle(mediaObject.subTitle);
416
+ image.setCopyright(mediaObject.copyright);
438 417
Iterable<String> defaultTags = Collections.singletonList(MediaTagConstants.GALLERY.getName());
439 418
image.setTags(defaultTags);
440 419
return image;
441 420
}
442 421
443 422
private VideoDTO createVideo(String url, String thumbnailUrl, MimeType mimeType) {
423
+ MediaObject mediaObject = mediaObjectMap.get(url);
444 424
final VideoDTO video = new VideoDTO(url, mimeType, new Date());
445
- video.setTitle(titleTextBox.getValue());
446
- video.setSubtitle(subtitleTextBox.getValue());
447
- video.setCopyright(copyrightTextBox.getValue());
425
+ video.setTitle(mediaObject.title);
426
+ video.setSubtitle(mediaObject.subTitle);
427
+ video.setCopyright(mediaObject.copyright);
448 428
video.setThumbnailRef(thumbnailUrl);
449 429
Iterable<String> defaultTags = Collections.singletonList(MediaTagConstants.GALLERY.getName());
450 430
video.setTags(defaultTags);
... ...
@@ -452,93 +432,174 @@ public abstract class AbstractMediaUploadPopup extends DialogBox {
452 432
}
453 433
454 434
private void cleanupTempFileUpload() {
455
- if (uri != null) {
456
- String url = DELETE_URL + uri;
457
- RequestBuilder requestBuilder = new RequestBuilder(RequestBuilder.DELETE, url);
458
- requestBuilder.setCallback(new RequestCallback() {
459
-
460
- @Override
461
- public void onResponseReceived(Request request, Response response) {
462
- String status = STATUS_NOT_OK;
463
- String message = EMPTY_MESSAGE;
464
- JSONValue jsonValue = parseAfterReplacingSurroundingPreElement(response.getText());
465
- JSONArray resultJson = jsonValue.isArray();
466
- if (resultJson != null) {
467
- if (resultJson.get(0).isObject().get(FileUploadConstants.STATUS) != null) {
468
- status = resultJson.get(0).isObject().get(FileUploadConstants.STATUS).isString()
469
- .stringValue();
470
- if (!STATUS_OK.equals(status)) {
471
- message = resultJson.get(0).isObject().get(FileUploadConstants.MESSAGE).isString()
435
+ for (String uri: mediaObjectMap.keySet()) {
436
+ MediaObject mediaObject = mediaObjectMap.get(uri);
437
+ if (uri != null
438
+ && mediaObject != null
439
+ && !Arrays.asList(MimeType.unknown, MimeType.youtube, MimeType.vimeo).contains(mediaObject.mimeType)) {
440
+ String url = DELETE_URL + uri;
441
+ RequestBuilder requestBuilder = new RequestBuilder(RequestBuilder.DELETE, url);
442
+ requestBuilder.setCallback(new RequestCallback() {
443
+ @Override
444
+ public void onResponseReceived(Request request, Response response) {
445
+ String status = STATUS_NOT_OK;
446
+ String message = EMPTY_MESSAGE;
447
+ JSONValue jsonValue = JSONParser.parseStrict(response.getText());
448
+ JSONArray resultJson = jsonValue.isArray();
449
+ if (resultJson != null) {
450
+ if (resultJson.get(0).isObject().get(FileUploadConstants.STATUS) != null) {
451
+ status = resultJson.get(0).isObject().get(FileUploadConstants.STATUS).isString()
472 452
.stringValue();
453
+ if (!STATUS_OK.equals(status)) {
454
+ message = resultJson.get(0).isObject().get(FileUploadConstants.MESSAGE).isString()
455
+ .stringValue();
456
+ }
473 457
}
458
+ } else {
459
+ status = jsonValue.isObject().get(FileUploadConstants.STATUS).isString().stringValue();
460
+ }
461
+ if (STATUS_OK.equals(status)) {
462
+ logger.log(Level.INFO, "Cleanup file successful. URL: " + uri);
463
+ } else if (EMPTY_MESSAGE.equals(message)) {
464
+ // No further message from service. Probably the file is not existing any more.
465
+ Notification.notify(i18n.error(), NotificationType.ERROR);
466
+ logger.log(Level.SEVERE,
467
+ "Cleanup file failed. " + status + ": File with URI could not be removed. URI: " + uri);
468
+ } else {
469
+ Notification.notify(i18n.fileUploadResult(status, message), NotificationType.ERROR);
470
+ logger.log(Level.SEVERE, "Cleanup file failed. Status: " + status + ", message: " + message);
474 471
}
475
- } else {
476
- status = jsonValue.isObject().get(FileUploadConstants.STATUS).isString().stringValue();
477 472
}
478
- if (STATUS_OK.equals(status)) {
479
- logger.log(Level.INFO, "Cleanup file successful. URL: " + uri);
480
- } else if (EMPTY_MESSAGE.equals(message)) {
481
- // No further message from service. Probably the file is not existing any more.
473
+
474
+ @Override
475
+ public void onError(Request request, Throwable exception) {
482 476
Notification.notify(i18n.error(), NotificationType.ERROR);
483 477
logger.log(Level.SEVERE,
484
- "Cleanup file failed. " + status + ": File with URI could not be removed. URI: " + uri);
485
- } else {
486
- Notification.notify(i18n.fileUploadResult(status, message), NotificationType.ERROR);
487
- logger.log(Level.SEVERE, "Cleanup file failed. Status: " + status + ", message: " + message);
478
+ "Cleanup file failed. Callback returned with error: " + exception.getMessage(), exception);
488 479
}
489
- }
490
-
491
- @Override
492
- public void onError(Request request, Throwable exception) {
480
+ });
481
+ try {
482
+ resetInput();
483
+ cleanFormElements();
484
+ requestBuilder.send();
485
+ } catch (RequestException e) {
493 486
Notification.notify(i18n.error(), NotificationType.ERROR);
494
- logger.log(Level.SEVERE,
495
- "Cleanup file failed. Callback returned with error: " + exception.getMessage(), exception);
487
+ logger.log(Level.SEVERE, "Cleanup file failed. Sending caused an error: " + e.getMessage(), e);
496 488
}
497
- });
498
- try {
499
- updateUri(null, "");
500
- cleanFormElements();
501
- requestBuilder.send();
502
- } catch (RequestException e) {
503
- Notification.notify(i18n.error(), NotificationType.ERROR);
504
- logger.log(Level.SEVERE, "Cleanup file failed. Sending caused an error: " + e.getMessage(), e);
505 489
}
506 490
}
491
+ mediaObjectMap.clear();
507 492
}
508 493
509
- /**
510
- * See https://github.com/twilson63/ngUpload/issues/43 and
511
- * https://www.sencha.com/forum/showthread.php?132949-Fileupload-Invalid-JSON-string. The JSON response of the file
512
- * upload is wrapped by a &lt;pre&gt; element which needs to be stripped off if present to allow the JSON parser to
513
- * succeed.
514
- */
515
- private JSONValue parseAfterReplacingSurroundingPreElement(String jsonString) {
516
- return JSONParser.parseStrict(jsonString.replaceFirst("<pre[^>]*>(.*)</pre>", "$1"));
494
+ private void resetInput() {
495
+ fileExistingPanel.setVisible(true);
496
+ files.clear();
497
+ saveButton.setEnabled(false);
498
+ // fileNameInput.setEnabled(false);
499
+ urlInput.setEnabled(true);
517 500
}
518
-
519
- private void updateUri(String uri, String fileName) {
520
- this.uri = uri;
521
- if (uri == null) {
522
- fileExistingPanel.setVisible(true);
523
- saveButton.setEnabled(false);
524
- // fileNameInput.setEnabled(false);
525
- urlInput.setEnabled(true);
501
+
502
+ private void setCollapsebleFilePanelHeader(final CollapsablePanel collapsebleFilePanel, final String fileName, final MimeType mimeType) {
503
+ String header;
504
+ if (fileName != null && !fileName.isEmpty() && mimeType != null) {
505
+ header = fileName + " (" + mimeType.name() + ")";
506
+ } else if (mimeType != null) {
507
+ header = mimeType.name();
526 508
} else {
527
- fileExistingPanel.setVisible(false);
528
- saveButton.setEnabled(true);
529
- // fileNameInput.setEnabled(true);
530
- urlInput.setEnabled(false);
531
- selectMimeTypeInBox(getMimeType(uri));
509
+ header = "n/a";
532 510
}
533
- updateFileName(fileName);
534
- checkSaveButton();
511
+ collapsebleFilePanel.getHeaderTextAccessor().setText(header);
535 512
}
536 513
537
- abstract protected void updateFileName(String fileName);
514
+ private void addUri(String uri, String fileName, MimeType mimeType) {
515
+ MediaObject mediaObject = new MediaObject();
516
+ mediaObjectMap.put(uri, mediaObject);
517
+ mediaObject.mimeType = mimeType;
518
+ final String title = getTitleFromFileName(fileName);
519
+ mediaObject.title = title;
520
+ final VerticalPanel vPanel = new VerticalPanel();
521
+ boolean firstCollapsible = true;
522
+ for (int i=0; i < files.getWidgetCount(); i++) {
523
+ if (files.getWidget(i) instanceof CollapsablePanel) {
524
+ firstCollapsible = false;
525
+ ((CollapsablePanel)files.getWidget(i)).setCollapsingEnabled(true);
526
+ break;
527
+ }
528
+ }
529
+ final CollapsablePanel collapsebleFilePanel = new CollapsablePanel(fileName, true);
530
+ setCollapsebleFilePanelHeader(collapsebleFilePanel, title, mimeType);
531
+ collapsebleFilePanel.setContent(vPanel);
532
+ collapsebleFilePanel.setWidth("100%");
533
+ if (firstCollapsible) {
534
+ collapsebleFilePanel.setCollapsingEnabled(false);
535
+ collapsebleFilePanel.setOpen(true);
536
+ }
537
+ final Label fileNameLabel = new Label(fileName);
538
+ fileNameLabel.addStyleName(sharedHomeResources.sharedHomeCss().subTitle());
539
+ //vPanel.add(fileNameLabel);
540
+ final Label titleLabel = new Label(i18n.title());
541
+ titleLabel.addStyleName(sharedHomeResources.sharedHomeCss().label());
542
+ vPanel.add(titleLabel);
543
+ TextBox titleTextBox = new TextBox();
544
+ titleTextBox.addStyleName(sharedHomeResources.sharedHomeCss().input());
545
+ titleTextBox.setText(title);
546
+ titleTextBox.addChangeHandler(new ChangeHandler() {
547
+ @Override
548
+ public void onChange(ChangeEvent event) {
549
+ mediaObject.title = titleTextBox.getValue();
550
+ setCollapsebleFilePanelHeader(collapsebleFilePanel, mediaObject.title, mediaObject.mimeType);
551
+ }
552
+ });
553
+ vPanel.add(titleTextBox);
554
+ final Label subtitleLabel = new Label(i18n.subtitle());
555
+ subtitleLabel.addStyleName(sharedHomeResources.sharedHomeCss().label());
556
+ vPanel.add(subtitleLabel);
557
+ TextBox subtitleTextBox = new TextBox();
558
+ subtitleTextBox.addChangeHandler(new ChangeHandler() {
559
+ @Override
560
+ public void onChange(ChangeEvent event) {
561
+ mediaObject.subTitle = subtitleTextBox.getValue();
562
+ }
563
+ });
564
+ subtitleTextBox.addStyleName(sharedHomeResources.sharedHomeCss().input());
565
+ vPanel.add(subtitleTextBox);
566
+ final Label copyrightLabel = new Label(i18n.copyright());
567
+ copyrightLabel.addStyleName(sharedHomeResources.sharedHomeCss().label());
568
+ vPanel.add(copyrightLabel);
569
+ TextBox copyrightTextBox = new TextBox();
570
+ copyrightTextBox.addChangeHandler(new ChangeHandler() {
571
+ @Override
572
+ public void onChange(ChangeEvent event) {
573
+ mediaObject.copyright = copyrightTextBox.getValue();
574
+ }
575
+ });
576
+ copyrightTextBox.addStyleName(sharedHomeResources.sharedHomeCss().input());
577
+ vPanel.add(copyrightTextBox);
578
+ final Label mimeTypeListLabel = new Label(i18n.mimeType());
579
+ mimeTypeListLabel.addStyleName(sharedHomeResources.sharedHomeCss().label());
580
+ vPanel.add(mimeTypeListLabel);
581
+ final ListBox mediaTypeListBox = initMediaTypes();
582
+ mediaTypeListBox.addChangeHandler(new ChangeHandler() {
583
+ @Override
584
+ public void onChange(ChangeEvent event) {
585
+ final MimeType mimeType = MimeType.byName(mediaTypeListBox.getSelectedItemText());
586
+ mediaObject.mimeType = mimeType;
587
+ setCollapsebleFilePanelHeader(collapsebleFilePanel, mediaObject.title, mediaObject.mimeType);
588
+ }
589
+ });
590
+ selectMimeTypeInBox(mediaTypeListBox, getMimeType(uri));
591
+ vPanel.add(mediaTypeListBox);
592
+ files.add(collapsebleFilePanel);
593
+ fileExistingPanel.setVisible(false);
594
+ saveButton.setEnabled(true);
595
+ // fileNameInput.setEnabled(true);
596
+ urlInput.setEnabled(mimeType == MimeType.vimeo || mimeType == MimeType.youtube || mimeType == MimeType.unknown);
597
+ checkSaveButton();
598
+ }
538 599
539 600
private void checkSaveButton() {
540 601
boolean urlInputNotEmpty = urlInput.getValue() != null && !urlInput.getValue().trim().isEmpty();
541
- boolean uriNotEmpty = uri != null && !uri.trim().isEmpty();
602
+ boolean uriNotEmpty = !mediaObjectMap.isEmpty();
542 603
saveButton.setEnabled(urlInputNotEmpty || uriNotEmpty);
543 604
}
544 605
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManageMediaModel.java
... ...
@@ -3,6 +3,7 @@ package com.sap.sailing.gwt.ui.shared;
3 3
import java.util.Collection;
4 4
import java.util.Comparator;
5 5
import java.util.LinkedHashSet;
6
+import java.util.List;
6 7
import java.util.function.Consumer;
7 8
import java.util.logging.Level;
8 9
import java.util.logging.Logger;
... ...
@@ -110,22 +111,40 @@ public class ManageMediaModel {
110 111
});
111 112
}
112 113
113
- public void addImage(ImageDTO image, Consumer<EventDTO> callback) {
114
+ public void addImages(List<ImageDTO> imageList, Consumer<EventDTO> callback) {
114 115
loadEventData(eventDto -> {
115
- eventDto.getImages().add(image);
116
+ for (ImageDTO image: imageList) {
117
+ eventDto.getImages().add(image);
118
+ mediaDto.addPhoto(new SailingImageDTO(null, image));
119
+ }
116 120
updateEventDto(eventDto, callback);
117
- mediaDto.addPhoto(new SailingImageDTO(null, image));
118 121
});
119 122
}
120 123
121
- public void addVideo(VideoDTO video, Consumer<EventDTO> callback) {
124
+ public void addVideos(List<VideoDTO> videoList, Consumer<EventDTO> callback) {
122 125
loadEventData(eventDto -> {
123
- eventDto.getVideos().add(video);
126
+ for (VideoDTO video: videoList) {
127
+ eventDto.getVideos().add(video);
128
+ mediaDto.addVideo(new SailingVideoDTO(null, video));
129
+ }
124 130
updateEventDto(eventDto, callback);
125
- mediaDto.addVideo(new SailingVideoDTO(null, video));
126 131
});
127 132
}
128
-
133
+
134
+ public void addImagesAndVideos(List<ImageDTO> imageList, List<VideoDTO> videoList, Consumer<EventDTO> callback) {
135
+ loadEventData(eventDto -> {
136
+ for (ImageDTO image: imageList) {
137
+ eventDto.getImages().add(image);
138
+ mediaDto.addPhoto(new SailingImageDTO(null, image));
139
+ }
140
+ for (VideoDTO video: videoList) {
141
+ eventDto.getVideos().add(video);
142
+ mediaDto.addVideo(new SailingVideoDTO(null, video));
143
+ }
144
+ updateEventDto(eventDto, callback);
145
+ });
146
+ }
147
+
129 148
public void reloadMedia(Consumer<EventDTO> callback) {
130 149
loadEventData(eventDto -> {
131 150
setVideos(eventDto.getVideos());
... ...
@@ -156,7 +175,6 @@ public class ManageMediaModel {
156 175
public void onSuccess(EventDTO eventDto) {
157 176
setEventDto(eventDto);
158 177
callback.accept(eventDto);
159
- Notification.notify(i18n.updateEventSuccessfully(), NotificationType.SUCCESS);
160 178
}
161 179
162 180
@Override
... ...
@@ -169,7 +187,7 @@ public class ManageMediaModel {
169 187
}
170 188
171 189
/**
172
- * Check media update permission on default object (eventViewDTO from init).
190
+ * Check permission on default object (eventViewDTO from init).
173 191
*/
174 192
public boolean hasPermissions() {
175 193
final boolean hasPermission;
java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/DataMining.gwt.xml
... ...
@@ -32,7 +32,7 @@
32 32
<inherits name="com.sap.sailing.ExpeditionConnectorCommon" />
33 33
34 34
<!-- module containing locale configuration -->
35
- <inherits name='com.sap.sailing.gwt.common.SailingLocalesAllPermutations' />
35
+ <inherits name='com.sap.sailing.gwt.common.SailingLocalesSinglePermutation' />
36 36
37 37
<!-- Disable logging -->
38 38
<set-property name="gwt.logging.enabled" value="FALSE"/>
java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/EmbeddedMapAndWindChart.gwt.xml
... ...
@@ -23,7 +23,7 @@
23 23
<entry-point class='com.sap.sailing.gwt.ui.raceboard.EmbeddedMapAndWindChartEntryPoint'/>
24 24
25 25
<!-- module containing locale configuration -->
26
- <inherits name='com.sap.sailing.gwt.common.SailingLocalesAllPermutations' />
26
+ <inherits name='com.sap.sailing.gwt.common.SailingLocalesSinglePermutation' />
27 27
28 28
<inherits name='com.sap.sailing.gwt.ui.SourceCommon' />
29 29
<inherits name='com.sap.sailing.gwt.ui.SourceClient' />
java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/Leaderboard.gwt.xml
... ...
@@ -24,7 +24,7 @@
24 24
<entry-point class='com.sap.sailing.gwt.ui.leaderboard.LeaderboardEntryPoint' />
25 25
26 26
<!-- module containing locale configuration -->
27
- <inherits name='com.sap.sailing.gwt.common.SailingLocalesAllPermutations' />
27
+ <inherits name='com.sap.sailing.gwt.common.SailingLocalesSinglePermutation' />
28 28
29 29
<inherits name='com.sap.sailing.gwt.ui.SourceLeaderboard' />
30 30
<inherits name='com.sap.sailing.gwt.ui.SourceCommon' />
java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/PairingList.gwt.xml
... ...
@@ -24,7 +24,7 @@
24 24
<entry-point class='com.sap.sailing.gwt.ui.pairinglist.PairingListEntryPoint' />
25 25
26 26
<!-- module containing locale configuration -->
27
- <inherits name='com.sap.sailing.gwt.common.SailingLocalesAllPermutations' />
27
+ <inherits name='com.sap.sailing.gwt.common.SailingLocalesSinglePermutation' />
28 28
29 29
<inherits name='com.sap.sailing.gwt.ui.SourceLeaderboard' />
30 30
<inherits name='com.sap.sailing.gwt.ui.SourceCommon' />
java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/Simulator.gwt.xml
... ...
@@ -20,7 +20,7 @@
20 20
<inherits name='com.google.gwt.maps.Maps' />
21 21
22 22
<!-- module containing locale configuration -->
23
- <inherits name='com.sap.sailing.gwt.common.SailingLocalesAllPermutations' />
23
+ <inherits name='com.sap.sailing.gwt.common.SailingLocalesSinglePermutation' />
24 24
25 25
<inherits name="com.sap.sailing.gwt.ui.SourceAdminConsole"/>
26 26
<inherits name='com.sap.sailing.gwt.ui.SourceClient'/>
java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/Spectator.gwt.xml
... ...
@@ -23,7 +23,7 @@
23 23
<entry-point class='com.sap.sailing.gwt.ui.spectator.SpectatorEntryPoint'/>
24 24
25 25
<!-- module containing locale configuration -->
26
- <inherits name='com.sap.sailing.gwt.common.SailingLocalesAllPermutations' />
26
+ <inherits name='com.sap.sailing.gwt.common.SailingLocalesSinglePermutation' />
27 27
28 28
<source path='spectator'/>
29 29
java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/VideoPopup.gwt.xml
... ...
@@ -21,7 +21,7 @@
21 21
<entry-point class="com.sap.sailing.gwt.ui.client.media.popup.VideoPopupWindow" />
22 22
23 23
<!-- module containing locale configuration -->
24
- <inherits name='com.sap.sailing.gwt.common.SailingLocalesAllPermutations' />
24
+ <inherits name='com.sap.sailing.gwt.common.SailingLocalesSinglePermutation' />
25 25
26 26
<source path='client' />
27 27
<source path='actions'/>
java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/YoutubePopup.gwt.xml
... ...
@@ -20,7 +20,7 @@
20 20
<inherits name='com.google.gwt.json.JSON' />
21 21
22 22
<!-- module containing locale configuration -->
23
- <inherits name='com.sap.sailing.gwt.common.SailingLocalesAllPermutations' />
23
+ <inherits name='com.sap.sailing.gwt.common.SailingLocalesSinglePermutation' />
24 24
25 25
<entry-point class="com.sap.sailing.gwt.ui.client.media.popup.YoutubePopupWindow" />
26 26
java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/AbstractJsonHttpServlet.java
... ...
@@ -8,7 +8,6 @@ import javax.servlet.http.HttpServletResponse;
8 8
*/
9 9
@SuppressWarnings("serial")
10 10
public abstract class AbstractJsonHttpServlet extends SailingServerHttpServlet {
11
-
12 11
protected void setJsonResponseHeader(HttpServletResponse resp) {
13 12
// to allow access to the json document directly from a client side javascript
14 13
resp.setHeader("Access-Control-Allow-Origin", "*");
java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/AbstractFileUploadServlet.java
... ...
@@ -45,6 +45,18 @@ public abstract class AbstractFileUploadServlet extends AbstractJsonHttpServlet
45 45
try {
46 46
@SuppressWarnings("unchecked")
47 47
List<FileItem> items = (List<FileItem>) upload.parseRequest(req);
48
+ // The content type and character encoding are expected to be set by AbstractFileUploadServlet.doPost already
49
+ // before invoking this method. The hairy part about all this is that we trigger file uploads from <form>
50
+ // elements in HTML pages, and by default these expect to receive content of type text/html, or at best
51
+ // application/xml, but application/json does not belong to the expected content types of an HTML form.
52
+ // This leads to the strange effect that browser which expect to need to display the response to a user
53
+ // will wrap the application/json content in a <pre> tag even before delivering it to the onSubmitComplete handler.
54
+ // Therefore, all such handlers must ensure they check for a <pre>...</pre> enclosing and remove it before
55
+ // handling the content.
56
+ // Conversely, trying to use text/html as content encoding leads some browsers---especially on mobile devices---
57
+ // to do ugly things to the content returned, such as replacing digit sequences by a corresponding <a> element
58
+ // that allows the user to dial that number with the phone app...
59
+ setJsonResponseHeader(resp);
48 60
process(items, req, resp);
49 61
} catch (FileUploadException e) {
50 62
throw new IOException("Could not parse request");
java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/FileUploadServlet.java
... ...
@@ -1,6 +1,8 @@
1 1
package com.sap.sailing.server.gateway.impl;
2 2
3
+import java.awt.image.BufferedImage;
3 4
import java.io.IOException;
5
+import java.io.InputStream;
4 6
import java.io.UnsupportedEncodingException;
5 7
import java.net.URI;
6 8
import java.nio.file.Paths;
... ...
@@ -8,6 +10,7 @@ import java.util.List;
8 10
import java.util.logging.Level;
9 11
import java.util.logging.Logger;
10 12
13
+import javax.imageio.ImageIO;
11 14
import javax.servlet.http.HttpServletRequest;
12 15
import javax.servlet.http.HttpServletResponse;
13 16
import javax.ws.rs.core.Response.Status;
... ...
@@ -47,6 +50,11 @@ public class FileUploadServlet extends AbstractFileUploadServlet {
47 50
final JSONObject result = new JSONObject();
48 51
final String fileExtension;
49 52
final String fileName = Paths.get(fileItem.getName()).getFileName().toString();
53
+ // special handling of double underscore in JSON. Double underscores will be encoded with hex representation.
54
+ // In some cases the JSON parser of Apples Safari on mobile devices cannot parse JSON with __. See also bug5127
55
+ // Disabled for testing the real reason of error:
56
+ // String fileNameUnderscoreEncoded = fileName.replace("__", "%5f%5f");
57
+ String fileNameUnderscoreEncoded = fileName;
50 58
final String fileType = fileItem.getContentType();
51 59
if (fileType.equals("image/jpeg")) {
52 60
fileExtension = ".jpg";
... ...
@@ -69,11 +77,25 @@ public class FileUploadServlet extends AbstractFileUploadServlet {
69 77
+ MAX_SIZE_IN_MB + "MB");
70 78
result.put(FileUploadConstants.STATUS, Status.INTERNAL_SERVER_ERROR.name());
71 79
result.put(FileUploadConstants.MESSAGE, errorMessage);
80
+ result.put(FileUploadConstants.FILE_SIZE, fileItem.getSize());
72 81
} else {
73 82
final URI fileUri = getService().getFileStorageManagementService().getActiveFileStorageService()
74 83
.storeFile(fileItem.getInputStream(), fileExtension, fileItem.getSize());
75
- result.put(FileUploadConstants.FILE_NAME, fileName);
84
+ result.put(FileUploadConstants.FILE_NAME, fileNameUnderscoreEncoded);
76 85
result.put(FileUploadConstants.FILE_URI, fileUri.toString());
86
+ result.put(FileUploadConstants.CONTENT_TYPE, fileItem.getContentType());
87
+ result.put(FileUploadConstants.FILE_SIZE, fileItem.getSize());
88
+ if (fileItem.getContentType() != null && fileItem.getContentType().startsWith("image/")) {
89
+ try (InputStream inputStream = fileItem.getInputStream()) {
90
+ BufferedImage image = ImageIO.read(inputStream);
91
+ if (image != null) {
92
+ int height = image.getHeight();
93
+ int width = image.getWidth();
94
+ result.put(FileUploadConstants.MEDIA_TYPE_HEIGHT, height);
95
+ result.put(FileUploadConstants.MEDIA_TYPE_WIDTH, width);
96
+ }
97
+ }
98
+ }
77 99
}
78 100
} catch (IOException | OperationFailedException | RuntimeException | InvalidPropertiesException e) {
79 101
final String errorMessage = "Could not store file"+ (e.getMessage()==null?"":(": " + e.getMessage()));
... ...
@@ -83,12 +105,7 @@ public class FileUploadServlet extends AbstractFileUploadServlet {
83 105
}
84 106
resultList.add(result);
85 107
}
86
- // surprise, surprise: see https://www.sencha.com/forum/showthread.php?132949-Fileupload-Invalid-JSON-string
87
- // When sending a JSON response for a file upload, don't use application/json as the content type. It would lead
88
- // to wrapping the content by a <pre> tag. Use text/html instead which should deliver the content to the app running
89
- // in the browser unchanged.
90
- resp.setContentType("text/html");
91
- resp.setCharacterEncoding("UTF-8");
108
+
92 109
resultList.writeJSONString(resp.getWriter());
93 110
}
94 111
}
java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/ProgressServlet.java
... ...
@@ -23,7 +23,7 @@ public class ProgressServlet extends HttpServlet {
23 23
}
24 24
25 25
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
26
- response.setContentType("text/html");
26
+ response.setContentType("application/json");
27 27
final HttpSession session = request.getSession(true);
28 28
if (session == null) {
29 29
logger.warning("Sorry, session is null"); // just to be safe
java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/trackfiles/impl/ExpeditionAllInOneImportServlet.java
... ...
@@ -57,7 +57,6 @@ public class ExpeditionAllInOneImportServlet extends AbstractFileUploadServlet {
57 57
@Override
58 58
protected void process(List<FileItem> fileItems, HttpServletRequest req, HttpServletResponse resp)
59 59
throws IOException {
60
- resp.setContentType("text/html;charset=UTF-8");
61 60
ImporterResult importerResult = null;
62 61
try {
63 62
String fileName = null;
java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/trackfiles/impl/SensorDataImportServlet.java
... ...
@@ -51,7 +51,6 @@ public class SensorDataImportServlet extends AbstractFileUploadServlet {
51 51
} catch (Exception e) {
52 52
importResult.add(e);
53 53
} finally {
54
- resp.setContentType("text/html;charset=UTF-8");
55 54
ImportResultSerializer.serializeImportResult(importResult).writeJSONString(resp.getWriter());
56 55
}
57 56
}
java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/trackfiles/impl/TrackFilesImportServlet.java
... ...
@@ -62,7 +62,6 @@ public class TrackFilesImportServlet extends AbstractFileUploadServlet {
62 62
} catch (Exception e) {
63 63
importResult.add(e);
64 64
} finally {
65
- resp.setContentType("text/html;charset=UTF-8");
66 65
ImportResultSerializer.serializeImportResult(importResult).writeJSONString(resp.getWriter());
67 66
}
68 67
}
java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/windimport/AbstractWindImportServlet.java
... ...
@@ -56,7 +56,7 @@ public abstract class AbstractWindImportServlet extends SailingServerHttpServlet
56 56
logger.log(Level.SEVERE, "Error ocurred trying to import wind fixes", e);
57 57
windImportResult.error = e.toString();
58 58
}
59
- response.setContentType("text/html;charset=UTF-8");
59
+ response.setContentType("application/json;charset=UTF-8");
60 60
windImportResult.json().writeJSONString(response.getWriter());
61 61
}
62 62
java/com.sap.sailing.www/.gitignore
... ...
@@ -1,2 +1,3 @@
1 1
/bin
2 2
/filestorage/
3
+/tmp/
java/com.sap.sse.common.test/src/com/sap/sse/common/media/MediaTypeTest.java
... ...
@@ -0,0 +1,68 @@
1
+package com.sap.sse.common.media;
2
+
3
+import static org.junit.Assert.assertEquals;
4
+
5
+import org.junit.Test;
6
+
7
+public class MediaTypeTest {
8
+
9
+ @Test
10
+ public void testMediaTypeByContentType() {
11
+ assertEquals(MimeType.unknown, MimeType.byContentType(null));
12
+ assertEquals(MimeType.mp4, MimeType.byContentType("video/mp4"));
13
+ assertEquals(MimeType.ogv, MimeType.byContentType("video/ogg"));
14
+ assertEquals(MimeType.ogg, MimeType.byContentType("audio/ogg"));
15
+ assertEquals(MimeType.mov, MimeType.byContentType("video/quicktime"));
16
+ assertEquals(MimeType.image, MimeType.byContentType("image/jpeg"));
17
+ assertEquals(MimeType.image, MimeType.byContentType("image/gif"));
18
+ assertEquals(MimeType.image, MimeType.byContentType("image/webp"));
19
+ assertEquals(MimeType.image, MimeType.byContentType("image/png"));
20
+ assertEquals(MimeType.unknown, MimeType.byContentType("image/xxxx"));
21
+ assertEquals(MimeType.unknown, MimeType.byContentType("xxx/png"));
22
+ assertEquals(MimeType.unknown, MimeType.byContentType("xyz"));
23
+ }
24
+
25
+ @Test
26
+ public void testMediaTypeByExtension() {
27
+ assertEquals(MimeType.unknown, MimeType.byExtension(null));
28
+ assertEquals(MimeType.mp4, MimeType.byExtension("video.mp4"));
29
+ assertEquals(MimeType.ogv, MimeType.byExtension("https://exmaple.video.com/download/video.ogv"));
30
+ assertEquals(MimeType.mov, MimeType.byExtension("video.mov"));
31
+ assertEquals(MimeType.qt, MimeType.byExtension("video.qt"));
32
+ assertEquals(MimeType.mp3, MimeType.byExtension("C:\\User\\Test\\Music\\audio.mp3"));
33
+ assertEquals(MimeType.ogg, MimeType.byExtension("audio.ogg"));
34
+ assertEquals(MimeType.image, MimeType.byExtension("https://exmaple.images.com/download/image.jpeg"));
35
+ assertEquals(MimeType.image, MimeType.byExtension("image.jpg"));
36
+ assertEquals(MimeType.image, MimeType.byExtension("image.gif"));
37
+ assertEquals(MimeType.image, MimeType.byExtension("image.webp"));
38
+ assertEquals(MimeType.image, MimeType.byExtension("image.png"));
39
+ assertEquals(MimeType.unknown, MimeType.byExtension("unknownExtension.xxx"));
40
+ assertEquals(MimeType.unknown, MimeType.byExtension("filenameWithoutExtension"));
41
+ assertEquals(MimeType.image, MimeType.byExtension(" image.jpg "));
42
+ assertEquals(MimeType.mp4, MimeType.byExtension(" video.mp4 "));
43
+ }
44
+
45
+ @Test
46
+ public void testMediaTypeFromUrl() {
47
+ assertEquals(MimeType.unknown, MimeType.extractFromUrl(null));
48
+ assertEquals(MimeType.mp4, MimeType.extractFromUrl("video.mp4"));
49
+ assertEquals(MimeType.ogv, MimeType.extractFromUrl("https://exmaple.video.com/download/video.ogv"));
50
+ assertEquals(MimeType.mov, MimeType.extractFromUrl("video.mov"));
51
+ assertEquals(MimeType.qt, MimeType.extractFromUrl("video.qt"));
52
+ assertEquals(MimeType.mp3, MimeType.extractFromUrl("C:\\User\\Test\\Music\\audio.mp3"));
53
+ assertEquals(MimeType.ogg, MimeType.extractFromUrl("audio.ogg"));
54
+ assertEquals(MimeType.image, MimeType.extractFromUrl("https://exmaple.images.com/download/image.jpeg"));
55
+ assertEquals(MimeType.image, MimeType.extractFromUrl("image.jpg"));
56
+ assertEquals(MimeType.image, MimeType.extractFromUrl("image.gif"));
57
+ assertEquals(MimeType.image, MimeType.extractFromUrl("image.webp"));
58
+ assertEquals(MimeType.image, MimeType.extractFromUrl("image.png"));
59
+ assertEquals(MimeType.unknown, MimeType.extractFromUrl("unknownExtension.xxx"));
60
+ assertEquals(MimeType.image, MimeType.extractFromUrl(" image.jpg "));
61
+ assertEquals(MimeType.mp4, MimeType.extractFromUrl(" video.mp4 "));
62
+ assertEquals(MimeType.unknown, MimeType.extractFromUrl("filenameWithoutExtension"));
63
+ assertEquals(MimeType.youtube, MimeType.extractFromUrl("https://www.youtube.com/watch?v=7A7XJLhRVVE"));
64
+ assertEquals(MimeType.youtube, MimeType.extractFromUrl("https://youtu.be/7A7XJLhRVVE"));
65
+ assertEquals(MimeType.vimeo, MimeType.extractFromUrl("https://vimeo.com/17632930"));
66
+ }
67
+
68
+}
java/com.sap.sse.common/src/com/sap/sse/common/fileupload/FileUploadConstants.java
... ...
@@ -3,6 +3,10 @@ package com.sap.sse.common.fileupload;
3 3
public interface FileUploadConstants {
4 4
String FILE_NAME = "file_name";
5 5
String FILE_URI = "file_uri";
6
+ String CONTENT_TYPE = "content_type";
7
+ String FILE_SIZE = "file_size";
8
+ String MEDIA_TYPE_HEIGHT = "media_type_height";
9
+ String MEDIA_TYPE_WIDTH = "media_type_width";
6 10
String MESSAGE = "message";
7 11
String STATUS = "status";
8 12
String PROGRESS_PERCENTAGE = "progress_percentage";
java/com.sap.sse.common/src/com/sap/sse/common/media/MediaTagConstants.java
... ...
@@ -27,7 +27,7 @@ public enum MediaTagConstants {
27 27
public static final Iterable<String> videoTagSuggestions = Arrays.asList(new String[] { BIGSCREEN.getName(),
28 28
LIVESTREAM.getName(), HIGHLIGHT.getName(), FEATURED.getName(), STAGE.getName() });
29 29
public static final Set<MimeType> SUPPORTED_VIDEO_TYPES = new HashSet<>(Arrays.asList(MimeType.youtube,
30
- MimeType.vimeo, MimeType.mp4, MimeType.mp4panorama, MimeType.mp4panoramaflip, MimeType.qt));
30
+ MimeType.vimeo, MimeType.mp4, MimeType.mp4panorama, MimeType.mp4panoramaflip, MimeType.mov, MimeType.qt));
31 31
32 32
private MediaTagConstants(String name, int minWidth, int maxWidth, int minHeight, int maxHeight) {
33 33
this.name = name;
java/com.sap.sse.common/src/com/sap/sse/common/media/MimeType.java
... ...
@@ -1,24 +1,24 @@
1 1
package com.sap.sse.common.media;
2 2
3
-import java.util.ArrayList;
4
-import java.util.List;
5
-
6 3
public enum MimeType {
7 4
8
- mp4(MediaType.video, MediaSubType.mp4, "mp4"),
9
- ogv(MediaType.video, MediaSubType.ogg, "ogv"),
10
- qt(MediaType.video, MediaSubType.quicktime, "qt|qtvr|qti|qtif"),
11
- mp3(MediaType.audio, MediaSubType.mpeg, "mp3"),
12
- ogg(MediaType.audio, MediaSubType.ogg, "ogg|oga|spx"),
13
- aac(MediaType.audio, MediaSubType.aac, "acc"),
14
- webm(MediaType.video, MediaSubType.webm, "webm"),
15
- youtube(MediaType.video, MediaSubType.youtube, ""),
16
- vimeo(MediaType.video, MediaSubType.vimeo, ""),
17
- image(MediaType.image, MediaSubType.unknown, "jpg|jpeg|jpe|jif|jfif|jfi|png|gif|webp|tiff|tif|apng|avif|svg|bmp|ico|cur"),
18
- unknown(MediaType.unknown, MediaSubType.unknown, ""),
19
- mp4panorama(MediaType.video, MediaSubType.mp4, "mp4"),
20
- mp4panoramaflip(MediaType.video, MediaSubType.mp4, "mp4"),
5
+ mp4(MediaType.video, MediaSubType.mp4, "mp4"),
6
+ ogv(MediaType.video, MediaSubType.ogg, "ogv"),
7
+ qt(MediaType.video, MediaSubType.mp4, "qt|qtvr|qti|qtif"),
8
+ mp3(MediaType.audio, MediaSubType.mpeg, "mp3"),
9
+ ogg(MediaType.audio, MediaSubType.ogg, "ogg|oga|spx"),
10
+ aac(MediaType.audio, MediaSubType.aac, "acc"),
11
+ webm(MediaType.video, MediaSubType.webm, "webm"),
12
+ youtube(MediaType.video, MediaSubType.youtube, ""),
13
+ vimeo(MediaType.video, MediaSubType.vimeo, ""),
14
+ image(MediaType.image, MediaSubType.unknown, "jpg|jpeg|jpe|jif|jfif|jfi|png|gif|webp|tiff|tif|apng|avif|svg|bmp|ico|cur"),
15
+ unknown(MediaType.unknown, MediaSubType.unknown, ""),
16
+ mp4panorama(MediaType.video, MediaSubType.mp4, "mp4"),
17
+ mp4panoramaflip(MediaType.video, MediaSubType.mp4, "mp4"),
21 18
mov(MediaType.video, MediaSubType.mp4, "mov|quicktime");
19
+
20
+ private final static String YOUTUBE_REGEX = "((http(s)?:\\/\\/)?)(www\\.)?((youtube\\.com\\/)|(youtu.be\\/))[\\S]+";
21
+ public final static String VIMEO_REGEX = "(?:http|https)?:?\\/?\\/?(?:www\\.)?(?:player\\.)?vimeo\\.com\\/(?:channels\\/(?:\\w+\\/)?|groups\\/(?:[^\\/]*)\\/videos\\/|video\\/|)(\\d+)(?:|\\/\\?)";
22 22
23 23
public final MediaType mediaType;
24 24
public final MediaSubType mediaSubType;
... ...
@@ -29,19 +29,27 @@ public enum MimeType {
29 29
this.mediaSubType = mediaSubType;
30 30
this.endingPattern = endingPattern;
31 31
}
32
-
33
- public boolean isPanorama(){
32
+
33
+ public boolean isPanorama() {
34 34
return this == mp4panorama || this == mp4panoramaflip;
35 35
}
36
-
37
- public boolean isFlippedPanorama(){
36
+
37
+ public boolean isFlippedPanorama() {
38 38
return this == mp4panoramaflip;
39 39
}
40
-
40
+
41 41
public String getEndingPattern() {
42 42
return endingPattern;
43 43
}
44
-
44
+
45
+ public boolean isImage() {
46
+ return mediaType == MediaType.image;
47
+ }
48
+
49
+ public boolean isVideo() {
50
+ return mediaType == MediaType.video;
51
+ }
52
+
45 53
public MediaSubType getMediaSubType() {
46 54
return mediaSubType;
47 55
}
... ...
@@ -62,21 +70,90 @@ public enum MimeType {
62 70
return null;
63 71
}
64 72
}
65
-
73
+
66 74
public static MimeType[] mp4MimeTypes() {
67
- return new MimeType[] { mp4, mp4panorama, mp4panoramaflip, mov};
75
+ return new MimeType[] { mp4, mp4panorama, mp4panoramaflip, mov };
76
+ }
77
+
78
+ /**
79
+ * Parses a file name by it's extension and return the matching {@link MimeType}.
80
+ *
81
+ * @param fileName
82
+ * String of the file name
83
+ * @return the matching MimeTpye or else {@link MimeType#unknown}
84
+ */
85
+ public static MimeType byExtension(String fileName) {
86
+ final MimeType result;
87
+ if (fileName == null) {
88
+ result = unknown;
89
+ } else {
90
+ int dotPos = fileName.trim().lastIndexOf('.');
91
+ if (dotPos >= 0) {
92
+ String fileEnding = fileName.trim().substring(dotPos + 1).toLowerCase();
93
+ MimeType bestMatch = unknown;
94
+ if (fileEnding != null) {
95
+ for (MimeType mimeType : MimeType.values()) {
96
+ if (!mimeType.getEndingPattern().isEmpty()) {
97
+ boolean matchFound = fileEnding.toLowerCase().matches(mimeType.endingPattern);
98
+ if (matchFound) {
99
+ bestMatch = mimeType;
100
+ break;
101
+ }
102
+ }
103
+ }
104
+ }
105
+ result = bestMatch;
106
+ } else {
107
+ result = unknown;
108
+ }
109
+ }
110
+ return result;
68 111
}
69 112
70
- public static List<MimeType> byExtension(String extension) {
71
- List<MimeType> result = new ArrayList<>();
72
- if (extension != null) {
73
- for (MimeType mimeType: MimeType.values()) {
74
- if (mimeType.getEndingPattern().contains(extension.toLowerCase())) {
75
- result.add(mimeType);
113
+ public static MimeType extractFromUrl(String url) {
114
+ final MimeType mimeType;
115
+ if (url == null) {
116
+ mimeType = MimeType.unknown;
117
+ } else if (url.trim().matches(YOUTUBE_REGEX)) {
118
+ mimeType = MimeType.youtube;
119
+ } else if (url.trim().matches(VIMEO_REGEX)) {
120
+ mimeType = MimeType.vimeo;
121
+ } else {
122
+ mimeType = byExtension(url);
123
+ }
124
+ return mimeType;
125
+ }
126
+
127
+ /**
128
+ * Gets a mime type based on content type, e.g. image/jpeg, video/mp4, ...
129
+ *
130
+ * The media type must always match (image, video, audio). Then it will compare the sub type first by mime type
131
+ * name, then by ending pattern and then by sub type name
132
+ *
133
+ * @param contentType
134
+ * the content type of the media in String form
135
+ * @return the matching {@link MimeType} or else {@link MimeType#unknown}
136
+ */
137
+ public static MimeType byContentType(String contentType) {
138
+ final MimeType result;
139
+ if (contentType != null && contentType.contains("/")) {
140
+ String mediaType = contentType.split("/")[0];
141
+ String subType = contentType.split("/")[1];
142
+ MimeType bestMatch = unknown;
143
+ for (MimeType mimeType : MimeType.values()) {
144
+ if (mimeType.mediaType.name().equals(mediaType)
145
+ && (mimeType.name().equals(subType)
146
+ || mimeType.getEndingPattern().contains(subType.toLowerCase())
147
+ || mimeType.getMediaSubType().name().equals(subType))) {
148
+ bestMatch = mimeType;
149
+ break;
76 150
}
77 151
}
152
+ result = bestMatch;
153
+ } else {
154
+ result = unknown;
78 155
}
79 156
return result;
80 157
}
81 158
82
-}
... ...
\ No newline at end of file
0
+}
java/com.sap.sse.gwt.adminconsole/src/com/sap/sse/gwt/adminconsole/URLFieldWithFileUpload.java
... ...
@@ -1,5 +1,10 @@
1 1
package com.sap.sse.gwt.adminconsole;
2 2
3
+import java.util.ArrayList;
4
+import java.util.HashMap;
5
+import java.util.LinkedHashMap;
6
+import java.util.List;
7
+import java.util.Map;
3 8
import java.util.logging.Level;
4 9
import java.util.logging.Logger;
5 10
... ...
@@ -19,7 +24,6 @@ import com.google.gwt.http.client.RequestException;
19 24
import com.google.gwt.http.client.Response;
20 25
import com.google.gwt.json.client.JSONArray;
21 26
import com.google.gwt.json.client.JSONParser;
22
-import com.google.gwt.json.client.JSONValue;
23 27
import com.google.gwt.user.client.ui.Button;
24 28
import com.google.gwt.user.client.ui.Composite;
25 29
import com.google.gwt.user.client.ui.FileUpload;
... ...
@@ -36,6 +40,7 @@ import com.google.gwt.user.client.ui.VerticalPanel;
36 40
import com.sap.sse.common.fileupload.FileUploadConstants;
37 41
import com.sap.sse.gwt.client.Notification;
38 42
import com.sap.sse.gwt.client.Notification.NotificationType;
43
+import com.sap.sse.gwt.client.fileupload.FileUploadUtil;
39 44
40 45
/**
41 46
* A {@link TextBox} together with an upload button that can use the file storage service to
... ...
@@ -44,33 +49,34 @@ import com.sap.sse.gwt.client.Notification.NotificationType;
44 49
* @author Axel Uhl (d043530)
45 50
*
46 51
*/
47
-public class URLFieldWithFileUpload extends Composite implements HasValue<String> {
48
-
52
+public class URLFieldWithFileUpload extends Composite implements HasValue<Map<String, String>> {
49 53
private final Logger logger = Logger.getLogger(getClass().getName());
50 54
private static final URLFieldWithFileUploadResources RESOURCES = URLFieldWithFileUploadResources.INSTANCE;
51 55
private static final String UPLOAD_URL = "/sailingserver/fileupload";
56
+ private static final String URI_DELIMITER = " ";
52 57
53 58
private final TextBox urlTextBox;
54
-
59
+
55 60
private final FileUpload fileUploadField;
56
-
57
- private String uri;
61
+
62
+ private Map<String, String> uriList;
58 63
private String name;
59 64
60 65
private boolean valueChangeHandlerInitialized = false;
61 66
private StartUploadEvent startUploadEvent;
62 67
private EndUploadEvent endUploadEvent;
63
-
68
+
64 69
private final Button removeButton;
65 70
private final FormPanel uploadFormPanel;
66 71
private final FlowPanel uploadPanel;
67
-
72
+
68 73
public URLFieldWithFileUpload(final StringMessages stringMessages, String acceptedFileTypes) {
69
- this(stringMessages, true, true, acceptedFileTypes);
74
+ this(stringMessages, false, true, true, acceptedFileTypes);
70 75
}
71
-
72
- public URLFieldWithFileUpload(final StringMessages stringMessages, boolean initiallyEnableUpload, boolean showUrlAfterUpload, String acceptedFileTypes) {
76
+
77
+ public URLFieldWithFileUpload(final StringMessages stringMessages, boolean multiFileUpload, boolean initiallyEnableUpload, boolean showUrlAfterUpload, String acceptedFileTypes) {
73 78
RESOURCES.urlFieldWithFileUploadStyle().ensureInjected();
79
+ this.uriList = new LinkedHashMap<>();
74 80
startUploadEvent = new StartUploadEvent() {
75 81
@Override
76 82
public void startUpload() {
... ...
@@ -93,7 +99,7 @@ public class URLFieldWithFileUpload extends Composite implements HasValue<String
93 99
removePanel.addSubmitCompleteHandler(new SubmitCompleteHandler() {
94 100
@Override
95 101
public void onSubmitComplete(SubmitCompleteEvent event) {
96
- setUriAndFireEvent(null);
102
+ setUri(null, true);
97 103
}
98 104
});
99 105
removePanel.setMethod(FormPanel.METHOD_POST);
... ...
@@ -106,9 +112,11 @@ public class URLFieldWithFileUpload extends Composite implements HasValue<String
106 112
removeButton.addClickHandler(new ClickHandler() {
107 113
@Override
108 114
public void onClick(ClickEvent event) {
109
- removePanel.setAction("/sailingserver/api/v1/file?uri=" + uri);
110
- removePanel.submit();
111
- setUriAndFireEvent(null);
115
+ for (String uri : uriList.keySet()) {
116
+ removePanel.setAction("/sailingserver/api/v1/file?uri=" + uri);
117
+ removePanel.submit();
118
+ }
119
+ setUri(null, true);
112 120
urlTextBox.setEnabled(true);
113 121
}
114 122
});
... ...
@@ -117,7 +125,7 @@ public class URLFieldWithFileUpload extends Composite implements HasValue<String
117 125
urlTextBox.getElement().addClassName("url-textbox");
118 126
urlTextBox.addStyleName(RESOURCES.urlFieldWithFileUploadStyle().urlTextboxClass());
119 127
urlTextBox.addValueChangeHandler(valueChangedEvent->{
120
- setUriAndFireEvent(valueChangedEvent.getValue());
128
+ setUri(valueChangedEvent.getValue(), true);
121 129
});
122 130
imageUrlPanel.add(urlTextBox);
123 131
final Button selectUploadButton = new Button();
... ...
@@ -133,6 +141,9 @@ public class URLFieldWithFileUpload extends Composite implements HasValue<String
133 141
uploadFormPanel.setEncoding(FormPanel.ENCODING_MULTIPART);
134 142
uploadFormPanel.setMethod(FormPanel.METHOD_POST);
135 143
fileUploadField = new FileUpload();
144
+ if (multiFileUpload) {
145
+ fileUploadField.getElement().setAttribute("multiple", "multiple");
146
+ }
136 147
if (acceptedFileTypes != null) {
137 148
fileUploadField.getElement().setAttribute("accept", acceptedFileTypes);
138 149
}
... ...
@@ -142,12 +153,11 @@ public class URLFieldWithFileUpload extends Composite implements HasValue<String
142 153
uploadPanel.add(fileUploadField);
143 154
selectUploadButton.addClickHandler(click -> {
144 155
fileUploadField.click();
145
- });
156
+ });
146 157
// the hidden submit button for uploading the file
147 158
final SubmitButton submitButton = new SubmitButton(stringMessages.send());
148 159
submitButton.setVisible(false);
149 160
submitButton.setEnabled(false);
150
-
151 161
fileUploadField.addChangeHandler(new ChangeHandler() {
152 162
@Override
153 163
public void onChange(ChangeEvent event) {
... ...
@@ -166,44 +176,56 @@ public class URLFieldWithFileUpload extends Composite implements HasValue<String
166 176
uploadFormPanel.addSubmitCompleteHandler(new SubmitCompleteHandler() {
167 177
@Override
168 178
public void onSubmitComplete(SubmitCompleteEvent event) {
169
- String localUri = uri;
170
- if (localUri != null && !"".equals(localUri)) {
171
- GWT.log("(1) remove uploaded file " + localUri);
172
- deleteFileOnServer(localUri);
179
+ for (String localUri : uriList.keySet()) {
180
+ if (localUri != null && !"".equals(localUri)) {
181
+ GWT.log("(1) remove uploaded file " + localUri);
182
+ deleteFileOnServer(localUri);
183
+ }
173 184
}
174 185
selectUploadButton.removeStyleName(RESOURCES.urlFieldWithFileUploadStyle().loadingClass());
175 186
endUploadEvent.endUpload();
176
- String result = event.getResults();
177
- JSONArray resultJson = parseAfterReplacingSurroundingPreElement(result).isArray();
187
+ JSONArray resultJson = JSONParser.parseStrict(FileUploadUtil.getApplicationJsonContent(event)).isArray();
178 188
if (resultJson != null) {
179
- if (resultJson.get(0).isObject().get(FileUploadConstants.FILE_URI) != null) {
180
- String uri = resultJson.get(0).isObject().get(FileUploadConstants.FILE_URI).isString().stringValue();
181
- setUriAndFireEvent(uri);
182
- String fileName = resultJson.get(0).isObject().get(FileUploadConstants.FILE_NAME).isString().stringValue();
183
- if (showUrlAfterUpload) {
184
- urlTextBox.setValue(uri);
185
- urlTextBox.setEnabled(true);
186
- } else {
187
- urlTextBox.setEnabled(false);
188
- String textShown = "";
189
- int dotPos = fileName.lastIndexOf('.');
190
- if (dotPos >= 0) {
191
- String fileEnding = fileName.substring(dotPos + 1);
192
- textShown = fileEnding + " - ";
193
- }
194
- name = fileName.substring(dotPos + 1);
195
- textShown += stringMessages.uploadSuccessful();
196
- urlTextBox.setValue(textShown);
197
- }
198
- urlTextBox.setTitle(fileName);
199
- name = getFileNameWithoutEnding(fileName);
200
- fireResultUri(uri);
201
- removeButton.setEnabled(true);
202
- } else {
203
- urlTextBox.setValue(stringMessages.error());
204
- Notification.notify(stringMessages.fileUploadResult(resultJson.get(0).isObject().get(FileUploadConstants.STATUS).isString().stringValue(),
189
+ Map<String, String> uris = new HashMap<>(resultJson.size());
190
+ List<String> titleStrings = new ArrayList<>(resultJson.size());
191
+ List<String> valueStrings = new ArrayList<>(resultJson.size());
192
+ for (int i = 0; i < resultJson.size(); i++) {
193
+ if (resultJson.get(i).isObject() != null) {
194
+ if (resultJson.get(i).isObject().get(FileUploadConstants.FILE_URI) != null) {
195
+ String uri = resultJson.get(i).isObject().get(FileUploadConstants.FILE_URI).isString().stringValue();
196
+ // special handling of double underscore in JSON. Double underscores were encoded with hex representation.
197
+ // In some cases the JSON parser of Apples Safari on mobile devices cannot parse JSON with __. See also bug5127
198
+ String fileNameUnderscoreEncoded = resultJson.get(i).isObject().get(FileUploadConstants.FILE_NAME).isString().stringValue();
199
+ String fileName = fileNameUnderscoreEncoded.replace("%5f%5f", "__");
200
+ uris.put(uri, fileName);
201
+ titleStrings.add(fileName);
202
+ if (showUrlAfterUpload) {
203
+ valueStrings.add(uri);
204
+ } else {
205
+ String textShown = "";
206
+ int dotPos = fileName.lastIndexOf('.');
207
+ if (dotPos >= 0) {
208
+ String fileEnding = fileName.substring(dotPos + 1);
209
+ textShown = fileEnding;
210
+ }
211
+ valueStrings.add(textShown);
212
+ }
213
+ name = getFileNameWithoutEnding(fileName); //TODO Only contains the last value
214
+ } else {
215
+ urlTextBox.setValue(stringMessages.error());
216
+ Notification.notify(stringMessages.fileUploadResult(resultJson.get(0).isObject().get(FileUploadConstants.STATUS).isString().stringValue(),
205 217
resultJson.get(0).isObject().get(FileUploadConstants.MESSAGE).isString().stringValue()), NotificationType.ERROR);
218
+ }
219
+ }
206 220
}
221
+ if (!showUrlAfterUpload) {
222
+ valueStrings.add("- " + stringMessages.uploadSuccessful());
223
+ }
224
+ setUris(uris, true);
225
+ urlTextBox.setTitle(String.join(" ", titleStrings));
226
+ urlTextBox.setValue(String.join(URI_DELIMITER, valueStrings));
227
+ urlTextBox.setEnabled(showUrlAfterUpload);
228
+ removeButton.setEnabled(true);
207 229
} else {
208 230
urlTextBox.setValue(stringMessages.error());
209 231
}
... ...
@@ -215,7 +237,7 @@ public class URLFieldWithFileUpload extends Composite implements HasValue<String
215 237
mainPanel.add(uploadFormPanel);
216 238
initWidget(mainPanel);
217 239
}
218
-
240
+
219 241
private String getFileNameWithoutEnding(String fileName) {
220 242
String result = fileName;
221 243
int dotPos = fileName.lastIndexOf('.');
... ...
@@ -226,86 +248,101 @@ public class URLFieldWithFileUpload extends Composite implements HasValue<String
226 248
result = result.replaceAll("(?=[-_][^\\\\S])(\\-)", " ");
227 249
return result;
228 250
}
229
-
230
- private void fireResultUri(String uri) {
231
- this.uri = uri;
232
- ValueChangeEvent.fire(this, uri);
233
- }
234
-
251
+
235 252
public String getName() {
236 253
return name;
237 254
}
238
-
239
- /**
240
- * Returns <code>null</code> if the trimmed URL field contents are empty
241
- */
242
- public String getURL() {
243
- final String trimmedUrl = (uri != null ? uri : "").trim();
244
- return trimmedUrl.isEmpty() ? null : trimmedUrl;
255
+
256
+ public List<String> getUris() {
257
+ return new ArrayList<>(uriList.keySet());
245 258
}
246 259
247
- public void setURL(String imageURL) {
248
- if (imageURL == null) {
260
+ private void setUris(Map<String, String> uris, boolean fireEvent) {
261
+ if (uris != null) {
262
+ this.uriList = uris;
263
+ } else {
264
+ this.uriList.clear();
265
+ }
266
+ if (uris == null || uris.size() == 0) {
249 267
urlTextBox.setValue("");
250 268
urlTextBox.setTitle("");
251
- uri = null;
252
- removeButton.setEnabled(false);
253 269
} else {
254
- urlTextBox.setValue(imageURL);
255
- uri = imageURL;
256
- removeButton.setEnabled(true);
270
+ urlTextBox.setValue(String.join(URI_DELIMITER, uriList.keySet()));
271
+ }
272
+ removeButton.setEnabled(uriList != null);
273
+ if (fireEvent) {
274
+ ValueChangeEvent.fire(this, uriList);
257 275
}
258 276
}
259
-
260
- private void setUriAndFireEvent(String uri) {
261
- this.uri = uri;
277
+
278
+ /**
279
+ * Returns <code>null</code> if the trimmed URI field contents are empty
280
+ * enabled this method will only return the first URI.
281
+ * @see {@link #getUris()}
282
+ */
283
+ public String getUri() {
284
+ String result = null;
285
+ if (uriList != null && uriList.size() > 0 && uriList.keySet().iterator().next() != null) {
286
+ result = uriList.keySet().iterator().next().trim();
287
+ }
288
+ return result;
289
+ }
290
+
291
+ public void setUri(String uri) {
292
+ setUri(uri, false);
293
+ }
294
+
295
+ public void setUri(String uri, boolean fireEvent) {
296
+ this.uriList.clear();
262 297
if (uri == null) {
263 298
urlTextBox.setValue("");
264 299
urlTextBox.setTitle("");
300
+ } else {
301
+ this.uriList.put(uri, extractFileName(uri));
302
+ urlTextBox.setValue(uri);
265 303
}
266 304
removeButton.setEnabled(uri != null);
267
- ValueChangeEvent.fire(this, uri);
305
+ if (fireEvent) {
306
+ ValueChangeEvent.fire(this, uriList);
307
+ }
268 308
}
269 309
310
+ private String extractFileName(String url) {
311
+ return url.substring(url.lastIndexOf("/") + 1);
312
+ }
313
+
270 314
public FocusWidget getInitialFocusWidget() {
271 315
return urlTextBox;
272 316
}
273 317
274
- private HandlerRegistration addChangeHandler(ChangeHandler handler) {
275
- return addDomHandler(handler, ChangeEvent.getType());
276
- }
277
-
278 318
@Override
279
- public HandlerRegistration addValueChangeHandler(ValueChangeHandler<String> handler) {
319
+ public HandlerRegistration addValueChangeHandler(ValueChangeHandler<Map<String, String>> handler) {
280 320
if (!valueChangeHandlerInitialized) {
281 321
valueChangeHandlerInitialized = true;
282
- addChangeHandler(new ChangeHandler() {
322
+ addDomHandler(new ChangeHandler() {
283 323
public void onChange(ChangeEvent event) {
284 324
ValueChangeEvent.fire(URLFieldWithFileUpload.this, getValue());
285 325
}
286
- });
326
+ }, ChangeEvent.getType());
287 327
}
288 328
return addHandler(handler, ValueChangeEvent.getType());
289 329
}
290 330
291 331
@Override
292
- public String getValue() {
293
- return this.uri;
332
+ public Map<String, String> getValue() {
333
+ return uriList;
294 334
}
295 335
296 336
@Override
297
- public void setValue(String value) {
298
- setURL(value);
337
+ public void setValue(Map<String, String> value) {
338
+ setUris(value, false);
299 339
}
300 340
301 341
@Override
302
- public void setValue(String value, boolean fireEvents) {
303
- setValue(value);
304
- if (fireEvents) {
305
- ValueChangeEvent.fire(this, value);
306
- }
342
+ public void setValue(Map<String, String> value, boolean fireEvents) {
343
+ setUris(value, fireEvents);
307 344
}
308
-
345
+
309 346
public void setStartUploadEvent(StartUploadEvent startUploadEvent) {
310 347
this.startUploadEvent = startUploadEvent;
311 348
}
... ...
@@ -314,16 +351,6 @@ public class URLFieldWithFileUpload extends Composite implements HasValue<String
314 351
this.endUploadEvent = endUploadEvent;
315 352
}
316 353
317
- /**
318
- * See https://github.com/twilson63/ngUpload/issues/43 and
319
- * https://www.sencha.com/forum/showthread.php?132949-Fileupload-Invalid-JSON-string. The JSON response of the file
320
- * upload is wrapped by a &lt;pre&gt; element which needs to be stripped off if present to allow the JSON parser to
321
- * succeed.
322
- */
323
- private JSONValue parseAfterReplacingSurroundingPreElement(String jsonString) {
324
- return JSONParser.parseStrict(jsonString.replaceFirst("<pre[^>]*>(.*)</pre>", "$1"));
325
- }
326
-
327 354
public void setUploadEnabled(boolean uploadEnabled) {
328 355
uploadFormPanel.remove(uploadPanel);
329 356
if (uploadEnabled) {
... ...
@@ -338,8 +365,9 @@ public class URLFieldWithFileUpload extends Composite implements HasValue<String
338 365
}
339 366
340 367
public void deleteCurrentFile() {
341
- final String localUri = uri;
342
- deleteFileOnServer(localUri);
368
+ for (final String localUri : uriList.keySet()) {
369
+ deleteFileOnServer(localUri);
370
+ }
343 371
}
344 372
345 373
private void deleteFileOnServer(String localUri) {
java/com.sap.sse.gwt/GWT xdStorage Sample SDM.launch
... ...
@@ -23,6 +23,7 @@
23 23
<booleanAttribute key="org.eclipse.jdt.debug.ui.CONSIDER_INHERITED_MAIN" value="true"/>
24 24
<booleanAttribute key="org.eclipse.jdt.debug.ui.INCLUDE_EXTERNAL_JARS" value="true"/>
25 25
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_CLASSPATH_ONLY_JAR" value="false"/>
26
+ <booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
26 27
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
27 28
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8&quot; javaProject=&quot;com.sap.sse.gwt&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#13;&#10;"/>
28 29
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.gwt/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/dialog/DataEntryDialog.java
... ...
@@ -5,6 +5,7 @@ import java.util.Date;
5 5
import java.util.List;
6 6
import java.util.function.Supplier;
7 7
8
+import com.google.gwt.core.client.GWT;
8 9
import com.google.gwt.core.client.Scheduler;
9 10
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
10 11
import com.google.gwt.dom.client.Element;
... ...
@@ -65,6 +66,7 @@ public abstract class DataEntryDialog<T> {
65 66
private final Button okButton;
66 67
private final Button cancelButton;
67 68
private final HTML statusLabel;
69
+ private final FlowPanel errorListPanel;
68 70
private final FlowPanel panelForAdditionalWidget;
69 71
private final DockPanel buttonPanel;
70 72
private final FlowPanel rightButtonPanel;
... ...
@@ -139,6 +141,8 @@ public abstract class DataEntryDialog<T> {
139 141
statusLabel = new HTML(SafeHtmlUtils.fromSafeConstant("&nbsp;"));
140 142
statusLabel.ensureDebugId("StatusLabel");
141 143
dialogFPanel.add(statusLabel);
144
+ errorListPanel = new FlowPanel();
145
+ dialogFPanel.add(errorListPanel);
142 146
if (message != null) {
143 147
Label messageLabel = new Label(message);
144 148
messageLabel.addStyleName("dialogMessageLabel");
... ...
@@ -222,13 +226,25 @@ public abstract class DataEntryDialog<T> {
222 226
*/
223 227
@Override
224 228
public void onSuccess(String errorMessage) {
229
+ errorListPanel.clear();
225 230
boolean invalidState = errorMessage != null && !errorMessage.isEmpty();
226 231
if (invalidState != dialogInInvalidState) {
227 232
onInvalidStateChanged(invalidState);
228 233
}
229 234
if (invalidState) {
230
- statusLabel.setHTML(SafeHtmlUtils.fromString(errorMessage));
231
- statusLabel.setStyleName("errorLabel");
235
+ String[] errors = errorMessage.split("\\r?\\n|\\r");
236
+ if (errors.length > 1) {
237
+ for (int i=0; i< errors.length; i++) {
238
+ GWT.log(i + " - " + errors[i]);
239
+ HTML errorLabel = new HTML(SafeHtmlUtils.fromString(errors[i]));
240
+ errorLabel.setStyleName("errorLabel");
241
+ errorListPanel.add(errorLabel);
242
+ }
243
+ statusLabel.setHTML(SafeHtmlUtils.fromSafeConstant("&nbsp;"));
244
+ } else {
245
+ statusLabel.setHTML(SafeHtmlUtils.fromString(errorMessage));
246
+ statusLabel.setStyleName("errorLabel");
247
+ }
232 248
getOkButton().setEnabled(false);
233 249
} else {
234 250
statusLabel.setHTML(SafeHtmlUtils.fromSafeConstant("&nbsp;"));
java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/fileupload/FileUploadUtil.java
... ...
@@ -0,0 +1,22 @@
1
+package com.sap.sse.gwt.client.fileupload;
2
+
3
+import com.google.gwt.user.client.ui.FormPanel;
4
+import com.google.gwt.user.client.ui.FormPanel.SubmitCompleteEvent;
5
+
6
+public class FileUploadUtil {
7
+ /**
8
+ * For file uploads through {@link FormPanel} elements, if the response is a JSON message (content type
9
+ * {@code application/json}) then the way the {@link FormPanel} intercepts this response (namely through
10
+ * an auxiliary {@code iframe} element into which the response is loaded) the content may get wrapped by
11
+ * a combination of {@code <pre>} and {@code </pre>} elements. These elements persist through the
12
+ * {@link SubmitCompleteEvent#getResults()} method, and hence must be stripped off before trying to
13
+ * parse the results as JSON. This is that this method does.<p>
14
+ *
15
+ * Conversely, trying to use {@code text/html} as content encoding leads some
16
+ * browsers---especially on mobile devices---to do ugly things to the content returned, such as replacing digit
17
+ * sequences by a corresponding {@code <a>} element that allows the user to dial that number with the phone app...
18
+ */
19
+ public static String getApplicationJsonContent(SubmitCompleteEvent submitResultWithApplicationJsonContent) {
20
+ return submitResultWithApplicationJsonContent.getResults().replaceFirst("<pre[^>]*>(.*)</pre>", "$1");
21
+ }
22
+}
java/com.sap.sse.security.ui/GWT Security DevMode.launch
... ...
@@ -25,6 +25,7 @@
25 25
</listAttribute>
26 26
<booleanAttribute key="org.eclipse.jdt.debug.ui.CONSIDER_INHERITED_MAIN" value="true"/>
27 27
<booleanAttribute key="org.eclipse.jdt.debug.ui.INCLUDE_EXTERNAL_JARS" value="true"/>
28
+ <booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
28 29
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
29 30
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8&quot; javaProject=&quot;com.sap.sailing.gwt.ui&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#10;"/>
30 31
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.gwt.ui/src/main/resources&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
java/com.sap.sse.security.ui/GWT Security SDM.launch
... ...
@@ -27,6 +27,7 @@
27 27
<booleanAttribute key="org.eclipse.jdt.debug.ui.CONSIDER_INHERITED_MAIN" value="true"/>
28 28
<booleanAttribute key="org.eclipse.jdt.debug.ui.INCLUDE_EXTERNAL_JARS" value="true"/>
29 29
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_CLASSPATH_ONLY_JAR" value="false"/>
30
+ <booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
30 31
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
31 32
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8&quot; javaProject=&quot;com.sap.sailing.gwt.ui&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#10;"/>
32 33
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.gwt.ui/src/main/resources&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages.java
... ...
@@ -107,6 +107,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages {
107 107
String refresh();
108 108
String preferredLanguage();
109 109
String cannotResetInvalidURL();
110
+ String cannotUploadVideosAndImagesTogether();
110 111
String showMoreInfoLogin();
111 112
String moreInfo();
112 113
String dismiss();
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages.properties
... ...
@@ -100,6 +100,7 @@ error=Unknown error
100 100
refresh=Refresh
101 101
preferredLanguage=Preferred language
102 102
cannotResetInvalidURL=Invalid reset URL...
103
+cannotUploadVideosAndImagesTogether=Cannot upload videos and images together, please upload them separately.
103 104
sharedSettingsLink=Link with settings
104 105
makeDefault=Make default
105 106
makeDefaultInProgress=In progress...
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/premium/PaywallResolver.java
... ...
@@ -75,7 +75,7 @@ public class PaywallResolver {
75 75
76 76
/**
77 77
* Get the permission based on the given action and secured DTO context object. If the disabled flag (Bug 5696
78
- * workaraound) is set on {@link PaywallResolver} the result will always be TRUE. If no action is set the default
78
+ * workaround) is set on {@link PaywallResolver} the result will always be TRUE. If no action is set the default
79 79
* {@link DefaultActions}.READ will be used. From the {@link UserService} there is the default behavior, that if the
80 80
* context object is NULL the result is set to FALSE.
81 81
*
java/com.sap.sse.security.ui/src/main/resources/com/sap/sse/security/ui/EditProfile.gwt.xml
... ...
@@ -11,7 +11,7 @@
11 11
<!-- Other module inherits -->
12 12
<inherits name="com.sap.sse.gwt.Settings" />
13 13
<inherits name="com.sap.sse.Security" />
14
- <inherits name="com.sap.sse.security.ui.SecurityLocalesAllPermutations" />
14
+ <inherits name="com.sap.sse.security.ui.SecurityLocalesSinglePermutation" />
15 15
<inherits name="com.sap.sse.security.SSESecuritySerializers" />
16 16
17 17
<!-- Specify the app entry point class. -->
java/com.sap.sse.security.ui/src/main/resources/com/sap/sse/security/ui/EmailValidation.gwt.xml
... ...
@@ -10,7 +10,7 @@
10 10
<!-- Other module inherits -->
11 11
<inherits name="com.sap.sse.gwt.Settings" />
12 12
<inherits name="com.sap.sse.Security" />
13
- <inherits name="com.sap.sse.security.ui.SecurityLocalesAllPermutations" />
13
+ <inherits name="com.sap.sse.security.ui.SecurityLocalesSinglePermutation" />
14 14
<inherits name="com.sap.sse.security.SSESecuritySerializers" />
15 15
16 16
<super-source path="jre" />
java/com.sap.sse.security.ui/src/main/resources/com/sap/sse/security/ui/Login.gwt.xml
... ...
@@ -4,7 +4,7 @@
4 4
<inherits name='com.google.gwt.debug.Debug' />
5 5
<inherits name="com.google.gwt.resources.Resources" />
6 6
7
- <inherits name="com.sap.sse.security.ui.SecurityLocalesAllPermutations" />
7
+ <inherits name="com.sap.sse.security.ui.SecurityLocalesSinglePermutation" />
8 8
<inherits name="com.sap.sse.security.ui.LoginPanel"/>
9 9
10 10
<!-- Specify the app entry point class. -->
java/com.sap.sse.security.ui/src/main/resources/com/sap/sse/security/ui/LoginPanel.gwt.xml
... ...
@@ -9,7 +9,7 @@
9 9
<inherits name='com.sap.sse.gwt.Settings'/>
10 10
<inherits name="com.sap.sse.gwt.Settings" />
11 11
<inherits name="com.sap.sse.Security" />
12
- <inherits name="com.sap.sse.security.ui.SecurityLocalesAllPermutations" />
12
+ <inherits name="com.sap.sse.security.ui.SecurityLocalesSinglePermutation" />
13 13
<inherits name="com.sap.sse.security.SSESecuritySerializers" />
14 14
15 15
<set-property name="gwt.logging.enabled" value="TRUE"/>
java/com.sap.sse.security.ui/src/main/resources/com/sap/sse/security/ui/OAuthLogin.gwt.xml
... ...
@@ -9,7 +9,7 @@
9 9
10 10
<!-- Other module inherits -->
11 11
<inherits name="com.sap.sse.gwt.Settings" />
12
- <inherits name="com.sap.sse.security.ui.SecurityLocalesAllPermutations" />
12
+ <inherits name="com.sap.sse.security.ui.SecurityLocalesSinglePermutation" />
13 13
14 14
<!-- Specify the app entry point class. -->
15 15
<entry-point class='com.sap.sse.security.ui.oauth.client.OAuthLoginEntryPoint'/>
java/com.sap.sse.security.ui/src/main/resources/com/sap/sse/security/ui/Register.gwt.xml
... ...
@@ -9,7 +9,7 @@
9 9
<!-- Other module inherits -->
10 10
<inherits name="com.sap.sse.gwt.Settings" />
11 11
<inherits name="com.sap.sse.Security" />
12
- <inherits name="com.sap.sse.security.ui.SecurityLocalesAllPermutations" />
12
+ <inherits name="com.sap.sse.security.ui.SecurityLocalesSinglePermutation" />
13 13
<inherits name="com.sap.sse.security.SSESecuritySerializers" />
14 14
15 15
<super-source path="jre" />
java/com.sap.sse.security.ui/src/main/resources/com/sap/sse/security/ui/UserManagement.gwt.xml
... ...
@@ -11,7 +11,7 @@
11 11
12 12
<inherits name="com.google.gwt.resources.Resources" />
13 13
14
- <inherits name="com.sap.sse.security.ui.SecurityLocalesAllPermutations" />
14
+ <inherits name="com.sap.sse.security.ui.SecurityLocalesSinglePermutation" />
15 15
<inherits name="com.sap.sse.security.ui.LoginPanel"/>
16 16
17 17
<!-- Specify the paths for translatable code -->
java/com.sap.sse.test/src/com/sap/sse/test/jsonsimple/TestNumericStringWithUnderscoresSerialization.java
... ...
@@ -0,0 +1,43 @@
1
+package com.sap.sse.test.jsonsimple;
2
+
3
+import static org.junit.Assert.assertEquals;
4
+
5
+import org.json.simple.JSONObject;
6
+import org.json.simple.parser.JSONParser;
7
+import org.json.simple.parser.ParseException;
8
+import org.junit.Test;
9
+
10
+/**
11
+ * We face a strange issue with JSON String literals that represent file names coming from an iPhone
12
+ * where the file name contains mostly digits, sometimes separated by two underscore characters.
13
+ * These literals, it seems, are not properly getting parsed on Safari on iPhones.<p>
14
+ *
15
+ * Does this have to do with the possibility to use single underscores in numeric literals to
16
+ * separate groups of digits, where two consecutive underscores are forbidden?<p>
17
+ *
18
+ * This test is asserting that at least no issues in this regard exist in the <tt>org.json.simple</tt>
19
+ * library we're using for (de-)serializtion on the server side.
20
+ *
21
+ * @author Axel Uhl (d043530)
22
+ *
23
+ */
24
+public class TestNumericStringWithUnderscoresSerialization {
25
+ private static final String FILE_NAME = "fileName";
26
+
27
+ @Test
28
+ public void testDoubleUnderscoreInStringStartingWithDigits() throws ParseException {
29
+ assertSerializationOfValue("012__3456");
30
+ assertSerializationOfValue("012__3456.jpeg");
31
+ assertSerializationOfValue("012___3456");
32
+ assertSerializationOfValue("01__3456");
33
+ }
34
+
35
+ private void assertSerializationOfValue(final String value) throws ParseException {
36
+ final JSONObject o = new JSONObject();
37
+ o.put(FILE_NAME, value);
38
+ final String oAsJsonString = o.toJSONString();
39
+ final JSONParser parser = new JSONParser();
40
+ final JSONObject oParsed = (JSONObject) parser.parse(oAsJsonString);
41
+ assertEquals(value, oParsed.get(FILE_NAME));
42
+ }
43
+}
wiki/howto/onboarding.md
... ...
@@ -233,5 +233,10 @@ When a file has "wrong line endings" (line endings are different to what is conf
233 233
234 234
See [RaceCommittee App](/wiki/info/mobile/racecommittee-app) for more information regarding the mobile app.
235 235
236
+### Java on ARM based Mac troubleshooting
237
+
238
+Irritating behavior can occur on ARM-based processors, such as on a MacBook. There are some problems, especially with graphical functions. There are cases where javax.imageio.ImageIO, javax.imageio.ImageReader or java.awt.graphics2D stops responding without error message.
239
+In such cases it might help to set AWT to headless mode (`-Djava.awt.headless=true`, see [stackoverflow](https://stackoverflow.com/questions/13796611/imageio-read-with-mac for more information)).
240
+
236 241
### GWT Browser Plugin
237 242
Install the GWT Browser Plugin for the GWT Development mode. As of 2016-08-31 Firefox is the only browser supporting the GWT plugin, you have to download Firefox version 24 for it to work. The Plugin can be found on this page: [https://code.google.com/archive/p/google-web-toolkit/downloads](https://code.google.com/archive/p/google-web-toolkit/downloads)