/* * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * This 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 software 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. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.xwiki.gwt.wysiwyg.client.widget.wizard.util; import org.xwiki.gwt.user.client.StringUtils; import org.xwiki.gwt.user.client.ui.wizard.AbstractInteractiveWizardStep; import org.xwiki.gwt.wysiwyg.client.Strings; import org.xwiki.gwt.wysiwyg.client.wiki.Attachment; import org.xwiki.gwt.wysiwyg.client.wiki.AttachmentReference; import org.xwiki.gwt.wysiwyg.client.wiki.WikiPageReference; import org.xwiki.gwt.wysiwyg.client.wiki.WikiServiceAsync; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.FileUpload; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.FormPanel; import com.google.gwt.user.client.ui.FormPanel.SubmitCompleteEvent; import com.google.gwt.user.client.ui.FormPanel.SubmitCompleteHandler; import com.google.gwt.user.client.ui.InlineLabel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.Panel; /** * Wizard step to handle the upload of a file to a wiki page: display the file input and upload on finish. * * @version $Id: b539342b892d7e8b9302de38de5df50ce04e7089 $ */ public abstract class AbstractFileUploadWizardStep extends AbstractInteractiveWizardStep { /** * The style of the fields under error. */ protected static final String FIELD_ERROR_STYLE = "xErrorField"; /** * The style used on the field label. */ protected static final String FIELD_LABEL_STYLE = "xInfoLabel"; /** * The style used on the field hint. */ protected static final String FIELD_HINT_STYLE = "xHelpLabel"; /** * The file upload form. */ private final FormPanel fileUploadForm = new FormPanel(); /** * The file input in the file upload form. */ private final FileUpload fileUploadInput = new FileUpload(); /** * The error label for the file input. */ private final Label fileErrorLabel = new Label(); /** * The help label for the file input. */ private final Label fileHelpLabel = new Label(); /** * The service used to access the uploaded attachments. */ private final WikiServiceAsync wikiService; /** * Creates a new file upload wizard step that uses the given service to get information about the uploaded files. * * @param wikiService the service used to access the uploaded attachments */ public AbstractFileUploadWizardStep(WikiServiceAsync wikiService) { this.wikiService = wikiService; setStepTitle(Strings.INSTANCE.fileUploadTitle()); display().addStyleName("xUploadPanel"); fileUploadForm.setEncoding(FormPanel.ENCODING_MULTIPART); fileUploadForm.setMethod(FormPanel.METHOD_POST); // set the url on submit time, just before upload Panel fileLabel = new FlowPanel(); fileLabel.setStyleName(FIELD_LABEL_STYLE); fileLabel.add(new InlineLabel(getFileLabel())); InlineLabel mandatoryLabel = new InlineLabel(Strings.INSTANCE.mandatory()); mandatoryLabel.addStyleName("xMandatory"); fileLabel.add(mandatoryLabel); fileUploadInput.setName(getFileUploadInputName()); FlowPanel formPanel = new FlowPanel(); formPanel.add(fileLabel); fileHelpLabel.setStyleName(FIELD_HINT_STYLE); fileHelpLabel.setVisible(false); formPanel.add(fileHelpLabel); fileErrorLabel.addStyleName("xErrorMsg"); fileErrorLabel.setVisible(false); formPanel.add(fileErrorLabel); formPanel.add(fileUploadInput); fileUploadForm.setWidget(formPanel); display().add(fileUploadForm); } /** * @return the label of the file input */ protected String getFileLabel() { return Strings.INSTANCE.fileUploadLabel(); } /** * @return the help label for the file input */ public String getFileHelpLabel() { return fileHelpLabel.getText(); } /** * Sets the help label for the file input. * * @param fileHelpLabelText the new help label text for the file input */ public void setFileHelpLabel(String fileHelpLabelText) { fileHelpLabel.setVisible(!StringUtils.isEmpty(fileHelpLabelText)); fileHelpLabel.setText(fileHelpLabelText); } /** * Requests the upload URL from the server. * * @param cb the object to be notified when the upload URL is received */ protected void getUploadURL(AsyncCallback<String> cb) { wikiService.getUploadURL(getTargetPageReference(), cb); } /** * @return the {@code name} attribute of the {@link #fileUploadInput}, to be returned by subclasses implementing * {@link #getUploadURL(AsyncCallback)} to set the file upload form data */ protected String getFileUploadInputName() { return "filepath"; } @Override public void init(Object data, AsyncCallback< ? > cb) { hideError(); cb.onSuccess(null); } @Override public void onCancel() { // ignore } @Override public void onSubmit(final AsyncCallback<Boolean> async) { getUploadURL(new AsyncCallback<String>() { @Override public void onFailure(Throwable caught) { async.onFailure(caught); } @Override public void onSuccess(String result) { submitTo(result, async); } }); } /** * Submits the upload form the the specified upload URL and notifies the given call-back when the response is * received. * * @param uploadURL where to submit the upload form * @param async the object to be notifies when the response is received */ private void submitTo(String uploadURL, final AsyncCallback<Boolean> async) { // Set the upload URL. fileUploadForm.setAction(uploadURL); // Handle the submit complete event on the file upload form. // Note: The registrations array is just a hack to be able to remove the handler from within the handler itself // (otherwise we get the "local variable may not have been initialized" compiler error). final HandlerRegistration[] registrations = new HandlerRegistration[1]; registrations[0] = fileUploadForm.addSubmitCompleteHandler(new SubmitCompleteHandler() { public void onSubmitComplete(SubmitCompleteEvent event) { AbstractFileUploadWizardStep.this.onSubmitComplete(event, async); // Stop handling the submit complete event on the file upload form. registrations[0].removeHandler(); } }); hideError(); // validate the form field if (fileUploadInput.getFilename().trim().length() == 0) { displayError(Strings.INSTANCE.fileUploadNoPathError()); async.onSuccess(false); return; } // Otherwise continue with submit. fileUploadForm.submit(); } /** * Handles the submit completion in asynchronous mode, to pass the result of processing the result in the received * callback. * * @param event the original {@link SubmitCompleteEvent} * @param async the callback used to send back the response of form event processing * @see #onSubmit */ protected void onSubmitComplete(SubmitCompleteEvent event, final AsyncCallback<Boolean> async) { // Create the link reference. wikiService.getAttachment(getAttachmentReference(extractFileName()), new AsyncCallback<Attachment>() { public void onSuccess(Attachment result) { if (result == null) { // there was a problem with the attachment, call it a failure displayError(Strings.INSTANCE.fileUploadSubmitError()); async.onSuccess(false); } else { onAttachmentUploaded(result, async); } } public void onFailure(Throwable caught) { async.onFailure(caught); } }); } /** * Notifies the successful completion of a file upload, to be overridden by subclasses to provide specific behavior. * * @param attach the successfully uploaded attachment * @param async the call-back used to indicate the completion of this method. It's required that the sub-classes * invoke async.onSuccess(true); method once they are done with processing the attachment. Without this * method being invoked, the submission of this wizard step will not complete. */ protected abstract void onAttachmentUploaded(Attachment attach, AsyncCallback<Boolean> async); /** * @return a reference to the page where the attachment will be uploaded */ protected abstract WikiPageReference getTargetPageReference(); /** * @param fileName the name of the file used to upload the attachment * @return a reference to the specified attachment */ private AttachmentReference getAttachmentReference(String fileName) { WikiPageReference targetPageReference = getTargetPageReference(); return new AttachmentReference(fileName, targetPageReference); } /** * @return the filename set in the file upload field. */ protected String extractFileName() { // not correct, since it strips \ out of unix filenames, but consistent with UploadAction behaviour, which we // need to match to get the correct information about uploaded file String fname = getFileUploadInput().getFilename(); fname = StringUtils.substringAfterLast(fname, "/"); fname = StringUtils.substringAfterLast(fname, "\\"); return fname; } /** * @return the fileUploadInput */ protected FileUpload getFileUploadInput() { return fileUploadInput; } /** * Displays the error message and markers for this dialog. * * @param errorMessage the error message to display. */ protected void displayError(String errorMessage) { fileErrorLabel.setText(errorMessage); fileErrorLabel.setVisible(true); fileUploadInput.addStyleName(FIELD_ERROR_STYLE); } /** * Hides the error message and markers for this dialog. */ protected void hideError() { fileErrorLabel.setVisible(false); fileUploadInput.removeStyleName(FIELD_ERROR_STYLE); } }