package org.sigmah.client.ui.presenter.base;
/*
* #%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 org.sigmah.client.Sigmah;
import org.sigmah.client.event.EventBus;
import org.sigmah.client.i18n.I18N;
import org.sigmah.client.inject.Injector;
import org.sigmah.client.page.Page;
import org.sigmah.client.page.PageManager;
import org.sigmah.client.page.PageRequest;
import org.sigmah.client.page.event.PageChangedEvent;
import org.sigmah.client.page.event.PageRequestEvent;
import org.sigmah.client.page.handler.PageRequestHandler;
import org.sigmah.client.ui.notif.ConfirmCallback;
import org.sigmah.client.ui.notif.N10N;
import org.sigmah.client.ui.presenter.base.HasSubPresenter.SubPresenter;
import org.sigmah.client.ui.presenter.base.Presenter.PagePresenter;
import org.sigmah.client.ui.view.base.AbstractView;
import org.sigmah.client.ui.view.base.ViewInterface;
import org.sigmah.client.ui.view.base.ViewPopupInterface;
import org.sigmah.client.ui.widget.form.FormPanel;
import org.sigmah.client.util.ClientUtils;
import org.sigmah.client.util.MessageType;
import com.allen_sauer.gwt.log.client.Log;
import com.google.inject.Inject;
import org.sigmah.client.util.profiler.Profiler;
import org.sigmah.client.util.profiler.Scenario;
/**
* <p>
* Abstract <b>page</b> presenter associated to a page token.
* </p>
* <p>
* <b>Rules to add a new <u>page</u> presenter:</b>
* <ol>
* <li>Define a new page token in {@link Page} enum.
* <li>Create a new class inheriting {@link AbstractPagePresenter} with {@link com.google.inject.Singleton} annotation
* (<u>crucial</u>).<br>
* The presenter's {@link PagePresenter#getPage()} method should return new page token.</li>
* <li>Define an inner <em>static</em> interface representing the presenter's view. This interface must have the
* {@link com.google.inject.ImplementedBy} annotation referencing the view implementation (<u>crucial</u>).
* See {@link AbstractView} javadoc to initialize the view implementation.</li>
* <li>Add an accessor to the presenter into client-side {@link Injector} and call it into {@link Sigmah#onModuleLoad()}
* entry point in order to register presenter.</li>
* </ol>
* </p>
* <p>
* <b>Utility methods provided to presenter implementations:</b>
* <ul>
* <li>{@link #auth()} to access the current authentication.</li>
* <li>{@link #isAnonymous()} to check if no user is currently authenticated.</li>
* </ul>
* </p>
*
* @author Denis Colliot (dcolliot@ideia.fr)
* @author Tom Miette (tmiette@ideia.fr)
* @param <V>
* View interface extending the {@link ViewInterface} interface
*/
public abstract class AbstractPagePresenter<V extends ViewInterface> extends AbstractPresenter<V> implements PagePresenter<V> {
/**
* Executes {@link #bind()} method and registers page with {@link PageManager}.
*
* @param view
* Page presenter's view interface.
* @param injector
* Injected application client-side injector.
*/
@Inject
protected AbstractPagePresenter(final V view, final Injector injector) {
super(view, injector); // Executes 'bind()' method.
// Registers page object.
injector.getPageManager().registerPage(this, isPopupView());
}
/**
* {@inheritDoc}
*/
@Override
public final void bind() {
final Page page = getPage();
if (page == null) {
return;
}
registerHandler(eventBus.addHandler(PageRequestEvent.getType(), new PageRequestHandler() {
@Override
public void onPageRequest(final PageRequestEvent event) {
if (!event.concern(page)) {
return;
}
loadPagePresenter(event, page);
}
}));
}
/**
* <p>
* Loads the current presenter by executing the following steps :
* <ul>
* <li>Initializes the view components by calling {@link #initialize()} method (only for the first access).</li>
* <li>Sends an {@link PageChangedEvent} to indicate that the page has changed.</li>
* <li>Executes the current presenter's method {@link #onPageRequest}.</li>
* <li>Executes the current presenter's method {@link #revealView}.</li>
* </ul>
* </p>
*
* @param event
* Page request event.
* @param page
* New page loaded.
*/
private final void loadPagePresenter(final PageRequestEvent event, final Page page) {
// Initializes presenter's view components.
initialize();
// Necessary when we've fired PRE (PageRequestEvent) from code vs Hyperlink.
eventBus.fireEvent(new PageChangedEvent(event.getRequest()));
// Resets page message.
displayPageMessage(null, null);
executeOnPageRequest(event, page);
}
/**
* <p>
* Can be overridden by child implementation.
* </p>
* <p>
* <b>Important:</b> don't forget to execute {@code super.afterOnPageRequest(PageRequestEvent, Page)} if the method is
* overridden.
* </p>
*
* @param event
* Page request event.
* @param page
* New page loaded.
*/
protected void executeOnPageRequest(final PageRequestEvent event, final Page page) {
afterOnPageRequest(event, page);
}
/**
* Must be called after {@link #executeOnPageRequest(PageRequestEvent, Page)}.
*
* @param event
* Page request event.
* @param page
* New page loaded.
*/
protected final void afterOnPageRequest(final PageRequestEvent event, final Page page) {
Profiler.INSTANCE.markCheckpoint(Scenario.OPEN_PROJECT, " afterOnPageRequest start ");
if (Log.isTraceEnabled()) {
Log.trace("Executing '" + page + "' onPageRequest() method.");
}
final PageRequest pageRequest = event.getRequest();
if (isSubPresenter()) {
((SubPresenter<?>) this).getParentPresenter().onSubPresenterRequest(pageRequest);
}
// Processing presenter 'onPageRequest()' implementation.
onPageRequest(pageRequest);
Profiler.INSTANCE.markCheckpoint(Scenario.OPEN_PROJECT, " onPageRequest end ");
revealView();
if (!isPopupView()) {
// Update application message and control.
displayApplicationMessage(page);
}
}
/**
* Gets the current presenter page title.
*
* @return the current presenter page title.
*/
protected final String getPageTitle() {
return Page.getTitle(getPage());
}
/**
* Sets the current presenter page title.<br/>
* If the presenter's view is a {@link ViewPopupInterface} implementation, the popup title is dynamically updated.
*
* @param title
* The new page title.
*/
protected final void setPageTitle(String title) {
if (getPage() == null) {
return;
}
getPage().setTitle(title);
if (isPopupView()) {
// Popup presenter's view case.
((ViewPopupInterface) view).setPopupTitle(title);
} else {
// Classic page.
injector.getApplicationPresenter().setPageTitle(title);
}
}
/**
* Checks if a message needs to be displayed in the application header. If it does, the message is sent to the
* application presenter's view.
*
* Also check if the {@code page} is still in progress from retrieved server properties. If this is the case, a
* warning message is displayed.
*
* @param page
* The accessed page.
*/
private void displayApplicationMessage(final Page page) {
// TODO retrieve application message from server-side (DB most-likely).
// eventBus.updateZone(Zone.MESSAGE_BANNER, "Welcome in Sigmah !", MessageType.WARNING);
}
/**
* Displays the given message at the top of the current page. If the message is {@code null} or {@code empty}, the
* message will be hidden.
*
* <strong>The page message must be initialized in the {@link #onPageRequest} method.</strong>
*
* @param message
* The message as HTML.
*/
protected final void displayPageMessage(final String message, MessageType type) {
if (isPopupView()) {
((ViewPopupInterface) view).setPageMessage(message, type);
} else {
injector.getApplicationPresenter().setPageMessage(message, type);
}
}
/**
* Hides the presenter's popup view.
* If the presenter's view is not a {@link ViewPopupInterface} implementation, the method does nothing.
*/
protected final void hideView() {
if (isPopupView()) {
((ViewPopupInterface) view).hide();
}
}
/**
* Returns if the current presenter's {@code view} is an instance of a popup view ({@link ViewPopupInterface}
* implementation).
*
* @return {@code true} if the current presenter's {@code view} is an instance of a popup view, {@code false}
* otherwise.
*/
protected final boolean isPopupView() {
return view instanceof ViewPopupInterface;
}
/**
* <p>
* {@inheritDoc}
* </p>
* <p>
* If a value has changed, user is invited to confirm its leaving process.
* </p>
* <p>
* Value change detection relies on following mechanisms:
* <ul>
* <li>{@link #hasValueChanged()} method result.</li>
* <li>And/Or {@link HasForm} presenters: provided form(s) are scanned to detect value change.</li>
* </ul>
* </p>
*/
@Override
public void beforeLeaving(final EventBus.LeavingCallback callback) {
if (!(this instanceof HasForm) && !hasValueChanged()) {
super.beforeLeaving(callback);
return;
}
boolean valueHasChanged = hasValueChanged();
if (!valueHasChanged && this instanceof HasForm) {
final FormPanel[] forms = ((HasForm) this).getForms();
if (ClientUtils.isEmpty(forms)) {
super.beforeLeaving(callback);
return;
}
for (final FormPanel form : forms) {
if (form != null && (valueHasChanged |= form.isValueHasChanged())) {
break;
}
}
}
if (valueHasChanged) {
N10N.confirmation(I18N.CONSTANTS.unsavedDataMessage(), new ConfirmCallback() {
@Override
public void onAction() {
// YES action.
onLeavingOk();
callback.leavingOk();
}
}, new ConfirmCallback() {
@Override
public void onAction() {
// NO action.
onLeavingKo();
callback.leavingKo();
}
});
} else {
onLeavingOk();
callback.leavingOk();
}
}
/**
* Callback executed when the user agrees to leave the current page presenter.<br>
* <em>Default implementation does nothing.</em>
*/
protected void onLeavingOk() {
// Default implementation does nothing.
}
/**
* Callback executed when the user refuses to leave the current page presenter.<br>
* <em>Default implementation does nothing.</em>
*/
protected void onLeavingKo() {
// Default implementation does nothing.
}
/**
* Returns if a value of the current page has changed.<br>
* <em>Default implementation returns {@code false}.</em>
*
* @return {@code true} if a value of the current page has changed, {@code false} otherwise.
*/
protected boolean hasValueChanged() {
return false;
}
}