/*
* This library is part of OpenCms -
* the Open Source Content Management System
*
* Copyright (c) Alkacon Software GmbH (http://www.alkacon.com)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* For further information about Alkacon Software, please see the
* company website: http://www.alkacon.com
*
* For further information about OpenCms, please see the
* project website: http://www.opencms.org
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.opencms.ade.upload.client.ui;
import org.opencms.ade.upload.client.Messages;
import org.opencms.ade.upload.client.ui.css.I_CmsLayoutBundle;
import org.opencms.ade.upload.shared.CmsUploadData;
import org.opencms.ade.upload.shared.CmsUploadFileBean;
import org.opencms.ade.upload.shared.CmsUploadProgessInfo;
import org.opencms.ade.upload.shared.I_CmsUploadConstants;
import org.opencms.ade.upload.shared.rpc.I_CmsUploadService;
import org.opencms.ade.upload.shared.rpc.I_CmsUploadServiceAsync;
import org.opencms.gwt.client.CmsCoreProvider;
import org.opencms.gwt.client.rpc.CmsRpcAction;
import org.opencms.gwt.client.rpc.CmsRpcPrefetcher;
import org.opencms.gwt.client.ui.CmsErrorDialog;
import org.opencms.gwt.client.ui.CmsList;
import org.opencms.gwt.client.ui.CmsListItem;
import org.opencms.gwt.client.ui.CmsListItemWidget;
import org.opencms.gwt.client.ui.CmsListItemWidget.Background;
import org.opencms.gwt.client.ui.CmsNotification;
import org.opencms.gwt.client.ui.CmsNotification.Type;
import org.opencms.gwt.client.ui.CmsPopup;
import org.opencms.gwt.client.ui.CmsProgressBar;
import org.opencms.gwt.client.ui.CmsPushButton;
import org.opencms.gwt.client.ui.I_CmsButton;
import org.opencms.gwt.client.ui.I_CmsListItem;
import org.opencms.gwt.client.ui.css.I_CmsConstantsBundle;
import org.opencms.gwt.client.ui.input.CmsCheckBox;
import org.opencms.gwt.client.ui.input.upload.CmsFileInfo;
import org.opencms.gwt.client.ui.input.upload.CmsFileInput;
import org.opencms.gwt.client.util.CmsChangeHeightAnimation;
import org.opencms.gwt.client.util.CmsDomUtil;
import org.opencms.gwt.shared.CmsIconUtil;
import org.opencms.gwt.shared.CmsListInfoBean;
import org.opencms.util.CmsStringUtil;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Style.Display;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.rpc.ServiceDefTarget;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.FormPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.PopupPanel;
/**
* Provides an upload dialog.<p>
*
* @since 8.0.0
*/
public abstract class A_CmsUploadDialog extends CmsPopup {
/**
* Provides the upload progress information.<p>
*
* Has a progressbar and a table for showing details.<p>
*/
private class CmsUploadProgressInfo extends FlowPanel {
/** The progress bar. */
private CmsProgressBar m_bar;
/** The table for showing upload details. */
private FlexTable m_fileinfo;
/** A sorted list of the filenames to upload. */
private List<String> m_orderedFilenamesToUpload;
/** Signals if the progress was set at least one time. */
private boolean m_started;
/**
* Default constructor.<p>
*/
public CmsUploadProgressInfo() {
// get a ordered list of filenames
m_orderedFilenamesToUpload = new ArrayList<String>(getFilesToUpload().keySet());
Collections.sort(m_orderedFilenamesToUpload, String.CASE_INSENSITIVE_ORDER);
// create the progress bar
m_bar = new CmsProgressBar();
// create the file info table
m_fileinfo = new FlexTable();
m_fileinfo.addStyleName(I_CmsLayoutBundle.INSTANCE.uploadCss().fileInfoTable());
// arrange the progress info
addStyleName(I_CmsLayoutBundle.INSTANCE.uploadCss().progressInfo());
add(m_bar);
add(m_fileinfo);
}
/**
* Finishes the state of the progress bar.<p>
*/
public void finish() {
String length = formatBytes(getContentLength());
int fileCount = m_orderedFilenamesToUpload.size();
m_bar.setValue(100);
m_fileinfo.removeAllRows();
m_fileinfo.setHTML(0, 0, "<b>" + Messages.get().key(Messages.GUI_UPLOAD_FINISH_UPLOADED_0) + "</b>");
m_fileinfo.setText(0, 1, Messages.get().key(
Messages.GUI_UPLOAD_FINISH_UPLOADED_VALUE_4,
new Integer(fileCount),
new Integer(fileCount),
getFileText(),
length));
}
/**
* Sets the progress information.<p>
*
* @param info the progress info bean
*/
public void setProgress(CmsUploadProgessInfo info) {
int currFile = info.getCurrentFile();
int currFileIndex = 0;
if (currFile == 0) {
// no files read so far
} else {
currFileIndex = currFile - 1;
if (currFileIndex >= m_orderedFilenamesToUpload.size()) {
currFileIndex = m_orderedFilenamesToUpload.size() - 1;
}
}
if (getContentLength() == 0) {
setContentLength(info.getContentLength());
}
String currFilename = m_orderedFilenamesToUpload.get(currFileIndex);
String contentLength = formatBytes(getContentLength());
int fileCount = m_orderedFilenamesToUpload.size();
String readBytes = formatBytes(getBytesRead(info.getPercent()));
m_bar.setValue(info.getPercent());
if (!m_started) {
m_started = true;
m_fileinfo.setHTML(0, 0, "<b>"
+ Messages.get().key(Messages.GUI_UPLOAD_PROGRESS_CURRENT_FILE_0)
+ "</b>");
m_fileinfo.setHTML(1, 0, "<b>" + Messages.get().key(Messages.GUI_UPLOAD_PROGRESS_UPLOADING_0) + "</b>");
m_fileinfo.setHTML(2, 0, "");
m_fileinfo.setText(0, 1, "");
m_fileinfo.setText(1, 1, "");
m_fileinfo.setText(2, 1, "");
m_fileinfo.getColumnFormatter().setWidth(0, "100px");
}
m_fileinfo.setText(0, 1, currFilename);
m_fileinfo.setText(1, 1, Messages.get().key(
Messages.GUI_UPLOAD_PROGRESS_CURRENT_VALUE_3,
new Integer(currFileIndex + 1),
new Integer(fileCount),
getFileText()));
m_fileinfo.setText(2, 1, Messages.get().key(
Messages.GUI_UPLOAD_PROGRESS_UPLOADING_VALUE_2,
readBytes,
contentLength));
}
/**
* Returns the bytes that are read so far.<p>
*
* The total request size is larger than the sum of all file sizes that are uploaded.
* Because boundaries and the target folder or even some other information than only
* the plain file contents are submited to the server.<p>
*
* This method calculates the bytes that are read with the help of the file sizes.<p>
*
* @param percent the server side determined percentage
*
* @return the bytes that are read so far
*/
private long getBytesRead(long percent) {
return percent != 0 ? (getContentLength() * percent) / 100 : 0;
}
}
/** Maximum width for the file item widget list. */
private static final int DIALOG_WIDTH = 600;
/** The size for kilobytes in bytes. */
private static final float KILOBYTE = 1024L;
/** The minimal height of the content wrapper. */
private static final int MIN_CONTENT_HEIGHT = 110;
/** Text metrics key. */
private static final String TM_FILE_UPLOAD_LIST = "FileUploadList";
/** The interval for updating the progress information in milliseconds. */
private static final int UPDATE_PROGRESS_INTERVALL = 1000;
/** Stores all files that were added. */
private Map<String, CmsFileInfo> m_allFiles;
/** Signals that the upload dialog was canceled. */
private boolean m_canceled;
/** Signals that the client currently loading. */
private boolean m_clientLoading;
/** The close handler. */
private CloseHandler<PopupPanel> m_closeHandler;
/** The sum of all file sizes. */
private long m_contentLength;
/** A flow panel with a dynamic height. */
private FlowPanel m_contentWrapper;
/** The upload data. */
private CmsUploadData m_data;
/** The user information text widget. */
private HTML m_dialogInfo;
/** The drag and drop message. */
protected HTML m_dragAndDropMessage;
/** The list of file item widgets. */
private CmsList<I_CmsListItem> m_fileList;
/** The list of filenames that should be unziped on the server. */
private List<String> m_filesToUnzip;
/** The Map of files to upload. */
private Map<String, CmsFileInfo> m_filesToUpload;
/** Stores the content height of the selection dialog. */
private int m_firstContentHeight;
/** Stores the height of the user information text widget of the selection dialog. */
private int m_firstInfoHeight;
/** Stores the height of the summary. */
private int m_firstSummaryHeight;
/** A local reference to the default gwt CSS. */
private org.opencms.gwt.client.ui.css.I_CmsLayoutBundle m_gwtCss = org.opencms.gwt.client.ui.css.I_CmsLayoutBundle.INSTANCE;
/** The close handler registration. */
private HandlerRegistration m_handlerReg;
/** Stores the list items of all added files. */
private Map<String, CmsListItem> m_listItems;
/** A panel for showing client loading. */
private FlowPanel m_loadingPanel;
/** A timer to delay the loading animation. */
private Timer m_loadingTimer;
/** The main panel. */
private FlowPanel m_mainPanel;
/** The OK button. */
private CmsPushButton m_okButton;
/** The progress bar for the upload process. */
private CmsUploadProgressInfo m_progressInfo;
/** Signals whether the selection is done or not. */
private boolean m_selectionDone;
/** The user information text widget. */
private HTML m_selectionSummary;
/** The target folder to upload the selected files. */
private String m_targetFolder;
/** The timer for updating the progress. */
private Timer m_updateProgressTimer = new Timer() {
/**
* @see com.google.gwt.user.client.Timer#run()
*/
@Override
public void run() {
updateProgress();
}
};
/** The upload button of this dialog. */
private CmsUploadButton m_uploadButton;
/** The upload service instance. */
private I_CmsUploadServiceAsync m_uploadService;
/**
* Default constructor.<p>
*/
public A_CmsUploadDialog() {
super(Messages.get().key(Messages.GUI_UPLOAD_DIALOG_TITLE_0));
I_CmsLayoutBundle.INSTANCE.uploadCss().ensureInjected();
m_data = (CmsUploadData)CmsRpcPrefetcher.getSerializedObject(getUploadService(), CmsUploadData.DICT_NAME);
setModal(true);
setGlassEnabled(true);
catchNotifications();
setWidth(DIALOG_WIDTH);
// create a map that stores all files (upload, existing, invalid)
m_allFiles = new HashMap<String, CmsFileInfo>();
// create a map the holds all the list items for the selection dialog
m_listItems = new HashMap<String, CmsListItem>();
m_filesToUnzip = new ArrayList<String>();
m_fileList = new CmsList<I_CmsListItem>();
m_fileList.truncate(TM_FILE_UPLOAD_LIST, DIALOG_WIDTH - 50);
// initialize a map that stores all the files that should be uploaded
m_filesToUpload = new HashMap<String, CmsFileInfo>();
// create the main panel
m_mainPanel = new FlowPanel();
// add the user info to the main panel
m_dialogInfo = new HTML();
m_dialogInfo.addStyleName(I_CmsLayoutBundle.INSTANCE.uploadCss().dialogInfo());
m_mainPanel.add(m_dialogInfo);
// add the content wrapper to the main panel
m_contentWrapper = new FlowPanel();
m_contentWrapper.addStyleName(I_CmsLayoutBundle.INSTANCE.uploadCss().mainContentWidget());
m_contentWrapper.addStyleName(m_gwtCss.generalCss().cornerAll());
m_contentWrapper.getElement().getStyle().setPropertyPx("minHeight", MIN_CONTENT_HEIGHT);
m_contentWrapper.add(m_fileList);
m_mainPanel.add(m_contentWrapper);
m_selectionSummary = new HTML();
m_selectionSummary.addStyleName(I_CmsLayoutBundle.INSTANCE.uploadCss().summary());
m_mainPanel.add(m_selectionSummary);
// set the main panel as content of the popup
setMainContent(m_mainPanel);
// create and add the "OK", "Cancel" and upload button
createButtons();
}
/**
* @see org.opencms.gwt.client.ui.CmsPopup#addCloseHandler(com.google.gwt.event.logical.shared.CloseHandler)
*/
@Override
public HandlerRegistration addCloseHandler(CloseHandler<PopupPanel> handler) {
m_closeHandler = handler;
m_handlerReg = super.addCloseHandler(handler);
return m_handlerReg;
}
/**
* Creates a bean that can be used for the list item widget.<p>
*
* @param file the info to create the bean for
*
* @return a list info bean
*/
public abstract CmsListInfoBean createInfoBean(CmsFileInfo file);
/**
* Returns the massage for too large files.<p>
*
* @param file the file
*
* @return the message
*/
public abstract String getFileSizeTooLargeMessage(CmsFileInfo file);
/**
* Returns <code>true</code> if the file is too large, <code>false</code> otherwise.<p>
*
* @param cmsFileInfo the file to check
*
* @return <code>true</code> if the file is too large, <code>false</code> otherwise
*/
public abstract boolean isTooLarge(CmsFileInfo cmsFileInfo);
/**
* Loads and shows this dialog.<p>
*/
public void loadAndShow() {
// enable or disable the OK button
if (getFilesToUpload().isEmpty()) {
disableOKButton(Messages.get().key(Messages.GUI_UPLOAD_NOTIFICATION_NO_FILES_0));
setDragAndDropMessage();
} else {
enableOKButton();
removeDragAndDropMessage();
}
// set the user info
displayDialogInfo(Messages.get().key(Messages.GUI_UPLOAD_INFO_SELECTION_0), false);
// set the selection summary
updateSummary();
// add a upload button
m_uploadButton.createFileInput();
// show the popup
if (!isShowing()) {
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
/**
* @see com.google.gwt.core.client.Scheduler.ScheduledCommand#execute()
*/
public void execute() {
setContentWrapperHeight();
center();
}
});
}
center();
}
/**
* Sets the target folder.<p>
*
* @param target the target folder to set
*/
public void setTargetFolder(String target) {
m_targetFolder = target;
}
/**
* Executes the submit action.<p>
*/
public abstract void submit();
/**
* Updates the file summary.<p>
*/
public abstract void updateSummary();
/**
* Adds the given file input field to this dialog.<p>
*
* @param fileInput the file input field to add
*/
protected void addFileInput(CmsFileInput fileInput) {
// add the files selected by the user to the list of files to upload
if (fileInput != null) {
addFiles(Arrays.asList(fileInput.getFiles()));
} else {
loadAndShow();
}
}
/**
* Adds the given file input field to this dialog.<p>
*
* @param fileInfos the file info objects
*/
protected void addFiles(List<CmsFileInfo> fileInfos) {
if (fileInfos != null) {
for (CmsFileInfo file : fileInfos) {
// store all files
m_allFiles.put(file.getFileName(), file);
// add those files to the list of files to upload that potential candidates
if (!isTooLarge(file) && (file.getFileSize() != 0)) {
m_filesToUpload.put(file.getFileName(), file);
}
// remove those files from the list to upload that were previously unchecked by the user
if ((m_listItems.get(file.getFileName()) != null)
&& (m_listItems.get(file.getFileName()).getCheckBox() != null)
&& !m_listItems.get(file.getFileName()).getCheckBox().isChecked()) {
m_filesToUpload.remove(file.getFileName());
}
}
// now rebuild the list: handle all files
m_fileList.clearList();
List<String> sortedFileNames = new ArrayList<String>(m_allFiles.keySet());
Collections.sort(sortedFileNames, String.CASE_INSENSITIVE_ORDER);
for (String filename : sortedFileNames) {
CmsFileInfo file = m_allFiles.get(filename);
addFileToList(file, false, isTooLarge(file));
}
}
loadAndShow();
}
/**
* Cancels the upload progress timer.<p>
*/
protected void cancelUpdateProgress() {
m_updateProgressTimer.cancel();
}
/**
* Cancels the upload.<p>
*/
protected void cancelUpload() {
m_canceled = true;
cancelUpdateProgress();
CmsRpcAction<Boolean> callback = new CmsRpcAction<Boolean>() {
/**
* @see org.opencms.gwt.client.rpc.CmsRpcAction#execute()
*/
@Override
public void execute() {
getUploadService().cancelUpload(this);
}
/**
* @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object)
*/
@Override
protected void onResponse(Boolean result) {
hide();
}
};
callback.execute();
}
/**
* Creates the loading animation HTML and adds is to the content wrapper.<p>
*
* @param msg the message to display below the animation
*/
protected void createLoadingAnimation(String msg) {
m_clientLoading = true;
m_loadingPanel = new FlowPanel();
m_loadingPanel.addStyleName(I_CmsLayoutBundle.INSTANCE.uploadCss().loadingPanel());
m_loadingPanel.addStyleName(m_gwtCss.generalCss().cornerAll());
HTML animationDiv = new HTML();
animationDiv.addStyleName(I_CmsLayoutBundle.INSTANCE.uploadCss().loadingAnimation());
m_loadingPanel.add(animationDiv);
HTML messageDiv = new HTML();
messageDiv.addStyleName(I_CmsLayoutBundle.INSTANCE.uploadCss().loadingText());
messageDiv.setHTML(msg);
m_loadingPanel.add(messageDiv);
m_contentWrapper.add(m_loadingPanel);
}
/**
* Disables the OK button.<p>
*
* @param disabledReason the reason for disabling the OK button
*/
protected void disableOKButton(String disabledReason) {
m_okButton.disable(disabledReason);
}
/**
* Enables the OK button.<p>
*/
protected void enableOKButton() {
m_okButton.enable();
}
/**
* Formats a given bytes value (file size).<p>
*
* @param filesize the file size to format
*
* @return the formated file size in KB
*/
protected String formatBytes(long filesize) {
double kByte = Math.ceil(filesize / KILOBYTE);
String formated = NumberFormat.getDecimalFormat().format(new Double(kByte));
return formated + " KB";
}
/**
* Returns the contentLength.<p>
*
* @return the contentLength
*/
protected long getContentLength() {
return m_contentLength;
}
/**
* Returns the contentWrapper.<p>
*
* @return the contentWrapper
*/
protected FlowPanel getContentWrapper() {
return m_contentWrapper;
}
/**
* Returns the data.<p>
*
* @return the data
*/
protected CmsUploadData getData() {
return m_data;
}
/**
* Returns the list of file names that have to unziped.<p>
*
* @param all <code>true</code> if the returned list should contain those filenames that
* are not inside the map of files to upload. <code>false</code> only those filenames are
* returned that are also inside the map of files to upload
*
* @return the list of file names that have to unziped
*/
protected List<String> getFilesToUnzip(boolean all) {
if (!all) {
List<String> result = new ArrayList<String>();
for (String fileName : m_filesToUnzip) {
if (m_filesToUpload.keySet().contains(fileName)) {
result.add(fileName);
}
}
return result;
}
return m_filesToUnzip;
}
/**
* Returns the filesToUpload.<p>
*
* @return the filesToUpload
*/
protected Map<String, CmsFileInfo> getFilesToUpload() {
return m_filesToUpload;
}
/**
* Returns "files" or "file" depending on the files to upload.<p>
*
* @return "files" or "file" depending on the files to upload
*/
protected String getFileText() {
if (m_filesToUpload.size() == 1) {
return Messages.get().key(Messages.GUI_UPLOAD_FILES_SINGULAR_0);
}
return Messages.get().key(Messages.GUI_UPLOAD_FILES_PLURAL_0);
}
/**
* Returns the resource type name for a given filename.<p>
*
* @param file the file info
*
* @return the resource type name
*/
protected String getResourceType(CmsFileInfo file) {
String typeName = null;
typeName = CmsCoreProvider.get().getExtensionMapping().get(file.getFileSuffix().toLowerCase());
if (typeName == null) {
typeName = "plain";
}
return typeName;
}
/**
* Returns the targetFolder.<p>
*
* @return the targetFolder
*/
protected String getTargetFolder() {
return m_targetFolder;
}
/**
* Returns the upload service instance.<p>
*
* @return the upload service instance
*/
protected I_CmsUploadServiceAsync getUploadService() {
if (m_uploadService == null) {
m_uploadService = GWT.create(I_CmsUploadService.class);
String serviceUrl = CmsCoreProvider.get().link("org.opencms.ade.upload.CmsUploadService.gwt");
((ServiceDefTarget)m_uploadService).setServiceEntryPoint(serviceUrl);
}
return m_uploadService;
}
/**
* Returns the upload JSP uri.<p>
*
* @return the upload JSP uri
*/
protected String getUploadUri() {
return CmsCoreProvider.get().link(I_CmsUploadConstants.UPLOAD_ACTION_JSP_URI);
}
/**
* Inserts a hidden form into.<p>
*
* @param form the form to insert
*/
protected void insertUploadForm(FormPanel form) {
form.getElement().getStyle().setDisplay(Display.NONE);
m_contentWrapper.add(form);
}
/**
* The action that is executed if the user clicks on the OK button.<p>
*
* If the selection dialog is currently shown the selected files are checked
* otherwise the upload is triggered.<p>
*/
protected void onOkClick() {
if (!m_selectionDone) {
checkSelection();
} else {
commit();
}
}
/**
* Execute to set the content wrapper height.<p>
*/
protected void setContentWrapperHeight() {
// set the max height of the content panel
int fixedContent = 0;
if (m_dialogInfo.isVisible()) {
fixedContent += m_dialogInfo.getOffsetHeight();
}
if (m_selectionSummary.isVisible()) {
fixedContent += m_selectionSummary.getOffsetHeight();
}
m_contentWrapper.getElement().getStyle().setPropertyPx("maxHeight", getAvailableHeight(fixedContent));
}
/**
* Parses the upload response of the server and decides what to do.<p>
*
* @param results a JSON Object
*/
protected void parseResponse(String results) {
cancelUpdateProgress();
stopLoadingAnimation();
if ((!m_canceled) && CmsStringUtil.isNotEmptyOrWhitespaceOnly(results)) {
JSONObject jsonObject = JSONParser.parseStrict(results).isObject();
boolean success = jsonObject.get(I_CmsUploadConstants.KEY_SUCCESS).isBoolean().booleanValue();
// If the upload is done so fast that we did not receive any progress information, then
// the content length is unknown. For that reason take the request size to show how
// much bytes were uploaded.
double size = jsonObject.get(I_CmsUploadConstants.KEY_REQUEST_SIZE).isNumber().doubleValue();
long requestSize = new Double(size).longValue();
if (m_contentLength == 0) {
m_contentLength = requestSize;
}
if (success) {
displayDialogInfo(Messages.get().key(Messages.GUI_UPLOAD_INFO_FINISHING_0), false);
m_progressInfo.finish();
closeOnSuccess();
} else {
String message = jsonObject.get(I_CmsUploadConstants.KEY_MESSAGE).isString().stringValue();
String stacktrace = jsonObject.get(I_CmsUploadConstants.KEY_STACKTRACE).isString().stringValue();
showErrorReport(message, stacktrace);
}
}
}
/**
* Decides how to go on depending on the information of the server response.<p>
*
* Shows a warning if there is another upload process active (inside the same session).<p>
*
* Otherwise if the list of files to upload contains already existent resources on the VFS or if there
* are files selected that have invalid file names the overwrite dialog is shown.<p>
*
* Only if there is no other upload process running and none of the selected files
* is already existent on the VFS the upload is triggered.<p>
*
* @param result the bean that contains the information to evaluate
*/
protected void proceedWorkflow(CmsUploadFileBean result) {
if (result.isActive()) {
m_okButton.enable();
CmsNotification.get().send(Type.WARNING, Messages.get().key(Messages.GUI_UPLOAD_NOTIFICATION_RUNNING_0));
} else {
if (!result.getExistingResourceNames().isEmpty() || !result.getInvalidFileNames().isEmpty()) {
showOverwriteDialog(result);
} else {
commit();
}
}
}
/**
* Sets the contentLength.<p>
*
* @param contentLength the contentLength to set
*/
protected void setContentLength(long contentLength) {
m_contentLength = contentLength;
}
/**
* Sets the HTML of the selection summary.<p>
*
* @param html the HTML to set as String
*/
protected void setSummaryHTML(String html) {
m_selectionSummary.setHTML(html);
}
/**
* Shows the error report.<p>
*
* @param message the message to show
* @param stacktrace the stacktrace to show
*/
protected void showErrorReport(final String message, final String stacktrace) {
if (!m_canceled) {
CmsErrorDialog errDialog = new CmsErrorDialog(message, stacktrace);
if (m_handlerReg != null) {
m_handlerReg.removeHandler();
}
if (m_closeHandler != null) {
errDialog.addCloseHandler(m_closeHandler);
}
hide();
errDialog.center();
}
}
/**
* Retrieves the progress information from the server.<p>
*/
protected void updateProgress() {
CmsRpcAction<CmsUploadProgessInfo> callback = new CmsRpcAction<CmsUploadProgessInfo>() {
/**
* @see org.opencms.gwt.client.rpc.CmsRpcAction#execute()
*/
@Override
public void execute() {
getUploadService().getUploadProgressInfo(this);
}
/**
* @see org.opencms.gwt.client.rpc.CmsRpcAction#onFailure(java.lang.Throwable)
*/
@Override
public void onFailure(Throwable t) {
super.onFailure(t);
cancelUpdateProgress();
}
/**
* @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object)
*/
@Override
protected void onResponse(CmsUploadProgessInfo result) {
updateProgressBar(result);
}
};
callback.execute();
}
/**
* Updates the progress bar.<p>
*
* @param info the progress info
*/
protected void updateProgressBar(CmsUploadProgessInfo info) {
switch (info.getState()) {
case notStarted:
break;
case running:
m_progressInfo.setProgress(info);
stopLoadingAnimation();
break;
case finished:
m_progressInfo.finish();
displayDialogInfo(Messages.get().key(Messages.GUI_UPLOAD_INFO_FINISHING_0), false);
startLoadingAnimation(Messages.get().key(Messages.GUI_UPLOAD_INFO_CREATING_RESOURCES_0), 1500);
break;
default:
break;
}
}
/**
* Adds a click handler for the given checkbox.<p>
*
* @param check the checkbox
* @param file the file
*/
private void addClickHandlerToCheckBox(final CmsCheckBox check, final CmsCheckBox unzip, final CmsFileInfo file) {
check.addClickHandler(new ClickHandler() {
/**
* @see com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event.dom.client.ClickEvent)
*/
public void onClick(ClickEvent event) {
// add or remove the file from the list of files to upload
if (check.isChecked()) {
getFilesToUpload().put(file.getFileName(), file);
if (unzip != null) {
unzip.enable();
}
} else {
getFilesToUpload().remove(file.getFileName());
if (unzip != null) {
unzip.disable(Messages.get().key(Messages.GUI_UPLOAD_FILE_NOT_SELECTED_0));
}
}
// disable or enable the OK button
if (getFilesToUpload().isEmpty()) {
disableOKButton(Messages.get().key(Messages.GUI_UPLOAD_NOTIFICATION_NO_FILES_0));
} else {
enableOKButton();
}
// update summary
updateSummary();
}
});
}
/**
* Adds a file to the list.<p>
*
* @param file the file to add
* @param invalid signals if the filename is invalid
* @param isTooLarge signals if the file size limit is exceeded
* @param isFolder signals if the file is a folder
*/
private void addFileToList(final CmsFileInfo file, boolean invalid, boolean isTooLarge) {
CmsListInfoBean infoBean = createInfoBean(file);
CmsListItemWidget listItemWidget = new CmsListItemWidget(infoBean);
String icon = CmsIconUtil.getResourceIconClasses(getResourceType(file), file.getFileName(), false);
listItemWidget.setIcon(icon);
CmsCheckBox check = new CmsCheckBox();
check.setChecked(false);
if (!invalid && !isTooLarge) {
if (file.getFileSize() == 0) {
check.setChecked(false);
}
check.setChecked(m_filesToUpload.containsKey(file.getFileName()));
check.setTitle(file.getFileName());
if (!m_selectionDone && file.getFileName().toLowerCase().endsWith(".zip")) {
final CmsCheckBox unzip = createUnzipCheckBox(file);
addClickHandlerToCheckBox(check, unzip, file);
listItemWidget.addButton(unzip);
} else {
addClickHandlerToCheckBox(check, null, file);
}
} else if (isTooLarge) {
String message = getFileSizeTooLargeMessage(file);
check.disable(message);
listItemWidget.setBackground(Background.RED);
listItemWidget.setSubtitleLabel(message);
} else {
// is invalid
String message = Messages.get().key(
Messages.GUI_UPLOAD_FILE_INVALID_NAME_2,
file.getFileName(),
formatBytes(file.getFileSize()));
check.disable(message);
listItemWidget.setBackground(Background.RED);
listItemWidget.setSubtitleLabel(message);
}
CmsListItem listItem = new CmsListItem(check, listItemWidget);
m_fileList.addItem(listItem);
m_listItems.put(file.getFileName(), listItem);
}
/**
* Changes the height of the content wrapper so that the dialog finally has the
* same height that the dialog has when the min height is set on the selection screen.<p>
*/
private void changeHeight() {
int firstHeight = MIN_CONTENT_HEIGHT + m_firstInfoHeight + m_firstSummaryHeight + 2;
int currentHeight = CmsDomUtil.getCurrentStyleInt(m_mainPanel.getElement(), CmsDomUtil.Style.height);
int targetHeight = firstHeight - m_dialogInfo.getOffsetHeight() - m_selectionSummary.getOffsetHeight();
if (currentHeight > firstHeight) {
CmsChangeHeightAnimation.change(m_contentWrapper.getElement(), targetHeight, null, 750);
}
}
/**
* Before the upload data is effectively submited we have to check
* for already existent resources in the VFS.<p>
*
* Executes the RPC call that checks the VFS for existing resources.
* Passes the response object to a method that evaluates the result.<p>
*/
private void checkSelection() {
m_okButton.disable(Messages.get().key(Messages.GUI_UPLOAD_BUTTON_OK_DISABLE_CHECKING_0));
if (!m_selectionDone) {
m_firstContentHeight = CmsDomUtil.getCurrentStyleInt(m_contentWrapper.getElement(), CmsDomUtil.Style.height);
m_firstInfoHeight = m_dialogInfo.getOffsetHeight();
m_firstSummaryHeight = m_selectionSummary.getOffsetHeight();
}
CmsRpcAction<CmsUploadFileBean> callback = new CmsRpcAction<CmsUploadFileBean>() {
/**
* @see org.opencms.gwt.client.rpc.CmsRpcAction#execute()
*/
@Override
public void execute() {
List<String> filesToCheck = new ArrayList<String>(getFilesToUpload().keySet());
filesToCheck.removeAll(getFilesToUnzip(false));
getUploadService().checkUploadFiles(filesToCheck, getTargetFolder(), this);
}
/**
* @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object)
*/
@Override
protected void onResponse(CmsUploadFileBean result) {
proceedWorkflow(result);
}
};
callback.execute();
}
/**
* Closes the dialog after a delay.<p>
*/
private void closeOnSuccess() {
Timer closeTimer = new Timer() {
/**
* @see com.google.gwt.user.client.Timer#run()
*/
@Override
public void run() {
A_CmsUploadDialog.this.hide();
}
};
closeTimer.schedule(1500);
}
/**
* Calls the submit action if there are any files selected for upload.<p>
*/
private void commit() {
m_selectionDone = true;
if (!m_filesToUpload.isEmpty()) {
m_okButton.disable(Messages.get().key(Messages.GUI_UPLOAD_BUTTON_OK_DISABLE_UPLOADING_0));
m_uploadButton.getElement().getStyle().setDisplay(Display.NONE);
showProgress();
submit();
}
}
/**
* Creates the "OK", the "Cancel" and the "Upload" button.<p>
*/
private void createButtons() {
addDialogClose(new Command() {
public void execute() {
cancelUpload();
}
});
CmsPushButton cancelButton = new CmsPushButton();
cancelButton.setTitle(org.opencms.gwt.client.Messages.get().key(org.opencms.gwt.client.Messages.GUI_CANCEL_0));
cancelButton.setText(org.opencms.gwt.client.Messages.get().key(org.opencms.gwt.client.Messages.GUI_CANCEL_0));
cancelButton.setSize(I_CmsButton.Size.medium);
cancelButton.setUseMinWidth(true);
cancelButton.addClickHandler(new ClickHandler() {
/**
* @see com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event.dom.client.ClickEvent)
*/
public void onClick(ClickEvent event) {
cancelUpload();
}
});
addButton(cancelButton);
m_okButton = new CmsPushButton();
m_okButton.setTitle(org.opencms.gwt.client.Messages.get().key(org.opencms.gwt.client.Messages.GUI_OK_0));
m_okButton.setText(org.opencms.gwt.client.Messages.get().key(org.opencms.gwt.client.Messages.GUI_OK_0));
m_okButton.setSize(I_CmsButton.Size.medium);
m_okButton.setUseMinWidth(true);
m_okButton.addClickHandler(new ClickHandler() {
/**
* @see com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event.dom.client.ClickEvent)
*/
public void onClick(ClickEvent event) {
onOkClick();
}
});
addButton(m_okButton);
// add a new upload button
m_uploadButton = new CmsUploadButton(this);
m_uploadButton.addStyleName(I_CmsLayoutBundle.INSTANCE.uploadCss().uploadDialogButton());
m_uploadButton.setText(Messages.get().key(Messages.GUI_UPLOAD_BUTTON_ADD_FILES_0));
addButton(m_uploadButton);
}
/**
* Creates the unzip checkbox.<p>
*
* @param file the file to create the checkbox for
*
* @return the unzip checkbox
*/
private CmsCheckBox createUnzipCheckBox(final CmsFileInfo file) {
final CmsCheckBox unzip = new CmsCheckBox();
unzip.addStyleName(org.opencms.gwt.client.ui.css.I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().permaVisible());
unzip.setChecked(getFilesToUnzip(true).contains(file.getFileName()));
unzip.setTitle(Messages.get().key(Messages.GUI_UPLOAD_UNZIP_FILE_0));
if (!m_filesToUpload.containsKey(file.getFileName())) {
unzip.disable(Messages.get().key(Messages.GUI_UPLOAD_FILE_NOT_SELECTED_0));
}
unzip.addClickHandler(new ClickHandler() {
/**
* @see com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event.dom.client.ClickEvent)
*/
public void onClick(ClickEvent event) {
// add or remove the file from the list of files to upload
if (unzip.isChecked()) {
getFilesToUnzip(true).add(file.getFileName());
} else {
getFilesToUnzip(true).remove(file.getFileName());
}
}
});
return unzip;
}
/**
* Sets the user info.<p>
*
* @param msg the message to display
* @param warning signals whether the message should be a warning or nor
*/
private void displayDialogInfo(String msg, boolean warning) {
StringBuffer buffer = new StringBuffer(64);
if (!warning) {
buffer.append("<p class=\"");
buffer.append(I_CmsLayoutBundle.INSTANCE.uploadCss().dialogMessage());
buffer.append("\">");
buffer.append(msg);
buffer.append("</p>");
} else {
buffer.append("<div class=\"");
buffer.append(I_CmsLayoutBundle.INSTANCE.uploadCss().warningIcon());
buffer.append("\"></div>");
buffer.append("<p class=\"");
buffer.append(I_CmsLayoutBundle.INSTANCE.uploadCss().warningMessage());
buffer.append("\">");
buffer.append(msg);
buffer.append("</p>");
}
m_dialogInfo.setHTML(buffer.toString());
}
/**
* Removes all widgets from the content wrapper.<p>
*/
private void removeContent() {
int widgetCount = m_contentWrapper.getWidgetCount();
for (int i = 0; i < widgetCount; i++) {
m_contentWrapper.remove(0);
}
}
/**
* Sets the height for the content so that the dialog finally has the same height
* as the dialog has on the selection screen.<p>
*/
private void setHeight() {
int infoDiff = m_firstInfoHeight - m_dialogInfo.getOffsetHeight();
int summaryDiff = m_firstSummaryHeight - m_selectionSummary.getOffsetHeight();
int height = m_firstContentHeight + infoDiff + summaryDiff;
m_contentWrapper.getElement().getStyle().setHeight(height, Unit.PX);
m_contentWrapper.getElement().getStyle().clearProperty("minHeight");
m_contentWrapper.getElement().getStyle().clearProperty("maxHeight");
}
/**
* Shows the overwrite dialog.<p>
*
* @param infoBean the info bean containing the existing and invalid file names
*/
private void showOverwriteDialog(CmsUploadFileBean infoBean) {
// update the dialog
m_selectionDone = true;
m_okButton.enable();
displayDialogInfo(Messages.get().key(Messages.GUI_UPLOAD_INFO_OVERWRITE_0), true);
m_uploadButton.getElement().getStyle().setDisplay(Display.NONE);
// clear the list
m_fileList.clearList();
// handle existing files
List<String> existings = new ArrayList<String>(infoBean.getExistingResourceNames());
Collections.sort(existings, String.CASE_INSENSITIVE_ORDER);
for (String filename : existings) {
addFileToList(m_filesToUpload.get(filename), false, false);
}
// handle the invalid files
List<String> invalids = new ArrayList<String>(infoBean.getInvalidFileNames());
Collections.sort(invalids, String.CASE_INSENSITIVE_ORDER);
for (String filename : invalids) {
addFileToList(m_filesToUpload.get(filename), true, false);
m_filesToUpload.remove(filename);
}
// set the height of the content
setHeight();
}
/**
* Starts the upload progress bar.<p>
*/
private void showProgress() {
removeContent();
displayDialogInfo(Messages.get().key(Messages.GUI_UPLOAD_INFO_UPLOADING_0), false);
m_selectionSummary.removeFromParent();
m_progressInfo = new CmsUploadProgressInfo();
m_contentWrapper.add(m_progressInfo);
m_updateProgressTimer.scheduleRepeating(UPDATE_PROGRESS_INTERVALL);
startLoadingAnimation(Messages.get().key(Messages.GUI_UPLOAD_CLIENT_LOADING_0), 0);
setHeight();
changeHeight();
}
/**
* Starts the loading animation.<p>
*
* Used while client is loading files from hard disk into memory.<p>
*
* @param msg the message that should be displayed below the loading animation (can also be HTML as String)
*/
private void startLoadingAnimation(final String msg, int delayMillis) {
m_loadingTimer = new Timer() {
@Override
public void run() {
createLoadingAnimation(msg);
}
};
if (delayMillis > 0) {
m_loadingTimer.schedule(delayMillis);
} else {
m_loadingTimer.run();
}
}
/**
* Stops the client loading animation.<p>
*/
private void stopLoadingAnimation() {
if (m_loadingTimer != null) {
m_loadingTimer.cancel();
}
if (m_clientLoading) {
m_contentWrapper.remove(m_loadingPanel);
m_clientLoading = false;
}
}
/**
* Displays the 'use drag and drop' / 'no drag and drop available' message.<p>
*/
protected void setDragAndDropMessage() {
if (m_dragAndDropMessage == null) {
m_dragAndDropMessage = new HTML();
m_dragAndDropMessage.setStyleName(I_CmsLayoutBundle.INSTANCE.uploadCss().dragAndDropMessage());
m_dragAndDropMessage.setText(Messages.get().key(Messages.GUI_UPLOAD_DRAG_AND_DROP_DISABLED_0));
}
getContentWrapper().add(m_dragAndDropMessage);
getContentWrapper().getElement().getStyle().setBackgroundColor(
I_CmsConstantsBundle.INSTANCE.css().notificationErrorBg());
}
/**
* Removes the drag and drop message.<p>
*/
protected void removeDragAndDropMessage() {
if (m_dragAndDropMessage != null) {
m_dragAndDropMessage.removeFromParent();
m_dragAndDropMessage = null;
getContentWrapper().getElement().getStyle().clearBackgroundColor();
}
}
}