package org.apache.isis.viewer.wicket.ui.panels; import java.util.List; import com.google.common.collect.Lists; import org.apache.wicket.Component; import org.apache.wicket.MarkupContainer; import org.apache.wicket.Page; import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.attributes.AjaxRequestAttributes; import org.apache.wicket.ajax.markup.html.form.AjaxButton; import org.apache.wicket.event.Broadcast; import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxButton; import org.apache.wicket.markup.head.IHeaderResponse; import org.apache.wicket.markup.head.JavaScriptContentHeaderItem; import org.apache.wicket.markup.head.OnDomReadyHeaderItem; import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.model.IModel; import org.apache.wicket.model.ResourceModel; import org.apache.wicket.util.string.AppendingStringBuffer; import org.apache.isis.applib.annotation.PromptStyle; import org.apache.isis.core.metamodel.adapter.ObjectAdapter; import org.apache.isis.viewer.wicket.model.hints.UiHintContainer; import org.apache.isis.viewer.wicket.model.isis.WicketViewerSettings; import org.apache.isis.viewer.wicket.model.models.ActionPrompt; import org.apache.isis.viewer.wicket.model.models.ActionPromptProvider; import org.apache.isis.viewer.wicket.model.models.FormExecutor; import org.apache.isis.viewer.wicket.model.models.FormExecutorContext; import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarModelSubscriber2; import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarPanelAbstract2; import org.apache.isis.viewer.wicket.ui.components.widgets.formcomponent.FormFeedbackPanel; import org.apache.isis.viewer.wicket.ui.errors.JGrowlBehaviour; import org.apache.isis.viewer.wicket.ui.pages.PageAbstract; import org.apache.isis.viewer.wicket.ui.pages.entity.EntityPage; public abstract class PromptFormAbstract<T extends IModel<ObjectAdapter> & FormExecutorContext> extends FormAbstract<ObjectAdapter> implements ScalarModelSubscriber2 { private static final String ID_OK_BUTTON = "okButton"; private static final String ID_CANCEL_BUTTON = "cancelButton"; private static final String ID_FEEDBACK = "feedback"; protected final List<ScalarPanelAbstract2> paramPanels = Lists.newArrayList(); private final Component parentPanel; private final WicketViewerSettings settings; private final T formExecutorContext; private final AjaxButton okButton; private final AjaxButton cancelButton; public PromptFormAbstract( final String id, final Component parentPanel, final WicketViewerSettings settings, final T model) { super(id, model); this.parentPanel = parentPanel; this.settings = settings; this.formExecutorContext = model; setOutputMarkupId(true); // for ajax button addParameters(); FormFeedbackPanel formFeedback = new FormFeedbackPanel(ID_FEEDBACK); addOrReplace(formFeedback); okButton = addOkButton(); cancelButton = addCancelButton(); doConfigureOkButton(okButton); doConfigureCancelButton(cancelButton); } @Override public void renderHead(final IHeaderResponse response) { super.renderHead(response); response.render(OnDomReadyHeaderItem.forScript( String.format("Wicket.Event.publish(Isis.Topic.FOCUS_FIRST_PARAMETER, '%s')", getMarkupId()))); } protected abstract void addParameters(); protected AjaxButton addOkButton() { AjaxButton okButton = settings.isUseIndicatorForFormSubmit() ? new IndicatingAjaxButton(ID_OK_BUTTON, new ResourceModel("okLabel")) { private static final long serialVersionUID = 1L; @Override public void onSubmit(AjaxRequestTarget target, Form<?> form) { onOkSubmittedOf(target, form, this); } @Override protected void updateAjaxAttributes(AjaxRequestAttributes attributes) { if (settings.isPreventDoubleClickForFormSubmit()) { PanelUtil.disableBeforeReenableOnComplete(attributes, this); } } @Override protected void onError(AjaxRequestTarget target, Form<?> form) { target.add(form); } } : new AjaxButton(ID_OK_BUTTON, new ResourceModel("okLabel")) { private static final long serialVersionUID = 1L; @Override public void onSubmit(AjaxRequestTarget target, Form<?> form) { onOkSubmittedOf(target, form, this); } @Override protected void updateAjaxAttributes(AjaxRequestAttributes attributes) { if (settings.isPreventDoubleClickForFormSubmit()) { PanelUtil.disableBeforeReenableOnComplete(attributes, this); } } @Override protected void onError(AjaxRequestTarget target, Form<?> form) { target.add(form); } }; okButton.add(new JGrowlBehaviour()); setDefaultButton(okButton); add(okButton); return okButton; } protected AjaxButton addCancelButton() { final AjaxButton cancelButton = new AjaxButton(ID_CANCEL_BUTTON, new ResourceModel("cancelLabel")) { private static final long serialVersionUID = 1L; @Override public void onSubmit(final AjaxRequestTarget target, Form<?> form) { closePromptIfAny(target); onCancelSubmitted(target); } }; // so can submit with invalid content (eg mandatory params missing) cancelButton.setDefaultFormProcessing(false); if (formExecutorContext.getPromptStyle() == PromptStyle.INLINE) { cancelButton.add(new FireOnEscapeKey() { @Override protected void respond(final AjaxRequestTarget target) { onCancelSubmitted(target); } }); } add(cancelButton); return cancelButton; } protected void closePromptIfAny(final AjaxRequestTarget target) { final ActionPrompt actionPromptIfAny = ActionPromptProvider.Util.getFrom(parentPanel).getActionPrompt(); if (actionPromptIfAny != null) { actionPromptIfAny.closePrompt(target); } } /** * Optional hook */ protected void doConfigureOkButton(final AjaxButton okButton) { } /** * Optional hook */ protected void doConfigureCancelButton(final AjaxButton cancelButton) { } private UiHintContainer getPageUiHintContainerIfAny() { Page page = getPage(); if (page instanceof EntityPage) { EntityPage entityPage = (EntityPage) page; return entityPage.getUiHintContainerIfAny(); } return null; } private void onOkSubmittedOf( final AjaxRequestTarget target, final Form<?> form, final AjaxButton okButton) { setLastFocusHint(); final FormExecutor formExecutor = formExecutorContext.getFormExecutor(); boolean succeeded = formExecutor.executeAndProcessResults(target.getPage(), target, form); if (succeeded) { completePrompt(target); okButton.send(target.getPage(), Broadcast.EXACT, newCompletedEvent(target, form)); target.add(form); } } private void setLastFocusHint() { final UiHintContainer entityModel = getPageUiHintContainerIfAny(); if (entityModel == null) { return; } MarkupContainer parent = this.parentPanel.getParent(); if (parent != null) { entityModel.setHint(getPage(), PageAbstract.UIHINT_FOCUS, parent.getPageRelativePath()); } } protected abstract Object newCompletedEvent( final AjaxRequestTarget target, final Form<?> form); @Override public void onError(AjaxRequestTarget target, ScalarPanelAbstract2 scalarPanel) { if (scalarPanel != null) { // ensure that any feedback error associated with the providing component is shown. target.add(scalarPanel); } } public void onCancelSubmitted(final AjaxRequestTarget target) { setLastFocusHint(); completePrompt(target); } private void completePrompt(final AjaxRequestTarget target) { final PromptStyle promptStyle = formExecutorContext.getPromptStyle(); if (promptStyle == PromptStyle.INLINE && formExecutorContext.getInlinePromptContext() != null) { formExecutorContext.reset(); rebuildGuiAfterInlinePromptDone(target); } else { closePromptIfAny(target); } } private void rebuildGuiAfterInlinePromptDone(final AjaxRequestTarget target) { // replace final String id = parentPanel.getId(); final MarkupContainer parent = parentPanel.getParent(); final WebMarkupContainer replacementPropertyEditFormPanel = new WebMarkupContainer(id); replacementPropertyEditFormPanel.setVisible(false); parent.addOrReplace(replacementPropertyEditFormPanel); // change visibility of inline components formExecutorContext.getInlinePromptContext().onCancel(); // redraw MarkupContainer scalarTypeContainer = formExecutorContext.getInlinePromptContext() .getScalarTypeContainer(); if (scalarTypeContainer != null) { String markupId = scalarTypeContainer.getMarkupId(); target.appendJavaScript( String.format("Wicket.Event.publish(Isis.Topic.FOCUS_FIRST_PROPERTY, '%s')", markupId)); } target.add(parent); } private AjaxButton defaultSubmittingComponent() { return okButton; } // workaround for https://issues.apache.org/jira/browse/WICKET-6364 @Override protected void appendDefaultButtonField() { AppendingStringBuffer buffer = new AppendingStringBuffer(); buffer.append( "<div style=\"width:0px;height:0px;position:absolute;left:-100px;top:-100px;overflow:hidden\">"); buffer.append("<input type=\"text\" tabindex=\"-1\" autocomplete=\"off\"/>"); Component submittingComponent = (Component) this.defaultSubmittingComponent(); buffer.append("<input type=\"submit\" tabindex=\"-1\" name=\""); buffer.append(this.defaultSubmittingComponent().getInputName()); buffer.append("\" onclick=\" var b=document.getElementById(\'"); buffer.append(submittingComponent.getMarkupId()); buffer.append( "\'); if (b!=null&&b.onclick!=null&&typeof(b.onclick) != \'undefined\') { var r = Wicket.bind(b.onclick, b)(); if (r != false) b.click(); } else { b.click(); }; return false;\" "); buffer.append(" />"); buffer.append("</div>"); this.getResponse().write(buffer); } static abstract class FireOnEscapeKey extends AbstractDefaultAjaxBehavior { private static final String PRE_JS = "" + "$(document).ready( function() { \n" + " $(document).bind('keyup', function(evt) { \n" + " if (evt.keyCode == 27) { \n"; private static final String POST_JS = "" + " evt.preventDefault(); \n " + " } \n" + " }); \n" + "});"; @Override public void renderHead(final Component component, final IHeaderResponse response) { super.renderHead(component, response); final String javascript = PRE_JS + getCallbackScript() + POST_JS; response.render( JavaScriptContentHeaderItem.forScript(javascript, null, null)); } @Override protected abstract void respond(final AjaxRequestTarget target); } }