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.CmsPublishResource;
031import org.opencms.ade.publish.shared.CmsWorkflowAction;
032import org.opencms.gwt.client.CmsCoreProvider;
033import org.opencms.gwt.client.rpc.CmsRpcAction;
034import org.opencms.gwt.client.ui.CmsList;
035import org.opencms.gwt.client.ui.CmsListItemWidget;
036import org.opencms.gwt.client.ui.CmsPushButton;
037import org.opencms.gwt.client.ui.CmsScrollPanel;
038import org.opencms.gwt.client.ui.contextmenu.A_CmsContextMenuItem;
039import org.opencms.gwt.client.ui.contextmenu.CmsContextMenuButton;
040import org.opencms.gwt.client.ui.contextmenu.CmsContextMenuHandler;
041import org.opencms.gwt.client.ui.contextmenu.CmsContextMenuItem;
042import org.opencms.gwt.client.ui.contextmenu.I_CmsContextMenuEntry;
043import org.opencms.gwt.client.ui.tree.CmsTreeItem;
044import org.opencms.gwt.client.util.CmsDomUtil;
045import org.opencms.gwt.shared.CmsCoreData.AdeContext;
046import org.opencms.util.CmsUUID;
047
048import java.util.ArrayList;
049import java.util.Collection;
050import java.util.List;
051
052import com.google.gwt.core.client.GWT;
053import com.google.gwt.event.dom.client.ClickEvent;
054import com.google.gwt.event.dom.client.ClickHandler;
055import com.google.gwt.uibinder.client.UiBinder;
056import com.google.gwt.uibinder.client.UiField;
057import com.google.gwt.uibinder.client.UiHandler;
058import com.google.gwt.user.client.Timer;
059import com.google.gwt.user.client.Window;
060import com.google.gwt.user.client.ui.Composite;
061import com.google.gwt.user.client.ui.Label;
062import com.google.gwt.user.client.ui.Widget;
063
064/**
065 * The panel for showing links which would be broken by publishing.<p>
066 *
067 * @since 8.0.0
068 */
069public class CmsBrokenLinksPanel extends Composite {
070
071    /** The UiBinder interface. */
072    protected interface I_CmsBrokenLinksPanelUiBinder extends UiBinder<Widget, CmsBrokenLinksPanel> {
073        // empty
074    }
075
076    /** Text metrics key. */
077    private static final String TM_PUBLISH_BROKEN = "PublishBroken";
078
079    /** The UiBinder instance for this widget. */
080    private static final I_CmsBrokenLinksPanelUiBinder UI_BINDER = GWT.create(I_CmsBrokenLinksPanelUiBinder.class);
081
082    /** Button slot mapping for showing broken links. */
083    public static int[] SLOT_MAPPING;
084
085    static {
086        SLOT_MAPPING = new int[4];
087        SLOT_MAPPING[CmsPublishGroupPanel.SLOT_WARNING] = 0;
088        SLOT_MAPPING[CmsPublishGroupPanel.SLOT_EDIT] = -1;
089        SLOT_MAPPING[CmsPublishGroupPanel.SLOT_REMOVE] = -1;
090        SLOT_MAPPING[CmsPublishGroupPanel.SLOT_MENU] = 1;
091    }
092
093    /** The button which makes the publish dialog go back to the "resource selection" state. */
094    @UiField
095    protected CmsPushButton m_backButton;
096
097    /** The button which cancels the publish dialog. */
098    @UiField
099    protected CmsPushButton m_cancelButton;
100
101    /** The text shown above the resource panel. */
102    @UiField
103    protected Label m_label;
104
105    /** The list containing the resource widgets representing broken links. */
106    @UiField
107    protected CmsList<CmsTreeItem> m_list;
108
109    /** The scroll panel containing the group panel. */
110    @UiField
111    protected CmsScrollPanel m_scrollPanel;
112
113    /** The action buttons. */
114    private List<CmsPushButton> m_actionButtons;
115
116    /** The available work flow actions. */
117    private List<CmsWorkflowAction> m_actions;
118
119    /** The publish dialog containing this widget. */
120    private CmsPublishDialog m_publishDialog;
121
122    /**
123     * Creates a new instance.<p>
124     *
125     * @param publishDialog the publish dialog to which this broken links panel belongs.
126     * @param scrollPanelHeight the available scroll panel height
127     */
128    public CmsBrokenLinksPanel(CmsPublishDialog publishDialog, int scrollPanelHeight) {
129
130        initWidget(UI_BINDER.createAndBindUi(this));
131        m_scrollPanel.getElement().getStyle().setPropertyPx(CmsDomUtil.Style.maxHeight.toString(), scrollPanelHeight);
132        prepareButton(m_cancelButton, Messages.get().key(Messages.GUI_PUBLISH_DIALOG_CANCEL_BUTTON_0));
133        prepareButton(m_backButton, Messages.get().key(Messages.GUI_PUBLISH_DIALOG_BACK_0));
134        m_label.setText(Messages.get().key(Messages.GUI_PUBLISH_DIALOG_BROKEN_LINKS_0));
135        m_publishDialog = publishDialog;
136        m_list.truncate(TM_PUBLISH_BROKEN, CmsPublishDialog.DIALOG_WIDTH);
137        m_actionButtons = new ArrayList<CmsPushButton>();
138    }
139
140    /**
141      * Adds a resource bean to be displayed.<p>
142      *
143      * @param res a resource bean
144      * @return the list item widget of the created entry
145      */
146    public CmsListItemWidget addEntry(CmsPublishResource res) {
147
148        final CmsListItemWidget itemWidget = CmsPublishGroupPanel.createListItemWidget(res, SLOT_MAPPING);
149        CmsTreeItem item = new CmsTreeItem(false, itemWidget);
150        addContextMenu(item, res);
151
152        item.setOpen(true);
153        for (CmsPublishResource subRes : res.getRelated()) {
154            final CmsListItemWidget subWidget = CmsPublishGroupPanel.createListItemWidget(subRes, SLOT_MAPPING);
155            CmsTreeItem subItem = new CmsTreeItem(false, subWidget);
156            addContextMenu(subItem, subRes);
157            item.addChild(subItem);
158        }
159        m_list.addItem(item);
160        m_scrollPanel.onResizeDescendant();
161        return itemWidget;
162    }
163
164    /**
165     * Returns the buttons which should be shown in the publish dialog's button panel.<p>
166     *
167     * @return a list of buttons
168     */
169    public List<CmsPushButton> getButtons() {
170
171        List<CmsPushButton> result = new ArrayList<CmsPushButton>();
172        result.add(m_backButton);
173        result.add(m_cancelButton);
174        m_actionButtons.clear();
175        if (m_actions != null) {
176            for (final CmsWorkflowAction action : m_actions) {
177                CmsPushButton actionButton = new CmsPushButton();
178                actionButton.setText(action.getLabel());
179                actionButton.setUseMinWidth(true);
180                actionButton.addClickHandler(new ClickHandler() {
181
182                    public void onClick(ClickEvent event) {
183
184                        executeAction(action);
185                    }
186                });
187                m_actionButtons.add(actionButton);
188            }
189        }
190        result.addAll(m_actionButtons);
191        return result;
192    }
193
194    /**
195     * Sets the resources to be displayed.<p>
196     *
197     * @param resourceBeans the resource beans to be displayed
198     * @param actions the available actions
199     */
200    public void setEntries(Collection<CmsPublishResource> resourceBeans, List<CmsWorkflowAction> actions) {
201
202        m_list.clear();
203        CmsListItemWidget listItemWidget = null;
204        for (CmsPublishResource res : resourceBeans) {
205            listItemWidget = addEntry(res);
206        }
207        if (listItemWidget != null) {
208            final CmsListItemWidget lastListItemWidget = listItemWidget;
209            Timer timer = new Timer() {
210
211                @Override
212                public void run() {
213
214                    CmsDomUtil.resizeAncestor(lastListItemWidget);
215                }
216            };
217            timer.schedule(10);
218        }
219        m_actions = actions;
220    }
221
222    /**
223     * Updates the dialog title.<p>
224     **/
225    public void updateTitle() {
226
227        m_publishDialog.setCaption(
228            Messages.get().key(
229                Messages.GUI_PUBLISH_DIALOG_PROBLEMS_2,
230                m_publishDialog.getSelectedWorkflow().getNiceName(),
231                String.valueOf(m_list.getWidgetCount())));
232    }
233
234    /**
235     * The event handler for the back button.<p>
236     *
237     * @param e the click event
238     */
239    @UiHandler("m_backButton")
240    protected void doClickBack(ClickEvent e) {
241
242        m_publishDialog.onGoBack();
243    }
244
245    /**
246     * The event handler for the cancel button.<p>
247     *
248     * @param e the click event
249     */
250    @UiHandler("m_cancelButton")
251    protected void doClickCancel(ClickEvent e) {
252
253        m_publishDialog.onCancel();
254    }
255
256    /**
257     * Executes the given action.<p>
258     *
259     * @param action the action to execute on the selected resources
260     */
261    protected void executeAction(CmsWorkflowAction action) {
262
263        m_publishDialog.executeAction(action);
264    }
265
266    /**
267     * Adds a context menu button to the resource box, unless the structure id is null (this can happen with already broken relations).
268     *
269     * @param item the item to add the button to
270     * @param res the publish resource data
271     */
272    private void addContextMenu(CmsTreeItem item, CmsPublishResource res) {
273
274        if (!res.getId().isNullUUID()) {
275            CmsContextMenuButton button = new CmsContextMenuButton(
276                res.getId(),
277                m_publishDialog.getContextMenuHandler(),
278                AdeContext.resourceinfo);
279            CmsPublishGroupPanel.fillButtonSlot(
280                item.getListItemWidget(),
281                CmsPublishGroupPanel.SLOT_MENU,
282                button,
283                SLOT_MAPPING);
284        } else if (CmsCoreProvider.get().getUserInfo().isWorkplaceUser()) {
285            // Null UUID, so resource probably doesn't exist.
286            // We can't use the normal context menu handler, since it uses the structure id for loading the context menu entries,
287            // so we create an individual context menu handler instance for each broken link item.
288            CmsContextMenuButton button = new CmsContextMenuButton(res.getId(), new CmsContextMenuHandler() {
289
290                @Override
291                public void loadContextMenu(CmsUUID structureId, AdeContext context, CmsContextMenuButton menuButton) {
292
293                    List<I_CmsContextMenuEntry> menuEntries = new ArrayList<>();
294                    I_CmsContextMenuEntry entry = new I_CmsContextMenuEntry() {
295
296                        public void execute() {
297
298                            CmsRpcAction<String> rpcAction = new CmsRpcAction<String>() {
299
300                                @Override
301                                public void execute() {
302
303                                    start(0, false);
304                                    CmsCoreProvider.getService().getWorkplaceLinkForPath(res.getSubTitle(), this);
305                                }
306
307                                @Override
308                                protected void onResponse(String result) {
309
310                                    stop(false);
311                                    if (result != null) {
312                                        Window.Location.assign(result);
313                                        // In case we already are in the workplace, setting the URL does not necessarily update the state.
314                                        // Schedule a timer to trigger a reload, which only fires in that scenario.
315                                        Timer timer = new Timer() {
316
317                                            @Override
318                                            public void run() {
319
320                                                Window.Location.reload();
321                                            }
322
323                                        };
324                                        timer.schedule(50);
325                                    }
326                                }
327                            };
328                            rpcAction.execute();
329                        }
330
331                        public A_CmsContextMenuItem generateMenuItem() {
332
333                            return new CmsContextMenuItem(this);
334
335                        }
336
337                        public String getIconClass() {
338
339                            return null;
340                        }
341
342                        public String getJspPath() {
343
344                            return null;
345                        }
346
347                        public String getLabel() {
348
349                            return Messages.get().key(Messages.GUI_BROKEN_LINK_SHOW_IN_EXPLORER_0);
350                        }
351
352                        public String getName() {
353
354                            return "";
355                        }
356
357                        public String getReason() {
358
359                            return null;
360                        }
361
362                        public List<I_CmsContextMenuEntry> getSubMenu() {
363
364                            return null;
365                        }
366
367                        public boolean hasSubMenu() {
368
369                            return false;
370                        }
371
372                        public boolean isActive() {
373
374                            return true;
375                        }
376
377                        public boolean isSeparator() {
378
379                            return false;
380                        }
381
382                        public boolean isVisible() {
383
384                            return true;
385                        }
386                    };
387                    menuEntries.add(entry);
388                    menuButton.showMenu(menuEntries);
389
390                }
391
392                @Override
393                public void refreshResource(CmsUUID structureId) {
394
395                    m_publishDialog.getContextMenuHandler().refreshResource(structureId);
396                }
397
398            }, AdeContext.resourceinfo);
399            CmsPublishGroupPanel.fillButtonSlot(
400                item.getListItemWidget(),
401                CmsPublishGroupPanel.SLOT_MENU,
402                button,
403                SLOT_MAPPING);
404        }
405    }
406
407    /**
408     * Sets the text on a button and formats the button.<p>
409     *
410     * @param button the button
411     * @param text the text to put on the button
412     */
413    private void prepareButton(CmsPushButton button, String text) {
414
415        button.setText(text);
416        button.setUseMinWidth(true);
417    }
418}