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.CmsPropertyDefinition; 032import org.opencms.file.CmsRequestContext; 033import org.opencms.i18n.CmsEncoder; 034import org.opencms.i18n.CmsLocaleManager; 035import org.opencms.jsp.CmsJspActionElement; 036import org.opencms.main.CmsException; 037import org.opencms.main.CmsLog; 038import org.opencms.main.OpenCms; 039import org.opencms.util.CmsHtmlConverter; 040import org.opencms.util.CmsStringUtil; 041import org.opencms.xml.CmsXmlException; 042import org.opencms.xml.page.CmsXmlPage; 043import org.opencms.xml.page.CmsXmlPageFactory; 044 045import java.io.IOException; 046import java.util.ArrayList; 047import java.util.Iterator; 048import java.util.List; 049import java.util.Locale; 050 051import javax.servlet.ServletException; 052import javax.servlet.jsp.JspException; 053 054import org.apache.commons.logging.Log; 055 056/** 057 * Provides methods for building editors for the CmsDefaultPage page type.<p> 058 * 059 * Extend this class for all editors that work with the CmsDefaultPage.<p> 060 * 061 * @since 6.0.0 062 */ 063public abstract class CmsDefaultPageEditor extends CmsEditor { 064 065 /** Parameter name for the request parameter "element name". */ 066 public static final String PARAM_ELEMENTNAME = "elementname"; 067 068 /** Parameter name for the request parameter "old element name". */ 069 public static final String PARAM_OLDELEMENTNAME = "oldelementname"; 070 071 /** option values for font select boxes. */ 072 public static final String[] SELECTBOX_FONTS = { 073 "Arial", 074 "Arial Narrow", 075 "System", 076 "Times New Roman", 077 "Verdana", 078 "Monospace", 079 "SansSerif"}; 080 081 /** Name of the special body element from an XMLTemplate. */ 082 public static final String XML_BODY_ELEMENT = "body"; 083 084 /** The log object for this class. */ 085 private static final Log LOG = CmsLog.getLog(CmsDefaultPageEditor.class); 086 087 /** File object used to read and write contents. */ 088 protected CmsFile m_file; 089 090 /** Page object used from the action and init methods, be sure to initialize this e.g. in the initWorkplaceRequestValues method. */ 091 protected CmsXmlPage m_page; 092 093 /** The element list. */ 094 private List<CmsDialogElement> m_elementList; 095 096 /** The element locale. */ 097 private Locale m_elementLocale; 098 099 /** The element name parameter. */ 100 private String m_paramElementname; 101 102 /** The old element name parameter. */ 103 private String m_paramOldelementname; 104 105 /** The URI of the style sheet to use in the editor. */ 106 private String m_uriStyleSheet; 107 108 /** 109 * Public constructor.<p> 110 * 111 * @param jsp an initialized JSP action element 112 */ 113 public CmsDefaultPageEditor(CmsJspActionElement jsp) { 114 115 super(jsp); 116 } 117 118 /** 119 * Performs the change body action of the editor.<p> 120 */ 121 public void actionChangeBodyElement() { 122 123 try { 124 // save eventually changed content of the editor to the temporary file 125 Locale oldLocale = CmsLocaleManager.getLocale(getParamOldelementlanguage()); 126 performSaveContent(getParamOldelementname(), oldLocale); 127 } catch (CmsException e) { 128 // show error page 129 try { 130 showErrorPage(this, e); 131 } catch (JspException exc) { 132 // should usually never happen 133 if (LOG.isInfoEnabled()) { 134 LOG.info(exc); 135 } 136 } 137 } 138 // re-initialize the element name if the language has changed 139 if (!getParamElementlanguage().equals(getParamOldelementlanguage())) { 140 initBodyElementName(getParamOldelementname()); 141 } 142 // get the new editor content 143 initContent(); 144 } 145 146 /** 147 * Performs the cleanup body action of the editor.<p> 148 */ 149 public void actionCleanupBodyElement() { 150 151 try { 152 // save eventually changed content of the editor to the temporary file 153 Locale oldLocale = CmsLocaleManager.getLocale(getParamOldelementlanguage()); 154 performSaveContent(getParamOldelementname(), oldLocale); 155 } catch (CmsException e) { 156 // show error page 157 try { 158 showErrorPage(this, e); 159 } catch (JspException exc) { 160 // should usually never happen 161 if (LOG.isInfoEnabled()) { 162 LOG.info(exc); 163 } 164 } 165 } 166 } 167 168 /** 169 * @see org.opencms.workplace.editors.CmsEditor#actionClear(boolean) 170 */ 171 @Override 172 public void actionClear(boolean forceUnlock) { 173 174 // delete the temporary file 175 deleteTempFile(); 176 boolean directEditMode = Boolean.valueOf(getParamDirectedit()).booleanValue(); 177 boolean modified = Boolean.valueOf(getParamModified()).booleanValue(); 178 if (directEditMode || forceUnlock || !modified) { 179 // unlock the resource when in direct edit mode, force unlock is true or resource was not modified 180 try { 181 getCms().unlockResource(getParamResource()); 182 } catch (CmsException e) { 183 // should usually never happen 184 if (LOG.isInfoEnabled()) { 185 LOG.info(e.getLocalizedMessage(), e); 186 } 187 } 188 } 189 } 190 191 /** 192 * Performs the delete locale action.<p> 193 * 194 * @throws JspException if something goes wrong 195 */ 196 public void actionDeleteElementLocale() throws JspException { 197 198 try { 199 Locale loc = getElementLocale(); 200 m_page.removeLocale(loc); 201 //write the modified xml content 202 m_file.setContents(m_page.marshal()); 203 m_file = getCms().writeFile(m_file); 204 List<Locale> locales = m_page.getLocales(); 205 if (locales.size() > 0) { 206 // set first locale as new display locale 207 Locale newLoc = locales.get(0); 208 setParamElementlanguage(newLoc.toString()); 209 m_elementLocale = newLoc; 210 } else { 211 if (LOG.isErrorEnabled()) { 212 LOG.error(Messages.get().getBundle().key(Messages.LOG_GET_LOCALES_1, getParamResource())); 213 } 214 } 215 initContent(); 216 } catch (CmsXmlException e) { 217 // an error occurred while trying to delete the locale, stop action 218 showErrorPage(e); 219 } catch (CmsException e) { 220 // should usually never happen 221 if (LOG.isInfoEnabled()) { 222 LOG.info(e.getLocalizedMessage(), e); 223 } 224 } 225 } 226 227 /** 228 * Performs a configurable action performed by the editor.<p> 229 * 230 * The default action is: save resource, clear temporary files and publish the resource directly.<p> 231 * 232 * @throws IOException if a forward fails 233 * @throws JspException if including a JSP fails 234 * @throws ServletException if a forward fails 235 */ 236 public void actionDirectEdit() throws IOException, JspException, ServletException { 237 238 // get the action class from the OpenCms runtime property 239 I_CmsEditorActionHandler actionClass = OpenCms.getWorkplaceManager().getEditorActionHandler(); 240 if (actionClass == null) { 241 // error getting the action class, save content and exit the editor 242 actionSave(); 243 actionExit(); 244 } else { 245 actionClass.editorAction(this, getJsp()); 246 } 247 } 248 249 /** 250 * Performs the exit editor action and deletes the temporary file.<p> 251 * 252 * @see org.opencms.workplace.editors.CmsEditor#actionExit() 253 */ 254 @Override 255 public void actionExit() throws IOException, JspException, ServletException { 256 257 if (getAction() == ACTION_CANCEL) { 258 // save and exit was canceled 259 return; 260 } 261 // clear temporary file and unlock resource, if in directedit mode 262 actionClear(false); 263 // close the editor 264 actionClose(); 265 } 266 267 /** 268 * Performs the preview page action in a new browser window.<p> 269 * 270 * @throws IOException if redirect fails 271 * @throws JspException if inclusion of error page fails 272 */ 273 public void actionPreview() throws IOException, JspException { 274 275 try { 276 // save content of the editor to the temporary file 277 performSaveContent(getParamElementname(), getElementLocale()); 278 } catch (CmsException e) { 279 // show error page 280 showErrorPage(this, e); 281 } 282 283 // redirect to the temporary file with current active element language 284 String param = "?" + org.opencms.i18n.CmsLocaleManager.PARAMETER_LOCALE + "=" + getParamElementlanguage(); 285 sendCmsRedirect(getParamTempfile() + param); 286 } 287 288 /** 289 * @see org.opencms.workplace.editors.CmsEditor#actionSave() 290 */ 291 @Override 292 public void actionSave() throws JspException { 293 294 try { 295 296 // save content to temporary file 297 performSaveContent(getParamElementname(), getElementLocale()); 298 // copy the temporary file content back to the original file 299 commitTempFile(); 300 // set the modified parameter 301 setParamModified(Boolean.TRUE.toString()); 302 } catch (CmsException e) { 303 showErrorPage(e); 304 } 305 306 if (getAction() != ACTION_CANCEL) { 307 // save successful, set save action 308 setAction(ACTION_SAVE); 309 } 310 } 311 312 /** 313 * Builds the html String for the element language selector.<p> 314 * 315 * @param attributes optional attributes for the <select> tag 316 * @return the html for the element language selectbox 317 */ 318 public String buildSelectElementLanguage(String attributes) { 319 320 return buildSelectElementLanguage(attributes, getParamResource(), getElementLocale()); 321 } 322 323 /** 324 * Builds the html String for the element name selector.<p> 325 * 326 * @param attributes optional attributes for the <select> tag 327 * @return the html for the element name selectbox 328 */ 329 public String buildSelectElementName(String attributes) { 330 331 // get the active page elements 332 List<CmsDialogElement> elementList = getElementList(); 333 334 int counter = 0; 335 int currentIndex = -1; 336 List<String> options = new ArrayList<String>(elementList.size()); 337 List<String> values = new ArrayList<String>(elementList.size()); 338 String elementName = getParamElementname(); 339 if (CmsStringUtil.isEmpty(elementName)) { 340 elementName = getParamOldelementname(); 341 } 342 for (int i = 0; i < elementList.size(); i++) { 343 // get the current list element 344 CmsDialogElement element = elementList.get(i); 345 346 if (CmsStringUtil.isNotEmpty(elementName) && elementName.equals(element.getName())) { 347 // current element is the displayed one, mark it as selected 348 currentIndex = counter; 349 } 350 if ((!m_page.hasValue(element.getName(), getElementLocale()) && element.isMandantory()) 351 || m_page.isEnabled(element.getName(), getElementLocale())) { 352 // add element if it is not available or if it is enabled 353 options.add(element.getNiceName()); 354 values.add(element.getName()); 355 counter++; 356 } 357 } 358 return buildSelect(attributes, options, values, currentIndex, false); 359 } 360 361 /** 362 * Builds the html for the font face select box of a WYSIWYG editor.<p> 363 * 364 * @param attributes optional attributes for the <select> tag 365 * @return the html for the font face select box 366 */ 367 public String buildSelectFonts(String attributes) { 368 369 List<String> names = new ArrayList<String>(); 370 for (int i = 0; i < CmsDefaultPageEditor.SELECTBOX_FONTS.length; i++) { 371 String value = CmsDefaultPageEditor.SELECTBOX_FONTS[i]; 372 names.add(value); 373 } 374 return buildSelect(attributes, names, names, -1, false); 375 } 376 377 /** 378 * Escapes the content and title parameters to display them in the editor form.<p> 379 * 380 * This method has to be called on the JSP right before the form display html is created.<p> * 381 */ 382 public void escapeParams() { 383 384 // escape the content 385 setParamContent(CmsEncoder.escapeWBlanks(getParamContent(), CmsEncoder.ENCODING_UTF_8)); 386 } 387 388 /** 389 * Returns the current element locale.<p> 390 * 391 * @return the current element locale 392 */ 393 public Locale getElementLocale() { 394 395 if (m_elementLocale == null) { 396 m_elementLocale = CmsLocaleManager.getLocale(getParamElementlanguage()); 397 } 398 return m_elementLocale; 399 } 400 401 /** 402 * Returns the current element name.<p> 403 * 404 * @return the current element name 405 */ 406 public String getParamElementname() { 407 408 return m_paramElementname; 409 } 410 411 /** 412 * Returns the old element name.<p> 413 * 414 * @return the old element name 415 */ 416 public String getParamOldelementname() { 417 418 return m_paramOldelementname; 419 } 420 421 /** 422 * Returns the OpenCms VFS uri of the style sheet of the current page.<p> 423 * 424 * @return the OpenCms VFS uri of the style sheet of the current page 425 */ 426 public String getUriStyleSheet() { 427 428 if (m_uriStyleSheet == null) { 429 try { 430 if (OpenCms.getWorkplaceManager().getEditorCssHandlers().size() > 0) { 431 // use the configured handlers to determine the CSS to use 432 Iterator<I_CmsEditorCssHandler> i = OpenCms.getWorkplaceManager().getEditorCssHandlers().iterator(); 433 while (i.hasNext()) { 434 I_CmsEditorCssHandler cssHandler = i.next(); 435 if (cssHandler.matches(getCms(), getParamTempfile())) { 436 m_uriStyleSheet = cssHandler.getUriStyleSheet(getCms(), getParamTempfile()); 437 break; 438 } 439 } 440 } else { 441 // for compatibility reasons, read the template property value from the template file to get the CSS 442 String currentTemplate = getUriTemplate(); 443 m_uriStyleSheet = getCms().readPropertyObject( 444 currentTemplate, 445 CmsPropertyDefinition.PROPERTY_TEMPLATE, 446 false).getValue(""); 447 } 448 } catch (CmsException e) { 449 LOG.warn(Messages.get().getBundle().key(Messages.LOG_READ_TEMPLATE_PROP_STYLESHEET_FAILED_0), e); 450 } 451 } 452 return m_uriStyleSheet; 453 } 454 455 /** 456 * Returns the OpenCms VFS uri of the template of the current page.<p> 457 * 458 * @return the OpenCms VFS uri of the template of the current page 459 */ 460 public String getUriTemplate() { 461 462 String result = ""; 463 try { 464 result = getCms().readPropertyObject( 465 getParamTempfile(), 466 CmsPropertyDefinition.PROPERTY_TEMPLATE, 467 true).getValue(""); 468 } catch (CmsException e) { 469 LOG.warn(Messages.get().getBundle().key(Messages.LOG_READ_TEMPLATE_PROP_FAILED_0), e); 470 } 471 return result; 472 } 473 474 /** 475 * Sets the current element name.<p> 476 * 477 * @param elementName the current element name 478 */ 479 public void setParamElementname(String elementName) { 480 481 m_paramElementname = elementName; 482 } 483 484 /** 485 * Sets the old element name.<p> 486 * 487 * @param oldElementName the old element name 488 */ 489 public void setParamOldelementname(String oldElementName) { 490 491 m_paramOldelementname = oldElementName; 492 } 493 494 /** 495 * Returns the list of active elements of the page.<p> 496 * 497 * @return the list of active elements of the page 498 */ 499 protected List<CmsDialogElement> getElementList() { 500 501 if (m_elementList == null) { 502 m_elementList = CmsDialogElements.computeElements(getCms(), m_page, getParamTempfile(), getElementLocale()); 503 } 504 return m_elementList; 505 } 506 507 /** 508 * Initializes the body element language for the first call of the editor.<p> 509 */ 510 protected void initBodyElementLanguage() { 511 512 List<Locale> locales = m_page.getLocales(); 513 Locale defaultLocale = OpenCms.getLocaleManager().getDefaultLocales(getCms(), getCms().getSitePath(m_file)).get( 514 0); 515 516 if (locales.size() == 0) { 517 // no body present, create default body 518 if (!m_page.hasValue(CmsDefaultPageEditor.XML_BODY_ELEMENT, defaultLocale)) { 519 m_page.addValue(CmsDefaultPageEditor.XML_BODY_ELEMENT, defaultLocale); 520 } 521 try { 522 m_file.setContents(m_page.marshal()); 523 getCms().writeFile(m_file); 524 } catch (CmsException e) { 525 // show error page 526 try { 527 showErrorPage(this, e); 528 } catch (JspException exc) { 529 // should usually never happen 530 if (LOG.isInfoEnabled()) { 531 LOG.info(exc); 532 } 533 } 534 } 535 setParamElementlanguage(defaultLocale.toString()); 536 } else { 537 // body present, get the language 538 if (locales.contains(defaultLocale)) { 539 // get the body for the default language 540 setParamElementlanguage(defaultLocale.toString()); 541 } else { 542 // get the first body that can be found 543 setParamElementlanguage(locales.get(0).toString()); 544 } 545 546 } 547 } 548 549 /** 550 * Initializes the body element name of the editor.<p> 551 * 552 * This has to be called after the element language has been set with setParamBodylanguage().<p> 553 * 554 * @param elementName the name of the element to initialize or null, if default element should be used 555 */ 556 protected void initBodyElementName(String elementName) { 557 558 if ((elementName == null) 559 || (m_page.hasValue(elementName, getElementLocale()) 560 && !m_page.isEnabled(elementName, getElementLocale()))) { 561 // elementName not specified or given element is disabled, determine default element 562 List<String> allElements = m_page.getNames(getElementLocale()); 563 int elementCount = allElements.size(); 564 List<String> elements = new ArrayList<String>(elementCount); 565 for (int i = 0; i < elementCount; i++) { 566 // filter disabled elements 567 if (m_page.isEnabled(allElements.get(i), getElementLocale())) { 568 elements.add(allElements.get(i)); 569 } 570 } 571 572 // get the active page elements 573 List<CmsDialogElement> elementList = getElementList(); 574 for (int i = 0; i < elementList.size(); i++) { 575 CmsDialogElement checkElement = elementList.get(i); 576 if (elements.contains(checkElement.getName())) { 577 // get the first active element from the element list 578 setParamElementname(checkElement.getName()); 579 return; 580 } 581 } 582 583 // no matching active element found 584 if (elements.contains(CmsDefaultPageEditor.XML_BODY_ELEMENT)) { 585 // default legacy element present, use it 586 setParamElementname(CmsDefaultPageEditor.XML_BODY_ELEMENT); 587 } else { 588 // use the first element from the element list 589 setParamElementname(elements.get(0)); 590 } 591 } else { 592 // elementName specified and element is enabled or not present, set to elementName 593 setParamElementname(elementName); 594 } 595 } 596 597 /** 598 * This method has to be called after initializing the body element name and language.<p> 599 * 600 * @see org.opencms.workplace.editors.CmsEditor#initContent() 601 */ 602 @Override 603 protected void initContent() { 604 605 if (CmsStringUtil.isNotEmpty(getParamContent())) { 606 if (CmsStringUtil.isNotEmpty(getParamElementname()) 607 && getParamElementname().equals(getParamOldelementname())) { 608 if (CmsStringUtil.isNotEmpty(getParamElementlanguage()) 609 && getParamElementlanguage().equals(getParamOldelementlanguage())) { 610 return; 611 } 612 } 613 } 614 getCms().getRequestContext().setAttribute(CmsRequestContext.ATTRIBUTE_EDITOR, Boolean.TRUE); 615 616 String elementData; 617 if (m_page.hasValue(getParamElementname(), getElementLocale())) { 618 // element value is available in the page 619 elementData = m_page.getStringValue(getCms(), getParamElementname(), getElementLocale()); 620 } else { 621 // value is not available in the page 622 if (Boolean.valueOf(getParamDirectedit()).booleanValue()) { 623 // direct edit on a non-existing element: create new element with this name 624 m_page.addValue(getParamElementname(), getElementLocale()); 625 } 626 elementData = ""; 627 } 628 setParamContent(elementData); 629 } 630 631 /** 632 * Saves the editor content to the temporary file.<p> 633 * 634 * @param body the body name to write 635 * @param locale the body locale to write 636 * @throws CmsException if writing the file fails 637 */ 638 protected void performSaveContent(String body, Locale locale) throws CmsException { 639 640 // prepare the content for saving 641 String content = prepareContent(true); 642 643 String contentConversion = m_page.getConversion(); 644 // check if cleanup was selected in the editor, we have to add the cleanup parameter 645 if (EDITOR_CLEANUP.equals(getParamAction())) { 646 if ((contentConversion == null) || (contentConversion.equals(CmsHtmlConverter.PARAM_DISABLED))) { 647 // if the current conversion mode is "false" only, we have to remove the "false" value and set it to "cleanup", as "false" will be stronger than all other values 648 contentConversion = CmsHtmlConverter.PARAM_WORD; 649 } else { 650 // add "cleanup" to the already existing values 651 contentConversion += ";" + CmsHtmlConverter.PARAM_WORD; 652 } 653 } 654 m_page.setConversion(contentConversion); 655 656 // create the element if necessary and if content is present 657 if (!m_page.hasValue(body, locale) && !"".equals(content)) { 658 m_page.addValue(body, locale); 659 } 660 661 // get the enabled state of the element 662 boolean enabled = m_page.isEnabled(body, locale); 663 664 // set the element data 665 if (m_page.hasValue(body, locale)) { 666 m_page.setStringValue(getCms(), body, locale, content); 667 } 668 669 // write the file 670 m_file.setContents(m_page.marshal()); 671 m_file = getCms().writeFile(m_file); 672 673 // content might have been modified during write operation 674 m_page = CmsXmlPageFactory.unmarshal(getCms(), m_file); 675 if (m_page.hasValue(body, locale)) { 676 getCms().getRequestContext().setAttribute(CmsRequestContext.ATTRIBUTE_EDITOR, Boolean.TRUE); 677 content = m_page.getStringValue(getCms(), body, locale); 678 if (content == null) { 679 content = ""; 680 } 681 setParamContent(content); 682 prepareContent(false); 683 m_page.setEnabled(body, locale, enabled); 684 } 685 } 686 687 /** 688 * Manipulates the content String for different editor views and the save operation.<p> 689 * 690 * @param save if set to true, the result String is not escaped and the content parameter is not updated 691 * @return the prepared content String 692 */ 693 protected abstract String prepareContent(boolean save); 694 695}