http://git-wip-us.apache.org/repos/asf/isis/blob/d84c6609/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionprompt/ActionPromptModalWindow.java
----------------------------------------------------------------------
diff --git 
a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionprompt/ActionPromptModalWindow.java
 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionprompt/ActionPromptModalWindow.java
new file mode 100644
index 0000000..0cdac52
--- /dev/null
+++ 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionprompt/ActionPromptModalWindow.java
@@ -0,0 +1,50 @@
+/**
+ *  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.actionprompt;
+
+import org.apache.isis.core.commons.config.IsisConfiguration;
+import org.apache.isis.core.runtime.system.context.IsisContext;
+import 
org.apache.isis.viewer.wicket.ui.components.widgets.bootstrap.ModalDialog;
+
+public class ActionPromptModalWindow extends ModalDialog<Void> {
+
+    private static final long serialVersionUID = 1L;
+
+    public static ActionPromptModalWindow 
getActionPromptModalWindowIfEnabled(ActionPromptModalWindow modalWindow) {
+        return !isActionPromptModalDialogDisabled() ? modalWindow : null;
+    }
+
+    public static boolean isActionPromptModalDialogDisabled() {
+        return 
getConfiguration().getBoolean("isis.viewer.wicket.disableModalDialogs", false);
+    }
+
+    private static IsisConfiguration getConfiguration() {
+        return IsisContext.getConfiguration();
+    }
+
+    public static ActionPromptModalWindow newModalWindow(String id) {
+        return new ActionPromptModalWindow(id);
+    }
+
+
+    // //////////////////////////////////////
+    
+    
+    public ActionPromptModalWindow(String id) {
+        super(id);
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/d84c6609/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionInfoPanel.html
----------------------------------------------------------------------
diff --git 
a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionInfoPanel.html
 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionInfoPanel.html
new file mode 100644
index 0000000..18935b9
--- /dev/null
+++ 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionInfoPanel.html
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
+<html xmlns="http://www.w3.org/1999/xhtml";  
+      
xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.4-strict.dtd";  
+      xml:lang="en"  
+      lang="en">
+    <body>
+        <wicket:panel>
+            <div class="actionInfoPanel actionInfoComponentType">
+                <ul>
+                    <li>Target: <span wicket:id="target">[target title 
here]</span></li>
+                    <li>Action: <span wicket:id="actionName">[target title 
here]</span></li>
+                </ul>
+                <br/>
+            </div>
+        </wicket:panel>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/isis/blob/d84c6609/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionInfoPanel.java
----------------------------------------------------------------------
diff --git 
a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionInfoPanel.java
 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionInfoPanel.java
new file mode 100644
index 0000000..11c1436
--- /dev/null
+++ 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionInfoPanel.java
@@ -0,0 +1,50 @@
+/*
+ *  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 org.apache.wicket.markup.html.basic.Label;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
+import org.apache.isis.viewer.wicket.model.models.ActionModel;
+import org.apache.isis.viewer.wicket.ui.panels.PanelAbstract;
+
+/**
+ * Renders a panel providing summary information about an action.
+ */
+public class ActionInfoPanel extends PanelAbstract<ActionModel> {
+
+    private static final long serialVersionUID = 1L;
+
+    private static final String ID_ACTION_NAME = "actionName";
+    private static final String ID_TARGET = "target";
+
+    public ActionInfoPanel(final String id, final ActionModel actionModel) {
+        super(id, actionModel);
+
+        final ObjectAdapter targetAdapter = getModel().getTargetAdapter();
+        final ObjectAction objectAction = 
getModel().getActionMemento().getAction();
+
+        // TODO: render instead as links (providing isn't a service; provide a
+        // component for this?)
+        add(new Label(ID_TARGET, targetAdapter.titleString()));
+        add(new Label(ID_ACTION_NAME, objectAction.getName()));
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/d84c6609/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionInfoPanelFactory.java
----------------------------------------------------------------------
diff --git 
a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionInfoPanelFactory.java
 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionInfoPanelFactory.java
new file mode 100644
index 0000000..ffc7094
--- /dev/null
+++ 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionInfoPanelFactory.java
@@ -0,0 +1,52 @@
+/*
+ *  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 org.apache.wicket.Component;
+import org.apache.wicket.model.IModel;
+
+import org.apache.isis.viewer.wicket.model.models.ActionModel;
+import org.apache.isis.viewer.wicket.ui.ComponentFactory;
+import org.apache.isis.viewer.wicket.ui.ComponentFactoryAbstract;
+import org.apache.isis.viewer.wicket.ui.ComponentType;
+
+/**
+ * {@link ComponentFactory} for {@link ActionInfoPanel}.
+ */
+public class ActionInfoPanelFactory extends ComponentFactoryAbstract {
+
+    private static final long serialVersionUID = 1L;
+
+    public ActionInfoPanelFactory() {
+        super(ComponentType.ACTION_INFO, ActionInfoPanel.class);
+    }
+
+    @Override
+    public ApplicationAdvice appliesTo(final IModel<?> model) {
+        return appliesIf(model instanceof ActionModel);
+    }
+
+    @Override
+    public Component createComponent(final String id, final IModel<?> model) {
+        final ActionModel actionModel = (ActionModel) model;
+        return new ActionInfoPanel(id, actionModel);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/d84c6609/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionPanel.html
----------------------------------------------------------------------
diff --git 
a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionPanel.html
 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionPanel.html
new file mode 100644
index 0000000..eb68afd
--- /dev/null
+++ 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionPanel.html
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
+<html xmlns="http://www.w3.org/1999/xhtml";  
+      
xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.4-strict.dtd";  
+      xml:lang="en"  
+      lang="en">
+    <body>
+        <wicket:panel>
+            <div class="actionPanel actionComponentType">
+                <div class="myBlockContainer">
+                    <div wicket:id="header" class="iconAndTitle panel 
panel-default actionPanelHeaderNew">
+                         <wicket:container 
wicket:id="entityIconAndTitle">[icon and title]</wicket:container>
+                         <h3 wicket:id="actionName" class="actionName">[action 
name]</h3>
+                    </div>
+                    <div wicket:id="parameters"></div>
+                </div>
+            </div>
+        </wicket:panel>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/isis/blob/d84c6609/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionPanel.java
----------------------------------------------------------------------
diff --git 
a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionPanel.java
 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionPanel.java
new file mode 100644
index 0000000..ad157b8
--- /dev/null
+++ 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionPanel.java
@@ -0,0 +1,341 @@
+/*
+ *  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.List;
+import com.google.common.base.Throwables;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.model.Model;
+import org.apache.isis.applib.services.command.Command;
+import org.apache.isis.applib.services.command.Command.Executor;
+import org.apache.isis.applib.services.command.CommandContext;
+import org.apache.isis.applib.services.exceprecog.ExceptionRecognizer;
+import org.apache.isis.applib.services.exceprecog.ExceptionRecognizerComposite;
+import org.apache.isis.core.commons.authentication.MessageBroker;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.adapter.version.ConcurrencyException;
+import org.apache.isis.core.metamodel.runtimecontext.ServicesInjector;
+import org.apache.isis.core.runtime.system.context.IsisContext;
+import org.apache.isis.core.runtime.system.transaction.IsisTransactionManager;
+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.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.ui.ComponentType;
+import org.apache.isis.viewer.wicket.ui.actionresponse.ActionResultResponse;
+import 
org.apache.isis.viewer.wicket.ui.actionresponse.ActionResultResponseHandlingStrategy;
+import 
org.apache.isis.viewer.wicket.ui.actionresponse.ActionResultResponseType;
+import org.apache.isis.viewer.wicket.ui.pages.BookmarkedPagesModelProvider;
+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>
+ * TODO: on results panel, have a button to resubmit?
+ */
+public class ActionPanel extends PanelAbstract<ActionModel> implements 
ActionExecutor {
+
+    private static final long serialVersionUID = 1L;
+
+    private static final String ID_HEADER = "header";
+
+    static final String ID_ACTION_NAME = "actionName";
+
+    private ActionPrompt actionPrompt;
+
+    /**
+     * 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 ActionPanel(final String id, final ActionModel actionModel) {
+        super(id, actionModel);
+        actionModel.setExecutor(this);
+        buildGui(actionModel);
+    }
+
+    /**
+     * Sets the owning action prompt (modal window), if any.
+     */
+    public void setActionPrompt(ActionPrompt actionPrompt) {
+        this.actionPrompt = actionPrompt;
+    }
+
+    @Override
+    protected void onConfigure() {
+        super.onConfigure();
+
+        buildGui(getModel());
+    }
+
+    private void buildGui(final ActionModel actionModel) {
+        if (actionModel.hasParameters()) {
+            buildGuiForParameters();
+        } else {
+
+            boolean succeeded = executeActionAndProcessResults(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 = 
actionModel.getTargetAdapter();
+
+                ActionResultResponse resultResponse = 
ActionResultResponseType.OBJECT.interpretResult(this.getActionModel(), 
targetAdapter, null);
+                resultResponse.getHandlingStrategy().handleResults(this, 
resultResponse);
+            }
+        }
+    }
+
+    ActionModel getActionModel() {
+        return super.getModel();
+    }
+
+    public ActionPanel setShowHeader(boolean showHeader) {
+        this.showHeader = showHeader;
+        return this;
+    }
+
+    private void buildGuiForParameters() {
+
+        WebMarkupContainer header = new WebMarkupContainer(ID_HEADER) {
+            @Override
+            protected void onConfigure() {
+                super.onConfigure();
+
+                setVisible(showHeader);
+            }
+        };
+        addOrReplace(header);
+
+        ObjectAdapter targetAdapter = null;
+        try {
+            targetAdapter = getActionModel().getTargetAdapter();
+            
+            getComponentFactoryRegistry().addOrReplaceComponent(this, 
ComponentType.PARAMETERS, getActionModel());
+            getComponentFactoryRegistry().addOrReplaceComponent(header, 
ComponentType.ENTITY_ICON_AND_TITLE, new EntityModel(targetAdapter));
+
+            final String actionName = 
getActionModel().getActionMemento().getAction().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(this, 
resultResponse);
+
+            getMessageBroker().addWarning(ex.getMessage());
+        }
+    }
+
+    protected void bookmarkPage(BookmarkableModel<?> model) {
+        getBookmarkedPagesModel().bookmarkPage(model);
+    }
+
+    private BookmarkedPagesModel getBookmarkedPagesModel() {
+        BookmarkedPagesModelProvider application = 
(BookmarkedPagesModelProvider) getSession();
+        return application.getBookmarkedPagesModel();
+    }
+
+    
+    /**
+     * @param feedbackForm - for feedback messages.
+     * @return 
+     */
+    @Override
+    public boolean executeActionAndProcessResults(AjaxRequestTarget target, 
Form<?> feedbackForm) {
+
+        permanentlyHide(ComponentType.ENTITY_ICON_AND_TITLE);
+
+        ObjectAdapter targetAdapter = null;
+        try {
+            targetAdapter = getModel().getTargetAdapter();
+
+            // no concurrency exception, so continue...
+            return executeActionOnTargetAndProcessResults(targetAdapter, 
target, feedbackForm);
+
+        } catch (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(this, 
resultResponse);
+
+            getMessageBroker().addWarning(ex.getMessage());
+            return false;
+        }
+    }
+
+    /**
+     * @param target 
+     * @return whether to clear args or not (they aren't if there was a 
validation exception)
+     */
+    private boolean executeActionOnTargetAndProcessResults(
+            final ObjectAdapter targetAdapter, 
+            final AjaxRequestTarget target, 
+            final Form<?> feedbackForm) {
+        
+        final ActionModel actionModel = getActionModel();
+        
+        // validate the action parameters (if any)
+        final String invalidReasonIfAny = actionModel.getReasonInvalidIfAny();
+        
+        if (invalidReasonIfAny != null) {
+            raiseWarning(target, feedbackForm, invalidReasonIfAny);
+            return false;
+        }
+        
+        final CommandContext commandContext = 
getServicesInjector().lookupService(CommandContext.class);
+        final Command command;
+        if (commandContext != null) {
+            command = commandContext.getCommand();
+            command.setExecutor(Executor.USER);
+        } else {
+            command = null;
+        }
+        
+        
+        // the object store could raise an exception (eg uniqueness constraint)
+        // so we handle it here.
+        try {
+            // could be programmatic flushing, so must include in the try... 
finally
+            final ObjectAdapter resultAdapter = 
getActionModel().executeHandlingApplicationExceptions();
+      
+            // flush any queued changes, so concurrency or violation 
exceptions (if any)
+            // will be thrown here
+            getTransactionManager().flushTransaction();
+            
+            ActionResultResponse resultResponse = 
ActionResultResponseType.determineAndInterpretResult(this.getActionModel(), 
target, resultAdapter);
+            resultResponse.getHandlingStrategy().handleResults(this, 
resultResponse);
+
+            if (actionModel.isBookmarkable()) {
+                bookmarkPage(actionModel);
+            }
+            
+            if(actionPrompt != null) {
+                actionPrompt.closePrompt(target);
+                // cos will be reused next time, so mustn't cache em.
+                actionModel.clearArguments();
+            }
+
+            return true;
+
+        } catch (RuntimeException ex) {
+
+            String message = recognizeException(ex, target, feedbackForm);
+            
+            if (message != null) {
+                // no need to add to message broker, should already have been 
added...
+                
+                if(feedbackForm == null) {
+                    // forward on instead to void page
+                    // (otherwise, we'll have rendered an action parameters 
page 
+                    // and so we'll be staying on that page)
+                    
ActionResultResponseHandlingStrategy.REDIRECT_TO_VOID.handleResults(this, null);
+                }
+
+                return false;
+            }
+            
+            // not handled, so capture and propagate
+            if(command != null) {
+                command.setException(Throwables.getStackTraceAsString(ex));
+            }
+
+            throw ex;
+        }
+    }
+
+
+    private String recognizeException(RuntimeException ex, AjaxRequestTarget 
target, Form<?> feedbackForm) {
+        
+        // REVIEW: this code is similar to stuff in EntityPropertiesForm, 
perhaps move up to superclass?
+        // REVIEW: similar code also in WebRequestCycleForIsis; combine?
+        
+        // see if the exception is recognized as being a non-serious error
+        // (nb: similar code in WebRequestCycleForIsis, as a fallback)
+        List<ExceptionRecognizer> exceptionRecognizers = 
getServicesInjector().lookupServices(ExceptionRecognizer.class);
+        String recognizedErrorIfAny = new 
ExceptionRecognizerComposite(exceptionRecognizers).recognize(ex);
+        if(recognizedErrorIfAny != null) {
+
+            // recognized
+            raiseWarning(target, feedbackForm, recognizedErrorIfAny);
+
+            getTransactionManager().getTransaction().clearAbortCause();
+            
+            // there's no need to abort the transaction, it will have already 
been done
+            // (in IsisTransactionManager#executeWithinTransaction(...)).
+        }
+        return recognizedErrorIfAny;
+    }
+
+    public void raiseWarning(AjaxRequestTarget target, Form<?> feedbackForm, 
String error) {
+        if(target != null && feedbackForm != null) {
+            target.add(feedbackForm);
+            feedbackForm.error(error);
+        } else {
+            getMessageBroker().addWarning(error);
+        }
+    }
+
+
+    ///////////////////////////////////////////////////////
+    // Dependencies (from context)
+    ///////////////////////////////////////////////////////
+    
+    protected IsisTransactionManager getTransactionManager() {
+        return IsisContext.getTransactionManager();
+    }
+
+    protected ServicesInjector getServicesInjector() {
+        return IsisContext.getPersistenceSession().getServicesInjector();
+    }
+
+    protected MessageBroker getMessageBroker() {
+        return getAuthenticationSession().getMessageBroker();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/d84c6609/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionPanelFactory.java
----------------------------------------------------------------------
diff --git 
a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionPanelFactory.java
 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionPanelFactory.java
new file mode 100644
index 0000000..30af5e9
--- /dev/null
+++ 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionPanelFactory.java
@@ -0,0 +1,52 @@
+/*
+ *  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 org.apache.wicket.Component;
+import org.apache.wicket.model.IModel;
+
+import org.apache.isis.viewer.wicket.model.models.ActionModel;
+import org.apache.isis.viewer.wicket.ui.ComponentFactory;
+import org.apache.isis.viewer.wicket.ui.ComponentFactoryAbstract;
+import org.apache.isis.viewer.wicket.ui.ComponentType;
+
+/**
+ * {@link ComponentFactory} for {@link ActionPanel}.
+ */
+public class ActionPanelFactory extends ComponentFactoryAbstract {
+
+    private static final long serialVersionUID = 1L;
+
+    public ActionPanelFactory() {
+        super(ComponentType.ACTION_PROMPT, ActionPanel.class);
+    }
+
+    @Override
+    public ApplicationAdvice appliesTo(final IModel<?> model) {
+        return appliesIf(model instanceof ActionModel);
+    }
+
+    @Override
+    public Component createComponent(final String id, final IModel<?> model) {
+        final ActionModel actionModel = (ActionModel) model;
+        return new ActionPanel(id, actionModel);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/d84c6609/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersFormPanel.html
----------------------------------------------------------------------
diff --git 
a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersFormPanel.html
 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersFormPanel.html
new file mode 100644
index 0000000..05eaaec
--- /dev/null
+++ 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersFormPanel.html
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<html xmlns:wicket="http://wicket.apache.org";>
+    <body>
+        <wicket:panel>
+            <div class="actionParametersForm inputForm">
+                <form wicket:id="inputForm" method="post" 
class="form-horizontal actionParametersForm">
+                    <fieldset class="inputFormTable parameters">
+                        <div wicket:id="parameters" class="parameter">
+                          <div wicket:id="scalarNameAndValue">[scalar]</div>
+                        </div>
+                        <span wicket:id="feedback"></span>
+                        <div class="buttons">
+                            <input type="submit" wicket:id="okButton" 
value="OK" class="ok btn btn-sm btn-primary"/>
+                            <input type="submit" wicket:id="cancelButton" 
value="Cancel" class="cancel btn btn-sm btn-default"/>
+                        </div>
+                    </fieldset>
+                </form>
+            </div>
+        </wicket:panel>
+    </body>
+</html>
+
+
+

http://git-wip-us.apache.org/repos/asf/isis/blob/d84c6609/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
new file mode 100644
index 0000000..8416491
--- /dev/null
+++ 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersFormPanel.java
@@ -0,0 +1,260 @@
+/*
+ *  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.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.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);
+            
+            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);
+            }
+        }
+
+        @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/d84c6609/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersFormPanel.properties
----------------------------------------------------------------------
diff --git 
a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersFormPanel.properties
 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersFormPanel.properties
new file mode 100644
index 0000000..71edcd9
--- /dev/null
+++ 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersFormPanel.properties
@@ -0,0 +1,22 @@
+#
+#  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.
+#
+
+okLabel=OK
+cancelLabel=Cancel
+editLabel=Edit

http://git-wip-us.apache.org/repos/asf/isis/blob/d84c6609/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersFormPanelFactory.java
----------------------------------------------------------------------
diff --git 
a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersFormPanelFactory.java
 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersFormPanelFactory.java
new file mode 100644
index 0000000..bb515d0
--- /dev/null
+++ 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersFormPanelFactory.java
@@ -0,0 +1,53 @@
+/*
+ *  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 org.apache.wicket.Component;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.request.resource.CssResourceReference;
+
+import org.apache.isis.viewer.wicket.model.models.ActionModel;
+import org.apache.isis.viewer.wicket.ui.ComponentFactory;
+import org.apache.isis.viewer.wicket.ui.ComponentFactoryAbstract;
+import org.apache.isis.viewer.wicket.ui.ComponentType;
+
+/**
+ * {@link ComponentFactory} for {@link ActionParametersFormPanel}.
+ */
+public class ActionParametersFormPanelFactory extends ComponentFactoryAbstract 
{
+
+    private static final long serialVersionUID = 1L;
+
+    public ActionParametersFormPanelFactory() {
+        super(ComponentType.PARAMETERS, ActionParametersFormPanel.class);
+    }
+
+    @Override
+    public ApplicationAdvice appliesTo(final IModel<?> model) {
+        return appliesIf(model instanceof ActionModel);
+    }
+
+    @Override
+    public Component createComponent(final String id, final IModel<?> model) {
+        final ActionModel actionModel = (ActionModel) model;
+        return new ActionParametersFormPanel(id, actionModel);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/d84c6609/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/bookmarkedpages/BookmarkedPagesPanel.css
----------------------------------------------------------------------
diff --git 
a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/bookmarkedpages/BookmarkedPagesPanel.css
 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/bookmarkedpages/BookmarkedPagesPanel.css
new file mode 100644
index 0000000..8a37d5e
--- /dev/null
+++ 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/bookmarkedpages/BookmarkedPagesPanel.css
@@ -0,0 +1,111 @@
+/*
+ *  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.
+ */
+
+.bookmarkedPagesPanel .bookmarkedPagesPanelHolder .bookmarkedPagesList {
+    margin-top:30px;
+}
+
+.bookmarkedPagesPanel .bookmarkedPagesPanelHolder .bookmarkedPageItem {
+    margin-top:10px;
+    margin-bottom:10px;
+}
+
+.bookmarkedPagesPanel .bookmarkedPagesPanelHolder 
span.bookmarkedPageImageAndTitle:hover {
+    background-color:#FFFFFF;
+}
+
+.bookmarkedPagesPanel .bookmarkedPagesList a img.bookmarkedPageImage {
+    background-color: transparent;
+    width: 16px;
+    height: 16px;
+}
+
+.bookmarkedPagesPanel .bookmarkRibbon {
+    width:10px;
+    margin-top: -120px;
+    height:120px;
+    position:fixed;
+    left:0;
+    top:120px;
+    display:block;
+    cursor:pointer;
+    z-index: 1997;
+    margin-left: 1px;
+    font-weight:bold;
+    border-right:6px solid transparent;
+    border-left:6px solid transparent;
+    border-bottom:6px solid white;
+}
+
+.bookmarkedPagesPanel #bookmarkedPagesSlidingDiv {
+    position:fixed;
+    left:0;
+    top:0;
+    width:500px;
+    z-index: 1998;
+    display: none;
+}
+
+.bookmarkedPagesPanel #bookmarkedPagesSlidingDiv .content {
+    width:490px;
+}
+
+.content>.list-group .list-group-item:first-child {
+    border-top-right-radius: 0;
+    border-top-left-radius: 0;
+}
+.content>.list-group .list-group-item {
+    border-width: 0 0;
+}
+.content>.list-group {
+    margin-bottom: 0;
+}
+.content .list-group-item {
+    border-radius:0;
+    /*background-color: inherit;*/
+}
+
+.content .list-group .list-group {
+    margin: 10px 0 0;
+}
+.content .list-group-item li.list-group-item {margin: 0 -15px;border-top: 1px 
solid #ddd;border-bottom: 0;padding-left: 30px;}
+.content .list-group-item li.list-group-item:last-child {padding-bottom: 0;}
+
+.content div.list-group div.list-group{margin: 0;}
+.content div.list-group .list-group a.list-group-item {border-top: 1px solid 
#ddd;border-bottom: 0;padding-left: 30px;}
+#bookmarkedPagesSlidingDiv .panel-group {margin-bottom: 0}
+
+
+ul#bookmarkedPages li.list-group-item.bookmarkDepth0 {
+}
+ul#bookmarkedPages li.list-group-item.bookmarkDepth1 {
+    margin-left: 20px;
+}
+ul#bookmarkedPages li.list-group-item.bookmarkDepth2 {
+    margin-left: 40px;
+}
+ul#bookmarkedPages li.list-group-item.bookmarkDepth3 {
+    margin-left: 60px;
+}
+ul#bookmarkedPages li.list-group-item.bookmarkDepth4 {
+    margin-left: 80px;
+}
+ul#bookmarkedPages li.list-group-item.bookmarkDepth5 {
+    margin-left: 100px;
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/d84c6609/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/bookmarkedpages/BookmarkedPagesPanel.html
----------------------------------------------------------------------
diff --git 
a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/bookmarkedpages/BookmarkedPagesPanel.html
 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/bookmarkedpages/BookmarkedPagesPanel.html
new file mode 100644
index 0000000..8df8ad7
--- /dev/null
+++ 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/bookmarkedpages/BookmarkedPagesPanel.html
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
+<html xmlns="http://www.w3.org/1999/xhtml";  
+      
xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.4-strict.dtd";  
+      xml:lang="en"  
+      lang="en">
+       <body>
+        <wicket:panel>
+            <div class="bookmarkedPagesPanel">
+                <div class="showPanelTab bookmarkRibbon navbar-inverse"></div>
+                <div id="bookmarkedPagesSlidingDiv">
+                    <div class="row">
+                        <div class="col-xs-12">
+                            <div class="panel-group">
+                                <div class="panel panel-default">
+                                    <div class="panel-heading">
+                                        <h4 class="panel-title"><span 
class="fa fa-fw fa-bookmark"></span> Bookmarks</h4>
+                                    </div>
+
+                                    <div class="panel-body" 
wicket:enclosure="helpText">
+                                        <span wicket:id="helpText"></span>
+                                    </div>
+
+                                    <ul id="bookmarkedPages" 
class="list-group">
+                                        <li class="list-group-item">
+                                            <ul wicket:id="bookmarkList" 
class="bookmarkedPagesList list-group">
+                                                <li 
wicket:id="bookmarkedPageItem" class="bookmarkedPageItem list-group-item">
+                                                    <div>
+                                                        <a href="#" 
wicket:id="bookmarkedPageLink">
+                                                            <div 
class="bookmarkedPageImageAndTitle" style="display: inline-block">
+                                                                <img 
wicket:id="bookmarkedPageImage" class="bookmarkedPageImage"/>
+                                                                <span 
wicket:id="bookmarkedPageTitle" class="bookmarkedPageTitle">[link title]</span>
+                                                            </div>
+                                                        </a>
+
+                                                        <a href="#" 
wicket:id="clearBookmarkLink" class="clearBookmarkPlaceHolder pull-right">
+                                                            <span class="fa 
fa-fw fa-ban text-danger"></span>
+                                                        </a>
+                                                    </div>
+                                                </li>
+                                            </ul>
+                                        </li>
+                                    </ul>
+
+                                    <div class="panel-footer" 
wicket:enclosure="clearBookmarks">
+                                        <span>&nbsp;</span>
+                                        <a href="#" wicket:id="clearBookmarks" 
class="clearBookmarksA btn-link pull-right" title="Clear Bookmarks">
+                                            <span class="text-danger">Clear 
all</span>
+                                        </a>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </wicket:panel>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/isis/blob/d84c6609/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/bookmarkedpages/BookmarkedPagesPanel.java
----------------------------------------------------------------------
diff --git 
a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/bookmarkedpages/BookmarkedPagesPanel.java
 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/bookmarkedpages/BookmarkedPagesPanel.java
new file mode 100644
index 0000000..c9429b5
--- /dev/null
+++ 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/bookmarkedpages/BookmarkedPagesPanel.java
@@ -0,0 +1,239 @@
+/*
+ *  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.bookmarkedpages;
+
+import org.apache.isis.core.metamodel.adapter.oid.RootOid;
+import org.apache.isis.core.metamodel.spec.ObjectSpecId;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.metamodel.spec.SpecificationLoaderSpi;
+import org.apache.isis.core.runtime.persistence.ObjectNotFoundException;
+import org.apache.isis.core.runtime.system.context.IsisContext;
+import org.apache.isis.viewer.wicket.model.models.BookmarkTreeNode;
+import org.apache.isis.viewer.wicket.model.models.BookmarkedPagesModel;
+import org.apache.isis.viewer.wicket.model.models.ImageResourceCache;
+import org.apache.isis.viewer.wicket.model.models.PageType;
+import org.apache.isis.viewer.wicket.ui.pages.PageClassRegistry;
+import org.apache.isis.viewer.wicket.ui.panels.PanelAbstract;
+import org.apache.isis.viewer.wicket.ui.util.CssClassAppender;
+import org.apache.isis.viewer.wicket.ui.util.Links;
+import org.apache.wicket.Component;
+import org.apache.wicket.Page;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.AjaxLink;
+import org.apache.wicket.markup.head.CssHeaderItem;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.JavaScriptReferenceHeaderItem;
+import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.image.Image;
+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.wicket.model.AbstractReadOnlyModel;
+import org.apache.wicket.model.IModel;
+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.ResourceReference;
+import org.apache.wicket.util.string.Strings;
+
+import com.google.inject.Inject;
+
+public class BookmarkedPagesPanel extends PanelAbstract<BookmarkedPagesModel> {
+
+    private static final long serialVersionUID = 1L;
+    
+    private static final String ID_BOOKMARK_LIST = "bookmarkList";
+    private static final String ID_BOOKMARKS_HELP_TEXT = "helpText";
+    private static final String ID_BOOKMARKED_PAGE_LINK = "bookmarkedPageLink";
+    private static final String ID_CLEAR_BOOKMARK_LINK = "clearBookmarkLink";
+    private static final String ID_BOOKMARKED_PAGE_ITEM = "bookmarkedPageItem";
+    private static final String ID_BOOKMARKED_PAGE_TITLE = 
"bookmarkedPageTitle";
+    
+    private static final String ID_BOOKMARKED_PAGE_ICON = 
"bookmarkedPageImage";
+    
+    private static final String CLEAR_BOOKMARKS = "clearBookmarks";
+
+
+    private static final JavaScriptResourceReference SLIDE_PANEL_JS = new 
JavaScriptResourceReference(BookmarkedPagesPanel.class, "slide-panel.js");
+
+    @Inject
+    private PageClassRegistry pageClassRegistry;
+
+    public BookmarkedPagesPanel(final String id, final BookmarkedPagesModel 
bookmarkedPagesModel) {
+        super(id, bookmarkedPagesModel);
+        buildGui();
+    }
+
+    @Override
+    public void renderHead(IHeaderResponse response) {
+        super.renderHead(response);
+
+        
response.render(OnDomReadyHeaderItem.forScript("$('.bookmarkRibbon').height($('.navbar.navbar-fixed-top').height()-5);"));
+    }
+
+    private void buildGui() {
+
+        final BookmarkedPagesModel bookmarkedPagesModel = getModel();
+
+        Component helpText = addHelpText(bookmarkedPagesModel);
+        addOrReplace(helpText);
+
+        final WebMarkupContainer container = new 
WebMarkupContainer(ID_BOOKMARK_LIST) {
+            private static final long serialVersionUID = 1L;
+            @Override
+            public void renderHead(IHeaderResponse response) {
+                response.render(CssHeaderItem.forReference(new 
CssResourceReference(BookmarkedPagesPanel.class, "BookmarkedPagesPanel.css")));
+                
response.render(JavaScriptReferenceHeaderItem.forReference(SLIDE_PANEL_JS));
+            }
+        };
+        // allow to be updated by AjaxLink
+        container.setOutputMarkupId(true); 
+        add(container);
+
+        final AjaxLink<Void> clearAllBookmarksLink = new 
AjaxLink<Void>(CLEAR_BOOKMARKS){
+
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            public void onClick(AjaxRequestTarget target) {
+                BookmarkedPagesPanel.this.getModel().clear();
+                setEnabled(false);
+                target.add(container, this);
+            }
+        };
+        clearAllBookmarksLink.setOutputMarkupId(true);
+        add(clearAllBookmarksLink);
+        clearAllBookmarksLink.setOutputMarkupId(true);
+
+        if(getModel().isEmpty()) {
+            clearAllBookmarksLink.setVisible(false);
+        }
+
+
+        final ListView<BookmarkTreeNode> listView = new 
ListView<BookmarkTreeNode>(ID_BOOKMARKED_PAGE_ITEM, bookmarkedPagesModel) {
+
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            protected void populateItem(ListItem<BookmarkTreeNode> item) {
+                final BookmarkTreeNode node = item.getModelObject();
+                try {
+                    final PageType pageType = node.getPageType();
+                    final Class<? extends Page> pageClass = 
pageClassRegistry.getPageClass(pageType);
+
+                    final AjaxLink<Object> clearBookmarkLink = new 
AjaxLink<Object>(ID_CLEAR_BOOKMARK_LINK) {
+
+                        private static final long serialVersionUID = 1L;
+                        
+                        @Override
+                        public void onClick(AjaxRequestTarget target) {
+                            bookmarkedPagesModel.remove(node);
+                            if(bookmarkedPagesModel.isEmpty()) {
+                                permanentlyHide(CLEAR_BOOKMARKS);
+                            }
+                            target.add(container, clearAllBookmarksLink);
+                        }
+                        
+                    };
+                    if(node.getDepth() == 0) {
+                        clearBookmarkLink.add(new 
CssClassAppender("clearBookmark"));
+                    } else {
+                        clearBookmarkLink.setEnabled(true);
+                    }
+                    item.add(clearBookmarkLink);
+                    
+                    PageParameters pageParameters = node.getPageParameters();
+                    final AbstractLink link = 
Links.newBookmarkablePageLink(ID_BOOKMARKED_PAGE_LINK, pageParameters, 
pageClass);
+
+                    ObjectSpecification objectSpec = null;
+                    RootOid oid = node.getOidNoVer();
+                    if(oid != null) {
+                        ObjectSpecId objectSpecId = oid.getObjectSpecId();
+                        objectSpec = 
getSpecificationLoader().lookupBySpecId(objectSpecId);
+                    }
+                    final ResourceReference imageResource = 
imageCache.resourceReferenceForSpec(objectSpec);
+                    final Image image = new Image(ID_BOOKMARKED_PAGE_ICON, 
imageResource) {
+                        private static final long serialVersionUID = 1L;
+                        @Override
+                        protected boolean shouldAddAntiCacheParameter() {
+                            return false;
+                        }
+                    };
+                    link.addOrReplace(image);
+
+                    String title = node.getTitle();
+                    final Label label = new Label(ID_BOOKMARKED_PAGE_TITLE, 
title);
+                    link.add(label);
+                    item.add(link);
+                    if(bookmarkedPagesModel.isCurrent(pageParameters)) {
+                        item.add(new CssClassAppender("disabled"));
+                    }
+                    item.add(new CssClassAppender("bookmarkDepth" + 
node.getDepth()));
+                } catch(ObjectNotFoundException ex) {
+                    // ignore
+                    // this is a partial fix for an infinite redirect loop.
+                    // should be a bit smarter here, though; see ISIS-596.
+                }
+
+            }
+        };
+        container.add(listView);
+    }
+
+    protected Component addHelpText(final BookmarkedPagesModel 
bookmarkedPagesModel) {
+
+        IModel<String> helpTextModel = new AbstractReadOnlyModel<String>() {
+            @Override
+            public String getObject() {
+                return bookmarkedPagesModel.isEmpty() ? "You have no 
bookmarks!" : "";
+            }
+        };
+
+        Label helpText = new Label(ID_BOOKMARKS_HELP_TEXT, helpTextModel) {
+            @Override
+            protected void onConfigure() {
+                super.onConfigure();
+
+                setVisible(!Strings.isEmpty(getDefaultModelObjectAsString()));
+            }
+        };
+        helpText.setOutputMarkupPlaceholderTag(true);
+        return helpText;
+    }
+
+    // ///////////////////////////////////////////////
+    // Dependency Injection
+    // ///////////////////////////////////////////////
+
+    @Inject
+    private ImageResourceCache imageCache;
+
+    protected ImageResourceCache getImageCache() {
+        return imageCache;
+    }
+
+    
+    protected SpecificationLoaderSpi getSpecificationLoader() {
+        return IsisContext.getSpecificationLoader();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/d84c6609/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/bookmarkedpages/BookmarkedPagesPanelFactory.java
----------------------------------------------------------------------
diff --git 
a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/bookmarkedpages/BookmarkedPagesPanelFactory.java
 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/bookmarkedpages/BookmarkedPagesPanelFactory.java
new file mode 100644
index 0000000..06378bc
--- /dev/null
+++ 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/bookmarkedpages/BookmarkedPagesPanelFactory.java
@@ -0,0 +1,46 @@
+/*
+ *  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.bookmarkedpages;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.model.IModel;
+import org.apache.isis.viewer.wicket.model.models.BookmarkedPagesModel;
+import org.apache.isis.viewer.wicket.ui.ComponentFactoryAbstract;
+import org.apache.isis.viewer.wicket.ui.ComponentType;
+
+public class BookmarkedPagesPanelFactory extends ComponentFactoryAbstract {
+
+    private static final long serialVersionUID = 1L;
+
+    public BookmarkedPagesPanelFactory() {
+        super(ComponentType.BOOKMARKED_PAGES, BookmarkedPagesPanel.class);
+    }
+
+    @Override
+    public ApplicationAdvice appliesTo(final IModel<?> model) {
+        return appliesIf(model instanceof BookmarkedPagesModel);
+    }
+
+    @Override
+    public Component createComponent(final String id, final IModel<?> model) {
+        final BookmarkedPagesModel bookmarkedPagesModel = 
(BookmarkedPagesModel) model;
+        return new BookmarkedPagesPanel(id, bookmarkedPagesModel);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/d84c6609/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/bookmarkedpages/images/clear_bookmarks.png
----------------------------------------------------------------------
diff --git 
a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/bookmarkedpages/images/clear_bookmarks.png
 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/bookmarkedpages/images/clear_bookmarks.png
new file mode 100644
index 0000000..b798031
Binary files /dev/null and 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/bookmarkedpages/images/clear_bookmarks.png
 differ

http://git-wip-us.apache.org/repos/asf/isis/blob/d84c6609/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/bookmarkedpages/slide-panel.js
----------------------------------------------------------------------
diff --git 
a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/bookmarkedpages/slide-panel.js
 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/bookmarkedpages/slide-panel.js
new file mode 100644
index 0000000..ca5bbac
--- /dev/null
+++ 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/bookmarkedpages/slide-panel.js
@@ -0,0 +1,53 @@
+/*
+ *  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.
+ */
+$(function(){
+
+    'use strict';
+
+    var showBookmarks = function(){
+        $('#bookmarkedPagesSlidingDiv').slideDown('50');
+        $('.bookmarkRibbon').fadeOut(50);
+    };
+
+    var hideBookmarks = function(){
+        $('#bookmarkedPagesSlidingDiv').slideUp('50');
+        $('.bookmarkRibbon').fadeIn(50);
+     };
+
+     var hideBookmarksQuickly = function() {
+         $('#bookmarkedPagesSlidingDiv').hide();
+         $('.bookmarkRibbon').show();
+      };
+
+    $('.bookmarkRibbon').mouseenter(showBookmarks);
+    $('#bookmarkedPagesSlidingDiv').mouseleave(hideBookmarks);
+    
+    $('body').keydown(function(e) {
+        // alt+[
+        if(e.which === 219 && e.altKey) {
+            if($('#bookmarkedPagesSlidingDiv').is(":visible")) {
+                hideBookmarksQuickly();
+            } else {
+                showBookmarks();
+            }
+        } else if (e.which === 27) {
+            hideBookmarksQuickly();
+        }
+    });
+});

http://git-wip-us.apache.org/repos/asf/isis/blob/d84c6609/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/CollectionPanel.html
----------------------------------------------------------------------
diff --git 
a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/CollectionPanel.html
 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/CollectionPanel.html
new file mode 100644
index 0000000..569d0a6
--- /dev/null
+++ 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/CollectionPanel.html
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
+<html xmlns="http://www.w3.org/1999/xhtml";  
+      
xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.4-strict.dtd";  
+      xml:lang="en"  
+      lang="en">
+    <body>
+        <wicket:panel>
+            <div class="collectionPanel 
collectionNameAndContentsComponentType">
+                <div class="collectionContents" 
wicket:id="collectionContents"></div>
+                <span wicket:id="feedback"></span>
+            </div>
+        </wicket:panel>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/isis/blob/d84c6609/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/CollectionPanel.java
----------------------------------------------------------------------
diff --git 
a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/CollectionPanel.java
 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/CollectionPanel.java
new file mode 100644
index 0000000..1ae3b3b
--- /dev/null
+++ 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/CollectionPanel.java
@@ -0,0 +1,130 @@
+/*
+ *  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.collection;
+
+import 
de.agilecoders.wicket.core.markup.html.bootstrap.common.NotificationPanel;
+
+import java.util.List;
+import com.google.common.collect.Lists;
+import org.apache.wicket.Component;
+import org.apache.wicket.feedback.ComponentFeedbackMessageFilter;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
+import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
+import org.apache.isis.core.runtime.system.DeploymentType;
+import org.apache.isis.core.runtime.system.context.IsisContext;
+import org.apache.isis.viewer.wicket.model.links.LinkAndLabel;
+import org.apache.isis.viewer.wicket.model.models.EntityCollectionModel;
+import org.apache.isis.viewer.wicket.model.models.EntityModel;
+import org.apache.isis.viewer.wicket.ui.ComponentType;
+import 
org.apache.isis.viewer.wicket.ui.components.actionmenu.entityactions.EntityActionUtil;
+import 
org.apache.isis.viewer.wicket.ui.components.collection.selector.CollectionSelectorPanel;
+import 
org.apache.isis.viewer.wicket.ui.components.collection.selector.CollectionSelectorProvider;
+import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarPanelAbstract;
+import org.apache.isis.viewer.wicket.ui.panels.PanelAbstract;
+
+/**
+ * Panel for rendering entity collection; analogous to (any concrete subclass
+ * of) {@link ScalarPanelAbstract}.
+ */
+public class CollectionPanel extends PanelAbstract<EntityCollectionModel> 
implements CollectionSelectorProvider {
+
+
+    private static final long serialVersionUID = 1L;
+
+    private static final String ID_FEEDBACK = "feedback";
+
+    private Component collectionContents;
+
+    private Label label;
+
+    public CollectionPanel(final String id, final EntityModel entityModel, 
OneToManyAssociation otma) {
+        this(id, newEntityCollectionModel(entityModel, otma), entityModel, 
otma);
+    }
+
+    private static EntityCollectionModel newEntityCollectionModel(final 
EntityModel entityModel, OneToManyAssociation otma) {
+        EntityCollectionModel collectionModel = 
EntityCollectionModel.createParented(entityModel, otma);
+        return collectionModel;
+    }
+
+    CollectionPanel(
+            final String id,
+            final EntityCollectionModel collectionModel) {
+        this(id, collectionModel, new 
EntityModel(collectionModel.getParentObjectAdapterMemento()), 
collectionModel.getCollectionMemento().getCollection());
+    }
+
+    CollectionPanel(
+            final String id,
+            final EntityCollectionModel collectionModel,
+            final EntityModel entityModel,
+            final OneToManyAssociation otma) {
+        super(id, collectionModel);
+
+        final List<LinkAndLabel> entityActionLinks = Lists.newArrayList();
+
+        final List<ObjectAction> associatedActions = 
EntityActionUtil.getObjectActionsForAssociation(entityModel, otma, 
getDeploymentType());
+
+        
entityActionLinks.addAll(EntityActionUtil.asLinkAndLabelsForAdditionalLinksPanel(entityModel,
 associatedActions));
+
+        collectionModel.addEntityActions(entityActionLinks);
+    }
+
+    @Override
+    protected void onInitialize() {
+        super.onInitialize();
+        buildGui();
+    }
+
+    private void buildGui() {
+        collectionContents = 
getComponentFactoryRegistry().addOrReplaceComponent(this, 
ComponentType.COLLECTION_CONTENTS, getModel());
+
+        addOrReplace(new NotificationPanel(ID_FEEDBACK, collectionContents, 
new ComponentFeedbackMessageFilter(collectionContents)));
+    }
+
+    public Label createLabel(final String id, final String collectionName) {
+        this.label = new Label(id, collectionName);
+        label.setOutputMarkupId(true);
+        return this.label;
+    }
+
+    //region > SelectorDropdownPanel (impl)
+
+    private CollectionSelectorPanel selectorDropdownPanel;
+
+    @Override
+    public CollectionSelectorPanel getSelectorDropdownPanel() {
+        return selectorDropdownPanel;
+    }
+    public void setSelectorDropdownPanel(CollectionSelectorPanel 
selectorDropdownPanel) {
+        this.selectorDropdownPanel = selectorDropdownPanel;
+    }
+    //endregion
+
+
+
+    //region > dependencies
+
+    protected DeploymentType getDeploymentType() {
+        return IsisContext.getDeploymentType();
+    }
+
+    //endregion
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/d84c6609/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/CollectionPanelFactory.java
----------------------------------------------------------------------
diff --git 
a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/CollectionPanelFactory.java
 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/CollectionPanelFactory.java
new file mode 100644
index 0000000..9a9f6e2
--- /dev/null
+++ 
b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/CollectionPanelFactory.java
@@ -0,0 +1,56 @@
+/*
+ *  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.collection;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.model.IModel;
+
+import org.apache.isis.viewer.wicket.model.models.EntityCollectionModel;
+import org.apache.isis.viewer.wicket.ui.ComponentFactory;
+import org.apache.isis.viewer.wicket.ui.ComponentFactoryAbstract;
+import org.apache.isis.viewer.wicket.ui.ComponentType;
+
+/**
+ * {@link ComponentFactory} for {@link CollectionPanel}.
+ */
+public class CollectionPanelFactory extends ComponentFactoryAbstract {
+
+    private static final long serialVersionUID = 1L;
+
+    private static final String NAME = "labelled";
+
+    public CollectionPanelFactory() {
+        super(ComponentType.COLLECTION_NAME_AND_CONTENTS, NAME, 
CollectionPanel.class);
+    }
+
+    @Override
+    public ApplicationAdvice appliesTo(final IModel<?> model) {
+        if (!(model instanceof EntityCollectionModel)) {
+            return ApplicationAdvice.DOES_NOT_APPLY;
+        }
+        return ApplicationAdvice.APPLIES;
+    }
+
+    @Override
+    public Component createComponent(final String id, final IModel<?> model) {
+        final EntityCollectionModel collectionModel = (EntityCollectionModel) 
model;
+        return new CollectionPanel(id, collectionModel);
+    }
+}

Reply via email to