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.containerpage.client.ui;
029
030import org.opencms.ade.containerpage.client.CmsContainerpageController;
031import org.opencms.ade.containerpage.client.Messages;
032import org.opencms.ade.containerpage.client.ui.css.I_CmsLayoutBundle;
033import org.opencms.ade.containerpage.shared.CmsCntPageData;
034import org.opencms.ade.containerpage.shared.CmsDialogOptions;
035import org.opencms.ade.containerpage.shared.CmsDialogOptionsAndInfo;
036import org.opencms.ade.contenteditor.client.CmsContentEditor;
037import org.opencms.ade.contenteditor.shared.CmsEditHandlerData;
038import org.opencms.ade.upload.client.I_CmsUploadContext;
039import org.opencms.ade.upload.client.lists.CmsUploadPopup;
040import org.opencms.gwt.client.CmsCoreProvider;
041import org.opencms.gwt.client.rpc.CmsRpcAction;
042import org.opencms.gwt.client.ui.A_CmsDirectEditButtons;
043import org.opencms.gwt.client.ui.CmsCreateModeSelectionDialog;
044import org.opencms.gwt.client.ui.CmsDeleteWarningDialog;
045import org.opencms.gwt.client.ui.CmsPushButton;
046import org.opencms.gwt.client.ui.I_CmsButton;
047import org.opencms.gwt.client.util.CmsDebugLog;
048import org.opencms.gwt.client.util.CmsDomUtil;
049import org.opencms.gwt.client.util.CmsPositionBean;
050import org.opencms.gwt.client.util.I_CmsSimpleCallback;
051import org.opencms.gwt.shared.CmsGwtConstants;
052import org.opencms.gwt.shared.CmsListInfoBean;
053import org.opencms.util.CmsStringUtil;
054import org.opencms.util.CmsUUID;
055
056import java.util.HashMap;
057import java.util.List;
058import java.util.Map;
059
060import com.google.common.collect.Maps;
061import com.google.gwt.dom.client.Element;
062import com.google.gwt.dom.client.Style;
063import com.google.gwt.dom.client.Style.Display;
064import com.google.gwt.dom.client.Style.Unit;
065import com.google.gwt.event.dom.client.ClickEvent;
066import com.google.gwt.event.dom.client.ClickHandler;
067import com.google.gwt.user.client.Command;
068import com.google.gwt.user.client.rpc.AsyncCallback;
069
070/**
071 * Class to provide direct edit buttons within list collector elements.<p>
072 *
073 * @since 8.0.0
074 */
075public class CmsListCollectorEditor extends A_CmsDirectEditButtons {
076
077    /** True if the parent element has offset height or width. */
078    private boolean m_parentHasDimensions;
079
080    /** The currently active upload popup. */
081    private CmsUploadPopup m_uploadPopup;
082
083    /**
084     * Creates a new instance.<p>
085     *
086     * @param editable the editable element
087     * @param parentId the parent id
088     */
089    public CmsListCollectorEditor(Element editable, String parentId) {
090
091        super(editable, parentId);
092    }
093
094    /**
095     * Creates the button to add an element to the user's favorites.<p>
096     *
097     * @return the created button
098     */
099    public CmsPushButton createFavButton() {
100
101        CmsPushButton favButton = new CmsPushButton();
102        favButton.setImageClass(I_CmsButton.ButtonData.ADD_TO_FAVORITES.getSmallIconClass());
103        favButton.setTitle(I_CmsButton.ButtonData.ADD_TO_FAVORITES.getTitle());
104        favButton.setButtonStyle(I_CmsButton.ButtonStyle.FONT_ICON, null);
105        add(favButton);
106        favButton.addClickHandler(new ClickHandler() {
107
108            public void onClick(ClickEvent event) {
109
110                CmsContainerpageController.get().getHandler().addToFavorites(getContentId().toString());
111            }
112        });
113        return favButton;
114    }
115
116    /**
117     * Returns true if the element view of the element is compatible with the currently set element view in the container page editor.<p>
118     *
119     * @return true if the element should be visible in the current mode
120     */
121    public boolean isVisibleInCurrentView() {
122
123        return CmsContainerpageController.get().matchRootView(m_editableData.getElementView());
124    }
125
126    /**
127     * Sets the 'parentHasDimensions' flag.<p>
128     *
129     * @param parentHasDimensions the new value of the flag
130     */
131    public void setParentHasDimensions(boolean parentHasDimensions) {
132
133        m_parentHasDimensions = parentHasDimensions;
134    }
135
136    /**
137     * @see org.opencms.gwt.client.ui.A_CmsDirectEditButtons#setPosition(org.opencms.gwt.client.util.CmsPositionBean, com.google.gwt.dom.client.Element)
138     */
139    @Override
140    public void setPosition(CmsPositionBean position, Element containerElement) {
141
142        m_position = position;
143        Element parent = CmsDomUtil.getPositioningParent(getElement());
144        Style style = getElement().getStyle();
145        int right = parent.getOffsetWidth()
146            - ((m_position.getLeft() + m_position.getWidth()) - parent.getAbsoluteLeft());
147
148        int top = m_position.getTop() - parent.getAbsoluteTop();
149        if (m_position.getHeight() < 24) {
150            // if the highlighted area has a lesser height than the buttons, center vertically
151            top -= (24 - m_position.getHeight()) / 2;
152        }
153
154        if (top < 25) {
155            // check if there is a parent option bar element present
156            Element parentOptionBar = CmsDomUtil.getFirstChildWithClass(
157                containerElement,
158                I_CmsLayoutBundle.INSTANCE.containerpageCss().optionBar());
159            if ((parentOptionBar != null) && !getElement().equals(parentOptionBar)) {
160                int optBarTop = parentOptionBar.getAbsoluteTop();
161                int optBarLeft = parentOptionBar.getAbsoluteLeft() + 22;
162                if ((Math.abs(optBarLeft - (m_position.getLeft() + m_position.getWidth())) < 25)
163                    && (Math.abs(optBarTop - m_position.getTop()) < 25)) {
164                    // in case the edit buttons overlap, move to the left
165                    right = ((parent.getOffsetWidth() + parent.getAbsoluteLeft()) - optBarLeft) + 25;
166                }
167            }
168        }
169        style.setRight(right, Unit.PX);
170        style.setTop(top, Unit.PX);
171        updateExpiredOverlayPosition(parent);
172    }
173
174    /**
175     * Shows or hides the widget depending on the current view and whether the parent element has width or height.<p>
176     *
177     * @param editableContainer true if this list element is part of an element in an editable container
178     */
179    public void updateVisibility(boolean editableContainer) {
180
181        boolean visible = m_parentHasDimensions && isVisibleInCurrentView() && editableContainer;
182        setDisplayNone(!visible);
183
184    }
185
186    /**
187     * Handles the 'default case' when using the new function on an editable element.<p>
188     */
189    protected void defaultNew() {
190
191        CmsUUID referenceId = m_editableData.getStructureId();
192        CmsCreateModeSelectionDialog.showDialog(referenceId, new AsyncCallback<String>() {
193
194            public void onFailure(Throwable caught) {
195
196                // is never called
197            }
198
199            public void onSuccess(String result) {
200
201                openEditDialog(true, result, null);
202                removeHighlighting();
203            }
204        });
205    }
206
207    /**
208     * Delete the editable element from page and VFS.<p>
209     */
210    protected void deleteElement() {
211
212        CmsContainerpageController.get().deleteElement(m_editableData.getStructureId().toString(), m_parentResourceId);
213    }
214
215    /**
216     * @see org.opencms.gwt.client.ui.A_CmsDirectEditButtons#getAdditionalButtons()
217     */
218    @Override
219    protected Map<Integer, CmsPushButton> getAdditionalButtons() {
220
221        Map<Integer, CmsPushButton> result = Maps.newHashMap();
222        // only show add to favorites and info button, in case there actually is a resource and not in case of create new only
223        if (m_editableData.hasResource()) {
224            if (m_editableData.canFavorite()) {
225                result.put(Integer.valueOf(130), createFavButton());
226            }
227            result.put(Integer.valueOf(160), createInfoButton());
228        }
229        return result;
230    }
231
232    /**
233     * @see org.opencms.gwt.client.ui.A_CmsDirectEditButtons#getInfoContext()
234     */
235    @Override
236    protected Map<String, String> getInfoContext() {
237
238        Map<String, String> result = new HashMap<>();
239        String elementId = m_editableData.getElementId();
240        if (elementId != null) {
241            result.put(CmsGwtConstants.ATTR_ELEMENT_ID, elementId);
242        }
243        String uri = CmsCoreProvider.get().getUri();
244        String siteRoot = CmsCoreProvider.get().getSiteRoot();
245        String pageRootPath = CmsStringUtil.joinPaths(siteRoot, uri);
246        result.put(CmsGwtConstants.ATTR_PAGE_ROOT_PATH, pageRootPath);
247        return result;
248
249    }
250
251    /**
252     * @see org.opencms.gwt.client.ui.A_CmsDirectEditButtons#getUploadButtonTitle(java.lang.String)
253     */
254    @Override
255    protected String getUploadButtonTitle(String uploadFolder) {
256
257        return org.opencms.ade.galleries.client.Messages.get().key(
258            org.opencms.ade.galleries.client.Messages.GUI_GALLERY_UPLOAD_TITLE_1,
259            uploadFolder);
260    }
261
262    /**
263     * @see org.opencms.gwt.client.ui.A_CmsDirectEditButtons#onClickDelete()
264     */
265    @Override
266    protected void onClickDelete() {
267
268        removeHighlighting();
269        CmsDomUtil.ensureMouseOut(getElement());
270        if (m_editableData.hasEditHandler()) {
271            final String elementId = CmsContentEditor.getClientIdForEditable(m_editableData);
272            final I_CmsSimpleCallback<String> deleteCallback = new I_CmsSimpleCallback<String>() {
273
274                public void execute(String arg) {
275
276                    if (CmsDialogOptions.REGULAR_DELETE.equals(arg)) {
277                        openWarningDialog();
278                    } else {
279                        CmsContainerpageController.get().handleDelete(elementId, arg, new I_CmsSimpleCallback<Void>() {
280
281                            public void execute(Void arg1) {
282
283                                CmsContainerpageController.get().reloadElements(
284                                    new String[] {getParentResourceId()},
285                                    () -> {/*do nothing*/});
286                            }
287                        });
288                    }
289                }
290            };
291            CmsContainerpageController.get().getDeleteOptions(
292                elementId,
293                new I_CmsSimpleCallback<CmsDialogOptionsAndInfo>() {
294
295                    public void execute(CmsDialogOptionsAndInfo arg) {
296
297                        if (arg == null) {
298                            deleteCallback.execute(CmsDialogOptions.REGULAR_DELETE);
299                        } else if (arg.getOptions().getOptions().size() == 1) {
300                            String deleteOpt = arg.getOptions().getOptions().get(0).getValue();
301                            deleteCallback.execute(deleteOpt);
302
303                        } else {
304                            CmsOptionDialog dialog = new CmsOptionDialog(
305                                Messages.get().key(Messages.GUI_EDIT_HANDLER_SELECT_DELETE_OPTION_0),
306                                arg.getOptions(),
307                                arg.getInfo(),
308                                deleteCallback);
309                            dialog.center();
310                        }
311                    }
312                });
313        } else {
314            openWarningDialog();
315        }
316        m_delete.clearHoverState();
317    }
318
319    /**
320     * @see org.opencms.gwt.client.ui.A_CmsDirectEditButtons#onClickEdit()
321     */
322    @Override
323    protected void onClickEdit() {
324
325        openEditDialog(false, null, null);
326        removeHighlighting();
327    }
328
329    /**
330     * @see org.opencms.gwt.client.ui.A_CmsDirectEditButtons#onClickNew(boolean)
331     */
332    @Override
333    protected void onClickNew(boolean askCreateMode) {
334
335        if (!askCreateMode) {
336            openEditDialog(true, null, null);
337            removeHighlighting();
338        } else {
339            if (m_editableData.hasEditHandler()) {
340                final String elementId = CmsContentEditor.getClientIdForEditable(m_editableData);
341                CmsCntPageData cntPageData = CmsContainerpageController.get().getData();
342                final CmsUUID pageId = cntPageData.getRpcContext().getPageStructureId();
343                final String requestParamStr = cntPageData.getRequestParams();
344
345                final I_CmsSimpleCallback<String> newCallback = new I_CmsSimpleCallback<String>() {
346
347                    public void execute(String choice) {
348
349                        CmsEditHandlerData data = new CmsEditHandlerData(elementId, choice, pageId, requestParamStr);
350                        openEditDialog(true, null, data);
351                        removeHighlighting();
352
353                    }
354                };
355
356                CmsContainerpageController.get().getNewOptions(
357                    elementId,
358                    new I_CmsSimpleCallback<CmsDialogOptionsAndInfo>() {
359
360                        public void execute(CmsDialogOptionsAndInfo arg) {
361
362                            if (arg == null) {
363                                CmsDebugLog.consoleLog("dialog options null, using default behavior");
364                                defaultNew();
365                            } else {
366                                CmsOptionDialog dialog = new CmsOptionDialog(
367                                    null,
368                                    arg.getOptions(),
369                                    arg.getInfo(),
370                                    newCallback);
371                                dialog.center();
372                            }
373
374                        }
375                    });
376            } else {
377                defaultNew();
378            }
379        }
380
381    }
382
383    /**
384     * @see org.opencms.gwt.client.ui.A_CmsDirectEditButtons#onClickUpload()
385     */
386    @Override
387    protected void onClickUpload() {
388
389        removeHighlighting();
390        I_CmsUploadContext context = new I_CmsUploadContext() {
391
392            @SuppressWarnings("synthetic-access")
393            public void onUploadFinished(List<String> uploadedFiles) {
394
395                closeUploadPopup();
396                CmsContainerpageController.get().reloadElements(
397                    new String[] {getParentResourceId()},
398                    () -> {/*do nothing*/});
399            }
400        };
401        if (m_editableData.getStructureId() != null) {
402            CmsRpcAction<CmsListInfoBean> action = new CmsRpcAction<CmsListInfoBean>() {
403
404                @SuppressWarnings("synthetic-access")
405                @Override
406                public void execute() {
407
408                    start(0, false);
409                    CmsCoreProvider.get();
410                    CmsCoreProvider.getVfsService().getUploadFolderInfo(
411                        m_editableData.getExtensions().getUploadFolder(),
412                        this);
413                }
414
415                @SuppressWarnings("synthetic-access")
416                @Override
417                protected void onResponse(CmsListInfoBean result) {
418
419                    stop(false);
420                    setUploadPopup(
421                        new CmsUploadPopup(
422                            m_editableData.getExtensions().getUploadFolder(),
423                            m_editableData.getPostCreateHandler(),
424                            context,
425                            result));
426                    m_uploadPopup.center();
427
428                }
429
430            };
431            action.execute();
432        } else {
433            setUploadPopup(
434                new CmsUploadPopup(
435                    m_editableData.getExtensions().getUploadFolder(),
436                    m_editableData.getPostCreateHandler(),
437                    context,
438                    null));
439            m_uploadPopup.center();
440
441        }
442
443    }
444
445    /**
446     * Opens the content editor.<p>
447     *
448     * @param isNew <code>true</code> to create and edit a new resource
449     * @param mode the content creation mode
450     * @param handlerDataForNew the data for the edit handler if it is used for the 'new' function
451     */
452    protected void openEditDialog(boolean isNew, String mode, CmsEditHandlerData handlerDataForNew) {
453
454        CmsContainerpageController.get().getContentEditorHandler().openDialog(
455            m_editableData,
456            isNew,
457            m_parentResourceId,
458            mode,
459            handlerDataForNew);
460    }
461
462    /**
463     * Shows the delete warning dialog.<p>
464     */
465    protected void openWarningDialog() {
466
467        CmsDeleteWarningDialog dialog = new CmsDeleteWarningDialog(m_editableData.getSitePath());
468        Command callback = new Command() {
469
470            /**
471             * @see com.google.gwt.user.client.Command#execute()
472             */
473            public void execute() {
474
475                deleteElement();
476            }
477        };
478        dialog.loadAndShow(callback);
479    }
480
481    /**
482     * Returns the edit content id.<p>
483     *
484     * @return the content id
485     */
486    CmsUUID getContentId() {
487
488        return m_editableData.getStructureId();
489    }
490
491    /**
492     * Returns the parent resource id.<p>
493     *
494     * @return the parent resource id
495     */
496    String getParentResourceId() {
497
498        return m_parentResourceId;
499    }
500
501    /**
502     * Sets the display CSS property to none, or clears it, depending on the given parameter.<p>
503     *
504     * @param displayNone true if the widget should not be displayed
505     */
506    void setDisplayNone(boolean displayNone) {
507
508        if (displayNone) {
509            getElement().getStyle().setDisplay(Display.NONE);
510        } else {
511            getElement().getStyle().clearDisplay();
512        }
513    }
514
515    /**
516     * Closes the currently active upload popup.
517     */
518    private void closeUploadPopup() {
519
520        if (m_uploadPopup != null) {
521            m_uploadPopup.hide();
522            m_uploadPopup = null;
523        }
524    }
525
526    /**
527     * Sets the upload popup, and closes the previous one if it exists.
528     *
529     * @param popup the upload popup
530     */
531    private void setUploadPopup(CmsUploadPopup popup) {
532
533        closeUploadPopup();
534        m_uploadPopup = popup;
535    }
536
537}