001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software GmbH & Co. KG, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.workplace.tools;
029
030import org.opencms.file.CmsObject;
031import org.opencms.file.CmsProperty;
032import org.opencms.file.CmsPropertyDefinition;
033import org.opencms.file.CmsResource;
034import org.opencms.i18n.CmsEncoder;
035import org.opencms.i18n.CmsMessages;
036import org.opencms.jsp.CmsJspNavBuilder;
037import org.opencms.jsp.CmsJspNavElement;
038import org.opencms.main.CmsException;
039import org.opencms.main.CmsLog;
040import org.opencms.util.CmsStringUtil;
041import org.opencms.workplace.CmsWorkplace;
042
043import java.util.HashMap;
044import java.util.Iterator;
045import java.util.Map;
046
047import org.apache.commons.logging.Log;
048
049/**
050 * Helper class to build easily other admin tool handlers.<p>
051 *
052 * @since 6.0.0
053 */
054public abstract class A_CmsToolHandler implements I_CmsToolHandler {
055
056    /** Property for the parameters argument.<p> */
057    public static final String ARG_PARAM_NAME = "params";
058
059    /** Property for the path argument.<p> */
060    public static final String ARG_PATH_NAME = "path";
061
062    /** Property definition for the arguments.<p> */
063    public static final String ARGS_PROPERTY_DEFINITION = "admintoolhandler-args";
064
065    /** Argument separator.<p> */
066    public static final String ARGUMENT_SEPARATOR = "|";
067
068    /** Default disabled help text constant.<p> */
069    public static final String DEFAULT_DISABLED_HELPTEXT = "${key." + Messages.GUI_TOOLS_DISABLED_HELP_0 + "}";
070
071    /** Argument name and value separator.<p> */
072    public static final String VALUE_SEPARATOR = ":";
073
074    /** Property for the confirmation message argument.<p> */
075    private static final String ARG_CONFIRMATION_NAME = "confirmation";
076
077    /** The static log object for this class. */
078    private static final Log LOG = CmsLog.getLog(A_CmsToolHandler.class);
079
080    /** Confirmation message. */
081    private String m_confirmationMessage;
082
083    /** Help text or description if disabled. */
084    private String m_disabledHelpText;
085
086    /** Group to be included in. */
087    private String m_group;
088
089    /** Help text or description. */
090    private String m_helpText;
091
092    /** Icon path (32x32). */
093    private String m_iconPath;
094
095    /** Link pointer. */
096    private String m_link;
097
098    /** Display name. */
099    private String m_name;
100
101    /** Needed parameters. */
102    private String m_parameters;
103
104    /** Tool path to install in. */
105    private String m_path;
106
107    /** Relative position in group. */
108    private float m_position;
109
110    /** Menu item name. */
111    private String m_shortName;
112
113    /** Small icon path (16x16). */
114    private String m_smallIconPath;
115
116    /**
117     * Returns the confirmation Message.<p>
118     *
119     * @return the confirmation Message
120     */
121    public String getConfirmationMessage() {
122
123        return m_confirmationMessage;
124    }
125
126    /**
127     * @see org.opencms.workplace.tools.I_CmsToolHandler#getDisabledHelpText()
128     */
129    public String getDisabledHelpText() {
130
131        return m_disabledHelpText;
132    }
133
134    /**
135     * @see org.opencms.workplace.tools.I_CmsToolHandler#getGroup()
136     */
137    public String getGroup() {
138
139        return m_group;
140    }
141
142    /**
143     * @see org.opencms.workplace.tools.I_CmsToolHandler#getHelpText()
144     */
145    public String getHelpText() {
146
147        return m_helpText;
148    }
149
150    /**
151     * @see org.opencms.workplace.tools.I_CmsToolHandler#getIconPath()
152     */
153    public String getIconPath() {
154
155        return m_iconPath;
156    }
157
158    /**
159     * @see org.opencms.workplace.tools.I_CmsToolHandler#getLink()
160     */
161    public String getLink() {
162
163        return m_link;
164    }
165
166    /**
167     * @see org.opencms.workplace.tools.I_CmsToolHandler#getName()
168     */
169    public String getName() {
170
171        return m_name;
172    }
173
174    /**
175     * @see org.opencms.workplace.tools.I_CmsToolHandler#getParameters(org.opencms.workplace.CmsWorkplace)
176     */
177    public Map<String, String[]> getParameters(CmsWorkplace wp) {
178
179        Map<String, String[]> argMap = new HashMap<String, String[]>();
180        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_parameters)) {
181            String toolParams = CmsEncoder.decode(wp.resolveMacros(m_parameters));
182            Iterator<String> itArgs = CmsStringUtil.splitAsList(toolParams, "&").iterator();
183            while (itArgs.hasNext()) {
184                String arg = itArgs.next();
185                int pos = arg.indexOf("=");
186                if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(arg.substring(pos + 1))) {
187                    argMap.put(arg.substring(0, pos), new String[] {arg.substring(pos + 1)});
188                }
189            }
190        }
191        return argMap;
192    }
193
194    /**
195     * @see org.opencms.workplace.tools.I_CmsToolHandler#getPath()
196     */
197    public String getPath() {
198
199        return m_path;
200    }
201
202    /**
203     * @see org.opencms.workplace.tools.I_CmsToolHandler#getPosition()
204     */
205    public float getPosition() {
206
207        return m_position;
208    }
209
210    /**
211     * @see org.opencms.workplace.tools.I_CmsToolHandler#getShortName()
212     */
213    public String getShortName() {
214
215        return m_shortName;
216    }
217
218    /**
219     * @see org.opencms.workplace.tools.I_CmsToolHandler#getSmallIconPath()
220     */
221    public String getSmallIconPath() {
222
223        return m_smallIconPath;
224    }
225
226    /**
227     * @see org.opencms.workplace.tools.I_CmsToolHandler#isEnabled(org.opencms.workplace.CmsWorkplace)
228     */
229    public boolean isEnabled(CmsWorkplace wp) {
230
231        return isEnabled(wp.getCms());
232    }
233
234    /**
235     * @see org.opencms.workplace.tools.I_CmsToolHandler#isVisible(org.opencms.workplace.CmsWorkplace)
236     */
237    public boolean isVisible(CmsWorkplace wp) {
238
239        return isVisible(wp.getCms());
240    }
241
242    /**
243     * Sets the confirmation Message.<p>
244     *
245     * @param confirmationMessage the confirmation Message to set
246     */
247    public void setConfirmationMessage(String confirmationMessage) {
248
249        m_confirmationMessage = confirmationMessage;
250    }
251
252    /**
253     * Sets the help text if disabled.<p>
254     *
255     * @param disabledHelpText the help text to set
256     */
257    public void setDisabledHelpText(String disabledHelpText) {
258
259        m_disabledHelpText = disabledHelpText;
260    }
261
262    /**
263     * Sets the group.<p>
264     *
265     * @param group the group to set
266     */
267    public void setGroup(String group) {
268
269        m_group = group;
270    }
271
272    /**
273     * Sets the help text.<p>
274     *
275     * @param helpText the help text to set
276     */
277    public void setHelpText(String helpText) {
278
279        m_helpText = helpText;
280    }
281
282    /**
283     * Sets the icon path.<p>
284     *
285     * @param iconPath the icon path to set
286     */
287    public void setIconPath(String iconPath) {
288
289        m_iconPath = iconPath;
290    }
291
292    /**
293     * Sets the link.<p>
294     *
295     * @param link the link to set
296     */
297    public void setLink(String link) {
298
299        m_link = link;
300    }
301
302    /**
303     * Sets the name.<p>
304     *
305     * @param name the name to set
306     */
307    public void setName(String name) {
308
309        m_name = name;
310    }
311
312    /**
313     * Sets the parameter string.<p>
314     *
315     * @param paramString the parameter string to set
316     */
317    public void setParameterString(String paramString) {
318
319        m_parameters = paramString;
320    }
321
322    /**
323     * Sets the path.<p>
324     *
325     * @param path the path to set
326     */
327    public void setPath(String path) {
328
329        m_path = path;
330    }
331
332    /**
333     * Sets the position.<p>
334     *
335     * @param position the position to set
336     */
337    public void setPosition(float position) {
338
339        m_position = position;
340    }
341
342    /**
343     * Sets the short name.<p>
344     *
345     * @param shortName the short name to set
346     */
347    public void setShortName(String shortName) {
348
349        m_shortName = shortName;
350    }
351
352    /**
353     * Sets the small icon path.<p>
354     *
355     * @param smallIconPath the samll icon path to set
356     */
357    public void setSmallIconPath(String smallIconPath) {
358
359        m_smallIconPath = smallIconPath;
360    }
361
362    /**
363     * Default implementation.<p>
364     *
365     * It takes the icon path from <code>{@link org.opencms.file.CmsPropertyDefinition#PROPERTY_NAVIMAGE}</code> property,
366     * or uses a default icon if undefined, the name is taken from the
367     * <code>{@link org.opencms.file.CmsPropertyDefinition#PROPERTY_NAVTEXT}</code> property,
368     * or uses the <code>{@link org.opencms.file.CmsPropertyDefinition#PROPERTY_TITLE}</code> property if undefined,
369     * or an default text, if still undefined. if you want 2 different names, one for the big icon tools and one for
370     * the menu/navbar entries, use a <code>{@link A_CmsToolHandler#VALUE_SEPARATOR}</code> to separate them in the property.
371     * (if you do so, the first one is for big icons and the second one for menu/navbar entries). the help text is taken from the
372     * <code>{@link org.opencms.file.CmsPropertyDefinition#PROPERTY_DESCRIPTION}</code> property or a
373     * default text if undefined, if you want to customize a help text while disabled, use a
374     * <code>{@link A_CmsToolHandler#VALUE_SEPARATOR}</code> as a separator in the same property.<p>
375     *
376     * The group is taken from the <code>{@link org.opencms.file.CmsPropertyDefinition#PROPERTY_NAVINFO}</code> property,
377     * the position from the <code>{@link org.opencms.file.CmsPropertyDefinition#PROPERTY_NAVPOS}</code>
378     * and the install path is given by the folder structure if the <code>{@link #ARGS_PROPERTY_DEFINITION}</code>
379     * property does not include path information.<p>
380     *
381     * For the icon path you can specify 2 paths separated by a <code>{@link A_CmsToolHandler#VALUE_SEPARATOR}</code>,
382     * the first one will be used as a group icon (32x32), and the second as an menu icon (16x16). The paths are relative
383     * to the /system/workplace/resources/ folder. If the tool is disabled, the names of the icons are composed as
384     * ${name}_disabled.${ext}<p>
385     *
386     * The confirmation message is taken from the <code>{@link #ARGS_PROPERTY_DEFINITION}</code> with key
387     * <code>#ARG_CONFIRMATION_NAME</code>
388     *
389     * @see org.opencms.workplace.tools.I_CmsToolHandler#setup(org.opencms.file.CmsObject, CmsToolRootHandler, java.lang.String)
390     */
391    public boolean setup(CmsObject cms, CmsToolRootHandler root, String resourcePath) {
392
393        try {
394            resourcePath = cms.getSitePath(cms.readResource(resourcePath));
395        } catch (CmsException e) {
396            // should not happen
397            if (LOG.isErrorEnabled()) {
398                LOG.error(e.getLocalizedMessage(), e);
399            }
400        }
401        CmsJspNavElement navElem = new CmsJspNavBuilder(cms).getNavigationForResource(resourcePath);
402
403        String name = navElem.getNavText();
404        if (CmsMessages.isUnknownKey(name)) {
405            name = navElem.getTitle();
406        }
407        if (CmsStringUtil.isEmptyOrWhitespaceOnly(name)) {
408            name = "${key." + Messages.GUI_TOOLS_DEFAULT_NAME_0 + "}";
409        }
410        String shortName = name;
411        if (name.indexOf(VALUE_SEPARATOR) >= 0) {
412            shortName = name.substring(0, name.indexOf(VALUE_SEPARATOR));
413            name = name.substring(name.indexOf(VALUE_SEPARATOR) + 1);
414        }
415        setName(name);
416        setShortName(shortName);
417
418        String iconPath = navElem.getNavImage();
419        if (CmsStringUtil.isEmptyOrWhitespaceOnly(iconPath)) {
420            iconPath = "admin/images/default_tool_big.png:admin/images/default_tool_small.png";
421        }
422        String smallIconPath = iconPath;
423        if (iconPath.indexOf(VALUE_SEPARATOR) >= 0) {
424            smallIconPath = iconPath.substring(iconPath.indexOf(VALUE_SEPARATOR) + 1);
425            iconPath = iconPath.substring(0, iconPath.indexOf(VALUE_SEPARATOR));
426        }
427        setIconPath(iconPath);
428        setSmallIconPath(smallIconPath);
429
430        String helpText = navElem.getDescription();
431        if (CmsStringUtil.isEmptyOrWhitespaceOnly(helpText)) {
432            helpText = "${key." + Messages.GUI_TOOLS_DEFAULT_HELP_0 + "}";
433        }
434        String disabledHelpText = DEFAULT_DISABLED_HELPTEXT;
435        if (helpText.indexOf(VALUE_SEPARATOR) >= 0) {
436            disabledHelpText = helpText.substring(helpText.indexOf(VALUE_SEPARATOR) + 1);
437            helpText = helpText.substring(0, helpText.indexOf(VALUE_SEPARATOR));
438        }
439        setHelpText(helpText);
440        setDisabledHelpText(disabledHelpText);
441
442        String group = navElem.getInfo();
443        if (CmsStringUtil.isEmptyOrWhitespaceOnly(group)) {
444            group = "${key." + Messages.GUI_TOOLS_DEFAULT_GROUP_0 + "}";
445        }
446
447        String path = resourcePath;
448        setLink(cms, resourcePath);
449        if (CmsResource.isFolder(path)) {
450            path = CmsToolManager.TOOLPATH_SEPARATOR
451                + resourcePath.substring(
452                    root.getUri().length(),
453                    resourcePath.lastIndexOf(CmsToolManager.TOOLPATH_SEPARATOR));
454        } else {
455            if (resourcePath.lastIndexOf('.') > -1) {
456                path = CmsToolManager.TOOLPATH_SEPARATOR
457                    + resourcePath.substring(root.getUri().length(), resourcePath.lastIndexOf('.'));
458            } else {
459                path = CmsToolManager.TOOLPATH_SEPARATOR + resourcePath.substring(root.getUri().length());
460            }
461        }
462        // install point
463        setPath(path);
464        setGroup(group);
465        setPosition(navElem.getNavPosition());
466
467        // parameters
468        setParameters(cms, resourcePath);
469
470        return !path.equals(resourcePath);
471    }
472
473    /**
474     * @see java.lang.Object#toString()
475     */
476    @Override
477    public String toString() {
478
479        return m_path + " - " + m_group + " - " + m_position;
480    }
481
482    /**
483     * Sets the link for the given resource.<p>
484     *
485     * Use the <code>resourcePath</code> as link if it is not a folder.
486     *
487     * If it is a folder, try to use the folder default file property value as link.
488     * if not use the {@link CmsToolManager#VIEW_JSPPAGE_LOCATION}.
489     *
490     * @param cms the cms context
491     * @param resourcePath the path to the resource to set the link for
492     */
493    protected void setLink(CmsObject cms, String resourcePath) {
494
495        String link = resourcePath;
496        try {
497            // make sure the res is a folder
498            cms.readFolder(resourcePath);
499
500            // adjust the path
501            if (resourcePath.endsWith(CmsToolManager.TOOLPATH_SEPARATOR)) {
502                resourcePath = resourcePath.substring(0, resourcePath.lastIndexOf(CmsToolManager.TOOLPATH_SEPARATOR));
503            }
504
505            // set admin page as link
506            link = CmsToolManager.VIEW_JSPPAGE_LOCATION;
507
508            // try to use the folder default file as link
509            CmsProperty prop = cms.readPropertyObject(resourcePath, CmsPropertyDefinition.PROPERTY_DEFAULT_FILE, false);
510            String defFile = "index.jsp";
511            if (!prop.isNullProperty()) {
512                defFile = prop.getValue();
513            }
514            if (!defFile.startsWith(CmsToolManager.TOOLPATH_SEPARATOR)) {
515                // try to use this relative link
516                defFile = resourcePath + CmsToolManager.TOOLPATH_SEPARATOR + defFile;
517            }
518            if (defFile.indexOf("?") > 0) {
519                if (cms.existsResource(defFile.substring(0, defFile.indexOf("?")))) {
520                    link = defFile;
521                }
522            } else if (cms.existsResource(defFile)) {
523                link = defFile;
524            }
525        } catch (CmsException e) {
526            // not a folder or no default file, ignore
527        }
528
529        setLink(link);
530    }
531
532    /**
533     * Sets the needed properties from the {@link #ARGS_PROPERTY_DEFINITION} property of the given resource.<p>
534     *
535     * @param cms the cms context
536     * @param resourcePath the path to the resource to read the property from
537     */
538    protected void setParameters(CmsObject cms, String resourcePath) {
539
540        try {
541            CmsProperty prop = cms.readPropertyObject(resourcePath, ARGS_PROPERTY_DEFINITION, false);
542            if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(prop.getValue())) {
543                Map<String, String> argsMap = new HashMap<String, String>();
544                Iterator<String> itArgs = CmsStringUtil.splitAsList(prop.getValue(), ARGUMENT_SEPARATOR).iterator();
545                while (itArgs.hasNext()) {
546                    String arg = "";
547                    try {
548                        arg = itArgs.next();
549                        int pos = arg.indexOf(VALUE_SEPARATOR);
550                        argsMap.put(arg.substring(0, pos), arg.substring(pos + 1));
551                    } catch (StringIndexOutOfBoundsException e) {
552                        LOG.error("sep: " + VALUE_SEPARATOR + "arg: " + arg);
553                        throw e;
554                    }
555                }
556                if (argsMap.get(ARG_PATH_NAME) != null) {
557                    setPath(argsMap.get(ARG_PATH_NAME));
558                }
559                if (argsMap.get(ARG_CONFIRMATION_NAME) != null) {
560                    setConfirmationMessage(argsMap.get(ARG_CONFIRMATION_NAME));
561                }
562                if (argsMap.get(ARG_PARAM_NAME) != null) {
563                    setParameterString(argsMap.get(ARG_PARAM_NAME));
564                }
565            }
566        } catch (CmsException e) {
567            // should never happen
568            if (LOG.isErrorEnabled()) {
569                LOG.error(e.getLocalizedMessage(), e);
570            }
571        }
572    }
573}