// // ERXNavigationManager.java // ERExtensions // // Created by Max Muller on Wed Oct 30 2002. // package er.extensions.appserver.navigation; import java.util.Enumeration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.webobjects.appserver.WOApplication; import com.webobjects.appserver.WOSession; import com.webobjects.foundation.NSArray; import com.webobjects.foundation.NSDictionary; import com.webobjects.foundation.NSMutableArray; import com.webobjects.foundation.NSMutableDictionary; import com.webobjects.foundation.NSNotification; import com.webobjects.foundation.NSSelector; import com.webobjects.foundation._NSUtilities; import er.extensions.appserver.ERXApplication; import er.extensions.eof.ERXConstant; import er.extensions.foundation.ERXFileNotificationCenter; import er.extensions.foundation.ERXFileUtilities; import er.extensions.foundation.ERXPatcher; import er.extensions.foundation.ERXProperties; import er.extensions.foundation.ERXUtilities; /** Please read "Documentation/Navigation.html" to fnd out how to use the navigation components.*/ public class ERXNavigationManager { private static final Logger log = LoggerFactory.getLogger(ERXNavigationManager.class); protected static ERXNavigationManager manager; public static String NAVIGATION_MAP_KEY = "navigationFlightPlan"; public static ERXNavigationManager manager() { if (manager == null) manager = new ERXNavigationManager(); return manager; } protected NSDictionary<String, ERXNavigationItem> navigationItemsByName = NSDictionary.EmptyDictionary; protected ERXNavigationItem rootNavigationItem; protected String navigationMenuFileName; protected boolean hasRegistered = false; public ERXNavigationState navigationStateForSession(WOSession session) { ERXNavigationState state = (ERXNavigationState)session.objectForKey(navigationStateSessionKey()); if (state == null) { state = new ERXNavigationState(); session.setObjectForKey(state, navigationStateSessionKey()); } return state; } /** * If the current request was created via a link on the navigation menu, * this will retrieve the matching navigation state as a string. * * @param session * @return the applicable navigation state as a string or null if it is * undefined */ public String navigationStateFromMap(WOSession session) { String navigationState = null; if (session.valueForKeyPath("objectStore." + ERXNavigationManager.NAVIGATION_MAP_KEY) != null) { // yes, the request was generated by a click on the navigation menu NSDictionary navigationDictionary = (NSDictionary) session.valueForKeyPath("objectStore." + ERXNavigationManager.NAVIGATION_MAP_KEY); // the current request's URI serves as the key String keyPath = session.context().request().uri().replace('.', '_'); // store it as it will be called keyPath = ERXApplication.erxApplication()._rewriteURL(keyPath); navigationState = (String) navigationDictionary.objectForKey(keyPath); if (navigationState != null) { // check whether the item has a default child ERXNavigationItem navigationItem = ERXNavigationManager.manager().navigationItemForName( NSArray.componentsSeparatedByString(navigationState, ".").lastObject()); // recurse until all default children choices have been considered while (navigationItem != null && navigationItem.defaultChild() != null) { log.debug("Replacing navigation state '{}' with default child's state '{}'", navigationState, navigationState.concat("." + navigationItem.defaultChild())); // change navigation state to that of the defaultChild navigationState = navigationState.concat("." + navigationItem.defaultChild()); navigationItem = navigationItemForName(navigationItem.defaultChild()); } // clear stale entries from flight plan to prevent it from growing navigationDictionary = new NSMutableDictionary(navigationState, keyPath); session.takeValueForKeyPath(navigationDictionary, "objectStore." + NAVIGATION_MAP_KEY); } } return navigationState; } /** * Stores a navigation item's fully qualified state (e.g. * "Movies.SearchMovie") under the corresponding URI for retrieval on the * next request. * * @param session * @param navigationItem * @param uri */ public void storeInNavigationMap(WOSession session, ERXNavigationItem navigationItem, String uri) { // we group the keys in a dictionary to allow easy removal NSMutableDictionary<String, String> navigationFlightPlan = (NSMutableDictionary<String, String>) session.objectForKey(ERXNavigationManager.NAVIGATION_MAP_KEY); if (navigationFlightPlan == null) { navigationFlightPlan = new NSMutableDictionary<>(); } if (uri != null) { navigationFlightPlan.setObjectForKey(navigationItem.navigationPath().replace('/', '.'), uri.replace('.', '_')); session.setObjectForKey(navigationFlightPlan, ERXNavigationManager.NAVIGATION_MAP_KEY); } } public String navigationStateSessionKey() { return "NavigationState"; } public String navigationMenuFileName() { if (navigationMenuFileName == null) { navigationMenuFileName = ERXProperties.stringForKeyWithDefault("er.extensions.ERXNavigationManager.NavigationMenuFileName", "NavigationMenu.plist"); } return navigationMenuFileName; } public void setNavigationMenuFileName(String name) { navigationMenuFileName = name; } public NSDictionary<String, ERXNavigationItem> navigationItemsByName() { return navigationItemsByName; } public ERXNavigationItem rootNavigationItem() { return rootNavigationItem; } public ERXNavigationItem navigationItemForName(String name) { return navigationItemsByName.objectForKey(name); } protected void setNavigationItems(NSArray items) { NSMutableDictionary<String, ERXNavigationItem> itemsByName = new NSMutableDictionary<>(); if (items != null && items.count() > 0) { for (Enumeration e = items.objectEnumerator(); e.hasMoreElements();) { ERXNavigationItem item = (ERXNavigationItem)e.nextElement(); if (itemsByName.objectForKey(item.name()) != null) { log.warn("Attempting to register multiple navigation items for the same name: {}", item.name()); } else { itemsByName.setObjectForKey(item, item.name()); if (item.name().equals("Root")) rootNavigationItem = item; } } } if (rootNavigationItem == null) log.warn("No root navigation item set. You need one."); navigationItemsByName = itemsByName.immutableClone(); } public void configureNavigation() { loadNavigationMenu(); hasRegistered = true; } public void loadNavigationMenu() { NSMutableArray navigationMenus = new NSMutableArray(); // First load the nav_menu from application. NSArray appNavigationMenu = (NSArray)ERXFileUtilities.readPropertyListFromFileInFramework(navigationMenuFileName(), null); if (appNavigationMenu != null) { log.debug("Found navigation menu in application: {}", WOApplication.application().name()); navigationMenus.addObjectsFromArray(createNavigationItemsFromDictionaries(appNavigationMenu)); registerObserverForFramework(null); } for (Enumeration e = ERXUtilities.allFrameworkNames().objectEnumerator(); e.hasMoreElements();) { String frameworkName = (String)e.nextElement(); NSArray aNavigationMenu = (NSArray)ERXFileUtilities.readPropertyListFromFileInFramework(navigationMenuFileName(), frameworkName); if (aNavigationMenu != null && aNavigationMenu.count() > 0) { log.debug("Found navigation menu in framework: {}", frameworkName); navigationMenus.addObjectsFromArray(createNavigationItemsFromDictionaries(aNavigationMenu)); registerObserverForFramework(frameworkName); } } setNavigationItems(navigationMenus); // compute default child items NSMutableDictionary<Object, String> fakeContext = new NSMutableDictionary<>(); for (ERXNavigationItem anItem : navigationItemsByName().allValues()) { anItem.childItemsInContext(fakeContext); } // set defaultChild values where actions match for (String anItemName : navigationItemsByName().allKeys()) { ERXNavigationItem anItem = navigationItemsByName().objectForKey(anItemName); if (anItem.children() != null && anItem.children().count() > 0 && anItem.defaultChild() == null) { for (String aChildName : anItem.children()) { ERXNavigationItem aChild = navigationItemsByName().objectForKey(aChildName); if (aChild != null) { if (anItem.action() != null && anItem.action().equals(aChild.action())) { anItem.setDefaultChild(aChild.name()); break; } else if (anItem.directActionName() != null && anItem.directActionName().equals(aChild.directActionName())) { anItem.setDefaultChild(aChild.name()); break; } else if (anItem.href() != null && anItem.href().equals(aChild.href())) { anItem.setDefaultChild(aChild.name()); break; } else if (anItem.pageName() != null && anItem.pageName().equals(aChild.pageName())) { anItem.setDefaultChild(aChild.name()); break; } } else { log.warn("You set an undefined child on the item {}", anItem.name()); } } } } log.debug("Navigation Menu Configured"); } public void registerObserverForFramework(String frameworkName) { if (!WOApplication.application().isCachingEnabled() && !hasRegistered) { String filePath = ERXFileUtilities.pathForResourceNamed(navigationMenuFileName(), frameworkName, null); log.debug("Registering observer for filePath: {}", filePath); ERXFileNotificationCenter.defaultCenter().addObserver(this, new NSSelector("reloadNavigationMenu", ERXConstant.NotificationClassArray), filePath); } } public ERXNavigationItem newNavigationItem(NSDictionary dict) { String className = (String) dict.objectForKey("navigationItemClassName"); if(className != null) { Class c = ERXPatcher.classForName(className); return (ERXNavigationItem) _NSUtilities.instantiateObject(c, new Class[] {NSDictionary.class}, new Object[]{dict}, true, true); } return new ERXNavigationItem(dict); } protected NSArray createNavigationItemsFromDictionaries(NSArray navItems) { NSMutableArray navigationItems = null; if (navItems != null && navItems.count() > 0) { navigationItems = new NSMutableArray(); for (Enumeration e = navItems.objectEnumerator(); e.hasMoreElements();) { navigationItems.addObject(newNavigationItem((NSDictionary)e.nextElement())); } } return navigationItems != null ? navigationItems : NSArray.EmptyArray; } public void reloadNavigationMenu(NSNotification notification) { log.info("Reloading Navigation Menu"); loadNavigationMenu(); } }