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