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}