001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.ui.client; 029 030import org.opencms.ade.upload.client.I_CmsUploadContext; 031import org.opencms.file.CmsResource; 032import org.opencms.gwt.client.CmsCoreProvider; 033import org.opencms.gwt.client.Messages; 034import org.opencms.gwt.client.rpc.CmsRpcAction; 035import org.opencms.gwt.client.ui.CmsErrorDialog; 036import org.opencms.gwt.client.ui.CmsListItemWidget; 037import org.opencms.gwt.client.ui.CmsPopup; 038import org.opencms.gwt.client.ui.CmsPushButton; 039import org.opencms.gwt.client.ui.I_CmsButton; 040import org.opencms.gwt.client.ui.css.I_CmsLayoutBundle; 041import org.opencms.gwt.client.ui.input.upload.CmsFileInfo; 042import org.opencms.gwt.client.ui.input.upload.CmsFileInput; 043import org.opencms.gwt.client.ui.input.upload.CmsUploadButton; 044import org.opencms.gwt.client.ui.input.upload.CmsUploadProgressInfo; 045import org.opencms.gwt.client.ui.input.upload.CmsUploader; 046import org.opencms.gwt.client.ui.input.upload.I_CmsUploadDialog; 047import org.opencms.gwt.client.ui.replace.CmsReplaceContentWidget; 048import org.opencms.gwt.shared.CmsListInfoBean; 049import org.opencms.gwt.shared.CmsUploadProgessInfo; 050import org.opencms.gwt.shared.I_CmsUploadConstants; 051import org.opencms.gwt.shared.rpc.I_CmsUploadService; 052import org.opencms.gwt.shared.rpc.I_CmsUploadServiceAsync; 053import org.opencms.util.CmsStringUtil; 054 055import java.util.ArrayList; 056import java.util.Collections; 057import java.util.List; 058 059import com.google.gwt.core.client.GWT; 060import com.google.gwt.event.dom.client.ClickEvent; 061import com.google.gwt.event.dom.client.ClickHandler; 062import com.google.gwt.event.logical.shared.CloseHandler; 063import com.google.gwt.event.shared.HandlerRegistration; 064import com.google.gwt.json.client.JSONArray; 065import com.google.gwt.json.client.JSONObject; 066import com.google.gwt.json.client.JSONParser; 067import com.google.gwt.json.client.JSONString; 068import com.google.gwt.json.client.JSONValue; 069import com.google.gwt.user.client.Command; 070import com.google.gwt.user.client.Timer; 071import com.google.gwt.user.client.rpc.ServiceDefTarget; 072import com.google.gwt.user.client.ui.PopupPanel; 073import com.google.gwt.user.client.ui.RootPanel; 074 075/** 076 * The single file upload dialog.<p> 077 */ 078public class CmsSingleFileUploadDialog extends CmsPopup implements I_CmsUploadDialog { 079 080 /** The upload context. */ 081 protected I_CmsUploadContext m_context; 082 083 /** The main content panel. */ 084 protected CmsReplaceContentWidget m_mainPanel; 085 086 /** Signals that the upload dialog was canceled. */ 087 private boolean m_canceled; 088 089 /** Flag indicating the client is waiting for a server response. */ 090 private boolean m_clientLoading; 091 092 /** The close handler. */ 093 private CloseHandler<PopupPanel> m_closeHandler; 094 095 /** The sum of all file sizes. */ 096 private long m_contentLength; 097 098 /** The current file input. */ 099 private CmsFileInput m_fileInput; 100 101 /** The file widget. */ 102 private CmsListItemWidget m_fileWidget; 103 104 /** The action to execute when the upload dialog is finished. */ 105 private Runnable m_finishAction; 106 107 /** The dialog handler. */ 108 private CmsSingleFileUploadHandler m_handler; 109 110 /** The close handler registration. */ 111 private HandlerRegistration m_handlerReg; 112 113 /** The loading timer. */ 114 private Timer m_loadingTimer; 115 116 /** The OK button. */ 117 private CmsPushButton m_okButton; 118 119 /** The upload progress widget. */ 120 private CmsUploadProgressInfo m_progressInfo; 121 122 /** The progress timer. */ 123 private Timer m_updateProgressTimer; 124 125 /** The upload button. */ 126 private CmsUploadButton m_uploadButton; 127 128 /** The upload service. */ 129 private I_CmsUploadServiceAsync m_uploadService; 130 131 /** 132 * Constructor.<p> 133 * 134 * @param handler the dialog handler 135 * @param dialogTitle the dialog title 136 */ 137 public CmsSingleFileUploadDialog(CmsSingleFileUploadHandler handler, String dialogTitle) { 138 139 super(dialogTitle); 140 m_handler = handler; 141 setModal(true); 142 setGlassEnabled(true); 143 catchNotifications(); 144 // create the main panel 145 m_mainPanel = new CmsReplaceContentWidget(); 146 // set the main panel as content of the popup 147 setMainContent(m_mainPanel); 148 addDialogClose(new Command() { 149 150 public void execute() { 151 152 cancelReplace(); 153 } 154 }); 155 156 CmsPushButton cancelButton = createButton( 157 org.opencms.gwt.client.Messages.get().key(org.opencms.gwt.client.Messages.GUI_CANCEL_0)); 158 cancelButton.addClickHandler(new ClickHandler() { 159 160 /** 161 * @see com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event.dom.client.ClickEvent) 162 */ 163 public void onClick(ClickEvent event) { 164 165 cancelReplace(); 166 } 167 }); 168 addButton(cancelButton); 169 createButtons(); 170 } 171 172 /** 173 * @see org.opencms.gwt.client.ui.CmsPopup#addCloseHandler(com.google.gwt.event.logical.shared.CloseHandler) 174 */ 175 @Override 176 public HandlerRegistration addCloseHandler(CloseHandler<PopupPanel> handler) { 177 178 m_closeHandler = handler; 179 m_handlerReg = super.addCloseHandler(handler); 180 return m_handlerReg; 181 } 182 183 /** 184 * Returns the action which should be executed when the upload dialog is finished.<p> 185 * 186 * @return an action to run when the upload dialog is finished 187 */ 188 public Runnable getFinishAction() { 189 190 return m_finishAction; 191 } 192 193 /** 194 * @see org.opencms.gwt.client.ui.CmsPopup#hide() 195 */ 196 @Override 197 public void hide() { 198 199 if ((m_fileInput != null) && RootPanel.get().getElement().isOrHasChild(m_fileInput.getElement())) { 200 m_fileInput.removeFromParent(); 201 } 202 super.hide(); 203 } 204 205 /** 206 * Parses the upload response of the server and decides what to do.<p> 207 * 208 * @param results a JSON Object 209 */ 210 @SuppressWarnings("null") 211 public void parseResponse(String results) { 212 213 cancelUpdateProgress(); 214 stopLoadingAnimation(); 215 216 if ((!m_canceled) && CmsStringUtil.isNotEmptyOrWhitespaceOnly(results)) { 217 JSONObject jsonObject = JSONParser.parseStrict(results).isObject(); 218 boolean success = jsonObject.get(I_CmsUploadConstants.KEY_SUCCESS).isBoolean().booleanValue(); 219 // If the upload is done so fast that we did not receive any progress information, then 220 // the content length is unknown. For that reason take the request size to show how 221 // much bytes were uploaded. 222 double size = jsonObject.get(I_CmsUploadConstants.KEY_REQUEST_SIZE).isNumber().doubleValue(); 223 long requestSize = Double.valueOf(size).longValue(); 224 if (m_contentLength == 0) { 225 m_contentLength = requestSize; 226 } 227 if (success) { 228 m_mainPanel.displayDialogInfo(Messages.get().key(Messages.GUI_UPLOAD_INFO_FINISHING_0), false); 229 m_progressInfo.finish(); 230 JSONValue uploadedFilesVal = jsonObject.get(I_CmsUploadConstants.KEY_UPLOADED_FILE_NAMES); 231 if (uploadedFilesVal != null) { 232 JSONArray uploadedFilesArray = uploadedFilesVal.isArray(); 233 if (uploadedFilesArray != null) { 234 List<String> uploadedFiles = new ArrayList<String>(); 235 if (uploadedFilesArray != null) { 236 for (int i = 0; i < uploadedFilesArray.size(); i++) { 237 JSONString entry = uploadedFilesArray.get(i).isString(); 238 if (entry != null) { 239 uploadedFiles.add(entry.stringValue()); 240 } 241 } 242 } 243 if (m_context != null) { 244 m_context.onUploadFinished(uploadedFiles); 245 } 246 } 247 } 248 closeOnSuccess(); 249 } else { 250 String message = jsonObject.get(I_CmsUploadConstants.KEY_MESSAGE).isString().stringValue(); 251 String stacktrace = jsonObject.get(I_CmsUploadConstants.KEY_STACKTRACE).isString().stringValue(); 252 showErrorReport(message, stacktrace); 253 } 254 } 255 } 256 257 /** 258 * Sets the upload context.<p> 259 * 260 * @param context the new upload context 261 */ 262 public void setContext(I_CmsUploadContext context) { 263 264 m_context = context; 265 } 266 267 /** 268 * Sets an action that should be executed if the upload dialog is finished.<p> 269 * 270 * @param action the action to execute when finished 271 */ 272 public void setFinishAction(Runnable action) { 273 274 m_finishAction = action; 275 } 276 277 /** 278 * Shows the error report.<p> 279 * 280 * @param message the message to show 281 * @param stacktrace the stacktrace to show 282 */ 283 public void showErrorReport(final String message, final String stacktrace) { 284 285 if (!m_canceled) { 286 CmsErrorDialog errDialog = new CmsErrorDialog(message, stacktrace); 287 if (m_handlerReg != null) { 288 m_handlerReg.removeHandler(); 289 } 290 if (m_closeHandler != null) { 291 errDialog.addCloseHandler(m_closeHandler); 292 } 293 hide(); 294 errDialog.center(); 295 } 296 } 297 298 /** 299 * Cancels the replace process.<p> 300 */ 301 protected void cancelReplace() { 302 303 m_canceled = true; 304 if (m_progressInfo != null) { 305 cancelUpdateProgress(); 306 CmsRpcAction<Boolean> callback = new CmsRpcAction<Boolean>() { 307 308 /** 309 * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute() 310 */ 311 @Override 312 public void execute() { 313 314 getUploadService().cancelUpload(this); 315 } 316 317 /** 318 * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object) 319 */ 320 @Override 321 protected void onResponse(Boolean result) { 322 323 hide(); 324 } 325 }; 326 callback.execute(); 327 } else { 328 hide(); 329 } 330 } 331 332 /** 333 * Cancels the upload progress timer.<p> 334 */ 335 protected void cancelUpdateProgress() { 336 337 if (m_updateProgressTimer != null) { 338 m_updateProgressTimer.cancel(); 339 } 340 } 341 342 /** 343 * Returns the resource type name for a given filename.<p> 344 * 345 * @param file the file info 346 * 347 * @return the resource type name 348 */ 349 protected String getResourceType(CmsFileInfo file) { 350 351 return CmsCoreProvider.get().getResourceType(file); 352 } 353 354 /** 355 * Returns the upload service instance.<p> 356 * 357 * @return the upload service instance 358 */ 359 protected I_CmsUploadServiceAsync getUploadService() { 360 361 if (m_uploadService == null) { 362 m_uploadService = GWT.create(I_CmsUploadService.class); 363 String serviceUrl = CmsCoreProvider.get().link("org.opencms.ade.upload.CmsUploadService.gwt"); 364 ((ServiceDefTarget)m_uploadService).setServiceEntryPoint(serviceUrl); 365 } 366 return m_uploadService; 367 } 368 369 /** 370 * Returns the upload JSP uri.<p> 371 * 372 * @return the upload JSP uri 373 */ 374 protected String getUploadUri() { 375 376 return CmsCoreProvider.get().link(I_CmsUploadConstants.UPLOAD_ACTION_JSP_URI); 377 } 378 379 /** 380 * Sets the file input.<p> 381 * 382 * @param fileInput the file input 383 */ 384 protected void setFileInput(CmsFileInput fileInput) { 385 386 // only accept file inputs with a single selected file 387 if (fileInput.getFiles().length == 1) { 388 if (m_okButton != null) { 389 m_okButton.enable(); 390 } 391 if (m_fileInput != null) { 392 m_fileInput.removeFromParent(); 393 } 394 m_fileInput = fileInput; 395 396 RootPanel.get().add(m_fileInput); 397 m_mainPanel.setContainerWidget(createFileWidget(m_fileInput.getFiles()[0])); 398 } 399 } 400 401 /** 402 * Retrieves the progress information from the server.<p> 403 */ 404 protected void updateProgress() { 405 406 CmsRpcAction<CmsUploadProgessInfo> callback = new CmsRpcAction<CmsUploadProgessInfo>() { 407 408 /** 409 * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute() 410 */ 411 @Override 412 public void execute() { 413 414 getUploadService().getUploadProgressInfo(this); 415 } 416 417 /** 418 * @see org.opencms.gwt.client.rpc.CmsRpcAction#onFailure(java.lang.Throwable) 419 */ 420 @Override 421 public void onFailure(Throwable t) { 422 423 super.onFailure(t); 424 cancelUpdateProgress(); 425 } 426 427 /** 428 * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object) 429 */ 430 @Override 431 protected void onResponse(CmsUploadProgessInfo result) { 432 433 updateProgressBar(result); 434 } 435 }; 436 callback.execute(); 437 } 438 439 /** 440 * Updates the progress bar.<p> 441 * 442 * @param info the progress info 443 */ 444 protected void updateProgressBar(CmsUploadProgessInfo info) { 445 446 switch (info.getState()) { 447 case notStarted: 448 break; 449 case running: 450 m_progressInfo.setProgress(info); 451 stopLoadingAnimation(); 452 break; 453 case finished: 454 m_progressInfo.finish(); 455 m_mainPanel.displayDialogInfo(Messages.get().key(Messages.GUI_UPLOAD_INFO_FINISHING_0), false); 456 startLoadingAnimation(Messages.get().key(Messages.GUI_UPLOAD_INFO_CREATING_RESOURCES_0), 1500); 457 break; 458 default: 459 break; 460 } 461 } 462 463 /** 464 * Uploads the selected file.<p> 465 */ 466 protected void uploadFile() { 467 468 hideOkAndUploadButtons(); 469 CmsUploader uploader = new CmsUploader(); 470 CmsFileInfo info = m_fileInput.getFiles()[0]; 471 info.setOverrideFileName( 472 CmsStringUtil.joinPaths(m_handler.getTargetFolderPath(), m_handler.getFileName(info.getFileName()))); 473 uploader.uploadFiles( 474 CmsCoreProvider.get().link(I_CmsUploadConstants.UPLOAD_ACTION_JSP_URI), 475 CmsResource.getFolderPath(m_handler.getTargetFolderPath()), 476 true, 477 null, 478 Collections.singletonList(info), 479 Collections.<String> emptyList(), 480 false, 481 this); 482 showProgress(); 483 } 484 485 /** 486 * Closes the dialog after a delay.<p> 487 */ 488 private void closeOnSuccess() { 489 490 Timer closeTimer = new Timer() { 491 492 /** 493 * @see com.google.gwt.user.client.Timer#run() 494 */ 495 @Override 496 public void run() { 497 498 CmsSingleFileUploadDialog.this.hide(); 499 } 500 }; 501 closeTimer.schedule(1000); 502 } 503 504 /** 505 * Creates a dialog text button.<p> 506 * 507 * @param buttonText the button text 508 * 509 * @return the button 510 */ 511 private CmsPushButton createButton(String buttonText) { 512 513 CmsPushButton button = new CmsPushButton(); 514 button.setTitle(buttonText); 515 button.setText(buttonText); 516 button.setSize(I_CmsButton.Size.medium); 517 button.setUseMinWidth(true); 518 return button; 519 } 520 521 /** 522 * Creates the "OK", the "Cancel" and the "Change file" button.<p> 523 */ 524 private void createButtons() { 525 526 m_okButton = createButton(org.opencms.gwt.client.Messages.get().key(org.opencms.gwt.client.Messages.GUI_OK_0)); 527 m_okButton.addClickHandler(new ClickHandler() { 528 529 /** 530 * @see com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event.dom.client.ClickEvent) 531 */ 532 public void onClick(ClickEvent event) { 533 534 uploadFile(); 535 } 536 }); 537 if (m_fileInput == null) { 538 m_okButton.disable(Messages.get().key(Messages.GUI_REPLACE_NO_FILE_SELECTED_0)); 539 } 540 addButton(m_okButton); 541 542 // add a new upload button 543 m_uploadButton = new CmsUploadButton(m_handler); 544 m_uploadButton.addStyleName(I_CmsLayoutBundle.INSTANCE.uploadButton().uploadDialogButton()); 545 m_uploadButton.setText(Messages.get().key(Messages.GUI_REPLACE_CHANGE_FILE_0)); 546 addButton(m_uploadButton); 547 m_handler.setButton(m_uploadButton); 548 } 549 550 /** 551 * Creates the widget to display the selected file information.<p> 552 * 553 * @param file the file info 554 * 555 * @return the widget 556 */ 557 private CmsListItemWidget createFileWidget(CmsFileInfo file) { 558 559 String subTitle; 560 String resourceType = getResourceType(file); 561 if (file.getFileSize() > 0) { 562 subTitle = CmsUploadButton.formatBytes(file.getFileSize()) + " (" + getResourceType(file) + ")"; 563 } else { 564 subTitle = resourceType; 565 } 566 CmsListInfoBean infoBean = new CmsListInfoBean(file.getFileName(), subTitle, null); 567 m_fileWidget = new CmsListItemWidget(infoBean); 568 m_fileWidget.setIcon(CmsCoreProvider.get().getResourceTypeIcon(file)); 569 return m_fileWidget; 570 } 571 572 /** 573 * Hides the OK and upload button while processing the upload.<p> 574 */ 575 private void hideOkAndUploadButtons() { 576 577 m_uploadButton.setVisible(false); 578 m_okButton.setVisible(false); 579 } 580 581 /** 582 * Starts the upload progress bar.<p> 583 */ 584 private void showProgress() { 585 586 CmsFileInfo fileInfo = m_fileInput.getFiles()[0]; 587 m_progressInfo = new CmsUploadProgressInfo(Collections.singletonList(fileInfo.getFileName())); 588 m_progressInfo.setContentLength(fileInfo.getFileSize()); 589 m_mainPanel.setContainerWidget(m_progressInfo); 590 m_updateProgressTimer = new Timer() { 591 592 @Override 593 public void run() { 594 595 updateProgress(); 596 } 597 }; 598 m_updateProgressTimer.scheduleRepeating(1000); 599 600 } 601 602 /** 603 * Starts the loading animation.<p> 604 * 605 * Used while client is loading files from hard disk into memory.<p> 606 * 607 * @param msg the message that should be displayed below the loading animation (can also be HTML as String) 608 * @param delayMillis the delay to start the animation with 609 */ 610 private void startLoadingAnimation(final String msg, int delayMillis) { 611 612 m_loadingTimer = new Timer() { 613 614 @Override 615 public void run() { 616 617 m_mainPanel.showLoadingAnimation(msg); 618 } 619 }; 620 if (delayMillis > 0) { 621 m_loadingTimer.schedule(delayMillis); 622 } else { 623 m_loadingTimer.run(); 624 } 625 } 626 627 /** 628 * Stops the client loading animation.<p> 629 */ 630 private void stopLoadingAnimation() { 631 632 if (m_loadingTimer != null) { 633 m_loadingTimer.cancel(); 634 } 635 if (m_clientLoading) { 636 m_mainPanel.removeLoadingAnimation(); 637 m_clientLoading = false; 638 } 639 } 640}