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 2013/03/28 11:35:31 UTC

[07/20] ISIS-381: mothballing HTML viewer, SQL security, LDAP security

http://git-wip-us.apache.org/repos/asf/isis/blob/91a8000b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/HtmlForm.java
----------------------------------------------------------------------
diff --git a/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/HtmlForm.java b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/HtmlForm.java
new file mode 100644
index 0000000..ce0be9f
--- /dev/null
+++ b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/HtmlForm.java
@@ -0,0 +1,183 @@
+/*
+ *  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.html.component.html;
+
+import java.io.PrintWriter;
+
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.runtime.system.context.IsisContext;
+import org.apache.isis.viewer.html.PathBuilder;
+import org.apache.isis.viewer.html.component.ComponentComposite;
+import org.apache.isis.viewer.html.component.Form;
+
+public class HtmlForm extends ComponentComposite implements Form {
+
+    private final boolean confirm;
+    private final boolean hasNext;
+    private final boolean hasPrevious;
+    private final boolean isEditing;
+    private final String id;
+
+    public HtmlForm(final PathBuilder pathBuilder, final String id, final String action, final int page, final int noOfPages, final boolean isEditing) {
+        this(pathBuilder, id, action, page, noOfPages, false, isEditing);
+    }
+
+    private HtmlForm(final PathBuilder pathBuilder, final String id, final String action, final int page, final int noOfPages, final boolean confirm, final boolean isEditing) {
+        super(pathBuilder);
+        this.id = id;
+        this.confirm = confirm;
+        this.isEditing = isEditing;
+        hasPrevious = page >= 1;
+        hasNext = page < noOfPages - 1;
+    }
+
+    private void addField(final String label, final String field, final String description, final boolean readOnly, final boolean required, final String errorMessage) {
+        String error = "";
+        if (errorMessage != null) {
+            error = "<span class=\"error\"> " + errorMessage + "</span>";
+        }
+        String optional = "";
+        if (!readOnly) {
+            if (!required) {
+                optional = "<span class=\"optional\"> (optional)</span>";
+            } else {
+                optional = "<span class=\"required\"> *</span>";
+            }
+        }
+        add(new Html(pathBuilder, "<div class=\"field\" title=\"" + description + "\"><span class=\"label\">" + label + "</span><span class=\"separator\">: </span> " + field + optional + error + "</div>"));
+    }
+
+    @Override
+    public void addField(final ObjectSpecification spec, final String label, final String title, final String field, final String value, final int noLines, final boolean wrap, final int maxLength, final int typicalLength, final boolean required, final String error) {
+        String inputField;
+        /*
+         * REVIEW the following qualification are a bit limited - it's the
+         * simplest thing than will work - we need to determine from the
+         * specification whether something is boolean type or a password.
+         * 
+         * Also see the note in the Form I/F.
+         */
+        boolean ignoreMandatory = false;
+        if (spec.isOfType(IsisContext.getSpecificationLoader().loadSpecification(boolean.class)) || spec.isOfType(IsisContext.getSpecificationLoader().loadSpecification(Boolean.class))) {
+            final String selected = (value != null && value.toLowerCase().equals("true")) ? "checked " : "";
+            inputField = "<input class=\"value\" type=\"checkbox\" name=\"" + field + "\"" + selected + " value=\"true\"/>";
+            ignoreMandatory = true;
+        } else if (spec.getFullIdentifier().endsWith(".Password")) {
+            final String typicalLengthStr = typicalLength == 0 ? "" : (" size=\"" + typicalLength + "\"");
+            final String maxLengthStr = maxLength == 0 ? "" : (" maxlength=\"" + maxLength + "\"");
+            inputField = "<input class=\"value\" type=\"password\" name=\"" + field + "\"" + typicalLengthStr + maxLengthStr + "value=\"" + value + "\"/>";
+
+        } else if (noLines > 1) {
+            final int w = typicalLength > 0 ? typicalLength / noLines : 50;
+            inputField = "<textarea class=\"value\" type=\"text\" name=\"" + field + "\" rows=\"" + noLines + "\" cols=\"" + w + "\" wrap=\"" + (wrap ? "hard" : "off") + "\">" + value + "</textarea>";
+        } else {
+            final String typicalLengthStr = typicalLength == 0 ? "" : (" size=\"" + typicalLength + "\"");
+            final String maxLengthStr = maxLength == 0 ? "" : (" maxlength=\"" + maxLength + "\"");
+            inputField = "<input class=\"value\" type=\"text\" name=\"" + field + "\"" + typicalLengthStr + maxLengthStr + "value=\"" + value + "\"/>";
+        }
+        addField(label, inputField, title, ignoreMandatory, required, error);
+    }
+
+    public void addFieldName(final String fieldLabel) {
+        add(new Heading(pathBuilder, fieldLabel, 4));
+    }
+
+    @Override
+    public void addLookup(final String fieldLabel, final String description, final String fieldId, final int selectedIndex, final String[] instances, final String[] ids, final boolean required, final String errorMessage) {
+        final StringBuffer testInputField = new StringBuffer();
+        testInputField.append("<select class=\"value\" name=\"" + fieldId + "\">");
+        if (!required) {
+            testInputField.append("<option");
+            if (selectedIndex < 0) {
+                testInputField.append(" selected");
+            }
+            testInputField.append(" value=\"");
+            testInputField.append("null");
+            testInputField.append("\" >[not set]</option>");
+        }
+        for (int i = 0; i < instances.length; i++) {
+            testInputField.append("<option");
+            if (i == selectedIndex) {
+                testInputField.append(" selected");
+            }
+            testInputField.append(" value=\"");
+            testInputField.append(ids[i]);
+            testInputField.append("\" >");
+            testInputField.append(instances[i]);
+            testInputField.append("</option>");
+        }
+        testInputField.append("</select>");
+
+        addField(fieldLabel, testInputField.toString(), description, false, required, errorMessage);
+    }
+
+    @Override
+    public void addReadOnlyField(final String fieldLabel, final String title, final String description) {
+        addField(fieldLabel, "<span class=\"value\">" + title + "</span>", description, true, false, null);
+    }
+
+    @Override
+    public void addReadOnlyCheckbox(final String fieldLabel, final boolean isSet, final String description) {
+        addField(fieldLabel, "<input class=\"value\" type=\"checkbox\" disabled " + (isSet ? "checked" : "") + "/>", description, true, false, null);
+    }
+
+    @Override
+    public void write(final PrintWriter writer) {
+        super.write(writer);
+    }
+
+    @Override
+    protected void writeAfter(final PrintWriter writer) {
+        writer.println("<div class=\"field\">");
+        if (hasNext) {
+            writer.println("<input class=\"action-button\" type=\"submit\" name=\"button\" value=\"Next\"/>");
+            if (isEditing) {
+                writer.println("<input class=\"action-button\" type=\"submit\" name=\"button\" value=\"Save\"/>");
+            } else {
+                writer.println("<input class=\"action-button\" type=\"submit\" name=\"button\" value=\"Finish\"/>");
+            }
+        } else {
+            if (isEditing) {
+                writer.println("<input class=\"action-button\" type=\"submit\" name=\"button\" value=\"Save\"/>");
+            } else {
+                writer.println("<input class=\"action-button\" type=\"submit\" name=\"button\" value=\"Ok\"/>");
+            }
+        }
+        if (hasPrevious) {
+            writer.println("<input class=\"action-button\" type=\"submit\" name=\"button\" value=\"Previous\"/>");
+        }
+        writer.println("<input class=\"action-button\" type=\"submit\" name=\"button\" value=\"Cancel\"/>");
+        writer.println("</div>");
+        writer.println("</form>");
+    }
+
+    @Override
+    protected void writeBefore(final PrintWriter writer) {
+        writer.print("<form name=\"form\" action=\"");
+        writer.print(pathTo("task"));
+        writer.print("\" method=\"post\"");
+        if (confirm) {
+            writer.print(" onSubmit=\"return confirm('Are you sure')\"");
+        }
+        writer.println(">");
+        writer.println("<input type=\"hidden\" name=\"id\" value=\"" + id + "\"/>");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/91a8000b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/HtmlTable.java
----------------------------------------------------------------------
diff --git a/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/HtmlTable.java b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/HtmlTable.java
new file mode 100644
index 0000000..0a14693
--- /dev/null
+++ b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/HtmlTable.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.html.component.html;
+
+import java.io.PrintWriter;
+
+import org.apache.isis.viewer.html.PathBuilder;
+import org.apache.isis.viewer.html.component.Component;
+import org.apache.isis.viewer.html.component.ComponentComposite;
+import org.apache.isis.viewer.html.component.Table;
+
+public class HtmlTable extends ComponentComposite implements Table {
+    private String summary;
+    private final TableHeader header;
+    private final int noColumns;
+    private Row row;
+    private int cellCount;
+    private final boolean addSelector;
+
+    public HtmlTable(final PathBuilder pathBuilder, final int noColumns, final boolean withSelectorColumn) {
+        super(pathBuilder);
+        this.noColumns = noColumns + (withSelectorColumn ? 1 : 0);
+        addSelector = withSelectorColumn;
+        header = new TableHeader(pathBuilder);
+    }
+
+    public Row newRow() {
+        final Row row = new Row(pathBuilder);
+        add(row);
+        return row;
+    }
+
+    @Override
+    public void setSummary(final String summary) {
+        this.summary = summary;
+    }
+
+    @Override
+    public void write(final PrintWriter writer) {
+        writer.print("<table summary=\"" + summary + "\">");
+        writer.print("<tr><th></th>");
+        header.write(writer);
+        writer.println("</tr>");
+        super.write(writer);
+        writer.println("</table>");
+    }
+
+    @Override
+    protected void write(final PrintWriter writer, final Component component) {
+        writer.print("<tr>");
+        component.write(writer);
+        if (addSelector) {
+            writer.print("<td><input type=\"checkbox\" value=\"selected\"/></td>");
+        }
+        writer.println("</tr>");
+    }
+
+    @Override
+    public void addCell(final String value, final boolean truncate) {
+        row.addCell(value, truncate);
+        cellCount++;
+        if (cellCount > noColumns) {
+            throw new HtmlException("Too many cells added: " + cellCount);
+        }
+    }
+
+    @Override
+    public void addEmptyCell() {
+        addCell(new Span("empty-cell", "", null));
+    }
+
+    @Override
+    public void addCell(final Component component) {
+        row.add(component);
+        cellCount++;
+        if (cellCount > noColumns) {
+            throw new HtmlException("Too many cells added: " + cellCount);
+        }
+    }
+
+    @Override
+    public void addColumnHeader(final String name) {
+        header.addHeader(name);
+        cellCount++;
+        if (cellCount > noColumns) {
+            throw new HtmlException("Too many cells added: " + cellCount);
+        }
+    }
+
+    @Override
+    public void addRowHeader(final Component component) {
+        row = new Row(pathBuilder);
+        add(row);
+        cellCount = 0;
+        row.addCell(component);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/91a8000b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/Link.java
----------------------------------------------------------------------
diff --git a/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/Link.java b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/Link.java
new file mode 100644
index 0000000..79d585f
--- /dev/null
+++ b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/Link.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.html.component.html;
+
+import java.io.PrintWriter;
+
+import org.apache.isis.viewer.html.PathBuilder;
+import org.apache.isis.viewer.html.component.Component;
+
+public class Link implements Component {
+
+    private final PathBuilder pathBuilder;
+    private final String link;
+    private final String name;
+    private final String description;
+
+    public Link(final PathBuilder pathBuilder, final String link, final String name, final String description) {
+        this.pathBuilder = pathBuilder;
+        this.link = link;
+        this.name = name;
+        this.description = description;
+    }
+
+    @Override
+    public void write(final PrintWriter writer) {
+        writer.print("<a class=\"link\" title=\"" + description + "\" href=\"" + pathTo(link) + "\">" + name + "</a>");
+    }
+
+    protected String pathTo(final String link) {
+        return pathBuilder.pathTo(link);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/91a8000b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/LogonFormPage.java
----------------------------------------------------------------------
diff --git a/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/LogonFormPage.java b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/LogonFormPage.java
new file mode 100644
index 0000000..c0993ce
--- /dev/null
+++ b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/LogonFormPage.java
@@ -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.
+ */
+
+package org.apache.isis.viewer.html.component.html;
+
+import java.io.PrintWriter;
+
+import org.apache.isis.viewer.html.PathBuilder;
+import org.apache.isis.viewer.html.component.Block;
+import org.apache.isis.viewer.html.component.Component;
+import org.apache.isis.viewer.html.component.DebugPane;
+import org.apache.isis.viewer.html.component.ViewPane;
+
+public class LogonFormPage extends AbstractHtmlPage {
+    private final String user;
+    private final String password;
+    private final boolean registerLink;
+    private final String error;
+
+    public LogonFormPage(final PathBuilder pathBuilder, final String styleSheet, final String header, final String footer, final String user, final String password, final boolean registerLink, final String error) {
+        super(pathBuilder, styleSheet, header, footer);
+        this.user = user;
+        this.password = password;
+        this.registerLink = registerLink;
+        this.error = error;
+    }
+
+    @Override
+    protected void writeContent(final PrintWriter writer) {
+        writer.println("<div id=\"view\">");
+        writer.println("<div class=\"header\">");
+        writer.println("<span class=\"header-text\">Please enter a user name and password.</span>");
+        writer.println("</div>");
+        writer.println("<FORM ACTION=\"" + pathTo("logon") + "\" METHOD=\"post\">");
+        writer.println("<div id=\"content\">");
+        if (error != null) {
+            writer.println("<div class=\"error\">");
+            writer.println(error);
+            writer.println("</div>");
+        }
+        writer.println("<div class=\"field\"><span class=\"label\">User name</span>" + "<span class=\"separator\">: </span><INPUT NAME=\"username\" value=\"" + user + "\"></DIV>");
+        writer.println("<div class=\"field\"><span class=\"label\">Password</span>" + "<span class=\"separator\">: </span><INPUT TYPE=\"password\" NAME=\"password\" value=\"" + password + "\"></DIV>");
+        writer.println("<div class=\"action-button\"><INPUT TYPE=\"submit\" VALUE=\"Log in\" NAME=\"Log in\"></div>");
+        if (registerLink) {
+            writer.print("<a class=\"link nav-link\" title=\"Register new user name and password\" href=\"" + pathTo("register") + "\">register</a>");
+        }
+        writer.println("</div>");
+        writer.println("</FORM>");
+        writer.println("</div>");
+
+    }
+
+    protected String pathTo(final String prefix) {
+        return pathBuilder.pathTo(prefix);
+    }
+
+    @Override
+    public Block getNavigation() {
+        return null;
+    }
+
+    @Override
+    public ViewPane getViewPane() {
+        return null;
+    }
+
+    @Override
+    public void setCrumbs(final Component component) {
+    }
+
+    @Override
+    public void setDebug(final DebugPane debugPane) {
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/91a8000b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/MenuItem.java
----------------------------------------------------------------------
diff --git a/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/MenuItem.java b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/MenuItem.java
new file mode 100644
index 0000000..753be7f
--- /dev/null
+++ b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/MenuItem.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.html.component.html;
+
+import java.io.PrintWriter;
+
+import org.apache.isis.core.metamodel.spec.ActionType;
+import org.apache.isis.viewer.html.PathBuilder;
+import org.apache.isis.viewer.html.component.Component;
+
+public class MenuItem implements Component {
+    private final String actionId;
+    private final String actionName;
+    private final String objectId;
+    private final String actionDescription;
+    private final String reasonDisabled;
+    private final boolean takesParameters;
+    private final ActionType type;
+    private final PathBuilder pathBuilder;
+
+    public MenuItem(final PathBuilder pathBuilder, final String actionId, final String actionName, final String actionDescription, final String reasonDisabled, final ActionType type, final boolean takesParameters, final String objectId) {
+        this.actionId = actionId;
+        this.pathBuilder = pathBuilder;
+        this.actionName = actionName;
+        this.actionDescription = actionDescription;
+        this.reasonDisabled = reasonDisabled;
+        this.type = type;
+        this.takesParameters = takesParameters;
+        this.objectId = objectId;
+    }
+
+    @Override
+    public void write(final PrintWriter writer) {
+        writer.print("<div class=\"menu-item\">");
+        if (isEmpty(reasonDisabled)) {
+            writeEnabledLink(writer);
+        } else {
+            writeDisabledLink(writer);
+        }
+        writer.println("</div>");
+    }
+
+    private boolean isEmpty(final String str) {
+        return str == null || str.length() == 0;
+    }
+
+    private void writeDisabledLink(final PrintWriter writer) {
+        writer.print("<div class=\"disabled\" title=\"");
+        writer.print(reasonDisabled);
+        writer.print("\">");
+        writer.print(actionName);
+        if (takesParameters) {
+            writer.print(". . .");
+        }
+        writer.print("</div>");
+    }
+
+    private void writeEnabledLink(final PrintWriter writer) {
+        writer.print("<a title=\"");
+        writer.print(actionDescription);
+        writer.print("\" href=\"");
+        writer.print(pathTo("method") + "?id=");
+        writer.print(objectId);
+        writer.print("&amp;action=");
+        writer.print(actionId);
+        writer.print("\">");
+        if (type == ActionType.EXPLORATION) {
+            writer.print("[");
+            writer.print(actionName);
+            writer.print("]");
+        } else {
+            writer.print(actionName);
+        }
+        if (takesParameters) {
+            writer.print(". . .");
+        }
+        writer.print("</a>");
+    }
+
+    protected String pathTo(final String prefix) {
+        return pathBuilder.pathTo(prefix);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/91a8000b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/ObjectIcon.java
----------------------------------------------------------------------
diff --git a/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/ObjectIcon.java b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/ObjectIcon.java
new file mode 100644
index 0000000..68211b1
--- /dev/null
+++ b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/ObjectIcon.java
@@ -0,0 +1,78 @@
+/*
+ *  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.html.component.html;
+
+import java.io.PrintWriter;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.viewer.html.PathBuilder;
+import org.apache.isis.viewer.html.component.Component;
+import org.apache.isis.viewer.html.image.ImageLookup;
+import org.apache.isis.viewer.html.request.Request;
+
+public class ObjectIcon implements Component {
+    private final ObjectAdapter element;
+    private final String id;
+    private final String style;
+    private final String description;
+    private final PathBuilder pathBuilder;
+
+    public ObjectIcon(final PathBuilder pathBuilder, final ObjectAdapter element, final String description, final String id, final String style) {
+        this.pathBuilder = pathBuilder;
+        this.element = element;
+        this.description = description;
+        this.id = id;
+        this.style = style;
+    }
+
+    @Override
+    public void write(final PrintWriter writer) {
+        writer.print("<div class=\"");
+        writer.print(style);
+        writer.print("\"");
+        if (description != null) {
+            writer.print(" title=\"");
+            writer.print(description);
+            writer.print("\"");
+        }
+        writer.print(">");
+
+        writer.print("<a href=\"");
+        writer.print(pathTo(Request.OBJECT_COMMAND) + "?id=");
+        writer.print(id);
+        writer.print("\"><img src=\"");
+        writer.print(ImageLookup.image(element));
+        writer.print("\" alt=\"");
+        final String singularName = element.getSpecification().getSingularName();
+        writer.print(singularName);
+        writer.print("\"");
+        writer.print("/>");
+        writer.print(element.titleString());
+        writer.print("</a>");
+
+        writer.println("</div>");
+
+    }
+
+    protected String pathTo(final String prefix) {
+        return pathBuilder.pathTo(prefix);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/91a8000b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/RegisterFormPage.java
----------------------------------------------------------------------
diff --git a/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/RegisterFormPage.java b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/RegisterFormPage.java
new file mode 100644
index 0000000..751e592
--- /dev/null
+++ b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/RegisterFormPage.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.html.component.html;
+
+import java.io.PrintWriter;
+
+import org.apache.isis.viewer.html.PathBuilder;
+import org.apache.isis.viewer.html.component.Block;
+import org.apache.isis.viewer.html.component.Component;
+import org.apache.isis.viewer.html.component.DebugPane;
+import org.apache.isis.viewer.html.component.ViewPane;
+
+public class RegisterFormPage extends AbstractHtmlPage {
+    private final String user;
+    private final String password;
+    private final String error;
+
+    public RegisterFormPage(final PathBuilder pathBuilder, final String styleSheet, final String header, final String footer, final String user, final String password, final String error) {
+        super(pathBuilder, styleSheet, header, footer);
+        this.user = user;
+        this.password = password;
+        this.error = error;
+    }
+
+    @Override
+    protected void writeContent(final PrintWriter writer) {
+        writer.println("<div id=\"view\">");
+        writer.println("<div class=\"header\">");
+        writer.println("<span class=\"header-text\">Please register by providing a user name and password.</span>");
+        writer.println("</div>");
+        writer.println("<FORM ACTION=\"" + pathTo("register") + "\" METHOD=\"post\">");
+        writer.println("<div id=\"content\">");
+        if (error != null) {
+            writer.println("<div class=\"error\">");
+            writer.println(error);
+            writer.println("</div>");
+        }
+        writer.println("<div class=\"field\"><span class=\"label\">User name</span>" + "<span class=\"separator\">: </span><INPUT NAME=\"username\" value=\"" + user + "\"></DIV>");
+        writer.println("<div class=\"field\"><span class=\"label\">Password</span>" + "<span class=\"separator\">: </span><INPUT TYPE=\"password\" NAME=\"password\" value=\"" + password + "\"></DIV>");
+        writer.println("<div class=\"field\"><span class=\"label\">Password (again)</span>" + "<span class=\"separator\">: </span><INPUT TYPE=\"password\" NAME=\"password2\" value=\"" + password + "\"></DIV>");
+        writer.println("<div class=\"action-button\"><INPUT TYPE=\"submit\" VALUE=\"Register\" NAME=\"Register\"></div>");
+        writer.print("<a class=\"link nav-link\" title=\"Back to the login page\" href=\"" + pathTo("login") + "\">back to login</a>");
+        writer.println("</div>");
+        writer.println("</FORM>");
+        writer.println("</div>");
+    }
+
+    protected String pathTo(final String prefix) {
+        return pathBuilder.pathTo(prefix);
+    }
+
+    @Override
+    public Block getNavigation() {
+        return null;
+    }
+
+    @Override
+    public ViewPane getViewPane() {
+        return null;
+    }
+
+    @Override
+    public void setCrumbs(final Component component) {
+    }
+
+    @Override
+    public void setDebug(final DebugPane debugPane) {
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/91a8000b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/Row.java
----------------------------------------------------------------------
diff --git a/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/Row.java b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/Row.java
new file mode 100644
index 0000000..92c4ad5
--- /dev/null
+++ b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/Row.java
@@ -0,0 +1,60 @@
+/*
+ *  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.html.component.html;
+
+import java.io.PrintWriter;
+
+import org.apache.isis.viewer.html.PathBuilder;
+import org.apache.isis.viewer.html.component.Component;
+import org.apache.isis.viewer.html.component.ComponentComposite;
+
+class Row extends ComponentComposite {
+
+    public Row(final PathBuilder pathBuilder) {
+        super(pathBuilder);
+    }
+
+    private static final int TRUNCATE_LENGTH = 18;
+
+    @Override
+    protected void write(final PrintWriter writer, final Component component) {
+        writer.print("<td>");
+        component.write(writer);
+        writer.println("</td>");
+    }
+
+    public void addCell(final String string, final boolean truncate) {
+        String s;
+        if (truncate) {
+            s = string.substring(0, Math.min(TRUNCATE_LENGTH, string.length()));
+            if (string.length() > TRUNCATE_LENGTH) {
+                s += "...";
+            }
+        } else {
+            s = string;
+        }
+        add(new Html(pathBuilder, s));
+    }
+
+    public void addCell(final Component component) {
+        add(component);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/91a8000b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/ServiceComponent.java
----------------------------------------------------------------------
diff --git a/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/ServiceComponent.java b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/ServiceComponent.java
new file mode 100644
index 0000000..29c4cfc
--- /dev/null
+++ b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/ServiceComponent.java
@@ -0,0 +1,62 @@
+/*
+ *  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.html.component.html;
+
+import java.io.PrintWriter;
+
+import org.apache.isis.viewer.html.PathBuilder;
+import org.apache.isis.viewer.html.component.Component;
+import org.apache.isis.viewer.html.image.ImageLookup;
+
+public class ServiceComponent implements Component {
+
+    private final String id;
+    private final String name;
+    private final String iconName;
+    private final PathBuilder pathBuilder;
+
+    public ServiceComponent(final PathBuilder pathBuilder, final String id, final String name, final String iconName) {
+        this.pathBuilder = pathBuilder;
+        this.id = id;
+        this.name = name;
+        this.iconName = iconName;
+    }
+
+    @Override
+    public void write(final PrintWriter writer) {
+        writer.print("<div class=\"item\">");
+
+        writer.print("<a href=\"");
+        writer.print(pathTo("serviceOption") + "?id=");
+        writer.print(id);
+        writer.print("\"><img src=\"");
+        writer.print(ImageLookup.image(iconName));
+        writer.print("\" alt=\"service\" />");
+        writer.print(name);
+        writer.print("</a>");
+
+        writer.println("</div>");
+    }
+
+    protected String pathTo(final String prefix) {
+        return pathBuilder.pathTo(prefix);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/91a8000b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/Span.java
----------------------------------------------------------------------
diff --git a/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/Span.java b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/Span.java
new file mode 100644
index 0000000..b9684f1
--- /dev/null
+++ b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/Span.java
@@ -0,0 +1,55 @@
+/*
+ *  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.html.component.html;
+
+import java.io.PrintWriter;
+
+import org.apache.commons.lang.StringEscapeUtils;
+
+import org.apache.isis.viewer.html.component.Component;
+
+public class Span implements Component {
+    private final String className;
+    private final String value;
+    private final String description;
+
+    public Span(final String className, final String value, final String description) {
+        this.className = className;
+        this.value = value;
+        this.description = description;
+    }
+
+    @Override
+    public void write(final PrintWriter writer) {
+        writer.print("<span class=\"");
+        writer.print(className);
+        writer.print("\"");
+        if (description != null) {
+            writer.print(" title=\"");
+            writer.print(description);
+            writer.print("\"");
+        }
+        writer.print(">");
+        if (value != null) {
+            writer.print(StringEscapeUtils.escapeHtml(value));
+        }
+        writer.print("</span>");
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/91a8000b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/Submenu.java
----------------------------------------------------------------------
diff --git a/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/Submenu.java b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/Submenu.java
new file mode 100644
index 0000000..7d5ad56
--- /dev/null
+++ b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/Submenu.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.html.component.html;
+
+import java.io.PrintWriter;
+
+import org.apache.isis.viewer.html.component.Component;
+
+public class Submenu implements Component {
+
+    private final String menuName;
+    private final Component[] items;
+
+    public Submenu(final String menuName, final Component[] items) {
+        this.menuName = menuName;
+        this.items = items;
+    }
+
+    @Override
+    public void write(final PrintWriter writer) {
+        writer.println("<div class=\"submenu-item\">");
+        writer.println(menuName);
+        for (final Component item : items) {
+            item.write(writer);
+        }
+        writer.println("</div>");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/91a8000b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/TableHeader.java
----------------------------------------------------------------------
diff --git a/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/TableHeader.java b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/TableHeader.java
new file mode 100644
index 0000000..3c75955
--- /dev/null
+++ b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/TableHeader.java
@@ -0,0 +1,44 @@
+/*
+ *  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.html.component.html;
+
+import java.io.PrintWriter;
+
+import org.apache.isis.viewer.html.PathBuilder;
+import org.apache.isis.viewer.html.component.Component;
+import org.apache.isis.viewer.html.component.ComponentComposite;
+
+class TableHeader extends ComponentComposite {
+
+    public TableHeader(final PathBuilder pathBuilder) {
+        super(pathBuilder);
+    }
+
+    @Override
+    protected void write(final PrintWriter writer, final Component component) {
+        writer.print("<th>");
+        component.write(writer);
+        writer.println("</th>");
+    }
+
+    public void addHeader(final String string) {
+        add(new Html(pathBuilder, string));
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/91a8000b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/TextBlock.java
----------------------------------------------------------------------
diff --git a/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/TextBlock.java b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/TextBlock.java
new file mode 100644
index 0000000..8f1716a
--- /dev/null
+++ b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/TextBlock.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.html.component.html;
+
+import java.io.PrintWriter;
+
+import org.apache.isis.viewer.html.PathBuilder;
+import org.apache.isis.viewer.html.component.ComponentAbstract;
+
+class TextBlock extends ComponentAbstract {
+    StringBuffer buffer = new StringBuffer();
+
+    public TextBlock(final PathBuilder pathBuilder, final String text) {
+        super(pathBuilder);
+        append(text);
+    }
+
+    public TextBlock(final PathBuilder pathBuilder) {
+        super(pathBuilder);
+    }
+
+    public void append(final String string) {
+        buffer.append(string);
+    }
+
+    public void appendBold(final String string) {
+        buffer.append("<b>");
+        buffer.append(string);
+        buffer.append("</b>");
+    }
+
+    @Override
+    public void write(final PrintWriter writer) {
+        writeTag(writer, "p class=\"unknown\"");
+        writer.print(buffer.toString());
+        writer.println("</p>");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/91a8000b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/UserSwapLink.java
----------------------------------------------------------------------
diff --git a/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/UserSwapLink.java b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/UserSwapLink.java
new file mode 100644
index 0000000..dad71c2
--- /dev/null
+++ b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/UserSwapLink.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.html.component.html;
+
+import java.io.PrintWriter;
+
+import org.apache.isis.viewer.html.PathBuilder;
+import org.apache.isis.viewer.html.component.Component;
+
+final class UserSwapLink implements Component {
+    private final PathBuilder pathBuilder;
+    private final String name;
+
+    UserSwapLink(final PathBuilder pathBuilder, final String name) {
+        this.pathBuilder = pathBuilder;
+        this.name = name;
+    }
+
+    @Override
+    public void write(final PrintWriter writer) {
+        writer.print("<a class=\"user\" href=\"" + pathTo("setuser") + "?name=");
+        writer.print(name);
+        writer.print("\" title=\"Change user to " + name);
+        writer.print("\">");
+        writer.print(name);
+        writer.println("</a>");
+    }
+
+    protected String pathTo(final String prefix) {
+        return pathBuilder.pathTo(prefix);
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/91a8000b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/ViewDiv.java
----------------------------------------------------------------------
diff --git a/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/ViewDiv.java b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/ViewDiv.java
new file mode 100644
index 0000000..5b99d4e
--- /dev/null
+++ b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/ViewDiv.java
@@ -0,0 +1,145 @@
+/*
+ *  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.html.component.html;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.isis.viewer.html.PathBuilder;
+import org.apache.isis.viewer.html.component.Component;
+import org.apache.isis.viewer.html.component.ComponentComposite;
+import org.apache.isis.viewer.html.component.ViewPane;
+import org.apache.isis.viewer.html.image.ImageLookup;
+
+public class ViewDiv extends ComponentComposite implements ViewPane {
+
+    private String iconName;
+    private String objectId;
+    private Component[] menu = new Component[0];
+    private String title;
+    private final List<String> messages = new ArrayList<String>();
+    private final List<String> warnings = new ArrayList<String>();
+    private String description;
+
+    public ViewDiv(final PathBuilder pathBuilder) {
+        super(pathBuilder);
+    }
+
+    @Override
+    public void setIconName(final String iconName) {
+        this.iconName = iconName;
+    }
+
+    public void setLink(final String objectId) {
+        this.objectId = objectId;
+    }
+
+    @Override
+    public void setMenu(final Component[] menu) {
+        this.menu = menu;
+    }
+
+    @Override
+    public void setTitle(final String title, final String description) {
+        this.title = title;
+        this.description = description;
+    }
+
+    @Override
+    public void setWarningsAndMessages(final List<String> messages, final List<String> warnings) {
+        this.messages.addAll(messages);
+        this.warnings.addAll(warnings);
+    }
+
+    @Override
+    protected void writeBefore(final PrintWriter writer) {
+        writer.println("<div id=\"view\">");
+        writeHeader(writer);
+        writeMenu(writer);
+        writer.println("<div id=\"content\">");
+    }
+
+    @Override
+    protected void writeAfter(final PrintWriter writer) {
+        writer.println("</div>");
+        writer.println("</div>");
+        writeMessages(writer);
+    }
+
+    private void writeMessages(final PrintWriter writer) {
+        if (warnings.size() > 0 || messages.size() > 0) {
+            writer.print("<div class=\"message-header\">");
+            for (final String warning : warnings) {
+                writer.print("<div class=\"warning\">");
+                writer.print(warning);
+                writer.println("</div>");
+            }
+            for (final String message : messages) {
+                writer.print("<div class=\"message\">");
+                writer.print(message);
+                writer.println("</div>");
+            }
+            writer.print("</div>");
+        }
+    }
+
+    private void writeMenu(final PrintWriter writer) {
+        writer.println("<div id=\"menu\">");
+        writer.println("<h3>Actions</h3>");
+        for (final Component element : menu) {
+            element.write(writer);
+        }
+        writer.println("</div>");
+    }
+
+    private void writeHeader(final PrintWriter writer) {
+        writer.print("<div class=\"header\"");
+        if (description != null) {
+            writer.print(" title=\"");
+            writer.print(description);
+            writer.print("\"");
+        }
+        writer.print(">");
+        if (objectId != null) {
+            writer.print("<a href=\"" + pathTo("object") + "?id=");
+            writer.print(objectId);
+            writer.print("\">");
+        }
+        if (iconName != null) {
+            writer.print("<span class=\"header-icon\"><img src=\"");
+            writer.print(ImageLookup.image(iconName));
+            writer.print("\" alt=\"icon\" /></span>");
+        }
+        writer.print("<span class=\"header-text\"");
+        writer.print(">");
+        writer.print(title);
+        writer.println("</span>");
+        if (objectId != null) {
+            writer.print("</a>");
+        }
+        writer.println("</div>");
+    }
+
+    @Override
+    protected String pathTo(final String prefix) {
+        return pathBuilder.pathTo(prefix);
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/91a8000b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/WebViewerException.java
----------------------------------------------------------------------
diff --git a/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/WebViewerException.java b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/WebViewerException.java
new file mode 100644
index 0000000..0da52ca
--- /dev/null
+++ b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/component/html/WebViewerException.java
@@ -0,0 +1,43 @@
+/*
+ *  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.html.component.html;
+
+import org.apache.isis.core.commons.exceptions.IsisException;
+
+public class WebViewerException extends IsisException {
+    private static final long serialVersionUID = 1L;
+
+    public WebViewerException() {
+        super();
+    }
+
+    public WebViewerException(final String msg, final Throwable cause) {
+        super(msg, cause);
+    }
+
+    public WebViewerException(final String msg) {
+        super(msg);
+    }
+
+    public WebViewerException(final Throwable cause) {
+        super(cause);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/91a8000b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/context/CollectionMapping.java
----------------------------------------------------------------------
diff --git a/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/context/CollectionMapping.java b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/context/CollectionMapping.java
new file mode 100644
index 0000000..e97f16e
--- /dev/null
+++ b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/context/CollectionMapping.java
@@ -0,0 +1,154 @@
+/*
+ *  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.html.context;
+
+import java.util.Iterator;
+import java.util.List;
+
+import com.google.common.collect.Lists;
+
+import org.apache.isis.core.commons.debug.DebugBuilder;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager;
+import org.apache.isis.core.metamodel.facets.collections.modify.CollectionFacet;
+import org.apache.isis.core.metamodel.facets.collections.modify.CollectionFacetUtils;
+import org.apache.isis.core.metamodel.facets.typeof.TypeOfFacet;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.runtime.system.context.IsisContext;
+import org.apache.isis.core.runtime.system.persistence.PersistenceSession;
+
+/**
+ * Has value semantics based on the value semantics of the underlying list that
+ * it wraps.
+ */
+public class CollectionMapping implements Iterable<String> {
+    
+    private final List<String> list = Lists.newArrayList();
+    private final ObjectSpecification elementSpecification;
+
+    public CollectionMapping(final Context context, final ObjectAdapter collection) {
+        final TypeOfFacet typeOfFacet = collection.getSpecification().getFacet(TypeOfFacet.class);
+        elementSpecification = typeOfFacet.valueSpec();
+
+        final CollectionFacet collectionFacet = CollectionFacetUtils.getCollectionFacetFromSpec(collection);
+        
+        for (ObjectAdapter element : collectionFacet.iterable(collection)) {
+            final String objectId = context.mapObject(element);
+            list.add(objectId);
+        }
+    }
+
+    public ObjectAdapter getCollection(final Context context) {
+        final List<Object> elementPojos = Lists.newArrayList();
+        
+        for (String elementId : list) {
+            final ObjectAdapter adapter = context.getMappedObject(elementId);
+            final Object pojo = adapter.getObject();
+            elementPojos.add(pojo);
+        }
+        return getAdapterManager().adapterFor(elementPojos);
+    }
+
+    public ObjectSpecification getElementSpecification() {
+        return elementSpecification;
+    }
+
+    
+    public Iterator<String> iterator() {
+        return list.iterator();
+    }
+
+    public boolean contains(final String id) {
+        for (String elementId : list) {
+            if (elementId.equals(id)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public void remove(final String existingId) {
+        for (final String elementId: list) {
+            if (elementId.equals(existingId)) {
+                list.remove(existingId);
+                break;
+            }
+        }
+    }
+
+
+    // //////////////////////////////////////////////////////
+    // debugging
+    // //////////////////////////////////////////////////////
+
+    public void debug(final DebugBuilder debug) {
+        debug.indent();
+        for (String elementId : list) {
+            debug.appendln(elementId);
+        }
+        debug.unindent();
+    }
+
+
+
+    // //////////////////////////////////////////////////////
+    // equals, hashCode
+    // //////////////////////////////////////////////////////
+
+    /**
+     * Value semantics based on the identity of the underlying list that this
+     * wraps.
+     */
+    @Override
+    public int hashCode() {
+        return list.hashCode();
+    }
+
+    @Override
+    public boolean equals(final Object other) {
+        if (other == this) {
+            return true;
+        }
+        if (other == null) {
+            return false;
+        }
+        if (other.getClass() != this.getClass()) {
+            return false;
+        }
+        return equals((CollectionMapping) other);
+    }
+
+    public boolean equals(final CollectionMapping other) {
+        return this.list.equals(other.list);
+    }
+
+    // //////////////////////////////////////////////////////
+    // Dependencies (from context)
+    // //////////////////////////////////////////////////////
+
+    private static AdapterManager getAdapterManager() {
+        return getPersistenceSession().getAdapterManager();
+    }
+
+    private static PersistenceSession getPersistenceSession() {
+        return IsisContext.getPersistenceSession();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/91a8000b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/context/Context.java
----------------------------------------------------------------------
diff --git a/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/context/Context.java b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/context/Context.java
new file mode 100644
index 0000000..c6545fa
--- /dev/null
+++ b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/context/Context.java
@@ -0,0 +1,654 @@
+/*
+ *  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.html.context;
+
+import java.io.Serializable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+import org.apache.log4j.Logger;
+
+import org.apache.isis.core.commons.authentication.AuthenticationSession;
+import org.apache.isis.core.commons.debug.DebugBuilder;
+import org.apache.isis.core.commons.ensure.Assert;
+import org.apache.isis.core.commons.exceptions.IsisException;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager;
+import org.apache.isis.core.metamodel.adapter.version.ConcurrencyException;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
+import org.apache.isis.core.runtime.system.context.IsisContext;
+import org.apache.isis.core.runtime.system.persistence.PersistenceSession;
+import org.apache.isis.core.runtime.system.transaction.UpdateNotifier;
+import org.apache.isis.core.runtime.userprofile.UserProfile;
+import org.apache.isis.viewer.html.component.Block;
+import org.apache.isis.viewer.html.component.ComponentFactory;
+import org.apache.isis.viewer.html.crumb.CollectionCrumb;
+import org.apache.isis.viewer.html.crumb.Crumb;
+import org.apache.isis.viewer.html.crumb.ObjectCrumb;
+import org.apache.isis.viewer.html.crumb.ObjectFieldCrumb;
+import org.apache.isis.viewer.html.crumb.TaskCrumb;
+import org.apache.isis.viewer.html.request.Request;
+import org.apache.isis.viewer.html.task.Task;
+
+public class Context implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    private static final Logger LOG = Logger.getLogger(Context.class);
+    
+    private transient ObjectHistory history;
+
+    private final ComponentFactory componentFactory;
+
+    private final Map<String, RootAdapterMapping> objectMap = Maps.newHashMap();
+    private final Map<String, RootAdapterMapping> serviceMap = Maps.newHashMap();
+    private final Map<String, CollectionMapping> collectionMap = Maps.newHashMap();
+    private final Map<String, ObjectAction> actionMap = Maps.newHashMap();
+    
+    private final Stack<Crumb> crumbs = new Stack<Crumb>();
+    private final List<String> messages = Lists.newArrayList();
+    private final List<String> warnings = Lists.newArrayList();
+
+    private AuthenticationSession session;
+    
+    private boolean isValid;
+    private int max;
+
+    // ////////////////////////////////////////////////////
+    // constructor, init
+    // ////////////////////////////////////////////////////
+
+    public Context(final ComponentFactory componentFactory) {
+        this.componentFactory = componentFactory;
+        this.isValid = true;
+        clearMessagesAndWarnings();
+        LOG.debug(this + " created with " + componentFactory);
+    }
+
+    public void init() {
+        final AdapterManager adapterManager = getAdapterManager();
+        final List<Object> services = getUserProfile().getPerspective().getServices();
+        for (final Object service : services) {
+            final ObjectAdapter serviceAdapter = adapterManager.adapterFor(service);
+            if (serviceAdapter == null) {
+                LOG.warn("unable to find service: " + service + "; skipping");
+                continue;
+            }
+            mapObject(serviceAdapter);
+        }
+        serviceMap.putAll(objectMap);
+    }
+
+
+    public ComponentFactory getComponentFactory() {
+        return componentFactory;
+    }
+
+
+    // ////////////////////////////////////////////////////
+    // validity 
+    // ////////////////////////////////////////////////////
+
+    public boolean isValid() {
+        return isValid;
+    }
+
+    public void invalidate() {
+        isValid = false;
+    }
+    
+    
+    // ////////////////////////////////////////////////////
+    // session 
+    // ////////////////////////////////////////////////////
+
+    public boolean isLoggedIn() {
+        return session != null;
+    }
+
+    public void setSession(final AuthenticationSession currentSession) {
+        this.session = currentSession;
+    }
+
+    public AuthenticationSession getSession() {
+        return session;
+    }
+
+
+    // ////////////////////////////////////////////////////
+    // processChanges 
+    // ////////////////////////////////////////////////////
+
+    public void processChanges() {
+        final List<ObjectAdapter> disposedObjects = getUpdateNotifier().getDisposedObjects();
+        for (final ObjectAdapter adapter : disposedObjects) {
+            final RootAdapterMapping mapping = persistentOrTransientObjectMappingFor(adapter);
+            if (objectMap.containsValue(mapping)) {
+                processChangeFor(mapping);
+            }
+        }
+    }
+
+    private void processChangeFor(final RootAdapterMapping mapping) {
+        final String existingId = findExistingInMap(objectMap, mapping);
+        getObjectHistory().remove(existingId);
+
+        final List<Crumb> relatedCrumbs = Lists.newArrayList();
+        for (final Crumb crumb : getCrumbs()) {
+            /*
+             * if (crumb.isFor(existingId)) { relatedCrumbs.add(crumb);
+             * }
+             */}
+        for (final Crumb crumb : relatedCrumbs) {
+            crumbs.remove(crumb);
+        }
+
+        for (final CollectionMapping collection : collectionMap.values()) {
+            collection.remove(existingId);
+        }
+        objectMap.remove(existingId);
+    }
+
+    // ////////////////////////////////////////////////////
+    // changeContext 
+    // ////////////////////////////////////////////////////
+
+    public Request changeContext(final int id) {
+        while (crumbs.size() - 1 > id) {
+            crumbs.pop();
+        }
+        final Crumb c = crumbs.lastElement();
+        return c.changeContext();
+    }
+
+
+    
+    // ////////////////////////////////////////////////////
+    // Crumbs
+    // ////////////////////////////////////////////////////
+
+    public void addCollectionFieldCrumb(final String collectionFieldName) {
+        crumbs.push(new ObjectFieldCrumb(collectionFieldName));
+    }
+
+    public void addCollectionCrumb(final String id) {
+        while (crumbs.size() > 0 && !(crumbs.lastElement() instanceof TaskCrumb)) {
+            crumbs.pop();
+        }
+        crumbs.push(new CollectionCrumb(id, getMappedCollection(id)));
+    }
+
+    public void setObjectCrumb(final ObjectAdapter object) {
+        while (crumbs.size() > 0 && !(crumbs.lastElement() instanceof TaskCrumb)) {
+            crumbs.pop();
+        }
+        final String id = mapObject(object);
+        crumbs.push(new ObjectCrumb(id, object));
+    }
+
+
+    public void addTaskCrumb(final Task task) {
+        while (crumbs.size() > 1 && !(crumbs.lastElement() instanceof ObjectCrumb)) {
+            crumbs.pop();
+        }
+        Assert.assertNotNull(task);
+        Assert.assertTrue(!isTask());
+        task.init(this);
+        crumbs.push(new TaskCrumb(task));
+    }
+
+    
+    public Crumb[] getCrumbs() {
+        final int size = crumbs.size();
+        final Crumb[] taskList = new Crumb[size];
+        for (int i = 0; i < crumbs.size(); i++) {
+            taskList[i] = crumbs.get(i);
+        }
+        return taskList;
+    }
+
+    public boolean[] isLinked() {
+        final int size = crumbs.size();
+        final boolean[] isLinked = new boolean[size];
+        for (int i = size - 1; i >= 0; i--) {
+            final boolean isTask = crumbs.elementAt(i) instanceof TaskCrumb;
+            isLinked[i] = i != size - 1;
+            if (isTask) {
+                break;
+            }
+        }
+        return isLinked;
+    }
+
+
+    // ////////////////////////////////////////////////////
+    // Mappings
+    // ////////////////////////////////////////////////////
+
+    public String mapAction(final ObjectAction action) {
+        return findExistingOrAddToMap(actionMap, action);
+    }
+
+    public String mapObject(final ObjectAdapter adapter) {
+        return findExistingOrAddToMap(objectMap, persistentOrTransientObjectMappingFor(adapter));
+    }
+
+    public String mapCollection(final ObjectAdapter collection) {
+        return findExistingOrAddToMap(collectionMap, new CollectionMapping(this, collection));
+    }
+
+    public ObjectAction getMappedAction(final String id) {
+        return getMappedInstance(actionMap, id);
+    }
+
+    public ObjectAdapter getMappedCollection(final String id) {
+        final CollectionMapping map = getMappedInstance(collectionMap, id);
+        return map.getCollection(this);
+    }
+
+    public ObjectAdapter getMappedObject(final String id) {
+        final RootAdapterMapping mappedObject = getMappedInstance(objectMap, id);
+        final ObjectAdapter adapter = mappedObject.getObject();
+
+        // ensure resolved if currently a ghost;
+        // start/end xactn if required
+        if (adapter.representsPersistent() && adapter.isGhost()) {
+            getPersistenceSession().resolveImmediately(adapter);
+        }
+
+        try {
+            mappedObject.checkVersion(adapter);
+        } catch (final ConcurrencyException e) {
+            LOG.info("concurrency conflict: " + e.getMessage());
+            messages.clear();
+            messages.add(e.getMessage());
+            messages.add("Reloaded object " + adapter.titleString());
+            updateVersion(adapter);
+        }
+        return adapter;
+    }
+
+    
+    private <T> String findExistingOrAddToMap(final Map<String,T> map, final T object) {
+        Assert.assertNotNull(object);
+        if (map.containsValue(object)) {
+            return findExistingInMap(map, object);
+        } else {
+            return addToMap(map, object);
+        }
+    }
+
+    private <T> String addToMap(final Map<String,T> map, final T object) {
+        
+        String id;
+        // bit hacky...
+        if(object instanceof RootAdapterMapping) {
+            // object or (internal) collection
+            RootAdapterMapping adapterMapping = (RootAdapterMapping) object;
+            id = adapterMapping.getOidStr();
+        } else {
+            max++;
+            id = "" + max;
+        }
+        map.put(id, object);
+
+        final String mapName = map == objectMap ? "object" : (map == collectionMap ? "collection" : "action");
+        if(LOG.isDebugEnabled()) {
+            LOG.debug("add " + object + " to " + mapName + " as #" + id);
+        }
+        return id;
+    }
+
+    private <T> String findExistingInMap(final Map<String, T> map, final Object object) {
+        for (final String id : map.keySet()) {
+            if (object.equals(map.get(id))) {
+                return id;
+            }
+        }
+        throw new IsisException();
+    }
+
+    private <T> T getMappedInstance(final Map<String,T> map, final String id) {
+        final T object = map.get(id);
+        if (object == null) {
+            final String mapName = mapNameFor(map);
+            throw new ObjectLookupException("No object in " + mapName + " map with id " + id);
+        }
+        return object;
+    }
+
+    private <T> String mapNameFor(final Map<String, T> map) {
+        return (map == objectMap) ? "object" : (map == collectionMap ? "collection" : "action");
+    }
+
+    private static RootAdapterMapping persistentOrTransientObjectMappingFor(final ObjectAdapter adapter) {
+        return adapter.isTransient() ? new TransientRootAdapterMapping(adapter) : new PersistentRootAdapterMapping(adapter);
+    }
+
+    
+
+    // ////////////////////////////////////////////////////
+    // Instances 
+    // ////////////////////////////////////////////////////
+
+    /**
+     * Returns an array of instances of the specified type that are currently
+     * known in the current context, ie have been recently seen by the user.
+     * 
+     * <p>
+     * These will be resolved if required, with a transaction created (and
+     * ended) if required.
+     */
+    public ObjectAdapter[] getKnownInstances(final ObjectSpecification type) {
+
+        final List<ObjectAdapter> instances = Lists.newArrayList();
+
+        for (final String id : objectMap.keySet()) {
+            final ObjectAdapter adapter = getMappedObject(id);
+            
+            getPersistenceSession().resolveImmediately(adapter);
+            if (adapter.getSpecification().isOfType(type)) {
+                instances.add(adapter);
+            }
+        }
+
+        final ObjectAdapter[] array = new ObjectAdapter[instances.size()];
+        instances.toArray(array);
+        return array;
+    }
+
+    public void restoreAllObjectsToLoader() {
+        for (Map.Entry<String, RootAdapterMapping> mapEntry : objectMap.entrySet()) {
+            final RootAdapterMapping rootAdapterMapping = mapEntry.getValue();
+            rootAdapterMapping.restoreToLoader();
+        }
+    }
+
+    public void purgeObjectsAndCollections() {
+        
+        clearMessagesAndWarnings();
+
+        final Map<String, CollectionMapping> collMappingById = Maps.newHashMap();
+        final Map<String, RootAdapterMapping> objectMappingById = Maps.newHashMap();
+
+        for (HistoryEntry entry : getObjectHistory()) {
+            if (entry.type == HistoryEntry.OBJECT) {
+                copyObjectMapping(objectMappingById, entry);
+            } else if (entry.type == HistoryEntry.COLLECTION) {
+                copyCollectionMapping(collMappingById, objectMappingById, entry);
+            }
+        }
+
+        collectionMap.clear();
+        collectionMap.putAll(collMappingById);
+        objectMap.clear();
+        objectMap.putAll(objectMappingById);
+        objectMap.putAll(serviceMap);
+    }
+
+    private void copyObjectMapping(final Map<String, RootAdapterMapping> objectMappingById, HistoryEntry entry) {
+        final RootAdapterMapping item = objectMap.get(entry.id);
+        objectMappingById.put(entry.id, item);
+        
+        LOG.debug("copied object map " + entry.id + " for " + item);
+        item.updateVersion();
+    }
+    
+    private void copyCollectionMapping(final Map<String, CollectionMapping> collMappingById, final Map<String, RootAdapterMapping> objectMappingById, HistoryEntry entry) {
+        
+        final CollectionMapping coll = collectionMap.get(entry.id);
+        collMappingById.put(entry.id, coll);
+        LOG.debug("copied collection map for " + coll);
+        
+        for (String elementId : coll) {
+            final RootAdapterMapping objMapping = objectMap.get(elementId);
+            
+            if (objMapping != null) {
+                objectMappingById.put(elementId, objMapping);
+                
+                LOG.debug("copied object map " + elementId + " for " + objMapping);
+                objMapping.updateVersion();
+            }
+        }
+    }
+
+    // ////////////////////////////////////////////////////
+    // Tasks 
+    // ////////////////////////////////////////////////////
+
+    public Task getTask(final String taskId) {
+        Task task = null;
+        for (int i = crumbs.size() - 1; i >= 0; i--) {
+            final Object crumb = crumbs.get(i);
+            if (crumb instanceof TaskCrumb) {
+                final TaskCrumb taskCrumb = (TaskCrumb) crumb;
+                final String id = taskCrumb.getTask().getId();
+                if (taskId.equals(id)) {
+                    task = taskCrumb.getTask();
+                    break;
+                }
+            }
+        }
+        return task;
+    }
+
+    public void endTask(final Task task) {
+        for (int i = crumbs.size() - 1; i >= 0; i--) {
+            final Object crumb = crumbs.get(i);
+            if (crumb instanceof TaskCrumb) {
+                final TaskCrumb taskCrumb = (TaskCrumb) crumb;
+                if (taskCrumb.getTask() == task) {
+                    crumbs.remove(taskCrumb);
+                    return;
+                }
+            }
+        }
+        throw new IsisException("No crumb found for " + task);
+    }
+
+
+    public Request cancelTask(final Task task) {
+        if (task != null) {
+            endTask(task);
+        }
+
+        // REVIEW does this take us back to the right object?
+        final Crumb crumb = crumbs.get(crumbs.size() - 1);
+        return crumb.changeContext();
+    }
+
+    private boolean isTask() {
+        final int index = crumbs.size() - 1;
+        return index >= 0 && crumbs.get(index) instanceof TaskCrumb;
+    }
+
+    
+
+    // ////////////////////////////////////////////////////
+    // Messages 
+    // ////////////////////////////////////////////////////
+
+    public List<String> getMessages() {
+        return messages;
+    }
+
+    public String getMessage(final int i) {
+        return messages.get(i);
+    }
+
+    public List<String> getWarnings() {
+        return warnings;
+    }
+
+    public String getWarning(final int i) {
+        return warnings.get(i);
+    }
+
+    public void setMessagesAndWarnings(final List<String> messages, final List<String> warnings) {
+        this.messages.clear();
+        this.messages.addAll(messages);
+        this.warnings.clear();
+        this.warnings.addAll(warnings);
+    }
+
+    public void clearMessagesAndWarnings() {
+        messages.clear();
+        warnings.clear();
+    }
+
+
+    public void listHistory(final Context context, final Block navigation) {
+        getObjectHistory().listObjects(context, navigation);
+    }
+
+    public void addObjectToHistory(final String idString) {
+        getObjectHistory().addObject(idString);
+    }
+
+    public void addCollectionToHistory(final String idString) {
+        getObjectHistory().addCollection(idString);
+    }
+
+    // ////////////////////////////////////////////////////
+    // 
+    // ////////////////////////////////////////////////////
+
+
+    public void updateVersion(final ObjectAdapter adapter) {
+        if (adapter.isTransient()) {
+            return;
+        }
+
+        // TODO refactor this for clarity: removes existing mapping and replaces
+        // it with a new one as it
+        // contains the new version
+        final String id = mapObject(adapter);
+        if (id != null) {
+            final RootAdapterMapping mapping = new PersistentRootAdapterMapping(adapter);
+            objectMap.put(id, mapping);
+        }
+    }
+
+
+    // ////////////////////////////////////////////////////
+    // Debug 
+    // ////////////////////////////////////////////////////
+
+    public void debug(final DebugBuilder debug) {
+        debug.startSection("Web Session Context");
+        debug.appendAsHexln("hash", hashCode());
+        debug.appendln("session", session);
+        debug.appendln("is valid", isValid);
+        debug.appendln("next id", max);
+        debug.appendln("factory", componentFactory);
+        debug.appendln("is task", isTask());
+
+        debug.appendln("crumbs (" + crumbs.size() + ")");
+
+        debug.indent();
+        for (int i = 0; i < crumbs.size(); i++) {
+            final Crumb crumb = crumbs.get(i);
+            debug.appendln(i + 1 + ". " + crumb);
+            debug.indent();
+            crumb.debug(debug);
+            debug.unindent();
+        }
+        debug.unindent();
+
+        debug.startSection("Objects");
+        for (final String id : objectMap.keySet()) {
+            final RootAdapterMapping object = objectMap.get(id);
+            debug.appendln(id + " -> " + object.getOidStr());
+            debug.indent();
+            object.debugData(debug);
+            debug.unindent();
+        }
+        debug.endSection();
+
+        debug.startSection("Collections");
+        for (final String id : collectionMap.keySet()) {
+            final CollectionMapping coll = collectionMap.get(id);
+            debug.appendln(id + " -> collection of " + coll.getElementSpecification().getPluralName());
+            coll.debug(debug);
+        }
+        debug.endSection();
+
+        debug.startSection("Actions");
+        debugMap(debug, actionMap);
+        debug.endSection();
+
+        debug.startSection("History");
+        getObjectHistory().debug(debug);
+        debug.endSection();
+
+        debug.endSection();
+    }
+
+    private void debugMap(final DebugBuilder debug, final Map<String,?> map) {
+        final Iterator<String> names = map.keySet().iterator();
+        while (names.hasNext()) {
+            final String name = names.next();
+            debug.appendln(name + " -> " + map.get(name));
+        }
+    }
+
+
+    // ////////////////////////////////////////////////////
+    // Non-serializable
+    // ////////////////////////////////////////////////////
+    
+    private ObjectHistory getObjectHistory() {
+        if(history == null) {
+            history = new ObjectHistory();
+        }
+        return history;
+    }
+
+
+    // ////////////////////////////////////////////////////
+    // Dependencies (from context)
+    // ////////////////////////////////////////////////////
+
+    protected UserProfile getUserProfile() {
+        return IsisContext.getUserProfile();
+    }
+
+    protected AdapterManager getAdapterManager() {
+        return getPersistenceSession().getAdapterManager();
+    }
+
+
+    protected PersistenceSession getPersistenceSession() {
+        return IsisContext.getPersistenceSession();
+    }
+
+    protected UpdateNotifier getUpdateNotifier() {
+        return IsisContext.getUpdateNotifier();
+    }
+
+
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/91a8000b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/context/HistoryEntry.java
----------------------------------------------------------------------
diff --git a/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/context/HistoryEntry.java b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/context/HistoryEntry.java
new file mode 100644
index 0000000..836a0e2
--- /dev/null
+++ b/mothballed/component/viewer/html/impl/src/main/java/org/apache/isis/viewer/html/context/HistoryEntry.java
@@ -0,0 +1,49 @@
+/*
+ *  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.html.context;
+
+public class HistoryEntry {
+    public static final int OBJECT = 1;
+    public static final int COLLECTION = 2;
+
+    public final int type;
+    public final String id;
+
+    public HistoryEntry(final String idString, final int type) {
+        this.id = idString;
+        this.type = type;
+    }
+
+    @Override
+    public int hashCode() {
+        return id.hashCode();
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return ((HistoryEntry) obj).id.equals(id);
+    }
+
+    @Override
+    public String toString() {
+        return (type == OBJECT ? "object " : "collection ") + id;
+    }
+
+}