You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2015/10/06 23:29:34 UTC

[1/4] isis git commit: ISIS-1007 Provide support for "are you sure" idiom, eg using a @AreYouSure annotation on the action

Repository: isis
Updated Branches:
  refs/heads/master 1a5192989 -> dbcd09655


ISIS-1007 Provide support for "are you sure" idiom, eg using a @AreYouSure annotation on the action

Add SemanticsOf#[NON_]IDEMPOTENT_ARE_YOU_SURE constants.
When used the action prompt for the action will show "Are you sure?" dialog after pressing the OK button


Project: http://git-wip-us.apache.org/repos/asf/isis/repo
Commit: http://git-wip-us.apache.org/repos/asf/isis/commit/f5f0fc63
Tree: http://git-wip-us.apache.org/repos/asf/isis/tree/f5f0fc63
Diff: http://git-wip-us.apache.org/repos/asf/isis/diff/f5f0fc63

Branch: refs/heads/master
Commit: f5f0fc6312c0db222231221a3cf85630e6b40d15
Parents: a930c1d
Author: Martin Tzvetanov Grigorov <mg...@apache.org>
Authored: Fri Aug 7 11:35:50 2015 +0300
Committer: Martin Tzvetanov Grigorov <mg...@apache.org>
Committed: Fri Aug 7 11:43:15 2015 +0300

----------------------------------------------------------------------
 .../isis/applib/annotation/ActionSemantics.java | 14 ++++++++--
 .../isis/applib/annotation/SemanticsOf.java     | 28 ++++++++++++++++++--
 core/pom.xml                                    |  8 +++---
 .../actions/ActionParametersFormPanel.java      | 21 ++++++++++++++-
 .../viewer/wicket/ui/pages/PageAbstract.java    |  4 +--
 5 files changed, 64 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/isis/blob/f5f0fc63/core/applib/src/main/java/org/apache/isis/applib/annotation/ActionSemantics.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/annotation/ActionSemantics.java b/core/applib/src/main/java/org/apache/isis/applib/annotation/ActionSemantics.java
index d5e21d7..0801922 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/annotation/ActionSemantics.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/annotation/ActionSemantics.java
@@ -55,10 +55,20 @@ public @interface ActionSemantics {
         @Deprecated
         IDEMPOTENT,
         /**
-         * @deprecated - see {@link SemanticsOf#IDEMPOTENT}
+         * @deprecated - see {@link SemanticsOf#NON_IDEMPOTENT}
+         */
+        @Deprecated
+        NON_IDEMPOTENT,
+        /**
+         * @deprecated - see {@link SemanticsOf#IDEMPOTENT_ARE_YOU_SURE}
+         */
+        @Deprecated
+        IDEMPOTENT_ARE_YOU_SURE,
+        /**
+         * @deprecated - see {@link SemanticsOf#NON_IDEMPOTENT_ARE_YOU_SURE}
          */
         @Deprecated
-        NON_IDEMPOTENT;
+        NON_IDEMPOTENT_ARE_YOU_SURE;
 
         /**
          * @deprecated - see {@link SemanticsOf#getFriendlyName()}

http://git-wip-us.apache.org/repos/asf/isis/blob/f5f0fc63/core/applib/src/main/java/org/apache/isis/applib/annotation/SemanticsOf.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/annotation/SemanticsOf.java b/core/applib/src/main/java/org/apache/isis/applib/annotation/SemanticsOf.java
index 5cbe820..7a4a570 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/annotation/SemanticsOf.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/annotation/SemanticsOf.java
@@ -46,7 +46,27 @@ public enum SemanticsOf {
      * <p>
      * An example is increasing the quantity of a line item in an Order by 1.
      */
-    NON_IDEMPOTENT;
+    NON_IDEMPOTENT,
+    /**
+     * Post-conditions are always the same, irrespective as to how many times called.
+     *
+     * <p>
+     * If supported the UI viewer will show a confirmation dialog before executing the action.
+     *
+     * <p>
+     * An example might be <tt>placeOrder()</tt>, that is a no-op if the order has already been placed.
+     */
+    IDEMPOTENT_ARE_YOU_SURE,
+    /**
+     * Neither safe nor idempotent; every invocation is likely to change the state of the object.
+     *
+     * <p>
+     * If supported the UI viewer will show a confirmation dialog before executing the action.
+     *
+     * <p>
+     * An example is increasing the quantity of a line item in an Order by 1.
+     */
+    NON_IDEMPOTENT_ARE_YOU_SURE;
 
     public String getFriendlyName() {
         return Enums.getFriendlyNameOf(this);
@@ -60,7 +80,7 @@ public enum SemanticsOf {
      * Any of {@link #SAFE}, {@link #SAFE_AND_REQUEST_CACHEABLE} or (obviously) {@link #IDEMPOTENT}.
      */
     public boolean isIdempotentInNature() {
-        return isSafeInNature() || this == IDEMPOTENT;
+        return isSafeInNature() || this == IDEMPOTENT || this == IDEMPOTENT_ARE_YOU_SURE;
     }
 
     /**
@@ -88,7 +108,9 @@ public enum SemanticsOf {
         if(semantics == SAFE_AND_REQUEST_CACHEABLE) return ActionSemantics.Of.SAFE_AND_REQUEST_CACHEABLE;
         if(semantics == SAFE) return ActionSemantics.Of.SAFE;
         if(semantics == IDEMPOTENT) return ActionSemantics.Of.IDEMPOTENT;
+        if(semantics == IDEMPOTENT_ARE_YOU_SURE) return ActionSemantics.Of.IDEMPOTENT_ARE_YOU_SURE;
         if(semantics == NON_IDEMPOTENT) return ActionSemantics.Of.NON_IDEMPOTENT;
+        if(semantics == NON_IDEMPOTENT_ARE_YOU_SURE) return ActionSemantics.Of.NON_IDEMPOTENT_ARE_YOU_SURE;
         // shouldn't happen
         throw new IllegalArgumentException("Unrecognized of: " + semantics);
     }
@@ -99,7 +121,9 @@ public enum SemanticsOf {
         if(semantics == ActionSemantics.Of.SAFE_AND_REQUEST_CACHEABLE) return SAFE_AND_REQUEST_CACHEABLE;
         if(semantics == ActionSemantics.Of.SAFE) return SAFE;
         if(semantics == ActionSemantics.Of.IDEMPOTENT) return IDEMPOTENT;
+        if(semantics == ActionSemantics.Of.IDEMPOTENT_ARE_YOU_SURE) return IDEMPOTENT_ARE_YOU_SURE;
         if(semantics == ActionSemantics.Of.NON_IDEMPOTENT) return NON_IDEMPOTENT;
+        if(semantics == ActionSemantics.Of.NON_IDEMPOTENT_ARE_YOU_SURE) return NON_IDEMPOTENT_ARE_YOU_SURE;
         // shouldn't happen
         throw new IllegalArgumentException("Unrecognized semantics: " + semantics);
     }

http://git-wip-us.apache.org/repos/asf/isis/blob/f5f0fc63/core/pom.xml
----------------------------------------------------------------------
diff --git a/core/pom.xml b/core/pom.xml
index 729b8b7..dfcabcd 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -131,11 +131,11 @@
 
         <jetty.version>9.2.11.v20150529</jetty.version>
 
-        <wicket.version>6.17.0</wicket.version>
-        <wicketstuff.version>6.17.0</wicketstuff.version>
+        <wicket.version>6.20.0</wicket.version>
+        <wicketstuff.version>6.20.0</wicketstuff.version>
 
-        <wicket-webjars.version>0.4.3</wicket-webjars.version>
-        <wicket-bootstrap.version>0.9.7</wicket-bootstrap.version>
+        <wicket-webjars.version>0.4.6</wicket-webjars.version>
+        <wicket-bootstrap.version>0.9.12-SNAPSHOT</wicket-bootstrap.version>
         <wicket-source.version>6.0.0.8</wicket-source.version>
 
         <wicket-select2.version>2.2.3</wicket-select2.version>

http://git-wip-us.apache.org/repos/asf/isis/blob/f5f0fc63/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersFormPanel.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersFormPanel.java b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersFormPanel.java
index 8416491..626c5f8 100644
--- a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersFormPanel.java
+++ b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersFormPanel.java
@@ -19,6 +19,8 @@
 
 package org.apache.isis.viewer.wicket.ui.components.actions;
 
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.confirmation.ConfirmationBehavior;
+
 import java.util.List;
 import com.google.common.collect.Lists;
 import org.apache.wicket.Component;
@@ -29,6 +31,7 @@ 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.ResourceModel;
+import org.apache.isis.applib.annotation.ActionSemantics;
 import org.apache.isis.core.commons.ensure.Ensure;
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
 import org.apache.isis.core.metamodel.adapter.version.ConcurrencyException;
@@ -182,7 +185,8 @@ public class ActionParametersFormPanel extends PanelAbstract<ActionModel> {
             okButton.add(new JGrowlBehaviour());
             setDefaultButton(okButton);
             add(okButton);
-            
+            applyAreYouSure(okButton);
+
             AjaxButton cancelButton = new AjaxButton(ID_CANCEL_BUTTON, new ResourceModel("cancelLabel")) {
                 private static final long serialVersionUID = 1L;
 
@@ -204,6 +208,21 @@ public class ActionParametersFormPanel extends PanelAbstract<ActionModel> {
             }
         }
 
+        /**
+         * 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();
+            ActionSemantics.Of semantics = action.getSemantics();
+            if (semantics == ActionSemantics.Of.IDEMPOTENT_ARE_YOU_SURE || semantics == ActionSemantics.Of.NON_IDEMPOTENT_ARE_YOU_SURE) {
+                button.add(new ConfirmationBehavior());
+            }
+        }
+
         @Override
         public void onUpdate(AjaxRequestTarget target, ScalarModelProvider provider) {
 

http://git-wip-us.apache.org/repos/asf/isis/blob/f5f0fc63/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/PageAbstract.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/PageAbstract.java b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/PageAbstract.java
index daf886e..60e273d 100644
--- a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/PageAbstract.java
+++ b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/PageAbstract.java
@@ -20,7 +20,7 @@
 package org.apache.isis.viewer.wicket.ui.pages;
 
 import de.agilecoders.wicket.core.Bootstrap;
-import de.agilecoders.wicket.core.markup.html.references.BootlintJavaScriptReference;
+import de.agilecoders.wicket.core.markup.html.references.BootlintHeaderItem;
 import de.agilecoders.wicket.core.settings.IBootstrapSettings;
 import de.agilecoders.wicket.core.settings.ITheme;
 import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesomeCssReference;
@@ -271,7 +271,7 @@ public abstract class PageAbstract extends WebPage implements ActionPromptProvid
     }
 
     private void addBootLint(final IHeaderResponse response) {
-        response.render(JavaScriptHeaderItem.forReference(BootlintJavaScriptReference.INSTANCE));
+        response.render(BootlintHeaderItem.INSTANCE);
     }
 
     private boolean isModernBrowser() {


[3/4] isis git commit: Merge remote-tracking branch 'origin/ISIS-1007-are-you-sure-semantics'

Posted by da...@apache.org.
Merge remote-tracking branch 'origin/ISIS-1007-are-you-sure-semantics'


Project: http://git-wip-us.apache.org/repos/asf/isis/repo
Commit: http://git-wip-us.apache.org/repos/asf/isis/commit/99e1e03e
Tree: http://git-wip-us.apache.org/repos/asf/isis/tree/99e1e03e
Diff: http://git-wip-us.apache.org/repos/asf/isis/diff/99e1e03e

Branch: refs/heads/master
Commit: 99e1e03e30408a46660dbebbec75a67b9e607fd0
Parents: 1a51929 2104d74
Author: Dan Haywood <da...@haywood-associates.co.uk>
Authored: Tue Oct 6 19:53:06 2015 +0100
Committer: Dan Haywood <da...@haywood-associates.co.uk>
Committed: Tue Oct 6 19:53:06 2015 +0100

----------------------------------------------------------------------
 .../isis/applib/annotation/ActionSemantics.java | 14 ++++++++--
 .../isis/applib/annotation/SemanticsOf.java     | 28 +++++++++++++++++--
 core/pom.xml                                    |  8 +++---
 .../actions/ActionParametersFormPanel.java      | 29 +++++++++++++++++++-
 .../viewer/wicket/ui/pages/PageAbstract.java    |  4 +--
 5 files changed, 72 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/isis/blob/99e1e03e/core/pom.xml
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/isis/blob/99e1e03e/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersFormPanel.java
----------------------------------------------------------------------
diff --cc core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersFormPanel.java
index 8416491,0000000..f20fbe7
mode 100644,000000..100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersFormPanel.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersFormPanel.java
@@@ -1,260 -1,0 +1,287 @@@
 +/*
 + *  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 de.agilecoders.wicket.extensions.markup.html.bootstrap.confirmation.ConfirmationBehavior;
++import de.agilecoders.wicket.extensions.markup.html.bootstrap.confirmation.ConfirmationConfig;
++
 +import java.util.List;
 +import com.google.common.collect.Lists;
 +import org.apache.wicket.Component;
 +import org.apache.wicket.ajax.AjaxRequestTarget;
 +import org.apache.wicket.ajax.markup.html.form.AjaxButton;
 +import org.apache.wicket.event.Broadcast;
 +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.ResourceModel;
++import org.apache.isis.applib.annotation.ActionSemantics;
 +import org.apache.isis.core.commons.ensure.Ensure;
 +import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
 +import org.apache.isis.core.metamodel.adapter.version.ConcurrencyException;
 +import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
 +import org.apache.isis.core.runtime.system.context.IsisContext;
 +import org.apache.isis.viewer.wicket.model.hints.IsisActionCompletedEvent;
 +import org.apache.isis.viewer.wicket.model.mementos.ActionParameterMemento;
 +import org.apache.isis.viewer.wicket.model.models.ActionExecutor;
 +import org.apache.isis.viewer.wicket.model.models.ActionModel;
 +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.ScalarModel;
 +import org.apache.isis.viewer.wicket.ui.ComponentType;
 +import org.apache.isis.viewer.wicket.ui.components.actionprompt.ActionPromptModalWindow;
 +import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarModelSubscriber;
 +import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarPanelAbstract;
 +import org.apache.isis.viewer.wicket.ui.components.scalars.TextFieldValueModel.ScalarModelProvider;
 +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.errors.JGrowlUtil;
 +import org.apache.isis.viewer.wicket.ui.pages.entity.EntityPage;
 +import org.apache.isis.viewer.wicket.ui.panels.PanelAbstract;
 +
 +import static org.hamcrest.CoreMatchers.*;
 +
 +/**
 + * {@link PanelAbstract Panel} to capture the arguments for an action
 + * invocation.
 + */
 +public class ActionParametersFormPanel extends PanelAbstract<ActionModel> {
 +
 +    private static final long serialVersionUID = 1L;
 +
 +    private static final String ID_OK_BUTTON = "okButton";
 +    private static final String ID_CANCEL_BUTTON = "cancelButton";
 +    private static final String ID_ACTION_PARAMETERS = "parameters";
 +
 +    private final ActionExecutor actionExecutor;
 +    //private final ActionPrompt actionPromptIfAny;
 +
 +    public ActionParametersFormPanel(final String id, final ActionModel model) {
 +        super(id, model);
 +
 +        Ensure.ensureThatArg(model.getExecutor(), is(not(nullValue())));
 +
 +        this.actionExecutor = model.getExecutor();
 +        //this.actionPromptIfAny = model.getActionPrompt();
 +        buildGui();
 +    }
 +
 +    private void buildGui() {
 +        ActionModel model = getModel();
 +        // in case previously used, eg prompt displayed then cancelled
 +        model.clearArguments();
 +        
 +        add(new ActionParameterForm("inputForm", model));
 +    }
 +
 +    class ActionParameterForm extends Form<ObjectAdapter> implements ScalarModelSubscriber  {
 +
 +        private static final long serialVersionUID = 1L;
 +
 +        private static final String ID_FEEDBACK = "feedback";
 +        
 +        private final List<ScalarPanelAbstract> paramPanels = Lists.newArrayList();
 +
 +        public ActionParameterForm(final String id, final ActionModel actionModel) {
 +            super(id, actionModel);
 +
 +            setOutputMarkupId(true); // for ajax button
 +            
 +            addParameters();
 +
 +            FormFeedbackPanel formFeedback = new FormFeedbackPanel(ID_FEEDBACK);
 +            addOrReplace(formFeedback);
 +            addButtons();
 +        }
 +
 +        private ActionModel getActionModel() {
 +            return (ActionModel) super.getModel();
 +        }
 +
 +        private void addParameters() {
 +            final ActionModel actionModel = getActionModel();
 +            List<ActionParameterMemento> parameterMementos = actionModel.primeArgumentModels();
 +            
 +            final RepeatingView rv = new RepeatingView(ID_ACTION_PARAMETERS);
 +            add(rv);
 +            
 +            paramPanels.clear();
 +            for (final ActionParameterMemento apm : parameterMementos) {
 +                final WebMarkupContainer container = new WebMarkupContainer(rv.newChildId());
 +                rv.add(container);
 +
 +                final ScalarModel argumentModel = actionModel.getArgumentModel(apm);
 +                argumentModel.setActionArgsHint(actionModel.getArgumentsAsArray());
 +                final Component component = getComponentFactoryRegistry().addOrReplaceComponent(container, ComponentType.SCALAR_NAME_AND_VALUE, argumentModel);
 +                final ScalarPanelAbstract paramPanel = component instanceof ScalarPanelAbstract ? (ScalarPanelAbstract) component : null;
 +                paramPanels.add(paramPanel);
 +                if(paramPanel != null) {
 +                    paramPanel.setOutputMarkupId(true);
 +                    paramPanel.notifyOnChange(this);
 +                }
 +            }
 +        }
 +
 +
 +        private void addButtons() {
 +            AjaxButton okButton = new AjaxButton(ID_OK_BUTTON, new ResourceModel("okLabel")) {
 +                private static final long serialVersionUID = 1L;
 +
 +                @Override
 +                public void onSubmit(AjaxRequestTarget target, Form<?> form) {
 +                    boolean succeeded = actionExecutor.executeActionAndProcessResults(target, form);
 +                    if(succeeded) {
 +                        // the Wicket ajax callbacks will have just started to hide the veil
 +                        // we now show it once more, so that a veil continues to be shown until the
 +                        // new page is rendered.
 +                        target.appendJavaScript("isisShowVeil();\n");
 +
 +                        send(getPage(), Broadcast.EXACT, new IsisActionCompletedEvent(getActionModel(), target, form));
 +
 +                        target.add(form);
 +                    } else {
 +                        //if (actionPromptIfAny != null) {
 +                            
 +                            final StringBuilder builder = new StringBuilder();
 +
 +                            // ensure any jGrowl errors are shown
 +                            // (normally would be flushed when traverse to next page).
 +                            String errorMessagesIfAny = JGrowlUtil.asJGrowlCalls(IsisContext.getMessageBroker());
 +                            builder.append(errorMessagesIfAny);
 +
 +                            // append the JS to the response. 
 +                            String buf = builder.toString();
 +                            target.appendJavaScript(buf);
 +                            target.add(form);
 +                        //}
 +                    }
 +                };
 +
 +                /**
 +                 * On validation error
 +                 */
 +                @Override
 +                protected void onError(AjaxRequestTarget target, Form<?> form) {
 +                    super.onError(target, form);
 +                    target.add(form);
 +                }
 +            };
 +            okButton.add(new JGrowlBehaviour());
 +            setDefaultButton(okButton);
 +            add(okButton);
-             
++            applyAreYouSure(okButton);
++
 +            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) {
 +                    final ActionPrompt actionPromptIfAny = ActionPromptProvider.Util.getFrom(ActionParametersFormPanel.this).getActionPrompt();
 +                    if(actionPromptIfAny != null) {
 +                        actionPromptIfAny.closePrompt(target);
 +                    }
 +                }
 +            };
 +            // so can submit with invalid content (eg mandatory params missing)
 +            cancelButton.setDefaultFormProcessing(false);
 +            add(cancelButton);
 +            
 +            // TODO: hide cancel button if dialogs disabled, as not yet implemented.
 +            if(ActionPromptModalWindow.isActionPromptModalDialogDisabled()) {
 +                cancelButton.setVisible(false);
 +            }
 +        }
 +
++        /**
++         * 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();
++            ActionSemantics.Of semantics = action.getSemantics();
++            if (semantics == ActionSemantics.Of.IDEMPOTENT_ARE_YOU_SURE || semantics == ActionSemantics.Of.NON_IDEMPOTENT_ARE_YOU_SURE) {
++                ConfirmationConfig confirmationConfig = new ConfirmationConfig();
++                // TODO ISIS-1007 Use i18n for the title and the labels
++                confirmationConfig.withTitle("Are you sure?");
++                confirmationConfig.withBtnOkLabel("Confirm");
++                confirmationConfig.withBtnCancelLabel("Cancel");
++                confirmationConfig.withBtnOkClass("btn btn-danger");
++                confirmationConfig.withBtnCancelClass("btn btn-default");
++                button.add(new ConfirmationBehavior(confirmationConfig));
++            }
++        }
++
 +        @Override
 +        public void onUpdate(AjaxRequestTarget target, ScalarModelProvider provider) {
 +
 +            final ActionModel actionModel = getActionModel();
 +            
 +            final ObjectAdapter[] pendingArguments = actionModel.getArgumentsAsArray();
 +            
 +            try {
 +                final ObjectAction action = actionModel.getActionMemento().getAction();
 +                final int numParams = action.getParameterCount();
 +                for (int i = 0; i < numParams; i++) {
 +                    final ScalarPanelAbstract paramPanel = paramPanels.get(i);
 +                    if(paramPanel != null) {
 +                        // 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(paramPanel.updateChoices(pendingArguments)) {
 +                            target.add(paramPanel);
 +                        }
 +                    }
 +                }
 +            } 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);
 +                
 +                ActionParametersFormPanel.this.setResponsePage(entityPage);
 +                
 +                getAuthenticationSession().getMessageBroker().addWarning(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).
 +        }
 +        
 +        @Override
 +        public void onError(AjaxRequestTarget target, ScalarModelProvider provider) {
 +            if(provider instanceof Component) {
 +                // ensure that any feedback error associated with the providing component is shown.
 +                target.add((Component)provider); 
 +            }
 +        }
 +
 +    }
 +
 +}

http://git-wip-us.apache.org/repos/asf/isis/blob/99e1e03e/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/PageAbstract.java
----------------------------------------------------------------------
diff --cc core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/PageAbstract.java
index daf886e,0000000..60e273d
mode 100644,000000..100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/PageAbstract.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/PageAbstract.java
@@@ -1,450 -1,0 +1,450 @@@
 +/*
 + *  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.pages;
 +
 +import de.agilecoders.wicket.core.Bootstrap;
- import de.agilecoders.wicket.core.markup.html.references.BootlintJavaScriptReference;
++import de.agilecoders.wicket.core.markup.html.references.BootlintHeaderItem;
 +import de.agilecoders.wicket.core.settings.IBootstrapSettings;
 +import de.agilecoders.wicket.core.settings.ITheme;
 +import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesomeCssReference;
 +
 +import java.util.Arrays;
 +import java.util.Collections;
 +import java.util.List;
 +import java.util.Locale;
 +import com.google.inject.Inject;
 +import com.google.inject.name.Named;
 +import org.apache.wicket.Component;
 +import org.apache.wicket.MarkupContainer;
 +import org.apache.wicket.Page;
 +import org.apache.wicket.RestartResponseAtInterceptPageException;
 +import org.apache.wicket.behavior.Behavior;
 +import org.apache.wicket.event.Broadcast;
 +import org.apache.wicket.markup.head.CssHeaderItem;
 +import org.apache.wicket.markup.head.CssReferenceHeaderItem;
 +import org.apache.wicket.markup.head.IHeaderResponse;
 +import org.apache.wicket.markup.head.JavaScriptHeaderItem;
 +import org.apache.wicket.markup.head.JavaScriptReferenceHeaderItem;
 +import org.apache.wicket.markup.head.PriorityHeaderItem;
 +import org.apache.wicket.markup.head.filter.HeaderResponseContainer;
 +import org.apache.wicket.markup.html.WebMarkupContainer;
 +import org.apache.wicket.markup.html.WebPage;
 +import org.apache.wicket.markup.html.basic.Label;
 +import org.apache.wicket.model.IModel;
 +import org.apache.wicket.protocol.http.ClientProperties;
 +import org.apache.wicket.protocol.http.WebSession;
 +import org.apache.wicket.protocol.http.request.WebClientInfo;
 +import org.apache.wicket.request.mapper.parameter.PageParameters;
 +import org.apache.wicket.request.resource.CssResourceReference;
 +import org.apache.wicket.request.resource.JavaScriptResourceReference;
 +import org.apache.wicket.request.resource.PackageResource;
 +import org.apache.wicket.request.resource.ResourceReference;
 +import org.slf4j.Logger;
 +import org.slf4j.LoggerFactory;
 +import org.apache.isis.applib.services.exceprecog.ExceptionRecognizer;
 +import org.apache.isis.applib.services.exceprecog.ExceptionRecognizerComposite;
 +import org.apache.isis.core.commons.authentication.AuthenticationSession;
 +import org.apache.isis.core.commons.authentication.MessageBroker;
 +import org.apache.isis.core.commons.config.IsisConfiguration;
 +import org.apache.isis.core.metamodel.services.ServicesInjectorSpi;
 +import org.apache.isis.core.metamodel.spec.SpecificationLoaderSpi;
 +import org.apache.isis.core.runtime.system.context.IsisContext;
 +import org.apache.isis.core.runtime.system.persistence.PersistenceSession;
 +import org.apache.isis.viewer.wicket.model.common.PageParametersUtils;
 +import org.apache.isis.viewer.wicket.model.hints.IsisEnvelopeEvent;
 +import org.apache.isis.viewer.wicket.model.hints.IsisEventLetterAbstract;
 +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.BookmarkableModel;
 +import org.apache.isis.viewer.wicket.model.models.BookmarkedPagesModel;
 +import org.apache.isis.viewer.wicket.model.models.EntityModel;
 +import org.apache.isis.viewer.wicket.model.models.PageType;
 +import org.apache.isis.viewer.wicket.ui.ComponentFactory;
 +import org.apache.isis.viewer.wicket.ui.ComponentType;
 +import org.apache.isis.viewer.wicket.ui.app.registry.ComponentFactoryRegistry;
 +import org.apache.isis.viewer.wicket.ui.app.registry.ComponentFactoryRegistryAccessor;
 +import org.apache.isis.viewer.wicket.ui.components.actionprompt.ActionPromptModalWindow;
 +import org.apache.isis.viewer.wicket.ui.components.widgets.favicon.Favicon;
 +import org.apache.isis.viewer.wicket.ui.errors.ExceptionModel;
 +import org.apache.isis.viewer.wicket.ui.errors.JGrowlBehaviour;
 +import org.apache.isis.viewer.wicket.ui.util.CssClassAppender;
 +
 +/**
 + * Convenience adapter for {@link WebPage}s built up using {@link ComponentType}s.
 + */
 +public abstract class PageAbstract extends WebPage implements ActionPromptProvider {
 +
 +    private static Logger LOG = LoggerFactory.getLogger(PageAbstract.class);
 +
 +    private static final long serialVersionUID = 1L;
 +
 +    /**
 +     * @see <a href="http://github.com/brandonaaron/livequery">livequery</a>
 +     */
 +    private static final JavaScriptResourceReference JQUERY_LIVEQUERY_JS = new JavaScriptResourceReference(PageAbstract.class, "jquery.livequery.js");
 +    private static final JavaScriptResourceReference JQUERY_ISIS_WICKET_VIEWER_JS = new JavaScriptResourceReference(PageAbstract.class, "jquery.isis.wicket.viewer.js");
 +    
 +    // not to be confused with the bootstrap theme...
 +    // is simply a CSS class derived from the application's name
 +    private static final String ID_THEME = "theme";
 +
 +    private static final String ID_BOOKMARKED_PAGES = "bookmarks";
 +
 +    private static final String ID_ACTION_PROMPT_MODAL_WINDOW = "actionPromptModalWindow";
 +    
 +    private static final String ID_PAGE_TITLE = "pageTitle";
 +
 +    private static final String ID_FAVICON = "favicon";
 +
 +    public static final String ID_MENU_LINK = "menuLink";
 +
 +    /**
 +     * This is a bit hacky, but best way I've found to pass an exception over to the WicketSignInPage
 +     * if there is a problem rendering this page.
 +     */
 +    public static ThreadLocal<ExceptionModel> EXCEPTION = new ThreadLocal<>();
 +
 +    private final List<ComponentType> childComponentIds;
 +
 +    /**
 +     * {@link Inject}ed when {@link #init() initialized}.
 +     */
 +    @Inject
 +    @Named("applicationName")
 +    private String applicationName;
 +
 +    /**
 +     * {@link Inject}ed when {@link #init() initialized}.
 +     */
 +    @Inject(optional = true)
 +    @Named("applicationCss")
 +    private String applicationCss;
 +    
 +    /**
 +     * {@link Inject}ed when {@link #init() initialized}.
 +     *///
 +    @Inject(optional = true)
 +    @Named("applicationJs")
 +    private String applicationJs;
 +
 +    /**
 +     * {@link Inject}ed when {@link #init() initialized}.
 +     */
 +    @Inject
 +    private PageClassRegistry pageClassRegistry;
 +
 +    /**
 +     * Top-level &lt;div&gt; to which all content is added.
 +     *
 +     * <p>
 +     *     Has <code>protected</code> visibility so that subclasses can also add directly to this div.
 +     * </p>
 +     */
 +    protected MarkupContainer themeDiv;
 +
 +    public PageAbstract(
 +            final PageParameters pageParameters,
 +            final String title,
 +            final ComponentType... childComponentIds) {
 +        super(pageParameters);
 +
 +        try {
 +            // for breadcrumbs support
 +            getSession().bind();
 +            
 +            setTitle(title);
 +
 +            add(new Favicon(ID_FAVICON));
 +
 +            themeDiv = new WebMarkupContainer(ID_THEME);
 +            add(themeDiv);
 +            if(applicationName != null) {
 +                themeDiv.add(new CssClassAppender(CssClassAppender.asCssStyle(applicationName)));
 +            }
 +
 +            MarkupContainer header = createPageHeader("header");
 +            themeDiv.add(header);
 +
 +            MarkupContainer footer = createPageFooter("footer");
 +            themeDiv.add(footer);
 +
 +            addActionPromptModalWindow(themeDiv);
 +
 +            this.childComponentIds = Collections.unmodifiableList(Arrays.asList(childComponentIds));
 +
 +            // ensure that all collected JavaScript contributions are loaded at the page footer
 +            add(new HeaderResponseContainer("footerJS", "footerJS"));
 +
 +        } catch(final RuntimeException ex) {
 +
 +            LOG.error("Failed to construct page, going back to sign in page", ex);
 +            
 +            // REVIEW: similar code in WebRequestCycleForIsis
 +            final  List<ExceptionRecognizer> exceptionRecognizers = getServicesInjector().lookupServices(ExceptionRecognizer.class);
 +            final String recognizedMessageIfAny = new ExceptionRecognizerComposite(exceptionRecognizers).recognize(ex);
 +            final ExceptionModel exceptionModel = ExceptionModel.create(recognizedMessageIfAny, ex);
 +
 +            getSession().invalidate();
 +            getSession().clear();
 +            
 +            // for the WicketSignInPage to render
 +            EXCEPTION.set(exceptionModel);
 +
 +            throw new RestartResponseAtInterceptPageException(getSignInPage());
 +        }
 +    }
 +
 +    /**
 +     * Creates the component that should be used as a page header/navigation bar
 +     *
 +     * @param id The component id
 +     * @return The container that should be used as a page header/navigation bar
 +     */
 +    protected MarkupContainer createPageHeader(final String id) {
 +        Component header = getComponentFactoryRegistry().createComponent(ComponentType.HEADER, id, null);
 +        return (MarkupContainer) header;
 +    }
 +
 +    /**
 +     * Creates the component that should be used as a page header/navigation bar
 +     *
 +     * @param id The component id
 +     * @return The container that should be used as a page header/navigation bar
 +     */
 +    protected MarkupContainer createPageFooter(final String id) {
 +        Component footer = getComponentFactoryRegistry().createComponent(ComponentType.FOOTER, id, null);
 +        return (MarkupContainer) footer;
 +    }
 +
 +
 +    protected void setTitle(final String title) {
 +        addOrReplace(new Label(ID_PAGE_TITLE, title != null? title: applicationName));
 +    }
 +
 +    private Class<? extends Page> getSignInPage() {
 +        return pageClassRegistry.getPageClass(PageType.SIGN_IN);
 +    }
 +
 +    @Override
 +    public void renderHead(final IHeaderResponse response) {
 +        
 +        super.renderHead(response);
 +
 +        response.render(new PriorityHeaderItem(JavaScriptHeaderItem.forReference(getApplication().getJavaScriptLibrarySettings().getJQueryReference())));
 +        response.render(CssHeaderItem.forReference(FontAwesomeCssReference.instance()));
 +        response.render(CssHeaderItem.forReference(new BootstrapOverridesCssResourceReference()));
 +        contributeThemeSpecificOverrides(response);
 +
 +        response.render(JavaScriptReferenceHeaderItem.forReference(JQUERY_LIVEQUERY_JS));
 +        response.render(JavaScriptReferenceHeaderItem.forReference(JQUERY_ISIS_WICKET_VIEWER_JS));
 +
 +        final JGrowlBehaviour jGrowlBehaviour = new JGrowlBehaviour();
 +        jGrowlBehaviour.renderFeedbackMessages(response);
 +
 +        if(applicationCss != null) {
 +            response.render(CssReferenceHeaderItem.forUrl(applicationCss));
 +        }
 +        if(applicationJs != null) {
 +            response.render(JavaScriptReferenceHeaderItem.forUrl(applicationJs));
 +        }
 +
 +        if(isModernBrowser()) {
 +            addBootLint(response);
 +        }
 +    }
 +
 +    private void addBootLint(final IHeaderResponse response) {
-         response.render(JavaScriptHeaderItem.forReference(BootlintJavaScriptReference.INSTANCE));
++        response.render(BootlintHeaderItem.INSTANCE);
 +    }
 +
 +    private boolean isModernBrowser() {
 +        return !isIePre9();
 +    }
 +
 +    private boolean isIePre9() {
 +        final WebClientInfo clientInfo = WebSession.get().getClientInfo();
 +        final ClientProperties properties = clientInfo.getProperties();
 +        if (properties.isBrowserInternetExplorer())
 +            if (properties.getBrowserVersionMajor() < 9)
 +                return true;
 +        return false;
 +    }
 +
 +    /**
 +     * Contributes theme specific Bootstrap CSS overrides if there is such resource
 +     *
 +     * @param response The header response to contribute to
 +     */
 +    private void contributeThemeSpecificOverrides(final IHeaderResponse response) {
 +        final IBootstrapSettings bootstrapSettings = Bootstrap.getSettings(getApplication());
 +        final ITheme activeTheme = bootstrapSettings.getActiveThemeProvider().getActiveTheme();
 +        final String name = activeTheme.name().toLowerCase(Locale.ENGLISH);
 +        final String themeSpecificOverride = "bootstrap-overrides-" + name + ".css";
 +        final ResourceReference.Key themeSpecificOverrideKey = new ResourceReference.Key(PageAbstract.class.getName(), themeSpecificOverride, null, null, null);
 +        if (PackageResource.exists(themeSpecificOverrideKey)) {
 +            response.render(CssHeaderItem.forReference(new CssResourceReference(themeSpecificOverrideKey)));
 +        }
 +    }
 +
 +    /**
 +     * As provided in the {@link #PageAbstract(org.apache.wicket.request.mapper.parameter.PageParameters, String, org.apache.isis.viewer.wicket.ui.ComponentType...)} constructor}.
 +     * 
 +     * <p>
 +     * This superclass doesn't do anything with this property directly, but
 +     * requiring it to be provided enforces standardization of the
 +     * implementation of the subclasses.
 +     */
 +    public List<ComponentType> getChildModelTypes() {
 +        return childComponentIds;
 +    }
 +
 +    /**
 +     * For subclasses to call.
 +     * 
 +     * <p>
 +     * Should be called in the subclass' constructor.
 +     * 
 +     * @param model
 +     *            - used to find the best matching {@link ComponentFactory} to
 +     *            render the model.
 +     */
 +    protected void addChildComponents(final MarkupContainer container, final IModel<?> model) {
 +        for (final ComponentType componentType : getChildModelTypes()) {
 +            addComponent(container, componentType, model);
 +        }
 +    }
 +
 +    private void addComponent(final MarkupContainer container, final ComponentType componentType, final IModel<?> model) {
 +        getComponentFactoryRegistry().addOrReplaceComponent(container, componentType, model);
 +    }
 +
 +
 +    ////////////////////////////////////////////////////////////////
 +    // bookmarked pages
 +    ////////////////////////////////////////////////////////////////
 +
 +    /**
 +     * Convenience for subclasses
 +     */
 +    protected void addBookmarkedPages(final MarkupContainer container) {
 +        Component bookmarks = getComponentFactoryRegistry().createComponent(ComponentType.BOOKMARKED_PAGES, ID_BOOKMARKED_PAGES, getBookmarkedPagesModel());
 +        container.add(bookmarks);
 +        bookmarks.add(new Behavior() {
 +            @Override
 +            public void onConfigure(Component component) {
 +                super.onConfigure(component);
 +
 +                PageParameters parameters = getPageParameters();
 +                component.setVisible(parameters.get(PageParametersUtils.ISIS_NO_HEADER_PARAMETER_NAME).isNull());
 +            }
 +        });
 +    }
 +
 +    protected void bookmarkPage(final BookmarkableModel<?> model) {
 +        getBookmarkedPagesModel().bookmarkPage(model);
 +    }
 +
 +    protected void removeAnyBookmark(final EntityModel model) {
 +        getBookmarkedPagesModel().remove(model);
 +    }
 +
 +    private BookmarkedPagesModel getBookmarkedPagesModel() {
 +        final BookmarkedPagesModelProvider session = (BookmarkedPagesModelProvider) getSession();
 +        return session.getBookmarkedPagesModel();
 +    }
 +
 +
 +
 +    // ///////////////////////////////////////////////////////////////////
 +    // ActionPromptModalWindowProvider
 +    // ///////////////////////////////////////////////////////////////////
 +    
 +    private ActionPromptModalWindow actionPromptModalWindow;
 +
 +    public ActionPrompt getActionPrompt() {
 +        return ActionPromptModalWindow.getActionPromptModalWindowIfEnabled(actionPromptModalWindow);
 +    }
 +
 +    private void addActionPromptModalWindow(final MarkupContainer parent) {
 +        actionPromptModalWindow = ActionPromptModalWindow.newModalWindow(ID_ACTION_PROMPT_MODAL_WINDOW); 
 +        parent.addOrReplace(actionPromptModalWindow);
 +    }
 +
 +    
 +    // ///////////////////////////////////////////////////////////////////
 +    // UI Hint
 +    // ///////////////////////////////////////////////////////////////////
 +
 +    /**
 +     * Propagates all {@link org.apache.isis.viewer.wicket.model.hints.IsisEventLetterAbstract letter} events down to
 +     * all child components, wrapped in an {@link org.apache.isis.viewer.wicket.model.hints.IsisEnvelopeEvent envelope} event.
 +     */
 +    public void onEvent(final org.apache.wicket.event.IEvent<?> event) {
 +        final Object payload = event.getPayload();
 +        if(payload instanceof IsisEventLetterAbstract) {
 +            final IsisEventLetterAbstract letter = (IsisEventLetterAbstract)payload;
 +            final IsisEnvelopeEvent broadcastEv = new IsisEnvelopeEvent(letter);
 +            send(this, Broadcast.BREADTH, broadcastEv);
 +        }
 +    }
 +    
 +
 +    // ///////////////////////////////////////////////////////////////////
 +    // Convenience
 +    // ///////////////////////////////////////////////////////////////////
 +
 +    protected ComponentFactoryRegistry getComponentFactoryRegistry() {
 +        final ComponentFactoryRegistryAccessor cfra = (ComponentFactoryRegistryAccessor) getApplication();
 +        return cfra.getComponentFactoryRegistry();
 +    }
 +
 +    protected <T> T lookupService(final Class<T> serviceClass) {
 +        return getServicesInjector().lookupService(serviceClass);
 +    }
 +
 +    // ///////////////////////////////////////////////////
 +    // System components
 +    // ///////////////////////////////////////////////////
 +
 +    protected ServicesInjectorSpi getServicesInjector() {
 +        return getPersistenceSession().getServicesInjector();
 +    }
 +
 +    protected PersistenceSession getPersistenceSession() {
 +        return IsisContext.getPersistenceSession();
 +    }
 +
 +    protected SpecificationLoaderSpi getSpecificationLoader() {
 +        return IsisContext.getSpecificationLoader();
 +    }
 +    
 +    protected AuthenticationSession getAuthenticationSession() {
 +        return IsisContext.getAuthenticationSession();
 +    }
 +    
 +    protected MessageBroker getMessageBroker() {
 +        return IsisContext.getMessageBroker();
 +    }
 +    
 +    protected IsisConfiguration getConfiguration() {
 +        return IsisContext.getConfiguration();
 +    }
 +
 +}


[4/4] isis git commit: ISIS-1007: extending for no-arg links. NB: however, this is currently broken, have asked martin-g to help out.

Posted by da...@apache.org.
ISIS-1007: extending for no-arg links.  NB: however, this is currently broken, have asked martin-g to help out.


Project: http://git-wip-us.apache.org/repos/asf/isis/repo
Commit: http://git-wip-us.apache.org/repos/asf/isis/commit/dbcd0965
Tree: http://git-wip-us.apache.org/repos/asf/isis/tree/dbcd0965
Diff: http://git-wip-us.apache.org/repos/asf/isis/diff/dbcd0965

Branch: refs/heads/master
Commit: dbcd09655318958cd2b5e4784a632ac8692ddb07
Parents: 99e1e03
Author: Dan Haywood <da...@haywood-associates.co.uk>
Authored: Tue Oct 6 22:29:09 2015 +0100
Committer: Dan Haywood <da...@haywood-associates.co.uk>
Committed: Tue Oct 6 22:29:09 2015 +0100

----------------------------------------------------------------------
 .../isis/applib/annotation/ActionSemantics.java |  8 +++++++
 .../isis/applib/annotation/SemanticsOf.java     |  4 ++++
 .../viewer/wicket/model/links/LinkAndLabel.java | 10 ++++++++-
 .../entityactions/AdditionalLinksPanel.java     | 22 +++++++++++++++++++-
 .../actions/ActionParametersFormPanel.java      | 18 +++++++++-------
 .../collection/bulk/BulkActionsLinkFactory.java |  6 +++++-
 .../linkandlabel/ActionLinkFactoryAbstract.java | 22 ++++++++++++--------
 7 files changed, 71 insertions(+), 19 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/isis/blob/dbcd0965/core/applib/src/main/java/org/apache/isis/applib/annotation/ActionSemantics.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/annotation/ActionSemantics.java b/core/applib/src/main/java/org/apache/isis/applib/annotation/ActionSemantics.java
index 0801922..edcaade 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/annotation/ActionSemantics.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/annotation/ActionSemantics.java
@@ -122,6 +122,14 @@ public @interface ActionSemantics {
         public boolean isSafeAndRequestCacheable() {
             return SemanticsOf.from(this).isSafeAndRequestCacheable();
         }
+
+        /**
+         * @deprecated - see {@link SemanticsOf#isAreYouSure()}.
+         */
+        @Deprecated
+        public boolean isAreYouSure() {
+            return SemanticsOf.from(this).isAreYouSure();
+        }
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/isis/blob/dbcd0965/core/applib/src/main/java/org/apache/isis/applib/annotation/SemanticsOf.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/annotation/SemanticsOf.java b/core/applib/src/main/java/org/apache/isis/applib/annotation/SemanticsOf.java
index 7a4a570..1fc8ce0 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/annotation/SemanticsOf.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/annotation/SemanticsOf.java
@@ -102,6 +102,10 @@ public enum SemanticsOf {
         return this == SAFE_AND_REQUEST_CACHEABLE;
     }
 
+    public boolean isAreYouSure() {
+        return this == IDEMPOTENT_ARE_YOU_SURE || this == NON_IDEMPOTENT_ARE_YOU_SURE;
+    }
+
     @Deprecated
     public static ActionSemantics.Of from(final SemanticsOf semantics) {
         if(semantics == null) return null;

http://git-wip-us.apache.org/repos/asf/isis/blob/dbcd0965/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/links/LinkAndLabel.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/links/LinkAndLabel.java b/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/links/LinkAndLabel.java
index 19e1fd4..d961fc2 100644
--- a/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/links/LinkAndLabel.java
+++ b/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/links/LinkAndLabel.java
@@ -25,6 +25,7 @@ import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import org.apache.wicket.markup.html.link.AbstractLink;
 import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.core.metamodel.facets.members.cssclassfa.CssClassFaPosition;
 
 public class LinkAndLabel implements Serializable {
@@ -49,6 +50,7 @@ public class LinkAndLabel implements Serializable {
     private final String cssClassFa;
     private final CssClassFaPosition cssClassFaPosition;
     private final ActionLayout.Position position;
+    private final SemanticsOf semanticsOf;
 
     public LinkAndLabel(
             final AbstractLink link,
@@ -61,7 +63,8 @@ public class LinkAndLabel implements Serializable {
             final String cssClass,
             final String cssClassFa,
             final CssClassFaPosition cssClassFaPosition,
-            final ActionLayout.Position position) {
+            final ActionLayout.Position position,
+            final SemanticsOf semanticsOf) {
         this.link = link;
         this.label = label;
         this.disabledReasonIfAny = disabledReasonIfAny;
@@ -73,6 +76,7 @@ public class LinkAndLabel implements Serializable {
         this.cssClassFa = cssClassFa;
         this.cssClassFaPosition = cssClassFaPosition;
         this.position = position;
+        this.semanticsOf = semanticsOf;
     }
 
     public AbstractLink getLink() {
@@ -119,6 +123,10 @@ public class LinkAndLabel implements Serializable {
         return position;
     }
 
+    public SemanticsOf getSemantics() {
+        return semanticsOf;
+    }
+
     public static class Predicates {
         public static Predicate<LinkAndLabel> positioned(final ActionLayout.Position position) {
             return new Predicate<LinkAndLabel>() {

http://git-wip-us.apache.org/repos/asf/isis/blob/dbcd0965/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/entityactions/AdditionalLinksPanel.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/entityactions/AdditionalLinksPanel.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/entityactions/AdditionalLinksPanel.java
index d80374a..1aa625b 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/entityactions/AdditionalLinksPanel.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/entityactions/AdditionalLinksPanel.java
@@ -20,7 +20,9 @@
 package org.apache.isis.viewer.wicket.ui.components.actionmenu.entityactions;
 
 import java.util.List;
+
 import com.google.common.base.Strings;
+
 import org.apache.wicket.MarkupContainer;
 import org.apache.wicket.behavior.AttributeAppender;
 import org.apache.wicket.markup.html.WebMarkupContainer;
@@ -28,8 +30,10 @@ import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.markup.html.link.AbstractLink;
 import org.apache.wicket.markup.html.list.ListItem;
 import org.apache.wicket.markup.html.list.ListView;
-import org.apache.isis.core.metamodel.facets.members.cssclassfa.CssClassFaPosition;
+
+import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.core.commons.lang.StringExtensions;
+import org.apache.isis.core.metamodel.facets.members.cssclassfa.CssClassFaPosition;
 import org.apache.isis.viewer.wicket.model.links.LinkAndLabel;
 import org.apache.isis.viewer.wicket.model.links.ListOfLinksModel;
 import org.apache.isis.viewer.wicket.ui.components.actionmenu.CssClassFaBehavior;
@@ -37,6 +41,9 @@ import org.apache.isis.viewer.wicket.ui.panels.PanelAbstract;
 import org.apache.isis.viewer.wicket.ui.util.Components;
 import org.apache.isis.viewer.wicket.ui.util.CssClassAppender;
 
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.confirmation.ConfirmationBehavior;
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.confirmation.ConfirmationConfig;
+
 public class AdditionalLinksPanel extends PanelAbstract<ListOfLinksModel> {
 
     private static final long serialVersionUID = 1L;
@@ -117,6 +124,19 @@ public class AdditionalLinksPanel extends PanelAbstract<ListOfLinksModel> {
                 }
                 link.add(new CssClassAppender(linkAndLabel.getActionIdentifier()));
 
+                SemanticsOf semantics = linkAndLabel.getSemantics();
+                if (    semantics.isAreYouSure() ) {
+                    ConfirmationConfig confirmationConfig = new ConfirmationConfig();
+                    // TODO ISIS-1007 Use i18n for the title and the labels
+                    confirmationConfig.withTitle("Are you sure?");
+                    confirmationConfig.withBtnOkLabel("Confirm");
+                    confirmationConfig.withBtnCancelLabel("Cancel");
+                    confirmationConfig.withBtnOkClass("btn btn-danger");
+                    confirmationConfig.withBtnCancelClass("btn btn-default");
+                    link.add(new ConfirmationBehavior(confirmationConfig));
+                }
+
+
                 final String cssClass = linkAndLabel.getCssClass();
                 CssClassAppender.appendCssClassTo(link, cssClass);
 

http://git-wip-us.apache.org/repos/asf/isis/blob/dbcd0965/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersFormPanel.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersFormPanel.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersFormPanel.java
index f20fbe7..5366944 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersFormPanel.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersFormPanel.java
@@ -19,11 +19,10 @@
 
 package org.apache.isis.viewer.wicket.ui.components.actions;
 
-import de.agilecoders.wicket.extensions.markup.html.bootstrap.confirmation.ConfirmationBehavior;
-import de.agilecoders.wicket.extensions.markup.html.bootstrap.confirmation.ConfirmationConfig;
-
 import java.util.List;
+
 import com.google.common.collect.Lists;
+
 import org.apache.wicket.Component;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.ajax.markup.html.form.AjaxButton;
@@ -32,7 +31,8 @@ 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.ResourceModel;
-import org.apache.isis.applib.annotation.ActionSemantics;
+
+import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.core.commons.ensure.Ensure;
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
 import org.apache.isis.core.metamodel.adapter.version.ConcurrencyException;
@@ -56,7 +56,11 @@ import org.apache.isis.viewer.wicket.ui.errors.JGrowlUtil;
 import org.apache.isis.viewer.wicket.ui.pages.entity.EntityPage;
 import org.apache.isis.viewer.wicket.ui.panels.PanelAbstract;
 
-import static org.hamcrest.CoreMatchers.*;
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.confirmation.ConfirmationBehavior;
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.confirmation.ConfirmationConfig;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.nullValue;
 
 /**
  * {@link PanelAbstract Panel} to capture the arguments for an action
@@ -218,8 +222,8 @@ public class ActionParametersFormPanel extends PanelAbstract<ActionModel> {
         private void applyAreYouSure(AjaxButton button) {
             ActionModel actionModel = getActionModel();
             final ObjectAction action = actionModel.getActionMemento().getAction();
-            ActionSemantics.Of semantics = action.getSemantics();
-            if (semantics == ActionSemantics.Of.IDEMPOTENT_ARE_YOU_SURE || semantics == ActionSemantics.Of.NON_IDEMPOTENT_ARE_YOU_SURE) {
+            SemanticsOf semanticsOf = SemanticsOf.from(action.getSemantics());
+            if (semanticsOf.isAreYouSure()) {
                 ConfirmationConfig confirmationConfig = new ConfirmationConfig();
                 // TODO ISIS-1007 Use i18n for the title and the labels
                 confirmationConfig.withTitle("Are you sure?");

http://git-wip-us.apache.org/repos/asf/isis/blob/dbcd0965/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/bulk/BulkActionsLinkFactory.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/bulk/BulkActionsLinkFactory.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/bulk/BulkActionsLinkFactory.java
index d5aebc7..de42f3f 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/bulk/BulkActionsLinkFactory.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/bulk/BulkActionsLinkFactory.java
@@ -26,6 +26,8 @@ import org.apache.wicket.Session;
 import org.apache.wicket.markup.html.link.AbstractLink;
 import org.apache.wicket.markup.html.link.Link;
 import org.apache.isis.applib.RecoverableException;
+import org.apache.isis.applib.annotation.ActionSemantics;
+import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.applib.services.actinvoc.ActionInvocationContext;
 import org.apache.isis.applib.annotation.ActionLayout;
 import org.apache.isis.applib.annotation.Bulk;
@@ -217,8 +219,10 @@ public final class BulkActionsLinkFactory implements ActionLinkFactory {
         final String cssClassFa = ObjectAction.Utils.cssClassFaFor(objectAction);
         final CssClassFaPosition cssClassFaPosition = ObjectAction.Utils.cssClassFaPositionFor(objectAction);
         final ActionLayout.Position position = ObjectAction.Utils.actionLayoutPositionOf(objectAction);
+        final ActionSemantics.Of semantics = objectAction.getSemantics();
 
-        return new LinkAndLabel(link, objectAction.getName(), null, description, false, explorationOrPrototype, actionIdentifier, cssClass, cssClassFa, cssClassFaPosition, position);
+        return new LinkAndLabel(link, objectAction.getName(), null, description, false, explorationOrPrototype, actionIdentifier, cssClass, cssClassFa, cssClassFaPosition, position,
+                SemanticsOf.from(semantics));
     }
 
 

http://git-wip-us.apache.org/repos/asf/isis/blob/dbcd0965/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/linkandlabel/ActionLinkFactoryAbstract.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/linkandlabel/ActionLinkFactoryAbstract.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/linkandlabel/ActionLinkFactoryAbstract.java
index d7f10d2..0608c7c 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/linkandlabel/ActionLinkFactoryAbstract.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/linkandlabel/ActionLinkFactoryAbstract.java
@@ -17,9 +17,19 @@
 
 package org.apache.isis.viewer.wicket.ui.components.widgets.linkandlabel;
 
+import org.apache.wicket.Application;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.attributes.AjaxRequestAttributes;
+import org.apache.wicket.ajax.markup.html.AjaxLink;
+import org.apache.wicket.markup.ComponentTag;
+import org.apache.wicket.markup.html.link.AbstractLink;
+import org.apache.wicket.request.IRequestHandler;
+
 import org.apache.isis.applib.annotation.ActionLayout;
-import org.apache.isis.core.metamodel.facets.members.cssclassfa.CssClassFaPosition;
+import org.apache.isis.applib.annotation.ActionSemantics;
+import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.facets.members.cssclassfa.CssClassFaPosition;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
 import org.apache.isis.viewer.wicket.model.links.LinkAndLabel;
 import org.apache.isis.viewer.wicket.model.models.ActionModel;
@@ -33,13 +43,6 @@ import org.apache.isis.viewer.wicket.ui.components.actions.ActionPanel;
 import org.apache.isis.viewer.wicket.ui.pages.PageClassRegistry;
 import org.apache.isis.viewer.wicket.ui.pages.PageClassRegistryAccessor;
 import org.apache.isis.viewer.wicket.ui.util.CssClassAppender;
-import org.apache.wicket.Application;
-import org.apache.wicket.ajax.AjaxRequestTarget;
-import org.apache.wicket.ajax.attributes.AjaxRequestAttributes;
-import org.apache.wicket.ajax.markup.html.AjaxLink;
-import org.apache.wicket.markup.ComponentTag;
-import org.apache.wicket.markup.html.link.AbstractLink;
-import org.apache.wicket.request.IRequestHandler;
 
 import de.agilecoders.wicket.core.markup.html.bootstrap.button.Buttons;
 
@@ -180,9 +183,10 @@ public abstract class ActionLinkFactoryAbstract implements ActionLinkFactory {
         final String cssClass = ObjectAction.Utils.cssClassFor(objectAction, objectAdapter);
         final String cssClassFa = ObjectAction.Utils.cssClassFaFor(objectAction);
         final CssClassFaPosition cssClassFaPosition = ObjectAction.Utils.cssClassFaPositionFor(objectAction);
+        final ActionSemantics.Of semantics = objectAction.getSemantics();
 
         return new LinkAndLabel(link, label, disabledReasonIfAny, description, blobOrClob, prototype, actionIdentifier,
-                cssClass, cssClassFa, cssClassFaPosition, position);
+                cssClass, cssClassFa, cssClassFaPosition, position, SemanticsOf.from(semantics));
     }
 
     // ////////////////////////////////////////////////////////////


[2/4] isis git commit: ISIS-1007 Upgrade Wicket-Bootstrap to 0.9.12 and Wicket Webjars to 0.4.7

Posted by da...@apache.org.
ISIS-1007 Upgrade Wicket-Bootstrap to 0.9.12 and Wicket Webjars to 0.4.7

Add code showing how to configure the confirmation dialog


Project: http://git-wip-us.apache.org/repos/asf/isis/repo
Commit: http://git-wip-us.apache.org/repos/asf/isis/commit/2104d743
Tree: http://git-wip-us.apache.org/repos/asf/isis/tree/2104d743
Diff: http://git-wip-us.apache.org/repos/asf/isis/diff/2104d743

Branch: refs/heads/master
Commit: 2104d743e2440211d28dfb5246bb6cc7fbe53bec
Parents: f5f0fc6
Author: Martin Tzvetanov Grigorov <mg...@apache.org>
Authored: Thu Aug 13 14:28:00 2015 +0300
Committer: Martin Tzvetanov Grigorov <mg...@apache.org>
Committed: Thu Aug 13 14:28:00 2015 +0300

----------------------------------------------------------------------
 core/pom.xml                                              |  4 ++--
 .../ui/components/actions/ActionParametersFormPanel.java  | 10 +++++++++-
 2 files changed, 11 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/isis/blob/2104d743/core/pom.xml
----------------------------------------------------------------------
diff --git a/core/pom.xml b/core/pom.xml
index dfcabcd..b8bd3ab 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -134,8 +134,8 @@
         <wicket.version>6.20.0</wicket.version>
         <wicketstuff.version>6.20.0</wicketstuff.version>
 
-        <wicket-webjars.version>0.4.6</wicket-webjars.version>
-        <wicket-bootstrap.version>0.9.12-SNAPSHOT</wicket-bootstrap.version>
+        <wicket-webjars.version>0.4.7</wicket-webjars.version>
+        <wicket-bootstrap.version>0.9.12</wicket-bootstrap.version>
         <wicket-source.version>6.0.0.8</wicket-source.version>
 
         <wicket-select2.version>2.2.3</wicket-select2.version>

http://git-wip-us.apache.org/repos/asf/isis/blob/2104d743/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersFormPanel.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersFormPanel.java b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersFormPanel.java
index 626c5f8..f20fbe7 100644
--- a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersFormPanel.java
+++ b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersFormPanel.java
@@ -20,6 +20,7 @@
 package org.apache.isis.viewer.wicket.ui.components.actions;
 
 import de.agilecoders.wicket.extensions.markup.html.bootstrap.confirmation.ConfirmationBehavior;
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.confirmation.ConfirmationConfig;
 
 import java.util.List;
 import com.google.common.collect.Lists;
@@ -219,7 +220,14 @@ public class ActionParametersFormPanel extends PanelAbstract<ActionModel> {
             final ObjectAction action = actionModel.getActionMemento().getAction();
             ActionSemantics.Of semantics = action.getSemantics();
             if (semantics == ActionSemantics.Of.IDEMPOTENT_ARE_YOU_SURE || semantics == ActionSemantics.Of.NON_IDEMPOTENT_ARE_YOU_SURE) {
-                button.add(new ConfirmationBehavior());
+                ConfirmationConfig confirmationConfig = new ConfirmationConfig();
+                // TODO ISIS-1007 Use i18n for the title and the labels
+                confirmationConfig.withTitle("Are you sure?");
+                confirmationConfig.withBtnOkLabel("Confirm");
+                confirmationConfig.withBtnCancelLabel("Cancel");
+                confirmationConfig.withBtnOkClass("btn btn-danger");
+                confirmationConfig.withBtnCancelClass("btn btn-default");
+                button.add(new ConfirmationBehavior(confirmationConfig));
             }
         }