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