Author: buildbot
Date: Tue Nov 18 20:55:23 2014
New Revision: 929689

Log:
Staging update by buildbot for isis

Added:
    websites/staging/isis/trunk/content/tutorials/
    websites/staging/isis/trunk/content/tutorials/apacheconeu-2014.html
Modified:
    websites/staging/isis/trunk/cgi-bin/   (props changed)
    websites/staging/isis/trunk/content/   (props changed)
    websites/staging/isis/trunk/content/documentation.html

Propchange: websites/staging/isis/trunk/cgi-bin/
------------------------------------------------------------------------------
--- cms:source-revision (original)
+++ cms:source-revision Tue Nov 18 20:55:23 2014
@@ -1 +1 @@
-1640372
+1640424

Propchange: websites/staging/isis/trunk/content/
------------------------------------------------------------------------------
--- cms:source-revision (original)
+++ cms:source-revision Tue Nov 18 20:55:23 2014
@@ -1 +1 @@
-1640372
+1640424

Modified: websites/staging/isis/trunk/content/documentation.html
==============================================================================
--- websites/staging/isis/trunk/content/documentation.html (original)
+++ websites/staging/isis/trunk/content/documentation.html Tue Nov 18 20:55:23 
2014
@@ -741,11 +741,11 @@
 <ul>
 <li><a href="core/unittestsupport.html">Unit Test Support</a></li>
 <li><a href="core/integtestsupport.html">Integration Test Support</a></li>
-<li><a href="core/specsupport-and-integtestsupport.html">BDD/Integ Test 
Support</a></li>
-<li><a href="./more-advanced-topics/Fixture-Scripts.html">Fixture Scripts</a>
+<li><a href="core/specsupport-and-integtestsupport.html">BDD/Integ Test 
Support</a>
 <!--</li>
-<li><a 
href="./more-advanced-topics/03-Fixtures-and-SwitchUser.html">Fixtures</a> (out 
of date)
+<li><a href="./more-advanced-topics/Fixture-Scripts.html">Fixture Scripts</a>
 --></li>
+<li><a 
href="./more-advanced-topics/03-Fixtures-and-SwitchUser.html">Fixtures</a> (out 
of date)</li>
 <li><a 
href="components/objectstores/jdo/IsisConfigurationForJdoIntegTests.html">IsisConfigurationForJdoIntegTests</a></li>
 </ul>
 

Added: websites/staging/isis/trunk/content/tutorials/apacheconeu-2014.html
==============================================================================
--- websites/staging/isis/trunk/content/tutorials/apacheconeu-2014.html (added)
+++ websites/staging/isis/trunk/content/tutorials/apacheconeu-2014.html Tue Nov 
18 20:55:23 2014
@@ -0,0 +1,2529 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+
+    <meta charset="utf-8">
+      <title></title>
+    <meta name="description" content="">
+    <meta name="author" content="">
+
+    <!-- Le HTML5 shim, for IE6-8 support of HTML elements -->
+    <!--[if lt IE 9]>
+      <script 
src="http://html5shim.googlecode.com/svn/trunk/html5.js";></script>
+    <![endif]-->
+
+    
+    <!-- Le styles -->
+    <link href="./../bootstrap-3.0.0/css/bootstrap.css" rel="stylesheet">
+    <link href="./../prettify.css" rel="stylesheet">
+
+    <style type="text/css">
+        body {
+          padding-top: 60px;
+        }
+        .sprite {
+            display: inline-block;
+            height: 20px;
+            margin: 0 auto 4px;
+            outline: medium none;
+            text-indent: -999em;
+            width: 24px;
+            background-image: url('./../images/sprites.png');
+            background-repeat: no-repeat;
+            overflow: hidden;
+            cursor: pointer;
+        }
+        .edit-page {
+            display: inline-block;
+            height: 20px;
+            margin: 0 auto 4px;
+            outline: medium none;
+            text-indent: -999em;
+            width: 24px;
+            background-image: url('./../images/edit.png');
+            background-repeat: no-repeat;
+            overflow: hidden;
+            cursor: pointer;
+        }
+        .fb-share {
+            background-position: 0px -40px;
+        }
+        .gp-share {
+            background-position: 0px 0px;
+        }
+        .tw-share {
+            background-position: 0px -80px;
+        }
+        .markdown-content {
+            min-height: 500px;
+        }
+        .book-image img {
+          border: 1px;
+          border-style: solid;
+        }
+        .release-matrix .heading {
+            background-color: #eeeeee;
+        }
+        .release-matrix .new {
+            color: #dd0000;
+            font-weight: bolder;
+        }
+        .stub,.note {
+            position: relative;
+            padding: 7px 15px;
+            margin-bottom: 18px;
+            color: #404040;
+            background-color: #eedc94;
+            background-repeat: repeat-x;
+            background-image: -khtml-gradient(linear, left top, left bottom, 
from(#fceec1), to(#eedc94));
+            background-image: -moz-linear-gradient(top, #fceec1, #eedc94);
+            background-image: -ms-linear-gradient(top, #fceec1, #eedc94);
+            background-image: -webkit-gradient(linear, left top, left bottom, 
color-stop(0%, #fceec1), color-stop(100%, #eedc94));
+            background-image: -webkit-linear-gradient(top, #fceec1, #eedc94);
+            background-image: -o-linear-gradient(top, #fceec1, #eedc94);
+            background-image: linear-gradient(top, #fceec1, #eedc94);
+            filter: 
progid:DXImageTransform.Microsoft.gradient(startColorstr='#fceec1', 
endColorstr='#eedc94', GradientType=0);
+            text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+            border-color: #eedc94 #eedc94 #e4c652;
+            border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 
0.25);
+            text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
+            border-width: 1px;
+            border-style: solid;
+            -webkit-border-radius: 4px;
+            -moz-border-radius: 4px;
+            border-radius: 4px;
+            -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
+            -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
+            box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
+        }
+        
+        div.XXXisis-tweak form {
+            margin-top: 6px;
+            margin-bottom: -2px;
+        }
+        
+        div.row div.col-md-12 {
+          border-top: 1px solid #eeeeee;
+       }
+        
+        
+        /* Isis specific stuff */
+
+        div.row div.col-md-12 {
+          margin-top: 20px;
+        }
+
+        .container {
+          width: 940px;
+        }
+
+        .markdown-content .documentation .span-one-third p {
+          margin-bottom: 0px;
+        }
+
+        .container .markdown-content .group,
+        .markdown-content .documentation .group {
+          margin-top: 9px;
+        }
+
+        .container .group h2,
+        .documentation .group h2 {
+          border-bottom: 1px solid #DDD
+        }
+
+        .container h2 a[name],
+        .documentation h2 a[name] {
+            padding-top: 50px;
+            margin-top: -50px;
+        }
+        
+        .container h2 a[name],
+        .container h3 a[name],
+        .container h4 a[name],
+        .documentation h2 a[name],
+        .documentation h3 a[name],
+        .documentation h4 a[name] {
+            color: black;
+            display: inline-block; 
+        }
+        .container h2 a[name]:hover,
+        .container h3 a[name]:hover,
+        .container h4 a[name]:hover,
+        .documentation h2 a[name]:hover,
+        .documentation h3 a[name]:hover,
+        .documentation h4 a[name]:hover {
+            text-decoration: none;
+        }
+
+        .documentation h2 a:not([name]),
+        .documentation h3 a:not([name]),
+        .documentation h4 a:not([name]) {
+          /* same as code style */
+          padding: 0 3px 2px;
+          font-family: Monaco, Andale Mono, Courier New, monospace;
+          font-size: 12px;
+          -webkit-border-radius: 3px;
+          -moz-border-radius: 3px;
+          border-radius: 3px;
+          padding: 1px 3px;
+        }
+        
+        .carousel-indicators li {
+          border: 1px solid rgb(192,70,1);
+        }
+
+        .carousel-indicators li.active {
+          background-color: rgb(192,70,1);
+        }
+        
+        .carousel-control .icon-prev,
+        .carousel-control .icon-next {
+          color: rgb(192,70,1);
+          font-size: 60px;
+        }
+        
+        .page-header {
+          margin-top: 0px;
+        }
+
+        .container blockquote p {
+            font-size: small;
+        }
+
+        .container blockquote p:not([author]) {
+            font-style: italic;
+        }
+
+        .container blockquote p {
+            font-size: small;
+            font-style: italic;
+            font-weight: bold;
+        }
+        
+        footer hr {
+            margin-top: 100px;
+        }
+
+        .markdown-content img {
+            margin-top: 10px;
+            margin-bottom: 20px;
+        }
+
+        .markdown-content a img {
+            margin-top: 0px;
+            margin-bottom: 0px;
+        }
+
+        
+        #forkongithub a{
+            display: none;
+            background:#090;
+            color:#fff;
+            text-decoration:none;
+            font-family:arial, sans-serif;
+            text-align:center;
+            font-weight:bold;
+            padding:5px 40px;
+            font-size:1rem;
+            line-height:2rem;
+            position:relative;
+            transition:0.5s;
+        }
+        #forkongithub a:hover{
+            background:#0D0;
+            color:#fff;
+        }
+        #forkongithub a::before,
+        #forkongithub a::after{
+            content:"";width:100%;
+            display:block;
+            position:fixed;
+            top:1px;
+            left:0;
+            height:1px;
+            background:#fff;
+            z-index: 9999;
+        }
+        #forkongithub a::after{
+            bottom:1px;
+            top:auto;
+        }
+        @media screen and (min-width:768px){
+            #forkongithub{
+                position:fixed;
+                display:block;
+                top:0;
+                right:0;
+                width:250px;
+                overflow:hidden;
+                height:250px;
+                z-index: 9999;
+            }
+            #forkongithub a{
+                display:inherit;
+                width:250px;
+                position:fixed;
+                font-size:small;
+                top:40px;
+                right:-60px;
+                transform:rotate(45deg);
+                -webkit-transform:rotate(45deg);
+                -ms-transform:rotate(45deg);
+                box-shadow:4px 4px 10px rgba(0,0,0,0.8);
+            }
+        }        
+    </style>
+
+    <!-- courtesy of http://codepo8.github.io/css-fork-on-github-ribbon/ -->
+    <span id="forkongithub">
+        <a href="https://github.com/apache/isis";>Fork me on GitHub</a>
+    </span>
+
+    <script type="text/javascript">
+      function fbshare () {
+          window.open(
+                  "http://www.facebook.com/sharer/sharer.php?u="+document.URL,
+                  'Share on Facebook',
+                  'width=640,height=426');
+      };
+      function gpshare () {
+          window.open(
+                  "https://plus.google.com/share?url="+document.URL,
+                  'Share on Google+',
+                  'width=584,height=385');
+      };
+      function twshare () {
+          window.open(
+                  
"https://twitter.com/intent/tweet?url="+document.URL+"&text=";,
+                  'Share on Twitter',
+                  'width=800,height=526');
+      };
+    </script>
+
+    <!-- Le fav and touch icons -->
+    <link rel="shortcut icon" href="./../images/favicon.ico">
+
+    <script src="./../javascript/prettify.js" type="text/javascript"></script>
+    
+    <script src="//code.jquery.com/jquery.js"></script>
+    <script src="./../javascript/prettyprint.js"></script>
+    <script src="./../bootstrap-3.0.0/js/bootstrap.js"></script>
+
+
+    <script src="http://platform.twitter.com/widgets.js"; 
type="text/javascript"></script>
+    <!--
+    <DISABLEDscript src="./../javascript/common.js"></DISABLEDscript>
+    -->
+
+    
+    
+    
+
+    <script>
+    $(function () { prettyPrint() })
+    $().dropdown()
+    </script>
+
+    
+  </head>
+
+  <body>
+
+    <nav class="navbar navbar-fixed-top navbar-inverse" role="navigation">
+      <div class="container">
+        <a class="navbar-brand" href="./../index.html">Apache Isis&trade;</a>
+        <ul class="nav navbar-nav">
+
+          <li class="dropdown">
+            <a href="#" class="dropdown-toggle" data-toggle="dropdown">Demos<b 
class="caret"></b></a>
+            <ul class="dropdown-menu">
+              <li><a 
href="./../intro/elevator-pitch/isis-in-pictures.html">Screenshots</a></li>
+              <li><a 
href="./../intro/tutorials/screencasts.html">Screencasts<img 
src="./../images/tv_show-25.png"></a></li>
+              <li><a href="./../intro/powered-by/powered-by.html">Powered 
by</a></li>
+              <li><a href="http://isisdemo.mmyco.co.uk/"; 
target="_blank">Online Demo</a></li>
+            </ul>
+          </li>
+
+          <li><a href="./../documentation.html">Docs</a></li>
+          <li><a href="http://www.isisaddons.org"; 
target="_blank">Add-ons</a></li>
+
+          <li class="dropdown">
+            <a href="#" class="dropdown-toggle" 
data-toggle="dropdown">Downloads<b class="caret"></b></a>
+            <ul class="dropdown-menu">
+              <li><a 
href="./../intro/getting-started/simpleapp-archetype.html">Isis (Maven 
archetype)</a></li>
+              <li><a href="./../download.html">Isis (downloads)</a></li>
+              <li><a href="http://www.isisaddons.org"; target="_blank">Isis 
Add-ons</a></li>
+            </ul>
+          </li>
+
+          <li class="dropdown">
+            <a href="#" class="dropdown-toggle" data-toggle="dropdown">Help<b 
class="caret"></b></a>
+            <ul class="dropdown-menu">
+              <li><a href="./../support.html">Mailing Lists</a></li>
+              <li><a href="http://isis.markmail.org/search/?q="; 
target="_blank">ML Archives</a></li>
+              <li><a href="https://issues.apache.org/jira/browse/ISIS"; 
target="_blank">JIRA</a></li>
+              <li><a href="http://stackoverflow.com/questions/tagged/isis"; 
target="_blank">Stack Overflow</a></li>
+              <li><a href="http://github.com/apache/isis"; 
target="_blank">Github mirror</a></li>
+            </ul>
+          </li>
+
+          <li class="dropdown navbar-right">
+            <a href="#" class="dropdown-toggle" data-toggle="dropdown">@ASF<b 
class="caret"></b></a>
+            <ul class="dropdown-menu">
+              <li>
+                <a href="http://www.apache.org/"; target="_blank">Apache 
Homepage <i class="icon-share-alt"></i></a>
+              </li>
+              <li>
+                <a href="http://www.apache.org/licenses/"; 
target="_blank">Licenses <i class="icon-share-alt"></i></a>
+              </li>
+              <li>
+                <a href="http://www.apache.org/security/"; 
target="_blank">Security <i class="icon-share-alt"></i></a>
+              </li>
+              <li>
+                <a href="http://www.apache.org/foundation/sponsorship.html"; 
target="_blank">Sponsorship <i class="icon-share-alt"></i></a>
+              </li>
+              <li>
+                <a href="http://www.apache.org/foundation/thanks.html";>Thanks 
<i class="icon-share-alt"></i></a>
+              </li>
+              <li>
+                <a href="./../more-thanks.html">More thanks <i 
class="icon-share-alt"></i></a>
+              </li>
+            </ul>
+          </li>
+      </ul>
+
+        <FORM class="navbar-form navbar-right" 
id="searchbox_012614087480249044419:dn-q5gtwxya" 
action="http://www.google.com/cse";>
+        <div class="form-group">
+          <input type="hidden" name="cx" 
value="012614087480249044419:dn-q5gtwxya">
+          <INPUT type="hidden" name="cof" value="FORID:0">
+          <INPUT class="form-control" name="q" type="text" 
placeholder="Search">
+        </div>
+      </FORM>
+
+
+      </div>
+
+
+  </nav>
+
+    <div class="container">
+      <div class="markdown-content">
+      
+
+<div class="page-header">
+<p><a href="./../documentation.html">Docs</a>&nbsp;&raquo&nbsp;<a 
href="./../tutorials/about.html">Tutorials</a></p>
+<h1>
+
+</h1>
+</div>
+
+<h1>Apache Isis: Stop scaffolding, start coding</h1>
+
+<h2>Run the archetype</h2>
+
+<pre><code>mvn archetype:generate  \
+    -D archetypeGroupId=org.apache.isis.archetype \
+    -D archetypeArtifactId=simpleapp-archetype \
+    -D archetypeVersion=1.8.0-SNAPSHOT \
+    -D groupId=com.mycompany \
+    -D artifactId=myapp \
+    -D version=1.0-SNAPSHOT \
+    -D 
archetypeRepository=http://repository-estatio.forge.cloudbees.com/snapshot/ \
+    -B
+</code></pre>
+
+<h2>Build and run</h2>
+
+<pre><code>cd myapp
+mvn clean install
+</code></pre>
+
+<p>then</p>
+
+<pre><code>mvn antrun:run -P self-host
+</code></pre>
+
+<p>or alternatively</p>
+
+<pre><code>mvn jetty:run    
+</code></pre>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>install fixtures</li>
+<li>list all</li>
+<li>create new</li>
+<li>list all    </li>
+</ul>
+
+<h2>Dev environment</h2>
+
+<h4>Configure</h4>
+
+<ul>
+<li>IDE:
+<ul>
+<li><a 
href="http://isis.apache.org/intro/getting-started/ide/intellij.html";>IntelliJ</a></li>
+<li><a 
href="http://isis.apache.org/intro/getting-started/ide/eclipse.html";>Eclipse</a></li>
+</ul></li>
+<li><a href="http://isis.apache.org/intro/resources/editor-templates.html";>IDE 
Editor templates</a></li>
+</ul>
+
+<h4>Run</h4>
+
+<ul>
+<li>Run the app from within the IDE</li>
+<li>Run with different deploymentTypes:
+<ul>
+<li><code>--type SERVER_PROTOTYPE</code></li>
+<li><code>--type SERVER</code></li>
+</ul></li>
+</ul>
+
+<h2>Explore codebase</h2>
+
+<ul>
+<li><code>myapp</code> : parent module</li>
+<li><code>myapp-dom</code>
+<ul>
+<li><code>dom.simple.SimpleObject</code></li>
+<li><code>dom.simple.SimpleObjects</code></li>
+</ul></li>
+<li><code>myapp-fixture</code>
+<ul>
+<li><code>fixture.simple.SimpleObjectsFixture</code></li>
+</ul></li>
+<li><code>myapp-integtests</code></li>
+<li><code>myapp-webapp</code></li>
+</ul>
+
+<h2>Testing</h2>
+
+<ul>
+<li><code>myapp-dom</code> unit tests
+<ul>
+<li>run </li>
+<li>inspect, eg
+<ul>
+<li><code>SimpleObjectTest</code></li>
+</ul></li>
+</ul></li>
+<li><code>myapp-integtests</code> integration tests
+<ul>
+<li>run</li>
+<li>inspect, eg: 
+<ul>
+<li><code>integration.tests.smoke.SimpleObjectsTest</code></li>
+<li><code>integration.specs.simple.SimpleObjectSpec_listAllAndCreate.feature</code></li>
+</ul></li>
+<li>generated report, eg
+<ul>
+<li><code>myapp/integtests/target/cucumber-html-report/index.html</code></li>
+</ul></li>
+<li>change test in IDE, re-run (in Maven)   </li>
+</ul></li>
+</ul>
+
+<h2>Prototyping</h2>
+
+<p>Exclude the <code>integtests</code> module.</p>
+
+<p>In the parent <code>pom.xml</code>:</p>
+
+<pre><code>&lt;modules&gt;
+    &lt;module&gt;dom&lt;/module&gt;
+    &lt;module&gt;fixture&lt;/module&gt;
+    &lt;module&gt;integtests&lt;/module&gt;
+    &lt;module&gt;webapp&lt;/module&gt;
+&lt;/modules&gt;
+</code></pre>
+
+<p>change to:</p>
+
+<pre><code>&lt;modules&gt;
+    &lt;module&gt;dom&lt;/module&gt;
+    &lt;module&gt;fixture&lt;/module&gt;
+    &lt;!--
+    &lt;module&gt;integtests&lt;/module&gt;
+    --&gt;
+    &lt;module&gt;webapp&lt;/module&gt;
+&lt;/modules&gt;
+</code></pre>
+
+<h1>Build a domain app</h1>
+
+<p>The remainder of the tutorial provides guidance on building a domain 
application.  We'd rather you build your own app, but if you're not feeling 
inspired, you could have a go at building our petclinic app.  Here's the 
design:</p>
+
+<p><img src="http://yuml.me/a070d071"; alt="" /></p>
+
+<pre>
+[Visit|-checkIn:DateTime;-checkout:DateTime;-diagnosis:String|+checkin();+checkout();+addNote()]->[Pet|-name:String;-species:PetSpecies]
+[Owner|-firstName:String;-lastName:String]<0..1-0..*>[Pet]
+</pre>
+
+<h2>Domain class</h2>
+
+<ul>
+<li>rename the <code>SimpleObject</code> class</li>
+<li>rename the <code>name</code> property</li>
+<li>specify a <a 
href="http://isis.apache.org/how-tos/how-to-01-040-How-to-specify-a-title-for-a-domain-entity.html";>title</a></li>
+<li>specify an <a 
href="http://isis.apache.org/how-tos/how-to-01-070-How-to-specify-the-icon-for-a-domain-entity.html";>icon</a></li>
+</ul>
+
+<h2>Domain service</h2>
+
+<ul>
+<li>rename the <code>SimpleObjects</code> class</li>
+<li>review <code>create</code> action (acting as a factory)
+<ul>
+<li>as per our <a 
href="http://isis.apache.org/how-tos/how-to-01-160-How-to-create-or-delete-objects-within-your-code.html";>docs</a></li>
+</ul></li>
+<li>review <code>listAll</code> action (acting as a repository)
+<ul>
+<li>as per our <a 
href="http://isis.apache.org/how-tos/how-to-09-040-How-to-write-a-custom-repository.html";>docs</a></li>
+<li>note the annotations on the corresponding domain class 
(<code>SimpleObject</code> above, probably renamed by now)</li>
+</ul></li>
+</ul>
+
+<h2>Fixture scripts</h2>
+
+<ul>
+<li>rename the <code>SimpleObjectsTearDownFixture</code> class
+<ul>
+<li>and update</li>
+</ul></li>
+<li>create for domain classes
+<ul>
+<li>inject in the corresponding domain service</li>
+</ul></li>
+</ul>
+
+<h2>Actions</h2>
+
+<ul>
+<li>update the domain property (<code>SimpleObject#name</code> above, renamed 
by now)</li>
+<li>use <a 
href="http://isis.apache.org/reference/recognized-annotations/ActionSemantics.html";>@ActionSemantics</a>
 annotation to indicate the semantics of the action (safe/query-only, 
idempotent or non-idempotent)</li>
+<li>annotate safe action as <a 
href="http://isis.apache.org/reference/recognized-annotations/Bookmarkable.html";>@Bookmarkable</a>
 
+<ul>
+<li>confirm is available from bookmark panel (top-left of Wicket UI)</li>
+</ul></li>
+</ul>
+
+<h2>REST API</h2>
+
+<ul>
+<li>add Chrome extensions
+<ul>
+<li>install Postman</li>
+<li>install JSON-View</li>
+</ul></li>
+<li>browse to Wicket viewer, install fixtures</li>
+<li>browse to the http://localhost:8080/restful API</li>
+<li>invoke the service to list all objects</li>
+</ul>
+
+<h2>Specify Action semantics</h2>
+
+<ul>
+<li>note the HTTP methods exposed in the REST API</li>
+</ul>
+
+<h2>Value properties</h2>
+
+<ul>
+<li>add some <a 
href="http://isis.apache.org/how-tos/how-to-01-030-How-to-add-a-property-to-a-domain-entity.html";>value
 properties</a>; also:
+<ul>
+<li><a 
href="http://isis.apache.org/components/objectstores/jdo/mapping-mandatory-and-optional-properties.html";>optional
 vs mandatory</a></li>
+<li><a 
href="http://isis.apache.org/components/objectstores/jdo/mapping-joda-dates.html";>joda
 date/time</a></li>
+<li><a 
href="http://isis.apache.org/components/objectstores/jdo/mapping-bigdecimals.html";>bigdecimals</a></li>
+<li><a 
href="http://isis.apache.org/components/objectstores/jdo/mapping-blobs.html";>blob/clobs</a></li>
+</ul></li>
+<li>update the corresponding domain service
+<ul>
+<li>if not optional</li>
+</ul></li>
+<li>update the title, if need be</li>
+<li><a 
href="http://isis.apache.org/how-tos/how-to-01-080-How-to-specify-the-order-in-which-properties-or-collections-are-displayed.html";>order
 the properties</a> using the <a 
href="http://isis.apache.org/reference/recognized-annotations/MemberOrder.html";>@MemberOrder</a>
 annotation and <a 
href="http://isis.apache.org/reference/recognized-annotations/MemberGroupLayout.html";>@MemberGroupLayout</a>
 annotation
+<ul>
+<li>see also this <a 
href="http://isis.apache.org/components/viewers/wicket/static-layouts.html";>static
 layouts</a> documentation</li>
+</ul></li>
+</ul>
+
+<h2>Reference properties</h2>
+
+<ul>
+<li>add some <a 
href="http://isis.apache.org/how-tos/how-to-01-030-How-to-add-a-property-to-a-domain-entity.html";>reference
 properties</a></li>
+<li>update the corresponding domain service</li>
+<li>use different techniques to obtain references (shown in drop-down list box)
+<ul>
+<li>use <a 
href="http://isis.apache.org/reference/recognized-annotations/Bounded.html";>@Bounded</a>
 annotation</li>
+<li>use the <a 
href="http://isis.apache.org/reference/recognized-annotations/AutoComplete.html";>@AutoComplete</a>
 annotation</li>
+<li>use a <code>choicesXxx()</code> supporting method on <a 
href="http://isis.apache.org/how-tos/how-to-03-010-How-to-specify-a-set-of-choices-for-a-property.html";>property</a>
 or <a 
href="http://isis.apache.org/how-tos/how-to-03-020-How-to-specify-a-set-of-choices-for-an-action-parameter.html";>action
 param</a></li>
+<li>use an <code>autoCompleteXxx()</code> supporting method on <a 
href="http://isis.apache.org/how-tos/how-to-03-015-How-to-specify-an-autocomplete-for-a-property.html";>property</a>
 or <a 
href="http://isis.apache.org/how-tos/how-to-03-025-How-to-specify-an-autocomplete-for-an-action-parameter.html";>action
 param</a></li>
+</ul></li>
+</ul>
+
+<h2>Usability: Defaults</h2>
+
+<ul>
+<li>Add <a 
href="http://isis.apache.org/how-tos/how-to-03-050-How-to-specify-default-values-for-an-action-parameter.html";>defaults</a>
 for action parameters</li>
+</ul>
+
+<h2>Collections</h2>
+
+<ul>
+<li>Ensure that all domain classes implement <code>Comparable</code>
+<ul>
+<li>use the <a 
href="http://isis.apache.org/reference/Utility.html";>ObjectContracts</a> 
utility class to help implement <code>Comparable</code> (also 
<code>equals()</code>, <code>hashCode()</code>, <code>toString()</code>)</li>
+</ul></li>
+<li>Add a <a 
href="http://isis.apache.org/components/objectstores/jdo/managed-1-to-m-relationships.html";>one-to-many-collection</a>
 to one of the entities
+<ul>
+<li>Use <code>SortedSet</code> as the class</li>
+</ul></li>
+<li>TODO: @Render 
(http://isis.apache.org/reference/recognized-annotations/Render.html)</li>
+<li>optional: Use the <a 
href="http://isis.apache.org/reference/recognized-annotations/SortedBy.html";>@SortedBy</a>
 annotation to specify a different comparator than the natural ordering</li>
+</ul>
+
+<h2>Actions (ctd)</h2>
+
+<ul>
+<li>Add domain actions to add/remove from the collection</li>
+</ul>
+
+<h2>Layout</h2>
+
+<ul>
+<li>Use the <a 
href="http://isis.apache.org/reference/recognized-annotations/MemberOrder.html";>@MemberOrder</a>
 annotation to associate an action with a property or with a collection
+<ul>
+<li>set the <code>name</code> attribute</li>
+</ul></li>
+<li>Delete the <code>@MemberOrder</code> annotations and use the associated <a 
href="http://isis.apache.org/components/viewers/wicket/dynamic-layouts.html";>.layout.json</a>
 file to specify layout hints instead</li>
+</ul>
+
+<h2>Business rules: See it!</h2>
+
+<ul>
+<li>Use the <a 
href="http://isis.apache.org/reference/recognized-annotations/Hidden.html";>@Hidden</a>
 annotation to make properties/collections/actions invisible
+<ul>
+<li>the <a 
href="http://isis.apache.org/reference/recognized-annotations/Programmatic.html";>@Programmatic</a>
 annotation can also be used and in many cases is to be preferred; the 
difference is that the latter means the member is not part of the Isis 
metamodel.</li>
+</ul></li>
+<li>Use the <code>hideXxx()</code> supporting method on <a 
href="http://isis.apache.org/how-tos/how-to-02-010-How-to-hide-a-property.html";>properties</a>,
 <a 
href="http://isis.apache.org/how-tos/how-to-02-020-How-to-hide-a-collection.html";>collections</a>
 and <a 
href="http://isis.apache.org/how-tos/how-to-02-030-How-to-hide-an-action.html";>actions</a>
 to make a property/collection/action invisible according to some imperative 
rule</li>
+</ul>
+
+<h2>Business rules: Use it!</h2>
+
+<ul>
+<li>Use the <a 
href="http://isis.apache.org/reference/recognized-annotations/Disabled.html";>@Disabled</a>
 annotation to make properties read-only/actions non-invokable ('greyed 
out')</li>
+<li>Use the <code>disabledXxx()</code> supporting method on <a 
href="http://isis.apache.org/how-tos/how-to-02-050-How-to-prevent-a-property-from-being-modified.html";>properties</a>
 and <a 
href="http://isis.apache.org/how-tos/how-to-02-070-How-to-prevent-an-action-from-being-invoked.html";>actions</a>
 to make a property/action disabled according to some imperative rule</li>
+</ul>
+
+<h2>Business rules: Do it!</h2>
+
+<ul>
+<li>Use the <a 
href="http://isis.apache.org/reference/recognized-annotations/RegEx.html";>@Regex</a>
 annotation on string properties or action parameters to validate strings</li>
+<li>Use the <a href="">@MinLength</a></li>
+</ul>
+
+<p>(or more generally the <a href="">@MustSatisfy</a> annotation) to specify 
</p>
+
+<p>Use the <code>validateXxx()</code> supporting method on [properties](</p>
+
+<ul>
+<li>disable</li>
+<li>hide</li>
+<li>vaidate</li>
+</ul>
+
+<p>Bookmarkable  </p>
+
+<p>dashboards (home page)</p>
+
+<p>contributed properties/collections</p>
+
+<p>contributed actions</p>
+
+<p>creating objects:
+- domainobjectcontainer</p>
+
+<p>clockservice</p>
+
+<p>.layout.json</p>
+
+<p>view models</p>
+
+<p>Customising the REST API</p>
+
+<p>wicket extensions
+- excel download
+- fullcalendar
+- gmap3</p>
+
+<p>add-ons
+- security
+- command
+- auditing
+- publishing</p>
+
+<p>event bus</p>
+
+<p>RESTful API</p>
+
+<p>composite fixture scripts (a la Estatio)</p>
+
+<h2>Stuff to do...</h2>
+
+<ul>
+<li>rename domain class</li>
+<li>rename domain service</li>
+<li>fixture scripts</li>
+</ul>
+
+<p>Tests</p>
+
+<ul>
+<li>delete the BDD specs</li>
+</ul>
+
+<h2>Dele</h2>
+
+<h2>Rename domain class, domain service, fixtures</h2>
+
+<h4>SimpleObject</h4>
+
+<p>rename to <code>ConferenceSession</code></p>
+
+<h4>SimpleObjects</h4>
+
+<p>rename to <code>ConferenceSessions</code></p>
+
+<h4>SimpleObjectsFixture</h4>
+
+<p>rename to <code>ConferenceSessionsFixture</code></p>
+
+<h4>myapp-webapp/src/main/webapp/WEB-INF/isis.properties</h4>
+
+<pre>
+isis.services = \
+                10:dom.simple.SimpleObjects,\
+                ...
+
+isis.fixtures=fixture.simple.SimpleObjectsFixture
+</pre>
+
+<p>to</p>
+
+<pre>
+isis.services = \
+                10:dom.simple.ConferenceSessions,\
+                ...
+
+isis.fixtures=fixture.simple.ConferenceSessionsFixture
+</pre>
+
+<h4>ConferenceSessionsFixture</h4>
+
+<pre>
+isisJdoSupport.executeUpdate("delete from \"SimpleObject\"");
+</pre>
+
+<p>to:</p>
+
+<pre>
+isisJdoSupport.executeUpdate("delete from \"ConferenceSession\"");
+</pre>
+
+<h4>icon</h4>
+
+<ul>
+<li>add icon, <code>icons/OverheadProjector.png</code> -> 
<code>myapp/dom/src/main/resources/images/ConferenceSession.png</code></li>
+</ul>
+
+<h4>ConferenceSessions</h4>
+
+<ul>
+<li>getId()</li>
+</ul>
+
+<p><em>a "nice to have", but has an effect on the URLs exposed in the REST 
API</em></p>
+
+<pre>
+public String getId() {
+    return "conferenceSessions";
+</div>
+</pre>
+
+<ul>
+<li><code>iconName()</code></li>
+</ul>
+
+<pre>
+public String iconName() {
+    return "ConferenceSession";
+</div>
+</pre>
+
+<h4>ConferenceSession</h4>
+
+<p><em>a "nice to have", but has an effect on the URLs exposed in the REST 
API</em></p>
+
+<pre>
+@ObjectType("SESSION")
+</pre>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>menu service changed to "Conference Sessions"</li>
+<li>tooltip on icon/title changed to "Conference Session"</li>
+<li>icon changed</li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-030</code></p>
+
+<h2>Refactor domain class</h2>
+
+<h4>ConferenceSession</h4>
+
+<ul>
+<li>rename <code>name</code> property to <code>sessionTitle</code></li>
+<li>update <code>compareTo()</code></li>
+</ul>
+
+<pre>
+public int compareTo(ConferenceSession other) {
+    return ObjectContracts.compare(this, other, "sessionTitle");
+</div>
+</pre>
+
+<ul>
+<li>review title</li>
+</ul>
+
+<pre>
+@Title
+public String getSessionTitle() {
+    return sessionTitle;
+</div>
+</pre>
+
+<h4>ConferenceSessionsFixture</h4>
+
+<ul>
+<li>Better fixture data</li>
+</ul>
+
+<pre>
+private void installObjects() {
+    create("RRRADDD! Apache Isis");
+    create("Best practices for AngularJS");
+    create("Refactor your specs!");
+    create("Why Kotlin?");
+    create("Introduction to the Play Framework");
+    create("Object Oriented Design in the Wild");
+</div>
+</pre>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>table shows sessionTitle column</li>
+<li>object form shows sessionTitle property</li>
+<li>object page labelled with title</li>
+<li>updated data</li>
+</ul>
+
+<p>also</p>
+
+<ul>
+<li>discuss injected services into fixtures, entities, services etc.</li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-040</code></p>
+
+<h2>Add date scalar property</h2>
+
+<h4>ConferenceSession</h4>
+
+<pre>
+// //////////////////////////////////////
+// date (property)
+// //////////////////////////////////////
+
+private LocalDate date;
+
[email protected]
[email protected](allowsNull = "true")
+@MemberOrder(sequence = "1")
+public LocalDate getDate() {
+    return date;
+</div>
+
+public void setDate(final LocalDate date) {
+    this.date = date;
+</div>
+</pre>
+
+<h4>ConferenceSessionsFixture</h4>
+
+<pre>
+private ConferenceSession create(final String name) {
+    ...
+    session.setDate(clockService.now().plusDays((int)(Math.random()*5)));
+    ...
+</div>
+</pre>
+
+<p>and</p>
+
+<pre>
+private ClockService clockService;
+
+public final void injectClockService(final ClockService clockService) {
+    this.clockService = clockService;
+</div>
+</pre>
+
+<p>></p>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>now has a date property in title and in form</li>
+<li>can edit, obviously</li>
+<li>is optional</li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-050</code></p>
+
+<h2>Add enum scalar property</h2>
+
+<ul>
+<li>type property</li>
+</ul>
+
+<h5>ConferenceSession</h5>
+
+<pre>
+// //////////////////////////////////////
+// Type (property)
+// //////////////////////////////////////
+
+public enum Type {
+    KEYNOTE, ALL_DAY_TUTORIAL, SESSION, OTHER
+</div>
+
+private Type Type;
[email protected](allowsNull="false")
+@MemberOrder(sequence = "1")
+public Type getType() {
+    return Type;
+</div>
+public void setType(final Type Type) {
+    this.Type = Type;
+</div>
+</pre>
+
+<h5>ConferenceSessions</h5>
+
+<ul>
+<li>update the <code>create(...)</code> method</li>
+</ul>
+
+<pre>
+public ConferenceSession create(
+        final @Named("Name") String name,
+        final @Named("Type") ConferenceSession.Type type) {
+    final ConferenceSession obj = 
newTransientInstance(ConferenceSession.class);
+    obj.setSessionTitle(name);
+    obj.setType(type);
+    persistIfNotAlready(obj);
+    return obj;
+</div>
+</pre>
+
+<h5>ConferenceSessionsFixture</h5>
+
+<ul>
+<li>update the fixture data</li>
+</ul>
+
+<pre>
+private void installObjects() {
+    create("RRRADDD! Apache Isis", ConferenceSession.Type.SESSION);
+    create("Best practices for AngularJS", ConferenceSession.Type.SESSION);
+    create("Refactor your specs!", ConferenceSession.Type.SESSION);
+    create("Why Kotlin?", ConferenceSession.Type.OTHER);
+    create("Introduction to the Play Framework", 
ConferenceSession.Type.SESSION);
+    create("Object Oriented Design in the Wild", 
ConferenceSession.Type.ALL_DAY_TUTORIAL);
+</div>
+
+private ConferenceSession create(final String name, final Type type) {
+    return simpleObjects.create(name, type);
+</div>
+</pre>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>list all
+<ul>
+<li>table with new property</li>
+</ul></li>
+<li>show object form</li>
+<li>edit object details
+<ul>
+<li>both are mandatory</li>
+</ul></li>
+<li>create new object</li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-060</code></p>
+
+<h2>UI hints</h2>
+
+<h5>`ConferenceSession</h5>
+
+<ul>
+<li>sessionTitle, then type, then date</li>
+</ul>
+
+<pre>
+@MemberOrder(sequence="1")
+public String getSessionTitle() {
+</pre>
+
+<ul>
+<li>then type</li>
+</ul>
+
+<pre>
+@MemberOrder(sequence = "2")
+public Type getType() {
+</pre>
+
+<ul>
+<li>then date (hide in table)</li>
+</ul>
+
+<pre>
+@Hidden(where=Where.ALL_TABLES)
+@MemberOrder(name="Scheduling", sequence = "3")
+public LocalDate getDate() {
+</pre>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>table shows only title, type properties (in that order)</li>
+<li>order form shows
+<ul>
+<li>title, type properties grouped together in the default 'General' group</li>
+<li>date property separately in a new 'Scheduling' group</li>
+</ul></li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-070</code></p>
+
+<h2>Add another type (and domain service)</h2>
+
+<h4>Speaker</h4>
+
+<pre><code>package dom.simple;
+
+import javax.jdo.annotations.IdentityType;
+import javax.jdo.annotations.VersionStrategy;
+
+import org.apache.isis.applib.DomainObjectContainer;
+import org.apache.isis.applib.annotation.Bookmarkable;
+import org.apache.isis.applib.annotation.MemberOrder;
+import org.apache.isis.applib.annotation.ObjectType;
+import org.apache.isis.applib.annotation.Title;
+import org.apache.isis.applib.util.ObjectContracts;
+
[email protected](identityType=IdentityType.DATASTORE)
[email protected](
+        strategy=javax.jdo.annotations.IdGeneratorStrategy.IDENTITY,
+         column="id")
[email protected](
+        strategy=VersionStrategy.VERSION_NUMBER, 
+        column="version")
+@ObjectType("SPEAKER")
+@Bookmarkable
+public class Speaker implements Comparable&lt;Speaker&gt; {
+
+
+    // //////////////////////////////////////
+    // givenName (property)
+    // //////////////////////////////////////
+
+    private String givenName;
+
+    @MemberOrder(sequence = "1")
+    @javax.jdo.annotations.Column(allowsNull="false")
+    @Title(sequence="2", prepend=", ")
+    public String getGivenName() {
+        return givenName;
+    }
+
+    public void setGivenName(final String name) {
+        this.givenName = name;
+    }
+
+
+    // //////////////////////////////////////
+    // familyName (property)
+    // //////////////////////////////////////
+
+    private String familyName;
+
+    @MemberOrder(sequence = "2")
+    @javax.jdo.annotations.Column(allowsNull="false")
+    @Title(sequence="1")
+    public String getFamilyName() {
+        return familyName;
+    }
+
+    public void setFamilyName(final String familyName) {
+        this.familyName = familyName;
+    }
+
+
+    // //////////////////////////////////////
+    // compareTo
+    // //////////////////////////////////////
+
+    @Override
+    public int compareTo(Speaker other) {
+        return ObjectContracts.compare(this, other, "givenName, familyName");
+    }
+
+
+    // //////////////////////////////////////
+    // Injected
+    // //////////////////////////////////////
+
+    @SuppressWarnings("unused")
+    private DomainObjectContainer container;
+    public void injectDomainObjectContainer(final DomainObjectContainer 
container) {
+        this.container = container;
+    }
+
+}
+</code></pre>
+
+<h4>Speakers</h4>
+
+<pre><code>package dom.simple;
+
+import java.util.List;
+
+import org.apache.isis.applib.AbstractFactoryAndRepository;
+import org.apache.isis.applib.annotation.ActionSemantics;
+import org.apache.isis.applib.annotation.ActionSemantics.Of;
+import org.apache.isis.applib.annotation.Bookmarkable;
+import org.apache.isis.applib.annotation.MemberOrder;
+import org.apache.isis.applib.annotation.Named;
+
+public class Speakers extends AbstractFactoryAndRepository {
+
+    @Override
+    public String getId() {
+        return "speakers";
+    }
+
+    public String iconName() {
+        return "Speaker";
+    }
+
+    @Bookmarkable
+    @ActionSemantics(Of.SAFE)
+    @MemberOrder(sequence = "1")
+    public List&lt;Speaker&gt; listAll() {
+        return allInstances(Speaker.class);
+    }
+
+    @MemberOrder(sequence = "2")
+    public Speaker create(
+            final @Named("Given name") String givenName,
+            final @Named("Family name") String familyName) {
+        Speaker speaker = getContainer().newTransientInstance(Speaker.class);
+        speaker.setGivenName(givenName);
+        speaker.setFamilyName(familyName);
+        getContainer().persistIfNotAlready(speaker);
+        return speaker;
+    }
+}
+</code></pre>
+
+<h5>ConferenceSessionsFixture</h5>
+
+<ul>
+<li>install()</p>
+
+<pre><code>isisJdoSupport.executeUpdate("delete from \"Speaker\"");
+</code></pre></li>
+<li>installObjects()</p>
+
+<pre><code>createSpeaker("Dan", "Haywood");
+createSpeaker("Misko", "Hevery");
+createSpeaker("Cyrille", "Martraire");
+createSpeaker("Svetlana", "Haywood");
+createSpeaker("James", "Ward");
+createSpeaker("Jessica", "Kerr");
+</code></pre></li>
+<li>createSpeaker() method:</p>
+
+<pre><code>private Speaker createSpeaker(final String givenName, final String 
familyName) {
+    return speakers.create(givenName, familyName);
+}
+</code></pre></li>
+<li>and inject new domain service:</p>
+
+<pre><code>private Speakers speakers;
+public final void injectSpeakers(final Speakers speakers) {
+    this.speakers = speakers;
+}
+</code></pre></li>
+</ul>
+
+<h5>isis.properties</h5>
+
+<ul>
+<li>number indicates the ordering in the menu</p>
+
+<pre><code>isis.services = \
+                10:dom.simple.ConferenceSessions,\
+                20:dom.simple.Speakers,\
+</code></pre></li>
+</ul>
+
+<h4>icon</h4>
+
+<ul>
+<li>add icon, <code>icons/Customer.gif</code> -> 
<code>myapp/dom/src/main/resources/images/Speaker.gif</code></li>
+</ul>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>new domain service</li>
+<li>list, create</li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-080</code></p>
+
+<h2>Add reference property</h2>
+
+<h4>ConferenceSession</h4>
+
+<ul>
+<li>'speaker' property (of type 'Speaker')</p>
+
+<pre><code>// //////////////////////////////////////
+// speaker (property)
+// //////////////////////////////////////
+
+
+private Speaker speaker;
+
+
[email protected](allowsNull="true")
+@MemberOrder(sequence = "3")
+public Speaker getSpeaker() {
+    return speaker;
+}
+
+
+public void setSpeaker(final Speaker speaker) {
+    this.speaker = speaker;
+}
+</code></pre></li>
+</ul>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>displays field, no way to associate :-(</li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-090</code></p>
+
+<h2>Add autoComplete (and repository queries)</h2>
+
+<h4>ConferenceSession</h4>
+
+<ul>
+<li>autoComplete method:</p>
+
+<pre><code>public List&lt;Speaker&gt; autoCompleteSpeaker(String search) {
+    return speakers.findByGivenOrFamilyName(search);
+}
+</code></pre></li>
+<li>inject service:</p>
+
+<pre><code>private Speakers speakers;
+
+
+public final void injectSpeakers(final Speakers speakers) {
+    this.speakers = speakers;
+}
+</code></pre></li>
+</ul>
+
+<h5>Speakers</h5>
+
+<ul>
+<li>finder (hidden because only intended to be called programmatically)</p>
+
+<pre><code>@Hidden
+public List&lt;Speaker&gt; findByGivenOrFamilyName(String search) {
+    return getContainer().allMatches(
+            new QueryDefault&lt;Speaker&gt;(Speaker.class, 
+                    "findByGivenOrFamilyName", 
+                    "givenOrFamilyName", ".*"+search+".*"));
+}
+</code></pre></li>
+</ul>
+
+<h5>Speaker</h5>
+
+<ul>
+<li>Isis configured to use JDO, so add the annotation</p>
+
+<pre><code>@javax.jdo.annotations.Queries({
+    @javax.jdo.annotations.Query(
+            name="findByGivenOrFamilyName", language="JDOQL",
+            value="SELECT "
+                    + "FROM dom.simple.Speaker "
+                    + "WHERE givenName.matches(:givenOrFamilyName) "
+                    + "   || familyName.matches(:givenOrFamilyName)")
+})
+</code></pre></li>
+</ul>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>can now add speaker
+<ul>
+<li>using either given or family name</li>
+</ul></li>
+<li>Speakers finder is not visible</li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-100</code></p>
+
+<h2>Update title (also, prototype actions)</h2>
+
+<h4>ConferenceSession</h4>
+
+<ul>
+<li>title imperatively, rather than declaratively</p>
+
+<pre><code>public String title() {
+    final TitleBuffer buf = new TitleBuffer();
+    if(getSessionTitle().length()&gt;20) {
+        buf.append(getSessionTitle().substring(0, 20)).append("...");
+    } else {
+        buf.append(getSessionTitle());
+    }
+    if(getSpeaker() != null) {
+        buf.append("(").append(container.titleOf(getSpeaker())).append(")");
+    }
+    return buf.toString();
+}
+</code></pre></li>
+</ul>
+
+<h4>DEMO:</h4>
+
+<ul>
+<li>metamodel validation exception</li>
+</ul>
+
+<h4>ConferenceSession</h4>
+
+<ul>
+<li>remove <code>@Title</code> from <code>#sessionTitle</code></li>
+</ul>
+
+<h4>ConferenceSessions</h4>
+
+<ul>
+<li>Add prototyping action to domain service</p>
+
+<pre><code>@Bookmarkable
+@Prototype
+@ActionSemantics(Of.SAFE)
+public ConferenceSession firstOne() {
+    return listAll().get(0);
+}
+</code></pre></li>
+</ul>
+
+<h4>DEMO:</h4>
+
+<ul>
+<li>now runs ok</li>
+<li>prototype action styled differently</li>
+<li>add speaker to session</li>
+<li>title changes</li>
+<li>(optional) run in SERVER mode</li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-110</code></p>
+
+<h2>Another new entity, Tag</h2>
+
+<p><em>the motivation for this is that we want to add a new collection of tags 
from ConferenceSession</em></p>
+
+<h4>Tag</h4>
+
+<ul>
+<li>new entity (note the <code>compareTo</code>, <code>toString</code>, using 
Isis helper methods)</p>
+
+<pre><code>package dom.simple;
+
+
+import javax.jdo.annotations.IdentityType;
+import javax.jdo.annotations.VersionStrategy;
+
+
+import org.apache.isis.applib.DomainObjectContainer;
+import org.apache.isis.applib.annotation.Bookmarkable;
+import org.apache.isis.applib.annotation.MemberOrder;
+import org.apache.isis.applib.annotation.ObjectType;
+import org.apache.isis.applib.annotation.Title;
+import org.apache.isis.applib.util.ObjectContracts;
+
+
[email protected](identityType=IdentityType.DATASTORE)
[email protected](
+        strategy=javax.jdo.annotations.IdGeneratorStrategy.IDENTITY,
+         column="id")
[email protected](
+        strategy=VersionStrategy.VERSION_NUMBER, 
+        column="version")
+@ObjectType("TAG")
+@Bookmarkable
+public class Tag implements Comparable&lt;Tag&gt; {
+
+
+<pre><code>// //////////////////////////////////////
+// name (property)
+// //////////////////////////////////////
+
+
+private String name;
+
+
[email protected](allowsNull = "true")
+@Title
+@MemberOrder(sequence = "1")
+public String getName() {
+    return name;
+}
+
+
+public void setName(final String name) {
+    this.name = name;
+}
+
+
+// //////////////////////////////////////
+// compareTo
+// //////////////////////////////////////
+
+
+@Override
+public int compareTo(Tag other) {
+    return ObjectContracts.compare(this, other, "name");
+}
+
+
+// //////////////////////////////////////
+// toString
+// //////////////////////////////////////
+
+
+@Override
+public String toString() {
+    return ObjectContracts.toString(this, "name");
+}
+
+
+// //////////////////////////////////////
+// Injected
+// //////////////////////////////////////
+
+
+@SuppressWarnings("unused")
+private DomainObjectContainer container;
+public void injectDomainObjectContainer(final DomainObjectContainer container) 
{
+    this.container = container;
+}
+</code></pre>
+
+}
+</code></pre></li>
+</ul>
+
+<h4>Tags</h4>
+
+<ul>
+<li>new domain service; all actions are hidden</p>
+
+<pre><code>package dom.simple;
+
+
+import java.util.List;
+
+
+import org.apache.isis.applib.AbstractFactoryAndRepository;
+import org.apache.isis.applib.annotation.Hidden;
+
+
+public class Tags extends AbstractFactoryAndRepository {
+
+
+<pre><code>@Override
+public String getId() {
+    return "tags";
+}
+public String iconName() {
+    return "Tag";
+}
+
+
+@Hidden
+public List&amp;lt;Tag&amp;gt; listAll() {
+    return allInstances(Tag.class);
+}
+@Hidden
+public Tag create(final String name) {
+    final Tag obj = newTransientInstance(Tag.class);
+    obj.setName(name);
+    persistIfNotAlready(obj);
+    return obj;
+}
+</code></pre>
+
+}
+</code></pre></li>
+</ul>
+
+<h4>ConferenceSessionsFixture</h4>
+
+<ul>
+<li>installObjects()</p>
+
+<pre><code>createTag("UX");
+createTag("Mobile");
+createTag("Java");
+createTag("Agile");
+createTag(".NET");
+createTag("Test");
+createTag("Mastery");
+createTag("Web");
+createTag("Architecture");
+createTag("Dev Ops");
+createTag("Cloud");
+createTag("Languages");
+createTag("Tools");
+createTag("Team");
+createTag("Database");
+createTag("Javascript");
+createTag("Keynote");
+</code></pre></li>
+<li>createTag() method</p>
+
+<pre><code>private Tag createTag(final String name) {
+    return tags.create(name);
+}
+</code></pre></li>
+<li>inject new service</p>
+
+<pre><code>private Tags tags;
+public final void injectTags(final Tags tags) {
+    this.tags = tags;
+}
+</code></pre></li>
+</ul>
+
+<h4>isis.properties</h4>
+
+<ul>
+<li>no number required, since not visible in UI</p>
+
+<pre><code>isis.services = \
+                10:dom.simple.ConferenceSessions,\
+                20:dom.simple.Speakers,\
+                dom.simple.Tags,\
+</code></pre></li>
+</ul>
+
+<h4>icon</h4>
+
+<ul>
+<li>add icon, <code>icons/RedFlag.png</code> -> 
<code>myapp/dom/src/main/resources/images/Tag.png</code></li>
+</ul>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>(no visible change, since Tags actions are all hidden)                </li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-120</code></p>
+
+<h2>Add collection</h2>
+
+<h4>ConferenceSession</h4>
+
+<ul>
+<li>tags collection</p>
+
+<pre><code>// //////////////////////////////////////
+// tags (collection)
+// //////////////////////////////////////
+
+
[email protected]
[email protected](dependent = "false")
+private SortedSet&lt;Tag&gt; tags = new TreeSet&lt;Tag&gt;();
+
+
+@Render(Render.Type.EAGERLY)
+@Disabled
+@MemberOrder(sequence = "1")
+public SortedSet&lt;Tag&gt; getTags() {
+    return tags;
+}
+
+
+public void setTags(final SortedSet&lt;Tag&gt; tags) {
+    this.tags = tags;
+}
+</code></pre></li>
+</ul>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>view the DDL</li>
+<li>open object</li>
+<li>can't add tags :-(</li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-130</code></p>
+
+<h2>Add actions</h2>
+
+<h4>ConferenceSession</h4>
+
+<ul>
+<li>addTag action</p>
+
+<pre><code>// //////////////////////////////////////
+// addTag (action)
+// //////////////////////////////////////
+
+
+@MemberOrder(sequence = "1")
+public ConferenceSession addTag(final Tag tag) {
+    getTags().add(tag);
+    return this;
+}
+</code></pre></li>
+<li>removeTag action</p>
+
+<pre><code>// //////////////////////////////////////
+// removeTag (action)
+// //////////////////////////////////////
+
+
+@MemberOrder(sequence = "2")
+public ConferenceSession removeTag(final Tag tag) {
+    getTags().remove(tag);
+    return this;
+}
+</code></pre></li>
+</ul>
+
+<h4>ConferenceSessionsFixture</h4>
+
+<ul>
+<li>create(...) method, add three (random) tags for each session</p>
+
+<pre><code>for (Tag tag : random(tags.listAll(), 3)) {
+    session.addTag(tag);
+}
+</code></pre></li>
+<li>supporting random(...) method</p>
+
+<pre><code>private List&lt;Tag&gt; random(List&lt;Tag&gt; tags, int num) {
+    List&lt;Tag&gt; availableTags = Lists.newArrayList(tags);
+    List&lt;Tag&gt; selectedTags = Lists.newArrayList();
+    while(selectedTags.size()&lt;num) {
+        int selected = (int)(availableTags.size() * Math.random());
+        try {
+            selectedTags.add(availableTags.remove(selected));
+        } catch(Exception ex) {}
+    }
+    return selectedTags;
+}
+</code></pre></li>
+</ul>
+
+<h4>DEMO:</h4>
+
+<ul>
+<li>view</li>
+<li>need to get a reference to the tag again...</li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-140</code></p>
+
+<h2>Add choices</h2>
+
+<p><em>an alternative to using autoComplete, useful if the list of choices is 
relatively short</em></p>
+
+<h4>ConferenceSession</h4>
+
+<ul>
+<li>inject Tags repo</p>
+
+<pre><code>private Tags tagRepo;
+
+
+public final void injectTags(final Tags tags) {
+    this.tagRepo = tags;
+}
+</code></pre></li>
+<li>added the "choices" supporting method for addTag action (all tags not yet 
added)</p>
+
+<pre><code>public Collection&lt;Tag&gt; choices0AddTag() {
+    List&lt;Tag&gt; tags = Lists.newArrayList(tagRepo.listAll());
+    tags.removeAll(getTags());
+    return tags;
+}
+</code></pre></li>
+<li>added the "choices" supporting method for removeTag action (only those 
tags previously added)</p>
+
+<pre><code>public Collection&lt;Tag&gt; choices0RemoveTag() {
+    return getTags();
+}
+</code></pre></li>
+</ul>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>can now add/remove tags, yay!</li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-150</code></p>
+
+<h2>More UI Hints (dynamic, this time)</h2>
+
+<h4>DEMO:</h4>
+
+<ul>
+<li>download layout</li>
+<li>edit column spans</li>
+<li>copy <code>ConferenceSession.layout.json</code> (below) to 
<code>myapp-dom/src/main/java/dom/simple</code></li>
+<li>refresh layout</p>
+
+<pre><code>{
+  "columns": [
+    {
+      "span": 6,
+      "memberGroups": {
+        "General": {
+          "members": {
+            "sessionTitle": {},
+            "type": {},
+            "speaker": {}
+          }
+        },
+        "Scheduling": {
+          "members": {
+            "date": {}
+          }
+        }
+      }
+    },
+    {
+      "span": 0,
+      "memberGroups": {}
+    },
+    {
+      "span": 0,
+      "memberGroups": {}
+    },
+    {
+      "span": 6,
+      "collections": {
+        "tags": {
+          "actions": {
+            "addTag": {},
+            "removeTag": {}
+          }
+        }
+      }
+    }
+  ],
+  "actions": {
+    "downloadLayout": {},
+    "refreshLayout": {}
+  }
+}
+</code></pre></li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-160</code></p>
+
+<h2>Declarative and imperative business rules</h2>
+
+<h4>ConferenceSession</h4>
+
+<ul>
+<li>add RegEx pattern and a maximum length to the 'sessionTitle' property 
(latter using JDO annotation)</p>
+
+<pre><code>@javax.jdo.annotations.Column(allowsNull="false",length=40)
+@RegEx(validation="[^%]+")
+public String getSessionTitle() {
+</code></pre></li>
+<li>can't add more than 4 tags</p>
+
+<pre><code>public String disableAddTag(Tag tag) {
+    return getTags().size() &gt;= 4? "Cannot add more than 4 tags": null;
+}
+</code></pre></li>
+</ul>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>cannot save session whose sessionTitle property has > 40 chars</li>
+<li>cannot add more than 4 tags to a session</li>
+* 
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-170</code></p>
+
+<h2>Other business rules (hiding, validation)</h2>
+
+<h4>ConferenceSession</h4>
+
+<ul>
+<li>hideAddTag </p>
+
+<pre><code>public boolean hideAddTag() {
+    return getTags().size() &gt;= 4;
+}
+</code></pre></li>
+</ul>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>action becomes hidden</li>
+</ul>
+
+<h4>ConferenceSession</h4>
+
+<ul>
+<li>offer tags that are already in the collection, but then prevent using 
validation</p>
+
+<pre><code>public Collection&lt;Tag&gt; choices0AddTag() {
+    List&lt;Tag&gt; tags = Lists.newArrayList(tagRepo.listAll());
+    //tags.removeAll(getTags());
+    return tags;
+}
+
+
+public String validateAddTag(Tag tag) {
+    return getTags().contains(tag)? "Already added that tag": null;
+}
+</code></pre></li>
+</ul>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>can attempt to add a tag, but prevented</li>
+</ul>
+
+<h4>ConferenceSession</h4>
+
+<ul>
+<li>backing out the above changes</p>
+
+<pre><code>public Collection&lt;Tag&gt; choices0AddTag() {
+    List&lt;Tag&gt; tags = Lists.newArrayList(tagRepo.listAll());
+    tags.removeAll(getTags());
+    return tags;
+}
+
+
+//    public String validateAddTag(Tag tag) {
+//        return getTags().contains(tag)? "Already added that tag": null;
+//    }
+
+
+//    public boolean hideAddTag() {
+//        return getTags().size() &gt;= 4;
+//    }
+
+
+public String disableAddTag(Tag tag) {
+    return getTags().size() &gt;= 4? "Cannot add more than 4 tags": null;
+}
+</code></pre></li>
+</ul>
+
+<h2>Contributed Actions etc</h2>
+
+<h4>ConferenceSessions</h4>
+
+<ul>
+<li>add action, will be contributed to Tag, as both action and a collection 
</p>
+
+<pre><code>// //////////////////////////////////////
+// Contributions
+// //////////////////////////////////////
+
+
+// @NotContributed
+@NotInServiceMenu
+public List&lt;ConferenceSession&gt; findByTag(Tag tag) {
+    return allMatches(new QueryDefault&lt;ConferenceSession&gt;(
+            ConferenceSession.class, "findByTag", "tag", tag));
+}
+</code></pre></li>
+</ul>
+
+<h4>ConferenceSession</h4>
+
+<ul>
+<li>declare JDO query</p>
+
+<pre><code>@javax.jdo.annotations.Queries({
+    @javax.jdo.annotations.Query(
+            name="findByTag", language="JDOQL",
+            value="SELECT "
+                    + "FROM dom.simple.ConferenceSession "
+                    + "WHERE tags.contains(:tag)")
+})
+</code></pre></li>
+</ul>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>view Tag; shows the sessions that are associated with it</li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-180</code></p>
+
+<h2>Bookmarks</h2>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>bookmarked objects
+<ul>
+<li>eg <code>ConferenceSession</code></li>
+</ul></li>
+<li>bookmarked actions
+<ul>
+<li>eg <code>ConferenceSessions#listAll</code></li>
+</ul></li>
+</ul>
+
+<h2>CSS</h2>
+
+<h4>myapp-webapp/src/main/webapp/css/application.css</h4>
+
+<ul>
+<li>example custom CSS already defined for "x-highlight" and "x-caution" CSS 
classes </li>
+</ul>
+
+<h4>ConferenceSession</h4>
+
+<ul>
+<li>addTag, add annotation</p>
+
+<pre><code>@CssClass("x-highlight")
+public ConferenceSession addTag(final Tag tag) {
+</code></pre></li>
+<li>removeTag, add annotation</p>
+
+<pre><code>@CssClass("x-caution")
+public ConferenceSession removeTag(final Tag tag) {
+</code></pre></li>
+</ul>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>view session object</li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-190</code></p>
+
+<h2>Widgets, also @HomePage</h2>
+
+<p><em>show Conference sessions on a calendar view.  This uses a "third-party" 
component, up on github</em></p>
+
+<h4>myapp/pom.xml</h4>
+
+<ul>
+<li>parent module, scope=import for dependency management</p>
+
+<pre><code>&lt;dependencyManagement&gt;
+    &lt;dependencies&gt;
+        ...
+
+
+<pre><code>    &amp;lt;dependency&amp;gt;
+        
&amp;lt;groupId&amp;gt;com.danhaywood.isis.wicket&amp;lt;/groupId&amp;gt;
+        
&amp;lt;artifactId&amp;gt;danhaywood-isis-wicket-fullcalendar2&amp;lt;/artifactId&amp;gt;
+        &amp;lt;version&amp;gt;1.3.0&amp;lt;/version&amp;gt;
+        &amp;lt;type&amp;gt;pom&amp;lt;/type&amp;gt;
+        &amp;lt;scope&amp;gt;import&amp;lt;/scope&amp;gt;
+    &amp;lt;/dependency&amp;gt;
+
+
+&amp;lt;/dependencies&amp;gt;
+</code></pre>
+
+&lt;/dependencyManagement&gt;
+</code></pre></li>
+</ul>
+
+<h4>myapp-dom/pom.xml</h4>
+
+<ul>
+<li>dom project depends on modules 'applib' (minimal coupling, defines an 
interface and value type)</p>
+
+<pre><code>&lt;dependencies&gt;
+    ...
+
+
+<pre><code>&amp;lt;dependency&amp;gt;
+    &amp;lt;groupId&amp;gt;com.danhaywood.isis.wicket&amp;lt;/groupId&amp;gt;
+    
&amp;lt;artifactId&amp;gt;danhaywood-isis-wicket-fullcalendar2-applib&amp;lt;/artifactId&amp;gt;
+&amp;lt;/dependency&amp;gt;
+</code></pre>
+
+&lt;/dependencies&gt;
+</code></pre></li>
+</ul>
+
+<h4>myapp-webapp/pom.xml</h4>
+
+<ul>
+<li>webapp project references the actual widget UI implementation</p>
+
+<pre><code>&lt;dependencies&gt;
+    ...
+
+
+<pre><code>&amp;lt;dependency&amp;gt;
+   &amp;lt;groupId&amp;gt;com.danhaywood.isis.wicket&amp;lt;/groupId&amp;gt;
+    
&amp;lt;artifactId&amp;gt;danhaywood-isis-wicket-fullcalendar2-ui&amp;lt;/artifactId&amp;gt;
+&amp;lt;/dependency&amp;gt;
+</code></pre>
+
+&lt;/dependencies&gt;
+</code></pre></li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-200</code></p>
+
+<h4>ConferenceSession</h4>
+
+<ul>
+<li>declare it implements the <code>CalendarEventable</code> interface</p>
+
+<pre><code>public class ConferenceSession implements 
Comparable&lt;ConferenceSession&gt;
+    , com.danhaywood.isis.wicket.fullcalendar2.applib.CalendarEventable {
+</code></pre></li>
+<li>and implement the required methods:</p>
+
+<pre><code>    // //////////////////////////////////////
+    // CalendarEventable impl
+    // //////////////////////////////////////
+
+
+<pre><code>@Programmatic
+@Override
+public String getCalendarName() {
+    return "date";
+}
+
+
+@Programmatic
+@Override
+public CalendarEvent toCalendarEvent() {
+    return new CalendarEvent(
+            getDate().toDateTimeAtStartOfDay(), 
+            "date", 
+            container.titleOf(this));
+}
+</code></pre>
+
+</code></pre></li>
+</ul>
+
+<h2>ConferenceSessions</h2>
+
+<ul>
+<li>indicate that the listAll action should be called for the home page</p>
+
+<pre><code>@HomePage
+@Bookmarkable
+@ActionSemantics(Of.SAFE)
+@MemberOrder(sequence = "1")
+public List&lt;ConferenceSession&gt; listAll() {
+    return allInstances(ConferenceSession.class);
+}
+</code></pre></li>
+</ul>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>all sessions displayed automatically on home page</li>
+<li>switch to calendar view (top right, new icon should appear)</li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-210</code></p>
+
+<h2>View models</h2>
+
+<h4>SpeakerViewModel</h4>
+
+<ul>
+<li>shows summary info of speaker, their sessions, the tags of those 
sessions</p>
+
+<pre><code>package dom.simple;
+
+
+import java.util.List;
+import java.util.Set;
+
+
+import org.apache.isis.applib.DomainObjectContainer;
+import org.apache.isis.applib.ViewModel;
+import org.apache.isis.applib.annotation.MemberOrder;
+import org.apache.isis.applib.annotation.Render;
+import org.apache.isis.applib.annotation.Render.Type;
+import org.apache.isis.applib.annotation.Title;
+import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.applib.services.bookmark.BookmarkService;
+
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.io.BaseEncoding;
+
+
+public class SpeakerViewModel implements ViewModel {
+
+
+<pre><code>// //////////////////////////////////////
+// ViewModel impl
+// //////////////////////////////////////
+
+
+@Override
+public String viewModelMemento() {
+    Bookmark bookmark = bookmarkService.bookmarkFor(getSpeaker());
+    return encode(bookmark);
+}
+
+
+@Override
+public void viewModelInit(String memento) {
+    Bookmark bookmark = decode(memento);
+    setSpeaker((Speaker) bookmarkService.lookup(bookmark));
+}
+
+
+static String encode(Bookmark bookmark) {
+    return BaseEncoding.base32().encode(bookmark.toString().getBytes());
+}
+
+
+private static Bookmark decode(String memento) {
+    return new Bookmark(new String(BaseEncoding.base32().decode(memento)));
+}
+
+
+// //////////////////////////////////////
+// speaker (property)
+// //////////////////////////////////////
+
+
+private Speaker speaker;
+
+
[email protected](allowsNull = "true")
+@Title
+@MemberOrder(sequence = "1")
+public Speaker getSpeaker() {
+    return speaker;
+}
+
+
+public void setSpeaker(final Speaker speaker) {
+    this.speaker = speaker;
+}
+
+
+// //////////////////////////////////////
+// numberOfSessions (property)
+// //////////////////////////////////////
+
+
+@MemberOrder(sequence = "2")
+public int getNumberOfSessions() {
+    return getPresenting().size();
+}
+
+
+// //////////////////////////////////////
+// presenting (collection)
+// //////////////////////////////////////
+
+
+@Render(Type.EAGERLY)
+@MemberOrder(sequence = "1")
+public List&amp;lt;ConferenceSession&amp;gt; getPresenting() {
+    return container.allMatches(ConferenceSession.class, new 
Predicate&amp;lt;ConferenceSession&amp;gt;() {
+        @Override
+        public boolean apply(ConferenceSession input) {
+            return input.getSpeaker() == getSpeaker();
+        }
+    });
+}
+
+
+// //////////////////////////////////////
+// tags (collection)
+// //////////////////////////////////////
+
+
+@Render(Type.EAGERLY)
+@MemberOrder(sequence = "2")
+public List&amp;lt;Tag&amp;gt; getTags() {
+    return Lists.newArrayList(
+            Iterables.concat(
+                Iterables.transform(
+                    getPresenting(), 
+                    new Function&amp;lt;ConferenceSession, 
Set&amp;lt;Tag&amp;gt;&amp;gt;(){
+                        @Override
+                        public Set&amp;lt;Tag&amp;gt; apply(ConferenceSession 
input) {
+                            return input.getTags();
+                        }
+                    })
+            ));
+}
+
+
+// //////////////////////////////////////
+// Injected
+// //////////////////////////////////////
+
+
+private DomainObjectContainer container;
+public final void injectDomainObjectContainer(final DomainObjectContainer 
container) {
+    this.container = container;
+}
+
+
+private BookmarkService bookmarkService;
+public final void injectBookmarkService(final BookmarkService bookmarkService) 
{
+    this.bookmarkService = bookmarkService;
+}  
+</code></pre>
+
+}
+</code></pre></li>
+</ul>
+
+<h4>Speakers</h4>
+
+<ul>
+<li>new action to return all speakers as home page (should really be in its 
own application service...)</p>
+
+<pre><code>// //////////////////////////////////////
+// view models
+// //////////////////////////////////////
+
+
+@ActionSemantics(Of.SAFE)
+@HomePage
+public List&lt;SpeakerViewModel&gt; all() {
+    return Lists.newArrayList(Iterables.transform(listAll(), 
+        new Function&lt;Speaker, SpeakerViewModel&gt;() {
+            @Override
+            public SpeakerViewModel apply(Speaker input) {
+                Bookmark bookmark = bookmarkService.bookmarkFor(input);
+                return getContainer().newViewModelInstance(
+                        SpeakerViewModel.class, 
+                        SpeakerViewModel.encode(bookmark));
+            }
+        })
+    );
+}
+</code></pre></li>
+<li>and inject in the framework-provided <code>BookmarkService</code>    </p>
+
+<pre><code>// //////////////////////////////////////
+// Injected
+// //////////////////////////////////////
+
+
+private BookmarkService bookmarkService;
+
+
+public final void injectBookmarkService(final BookmarkService bookmarkService) 
{
+    this.bookmarkService = bookmarkService;
+}
+</code></pre></li>
+</ul>
+
+<h4>ConferenceSessions</h4>
+
+<ul>
+<li>remove @HomePage annotation from the earlier <code>listAll()</code> 
action</li>
+</ul>
+
+<h4>ConferenceSessionsFixture</h4>
+
+<ul>
+<li>create(...) set up random speaker with each session</p>
+
+<pre><code>session.setSpeaker(randomSpeaker());
+</code></pre></li>
+<li>randomSpeaker() supporting method</p>
+
+<pre><code>private Speaker randomSpeaker() {
+    List&lt;Speaker&gt; all = speakers.listAll();
+    while(true) {
+        try {
+            int selected = (int)(all.size() * Math.random());
+            return all.get(selected);
+        } catch(Exception ex){}
+    }
+}
+</code></pre></li>
+</ul>
+
+<h4>Demo</h4>
+
+<ul>
+<li>hit the home page</li>
+<li>select a speaker view model</li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-220</code></p>
+
+<h2>REST API</h2>
+
+<h4>DEMO</h4>
+
+<ul>
+<li><a 
href="http://localhost:8080/restful";>http://localhost:8080/restful</a></li>
+</ul>
+
+
+
+      </div>
+
+        <div id="edit" class="modal hide fade in" style="xxxdisplay: none; ">
+            <div class="modal-header">
+                <a class="close" data-dismiss="modal">x</a>
+
+                <h3>Thank you for contributing to the documention!</h3>
+            </div>
+            <div class="modal-body">
+                <h4>Any help with the documentation is greatly 
appreciated.</h4>
+                <p>All edits are reviewed before going live, so feel free to 
do much more than fix typos or links.  If you see a page that could benefit 
from an entire rewrite, we'd be thrilled to review it.  Don't be surprised if 
we like it so much we ask you for help with other pages :)</p>
+                <small>NOTICE: unless indicated otherwise on the pages in 
question, all editable content available from apache.org is presumed to be 
licensed under the Apache License (AL) version 2.0 and hence all submissions to 
apache.org treated as formal Contributions under the license terms.</small>
+                <!--[if gt IE 6]>
+                <h4>Internet Explorer Users</h4>
+                <p>If you are not an Apache committer, click the Yes link and 
enter a <i>anonymous</i> for the username and leave the password empty</p>
+                <![endif]-->
+
+            </div>
+            <div class="modal-footer">
+                Do you have an Apache ID?
+                <a 
href="javascript:void(location.href='https://cms.apache.org/redirect?uri='+escape(location.href))"
 class="btn">Yes</a>
+                <a 
href="javascript:void(location.href='https://anonymous:@cms.apache.org/redirect?uri='+escape(location.href))"
 class="btn">No</a>
+            </div>
+        </div>
+        <!--
+        <script src="./../javascript/bootstrap-modal.js"></script>
+        -->
+
+      <footer>
+        <hr/>
+        <p>
+        Copyright &copy; 2010~2014 The Apache Software Foundation, Licensed 
under the Apache License, Version 2.0.
+        <br/>
+        Apache Isis, Isis, Apache, the Apache feather logo, and the Apache 
Isis project logo are trademarks of The Apache Software Foundation.
+        </p>
+      </footer>
+
+    </div> <!-- /container -->
+
+  </body>
+</html>


Reply via email to