You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by bu...@apache.org on 2014/11/18 22:25:57 UTC

svn commit: r929693 - in /websites/staging/isis/trunk: cgi-bin/ content/ content/documentation.html content/reference/recognized-annotations/about.html content/tutorials/apacheconeu-2014.html

Author: buildbot
Date: Tue Nov 18 21:25:56 2014
New Revision: 929693

Log:
Staging update by buildbot for isis

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

Propchange: websites/staging/isis/trunk/cgi-bin/
------------------------------------------------------------------------------
--- cms:source-revision (original)
+++ cms:source-revision Tue Nov 18 21:25:56 2014
@@ -1 +1 @@
-1640425
+1640429

Propchange: websites/staging/isis/trunk/content/
------------------------------------------------------------------------------
--- cms:source-revision (original)
+++ cms:source-revision Tue Nov 18 21:25:56 2014
@@ -1 +1 @@
-1640425
+1640429

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 21:25:56 2014
@@ -477,7 +477,7 @@
 
 <ul>
 <li><a href="intro/resources/downloadable-presentations.html">Downloadable Presentations</a></li>
-<li><strong><a href="intro/resources/editor-templates.html">IDE templates</strong></a>** (IntelliJ and Eclipse)</li>
+<li><strong><a href="intro/resources/editor-templates.html">IDE templates</a></strong> (IntelliJ and Eclipse)</li>
 <li><a href="intro/resources/icons.html">Icons</a></li>
 <li><strong><a href="intro/resources/cheat-sheet.html">Cheat Sheet</a></strong>
 </div>

Modified: websites/staging/isis/trunk/content/reference/recognized-annotations/about.html
==============================================================================
--- websites/staging/isis/trunk/content/reference/recognized-annotations/about.html (original)
+++ websites/staging/isis/trunk/content/reference/recognized-annotations/about.html Tue Nov 18 21:25:56 2014
@@ -436,7 +436,7 @@
         <td></td>
     </tr>
     <tr>
-        <td><a href="./ActionOrder-deprecated.html">@ActionOrder</a></td>
+        <td><a href="./ActionOrder-deprecated.html" style="text-decoration: line-through;" >@ActionOrder</a></td>
         <td>UI</td>
         <td>Order of buttons and menu items representing actions.
         </td>
@@ -535,7 +535,7 @@
         <td></td>
     </tr>
     <tr>
-        <td><a href="./Debug-deprecated.html">@Debug</a></td>
+        <td><a href="./Debug-deprecated.html" style="text-decoration: line-through;" >@Debug</a></td>
         <td>UI</td>
         <td>Action only invocable in debug mode.
         </td>
@@ -591,7 +591,7 @@
         <td></td>
     </tr>
     <tr>
-        <td><a href="./Exploration-deprecated.html">@Exploration</a></td>
+        <td><a href="./Exploration-deprecated.html" style="text-decoration: line-through;" >@Exploration</a></td>
         <td>UI</td>
         <td>Action available in special 'exploration' mode.
         </td>
@@ -607,7 +607,7 @@
         <td></td>
     </tr>
     <tr>
-        <td><a href="./FieldOrder-deprecated.html">@FieldOrder</a></td>
+        <td><a href="./FieldOrder-deprecated.html" style="text-decoration: line-through;" >@FieldOrder</a></td>
         <td>UI</td>
         <td>Order of properties and collections
         </td>
@@ -632,7 +632,7 @@
         <td></td>
     </tr>
     <tr>
-        <td><a href="./Idempotent-deprecated.html">@Idempotent</a></td>
+        <td><a href="./Idempotent-deprecated.html" style="text-decoration: line-through;" >@Idempotent</a></td>
         <td>Domain</td>
         <td>Replaced by @ActionSemantics.
         </td>
@@ -640,7 +640,7 @@
         <td>Y</td>
     </tr>
     <tr>
-        <td><a href="./Ignore-deprecated.html">@Ignore</a></td>
+        <td><a href="./Ignore-deprecated.html" style="text-decoration: line-through;" >@Ignore</a></td>
         <td>Domain</td>
         <td>Replaced by @Programmatic.
         </td>
@@ -822,7 +822,7 @@
         <td></td>
     </tr>
     <tr>
-        <td><a href="./PostsPropertyChangedEvent-deprecated.html">@PostsPropertyChangedEvent</a></td>
+        <td><a href="./PostsPropertyChangedEvent-deprecated.html" style="text-decoration: line-through;" >@PostsPropertyChangedEvent</a></td>
         <td>Domain</td>
         <td>Replaced by @PropertyInteraction
         </td>
@@ -873,7 +873,7 @@
         <td></td>
     </tr>
     <tr>
-        <td><a href="./QueryOnly-deprecated.html">@QueryOnly</a></td>
+        <td><a href="./QueryOnly-deprecated.html" style="text-decoration: line-through;" >@QueryOnly</a></td>
         <td>Domain</td>
         <td>Replaced by @ActionSemantics.
         </td>
@@ -913,7 +913,7 @@
         <td></td>
     </tr>
     <tr>
-        <td><a href="./Resolve-deprecated.html">@Resolve</a></td>
+        <td><a href="./Resolve-deprecated.html" style="text-decoration: line-through;" >@Resolve</a></td>
         <td>UI</td>
         <td>Replaced by @Render.
         </td>

Modified: websites/staging/isis/trunk/content/tutorials/apacheconeu-2014.html
==============================================================================
--- websites/staging/isis/trunk/content/tutorials/apacheconeu-2014.html (original)
+++ websites/staging/isis/trunk/content/tutorials/apacheconeu-2014.html Tue Nov 18 21:25:56 2014
@@ -417,7 +417,9 @@
 </h1>
 </div>
 
-<p>A half-day tutorial on developing domain-driven apps using Apache Isis.</p>
+<p><div class="note">
+A half-day tutorial on developing domain-driven apps using Apache Isis.
+</div></p>
 
 <h2>Run the archetype</h2>
 
@@ -448,7 +450,7 @@ mvn clean install
 <pre><code>mvn jetty:run    
 </code></pre>
 
-<h4>DEMO</h4>
+<h2>Using the app</h2>
 
 <ul>
 <li>install fixtures</li>
@@ -459,22 +461,24 @@ mvn clean install
 
 <h2>Dev environment</h2>
 
+<p>Set up an IDE and import the project to be able to run and debug the app</p>
+
 <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>
+<li>configure <a href="http://isis.apache.org/intro/getting-started/ide/intellij.html">IntelliJ</a>, import app</li>
+<li>configure <a href="http://isis.apache.org/intro/getting-started/ide/eclipse.html">Eclipse</a>, import app</li>
 </ul></li>
-<li><a href="http://isis.apache.org/intro/resources/editor-templates.html">IDE Editor templates</a></li>
+<li>Set up IDE <a href="http://isis.apache.org/intro/resources/editor-templates.html">editor templates</a></li>
 </ul>
 
 <h4>Run</h4>
 
 <ul>
 <li>Run the app from within the IDE</li>
-<li>Run with different deploymentTypes:
+<li>Run with different deploymentTypes, note whether <code>@Prototype</code> actions are available or not:
 <ul>
 <li><code>--type SERVER_PROTOTYPE</code></li>
 <li><code>--type SERVER</code></li>
@@ -485,17 +489,20 @@ mvn clean install
 
 <ul>
 <li><code>myapp</code> : parent module</li>
-<li><code>myapp-dom</code>
+<li><code>myapp-dom</code>: domain objects module
+<ul>
+<li>entity: <code>dom.simple.SimpleObject</code></li>
+<li>repository: <code>dom.simple.SimpleObjects</code></li>
+</ul></li>
+<li><code>myapp-fixture</code>: fixtures module
 <ul>
-<li><code>dom.simple.SimpleObject</code></li>
-<li><code>dom.simple.SimpleObjects</code></li>
+<li>fixture script:<code>fixture.simple.SimpleObjectsFixture</code></li>
 </ul></li>
-<li><code>myapp-fixture</code>
+<li><code>myapp-integtests</code>: integration tests module</li>
+<li><code>myapp-webapp</code>: webapp module
 <ul>
-<li><code>fixture.simple.SimpleObjectsFixture</code></li>
+<li>(builds the WAR file)</li>
 </ul></li>
-<li><code>myapp-integtests</code></li>
-<li><code>myapp-webapp</code></li>
 </ul>
 
 <h2>Testing</h2>
@@ -557,6 +564,8 @@ mvn clean install
 
 <p><img src="http://yuml.me/a070d071" alt="" /></p>
 
+<p>which in yuml.me's DSL is:</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]
@@ -566,7 +575,7 @@ mvn clean install
 
 <ul>
 <li>rename the <code>SimpleObject</code> class</li>
-<li>rename the <code>name</code> property</li>
+<li>rename the <code>SimpleObject</code> class' <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>
@@ -602,7 +611,8 @@ mvn clean install
 <h2>Actions</h2>
 
 <ul>
-<li>update the domain property (<code>SimpleObject#name</code> above, renamed by now)</li>
+<li>update the domain action (<code>SimpleObject#name</code> above, renamed by now)</li>
+<li>use the <a href="http://isis.apache.org/reference/recognized-annotations/Named.html">@Named</a> annotation to specify the name of action parameters</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>
@@ -615,8 +625,8 @@ mvn clean install
 <ul>
 <li>add Chrome extensions
 <ul>
-<li>install Postman</li>
-<li>install JSON-View</li>
+<li>install <a href="https://chrome.google.com/webstore/detail/postman-rest-client/fdmmgilgnpjigdojojpjoooidkmcomcm?hl=en">Postman</a></li>
+<li>install <a href="https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc?hl=en">JSON-View</a></li>
 </ul></li>
 <li>browse to Wicket viewer, install fixtures</li>
 <li>browse to the http://localhost:8080/restful API</li>
@@ -626,7 +636,10 @@ mvn clean install
 <h2>Specify Action semantics</h2>
 
 <ul>
-<li>note the HTTP methods exposed in the REST API</li>
+<li>experiment changing [@ActionSemantics] on actions
+<ul>
+<li>note the HTTP methods exposed in the REST API change</li>
+</ul></li>
 </ul>
 
 <h2>Value properties</h2>
@@ -634,16 +647,23 @@ mvn clean install
 <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>
+<li>for string properties</li>
+<li>use the <a href="http://isis.apache.org/reference/recognized-annotations/MultiLine.html">@Multiline</a> annotation to render a text area instead of a text box</li>
+<li>use the <a href="http://isis.apache.org/reference/recognized-annotations/MaxLength.html">@MaxLength</a> annotation to specify the maximum number of characters allowable</li>
+<li>use <a href="http://isis.apache.org/components/objectstores/jdo/mapping-joda-dates.html">joda date/time</a> properties</li>
+<li>use <a href="http://isis.apache.org/components/objectstores/jdo/mapping-bigdecimals.html">bigdecimals</a> properties</li>
+<li>use <a href="http://isis.apache.org/components/objectstores/jdo/mapping-blobs.html">blob/clobs</a> properties</li>
+<li>specify whether <a href="http://isis.apache.org/components/objectstores/jdo/mapping-mandatory-and-optional-properties.html">optional or mandatory</a></li>
+</ul></li>
+<li>TODO: enums</li>
+<li>update the corresponding domain service for creating new instances
+<ul>
+<li>for all non-optional properties will either need to prompt for a value, or calculate some suitable default</li>
 </ul></li>
-<li>update the corresponding domain service
+<li>change the implementation of title, if need be
 <ul>
-<li>if not optional</li>
+<li>might prefer to use <a href="http://isis.apache.org/reference/recognized-annotations/Title.html">@Title</a> annotation rather than the <code>title()</code> method</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>
@@ -673,7 +693,7 @@ mvn clean install
 <h2>Collections</h2>
 
 <ul>
-<li>Ensure that all domain classes implement <code>Comparable</code>
+<li>Ensure that all domain classes implement <code>java.lang.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>
@@ -681,7 +701,7 @@ mvn clean install
 <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>Use the @Render (http://isis.apache.org/reference/recognized-annotations/Render.html) annotation to indicate if the collection should be visible or hidden by default</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>
 
@@ -701,7 +721,9 @@ mvn clean install
 <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>
+<h2>Business rules</h2>
+
+<h3>See it!</h3>
 
 <ul>
 <li>Use the <a href="http://isis.apache.org/reference/recognized-annotations/Hidden.html">@Hidden</a> annotation to make properties/collections/actions invisible
@@ -711,22 +733,27 @@ mvn clean install
 <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>
+<h3>Use it!</h3>
 
 <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>
+<h3>Do it!</h3>
 
 <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>
+<li>Validate string properties or action paramters:
+<ul>
+<li>use the <a href="http://isis.apache.org/reference/recognized-annotations/RegEx.html">@Regex</a> annotation to specify a pattern</li>
+<li>use the <a href="http://isis.apache.org/reference/recognized-annotations/MinLength.html">@MinLength</a> annotation to indicate a minimum number of characters</li>
+</ul></li>
+<li>For any data type:
+<ul>
+<li>use the <a href="http://isis.apache.org/reference/recognized-annotations/MustSatisfy.html">@MustSatisfy</a> annotation to specify an arbitrary constraint</li>
+</ul></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>
@@ -750,6 +777,10 @@ mvn clean install
 
 <p>.layout.json</p>
 
+<p>CSS</p>
+
+<p>@HomePage</p>
+
 <p>view models</p>
 
 <p>Customising the REST API</p>
@@ -771,1718 +802,7 @@ mvn clean install
 
 <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;
-
-@javax.jdo.annotations.Persistent
-@javax.jdo.annotations.Column(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;
-@javax.jdo.annotations.Column(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;
-
-@javax.jdo.annotations.PersistenceCapable(identityType=IdentityType.DATASTORE)
-@javax.jdo.annotations.DatastoreIdentity(
-        strategy=javax.jdo.annotations.IdGeneratorStrategy.IDENTITY,
-         column="id")
-@javax.jdo.annotations.Version(
-        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;
-
-
-@javax.jdo.annotations.Column(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;
-
-
-@javax.jdo.annotations.PersistenceCapable(identityType=IdentityType.DATASTORE)
-@javax.jdo.annotations.DatastoreIdentity(
-        strategy=javax.jdo.annotations.IdGeneratorStrategy.IDENTITY,
-         column="id")
-@javax.jdo.annotations.Version(
-        strategy=VersionStrategy.VERSION_NUMBER, 
-        column="version")
-@ObjectType("TAG")
-@Bookmarkable
-public class Tag implements Comparable&lt;Tag&gt; {
-
-
-<pre><code>// //////////////////////////////////////
-// name (property)
-// //////////////////////////////////////
-
-
-private String name;
-
-
-@javax.jdo.annotations.Column(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)
-// //////////////////////////////////////
-
-
-@javax.jdo.annotations.Join
-@javax.jdo.annotations.Element(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;
-
-
-@javax.jdo.annotations.Column(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>
+<p>integration tests</p>