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 GmbH & Co. KG, 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.workplace.editors; 029 030import org.opencms.file.CmsFile; 031import org.opencms.file.CmsObject; 032import org.opencms.file.CmsProperty; 033import org.opencms.file.CmsPropertyDefinition; 034import org.opencms.file.CmsResource; 035import org.opencms.file.CmsResourceFilter; 036import org.opencms.i18n.CmsEncoder; 037import org.opencms.i18n.CmsLocaleManager; 038import org.opencms.jsp.CmsJspActionElement; 039import org.opencms.lock.CmsLock; 040import org.opencms.lock.CmsLockType; 041import org.opencms.main.CmsException; 042import org.opencms.main.CmsLog; 043import org.opencms.main.OpenCms; 044import org.opencms.security.CmsPermissionSet; 045import org.opencms.security.I_CmsPrincipal; 046import org.opencms.ui.CmsVaadinUtils; 047import org.opencms.util.CmsStringUtil; 048import org.opencms.workplace.CmsWorkplace; 049import org.opencms.xml.content.CmsXmlContent; 050import org.opencms.xml.content.CmsXmlContentFactory; 051 052import java.io.IOException; 053import java.util.ArrayList; 054import java.util.HashMap; 055import java.util.List; 056import java.util.Locale; 057 058import javax.servlet.ServletException; 059import javax.servlet.http.HttpSession; 060import javax.servlet.jsp.JspException; 061 062import org.apache.commons.logging.Log; 063 064/** 065 * Provides basic methods for building the file editors of OpenCms.<p> 066 * 067 * The editor classes have to extend this class and implement action methods for common editor actions.<p> 068 * 069 * @since 6.0.0 070 */ 071public abstract class CmsEditor extends CmsEditorBase { 072 073 /** Value for the action: change the body. */ 074 public static final int ACTION_CHANGE_BODY = 124; 075 076 /** Value for the action: delete the current locale. */ 077 public static final int ACTION_DELETELOCALE = 140; 078 079 /** Value for the action: exit. */ 080 public static final int ACTION_EXIT = 122; 081 082 /** Value for the action: show a preview. */ 083 public static final int ACTION_PREVIEW = 126; 084 085 /** Value for the action: save. */ 086 public static final int ACTION_SAVE = 121; 087 088 /** Constant value for the customizable action button. */ 089 public static final int ACTION_SAVEACTION = 130; 090 091 /** Value for the action: save and exit. */ 092 public static final int ACTION_SAVEEXIT = 123; 093 094 /** Value for the action: show the editor. */ 095 public static final int ACTION_SHOW = 125; 096 097 /** Value for the action: an error occurred. */ 098 public static final int ACTION_SHOW_ERRORMESSAGE = 127; 099 100 /** Value for the action parameter: change the element. */ 101 public static final String EDITOR_CHANGE_ELEMENT = "changeelement"; 102 103 /** Value for the action parameter: cleanup content. */ 104 public static final String EDITOR_CLEANUP = "cleanup"; 105 106 /** Value for the action parameter: close browser window (accidentally). */ 107 public static final String EDITOR_CLOSEBROWSER = "closebrowser"; 108 109 /** Value for the action parameter: delete the current locale. */ 110 public static final String EDITOR_DELETELOCALE = "deletelocale"; 111 112 /** Value for the action parameter: exit editor. */ 113 public static final String EDITOR_EXIT = "exit"; 114 115 /** Value for the action parameter: show a preview. */ 116 public static final String EDITOR_PREVIEW = "preview"; 117 118 /** Value for the action parameter: save content. */ 119 public static final String EDITOR_SAVE = "save"; 120 121 /** Value for the customizable action button. */ 122 public static final String EDITOR_SAVEACTION = "saveaction"; 123 124 /** Value for the action parameter: save and exit. */ 125 public static final String EDITOR_SAVEEXIT = "saveexit"; 126 127 /** Value for the action parameter: show the editor. */ 128 public static final String EDITOR_SHOW = "show"; 129 130 /** Value for the action parameter: an error occurred. */ 131 public static final String EDITOR_SHOW_ERRORMESSAGE = "error"; 132 133 /** Marker for empty locale in locale selection. */ 134 public static final String EMPTY_LOCALE = " [-]"; 135 136 /** Parameter name for the request parameter "backlink". */ 137 public static final String PARAM_BACKLINK = "backlink"; 138 139 /** Parameter name for the request parameter "content". */ 140 public static final String PARAM_CONTENT = "content"; 141 142 /** Parameter name for the request parameter "directedit". */ 143 public static final String PARAM_DIRECTEDIT = "directedit"; 144 145 /** Parameter name for the request parameter "editastext". */ 146 public static final String PARAM_EDITASTEXT = "editastext"; 147 148 /** Parameter name for the request parameter "editormode". */ 149 public static final String PARAM_EDITORMODE = "editormode"; 150 151 /** Parameter name for the request parameter "element language". */ 152 public static final String PARAM_ELEMENTLANGUAGE = "elementlanguage"; 153 154 /** Parameter name for the request parameter "loaddefault". */ 155 public static final String PARAM_LOADDEFAULT = "loaddefault"; 156 157 /** Parameter name for the request parameter "modified". */ 158 public static final String PARAM_MODIFIED = "modified"; 159 160 /** Parameter name for the request parameter "old element language". */ 161 public static final String PARAM_OLDELEMENTLANGUAGE = "oldelementlanguage"; 162 163 /** Parameter name for the request parameter "tempfile". */ 164 public static final String PARAM_TEMPFILE = "tempfile"; 165 166 /** Stores the VFS editor path. */ 167 public static final String PATH_EDITORS = PATH_WORKPLACE + "editors/"; 168 169 /** The log object for this class. */ 170 private static final Log LOG = CmsLog.getLog(CmsEditor.class); 171 172 /** The editor session info bean. */ 173 private CmsEditorSessionInfo m_editorSessionInfo; 174 175 /** The encoding to use (will be read from the file property). */ 176 private String m_fileEncoding; 177 178 /** Back link parameter. */ 179 private String m_paramBackLink; 180 181 /** Content parameter. */ 182 private String m_paramContent; 183 184 /** Direct edit parameter. */ 185 private String m_paramDirectedit; 186 187 /** Edit as text parameter. */ 188 private String m_paramEditAsText; 189 190 /** Editor mode parameter. */ 191 private String m_paramEditormode; 192 193 /** Element language parameter. */ 194 private String m_paramElementlanguage; 195 196 /** Load default parameter. */ 197 private String m_paramLoadDefault; 198 199 /** Modified parameter. */ 200 private String m_paramModified; 201 202 /** Old element language parameter. */ 203 private String m_paramOldelementlanguage; 204 205 /** Temporary file parameter. */ 206 private String m_paramTempFile; 207 208 /** Helper variable to store the uri to the editors pictures. */ 209 private String m_picsUri; 210 211 /** 212 * Public constructor.<p> 213 * 214 * @param jsp an initialized JSP action element 215 */ 216 public CmsEditor(CmsJspActionElement jsp) { 217 218 super(jsp); 219 } 220 221 /** 222 * Unlocks the edited resource when in direct edit mode or when the resource was not modified.<p> 223 * 224 * @param forceUnlock if true, the resource will be unlocked anyway 225 */ 226 public abstract void actionClear(boolean forceUnlock); 227 228 /** 229 * Performs the exit editor action.<p> 230 * 231 * @throws CmsException if something goes wrong 232 * @throws IOException if a forward fails 233 * @throws ServletException if a forward fails 234 * @throws JspException if including an element fails 235 */ 236 public abstract void actionExit() throws CmsException, IOException, ServletException, JspException; 237 238 /** 239 * Performs the save content action.<p> 240 * 241 * @throws IOException if a redirection fails 242 * @throws JspException if including an element fails 243 */ 244 public abstract void actionSave() throws IOException, JspException; 245 246 /** 247 * Builds the html String for the element language selector.<p> 248 * 249 * @param attributes optional attributes for the <select> tag 250 * @param resourceName the name of the resource to edit 251 * @param selectedLocale the currently selected Locale 252 * @return the html for the element language selectbox 253 */ 254 public String buildSelectElementLanguage(String attributes, String resourceName, Locale selectedLocale) { 255 256 // get locale names based on properties and global settings 257 List<Locale> locales = OpenCms.getLocaleManager().getAvailableLocales(getCms(), resourceName); 258 List<String> options = new ArrayList<String>(locales.size()); 259 List<String> selectList = new ArrayList<String>(locales.size()); 260 int currentIndex = -1; 261 262 //get the locales already used in the resource 263 List<Locale> contentLocales = new ArrayList<Locale>(); 264 try { 265 266 CmsResource res = getCms().readResource(resourceName, CmsResourceFilter.IGNORE_EXPIRATION); 267 String temporaryFilename = CmsWorkplace.getTemporaryFileName(resourceName); 268 if (getCms().existsResource(temporaryFilename, CmsResourceFilter.IGNORE_EXPIRATION)) { 269 res = getCms().readResource(temporaryFilename, CmsResourceFilter.IGNORE_EXPIRATION); 270 } 271 CmsFile file = getCms().readFile(res); 272 CmsXmlContent xmlContent = CmsXmlContentFactory.unmarshal(getCms(), file); 273 contentLocales = xmlContent.getLocales(); 274 } catch (CmsException e) { 275 // to nothing here in case the resource could not be opened 276 if (LOG.isErrorEnabled()) { 277 LOG.error(Messages.get().getBundle().key(Messages.LOG_GET_LOCALES_1, resourceName), e); 278 } 279 } 280 281 for (int counter = 0; counter < locales.size(); counter++) { 282 // create the list of options and values 283 Locale curLocale = locales.get(counter); 284 selectList.add(curLocale.toString()); 285 StringBuffer buf = new StringBuffer(); 286 buf.append(curLocale.getDisplayName(getLocale())); 287 if (!contentLocales.contains(curLocale)) { 288 buf.append(EMPTY_LOCALE); 289 } 290 options.add(buf.toString()); 291 if (curLocale.equals(selectedLocale)) { 292 // set the selected index of the selector 293 currentIndex = counter; 294 } 295 } 296 297 if (currentIndex == -1) { 298 // no matching element language found, use first element language in list 299 if (selectList.size() > 0) { 300 currentIndex = 0; 301 setParamElementlanguage(selectList.get(0)); 302 } 303 } 304 305 return buildSelect(attributes, options, selectList, currentIndex, false); 306 } 307 308 /** 309 * Generates a button for the OpenCms editor.<p> 310 * 311 * @param href the href link for the button, if none is given the button will be disabled 312 * @param target the href link target for the button, if none is given the target will be same window 313 * @param image the image name for the button, skin path will be automatically added as prefix 314 * @param label the label for the text of the button 315 * @param type 0: image only (default), 1: image and text, 2: text only 316 * @param useCustomImage if true, the button has to be placed in the editors "custom pics" folder 317 * 318 * @return a button for the OpenCms editor 319 */ 320 public String button(String href, String target, String image, String label, int type, boolean useCustomImage) { 321 322 if (useCustomImage) { 323 // search the picture in the "custom pics" folder 324 return button(href, target, image, label, type, getPicsUri()); 325 } else { 326 // search the picture in the common "buttons" folder 327 return button(href, target, image, label, type); 328 } 329 } 330 331 /** 332 * Returns the editor action for a "cancel" button.<p> 333 * 334 * This overwrites the cancel method of the CmsDialog class.<p> 335 * 336 * Always use this value, do not write anything directly in the html page.<p> 337 * 338 * @return the default action for a "cancel" button 339 */ 340 public String buttonActionCancel() { 341 342 String target = null; 343 if (Boolean.valueOf(getParamDirectedit()).booleanValue()) { 344 // editor is in direct edit mode 345 if (CmsStringUtil.isNotEmpty(getParamBacklink())) { 346 // set link to the specified back link target 347 target = getParamBacklink(); 348 } else { 349 // set link to the edited resource 350 target = getParamResource(); 351 } 352 } else { 353 // in workplace mode, show explorer view 354 target = CmsVaadinUtils.getWorkplaceLink(); 355 } 356 return "onclick=\"top.location.href='" + getJsp().link(target) + "';\""; 357 } 358 359 /** 360 * Builds the html to display the special action button for the direct edit mode of the editor.<p> 361 * 362 * @param jsFunction the JavaScript function which will be executed on the mouseup event 363 * @param type 0: image only (default), 1: image and text, 2: text only 364 * @return the html to display the special action button 365 */ 366 public String buttonActionDirectEdit(String jsFunction, int type) { 367 368 // get the action class from the OpenCms runtime property 369 I_CmsEditorActionHandler actionClass = OpenCms.getWorkplaceManager().getEditorActionHandler(); 370 String url; 371 String name; 372 boolean active = false; 373 if (actionClass != null) { 374 // get button parameters and state from action class 375 url = actionClass.getButtonUrl(getJsp(), getParamResource()); 376 name = actionClass.getButtonName(); 377 active = actionClass.isButtonActive(getJsp(), getParamResource()); 378 } else { 379 // action class not defined, display inactive button 380 url = getSkinUri() + "buttons/publish_in.png"; 381 name = Messages.GUI_EXPLORER_CONTEXT_PUBLISH_0; 382 } 383 String image = url.substring(url.lastIndexOf("/") + 1); 384 if (url.endsWith(".gif")) { 385 image = image.substring(0, image.length() - 4); 386 } 387 388 if (active) { 389 // create the link for the button 390 return button( 391 "javascript:" + jsFunction, 392 null, 393 image, 394 name, 395 type, 396 url.substring(0, url.lastIndexOf("/") + 1)); 397 } else { 398 // create the inactive button 399 return button(null, null, image, name, type, url.substring(0, url.lastIndexOf("/") + 1)); 400 } 401 } 402 403 /** 404 * @see org.opencms.workplace.CmsWorkplace#checkLock(String, CmsLockType) 405 */ 406 @Override 407 public void checkLock(String resource, CmsLockType type) throws CmsException { 408 409 CmsResource res = getCms().readResource(resource, CmsResourceFilter.ALL); 410 CmsLock lock = getCms().getLock(res); 411 if (!lock.isNullLock()) { 412 setParamModified(Boolean.TRUE.toString()); 413 } 414 415 // for resources with siblings make sure all sibling have at least a 416 // temporary lock 417 if ((res.getSiblingCount() > 1) && (lock.isInherited())) { 418 super.checkLock(resource, CmsLockType.TEMPORARY); 419 } else { 420 super.checkLock(resource, type); 421 } 422 } 423 424 /** 425 * Generates a button for delete locale.<p> 426 * 427 * @param href the href link for the button, if none is given the button will be disabled 428 * @param target the href link target for the button, if none is given the target will be same window 429 * @param image the image name for the button, skin path will be automatically added as prefix 430 * @param label the label for the text of the button 431 * @param type 0: image only (default), 1: image and text, 2: text only 432 * 433 * @return a button for the OpenCms workplace 434 */ 435 public String deleteLocaleButton(String href, String target, String image, String label, int type) { 436 437 String filename = getParamResource(); 438 439 try { 440 CmsResource res = getCms().readResource(filename, CmsResourceFilter.IGNORE_EXPIRATION); 441 442 String temporaryFilename = CmsWorkplace.getTemporaryFileName(filename); 443 if (getCms().existsResource(temporaryFilename, CmsResourceFilter.IGNORE_EXPIRATION)) { 444 res = getCms().readResource(temporaryFilename, CmsResourceFilter.IGNORE_EXPIRATION); 445 } 446 CmsFile file = getCms().readFile(res); 447 CmsXmlContent xmlContent = CmsXmlContentFactory.unmarshal(getCms(), file); 448 int locales = xmlContent.getLocales().size(); 449 // there are less than 2 locales, so disable the delete locale button 450 if (locales < 2) { 451 href = null; 452 target = null; 453 image += "_in"; 454 } 455 } catch (CmsException e) { 456 // to nothing here in case the resource could not be opened 457 if (LOG.isErrorEnabled()) { 458 LOG.error(Messages.get().getBundle().key(Messages.LOG_GET_LOCALES_1, filename), e); 459 } 460 } 461 return button(href, target, image, label, type, getSkinUri() + "buttons/"); 462 463 } 464 465 /** 466 * Returns the instantiated editor display option class from the workplace manager.<p> 467 * 468 * This is a convenience method to be used on editor JSPs.<p> 469 * 470 * @return the instantiated editor display option class 471 */ 472 public CmsEditorDisplayOptions getEditorDisplayOptions() { 473 474 return OpenCms.getWorkplaceManager().getEditorDisplayOptions(); 475 } 476 477 /** 478 * Returns the URI to the editor resource folder where button images and javascripts are located.<p> 479 * 480 * @return the URI to the editor resource folder 481 */ 482 public abstract String getEditorResourceUri(); 483 484 /** 485 * Returns the OpenCms request context path.<p> 486 * 487 * This is a convenience method to use in the editor.<p> 488 * 489 * @return the OpenCms request context path 490 */ 491 public String getOpenCmsContext() { 492 493 return OpenCms.getSystemInfo().getOpenCmsContext(); 494 } 495 496 /** 497 * Returns the back link when closing the editor.<p> 498 * 499 * @return the back link 500 */ 501 public String getParamBacklink() { 502 503 if ((m_editorSessionInfo != null) 504 && CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_editorSessionInfo.getBackLink())) { 505 m_paramBackLink = m_editorSessionInfo.getBackLink(); 506 } 507 if (m_paramBackLink == null) { 508 m_paramBackLink = ""; 509 } 510 return m_paramBackLink; 511 } 512 513 /** 514 * Returns the content of the editor.<p> 515 * @return the content of the editor 516 */ 517 public String getParamContent() { 518 519 if (m_paramContent == null) { 520 m_paramContent = ""; 521 } 522 return m_paramContent; 523 } 524 525 /** 526 * Returns the direct edit flag parameter.<p> 527 * 528 * @return the direct edit flag parameter 529 */ 530 public String getParamDirectedit() { 531 532 if (m_editorSessionInfo != null) { 533 return String.valueOf(m_editorSessionInfo.isDirectEdit()); 534 } 535 return m_paramDirectedit; 536 } 537 538 /** 539 * Returns the edit as text parameter.<p> 540 * 541 * @return the edit as text parameter 542 */ 543 public String getParamEditastext() { 544 545 return m_paramEditAsText; 546 } 547 548 /** 549 * Returns the editor mode parameter.<p> 550 * 551 * @return the editor mode parameter 552 */ 553 public String getParamEditormode() { 554 555 return m_paramEditormode; 556 } 557 558 /** 559 * Returns the current element language.<p> 560 * 561 * @return the current element language 562 */ 563 public String getParamElementlanguage() { 564 565 if (m_paramElementlanguage == null) { 566 if ((m_editorSessionInfo != null) && (m_editorSessionInfo.getElementLocale() != null)) { 567 m_paramElementlanguage = m_editorSessionInfo.getElementLocale().toString(); 568 } 569 } 570 return m_paramElementlanguage; 571 } 572 573 /** 574 * Returns the "loaddefault" parameter to determine if the default editor should be loaded.<p> 575 * 576 * @return the "loaddefault" parameter 577 */ 578 public String getParamLoaddefault() { 579 580 return m_paramLoadDefault; 581 } 582 583 /** 584 * Returns the modified parameter indicating if the resource has been saved.<p> 585 * 586 * @return the modified parameter indicating if the resource has been saved 587 */ 588 public String getParamModified() { 589 590 return m_paramModified; 591 } 592 593 /** 594 * Returns the old element language.<p> 595 * 596 * @return the old element language 597 */ 598 public String getParamOldelementlanguage() { 599 600 return m_paramOldelementlanguage; 601 } 602 603 /** 604 * Returns the name of the temporary file.<p> 605 * 606 * @return the name of the temporary file 607 */ 608 public String getParamTempfile() { 609 610 return m_paramTempFile; 611 } 612 613 /** 614 * Returns the path to the images used by this editor.<p> 615 * 616 * @return the path to the images used by this editor 617 */ 618 public String getPicsUri() { 619 620 if (m_picsUri == null) { 621 m_picsUri = getEditorResourceUri() + "pics/"; 622 } 623 return m_picsUri; 624 } 625 626 /** 627 * Sets the back link when closing the editor.<p> 628 * 629 * @param backLink the back link 630 */ 631 public void setParamBacklink(String backLink) { 632 633 m_paramBackLink = backLink; 634 } 635 636 /** 637 * Sets the content of the editor.<p> 638 * 639 * @param content the content of the editor 640 */ 641 public void setParamContent(String content) { 642 643 if (content == null) { 644 content = ""; 645 } 646 m_paramContent = content; 647 } 648 649 /** 650 * Sets the direct edit flag parameter.<p> 651 * 652 * @param direct the direct edit flag parameter 653 */ 654 public void setParamDirectedit(String direct) { 655 656 m_paramDirectedit = direct; 657 } 658 659 /** 660 * Sets the edit as text parameter.<p> 661 * 662 * @param editAsText <code>"true"</code> if the resource should be handled like a text file 663 */ 664 public void setParamEditastext(String editAsText) { 665 666 m_paramEditAsText = editAsText; 667 } 668 669 /** 670 * Sets the editor mode parameter.<p> 671 * 672 * @param mode the editor mode parameter 673 */ 674 public void setParamEditormode(String mode) { 675 676 m_paramEditormode = mode; 677 } 678 679 /** 680 * Sets the current element language.<p> 681 * 682 * @param elementLanguage the current element language 683 */ 684 public void setParamElementlanguage(String elementLanguage) { 685 686 m_paramElementlanguage = elementLanguage; 687 } 688 689 /** 690 * Sets the "loaddefault" parameter to determine if the default editor should be loaded.<p> 691 * 692 * @param loadDefault the "loaddefault" parameter 693 */ 694 public void setParamLoaddefault(String loadDefault) { 695 696 m_paramLoadDefault = loadDefault; 697 } 698 699 /** 700 * Sets the modified parameter indicating if the resource has been saved.<p> 701 * 702 * @param modified the modified parameter indicating if the resource has been saved 703 */ 704 public void setParamModified(String modified) { 705 706 m_paramModified = modified; 707 } 708 709 /** 710 * Sets the old element language.<p> 711 * 712 * @param oldElementLanguage the old element language 713 */ 714 public void setParamOldelementlanguage(String oldElementLanguage) { 715 716 m_paramOldelementlanguage = oldElementLanguage; 717 } 718 719 /** 720 * Sets the name of the temporary file.<p> 721 * 722 * @param fileName the name of the temporary file 723 */ 724 public void setParamTempfile(String fileName) { 725 726 m_paramTempFile = fileName; 727 } 728 729 /** 730 * Closes the editor and forwards to the workplace or the resource depending on the editor mode.<p> 731 * 732 * @throws IOException if forwarding fails 733 * @throws ServletException if forwarding fails 734 * @throws JspException if including a JSP fails 735 */ 736 protected void actionClose() throws IOException, JspException, ServletException { 737 738 try { 739 if (Boolean.valueOf(getParamDirectedit()).booleanValue()) { 740 // editor is in direct edit mode 741 if (CmsStringUtil.isNotEmpty(getParamBacklink())) { 742 // set link to the specified back link target 743 setParamCloseLink(getJsp().link(getParamBacklink())); 744 } else { 745 // set link to the edited resource 746 setParamCloseLink(getJsp().link(getParamResource())); 747 } 748 // save initialized instance of this class in request attribute for included sub-elements 749 getJsp().getRequest().setAttribute(SESSION_WORKPLACE_CLASS, this); 750 // load the common JSP close dialog 751 getJsp().include(FILE_DIALOG_CLOSE); 752 } else { 753 if (CmsStringUtil.isNotEmpty(getParamBacklink())) { 754 // set link to the specified back link target 755 setParamCloseLink(getJsp().link(getParamBacklink())); 756 // save initialized instance of this class in request attribute for included sub-elements 757 getJsp().getRequest().setAttribute(SESSION_WORKPLACE_CLASS, this); 758 // load the common JSP close dialog 759 getJsp().include(FILE_DIALOG_CLOSE); 760 } else { 761 // forward to the workplace explorer view 762 sendForward(CmsVaadinUtils.getWorkplaceLink(), new HashMap<String, String[]>()); 763 } 764 } 765 } finally { 766 clearEditorSessionInfo(); 767 } 768 } 769 770 /** 771 * Clears the editor session info bean.<p> 772 */ 773 protected void clearEditorSessionInfo() { 774 775 if (m_editorSessionInfo != null) { 776 getSession().removeAttribute(m_editorSessionInfo.getEditorSessionInfoKey()); 777 } 778 m_editorSessionInfo = null; 779 } 780 781 /** 782 * Writes the content of a temporary file back to the original file.<p> 783 * 784 * @throws CmsException if something goes wrong 785 */ 786 protected void commitTempFile() throws CmsException { 787 788 CmsObject cms = getCms(); 789 CmsFile tempFile; 790 List<CmsProperty> properties; 791 try { 792 switchToTempProject(); 793 tempFile = cms.readFile(getParamTempfile(), CmsResourceFilter.ALL); 794 properties = cms.readPropertyObjects(getParamTempfile(), false); 795 } finally { 796 // make sure the project is reset in case of any exception 797 switchToCurrentProject(); 798 } 799 if (cms.existsResource(getParamResource(), CmsResourceFilter.ALL)) { 800 // update properties of original file first (required if change in encoding occurred) 801 cms.writePropertyObjects(getParamResource(), properties); 802 // now replace the content of the original file 803 CmsFile orgFile = cms.readFile(getParamResource(), CmsResourceFilter.ALL); 804 orgFile.setContents(tempFile.getContents()); 805 getCloneCms().writeFile(orgFile); 806 } else { 807 // original file does not exist, remove visibility permission entries and copy temporary file 808 809 // switch to the temporary file project 810 try { 811 switchToTempProject(); 812 // lock the temporary file 813 cms.changeLock(getParamTempfile()); 814 // remove visibility permissions for everybody on temporary file if possible 815 if (cms.hasPermissions(tempFile, CmsPermissionSet.ACCESS_CONTROL)) { 816 cms.rmacc( 817 getParamTempfile(), 818 I_CmsPrincipal.PRINCIPAL_GROUP, 819 OpenCms.getDefaultUsers().getGroupUsers()); 820 } 821 } finally { 822 // make sure the project is reset in case of any exception 823 switchToCurrentProject(); 824 } 825 826 cms.copyResource(getParamTempfile(), getParamResource(), CmsResource.COPY_AS_NEW); 827 // ensure the content handler is called 828 CmsFile orgFile = cms.readFile(getParamResource(), CmsResourceFilter.ALL); 829 getCloneCms().writeFile(orgFile); 830 831 } 832 // remove the temporary file flag 833 int flags = cms.readResource(getParamResource(), CmsResourceFilter.ALL).getFlags(); 834 if ((flags & CmsResource.FLAG_TEMPFILE) == CmsResource.FLAG_TEMPFILE) { 835 flags ^= CmsResource.FLAG_TEMPFILE; 836 cms.chflags(getParamResource(), flags); 837 } 838 } 839 840 /** 841 * Creates a temporary file which is needed while working in an editor with preview option.<p> 842 * 843 * @return the file name of the temporary file 844 * @throws CmsException if something goes wrong 845 */ 846 protected String createTempFile() throws CmsException { 847 848 return OpenCms.getWorkplaceManager().createTempFile(getCms(), getParamResource(), getSettings().getProject()); 849 } 850 851 /** 852 * Decodes the given content the same way the client would do it.<p> 853 * 854 * Content is decoded as if it was encoded using the JavaScript 855 * "encodeURIComponent()" function.<p> 856 * 857 * @param content the content to decode 858 * @return the decoded content 859 */ 860 protected String decodeContent(String content) { 861 862 return CmsEncoder.unescape(content, CmsEncoder.ENCODING_UTF_8); 863 } 864 865 /** 866 * Decodes an individual parameter value, ensuring the content is always decoded in UTF-8.<p> 867 * 868 * For editors the content is always encoded using the 869 * JavaScript encodeURIComponent() method on the client, 870 * which always encodes in UTF-8.<p> 871 * 872 * @param paramName the name of the parameter 873 * @param paramValue the unencoded value of the parameter 874 * @return the encoded value of the parameter 875 */ 876 @Override 877 protected String decodeParamValue(String paramName, String paramValue) { 878 879 if ((paramName != null) && (paramValue != null)) { 880 if (PARAM_CONTENT.equals(paramName)) { 881 // content will be always encoded in UTF-8 unicode by the editor client 882 return CmsEncoder.decode(paramValue, CmsEncoder.ENCODING_UTF_8); 883 } else if (PARAM_RESOURCE.equals(paramName) || PARAM_TEMPFILE.equals(paramName)) { 884 String filename = CmsEncoder.decode(paramValue, getCms().getRequestContext().getEncoding()); 885 if (PARAM_TEMPFILE.equals(paramName) || CmsStringUtil.isEmpty(getParamTempfile())) { 886 // always use value from temp file if it is available 887 setFileEncoding(getFileEncoding(getCms(), filename)); 888 } 889 return filename; 890 } else { 891 return CmsEncoder.decode(paramValue, getCms().getRequestContext().getEncoding()); 892 } 893 } else { 894 return null; 895 } 896 } 897 898 /** 899 * Deletes a temporary file from the OpenCms VFS, needed when exiting an editor.<p> 900 */ 901 protected void deleteTempFile() { 902 903 try { 904 // switch to the temporary file project 905 switchToTempProject(); 906 // delete the temporary file 907 getCms().deleteResource(getParamTempfile(), CmsResource.DELETE_PRESERVE_SIBLINGS); 908 } catch (CmsException e) { 909 // should usually never happen 910 if (LOG.isInfoEnabled()) { 911 LOG.info(e.getLocalizedMessage(), e); 912 } 913 } finally { 914 try { 915 // switch back to the current project 916 switchToCurrentProject(); 917 } catch (CmsException e) { 918 // should usually never happen 919 if (LOG.isInfoEnabled()) { 920 LOG.info(e.getLocalizedMessage(), e); 921 } 922 } 923 } 924 } 925 926 /** 927 * Encodes the given content so that it can be transfered to the client.<p> 928 * 929 * Content is encoded so that it is compatible with the JavaScript 930 * "decodeURIComponent()" function.<p> 931 * 932 * @param content the content to encode 933 * @return the encoded content 934 */ 935 protected String encodeContent(String content) { 936 937 return CmsEncoder.escapeWBlanks(content, CmsEncoder.ENCODING_UTF_8); 938 } 939 940 /** 941 * Returns a cloned cms instance that prevents the time range resource filter check.<p> 942 * 943 * Use it always for unmarshalling and file writing.<p> 944 * 945 * @return a cloned cms instance that prevents the time range resource filter check 946 * 947 * @throws CmsException if something goes wrong 948 */ 949 protected CmsObject getCloneCms() throws CmsException { 950 951 CmsObject cloneCms = OpenCms.initCmsObject(getCms()); 952 cloneCms.getRequestContext().setRequestTime(CmsResource.DATE_RELEASED_EXPIRED_IGNORE); 953 return cloneCms; 954 } 955 956 /** 957 * Returns the editor session info bean.<p> 958 * 959 * @return the editor session info bean 960 */ 961 protected CmsEditorSessionInfo getEditorSessionInfo() { 962 963 return m_editorSessionInfo; 964 } 965 966 /** 967 * Returns the encoding parameter.<p> 968 * 969 * @return the encoding parameter 970 */ 971 protected String getFileEncoding() { 972 973 return m_fileEncoding; 974 } 975 976 /** 977 * Helper method to determine the encoding of the given file in the VFS, 978 * which must be set using the "content-encoding" property.<p> 979 * 980 * @param cms the CmsObject 981 * @param filename the name of the file which is to be checked 982 * @return the encoding for the file 983 */ 984 protected String getFileEncoding(CmsObject cms, String filename) { 985 986 try { 987 return cms.readPropertyObject(filename, CmsPropertyDefinition.PROPERTY_CONTENT_ENCODING, true).getValue( 988 OpenCms.getSystemInfo().getDefaultEncoding()); 989 } catch (CmsException e) { 990 return OpenCms.getSystemInfo().getDefaultEncoding(); 991 } 992 } 993 994 /** 995 * Initializes the editor content when openening the editor for the first time.<p> 996 */ 997 protected abstract void initContent(); 998 999 /** 1000 * @see org.opencms.workplace.CmsWorkplace#initMessages() 1001 */ 1002 @Override 1003 protected void initMessages() { 1004 1005 initSessionInfo(); 1006 super.initMessages(); 1007 } 1008 1009 /** 1010 * Initializes the editor session info bean.<p> 1011 */ 1012 protected void initSessionInfo() { 1013 1014 CmsResource editedResource = null; 1015 try { 1016 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(getParamResource())) { 1017 editedResource = getCms().readResource(getParamResource()); 1018 } 1019 } catch (CmsException e) { 1020 // ignore 1021 } 1022 1023 CmsEditorSessionInfo info = null; 1024 if (editedResource != null) { 1025 HttpSession session = getSession(); 1026 info = (CmsEditorSessionInfo)session.getAttribute( 1027 CmsEditorSessionInfo.getEditorSessionInfoKey(editedResource)); 1028 if (info == null) { 1029 info = new CmsEditorSessionInfo(editedResource.getStructureId()); 1030 } 1031 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_paramBackLink)) { 1032 info.setBackLink(m_paramBackLink); 1033 } 1034 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_paramElementlanguage)) { 1035 info.setElementLocale(CmsLocaleManager.getLocale(m_paramElementlanguage)); 1036 } 1037 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_paramDirectedit)) { 1038 info.setDirectEdit(Boolean.parseBoolean(m_paramDirectedit)); 1039 } 1040 session.setAttribute(info.getEditorSessionInfoKey(), info); 1041 } 1042 m_editorSessionInfo = info; 1043 } 1044 1045 /** 1046 * Sets the encoding parameter.<p> 1047 * 1048 * @param value the encoding value to set 1049 */ 1050 protected void setFileEncoding(String value) { 1051 1052 m_fileEncoding = CmsEncoder.lookupEncoding(value, value); 1053 } 1054 1055 /** 1056 * Shows the selected error page in case of an exception.<p> 1057 * 1058 * @param exception the current exception 1059 * @throws JspException if inclusion of the error page fails 1060 */ 1061 protected void showErrorPage(Exception exception) throws JspException { 1062 1063 // reset the action parameter 1064 setParamAction(""); 1065 showErrorPage(this, exception); 1066 // save not successful, set cancel action 1067 setAction(ACTION_CANCEL); 1068 return; 1069 } 1070 1071 /** 1072 * Shows the selected error page in case of an exception.<p> 1073 * 1074 * @param editor initialized instance of the editor class 1075 * @param exception the current exception 1076 * @throws JspException if inclusion of the error page fails 1077 */ 1078 protected void showErrorPage(Object editor, Exception exception) throws JspException { 1079 1080 // save initialized instance of the editor class in request attribute for included sub-elements 1081 getJsp().getRequest().setAttribute(SESSION_WORKPLACE_CLASS, editor); 1082 1083 // reading of file contents failed, show error dialog 1084 setAction(ACTION_SHOW_ERRORMESSAGE); 1085 setParamTitle(key(Messages.GUI_TITLE_EDIT_1, new Object[] {CmsResource.getName(getParamResource())})); 1086 if (exception != null) { 1087 getJsp().getRequest().setAttribute(ATTRIBUTE_THROWABLE, exception); 1088 if (CmsLog.getLog(editor).isWarnEnabled()) { 1089 CmsLog.getLog(editor).warn(exception.getLocalizedMessage(), exception); 1090 } 1091 } 1092 // include the common error dialog 1093 getJsp().include(FILE_DIALOG_SCREEN_ERRORPAGE); 1094 } 1095}