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/09/03 14:32:35 UTC

[44/87] [abbrv] [partial] isis git commit: ISIS-1194: moving the wicket submodules to be direct children of core; removing the isis-viewer-wicket parent pom.

http://git-wip-us.apache.org/repos/asf/isis/blob/99094b7e/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/ComponentFactoryAbstract.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/ComponentFactoryAbstract.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/ComponentFactoryAbstract.java
new file mode 100644
index 0000000..e018a74
--- /dev/null
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/ComponentFactoryAbstract.java
@@ -0,0 +1,115 @@
+/*
+ *  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;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.request.resource.CssResourceReference;
+
+import org.apache.isis.viewer.wicket.ui.panels.PanelUtil;
+
+/**
+ * Adapter implementation for {@link ComponentFactory}.
+ */
+public abstract class ComponentFactoryAbstract implements ComponentFactory {
+
+    private static final long serialVersionUID = 1L;
+
+    private final ComponentType componentType;
+    private final String name;
+
+    private final Class<?> componentClass;
+
+    public ComponentFactoryAbstract(final ComponentType componentType) {
+        this(componentType, null, null);
+    }
+
+    public ComponentFactoryAbstract(final ComponentType componentType, final String name) {
+        this(componentType, name, null);
+    }
+    
+    public ComponentFactoryAbstract(final ComponentType componentType, @SuppressWarnings("rawtypes") Class componentClass) {
+        this(componentType, null, componentClass);
+    }
+
+    public ComponentFactoryAbstract(final ComponentType componentType, final String name, @SuppressWarnings("rawtypes") Class componentClass) {
+        this.componentType = componentType;
+        this.name = name != null ? name : getClass().getSimpleName();
+        if(componentClass != null && ComponentFactory.class.isAssignableFrom(componentClass)) {
+            throw new IllegalArgumentException("specified a ComponentFactory as a componentClass... you probably meant the component instead? componentClass = " + componentClass.getName());
+        }
+        this.componentClass = componentClass;
+    }
+
+    @Override
+    public ComponentType getComponentType() {
+        return componentType;
+    }
+
+    /**
+     * Applies if {@link #getComponentType()} matches; disregards the provided
+     * {@link IModel}.
+     * 
+     * @see #appliesTo(IModel)
+     */
+    @Override
+    public final ApplicationAdvice appliesTo(final ComponentType componentType, final IModel<?> model) {
+        if (componentType != getComponentType()) {
+            return ApplicationAdvice.DOES_NOT_APPLY;
+        }
+        return appliesTo(model);
+    }
+
+    /**
+     * Hook for subclasses to check the {@link IModel}.
+     */
+    protected abstract ApplicationAdvice appliesTo(IModel<?> model);
+
+    protected final ApplicationAdvice appliesIf(final boolean b) {
+        return ApplicationAdvice.appliesIf(b);
+    }
+
+    /**
+     * Convenience for subclasses to call from {@link #appliesTo(IModel)}
+     */
+    protected final ApplicationAdvice appliesExclusivelyIf(final boolean b) {
+        return b ? ApplicationAdvice.APPLIES_EXCLUSIVELY : ApplicationAdvice.DOES_NOT_APPLY;
+    }
+
+    @Override
+    public final Component createComponent(final IModel<?> model) {
+        return createComponent(getComponentType().toString(), model);
+    }
+
+    @Override
+    public abstract Component createComponent(String id, IModel<?> model);
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public CssResourceReference getCssResourceReference() {
+        return PanelUtil.cssResourceReferenceFor(componentClass);
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/99094b7e/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/ComponentType.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/ComponentType.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/ComponentType.java
new file mode 100644
index 0000000..70281cf
--- /dev/null
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/ComponentType.java
@@ -0,0 +1,180 @@
+/*
+ *  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;
+
+import org.apache.wicket.Component;
+
+import org.apache.isis.core.commons.lang.StringExtensions;
+
+/**
+ * Enumerates the different types of {@link Component}s that can be constructed
+ * using {@link ComponentFactory}.
+ * 
+ * <p>
+ * Some are fine-grained (such as {@link ComponentType#SCALAR_NAME_AND_VALUE}, a
+ * panel to represent a single scalar property or parameter), but others are
+ * somewhat larger (such as {@link ComponentType#ENTITY}, representing an
+ * entity, with its actions, properties and collections).
+ */
+public enum ComponentType {
+
+    
+    /**
+     * About page text.
+     */
+    ABOUT,
+    /**
+     * Welcome page text.
+     */
+    WELCOME,
+    /**
+     * List of services and their actions.
+     * 
+     * <p>
+     * Could be rendered using a JavaScript or DHTML menu, an accordion, or a
+     * tree view.
+     */
+    SERVICE_ACTIONS,
+    /**
+     * A single domain entity.
+     */
+    ENTITY,
+    /**
+     * Icon and title for a single entity.
+     */
+    ENTITY_ICON_AND_TITLE,
+    /**
+     * Icon, title and a copy link for a single entity.
+     */
+    ENTITY_ICON_TITLE_AND_COPYLINK,
+    /**
+     * Title, icon and action list for a single entity.
+     */
+    ENTITY_SUMMARY,
+    /**
+     * The set of properties for a single entity.
+     */
+    ENTITY_PROPERTIES,
+    /**
+     * The set of collections of a single entity, designed to be standalone outside of a form.
+     * 
+     * <p>
+     * compare with {@value #ENTITY_COLLECTIONS}.
+     */
+    ENTITY_COLLECTIONS,
+    /**
+     * A single standalone value, as might be returned from an action.
+     */
+    VALUE,
+    /**
+     * The name and value of a single property or parameter, ie a scalar.
+     */
+    SCALAR_NAME_AND_VALUE,
+    /**
+     * The name and contents of a single collection of an entity;
+     * {@link Component}s are expected to use {@link #COLLECTION_CONTENTS} to
+     * actually render the contents.
+     */
+    COLLECTION_NAME_AND_CONTENTS,
+    /**
+     * The parameter form (dialog box) of an action.
+     */
+    PARAMETERS,
+    /**
+     * Info details for an action, eg to display the target, a resubmit button,
+     * any description or help text, and so on.
+     */
+    ACTION_INFO,
+    /**
+     * Used to display the parameters of an action.
+     */
+    ACTION_PROMPT,
+    /**
+     * Top-level component for rendering a standalone collection (ie as returned by
+     * an action).
+     */
+    STANDALONE_COLLECTION,
+    /**
+     * Bookmarked link to invoke an action
+     */
+    ACTION_LINK,
+    /**
+     * A collection of entities (the value of)
+     */
+    COLLECTION_CONTENTS,
+    /**
+     * A link to an entity.
+     */
+    ENTITY_LINK,
+    /**
+     * A collection of entities, from an action, but none returned.
+     */
+    EMPTY_COLLECTION,
+    /**
+     * A void result from an action.
+     */
+    VOID_RETURN,
+    /**
+     * A list of {@link IsisModel}s, rendered as a list of links.
+     */
+    BOOKMARKED_PAGES, 
+    /**
+     * Place holder for a component used to represent an unknown model;
+     * not used for matching, since the {@link ComponentFactory} implementation
+     * acts as a fallback whenever a more suitable factory cannot be located.
+     */
+    UNKNOWN,
+
+    /**
+     * The header (navigation bar) of the page
+     */
+    HEADER,
+
+    /**
+     * The footer of the page
+     */
+    FOOTER;
+
+    /**
+     * Returns the {@link #name()} formatted as
+     * {@link org.apache.isis.core.commons.lang.StringExtensions#toCamelCase(String) case}.
+     * 
+     * <p>
+     * For example, <tt>OBJECT_EDIT</tt> becomes <tt>objectEdit</tt>.
+     */
+    @Override
+    public String toString() {
+        return getWicketId();
+    }
+
+    public String getWicketId() {
+        return StringExtensions.toCamelCase(name());
+    }
+
+    public static ComponentType lookup(final String id) {
+        for (final ComponentType componentType : values()) {
+            if (componentType.getWicketId().equals(id)) {
+                return componentType;
+            }
+        }
+        return null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/99094b7e/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/HeaderContributorProvider.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/HeaderContributorProvider.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/HeaderContributorProvider.java
new file mode 100644
index 0000000..fd9663b
--- /dev/null
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/HeaderContributorProvider.java
@@ -0,0 +1,27 @@
+/*
+ *  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;
+
+import org.apache.wicket.markup.html.IHeaderContributor;
+
+// TODO unused. Remove ?!
+public interface HeaderContributorProvider {
+
+    IHeaderContributor getHeaderContributor();
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/99094b7e/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/actionresponse/ActionResultResponse.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/actionresponse/ActionResultResponse.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/actionresponse/ActionResultResponse.java
new file mode 100644
index 0000000..ad5bbf5
--- /dev/null
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/actionresponse/ActionResultResponse.java
@@ -0,0 +1,92 @@
+/**
+ *  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.actionresponse;
+
+import java.net.URL;
+
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.request.IRequestHandler;
+
+import org.apache.isis.viewer.wicket.ui.pages.PageAbstract;
+
+/**
+ * The response to provide as a result of interpreting the response;
+ * either to show a {@link #toPage(PageAbstract) page}, or to {@link #withHandler(IRequestHandler) redirect} to a 
+ * handler (eg a download).
+ */
+public class ActionResultResponse {
+    
+    private final ActionResultResponseHandlingStrategy handlingStrategy;
+    private final IRequestHandler handler;
+    private final PageAbstract page;
+    private final AjaxRequestTarget target;
+    private final URL url;
+    
+    public static ActionResultResponse withHandler(IRequestHandler handler) {
+        return new ActionResultResponse(
+                ActionResultResponseHandlingStrategy.SCHEDULE_HANDLER, handler, null, null, null);
+    }
+    public static ActionResultResponse toPage(PageAbstract page) {
+        return new ActionResultResponse(
+                ActionResultResponseHandlingStrategy.REDIRECT_TO_PAGE, null, page, null, null);
+    }
+    public static ActionResultResponse openUrlInBrowser(final AjaxRequestTarget target, final URL url) {
+        return new ActionResultResponse(
+                ActionResultResponseHandlingStrategy.OPEN_URL_IN_BROWSER, null, null, target, url);
+    }
+    private ActionResultResponse(
+            final ActionResultResponseHandlingStrategy strategy, 
+            final IRequestHandler handler, 
+            final PageAbstract page, 
+            final AjaxRequestTarget target,
+            final URL url) {
+        handlingStrategy = strategy;
+        this.handler = handler;
+        this.page = page;
+        this.target = target;
+        this.url = url;
+    }
+
+    public ActionResultResponseHandlingStrategy getHandlingStrategy() {
+        return handlingStrategy;
+    }
+
+    /**
+     * Populated only if {@link #getHandlingStrategy() handling strategy} is {@link ActionResultResponseHandlingStrategy#SCHEDULE_HANDLER}
+     */
+    public IRequestHandler getHandler() {
+        return handler;
+    }
+    /**
+     * Populated only if {@link #getHandlingStrategy() handling strategy} is {@link ActionResultResponseHandlingStrategy#REDIRECT_TO_PAGE}
+     */
+    public PageAbstract getToPage() {
+        return page;
+    }
+    /**
+     * Populated only if {@link #getHandlingStrategy() handling strategy} is {@link ActionResultResponseHandlingStrategy#OPEN_URL_IN_BROWSER}
+     */
+    public AjaxRequestTarget getTarget() {
+        return target;
+    }
+    /**
+     * Populated only if {@link #getHandlingStrategy() handling strategy} is {@link ActionResultResponseHandlingStrategy#OPEN_URL_IN_BROWSER}
+     */
+    public URL getUrl() {
+        return url;
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/99094b7e/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/actionresponse/ActionResultResponseHandlingStrategy.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/actionresponse/ActionResultResponseHandlingStrategy.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/actionresponse/ActionResultResponseHandlingStrategy.java
new file mode 100644
index 0000000..3bc6e65
--- /dev/null
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/actionresponse/ActionResultResponseHandlingStrategy.java
@@ -0,0 +1,139 @@
+/**
+ *  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.actionresponse;
+
+import java.net.URL;
+
+import org.apache.isis.core.runtime.system.context.IsisContext;
+import org.apache.isis.viewer.wicket.model.models.VoidModel;
+import org.apache.isis.viewer.wicket.ui.pages.voidreturn.VoidReturnPage;
+import org.apache.wicket.Component;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.behavior.AbstractAjaxBehavior;
+import org.apache.wicket.request.Url;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.handler.resource.ResourceStreamRequestHandler;
+import org.apache.wicket.request.resource.ContentDisposition;
+import org.apache.wicket.util.resource.IResourceStream;
+import org.apache.wicket.util.time.Duration;
+
+public enum ActionResultResponseHandlingStrategy {
+    REDIRECT_TO_VOID {
+        @Override
+        public void handleResults(Component component, ActionResultResponse resultResponse) {
+            component.setResponsePage(new VoidReturnPage(new VoidModel()));
+        }
+    },
+    REDIRECT_TO_PAGE {
+        @Override
+        public void handleResults(final Component component, final ActionResultResponse resultResponse) {
+            // force any changes in state etc to happen now prior to the redirect;
+            // in the case of an object being returned, this should cause our page mementos 
+            // (eg EntityModel) to hold the correct state.  I hope.
+            IsisContext.getTransactionManager().flushTransaction();
+            
+            // "redirect-after-post"
+            component.setResponsePage(resultResponse.getToPage());
+        }
+    },
+    SCHEDULE_HANDLER {
+        @Override
+        public void handleResults(final Component component, final ActionResultResponse resultResponse) {
+            RequestCycle requestCycle = component.getRequestCycle();
+            AjaxRequestTarget target = requestCycle.find(AjaxRequestTarget.class);
+            if (target == null) {
+                // normal (non-Ajax) request => just stream the Lob to the browser
+                requestCycle.scheduleRequestHandlerAfterCurrent(resultResponse.getHandler());
+            } else {
+                // Ajax request => respond with a redirect to be able to stream the Lob to the client
+                ResourceStreamRequestHandler scheduledHandler = (ResourceStreamRequestHandler) resultResponse.getHandler();
+                StreamAfterAjaxResponseBehavior streamingBehavior = new StreamAfterAjaxResponseBehavior(scheduledHandler);
+                component.getPage().add(streamingBehavior);
+                CharSequence callbackUrl = streamingBehavior.getCallbackUrl();
+                target.appendJavaScript("setTimeout(\"window.location.href='" + callbackUrl + "'\", 10);");
+            }
+        }
+    },
+    OPEN_URL_IN_BROWSER {
+        @Override
+        public void handleResults(final Component component, final ActionResultResponse resultResponse) {
+            final AjaxRequestTarget target = resultResponse.getTarget();
+            final URL url = resultResponse.getUrl();
+            
+            RequestCycle requestCycle = component.getRequestCycle();
+
+            final String fullUrl = expanded(requestCycle, url);
+            target.appendJavaScript("setTimeout(function(){Wicket.Event.publish(Isis.Topic.OPEN_IN_NEW_TAB, '" + fullUrl + "');}, 100);");
+        }
+
+    };
+
+    public abstract void handleResults(Component component, ActionResultResponse resultResponse);
+
+    /**
+     * @see #expanded(String)
+     */
+    public static String expanded(RequestCycle requestCycle, final URL url) {
+        String urlStr = expanded(url);
+        return requestCycle.getUrlRenderer().renderFullUrl(Url.parse(urlStr));
+    }
+
+    /**
+     * @see #expanded(String)
+     */
+    public static String expanded(final URL url) {
+        return expanded(url.toString());
+    }
+
+    /**
+     * very simple templating support, the idea being that "antiCache=${currentTimeMillis}"
+     * will be replaced automatically.
+     */
+    public static String expanded(String urlStr) {
+        if(urlStr.contains("antiCache=${currentTimeMillis}")) {
+            urlStr = urlStr.replace("antiCache=${currentTimeMillis}", "antiCache="+System.currentTimeMillis());
+        }
+        return urlStr;
+    }
+
+    /**
+     * A special Ajax behavior that is used to stream the contents of a Lob after
+     * an Ajax request.
+     */
+    private static class StreamAfterAjaxResponseBehavior extends AbstractAjaxBehavior {
+
+        private final String fileName;
+        private final IResourceStream resourceStream;
+        private final Duration cacheDuration;
+
+        public StreamAfterAjaxResponseBehavior(ResourceStreamRequestHandler scheduledHandler) {
+            this.fileName = scheduledHandler.getFileName();
+            this.resourceStream = scheduledHandler.getResourceStream();
+            this.cacheDuration = scheduledHandler.getCacheDuration();
+        }
+
+        @Override
+        public void onRequest() {
+            ResourceStreamRequestHandler handler = new ResourceStreamRequestHandler(resourceStream, fileName);
+            handler.setCacheDuration(cacheDuration);
+            handler.setContentDisposition(ContentDisposition.ATTACHMENT);
+            Component page = getComponent();
+            page.getRequestCycle().scheduleRequestHandlerAfterCurrent(handler);
+            page.remove(this);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/99094b7e/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/actionresponse/ActionResultResponseType.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/actionresponse/ActionResultResponseType.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/actionresponse/ActionResultResponseType.java
new file mode 100644
index 0000000..62291f8
--- /dev/null
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/actionresponse/ActionResultResponseType.java
@@ -0,0 +1,202 @@
+/**
+ *  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.actionresponse;
+
+import java.net.URL;
+import java.util.Collection;
+import java.util.List;
+import com.google.common.collect.Lists;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.request.IRequestHandler;
+import org.apache.isis.applib.value.Blob;
+import org.apache.isis.applib.value.Clob;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.adapter.version.ConcurrencyException;
+import org.apache.isis.core.metamodel.facets.object.value.ValueFacet;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.runtime.system.context.IsisContext;
+import org.apache.isis.viewer.wicket.model.models.ActionModel;
+import org.apache.isis.viewer.wicket.model.models.EntityCollectionModel;
+import org.apache.isis.viewer.wicket.model.models.ValueModel;
+import org.apache.isis.viewer.wicket.model.models.VoidModel;
+import org.apache.isis.viewer.wicket.ui.pages.entity.EntityPage;
+import org.apache.isis.viewer.wicket.ui.pages.standalonecollection.StandaloneCollectionPage;
+import org.apache.isis.viewer.wicket.ui.pages.value.ValuePage;
+import org.apache.isis.viewer.wicket.ui.pages.voidreturn.VoidReturnPage;
+
+public enum ActionResultResponseType {
+    OBJECT {
+        @Override
+        public ActionResultResponse interpretResult(final ActionModel model, final AjaxRequestTarget target, final ObjectAdapter resultAdapter) {
+            final ObjectAdapter actualAdapter = determineActualAdapter(resultAdapter);
+            return toEntityPage(model, actualAdapter, null);
+        }
+
+        @Override
+        public ActionResultResponse interpretResult(final ActionModel model, ObjectAdapter targetAdapter, ConcurrencyException ex) {
+            return toEntityPage(model, targetAdapter, ex);
+        }
+    },
+    COLLECTION {
+        @Override
+        public ActionResultResponse interpretResult(final ActionModel actionModel, final AjaxRequestTarget target, final ObjectAdapter resultAdapter) {
+            final EntityCollectionModel collectionModel = EntityCollectionModel.createStandalone(resultAdapter);
+            // take a copy of the actionModel, because the original can get mutated (specifically: its arguments cleared)
+            final ActionModel actionModelCopy = actionModel.copy();
+            collectionModel.setActionHint(actionModelCopy);
+            return ActionResultResponse.toPage(new StandaloneCollectionPage(collectionModel));
+        }
+    },
+    VALUE {
+        @Override
+        public ActionResultResponse interpretResult(final ActionModel model, final AjaxRequestTarget target, final ObjectAdapter resultAdapter) {
+            ValueModel valueModel = new ValueModel(resultAdapter);
+            valueModel.setActionHint(model);
+            final ValuePage valuePage = new ValuePage(valueModel);
+            return ActionResultResponse.toPage(valuePage);
+        }
+    },
+    VALUE_CLOB {
+        @Override
+        public ActionResultResponse interpretResult(final ActionModel model, final AjaxRequestTarget target, final ObjectAdapter resultAdapter) {
+            final Object value = resultAdapter.getObject();
+            IRequestHandler handler = ActionModel.downloadHandler(value);
+            return ActionResultResponse.withHandler(handler);
+        }
+    },
+    VALUE_BLOB {
+        @Override
+        public ActionResultResponse interpretResult(final ActionModel model, final AjaxRequestTarget target, final ObjectAdapter resultAdapter) {
+            final Object value = resultAdapter.getObject();
+            IRequestHandler handler = ActionModel.downloadHandler(value);
+            return ActionResultResponse.withHandler(handler);
+        }
+    },
+    VALUE_URL_AJAX {
+        @Override
+        public ActionResultResponse interpretResult(final ActionModel model, final AjaxRequestTarget target, final ObjectAdapter resultAdapter) {
+            final URL url = (URL)resultAdapter.getObject();
+            return ActionResultResponse.openUrlInBrowser(target, url);
+        }
+
+    },
+    VALUE_URL_NOAJAX {
+        @Override
+        public ActionResultResponse interpretResult(final ActionModel model, final AjaxRequestTarget target, final ObjectAdapter resultAdapter) {
+            // open URL server-side redirect
+            final Object value = resultAdapter.getObject();
+            IRequestHandler handler = ActionModel.redirectHandler(value);
+            return ActionResultResponse.withHandler(handler);
+        }
+        
+    },
+    VOID {
+        @Override
+        public ActionResultResponse interpretResult(final ActionModel model, final AjaxRequestTarget target, final ObjectAdapter resultAdapter) {
+            final VoidModel voidModel = new VoidModel();
+            voidModel.setActionHint(model);
+            return ActionResultResponse.toPage(new VoidReturnPage(voidModel));
+        }
+    };
+
+    public abstract ActionResultResponse interpretResult(ActionModel model, final AjaxRequestTarget target, ObjectAdapter resultAdapter);
+
+    /**
+     * Only overridden for {@link ActionResultResponseType#OBJECT object}
+     */
+    public ActionResultResponse interpretResult(ActionModel model, ObjectAdapter targetAdapter, ConcurrencyException ex) {
+        throw new UnsupportedOperationException("Cannot render concurrency exception for any result type other than OBJECT");
+    }
+
+    private static ObjectAdapter determineActualAdapter(final ObjectAdapter resultAdapter) {
+        if (resultAdapter.getSpecification().isNotCollection()) {
+            return resultAdapter;
+        } else {
+            // will only be a single element
+            final List<Object> pojoList = asList(resultAdapter);
+            final Object pojo = pojoList.get(0);
+            return adapterFor(pojo);
+        }
+    }
+    private static ObjectAdapter adapterFor(final Object pojo) {
+        return IsisContext.getPersistenceSession().getAdapterManager().adapterFor(pojo);
+    }
+
+    private static ActionResultResponse toEntityPage(final ActionModel model, final ObjectAdapter actualAdapter, ConcurrencyException exIfAny) {
+        // this will not preserve the URL (because pageParameters are not copied over)
+        // but trying to preserve them seems to cause the 302 redirect to be swallowed somehow
+        final EntityPage entityPage = new EntityPage(actualAdapter, exIfAny);
+        return ActionResultResponse.toPage(entityPage);
+    }
+
+
+    // //////////////////////////////////////
+
+    public static ActionResultResponse determineAndInterpretResult(
+            final ActionModel model, 
+            final AjaxRequestTarget target, 
+            final ObjectAdapter resultAdapter) {
+        ActionResultResponseType arrt = determineFor(resultAdapter, target);
+        return arrt.interpretResult(model, target, resultAdapter);
+    }
+
+    private static ActionResultResponseType determineFor(
+            final ObjectAdapter resultAdapter, 
+            final AjaxRequestTarget target) {
+        if(resultAdapter == null) {
+            return ActionResultResponseType.VOID;
+        }
+        final ObjectSpecification resultSpec = resultAdapter.getSpecification();
+        if (resultSpec.isNotCollection()) {
+            if (resultSpec.getFacet(ValueFacet.class) != null) {
+                
+                final Object value = resultAdapter.getObject();
+                if(value instanceof Clob) {
+                    return ActionResultResponseType.VALUE_CLOB;
+                } 
+                if(value instanceof Blob) {
+                    return ActionResultResponseType.VALUE_BLOB;
+                } 
+                if(value instanceof java.net.URL) {
+                    return target != null? ActionResultResponseType.VALUE_URL_AJAX: ActionResultResponseType.VALUE_URL_NOAJAX;
+                } 
+                // else
+                return ActionResultResponseType.VALUE;
+            } else {
+                return ActionResultResponseType.OBJECT;
+            }
+        } else {
+            final List<Object> pojoList = asList(resultAdapter);
+            switch (pojoList.size()) {
+            case 1:
+                return ActionResultResponseType.OBJECT;
+            default:
+                return ActionResultResponseType.COLLECTION;
+            }
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private static List<Object> asList(final ObjectAdapter resultAdapter) {
+        final Collection<Object> coll = (Collection<Object>) resultAdapter.getObject();
+        return coll instanceof List
+                ? (List<Object>)coll
+                : Lists.<Object>newArrayList(coll);
+    }
+
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/isis/blob/99094b7e/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/app/registry/ComponentFactoryRegistrar.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/app/registry/ComponentFactoryRegistrar.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/app/registry/ComponentFactoryRegistrar.java
new file mode 100644
index 0000000..5952f0c
--- /dev/null
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/app/registry/ComponentFactoryRegistrar.java
@@ -0,0 +1,105 @@
+/*
+ *  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.app.registry;
+
+import java.util.Iterator;
+import java.util.List;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Lists;
+
+import org.apache.isis.viewer.wicket.ui.ComponentFactory;
+import org.apache.isis.viewer.wicket.ui.ComponentType;
+
+/**
+ * Defines an API for registering {@link ComponentFactory}s.
+ * 
+ * <p>
+ * As used by {@link ComponentFactoryListDefault}.
+ */
+public interface ComponentFactoryRegistrar {
+    
+    public static class ComponentFactoryList implements Iterable<ComponentFactory> {
+        private final List<ComponentFactory> componentFactories = Lists.newArrayList();
+
+        public void add(ComponentFactory componentFactory) {
+            componentFactories.add(componentFactory);
+        }
+
+        public void replace(final ComponentFactory replacementComponentFactory) {
+            removeExisting(matching(replacementComponentFactory.getComponentType()));
+            add(replacementComponentFactory);
+        }
+
+        public void replace(final Class<? extends ComponentFactory> toReplace, final ComponentFactory replacementComponentFactory) {
+            int indexOfOldFactory = removeExisting(matching(toReplace));
+            insert(indexOfOldFactory, replacementComponentFactory);
+        }
+
+        private void insert(final int indexToInsertInto, final ComponentFactory replacementComponentFactory) {
+            if (indexToInsertInto > -1 && indexToInsertInto < componentFactories.size()) {
+                componentFactories.add(indexToInsertInto, replacementComponentFactory);
+            } else {
+                componentFactories.add(replacementComponentFactory);
+            }
+        }
+
+        private int removeExisting(final Predicate<ComponentFactory> predicate) {
+            int indexOfFirst = -1;
+            for (int i = 0; i < componentFactories.size(); i++) {
+                ComponentFactory factory = componentFactories.get(i);
+                if (predicate.apply(factory)) {
+                    componentFactories.remove(i);
+                    if (indexOfFirst == -1) {
+                        indexOfFirst = i;
+                    }
+                    i--;
+                }
+            }
+
+            return indexOfFirst;
+        }
+
+        private static Predicate<ComponentFactory> matching(final ComponentType componentType) {
+            return new Predicate<ComponentFactory>() {
+                @Override
+                public boolean apply(ComponentFactory input) {
+                    return input.getComponentType() == componentType;
+                }
+            };
+        }
+
+        private static Predicate<ComponentFactory> matching(final Class<? extends ComponentFactory> toReplace) {
+            return new Predicate<ComponentFactory>() {
+                @Override
+                public boolean apply(ComponentFactory input) {
+                    return toReplace.isAssignableFrom(input.getClass());
+                }
+            };
+        }
+
+        @Override
+        public Iterator<ComponentFactory> iterator() {
+            return componentFactories.iterator();
+        }
+    }
+
+    void addComponentFactories(ComponentFactoryList componentFactoryList);
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/99094b7e/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/app/registry/ComponentFactoryRegistry.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/app/registry/ComponentFactoryRegistry.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/app/registry/ComponentFactoryRegistry.java
new file mode 100644
index 0000000..f037378
--- /dev/null
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/app/registry/ComponentFactoryRegistry.java
@@ -0,0 +1,87 @@
+/*
+ *  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.app.registry;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.MarkupContainer;
+import org.apache.wicket.model.IModel;
+
+import org.apache.isis.viewer.wicket.ui.ComponentFactory;
+import org.apache.isis.viewer.wicket.ui.ComponentType;
+
+/**
+ * API for finding registered {@link ComponentFactory}s.
+ * 
+ * <p>
+ * Ultimately all requests to locate {@link ComponentFactory}s are routed
+ * through to an object implementing this interface.
+ */
+public interface ComponentFactoryRegistry {
+
+    List<ComponentFactory> findComponentFactories(ComponentType componentType, IModel<?> model);
+
+    /**
+     * Finds the "best" {@link ComponentFactory} for the viewId.
+     */
+    ComponentFactory findComponentFactory(ComponentType componentType, IModel<?> model);
+
+    /**
+     * As per
+     * {@link #addOrReplaceComponent(MarkupContainer, ComponentType, IModel)},
+     * but with the wicket id derived from the {@link ComponentType}.
+     */
+    Component addOrReplaceComponent(MarkupContainer markupContainer, ComponentType componentType, IModel<?> model);
+
+    /**
+     * {@link #createComponent(ComponentType, String, IModel) Creates} the
+     * relevant {@link Component} for the provided arguments, and adds to the
+     * provided {@link MarkupContainer}; the wicket id is as specified.
+     * 
+     * <p>
+     * If none can be found, will fail fast.
+     */
+    Component addOrReplaceComponent(MarkupContainer markupContainer, String id, ComponentType componentType, IModel<?> model);
+
+    /**
+     * As per {@link #createComponent(ComponentType, String, IModel)}, but with
+     * the wicket id derived from the {@link ComponentType}.
+     * 
+     * @see #createComponent(ComponentType, String, IModel)
+     */
+    Component createComponent(ComponentType componentType, IModel<?> model);
+
+    /**
+     * Create the {@link Component} matching the specified {@link ComponentType}
+     * and {@link IModel} to the provided {@link MarkupContainer}; the id is
+     * specified explicitly.
+     * 
+     * <p>
+     * If none can be found, will fail fast.
+     */
+    Component createComponent(ComponentType componentType, String id, IModel<?> model);
+
+    ComponentFactory findComponentFactoryElseFailFast(ComponentType componentType, IModel<?> model);
+
+    Collection<ComponentFactory> listComponentFactories();
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/isis/blob/99094b7e/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/app/registry/ComponentFactoryRegistryAccessor.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/app/registry/ComponentFactoryRegistryAccessor.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/app/registry/ComponentFactoryRegistryAccessor.java
new file mode 100644
index 0000000..b433bf4
--- /dev/null
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/app/registry/ComponentFactoryRegistryAccessor.java
@@ -0,0 +1,31 @@
+/*
+ *  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.app.registry;
+
+import java.io.Serializable;
+
+/**
+ * For obtaining the {@link ComponentFactoryRegistry}.
+ */
+public interface ComponentFactoryRegistryAccessor extends Serializable {
+
+    ComponentFactoryRegistry getComponentFactoryRegistry();
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/isis/blob/99094b7e/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/AboutPanel.html
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/AboutPanel.html b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/AboutPanel.html
new file mode 100644
index 0000000..c85780d
--- /dev/null
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/AboutPanel.html
@@ -0,0 +1,26 @@
+<?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">
+<wicket:panel>
+    <div class="aboutPanel aboutComponentType">
+        <div wicket:id="manifestAttributes"></div>
+    </div>
+</wicket:panel>
+</html>

http://git-wip-us.apache.org/repos/asf/isis/blob/99094b7e/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/AboutPanel.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/AboutPanel.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/AboutPanel.java
new file mode 100644
index 0000000..68f16fa
--- /dev/null
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/AboutPanel.java
@@ -0,0 +1,69 @@
+/*
+ *  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.about;
+
+import java.io.InputStream;
+
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
+
+import org.apache.isis.viewer.wicket.model.models.AboutModel;
+import org.apache.isis.viewer.wicket.ui.pages.home.HomePage;
+import org.apache.isis.viewer.wicket.ui.panels.PanelAbstract;
+
+/**
+ * {@link PanelAbstract Panel} displaying welcome message (as used on
+ * {@link HomePage}).
+ */
+public class AboutPanel extends PanelAbstract<AboutModel> {
+
+    private static final long serialVersionUID = 1L;
+
+    private static final String ID_MANIFEST_ATTRIBUTES = "manifestAttributes";
+
+    @Inject
+    @Named("aboutMessage")
+    private String aboutMessage;
+    
+    /**
+     * We take care to read this only once.
+     *
+     * <p>
+     *     Is <code>transient</code> because
+     * </p>
+     */
+    @Inject
+    @Named("metaInfManifest")
+    private transient InputStream metaInfManifestIs;
+    
+    private JarManifestModel jarManifestModel;
+    
+    public AboutPanel(final String id) {
+        super(id);
+        
+        if(jarManifestModel == null) {
+            jarManifestModel = new JarManifestModel(aboutMessage, metaInfManifestIs);
+        }
+        
+        add(new JarManifestPanel(ID_MANIFEST_ATTRIBUTES, jarManifestModel));
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/99094b7e/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/AboutPanelFactory.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/AboutPanelFactory.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/AboutPanelFactory.java
new file mode 100644
index 0000000..3439855
--- /dev/null
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/AboutPanelFactory.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.about;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.model.IModel;
+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 AboutPanel}.
+ */
+public class AboutPanelFactory extends ComponentFactoryAbstract {
+
+    private static final long serialVersionUID = 1L;
+
+    public AboutPanelFactory() {
+        super(ComponentType.ABOUT, AboutPanel.class);
+    }
+
+    @Override
+    public ApplicationAdvice appliesTo(final IModel<?> model) {
+        return ApplicationAdvice.APPLIES;
+    }
+
+    @Override
+    public Component createComponent(final String id, final IModel<?> model) {
+        return new AboutPanel(id);
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/99094b7e/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/JarManifestAttributes.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/JarManifestAttributes.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/JarManifestAttributes.java
new file mode 100644
index 0000000..fac290c
--- /dev/null
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/JarManifestAttributes.java
@@ -0,0 +1,68 @@
+/*
+ *  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.about;
+
+import java.io.Serializable;
+import java.net.URL;
+import java.util.Map.Entry;
+
+public class JarManifestAttributes implements Serializable {
+    
+    private static final long serialVersionUID = 1L;
+
+    public static JarManifestAttributes jarName(String jarName) {
+        return new JarManifestAttributes(JarManifestAttributes.Type.JAR_NAME, jarName);
+    }
+
+    public static JarManifestAttributes jarUrl(URL jarUrl) {
+        return new JarManifestAttributes(JarManifestAttributes.Type.JAR_URL, jarUrl != null? jarUrl.toExternalForm(): "");
+    }
+
+    public static JarManifestAttributes attribute(Entry<Object,Object> entry) {
+        StringBuilder buf = new StringBuilder();
+        buf .append("    ")
+            .append(entry.getKey())
+            .append(": ")
+            .append(entry.getValue())
+            .append("\n")
+            ;
+        return new JarManifestAttributes(JarManifestAttributes.Type.MANIFEST_ATTRIBUTE, buf.toString());
+    }
+
+    enum Type {
+        JAR_NAME,
+        JAR_URL,
+        MANIFEST_ATTRIBUTE
+    }
+    
+    private final Type type;
+    private final String line;
+    
+    public JarManifestAttributes(Type type, String line) {
+        this.type = type;
+        this.line = line;
+    }
+    public JarManifestAttributes.Type getType() {
+        return type;
+    }
+    public String getLine() {
+        return line;
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/isis/blob/99094b7e/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/JarManifestListView.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/JarManifestListView.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/JarManifestListView.java
new file mode 100644
index 0000000..1565ac9
--- /dev/null
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/JarManifestListView.java
@@ -0,0 +1,45 @@
+/*
+ *  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.about;
+
+import java.util.List;
+
+import org.apache.wicket.behavior.AttributeAppender;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.list.ListItem;
+import org.apache.wicket.markup.html.list.ListView;
+
+public final class JarManifestListView extends ListView<JarManifestAttributes> {
+    
+    private static final long serialVersionUID = 1L;
+    private final String idLine;
+
+    public JarManifestListView(String id, String idLine, List<? extends JarManifestAttributes> list) {
+        super(id, list);
+        this.idLine = idLine;
+    }
+
+    @Override
+    protected void populateItem(ListItem<JarManifestAttributes> item) {
+        final JarManifestAttributes detail = item.getModelObject();
+        Label label = new Label(idLine, detail.getLine());
+        item.add(new AttributeAppender("class", detail.getType().name().toLowerCase()));
+        item.add(label);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/isis/blob/99094b7e/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/JarManifestModel.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/JarManifestModel.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/JarManifestModel.java
new file mode 100644
index 0000000..d78a290
--- /dev/null
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/JarManifestModel.java
@@ -0,0 +1,233 @@
+/*
+ *  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.about;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.jar.Attributes;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+
+import com.google.common.base.CharMatcher;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Lists;
+
+import org.apache.isis.core.commons.lang.CloseableExtensions;
+import org.apache.isis.viewer.wicket.model.models.ModelAbstract;
+
+public class JarManifestModel extends ModelAbstract<JarManifestModel> {
+
+    private static final long serialVersionUID = 1L;
+
+    private static final List<String> VERSION_KEY_CANDIDATES = Arrays.asList("Implementation-Version", "Build-Time");
+    
+    private String aboutMessage;
+
+    private final List<JarManifestAttributes> manifests = Lists.newArrayList();
+
+    /**
+     * @param aboutMessage
+     * @param metaInfManifestIs provide using <tt>getServletContext().getResourceAsStream("/META-INF/MANIFEST.MF")</tt>
+     */
+    public JarManifestModel(String aboutMessage, InputStream metaInfManifestIs) {
+
+        this.aboutMessage = aboutMessage;
+        
+        Manifest manifest;
+        try {
+            manifest = new Manifest(metaInfManifestIs);
+            manifests.add(JarManifestAttributes.jarName("Web archive (war file)"));
+            manifests.add(JarManifestAttributes.jarUrl(null));
+            addAttributes(manifest, manifests);
+            
+            // append the version if able to guess
+            String versionIfAny = guessVersion(manifest); 
+            this.aboutMessage = this.aboutMessage + (versionIfAny != null? "\n\n" + versionIfAny: "");
+            
+        } catch (Exception ex) {
+            // ignore
+        } finally {
+            CloseableExtensions.closeSafely(metaInfManifestIs);
+        }
+        
+        Enumeration<?> resEnum;
+        try {
+            resEnum = Thread.currentThread().getContextClassLoader().getResources(JarFile.MANIFEST_NAME);
+        } catch (IOException e) {
+            return;
+        }
+        final List<JarManifest> jarManifests = Lists.newArrayList(); 
+        while (resEnum.hasMoreElements()) {
+            URL url = (URL)resEnum.nextElement();
+            JarManifest jarManifest = new JarManifest(url);
+            jarManifests.add(jarManifest);
+
+            InputStream is = null;
+            try {
+                is = url.openStream();
+                if (is != null) {
+                    manifest = new Manifest(is);
+                    jarManifest.addAttributesFrom(manifest);
+                }
+            } catch(Exception e3) {
+                // ignore
+            } finally {
+                CloseableExtensions.closeSafely(is);
+            }
+        }
+        
+        Collections.sort(jarManifests);
+        
+        for (JarManifest jarManifest : jarManifests) {
+            jarManifest.addAttributesTo(manifests);
+        }
+    }
+
+    private static class JarManifest implements Comparable<JarManifest> {
+        private final List<JarManifestAttributes> attributes = Lists.newArrayList();
+
+        private final URL url;
+
+        private JarName jarName;
+        
+        public JarManifest(URL url) {
+            this.url = url;
+            jarName = asJarName(url);
+        }
+
+        void addAttributesFrom(Manifest manifest) {
+            addAttributes(manifest, attributes);
+        }
+
+        void addAttributesTo(List<JarManifestAttributes> manifests) {
+            manifests.add(JarManifestAttributes.jarName(jarName.name));
+            manifests.add(JarManifestAttributes.jarUrl(url));
+            manifests.addAll(attributes);
+        }
+
+        @Override
+        public int compareTo(JarManifest o) {
+            return jarName.compareTo(o.jarName);
+        }
+    }
+    
+    static class JarName implements Comparable<JarName>{
+        enum Type {
+            CLASSES, JAR, OTHER
+        }
+        Type type;
+        String name;
+        JarName(Type type, String name) {
+            this.type = type;
+            this.name = name;
+        }
+        @Override
+        public int compareTo(JarName o) {
+            int x = type.compareTo(o.type);
+            if(x != 0) return x;
+            return name.compareTo(o.name);
+        }
+    }
+    
+    private static JarName asJarName(URL url) {
+        final String path = url.getPath();
+        // strip off the meta-inf
+        String strippedPath = stripSuffix(path, "/META-INF/MANIFEST.MF");
+        strippedPath = stripSuffix(strippedPath, "!");
+        
+        // split the path into parts, and reverse
+        List<String> parts = Lists.newArrayList(Splitter.on(CharMatcher.anyOf("/\\")).split(strippedPath));
+        Collections.reverse(parts);
+        
+        // searching from the end, return the jar name if possible
+        for (String part : parts) {
+            if(part.endsWith(".jar")) {
+                return new JarName(JarName.Type.JAR, part);
+            }
+        }
+        
+        // see if running in an IDE, under target*/classes; return the part prior to that. 
+        if(parts.size()>=3) {
+            if(parts.get(0).equals("classes") && parts.get(1).startsWith("target")) {
+                return new JarName(JarName.Type.CLASSES, parts.get(2));
+            }
+        }
+        
+        // otherwise, return the stripped path 
+        return new JarName(JarName.Type.OTHER, strippedPath);
+    }
+
+    public static String stripSuffix(String path, String suffix) {
+        int indexOf = path.indexOf(suffix);
+        if(indexOf != -1) {
+            path = path.substring(0, indexOf);
+        }
+        return path;
+    }
+
+    static void addAttributes(Manifest manifest, List<JarManifestAttributes> attributes) {
+        final Attributes mainAttribs = manifest.getMainAttributes();
+        Set<Entry<Object, Object>> entrySet = mainAttribs.entrySet();
+        for (Entry<Object, Object> entry : entrySet) {
+            JarManifestAttributes attribute = JarManifestAttributes.attribute(entry);
+            attributes.add(attribute);
+        }
+    }
+
+
+    private static String guessVersion(Manifest manifest) {
+        final Attributes mainAttribs = manifest.getMainAttributes();
+        Set<Entry<Object, Object>> entrySet = mainAttribs.entrySet();
+        for (String candidate : VERSION_KEY_CANDIDATES) {
+            for (Entry<Object, Object> entry : entrySet) {
+                if(candidate.equals(entry.getKey().toString())) {
+                    return entry.getValue().toString();
+                }
+            }
+        }
+        return null;
+    }
+
+
+    @Override
+    protected JarManifestModel load() {
+        return this;
+    }
+
+    @Override
+    public void setObject(JarManifestModel ex) {
+        // no-op
+    }
+
+    public String getAboutMessage() {
+        return aboutMessage;
+    }
+
+    public List<JarManifestAttributes> getDetail() {
+        return manifests;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/99094b7e/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/JarManifestPanel.css
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/JarManifestPanel.css b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/JarManifestPanel.css
new file mode 100644
index 0000000..2f05abd
--- /dev/null
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/JarManifestPanel.css
@@ -0,0 +1,91 @@
+/*
+ *  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.
+ */
+
+ .jarManifestPanel .aboutInfo {
+    margin-left: 50px;
+    margin-right: 50px;
+    padding-top: 50px;
+}
+
+
+.jarManifestPanel .aboutMessage {
+    background:#FFFFFF;
+    border-radius:4px;
+    -moz-border-radius:4px;
+    -webkit-border-radius:4px;
+    padding: 15px;
+    display: block;
+    text-align:center;
+    font-size:1.2em;
+}
+
+ .jarManifestPanel .manifestAttributes {
+    margin-top: 30px; 
+}
+
+.jarManifestPanel .heading {
+    display:block;
+    font-style:normal !important;
+
+    padding:1px 6px 1px 6px;
+}
+
+.jarManifestPanel .heading span {
+    display:block;
+    font-style:normal !important;
+    padding:3px 3px 3px 3px;
+    font-size: 0.8em;
+    font-weight:bold;
+}
+
+.jarManifestPanel .heading:hover {
+    background-color:#FFFFFF;
+    cursor: pointer;
+}
+
+.jarManifestPanel h3 {
+    font-size: larger;
+}
+
+.jarManifestPanel .manifestAttributesList .caused_by_label {
+    margin-top: 30px;
+    font-style:normal !important;
+    font-size: 0.8em;
+    font-weight:bold;
+    color: #46423C;
+}
+
+.jarManifestPanel .manifestAttributesList .jar_name {
+    margin-top: 15px;
+    font-size: 1.2em;
+    font-weight:bold;
+    color: #46423C;
+}
+
+.jarManifestPanel .manifestAttributesList .jar_url {
+    margin-top: 10px;
+    margin-bottom: 10px;
+    font-style: italic;
+    font-size: 1.0em;
+    color: #00477F;
+}
+
+.jarManifestPanel .manifestAttributesList .manifest_attribute{
+    margin-left: 30px;
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/99094b7e/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/JarManifestPanel.html
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/JarManifestPanel.html b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/JarManifestPanel.html
new file mode 100644
index 0000000..340922a
--- /dev/null
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/JarManifestPanel.html
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html>
+<!--
+  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="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="jarManifestPanel">
+                <div class="aboutInfo clear">
+                    <h2 wicket:id="aboutMessage" class="aboutMessage">[about message text]</h2>
+                    <div class="manifestAttributes" wicket:id="manifestAttributes">
+                        <h4 class="heading well">Jar manifest attributes</h4>
+                        <div class="content">
+                            <div class="manifestAttributesList">
+                                <ul>
+                                    <li wicket:id="manifestAttribute">
+                                        <span wicket:id="manifestAttributeLine">[manifest attribute line]</span>
+                                    </li>
+                                </ul>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </wicket:panel>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/isis/blob/99094b7e/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/JarManifestPanel.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/JarManifestPanel.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/JarManifestPanel.java
new file mode 100644
index 0000000..8a76487
--- /dev/null
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/JarManifestPanel.java
@@ -0,0 +1,68 @@
+/*
+ *  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.about;
+
+import org.apache.wicket.MarkupContainer;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.JavaScriptReferenceHeaderItem;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.request.resource.JavaScriptResourceReference;
+import org.apache.isis.viewer.wicket.ui.panels.PanelUtil;
+
+public class JarManifestPanel extends Panel {
+
+    private static final long serialVersionUID = 1L;
+
+    private static final String ID_ABOUT_MESSAGE = "aboutMessage";
+
+    private static final String ID_MANIFEST_ATTRIBUTES = "manifestAttributes";
+
+    private static final String ID_MANIFEST_ATTRIBUTE = "manifestAttribute";
+    private static final String ID_LINE = "manifestAttributeLine";
+
+    private static final JavaScriptResourceReference DIV_TOGGLE_JS = new JavaScriptResourceReference(JarManifestPanel.class, "div-toggle.js");
+
+    public JarManifestPanel(String id, JarManifestModel manifestModel) {
+        super(id, manifestModel);
+
+        final String aboutMessage = manifestModel.getAboutMessage();
+        final Label label = new Label(ID_ABOUT_MESSAGE, aboutMessage);
+        // safe to not escape, about message is read from file (part of deployed WAR)
+        label.setEscapeModelStrings(false);
+        add(label);
+
+        MarkupContainer container = new WebMarkupContainer(ID_MANIFEST_ATTRIBUTES) {
+            private static final long serialVersionUID = 1L;
+            @Override
+            public void renderHead(IHeaderResponse response) {
+                response.render(JavaScriptReferenceHeaderItem.forReference(DIV_TOGGLE_JS));
+            }
+        };
+        container.add(new JarManifestListView(ID_MANIFEST_ATTRIBUTE, JarManifestPanel.ID_LINE, manifestModel.getDetail()));
+        add(container);
+    }
+
+    public void renderHead(final IHeaderResponse response) {
+        PanelUtil.renderHead(response, this.getClass());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/99094b7e/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/div-toggle.js
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/div-toggle.js b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/div-toggle.js
new file mode 100644
index 0000000..d51175a
--- /dev/null
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/about/div-toggle.js
@@ -0,0 +1,25 @@
+/*
+ *  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.
+ */
+jQuery(document).ready(function() {
+  jQuery(".jarManifestPanel .content").hide();
+  jQuery(".jarManifestPanel .heading").click(function()
+  {
+    jQuery(this).next(".jarManifestPanel .content").slideToggle(500);
+  });
+});

http://git-wip-us.apache.org/repos/asf/isis/blob/99094b7e/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionlink/ActionLinkPanel.html
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionlink/ActionLinkPanel.html b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionlink/ActionLinkPanel.html
new file mode 100644
index 0000000..e239527
--- /dev/null
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionlink/ActionLinkPanel.html
@@ -0,0 +1,31 @@
+<?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>
+		     <span wicket:id="actionLinkWrapper" class="actionLinkPanel actionLinkComponentType">
+				<a href="#" wicket:id="actionLink">
+					<span wicket:id="actionTitle" class="actionTitle">[action title]</span>
+				</a>
+			</span>
+		</wicket:panel>
+	</body>
+</html>
+

http://git-wip-us.apache.org/repos/asf/isis/blob/99094b7e/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionlink/ActionLinkPanel.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionlink/ActionLinkPanel.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionlink/ActionLinkPanel.java
new file mode 100644
index 0000000..0b81418
--- /dev/null
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionlink/ActionLinkPanel.java
@@ -0,0 +1,101 @@
+/*
+ *  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.actionlink;
+
+import org.apache.wicket.Page;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.link.AbstractLink;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+
+import org.apache.isis.viewer.wicket.model.models.ActionModel;
+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.pages.PageClassRegistry;
+import org.apache.isis.viewer.wicket.ui.pages.PageClassRegistryAccessor;
+import org.apache.isis.viewer.wicket.ui.panels.PanelAbstract;
+import org.apache.isis.viewer.wicket.ui.util.Links;
+
+/**
+ * {@link PanelAbstract Panel} representing the icon and title of an entity,
+ * as per the provided {@link EntityModel}.
+ */
+public class ActionLinkPanel extends PanelAbstract<ActionModel> {
+
+    private static final long serialVersionUID = 1L;
+
+    private static final String ID_ACTION_LINK_WRAPPER = "actionLinkWrapper";
+    private static final String ID_ACTION_LINK = "actionLink";
+    private static final String ID_ACTION_TITLE = "actionTitle";
+
+    private Label label;
+
+    public ActionLinkPanel(final String id, final ActionModel actionModel) {
+        super(id, actionModel);
+    }
+
+    @Override
+    protected void onBeforeRender() {
+        buildGui();
+        super.onBeforeRender();
+    }
+
+    private void buildGui() {
+        addOrReplaceLinkWrapper();
+    }
+
+    private void addOrReplaceLinkWrapper() {
+        final WebMarkupContainer entityLinkWrapper = addOrReplaceLinkWrapper(getModel());
+        addOrReplace(entityLinkWrapper);
+    }
+
+    private WebMarkupContainer addOrReplaceLinkWrapper(final ActionModel actionModel) {
+
+        final PageParameters pageParameters = actionModel.getPageParameters();
+        final Class<? extends Page> pageClass = getPageClassRegistry().getPageClass(PageType.ACTION_PROMPT);
+        final AbstractLink link = newLink(ID_ACTION_LINK, pageClass, pageParameters);
+        
+        label = new Label(ID_ACTION_TITLE, determineTitle());
+        link.add(label);
+
+        final WebMarkupContainer actionLinkWrapper = new WebMarkupContainer(ID_ACTION_LINK_WRAPPER);
+        actionLinkWrapper.addOrReplace(link);
+        return actionLinkWrapper;
+    }
+
+    private String determineTitle() {
+        return getModel().getActionMemento().getAction().getId();
+    }
+
+    private AbstractLink newLink(final String linkId, final Class<? extends Page> pageClass, final PageParameters pageParameters) {
+        return Links.newBookmarkablePageLink(linkId, pageParameters, pageClass);
+    }
+
+    
+    // ///////////////////////////////////////////////////////////////////
+    // Convenience
+    // ///////////////////////////////////////////////////////////////////
+
+    protected PageClassRegistry getPageClassRegistry() {
+        final PageClassRegistryAccessor pcra = (PageClassRegistryAccessor) getApplication();
+        return pcra.getPageClassRegistry();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/99094b7e/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionlink/ActionLinkPanelFactory.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionlink/ActionLinkPanelFactory.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionlink/ActionLinkPanelFactory.java
new file mode 100644
index 0000000..f36ac36
--- /dev/null
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionlink/ActionLinkPanelFactory.java
@@ -0,0 +1,59 @@
+/*
+ *  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.actionlink;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.model.IModel;
+
+import org.apache.isis.applib.annotation.ActionSemantics;
+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 ActionLinkPanel}.
+ */
+public class ActionLinkPanelFactory extends ComponentFactoryAbstract {
+
+    private static final long serialVersionUID = 1L;
+
+    public ActionLinkPanelFactory() {
+        super(ComponentType.ACTION_LINK, ActionLinkPanel.class);
+    }
+
+    @Override
+    protected ApplicationAdvice appliesTo(IModel<?> model) {
+        if(!(model instanceof ActionModel)) {
+            return ApplicationAdvice.DOES_NOT_APPLY;
+        }
+        final ActionModel actionModel = (ActionModel) model;
+        final ActionSemantics.Of semantics = actionModel.getActionMemento().getAction().getSemantics();
+        return ApplicationAdvice.appliesIf(semantics.isSafeInNature());
+    }
+
+    @Override
+    public Component createComponent(final String id, final IModel<?> model) {
+        final ActionModel actionModel = (ActionModel) model;
+        return new ActionLinkPanel(id, actionModel);
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/99094b7e/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/CssClassFaBehavior.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/CssClassFaBehavior.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/CssClassFaBehavior.java
new file mode 100644
index 0000000..58294c9
--- /dev/null
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/CssClassFaBehavior.java
@@ -0,0 +1,54 @@
+/*
+ *  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.actionmenu;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.behavior.Behavior;
+import org.apache.isis.core.metamodel.facets.members.cssclassfa.CssClassFaPosition;
+
+/**
+ * A behavior that prepends or appends the markup needed to show a Font Awesome icon
+ * for a LinkAndLabel
+ */
+public class CssClassFaBehavior extends Behavior {
+
+    private final String cssClassFa;
+    private final CssClassFaPosition position;
+
+    public CssClassFaBehavior(final String cssClassFa, final CssClassFaPosition position) {
+        this.cssClassFa = cssClassFa;
+        this.position = position;
+    }
+
+    @Override
+    public void beforeRender(final Component component) {
+        super.beforeRender(component);
+        if (position == null || CssClassFaPosition.LEFT == position) {
+            component.getResponse().write("<span class=\""+cssClassFa+" fontAwesomeIcon\"></span>");
+        }
+    }
+
+    @Override
+    public void afterRender(final Component component) {
+        if (CssClassFaPosition.RIGHT == position) {
+            component.getResponse().write("<span class=\""+cssClassFa+" fontAwesomeIcon\"></span>");
+        }
+        super.afterRender(component);
+    }
+}