3f36adb950801a27f5a6830dcd5f8507c1f1c5e0
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="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8" javaProject="com.sap.sailing.gwt.ui" path="1" type="4"/> "/> |
| 25 | 26 | <listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry id="org.eclipse.jdt.launching.classpathentry.defaultClasspath"> <memento exportedEntriesOnly="false" project="com.sap.sailing.dashboards.gwt"/> </runtimeClasspathEntry> "/> |
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="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8" javaProject="com.sap.sailing.gwt.ui" path="1" type="4"/> "/> |
| 27 | 28 | <listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry id="org.eclipse.jdt.launching.classpathentry.defaultClasspath"> <memento exportedEntriesOnly="false" project="com.sap.sailing.dashboards.gwt"/> </runtimeClasspathEntry> "/> |
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="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8" javaProject="com.sap.sailing.gwt.ui" path="1" type="4"/> "/> |
| 38 | 39 | <listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.gwt.ui/src/main/resources" path="3" type="2"/> "/> |
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="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8" javaProject="com.sap.sailing.gwt.ui" path="1" type="4"/> "/> |
| 30 | 31 | <listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.gwt.ui/src/main/resources" path="3" type="2"/> "/> |
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="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8" javaProject="com.sap.sailing.gwt.ui" path="1" type="4"/> "/> |
| 27 | 28 | <listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.gwt.ui/src/main/resources" path="3" type="2"/> "/> |
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="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8" javaProject="com.sap.sailing.gwt.ui" path="1" type="4"/> "/> |
| 43 | 44 | <listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.gwt.ui/src/main/resources" path="3" type="2"/> "/> |
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(" ")); |
| 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(" ")); |
|
| 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(" ")); |
|
| 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(" ")); |
|
| 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(" ")); |
|
| 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 <pre> 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 <pre> 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="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8" javaProject="com.sap.sse.gwt" path="1" type="4"/> "/> |
| 28 | 29 | <listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.gwt/src" path="3" type="2"/> "/> |
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(" ")); |
| 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(" ")); |
|
| 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(" ")); |
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="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8" javaProject="com.sap.sailing.gwt.ui" path="1" type="4"/> "/> |
| 30 | 31 | <listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.gwt.ui/src/main/resources" path="3" type="2"/> "/> |
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="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8" javaProject="com.sap.sailing.gwt.ui" path="1" type="4"/> "/> |
| 32 | 33 | <listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.gwt.ui/src/main/resources" path="3" type="2"/> "/> |
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) |