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;
029
030import org.opencms.db.CmsDbEntryNotFoundException;
031import org.opencms.db.CmsUserSettings;
032import org.opencms.file.CmsObject;
033import org.opencms.file.CmsProject;
034import org.opencms.file.CmsRequestContext;
035import org.opencms.file.CmsResource;
036import org.opencms.file.CmsResourceFilter;
037import org.opencms.file.CmsUser;
038import org.opencms.i18n.CmsEncoder;
039import org.opencms.i18n.CmsMessages;
040import org.opencms.i18n.CmsMultiMessages;
041import org.opencms.jsp.CmsJspActionElement;
042import org.opencms.lock.CmsLock;
043import org.opencms.lock.CmsLockType;
044import org.opencms.main.CmsBroadcast;
045import org.opencms.main.CmsContextInfo;
046import org.opencms.main.CmsException;
047import org.opencms.main.CmsIllegalStateException;
048import org.opencms.main.CmsLog;
049import org.opencms.main.CmsSessionInfo;
050import org.opencms.main.CmsStaticResourceHandler;
051import org.opencms.main.OpenCms;
052import org.opencms.security.CmsRole;
053import org.opencms.security.CmsRoleViolationException;
054import org.opencms.site.CmsSite;
055import org.opencms.site.CmsSiteManagerImpl;
056import org.opencms.synchronize.CmsSynchronizeSettings;
057import org.opencms.util.CmsMacroResolver;
058import org.opencms.util.CmsRequestUtil;
059import org.opencms.util.CmsStringUtil;
060import org.opencms.util.CmsUUID;
061
062import java.io.IOException;
063import java.lang.reflect.InvocationTargetException;
064import java.lang.reflect.Method;
065import java.util.ArrayList;
066import java.util.Collection;
067import java.util.HashMap;
068import java.util.Iterator;
069import java.util.List;
070import java.util.Locale;
071import java.util.Map;
072import java.util.Map.Entry;
073
074import javax.servlet.ServletException;
075import javax.servlet.http.HttpServletRequest;
076import javax.servlet.http.HttpServletResponse;
077import javax.servlet.http.HttpSession;
078import javax.servlet.jsp.PageContext;
079
080import org.apache.commons.collections.Buffer;
081import org.apache.commons.fileupload.FileItem;
082import org.apache.commons.logging.Log;
083
084/**
085 * Master class for the JSP based workplace which provides default methods and
086 * session handling for all JSP workplace classes.<p>
087 *
088 * @since 6.0.0
089 */
090public abstract class CmsWorkplace {
091
092    /** The debug flag. */
093    public static final boolean DEBUG = false;
094
095    /** Path to the JSP workplace frame loader file. */
096    public static final String JSP_WORKPLACE_URI = CmsWorkplace.VFS_PATH_VIEWS + "workplace.jsp";
097
098    /** Request parameter name for the model file. */
099    public static final String PARAM_MODELFILE = "modelfile";
100
101    /** Request parameter name prefix for the preferred editors. */
102    public static final String INPUT_DEFAULT = "default";
103
104    /** Request parameter name for the resource list. */
105    public static final String PARAM_RESOURCELIST = "resourcelist";
106
107    /** Request parameter name for no settings in start galleries. */
108    public static final String INPUT_NONE = "none";
109
110    /** Path to system folder. */
111    public static final String VFS_PATH_SYSTEM = "/system/";
112
113    /** Path to sites folder. */
114    public static final String VFS_PATH_SITES = "/sites/";
115
116    /** Path to the workplace. */
117    public static final String VFS_PATH_WORKPLACE = VFS_PATH_SYSTEM + "workplace/";
118
119    /** Constant for the JSP dialogs path. */
120    public static final String PATH_DIALOGS = VFS_PATH_WORKPLACE + "commons/";
121
122    /** Parameter for the default locale. */
123    public static final Locale DEFAULT_LOCALE = Locale.ENGLISH;
124
125    /** Parameter for the default language. */
126    public static final String DEFAULT_LANGUAGE = DEFAULT_LOCALE.getLanguage();
127
128    /** Constant for the JSP common files (e.g. error page) path. */
129    public static final String DIALOG_PATH_COMMON = PATH_DIALOGS + "includes/";
130
131    /** Constant for the JSP common close dialog page. */
132    public static final String FILE_DIALOG_CLOSE = DIALOG_PATH_COMMON + "closedialog.jsp";
133
134    /** Constant for the JSP common confirmation dialog. */
135    public static final String FILE_DIALOG_SCREEN_CONFIRM = DIALOG_PATH_COMMON + "confirmation.jsp";
136
137    /** Constant for the JSP common error dialog. */
138    public static final String FILE_DIALOG_SCREEN_ERROR = DIALOG_PATH_COMMON + "error.jsp";
139
140    /** Constant for the JSP common error dialog. */
141    public static final String FILE_DIALOG_SCREEN_ERRORPAGE = DIALOG_PATH_COMMON + "errorpage.jsp";
142
143    /** Constant for the JSP common wait screen. */
144    public static final String FILE_DIALOG_SCREEN_WAIT = DIALOG_PATH_COMMON + "wait.jsp";
145
146    /** Path to workplace views. */
147    public static final String VFS_PATH_VIEWS = VFS_PATH_WORKPLACE + "views/";
148
149    /** Constant for the JSP explorer filelist file. */
150    public static final String FILE_EXPLORER_FILELIST = VFS_PATH_VIEWS + "explorer/explorer_files.jsp";
151
152    /** Constant for the JSP common report page. */
153    public static final String FILE_REPORT_OUTPUT = DIALOG_PATH_COMMON + "report.jsp";
154
155    /** Helper variable to deliver the html end part. */
156    public static final int HTML_END = 1;
157
158    /** Helper variable to deliver the html start part. */
159    public static final int HTML_START = 0;
160
161    /** The request parameter for the workplace project selection. */
162    public static final String PARAM_WP_EXPLORER_RESOURCE = "wpExplorerResource";
163
164    /** The request parameter for the workplace project selection. */
165    public static final String PARAM_WP_PROJECT = "wpProject";
166
167    /** The request parameter for the workplace site selection. */
168    public static final String PARAM_WP_SITE = "wpSite";
169
170    /** Constant for the JSP workplace path. */
171    public static final String PATH_WORKPLACE = VFS_PATH_WORKPLACE;
172
173    /** Path for file type icons relative to the resources folder. */
174    public static final String RES_PATH_FILETYPES = "filetypes/";
175
176    /** Path to exported system image folder. */
177    public static final String RFS_PATH_RESOURCES = "/resources/";
178
179    /** Directory name of content default_bodies folder. */
180    public static final String VFS_DIR_DEFAULTBODIES = "default_bodies/";
181
182    /** Directory name of content templates folder. */
183    public static final String VFS_DIR_TEMPLATES = "templates/";
184
185    /** Path to commons. */
186    public static final String VFS_PATH_COMMONS = VFS_PATH_WORKPLACE + "commons/";
187
188    /** Path to the workplace editors. */
189    public static final String VFS_PATH_EDITORS = VFS_PATH_WORKPLACE + "editors/";
190
191    /** Path to the galleries. */
192    public static final String VFS_PATH_GALLERIES = VFS_PATH_SYSTEM + "galleries/";
193
194    /** Path to locales. */
195    public static final String VFS_PATH_LOCALES = VFS_PATH_WORKPLACE + "locales/";
196
197    /** Path to modules folder. */
198    public static final String VFS_PATH_MODULES = VFS_PATH_SYSTEM + "modules/";
199
200    /** Path to system image folder. */
201    public static final String VFS_PATH_RESOURCES = VFS_PATH_WORKPLACE + "resources/";
202
203    /** Constant for the direct edit view JSP. */
204    public static final String VIEW_DIRECT_EDIT = VFS_PATH_VIEWS + "explorer/directEdit.jsp";
205
206    /** Constant for the explorer view JSP. */
207    public static final String VIEW_WORKPLACE = VFS_PATH_VIEWS + "explorer/explorer_fs.jsp";
208
209    /** Constant for the admin view JSP. */
210    public static final String VIEW_ADMIN = CmsWorkplace.VFS_PATH_VIEWS + "admin/admin-fs.jsp";
211
212    /** Key name for the request attribute to indicate a multipart request was already parsed. */
213    protected static final String REQUEST_ATTRIBUTE_MULTIPART = "__CmsWorkplace.MULTIPART";
214
215    /** Key name for the request attribute to reload the folder tree view. */
216    protected static final String REQUEST_ATTRIBUTE_RELOADTREE = "__CmsWorkplace.RELOADTREE";
217
218    /** Key name for the session workplace class. */
219    protected static final String SESSION_WORKPLACE_CLASS = "__CmsWorkplace.WORKPLACE_CLASS";
220
221    /** The "explorerview" view selection. */
222    public static final String VIEW_EXPLORER = "explorerview";
223
224    /** The "galleryview" view selection. */
225    public static final String VIEW_GALLERY = "galleryview";
226
227    /** The "list" view selection. */
228    public static final String VIEW_LIST = "listview";
229
230    /** Request parameter name for the directpublish parameter. */
231    public static final String PARAM_DIRECTPUBLISH = "directpublish";
232
233    /** Request parameter name for the publishsiblings parameter. */
234    public static final String PARAM_PUBLISHSIBLINGS = "publishsiblings";
235
236    /** Request parameter name for the relatedresources parameter. */
237    public static final String PARAM_RELATEDRESOURCES = "relatedresources";
238
239    /** Request parameter name for the subresources parameter. */
240    public static final String PARAM_SUBRESOURCES = "subresources";
241
242    /** Default value for date last modified, the release and expire date. */
243    public static final String DEFAULT_DATE_STRING = "-";
244
245    /** Absolute path to the model file dialog. */
246    public static final String VFS_PATH_MODELDIALOG = CmsWorkplace.VFS_PATH_COMMONS
247        + "newresource_xmlcontent_modelfile.jsp";
248
249    /** Absolute path to thenew resource dialog. */
250    public static final String VFS_PATH_NEWRESOURCEDIALOG = CmsWorkplace.VFS_PATH_COMMONS
251        + "newresource_xmlcontent.jsp";
252
253    /** Request parameter name for the new resource type. */
254    public static final String PARAM_NEWRESOURCETYPE = "newresourcetype";
255
256    /** The log object for this class. */
257    private static final Log LOG = CmsLog.getLog(CmsWorkplace.class);
258
259    /** The link to the explorer file list (cached for performance reasons). */
260    private static String m_file_explorer_filelist;
261
262    /** The URI to the skin resources (cached for performance reasons). */
263    private static String m_skinUri;
264
265    /** The URI to the stylesheet resources (cached for performance reasons). */
266    private static String m_styleUri;
267
268    /** The request parameter for the workplace view selection. */
269    public static final String PARAM_WP_VIEW = "wpView";
270
271    /** The request parameter for the workplace start selection. */
272    public static final String PARAM_WP_START = "wpStart";
273
274    /** The current users OpenCms context. */
275    private CmsObject m_cms;
276
277    /** Helper variable to store the id of the current project. */
278    private CmsUUID m_currentProjectId;
279
280    /** Flag for indicating that request forwarded was. */
281    private boolean m_forwarded;
282
283    /** The current JSP action element. */
284    private CmsJspActionElement m_jsp;
285
286    /** The macro resolver, this is cached to avoid multiple instance generation. */
287    private CmsMacroResolver m_macroResolver;
288
289    /**  The currently used message bundle. */
290    private CmsMultiMessages m_messages;
291
292    /** The list of multi part file items (if available). */
293    private List<FileItem> m_multiPartFileItems;
294
295    /** The map of parameters read from the current request. */
296    private Map<String, String[]> m_parameterMap;
297
298    /** The current resource URI. */
299    private String m_resourceUri;
300
301    /** The current OpenCms users http session. */
302    private HttpSession m_session;
303
304    /** The current OpenCms users workplace settings. */
305    private CmsWorkplaceSettings m_settings;
306
307    /**
308     * Public constructor.<p>
309     *
310     * @param jsp the initialized JSP context
311     */
312    public CmsWorkplace(CmsJspActionElement jsp) {
313
314        initWorkplaceMembers(jsp);
315    }
316
317    /**
318     * Constructor in case no page context is available.<p>
319     *
320     * @param cms the current user context
321     * @param session the session
322     */
323    public CmsWorkplace(CmsObject cms, HttpSession session) {
324
325        initWorkplaceMembers(cms, session);
326    }
327
328    /**
329     * Public constructor with JSP variables.<p>
330     *
331     * @param context the JSP page context
332     * @param req the JSP request
333     * @param res the JSP response
334     */
335    public CmsWorkplace(PageContext context, HttpServletRequest req, HttpServletResponse res) {
336
337        this(new CmsJspActionElement(context, req, res));
338    }
339
340    /**
341     * Generates a html select box out of the provided values.<p>
342     *
343     * @param parameters a string that will be inserted into the initial select tag,
344     *      if null no parameters will be inserted
345     * @param options the options
346     * @param values the option values, if null the select will have no value attributes
347     * @param selected the index of the pre-selected option, if -1 no option is pre-selected
348     * @param useLineFeed if true, adds some formatting "\n" to the output String
349     * @return a String representing a html select box
350     */
351    public static String buildSelect(
352        String parameters,
353        List<String> options,
354        List<String> values,
355        int selected,
356        boolean useLineFeed) {
357
358        StringBuffer result = new StringBuffer(1024);
359        result.append("<select ");
360        if (parameters != null) {
361            result.append(parameters);
362        }
363        result.append(">");
364        if (useLineFeed) {
365            result.append("\n");
366        }
367        int length = options.size();
368        String value = null;
369        for (int i = 0; i < length; i++) {
370            if (values != null) {
371                try {
372                    value = values.get(i);
373                } catch (Exception e) {
374                    // can usually be ignored
375                    if (LOG.isInfoEnabled()) {
376                        LOG.info(e.getLocalizedMessage());
377                    }
378                    // lists are not properly initialized, just don't use the value
379                    value = null;
380                }
381            }
382            if (value == null) {
383                result.append("<option");
384                if (i == selected) {
385                    result.append(" selected=\"selected\"");
386                }
387                result.append(">");
388                result.append(options.get(i));
389                result.append("</option>");
390                if (useLineFeed) {
391                    result.append("\n");
392                }
393            } else {
394                result.append("<option value=\"");
395                result.append(value);
396                result.append("\"");
397                if (i == selected) {
398                    result.append(" selected=\"selected\"");
399                }
400                result.append(">");
401                result.append(options.get(i));
402                result.append("</option>");
403                if (useLineFeed) {
404                    result.append("\n");
405                }
406            }
407        }
408        result.append("</select>");
409        if (useLineFeed) {
410            result.append("\n");
411        }
412        return result.toString();
413    }
414
415    /**
416     * Checks if permissions for roles should be editable for the current user on the resource with the given path.<p>
417     *
418     * @param cms the CMS context
419     * @param path the path of a resource
420     *
421     * @return <code>true</code> if permissions for roles should be editable for the current user on the resource with the given path
422     */
423    public static boolean canEditPermissionsForRoles(CmsObject cms, String path) {
424
425        return OpenCms.getRoleManager().hasRoleForResource(cms, CmsRole.VFS_MANAGER, path)
426            && path.startsWith(VFS_PATH_SYSTEM);
427    }
428
429    /**
430     * Returns the style sheets for the report.<p>
431     *
432     * @param cms the current users context
433     * @return the style sheets for the report
434     */
435    public static String generateCssStyle(CmsObject cms) {
436
437        StringBuffer result = new StringBuffer(128);
438        result.append("<style type='text/css'>\n");
439        String contents = "";
440        try {
441            contents = new String(
442                cms.readFile(VFS_PATH_COMMONS + "style/report.css").getContents(),
443                OpenCms.getSystemInfo().getDefaultEncoding());
444        } catch (Exception e) {
445            // ignore
446        }
447        if (CmsStringUtil.isEmpty(contents)) {
448            // css file not found, create default styles
449            result.append(
450                "body       { box-sizing: border-box; -moz-box-sizing: border-box; padding: 2px; margin: 0; color: /*begin-color WindowText*/#000000/*end-color*/; background-color: /*begin-color Window*/#ffffff/*end-color*/; font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 11px; }\n");
451            result.append(
452                "div.main   { box-sizing: border-box; -moz-box-sizing: border-box; color: /*begin-color WindowText*/#000000/*end-color*/; white-space: nowrap; }\n");
453            result.append("span.head  { color: #000099; font-weight: bold; }\n");
454            result.append("span.note  { color: #666666; }\n");
455            result.append("span.ok    { color: #009900; }\n");
456            result.append("span.warn  { color: #990000; padding-left: 40px; }\n");
457            result.append("span.err   { color: #990000; font-weight: bold; padding-left: 40px; }\n");
458            result.append("span.throw { color: #990000; font-weight: bold; }\n");
459            result.append("span.link1 { color: #666666; }\n");
460            result.append("span.link2 { color: #666666; padding-left: 40px; }\n");
461            result.append("span.link2 { color: #990000; }\n");
462        } else {
463            result.append(contents);
464        }
465        result.append("</style>\n");
466        return result.toString();
467    }
468
469    /**
470     * Generates the footer for the extended report view.<p>
471     *
472     * @return html code
473     */
474    public static String generatePageEndExtended() {
475
476        StringBuffer result = new StringBuffer(128);
477        result.append("</div>\n");
478        result.append("</body>\n");
479        result.append("</html>\n");
480        return result.toString();
481    }
482
483    /**
484     * Generates the footer for the simple report view.<p>
485     *
486     * @return html code
487     */
488    public static String generatePageEndSimple() {
489
490        StringBuffer result = new StringBuffer(128);
491        result.append("</td></tr>\n");
492        result.append("</table></div>\n");
493        result.append("</body>\n</html>");
494        return result.toString();
495    }
496
497    /**
498     * Generates the header for the extended report view.<p>
499     *
500     * @param cms the current users context
501     * @param encoding the encoding string
502     *
503     * @return html code
504     */
505    public static String generatePageStartExtended(CmsObject cms, String encoding) {
506
507        StringBuffer result = new StringBuffer(128);
508        result.append("<html>\n<head>\n");
509        result.append("<meta HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=");
510        result.append(encoding);
511        result.append("'>\n");
512        result.append(generateCssStyle(cms));
513        result.append("</head>\n");
514        result.append("<body style='overflow: auto;'>\n");
515        result.append("<div class='main'>\n");
516        return result.toString();
517    }
518
519    /**
520     * Generates the header for the simple report view.<p>
521     *
522     * @param wp the workplace instance
523     *
524     * @return html code
525     */
526    public static String generatePageStartSimple(CmsWorkplace wp) {
527
528        StringBuffer result = new StringBuffer(128);
529        result.append("<html>\n<head>\n");
530        result.append("<meta HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=");
531        result.append(wp.getEncoding());
532        result.append("'>\n");
533        result.append("<link rel='stylesheet' type='text/css' href='");
534        result.append(wp.getStyleUri("workplace.css"));
535        result.append("'>\n");
536        result.append(generateCssStyle(wp.getCms()));
537        result.append("</head>\n");
538        result.append("<body style='background-color:/*begin-color Menu*/#f0f0f0/*end-color*/;'>\n");
539        result.append("<div style='vertical-align:middle; height: 100%;'>\n");
540        result.append("<table border='0' style='vertical-align:middle; height: 100%;'>\n");
541        result.append("<tr><td width='40' align='center' valign='middle'><img name='report_img' src='");
542        result.append(getSkinUri());
543        result.append("commons/wait.gif' width='32' height='32' alt=''></td>\n");
544        result.append("<td valign='middle'>");
545        return result.toString();
546    }
547
548    /**
549     * Returns the full Workplace resource path to the selected resource.<p>
550     *
551     * @param resourceName the name of the resource to get the resource path for
552     *
553     * @return the full Workplace resource path to the selected resource
554     */
555    public static String getResourceUri(String resourceName) {
556
557        StringBuffer result = new StringBuffer(256);
558        result.append(getSkinUri());
559        result.append(resourceName);
560        return result.toString();
561    }
562
563    /**
564     * Returns the path to the skin resources.<p>
565     *
566     * @return the path to the skin resources
567     */
568    public static String getSkinUri() {
569
570        if (m_skinUri == null) {
571            m_skinUri = OpenCms.getSystemInfo().getContextPath() + RFS_PATH_RESOURCES;
572        }
573        return m_skinUri;
574    }
575
576    /**
577     * Returns the start site from the given user settings.<p>
578     *
579     * @param cms the cms context
580     * @param userSettings the user settings
581     *
582     * @return the start site root
583     */
584    public static String getStartSiteRoot(CmsObject cms, CmsUserSettings userSettings) {
585
586        String startSiteRoot = userSettings.getStartSite();
587        if (startSiteRoot.endsWith("/")) {
588            // remove trailing slash
589            startSiteRoot = startSiteRoot.substring(0, startSiteRoot.length() - 1);
590        }
591        if (CmsStringUtil.isNotEmpty(startSiteRoot)
592            && (OpenCms.getSiteManager().getSiteForSiteRoot(startSiteRoot) == null)) {
593            // this is not the root site and the site is not in the list
594            List<CmsSite> sites = OpenCms.getSiteManager().getAvailableSites(
595                cms,
596                false,
597                cms.getRequestContext().getCurrentUser().getOuFqn());
598            if (sites.size() == 1) {
599                startSiteRoot = sites.get(0).getSiteRoot();
600            } else if (sites.size() > 1) {
601                String siteRoot = null;
602                String defaultSite = OpenCms.getWorkplaceManager().getDefaultUserSettings().getStartSite();
603                // check if the default start site is available to the user
604                for (CmsSite site : sites) {
605                    if (site.getSiteRoot().equals(defaultSite)) {
606                        siteRoot = defaultSite;
607                        break;
608                    }
609                }
610                if (siteRoot == null) {
611                    // list of available sites contains more than one site, but not the configured default, pick any
612                    startSiteRoot = sites.get(0).getSiteRoot();
613                } else {
614                    startSiteRoot = siteRoot;
615                }
616            }
617        }
618        return startSiteRoot;
619    }
620
621    /**
622     * Returns the start site from the given workplace settings.<p>
623     *
624     * @param cms the cms context
625     * @param settings the workplace settings
626     *
627     * @return the start site root
628     */
629    public static String getStartSiteRoot(CmsObject cms, CmsWorkplaceSettings settings) {
630
631        return getStartSiteRoot(cms, settings.getUserSettings());
632    }
633
634    /**
635     * Returns the URI to static resources served from the class path.<p>
636     *
637     * @param resourceName the resource name
638     *
639     * @return the URI
640     */
641    public static String getStaticResourceUri(String resourceName) {
642
643        return getStaticResourceUri(resourceName, null);
644    }
645
646    /**
647     * Returns the URI to static resources served from the class path.<p>
648     *
649     * @param resourceName the resource name
650     * @param versionInfo add an additional version info parameter to avoid browser caching issues
651     *
652     * @return the URI
653     */
654    public static String getStaticResourceUri(String resourceName, String versionInfo) {
655
656        resourceName = CmsStaticResourceHandler.removeStaticResourcePrefix(resourceName);
657        String uri = CmsStringUtil.joinPaths(OpenCms.getSystemInfo().getStaticResourceContext(), resourceName);
658        if (versionInfo != null) {
659            uri += "?v=" + versionInfo;
660        }
661        return uri;
662    }
663
664    /**
665     * Returns the path to the cascading stylesheets.<p>
666     *
667     * @param jsp the JSP context
668     * @return the path to the cascading stylesheets
669     */
670    public static String getStyleUri(CmsJspActionElement jsp) {
671
672        if (m_styleUri == null) {
673
674            CmsProject project = jsp.getCmsObject().getRequestContext().getCurrentProject();
675            try {
676                jsp.getCmsObject().getRequestContext().setCurrentProject(
677                    jsp.getCmsObject().readProject(CmsProject.ONLINE_PROJECT_ID));
678                m_styleUri = jsp.link("/system/workplace/commons/style/");
679            } catch (CmsException e) {
680                LOG.error(e.getLocalizedMessage());
681            } finally {
682                jsp.getCmsObject().getRequestContext().setCurrentProject(project);
683            }
684        }
685        return m_styleUri;
686    }
687
688    /**
689     * Returns the path to the cascading stylesheets.<p>
690     *
691     * @param jsp the JSP context
692     * @param filename the name of the stylesheet
693     * @return the path to the cascading stylesheets
694     */
695    public static String getStyleUri(CmsJspActionElement jsp, String filename) {
696
697        if (m_styleUri == null) {
698            CmsProject project = jsp.getCmsObject().getRequestContext().getCurrentProject();
699            try {
700                jsp.getCmsObject().getRequestContext().setCurrentProject(
701                    jsp.getCmsObject().readProject(CmsProject.ONLINE_PROJECT_ID));
702                m_styleUri = jsp.link("/system/workplace/commons/style/");
703            } catch (CmsException e) {
704                if (LOG.isErrorEnabled()) {
705                    LOG.error(e.getLocalizedMessage(), e);
706                }
707            } finally {
708                jsp.getCmsObject().getRequestContext().setCurrentProject(project);
709            }
710        }
711        return m_styleUri + filename;
712    }
713
714    /**
715     * Returns the temporary file name for the given resource name.<p>
716     *
717     * To create a temporary file name of a resource name, the prefix char <code>'~'</code> (tilde)
718     * is added to the file name after all parent folder names have been removed.<p>
719     *
720     * @param resourceName the resource name to return the temporary file name for
721     *
722     * @return the temporary file name for the given resource name
723     *
724     * @see CmsResource#isTemporaryFileName(String)
725     * @see #isTemporaryFile(CmsResource)
726     */
727    public static String getTemporaryFileName(String resourceName) {
728
729        if (resourceName == null) {
730            return null;
731        }
732        StringBuffer result = new StringBuffer(resourceName.length() + 2);
733        result.append(CmsResource.getFolderPath(resourceName));
734        result.append(CmsResource.TEMP_FILE_PREFIX);
735        result.append(CmsResource.getName(resourceName));
736        return result.toString();
737    }
738
739    /**
740     * Creates a link for the OpenCms workplace that will reload the whole workplace, switch to the explorer view, the
741     * site of the given explorerRootPath and show the folder given in the explorerRootPath.
742     * <p>
743     *
744     * @param jsp
745     *            needed for link functionality.
746     *
747     * @param explorerRootPath
748     *            a root relative folder link (has to end with '/').
749     *
750     * @return a link for the OpenCms workplace that will reload the whole workplace, switch to the explorer view, the
751     *         site of the given explorerRootPath and show the folder given in the explorerRootPath.
752     */
753    public static String getWorkplaceExplorerLink(final CmsJspActionElement jsp, final String explorerRootPath) {
754
755        return getWorkplaceExplorerLink(jsp.getCmsObject(), explorerRootPath);
756
757    }
758
759    /**
760     * Creates a link for the OpenCms workplace that will reload the whole workplace, switch to the explorer view, the
761     * site of the given explorerRootPath and show the folder given in the explorerRootPath.
762     * <p>
763     *
764     * @param cms
765     *            the cms object
766     *
767     * @param explorerRootPath
768     *            a root relative folder link (has to end with '/').
769     *
770     * @return a link for the OpenCms workplace that will reload the whole workplace, switch to the explorer view, the
771     *         site of the given explorerRootPath and show the folder given in the explorerRootPath.
772     */
773    public static String getWorkplaceExplorerLink(final CmsObject cms, final String explorerRootPath) {
774
775        // split the root site:
776        String targetSiteRoot = OpenCms.getSiteManager().getSiteRoot(explorerRootPath);
777        if (targetSiteRoot == null) {
778            if (OpenCms.getSiteManager().startsWithShared(explorerRootPath)) {
779                targetSiteRoot = OpenCms.getSiteManager().getSharedFolder();
780            } else {
781                targetSiteRoot = "";
782            }
783        }
784        String targetVfsFolder;
785        if (explorerRootPath.startsWith(targetSiteRoot)) {
786            targetVfsFolder = explorerRootPath.substring(targetSiteRoot.length());
787            targetVfsFolder = CmsStringUtil.joinPaths("/", targetVfsFolder);
788        } else {
789            targetVfsFolder = "/";
790            // happens in case of the shared site
791        }
792
793        StringBuilder link2Source = new StringBuilder();
794        link2Source.append(JSP_WORKPLACE_URI);
795        link2Source.append("?");
796        link2Source.append(CmsWorkplace.PARAM_WP_EXPLORER_RESOURCE);
797        link2Source.append("=");
798        link2Source.append(targetVfsFolder);
799        link2Source.append("&");
800        link2Source.append(PARAM_WP_VIEW);
801        link2Source.append("=");
802        link2Source.append(
803            OpenCms.getLinkManager().substituteLinkForUnknownTarget(
804                cms,
805                "/system/workplace/views/explorer/explorer_fs.jsp"));
806        link2Source.append("&");
807        link2Source.append(PARAM_WP_SITE);
808        link2Source.append("=");
809        link2Source.append(targetSiteRoot);
810
811        String result = link2Source.toString();
812        result = OpenCms.getLinkManager().substituteLinkForUnknownTarget(cms, result);
813        return result;
814    }
815
816    /**
817     * Returns the workplace settings of the current user.<p>
818     *
819     * @param cms the cms context
820     * @param req the request
821     *
822     * @return the workplace settings or <code>null</code> if the user is not logged in
823     */
824    public static CmsWorkplaceSettings getWorkplaceSettings(CmsObject cms, HttpServletRequest req) {
825
826        HttpSession session = req.getSession(false);
827        CmsWorkplaceSettings workplaceSettings = null;
828        if (session != null) {
829            // all logged in user will have a session
830            workplaceSettings = (CmsWorkplaceSettings)session.getAttribute(
831                CmsWorkplaceManager.SESSION_WORKPLACE_SETTINGS);
832            // ensure workplace settings attribute is set
833            if (workplaceSettings == null) {
834                // creating any instance of {@link org.opencms.workplace.CmsWorkplaceSettings} and store it
835                workplaceSettings = initWorkplaceSettings(cms, null, false);
836                storeSettings(session, workplaceSettings);
837            }
838        }
839        return workplaceSettings;
840    }
841
842    /**
843     * Updates the user settings in the given workplace settings for the current user, reading the user settings
844     * from the database if required.<p>
845     *
846     * @param cms the cms object for the current user
847     * @param settings the workplace settings to update (if <code>null</code> a new instance is created)
848     * @param update flag indicating if settings are only updated (user preferences)
849     *
850     * @return the current users workplace settings
851     *
852     * @see #initWorkplaceSettings(CmsObject, CmsWorkplaceSettings, boolean)
853     */
854    public static CmsWorkplaceSettings initUserSettings(CmsObject cms, CmsWorkplaceSettings settings, boolean update) {
855
856        if (settings == null) {
857            settings = new CmsWorkplaceSettings();
858        }
859
860        // save current workplace user & user settings object
861        CmsUser user;
862        if (update) {
863            try {
864                // read the user from db to get the latest user information if required
865                user = cms.readUser(cms.getRequestContext().getCurrentUser().getId());
866            } catch (CmsException e) {
867                // can usually be ignored
868                if (LOG.isInfoEnabled()) {
869                    LOG.info(e.getLocalizedMessage());
870                }
871                user = cms.getRequestContext().getCurrentUser();
872            }
873        } else {
874            user = cms.getRequestContext().getCurrentUser();
875        }
876        // store the user and it's settings in the Workplace settings
877        settings.setUser(user);
878        settings.setUserSettings(new CmsUserSettings(user));
879
880        // return the result settings
881        return settings;
882    }
883
884    /**
885     * Updates the given workplace settings, also re-initializing
886     * the state of the Workplace to the users preferences (for example setting the startup site and project).
887     *
888     * The user settings will also be updated by calling <code>{@link #initUserSettings(CmsObject, CmsWorkplaceSettings, boolean)}</code>
889     * before updating the workplace project, selected site etc.<p>
890     *
891     * @param cms the cms object for the current user
892     * @param settings the workplace settings to update (if <code>null</code> a new instance is created)
893     * @param update flag indicating if settings are only updated (user preferences)
894     *
895     * @return the current users initialized workplace settings
896     *
897     * @see #initUserSettings(CmsObject, CmsWorkplaceSettings, boolean)
898     */
899    public static synchronized CmsWorkplaceSettings initWorkplaceSettings(
900        CmsObject cms,
901        CmsWorkplaceSettings settings,
902        boolean update) {
903
904        // init the workplace user settings
905        settings = initUserSettings(cms, settings, update);
906
907        // save current project
908        settings.setProject(cms.getRequestContext().getCurrentProject().getUuid());
909        String currentSite = cms.getRequestContext().getSiteRoot();
910        // keep the current site
911        settings.setSite(currentSite);
912
913        // switch to users preferred site
914        String startSiteRoot = getStartSiteRoot(cms, settings);
915
916        try {
917            CmsObject cloneCms = OpenCms.initCmsObject(cms);
918            cloneCms.getRequestContext().setSiteRoot(startSiteRoot);
919            String projectName = settings.getUserSettings().getStartProject();
920            if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(projectName)) {
921                cloneCms.getRequestContext().setCurrentProject(cloneCms.readProject(projectName));
922            }
923            // check start folder:
924            String startFolder = settings.getUserSettings().getStartFolder();
925            if (!cloneCms.existsResource(startFolder, CmsResourceFilter.IGNORE_EXPIRATION)) {
926                // self - healing:
927                startFolder = "/";
928                settings.getUserSettings().setStartFolder(startFolder);
929            }
930            settings.setSite(startSiteRoot);
931            settings.setExplorerResource(startFolder, cloneCms);
932        } catch (Exception e) {
933            settings.getUserSettings().setStartFolder("/");
934            settings.setSite(startSiteRoot);
935            settings.setExplorerResource("/", null);
936        } finally {
937            settings.setSite(currentSite);
938        }
939        // get the default view from the user settings
940        settings.setViewUri(OpenCms.getLinkManager().substituteLink(cms, settings.getUserSettings().getStartView()));
941        return settings;
942    }
943
944    /**
945     * Returns <code>true</code> if the given resource is a temporary file.<p>
946     *
947     * A resource is considered a temporary file it is a file where the
948     * {@link CmsResource#FLAG_TEMPFILE} flag has been set, or if the file name (without parent folders)
949     * starts with the prefix char <code>'~'</code> (tilde).<p>
950     *
951     * @param resource the resource name to check
952     *
953     * @return <code>true</code> if the given resource name is a temporary file
954     *
955     * @see #getTemporaryFileName(String)
956     * @see CmsResource#isTemporaryFileName(String)
957     */
958    public static boolean isTemporaryFile(CmsResource resource) {
959
960        return (resource != null)
961            && ((resource.isFile()
962                && (((resource.getFlags() & CmsResource.FLAG_TEMPFILE) > 0)
963                    || (CmsResource.isTemporaryFileName(resource.getName())))));
964    }
965
966    /**
967     * Substitutes the site title.<p>
968     *
969     * @param title the raw site title
970     * @param locale the localel
971     *
972     * @return the locale specific site title
973     */
974    public static String substituteSiteTitleStatic(String title, Locale locale) {
975
976        if (title.equals(CmsSiteManagerImpl.SHARED_FOLDER_TITLE)) {
977            return Messages.get().getBundle(locale).key(Messages.GUI_SHARED_TITLE_0);
978        }
979        return title;
980
981    }
982
983    /**
984     * Updates the user preferences after changes have been made.<p>
985     *
986     * @param cms the current cms context
987     * @param req the current http request
988     */
989    public static void updateUserPreferences(CmsObject cms, HttpServletRequest req) {
990
991        HttpSession session = req.getSession(false);
992        if (session == null) {
993            return;
994        }
995        CmsWorkplaceSettings settings = (CmsWorkplaceSettings)session.getAttribute(
996            CmsWorkplaceManager.SESSION_WORKPLACE_SETTINGS);
997        if (settings == null) {
998            return;
999        }
1000        // keep old synchronize settings
1001        CmsSynchronizeSettings synchronizeSettings = settings.getUserSettings().getSynchronizeSettings();
1002        settings = CmsWorkplace.initWorkplaceSettings(cms, settings, true);
1003        settings.getUserSettings().setSynchronizeSettings(synchronizeSettings);
1004    }
1005
1006    /**
1007     * Stores the settings in the given session.<p>
1008     *
1009     * @param session the session to store the settings in
1010     * @param settings the settings
1011     */
1012    static void storeSettings(HttpSession session, CmsWorkplaceSettings settings) {
1013
1014        // save the workplace settings in the session
1015        session.setAttribute(CmsWorkplaceManager.SESSION_WORKPLACE_SETTINGS, settings);
1016    }
1017
1018    /**
1019     * Returns all parameters of the current workplace class
1020     * as hidden field tags that can be inserted in a form.<p>
1021     *
1022     * @return all parameters of the current workplace class
1023     * as hidden field tags that can be inserted in a html form
1024     */
1025    public String allParamsAsHidden() {
1026
1027        StringBuffer result = new StringBuffer(512);
1028        Map<String, Object> params = allParamValues();
1029        Iterator<Entry<String, Object>> i = params.entrySet().iterator();
1030        while (i.hasNext()) {
1031            Entry<String, Object> entry = i.next();
1032            result.append("<input type=\"hidden\" name=\"");
1033            result.append(entry.getKey());
1034            result.append("\" value=\"");
1035            String encoded = CmsEncoder.encode(entry.getValue().toString(), getCms().getRequestContext().getEncoding());
1036            result.append(encoded);
1037            result.append("\">\n");
1038        }
1039        return result.toString();
1040    }
1041
1042    /**
1043     * Returns all present request parameters as String.<p>
1044     *
1045     * The String is formatted as a parameter String (<code>param1=val1&amp;param2=val2</code>) with UTF-8 encoded values.<p>
1046     *
1047     * @return all present request parameters as String
1048     */
1049    public String allParamsAsRequest() {
1050
1051        StringBuffer retValue = new StringBuffer(512);
1052        HttpServletRequest request = getJsp().getRequest();
1053        Iterator<String> paramNames = request.getParameterMap().keySet().iterator();
1054        while (paramNames.hasNext()) {
1055            String paramName = paramNames.next();
1056            String paramValue = request.getParameter(paramName);
1057            retValue.append(
1058                paramName + "=" + CmsEncoder.encode(paramValue, getCms().getRequestContext().getEncoding()));
1059            if (paramNames.hasNext()) {
1060                retValue.append("&");
1061            }
1062        }
1063        return retValue.toString();
1064    }
1065
1066    /**
1067     * Builds the end html of the body.<p>
1068     *
1069     * @return the end html of the body
1070     */
1071    public String bodyEnd() {
1072
1073        return pageBody(HTML_END, null, null);
1074    }
1075
1076    /**
1077     * Builds the start html of the body.<p>
1078     *
1079     * @param className optional class attribute to add to the body tag
1080     * @return the start html of the body
1081     */
1082    public String bodyStart(String className) {
1083
1084        return pageBody(HTML_START, className, null);
1085    }
1086
1087    /**
1088     * Builds the start html of the body.<p>
1089     *
1090     * @param className optional class attribute to add to the body tag
1091     * @param parameters optional parameters to add to the body tag
1092     * @return the start html of the body
1093     */
1094    public String bodyStart(String className, String parameters) {
1095
1096        return pageBody(HTML_START, className, parameters);
1097    }
1098
1099    /**
1100     * Generates a html select box out of the provided values.<p>
1101     *
1102     * @param parameters a string that will be inserted into the initial select tag,
1103     *      if null no parameters will be inserted
1104     * @param options the options
1105     * @param values the option values, if null the select will have no value attributes
1106     * @param selected the index of the pre-selected option, if -1 no option is pre-selected
1107     * @return a formatted html String representing a html select box
1108     */
1109    public String buildSelect(String parameters, List<String> options, List<String> values, int selected) {
1110
1111        return buildSelect(parameters, options, values, selected, true);
1112    }
1113
1114    /**
1115     * Generates a button for the OpenCms workplace.<p>
1116     *
1117     * @param href the href link for the button, if none is given the button will be disabled
1118     * @param target the href link target for the button, if none is given the target will be same window
1119     * @param image the image name for the button, skin path will be automattically added as prefix
1120     * @param label the label for the text of the button
1121     * @param type 0: image only (default), 1: image and text, 2: text only
1122     *
1123     * @return a button for the OpenCms workplace
1124     */
1125    public String button(String href, String target, String image, String label, int type) {
1126
1127        return button(href, target, image, label, type, getSkinUri() + "buttons/");
1128    }
1129
1130    /**
1131     * Generates a button for the OpenCms workplace.<p>
1132     *
1133     * @param href the href link for the button, if none is given the button will be disabled
1134     * @param target the href link target for the button, if none is given the target will be same window
1135     * @param image the image name for the button, skin path will be automattically added as prefix
1136     * @param label the label for the text of the button
1137     * @param type 0: image only (default), 1: image and text, 2: text only
1138     * @param imagePath the path to the image
1139     *
1140     * @return a button for the OpenCms workplace
1141     */
1142    public String button(String href, String target, String image, String label, int type, String imagePath) {
1143
1144        StringBuffer result = new StringBuffer(256);
1145
1146        String anchorStart = "<a href=\"";
1147        if ((href != null) && href.toLowerCase().startsWith("javascript:")) {
1148            anchorStart = "<a href=\"#\" onclick=\"";
1149        }
1150
1151        result.append("<td style=\"vertical-align: top;\">");
1152        switch (type) {
1153            case 1:
1154                // image and text
1155                if (href != null) {
1156                    result.append(anchorStart);
1157                    result.append(href);
1158                    result.append("\" class=\"button\"");
1159                    if (target != null) {
1160                        result.append(" target=\"");
1161                        result.append(target);
1162                        result.append("\"");
1163                    }
1164                    result.append(">");
1165                }
1166                result.append("<span unselectable=\"on\" ");
1167                if (href != null) {
1168                    result.append(
1169                        "class=\"norm\" onmouseover=\"className='over'\" onmouseout=\"className='norm'\" onmousedown=\"className='push'\" onmouseup=\"className='over'\"");
1170                } else {
1171                    result.append("class=\"disabled\"");
1172                }
1173                result.append("><span unselectable=\"on\" class=\"combobutton\" ");
1174                result.append("style=\"background-image: url('");
1175                result.append(imagePath);
1176                result.append(image);
1177                if ((image != null) && (image.indexOf('.') == -1)) {
1178                    // append default suffix for button images
1179                    result.append(".png");
1180                }
1181                result.append("');\">");
1182                result.append(shortKey(label));
1183                result.append("</span></span>");
1184                if (href != null) {
1185                    result.append("</a>");
1186                }
1187                break;
1188
1189            case 2:
1190                // text only
1191                if (href != null) {
1192                    result.append(anchorStart);
1193                    result.append(href);
1194                    result.append("\" class=\"button\"");
1195                    if (target != null) {
1196                        result.append(" target=\"");
1197                        result.append(target);
1198                        result.append("\"");
1199                    }
1200                    result.append(">");
1201                }
1202                result.append("<span unselectable=\"on\" ");
1203                if (href != null) {
1204                    result.append(
1205                        "class=\"norm\" onmouseover=\"className='over'\" onmouseout=\"className='norm'\" onmousedown=\"className='push'\" onmouseup=\"className='over'\"");
1206                } else {
1207                    result.append("class=\"disabled\"");
1208                }
1209                result.append("><span unselectable=\"on\" class=\"txtbutton\">");
1210                result.append(shortKey(label));
1211                result.append("</span></span>");
1212                if (href != null) {
1213                    result.append("</a>");
1214                }
1215                break;
1216
1217            default:
1218                // only image
1219                if (href != null) {
1220                    result.append(anchorStart);
1221                    result.append(href);
1222                    result.append("\" class=\"button\"");
1223                    if (target != null) {
1224                        result.append(" target=\"");
1225                        result.append(target);
1226                        result.append("\"");
1227                    }
1228                    result.append(" title=\"");
1229                    result.append(key(label));
1230                    result.append("\">");
1231                }
1232                result.append("<span unselectable=\"on\" ");
1233                if (href != null) {
1234                    result.append(
1235                        "class=\"norm\" onmouseover=\"className='over'\" onmouseout=\"className='norm'\" onmousedown=\"className='push'\" onmouseup=\"className='over'\"");
1236                } else {
1237                    result.append("class=\"disabled\"");
1238                }
1239                result.append("><img class=\"button\" src=\"");
1240                result.append(imagePath);
1241                result.append(image);
1242                if ((image != null) && (image.indexOf('.') == -1)) {
1243                    // append default suffix for button images
1244                    result.append(".png");
1245                }
1246                result.append("\" alt=\"");
1247                result.append(key(label));
1248                result.append("\">");
1249                result.append("</span>");
1250                if (href != null) {
1251                    result.append("</a>");
1252                }
1253                break;
1254        }
1255        result.append("</td>\n");
1256        return result.toString();
1257    }
1258
1259    /**
1260     * Returns the html for a button bar.<p>
1261     *
1262     * @param segment the HTML segment (START / END)
1263     *
1264     * @return a button bar html start / end segment
1265     */
1266    public String buttonBar(int segment) {
1267
1268        return buttonBar(segment, null);
1269    }
1270
1271    /**
1272     * Returns the html for a button bar.<p>
1273     *
1274     * @param segment the HTML segment (START / END)
1275     * @param attributes optional attributes for the table tag
1276     *
1277     * @return a button bar html start / end segment
1278     */
1279    public String buttonBar(int segment, String attributes) {
1280
1281        if (segment == HTML_START) {
1282            String result = "<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\"";
1283            if (attributes != null) {
1284                result += " " + attributes;
1285            }
1286            return result + "><tr>\n";
1287        } else {
1288            return "</tr></table>";
1289        }
1290    }
1291
1292    /**
1293     * Generates a horizontal button bar separator line with maximum width.<p>
1294     *
1295     * @return a horizontal button bar separator line
1296     */
1297    public String buttonBarHorizontalLine() {
1298
1299        StringBuffer result = new StringBuffer(256);
1300        result.append("<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" class=\"maxwidth\">\n");
1301        result.append("<tr>\n");
1302        result.append("\t<td class=\"horseparator\" ><img src=\"");
1303        result.append(getSkinUri());
1304        result.append("tree/empty.gif\" border=\"0\" width=\"1\" height=\"1\" alt=\"\"></td>\n");
1305        result.append("</tr>\n");
1306        result.append("</table>\n");
1307        return result.toString();
1308    }
1309
1310    /**
1311     * Generates a button bar label.<p>
1312     *
1313     * @param label the label to show
1314     *
1315     * @return a button bar label
1316     */
1317    public String buttonBarLabel(String label) {
1318
1319        return buttonBarLabel(label, "norm");
1320    }
1321
1322    /**
1323     * Generates a button bar label.<p>
1324     *
1325     * @param label the label to show
1326     * @param className the css class name for the formatting
1327     *
1328     * @return a button bar label
1329     */
1330    public String buttonBarLabel(String label, String className) {
1331
1332        StringBuffer result = new StringBuffer(128);
1333        result.append("<td><span class=\"");
1334        result.append(className);
1335        result.append("\"><span unselectable=\"on\" class=\"txtbutton\">");
1336        result.append(key(label));
1337        result.append("</span></span></td>\n");
1338        return result.toString();
1339    }
1340
1341    /**
1342     * Generates a variable button bar separator line.<p>
1343     *
1344     * @param leftPixel the amount of pixel left to the line
1345     * @param rightPixel the amount of pixel right to the line
1346     * @param className the css class name for the formatting
1347     *
1348     * @return  a variable button bar separator line
1349     */
1350    public String buttonBarLine(int leftPixel, int rightPixel, String className) {
1351
1352        StringBuffer result = new StringBuffer(512);
1353        if (leftPixel > 0) {
1354            result.append(buttonBarLineSpacer(leftPixel));
1355        }
1356        result.append("<td><span class=\"");
1357        result.append(className);
1358        result.append("\"></span></td>\n");
1359        if (rightPixel > 0) {
1360            result.append(buttonBarLineSpacer(rightPixel));
1361        }
1362        return result.toString();
1363    }
1364
1365    /**
1366     * Generates a variable button bar separator line spacer.<p>
1367     *
1368     * @param pixel the amount of pixel space
1369     *
1370     * @return a variable button bar separator line spacer
1371     */
1372    public String buttonBarLineSpacer(int pixel) {
1373
1374        StringBuffer result = new StringBuffer(128);
1375        result.append(
1376            "<td><span class=\"norm\"><span unselectable=\"on\" class=\"txtbutton\" style=\"padding-right: 0px; padding-left: ");
1377        result.append(pixel);
1378        result.append("px;\"></span></span></td>\n");
1379        return result.toString();
1380    }
1381
1382    /**
1383     * Generates a button bar separator.<p>
1384     *
1385     * @param leftPixel the amount of pixel left to the separator
1386     * @param rightPixel the amount of pixel right to the separator
1387     *
1388     * @return a button bar separator
1389     */
1390    public String buttonBarSeparator(int leftPixel, int rightPixel) {
1391
1392        return buttonBarLine(leftPixel, rightPixel, "separator");
1393    }
1394
1395    /**
1396     * Returns the html for an invisible spacer between button bar contents like buttons, labels, etc.<p>
1397     *
1398     * @param width the width of the invisible spacer
1399     * @return the html for the invisible spacer
1400     */
1401    public String buttonBarSpacer(int width) {
1402
1403        StringBuffer result = new StringBuffer(128);
1404        result.append("<td><span class=\"norm\"><span unselectable=\"on\" class=\"txtbutton\" style=\"width: ");
1405        result.append(width);
1406        result.append("px;\"></span></span></td>\n");
1407        return result.toString();
1408    }
1409
1410    /**
1411     * Generates a button bar starter tab.<p>
1412     *
1413     * @param leftPixel the amount of pixel left to the starter
1414     * @param rightPixel the amount of pixel right to the starter
1415     *
1416     * @return a button bar starter tab
1417     */
1418    public String buttonBarStartTab(int leftPixel, int rightPixel) {
1419
1420        StringBuffer result = new StringBuffer(512);
1421        result.append(buttonBarLineSpacer(leftPixel));
1422        result.append("<td><span class=\"starttab\"><span style=\"width:1px; height:1px\"></span></span></td>\n");
1423        result.append(buttonBarLineSpacer(rightPixel));
1424        return result.toString();
1425    }
1426
1427    /**
1428     * Checks the lock state of the resource and locks it if the autolock feature is enabled.<p>
1429     *
1430     * @param resource the resource name which is checked
1431     * @throws CmsException if reading or locking the resource fails
1432     */
1433    public void checkLock(String resource) throws CmsException {
1434
1435        checkLock(resource, CmsLockType.EXCLUSIVE);
1436    }
1437
1438    /**
1439     * Checks the lock state of the resource and locks it if the autolock feature is enabled.<p>
1440     *
1441     * @param resource the resource name which is checked
1442     * @param type indicates the mode {@link CmsLockType#EXCLUSIVE} or {@link CmsLockType#TEMPORARY}
1443     *
1444     * @throws CmsException if reading or locking the resource fails
1445     */
1446    public void checkLock(String resource, CmsLockType type) throws CmsException {
1447
1448        CmsResource res = getCms().readResource(resource, CmsResourceFilter.ALL);
1449        CmsLock lock = getCms().getLock(res);
1450        boolean lockable = lock.isLockableBy(getCms().getRequestContext().getCurrentUser());
1451
1452        if (OpenCms.getWorkplaceManager().autoLockResources()) {
1453            // autolock is enabled, check the lock state of the resource
1454            if (lockable) {
1455                // resource is lockable, so lock it automatically
1456                if (type == CmsLockType.TEMPORARY) {
1457                    getCms().lockResourceTemporary(resource);
1458                } else {
1459                    getCms().lockResource(resource);
1460                }
1461            } else {
1462                throw new CmsException(Messages.get().container(Messages.ERR_WORKPLACE_LOCK_RESOURCE_1, resource));
1463            }
1464        } else {
1465            if (!lockable) {
1466                throw new CmsException(Messages.get().container(Messages.ERR_WORKPLACE_LOCK_RESOURCE_1, resource));
1467            }
1468        }
1469    }
1470
1471    /**
1472     * First sets site and project in the workplace settings, then fills all class parameter values from the data
1473     * provided in the current request.<p>
1474     *
1475     * @param settings the workplace settings
1476     * @param request the current request
1477     */
1478    public void fillParamValues(CmsWorkplaceSettings settings, HttpServletRequest request) {
1479
1480        initSettings(settings, request);
1481        fillParamValues(request);
1482    }
1483
1484    /**
1485     * Fills all class parameter values from the data provided in the current request.<p>
1486     *
1487     * All methods that start with "setParam" are possible candidates to be
1488     * automatically filled. The remaining part of the method name is converted
1489     * to lower case. Then a parameter of this name is searched in the request parameters.
1490     * If the parameter is found, the "setParam" method is automatically invoked
1491     * by reflection with the value of the parameter.<p>
1492     *
1493     * @param request the current JSP request
1494     */
1495    public void fillParamValues(HttpServletRequest request) {
1496
1497        m_parameterMap = null;
1498        // ensure a multipart request is parsed only once (for "forward" scenarios with reports)
1499        if (null == request.getAttribute(REQUEST_ATTRIBUTE_MULTIPART)) {
1500            // check if this is a multipart request
1501            m_multiPartFileItems = CmsRequestUtil.readMultipartFileItems(request);
1502            if (m_multiPartFileItems != null) {
1503                // this was indeed a multipart form request
1504                m_parameterMap = CmsRequestUtil.readParameterMapFromMultiPart(
1505                    getCms().getRequestContext().getEncoding(),
1506                    m_multiPartFileItems);
1507                request.setAttribute(REQUEST_ATTRIBUTE_MULTIPART, Boolean.TRUE);
1508            }
1509        }
1510        if (m_parameterMap == null) {
1511            // the request was a "normal" request
1512            m_parameterMap = request.getParameterMap();
1513        }
1514
1515        List<Method> methods = paramSetMethods();
1516        Iterator<Method> i = methods.iterator();
1517        while (i.hasNext()) {
1518            Method m = i.next();
1519            String name = m.getName().substring(8).toLowerCase();
1520            String[] values = m_parameterMap.get(name);
1521            String value = null;
1522            if (values != null) {
1523                // get the parameter value from the map
1524                value = values[0];
1525            }
1526            if (CmsStringUtil.isEmpty(value)) {
1527                value = null;
1528            }
1529
1530            // TODO: this is very dangerous since most of the dialogs does not send encoded data
1531            // and by decoding not encoded data the data will get corrupted, for instance '1+2' will become '1 2'.
1532            // we should ensure that we decode the data only if the data has been encoded
1533            value = decodeParamValue(name, value);
1534            try {
1535                if (LOG.isDebugEnabled() && (value != null)) {
1536                    LOG.debug(Messages.get().getBundle().key(Messages.LOG_SET_PARAM_2, m.getName(), value));
1537                }
1538                m.invoke(this, new Object[] {value});
1539            } catch (InvocationTargetException ite) {
1540                // can usually be ignored
1541                if (LOG.isInfoEnabled()) {
1542                    LOG.info(ite.getLocalizedMessage());
1543                }
1544            } catch (IllegalAccessException eae) {
1545                // can usually be ignored
1546                if (LOG.isInfoEnabled()) {
1547                    LOG.info(eae.getLocalizedMessage());
1548                }
1549            }
1550        }
1551    }
1552
1553    /**
1554     * Returns the message String for the broadcast message alert of the workplace.<p>
1555     *
1556     * Caution: returns the pure message String (not escaped) or null, if no message is pending.<p>
1557     *
1558     * @return the message String for the broadcast message alert of the workplace
1559     */
1560    public String getBroadcastMessageString() {
1561
1562        CmsSessionInfo sessionInfo = OpenCms.getSessionManager().getSessionInfo(getSession());
1563        if (sessionInfo == null) {
1564            return null;
1565        }
1566        String sessionId = sessionInfo.getSessionId().toString();
1567        Buffer messageQueue = OpenCms.getSessionManager().getBroadcastQueue(sessionId);
1568        if (!messageQueue.isEmpty()) {
1569            // create message String
1570            StringBuffer result = new StringBuffer(512);
1571            // the user has pending messages, display them all
1572            while (!messageQueue.isEmpty()) {
1573                CmsBroadcast message = (CmsBroadcast)messageQueue.remove();
1574                result.append('[');
1575                result.append(getMessages().getDateTime(message.getSendTime()));
1576                result.append("] ");
1577                result.append(key(Messages.GUI_LABEL_BROADCASTMESSAGEFROM_0));
1578                result.append(' ');
1579                if (message.getUser() != null) {
1580                    result.append(message.getUser().getName());
1581                } else {
1582                    // system message
1583                    result.append(key(Messages.GUI_LABEL_BROADCAST_FROM_SYSTEM_0));
1584                }
1585                result.append(":\n");
1586                result.append(message.getMessage());
1587                result.append("\n\n");
1588            }
1589            return result.toString();
1590        }
1591        // no message pending, return null
1592        return null;
1593    }
1594
1595    /**
1596     * Returns the initialized cms object for the current user.<p>
1597     *
1598     * @return the initialized cms object for the current user
1599     */
1600    public CmsObject getCms() {
1601
1602        return m_cms;
1603    }
1604
1605    /**
1606     * Returns the current workplace encoding.<p>
1607     *
1608     * @return the current workplace encoding
1609     */
1610    public String getEncoding() {
1611
1612        return OpenCms.getWorkplaceManager().getEncoding();
1613    }
1614
1615    /**
1616     * Returns the uri (including context path) to the explorer file list.<p>
1617     *
1618     * @return the uri (including context path) to the explorer file list
1619     */
1620    public String getExplorerFileListFullUri() {
1621
1622        if (m_file_explorer_filelist != null) {
1623            return m_file_explorer_filelist;
1624        }
1625        synchronized (this) {
1626            m_file_explorer_filelist = OpenCms.getLinkManager().substituteLink(getCms(), FILE_EXPLORER_FILELIST);
1627        }
1628        return m_file_explorer_filelist;
1629    }
1630
1631    /**
1632     * Returns the html for the frame name and source and stores this information in the workplace settings.<p>
1633     *
1634     * @param frameName the name of the frame
1635     * @param uri the absolute path of the frame
1636     * @return the html for the frame name and source
1637     */
1638    public String getFrameSource(String frameName, String uri) {
1639
1640        String frameString = "name=\"" + frameName + "\" src=\"" + uri + "\"";
1641        int paramIndex = uri.indexOf("?");
1642        if (paramIndex != -1) {
1643            // remove request parameters from URI before putting it to Map
1644            uri = uri.substring(0, uri.indexOf("?"));
1645        }
1646        getSettings().getFrameUris().put(frameName, uri);
1647        return frameString;
1648    }
1649
1650    /**
1651     * Returns the JSP action element.<p>
1652     *
1653     * @return the JSP action element
1654     */
1655    public CmsJspActionElement getJsp() {
1656
1657        return m_jsp;
1658    }
1659
1660    /**
1661     * Returns the current users workplace locale settings.<p>
1662     *
1663     * @return the current users workplace locale setting
1664     */
1665    public Locale getLocale() {
1666
1667        return getSettings().getUserSettings().getLocale();
1668    }
1669
1670    /**
1671     * Returns the current used macro resolver instance.<p>
1672     *
1673     * @return the macro resolver
1674     */
1675    public CmsMacroResolver getMacroResolver() {
1676
1677        if (m_macroResolver == null) {
1678            // create a new macro resolver "with everything we got"
1679            m_macroResolver = CmsMacroResolver.newInstance()
1680                // initialize resolver with the objects available
1681                .setCmsObject(m_cms).setMessages(getMessages()).setJspPageContext(
1682                    (m_jsp == null) ? null : m_jsp.getJspContext());
1683            m_macroResolver.setParameterMap(m_parameterMap);
1684        }
1685        return m_macroResolver;
1686    }
1687
1688    /**
1689     * Returns the current used message object.<p>
1690     *
1691     * @return the current used message object
1692     */
1693    public CmsMessages getMessages() {
1694
1695        return m_messages;
1696    }
1697
1698    /**
1699     * Returns a list of FileItem instances parsed from the request, in the order that they were transmitted.<p>
1700     *
1701     * This list is automatically initialized from the createParameterMapFromMultiPart(HttpServletRequest) method.<p>
1702     *
1703     * @return list of FileItem instances parsed from the request, in the order that they were transmitted
1704     */
1705    public List<FileItem> getMultiPartFileItems() {
1706
1707        return m_multiPartFileItems;
1708    }
1709
1710    /**
1711     * Returns the path to the workplace static resources.<p>
1712     *
1713     * Workplaces static resources are images, css files etc.
1714     * These are exported during the installation of OpenCms,
1715     * and are usually only read from this exported location to
1716     * avoid the overhaead of accessing the database later.<p>
1717     *
1718     * @return the path to the workplace static resources
1719     */
1720    public String getResourceUri() {
1721
1722        if (m_resourceUri == null) {
1723            m_resourceUri = OpenCms.getSystemInfo().getContextPath() + CmsWorkplace.RFS_PATH_RESOURCES;
1724        }
1725        return m_resourceUri;
1726    }
1727
1728    /**
1729     * Returns the current user http session.<p>
1730     *
1731     * @return the current user http session
1732     */
1733    public HttpSession getSession() {
1734
1735        return m_session;
1736    }
1737
1738    /**
1739     * Returns the current users workplace settings.<p>
1740     *
1741     * @return the current users workplace settings
1742     */
1743    public CmsWorkplaceSettings getSettings() {
1744
1745        return m_settings;
1746    }
1747
1748    /**
1749     * Returns the path to the cascading stylesheets.<p>
1750     *
1751     * @param filename the name of the stylesheet
1752     * @return the path to the cascading stylesheets
1753     */
1754    public String getStyleUri(String filename) {
1755
1756        return getStyleUri(getJsp(), filename);
1757    }
1758
1759    /**
1760     * Builds the end html of the page.<p>
1761     *
1762     * @return the end html of the page
1763     */
1764    public String htmlEnd() {
1765
1766        return pageHtml(HTML_END, null);
1767    }
1768
1769    /**
1770     * Builds the start html of the page, including setting of DOCTYPE and
1771     * inserting a header with the content-type.<p>
1772     *
1773     * @param title the content for the title tag
1774     * @return the start html of the page
1775     */
1776    public String htmlStart(String title) {
1777
1778        return pageHtml(HTML_START, title);
1779    }
1780
1781    /**
1782     * Sets site and project in the workplace settings with the request values of parameters
1783     * <code>{@link CmsWorkplace#PARAM_WP_SITE}</code> and <code>{@link CmsWorkplace#PARAM_WP_PROJECT}</code>.<p>
1784     *
1785     * @param settings the workplace settings
1786     * @param request the current request
1787     *
1788     * @return true, if a reload of the main body frame is required
1789     */
1790    public boolean initSettings(CmsWorkplaceSettings settings, HttpServletRequest request) {
1791
1792        // check if the user requested a project change
1793        String project = request.getParameter(PARAM_WP_PROJECT);
1794        boolean reloadRequired = false;
1795        if (project != null) {
1796            reloadRequired = true;
1797            try {
1798                getCms().readProject(new CmsUUID(project));
1799            } catch (Exception e) {
1800                // project not found, set online project
1801                project = String.valueOf(CmsProject.ONLINE_PROJECT_ID);
1802            }
1803            try {
1804                m_cms.getRequestContext().setCurrentProject(getCms().readProject(new CmsUUID(project)));
1805            } catch (Exception e) {
1806                if (LOG.isInfoEnabled()) {
1807                    LOG.info(e.getLocalizedMessage(), e);
1808                }
1809            }
1810            settings.setProject(new CmsUUID(project));
1811        }
1812
1813        // check if the user requested a site change
1814        String site = request.getParameter(PARAM_WP_SITE);
1815        if (site != null) {
1816            reloadRequired = true;
1817            m_cms.getRequestContext().setSiteRoot(site);
1818            settings.setSite(site);
1819        }
1820
1821        // check which resource was requested
1822        String explorerResource = request.getParameter(PARAM_WP_EXPLORER_RESOURCE);
1823        if (explorerResource != null) {
1824            reloadRequired = true;
1825            settings.setExplorerResource(explorerResource, getCms());
1826        }
1827
1828        return reloadRequired;
1829    }
1830
1831    /**
1832     * Returns the forwarded flag.<p>
1833     *
1834     * @return the forwarded flag
1835     */
1836    public boolean isForwarded() {
1837
1838        return m_forwarded;
1839    }
1840
1841    /**
1842     * Returns true if the online help for the users current workplace language is installed.<p>
1843     *
1844     * @return true if the online help for the users current workplace language is installed
1845     */
1846    public boolean isHelpEnabled() {
1847
1848        return false;
1849    }
1850
1851    /**
1852     * Returns true if the currently processed element is an included sub element.<p>
1853     *
1854     * @return true if the currently processed element is an included sub element
1855     */
1856    public boolean isSubElement() {
1857
1858        return !getJsp().getRequestContext().getUri().equals(getJsp().info("opencms.request.element.uri"));
1859    }
1860
1861    /**
1862     * Returns the localized resource string for a given message key,
1863     * checking the workplace default resources and all module bundles.<p>
1864     *
1865     * If the key was not found, the return value is
1866     * <code>"??? " + keyName + " ???"</code>.<p>
1867     *
1868     * If the key starts with <code>"help."</code> and is not found,
1869     * the value <code>"index.html"</code> is returned.<p>
1870     *
1871     * @param keyName the key for the desired string
1872     * @return the resource string for the given key
1873     *
1874     * @see CmsMessages#key(String)
1875     */
1876    public String key(String keyName) {
1877
1878        return getMessages().key(keyName);
1879    }
1880
1881    /**
1882     * Returns the localized resource string for a given message key,
1883     * with the provided replacement parameters.<p>
1884     *
1885     * If the key was found in the bundle, it will be formatted using
1886     * a <code>{@link java.text.MessageFormat}</code> using the provided parameters.<p>
1887     *
1888     * If the key was not found in the bundle, the return value is
1889     * <code>"??? " + keyName + " ???"</code>. This will also be returned
1890     * if the bundle was not properly initialized first.
1891     *
1892     * @param keyName the key for the desired string
1893     * @param params the parameters to use for formatting
1894     * @return the resource string for the given key
1895     *
1896     * @see CmsMessages#key(String)
1897     */
1898    public String key(String keyName, Object[] params) {
1899
1900        return getMessages().key(keyName, params);
1901    }
1902
1903    /**
1904     * Returns the localized resource string for the given message key,
1905     * checking the workplace default resources and all module bundles.<p>
1906     *
1907     * If the key was not found, the provided default value
1908     * is returned.<p>
1909     *
1910     * @param keyName the key for the desired string
1911     * @param defaultValue the default value in case the key does not exist in the bundle
1912     * @return the resource string for the given key it it exists, or the given default if not
1913     *
1914     * @see CmsMessages#keyDefault(String, String)
1915     */
1916    public String keyDefault(String keyName, String defaultValue) {
1917
1918        return getMessages().keyDefault(keyName, defaultValue);
1919    }
1920
1921    /**
1922     * Returns the empty String "" if the provided value is null, otherwise just returns
1923     * the provided value.<p>
1924     *
1925     * Use this method in forms if a getParamXXX method is used, but a String (not null)
1926     * is required.
1927     *
1928     * @param value the String to check
1929     * @return the empty String "" if the provided value is null, otherwise just returns
1930     * the provided value
1931     */
1932    public String nullToEmpty(String value) {
1933
1934        if (value != null) {
1935            return value;
1936        }
1937        return "";
1938    }
1939
1940    /**
1941     * Builds the html of the body.<p>
1942     *
1943     * @param segment the HTML segment (START / END)
1944     * @param className optional class attribute to add to the body tag
1945     * @param parameters optional parameters to add to the body tag
1946     * @return the html of the body
1947     */
1948    public String pageBody(int segment, String className, String parameters) {
1949
1950        if (segment == HTML_START) {
1951            StringBuffer result = new StringBuffer(128);
1952            result.append("</head>\n<body unselectable=\"on\"");
1953            if (className != null) {
1954                result.append(" class=\"");
1955                result.append(className);
1956                result.append("\"");
1957            }
1958            if (CmsStringUtil.isNotEmpty(parameters)) {
1959                result.append(" ");
1960                result.append(parameters);
1961            }
1962            result.append(">\n");
1963            return result.toString();
1964        } else {
1965            return "</body>";
1966        }
1967    }
1968
1969    /**
1970     * Returns the default html for a workplace page, including setting of DOCTYPE and
1971     * inserting a header with the content-type.<p>
1972     *
1973     * @param segment the HTML segment (START / END)
1974     * @param title the title of the page, if null no title tag is inserted
1975     * @return the default html for a workplace page
1976     */
1977    public String pageHtml(int segment, String title) {
1978
1979        return pageHtmlStyle(segment, title, null);
1980    }
1981
1982    /**
1983     * Returns the default html for a workplace page, including setting of DOCTYPE and
1984     * inserting a header with the content-type, allowing the selection of an individual style sheet.<p>
1985     *
1986     * @param segment the HTML segment (START / END)
1987     * @param title the title of the page, if null no title tag is inserted
1988     * @param stylesheet the used style sheet, if null the default stylesheet 'workplace.css' is inserted
1989     * @return the default html for a workplace page
1990     */
1991    public String pageHtmlStyle(int segment, String title, String stylesheet) {
1992
1993        if (segment == HTML_START) {
1994            StringBuffer result = new StringBuffer(512);
1995            result.append("<!DOCTYPE html>\n");
1996            result.append("<html>\n<head>\n");
1997            if (title != null) {
1998                result.append("<title>");
1999                result.append(title);
2000                result.append("</title>\n");
2001            }
2002            result.append("<meta HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=");
2003            result.append(getEncoding());
2004            result.append("\">\n");
2005            result.append("<link rel=\"stylesheet\" type=\"text/css\" href=\"");
2006
2007            result.append(getStyleUri(getJsp(), stylesheet == null ? "workplace.css" : stylesheet));
2008            result.append("\">\n");
2009            return result.toString();
2010        } else {
2011            return "</html>";
2012        }
2013    }
2014
2015    /**
2016     * Returns all initialized parameters of the current workplace class
2017     * as hidden field tags that can be inserted in a form.<p>
2018     *
2019     * @return all initialized parameters of the current workplace class
2020     * as hidden field tags that can be inserted in a html form
2021     */
2022    public String paramsAsHidden() {
2023
2024        return paramsAsHidden(null);
2025    }
2026
2027    /**
2028     * Returns all initialized parameters of the current workplace class
2029     * that are not in the given exclusion list as hidden field tags that can be inserted in a form.<p>
2030     *
2031     * @param excludes the parameters to exclude
2032     *
2033     * @return all initialized parameters of the current workplace class
2034     * that are not in the given exclusion list as hidden field tags that can be inserted in a form
2035     */
2036    public String paramsAsHidden(Collection<String> excludes) {
2037
2038        StringBuffer result = new StringBuffer(512);
2039        Map<String, Object> params = paramValues();
2040        Iterator<Entry<String, Object>> i = params.entrySet().iterator();
2041        while (i.hasNext()) {
2042            Entry<String, Object> entry = i.next();
2043            String param = entry.getKey();
2044            if ((excludes == null) || (!excludes.contains(param))) {
2045                result.append("<input type=\"hidden\" name=\"");
2046                result.append(param);
2047                result.append("\" value=\"");
2048                String encoded = CmsEncoder.encode(
2049                    entry.getValue().toString(),
2050                    getCms().getRequestContext().getEncoding());
2051                result.append(encoded);
2052                result.append("\">\n");
2053            }
2054        }
2055        return result.toString();
2056    }
2057
2058    /**
2059     * Returns all initialized parameters of the current workplace class in the
2060     * form of a parameter map, i.e. the values are arrays.<p>
2061     *
2062     * @return all initialized parameters of the current workplace class in the
2063     * form of a parameter map
2064     */
2065    public Map<String, String[]> paramsAsParameterMap() {
2066
2067        return CmsRequestUtil.createParameterMap(paramValues());
2068    }
2069
2070    /**
2071     * Returns all initialized parameters of the current workplace class
2072     * as request parameters, i.e. in the form <code>key1=value1&key2=value2</code> etc.
2073     *
2074     * @return all initialized parameters of the current workplace class
2075     * as request parameters
2076     */
2077    public String paramsAsRequest() {
2078
2079        StringBuffer result = new StringBuffer(512);
2080        Map<String, Object> params = paramValues();
2081        Iterator<Entry<String, Object>> i = params.entrySet().iterator();
2082        while (i.hasNext()) {
2083            Entry<String, Object> entry = i.next();
2084            result.append(entry.getKey());
2085            result.append("=");
2086            result.append(CmsEncoder.encode(entry.getValue().toString(), getCms().getRequestContext().getEncoding()));
2087            if (i.hasNext()) {
2088                result.append("&");
2089            }
2090        }
2091        return result.toString();
2092    }
2093
2094    /**
2095     * Resolves the macros in the given String and replaces them by their localized keys.<p>
2096     *
2097     * The following macro contexts are available in the Workplace:<ul>
2098     * <li>Macros based on the current users OpenCms context (obtained from the current <code>{@link CmsObject}</code>).</li>
2099     * <li>Localized key macros (obtained from the current <code>{@link CmsMessages}</code>).</li>
2100     * <li>Macros from the current JSP page context (obtained by <code>{@link #getJsp()}</code>).</li>
2101     * </ul>
2102     *
2103     * @param input the input String containing the macros
2104     * @return the resolved String
2105     *
2106     * @see CmsMacroResolver#resolveMacros(String)
2107     */
2108    public String resolveMacros(String input) {
2109
2110        // resolve the macros
2111        return getMacroResolver().resolveMacros(input);
2112    }
2113
2114    /**
2115     * Sends a http redirect to the specified URI in the OpenCms VFS.<p>
2116     *
2117     * @param location the location the response is redirected to
2118     * @throws IOException in case redirection fails
2119     */
2120    public void sendCmsRedirect(String location) throws IOException {
2121
2122        // TOOD: IBM Websphere v5 has problems here, use forward instead (which has other problems)
2123        getJsp().getResponse().sendRedirect(OpenCms.getSystemInfo().getOpenCmsContext() + location);
2124    }
2125
2126    /**
2127     * Forwards to the specified location in the OpenCms VFS.<p>
2128     *
2129     * @param location the location the response is redirected to
2130     * @param params the map of parameters to use for the forwarded request
2131     *
2132     * @throws IOException in case the forward fails
2133     * @throws ServletException in case the forward fails
2134     */
2135    public void sendForward(String location, Map<String, String[]> params) throws IOException, ServletException {
2136
2137        setForwarded(true);
2138        // params must be arrays of String, ensure this is the case
2139        Map<String, String[]> parameters = CmsRequestUtil.createParameterMap(params);
2140        CmsRequestUtil.forwardRequest(
2141            getJsp().link(location),
2142            parameters,
2143            getJsp().getRequest(),
2144            getJsp().getResponse());
2145    }
2146
2147    /**
2148     * Sets the forwarded flag.<p>
2149     *
2150     * @param forwarded the forwarded flag to set
2151     */
2152    public void setForwarded(boolean forwarded) {
2153
2154        m_forwarded = forwarded;
2155    }
2156
2157    /**
2158     * Get a localized short key value for the workplace.<p>
2159     *
2160     * @param keyName name of the key
2161     * @return a localized short key value
2162     */
2163    public String shortKey(String keyName) {
2164
2165        String value = keyDefault(keyName + CmsMessages.KEY_SHORT_SUFFIX, (String)null);
2166        if (value == null) {
2167            // short key value not found, return "long" key value
2168            return key(keyName);
2169        }
2170        return value;
2171    }
2172
2173    /**
2174     * Auxiliary method for initialization of messages.<p>
2175     *
2176     * @param messages the {@link CmsMessages} to add
2177     */
2178    protected void addMessages(CmsMessages messages) {
2179
2180        if (messages != null) {
2181            m_messages.addMessages(messages);
2182        }
2183    }
2184
2185    /**
2186     * Auxiliary method for initialization of messages.<p>
2187     *
2188     * @param bundleName the resource bundle name to add
2189     */
2190    protected void addMessages(String bundleName) {
2191
2192        addMessages(new CmsMessages(bundleName, getLocale()));
2193    }
2194
2195    /**
2196     * Returns the values of all parameter methods of this workplace class instance.<p>
2197     *
2198     * @return the values of all parameter methods of this workplace class instance
2199     */
2200    protected Map<String, Object> allParamValues() {
2201
2202        List<Method> methods = paramGetMethods();
2203        Map<String, Object> map = new HashMap<String, Object>(methods.size());
2204        Iterator<Method> i = methods.iterator();
2205        while (i.hasNext()) {
2206            Method m = i.next();
2207            Object o = null;
2208            try {
2209                o = m.invoke(this, new Object[0]);
2210            } catch (InvocationTargetException ite) {
2211                // can usually be ignored
2212                if (LOG.isInfoEnabled()) {
2213                    LOG.info(ite);
2214                }
2215            } catch (IllegalAccessException eae) {
2216                // can usually be ignored
2217                if (LOG.isInfoEnabled()) {
2218                    LOG.info(eae);
2219                }
2220            }
2221            if (o == null) {
2222                o = "";
2223            }
2224            map.put(m.getName().substring(8).toLowerCase(), o);
2225        }
2226        return map;
2227    }
2228
2229    /**
2230     * Checks that the current user is a workplace user.<p>
2231     *
2232     * @throws CmsRoleViolationException if the user does not have the required role
2233     */
2234    protected void checkRole() throws CmsRoleViolationException {
2235
2236        OpenCms.getRoleManager().checkRole(m_cms, CmsRole.WORKPLACE_USER);
2237    }
2238
2239    /**
2240     * Decodes an individual parameter value.<p>
2241     *
2242     * In special cases some parameters might require a different-from-default
2243     * encoding. This is the case if the content of the parameter was
2244     * encoded using the JavaScript encodeURIComponent() method on the client,
2245     * which always encodes in UTF-8.<p>
2246     *
2247     * @param paramName the name of the parameter
2248     * @param paramValue the unencoded value of the parameter
2249     *
2250     * @return the encoded value of the parameter
2251     */
2252    protected String decodeParamValue(String paramName, String paramValue) {
2253
2254        if ((paramName != null) && (paramValue != null)) {
2255            return CmsEncoder.decode(paramValue, getCms().getRequestContext().getEncoding());
2256        } else {
2257            return null;
2258        }
2259    }
2260
2261    /**
2262     * Returns the map of parameters read from the current request.<p>
2263     *
2264     * This method will also handle parameters from forms
2265     * of type <code>multipart/form-data</code>.<p>
2266     *
2267     * @return the map of parameters read from the current request
2268     */
2269    protected Map<String, String[]> getParameterMap() {
2270
2271        return m_parameterMap;
2272    }
2273
2274    /**
2275     * Initializes the message object.<p>
2276     *
2277     * By default the {@link CmsWorkplaceMessages} are initialized.<p>
2278     *
2279     * You SHOULD override this method for setting the bundles you really need,
2280     * using the <code>{@link #addMessages(CmsMessages)}</code> or <code>{@link #addMessages(String)}</code> method.<p>
2281     */
2282    protected void initMessages() {
2283
2284        // no bundles are added by default as all core bundles are added as part of the WorkplaceModuleMessages
2285    }
2286
2287    /**
2288     * Sets the users time warp if configured and if the current timewarp setting is different or
2289     * clears the current time warp setting if the user has no configured timewarp.<p>
2290     *
2291     * Timwarping is controlled by the session attribute
2292     * {@link CmsContextInfo#ATTRIBUTE_REQUEST_TIME} with a value of type <code>Long</code>.<p>
2293     *
2294     * @param settings the user settings which are configured via the preferences dialog
2295     *
2296     * @param session the session of the user
2297     */
2298    protected void initTimeWarp(CmsUserSettings settings, HttpSession session) {
2299
2300        long timeWarpConf = settings.getTimeWarp();
2301        Long timeWarpSetLong = (Long)session.getAttribute(CmsContextInfo.ATTRIBUTE_REQUEST_TIME);
2302        long timeWarpSet = (timeWarpSetLong != null) ? timeWarpSetLong.longValue() : CmsContextInfo.CURRENT_TIME;
2303
2304        if (timeWarpConf == CmsContextInfo.CURRENT_TIME) {
2305            // delete:
2306            if (timeWarpSetLong != null) {
2307                // we may come from direct_edit.jsp: don't remove attribute, this is
2308                session.removeAttribute(CmsContextInfo.ATTRIBUTE_REQUEST_TIME);
2309            }
2310        } else {
2311            // this is dominant: if configured we will use it
2312            if (timeWarpSet != timeWarpConf) {
2313                session.setAttribute(CmsContextInfo.ATTRIBUTE_REQUEST_TIME, new Long(timeWarpConf));
2314            }
2315        }
2316    }
2317
2318    /**
2319     * Initializes this workplace class instance.<p>
2320     *
2321     * This method can be used in case there a workplace class was generated using
2322     * {@link Class#forName(java.lang.String)} to initialize the class members.<p>
2323     *
2324     * @param jsp the initialized JSP context
2325     */
2326    protected void initWorkplaceMembers(CmsJspActionElement jsp) {
2327
2328        if (jsp != null) {
2329            m_jsp = jsp;
2330            initWorkplaceMembers(m_jsp.getCmsObject(), m_jsp.getRequest().getSession());
2331        }
2332    }
2333
2334    /**
2335     * Initializes this workplace class instance.<p>
2336     *
2337     * @param cms the user context
2338     * @param session the session
2339     */
2340    protected void initWorkplaceMembers(CmsObject cms, HttpSession session) {
2341
2342        m_cms = cms;
2343        m_session = session;
2344        // check role
2345        try {
2346            checkRole();
2347        } catch (CmsRoleViolationException e) {
2348            throw new CmsIllegalStateException(e.getMessageContainer(), e);
2349        }
2350
2351        // get / create the workplace settings
2352        m_settings = (CmsWorkplaceSettings)m_session.getAttribute(CmsWorkplaceManager.SESSION_WORKPLACE_SETTINGS);
2353
2354        if (m_settings == null) {
2355            // create the settings object
2356            m_settings = new CmsWorkplaceSettings();
2357            m_settings = initWorkplaceSettings(m_cms, m_settings, false);
2358
2359            storeSettings(m_session, m_settings);
2360        }
2361
2362        // initialize messages
2363        CmsMessages messages = OpenCms.getWorkplaceManager().getMessages(getLocale());
2364        // generate a new multi messages object and add the messages from the workplace
2365        m_messages = new CmsMultiMessages(getLocale());
2366        m_messages.addMessages(messages);
2367        initMessages();
2368
2369        if (m_jsp != null) {
2370            // check request for changes in the workplace settings
2371            initWorkplaceRequestValues(m_settings, m_jsp.getRequest());
2372        }
2373        // set cms context accordingly
2374        initWorkplaceCmsContext(m_settings, m_cms);
2375
2376        // timewarp reset logic
2377        initTimeWarp(m_settings.getUserSettings(), m_session);
2378    }
2379
2380    /**
2381     * Analyzes the request for workplace parameters and adjusts the workplace
2382     * settings accordingly.<p>
2383     *
2384     * @param settings the workplace settings
2385     * @param request the current request
2386     */
2387    protected abstract void initWorkplaceRequestValues(CmsWorkplaceSettings settings, HttpServletRequest request);
2388
2389    /**
2390     * Returns the values of all parameter methods of this workplace class instance.<p>
2391     *
2392     * @return the values of all parameter methods of this workplace class instance
2393     */
2394    protected Map<String, Object> paramValues() {
2395
2396        List<Method> methods = paramGetMethods();
2397        Map<String, Object> map = new HashMap<String, Object>(methods.size());
2398        Iterator<Method> i = methods.iterator();
2399        while (i.hasNext()) {
2400            Method m = i.next();
2401            Object o = null;
2402            try {
2403                o = m.invoke(this, new Object[0]);
2404            } catch (InvocationTargetException ite) {
2405                // can usually be ignored
2406                if (LOG.isInfoEnabled()) {
2407                    LOG.info(ite.getLocalizedMessage());
2408                }
2409            } catch (IllegalAccessException eae) {
2410                // can usually be ignored
2411                if (LOG.isInfoEnabled()) {
2412                    LOG.info(eae.getLocalizedMessage());
2413                }
2414            }
2415            if (o != null) {
2416                map.put(m.getName().substring(8).toLowerCase(), o);
2417            }
2418        }
2419        return map;
2420    }
2421
2422    /**
2423     * Replaces the site title, if necessary.<p>
2424     *
2425     * @param title the site title
2426     *
2427     * @return the new site title
2428     */
2429    protected String substituteSiteTitle(String title) {
2430
2431        return substituteSiteTitleStatic(title, getSettings().getUserSettings().getLocale());
2432    }
2433
2434    /**
2435     * Helper method to change back from the temporary project to the current project.<p>
2436     *
2437     * @throws CmsException if switching back fails
2438     */
2439    protected void switchToCurrentProject() throws CmsException {
2440
2441        if (m_currentProjectId != null) {
2442            // switch back to the current users project
2443            getCms().getRequestContext().setCurrentProject(getCms().readProject(m_currentProjectId));
2444        }
2445    }
2446
2447    /**
2448     * Helper method to change the current project to the temporary file project.<p>
2449     *
2450     * The id of the old project is stored in a member variable to switch back.<p>
2451     *
2452     * @return the id of the tempfileproject
2453     * @throws CmsException if getting the tempfileproject id fails
2454     */
2455    protected CmsUUID switchToTempProject() throws CmsException {
2456
2457        // store the current project id in member variable
2458        m_currentProjectId = getSettings().getProject();
2459        CmsUUID tempProjectId = OpenCms.getWorkplaceManager().getTempFileProjectId();
2460        getCms().getRequestContext().setCurrentProject(getCms().readProject(tempProjectId));
2461        return tempProjectId;
2462    }
2463
2464    /**
2465     * Sets the cms request context and other cms related settings to the
2466     * values stored in the workplace settings.<p>
2467     *
2468     * @param settings the workplace settings
2469     * @param cms the current cms object
2470     */
2471    private void initWorkplaceCmsContext(CmsWorkplaceSettings settings, CmsObject cms) {
2472
2473        CmsRequestContext reqCont = cms.getRequestContext();
2474
2475        // check project setting
2476        if (!settings.getProject().equals(reqCont.getCurrentProject().getUuid())) {
2477            try {
2478                reqCont.setCurrentProject(cms.readProject(settings.getProject()));
2479            } catch (CmsDbEntryNotFoundException e) {
2480                try {
2481                    // project not found, set current project and settings to online project
2482                    reqCont.setCurrentProject(cms.readProject(CmsProject.ONLINE_PROJECT_ID));
2483                    settings.setProject(CmsProject.ONLINE_PROJECT_ID);
2484                } catch (CmsException ex) {
2485                    // log error
2486                    if (LOG.isInfoEnabled()) {
2487                        LOG.info(ex.getLocalizedMessage());
2488                    }
2489                }
2490            } catch (CmsException e1) {
2491                if (LOG.isInfoEnabled()) {
2492                    LOG.info(e1.getLocalizedMessage());
2493                }
2494            }
2495        }
2496
2497        // check site setting
2498        if (!(settings.getSite().equals(reqCont.getSiteRoot()))) {
2499            // site was switched, set new site root
2500            reqCont.setSiteRoot(settings.getSite());
2501            // removed setting explorer resource to "/" to get the stored folder
2502        }
2503    }
2504
2505    /**
2506     * Returns a list of all methods of the current class instance that
2507     * start with "getParam" and have no parameters.<p>
2508     *
2509     * @return a list of all methods of the current class instance that
2510     * start with "getParam" and have no parameters
2511     */
2512    private List<Method> paramGetMethods() {
2513
2514        List<Method> list = new ArrayList<Method>();
2515        Method[] methods = this.getClass().getMethods();
2516        int length = methods.length;
2517        for (int i = 0; i < length; i++) {
2518            Method method = methods[i];
2519            if (method.getName().startsWith("getParam") && (method.getParameterTypes().length == 0)) {
2520                if (DEBUG) {
2521                    System.err.println("getMethod: " + method.getName());
2522                }
2523                list.add(method);
2524            }
2525        }
2526        return list;
2527    }
2528
2529    /**
2530     * Returns a list of all methods of the current class instance that
2531     * start with "setParam" and have exactly one String parameter.<p>
2532     *
2533     * @return a list of all methods of the current class instance that
2534     * start with "setParam" and have exactly one String parameter
2535     */
2536    private List<Method> paramSetMethods() {
2537
2538        List<Method> list = new ArrayList<Method>();
2539        Method[] methods = getClass().getMethods();
2540        int length = methods.length;
2541        for (int i = 0; i < length; i++) {
2542            Method method = methods[i];
2543            if (method.getName().startsWith("setParam")
2544                && (method.getParameterTypes().length == 1)
2545                && (method.getParameterTypes()[0].equals(java.lang.String.class))) {
2546                if (DEBUG) {
2547                    System.err.println("setMethod: " + method.getName());
2548                }
2549                list.add(method);
2550            }
2551        }
2552        return list;
2553    }
2554}