package org.gcube.portlets.user.shareupdates.client.view; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import net.eliasbalasis.tibcopagebus4gwt.client.PageBusAdapter; import net.eliasbalasis.tibcopagebus4gwt.client.PageBusAdapterException; import org.gcube.portal.databook.shared.ClientFeed; import org.gcube.portal.databook.shared.ClientFeed.ClientFeedJsonizer; import org.gcube.portal.databook.shared.FeedType; import org.gcube.portal.databook.shared.PrivacyLevel; import org.gcube.portal.databook.shared.UserInfo; import org.gcube.portlets.user.shareupdates.client.ShareUpdateService; import org.gcube.portlets.user.shareupdates.client.ShareUpdateServiceAsync; import org.gcube.portlets.user.shareupdates.shared.LinkPreview; import org.gcube.portlets.user.shareupdates.shared.UploadedFile; import org.gcube.portlets.user.shareupdates.shared.UserSettings; import org.gcube.portlets.widgets.fileupload.client.events.FileTooLargeEvent; import org.gcube.portlets.widgets.fileupload.client.events.FileTooLargeEventHandler; import org.gcube.portlets.widgets.fileupload.client.events.FileUploadCompleteEvent; import org.gcube.portlets.widgets.fileupload.client.events.FileUploadCompleteEventHandler; import org.gcube.portlets.widgets.fileupload.client.view.FileSubmit; import org.gcube.portlets.widgets.fileupload.client.view.UploadProgressPanel; import org.jsonmaker.gwt.client.Jsonizer; import com.github.gwtbootstrap.client.ui.Button; import com.github.gwtbootstrap.client.ui.ListBox; import com.github.gwtbootstrap.client.ui.constants.Device; import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Style.BorderStyle; import com.google.gwt.dom.client.Style.FontWeight; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.dom.client.Style.Visibility; import com.google.gwt.event.dom.client.ChangeEvent; import com.google.gwt.event.dom.client.ChangeHandler; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.DragLeaveEvent; import com.google.gwt.event.dom.client.DragLeaveHandler; import com.google.gwt.event.dom.client.DragOverEvent; import com.google.gwt.event.dom.client.DragOverHandler; import com.google.gwt.event.shared.HandlerManager; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiFactory; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.uibinder.client.UiHandler; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.FileUpload; import com.google.gwt.user.client.ui.HTMLPanel; import com.google.gwt.user.client.ui.Image; import com.google.gwt.user.client.ui.ValueBoxBase.TextAlignment; import com.google.gwt.user.client.ui.Widget; /** * The main share update class. * @author Massimiliano Assante at ISTI CNR * @author Costantino Perciante at ISTI CNR */ public class ShareUpdateForm extends Composite { //Create a remote service proxy to talk to the server-side Greeting service. private final ShareUpdateServiceAsync shareupdateService = GWT .create(ShareUpdateService.class); final PageBusAdapter pageBusAdapter = new PageBusAdapter(); // the label for all Vres/channels private final static String ALL_VRES = "Share with: your Virtual Research Environments"; // maximum number of files that can be attached private static final int MAX_NUMBER_ATTACHMENTS = 10; // Labels protected final static String SHARE_UPDATE_TEXT = "Share an update or a link, use “@” to mention and “#” to add a topic"; protected final static String ERROR_UPDATE_TEXT = "Looks like empty to me!"; public final static String NO_TEXT_FILE_SHARE = "_N0_73X7_SH4R3_"; private final static String LISTBOX_LEVEL = " - "; public static final String DROP_FILE_HERE_TEXT = "Drop your file(s) here!"; public static final String ATTACHMENT_LOADED = "Attachment loaded!"; public static final String ATTACHMENT_NOT_LOADED = "Attachment not loaded!"; private static final String DELETE_LINK_PREVIEW = "The link preview will be removed. Would you like to continue?"; private static final String DELETE_ATTACHMENTS = "The attachment(s) will be removed. Would you like to continue?"; private static final String TOO_MUCH_ATTACHMENT_ALERT = "Sorry, but you cannot upload more than " + MAX_NUMBER_ATTACHMENTS + " attachments!"; private static final String WAIT_CURRENT_UPLOAD_FINISHING = "Please, wait the current upload to finish"; // image urls public static final String loading = GWT.getModuleBaseURL() + "../images/avatarLoader.gif"; public static final String avatar_default = GWT.getModuleBaseURL() + "../images/Avatar_default.png"; public static final String attachImageUrl = GWT.getModuleBaseURL() + "../images/attach.png"; public static final String attachedDefaultImageUrl = GWT.getModuleBaseURL() + "../images/attachment_default.png"; public static final String loadedAttachmentImageUrl = GWT.getModuleBaseURL() + "../images/load.png"; public static final String notLoadedAttachmentImageUrl = GWT.getModuleBaseURL() + "../images/not_load.png"; // remember the previous text in the textarea (while handling drag and drop) private static String previousText; // list of attachedFiles (both correctly or not correctly uploaded) private List listOfAttachedFiles = new ArrayList<>(); private HandlerManager eventBus = new HandlerManager(null); private static ShareUpdateFormUiBinder uiBinder = GWT .create(ShareUpdateFormUiBinder.class); // The link previewer private LinkPreviewer linkPreviewer; // panel that show the in progress upload of an attachment private UploadProgressPanel uploadProgress; interface ShareUpdateFormUiBinder extends UiBinder { } // this instance private static ShareUpdateForm singleton; /** * Get this ShareUpdateForm object * @return */ public static ShareUpdateForm get() { return singleton; } @UiField HTMLPanel mainPanel; @UiField Placeholder preview; @UiField Button submitButtonScreen; @UiField Button attachButtonScreen; @UiField Button submitButtonPhoneTablet; @UiField Button attachButtonPhoneTablet; @UiField Image avatarImage; @UiField SuperPosedTextArea shareTextArea; @UiField ListBox privacyLevel = new ListBox(); @UiField ListBox notifyListbox = new ListBox(); @UiField SaveInWorkspaceBox saveInWorkspaceCheckbox; // requested user's information private UserInfo myUserInfo; /** * Constructor */ public ShareUpdateForm() { initWidget(uiBinder.createAndBindUi(this)); singleton = this; bind(); avatarImage.setUrl(loading); shareTextArea.setText(SHARE_UPDATE_TEXT); // set the style and on which devices we show the buttons submitButtonScreen.addStyleName("upload-btn-m"); attachButtonScreen.addStyleName("upload-btn-m"); submitButtonScreen.setShowOn(Device.DESKTOP); attachButtonScreen.setShowOn(Device.DESKTOP); submitButtonScreen.setHideOn(Device.TABLET); attachButtonScreen.setHideOn(Device.TABLET); submitButtonScreen.setHideOn(Device.PHONE); attachButtonScreen.setHideOn(Device.PHONE); submitButtonPhoneTablet.setHideOn(Device.DESKTOP); attachButtonPhoneTablet.setHideOn(Device.DESKTOP); submitButtonPhoneTablet.setShowOn(Device.TABLET); attachButtonPhoneTablet.setShowOn(Device.TABLET); submitButtonPhoneTablet.setShowOn(Device.PHONE); attachButtonPhoneTablet.setShowOn(Device.PHONE); shareupdateService.getUserSettings(new AsyncCallback() { public void onFailure(Throwable caught) { avatarImage.setUrl(avatar_default); } public void onSuccess(UserSettings userSettings) { myUserInfo = userSettings.getUserInfo(); avatarImage.getElement().getParentElement().setAttribute("href", myUserInfo.getAccountURL()); avatarImage.setUrl(myUserInfo.getAvatarId()); String singleVREName = ""; boolean notificationEmail = userSettings.isNotificationViaEmailEnabled(); if (myUserInfo.getOwnVREs().size() > 1) { privacyLevel.addItem(ALL_VRES, PrivacyLevel.VRES.toString()); for (String vreId : myUserInfo.getOwnVREs().keySet()) privacyLevel.addItem(LISTBOX_LEVEL + "Share with: " + myUserInfo.getOwnVREs().get(vreId), vreId); } else if (myUserInfo.getOwnVREs().size() == 1) for (String vreId : myUserInfo.getOwnVREs().keySet()) { singleVREName = myUserInfo.getOwnVREs().get(vreId); privacyLevel.addItem(LISTBOX_LEVEL + "Share with: " + singleVREName, vreId); } if (myUserInfo.isAdmin()) privacyLevel.addItem("Share with: Everyone", PrivacyLevel.PORTAL.toString()); //change css if deployed in VRE scope if (!userSettings.isInfrastructure()) { notifyListbox.addItem("Share with: " + singleVREName); notifyListbox.addItem("Share with: " + singleVREName+" + Notification to members"); if (notificationEmail) { notifyListbox.setSelectedIndex(1); } notifyListbox.setVisible(true); } else privacyLevel.setVisible(true); // all visible attachButtonScreen.setVisible(true); submitButtonScreen.setVisible(true); attachButtonPhoneTablet.setVisible(true); submitButtonPhoneTablet.setVisible(true); // check if DND can be activated and enable it if it's possible if(checkDNDAvailability()){ // add drag over handler on shareTextArea shareTextArea.addDragOverHandler(new DragOverHandler() { @Override public void onDragOver(DragOverEvent event) { GWT.log("Drag over handler"); // add style change addDNDStyleEffects(); } }); // clear drag over effect shareTextArea.addDragLeaveHandler(new DragLeaveHandler() { @Override public void onDragLeave(DragLeaveEvent event) { GWT.log("Drag leave handler"); // remove style changes resetDNDStyleEffects(); } }); // enable shareTextArea as drop target (using native javascript) addNativeDropHandler(singleton, FileSubmit.URL); }else{ GWT.log("Drag and drop not supported."); } } }); // add onWindowClosing handler to alert the user who didn't share post if really wants to leave Window.addWindowClosingHandler(new Window.ClosingHandler() { public void onWindowClosing(Window.ClosingEvent closingEvent) { String currentText = shareTextArea.getText(); if(!currentText.isEmpty() && !currentText.equals(SHARE_UPDATE_TEXT)){ closingEvent.setMessage("Do you really want to leave the page?"); } } }); } /** * Bind events to manage */ private void bind() { //get the uploaded file result eventBus.addHandler(FileUploadCompleteEvent.TYPE, new FileUploadCompleteEventHandler() { @Override public void onUploadComplete(FileUploadCompleteEvent event) { String absolutePathOnServer = event.getUploadedFileInfo().getAbsolutePath(); GWT.log("uploaded on Server here: " + absolutePathOnServer); checkFile(event.getUploadedFileInfo().getFilename(), absolutePathOnServer); } }); // on too large files event put to enabled the buttons attach/submit eventBus.addHandler(FileTooLargeEvent.TYPE, new FileTooLargeEventHandler() { @Override public void onFileTooLargeEvent(FileTooLargeEvent fileTooLargeEvent) { // We need to delay the enable calls because this event is launched in a // FileUpload.onChange() that is also used for disabling the same buttons. // In order to be sure they are first disabled and then enabled a delay is needed. Timer timer = new Timer() { public void run() { enableSubmitButtons(true); enableAttachButtons(true); } }; // Execute the timer to expire 2 seconds in the future timer.schedule(300); } }); } /** Used by UiBinder to instantiate UploadProgressView */ @UiFactory UploadProgressPanel instatiateProgressView() { uploadProgress = new UploadProgressPanel(eventBus); uploadProgress.setVisible(false); return uploadProgress; } @UiHandler("shareTextArea") void onShareUpdateClick(ClickEvent e) { shareTextArea.removeSampleText(); if (shareTextArea.getText().compareTo("") == 0) { Document.get().getElementById("highlighterContainer").getStyle().setHeight(52, Unit.PX); Document.get().getElementById("highlighter").getStyle().setHeight(52, Unit.PX); Document.get().getElementById("postTextArea").getStyle().setHeight(52, Unit.PX); } } /** * One handler for the two attach buttons. * @param e */ private void onAttachClickHandlerBody(ClickEvent e){ // check if there is a link preview if(linkPreviewer != null){ // ask the user boolean delete = wantToDeleteLinkPreview(); if(!delete) return; // remove link preview cancelLinkPreview(); } // check the number of already attached files if(numberOfAttachments() >= MAX_NUMBER_ATTACHMENTS){ Window.alert(TOO_MUCH_ATTACHMENT_ALERT); return; } // proceed with the upload FileUpload up = uploadProgress.initialize(); up.click(); up.setVisible(false); // disable attach button when users select a file up.addChangeHandler(new ChangeHandler() { @Override public void onChange(ChangeEvent event) { enableSubmitButtons(false); enableAttachButtons(false); } }); uploadProgress.setVisible(true); } @UiHandler("attachButtonScreen") void onAttachClickScreen(ClickEvent e) { onAttachClickHandlerBody(e); } @UiHandler("attachButtonPhoneTablet") void onAttachClickOther(ClickEvent e) { onAttachClickHandlerBody(e); } /** * One handler for the two submit buttons. * @param e */ private void onSubmitClickHandlerBody(ClickEvent e){ //because otherwise it looses the other properties setting attachButtonScreen.getElement().getStyle().setVisibility(Visibility.VISIBLE); attachButtonPhoneTablet.getElement().getStyle().setVisibility(Visibility.VISIBLE); shareupdateService.getUserSettings(new AsyncCallback() { public void onFailure(Throwable caught) { Window.alert("Ops! we encountered some problems delivering your message, server is not responding, please try again in a short while."); } public void onSuccess(UserSettings result) { if (result.getUserInfo().getUsername().equals("test.user")) { Window.alert("Your session has expired, please log out and login again"); return; } myUserInfo = result.getUserInfo(); String toShare = shareTextArea.getText().trim(); // Establish the content of this post PostContent postContent = PostContent.ONLY_TEXT; // check if we are going to send a link preview (The shared text cannot be empty nor it can be an error message) if(linkPreviewer != null && (toShare.equals(SHARE_UPDATE_TEXT) || toShare.equals(ERROR_UPDATE_TEXT) || toShare.equals(""))){ shareTextArea.addStyleName("error"); shareTextArea.setText(ERROR_UPDATE_TEXT); return; }else{ if(linkPreviewer != null) postContent = PostContent.TEXT_AND_LINK; if(numberOfAttachmentsUploaded() > 0){ postContent = PostContent.TEXT_AND_ATTACHMENTS; if(toShare.equals("") || toShare.equals(SHARE_UPDATE_TEXT) || toShare.equals(ERROR_UPDATE_TEXT)) toShare = NO_TEXT_FILE_SHARE; } } // check the text (attachment can be sent without shared text) if (toShare.equals(SHARE_UPDATE_TEXT) || toShare.equals(ERROR_UPDATE_TEXT) || toShare.equals("")) { shareTextArea.addStyleName("error"); shareTextArea.setText(ERROR_UPDATE_TEXT); return; } //then you can post but you have to pass html checks now String toPost = toShare; postTweet(toPost, shareTextArea.getMentionedUsers(), postContent); } }); } @UiHandler("submitButtonScreen") void onClickSubmitScreen(ClickEvent e) { onSubmitClickHandlerBody(e); } @UiHandler("submitButtonPhoneTablet") void onClickSubmitOtherDevices(ClickEvent e) { onSubmitClickHandlerBody(e); } /** * Publish a post. * @param textToPost the text of this port * @param mentionedUsers list of users mentioned in the text (if any) * @param postContent the type of post */ private void postTweet(String textToPost, ArrayList mentionedUsers, PostContent postContent) { // escape html text String toShareText = escapeHtml(textToPost); if (! checkTextLength(toShareText)) { Window.alert("We found a single word containing more than 50 chars and it's not a link, is it meaningful?"); return; } // disable text edit and submission button enableSubmitButtons(false); shareTextArea.setEnabled(false); // retrieve the vre id String vreId = ""; if (getPrivacyLevel() == PrivacyLevel.SINGLE_VRE) { vreId = privacyLevel.getValue(privacyLevel.getSelectedIndex()); } // notify group information boolean notifyGroup = notifyListbox.getSelectedIndex() > 0; // case in which there are no attachments but there could be a link preview if(postContent == PostContent.ONLY_TEXT || postContent == PostContent.TEXT_AND_LINK){ //preparing to send stuff String linkTitle = "", linkDescription = "" , linkUrl = "", linkUrlThumbnail = "", linkHost = ""; if (linkPreviewer != null) { linkTitle = linkPreviewer.getLinkTitle(); linkDescription = linkPreviewer.getLinkDescription(); linkUrl = linkPreviewer.getUrl(); linkUrlThumbnail = linkPreviewer.getUrlThumbnail(); linkHost = linkPreviewer.getHost(); } LinkPreview preview2Share = new LinkPreview(linkTitle, linkDescription, linkUrl, linkHost, null); Long vreOrgId = Long.parseLong(vreId); // share post (it could contain a link preview) shareupdateService.sharePostWithLinkPreview(toShareText, FeedType.TWEET, getPrivacyLevel(), vreOrgId, preview2Share, linkUrlThumbnail, mentionedUsers, notifyGroup, new AsyncCallback() { public void onFailure(Throwable caught) { enableSubmitButtons(true); shareTextArea.setEnabled(true); shareTextArea.setText(SHARE_UPDATE_TEXT); shareTextArea.cleanHighlighterDiv(); preview.clear(); linkPreviewer = null; } public void onSuccess(ClientFeed feed) { enableSubmitButtons(true); shareTextArea.setEnabled(true); shareTextArea.setText(SHARE_UPDATE_TEXT); shareTextArea.cleanHighlighterDiv(); preview.clear(); linkPreviewer = null; if (feed == null) Window.alert("Ops! we encountered some problems delivering your message, please try again in a short while."); else { // publish a message with the refresh notification try { pageBusAdapter.PageBusPublish("org.gcube.portal.databook.shared", feed, (Jsonizer)GWT.create(ClientFeedJsonizer.class)); } catch (PageBusAdapterException ex) { GWT.log(ex.getMessage()); } } //needed when posting long texts otherwise it stays with the current height shareTextArea.getElement().getStyle().setHeight(54, Unit.PX); } }); } else{ // case with at least one attachment available ArrayList uploadedFiles = new ArrayList(); // consider only correctly uploaded file(s) for(AttachedFile file: listOfAttachedFiles){ if(file.isCorrectlyUploaded()) uploadedFiles.add( new UploadedFile( file.getFileName(), file.getFileAbsolutePathOnServer(), file.getDescription(), file.getDownloadUrl(), file.getThumbnailUrl(), file.getFormat())); } Long vreOrgId = Long.parseLong(vreId); // share the post shareupdateService.sharePostWithAttachments(toShareText, FeedType.TWEET, getPrivacyLevel(), vreOrgId, uploadedFiles, mentionedUsers, notifyGroup, saveInWorkspaceCheckbox.getValue(), new AsyncCallback() { @Override public void onSuccess(ClientFeed feed) { //GWT.log("Saved feed looks like " + feed.toString()); enableSubmitButtons(true); shareTextArea.setEnabled(true); shareTextArea.setText(SHARE_UPDATE_TEXT); shareTextArea.cleanHighlighterDiv(); saveInWorkspaceCheckbox.setVisible(false); preview.clear(); listOfAttachedFiles.clear(); if (feed == null) Window.alert("Ops! we encountered some problems delivering your message, please try again in a short while."); else { // publish a message with the refresh notification try { pageBusAdapter.PageBusPublish("org.gcube.portal.databook.shared", feed, (Jsonizer)GWT.create(ClientFeedJsonizer.class)); GWT.log("SENT"); } catch (PageBusAdapterException ex) { GWT.log(ex.getMessage()); } } //needed when posting long texts otherwise it stays with the current height shareTextArea.getElement().getStyle().setHeight(54, Unit.PX); } @Override public void onFailure(Throwable caught) { GWT.log(caught.toString()); enableSubmitButtons(true); shareTextArea.setEnabled(true); shareTextArea.setText(SHARE_UPDATE_TEXT); shareTextArea.cleanHighlighterDiv(); saveInWorkspaceCheckbox.setVisible(false); preview.clear(); listOfAttachedFiles.clear(); } }); } } /** * Determines the privacy level of the post to be shared. * @return */ private PrivacyLevel getPrivacyLevel() { String selected = privacyLevel.getValue(privacyLevel.getSelectedIndex()); if (selected.compareTo(PrivacyLevel.CONNECTION.toString()) == 0) return PrivacyLevel.CONNECTION; else if (selected.compareTo(PrivacyLevel.VRES.toString()) == 0) return PrivacyLevel.VRES; else if (selected.compareTo(PrivacyLevel.PRIVATE.toString()) == 0) return PrivacyLevel.PRIVATE; else if (selected.compareTo(PrivacyLevel.PORTAL.toString()) == 0) return PrivacyLevel.PORTAL; else return PrivacyLevel.SINGLE_VRE; } /** * Escape an html string. Escaping data received from the client helps to * prevent cross-site script vulnerabilities. * * @param html the html string to escape * @return the escaped string */ private String escapeHtml(String html) { if (html == null) { return null; } return html.replaceAll("&", "&").replaceAll("<", "<") .replaceAll(">", ">"); } /** * called when pasting a possible link * @param linkToCheck */ protected void checkLink(String textToCheck) { if (linkPreviewer == null) { String [] parts = textToCheck.split("\\s"); // Attempt to convert each item into an URL. for( String item : parts ) { if (item.startsWith("http") || item.startsWith("www")) { // check if there are attachments and inform the user that they will be lost if(!listOfAttachedFiles.isEmpty()){ // in this case let the user to choose what to do boolean confirm = Window.confirm(DELETE_ATTACHMENTS); if(!confirm) return; // else... remove attachments and continue listOfAttachedFiles.clear(); preview.clear(); saveInWorkspaceCheckbox.setVisible(false); } preview.add(new LinkLoader()); enableSubmitButtons(false); //GWT.log("It's http link:" + linkToCheck); shareupdateService.checkLink(textToCheck, new AsyncCallback() { public void onFailure(Throwable caught) { preview.clear(); enableSubmitButtons(true); } public void onSuccess(LinkPreview result) { // For a link, the LinkPreview object is like this // LinkPreview [title=ANSA.it - Homepage, description=ANSA.it: Il sito Internet dell'Agenzia ANSA. Ultime notizie, foto, video e approfondimenti su: cronaca, politica, economia, regioni, mondo, sport, calcio, cultura e tecnologia, // url=http://www.ansa.it/, host=ansa.it, imageUrls=[http://www.ansa.it/sito/img/ico/ansa-57-precomposed.png]] // GWT.log(result.toString()); preview.clear(); if (result != null) addPreviewLink(result); enableSubmitButtons(true); } }); break; } } } else { Window.alert("You cannot post two links, please remove the previous one first."); } } /** * called when the file was correctly uploaded on server * @param fileNameLabel the name of the file * @param absolutePathOnServer the path of the file ending with its name on the server temp */ protected void checkFile(final String fileName, final String absolutePathOnServer) { // create temp view of the attached file and add to the previewer final AttachmentPreviewer atPrev = new AttachmentPreviewer(fileName, attachedDefaultImageUrl, preview, this); shareupdateService.checkUploadedFile(fileName, absolutePathOnServer, new AsyncCallback() { public void onFailure(Throwable caught) { GWT.log("Unable to check uploaded file!"); // hide progress bar uploadProgress.setVisible(false); // attach the file with error.. listOfAttachedFiles.add( new AttachedFile( fileName, absolutePathOnServer, atPrev, null) ); // there is no a linkPreview... addPreviewAttachment(null, atPrev); // enable anyway the button enableSubmitButtons(true); // enable attach button enableAttachButtons(true); } // it returns a LinkPreview (for compatibility with old code) public void onSuccess(LinkPreview result) { if(result == null) return; listOfAttachedFiles.add( new AttachedFile( result.getTitle(), absolutePathOnServer, result.getDescription(), result.getUrl(), result.getImageUrls().get(0), result.getHost(), atPrev, true) ); addPreviewAttachment(result, atPrev); // enable anyway the button enableSubmitButtons(true); // enable attach button enableAttachButtons(true); } }); } /** * called when pasting. it tries to avoid pasting long non spaced strings * @param linkToCheck */ private boolean checkTextLength(String textToCheck) { String [] parts = textToCheck.split("\\s"); // check the length of tokens for( String item : parts ) { if (!item.startsWith("http") && !item.startsWith("ftp")) { //url are accepted as they can be trunked if (item.length() > 50) { return false; } } } return true; } /** * add the link preview in the view * @param result */ private void addPreviewLink(LinkPreview result) { //GWT.log(result.toString()); preview.clear(); uploadProgress.setVisible(false); linkPreviewer = new LinkPreviewer(this, result); preview.add(linkPreviewer); } /** * Call it to show attachment(s) */ private void addPreviewAttachment(LinkPreview result, AttachmentPreviewer atPrev){ // GWT.log(result.toString()); // disable progress bar uploadProgress.setVisible(false); // check the result if(result == null){ // failed upload atPrev.setResultAttachment(ATTACHMENT_NOT_LOADED, notLoadedAttachmentImageUrl); // change the atPrev object and let the user retry the upload atPrev.retryToUpload(atPrev); } else{ // set the preview information (the first image is the one related to the type of file) atPrev.setResultAttachment(ATTACHMENT_LOADED, loadedAttachmentImageUrl); atPrev.setImagePreview(result.getImageUrls().get(0)); } preview.add(atPrev); // enable checkbox to save in workspace if it's the case if(numberOfAttachments() > 0 && !saveInWorkspaceCheckbox.isVisible()) saveInWorkspaceCheckbox.setVisible(true); } /** * Delete the only link previewer allowed. */ protected void cancelLinkPreview() { preview.clear(); linkPreviewer = null; attachButtonScreen.getElement().getStyle().setVisibility(Visibility.VISIBLE); //beacuse otherwise it looses the other properties setting attachButtonPhoneTablet.getElement().getStyle().setVisibility(Visibility.VISIBLE); } /** * Handle drop of files within shareTextArea (native javascript code) * @param instance */ private static native void addNativeDropHandler(ShareUpdateForm instance, String servletUrl)/*-{ // retrieve textArea by id var drop = $wnd.$('#postTextArea')[0]; // check if this file is a folder function isFolder(file) { if (file != null && !file.type && file.size % 4096 == 0) { return true; } return false; } // function used to add the handler function addEventHandler(obj, evt, handler) { if (obj.addEventListener) { // W3C method obj.addEventListener(evt, handler, false); } else if (obj.attachEvent) { // IE method. obj.attachEvent('on' + evt, handler); } else { // Old school method. obj['on' + evt] = handler; } } // The real drop handler addEventHandler( drop, 'drop', function(e) { // get window.event if e argument missing (in IE) e = e || window.event; // stops the browser from redirecting off to the image. if (e.preventDefault) { e.preventDefault(); } // opts for the remote call var opts = { url : servletUrl, type : "POST", processData : false }; // get the file(s) var dt = e.dataTransfer; var files = dt.files; // chek if a link preview is already there var linkPreviewPresent = instance.@org.gcube.portlets.user.shareupdates.client.view.ShareUpdateForm::isLinkPreviewPresent()(); if(linkPreviewPresent){ var hasToBeDelete = instance.@org.gcube.portlets.user.shareupdates.client.view.ShareUpdateForm::wantToDeleteLinkPreview()(); if(!hasToBeDelete){ instance.@org.gcube.portlets.user.shareupdates.client.view.ShareUpdateForm::resetDNDStyleEffects()(); return; } // else delete the link preview, enable the attach buttons and proceeed instance.@org.gcube.portlets.user.shareupdates.client.view.ShareUpdateForm::cancelLinkPreview()(); instance.@org.gcube.portlets.user.shareupdates.client.view.ShareUpdateForm::enableAttachButtons(Z)(true); } // check limit for number of files var numberOfAlreadyAttachedFiles = instance.@org.gcube.portlets.user.shareupdates.client.view.ShareUpdateForm::numberOfAttachments()(); numberOfAlreadyAttachedFiles += files.length; var limitExceeded = (numberOfAlreadyAttachedFiles > @org.gcube.portlets.user.shareupdates.client.view.ShareUpdateForm::MAX_NUMBER_ATTACHMENTS); if(limitExceeded){ var msg = "Sorry, you are trying to attach more than " + + @org.gcube.portlets.user.shareupdates.client.view.ShareUpdateForm::MAX_NUMBER_ATTACHMENTS " files!" instance.@org.gcube.portlets.user.shareupdates.client.view.ShareUpdateForm::showAlert(Ljava/lang/String;)(msg); console.log(msg); // reset text area instance.@org.gcube.portlets.user.shareupdates.client.view.ShareUpdateForm::resetDNDStyleEffects()(); return; } // reset if no file was dropped (??) if (files.length == 0) { // reset text area instance.@org.gcube.portlets.user.shareupdates.client.view.ShareUpdateForm::resetDNDStyleEffects()(); return; } console.log("Number of dropped file(s): " + files.length); var numFolder = 0; // save maximum allowed size var maximumSize = @org.gcube.portlets.widgets.fileupload.client.view.FileSubmit::MAX_SIZE_ATTACHED_FILE_MB; // msg for ignored (too big files) var ignoredFilesAlert = " file(s) ignored because larger than " + maximumSize + "MB"; // number of ignored files var numberIgnoredFiles = 0; // disable attach button instance.@org.gcube.portlets.user.shareupdates.client.view.ShareUpdateForm::enableAttachButtons(Z)(false); // disable submit button instance.@org.gcube.portlets.user.shareupdates.client.view.ShareUpdateForm::enableSubmitButtons(Z)(false); // for each dropped file for (var i = 0; i < files.length; i++) { var file = files[i]; var fileSelected = file.name + ";"; // be sure it is not a folder if (!isFolder(file)) { console.log("filesSelected: " + fileSelected); // check its size var fileSize = file.size / 1024 / 1024; console.log("File size is " + fileSize + "MB"); if(fileSize > maximumSize){ numberIgnoredFiles ++; continue; } // create new progress bar instance.@org.gcube.portlets.user.shareupdates.client.view.ShareUpdateForm::showProgressDND()(); // create request var xhr = new XMLHttpRequest(); xhr.open(opts.type, opts.url, true); var formdata = new FormData(); // append the file formdata.append("fileUpload", file); // send data xhr.send(formdata); console.log("File " + file.name + " sent at " + servletUrl); }else{ // increment the number of skipped folders numFolder++; } } // enable attach button (the checkFile method will do this...) // alert the user that folder(s) can't be uploaded if(numFolder > 0){ var msg; if(numFolder == files.length){ msg = "Sorry but it's not possible to upload a folder!"; instance.@org.gcube.portlets.user.shareupdates.client.view.ShareUpdateForm::showAlert(Ljava/lang/String;)(msg); // reset text area instance.@org.gcube.portlets.user.shareupdates.client.view.ShareUpdateForm::resetDNDStyleEffects()(); }else{ // print ignored folders, if any var msg = "Ignored "; msg += numFolder > 1? numFolder+" folders": numFolder+" folder"; msg += " during upload."; console.log(msg); instance.@org.gcube.portlets.user.shareupdates.client.view.ShareUpdateForm::showAlert(Ljava/lang/String;)(msg); } } // alert for too large files if(numberIgnoredFiles > 0){ var msg = numberIgnoredFiles + ignoredFilesAlert; if(numberIgnoredFiles == files.length){ msg = file.name + " can't be uploaded because it is too large!"; instance.@org.gcube.portlets.user.shareupdates.client.view.ShareUpdateForm::showAlert(Ljava/lang/String;)(msg); }else{ var msg = numberIgnoredFiles + ignoredFilesAlert; console.log(msg); instance.@org.gcube.portlets.user.shareupdates.client.view.ShareUpdateForm::showAlert(Ljava/lang/String;)(msg); } } // reset text area instance.@org.gcube.portlets.user.shareupdates.client.view.ShareUpdateForm::resetDNDStyleEffects()(); // enable attach buttons instance.@org.gcube.portlets.user.shareupdates.client.view.ShareUpdateForm::enableAttachButtons(Z)(true); // enable submit buttons instance.@org.gcube.portlets.user.shareupdates.client.view.ShareUpdateForm::enableSubmitButtons(Z)(true); }); }-*/; /** * Check if DND could be enabled (i.e, it's supported by the browser) * @return */ public static native boolean checkDNDAvailability()/*-{ return window.FileReader; }-*/; /** * Add DND style effect on drag over. */ private void addDNDStyleEffects() { // save current text (note that the DragOverEvent event can be fired several times) boolean conditionToSave = !shareTextArea.getText().equals(DROP_FILE_HERE_TEXT) && !shareTextArea.getText().equals(SHARE_UPDATE_TEXT); previousText = conditionToSave ? shareTextArea.getText() : previousText; // change border properties shareTextArea.getElement().getStyle().setBorderStyle(BorderStyle.DASHED); shareTextArea.getElement().getStyle().setBorderColor("rgba(82, 168, 236, 0.6)"); shareTextArea.getElement().getStyle().setBorderWidth(2.5, Unit.PX); // change background color shareTextArea.getElement().getStyle().setBackgroundColor("rgba(82, 168, 236, 0.2)"); // enlarge the window Document.get().getElementById("highlighterContainer").getStyle().setHeight(52, Unit.PX); Document.get().getElementById("highlighter").getStyle().setHeight(52, Unit.PX); Document.get().getElementById("postTextArea").getStyle().setHeight(52, Unit.PX); // add "Drop file here" text shareTextArea.setText(DROP_FILE_HERE_TEXT); shareTextArea.setAlignment(TextAlignment.CENTER); shareTextArea.getElement().getStyle().setFontWeight(FontWeight.BOLD); shareTextArea.getElement().getStyle().setPaddingTop( (Double.parseDouble(shareTextArea.getElement().getStyle().getHeight().replace("px", "")) + 20)/2.0, Unit.PX); // set the color of the text if needed to gray if(!previousText.equals(SHARE_UPDATE_TEXT)) shareTextArea.getElement().getStyle().setColor("#999"); } /** * On dragLeave reset changes on the text area */ private void resetDNDStyleEffects() { // remove border properties shareTextArea.getElement().getStyle().setBorderStyle(BorderStyle.SOLID); shareTextArea.getElement().getStyle().setBorderColor("#333"); shareTextArea.getElement().getStyle().setBorderWidth(1, Unit.PX); // change back background color shareTextArea.getElement().getStyle().setBackgroundColor("transparent"); // remove text "Drop file here" and reput the old text shareTextArea.setText(previousText); shareTextArea.setAlignment(TextAlignment.LEFT); // change text color if needed if(!previousText.equals(DROP_FILE_HERE_TEXT) && !previousText.equals(SHARE_UPDATE_TEXT)) shareTextArea.getElement().getStyle().setColor("#333"); // reset padding top shareTextArea.getElement().getStyle().setPaddingTop(4, Unit.PX); // reset font weight shareTextArea.getElement().getStyle().setFontWeight(FontWeight.NORMAL); } /** * Alert the user about something. * * @param msg the msg to show */ private void showAlert(String msg){ Window.alert(msg); } /** * Show progress bar and start the ProgressController * @param e */ private void showProgressDND() { uploadProgress.initializeDND(); uploadProgress.setVisible(true); } /** * Remove an attached file from the listOfAttachedFiles * @param attachmentPreviewer */ public void removeAttachedFile(AttachmentPreviewer attachmentPreviewer) { Iterator iterator = listOfAttachedFiles.iterator(); while (iterator.hasNext()) { AttachedFile attachedFile = (AttachedFile) iterator.next(); if(attachedFile.getAtPrev().equals(attachmentPreviewer)){ iterator.remove(); break; } } // check the final number of attachments and if it's less than one, set to false // the save in workspace checkbox visibility if(numberOfAttachments() == 0) saveInWorkspaceCheckbox.setVisible(false); } /** * Get the number of attached files (both uploaded and not). * @return number of attached files */ private int numberOfAttachments(){ return listOfAttachedFiles.size(); } /** * Retrieve the number of correctly uploaded attached files. * @return number of attached files correctly uploaded. */ private int numberOfAttachmentsUploaded(){ int counter = 0; for (AttachedFile attachedFile : listOfAttachedFiles) { if(attachedFile.isCorrectlyUploaded()) counter ++; } return counter; } /** * Is there any link preview? * @return if a link preview is already there */ private boolean isLinkPreviewPresent(){ return linkPreviewer != null; } /** * Asks the user if he/she wants to delete the link preview * @return */ private boolean wantToDeleteLinkPreview(){ return Window.confirm(DELETE_LINK_PREVIEW); } /** * Enables or disable the attach buttons * @param enable */ private void enableAttachButtons(boolean enable){ attachButtonScreen.setEnabled(enable); attachButtonPhoneTablet.setEnabled(enable); } /** * Enables or disable the submit buttons * @param enable */ private void enableSubmitButtons(boolean enable){ GWT.log("enable is " + enable); submitButtonScreen.setEnabled(enable); submitButtonPhoneTablet.setEnabled(enable); } }