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.apps.resourcetypes; 029 030import org.opencms.ade.configuration.formatters.CmsFormatterConfigurationCache; 031import org.opencms.file.CmsFile; 032import org.opencms.file.CmsObject; 033import org.opencms.file.CmsProject; 034import org.opencms.file.CmsProperty; 035import org.opencms.file.CmsPropertyDefinition; 036import org.opencms.file.CmsResource; 037import org.opencms.file.CmsUser; 038import org.opencms.file.types.CmsResourceTypeFolder; 039import org.opencms.file.types.CmsResourceTypeXmlContent; 040import org.opencms.file.types.I_CmsResourceType; 041import org.opencms.i18n.CmsLocaleManager; 042import org.opencms.lock.CmsLock; 043import org.opencms.lock.CmsLockUtil; 044import org.opencms.main.CmsException; 045import org.opencms.main.CmsLog; 046import org.opencms.main.OpenCms; 047import org.opencms.module.CmsModule; 048import org.opencms.report.CmsLogReport; 049import org.opencms.ui.A_CmsUI; 050import org.opencms.ui.CmsVaadinUtils; 051import org.opencms.ui.apps.Messages; 052import org.opencms.ui.apps.modules.CmsModuleApp; 053import org.opencms.ui.apps.modules.edit.CmsEditModuleForm; 054import org.opencms.ui.components.CmsBasicDialog; 055import org.opencms.ui.components.CmsResourceInfo; 056import org.opencms.ui.components.fileselect.CmsPathSelectField; 057import org.opencms.util.CmsFileUtil; 058import org.opencms.util.CmsMacroResolver; 059import org.opencms.util.CmsStringUtil; 060import org.opencms.workplace.explorer.CmsExplorerTypeSettings; 061import org.opencms.xml.CmsXmlUtils; 062import org.opencms.xml.content.CmsVfsBundleLoaderXml; 063import org.opencms.xml.content.CmsXmlContent; 064import org.opencms.xml.content.CmsXmlContentFactory; 065import org.opencms.xml.types.I_CmsXmlContentValue; 066 067import java.io.UnsupportedEncodingException; 068import java.nio.charset.Charset; 069import java.util.ArrayList; 070import java.util.Arrays; 071import java.util.List; 072import java.util.Locale; 073import java.util.Map; 074import java.util.Map.Entry; 075import java.util.TreeMap; 076 077import org.apache.commons.logging.Log; 078 079import org.dom4j.Element; 080 081import com.google.common.collect.Lists; 082import com.vaadin.ui.Button; 083import com.vaadin.ui.Window; 084import com.vaadin.v7.data.Validator; 085import com.vaadin.v7.ui.TextArea; 086import com.vaadin.v7.ui.TextField; 087 088/** 089 * Dialog to edit or create resourcetypes.<p> 090 */ 091@SuppressWarnings("deprecation") 092public class CmsNewResourceTypeDialog extends CmsBasicDialog { 093 094 /** 095 * XPath elements. 096 */ 097 public static class XMLPath { 098 099 /**Module Config Constant. */ 100 private static final String CONFIG_RESOURCETYPE = "ResourceType"; 101 102 /**Module Config Constant. */ 103 private static final String CONFIG_RESOURCETYPE_TYPENAME = "/TypeName"; 104 105 /**Module Config Constant. */ 106 private static final String CONFIG_RESOURCETYPE_NAMEPATTERN = "/NamePattern"; 107 108 /** 109 * Adds the count to the path, e.g., <code>"Title" + num(2)</code> results in <code>"Title[2]"</code>. 110 * @param i the count to add to the XPath 111 * @return the argument wrapped in square brackets. 112 */ 113 public static String num(int i) { 114 115 return "[" + i + "]"; 116 } 117 } 118 119 /** 120 * Validator for the bundle resource field.<p> 121 */ 122 class BundleValidator implements Validator { 123 124 /**Vaadin serial id. */ 125 private static final long serialVersionUID = 7872665683495080792L; 126 127 /** 128 * @see com.vaadin.v7.data.Validator#validate(java.lang.Object) 129 */ 130 public void validate(Object value) throws InvalidValueException { 131 132 if (!m_cms.existsResource((String)value)) { 133 throw new InvalidValueException( 134 CmsVaadinUtils.getMessageText(Messages.GUI_RESOURCETYPE_EDIT_INVALID_RESORUCE_0)); 135 } 136 137 try { 138 CmsResource res = m_cms.readResource((String)value); 139 if (!OpenCms.getResourceManager().getResourceType(res).equals( 140 OpenCms.getResourceManager().getResourceType("propertyvfsbundle"))) { 141 throw new InvalidValueException( 142 CmsVaadinUtils.getMessageText(Messages.GUI_RESOURCETYPE_EDIT_INVALID_RESORUCE_NO_VFSBUNDLE_0)); 143 144 } 145 } catch (CmsException e) { 146 LOG.error("Unable to read resource", e); 147 } 148 149 } 150 151 } 152 153 /** 154 * Validator for the title field.<p> 155 */ 156 157 class IDValidator implements Validator { 158 159 /**vaadin serial id.*/ 160 private static final long serialVersionUID = 7878441125879949490L; 161 162 /** 163 * @see com.vaadin.v7.data.Validator#validate(java.lang.Object) 164 */ 165 public void validate(Object value) throws InvalidValueException { 166 167 if (((String)value).isEmpty()) { 168 throw new InvalidValueException( 169 CmsVaadinUtils.getMessageText(Messages.GUI_RESOURCETYPE_EDIT_INVALID_ID_0)); 170 } 171 int id = Integer.parseInt((String)value); 172 if (!CmsResourceTypeApp.isResourceTypeIdFree(id)) { 173 throw new InvalidValueException( 174 CmsVaadinUtils.getMessageText(Messages.GUI_RESOURCETYPE_EDIT_INVALID_ID_0)); 175 } 176 177 } 178 179 } 180 181 /** 182 * Validator for the title field.<p> 183 */ 184 class NameValidator implements Validator { 185 186 /**vaadin serial id.*/ 187 private static final long serialVersionUID = 7878441125879949490L; 188 189 /** 190 * @see com.vaadin.v7.data.Validator#validate(java.lang.Object) 191 */ 192 public void validate(Object value) throws InvalidValueException { 193 194 if (((String)value).isEmpty()) { 195 throw new InvalidValueException( 196 CmsVaadinUtils.getMessageText(Messages.GUI_RESOURCETYPE_EDIT_INVALID_NAME_0)); 197 } 198 if (!CmsResourceTypeApp.isResourceTypeNameFree((String)value)) { 199 throw new InvalidValueException( 200 CmsVaadinUtils.getMessageText(Messages.GUI_RESOURCETYPE_EDIT_INVALID_NAME_0)); 201 } 202 203 } 204 205 } 206 207 /** 208 * Validator for the title field.<p> 209 */ 210 class ResourceValidator implements Validator { 211 212 /**vaadin serial id.*/ 213 private static final long serialVersionUID = 7878441125879949490L; 214 215 /** 216 * @see com.vaadin.v7.data.Validator#validate(java.lang.Object) 217 */ 218 public void validate(Object value) throws InvalidValueException { 219 220 if ((value == null) || ((String)value).isEmpty()) { 221 return; 222 } 223 224 String resource = (String)value; 225 if (!resource.startsWith("/system/")) { 226 throw new InvalidValueException( 227 CmsVaadinUtils.getMessageText(Messages.GUI_RESOURCETYPE_EDIT_INVALID_RESORUCE_0)); 228 } 229 if (CmsResource.getName(resource).equals(SCHEMA) || CmsResource.getName(resource).equals(FORMATTER)) { 230 if (!m_cms.existsResource(CmsResource.getParentFolder(resource))) { 231 throw new InvalidValueException( 232 CmsVaadinUtils.getMessageText(Messages.GUI_RESOURCETYPE_EDIT_INVALID_RESORUCE_0)); 233 } 234 return; 235 } 236 if (!m_cms.existsResource((String)value)) { 237 throw new InvalidValueException( 238 CmsVaadinUtils.getMessageText(Messages.GUI_RESOURCETYPE_EDIT_INVALID_RESORUCE_0)); 239 } 240 241 } 242 243 } 244 245 /**Serial id.*/ 246 private static final long serialVersionUID = 8775619886579477116L; 247 248 /** Message bundle folder name. */ 249 protected static final String PATH_I18N = "i18n"; 250 251 /** Formatter folder name.*/ 252 private static final String FORMATTER = "formatters/"; 253 254 /**Message key schema. */ 255 private static final String MESSAGE_KEY_FORMATTER = "type.%s.%s"; 256 257 /**key: name.*/ 258 private static final String MESSAGE_KEY_FORMATTER_NAME = "name"; 259 260 /**key: description. */ 261 private static final String MESSAGE_KEY_FORMATTER_DESCRIPTION = "description"; 262 263 /**key: title. */ 264 private static final String MESSAGE_KEY_FORMATTER_TITLE = "title"; 265 266 /**key: formatter. */ 267 private static final String MESSAGE_KEY_FORMATTER_FORMATTER = "formatter"; 268 269 /** Logger instance for this class. */ 270 protected static final Log LOG = CmsLog.getLog(CmsNewResourceTypeDialog.class); 271 272 /**Sample path. */ 273 private static final String PATH_SAMPLE = "/system/modules/org.opencms.base/copyresources/"; 274 275 /** Message properties file encoding. */ 276 private static final String PROPERTIES_ENCODING = "ISO-8859-1"; 277 278 /** Sample formatter. */ 279 private static final String SAMPLE_FORMATTER = PATH_SAMPLE + "sample-formatter.jsp"; 280 281 /** Message bundle file name suffix. */ 282 private static final String SUFFIX_BUNDLE_FILE = ".messages"; 283 284 /** Sample schema. */ 285 private static final String SAMPLE_SCHEMA = PATH_SAMPLE + "sample-schema.xsd"; 286 287 /**Sample schema element type. */ 288 private static final String SAMPLE_TYPE_SCHEMA_ELEMENT = "SampleType"; 289 290 /** Schema folder name.*/ 291 private static final String SCHEMA = "schemas/"; 292 293 /** Default small icon.*/ 294 static final String ICON_SMALL_DEFAULT = "oc-icon-16-default"; 295 296 /** Default big icon.*/ 297 static final String ICON_BIG_DEFAULT = "oc-icon-24-default"; 298 299 /**CmsObject. */ 300 CmsObject m_cms; 301 302 /** vaadin component.*/ 303 private Button m_cancel; 304 305 /** vaadin component.*/ 306 private Button m_ok; 307 308 /**vaadin component. */ 309 private CmsPathSelectField m_parentFormatter; 310 311 /** vaadin component.*/ 312 private CmsPathSelectField m_parentSchema; 313 314 /** vaadin component.*/ 315 private TextArea m_typeDescription; 316 317 /** vaadin component.*/ 318 private TextField m_typeID; 319 320 /** vaadin component.*/ 321 private TextField m_typeName; 322 323 /** vaadin component.*/ 324 private TextField m_typeShortName; 325 326 /** vaadin component.*/ 327 private TextField m_typeXPathName; 328 329 /** Module of the new resource type.*/ 330 private CmsModule m_module; 331 332 /** Vaadin component.*/ 333 private CmsPathSelectField m_bundle; 334 335 /** Vaadin component.*/ 336 private CmsPathSelectField m_config; 337 338 /** 339 * Public cosntructor.<p> 340 * 341 * @param window window 342 * @param app app 343 */ 344 public CmsNewResourceTypeDialog(final Window window, CmsResourceTypeApp app) { 345 346 init(window, app); 347 } 348 349 /** 350 * Get parent folder of messages.<p> 351 * 352 * @param moduleName name of module 353 * @return path name 354 */ 355 protected static String getMessageParentFolder(String moduleName) { 356 357 CmsModule module = OpenCms.getModuleManager().getModule(moduleName); 358 for (String resource : module.getResources()) { 359 if (resource.contains(PATH_I18N + "/" + module.getName())) { 360 return resource.substring(0, resource.indexOf(PATH_I18N)); 361 } 362 } 363 return "/system/modules/" + module.getName(); 364 } 365 366 /** 367 * Tries to find the module folder under /system/modules for a given module.<p> 368 * 369 * @param moduleName the name of the module 370 * 371 * @return the module folder, or null if this module doesn't have one 372 */ 373 private static String getModuleFolder(String moduleName) { 374 375 if (moduleName == null) { 376 return null; 377 } 378 CmsModule module = OpenCms.getModuleManager().getModule(moduleName); 379 if (module != null) { 380 for (String resource : module.getResources()) { 381 if (CmsStringUtil.comparePaths("/system/modules/" + moduleName, resource)) { 382 return resource; 383 } 384 } 385 } 386 return null; 387 } 388 389 /** 390 * Creates the parents of nested XML elements if necessary. 391 * @param xmlContent the XML content that is edited. 392 * @param xmlPath the path of the (nested) element, for which the parents should be created 393 * @param l the locale for which the XML content is edited. 394 */ 395 protected void createParentXmlElements(CmsXmlContent xmlContent, String xmlPath, Locale l) { 396 397 if (CmsXmlUtils.isDeepXpath(xmlPath)) { 398 String parentPath = CmsXmlUtils.removeLastXpathElement(xmlPath); 399 if (null == xmlContent.getValue(parentPath, l)) { 400 createParentXmlElements(xmlContent, parentPath, l); 401 xmlContent.addValue(m_cms, parentPath, l, CmsXmlUtils.getXpathIndexInt(parentPath) - 1); 402 } 403 } 404 405 } 406 407 /** 408 * Opens the module select dialog.<p> 409 * 410 * @param window window 411 */ 412 protected void openModuleSelect(Window window) { 413 414 window.setContent(new CmsMoveResourceTypeDialog(this)); 415 window.center(); 416 } 417 418 /** 419 * Sets the module name.<p> 420 * 421 * @param moduleName to be set 422 * @param dialog dialog 423 */ 424 protected void setModule(String moduleName, CmsBasicDialog dialog) { 425 426 Window window = CmsVaadinUtils.getWindow(dialog); 427 window.setContent(this); 428 m_module = OpenCms.getModuleManager().getModule(moduleName).clone(); 429 CmsResourceInfo resInfo = new CmsResourceInfo( 430 m_module.getName(), 431 m_module.getNiceName(), 432 CmsModuleApp.Icons.RESINFO_ICON); 433 displayResourceInfoDirectly(Arrays.asList(resInfo)); 434 fillFields(); 435 } 436 437 /** 438 * Submit the entered data.<p> 439 * 440 * @param window window 441 * @param app app 442 */ 443 protected void submit(Window window, CmsResourceTypeApp app) { 444 445 if (isValid()) { 446 createResourceType(); 447 try { 448 OpenCms.getModuleManager().updateModule(m_cms, m_module); 449 OpenCms.getResourceManager().initialize(m_cms); 450 OpenCms.getWorkplaceManager().addExplorerTypeSettings(m_module); 451 // re-initialize the workplace 452 OpenCms.getWorkplaceManager().initialize(m_cms); 453 } catch (CmsException e) { 454 LOG.error("Unable to save resource type", e); 455 } 456 window.close(); 457 app.reload(); 458 } 459 } 460 461 /** 462 * Adds the given messages to the workplace properties file.<p> 463 * 464 * @param messages the messages 465 * @param propertiesFile the properties file 466 * @param forcePropertyFileEncoding flag, indicating if encoding {@link #PROPERTIES_ENCODING} should be forced 467 * 468 * @throws CmsException if writing the properties fails 469 * @throws UnsupportedEncodingException in case of encoding issues 470 */ 471 private void addMessagesToPropertiesFile( 472 Map<String, String> messages, 473 CmsFile propertiesFile, 474 boolean forcePropertyFileEncoding) 475 throws CmsException, UnsupportedEncodingException { 476 477 lockTemporary(propertiesFile); 478 String encoding = forcePropertyFileEncoding 479 ? PROPERTIES_ENCODING 480 : CmsFileUtil.getEncoding(m_cms, propertiesFile); 481 StringBuffer contentBuffer = new StringBuffer(); 482 contentBuffer.append(new String(propertiesFile.getContents(), encoding).trim()); 483 for (Entry<String, String> entry : messages.entrySet()) { 484 contentBuffer.append("\n"); 485 contentBuffer.append(entry.getKey()); 486 contentBuffer.append("="); 487 contentBuffer.append(entry.getValue()); 488 } 489 contentBuffer.append("\n"); 490 propertiesFile.setContents(contentBuffer.toString().getBytes(encoding)); 491 m_cms.writeFile(propertiesFile); 492 } 493 494 /** 495 * Adds the given messages to the vfs message bundle.<p> 496 * 497 * @param messages the messages 498 * @param vfsBundleFile the bundle file 499 * 500 * @throws CmsException if something goes wrong writing the file 501 */ 502 private void addMessagesToVfsBundle(Map<String, String> messages, CmsFile vfsBundleFile) throws CmsException { 503 504 lockTemporary(vfsBundleFile); 505 CmsObject cms = m_cms; 506 CmsXmlContent content = CmsXmlContentFactory.unmarshal(cms, vfsBundleFile); 507 Locale locale = CmsLocaleManager.getDefaultLocale(); 508 if (!content.hasLocale(locale)) { 509 content.addLocale(cms, locale); 510 } 511 Element root = content.getLocaleNode(locale); 512 for (Entry<String, String> entry : messages.entrySet()) { 513 Element message = root.addElement(CmsVfsBundleLoaderXml.N_MESSAGE); 514 Element key = message.addElement(CmsVfsBundleLoaderXml.N_KEY); 515 key.setText(entry.getKey()); 516 Element value = message.addElement(CmsVfsBundleLoaderXml.N_VALUE); 517 value.setText(entry.getValue()); 518 } 519 content.initDocument(); 520 vfsBundleFile.setContents(content.marshal()); 521 cms.writeFile(vfsBundleFile); 522 } 523 524 /** 525 * Adds the given resource to the module if necessary.<p> 526 * 527 * @param resourcePath to be added 528 * @param module module 529 */ 530 private void addResourceToModule(String resourcePath, CmsModule module) { 531 532 List<String> currentRessources = Lists.newArrayList(module.getResources()); 533 for (String resource : currentRessources) { 534 if (resourcePath.startsWith(resource)) { 535 return; 536 } 537 } 538 currentRessources.add(resourcePath); 539 module.setResources(currentRessources); 540 } 541 542 /** 543 * Adds the explorer type messages to the modules workplace bundle.<p> 544 * 545 * @param setting the explorer type settings 546 * @param moduleFolder the module folder name 547 * 548 * @throws CmsException if writing the bundle fails 549 * @throws UnsupportedEncodingException in case of encoding issues 550 */ 551 private void addTypeMessages(CmsExplorerTypeSettings setting, String moduleFolder) 552 throws CmsException, UnsupportedEncodingException { 553 554 Map<String, String> messages = new TreeMap<String, String>(); 555 556 // check if any messages to set 557 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_typeName.getValue())) { 558 String key = String.format(MESSAGE_KEY_FORMATTER, m_typeShortName.getValue(), MESSAGE_KEY_FORMATTER_NAME); 559 messages.put(key, m_typeName.getValue()); 560 setting.setKey(key); 561 } 562 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_typeDescription.getValue())) { 563 String key = String.format( 564 MESSAGE_KEY_FORMATTER, 565 m_typeShortName.getValue(), 566 MESSAGE_KEY_FORMATTER_DESCRIPTION); 567 messages.put(key, m_typeDescription.getValue()); 568 setting.setInfo(key); 569 } 570 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_typeName.getValue())) { 571 String key = String.format(MESSAGE_KEY_FORMATTER, m_typeShortName.getValue(), MESSAGE_KEY_FORMATTER_TITLE); 572 messages.put(key, m_typeName.getValue()); 573 String key2 = String.format( 574 MESSAGE_KEY_FORMATTER, 575 m_typeShortName.getValue(), 576 MESSAGE_KEY_FORMATTER_FORMATTER); 577 messages.put(key2, m_typeName.getValue()); 578 setting.setTitleKey(key); 579 } 580 581 if (!messages.isEmpty()) { 582 addMessagesToPropertiesFile(messages, m_cms.readFile(m_bundle.getValue()), false); 583 } 584 } 585 586 /** 587 * 588 * Adjustes formatter config.<p> 589 * 590 * @param formatterPath path of formatter 591 * @param formatterConfigPath config path 592 * @throws CmsException exception 593 */ 594 595 private void adjustFormatterConfig(String formatterPath, String formatterConfigPath) throws CmsException { 596 597 CmsResource config = m_cms.readResource(formatterConfigPath); 598 599 CmsFile file = m_cms.readFile(config); 600 601 CmsXmlContent xmlContent = CmsXmlContentFactory.unmarshal(m_cms, file); 602 Locale l = new Locale("en"); 603 if (!xmlContent.hasLocale(l)) { 604 xmlContent.addLocale(m_cms, l); 605 } 606 CmsResource formatter = m_cms.readResource(formatterPath); 607 608 I_CmsXmlContentValue v = xmlContent.getValue("Jsp", l); 609 v.setStringValue(m_cms, formatter.getRootPath()); 610 611 String xmlPath = "NiceName"; 612 v = xmlContent.getValue(xmlPath, l); 613 v.setStringValue(m_cms, m_typeName.getValue()); 614 615 xmlPath = "Type"; 616 v = xmlContent.getValue(xmlPath, l); 617 v.setStringValue(m_cms, m_typeShortName.getValue()); 618 619 xmlPath = "AutoEnabled"; 620 v = xmlContent.getValue(xmlPath, l); 621 v.setStringValue(m_cms, "true"); 622 623 xmlPath = "Match/Width/Width"; 624 createParentXmlElements(xmlContent, xmlPath, l); 625 v = xmlContent.getValue(xmlPath, l); 626 v.setStringValue(m_cms, "-1"); 627 628 file.setContents(xmlContent.marshal()); 629 CmsLockUtil.ensureLock(m_cms, file); 630 m_cms.writeFile(file); 631 CmsLockUtil.tryUnlock(m_cms, file); 632 633 } 634 635 /** 636 * Adjustes module config.<p> 637 */ 638 private void adjustModuleConfig() { 639 640 Locale l = CmsLocaleManager.getLocale("en"); 641 try { 642 CmsResource config = m_cms.readResource(m_config.getValue()); 643 CmsFile configFile = m_cms.readFile(config); 644 CmsXmlContent xmlContent = CmsXmlContentFactory.unmarshal(m_cms, configFile); 645 int number = xmlContent.getIndexCount(XMLPath.CONFIG_RESOURCETYPE, l) + 1; 646 createParentXmlElements( 647 xmlContent, 648 XMLPath.CONFIG_RESOURCETYPE + XMLPath.num(number) + XMLPath.CONFIG_RESOURCETYPE_TYPENAME, 649 l); 650 I_CmsXmlContentValue v = xmlContent.getValue( 651 XMLPath.CONFIG_RESOURCETYPE + XMLPath.num(number) + XMLPath.CONFIG_RESOURCETYPE_TYPENAME, 652 l); 653 v.setStringValue(m_cms, m_typeShortName.getValue()); 654 v = xmlContent.addValue( 655 m_cms, 656 XMLPath.CONFIG_RESOURCETYPE + XMLPath.num(number) + XMLPath.CONFIG_RESOURCETYPE_NAMEPATTERN, 657 l, 658 CmsXmlUtils.getXpathIndexInt( 659 XMLPath.CONFIG_RESOURCETYPE + XMLPath.num(number) + XMLPath.CONFIG_RESOURCETYPE_NAMEPATTERN) - 1); 660 v.setStringValue(m_cms, getNamePattern()); 661 configFile.setContents(xmlContent.marshal()); 662 663 CmsLockUtil.ensureLock(m_cms, configFile); 664 m_cms.writeFile(configFile); 665 CmsLockUtil.tryUnlock(m_cms, configFile); 666 } catch (CmsException e) { 667 LOG.error("Can't read module config resource", e); 668 } 669 } 670 671 /** 672 * Adjustes schema.<p> 673 * 674 * @param schemaPath path to schema resource 675 * @param newElementString new Element name 676 */ 677 private void adjustSchema(String schemaPath, String newElementString) { 678 679 newElementString = newElementString.substring(0, 1).toUpperCase() + newElementString.substring(1); 680 try { 681 CmsFile file = m_cms.readFile(schemaPath); 682 683 CmsMacroResolver macroResolver = new CmsMacroResolver(); 684 macroResolver.setKeepEmptyMacros(true); 685 686 macroResolver.addMacro(SAMPLE_TYPE_SCHEMA_ELEMENT, newElementString); 687 String bundleName = m_bundle.getValue(); 688 689 bundleName = bundleName.split("/")[bundleName.split("/").length - 1]; 690 if (bundleName.contains("_")) { 691 bundleName = bundleName.split("_")[0]; 692 } 693 macroResolver.addMacro("ResourceBundle", bundleName); 694 macroResolver.addMacro("typeName", m_typeShortName.getValue()); 695 String encoding = m_cms.readPropertyObject( 696 file, 697 CmsPropertyDefinition.PROPERTY_CONTENT_ENCODING, 698 true).getValue(OpenCms.getSystemInfo().getDefaultEncoding()); 699 String newContent = macroResolver.resolveMacros(new String(file.getContents(), encoding)); 700 // update the content 701 try { 702 file.setContents(newContent.getBytes(encoding)); 703 } catch (UnsupportedEncodingException e) { 704 try { 705 file.setContents(newContent.getBytes(Charset.defaultCharset().toString())); 706 } catch (UnsupportedEncodingException e1) { 707 file.setContents(newContent.getBytes()); 708 } 709 } 710 // write the target file 711 CmsLockUtil.ensureLock(m_cms, file); 712 m_cms.writeFile(file); 713 CmsLockUtil.tryUnlock(m_cms, file); 714 } catch ( 715 716 CmsException e) { 717 LOG.error("Unable to read schema definition", e); 718 } catch (UnsupportedEncodingException e) { 719 LOG.error("Unable to fetch encoding", e); 720 } 721 } 722 723 /** 724 * Creates the new resource type.<p> 725 */ 726 private void createResourceType() { 727 728 CmsProject currentProject = m_cms.getRequestContext().getCurrentProject(); 729 try { 730 731 CmsProject workProject = m_cms.createProject( 732 "Add_resource_type_project", 733 "Add resource type project", 734 OpenCms.getDefaultUsers().getGroupAdministrators(), 735 OpenCms.getDefaultUsers().getGroupAdministrators(), 736 CmsProject.PROJECT_TYPE_TEMPORARY); 737 m_cms.getRequestContext().setCurrentProject(workProject); 738 739 if (!m_cms.existsResource(m_parentSchema.getValue())) { 740 741 I_CmsResourceType type = OpenCms.getResourceManager().getResourceType( 742 CmsResourceTypeFolder.RESOURCE_TYPE_NAME); 743 CmsResource res = m_cms.createResource(m_parentSchema.getValue(), type); 744 m_cms.unlockResource(res); 745 746 } 747 if (!m_cms.existsResource(m_parentFormatter.getValue())) { 748 749 I_CmsResourceType type = OpenCms.getResourceManager().getResourceType( 750 CmsResourceTypeFolder.RESOURCE_TYPE_NAME); 751 CmsResource res = m_cms.createResource(m_parentFormatter.getValue(), type); 752 m_cms.unlockResource(res); 753 754 } 755 756 String formatterPath = m_parentFormatter.getValue() + m_typeShortName.getValue() + ".jsp"; 757 String formatterConfigPath = m_parentFormatter.getValue() + m_typeShortName.getValue() + ".xml"; 758 String schemaPath = m_parentSchema.getValue() + m_typeShortName.getValue() + ".xsd"; 759 if (!m_cms.existsResource(formatterPath)) { 760 m_cms.copyResource(SAMPLE_FORMATTER, formatterPath); 761 } 762 if (!m_cms.existsResource(schemaPath)) { 763 m_cms.copyResource(SAMPLE_SCHEMA, schemaPath); 764 } 765 766 if (!m_cms.existsResource(formatterConfigPath)) { 767 I_CmsResourceType configType = OpenCms.getResourceManager().getResourceType( 768 CmsFormatterConfigurationCache.TYPE_FORMATTER_CONFIG); 769 List<CmsProperty> props = new ArrayList<CmsProperty>(); 770 props.add(new CmsProperty(CmsPropertyDefinition.PROPERTY_LOCALE, "en", null)); 771 props.add(new CmsProperty(CmsPropertyDefinition.PROPERTY_AVAILABLE_LOCALES, "en", null)); 772 773 m_cms.createResource(formatterConfigPath, configType, null, props); 774 } 775 CmsLockUtil.tryUnlock(m_cms, m_cms.readResource(formatterPath)); 776 CmsLockUtil.tryUnlock(m_cms, m_cms.readResource(formatterConfigPath)); 777 CmsLockUtil.tryUnlock(m_cms, m_cms.readResource(schemaPath)); 778 addResourceToModule(formatterPath, m_module); 779 addResourceToModule(formatterConfigPath, m_module); 780 addResourceToModule(schemaPath, m_module); 781 adjustFormatterConfig(formatterPath, formatterConfigPath); 782 adjustSchema(schemaPath, m_typeXPathName.getValue()); 783 adjustModuleConfig(); 784 785 List<I_CmsResourceType> types = new ArrayList<I_CmsResourceType>(m_module.getResourceTypes()); 786 // create the new resource type 787 CmsResourceTypeXmlContent type = new CmsResourceTypeXmlContent(); 788 type.addConfigurationParameter(CmsResourceTypeXmlContent.CONFIGURATION_SCHEMA, schemaPath); 789 type.setAdditionalModuleResourceType(true); 790 type.setModuleName(m_module.getName()); 791 type.initConfiguration( 792 m_typeShortName.getValue(), 793 m_typeID.getValue(), 794 CmsResourceTypeXmlContent.class.getName()); 795 types.add(type); 796 m_module.setResourceTypes(types); 797 798 List<CmsExplorerTypeSettings> settings = new ArrayList<CmsExplorerTypeSettings>( 799 m_module.getExplorerTypes()); 800 // create the matching explorer type 801 CmsExplorerTypeSettings setting = new CmsExplorerTypeSettings(); 802 setting.setTypeAttributes( 803 m_typeShortName.getValue(), 804 m_typeName.getValue(), //ToDo nicename 805 null, 806 null, 807 ICON_SMALL_DEFAULT, 808 ICON_BIG_DEFAULT, 809 CmsResourceTypeXmlContent.getStaticTypeName(), 810 null, 811 "false", 812 null, 813 null); 814 setting.setAutoSetNavigation("false"); 815 setting.setAutoSetTitle("false"); 816 setting.setNewResourceOrder("10"); 817 setting.setAddititionalModuleExplorerType(true); 818 addTypeMessages(setting, getMessageParentFolder(m_module.getName())); 819 settings.add(setting); 820 m_module.setExplorerTypes(settings); 821 m_cms.unlockProject(workProject.getUuid()); 822 OpenCms.getPublishManager().publishProject( 823 m_cms, 824 new CmsLogReport(m_cms.getRequestContext().getLocale(), getClass())); 825 OpenCms.getPublishManager().waitWhileRunning(); 826 } catch (Exception e) { 827 LOG.error("Unable to create resource type", e); 828 } finally { 829 m_cms.getRequestContext().setCurrentProject(currentProject); 830 } 831 } 832 833 /** 834 * Fills the fields.<p> 835 */ 836 private void fillFields() { 837 838 String folderName = getModuleFolder(m_module.getName()); 839 if (folderName != null) { 840 if (CmsStringUtil.isEmptyOrWhitespaceOnly(m_parentFormatter.getValue())) { 841 m_parentFormatter.setValue(folderName + FORMATTER); 842 } 843 if (CmsStringUtil.isEmptyOrWhitespaceOnly(m_parentSchema.getValue())) { 844 m_parentSchema.setValue(folderName + SCHEMA); 845 } 846 } 847 if (m_cms.existsResource(folderName + CmsEditModuleForm.CONFIG_FILE)) { 848 m_config.setValue(folderName + CmsEditModuleForm.CONFIG_FILE); 849 } 850 m_parentFormatter.removeAllValidators(); 851 m_parentSchema.removeAllValidators(); 852 m_config.removeAllValidators(); 853 m_config.addValidator(new ResourceValidator()); 854 m_bundle.addValidator(new BundleValidator()); 855 m_parentFormatter.addValidator(new ResourceValidator()); 856 m_parentSchema.addValidator(new ResourceValidator()); 857 CmsResource bundle = getMessageBundle(); 858 if (bundle != null) { 859 m_bundle.setValue(bundle.getRootPath()); 860 861 } 862 } 863 864 /** 865 * Returns the property string for all available locales.<p> 866 * 867 * @return String to use in properties 868 */ 869 private String getAvailableLocalString() { 870 871 String res = ""; 872 873 for (Locale locale : OpenCms.getLocaleManager().getAvailableLocales()) { 874 res += locale.toString() + ","; 875 } 876 if (res.endsWith(",")) { 877 res = res.substring(0, res.length() - 1); 878 } 879 return res; 880 } 881 882 /** 883 * Returns a random valid resource type id.<p> 884 * 885 * @return valid id 886 */ 887 private int getFreeId() { 888 889 int tryID = (int)(10000 + (Math.random() * 90000)); 890 while (!CmsResourceTypeApp.isResourceTypeIdFree(tryID)) { 891 tryID = (int)(10000 + (Math.random() * 90000)); 892 } 893 return tryID; 894 } 895 896 /** 897 * Gets the message bundle.<p> 898 * @return Message bundle resource 899 */ 900 private CmsResource getMessageBundle() { 901 902 OpenCms.getLocaleManager(); 903 String localString = CmsLocaleManager.getDefaultLocale().toString(); 904 List<String> moduleResource = m_module.getResources(); 905 for (String resourcePath : moduleResource) { 906 if (resourcePath.contains(PATH_I18N) && resourcePath.endsWith(localString)) { 907 try { 908 return m_cms.readResource(resourcePath); 909 } catch (CmsException e) { 910 LOG.error("Can not read message bundle", e); 911 } 912 } 913 } 914 String moduleFolder = getModuleFolder(m_module.getName()); 915 if (CmsStringUtil.isEmptyOrWhitespaceOnly(moduleFolder)) { 916 return null; 917 } 918 String bundlePath = CmsStringUtil.joinPaths( 919 moduleFolder, 920 PATH_I18N, 921 m_module.getName() + SUFFIX_BUNDLE_FILE + "_" + localString); 922 if (m_cms.existsResource(bundlePath)) { 923 try { 924 return m_cms.readResource(bundlePath); 925 } catch (CmsException e) { 926 LOG.error("No bundle found for module", e); 927 } 928 } 929 return null; 930 } 931 932 /** 933 * Creates a name pattern for the resource type.<p> 934 * 935 * @return String name-pattern 936 */ 937 private String getNamePattern() { 938 939 String niceName = m_typeShortName.getValue(); 940 if (m_typeShortName.getValue().contains("-")) { 941 int maxLength = 0; 942 String[] nameParts = niceName.split("-"); 943 for (int i = 0; i < nameParts.length; i++) { 944 if (nameParts[i].length() > maxLength) { 945 maxLength = nameParts[i].length(); 946 niceName = nameParts[i]; 947 } 948 } 949 } 950 return niceName + "_%(number).xml"; 951 } 952 953 /** 954 * Initializes the form.<p> 955 * 956 * @param window Window 957 * @param app app 958 */ 959 private void init(Window window, CmsResourceTypeApp app) { 960 961 CmsObject rootCms = null; 962 try { 963 m_cms = OpenCms.initCmsObject(A_CmsUI.getCmsObject()); 964 rootCms = OpenCms.initCmsObject(m_cms); 965 rootCms.getRequestContext().setSiteRoot(""); 966 } catch (CmsException e1) { 967 m_cms = A_CmsUI.getCmsObject(); 968 rootCms = m_cms; 969 } 970 CmsVaadinUtils.readAndLocalizeDesign(this, CmsVaadinUtils.getWpMessagesForCurrentLocale(), null); 971 972 m_typeID.addValidator(new IDValidator()); 973 m_typeShortName.addValidator(new NameValidator()); 974 975 m_parentSchema.setCmsObject(rootCms); 976 m_parentFormatter.setCmsObject(rootCms); 977 m_bundle.setCmsObject(rootCms); 978 m_config.setCmsObject(rootCms); 979 980 m_parentSchema.setRequired(true); 981 m_parentFormatter.setRequired(true); 982 m_typeID.setRequired(true); 983 m_typeDescription.setRequired(true); 984 m_typeShortName.setRequired(true); 985 m_typeXPathName.setRequired(true); 986 m_bundle.setRequired(true); 987 988 m_typeName.setRequired(true); 989 990 m_parentSchema.setRequiredError(CmsVaadinUtils.getMessageText(Messages.GUI_RESOURCETYPE_EDIT_NOT_EMPTY_0)); 991 m_parentFormatter.setRequiredError(CmsVaadinUtils.getMessageText(Messages.GUI_RESOURCETYPE_EDIT_NOT_EMPTY_0)); 992 m_typeID.setRequiredError(CmsVaadinUtils.getMessageText(Messages.GUI_RESOURCETYPE_EDIT_NOT_EMPTY_0)); 993 m_typeDescription.setRequiredError(CmsVaadinUtils.getMessageText(Messages.GUI_RESOURCETYPE_EDIT_NOT_EMPTY_0)); 994 m_typeShortName.setRequiredError(CmsVaadinUtils.getMessageText(Messages.GUI_RESOURCETYPE_EDIT_NOT_EMPTY_0)); 995 m_typeXPathName.setRequiredError(CmsVaadinUtils.getMessageText(Messages.GUI_RESOURCETYPE_EDIT_NOT_EMPTY_0)); 996 m_bundle.setRequiredError(CmsVaadinUtils.getMessageText(Messages.GUI_RESOURCETYPE_EDIT_NOT_EMPTY_0)); 997 998 m_typeName.setRequiredError(CmsVaadinUtils.getMessageText(Messages.GUI_RESOURCETYPE_EDIT_NOT_EMPTY_0)); 999 1000 m_typeID.setValue(String.valueOf(getFreeId())); 1001 m_ok.addClickListener(e -> submit(window, app)); 1002 m_cancel.addClickListener(e -> window.close()); 1003 1004 } 1005 1006 /** 1007 * Checks if form is valid.<p> 1008 * 1009 * @return true if form is valid 1010 */ 1011 private boolean isValid() { 1012 1013 return m_typeID.isValid() 1014 && m_typeShortName.isValid() 1015 && m_parentFormatter.isValid() 1016 && m_parentSchema.isValid() 1017 && m_bundle.isValid() 1018 && m_config.isValid() 1019 && m_typeDescription.isValid() 1020 && m_typeName.isValid() 1021 && m_typeXPathName.isValid(); 1022 } 1023 1024 /** 1025 * Locks the given resource temporarily.<p> 1026 * 1027 * @param resource the resource to lock 1028 * 1029 * @throws CmsException if locking fails 1030 */ 1031 private void lockTemporary(CmsResource resource) throws CmsException { 1032 1033 CmsUser user = m_cms.getRequestContext().getCurrentUser(); 1034 CmsLock lock = m_cms.getLock(resource); 1035 if (!lock.isOwnedBy(user)) { 1036 m_cms.lockResourceTemporary(resource); 1037 } else if (!lock.isOwnedInProjectBy(user, m_cms.getRequestContext().getCurrentProject())) { 1038 m_cms.changeLock(resource); 1039 } 1040 } 1041}