
org.opencms.ade.containerpage.client.CmsContentEditorHandler Maven / Gradle / Ivy
Show all versions of opencms-gwt Show documentation
/*
* This library is part of OpenCms -
* the Open Source Content Management System
*
* Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* For further information about Alkacon Software, please see the
* company website: http://www.alkacon.com
*
* For further information about OpenCms, please see the
* project website: http://www.opencms.org
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.opencms.ade.containerpage.client;
import org.opencms.ade.containerpage.client.CmsContainerpageController.I_ReloadHandler;
import org.opencms.ade.containerpage.client.ui.CmsContainerPageContainer;
import org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel;
import org.opencms.ade.containerpage.client.ui.CmsOptionDialog;
import org.opencms.ade.containerpage.client.ui.I_CmsDropContainer;
import org.opencms.ade.containerpage.shared.CmsCntPageData;
import org.opencms.ade.containerpage.shared.CmsDialogOptionsAndInfo;
import org.opencms.ade.contenteditor.client.CmsContentEditor;
import org.opencms.ade.contenteditor.client.CmsEditorContext;
import org.opencms.ade.contenteditor.client.I_CmsEditorCloseHandler;
import org.opencms.ade.contenteditor.shared.CmsContentDefinition;
import org.opencms.ade.contenteditor.shared.CmsEditHandlerData;
import org.opencms.ade.publish.shared.CmsPublishOptions;
import org.opencms.gwt.client.CmsCoreProvider;
import org.opencms.gwt.client.CmsEditableData;
import org.opencms.gwt.client.I_CmsEditableData;
import org.opencms.gwt.client.ui.contenteditor.CmsContentEditorDialog;
import org.opencms.gwt.client.ui.contenteditor.CmsContentEditorDialog.DialogOptions;
import org.opencms.gwt.client.ui.contenteditor.I_CmsContentEditorHandler;
import org.opencms.gwt.client.util.CmsDebugLog;
import org.opencms.gwt.client.util.CmsDomUtil;
import org.opencms.gwt.client.util.I_CmsSimpleCallback;
import org.opencms.gwt.shared.CmsGwtConstants;
import org.opencms.util.CmsStringUtil;
import org.opencms.util.CmsUUID;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.google.gwt.dom.client.Element;
import com.google.gwt.http.client.URL;
import com.google.gwt.json.client.JSONBoolean;
import com.google.gwt.json.client.JSONNumber;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONString;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.Window;
import jsinterop.base.Js;
/**
* The container-page editor implementation of the XML content editor handler.
*
* @since 8.0.0
*/
public class CmsContentEditorHandler implements I_CmsContentEditorHandler {
/** Content editor hash key whre a return to the opened editor is not possible. */
private static final String EDITOR_FOR_NO_RETURN_HASH_KEY = "cE";
/** Content editor hash key used for history management. */
private static final String EDITOR_HASH_KEY = "cE:";
/** The container-page handler. */
CmsContainerpageHandler m_handler;
/** The content element to be replaced by the edited content. */
CmsContainerPageElementPanel m_replaceElement;
/** The currently edited element's id. */
private String m_currentElementId;
/** The depending element's id. */
private String m_dependingElementId;
/** Flag indicating the content editor is currently opened. */
private boolean m_editorOpened;
/**
* Constructor.
*
* @param handler the container-page handler
*/
public CmsContentEditorHandler(CmsContainerpageHandler handler) {
m_handler = handler;
}
/**
* Closes the content editor.
*/
public void closeContentEditor() {
CmsContentEditor.getInstance().closeEditor();
m_editorOpened = false;
}
/**
* @see org.opencms.gwt.client.ui.contenteditor.I_CmsContentEditorHandler#onClose(java.lang.String, org.opencms.util.CmsUUID, boolean, boolean, boolean)
*/
public void onClose(
String sitePath,
CmsUUID structureId,
boolean isNew,
boolean hasChangedSettings,
boolean usedPublishDialog) {
if (m_currentElementId == null) {
m_currentElementId = structureId.toString();
}
// we keep track of how many reloads are still ongoing - if all of them are done, we can send the edit events
final int[] reloadCounter = {0};
I_ReloadHandler reloadHandler = new I_ReloadHandler() {
private List m_todo = new ArrayList<>();
@Override
public void finish() {
if (usedPublishDialog) {
m_handler.m_controller.startPublishLockCheck();
}
reloadCounter[0] -= 1;
if (reloadCounter[0] <= 0) {
m_todo.forEach(item -> item.run());
}
}
@Override
public void onReload(CmsContainerPageElementPanel oldElement, CmsContainerPageElementPanel newElement) {
m_todo.add(() -> {
m_handler.m_controller.sendElementEditedContent(
newElement,
Js.cast(oldElement.getElement()),
isNew);
});
}
};
if (m_replaceElement != null) {
if ((m_handler.m_controller.getData().getDetailId() != null)
&& m_replaceElement.getId().startsWith(m_handler.m_controller.getData().getDetailId().toString())) {
Window.Location.assign(
CmsStringUtil.joinPaths(
CmsCoreProvider.get().getVfsPrefix(),
CmsContainerpageController.getCurrentUri(),
CmsContainerpageController.getServerId(m_currentElementId)));
}
reloadCounter[0] += 1;
m_handler.m_controller.replaceElement(
m_replaceElement,
m_currentElementId,
reloadHandler);
m_replaceElement = null;
if (m_dependingElementId != null) {
reloadCounter[0] += 1;
m_handler.m_controller.reloadElements(new String[] {m_dependingElementId}, reloadHandler);
m_dependingElementId = null;
}
} else if (m_dependingElementId != null) {
reloadCounter[0] += 1;
m_handler.m_controller.reloadElements(new String[] {m_currentElementId, m_dependingElementId}, reloadHandler);
m_dependingElementId = null;
} else {
reloadCounter[0] += 1;
m_handler.m_controller.reloadElements(new String[] {m_currentElementId}, reloadHandler);
}
if (m_currentElementId != null) {
m_handler.addToRecent(m_currentElementId);
}
m_handler.enableToolbarButtons();
m_handler.activateSelection();
m_handler.m_controller.setContentEditing(false);
m_handler.m_controller.reInitInlineEditing();
m_currentElementId = null;
if (hasChangedSettings) {
m_handler.m_controller.setPageChanged(new Runnable[] {});
}
m_editorOpened = false;
}
/**
* Opens the XML content editor.
*
* @param element the container element widget
* @param inline true
to open the in-line editor for the given element if available
* @param wasNew true
in case this is a newly created element not previously edited
*/
public void openDialog(final CmsContainerPageElementPanel element, final boolean inline, boolean wasNew) {
if (!inline && element.hasEditHandler()) {
m_handler.m_controller.getEditOptions(
element.getId(),
false,
new I_CmsSimpleCallback() {
public void execute(CmsDialogOptionsAndInfo editOptions) {
final I_CmsSimpleCallback editCallBack = new I_CmsSimpleCallback() {
public void execute(CmsUUID arg) {
String contentId = element.getId();
if (!element.getId().startsWith(arg.toString())) {
// the content structure ID has changed, the current element needs to be replaced after editing
m_replaceElement = element;
contentId = arg.toString();
}
internalOpenDialog(element, contentId, inline, wasNew);
}
};
if (editOptions == null) {
internalOpenDialog(element, element.getId(), inline, wasNew);
} else if (editOptions.getOptions().getOptions().size() == 1) {
m_handler.m_controller.prepareForEdit(
element.getId(),
editOptions.getOptions().getOptions().get(0).getValue(),
editCallBack);
} else {
CmsOptionDialog dialog = new CmsOptionDialog(
Messages.get().key(Messages.GUI_EDIT_HANDLER_SELECT_EDIT_OPTION_0),
editOptions.getOptions(),
editOptions.getInfo(),
new I_CmsSimpleCallback() {
public void execute(String arg) {
m_handler.m_controller.prepareForEdit(element.getId(), arg, editCallBack);
}
});
dialog.addDialogClose(new Command() {
public void execute() {
cancelEdit();
}
});
dialog.center();
}
}
});
} else {
internalOpenDialog(element, element.getId(), inline, wasNew);
}
}
/**
* Opens the XML content editor, checking for if an edit handler is configured first.
*
* @param editableData the data of the element to edit
* @param isNew true
if a new resource should be created
* @param dependingElementId the id of a depending element
* @param mode the element creation mode
* @param handlerDataForNew the edit handler data, if we are using an edit handler to create a new element; null otherwise
*/
public void openDialog(
final I_CmsEditableData editableData,
final boolean isNew,
final String dependingElementId,
final String mode,
final CmsEditHandlerData handlerDataForNew) {
if (!m_editorOpened) {
m_editorOpened = true;
m_handler.disableToolbarButtons();
m_handler.deactivateCurrentButton();
if (!isNew && (editableData.getStructureId() != null) && editableData.hasEditHandler()) {
final String elementId = CmsContentEditor.getClientIdForEditable(editableData);
m_handler.m_controller.getEditOptions(
elementId,
true,
new I_CmsSimpleCallback() {
public void execute(CmsDialogOptionsAndInfo editOptions) {
final I_CmsSimpleCallback editCallBack = new I_CmsSimpleCallback() {
public void execute(CmsUUID arg) {
I_CmsEditableData data = editableData;
if (!data.getStructureId().equals(arg)) {
// the content structure ID has changed, change the editableData
data = new CmsEditableData(data);
((CmsEditableData)data).setStructureId(arg);
}
internalOpenDialog(data, isNew, dependingElementId, mode, null);
}
};
if (editOptions == null) {
internalOpenDialog(editableData, isNew, dependingElementId, mode, null);
} else if (editOptions.getOptions().getOptions().size() == 1) {
m_handler.m_controller.prepareForEdit(
elementId,
editOptions.getOptions().getOptions().get(0).getValue(),
editCallBack);
} else {
CmsOptionDialog dialog = new CmsOptionDialog(
Messages.get().key(Messages.GUI_EDIT_HANDLER_SELECT_EDIT_OPTION_0),
editOptions.getOptions(),
editOptions.getInfo(),
new I_CmsSimpleCallback() {
public void execute(String arg) {
m_handler.m_controller.prepareForEdit(elementId, arg, editCallBack);
}
});
dialog.addDialogClose(new Command() {
public void execute() {
cancelEdit();
}
});
dialog.center();
}
}
});
} else {
internalOpenDialog(editableData, isNew, dependingElementId, mode, handlerDataForNew);
}
} else {
CmsDebugLog.getInstance().printLine("Editor is already being opened.");
}
}
/**
* Opens the content editor according to the history hash.
*
* @param historyHash the history hash
*/
public void openEditorForHistory(String historyHash) {
if (historyHash.startsWith(EDITOR_HASH_KEY)) {
if (!m_editorOpened) {
m_editorOpened = true;
CmsDebugLog.getInstance().printLine("EditorHandler - Opening editor from history");
m_handler.m_controller.setContentEditing(true);
String id = historyHash.substring(EDITOR_HASH_KEY.length(), historyHash.indexOf(";"));
if (id.contains(",")) {
String[] ids = id.split(",");
m_currentElementId = URL.decodePathSegment(ids[0]);
m_dependingElementId = URL.decodePathSegment(ids[1]);
} else {
m_currentElementId = URL.decodePathSegment(id);
}
I_CmsEditorCloseHandler onClose = new I_CmsEditorCloseHandler() {
public void onClose(boolean hasChangedSettings, boolean usedPublishDialog) {
addClosedEditorHistoryItem();
CmsContentEditorHandler.this.onClose(
null,
new CmsUUID(getCurrentElementId()),
false,
hasChangedSettings,
usedPublishDialog);
}
};
String editorLocale = CmsCoreProvider.get().getLocale();
CmsContentEditor.getInstance().openFormEditor(
getEditorContext(),
editorLocale,
m_currentElementId,
null,
null,
null,
null,
null,
m_handler.m_controller.getData().getMainLocale(),
null,
onClose);
}
} else {
closeContentEditor();
}
}
/**
* Returns the currently edited element's id.
*
* @return the currently edited element's id
*/
protected String getCurrentElementId() {
return m_currentElementId;
}
/**
* Adds a history item for the closed editor.
*/
void addClosedEditorHistoryItem() {
History.newItem("", false);
}
/**
* Cancels opening the editor.
*/
void cancelEdit() {
m_handler.enableToolbarButtons();
m_handler.activateSelection();
m_handler.m_controller.setContentEditing(false);
m_handler.m_controller.reInitInlineEditing();
m_replaceElement = null;
m_dependingElementId = null;
m_currentElementId = null;
m_editorOpened = false;
}
/**
* Gets the editor context to use for the Acacia editor.
*
* @return the editor context
*/
CmsEditorContext getEditorContext() {
CmsEditorContext result = new CmsEditorContext();
result.getPublishParameters().put(
CmsPublishOptions.PARAM_CONTAINERPAGE,
"" + CmsCoreProvider.get().getStructureId());
result.getPublishParameters().put(
CmsPublishOptions.PARAM_DETAIL,
"" + CmsContainerpageController.get().getData().getDetailId());
result.getPublishParameters().put(CmsPublishOptions.PARAM_START_WITH_CURRENT_PAGE, "");
elemental2.dom.HTMLMetaElement meta = Js.cast(
elemental2.dom.DomGlobal.document.querySelector(
"meta[name=" + CmsGwtConstants.META_EDITOR_STYLESHEET + "]"));
if (meta != null) {
result.setEditorStylesheet(meta.content);
}
return result;
}
/**
* Opens the edit dialog.
*
* @param element the element to edit
* @param editContentId the edit content id
* @param inline true
to edit the content inline
* @param wasNew true
in case this is a newly created element not previously edited
*/
void internalOpenDialog(
final CmsContainerPageElementPanel element,
String editContentId,
final boolean inline,
boolean wasNew) {
if (!m_editorOpened) {
m_editorOpened = true;
m_handler.disableToolbarButtons();
m_handler.deactivateCurrentButton();
m_currentElementId = editContentId;
final String serverId = CmsContainerpageController.getServerId(m_currentElementId);
final Runnable classicEdit = new Runnable() {
public void run() {
CmsEditableData editableData = new CmsEditableData();
editableData.setElementLanguage(CmsCoreProvider.get().getLocale());
editableData.setStructureId(new CmsUUID(serverId));
editableData.setSitePath(element.getSitePath());
editableData.setMainLanguage(m_handler.m_controller.getData().getMainLocale());
CmsContentEditorDialog.get().openEditDialog(
editableData,
false,
null,
new DialogOptions(),
CmsContentEditorHandler.this);
}
};
if (m_handler.m_controller.getData().isUseClassicEditor() || element.isNewEditorDisabled()) {
classicEdit.run();
} else {
String editorLocale = CmsCoreProvider.get().getLocale();
final String mainLocale;
if (m_handler.m_controller.getData().getMainLocale() == null) {
Element htmlEl = CmsDomUtil.querySelector(
"[" + CmsGwtConstants.ATTR_DATA_ID + "*='" + serverId + "']",
element.getElement());
if (htmlEl != null) {
String entityId = htmlEl.getAttribute(CmsGwtConstants.ATTR_DATA_ID);
mainLocale = CmsContentDefinition.getLocaleFromId(entityId);
} else {
mainLocale = null;
}
} else {
mainLocale = m_handler.m_controller.getData().getMainLocale();
}
I_CmsEditorCloseHandler onClose = new I_CmsEditorCloseHandler() {
public void onClose(boolean hasChangedSettings, boolean usedPublishDialog) {
addClosedEditorHistoryItem();
CmsContentEditorHandler.this.onClose(
element.getSitePath(),
new CmsUUID(serverId),
wasNew,
hasChangedSettings,
usedPublishDialog);
}
};
if (inline && CmsContentEditor.hasEditable(element.getElement())) {
addEditingHistoryItem(true);
CmsEditorContext context = getEditorContext();
context.setReusedElement(element.isReused());
context.setHtmlContextInfo(getContextInfo(element));
// remove expired style before initializing the editor
element.setReleasedAndNotExpired(true);
// in case of new elements, ignore load time
long loadTime = wasNew ? Long.MAX_VALUE : m_handler.m_controller.getLoadTime();
CmsContentEditor.getInstance().openInlineEditor(
context,
new CmsUUID(serverId),
editorLocale,
element,
mainLocale,
loadTime,
onClose);
} else {
addEditingHistoryItem(false);
Map settingPresets = new HashMap();
CmsEditorContext context = getEditorContext();
context.setReusedElement(element.isReused());
I_CmsDropContainer dropContainer = element.getParentTarget();
if (dropContainer instanceof CmsContainerPageContainer) {
CmsContainerPageContainer container = (CmsContainerPageContainer)dropContainer;
settingPresets.putAll(container.getSettingPresets());
}
context.setSettingPresets(settingPresets);
boolean allowSettings = m_handler.m_controller.getData().allowSettingsInEditor()
&& !m_handler.m_controller.isEditingDisabled()
&& !serverId.equals(String.valueOf(m_handler.m_controller.getData().getDetailId()));
I_CmsSimpleCallback openEditor = new I_CmsSimpleCallback() {
public void execute(Boolean lockedPage) {
CmsContentEditor.getInstance().openFormEditor(
context,
editorLocale,
serverId,
lockedPage.booleanValue() ? getCurrentElementId() : null,
null,
null,
null,
null,
mainLocale,
null,
onClose);
}
};
if (allowSettings) {
if (m_handler.m_controller.getData().getDetailContainerPage() != null) {
CmsCoreProvider.get().lock(
m_handler.m_controller.getData().getDetailContainerPage(),
m_handler.m_controller.getLoadTime(),
openEditor);
} else {
CmsCoreProvider.get().lock(
CmsCoreProvider.get().getStructureId(),
m_handler.m_controller.getLoadTime(),
openEditor);
}
} else {
openEditor.execute(Boolean.FALSE);
}
}
}
} else {
CmsDebugLog.getInstance().printLine("Editor is already being opened.");
}
}
/**
* Opens the XML content editor internally.
*
* @param editableData the data of the element to edit
* @param isNew true
if a new resource should be created
* @param dependingElementId the id of a depending element
* @param mode the element creation mode
* @param editHandlerData the edit handler data, if we are using an edit handler to create a new element; null otherwise
*/
void internalOpenDialog(
final I_CmsEditableData editableData,
final boolean isNew,
String dependingElementId,
String mode,
CmsEditHandlerData editHandlerData) {
if ((editableData.getStructureId() != null) && !isNew) {
m_currentElementId = editableData.getStructureId().toString();
} else {
m_currentElementId = null;
}
m_dependingElementId = dependingElementId;
if (m_handler.m_controller.getData().isUseClassicEditor()) {
CmsContentEditorDialog.get().openEditDialog(editableData, isNew, mode, new DialogOptions(), this);
} else {
String newLink = null;
if (isNew) {
newLink = editableData.getNewLink();
// the new link is URL encoded twice, decode it
newLink = URL.decodeQueryString(newLink);
newLink = URL.decodeQueryString(newLink);
}
addEditingHistoryItem(isNew);
CmsContentEditor.getInstance().openFormEditor(
getEditorContext(),
CmsCoreProvider.get().getLocale(),
editableData.getStructureId().toString(),
null,
newLink,
null,
editableData.getPostCreateHandler(),
mode,
m_handler.m_controller.getData().getMainLocale(),
editHandlerData,
new I_CmsEditorCloseHandler() {
public void onClose(boolean hasChangedSettings, boolean usedPublishDialog) {
addClosedEditorHistoryItem();
CmsContentEditorHandler.this.onClose(
editableData.getSitePath(),
editableData.getStructureId(),
isNew,
hasChangedSettings,
usedPublishDialog);
}
});
}
}
/**
* Adds a history item for the opened editor.
* Use the prohibitReturn flag to deny a return to the opened editor through the browser history.
* Use this feature for inline editing or when opening the editor for new resources.
*
* @param prohibitReturn if true
returning to the opened editor through the browser history is denied
*/
private void addEditingHistoryItem(boolean prohibitReturn) {
if (prohibitReturn) {
History.newItem(EDITOR_FOR_NO_RETURN_HASH_KEY, false);
} else {
History.newItem(
EDITOR_HASH_KEY
+ CmsContainerpageController.getServerId(getCurrentElementId())
+ (m_dependingElementId != null ? "," + m_dependingElementId + ";" : ";"),
false);
}
}
/**
* Returns the HTML context info for the given element.
*
* @param element the edited element
*
* @return the JSON string
*/
private String getContextInfo(CmsContainerPageElementPanel element) {
CmsContainerPageContainer container;
if (m_handler.m_controller.isGroupcontainerEditing()) {
container = (CmsContainerPageContainer)((CmsContainerPageElementPanel)element.getParentTarget()).getParentTarget();
} else {
container = (CmsContainerPageContainer)element.getParentTarget();
}
JSONObject result = new JSONObject();
putString(result, CmsCntPageData.JSONKEY_ELEMENT_ID, element.getId());
CmsUUID detailId = m_handler.m_controller.getData().getDetailId();
if (detailId != null) {
putString(result, CmsCntPageData.JSONKEY_DETAIL_ELEMENT_ID, "" + detailId);
}
putString(result, CmsCntPageData.JSONKEY_NAME, "" + container.getContainerId());
putString(result, CmsCntPageData.JSONKEY_TYPE, "" + container.getContainerType());
result.put(CmsCntPageData.JSONKEY_WIDTH, new JSONNumber(container.getConfiguredWidth()));
result.put(CmsCntPageData.JSONKEY_DETAILVIEW, JSONBoolean.getInstance(container.isDetailView()));
result.put(
CmsCntPageData.JSONKEY_ISDETAILVIEWCONTAINER,
JSONBoolean.getInstance(container.isDetailViewContainer()));
result.put(CmsCntPageData.JSONKEY_DETAILONLY, JSONBoolean.getInstance(container.isDetailOnly()));
result.put(CmsCntPageData.JSONKEY_MAXELEMENTS, new JSONNumber(1));
JSONObject presets = new JSONObject();
Map presetsMap = container.getSettingPresets();
for (Map.Entry entry : presetsMap.entrySet()) {
presets.put(entry.getKey(), new JSONString(entry.getValue()));
}
result.put(CmsCntPageData.JSONKEY_PRESETS, presets);
return result.toString();
}
/**
* Adds a String value to the JSON object.
*
* @param obj the object to manipulate
* @param key the key
* @param val the value
*/
private void putString(JSONObject obj, String key, String val) {
obj.put(key, new JSONString(val));
}
}