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