org.opencms.ade.sitemap.client.CmsSitemapView 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 (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.sitemap.client;
import org.opencms.ade.sitemap.client.control.CmsSitemapChangeEvent;
import org.opencms.ade.sitemap.client.control.CmsSitemapController;
import org.opencms.ade.sitemap.client.control.CmsSitemapDNDController;
import org.opencms.ade.sitemap.client.control.CmsSitemapLoadEvent;
import org.opencms.ade.sitemap.client.control.I_CmsSitemapChangeHandler;
import org.opencms.ade.sitemap.client.control.I_CmsSitemapLoadHandler;
import org.opencms.ade.sitemap.client.hoverbar.CmsSitemapHoverbar;
import org.opencms.ade.sitemap.client.toolbar.CmsSitemapToolbar;
import org.opencms.ade.sitemap.client.ui.CmsStatusIconUpdateHandler;
import org.opencms.ade.sitemap.client.ui.css.I_CmsImageBundle;
import org.opencms.ade.sitemap.client.ui.css.I_CmsSitemapLayoutBundle;
import org.opencms.ade.sitemap.shared.CmsClientSitemapEntry;
import org.opencms.ade.sitemap.shared.CmsDetailPageTable;
import org.opencms.ade.sitemap.shared.CmsSitemapChange;
import org.opencms.ade.sitemap.shared.CmsSitemapData;
import org.opencms.ade.sitemap.shared.CmsSitemapInfo;
import org.opencms.gwt.client.A_CmsEntryPoint;
import org.opencms.gwt.client.CmsPingTimer;
import org.opencms.gwt.client.dnd.CmsDNDHandler;
import org.opencms.gwt.client.ui.CmsErrorDialog;
import org.opencms.gwt.client.ui.CmsInfoHeader;
import org.opencms.gwt.client.ui.CmsListItemWidget.Background;
import org.opencms.gwt.client.ui.CmsNotification;
import org.opencms.gwt.client.ui.tree.CmsLazyTree;
import org.opencms.gwt.client.ui.tree.CmsLazyTreeItem;
import org.opencms.gwt.client.ui.tree.CmsTreeItem;
import org.opencms.gwt.client.ui.tree.I_CmsLazyOpenHandler;
import org.opencms.gwt.client.util.CmsDomUtil;
import org.opencms.gwt.client.util.CmsStyleVariable;
import org.opencms.gwt.shared.CmsIconUtil;
import org.opencms.util.CmsPair;
import org.opencms.util.CmsStringUtil;
import org.opencms.util.CmsUUID;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.event.logical.shared.OpenEvent;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
/**
* Sitemap editor.
*
* @since 8.0.0
*/
public final class CmsSitemapView extends A_CmsEntryPoint implements I_CmsSitemapChangeHandler, I_CmsSitemapLoadHandler {
/** The sitemap editor modes. */
public enum EditorMode {
/** The navigation mode. */
navigation,
/** The VFS mode. */
vfs
}
/**
* The sitemap tree open handler.
*/
protected class TreeOpenHandler implements I_CmsLazyOpenHandler {
/** Flag indicating the tree is initializing. */
private boolean m_initializing;
/**
* @see org.opencms.gwt.client.ui.tree.I_CmsLazyOpenHandler#load(org.opencms.gwt.client.ui.tree.CmsLazyTreeItem)
*/
public void load(final CmsSitemapTreeItem target) {
// not used
}
/**
* @see org.opencms.gwt.client.ui.tree.I_CmsLazyOpenHandler#onOpen(com.google.gwt.event.logical.shared.OpenEvent)
*/
public void onOpen(OpenEvent event) {
CmsSitemapTreeItem target = event.getTarget();
if ((target.getLoadState() == CmsLazyTreeItem.LoadState.UNLOADED)) {
target.onStartLoading();
target.setOpen(false);
getController().getChildren(target.getEntryId(), true, null);
} else if (!m_initializing
&& ((target.getChildren().getWidgetCount() > 0) && (((CmsSitemapTreeItem)target.getChild(0)).getLoadState() == CmsLazyTreeItem.LoadState.UNLOADED))) {
// load grand children in advance
getController().getChildren(target.getEntryId(), false, null);
}
}
/**
* Sets the initializing flag.
*
* @param initializing the initializing flag
*/
protected void setInitializing(boolean initializing) {
m_initializing = initializing;
}
}
/** The singleton instance. */
private static CmsSitemapView m_instance;
/** Text metrics key. */
private static final String TM_SITEMAP = "Sitemap";
/** The displayed sitemap tree. */
protected CmsLazyTree m_tree;
/** The controller. */
private CmsSitemapController m_controller;
/** The current sitemap editor mode. */
private EditorMode m_editorMode;
/** Style variable which keeps track of whether we are in VFS mode or navigation mode. */
private CmsStyleVariable m_inNavigationStyle;
/** The tree open handler. */
private TreeOpenHandler m_openHandler;
/** The sitemap toolbar. */
private CmsSitemapToolbar m_toolbar;
/** The registered tree items. */
private Map m_treeItems;
/**
* Returns the instance.
*
* @return the instance
*/
public static CmsSitemapView getInstance() {
return m_instance;
}
/**
* Creates a new tree item from the given sitemap entry.
*
* @param entry the sitemap entry
*
* @return the new created (still orphan) tree item
*/
public CmsSitemapTreeItem create(CmsClientSitemapEntry entry) {
CmsSitemapTreeItem treeItem = new CmsSitemapTreeItem(entry);
CmsSitemapHoverbar.installOn(m_controller, treeItem);
// highlight the open path
if (isLastPage(entry)) {
treeItem.setBackgroundColor(Background.YELLOW);
}
m_treeItems.put(entry.getId(), treeItem);
return treeItem;
}
/**
* Creates a sitemap tree item from a client sitemap entry.
*
* @param entry the entry from which the sitemap tree item should be created
*
* @return the new sitemap tree item
*/
public CmsSitemapTreeItem createSitemapItem(CmsClientSitemapEntry entry) {
CmsSitemapTreeItem result = create(entry);
result.clearChildren();
for (CmsClientSitemapEntry child : entry.getSubEntries()) {
CmsSitemapTreeItem childItem = createSitemapItem(child);
result.addChild(childItem);
}
if (entry.getChildrenLoadedInitially()) {
result.onFinishLoading();
}
return result;
}
/**
* Ensures the given item is visible in the viewport.
*
* @param item the item to see
*/
public void ensureVisible(CmsSitemapTreeItem item) {
// open the tree
CmsTreeItem ti = item.getParentItem();
while (ti != null) {
ti.setOpen(true);
ti = ti.getParentItem();
}
// scroll
CmsDomUtil.ensureVisible(RootPanel.getBodyElement(), item.getElement(), 200);
}
/**
* Returns the controller.
*
* @return the controller
*/
public CmsSitemapController getController() {
return m_controller;
}
/**
* Returns the editor mode.
*
* @return the editor mode
*/
public EditorMode getEditorMode() {
return m_editorMode;
}
/**
* Returns the icon class for the given entry depending on the editor mode.
*
* @param entry the entry to get the icon for
*
* @return the icon CSS class
*/
public String getIconForEntry(CmsClientSitemapEntry entry) {
if (!entry.isSubSitemapType() && entry.isNavigationLevelType()) {
return "cms_type_icon " + I_CmsSitemapLayoutBundle.INSTANCE.sitemapItemCss().navigationLevelIcon();
}
String iconClass = CmsIconUtil.getResourceIconClasses(entry.getResourceTypeName(), entry.getSitePath(), false);
if (isNavigationMode()) {
if (m_controller.isDetailPage(entry.getId())) {
iconClass = CmsIconUtil.getResourceIconClasses(
m_controller.getDetailPageInfo(entry.getId()).getIconType(),
false);
} else if (!entry.isSubSitemapType()
&& CmsStringUtil.isNotEmptyOrWhitespaceOnly(entry.getDefaultFileType())) {
iconClass = CmsIconUtil.getResourceIconClasses(entry.getDefaultFileType(), false);
}
}
return iconClass;
}
/**
* Gets the list of descendants of a path and splits it into two lists, one containing the sitemap entries whose children have
* already been loaded, and those whose children haven't been loaded.
*
* @param path the path for which the open and closed descendants should be returned
*
* @return a pair whose first and second components are lists of open and closed descendant entries of the path, respectively
*/
public CmsPair, List> getOpenAndClosedDescendants(String path) {
List descendants = m_controller.getLoadedDescendants(path);
List openDescendants = new ArrayList();
List closedDescendants = new ArrayList();
for (CmsClientSitemapEntry entry : descendants) {
CmsSitemapTreeItem treeItem = getTreeItem(entry.getSitePath());
List listToAddTo = treeItem.isLoaded() ? openDescendants : closedDescendants;
listToAddTo.add(entry);
}
return new CmsPair, List>(openDescendants, closedDescendants);
}
/**
* Gets the sitemap toolbar.
*
* @return the sitemap toolbar
*/
public CmsSitemapToolbar getToolbar() {
return m_toolbar;
}
/**
* Returns the tree.
*
* @return the tree
*/
public CmsLazyTree getTree() {
return m_tree;
}
/**
* Returns the tree entry with the given path.
*
* @param entryId the id of the sitemap entry
*
* @return the tree entry with the given path, or null
if not found
*/
public CmsSitemapTreeItem getTreeItem(CmsUUID entryId) {
return m_treeItems.get(entryId);
}
/**
* Returns the tree entry with the given path.
*
* @param path the path to look for
*
* @return the tree entry with the given path, or null
if not found
*/
public CmsSitemapTreeItem getTreeItem(String path) {
CmsSitemapData data = m_controller.getData();
CmsClientSitemapEntry root = data.getRoot();
String rootSitePath = root.getSitePath();
String remainingPath = path.substring(rootSitePath.length());
CmsSitemapTreeItem result = getRootItem();
String[] names = CmsStringUtil.splitAsArray(remainingPath, "/");
for (String name : names) {
if (CmsStringUtil.isEmptyOrWhitespaceOnly(name)) {
continue;
}
result = (CmsSitemapTreeItem)result.getChild(name);
if (result == null) {
return null;
}
}
return result;
}
/**
* Highlights the sitemap entry with the given path.
*
* @param sitePath the sitemap path of the entry to highlight
*/
public void highlightPath(String sitePath) {
openItemsOnPath(sitePath);
CmsSitemapTreeItem item = getTreeItem(sitePath);
if (item != null) {
item.highlightTemporarily(1500, isLastPage(item.getSitemapEntry()) ? Background.YELLOW : Background.DEFAULT);
}
}
/**
* Returns if the current sitemap editor mode is navigation.
*
* @return true
if the current sitemap editor mode is navigation
*/
public boolean isNavigationMode() {
return EditorMode.navigation == m_editorMode;
}
/**
* @see org.opencms.ade.sitemap.client.control.I_CmsSitemapChangeHandler#onChange(org.opencms.ade.sitemap.client.control.CmsSitemapChangeEvent)
*/
public void onChange(CmsSitemapChangeEvent changeEvent) {
CmsSitemapChange change = changeEvent.getChange();
switch (change.getChangeType()) {
case delete:
CmsSitemapTreeItem item = getTreeItem(change.getEntryId());
item.getParentItem().removeChild(item);
break;
case undelete:
case create:
CmsClientSitemapEntry newEntry = m_controller.getEntryById(change.getEntryId());
CmsSitemapTreeItem newItem = createSitemapItem(newEntry);
getTreeItem(change.getParentId()).insertChild(newItem, newEntry.getPosition());
break;
case bumpDetailPage:
updateDetailPageView(m_controller.getEntryById(change.getEntryId()));
updateAll(m_controller.getEntryById(change.getEntryId()));
break;
case modify:
if (change.hasChangedPosition() || change.hasNewParent()) {
CmsClientSitemapEntry entry = m_controller.getEntryById(change.getEntryId());
CmsSitemapTreeItem moveEntry = getTreeItem(change.getEntryId());
CmsSitemapTreeItem sourceParent = (CmsSitemapTreeItem)moveEntry.getParentItem();
getTree().setAnimationEnabled(false);
sourceParent.removeChild(moveEntry);
CmsSitemapTreeItem destParent = change.hasNewParent()
? getTreeItem(change.getParentId())
: sourceParent;
if (entry.getPosition() < destParent.getChildCount()) {
destParent.insertChild(moveEntry, entry.getPosition());
} else {
destParent.addChild(moveEntry);
}
updateAll(entry);
ensureVisible(moveEntry);
getTree().setAnimationEnabled(true);
break;
}
//$FALL-THROUGH$
case remove:
updateAll(m_controller.getEntryById(change.getEntryId()));
break;
default:
}
}
/**
* @see org.opencms.ade.sitemap.client.control.I_CmsSitemapLoadHandler#onLoad(org.opencms.ade.sitemap.client.control.CmsSitemapLoadEvent)
*/
public void onLoad(CmsSitemapLoadEvent event) {
CmsSitemapTreeItem target = getTreeItem(event.getEntry().getId());
target.getTree().setAnimationEnabled(false);
target.clearChildren();
for (CmsClientSitemapEntry child : event.getEntry().getSubEntries()) {
CmsSitemapTreeItem childItem = createSitemapItem(child);
target.addChild(childItem);
}
target.onFinishLoading();
target.getTree().setAnimationEnabled(true);
if (event.isSetOpen()) {
target.setOpen(true);
}
m_controller.recomputeProperties();
}
/**
* @see com.google.gwt.core.client.EntryPoint#onModuleLoad()
*/
@Override
public void onModuleLoad() {
super.onModuleLoad();
CmsPingTimer.start();
m_instance = this;
RootPanel rootPanel = RootPanel.get();
m_editorMode = EditorMode.navigation;
// init
I_CmsSitemapLayoutBundle.INSTANCE.sitemapCss().ensureInjected();
I_CmsSitemapLayoutBundle.INSTANCE.clipboardCss().ensureInjected();
I_CmsSitemapLayoutBundle.INSTANCE.sitemapItemCss().ensureInjected();
I_CmsSitemapLayoutBundle.INSTANCE.propertiesCss().ensureInjected();
I_CmsImageBundle.INSTANCE.buttonCss().ensureInjected();
rootPanel.addStyleName(I_CmsSitemapLayoutBundle.INSTANCE.sitemapCss().root());
m_treeItems = new HashMap();
// controller
m_controller = new CmsSitemapController();
if (m_controller.getData() == null) {
CmsErrorDialog dialog = new CmsErrorDialog(Messages.get().key(Messages.GUI_ERROR_ON_SITEMAP_LOAD_0), null);
dialog.center();
return;
}
m_controller.addChangeHandler(this);
m_controller.addLoadHandler(this);
// toolbar
m_toolbar = new CmsSitemapToolbar(m_controller);
rootPanel.add(m_toolbar);
CmsSitemapInfo info = m_controller.getData().getSitemapInfo();
// header
CmsInfoHeader header = new CmsInfoHeader(
info.getTitle(),
info.getDescription(),
info.getSiteHost(),
info.getSiteLocale(),
CmsIconUtil.getResourceIconClasses(m_controller.getData().getRoot().getResourceTypeName(), false));
header.addStyleName(I_CmsSitemapLayoutBundle.INSTANCE.sitemapCss().pageCenter());
rootPanel.add(header);
final FlowPanel page = new FlowPanel();
page.setStyleName(I_CmsSitemapLayoutBundle.INSTANCE.sitemapCss().page());
page.addStyleName(I_CmsSitemapLayoutBundle.INSTANCE.generalCss().cornerAll());
rootPanel.add(page);
// initial content
final Label loadingLabel = new Label(org.opencms.gwt.client.Messages.get().key(
org.opencms.gwt.client.Messages.GUI_LOADING_0));
page.add(loadingLabel);
// initialize the tree
m_openHandler = new TreeOpenHandler();
m_tree = new CmsLazyTree(m_openHandler);
m_inNavigationStyle = new CmsStyleVariable(m_tree);
if (m_controller.isEditable()) {
// enable drag'n drop
CmsDNDHandler dndHandler = new CmsDNDHandler(new CmsSitemapDNDController(m_controller, m_toolbar));
dndHandler.addTarget(m_tree);
m_tree.setDNDHandler(dndHandler);
m_tree.setDropEnabled(true);
m_tree.setDNDTakeAll(true);
}
m_tree.truncate(TM_SITEMAP, 920);
m_tree.setAnimationEnabled(true);
page.add(m_tree);
// draw tree items
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
public void execute() {
initiateTreeItems(page, loadingLabel);
}
});
}
/**
* Removes deleted entry widget reference.
*
* @param entry the entry being deleted
*/
public void removeDeleted(CmsClientSitemapEntry entry) {
for (CmsClientSitemapEntry child : entry.getSubEntries()) {
removeDeleted(child);
}
m_treeItems.remove(entry.getId());
}
/**
* Sets the editor mode.
*
* @param editorMode the editor mode to set
*/
public void setEditorMode(EditorMode editorMode) {
m_editorMode = editorMode;
if (m_editorMode == EditorMode.vfs) {
m_toolbar.setNewEnabled(false, Messages.get().key(Messages.GUI_TOOLBAR_NEW_DISABLE_0));
m_inNavigationStyle.setValue(I_CmsSitemapLayoutBundle.INSTANCE.sitemapItemCss().vfsMode());
} else {
m_toolbar.setNewEnabled(true, null);
m_inNavigationStyle.setValue(I_CmsSitemapLayoutBundle.INSTANCE.sitemapItemCss().navMode());
}
getRootItem().updateEditorMode();
}
/**
* Updates the detail page view for a given changed entry.
*
* @param entry the entry which was changed
*/
public void updateDetailPageView(CmsClientSitemapEntry entry) {
CmsDetailPageTable detailPageTable = m_controller.getDetailPageTable();
List idsToUpdate = new ArrayList();
if (m_controller.isDetailPage(entry)) {
idsToUpdate.add(entry.getId());
idsToUpdate.addAll(detailPageTable.getAllIds());
}
updateEntriesById(idsToUpdate);
}
/**
* Updates the entries whose id is in the given list of ids.
*
* @param ids a list of sitemap entry ids
*/
public void updateEntriesById(Collection ids) {
Map entries = m_controller.getEntriesById(ids);
for (CmsClientSitemapEntry entry : entries.values()) {
CmsSitemapTreeItem item = CmsSitemapTreeItem.getItemById(entry.getId());
item.updateEntry(entry);
}
}
/**
* Gets the sitemap tree item widget which represents the root of the current sitemap.
*
* @return the root sitemap tree item widget
*/
protected CmsSitemapTreeItem getRootItem() {
return (CmsSitemapTreeItem)(m_tree.getWidget(0));
}
/**
* Builds the tree items initially.
*
* @param page the page
* @param loadingLabel the loading label, will be removed when finished
*/
void initiateTreeItems(FlowPanel page, Label loadingLabel) {
CmsClientSitemapEntry root = m_controller.getData().getRoot();
CmsSitemapTreeItem rootItem = createSitemapItem(root);
rootItem.onFinishLoading();
rootItem.setOpen(true);
m_tree.addItem(rootItem);
setEditorMode(EditorMode.navigation);
m_controller.addPropertyUpdateHandler(new CmsStatusIconUpdateHandler());
m_controller.recomputeProperties();
rootItem.updateSitePath();
// check if editable
if (!m_controller.isEditable()) {
// notify user
CmsNotification.get().sendSticky(
CmsNotification.Type.WARNING,
Messages.get().key(Messages.GUI_NO_EDIT_NOTIFICATION_1, m_controller.getData().getNoEditReason()));
}
String openPath = m_controller.getData().getOpenPath();
if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(openPath)) {
m_openHandler.setInitializing(true);
openItemsOnPath(openPath);
m_openHandler.setInitializing(false);
}
page.remove(loadingLabel);
}
/**
* Helper method to get all sitemap tree items from the root to a given path.
*
* For example, if the root item has the site path '/root/', and the value of path is
* '/root/a/b/', the sitemap tree items corresponding to '/root/', '/root/a/' and '/root/a/b'
* will be returned (in that order).
*
* @param path the path for which the sitemap tree items should be returned
*
* @return the sitemap tree items on the path
*/
private List getItemsOnPath(String path) {
List result = new ArrayList();
CmsSitemapData data = m_controller.getData();
CmsClientSitemapEntry root = data.getRoot();
String rootSitePath = root.getSitePath();
String remainingPath = path.substring(rootSitePath.length());
CmsSitemapTreeItem currentItem = getRootItem();
result.add(currentItem);
String[] names = CmsStringUtil.splitAsArray(remainingPath, "/");
for (String name : names) {
if (currentItem == null) {
break;
}
if (CmsStringUtil.isEmptyOrWhitespaceOnly(name)) {
continue;
}
currentItem = (CmsSitemapTreeItem)currentItem.getChild(name);
if (currentItem != null) {
result.add(currentItem);
}
}
return result;
}
/**
* Checks if the given entry represents the last opened page.
*
* @param entry the entry to check
*
* @return true
if the given entry is the last opened page
*/
private boolean isLastPage(CmsClientSitemapEntry entry) {
return ((entry.isInNavigation() && (entry.getId().toString().equals(m_controller.getData().getReturnCode()))) || ((entry.getDefaultFileId() != null) && entry.getDefaultFileId().toString().equals(
m_controller.getData().getReturnCode())));
}
/**
* Opens all sitemap tree items on a path, except the last one.
*
* @param path the path for which all intermediate sitemap items should be opened
*/
private void openItemsOnPath(String path) {
List itemsOnPath = getItemsOnPath(path);
for (CmsSitemapTreeItem item : itemsOnPath) {
item.setOpen(true);
}
}
/**
* Updates the entry and it's children's view.
*
* @param entry the entry to update
*/
private void updateAll(CmsClientSitemapEntry entry) {
CmsSitemapTreeItem item = getTreeItem(entry.getId());
if (item != null) {
item.updateEntry(entry);
item.updateSitePath(entry.getSitePath());
for (CmsClientSitemapEntry child : entry.getSubEntries()) {
updateAll(child);
}
}
}
}