001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.ade.publish.client;
029
030import org.opencms.ade.publish.shared.CmsPublishData;
031import org.opencms.ade.publish.shared.CmsPublishGroupList;
032import org.opencms.ade.publish.shared.CmsPublishListToken;
033import org.opencms.ade.publish.shared.CmsPublishOptions;
034import org.opencms.ade.publish.shared.CmsWorkflow;
035import org.opencms.ade.publish.shared.CmsWorkflowAction;
036import org.opencms.ade.publish.shared.CmsWorkflowActionParams;
037import org.opencms.ade.publish.shared.CmsWorkflowResponse;
038import org.opencms.ade.publish.shared.rpc.I_CmsPublishService;
039import org.opencms.ade.publish.shared.rpc.I_CmsPublishServiceAsync;
040import org.opencms.gwt.client.CmsCoreProvider;
041import org.opencms.gwt.client.CmsEditableDataJSO;
042import org.opencms.gwt.client.rpc.CmsRpcAction;
043import org.opencms.gwt.client.ui.CmsNotification;
044import org.opencms.gwt.client.ui.CmsPopup;
045import org.opencms.gwt.client.ui.CmsPushButton;
046import org.opencms.gwt.client.ui.contenteditor.I_CmsContentEditorHandler;
047import org.opencms.gwt.client.ui.contextmenu.CmsContextMenuHandler;
048import org.opencms.gwt.client.ui.contextmenu.CmsDialogContextMenuHandler;
049import org.opencms.gwt.client.util.CmsDomUtil;
050import org.opencms.gwt.shared.CmsGwtConstants;
051import org.opencms.util.CmsStringUtil;
052import org.opencms.util.CmsUUID;
053
054import java.util.ArrayList;
055import java.util.HashMap;
056import java.util.List;
057import java.util.Map;
058
059import com.google.common.collect.Lists;
060import com.google.gwt.core.client.GWT;
061import com.google.gwt.dom.client.Element;
062import com.google.gwt.event.logical.shared.CloseHandler;
063import com.google.gwt.user.client.Window;
064import com.google.gwt.user.client.rpc.ServiceDefTarget;
065import com.google.gwt.user.client.ui.DeckPanel;
066import com.google.gwt.user.client.ui.PopupPanel;
067
068/**
069 *
070 * Main class for the publish dialog.<p>
071 *
072 * This class is mostly responsible for the control flow and RPC calls of the publish dialog.
073 * It delegates most of the actual GUI work to the {@link CmsPublishSelectPanel} and {@link CmsBrokenLinksPanel} classes.
074 *
075 * @since 8.0.0
076 *
077 */
078public class CmsPublishDialog extends CmsPopup {
079
080    /**
081     * A type which represents the state of a publish action.<p>
082     */
083    public enum State {
084        /** The publish dialog was cancelled. */
085        cancel,
086
087        /** The publish dialog has failed. */
088        failure,
089
090        /** The publish dialog has succeeded. */
091        success;
092    }
093
094    /**
095     * The action for publishing and/or removing resources from the publish list.<p>
096     */
097    private class CmsPublishAction extends CmsRpcAction<CmsWorkflowResponse> {
098
099        /** If true, try to ignore broken links when publishing. */
100        private CmsWorkflowAction m_action;
101
102        /** Creates a new instance of this action.
103         *
104         * @param action the workflow action to execute
105         */
106        public CmsPublishAction(CmsWorkflowAction action) {
107
108            m_action = action;
109        }
110
111        /**
112         * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute()
113         */
114        @Override
115        public void execute() {
116
117            start(0, true);
118            setLastAction(m_action);
119            CmsWorkflowActionParams actionParams = getWorkflowActionParams();
120            getService().executeAction(m_action, actionParams, this);
121
122        }
123
124        /**
125         * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object)
126         */
127        @Override
128        protected void onResponse(CmsWorkflowResponse result) {
129
130            stop(false);
131            onReceiveStatus(result);
132
133        }
134    }
135
136    /**
137     * The action for loading the publish list.<p>
138     */
139    private class CmsPublishListAction extends CmsRpcAction<CmsPublishGroupList> {
140
141        /**
142         * Constructor.<p>
143         */
144        protected CmsPublishListAction() {
145
146            // nothing to do
147        }
148
149        /**
150         * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute()
151         */
152        @Override
153        public void execute() {
154
155            start(0, true);
156            CmsPublishOptions options = getPublishOptions();
157            boolean projectChanged = m_projectChanged;
158            m_projectChanged = false;
159
160            getService().getResourceGroups(getSelectedWorkflow(), options, projectChanged, this);
161        }
162
163        /**
164         * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object)
165         */
166        @Override
167        protected void onResponse(CmsPublishGroupList result) {
168
169            stop(false);
170            onReceivePublishList(result);
171
172        }
173    }
174
175    /** The dialog width in pixels. */
176    public static final int DIALOG_WIDTH = 766;
177
178    /** The project map used by showPublishDialog. */
179    public static Map<String, String> m_staticProjects;
180
181    /** The index of the "broken links" panel. */
182    public static final int PANEL_BROKEN_LINKS = 1;
183
184    /** The index of the publish selection panel. */
185    public static final int PANEL_SELECT = 0;
186
187    /** The CSS bundle used for this widget. */
188    private static final I_CmsPublishCss CSS = I_CmsPublishLayoutBundle.INSTANCE.publishCss();
189    /** Flag indicating if the CSS has been initialized. */
190    private static boolean CSS_INITIALIZED;
191
192    /** The publish service instance. */
193    private static I_CmsPublishServiceAsync SERVICE;
194
195    /** The panel for selecting the resources to publish or remove from the publish list. */
196    protected CmsPublishSelectPanel m_publishSelectPanel;
197
198    /** Flag to keep track of whether the user just changed the project. */
199    boolean m_projectChanged;
200
201    /** The panel for showing the links that would be broken by publishing. */
202    private CmsBrokenLinksPanel m_brokenLinksPanel;
203
204    /** The content editor handler. */
205    private I_CmsContentEditorHandler m_editorHandler;
206
207    /** Stores a failure message. */
208    private String m_failureMessage;
209
210    /** Stores the last workflow action. */
211    private CmsWorkflowAction m_lastAction;
212
213    /** The context menu handler. */
214    private CmsContextMenuHandler m_menuHandler;
215
216    /** The root panel of this dialog which contains both the selection panel and the panel for displaying broken links. */
217    private DeckPanel m_panel = new DeckPanel();
218
219    /** The current publish list options. */
220    private CmsPublishOptions m_publishOptions;
221
222    /** Stores the state. */
223    private State m_state = State.cancel;
224
225    /** The id of the current workflow. */
226    private String m_workflowId;
227
228    /** The available workflows. */
229    private Map<String, CmsWorkflow> m_workflows;
230
231    /**
232     * Constructs a new publish dialog.<p>
233     *
234     * @param initData the initial data
235     * @param refreshAction the action to perform on a context menu triggered refresh
236     * @param editorHandler the content editor handler
237     */
238    public CmsPublishDialog(
239        CmsPublishData initData,
240        final Runnable refreshAction,
241        I_CmsContentEditorHandler editorHandler) {
242
243        super(800);
244        initCss();
245        setGlassEnabled(true);
246        setPositionFixed();
247        setAutoHideEnabled(false);
248        setModal(true);
249        addStyleName(CSS.publishDialog());
250        m_menuHandler = new CmsDialogContextMenuHandler() {
251
252            @Override
253            public void refreshResource(CmsUUID structureId) {
254
255                CmsPublishDialog.this.hide();
256                refreshAction.run();
257            }
258        };
259        m_editorHandler = editorHandler;
260        m_menuHandler.setEditorHandler(editorHandler);
261        m_workflows = initData.getWorkflows();
262        m_workflowId = initData.getSelectedWorkflowId();
263        m_publishOptions = initData.getOptions();
264        int availableHeight = Window.getClientHeight() - 290;
265        m_publishSelectPanel = new CmsPublishSelectPanel(
266            this,
267            initData.getProjects(),
268            initData.getOptions(),
269            initData.getWorkflows(),
270            initData.getSelectedWorkflowId(),
271            availableHeight);
272        m_brokenLinksPanel = new CmsBrokenLinksPanel(this, availableHeight);
273
274        addDialogClose(null);
275
276        m_panel = new DeckPanel();
277        m_panel.add(m_publishSelectPanel);
278        m_panel.add(m_brokenLinksPanel);
279        setMainContent(m_panel);
280        onReceivePublishList(initData.getGroups());
281    }
282
283    /**
284     * Shows the publish dialog.<p>
285     *
286     * @param result the publish data
287     * @param handler the dialog close handler (may be null)
288     * @param refreshAction the action to execute on a context menu triggered refresh
289     * @param editorHandler the content editor handler (may be null)
290     */
291    public static void showPublishDialog(
292        CmsPublishData result,
293        CloseHandler<PopupPanel> handler,
294        Runnable refreshAction,
295        I_CmsContentEditorHandler editorHandler) {
296
297        CmsPublishDialog publishDialog = new CmsPublishDialog(result, refreshAction, editorHandler);
298        if (handler != null) {
299            publishDialog.addCloseHandler(handler);
300        }
301        publishDialog.centerHorizontally(50);
302        // replace current notification widget by overlay
303        publishDialog.catchNotifications();
304    }
305
306    /**
307     * Convenience method which opens a publish dialog.<p>
308     *
309     * @param handler the close handler
310     * @param params the additional publish dialog parameters
311     * @param refreshAction the action to execute after a context menu triggered refresh
312     * @param editorHandler the content editor handler
313     */
314    public static void showPublishDialog(
315        final HashMap<String, String> params,
316        final CloseHandler<PopupPanel> handler,
317        final Runnable refreshAction,
318        final I_CmsContentEditorHandler editorHandler) {
319
320        List<String> structureIds = Lists.newArrayList();
321        for (CmsEditableDataJSO editableData : CmsDomUtil.getAllEditableDataForPage()) {
322            structureIds.add("" + editableData.getStructureId());
323        }
324
325        List<Element> collectorInfos = CmsDomUtil.getElementsByClass(CmsGwtConstants.CLASS_COLLECTOR_INFO);
326        int j = 1;
327        for (Element elem : collectorInfos) {
328            String infoJson = elem.getAttribute(CmsGwtConstants.ATTR_DATA_COLLECTOR);
329            params.put(CmsPublishOptions.PARAM_COLLECTOR_INFO + "." + j, infoJson);
330            j += 1;
331        }
332        String editableIdList = CmsStringUtil.listAsString(structureIds, ",");
333        params.put(CmsPublishOptions.PARAM_COLLECTOR_ITEMS, editableIdList);
334
335        (new CmsRpcAction<CmsPublishData>() {
336
337            /**
338             * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute()
339             */
340            @Override
341            public void execute() {
342
343                start(0, true);
344                getService().getInitData(params, this);
345            }
346
347            /**
348             * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object)
349             */
350            @Override
351            protected void onResponse(CmsPublishData result) {
352
353                stop(false);
354                showPublishDialog(result, handler, refreshAction, editorHandler);
355            }
356        }).execute();
357    }
358
359    /**
360     * Convenience method which opens a publish dialog.<p>
361     *
362     * @param refreshAction the action to execute after a context menu triggered refresh
363     */
364    public static void showPublishDialog(Runnable refreshAction) {
365
366        showPublishDialog(new HashMap<String, String>(), null, refreshAction, null);
367    }
368
369    /**
370     * Returns the publish service instance.<p>
371     *
372     * @return the publish service instance
373     */
374    protected static I_CmsPublishServiceAsync getService() {
375
376        if (SERVICE == null) {
377            SERVICE = GWT.create(I_CmsPublishService.class);
378            String serviceUrl = CmsCoreProvider.get().link("org.opencms.ade.publish.CmsPublishService.gwt");
379            ((ServiceDefTarget)SERVICE).setServiceEntryPoint(serviceUrl);
380        }
381        return SERVICE;
382    }
383
384    /**
385     * Executes the specified action for the selected resources.<p>
386     *
387     * @param actionKey the workflow action
388     */
389    public void executeAction(CmsWorkflowAction actionKey) {
390
391        (new CmsPublishAction(actionKey)).execute();
392    }
393
394    /**
395     * Gets the context menu handler.<p>
396     *
397     * @return the context menu handler
398     */
399    public CmsContextMenuHandler getContextMenuHandler() {
400
401        return m_menuHandler;
402    }
403
404    /**
405     * Returns the content editor handler.<p>
406     *
407     * @return the content editor handler
408     */
409    public I_CmsContentEditorHandler getEditorHandler() {
410
411        return m_editorHandler;
412    }
413
414    /**
415     * Gets the failure message.<p>
416     *
417     * @return the failure message
418     */
419    public String getFailureMessage() {
420
421        return m_failureMessage;
422    }
423
424    /**
425     * Gets the last workflow action.<p>
426     *
427     * @return the last workflow action
428     */
429    public CmsWorkflowAction getLastAction() {
430
431        return m_lastAction;
432    }
433
434    /**
435     * Gets the context menu handler.
436     */
437    public CmsContextMenuHandler getMenuHandler() {
438
439        return m_menuHandler;
440    }
441
442    /**
443     * Returns the current publish options.<p>
444     *
445     * @return a publish options bean
446     */
447    public CmsPublishOptions getPublishOptions() {
448
449        return m_publishOptions;
450    }
451
452    /**
453     * Gets the publish dialog state.<p>
454     *
455     * @return the publish dialog state
456     */
457    public State getState() {
458
459        return m_state;
460    }
461
462    /**
463     * Checks whether the publish dialog has failed.<p>
464     *
465     * @return checks whether the publish dialog has succeeded
466     */
467    public boolean hasFailed() {
468
469        return m_state == State.failure;
470    }
471
472    /**
473     * Checks whether the publish dialog has succeeded.<p>
474     *
475     * @return true if the publish dialog has succeeded
476     */
477    public boolean hasSucceeded() {
478
479        return m_state == State.success;
480    }
481
482    /**
483     * Method which is called when the cancel button is pressed.<p>
484     */
485    public void onCancel() {
486
487        if (m_publishSelectPanel.m_model != null) {
488            final List<CmsUUID> toRemove = m_publishSelectPanel.m_model.getIdsOfAlreadyPublishedResources();
489            if (toRemove.isEmpty()) {
490                hide();
491            } else {
492                CmsRpcAction<CmsWorkflowResponse> action = new CmsRpcAction<CmsWorkflowResponse>() {
493
494                    @Override
495                    public void execute() {
496
497                        start(0, true);
498                        CmsWorkflowActionParams params = getWorkflowActionParams();
499                        getService().executeAction(
500                            new CmsWorkflowAction(CmsWorkflowAction.ACTION_CANCEL, "", true),
501                            params,
502                            this);
503                    }
504
505                    @Override
506                    protected void onResponse(CmsWorkflowResponse result) {
507
508                        stop(false);
509                        hide();
510
511                    }
512                };
513                action.execute();
514            }
515        } else {
516            hide();
517        }
518    }
519
520    /**
521     * Method which is  called when the back button is pressed.<p>
522     */
523    public void onGoBack() {
524
525        setPanel(PANEL_SELECT);
526    }
527
528    /**
529     * Method which is called after the publish list has been received from the server.<p>
530     *
531     * @param groups the groups of the publish list
532     */
533    public void onReceivePublishList(CmsPublishGroupList groups) {
534
535        m_publishSelectPanel.setGroupList(groups, true, groups.getOverrideWorkflowId());
536        setPanel(PANEL_SELECT);
537        if (!isVisible()) {
538            center();
539        }
540    }
541
542    /**
543     * Method which is called after the status from a publish action has arrived.<p>
544     *
545     * @param brokenResources the list of broken resources
546     */
547    public void onReceiveStatus(CmsWorkflowResponse brokenResources) {
548
549        if (brokenResources.isSuccess()) {
550            succeed();
551            hide();
552            CmsNotification.get().send(
553                CmsNotification.Type.NORMAL,
554                org.opencms.gwt.client.Messages.get().key(org.opencms.gwt.client.Messages.GUI_DONE_0));
555
556        } else {
557            m_failureMessage = brokenResources.getMessage();
558            m_state = State.failure;
559            m_brokenLinksPanel.setEntries(brokenResources.getResources(), brokenResources.getAvailableActions());
560            setPanel(PANEL_BROKEN_LINKS);
561        }
562    }
563
564    /**
565     * Sets the include related resources option.<p>
566     *
567     * @param includeRelated the include related option
568     */
569    public void setIncludeRelated(boolean includeRelated) {
570
571        m_publishOptions.setIncludeRelated(includeRelated);
572    }
573
574    /**
575     * Sets the include sibling resources option.<p>
576     *
577     * @param includeSiblings the include siblings option
578     */
579    public void setIncludeSiblings(boolean includeSiblings) {
580
581        m_publishOptions.setIncludeSiblings(includeSiblings);
582    }
583
584    /**
585     * Changes the currently active panel.<p>
586     *
587     * @param panelId the number of the panel to show
588     */
589    public void setPanel(int panelId) {
590
591        m_panel.showWidget(panelId);
592        removeAllButtons();
593        if (panelId == PANEL_SELECT) {
594            for (CmsPushButton button : m_publishSelectPanel.getButtons()) {
595                addButton(button);
596            }
597            m_publishSelectPanel.updateDialogTitle();
598        } else if (panelId == PANEL_BROKEN_LINKS) {
599            for (CmsPushButton button : m_brokenLinksPanel.getButtons()) {
600                addButton(button);
601            }
602            m_brokenLinksPanel.updateTitle();
603        }
604    }
605
606    /**
607     * This is called when the user just changed the project.<p>
608     */
609    public void setProjectChanged() {
610
611        m_projectChanged = true;
612    }
613
614    /**
615     * Sets the selected project id.<p>
616     *
617     * @param projectId the project id
618     */
619    public void setProjectId(CmsUUID projectId) {
620
621        m_publishOptions.setProjectId(projectId);
622    }
623
624    /**
625     * Sets the selected workflow id.<p>
626     *
627     * @param workflowId the workflow id
628     */
629    public void setWorkflowId(String workflowId) {
630
631        m_workflowId = workflowId;
632        if (!m_workflows.containsKey(workflowId)) {
633            m_workflowId = "WORKFLOW_PUBLISH";
634        }
635    }
636
637    /**
638     * Sets the publish dialog state to 'success'.<p>
639     */
640    public void succeed() {
641
642        m_state = State.success;
643        CmsCoreProvider.get().fireEvent(new CmsPublishEvent());
644    }
645
646    /**
647     * Method which is called when the publish options are changed.<p>
648     */
649    public void updateResourceList() {
650
651        (new CmsPublishListAction()).execute();
652    }
653
654    /**
655     * Returns the selected workflow.<p>
656     *
657     * @return the selected workflow
658     */
659    protected CmsWorkflow getSelectedWorkflow() {
660
661        return m_workflows.get(m_workflowId);
662    }
663
664    /**
665     * Gets the workflow action parameters to which the workflow action should be applied.<p>
666     *
667     * @return the workflow action parameters
668     */
669    protected CmsWorkflowActionParams getWorkflowActionParams() {
670
671        if (m_publishSelectPanel.isShowResources()) {
672            List<CmsUUID> resourcesToPublish = new ArrayList<CmsUUID>(m_publishSelectPanel.getResourcesToPublish());
673            List<CmsUUID> resourcesToRemove = new ArrayList<CmsUUID>(m_publishSelectPanel.getResourcesToRemove());
674            CmsWorkflowActionParams actionParams = new CmsWorkflowActionParams(resourcesToPublish, resourcesToRemove);
675            return actionParams;
676        } else {
677            CmsPublishOptions options = getPublishOptions();
678            CmsWorkflow workflow = getSelectedWorkflow();
679            return new CmsWorkflowActionParams(new CmsPublishListToken(workflow, options));
680
681        }
682    }
683
684    /**
685     * Sets the last workflow action.<p>
686     *
687     * @param action a workflow action
688     */
689    protected void setLastAction(CmsWorkflowAction action) {
690
691        m_lastAction = action;
692    }
693
694    /**
695     * Ensures all style sheets are loaded.<p>
696     */
697    private void initCss() {
698
699        if (!CSS_INITIALIZED) {
700            I_CmsPublishLayoutBundle.INSTANCE.publishCss().ensureInjected();
701            CSS_INITIALIZED = true;
702        }
703    }
704
705}