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.configuration.CmsDefaultUserSettings;
031import org.opencms.db.CmsUserSettings;
032import org.opencms.file.CmsProject;
033import org.opencms.file.CmsResourceFilter;
034import org.opencms.i18n.CmsEncoder;
035import org.opencms.jsp.CmsJspActionElement;
036import org.opencms.main.CmsException;
037import org.opencms.main.CmsLog;
038import org.opencms.main.OpenCms;
039import org.opencms.security.CmsOrganizationalUnit;
040import org.opencms.site.CmsSite;
041import org.opencms.synchronize.CmsSynchronizeSettings;
042import org.opencms.util.CmsFileUtil;
043import org.opencms.util.CmsRequestUtil;
044import org.opencms.util.CmsStringUtil;
045
046import java.util.ArrayList;
047import java.util.Arrays;
048import java.util.Collections;
049import java.util.Iterator;
050import java.util.List;
051import java.util.Map.Entry;
052import java.util.Set;
053
054import javax.servlet.http.HttpServletRequest;
055import javax.servlet.http.HttpSession;
056
057import org.apache.commons.logging.Log;
058
059/**
060 * Provides methods for building the main framesets of the OpenCms Workplace.<p>
061 *
062 * The following files use this class:
063 * <ul>
064 * <li>/views/top.html
065 * <li>/views/top_foot.html
066 * <li>/views/top_head.html
067 * </ul>
068 * <p>
069 *
070 * @since 6.0.0
071 */
072public class CmsFrameset extends CmsWorkplace {
073
074    /** Path to the JSP workplace frame loader file. */
075    public static final String JSP_WORKPLACE_URI = CmsWorkplace.JSP_WORKPLACE_URI;
076
077    /** The request parameter for the selection of the frame. */
078    public static final String PARAM_WP_FRAME = "wpFrame";
079
080    /** The request parameter for the workplace start selection. */
081    public static final String PARAM_WP_START = "wpStart";
082
083    /** The request parameter for the workplace view selection. */
084    public static final String PARAM_WP_VIEW = "wpView";
085
086    /** The names of the supported frames. */
087    private static final String[] FRAMES = {"top", "head", "body", "foot"};
088
089    /** The names of the supported frames in a list. */
090    public static final List<String> FRAMES_LIST = Collections.unmodifiableList(Arrays.asList(FRAMES));
091
092    /** The log object for this class. */
093    private static final Log LOG = CmsLog.getLog(CmsFrameset.class);
094
095    /** Indicates if a reload of the main body frame is required. */
096    private boolean m_reloadRequired;
097
098    /**
099     * Public constructor.<p>
100     *
101     * @param jsp an initialized JSP action element
102     */
103    public CmsFrameset(CmsJspActionElement jsp) {
104
105        super(jsp);
106    }
107
108    /**
109     * Performs additional filtering on the list of projects for the project selector.<p>
110     *
111     * @param projects the original project list
112     *
113     * @return the filtered project list
114     */
115    public List<CmsProject> filterProjectsForSelector(List<CmsProject> projects) {
116
117        List<CmsProject> result = new ArrayList<CmsProject>();
118        for (CmsProject project : projects) {
119            if (!project.isHiddenFromSelector()) {
120                result.add(project);
121            }
122        }
123        return result;
124    }
125
126    /**
127     * Returns the javascript code for the broadcast message alert in the foot of the workplace.<p>
128     *
129     * @return javascript code showing an alert box when the foot load
130     */
131    public String getBroadcastMessage() {
132
133        StringBuffer result = new StringBuffer(512);
134        String message = getBroadcastMessageString();
135
136        if (CmsStringUtil.isNotEmpty(message)) {
137            // create a javascript alert for the message
138            result.append("\n<script >\n<!--\n");
139            // the timeout gives the frameset enough time to load before the alert is shown
140            result.append("function showMessage() {\n");
141            result.append("\talert(decodeURIComponent(\"");
142            // the user has pending messages, display them all
143            result.append(CmsEncoder.escapeWBlanks(message, CmsEncoder.ENCODING_UTF_8));
144            result.append("\"));\n}\n");
145            result.append("setTimeout('showMessage();', 2000);");
146            result.append("\n//-->\n</script>");
147        }
148        return result.toString();
149    }
150
151    /**
152     * Returns the remote ip address of the current user.<p>
153     *
154     * @return the remote ip address of the current user
155     */
156    public String getLoginAddress() {
157
158        return getCms().getRequestContext().getRemoteAddress();
159    }
160
161    /**
162     * Returns the last login time of the current user in localized format.<p>
163     *
164     * @return the last login time of the current user in localized format
165     */
166    public String getLoginTime() {
167
168        return getMessages().getDateTime(getSettings().getUser().getLastlogin());
169    }
170
171    /**
172     * Returns the html for the "preferences" button depending on the current users permissions and
173     * the default workplace settings.<p>
174     *
175     * @return the html for the "preferences" button
176     */
177    public String getPreferencesButton() {
178
179        int buttonStyle = getSettings().getUserSettings().getWorkplaceButtonStyle();
180        if (!getCms().getRequestContext().getCurrentUser().isManaged()) {
181            return button(
182                "../commons/preferences.jsp",
183                "body",
184                "preferences.png",
185                Messages.GUI_BUTTON_PREFERENCES_0,
186                buttonStyle);
187        } else {
188            return button(null, null, "preferences_in.png", Messages.GUI_BUTTON_PREFERENCES_0, buttonStyle);
189        }
190    }
191
192    /**
193     * Returns a html select box filled with the current users accessible projects.<p>
194     *
195     * @param htmlAttributes attributes that will be inserted into the generated html
196     * @param htmlWidth additional style attributes containing width information
197     * @return a html select box filled with the current users accessible projects
198     */
199    public String getProjectSelect(String htmlAttributes, String htmlWidth) {
200
201        // get all project information
202        List<CmsProject> allProjects;
203        try {
204            String ouFqn = "";
205            CmsUserSettings settings = new CmsUserSettings(getCms());
206            if (!settings.getListAllProjects()) {
207                ouFqn = getCms().getRequestContext().getCurrentUser().getOuFqn();
208            }
209            allProjects = OpenCms.getOrgUnitManager().getAllAccessibleProjects(
210                getCms(),
211                ouFqn,
212                settings.getListAllProjects());
213        } catch (CmsException e) {
214            // should usually never happen
215            if (LOG.isErrorEnabled()) {
216                LOG.error(e.getLocalizedMessage(), e);
217            }
218            allProjects = Collections.emptyList();
219        }
220        allProjects = filterProjectsForSelector(allProjects);
221
222        boolean singleOu = true;
223        String ouFqn = null;
224        Iterator<CmsProject> itProjects = allProjects.iterator();
225        while (itProjects.hasNext()) {
226            CmsProject prj = itProjects.next();
227            if (prj.isOnlineProject()) {
228                // skip the online project
229                continue;
230            }
231            if (ouFqn == null) {
232                // set the first ou
233                ouFqn = prj.getOuFqn();
234            }
235            if (!ouFqn.equals(prj.getOuFqn())) {
236                // break if one different ou is found
237                singleOu = false;
238                break;
239            }
240        }
241
242        List<String> options = new ArrayList<String>();
243        List<String> values = new ArrayList<String>();
244        int selectedIndex = -1;
245        int ouDefaultProjIndex = -1;
246
247        CmsOrganizationalUnit ou = null;
248        try {
249            ou = OpenCms.getOrgUnitManager().readOrganizationalUnit(getCms(), getCms().getRequestContext().getOuFqn());
250        } catch (CmsException e) {
251            // should never happen, ignore
252        }
253
254        // now loop through all projects and fill the result vectors
255        for (int i = 0, n = allProjects.size(); i < n; i++) {
256            CmsProject project = allProjects.get(i);
257            String projectId = project.getUuid().toString();
258            String projectName = project.getSimpleName();
259            if (!singleOu && !project.isOnlineProject()) {
260                try {
261                    projectName = projectName
262                        + " - "
263                        + OpenCms.getOrgUnitManager().readOrganizationalUnit(
264                            getCms(),
265                            project.getOuFqn()).getDisplayName(getLocale());
266                } catch (CmsException e) {
267                    projectName = projectName + " - " + project.getOuFqn();
268                }
269            }
270
271            values.add(projectId);
272            options.add(projectName);
273
274            if (project.getUuid().equals(getSettings().getProject())) {
275                // this is the user's current project
276                selectedIndex = i;
277            }
278            if ((ou != null) && project.getUuid().equals(ou.getProjectId())) {
279                ouDefaultProjIndex = i;
280            }
281        }
282        if (selectedIndex == -1) {
283            if (ouDefaultProjIndex == -1) {
284                selectedIndex = 0;
285            } else {
286                selectedIndex = ouDefaultProjIndex;
287            }
288        }
289        if (CmsStringUtil.isNotEmpty(htmlWidth)) {
290            StringBuffer buf = new StringBuffer(htmlAttributes.length() + htmlWidth.length() + 2);
291            buf.append(htmlAttributes);
292            buf.append(" ");
293            buf.append(htmlWidth);
294            htmlAttributes = buf.toString();
295        }
296
297        return buildSelect(htmlAttributes, options, values, selectedIndex);
298    }
299
300    /**
301     * Returns the html for the "publish project" button depending on the current users permissions and the default
302     * workplace settings.<p>
303     *
304     * @return the html for the "publish project" button
305     */
306    public String getPublishButton() {
307
308        String publishButton = OpenCms.getWorkplaceManager().getDefaultUserSettings().getPublishButtonAppearance();
309        if (CmsDefaultUserSettings.PUBLISHBUTTON_SHOW_NEVER.equals(publishButton)) {
310            return "";
311        }
312
313        int buttonStyle = getSettings().getUserSettings().getWorkplaceButtonStyle();
314
315        if (CmsDefaultUserSettings.PUBLISHBUTTON_SHOW_AUTO.equals(publishButton)) {
316            if (getCms().isManagerOfProject()) {
317                return button(
318                    "../../workplace/commons/publish_project.jsp",
319                    "body",
320                    "publish.png",
321                    Messages.GUI_BUTTON_PUBLISH_0,
322                    buttonStyle);
323            } else {
324                return "";
325            }
326        }
327
328        if (getCms().isManagerOfProject()) {
329            return (button(
330                "../../workplace/commons/publish_project.jsp",
331                "body",
332                "publish.png",
333                Messages.GUI_BUTTON_PUBLISH_0,
334                buttonStyle));
335        } else {
336            return (button(null, null, "publish_in.png", Messages.GUI_BUTTON_PUBLISH_0, buttonStyle));
337        }
338    }
339
340    /**
341     * Returns the html for the "publish queue" button.<p>
342     *
343     * @return the html for the "publish queue" button
344     */
345    public String getPublishQueueButton() {
346
347        int buttonStyle = getSettings().getUserSettings().getWorkplaceButtonStyle();
348        StringBuffer js = new StringBuffer(128);
349        js.append("javascript:if (parent.body.admin_content && parent.body.admin_menu) {");
350        js.append("parent.body.location.href = '");
351        js.append(getJsp().link("/system/workplace/views/admin/admin-fs.jsp?root=admin&path=/publishqueue"));
352        js.append("';");
353        js.append("} else {");
354        js.append("parent.body.explorer_body.explorer_files.location.href = '");
355        js.append(getJsp().link("/system/workplace/views/admin/admin-fs.jsp?root=explorer&path=/publishqueue&menu=no"));
356        js.append("';");
357        js.append("};");
358        return button(js.toString(), null, "publish_queue.png", Messages.GUI_BUTTON_PUBLISHQUEUE_0, buttonStyle);
359    }
360
361    /**
362     * Returns a html select box filled with the current users accessible sites.<p>
363     *
364     * @param htmlAttributes attributes that will be inserted into the generated html
365     * @return a html select box filled with the current users accessible sites
366     */
367    public String getSiteSelect(String htmlAttributes) {
368
369        List<String> options = new ArrayList<String>();
370        List<String> values = new ArrayList<String>();
371        int selectedIndex = 0;
372
373        List<CmsSite> sites = OpenCms.getSiteManager().getAvailableSites(getCms(), true);
374
375        Iterator<CmsSite> i = sites.iterator();
376        int pos = 0;
377        while (i.hasNext()) {
378            CmsSite site = i.next();
379            values.add(site.getSiteRoot());
380            options.add(substituteSiteTitle(site.getTitle()));
381            String siteRoot = CmsFileUtil.addTrailingSeparator(site.getSiteRoot());
382            String settingsSiteRoot = getSettings().getSite();
383            if (settingsSiteRoot != null) {
384                settingsSiteRoot = CmsFileUtil.addTrailingSeparator(settingsSiteRoot);
385            }
386            if (siteRoot.equals(settingsSiteRoot)) {
387                // this is the user's current site
388                selectedIndex = pos;
389            }
390            pos++;
391        }
392
393        return buildSelect(htmlAttributes, options, values, selectedIndex);
394    }
395
396    /**
397     * Returns the startup URI for display in the main body frame, this can
398     * either be the user default view, or (if set) a specific startup resource.<p>
399     *
400     * @return the startup URI for display in the main body frame
401     */
402    public String getStartupUri() {
403
404        String result = getSettings().getViewStartup();
405        if (result == null) {
406            // no specific startup URI is set, use view from user settings
407            result = getSettings().getViewUri();
408        } else {
409            // reset the startup URI, so that it is not displayed again on reload of the frameset
410            getSettings().setViewStartup(null);
411        }
412        // add eventual request parameters to startup uri
413        if (getJsp().getRequest().getParameterMap().size() > 0) {
414            @SuppressWarnings("unchecked")
415            Set<Entry<String, String[]>> params = getJsp().getRequest().getParameterMap().entrySet();
416            Iterator<Entry<String, String[]>> i = params.iterator();
417            while (i.hasNext()) {
418                Entry<?, ?> entry = i.next();
419                result = CmsRequestUtil.appendParameter(
420                    result,
421                    (String)entry.getKey(),
422                    ((String[])entry.getValue())[0]);
423            }
424        }
425        // append the frame name to the startup uri
426        return CmsRequestUtil.appendParameter(result, CmsFrameset.PARAM_WP_FRAME, FRAMES[2]);
427    }
428
429    /**
430     * Returns a html select box filled with the views accessible by the current user.<p>
431     *
432     * @param htmlAttributes attributes that will be inserted into the generated html
433     * @return a html select box filled with the views accessible by the current user
434     */
435    public String getViewSelect(String htmlAttributes) {
436
437        List<String> options = new ArrayList<String>();
438        List<String> values = new ArrayList<String>();
439        int selectedIndex = 0;
440
441        // loop through the vectors and fill the result vectors
442        Iterator<CmsWorkplaceView> i = OpenCms.getWorkplaceManager().getViews().iterator();
443        int count = -1;
444        String currentView = getSettings().getViewUri();
445        if (CmsStringUtil.isNotEmpty(currentView)) {
446            // remove possible parameters from current view
447            int pos = currentView.indexOf('?');
448            if (pos >= 0) {
449                currentView = currentView.substring(0, pos);
450            }
451        }
452        while (i.hasNext()) {
453            CmsWorkplaceView view = i.next();
454            if (getCms().existsResource(view.getUri(), CmsResourceFilter.ONLY_VISIBLE_NO_DELETED)) {
455                count++;
456                // ensure the current user has +v+r permissions on the view
457                String loopLink = getJsp().link(view.getUri());
458                String localizedKey = resolveMacros(view.getKey());
459                options.add(localizedKey);
460                values.add(loopLink);
461
462                if (loopLink.equals(currentView)) {
463                    selectedIndex = count;
464                }
465            }
466        }
467
468        return buildSelect(htmlAttributes, options, values, selectedIndex);
469    }
470
471    /**
472     * Returns the reload URI for the OpenCms workplace.<p>
473     *
474     * @return the reload URI for the OpenCms workplace
475     */
476    public String getWorkplaceReloadUri() {
477
478        return getJsp().link(CmsFrameset.JSP_WORKPLACE_URI);
479    }
480
481    /**
482     * Returns <code>true</code> if a reload of the main body frame is required.<p>
483     *
484     * This value is modified with the select options (project, site or view) in the head frame of
485     * the Workplace. If a user changes one of these select values, the head frame is posted
486     * "against itself". The posted values will be processed by this class, causing
487     * the internal Workplace settings to change. After these settings have been changed,
488     * a reload of the main body frame is required in order to update it with the new values.
489     * A JavaScript in the Workplace head frame will be executed in this case.<p>
490     *
491     * @return <code>true</code> if a reload of the main body frame is required
492     */
493    public boolean isReloadRequired() {
494
495        return m_reloadRequired;
496    }
497
498    /**
499     * Returns true if the user has enabled synchronization.<p>
500     *
501     * @return true if the user has enabled synchronization
502     */
503    public boolean isSyncEnabled() {
504
505        CmsSynchronizeSettings syncSettings = getSettings().getUserSettings().getSynchronizeSettings();
506        return (syncSettings != null) && syncSettings.isSyncEnabled();
507    }
508
509    /**
510     * Indicates if the site selector should be shown in the top frame depending on the count of accessible sites.<p>
511     *
512     * @return true if site selector should be shown, otherwise false
513     */
514    public boolean showSiteSelector() {
515
516        if (getSettings().getUserSettings().getRestrictExplorerView()) {
517            // restricted explorer view to site and folder, do not show site selector
518            return false;
519        }
520        // count available sites
521        int siteCount = OpenCms.getSiteManager().getAvailableSites(getCms(), true).size();
522        return (siteCount > 1);
523    }
524
525    /**
526     * @see org.opencms.workplace.CmsWorkplace#initTimeWarp(org.opencms.db.CmsUserSettings, javax.servlet.http.HttpSession)
527     */
528    @Override
529    protected void initTimeWarp(CmsUserSettings settings, HttpSession session) {
530
531        // overriden to avoid deletion of the configured time warp:
532        // this is triggered by editors and in auto time warping a direct edit
533        // must not delete a potential auto warped request time
534    }
535
536    /**
537     * @see org.opencms.workplace.CmsWorkplace#initWorkplaceRequestValues(org.opencms.workplace.CmsWorkplaceSettings, javax.servlet.http.HttpServletRequest)
538     */
539    @Override
540    protected void initWorkplaceRequestValues(CmsWorkplaceSettings settings, HttpServletRequest request) {
541
542        // check if a startup page has been set
543        String frame = CmsRequestUtil.getNotEmptyDecodedParameter(request, CmsFrameset.PARAM_WP_FRAME);
544        if ((frame == null) || (FRAMES_LIST.indexOf(frame) < 0)) {
545            // illegal or no frame selected, assume the "top" frame
546            frame = FRAMES[0];
547        }
548
549        if (FRAMES[0].equals(frame)) {
550            // top frame requested - execute special reload actions
551            topFrameReload(settings);
552        }
553
554        // check if a startup page has been set
555        String startup = CmsRequestUtil.getNotEmptyDecodedParameter(request, CmsFrameset.PARAM_WP_START);
556        if (startup != null) {
557            m_reloadRequired = true;
558            settings.setViewStartup(startup);
559        }
560
561        // check if the user requested a view change
562        String view = request.getParameter(CmsFrameset.PARAM_WP_VIEW);
563        if (view != null) {
564            m_reloadRequired = true;
565            settings.setViewUri(view);
566            settings.getFrameUris().put("body", view);
567        }
568
569        m_reloadRequired = initSettings(settings, request) || m_reloadRequired;
570    }
571
572    /**
573     * Performs certain clear cache actions if the top frame is reloaded.<p>
574     *
575     * @param settings the current users workplace settings
576     */
577    protected void topFrameReload(CmsWorkplaceSettings settings) {
578
579        // ensure to read the settings from the database
580        initUserSettings(getCms(), settings, true);
581
582        // reset the HTML list in order to force a full reload
583        settings.setListObject(null);
584    }
585}