001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software GmbH & Co. KG, please see the
018 * company website: https://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: https://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.workplace.commons;
029
030import org.opencms.configuration.CmsParameterConfiguration;
031import org.opencms.file.CmsObject;
032import org.opencms.file.CmsResource;
033import org.opencms.file.CmsResourceFilter;
034import org.opencms.jsp.CmsJspActionElement;
035import org.opencms.lock.CmsLockFilter;
036import org.opencms.main.CmsException;
037import org.opencms.main.CmsLog;
038import org.opencms.relations.CmsRelation;
039import org.opencms.relations.CmsRelationFilter;
040import org.opencms.security.CmsPermissionSet;
041import org.opencms.util.CmsStringUtil;
042import org.opencms.workplace.CmsDialog;
043import org.opencms.workplace.CmsDialogSelector;
044import org.opencms.workplace.CmsMultiDialog;
045import org.opencms.workplace.CmsWorkplace;
046import org.opencms.workplace.CmsWorkplaceSettings;
047import org.opencms.workplace.I_CmsDialogHandler;
048import org.opencms.workplace.list.CmsListExplorerColumn;
049
050import java.io.IOException;
051import java.util.ArrayList;
052import java.util.Collections;
053import java.util.HashMap;
054import java.util.HashSet;
055import java.util.Iterator;
056import java.util.List;
057import java.util.Map;
058import java.util.Set;
059
060import org.apache.commons.logging.Log;
061
062import jakarta.servlet.ServletException;
063import jakarta.servlet.http.HttpServletRequest;
064import jakarta.servlet.http.HttpServletResponse;
065import jakarta.servlet.jsp.JspException;
066import jakarta.servlet.jsp.PageContext;
067
068/**
069 * Creates the dialogs for locking, unlocking or steal lock operations on a resource.<p>
070 *
071 * The following files use this class:
072 * <ul>
073 * <li>/commons/lock_standard.jsp
074 * <li>/commons/lockchange_standard.jsp
075 * <li>/commons/unlock_standard.jsp
076 * <li>/commons/locks.jsp
077 * </ul>
078 * <p>
079 *
080 * @since 6.0.0
081 */
082public class CmsLock extends CmsMultiDialog implements I_CmsDialogHandler {
083
084    /** Value for the action: confirmed. */
085    public static final int ACTION_SUBMIT_NOCONFIRMATION = 200;
086
087    /** Request parameter value for the action: submit form without user interaction. */
088    public static final String DIALOG_SUBMIT_NOCONFIRMATION = "submitnoconfirmation";
089
090    /** The dialog type: lock a resource. */
091    public static final String DIALOG_TYPE_LOCK = "lock";
092
093    /** The dialog type: Steal a lock. */
094    public static final String DIALOG_TYPE_LOCKCHANGE = "lockchange";
095
096    /** The dialog type: locked subresources. */
097    public static final String DIALOG_TYPE_LOCKS = "locks";
098
099    /** The dialog type: unlock a resource. */
100    public static final String DIALOG_TYPE_UNLOCK = "unlock";
101
102    /** Request parameter name for the 'include unpublished related resources' flag. */
103    public static final String PARAM_INCLUDERELATED = "includerelated";
104
105    /** Request parameter name for the project id. */
106    public static final String PARAM_PROJECT_ID = "projectid";
107
108    /** Request parameter name for the publishsiblings parameter. */
109    public static final String PARAM_PUBLISHSIBLINGS = "publishsiblings";
110
111    /** Request parameter name for the 'show own locked resources' flag. */
112    public static final String PARAM_SHOWOWNLOCKS = "showownlocks";
113
114    /** Request parameter name for the source dialog uri. */
115    public static final String PARAM_SOURCE_DIALOG = "sourcedialog";
116
117    /** Request parameter name for the subresources parameter. */
118    public static final String PARAM_SUBRESOURCES = "subresources";
119
120    /** Type of the operation which is performed: lock resource. */
121    public static final int TYPE_LOCK = 1;
122
123    /** Type of the operation which is performed: steal a lock. */
124    public static final int TYPE_LOCKCHANGE = 2;
125
126    /** Type of the operation which is performed: locked subresources. */
127    public static final int TYPE_LOCKS = 4;
128
129    /** Type of the operation which is performed: unlock resource. */
130    public static final int TYPE_UNLOCK = 3;
131
132    /** The lock dialog URI. */
133    public static final String URI_LOCK_DIALOG = PATH_DIALOGS + "lock_standard.jsp";
134
135    /** The steal lock dialog URI. */
136    public static final String URI_LOCKCHANGE_DIALOG = PATH_DIALOGS + "lockchange_standard.jsp";
137
138    /** The locks dialog URI. */
139    public static final String URI_LOCKS_DIALOG = PATH_DIALOGS + "locks.jsp";
140
141    /** The unlock dialog URI. */
142    public static final String URI_UNLOCK_DIALOG = PATH_DIALOGS + "unlock_standard.jsp";
143
144    /** The log object for this class. */
145    private static final Log LOG = CmsLog.getLog(CmsLock.class);
146
147    /** the filter to get all blocking locks. */
148    private CmsLockFilter m_blockingFilter;
149
150    /** the nunmber of blocking locked resources. */
151    private int m_blockingLocks = -1;
152
153    /** The list of locked resources.  */
154    private List<String> m_lockedResources;
155
156    /** the filter to get all non blocking locks. */
157    private CmsLockFilter m_nonBlockingFilter;
158
159    /** The 'include unpublished related resources' parameter value. */
160    private String m_paramIncluderelated;
161
162    /** The project id parameter value. */
163    private String m_paramProjectid;
164
165    /** The 'show own locked resources' parameter value. */
166    private String m_paramShowownlocks;
167
168    /**
169     * Default constructor needed for dialog handler implementation.<p>
170     */
171    public CmsLock() {
172
173        super(null);
174    }
175
176    /**
177     * Public constructor.<p>
178     *
179     * @param jsp an initialized JSP action element
180     */
181    public CmsLock(CmsJspActionElement jsp) {
182
183        super(jsp);
184    }
185
186    /**
187     * Public constructor with JSP variables.<p>
188     *
189     * @param context the JSP page context
190     * @param req the JSP request
191     * @param res the JSP response
192     */
193    public CmsLock(PageContext context, HttpServletRequest req, HttpServletResponse res) {
194
195        this(new CmsJspActionElement(context, req, res));
196    }
197
198    /**
199     * Returns the html code to build the lock dialog.<p>
200     *
201     * @return html code
202     *
203     * @throws CmsException if something goes wrong
204     */
205    public static String buildLockDialog(CmsDialog dialog) throws CmsException {
206
207        return buildLockDialog(dialog, null, null, 2000, false);
208    }
209
210    /**
211     * Returns the html code to build the lock dialog.<p>
212     *
213     * @param nonBlockingFilter the filter to get all non blocking locks
214     * @param blockingFilter the filter to get all blocking locks
215     * @param hiddenTimeout the maximal number of milliseconds the dialog will be hidden
216     * @param includeRelated indicates if the report should include related resources
217     *
218     * @return html code
219     *
220     * @throws CmsException if something goes wrong
221     */
222    public static String buildLockDialog(
223        CmsDialog dialog,
224        CmsLockFilter nonBlockingFilter,
225        CmsLockFilter blockingFilter,
226        int hiddenTimeout,
227        boolean includeRelated)
228    throws CmsException {
229
230        dialog.setParamAction(CmsDialog.DIALOG_LOCKS_CONFIRMED);
231        CmsLock lockwp = new CmsLock(dialog.getJsp());
232        lockwp.setBlockingFilter(blockingFilter);
233        lockwp.setNonBlockingFilter(nonBlockingFilter);
234
235        StringBuffer html = new StringBuffer(512);
236        html.append(dialog.htmlStart("help.explorer.contextmenu.lock"));
237        html.append(lockwp.buildIncludeJs());
238        html.append(dialog.buildLockConfirmationMessageJS());
239        html.append(dialog.bodyStart("dialog"));
240        html.append("<div id='lock-body-id' class='hide'>\n");
241        html.append(dialog.dialogStart());
242        html.append(dialog.dialogContentStart(dialog.getParamTitle()));
243        html.append(dialog.buildLockHeaderBox());
244        html.append(dialog.dialogSpacer());
245        html.append("<form name='main' action='");
246        html.append(dialog.getDialogUri());
247        html.append("' method='post' class='nomargin' onsubmit=\"return submitAction('");
248        html.append(CmsDialog.DIALOG_OK);
249        html.append("', null, 'main');\">\n");
250        html.append(dialog.paramsAsHidden());
251        html.append("<input type='hidden' name='");
252        html.append(CmsDialog.PARAM_FRAMENAME);
253        html.append("' value=''>\n");
254        html.append(
255            dialog.buildAjaxResultContainer(
256                dialog.key(org.opencms.workplace.commons.Messages.GUI_LOCK_RESOURCES_TITLE_0)));
257        html.append("<div id='conf-msg'></div>\n");
258        html.append(dialog.buildLockAdditionalOptions());
259        html.append(dialog.dialogContentEnd());
260        html.append(dialog.dialogLockButtons());
261        html.append("</form>\n");
262        html.append(dialog.dialogEnd());
263        html.append("</div>\n");
264        html.append(dialog.bodyEnd());
265        html.append(lockwp.buildLockRequest(hiddenTimeout, includeRelated));
266        html.append(dialog.htmlEnd());
267        return html.toString();
268    }
269
270    /**
271     * Determines if the resource should be locked, unlocked or if the lock should be stolen.<p>
272     *
273     * @param cms the CmsObject
274     * @return the dialog action: lock, change lock (steal) or unlock
275     */
276    public static int getDialogAction(CmsObject cms) {
277
278        String fileName = CmsResource.getName(cms.getRequestContext().getUri());
279        if (fileName == null) {
280            // file name could not be determined, return "see locked subresources" action
281            return TYPE_LOCKS;
282        } else if (fileName.equalsIgnoreCase("lock.jsp")) {
283            // a "lock" action is requested
284            return TYPE_LOCK;
285        } else if (fileName.indexOf("change") != -1) {
286            // a "steal lock" action is requested
287            return TYPE_LOCKCHANGE;
288        } else if (fileName.indexOf("unlock") != -1) {
289            // an "unlock" action is requested
290            return TYPE_UNLOCK;
291        } else {
292            // an "see locked subresources" action is requested
293            return TYPE_LOCKS;
294        }
295    }
296
297    /**
298     * Performs the lock/unlock operation, will be called by the JSP page.<p>
299     *
300     * @throws JspException if problems including sub-elements occur
301     */
302    public void actionToggleLock() throws JspException {
303
304        // save initialized instance of this class in request attribute for included sub-elements
305        getJsp().getRequest().setAttribute(SESSION_WORKPLACE_CLASS, this);
306
307        try {
308            if (performDialogOperation()) {
309                // if no exception is caused and "true" is returned the lock/unlock operation was successful
310                actionCloseDialog();
311            } else {
312                // "false" returned, display "please wait" screen
313                getJsp().include(FILE_DIALOG_SCREEN_WAIT);
314            }
315        } catch (Throwable e) {
316            // exception occurred, show error dialog
317            includeErrorpage(this, e);
318        }
319    }
320
321    /**
322     * @see org.opencms.configuration.I_CmsConfigurationParameterHandler#addConfigurationParameter(java.lang.String, java.lang.String)
323     */
324    @Override
325    public void addConfigurationParameter(String paramName, String paramValue) {
326
327        // not implemented yet
328
329    }
330
331    /**
332     * Returns the html code to build the dialogs default confirmation message js.<p>
333     *
334     * @return html code
335     */
336    public String buildDefaultConfirmationJS() {
337
338        StringBuffer html = new StringBuffer(512);
339        html.append("<script ><!--\n");
340        html.append("function setConfirmationMessage(locks, blockinglocks) {\n");
341        html.append("\tvar confMsg = document.getElementById('conf-msg');\n");
342        html.append("\tif (locks > -1) {\n");
343        if (!getSettings().getUserSettings().getDialogShowLock()
344            && (CmsLock.getDialogAction(getCms()) != CmsLock.TYPE_LOCKS)) {
345            // auto commit if lock dialog disabled
346            html.append("\t\tif (blockinglocks == 0) {\n");
347            html.append("\t\t\tsubmitAction('");
348            html.append(CmsDialog.DIALOG_OK);
349            html.append("', null, 'main');\n");
350            html.append("\t\t\tdocument.forms['main'].submit();\n");
351            html.append("\t\t\treturn;\n");
352            html.append("\t\t}\n");
353        }
354        html.append("\t\tdocument.getElementById('lock-body-id').className = '';\n");
355        html.append("\t\tif (locks > '0') {\n");
356        html.append("\t\t\tshowAjaxReportContent();\n");
357        html.append("\t\t\tconfMsg.innerHTML = '");
358        html.append(getConfirmationMessage(false));
359        html.append("';\n");
360        html.append("\t\t} else {\n");
361        html.append("\t\t\tshowAjaxOk();\n");
362        html.append("\t\t\tconfMsg.innerHTML = '");
363        html.append(getConfirmationMessage(true));
364        html.append("';\n");
365        html.append("\t\t}\n");
366        html.append("\t} else {\n");
367        html.append("\t\tconfMsg.innerHTML = '");
368        html.append(key(org.opencms.workplace.Messages.GUI_AJAX_REPORT_WAIT_0));
369        html.append("';\n");
370        html.append("\t}\n");
371        html.append("}\n");
372        html.append("// -->\n");
373        html.append("</script>\n");
374        return html.toString();
375    }
376
377    /**
378     * Returns the html code to include the needed js code.<p>
379     *
380     * @return html code
381     */
382    public String buildIncludeJs() {
383
384        StringBuffer html = new StringBuffer(512);
385        html.append("<script  src='");
386        html.append(CmsWorkplace.getSkinUri());
387        html.append("commons/ajax.js'></script>\n");
388        html.append("<script  src='");
389        html.append(CmsWorkplace.getSkinUri());
390        html.append("editors/xmlcontent/help.js'></script>\n");
391        html.append("<script  src='");
392        html.append(CmsWorkplace.getSkinUri());
393        html.append("admin/javascript/general.js'></script>\n");
394        html.append("<script  src='");
395        html.append(CmsWorkplace.getSkinUri());
396        html.append("admin/javascript/list.js'></script>\n");
397        html.append("<script ><!--\n");
398        html.append("function showAjaxOk() {\n");
399        html.append("\tdocument.getElementById('ajaxreport-img').src = '");
400        html.append(CmsWorkplace.getSkinUri());
401        html.append("commons/ok.png';\n");
402        html.append("\tdocument.getElementById('ajaxreport-txt').innerHTML = '");
403        html.append(key(Messages.GUI_OPERATION_NO_LOCKS_0));
404        html.append("';\n");
405        html.append("}\n");
406        html.append("var ajaxReportContent = '';\n");
407        html.append("var ajaxWaitMessage = '");
408        html.append(CmsStringUtil.escapeJavaScript(buildAjaxWaitMessage()));
409        html.append("';\n");
410        html.append("function showAjaxReportContent() {\n");
411        html.append("\tif (ajaxReportContent != '') {\n");
412        html.append("\t\tdocument.getElementById('ajaxreport').innerHTML = ajaxReportContent;\n");
413        html.append("\t}\n");
414        html.append("}\n");
415        html.append("function doReportUpdate(msg, state) {\n");
416        html.append("\tvar img = state + '.png';\n");
417        html.append("\tvar txt = '';\n");
418        html.append("\tvar locks = -1;\n");
419        html.append("\tvar blockinglocks = -1;\n");
420        html.append("\tvar elem = document.getElementById('ajaxreport');\n");
421        html.append("\tif (state != 'ok') {\n");
422        html.append("\t\tif (state != 'wait') {\n");
423        html.append("\t\t\tdocument.getElementById('lock-body-id').className = '';\n");
424        html.append("\t\t}\n");
425        html.append("\t\tvar img = state + '.png';\n");
426        html.append("\t\tvar txt = msg;\n");
427        html.append("\t\tif (state == 'fatal') {\n");
428        html.append("\t\t\timg = 'error.png';\n");
429        html.append("\t\t\ttxt = '");
430        html.append(key(org.opencms.workplace.Messages.GUI_AJAX_REPORT_GIVEUP_0));
431        html.append("';\n");
432        html.append("\t\t} else if (state == 'wait') {\n");
433        html.append("\t\t\timg = 'wait.gif';\n");
434        html.append("\t\t\ttxt = '");
435        html.append(key(org.opencms.workplace.Messages.GUI_AJAX_REPORT_WAIT_0));
436        html.append("'\n");
437        html.append("\t\t} else if (state == 'error') {\n");
438        html.append("\t\t\ttxt = '");
439        html.append(key(org.opencms.workplace.Messages.GUI_AJAX_REPORT_ERROR_0));
440        html.append("' + msg;\n");
441        html.append("\t\t}\n");
442        html.append("\t\tdocument.getElementById('ajaxreport-img').src = '");
443        html.append(CmsWorkplace.getSkinUri());
444        html.append("commons/' + img;\n");
445        html.append("\t\tdocument.getElementById('ajaxreport-txt').innerHTML = txt;\n");
446        html.append("\t} else {\n");
447        html.append("\t\telem.innerHTML = elem.innerHTML + msg.substring(0, 120);\n");
448        html.append("\t\tajaxReportContent = msg;\n");
449        html.append("\t}\n");
450        html.append("\tif (txt != '') {\n");
451        html.append("\t}\n");
452        html.append("\tif (state == 'ok') {\n");
453        html.append("\t\tlocks = document.forms['main'].locks.value;\n");
454        html.append("\t\tblockinglocks = document.forms['main'].blockinglocks.value;\n");
455        html.append("\t}\n");
456        html.append("\tsetConfirmationMessage(locks, blockinglocks);\n");
457        html.append("}\n");
458        html.append("// -->\n");
459        html.append("</script>\n");
460        return html.toString();
461    }
462
463    /**
464     * Returns the html code to build the lock request.<p>
465     *
466     * @return html code
467     */
468    public String buildLockRequest() {
469
470        return buildLockRequest(0, false);
471    }
472
473    /**
474     * Returns the html code to build the lock request.<p>
475     *
476     * @param hiddenTimeout the maximal number of millis the dialog will be hidden
477     * @param includeRelated indicates if the report should include related resources
478     *
479     * @return html code
480     */
481    public String buildLockRequest(int hiddenTimeout, boolean includeRelated) {
482
483        StringBuffer html = new StringBuffer(512);
484        html.append("<script ><!--\n");
485        html.append("makeRequest('");
486        html.append(getJsp().link("/system/workplace/commons/report-locks.jsp"));
487        html.append("', '");
488        html.append(CmsWorkplace.PARAM_RESOURCELIST);
489        html.append("=");
490        html.append(getParamResourcelist());
491        html.append("&");
492        html.append(CmsDialog.PARAM_RESOURCE);
493        html.append("=");
494        html.append(getParamResource());
495        html.append("&");
496        html.append(CmsLock.PARAM_INCLUDERELATED);
497        html.append("=");
498        html.append(includeRelated);
499        html.append("', 'doReportUpdate');\n");
500        html.append("function showLockDialog() {\n");
501        html.append("\tdocument.getElementById('lock-body-id').className = '';\n");
502        html.append("}\n");
503        html.append("setTimeout('showLockDialog()', " + hiddenTimeout + ");\n");
504        html.append("// -->\n");
505        html.append("</script>\n");
506        return html.toString();
507    }
508
509    /**
510     * Returns the report of all locked subresources.<p>
511     *
512     * @return the report of all locked subresources
513     *
514     * @throws JspException if dialog actions fail
515     * @throws IOException in case of errros forwarding to the required result page
516     * @throws ServletException in case of errros forwarding to the required result page
517     */
518    public String buildReport() throws JspException, ServletException, IOException {
519
520        List<String> lockedResources;
521        if (Boolean.valueOf(getParamShowownlocks()).booleanValue()) {
522            lockedResources = getLockedResources();
523        } else {
524            lockedResources = new ArrayList<String>(getBlockingLockedResources());
525        }
526        Collections.sort(lockedResources);
527        Map<String, String> lockParams = new HashMap<String, String>();
528        if (getParamResource() != null) {
529            lockParams.put(PARAM_RESOURCE, getParamResource());
530        }
531        if (getParamResourcelist() != null) {
532            lockParams.put(PARAM_RESOURCELIST, getParamResourcelist());
533        }
534        if (getParamShowownlocks() != null) {
535            lockParams.put(PARAM_SHOWOWNLOCKS, getParamShowownlocks());
536        }
537        if (getParamIncluderelated() != null) {
538            lockParams.put(PARAM_INCLUDERELATED, getParamIncluderelated());
539        }
540
541        CmsLockedResourcesList list = new CmsLockedResourcesList(
542            getJsp(),
543            lockedResources,
544            CmsResource.getParentFolder(getResourceList().get(0)),
545            lockParams);
546        list.actionDialog();
547        list.getList().setBoxed(false);
548
549        StringBuffer result = new StringBuffer(512);
550        result.append("<input type='hidden' name='locks' value='");
551        result.append(getLockedResources().size()).append("'>\n");
552        result.append("<input type='hidden' name='blockinglocks' value='");
553        result.append(getBlockingLockedResources().size()).append("'>\n");
554        result.append(CmsStringUtil.padLeft("", 120 - result.length()));
555        result.append(CmsListExplorerColumn.getExplorerStyleDef());
556        result.append("<div style='height:150px; overflow: auto;'>\n");
557        result.append(list.getList().listHtml());
558        result.append("</div>\n");
559        return result.toString();
560    }
561
562    /**
563     * Builds the necessary button row.<p>
564     *
565     * @return the button row
566     */
567    public String dialogButtons() {
568
569        if (CmsLock.getDialogAction(getCms()) != CmsLock.TYPE_LOCKS) {
570            return dialogButtonsOkCancel();
571        } else {
572            return dialogButtonsClose();
573        }
574    }
575
576    /**
577     * Returns the filter to get all blocking locks.<p>
578     *
579     * @return the filter to get all blocking locks
580     */
581    public CmsLockFilter getBlockingFilter() {
582
583        if (m_blockingFilter == null) {
584            m_blockingFilter = CmsLockFilter.FILTER_ALL;
585            m_blockingFilter = m_blockingFilter.filterNotLockableByUser(getCms().getRequestContext().getCurrentUser());
586        }
587        return m_blockingFilter;
588    }
589
590    /**
591     * Returns locked resources that do not belong to the current user.<p>
592     *
593     * @return the locked Resources
594     */
595    public Set<String> getBlockingLockedResources() {
596
597        Set<String> blockingResources = new HashSet<String>();
598        Iterator<String> i = getResourceList().iterator();
599        while (i.hasNext()) {
600            String resName = i.next();
601            try {
602                blockingResources.addAll(getCms().getLockedResources(resName, getBlockingFilter()));
603            } catch (CmsException e) {
604                // error reading a resource, should usually never happen
605                if (LOG.isErrorEnabled()) {
606                    LOG.error(e.getLocalizedMessage(), e);
607                }
608            }
609            if (Boolean.valueOf(getParamIncluderelated()).booleanValue()) {
610                addLockedRelatedResources(resName, getBlockingFilter(), blockingResources);
611            }
612        }
613        m_blockingLocks = blockingResources.size();
614        return blockingResources;
615    }
616
617    /**
618     * Returns the number of blocking locks.<p>
619     *
620     * @return the number of  blocking locks
621     */
622    public int getBlockingLocks() {
623
624        if (m_blockingLocks == -1) {
625            // to initialize the blocking locks flag
626            getLockedResources();
627        }
628        return m_blockingLocks;
629    }
630
631    /**
632     * @see org.opencms.configuration.I_CmsConfigurationParameterHandler#getConfiguration()
633     */
634    @Override
635    public CmsParameterConfiguration getConfiguration() {
636
637        return CmsParameterConfiguration.EMPTY_PARAMETERS;
638    }
639
640    /**
641     * Returns the confirmation message.<p>
642     *
643     * @param state if <code>true</code> everything is ok
644     *
645     * @return the confirmation message
646     */
647    public String getConfirmationMessage(boolean state) {
648
649        if (getDialogAction(getCms()) == TYPE_LOCKS) {
650            return "";
651        }
652        if (state) {
653            if (isMultiOperation()) {
654                return key(Messages.GUI_LOCK_MULTI_LOCK_CONFIRMATION_0);
655            } else {
656                return key(Messages.GUI_LOCK_CONFIRMATION_0);
657            }
658        }
659        switch (getDialogAction(getCms())) {
660            case TYPE_LOCK:
661                if (isMultiOperation()) {
662                    return key(Messages.GUI_LOCK_MULTI_INFO_LOCKEDSUBRESOURCES_0);
663                } else {
664                    return key(Messages.GUI_LOCK_INFO_LOCKEDSUBRESOURCES_0);
665                }
666            case TYPE_LOCKCHANGE:
667                return key(Messages.GUI_LOCK_CHANGE_CONFIRMATION_0);
668            case TYPE_UNLOCK:
669                if (isMultiOperation()) {
670                    return key(Messages.GUI_LOCK_MULTI_UNLOCK_CONFIRMATION_0);
671                } else {
672                    return key(Messages.GUI_LOCK_UNLOCK_CONFIRMATION_0);
673                }
674            case TYPE_LOCKS:
675            default:
676                return "";
677        }
678    }
679
680    /**
681     * @see org.opencms.workplace.I_CmsDialogHandler#getDialogHandler()
682     */
683    @Override
684    public String getDialogHandler() {
685
686        return CmsDialogSelector.DIALOG_LOCK;
687    }
688
689    /**
690     * @see org.opencms.workplace.I_CmsDialogHandler#getDialogUri(java.lang.String, CmsJspActionElement)
691     */
692    @Override
693    public String getDialogUri(String resource, CmsJspActionElement jsp) {
694
695        switch (getDialogAction(jsp.getCmsObject())) {
696            case TYPE_LOCK:
697                return URI_LOCK_DIALOG;
698            case TYPE_LOCKCHANGE:
699                return URI_LOCKCHANGE_DIALOG;
700            case TYPE_UNLOCK:
701                return URI_UNLOCK_DIALOG;
702            case TYPE_LOCKS:
703            default:
704                return URI_LOCKS_DIALOG;
705        }
706    }
707
708    /**
709     * Returns all the locked Resources.<p>
710     *
711     * @return all the locked Resources
712     */
713    public List<String> getLockedResources() {
714
715        if (m_lockedResources == null) {
716            // collect my locked resources
717            Set<String> lockedResources = new HashSet<String>();
718            Iterator<String> i = getResourceList().iterator();
719            while (i.hasNext()) {
720                String resName = i.next();
721                try {
722                    lockedResources.addAll(getCms().getLockedResources(resName, getNonBlockingFilter()));
723                } catch (CmsException e) {
724                    // error reading a resource, should usually never happen
725                    if (LOG.isErrorEnabled()) {
726                        LOG.error(e.getLocalizedMessage(getLocale()), e);
727                    }
728                }
729                if (Boolean.valueOf(getParamIncluderelated()).booleanValue()) {
730                    addLockedRelatedResources(resName, getNonBlockingFilter(), lockedResources);
731                }
732            }
733            // get blocking resources needs the locked resources
734            m_lockedResources = new ArrayList<String>(lockedResources);
735            lockedResources.addAll(getBlockingLockedResources());
736            // create the locked resources list again, with the blocking locked resources
737            m_lockedResources = new ArrayList<String>(lockedResources);
738            Collections.sort(m_lockedResources);
739        }
740        return m_lockedResources;
741    }
742
743    /**
744     * Returns the filter to get all non blocking locks.<p>
745     *
746     * @return the filter to get all non blocking locks
747     */
748    public CmsLockFilter getNonBlockingFilter() {
749
750        if (m_nonBlockingFilter == null) {
751            m_nonBlockingFilter = CmsLockFilter.FILTER_ALL;
752            m_nonBlockingFilter = m_nonBlockingFilter.filterLockableByUser(
753                getCms().getRequestContext().getCurrentUser());
754            m_nonBlockingFilter = m_nonBlockingFilter.filterSharedExclusive();
755        }
756        return m_nonBlockingFilter;
757    }
758
759    /**
760     * Returns the 'include unpublished related resources' parameter value.<p>
761     *
762     * @return the 'include unpublished related resources' parameter value
763     */
764    public String getParamIncluderelated() {
765
766        return m_paramIncluderelated;
767    }
768
769    /**
770     * Returns the project id parameter value.<p>
771     *
772     * @return the project id parameter value
773     */
774    public String getParamProjectid() {
775
776        return m_paramProjectid;
777    }
778
779    /**
780     * Returns the 'show own locked resources' parameter value.<p>
781     *
782     * @return the 'show own locked resources' parameter value
783     */
784    public String getParamShowownlocks() {
785
786        return m_paramShowownlocks;
787    }
788
789    /**
790     * @see org.opencms.configuration.I_CmsConfigurationParameterHandler#initConfiguration()
791     */
792    @Override
793    public void initConfiguration() {
794
795        // not implemented yet
796
797    }
798
799    /**
800     * Sets the filter to get all blocking locks.<p>
801     *
802     * @param blockingFilter the filter to set
803     */
804    public void setBlockingFilter(CmsLockFilter blockingFilter) {
805
806        m_blockingFilter = blockingFilter;
807        // reset blocking locks count
808        m_blockingLocks = -1;
809        // reset locked resources
810        m_lockedResources = null;
811    }
812
813    /**
814     * Sets the filter to get all non blocking locks.<p>
815     *
816     * @param nonBlockingFilter the filter to set
817     */
818    public void setNonBlockingFilter(CmsLockFilter nonBlockingFilter) {
819
820        m_nonBlockingFilter = nonBlockingFilter;
821        // reset locked resources
822        m_lockedResources = null;
823    }
824
825    /**
826     * Sets the 'include unpublished related resources' parameter value.<p>
827     *
828     * @param paramIncluderelated the 'include unpublished related resources' parameter value to set
829     */
830    public void setParamIncluderelated(String paramIncluderelated) {
831
832        m_paramIncluderelated = paramIncluderelated;
833    }
834
835    /**
836     * Sets the project id parameter value.<p>
837     *
838     * @param projectid the project id parameter value to set
839     */
840    public void setParamProjectid(String projectid) {
841
842        m_paramProjectid = projectid;
843    }
844
845    /**
846     * Sets the 'show own locked resources' parameter value.<p>
847     *
848     * @param paramShowownlocks the 'show own locked resources' parameter value to set
849     */
850    public void setParamShowownlocks(String paramShowownlocks) {
851
852        m_paramShowownlocks = paramShowownlocks;
853    }
854
855    /**
856     * Determines whether to show the lock dialog depending on the users settings and the dilaog type.<p>
857     *
858     * In case of locking a folder, a confirmation dialog is needed if any sub resources are already locked.<p>
859     *
860     * @return true if dialogs should be shown, otherwise false
861     */
862    public boolean showConfirmation() {
863
864        boolean showConfirmation = getSettings().getUserSettings().getDialogShowLock();
865        if (DIALOG_TYPE_LOCK.equals(getParamDialogtype())) {
866            // in case of locking resources, check if there are locked sub resources in the selected folder(s)
867            showConfirmation = showConfirmation || (getLockedResources().size() > 0);
868        }
869        return showConfirmation;
870    }
871
872    /**
873     * @see org.opencms.workplace.CmsWorkplace#initWorkplaceRequestValues(org.opencms.workplace.CmsWorkplaceSettings, jakarta.servlet.http.HttpServletRequest)
874     */
875    @Override
876    protected void initWorkplaceRequestValues(CmsWorkplaceSettings settings, HttpServletRequest request) {
877
878        // fill the parameter values in the get/set methods
879        fillParamValues(request);
880
881        // set the action for the JSP switch
882        if (DIALOG_CONFIRMED.equals(getParamAction())) {
883            setAction(ACTION_CONFIRMED);
884        } else if (DIALOG_CANCEL.equals(getParamAction())) {
885            setAction(ACTION_CANCEL);
886        } else if (DIALOG_WAIT.equals(getParamAction())) {
887            setAction(ACTION_WAIT);
888        } else {
889            switch (getDialogAction(getCms())) {
890                case TYPE_LOCK:
891                    setDialogTitle(Messages.GUI_LOCK_RESOURCE_1, Messages.GUI_LOCK_MULTI_LOCK_2);
892                    setParamDialogtype(DIALOG_TYPE_LOCK);
893                    // check the required permissions to lock/unlock
894                    if (!checkResourcePermissions(CmsPermissionSet.ACCESS_WRITE, false)) {
895                        // no write permissions for the resource, set cancel action to close dialog
896                        setAction(ACTION_CANCEL);
897                        return;
898                    }
899                    break;
900                case TYPE_LOCKCHANGE:
901                    setDialogTitle(Messages.GUI_LOCK_STEAL_1, Messages.GUI_LOCK_MULTI_STEAL_2);
902                    setParamDialogtype(DIALOG_TYPE_UNLOCK);
903                    break;
904                case TYPE_UNLOCK:
905                    setDialogTitle(Messages.GUI_LOCK_UNLOCK_1, Messages.GUI_LOCK_MULTI_UNLOCK_2);
906                    setParamDialogtype(DIALOG_TYPE_UNLOCK);
907                    break;
908                case TYPE_LOCKS:
909                default:
910                    setDialogTitle(Messages.GUI_LOCK_LOCKS_1, Messages.GUI_LOCK_MULTI_LOCKS_2);
911                    setParamDialogtype(DIALOG_TYPE_LOCKS);
912            }
913            // set action depending on user settings
914            if ((getDialogAction(getCms()) == TYPE_LOCKS) || showConfirmation()) {
915                // show confirmation dialog
916                setAction(ACTION_DEFAULT);
917            } else {
918                // lock/unlock resource without confirmation
919                setAction(ACTION_SUBMIT_NOCONFIRMATION);
920            }
921        }
922
923        if ((getParamResource() == null) && (getParamResourcelist() == null)) {
924            // this if in case of publish project
925            setParamResource("/");
926        }
927
928    }
929
930    /**
931     * Performs the lock/unlock/steal lock operation.<p>
932     *
933     * @return true, if the operation was performed, otherwise false
934     * @throws CmsException if operation is not successful
935     */
936    @Override
937    protected boolean performDialogOperation() throws CmsException {
938
939        //on multi resource operation display "please wait" screen
940        if (isMultiOperation() && !DIALOG_WAIT.equals(getParamAction())) {
941            return false;
942        }
943        // determine action to perform (lock, unlock, change lock)
944        int dialogAction = getDialogAction(getCms());
945
946        // now perform the operation on the resource(s)
947        Iterator<String> i = getResourceList().iterator();
948        while (i.hasNext()) {
949            String resName = i.next();
950            try {
951                performSingleResourceOperation(resName, dialogAction);
952            } catch (CmsException e) {
953                // collect exceptions to create a detailed output
954                addMultiOperationException(e);
955            }
956        }
957        // generate the error message for exception
958        String message;
959        if (dialogAction == TYPE_LOCK) {
960            message = Messages.ERR_LOCK_MULTI_0;
961        } else {
962            message = Messages.ERR_UNLOCK_MULTI_0;
963        }
964        checkMultiOperationException(Messages.get(), message);
965
966        return true;
967    }
968
969    /**
970     * Performs the lock state operation on a single resource.<p>
971     *
972     * @param resourceName the resource name to perform the operation on
973     * @param dialogAction the lock action: lock, unlock or change lock
974     * @throws CmsException if the operation fails
975     */
976    protected void performSingleResourceOperation(String resourceName, int dialogAction) throws CmsException {
977
978        // store original name to use for lock action
979        String originalResourceName = resourceName;
980        CmsResource res = getCms().readResource(resourceName, CmsResourceFilter.ALL);
981        if (res.isFolder() && !resourceName.endsWith("/")) {
982            resourceName += "/";
983        }
984        org.opencms.lock.CmsLock lock = getCms().getLock(res);
985        // perform action depending on dialog uri
986        switch (dialogAction) {
987            case TYPE_LOCKCHANGE:
988            case TYPE_LOCK:
989                if (lock.isNullLock()) {
990                    getCms().lockResource(originalResourceName);
991                } else if (!lock.isDirectlyOwnedInProjectBy(getCms())) {
992                    getCms().changeLock(resourceName);
993                }
994                break;
995            case TYPE_UNLOCK:
996            default:
997                if (lock.isNullLock()) {
998                    break;
999                }
1000                if (lock.isOwnedBy(getCms().getRequestContext().getCurrentUser())) {
1001                    getCms().unlockResource(resourceName);
1002                }
1003        }
1004    }
1005
1006    /**
1007     * Returns a set of locked unpublished related resources.<p>
1008     *
1009     * @param resName the resource to check the related resources for
1010     * @param filter the lock filter to use
1011     * @param lockedResources a set of site relative paths, of locked resources to exclude
1012     */
1013    private void addLockedRelatedResources(String resName, CmsLockFilter filter, Set<String> lockedResources) {
1014
1015        try {
1016            // get and iterate over all related resources
1017            Iterator<CmsRelation> itRelations = getCms().getRelationsForResource(
1018                resName,
1019                CmsRelationFilter.TARGETS.filterStrong().filterIncludeChildren()).iterator();
1020            while (itRelations.hasNext()) {
1021                CmsRelation relation = itRelations.next();
1022                CmsResource target = null;
1023                try {
1024                    target = relation.getTarget(getCms(), CmsResourceFilter.ALL);
1025                } catch (CmsException e) {
1026                    // error reading a resource, should usually never happen
1027                    if (LOG.isDebugEnabled()) {
1028                        LOG.debug(e.getLocalizedMessage(getLocale()), e);
1029                    }
1030                    continue;
1031                }
1032                // we are interested just in unpublished resources
1033                if (target.getState().isUnchanged()) {
1034                    continue;
1035                }
1036                String targetName = getCms().getSitePath(target);
1037                // if already selected
1038                if (lockedResources.contains(targetName) || lockedResources.contains(targetName + "*")) {
1039                    continue;
1040                }
1041                if (m_lockedResources != null) {
1042                    if (m_lockedResources.contains(targetName) || m_lockedResources.contains(targetName + "*")) {
1043                        continue;
1044                    }
1045                }
1046                try {
1047                    org.opencms.lock.CmsLock lock = getCms().getLock(targetName);
1048                    if (!lock.isUnlocked() && filter.match("/", lock)) {
1049                        // just add resources that may come in question
1050                        lockedResources.add(targetName + "*");
1051                    }
1052                } catch (CmsException e) {
1053                    // error reading a lock, should usually never happen
1054                    if (LOG.isErrorEnabled()) {
1055                        LOG.error(e.getLocalizedMessage(getLocale()), e);
1056                    }
1057                    continue;
1058                }
1059            }
1060        } catch (CmsException e) {
1061            // error reading the relations, should usually never happen
1062            if (LOG.isErrorEnabled()) {
1063                LOG.error(e.getLocalizedMessage(getLocale()), e);
1064            }
1065        }
1066    }
1067}