package org.sigmah.client.page; /* * #%L * Sigmah * %% * Copyright (C) 2010 - 2016 URD * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-3.0.html>. * #L% */ import java.util.HashMap; import java.util.Map; import org.sigmah.client.event.EventBus; import org.sigmah.client.page.event.PageChangedEvent; import org.sigmah.client.page.event.PageRequestEvent; import org.sigmah.client.page.handler.PageChangedHandler; import org.sigmah.client.page.handler.PageRequestHandler; import org.sigmah.client.ui.presenter.base.Presenter.PagePresenter; import org.sigmah.client.ui.view.base.AbstractPopupView; import org.sigmah.client.ui.zone.Zone; import org.sigmah.shared.util.Pair; import com.allen_sauer.gwt.log.client.Log; import com.google.gwt.event.logical.shared.ValueChangeEvent; import com.google.gwt.event.logical.shared.ValueChangeHandler; import com.google.gwt.user.client.History; import com.google.inject.Inject; /** * Page manager handling History and pages loading. * * @author Denis Colliot (dcolliot@ideia.fr) * @author Tom Miette (tmiette@ideia.fr) */ public class PageManager implements ValueChangeHandler<String>, PageChangedHandler, PageRequestHandler { /** * Enable or disable Google Analytics tracking. */ public static boolean trackingEnabled = false; /** * Application event bus. */ private final EventBus eventBus; /** * Record of all the application presenter's pages. */ private final Map<String, Pair<Page, Boolean>> pages; /** * Record of all the application {@link Page}s with their corresponding {@link PagePresenter} instance. */ private final Map<Page, PagePresenter<?>> presenters; /** * The current page request. */ private PageRequest currentPageRequest; /** * The current popup page request. */ private PageRequest currentPopupPageRequest; /** * Page manager constructor. * * @param eventBus * application event bus. */ @Inject public PageManager(final EventBus eventBus) { this.eventBus = eventBus; pages = new HashMap<String, Pair<Page, Boolean>>(); presenters = new HashMap<Page, PagePresenter<?>>(); // Register ourselves with the History API. History.addValueChangeHandler(this); // Listen for manual place change events. eventBus.addHandler(PageChangedEvent.getType(), this); } /** * Registers a {@link PagePresenter} instance. * * @param pagePresenter * The {@link PagePresenter} instance associated to a {@link Page} token. * @param popupView * {@code true} if the given {@code page} view is displayed as a popup view. */ public void registerPage(final PagePresenter<?> pagePresenter, final boolean popupView) { if (pagePresenter == null) { throw new IllegalArgumentException("Invalid page presenter instance."); } final Page page = pagePresenter.getPage(); if (page != null) { presenters.put(page, pagePresenter); pages.put(page.getToken(), new Pair<Page, Boolean>(page, popupView)); } } /** * Returns the {@link Page} instance associated to the given page id. * * @param pageId * page id value * @return the {@link Page} instance associated to the given page id, or {@code null} if no page instance exists for * the given id. */ public Page getPage(final String pageId) { return pages.get(pageId) != null ? pages.get(pageId).left : null; } /** * Returns if the given {@link Page} instance is associated to a popup view. * If the page is {@code null} or is not registered into page manager, the method returns {@code false}. * * @param page * The page instance. * @return {@code true} if the given {@link Page} instance is associated to a popup view, {@code false} otherwise. */ public boolean isPopupView(final Page page) { if (page == null || pages.get(page.getToken()) == null) { return false; } return pages.get(page.getToken()).right; } /** * {@inheritDoc} */ @Override public void onValueChange(final ValueChangeEvent<String> event) { try { final PageRequest pageRequest = PageRequest.fromString(event.getValue(), pages); // A popup page cannot be accessed directly by URL modification (such as <a> elements). // Except the 'release' popup. if (isPopupView(pageRequest.getPage())) { if (Log.isInfoEnabled()) { Log.info("Popup page '" + pageRequest + "' cannot be accessed directly by URL."); } eventBus.navigate(null); } else { eventBus.fireEvent(new PageRequestEvent(pageRequest, true)); } } catch (final PageParsingException e) { eventBus.navigate(null); } } /** * {@inheritDoc} */ @Override public void onPageChange(final PageChangedEvent event) { final PageRequest pageRequest = event.getRequest(); final Page page = pageRequest.getPage(); // Tracks current page. trackPage(page); if (page != null && isPopupView(page)) { currentPopupPageRequest = pageRequest; } else { currentPageRequest = pageRequest; } newPlace(pageRequest); updateZones(pageRequest); } /** * Updates zones. * Does nothing if given {@code pageRequest} references a pop-up view. * * @param pageRequest * The page request (containing access rights). */ private void updateZones(final PageRequest pageRequest) { final Page page = pageRequest.getPage(); if (page != null && isPopupView(page)) { return; } eventBus.updateZone(Zone.ORG_BANNER); eventBus.updateZone(Zone.AUTH_BANNER); eventBus.updateZone(Zone.OFFLINE_BANNER); eventBus.updateZoneRequest(Zone.MENU_BANNER.requestWith(RequestParameter.REQUEST, pageRequest)); } /** * Adds a new browser history entry only if the requested page exists among the {@code pages} map attribute. * Does nothing if given {@code pageRequest} references a pop-up view or a <em>skip history</em> page. * * @param request * Page request. */ private void newPlace(final PageRequest request) { final Page page = request.getPage(); if (page != null && (page.skipHistory() || isPopupView(page))) { return; // "Pop-up views" and "Skip history pages" don't generate a new History item. } History.newItem(request.toString(), false); } /** * {@inheritDoc} */ @Override public void onPageRequest(final PageRequestEvent event) { if (!event.isFromHistory()) { currentPageRequest = event.getRequest(); newPlace(event.getRequest()); } } public void fireCurrentPlace() { if (History.getToken() != null) { History.fireCurrentHistoryState(); } } /** * Returns the current page (even if the current page is a popup). * * @return the current page or {@code null}. */ public Page getCurrentPage() { return getCurrentPage(true); } /** * Returns the current page. * * @param includeCurrentPopup * Set to <code>true</code> to get the current popup's page if any popup is currently displayed, set to * <code>false</code> to get the current non-popup page even if a popup is currently displayed. * @return the current page or {@code null}. */ public Page getCurrentPage(boolean includeCurrentPopup) { final PageRequest pageRequest = getCurrentPageRequest(includeCurrentPopup); return pageRequest != null ? pageRequest.getPage() : null; } /** * Returns the current page token (even if the current page is a popup). * * @return the current page <b>token</b> or {@code null}. */ public String getCurrentPageToken() { return getCurrentPageToken(true); } /** * Returns the current page <b>token</b>. * * @param includeCurrentPopup * Set to <code>true</code> to get the current popup's page token if any popup is currently displayed, set to * <code>false</code> to get the current non-popup page token even if a popup is currently displayed. * @return the current page token or {@code null}. */ public String getCurrentPageToken(boolean includeCurrentPopup) { final Page currentPage = getCurrentPage(includeCurrentPopup); return currentPage != null ? currentPage.getToken() : null; } /** * Returns the current page request (even if the current page is a popup). * * @return the current page request or {@code null}. */ public PageRequest getCurrentPageRequest() { return getCurrentPageRequest(true); } /** * Returns the current page request. * * @param includeCurrentPopup * Set to <code>true</code> to get the current popup's page request if any popup is currently displayed, set * to <code>false</code> to get the current non-popup page request even if a popup is currently displayed. * @return the current page request or {@code null}. */ public PageRequest getCurrentPageRequest(boolean includeCurrentPopup) { if (includeCurrentPopup && AbstractPopupView.isPopupDisplayed()) { return new PageRequest(currentPopupPageRequest); } else { return new PageRequest(currentPageRequest); } } /** * Returns if the current page is a popup view. * * @return {@code true} if the current page is a popup view. */ public boolean isCurrentPagePopup() { return isPopupView(getCurrentPage()); } /** * Returns the current {@link PagePresenter} (even if the current page is a popup). * * @return the current {@link PagePresenter} instance or {@code null}. */ public PagePresenter<?> getCurrentPresenter() { return getCurrentPresenter(true); } /** * Returns the current {@link PagePresenter} instance. * * @param includeCurrentPopup * Set to <code>true</code> to get the current popup's {@link PagePresenter} if any popup is currently * displayed, set to <code>false</code> to get the current non-popup {@link PagePresenter} even if a popup is * currently displayed. * @return the current {@link PagePresenter} instance or {@code null}. */ public PagePresenter<?> getCurrentPresenter(boolean includeCurrentPopup) { final Page currentPage = getCurrentPage(includeCurrentPopup); return currentPage != null ? presenters.get(currentPage) : null; } /** * Returns the given {@code url} corresponding {@code PageRequest}. * * @param url * The URL string value to parse. * @return the given {@code url} corresponding {@code PageRequest}. * @throws IllegalArgumentException * If the given {@code url} is invalid. */ public PageRequest getPageRequest(final String url) { try { return PageRequest.fromString(url, pages); } catch (final Exception e) { throw new IllegalArgumentException("URL '" + url + "' is invalid.", e); } } /** * Tracks given page in Google Analytics. * * @param page * The tracked page. */ public static void trackPage(final Page page) { if (trackingEnabled && page != null) { trackPage(page.getToken()); } } /** * Tracks given page in Google Analytics. * * @param pageName * The tracked page name. */ private static native void trackPage(final String pageName) /*-{ try { $wnd._gaq.push([ '_setAccount', 'UA-000000000-1' ]); $wnd._gaq.push([ '_trackPageview', pageName ]); $wnd._gaq.push([ '_trackPageLoadTime' ]); } catch (err) { // Custom exception handling. } }-*/; }