package org.apache.isis.viewer.wicket.ui.components.actions; import java.util.List; import org.apache.wicket.Component; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.markup.html.form.AjaxButton; import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.repeater.RepeatingView; import org.apache.wicket.model.IModel; import org.apache.isis.applib.annotation.SemanticsOf; import org.apache.isis.applib.services.message.MessageService; import org.apache.isis.core.metamodel.adapter.ObjectAdapter; import org.apache.isis.core.metamodel.adapter.version.ConcurrencyException; import org.apache.isis.core.metamodel.services.ServicesInjector; import org.apache.isis.core.metamodel.spec.feature.ObjectAction; import org.apache.isis.viewer.wicket.model.hints.IsisActionCompletedEvent; import org.apache.isis.viewer.wicket.model.isis.WicketViewerSettings; import org.apache.isis.viewer.wicket.model.mementos.ActionParameterMemento; import org.apache.isis.viewer.wicket.model.models.ActionArgumentModel; import org.apache.isis.viewer.wicket.model.models.ActionModel; import org.apache.isis.viewer.wicket.ui.ComponentType; import org.apache.isis.viewer.wicket.ui.components.scalars.PanelWithChoices; import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarPanelAbstract2; import org.apache.isis.viewer.wicket.ui.pages.entity.EntityPage; import org.apache.isis.viewer.wicket.ui.panels.PanelUtil; import org.apache.isis.viewer.wicket.ui.panels.PromptFormAbstract; import de.agilecoders.wicket.extensions.markup.html.bootstrap.confirmation.ConfirmationBehavior; class ActionParametersForm extends PromptFormAbstract<ActionModel> { private static final long serialVersionUID = 1L; public ActionParametersForm( final String id, final Component parentPanel, final WicketViewerSettings settings, final ActionModel actionModel) { super(id, parentPanel, settings, actionModel); } private ActionModel getActionModel() { return (ActionModel) super.getModel(); } @Override protected void addParameters() { final ActionModel actionModel = getActionModel(); final RepeatingView rv = new RepeatingView(ActionParametersFormPanel.ID_ACTION_PARAMETERS); add(rv); paramPanels.clear(); List<ActionParameterMemento> parameterMementos = actionModel.primeArgumentModels(); for (final ActionParameterMemento apm : parameterMementos) { final WebMarkupContainer container = new WebMarkupContainer(rv.newChildId()); rv.add(container); final ActionArgumentModel actionArgumentModel = actionModel.getArgumentModel(apm); actionArgumentModel.setActionArgsHint(actionModel.getArgumentsAsArray()); final ScalarPanelAbstract2 paramPanel = newParamPanel(container, actionArgumentModel); paramPanels.add(paramPanel); } } private ScalarPanelAbstract2 newParamPanel(final WebMarkupContainer container, final IModel<?> model) { final Component component = getComponentFactoryRegistry() .addOrReplaceComponent(container, ComponentType.SCALAR_NAME_AND_VALUE, model); final ScalarPanelAbstract2 paramPanel = component instanceof ScalarPanelAbstract2 ? (ScalarPanelAbstract2) component : null; if (paramPanel != null) { paramPanel.setOutputMarkupId(true); paramPanel.notifyOnChange(this); } return paramPanel; } @Override protected Object newCompletedEvent(final AjaxRequestTarget target, final Form<?> form) { return new IsisActionCompletedEvent(getActionModel(), target, form); } @Override protected void doConfigureOkButton(final AjaxButton okButton) { applyAreYouSure(okButton); } /** * If the {@literal @}Action has "are you sure?" semantics then apply {@link ConfirmationBehavior} * that will ask for confirmation before executing the Ajax request. * * @param button The button which action should be confirmed */ private void applyAreYouSure(AjaxButton button) { ActionModel actionModel = getActionModel(); final ObjectAction action = actionModel.getActionMemento().getAction(getSpecificationLoader()); SemanticsOf semanticsOf = SemanticsOf.from(action.getSemantics()); final ServicesInjector servicesInjector = getPersistenceSession().getServicesInjector(); PanelUtil.addConfirmationDialogIfAreYouSureSemantics(button, semanticsOf, servicesInjector); } @Override public void onUpdate(AjaxRequestTarget target, ScalarPanelAbstract2 scalarPanel) { final ActionModel actionModel = getActionModel(); final ObjectAdapter[] pendingArguments = actionModel.getArgumentsAsArray(); try { final ObjectAction action = actionModel.getActionMemento().getAction(getSpecificationLoader()); final int numParams = action.getParameterCount(); for (int i = 0; i < numParams; i++) { final ScalarPanelAbstract2 paramPanel = paramPanels.get(i); if (paramPanel != null && paramPanel instanceof PanelWithChoices) { final PanelWithChoices panelWithChoices = (PanelWithChoices) paramPanel; // this could throw a ConcurrencyException as we may have to reload the // object adapter of the action in order to compute the choices // (and that object adapter might have changed) if (panelWithChoices.updateChoices(pendingArguments)) { paramPanel.repaint(target); } } } } catch (ConcurrencyException ex) { // second attempt should succeed, because the Oid would have // been updated in the attempt ObjectAdapter targetAdapter = getActionModel().getTargetAdapter(); // forward onto the target page with the concurrency exception final EntityPage entityPage = new EntityPage(targetAdapter, ex); setResponsePage(entityPage); final MessageService messageService = getServicesInjector().lookupService(MessageService.class); messageService.warnUser(ex.getMessage()); return; } // previously this method was also doing: // target.add(this); // ie to update the entire form (in addition to the updates to the individual impacted parameter fields // done in the loop above). However, that logic is wrong, because any values entered in the browser // get trampled over (ISIS-629). } }