/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.isis.viewer.wicket.ui.components.actions; import java.util.concurrent.Callable; import org.apache.wicket.Page; import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.model.Model; import org.apache.wicket.request.cycle.RequestCycle; import org.apache.isis.applib.services.message.MessageService; import org.apache.isis.core.metamodel.adapter.ObjectAdapter; import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager; import org.apache.isis.core.metamodel.adapter.version.ConcurrencyException; import org.apache.isis.viewer.wicket.model.models.ActionModel; import org.apache.isis.viewer.wicket.model.models.ActionPrompt; import org.apache.isis.viewer.wicket.ui.ComponentType; import org.apache.isis.viewer.wicket.ui.actionresponse.ActionResultResponse; import org.apache.isis.viewer.wicket.ui.actionresponse.ActionResultResponseType; import org.apache.isis.viewer.wicket.ui.components.property.PropertyEditPanel; import org.apache.isis.viewer.wicket.ui.pages.entity.EntityPage; import org.apache.isis.viewer.wicket.ui.panels.PanelAbstract; /** * {@link PanelAbstract Panel} representing an action invocation, backed by an * {@link ActionModel}. * * <p> * Based on the {@link ActionModel.Mode mode}, will render either parameter * dialog or the results. * * <p> * Corresponding component to edit properties is {@link PropertyEditPanel}. */ public class ActionParametersPanel extends PanelAbstract<ActionModel> { private static final long serialVersionUID = 1L; private static final String ID_HEADER = "header"; static final String ID_ACTION_NAME = "actionName"; /** * Gives a chance to hide the header part of this action panel, e.g. when shown in an action prompt */ private boolean showHeader = true; public ActionParametersPanel(final String id, final ActionModel actionModel) { super(id, actionModel); actionModel.setFormExecutor(new ActionParametersFormExecutor(actionModel)); //buildGui(getActionModel()); } /** * Sets the owning action prompt (modal window), if any. * * REVIEW: I wonder if this is necessary... there isn't anything exactly the same for property edits... */ public void setActionPrompt(ActionPrompt actionPrompt) { ActionParametersFormExecutor formExecutor = (ActionParametersFormExecutor) getActionModel().getFormExecutor(); formExecutor.setActionPrompt(actionPrompt); } @Override protected void onInitialize() { super.onInitialize(); buildGui(getModel()); } @Override protected void onConfigure() { super.onConfigure(); } private void buildGui(final ActionModel actionModel) { if (actionModel.hasParameters()) { buildGuiForParameters(getActionModel()); } else { buildGuiForNoParameters(actionModel); } } ActionModel getActionModel() { return super.getModel(); } public ActionParametersPanel setShowHeader(boolean showHeader) { this.showHeader = showHeader; return this; } private void buildGuiForParameters(final ActionModel actionModel) { WebMarkupContainer header = addHeader(); ObjectAdapter targetAdapter = null; try { targetAdapter = actionModel.getTargetAdapter(); getComponentFactoryRegistry().addOrReplaceComponent(this, ComponentType.PARAMETERS, getActionModel()); getComponentFactoryRegistry().addOrReplaceComponent(header, ComponentType.ENTITY_ICON_AND_TITLE, actionModel.getParentEntityModel()); final String actionName = getActionModel().getActionMemento().getAction(actionModel.getSpecificationLoader()).getName(); header.add(new Label(ID_ACTION_NAME, Model.of(actionName))); } catch (final ConcurrencyException ex) { // second attempt should succeed, because the Oid would have // been updated in the attempt if (targetAdapter == null) { targetAdapter = getModel().getTargetAdapter(); } // forward onto the target page with the concurrency exception ActionResultResponse resultResponse = ActionResultResponseType.OBJECT.interpretResult(this.getActionModel(), targetAdapter, ex); resultResponse.getHandlingStrategy().handleResults(resultResponse, getIsisSessionFactory()); final MessageService messageService = getServicesInjector().lookupService(MessageService.class); messageService.warnUser(ex.getMessage()); } } private WebMarkupContainer addHeader() { WebMarkupContainer header = new WebMarkupContainer(ID_HEADER) { @Override protected void onConfigure() { super.onConfigure(); setVisible(showHeader); } }; addOrReplace(header); return header; } private void buildGuiForNoParameters(final ActionModel formExecutor) { final Page page = this.getPage(); boolean succeeded = formExecutor.getFormExecutor().executeAndProcessResults(page, null, null); if(succeeded) { // nothing to do } else { // render the target entity again // // (One way this can occur is if an event subscriber has a defect and throws an exception; in which case // the EventBus' exception handler will automatically veto. This results in a growl message rather than // an error page, but is probably 'good enough'). final ObjectAdapter targetAdapter = formExecutor.getTargetAdapter(); final EntityPage entityPage = // disabling concurrency checking after the layout XML (grid) feature // was throwing an exception when rebuild grid after invoking action // not certain why that would be the case, but think it should be // safe to simply disable while recreating the page to re-render back to user. AdapterManager.ConcurrencyChecking.executeWithConcurrencyCheckingDisabled( new Callable<EntityPage>() { @Override public EntityPage call() throws Exception { return new EntityPage(targetAdapter, null); } } ); getIsisSessionFactory().getCurrentSession().getPersistenceSession().getTransactionManager().flushTransaction(); // "redirect-after-post" final RequestCycle requestCycle = RequestCycle.get(); requestCycle.setResponsePage(entityPage); } } private static ActionResultResponse toEntityPage(final ActionModel model, final ObjectAdapter actualAdapter, final ConcurrencyException exIfAny) { // this will not preserve the URL (because pageParameters are not copied over) // but trying to preserve them seems to cause the 302 redirect to be swallowed somehow final EntityPage entityPage = // disabling concurrency checking after the layout XML (grid) feature // was throwing an exception when rebuild grid after invoking action // not certain why that would be the case, but think it should be // safe to simply disable while recreating the page to re-render back to user. AdapterManager.ConcurrencyChecking.executeWithConcurrencyCheckingDisabled( new Callable<EntityPage>() { @Override public EntityPage call() throws Exception { return new EntityPage(actualAdapter, exIfAny); } } ); return ActionResultResponse.toPage(entityPage); } }