001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software, please see the
018 * company website: https://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: https://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.ade.upload.client.ui;
029
030import org.opencms.ade.upload.client.I_CmsUploadContext;
031import org.opencms.ade.upload.client.Messages;
032import org.opencms.ade.upload.client.ui.css.I_CmsLayoutBundle;
033import org.opencms.ade.upload.client.ui.css.I_CmsLayoutBundle.I_CmsUploadCss;
034import org.opencms.gwt.client.CmsCoreProvider;
035import org.opencms.gwt.client.rpc.CmsRpcAction;
036import org.opencms.gwt.client.ui.CmsErrorDialog;
037import org.opencms.gwt.client.ui.CmsList;
038import org.opencms.gwt.client.ui.CmsListItem;
039import org.opencms.gwt.client.ui.CmsListItemWidget;
040import org.opencms.gwt.client.ui.CmsListItemWidget.Background;
041import org.opencms.gwt.client.ui.CmsLoadingAnimation;
042import org.opencms.gwt.client.ui.CmsNotification;
043import org.opencms.gwt.client.ui.CmsNotification.Type;
044import org.opencms.gwt.client.ui.CmsPopup;
045import org.opencms.gwt.client.ui.CmsPushButton;
046import org.opencms.gwt.client.ui.CmsScrollPanel;
047import org.opencms.gwt.client.ui.CmsToggleButton;
048import org.opencms.gwt.client.ui.CmsVirusReport;
049import org.opencms.gwt.client.ui.FontOpenCms;
050import org.opencms.gwt.client.ui.I_CmsButton;
051import org.opencms.gwt.client.ui.I_CmsListItem;
052import org.opencms.gwt.client.ui.css.I_CmsConstantsBundle;
053import org.opencms.gwt.client.ui.input.CmsCheckBox;
054import org.opencms.gwt.client.ui.input.upload.CmsFileInfo;
055import org.opencms.gwt.client.ui.input.upload.CmsFileInput;
056import org.opencms.gwt.client.ui.input.upload.CmsUploadButton;
057import org.opencms.gwt.client.ui.input.upload.CmsUploadProgressInfo;
058import org.opencms.gwt.client.ui.input.upload.CmsUploader;
059import org.opencms.gwt.client.ui.input.upload.I_CmsUploadButton;
060import org.opencms.gwt.client.ui.input.upload.I_CmsUploadDialog;
061import org.opencms.gwt.client.util.CmsChangeHeightAnimation;
062import org.opencms.gwt.client.util.CmsDomUtil;
063import org.opencms.gwt.shared.CmsListInfoBean;
064import org.opencms.gwt.shared.CmsUploadFileBean;
065import org.opencms.gwt.shared.CmsUploadProgessInfo;
066import org.opencms.gwt.shared.CmsUploadRestrictionInfo;
067import org.opencms.gwt.shared.I_CmsUploadConstants;
068import org.opencms.gwt.shared.rpc.I_CmsUploadService;
069import org.opencms.gwt.shared.rpc.I_CmsUploadServiceAsync;
070import org.opencms.util.CmsStringUtil;
071import org.opencms.util.CmsUUID;
072
073import java.util.ArrayList;
074import java.util.Arrays;
075import java.util.Collections;
076import java.util.HashMap;
077import java.util.HashSet;
078import java.util.List;
079import java.util.Map;
080import java.util.Set;
081import java.util.stream.Collectors;
082
083import com.google.common.base.Supplier;
084import com.google.gwt.core.client.GWT;
085import com.google.gwt.core.client.Scheduler;
086import com.google.gwt.core.client.Scheduler.ScheduledCommand;
087import com.google.gwt.dom.client.Style.Display;
088import com.google.gwt.dom.client.Style.Unit;
089import com.google.gwt.event.dom.client.ClickEvent;
090import com.google.gwt.event.dom.client.ClickHandler;
091import com.google.gwt.event.logical.shared.CloseEvent;
092import com.google.gwt.event.logical.shared.CloseHandler;
093import com.google.gwt.event.shared.HandlerRegistration;
094import com.google.gwt.json.client.JSONArray;
095import com.google.gwt.json.client.JSONObject;
096import com.google.gwt.json.client.JSONParser;
097import com.google.gwt.json.client.JSONString;
098import com.google.gwt.json.client.JSONValue;
099import com.google.gwt.user.client.Command;
100import com.google.gwt.user.client.Timer;
101import com.google.gwt.user.client.rpc.AsyncCallback;
102import com.google.gwt.user.client.rpc.ServiceDefTarget;
103import com.google.gwt.user.client.ui.FlowPanel;
104import com.google.gwt.user.client.ui.FormPanel;
105import com.google.gwt.user.client.ui.HTML;
106import com.google.gwt.user.client.ui.PopupPanel;
107import com.google.gwt.user.client.ui.UIObject;
108import com.google.gwt.user.client.ui.Widget;
109
110/**
111 * Provides an upload dialog.<p>
112 *
113 * @since 8.0.0
114 */
115public abstract class A_CmsUploadDialog extends CmsPopup implements I_CmsUploadDialog {
116
117    /** The minimal height of the content wrapper. */
118    private static final int MIN_CONTENT_HEIGHT = 110;
119
120    /** Text metrics key. */
121    private static final String TM_FILE_UPLOAD_LIST = "FileUploadList";
122
123    /** The interval for updating the progress information in milliseconds. */
124    private static final int UPDATE_PROGRESS_INTERVALL = 1000;
125
126    /** The upload context. */
127    protected I_CmsUploadContext m_context;
128
129    /** The drag and drop message. */
130    protected HTML m_dragAndDropMessage;
131
132    /** The scroll panel. */
133    protected CmsScrollPanel m_scrollPanel;
134
135    /** The uploaded file names. */
136    protected List<String> m_uploadedFiles;
137
138    /** Signals that the upload dialog was canceled. */
139    boolean m_canceled;
140
141    /** True if the target folder is given as a root path. */
142    boolean m_isTargetRootPath;
143
144    /** Stores all files that were added. */
145    private Map<String, CmsFileInfo> m_allFiles;
146
147    /** Signals that the client currently loading. */
148    private boolean m_clientLoading;
149
150    /** The close handler. */
151    private CloseHandler<PopupPanel> m_closeHandler;
152
153    /** The sum of all file sizes. */
154    private long m_contentLength;
155
156    /** A flow panel with a dynamic height. */
157    private FlowPanel m_contentWrapper;
158
159    /** The user information text widget. */
160    private HTML m_dialogInfo;
161
162    /** The list of file item widgets. */
163    private CmsList<I_CmsListItem> m_fileList;
164
165    /** The list of filenames that should be unziped on the server. */
166    private List<String> m_filesToUnzip;
167
168    /** The Map of files to upload. */
169    private Map<String, CmsFileInfo> m_filesToUpload;
170
171    /** Stores the content height of the selection dialog. */
172    private int m_firstContentHeight;
173
174    /** Stores the height of the user information text widget of the selection dialog. */
175    private int m_firstInfoHeight;
176
177    /** Stores the height of the summary. */
178    private int m_firstSummaryHeight;
179
180    /** A local reference to the default gwt CSS. */
181    private org.opencms.gwt.client.ui.css.I_CmsLayoutBundle m_gwtCss = org.opencms.gwt.client.ui.css.I_CmsLayoutBundle.INSTANCE;
182
183    /** The close handler registration. */
184    private HandlerRegistration m_handlerReg;
185
186    /** Stores the list items of all added files. */
187    private Map<String, CmsListItem> m_listItems;
188
189    /** A panel for showing client loading. */
190    private FlowPanel m_loadingPanel;
191
192    /** A timer to delay the loading animation. */
193    private Timer m_loadingTimer;
194
195    /** The main panel. */
196    private FlowPanel m_mainPanel;
197
198    /** The OK button. */
199    private CmsPushButton m_okButton;
200
201    /** The post-create handler. */
202    private String m_postCreateHandler;
203
204    /** The progress bar for the upload process. */
205    private CmsUploadProgressInfo m_progressInfo;
206
207    /** The names of restricted upload files. */
208    private Set<String> m_restricted = new HashSet<>();
209
210    /** Signals whether the selection is done or not. */
211    private boolean m_selectionDone;
212
213    /** The user information text widget. */
214    private HTML m_selectionSummary;
215
216    /** The target folder to upload the selected files. */
217    private String m_targetFolder;
218
219    /** The timer for updating the progress. */
220    private Timer m_updateProgressTimer = new Timer() {
221
222        /**
223         * @see com.google.gwt.user.client.Timer#run()
224         */
225        @Override
226        public void run() {
227
228            updateProgress();
229        }
230    };
231
232    /** The upload button of this dialog. */
233    private I_CmsUploadButton m_uploadButton;
234
235    /** The upload service instance. */
236    private I_CmsUploadServiceAsync m_uploadService;
237
238    /**
239     * Default constructor.<p>
240     */
241    public A_CmsUploadDialog() {
242
243        super(Messages.get().key(Messages.GUI_UPLOAD_DIALOG_TITLE_1));
244
245        I_CmsLayoutBundle.INSTANCE.uploadCss().ensureInjected();
246        setModal(true);
247        setGlassEnabled(true);
248        catchNotifications();
249        setWidth(CmsPopup.DEFAULT_WIDTH);
250
251        // create a map that stores all files (upload, existing, invalid)
252        m_allFiles = new HashMap<String, CmsFileInfo>();
253        // create a map the holds all the list items for the selection dialog
254        m_listItems = new HashMap<String, CmsListItem>();
255        m_filesToUnzip = new ArrayList<String>();
256        m_fileList = new CmsList<I_CmsListItem>();
257        m_fileList.truncate(TM_FILE_UPLOAD_LIST, CmsPopup.DEFAULT_WIDTH - 50);
258
259        // initialize a map that stores all the files that should be uploaded
260        m_filesToUpload = new HashMap<String, CmsFileInfo>();
261
262        // create the main panel
263        m_mainPanel = new FlowPanel();
264
265        // add the user info to the main panel
266        m_dialogInfo = new HTML();
267        m_dialogInfo.addStyleName(I_CmsLayoutBundle.INSTANCE.uploadCss().dialogInfo());
268        m_mainPanel.add(m_dialogInfo);
269        m_scrollPanel = GWT.create(CmsScrollPanel.class);
270        m_scrollPanel.getElement().getStyle().setPropertyPx("minHeight", MIN_CONTENT_HEIGHT);
271        m_scrollPanel.addStyleName(I_CmsLayoutBundle.INSTANCE.uploadCss().mainContentWidget());
272        m_scrollPanel.addStyleName(m_gwtCss.generalCss().cornerAll());
273        m_mainPanel.add(m_scrollPanel);
274        // add the content wrapper
275        m_contentWrapper = new FlowPanel();
276        m_contentWrapper.add(m_fileList);
277        m_scrollPanel.add(m_contentWrapper);
278
279        m_selectionSummary = new HTML();
280        m_selectionSummary.addStyleName(I_CmsLayoutBundle.INSTANCE.uploadCss().summary());
281        m_mainPanel.add(m_selectionSummary);
282
283        // set the main panel as content of the popup
284        setMainContent(m_mainPanel);
285
286        addCloseHandler(new CloseHandler<PopupPanel>() {
287
288            /**
289             * @see com.google.gwt.event.logical.shared.CloseHandler#onClose(com.google.gwt.event.logical.shared.CloseEvent)
290             */
291            public void onClose(CloseEvent<PopupPanel> e) {
292
293                if (m_context != null) {
294                    m_context.onUploadFinished(m_uploadedFiles);
295                }
296            }
297        });
298
299        // create and add the "OK", "Cancel" and upload button
300        createButtons();
301    }
302
303    /**
304     * @see org.opencms.gwt.client.ui.CmsPopup#addCloseHandler(com.google.gwt.event.logical.shared.CloseHandler)
305     */
306    @Override
307    public HandlerRegistration addCloseHandler(CloseHandler<PopupPanel> handler) {
308
309        m_closeHandler = handler;
310        m_handlerReg = super.addCloseHandler(handler);
311        return m_handlerReg;
312    }
313
314    /**
315     * Creates a bean that can be used for the list item widget.<p>
316     *
317     * @param file the info to create the bean for
318     *
319     * @return a list info bean
320     */
321    public abstract CmsListInfoBean createInfoBean(CmsFileInfo file);
322
323    /**
324     * Returns the massage for too large files.<p>
325     *
326     * @param file the file
327     *
328     * @return the message
329     */
330    public abstract String getFileSizeTooLargeMessage(CmsFileInfo file);
331
332    /**
333     * Returns <code>true</code> if the file is too large, <code>false</code> otherwise.<p>
334     *
335     * @param cmsFileInfo the file to check
336     *
337     * @return <code>true</code> if the file is too large, <code>false</code> otherwise
338     */
339    public abstract boolean isTooLarge(CmsFileInfo cmsFileInfo);
340
341    /**
342     * Loads and shows this dialog.<p>
343     */
344    public void loadAndShow() {
345
346        // enable or disable the OK button
347        if (getFilesToUpload().isEmpty()) {
348            disableOKButton(Messages.get().key(Messages.GUI_UPLOAD_NOTIFICATION_NO_FILES_0));
349            if (m_restricted.isEmpty()) {
350                setDragAndDropMessage();
351            } else {
352                removeDragAndDropMessage();
353            }
354        } else {
355            enableOKButton();
356            removeDragAndDropMessage();
357        }
358        m_uploadedFiles = null;
359        // set the user info
360        displayDialogInfo(Messages.get().key(Messages.GUI_UPLOAD_INFO_SELECTION_0), false);
361        // set the selection summary
362        updateSummary();
363
364        // add a upload button
365        m_uploadButton.createFileInput();
366
367        // show the popup
368        if (!isShowing()) {
369            Scheduler.get().scheduleDeferred(new ScheduledCommand() {
370
371                /**
372                 * @see com.google.gwt.core.client.Scheduler.ScheduledCommand#execute()
373                 */
374                public void execute() {
375
376                    setContentWrapperHeight();
377                    center();
378                }
379            });
380        }
381        center();
382    }
383
384    /**
385     * @see org.opencms.gwt.client.ui.input.upload.I_CmsUploadDialog#parseResponse(java.lang.String)
386     */
387    public void parseResponse(String results) {
388
389        cancelUpdateProgress();
390        stopLoadingAnimation();
391
392        if ((!m_canceled) && CmsStringUtil.isNotEmptyOrWhitespaceOnly(results)) {
393            JSONObject jsonObject = JSONParser.parseStrict(results).isObject();
394            boolean success = jsonObject.get(I_CmsUploadConstants.KEY_SUCCESS).isBoolean().booleanValue();
395            // If the upload is done so fast that we did not receive any progress information, then
396            // the content length is unknown. For that reason take the request size to show how
397            // much bytes were uploaded.
398            double size = jsonObject.get(I_CmsUploadConstants.KEY_REQUEST_SIZE).isNumber().doubleValue();
399            long requestSize = Double.valueOf(size).longValue();
400            if (m_contentLength == 0) {
401                m_contentLength = requestSize;
402            }
403            if (success) {
404                m_uploadedFiles = new ArrayList<String>();
405                List<String> uploadedFileIds = new ArrayList<String>();
406                displayDialogInfo(
407                    org.opencms.gwt.client.Messages.get().key(
408                        org.opencms.gwt.client.Messages.GUI_UPLOAD_INFO_FINISHING_0),
409                    false);
410                JSONValue uploadedFilesVal = jsonObject.get(I_CmsUploadConstants.KEY_UPLOADED_FILE_NAMES);
411                JSONValue uploadHook = jsonObject.get(I_CmsUploadConstants.KEY_UPLOAD_HOOK);
412                JSONValue uploadedFileIdsVal = jsonObject.get(I_CmsUploadConstants.KEY_UPLOADED_FILES);
413                JSONArray uploadedFileIdsArray = uploadedFileIdsVal.isArray();
414
415                String hookUri = null;
416                if ((uploadHook != null) && (uploadHook.isString() != null)) {
417                    hookUri = uploadHook.isString().stringValue();
418                    if (uploadedFileIdsArray != null) {
419                        for (int i = 0; i < uploadedFileIdsArray.size(); i++) {
420                            JSONString entry = uploadedFileIdsArray.get(i).isString();
421                            if (entry != null) {
422                                uploadedFileIds.add(entry.stringValue());
423                            }
424                        }
425                    }
426                }
427                if (uploadedFileIds.size() == 0) {
428                    // no files uploaded, probably because of virus scanner - don't show upload hook dialog
429                    hookUri = null;
430                }
431                JSONArray uploadedFilesArray = uploadedFilesVal.isArray();
432                if (uploadedFilesArray != null) {
433                    for (int i = 0; i < uploadedFilesArray.size(); i++) {
434                        JSONString entry = uploadedFilesArray.get(i).isString();
435                        if (entry != null) {
436                            m_uploadedFiles.add(entry.stringValue());
437                        }
438                    }
439                }
440                Map<String, List<String>> viruses = CmsVirusReport.getVirusWarnings(jsonObject);
441                final I_CmsUploadContext context = m_context;
442                final String finalHookUri = hookUri;
443                m_progressInfo.finish();
444                if (!viruses.isEmpty()) {
445                    m_context = null;
446                    hide();
447                    CmsPopup virusPopup = CmsVirusReport.createPopup(viruses, () -> {
448                        if (finalHookUri != null) {
449                            openHookDialog(uploadedFileIds, finalHookUri, context);
450                        } else {
451                            context.onUploadFinished(m_uploadedFiles);
452                        }
453                    });
454                    virusPopup.center();
455                } else {
456                    if (hookUri != null) {
457                        // Set the context to be null so that it isn't called when the upload dialog closed;
458                        // we want it to be called when the upload property dialog is closed instead.<p>
459                        m_context = null;
460                        openHookDialog(uploadedFileIds, hookUri, context);
461                    }
462                    closeOnSuccess();
463                }
464            } else {
465                String message = jsonObject.get(I_CmsUploadConstants.KEY_MESSAGE).isString().stringValue();
466                String stacktrace = jsonObject.get(I_CmsUploadConstants.KEY_STACKTRACE).isString().stringValue();
467                showErrorReport(message, stacktrace);
468            }
469        }
470    }
471
472    /**
473     * Sets the upload context.<p>
474     *
475     * @param context the new upload context
476     */
477    public void setContext(I_CmsUploadContext context) {
478
479        if (context != null) {
480            m_context = new I_CmsUploadContext() {
481
482                @Override
483                public void onUploadFinished(List<String> uploadedFiles) {
484
485                    context.onUploadFinished(uploadedFiles);
486                }
487            };
488
489        } else {
490            m_context = context;
491        }
492    }
493
494    /**
495     * Sets the boolean flag to control whether the target folder is interpreted as a root path.<p>
496     *
497     * @param isTargetRootPath true if the target folder should be treated as a root path
498     */
499    public void setIsTargetRootPath(boolean isTargetRootPath) {
500
501        m_isTargetRootPath = isTargetRootPath;
502    }
503
504    /**
505     * Sets the post-create handler.
506     *
507     * @param postCreateHandler the post-create handler
508     */
509    public void setPostCreateHandler(String postCreateHandler) {
510
511        m_postCreateHandler = postCreateHandler;
512    }
513
514    /**
515     * Sets the target folder.<p>
516     *
517     * @param target the target folder to set
518     */
519    public void setTargetFolder(String target) {
520
521        m_targetFolder = target;
522        setCaption(Messages.get().key(Messages.GUI_UPLOAD_DIALOG_TITLE_1, m_targetFolder));
523    }
524
525    /**
526     * Shows the error report.<p>
527     *
528     * @param message the message to show
529     * @param stacktrace the stacktrace to show
530     */
531    public void showErrorReport(final String message, final String stacktrace) {
532
533        if (!m_canceled) {
534            CmsErrorDialog errDialog = new CmsErrorDialog(message, stacktrace);
535            if (m_handlerReg != null) {
536                m_handlerReg.removeHandler();
537            }
538            if (m_closeHandler != null) {
539                errDialog.addCloseHandler(m_closeHandler);
540            }
541            hide();
542            errDialog.center();
543        }
544    }
545
546    /**
547     * Executes the submit action.<p>
548     */
549    public void submit() {
550
551        // create a JsArray containing the files to upload
552        List<CmsFileInfo> filesToUpload = new ArrayList<CmsFileInfo>(getFilesToUpload().values());
553        Collections.sort(filesToUpload, CmsFileInfo.INFO_COMPARATOR);
554
555        CmsUploader uploader = new CmsUploader();
556        uploader.uploadFiles(
557            getUploadUri(),
558            getTargetFolder(),
559            m_isTargetRootPath,
560            getPostCreateHandler(),
561            filesToUpload,
562            getFilesToUnzip(false),
563            false,
564            this);
565    }
566
567    /**
568     * Updates the button handler.
569     */
570    public void updateHandler() {
571
572        CmsDialogUploadButtonHandler handler = new CmsDialogUploadButtonHandler(
573            () -> m_context,
574            m_targetFolder,
575            m_isTargetRootPath);
576        handler.setUploadDialog(this);
577        m_uploadButton.reinitButton(handler);
578    }
579
580    /**
581     * Updates the file summary.<p>
582     */
583    public abstract void updateSummary();
584
585    /**
586     * Adds the given file input field to this dialog.<p>
587     *
588     * @param fileInput the file input field to add
589     */
590    protected void addFileInput(CmsFileInput fileInput) {
591
592        // add the files selected by the user to the list of files to upload
593        if (fileInput != null) {
594            addFiles(Arrays.asList(fileInput.getFiles()));
595        } else {
596            loadAndShow();
597        }
598    }
599
600    /**
601     * Adds the given file input field to this dialog.<p>
602     *
603     * @param fileInfos the file info objects
604     */
605    protected void addFiles(List<CmsFileInfo> fileInfos) {
606
607        if (fileInfos != null) {
608            for (CmsFileInfo file : fileInfos) {
609
610                // store all files
611                m_allFiles.put(file.getFileName(), file);
612                CmsUploadRestrictionInfo restriction = CmsCoreProvider.get().getUploadRestriction();
613
614                String targetRootPath = getTargetRootPath();
615                boolean restricted = !restriction.isUploadEnabled(targetRootPath)
616                    || !restriction.checkTypeAllowed(targetRootPath, file.getFileSuffix());
617                if (restricted) {
618                    m_restricted.add(file.getFileName());
619                }
620
621                // add those files to the list of files to upload that potential candidates
622                if (!isTooLarge(file) && !restricted) {
623                    m_filesToUpload.put(file.getFileName(), file);
624                }
625
626                // remove those files from the list to upload that were previously unchecked by the user
627                if ((m_listItems.get(file.getFileName()) != null)
628                    && (m_listItems.get(file.getFileName()).getCheckBox() != null)
629                    && !m_listItems.get(file.getFileName()).getCheckBox().isChecked()) {
630                    m_filesToUpload.remove(file.getFileName());
631                }
632            }
633
634            // now rebuild the list: handle all files
635            m_fileList.clearList();
636            List<String> sortedFileNames = new ArrayList<String>(m_allFiles.keySet());
637            Collections.sort(sortedFileNames, String.CASE_INSENSITIVE_ORDER);
638            for (String filename : sortedFileNames) {
639                CmsFileInfo file = m_allFiles.get(filename);
640                addFileToList(file, false, false, isTooLarge(file), m_restricted.contains(file.getFileName()));
641            }
642            doResize();
643        }
644        loadAndShow();
645    }
646
647    /**
648     * Cancels the upload progress timer.<p>
649     */
650    protected void cancelUpdateProgress() {
651
652        m_updateProgressTimer.cancel();
653    }
654
655    /**
656     * Cancels the upload.<p>
657     */
658    protected void cancelUpload() {
659
660        m_canceled = true;
661        cancelUpdateProgress();
662
663        CmsRpcAction<Boolean> callback = new CmsRpcAction<Boolean>() {
664
665            /**
666             * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute()
667             */
668            @Override
669            public void execute() {
670
671                getUploadService().cancelUpload(this);
672            }
673
674            /**
675             * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object)
676             */
677            @Override
678            protected void onResponse(Boolean result) {
679
680                hide();
681            }
682        };
683        callback.execute();
684    }
685
686    /**
687     * Creates the loading animation HTML and adds is to the content wrapper.<p>
688     *
689     * @param msg the message to display below the animation
690     */
691    protected void createLoadingAnimation(String msg) {
692
693        m_clientLoading = true;
694        if (m_loadingPanel != null) {
695            m_loadingPanel.removeFromParent();
696        }
697        m_loadingPanel = new FlowPanel();
698        m_loadingPanel.addStyleName(
699            org.opencms.gwt.client.ui.css.I_CmsLayoutBundle.INSTANCE.uploadButton().loadingPanel());
700        m_loadingPanel.addStyleName(m_gwtCss.generalCss().cornerAll());
701
702        CmsLoadingAnimation animationDiv = new CmsLoadingAnimation();
703        animationDiv.addStyleName(
704            org.opencms.gwt.client.ui.css.I_CmsLayoutBundle.INSTANCE.uploadButton().loadingAnimation());
705        m_loadingPanel.add(animationDiv);
706
707        HTML messageDiv = new HTML();
708        messageDiv.addStyleName(org.opencms.gwt.client.ui.css.I_CmsLayoutBundle.INSTANCE.uploadButton().loadingText());
709        messageDiv.setHTML(msg);
710        m_loadingPanel.add(messageDiv);
711
712        m_contentWrapper.add(m_loadingPanel);
713        doResize();
714    }
715
716    /**
717     * Disables the OK button.<p>
718     *
719     * @param disabledReason the reason for disabling the OK button
720     */
721    protected void disableOKButton(String disabledReason) {
722
723        m_okButton.disable(disabledReason);
724    }
725
726    /**
727     * Required to be called when the content has changed.<p>
728     */
729    protected void doResize() {
730
731        m_scrollPanel.onResizeDescendant();
732    }
733
734    /**
735     * Enables the OK button.<p>
736     */
737    protected void enableOKButton() {
738
739        m_okButton.enable();
740    }
741
742    /**
743     * Returns the contentLength.<p>
744     *
745     * @return the contentLength
746     */
747    protected long getContentLength() {
748
749        return m_contentLength;
750    }
751
752    /**
753     * Returns the contentWrapper.<p>
754     *
755     * @return the contentWrapper
756     */
757    protected FlowPanel getContentWrapper() {
758
759        return m_contentWrapper;
760    }
761
762    /**
763     * Returns the list of file names that have to unziped.<p>
764     *
765     * @param all <code>true</code> if the returned list should contain those filenames that
766     * are not inside the map of files to upload. <code>false</code> only those filenames are
767     * returned that are also inside the map of files to upload
768     *
769     * @return the list of file names that have to unziped
770     */
771    protected List<String> getFilesToUnzip(boolean all) {
772
773        if (!all) {
774            List<String> result = new ArrayList<String>();
775            for (String fileName : m_filesToUnzip) {
776                if (m_filesToUpload.keySet().contains(fileName)) {
777                    result.add(fileName);
778                }
779            }
780            return result;
781        }
782        return m_filesToUnzip;
783    }
784
785    /**
786     * Returns the filesToUpload.<p>
787     *
788     * @return the filesToUpload
789     */
790    protected Map<String, CmsFileInfo> getFilesToUpload() {
791
792        return m_filesToUpload;
793    }
794
795    /**
796     * Returns "files" or "file" depending on the files to upload.<p>
797     *
798     * @return "files" or "file" depending on the files to upload
799     */
800    protected String getFileText() {
801
802        if (m_filesToUpload.size() == 1) {
803            return org.opencms.gwt.client.Messages.get().key(
804                org.opencms.gwt.client.Messages.GUI_UPLOAD_FILES_SINGULAR_0);
805        }
806        return org.opencms.gwt.client.Messages.get().key(org.opencms.gwt.client.Messages.GUI_UPLOAD_FILES_PLURAL_0);
807    }
808
809    /**
810     * Gets the post-create handler.
811     *
812     * @return the post-create handler
813     */
814    protected String getPostCreateHandler() {
815
816        return m_postCreateHandler;
817    }
818
819    /**
820     * Returns the resource type name for a given filename.<p>
821     *
822     * @param file the file info
823     *
824     * @return the resource type name
825     */
826    protected String getResourceType(CmsFileInfo file) {
827
828        return CmsCoreProvider.get().getResourceType(file);
829    }
830
831    /**
832     * Returns the targetFolder.<p>
833     *
834     * @return the targetFolder
835     */
836    protected String getTargetFolder() {
837
838        return m_targetFolder;
839    }
840
841    /**
842     * Returns the upload service instance.<p>
843     *
844     * @return the upload service instance
845     */
846    protected I_CmsUploadServiceAsync getUploadService() {
847
848        if (m_uploadService == null) {
849            m_uploadService = GWT.create(I_CmsUploadService.class);
850            String serviceUrl = CmsCoreProvider.get().link("org.opencms.ade.upload.CmsUploadService.gwt");
851            ((ServiceDefTarget)m_uploadService).setServiceEntryPoint(serviceUrl);
852        }
853        return m_uploadService;
854    }
855
856    /**
857     * Returns the upload JSP uri.<p>
858     *
859     * @return the upload JSP uri
860     */
861    protected String getUploadUri() {
862
863        return CmsCoreProvider.get().link(I_CmsUploadConstants.UPLOAD_ACTION_JSP_URI);
864    }
865
866    /**
867     * Inserts a hidden form into.<p>
868     *
869     * @param form the form to insert
870     */
871    protected void insertUploadForm(FormPanel form) {
872
873        form.getElement().getStyle().setDisplay(Display.NONE);
874        m_contentWrapper.add(form);
875    }
876
877    /**
878     * The action that is executed if the user clicks on the OK button.<p>
879     *
880     * If the selection dialog is currently shown the selected files are checked
881     * otherwise the upload is triggered.<p>
882     */
883    protected void onOkClick() {
884
885        if (!m_selectionDone) {
886            checkSelection();
887        } else {
888            commit();
889        }
890    }
891
892    /**
893     * Required to be called when the content has changed.<p>
894     */
895    protected void onResize() {
896
897        m_scrollPanel.onResize();
898    }
899
900    /**
901     * Decides how to go on depending on the information of the server response.<p>
902     *
903     * Shows a warning if there is another upload process active (inside the same session).<p>
904     *
905     * Otherwise if the list of files to upload contains already existent resources on the VFS or if there
906     * are files selected that have invalid file names the overwrite dialog is shown.<p>
907     *
908     * Only if there is no other upload process running and none of the selected files
909     * is already existent on the VFS the upload is triggered.<p>
910     *
911     * @param result the bean that contains the information to evaluate
912     */
913    protected void proceedWorkflow(CmsUploadFileBean result) {
914
915        if (result.isActive()) {
916            m_okButton.enable();
917            CmsNotification.get().send(Type.WARNING, Messages.get().key(Messages.GUI_UPLOAD_NOTIFICATION_RUNNING_0));
918        } else {
919            if (!result.getExistingResourceNames().isEmpty()
920                || !result.getInvalidFileNames().isEmpty()
921                || !result.getExistingDeletedFileNames().isEmpty()) {
922                showOverwriteDialog(result);
923            } else {
924                commit();
925            }
926        }
927    }
928
929    /**
930     * Removes the drag and drop message.<p>
931     */
932    protected void removeDragAndDropMessage() {
933
934        if (m_dragAndDropMessage != null) {
935            m_dragAndDropMessage.removeFromParent();
936            m_dragAndDropMessage = null;
937            getContentWrapper().getElement().getStyle().clearBackgroundColor();
938        }
939    }
940
941    /**
942     * Sets the contentLength.<p>
943     *
944     * @param contentLength the contentLength to set
945     */
946    protected void setContentLength(long contentLength) {
947
948        m_contentLength = contentLength;
949    }
950
951    /**
952     * Execute to set the content wrapper height.<p>
953     */
954    protected void setContentWrapperHeight() {
955
956        // set the max height of the content panel
957        int fixedContent = 0;
958        if (m_dialogInfo.isVisible()) {
959            fixedContent += m_dialogInfo.getOffsetHeight();
960        }
961        if (m_selectionSummary.isVisible()) {
962            fixedContent += m_selectionSummary.getOffsetHeight();
963        }
964        m_scrollPanel.getElement().getStyle().setPropertyPx("maxHeight", getAvailableHeight(fixedContent));
965        doResize();
966    }
967
968    /**
969     * Displays the 'use drag and drop' / 'no drag and drop available' message.<p>
970     */
971    protected void setDragAndDropMessage() {
972
973        if (m_dragAndDropMessage == null) {
974            m_dragAndDropMessage = new HTML();
975            m_dragAndDropMessage.setStyleName(I_CmsLayoutBundle.INSTANCE.uploadCss().dragAndDropMessage());
976            m_dragAndDropMessage.setText(Messages.get().key(Messages.GUI_UPLOAD_DRAG_AND_DROP_DISABLED_0));
977        }
978        getContentWrapper().add(m_dragAndDropMessage);
979        getContentWrapper().getElement().getStyle().setBackgroundColor(
980            I_CmsConstantsBundle.INSTANCE.css().notificationErrorBg());
981        doResize();
982    }
983
984    /**
985     * Sets the HTML of the selection summary.<p>
986     *
987     * @param html the HTML to set as String
988     */
989    protected void setSummaryHTML(String html) {
990
991        m_selectionSummary.setHTML(html);
992    }
993
994    /**
995     * Retrieves the progress information from the server.<p>
996     */
997    protected void updateProgress() {
998
999        CmsRpcAction<CmsUploadProgessInfo> callback = new CmsRpcAction<CmsUploadProgessInfo>() {
1000
1001            /**
1002             * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute()
1003             */
1004            @Override
1005            public void execute() {
1006
1007                getUploadService().getUploadProgressInfo(this);
1008            }
1009
1010            /**
1011             * @see org.opencms.gwt.client.rpc.CmsRpcAction#onFailure(java.lang.Throwable)
1012             */
1013            @Override
1014            public void onFailure(Throwable t) {
1015
1016                super.onFailure(t);
1017                cancelUpdateProgress();
1018            }
1019
1020            /**
1021             * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object)
1022             */
1023            @Override
1024            protected void onResponse(CmsUploadProgessInfo result) {
1025
1026                updateProgressBar(result);
1027            }
1028        };
1029        callback.execute();
1030    }
1031
1032    /**
1033     * Updates the progress bar.<p>
1034     *
1035     * @param info the progress info
1036     */
1037    protected void updateProgressBar(CmsUploadProgessInfo info) {
1038
1039        switch (info.getState()) {
1040            case notStarted:
1041                break;
1042            case running:
1043                m_progressInfo.setProgress(info);
1044                stopLoadingAnimation();
1045                break;
1046            case finished:
1047                m_progressInfo.finish();
1048                displayDialogInfo(
1049                    org.opencms.gwt.client.Messages.get().key(
1050                        org.opencms.gwt.client.Messages.GUI_UPLOAD_INFO_FINISHING_0),
1051                    false);
1052                startLoadingAnimation(
1053                    org.opencms.gwt.client.Messages.get().key(
1054                        org.opencms.gwt.client.Messages.GUI_UPLOAD_INFO_CREATING_RESOURCES_0),
1055                    1500);
1056                break;
1057            default:
1058                break;
1059        }
1060    }
1061
1062    /**
1063     * Adds a click handler for the given check box.<p>
1064     *
1065     * @param check the check box
1066     * @param unzipWidget the un-zip check box
1067     * @param file the file
1068     */
1069    private void addClickHandlerToCheckBox(final CmsCheckBox check, final Widget unzipWidget, final CmsFileInfo file) {
1070
1071        check.addClickHandler(new ClickHandler() {
1072
1073            /**
1074             * @see com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event.dom.client.ClickEvent)
1075             */
1076            public void onClick(ClickEvent event) {
1077
1078                // add or remove the file from the list of files to upload
1079                if (check.isChecked()) {
1080                    getFilesToUpload().put(file.getFileName(), file);
1081                    if (unzipWidget != null) {
1082                        enableUnzip(unzipWidget);
1083                    }
1084                } else {
1085                    getFilesToUpload().remove(file.getFileName());
1086                    if (unzipWidget != null) {
1087                        disableUnzip(unzipWidget);
1088                    }
1089                }
1090
1091                // disable or enable the OK button
1092                if (getFilesToUpload().isEmpty()) {
1093                    disableOKButton(Messages.get().key(Messages.GUI_UPLOAD_NOTIFICATION_NO_FILES_0));
1094                } else {
1095                    enableOKButton();
1096                }
1097
1098                // update summary
1099                updateSummary();
1100
1101            }
1102
1103            /**
1104             * Disables the 'unzip' button
1105             *
1106             * @param unzip the unzip button
1107             */
1108            private void disableUnzip(Widget unzip) {
1109
1110                ((CmsToggleButton)unzip).setEnabled(false);
1111            }
1112
1113            /**
1114             * Enables the 'unzip' button
1115             *
1116             * @param unzip the unzip button
1117             */
1118            private void enableUnzip(Widget unzip) {
1119
1120                ((CmsToggleButton)unzip).setEnabled(true);
1121            }
1122        });
1123    }
1124
1125    /**
1126     * Adds a file to the list.<p>
1127     *
1128     * @param file the file to add
1129     * @param invalid signals if the filename is invalid
1130     * @param existingDeleted in case of existing files marked as deleted
1131     * @param isTooLarge signals if the file size limit is exceeded
1132     * @param forbiddenType true if the file has a forbidden type
1133     */
1134    private void addFileToList(
1135        final CmsFileInfo file,
1136        boolean invalid,
1137        boolean existingDeleted,
1138        boolean isTooLarge,
1139        boolean forbiddenType) {
1140
1141        CmsListInfoBean infoBean = createInfoBean(file);
1142        CmsListItemWidget listItemWidget = new CmsListItemWidget(infoBean);
1143        listItemWidget.setIcon(CmsCoreProvider.get().getResourceTypeIcon(file));
1144        CmsCheckBox check = new CmsCheckBox();
1145        check.setChecked(false);
1146        if (!invalid && !isTooLarge && !existingDeleted && !forbiddenType) {
1147            if (file.getFileSize() == 0) {
1148                check.setChecked(false);
1149            }
1150            check.setChecked(m_filesToUpload.containsKey(file.getFileName()));
1151            check.setTitle(file.getFileName());
1152            if (!m_selectionDone && file.getFileName().toLowerCase().endsWith(".zip")) {
1153                final Widget unzip = createUnzipCheckBox(file);
1154                addClickHandlerToCheckBox(check, unzip, file);
1155                listItemWidget.addButton(unzip);
1156            } else {
1157                addClickHandlerToCheckBox(check, null, file);
1158            }
1159        } else if (forbiddenType) {
1160            String message = Messages.get().key(Messages.GUI_UPLOAD_RESTRICTED_0);
1161            check.disable(message);
1162            listItemWidget.setBackground(Background.RED);
1163            listItemWidget.setSubtitleLabel(message);
1164        } else if (existingDeleted) {
1165            // is invalid
1166            String message = Messages.get().key(Messages.GUI_UPLOAD_FILE_EXISTING_DELETED_1, file.getFileName());
1167            check.disable(message);
1168            listItemWidget.setBackground(Background.RED);
1169            listItemWidget.setSubtitleLabel(message);
1170        } else if (isTooLarge) {
1171            String message = getFileSizeTooLargeMessage(file);
1172            check.disable(message);
1173            listItemWidget.setBackground(Background.RED);
1174            listItemWidget.setSubtitleLabel(message);
1175        } else {
1176            // is invalid
1177            String message = Messages.get().key(
1178                Messages.GUI_UPLOAD_FILE_INVALID_NAME_2,
1179                file.getFileName(),
1180                CmsUploadButton.formatBytes(file.getFileSize()));
1181            check.disable(message);
1182            listItemWidget.setBackground(Background.RED);
1183            listItemWidget.setSubtitleLabel(message);
1184        }
1185
1186        CmsListItem listItem = new CmsListItem(check, listItemWidget);
1187        m_fileList.addItem(listItem);
1188        m_listItems.put(file.getFileName(), listItem);
1189        doResize();
1190    }
1191
1192    /**
1193     * Changes the height of the content wrapper so that the dialog finally has the
1194     * same height that the dialog has when the min height is set on the selection screen.<p>
1195     */
1196    private void changeHeight() {
1197
1198        int firstHeight = MIN_CONTENT_HEIGHT + m_firstInfoHeight + m_firstSummaryHeight + 2;
1199        int currentHeight = CmsDomUtil.getCurrentStyleInt(m_mainPanel.getElement(), CmsDomUtil.Style.height);
1200        int targetHeight = firstHeight - m_dialogInfo.getOffsetHeight() - m_selectionSummary.getOffsetHeight();
1201        if (currentHeight > firstHeight) {
1202            CmsChangeHeightAnimation.change(m_scrollPanel.getElement(), targetHeight, new Command() {
1203
1204                public void execute() {
1205
1206                    doResize();
1207                }
1208            }, 750);
1209        }
1210    }
1211
1212    /**
1213     * Before the upload data is effectively submited we have to check
1214     * for already existent resources in the VFS.<p>
1215     *
1216     * Executes the RPC call that checks the VFS for existing resources.
1217     * Passes the response object to a method that evaluates the result.<p>
1218     */
1219    private void checkSelection() {
1220
1221        m_okButton.disable(Messages.get().key(Messages.GUI_UPLOAD_BUTTON_OK_DISABLE_CHECKING_0));
1222
1223        if (!m_selectionDone) {
1224            m_firstContentHeight = CmsDomUtil.getCurrentStyleInt(m_scrollPanel.getElement(), CmsDomUtil.Style.height);
1225            m_firstInfoHeight = m_dialogInfo.getOffsetHeight();
1226            m_firstSummaryHeight = m_selectionSummary.getOffsetHeight();
1227        }
1228
1229        CmsRpcAction<CmsUploadFileBean> callback = new CmsRpcAction<CmsUploadFileBean>() {
1230
1231            /**
1232             * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute()
1233             */
1234            @Override
1235            public void execute() {
1236
1237                List<String> filesToCheck = new ArrayList<String>(getFilesToUpload().keySet());
1238                filesToCheck.removeAll(getFilesToUnzip(false));
1239                getUploadService().checkUploadFiles(filesToCheck, getTargetFolder(), m_isTargetRootPath, this);
1240            }
1241
1242            /**
1243             * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object)
1244             */
1245            @Override
1246            protected void onResponse(CmsUploadFileBean result) {
1247
1248                proceedWorkflow(result);
1249            }
1250        };
1251        callback.execute();
1252    }
1253
1254    /**
1255     * Closes the dialog after a delay.<p>
1256     */
1257    private void closeOnSuccess() {
1258
1259        Timer closeTimer = new Timer() {
1260
1261            /**
1262             * @see com.google.gwt.user.client.Timer#run()
1263             */
1264            @Override
1265            public void run() {
1266
1267                A_CmsUploadDialog.this.hide();
1268            }
1269        };
1270        closeTimer.schedule(1500);
1271    }
1272
1273    /**
1274     * Calls the submit action if there are any files selected for upload.<p>
1275     */
1276    private void commit() {
1277
1278        m_selectionDone = true;
1279        if (!m_filesToUpload.isEmpty()) {
1280            m_okButton.disable(Messages.get().key(Messages.GUI_UPLOAD_BUTTON_OK_DISABLE_UPLOADING_0));
1281            if (m_uploadButton instanceof UIObject) {
1282                ((UIObject)m_uploadButton).getElement().getStyle().setDisplay(Display.NONE);
1283            }
1284            showProgress();
1285            submit();
1286        }
1287    }
1288
1289    /**
1290     * Creates the "OK", the "Cancel" and the "Upload" button.<p>
1291     */
1292    private void createButtons() {
1293
1294        addDialogClose(new Command() {
1295
1296            public void execute() {
1297
1298                cancelUpload();
1299            }
1300        });
1301
1302        CmsPushButton cancelButton = new CmsPushButton();
1303        cancelButton.setTitle(org.opencms.gwt.client.Messages.get().key(org.opencms.gwt.client.Messages.GUI_CANCEL_0));
1304        cancelButton.setText(org.opencms.gwt.client.Messages.get().key(org.opencms.gwt.client.Messages.GUI_CANCEL_0));
1305        cancelButton.setSize(I_CmsButton.Size.medium);
1306        cancelButton.setUseMinWidth(true);
1307        cancelButton.addClickHandler(new ClickHandler() {
1308
1309            /**
1310             * @see com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event.dom.client.ClickEvent)
1311             */
1312            public void onClick(ClickEvent event) {
1313
1314                cancelUpload();
1315            }
1316        });
1317        addButton(cancelButton);
1318
1319        m_okButton = new CmsPushButton();
1320        m_okButton.setTitle(org.opencms.gwt.client.Messages.get().key(org.opencms.gwt.client.Messages.GUI_OK_0));
1321        m_okButton.setText(org.opencms.gwt.client.Messages.get().key(org.opencms.gwt.client.Messages.GUI_OK_0));
1322        m_okButton.setSize(I_CmsButton.Size.medium);
1323        m_okButton.setUseMinWidth(true);
1324        m_okButton.addClickHandler(new ClickHandler() {
1325
1326            /**
1327             * @see com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event.dom.client.ClickEvent)
1328             */
1329            public void onClick(ClickEvent event) {
1330
1331                onOkClick();
1332            }
1333        });
1334        addButton(m_okButton);
1335
1336        CmsDialogUploadButtonHandler buttonHandler = new CmsDialogUploadButtonHandler(
1337            new Supplier<I_CmsUploadContext>() {
1338
1339                public I_CmsUploadContext get() {
1340
1341                    return m_context;
1342                }
1343            });
1344        buttonHandler.setUploadDialog(this);
1345        // add a new upload button
1346        CmsUploadButton uploadButton = new CmsUploadButton(buttonHandler);
1347        uploadButton.addStyleName(I_CmsLayoutBundle.INSTANCE.uploadCss().uploadDialogButton());
1348        uploadButton.setText(Messages.get().key(Messages.GUI_UPLOAD_BUTTON_ADD_FILES_0));
1349        addButton(uploadButton);
1350        m_uploadButton = uploadButton;
1351    }
1352
1353    /**
1354     * Creates the unzip checkbox.<p>
1355     *
1356     * @param file the file to create the checkbox for
1357     *
1358     * @return the unzip checkbox
1359     */
1360    private Widget createUnzipCheckBox(final CmsFileInfo file) {
1361
1362        final CmsToggleButton unzip = new CmsToggleButton();
1363        I_CmsUploadCss uploadCss = I_CmsLayoutBundle.INSTANCE.uploadCss();
1364        String caption = Messages.get().key(Messages.GUI_UNZIP_BUTTON_TEXT_0);
1365        unzip.setUpFace(caption, uploadCss.unzipButtonUpFace());
1366        unzip.setDownFace(caption, uploadCss.unzipButtonDownFace());
1367        unzip.addStyleName(uploadCss.unzipButton());
1368        unzip.addStyleName(org.opencms.gwt.client.ui.css.I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().permaVisible());
1369        unzip.setDown(getFilesToUnzip(true).contains(file.getFileName()));
1370        if (!m_filesToUpload.containsKey(file.getFileName())) {
1371            unzip.disable(Messages.get().key(Messages.GUI_UPLOAD_FILE_NOT_SELECTED_0));
1372        }
1373        unzip.addClickHandler(new ClickHandler() {
1374
1375            /**
1376             * @see com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event.dom.client.ClickEvent)
1377             */
1378            public void onClick(ClickEvent event) {
1379
1380                // add or remove the file from the list of files to upload
1381                if (unzip.isDown()) {
1382                    getFilesToUnzip(true).add(file.getFileName());
1383                } else {
1384                    getFilesToUnzip(true).remove(file.getFileName());
1385                }
1386            }
1387        });
1388        return unzip;
1389    }
1390
1391    /**
1392     * Sets the user info.<p>
1393     *
1394     * @param msg the message to display
1395     * @param warning signals whether the message should be a warning or nor
1396     */
1397    private void displayDialogInfo(String msg, boolean warning) {
1398
1399        StringBuffer buffer = new StringBuffer(64);
1400        if (!warning) {
1401            buffer.append("<p class=\"");
1402            buffer.append(org.opencms.gwt.client.ui.css.I_CmsLayoutBundle.INSTANCE.uploadButton().dialogMessage());
1403            buffer.append("\">");
1404            buffer.append(msg);
1405            buffer.append("</p>");
1406        } else {
1407            buffer.append(FontOpenCms.WARNING.getHtml(32, I_CmsConstantsBundle.INSTANCE.css().colorWarning()));
1408            buffer.append("<p class=\"");
1409            buffer.append(org.opencms.gwt.client.ui.css.I_CmsLayoutBundle.INSTANCE.uploadButton().warningMessage());
1410            buffer.append("\">");
1411            buffer.append(msg);
1412            buffer.append("</p>");
1413        }
1414        m_dialogInfo.setHTML(buffer.toString());
1415    }
1416
1417    /**
1418     * Gets the target root path.
1419     *
1420     * @return the target root path
1421     */
1422    private String getTargetRootPath() {
1423
1424        if (m_isTargetRootPath) {
1425            return m_targetFolder;
1426        } else {
1427            return CmsCoreProvider.get().addSiteRoot(m_targetFolder);
1428        }
1429    }
1430
1431    /**
1432     * Helper method to upload the hook dialog after an upload.
1433     *
1434     * @param uploadedFileIds the uploaded file ids
1435     * @param hookUri the hook URI
1436     * @param context the upload context
1437     */
1438    private void openHookDialog(List<String> uploadedFileIds, String hookUri, final I_CmsUploadContext context) {
1439
1440        CloseHandler<PopupPanel> closeHandler;
1441        closeHandler = new CloseHandler<PopupPanel>() {
1442
1443            public void onClose(CloseEvent<PopupPanel> event) {
1444
1445                if (context != null) {
1446                    List<CmsUUID> actualIds = uploadedFileIds.stream().map(id -> new CmsUUID(id)).collect(
1447                        Collectors.toList());
1448                    // post-upload hook may rename files
1449                    CmsCoreProvider.getVfsService().getSitePaths(actualIds, new AsyncCallback<List<String>>() {
1450
1451                        @Override
1452                        public void onFailure(Throwable caught) {}
1453
1454                        @Override
1455                        public void onSuccess(List<String> result) {
1456
1457                            for (int i = 0; i < result.size(); i++) {
1458                                String path = result.get(i);
1459                                if (path.startsWith(m_targetFolder)) {
1460                                    result.set(i, path.substring(m_targetFolder.length()));
1461                                }
1462                            }
1463                            context.onUploadFinished(result);
1464                        }
1465                    });
1466                }
1467            }
1468        };
1469
1470        String title = Messages.get().key(Messages.GUI_UPLOAD_HOOK_DIALOG_TITLE_0);
1471        CmsUploadHookDialog.openDialog(title, hookUri, uploadedFileIds, closeHandler);
1472    }
1473
1474    /**
1475     * Removes all widgets from the content wrapper.<p>
1476     */
1477    private void removeContent() {
1478
1479        m_contentWrapper.clear();
1480        doResize();
1481    }
1482
1483    /**
1484     * Sets the height for the content so that the dialog finally has the same height
1485     * as the dialog has on the selection screen.<p>
1486     */
1487    private void setHeight() {
1488
1489        int infoDiff = m_firstInfoHeight - m_dialogInfo.getOffsetHeight();
1490        int summaryDiff = m_firstSummaryHeight - m_selectionSummary.getOffsetHeight();
1491        int height = m_firstContentHeight + infoDiff + summaryDiff;
1492        m_scrollPanel.getElement().getStyle().setHeight(height, Unit.PX);
1493        m_scrollPanel.getElement().getStyle().clearProperty("minHeight");
1494        m_scrollPanel.getElement().getStyle().clearProperty("maxHeight");
1495        doResize();
1496    }
1497
1498    /**
1499     * Shows the overwrite dialog.<p>
1500     *
1501     * @param infoBean the info bean containing the existing and invalid file names
1502     */
1503    private void showOverwriteDialog(CmsUploadFileBean infoBean) {
1504
1505        // update the dialog
1506        m_selectionDone = true;
1507        m_okButton.enable();
1508        if (infoBean.getInvalidFileNames().isEmpty() && infoBean.getExistingDeletedFileNames().isEmpty()) {
1509            displayDialogInfo(Messages.get().key(Messages.GUI_UPLOAD_INFO_OVERWRITE_0), true);
1510        } else {
1511            displayDialogInfo(Messages.get().key(Messages.GUI_UPLOAD_INFO_INVALID_0), true);
1512        }
1513        if (m_uploadButton instanceof UIObject) {
1514            ((UIObject)m_uploadButton).getElement().getStyle().setDisplay(Display.NONE);
1515        }
1516        // clear the list
1517        m_fileList.clearList();
1518
1519        // handle existing files
1520        List<String> existings = new ArrayList<String>(infoBean.getExistingResourceNames());
1521        Collections.sort(existings, String.CASE_INSENSITIVE_ORDER);
1522        for (String filename : existings) {
1523            addFileToList(m_filesToUpload.get(filename), false, false, false, false);
1524        }
1525
1526        // handle the invalid files
1527        List<String> invalids = new ArrayList<String>(infoBean.getInvalidFileNames());
1528        Collections.sort(invalids, String.CASE_INSENSITIVE_ORDER);
1529        for (String filename : invalids) {
1530            addFileToList(m_filesToUpload.get(filename), true, false, false, false);
1531            m_filesToUpload.remove(filename);
1532        }
1533
1534        // handle the invalid files
1535        List<String> existingDeleted = new ArrayList<String>(infoBean.getExistingDeletedFileNames());
1536        Collections.sort(existingDeleted, String.CASE_INSENSITIVE_ORDER);
1537        for (String filename : existingDeleted) {
1538            addFileToList(m_filesToUpload.get(filename), false, true, false, false);
1539            m_filesToUpload.remove(filename);
1540        }
1541
1542        // set the height of the content
1543        setHeight();
1544    }
1545
1546    /**
1547     * Starts the upload progress bar.<p>
1548     */
1549    private void showProgress() {
1550
1551        removeContent();
1552        displayDialogInfo(
1553            org.opencms.gwt.client.Messages.get().key(org.opencms.gwt.client.Messages.GUI_UPLOAD_INFO_UPLOADING_0),
1554            false);
1555        m_selectionSummary.removeFromParent();
1556        List<String> files = new ArrayList<String>(getFilesToUpload().keySet());
1557        Collections.sort(files, String.CASE_INSENSITIVE_ORDER);
1558        m_progressInfo = new CmsUploadProgressInfo(files);
1559        m_progressInfo.setContentLength(m_contentLength);
1560        m_contentWrapper.add(m_progressInfo);
1561        m_updateProgressTimer.scheduleRepeating(UPDATE_PROGRESS_INTERVALL);
1562        startLoadingAnimation(Messages.get().key(Messages.GUI_UPLOAD_CLIENT_LOADING_0), 0);
1563        setHeight();
1564        changeHeight();
1565    }
1566
1567    /**
1568     * Starts the loading animation.<p>
1569     *
1570     * Used while client is loading files from hard disk into memory.<p>
1571     *
1572     * @param msg the message that should be displayed below the loading animation (can also be HTML as String)
1573     * @param delayMillis the delay to start the animation with
1574     */
1575    private void startLoadingAnimation(final String msg, int delayMillis) {
1576
1577        m_loadingTimer = new Timer() {
1578
1579            @Override
1580            public void run() {
1581
1582                createLoadingAnimation(msg);
1583            }
1584        };
1585        if (delayMillis > 0) {
1586            m_loadingTimer.schedule(delayMillis);
1587        } else {
1588            m_loadingTimer.run();
1589        }
1590    }
1591
1592    /**
1593     * Stops the client loading animation.<p>
1594     */
1595    private void stopLoadingAnimation() {
1596
1597        if (m_loadingTimer != null) {
1598            m_loadingTimer.cancel();
1599        }
1600        if (m_clientLoading) {
1601            m_contentWrapper.remove(m_loadingPanel);
1602            doResize();
1603            m_clientLoading = false;
1604        }
1605    }
1606}