package er.ajax.mootools;
import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.text.NumberFormat;
import com.webobjects.appserver.WOActionResults;
import com.webobjects.appserver.WOApplication;
import com.webobjects.appserver.WOComponent;
import com.webobjects.appserver.WOContext;
import com.webobjects.foundation.NSData;
import er.ajax.AjaxFileUpload;
import er.ajax.AjaxFileUploadRequestHandler;
import er.ajax.AjaxResponse;
import er.ajax.AjaxUploadProgress;
import er.ajax.AjaxUtils;
import er.extensions.appserver.ERXRequest;
import er.extensions.appserver.ERXWOContext;
import er.extensions.components.ERXComponentUtilities;
import er.extensions.formatters.ERXUnitAwareDecimalFormat;
import er.extensions.foundation.ERXFileUtilities;
/**
* AjaxFileUpload provides an Ajax wrapper around the file upload process. This works pretty differently than
* WOFileUpload. The AjaxFileUpload component itself provides its own form and autosubmits when the user selects a file
* to upload. The upload runs in a hidden iframe, with Ajax updates occurring in the main window. When the final ajax
* update occurs after the completion of the upload, the appropriate actions fire. This means that if the user navigates
* away during the upload, no completion/failure/etc notifications will occur.
*
* @binding cancelText the text to display for the cancel link
* @binding cancelingText the text to display when the progress is being canceled
* @binding startingText the text to display when the progress is starting
* @binding startedFunction the javascript function to execute when the progress is started
* @binding canceledFunction the javascript function to execute when the upload is canceled
* @binding succeededFunction the javascript function to execute when the upload succeeds
* @binding failedFunction the javascript function to execute when the upload fails
* @binding finishedFunction the javascript function to execute when the upload finishes (succeeded, failed, or
* canceled)
* @binding finishedAction the action to fire when the upload finishes (cancel, failed, or succeeded)
* @binding canceledAction the action to fire when the upload is canceled
* @binding succeededAction the action to fire when the upload succeeded
* @binding failedAction the action to fire when the upload fails
* @binding data the NSData that will be bound with the contents of the upload
* @binding inputStream will be bound to an input stream on the contents of the upload
* @binding outputStream the output stream to write the contents of the upload to
* @binding streamToFilePath the path to write the upload to, can be a directory
* @binding finalFilePath the final file path of the upload (when streamToFilePath is set or keepTempFile = true)
* @binding filePath the name of the uploaded file
* @binding allowCancel if true, the cancel link is visible
* @binding progressBarBeforeStart if true, the progress bar is visible before the upload is started
* @binding progressBarAfterDone if true, the progress bar is visible after the upload is done
* @binding refreshTime the number of milliseconds to wait between refreshes
* @binding keepTempFile if true, don't delete the temp file that AjaxFileUpload creates
* @binding uploadLabel the label to display on the Upload button ("Upload" by default)
* @binding uploadFunctionName the upload button will instead be a function with the given name
* @binding progressOfText the text to display for the word "of" in the "[size] of [totalsize]" string during upload
* @binding mimeType set from the content-type of the upload header if available
* @binding class the class attribute of the file input
* @binding style the style attribute of the file input
* @binding id the id attribute of the file input
* @binding onFileSelected optional JS code that is called when the file selection changes. To auto-start the upload when
* a file is selected, set uploadFunctionName to e.g. "startUpload" and onFileSelected to "startUpload()"
* @binding uploadProgress access to the underlying AjaxUploadProgress object
*
* @author mschrag
*/
public class MTAjaxFileUpload extends WOComponent {
private static final long serialVersionUID = 1L;
private static boolean _requestHandlerRegistered = false;
private String _id;
protected boolean _uploadStarted;
protected AjaxUploadProgress _progress;
protected boolean _triggerUploadStart;
private String _requestHandlerKey;
public MTAjaxFileUpload(WOContext context) {
super(context);
_requestHandlerKey = AjaxFileUploadRequestHandler.REQUEST_HANDLER_KEY;
if (!_requestHandlerRegistered) {
synchronized (AjaxFileUpload.class) {
if (!_requestHandlerRegistered) {
if (WOApplication.application().requestHandlerForKey(AjaxFileUploadRequestHandler.REQUEST_HANDLER_KEY) == null) {
WOApplication.application().registerRequestHandler(new AjaxFileUploadRequestHandler(), AjaxFileUploadRequestHandler.REQUEST_HANDLER_KEY);
}
_requestHandlerRegistered = true;
}
}
}
}
public void setRequestHandlerKey(String requestHandlerKey) {
_requestHandlerKey = requestHandlerKey;
}
public String requestHandlerKey() {
return _requestHandlerKey;
}
@Override
public boolean synchronizesVariablesWithBindings() {
return false;
}
public String uploadLabel() {
String uploadLabel = (String)valueForBinding("uploadLabel");
if (uploadLabel == null) {
uploadLabel = "Upload";
}
return uploadLabel;
}
public boolean progressBarBeforeStart() {
boolean progressBarBeforeStart = false;
if (hasBinding("progressBarBeforeStart")) {
progressBarBeforeStart = ERXComponentUtilities.booleanValueForBinding(this, "progressBarBeforeStart");
}
return progressBarBeforeStart;
}
public boolean progressBarAfterDone() {
boolean progressBarAfterDone = false;
if (hasBinding("progressBarAfterDone")) {
progressBarAfterDone = ERXComponentUtilities.booleanValueForBinding(this, "progressBarAfterDone");
}
return progressBarAfterDone;
}
public void setUploadProgress(AjaxUploadProgress progress) {
_progress = progress;
if (hasBinding("uploadProgress")) {
setValueForBinding(progress, "uploadProgress");
}
if (progress == null && !hasBinding("uploadStarted")) {
_uploadStarted = false;
}
}
public AjaxUploadProgress uploadProgress() {
return _progress;
}
public String id() {
String id = _id;
if (id == null) {
id = (String) valueForBinding("id");
if (id == null) {
id = ERXWOContext.safeIdentifierName(context(), true);
}
_id = id;
}
return id;
}
public String uploadUrl() {
String uploadUrl = context().urlWithRequestHandlerKey(_requestHandlerKey, "", null);
return uploadUrl;
}
public String bytesReadSize() {
String bytesReadSize = null;
AjaxUploadProgress progress = uploadProgress();
if (progress != null) {
NumberFormat formatter = new ERXUnitAwareDecimalFormat(ERXUnitAwareDecimalFormat.BYTE);
formatter.setMaximumFractionDigits(2);
bytesReadSize = formatter.format(progress.value());
}
return bytesReadSize;
}
public String streamLengthSize() {
String streamLengthSize = null;
AjaxUploadProgress progress = uploadProgress();
if (progress != null) {
NumberFormat formatter = new ERXUnitAwareDecimalFormat(ERXUnitAwareDecimalFormat.BYTE);
formatter.setMaximumFractionDigits(2);
streamLengthSize = formatter.format(progress.maximum());
}
return streamLengthSize;
}
public String uploadFrameName() {
return id() + "UploadFrame";
}
public String startUploadName() {
return id() + "StartUpload";
}
public String startUploadFunctionCall() {
return "form.submit(true); " + startUploadName() + "();";
}
public boolean triggerStartUpload() {
boolean triggerUploadStart = _triggerUploadStart;
if (triggerUploadStart) {
_triggerUploadStart = false;
}
return triggerUploadStart;
}
public String uploadFormID() {
return id() + "Form";
}
public String progressBarID() {
return id() + "ProgressBar";
}
public String startingText() {
String startingText = (String) valueForBinding("startingText");
if (startingText == null) {
startingText = "Upload Starting ...";
}
return startingText;
}
public String cancelText() {
String cancelText = (String) valueForBinding("cancelText");
if (cancelText == null) {
cancelText = "cancel";
}
return cancelText;
}
public String cancelingText() {
String cancelingText = (String) valueForBinding("cancelingText");
if (cancelingText == null) {
cancelingText = "Canceling Upload ...";
}
return cancelingText;
}
public WOActionResults startUpload() {
_triggerUploadStart = true;
if (_progress != null) {
_progress.reset();
}
_progress = null;
if (hasBinding("uploadProgress")) {
setValueForBinding(null, "uploadProgress");
}
_uploadStarted = true;
if (hasBinding("uploadStarted")) {
setValueForBinding(Boolean.TRUE, "uploadStarted");
}
AjaxResponse response = AjaxUtils.createResponse(context().request(), context());
AjaxUtils.appendScriptHeaderIfNecessary(context().request(), response);
response.appendContentString("document." + uploadFormID() + ".submit();");
AjaxUtils.appendScriptFooterIfNecessary(context().request(), response);
return response;
}
public boolean isUploadStarted() {
boolean uploadStarted;
if (hasBinding("uploadStarted")) {
uploadStarted = ERXComponentUtilities.booleanValueForBinding(this, "uploadStarted");
}
else {
AjaxUploadProgress progress = uploadProgress();
if (progress != null && progress.shouldReset()) {
_uploadStarted = false;
setValueForBinding(Boolean.FALSE, "uploadStarted");
}
uploadStarted = _uploadStarted;
}
return uploadStarted;
}
public void uploadFinished() {
valueForBinding("finishedAction");
}
public WOActionResults uploadCanceled() {
uploadFinished();
WOActionResults results = (WOActionResults) valueForBinding("canceledAction");
return results;
}
public WOActionResults uploadSucceeded() {
AjaxUploadProgress progress = uploadProgress();
try {
boolean deleteFile = true;
if (hasBinding("filePath")) {
setValueForBinding(progress.fileName(), "filePath");
}
if (hasBinding("data")) {
NSData data = new NSData(progress.tempFile().toURI().toURL());
setValueForBinding(data, "data");
}
if (hasBinding("mimeType")) {
String contentType = progress.contentType();
if (contentType != null) {
setValueForBinding(contentType, "mimeType");
}
}
if (hasBinding("inputStream")) {
setValueForBinding(new FileInputStream(progress.tempFile()), "inputStream");
deleteFile = false;
}
if (hasBinding("outputStream")) {
OutputStream outputStream = (OutputStream) valueForBinding("outputStream");
if (outputStream != null) {
ERXFileUtilities.writeInputStreamToOutputStream(new FileInputStream(progress.tempFile()), true, outputStream, true);
}
}
String finalFilePath = progress.tempFile().getAbsolutePath();
if (hasBinding("streamToFilePath")) {
File streamToFile = new File((String) valueForBinding("streamToFilePath"));
boolean renamedFile;
boolean renameFile;
if (streamToFile.exists()) {
boolean overwrite = ERXComponentUtilities.booleanValueForBinding(this, "overwrite");
if (streamToFile.isDirectory()) {
File parentDir = streamToFile;
String fileName = ERXFileUtilities.fileNameFromBrowserSubmittedPath(progress.fileName());
streamToFile = ERXFileUtilities.reserveUniqueFile(new File(parentDir, fileName), overwrite);
renameFile = true;
}
else {
renameFile = overwrite;
}
}
else {
renameFile = true;
}
if (renameFile && !streamToFile.isDirectory()) {
ERXFileUtilities.renameTo(progress.tempFile(), streamToFile);
renamedFile = true;
}
else {
renamedFile = false;
progress.setFailure(new Exception ("Could not rename file."));
return uploadFailed();
}
if (renamedFile) {
finalFilePath = streamToFile.getAbsolutePath();
}
deleteFile = false;
}
else if (hasBinding("keepTempFile") && deleteFile) {
deleteFile = !ERXComponentUtilities.booleanValueForBinding(this, "keepTempFile");
}
if (deleteFile) {
progress.dispose();
}
else if (hasBinding("finalFilePath")) {
setValueForBinding(finalFilePath, "finalFilePath");
}
}
catch (Throwable t) {
t.printStackTrace();
progress.setFailure(t);
return uploadFailed();
}
finally {
uploadFinished();
}
WOActionResults results = (WOActionResults) valueForBinding("succeededAction");
return results;
}
public WOActionResults uploadFailed() {
uploadFinished();
WOActionResults results = (WOActionResults) valueForBinding("failedAction");
return results;
}
public String srcUrl() {
return context()._directActionURL("ERXDirectAction/empty", null, ERXRequest.isRequestSecure(context().request()), 0, false);
}
}