blob: 5d16a2a1ed44bb3fde4e57187088dd81655f0cf0 [file] [log] [blame]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Extending the Viewer :: Apache Isis</title>
<link rel="canonical" href="https://isis.apache.org/vw/2.0.0-M3/extending.html">
<meta name="generator" content="Antora 2.2.0">
<link rel="stylesheet" href="../../_/css/site.css">
<link rel="stylesheet" href="../../_/css/site-custom.css">
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,300i,400,400i,700,700i|Raleway:300,400,500,700,800|Montserrat:300,400,700" rel="stylesheet">
<link rel="home" href="https://isis.apache.org" title="Apache Isis">
<link rel="next" href="hints-and-tips.html" title="Hints-n-Tips">
<link rel="prev" href="customisation.html" title="Customisation">
</head>
<body class="article">
<header class="header">
<nav class="navbar">
<div class="navbar-brand">
<a class="navbar-item" href="https://isis.apache.org">
<span class="icon">
<img src="../../_/img/isis-logo-48x48.png"></img>
</span>
<span>Apache Isis</span>
</a>
<button class="navbar-burger" data-target="topbar-nav">
<span></span>
<span></span>
<span></span>
</button>
</div>
<div id="topbar-nav" class="navbar-menu">
<a class="navbar-end">
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link" href="#">Quick Start</a>
<div class="navbar-dropdown">
<span class="navbar-item navbar-heading">Starter Apps</span>
<a class="navbar-item" href="../../docs/latest/starters/helloworld.html">Hello World</a>
<a class="navbar-item" href="../../docs/latest/starters/simpleapp.html">Simple App</a>
<hr class="navbar-divider"/>
<span class="navbar-item navbar-heading">Demos &amp; Tutorials</span>
<a class="navbar-item" href="../../docs/latest/demo/about.html">Demo App</a>
<a class="navbar-item" href="https://danhaywood.gitlab.io/isis-petclinic-tutorial-docs/petclinic/1.16.2/intro.html">Petclinic (tutorial)</a>
<hr class="navbar-divider"/>
<span class="navbar-item navbar-heading">Resources</span>
<a class="navbar-item" href="../../docs/latest/resources/cheatsheet.html">Cheatsheet</a>
<a class="navbar-item" href="../../docs/latest/resources/icons.html">Icons</a>
</div>
</div>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link" href="#">Guides</a>
<div class="navbar-dropdown">
<span class="navbar-item navbar-heading">Development</span>
<a class="navbar-item" href="../../setupguide/latest/about.html">Setup Guide</a>
<hr class="navbar-divider"/>
<span class="navbar-item navbar-heading">Core</span>
<a class="navbar-item" href="../../userguide/latest/about.html">User Guide</a>
<a class="navbar-item" href="../../refguide/latest/about.html">Reference Guide</a>
<a class="navbar-item" href="../../testing/latest/about.html">Testing Guide</a>
</div>
</div>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link" href="#">Libraries</a>
<div class="navbar-dropdown">
<span class="navbar-item navbar-heading">For Use in Apps</span>
<a class="navbar-item" href="../../subdomains/latest/about.html">Subdomain Libraries</a>
<a class="navbar-item" href="../../valuetypes/latest/about.html">Value Types</a>
<hr class="navbar-divider"/>
<span class="navbar-item navbar-heading">Integrate between Apps</span>
<a class="navbar-item" href="../../mappings/latest/about.html">Bounded Context Mapping Libraries</a>
<hr class="navbar-divider"/>
<span class="navbar-item navbar-heading">Other</span>
<a class="navbar-item" href="../../incubator/latest/about.html">Incubator</a>
<a class="navbar-item" href="../../legacy/latest/about.html">Legacy</a>
</div>
</div>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link" href="#">Components</a>
<div class="navbar-dropdown">
<span class="navbar-item navbar-heading">Viewers</span>
<a class="navbar-item" href="../../vw/latest/about.html">Wicket UI</a>
<a class="navbar-item" href="../../vro/latest/about.html">Restful Objects (REST)</a>
<hr class="navbar-divider"/>
<span class="navbar-item navbar-heading">Security</span>
<a class="navbar-item" href="../../security/latest/about.html">Security Guide</a>
<hr class="navbar-divider"/>
<span class="navbar-item navbar-heading">Persistence</span>
<a class="navbar-item" href="../../pjdo/latest/about.html">DataNucleus (JDO)</a>
<hr class="navbar-divider"/>
<span class="navbar-item navbar-heading">Extensions</span>
<a class="navbar-item" href="../../extensions/latest/about.html">Extensions Catalog</a>
</div>
</div>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link" href="#">Support</a>
<div class="navbar-dropdown">
<span class="navbar-item navbar-heading">Contact</span>
<a class="navbar-item" href="../../docs/latest/support/slack-channel.html">Slack</a>
<a class="navbar-item" href="../../docs/latest/support/mailing-list.html">Mailing Lists</a>
<a class="navbar-item" href="https://issues.apache.org/jira/browse/ISIS">JIRA</a>
<a class="navbar-item" href="https://stackoverflow.com/questions/tagged/isis">Stack Overflow</a>
<hr class="navbar-divider"/>
<span class="navbar-item navbar-heading">Releases</span>
<a class="navbar-item" href="../../docs/latest/downloads/how-to.html">Downloads</a>
<a class="navbar-item" href="../../relnotes/latest/about.html">Release Notes</a>
<a class="navbar-item" href="../../docs/latest/archive/1-x.html">Archive (1.x)</a>
<hr class="navbar-divider"/>
<span class="navbar-item navbar-heading">Framework</span>
<a class="navbar-item" href="../../conguide/latest/about.html">Contributors' Guide</a>
<a class="navbar-item" href="../../comguide/latest/about.html">Committers' Guide</a>
<a class="navbar-item" href="../../core/latest/about.html">Core Design</a>
</div>
</div>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link" href="#">ASF</a>
<div class="navbar-dropdown">
<a class="navbar-item" href="http://www.apache.org/">Apache Homepage</a>
<a class="navbar-item" href="https://www.apache.org/events/current-event">Events</a>
<a class="navbar-item" href="https://www.apache.org/licenses/">Licenses</a>
<a class="navbar-item" href="https://www.apache.org/security/">Security</a>
<a class="navbar-item" href="https://www.apache.org/foundation/sponsorship.html">Sponsorship</a>
<a class="navbar-item" href="https://www.apache.org/foundation/thanks.html">Thanks</a>
<hr class="navbar-divider"/>
<a class="navbar-item" href="https://whimsy.apache.org/board/minutes/Isis.html">PMC board minutes</a>
</div>
</div>
<a class="navbar-item" href="../../docs/latest/about.html">
<span class="icon">
<img src="../../_/img/home.png"></img>
</span>
</a>
</div>
</div>
</nav>
</header>
<div class="body ">
<div class="nav-container" data-component="vw" data-version="2.0.0-M3">
<aside class="nav">
<div class="panels">
<div class="nav-panel-pagination">
<a class="page-previous" rel="prev" href="customisation.html" title="Customisation"><span></span></a>
<a class="page-next" rel="next"
href="hints-and-tips.html" title="Hints-n-Tips"><span></span></a>
<!--
page.parent doesn't seem to be set...
<a class="page-parent disabled" rel="prev" href="" title="Customisation"><span></span></a>
-->
</div>
<div class="nav-panel-menu is-active" data-panel="menu">
<nav class="nav-menu">
<h3 class="title"><a href="about.html">Wicket Viewer</a></h3>
<ul class="nav-list">
<li class="nav-item" data-depth="0">
<ul class="nav-list">
<li class="nav-item" data-depth="1">
<a class="nav-link" href="configuration-properties.html">Configuration Properties</a>
</li>
<li class="nav-item" data-depth="1">
<a class="nav-link" href="features.html">End-user Features</a>
</li>
<li class="nav-item" data-depth="1">
<a class="nav-link" href="security.html">Security</a>
</li>
<li class="nav-item" data-depth="1">
<a class="nav-link" href="customisation.html">Customisation</a>
</li>
<li class="nav-item is-current-page" data-depth="1">
<a class="nav-link" href="extending.html">Extending</a>
</li>
<li class="nav-item" data-depth="1">
<a class="nav-link" href="hints-and-tips.html">Hints-n-Tips</a>
</li>
<li class="nav-item" data-depth="1">
<button class="nav-item-toggle"></button>
<span class="nav-text">Extensions</span>
<ul class="nav-list">
<li class="nav-item" data-depth="2">
<a class="nav-link" href="exceldownload/about.html">Excel Download</a>
</li>
<li class="nav-item" data-depth="2">
<a class="nav-link" href="fullcalendar/about.html">fullcalendar</a>
</li>
<li class="nav-item" data-depth="2">
<a class="nav-link" href="gmap3/about.html">GMap3</a>
</li>
<li class="nav-item" data-depth="2">
<a class="nav-link" href="pdfjs/about.html">pdf.js</a>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</nav>
</div>
<div class="nav-panel-explore" data-panel="explore">
<div class="context">
<span class="title">Wicket Viewer</span>
<span class="version">2.0.0-M3</span>
</div>
<ul class="components">
<li class="component">
<span class="title"> </span>
<ul class="versions">
<li class="version is-latest">
<a href="../../docs/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">BC Mappings Catalog</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../mappings/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Committers' Guide</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../comguide/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Contributors' Guide</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../conguide/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Core Framework</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../core/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Extensions Catalog</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../extensions/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Incubator Catalog</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../incubator/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">JDO/DataNucleus</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../pjdo/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Legacy Catalog</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../legacy/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Reference Guide</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../refguide/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Release Notes</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../relnotes/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Restful Objects Viewer</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../vro/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Security Guide</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../security/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Setup Guide</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../setupguide/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Subdomains Catalog</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../subdomains/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Testing Guide</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../testing/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">User Guide</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../userguide/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Value Types Catalog</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../valuetypes/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component is-current">
<span class="title">Wicket Viewer</span>
<ul class="versions">
<li class="version is-current is-latest">
<a href="about.html">2.0.0-M3</a>
</li>
</ul>
</li>
</ul>
</div>
</div>
</aside>
</div>
<main role="main">
<div class="toolbar" role="navigation">
<button class="nav-toggle"></button>
<a href="../../docs/2.0.0-M3/about.html" class="home-link"></a>
<nav class="breadcrumbs" aria-label="breadcrumbs">
<ul>
<li><a href="about.html">Wicket Viewer</a></li>
<li><a href="extending.html">Extending</a></li>
</ul>
</nav>
<div class="edit-this-page"><a href="https://github.com/apache/isis/edit/2.0.0-M3/viewers/wicket/adoc/modules/ROOT/pages/extending.adoc">Edit</a></div>
</div>
<article class="doc">
<a name="section-top"></a>
<h1 class="page">Extending the Viewer</h1>
<div id="preamble">
<div class="sectionbody">
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
TODO: this content has not yet been reviewed/updated for v2.0
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The Wicket viewer allows you to customize the GUI in several (progressively more sophisticated) ways:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>the <a href="customisation.html" class="page">customisation</a> chapter described:</p>
<div class="ulist">
<ul>
<li>
<p>by <a href="customisation.html#tweaking-css-classes" class="page">tweaking the UI using CSS</a></p>
</li>
<li>
<p>by <a href="customisation.html#custom-javascript" class="page">tweaking the UI using Javascript</a></p>
</li>
</ul>
</div>
</li>
<li>
<p>in this chapter we have a number of heavier-weight approaches:</p>
<div class="ulist">
<ul>
<li>
<p>by writing a <a href="#custom-bootstrap-theme">custom bootstrap theme</a></p>
</li>
<li>
<p>by <a href="#replacing-page-elements">replacing elements of the page</a> using the <code>ComponentFactory</code> interface</p>
</li>
<li>
<p>by implementing <a href="#custom-pages">replacement page implementations</a> for the standard page types</p>
</li>
</ul>
</div>
</li>
</ul>
</div>
<div class="paragraph">
<p>The chapter also includes a technique useful when prototyping, allowing <a href="#login-via-query-args-prototyping">user/passwords to be specified as query arguments</a>.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="custom-bootstrap-theme"><a class="anchor" href="#custom-bootstrap-theme"></a>Custom Bootstrap theme</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The Apache Isis Wicket viewer uses <a href="http://getbootstrap.com/">Bootstrap</a> styles and components, courtesy of the <a href="https://github.com/l0rdn1kk0n/wicket-bootstrap">Wicket Bootstrap</a> integration.</p>
</div>
<div class="paragraph">
<p>By default the viewer uses the default bootstrap theme.
It is possible to configure the Wicket viewer to allow the user to <a href="configuration-properties.html#themes" class="page">select other themes</a> provided by <a href="http://bootswatch.com">bootswatch.com</a>, and if required one of these can be <a href="customisation.html#default-theme" class="page">set as the default</a>.</p>
</div>
<div class="paragraph">
<p>However, you may instead want to write your own custom theme, for example to fit your company&#8217;s look-n-feel/interface guidelines.
This is done by implementing <a href="https://github.com/l0rdn1kk0n/wicket-bootstrap">Wicket Bootstrap</a>'s <code>de.agilecoders.wicket.core.settings.ITheme</code> class.
This defines:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>the name of the theme</p>
</li>
<li>
<p>the resources it needs (the CSS and optional JS and/or fonts), and</p>
</li>
<li>
<p>optional urls to load them from a Content Delivery Network (CDN).</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>To make use of the custom <code>ITheme</code> the application should register it by subclassing <code>IsisWicketApplication</code> (also register this in <code>web.xml</code>) and add the following snippet:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public void init() {
...
IBootstrapSettings settings = new BootstrapSettings();
ThemeProvider themeProvider = new SingleThemeProvider(new MyTheme());
settings.setThemeProvider(themeProvider);
Bootstrap.install(getClass(), settings);
}</code></pre>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="replacing-page-elements"><a class="anchor" href="#replacing-page-elements"></a>Replacing page elements</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Replacing elements of the page is the most powerful general-purpose way to customize the look-n-feel of the viewer.
Examples include the
<a href="gmap3/about.html" class="page">Gmap3</a>, <a href="fullcalendar/about.html" class="page">Fullcalendar</a>, <a href="exceldownload/about.html" class="page">Excel Download</a> and <a href="pdfjs/about.html" class="page">pdf.js</a> components.</p>
</div>
<div class="paragraph">
<p>The pages generated by Apache Isis' Wicket viewer are built up of numerous elements, from fine-grained widgets for property/parameter fields, to much larger components that take responsibility for rendering an entire entity, or a collection of entities.
Under the covers these are all implementations of the the Apache Wicket <code>Component</code> API.
The larger components delegate to the smaller, of course.</p>
</div>
<div class="sect2">
<h3 id="how-the-viewer-selects-components"><a class="anchor" href="#how-the-viewer-selects-components"></a>How the viewer selects components</h3>
<div class="paragraph">
<p>Components are created using Apache Isis' <code>ComponentFactory</code> interface, which are registered in turn through the <code>ComponentFactoryRegistrar</code> interface.
Every component is categorizes by type (the <code>ComponentType</code> enum), and Apache Isis uses this to determine which <code>ComponentFactory</code> to use.
For example, the <code>ComponentType.BOOKMARKED_PAGES</code> is used to locate the <code>ComponentFactory</code> that will build the bookmarked pages panel.</p>
</div>
<div class="paragraph">
<p>Each factory is also handed a model (an implementation of <code>org.apache.wicket.IModel</code>) appropriate to its <code>ComponentType</code>; this holds the data to be rendered.
For example, <code>ComponentType.BOOKMARKED_PAGES</code> is given a <code>BookmarkedPagesModel</code>, while <code>ComponentType.SCALAR_NAME_AND_VALUE</code> factories are provided a model of type of type <code>ScalarModel</code> .</p>
</div>
<div class="paragraph">
<p>In some cases there are several factories for a given <code>ComponentType</code>; this is most notably the case for <code>ComponentType.SCALAR_NAME_AND_VALUE</code>.
After doing a first pass selection of candidate factories by <code>ComponentType</code>, each factory is then asked if it <code>appliesTo(Model)</code>.
This is an opportunity for the factory to check the model itself to see if the data within it is of the appropriate type.</p>
</div>
<div class="paragraph">
<p>Thus, the <code>BooleanPanelFactory</code> checks that the <code>ScalarModel</code> holds a boolean, while the <code>JodaLocalDatePanelFactory</code> checks to see if it holds <code>org.joda.time.LocalDate</code>.</p>
</div>
<div class="paragraph">
<p>There will typically be only one <code>ComponentFactory</code> capable of rendering a particular <code>ComponentType</code>/<code>ScalarModel</code> combination; at any rate, the framework stops as soon as one is found.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>There is one refinement to the above algorithm where multiple component factories might be used to render an object; this is discussed in <a href="extending/replacing-page-elements.html#collections" class="page">Additional Views of Collections</a>, below.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="how-to-replace-a-component"><a class="anchor" href="#how-to-replace-a-component"></a>How to replace a component</h3>
<div class="paragraph">
<p>This design (the <a href="http://en.wikipedia.org/wiki/Chain-of-responsibility_pattern">chain of responsibility</a> design pattern) makes it quite straightforward to change the rendering of any element of the page.
For example, you might switch out Apache Isis' sliding bookmark panel and replace it with one that presents the bookmarks in some different fashion.</p>
</div>
<div class="paragraph">
<p>First, you need to write a <code>ComponentFactory</code> and corresponding <code>Component</code>.
The recommended approach is to start with the source of the <code>Component</code> you want to switch out.
For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public class MyBookmarkedPagesPanelFactory extends ComponentFactoryAbstract {
public MyBookmarkedPagesPanelFactory() {
super(ComponentType.BOOKMARKED_PAGES);
}
@Override
public ApplicationAdvice appliesTo(final IModel&lt;?&gt; model) {
return appliesIf(model instanceof BookmarkedPagesModel);
}
@Override
public Component createComponent(final String id, final IModel&lt;?&gt; model) {
final BookmarkedPagesModel bookmarkedPagesModel = (BookmarkedPagesModel) model;
return new MyBookmarkedPagesPanel(id, bookmarkedPagesModel);
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>and</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public class MyBookmarkedPagesPanel
extends PanelAbstract&lt;BookmarkedPagesModel&gt; {
...
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Here <code>PanelAbstract</code> ultimately inherits from <code>org.apache.wicket.Component</code>.
Your new <code>Component</code> uses the information in the provided model (eg <code>BookmarkedPagesModel</code>) to know what to render.</p>
</div>
<div class="paragraph">
<p>Next, you will require a custom implementation of the <code>ComponentFactoryRegistrar</code> that registers your custom <code>ComponentFactory</code> as a replacement:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Singleton
public class MyComponentFactoryRegistrar extends ComponentFactoryRegistrarDefault {
@Override
public void addComponentFactories(ComponentFactoryList componentFactories) {
super.addComponentFactories(componentFactories);
componentFactories.add(new MyBookmarkedPagesPanelFactory());
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>This will result in the new component being used instead of (that is, discovered prior to) Isis' default implementation.</p>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Previously we suggested using "replace" rather than "add"; however this has unclear semantics for some component types; see <a href="https://issues.apache.org/jira/browse/ISIS-996">ISIS-996</a>.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Finally, you&#8217;ll need to subclass <code>IsisWicketApplication</code> (register in <code>web.xml</code>)and then adjust the Guice bindings returned in the Guice <code>Module</code> returned by <code>newIsisWicketModule()</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public class MyAppApplication extends IsisWicketApplication {
@Override
protected Module newIsisWicketModule() {
final Module isisDefaults = super.newIsisWicketModule();
final Module myAppOverrides = new AbstractModule() {
@Override
protected void configure() {
...
bind(ComponentFactoryRegistrar.class)
.to(MyComponentFactoryRegistrar.class);
...
}
};
return Modules.override(isisDefaults).with(myAppOverrides);
}
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="additional-views-of-collections"><a class="anchor" href="#additional-views-of-collections"></a>Additional Views of Collections</h3>
<div class="paragraph">
<p>As explained above, in most cases Apache Isis' Wicket viewer will search for the first <code>ComponentFactory</code> that can render an element, and use it.
In the case of (either standalone or parented) collections, though, Apache Isis will show all available views.</p>
</div>
<div class="paragraph">
<p>For example, out-of-the-box Apache Isis provides a table view, a summary view (totals/sums/averages of any data), and a collapsed view.
These are selected by clicking on the toolbar by each collection.</p>
</div>
<div class="paragraph">
<p>Additional views though could render the objects in the collection as a variety of ways.
Indeed, some third-party implementations in the (non-ASF) <a href="https://platform.incode.org" target="_blank" rel="noopener">Incode Platform</a> already exist, including:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>excel component - collection as a downloadable excel spreadsheet</p>
</li>
<li>
<p>gmap3 component - render any objects with a location on a map</p>
</li>
<li>
<p>pdf.js component - render Blob containined PDF as a scrollable image</p>
</li>
<li>
<p>wicked charts component - barchart of any data</p>
</li>
<li>
<p>full calendar - render any objects with date properties on a calendar</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Registering these custom views is just a matter of adding the appropriate Maven module to the classpath.
Apache Isis uses the JDK <code>ServiceLoader</code> API to automatically discover and register the <code>ComponentFactory</code> of each such component.</p>
</div>
<div class="paragraph">
<p>If you want to write your own alternative component and auto-register, then include a file <code>META-INF/services/org.apache.isis.viewer.wicket.ui.ComponentFactory</code> whose contents is the fully-qualified class name of the custom <code>ComponentFactory</code> that you have written.</p>
</div>
<div class="paragraph">
<p>Wicket itself has lots of components available at its <a href="http://wicketstuff.org">wicketstuff.org</a> companion website; you might find some of these useful for your own customizations.</p>
</div>
</div>
<div class="sect2">
<h3 id="custom-object-view-eg-dashboard"><a class="anchor" href="#custom-object-view-eg-dashboard"></a>Custom object view (eg dashboard)</h3>
<div class="paragraph">
<p>One further use case in particular is worth highlighting; the rendering of an entire entity.
Normally entities this is done using <code>EntityCombinedPanelFactory</code>, this being the first <code>ComponentFactory</code> for the <code>ComponentType.ENTITY</code> that is registered in Apache Isis default <code>ComponentFactoryRegistrarDefault</code>.</p>
</div>
<div class="paragraph">
<p>You could, though, register your own <code>ComponentFactory</code> for entities that is targeted at a particular class of entity - some sort of object representing a dashboard, for example.
It can use the <code>EntityModel</code> provided to it to determine the class of the entity, checking if it is of the appropriate type.
Your custom factory should also be registered before the <code>EntityCombinedPanelFactory</code> so that it is checked prior to the default <code>EntityCombinedPanelFactory</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Singleton
public class MyComponentFactoryRegistrar extends ComponentFactoryRegistrarDefault {
@Override
public void addComponentFactories(ComponentFactoryList componentFactories) {
componentFactories.add(new DashboardEntityFactory());
...
super.addComponentFactories(componentFactories);
...
}
}</code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="custom-pages"><a class="anchor" href="#custom-pages"></a>Custom pages</h2>
<div class="sectionbody">
<div class="paragraph">
<p>In the vast majority of cases customization should be sufficient by <a href="#replacing-page-elements">replacing elements of a page</a>.
However, it is also possible to define an entirely new page for a given page type.</p>
</div>
<div class="paragraph">
<p>Isis defines eight page types (see the <code>org.apache.isis.viewer.wicket.model.models.PageType</code> enum):</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<caption class="title">Table 1. PageType enum</caption>
<colgroup>
<col style="width: 20%;">
<col style="width: 80%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Page type</th>
<th class="tableblock halign-left valign-top">Renders</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">SIGN_IN</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The initial sign-in (aka login) page</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">SIGN_UP</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The sign-up page (if <a href="../../refguide/2.0.0-M3/applib-svc/UserRegistrationService.html" class="page">user registration</a> is enabled).</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">SIGN_UP_VERIFY</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The sign-up verification page (if <a href="../../refguide/2.0.0-M3/applib-svc/UserRegistrationService.html" class="page">user registration</a> is enabled; as accessed by link from verification email)</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">PASSWORD_RESET</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The password reset page (if enabled).</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">HOME</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The home page, displaying either the welcome message or dashboard</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">ABOUT</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The about page, accessible from link top-right</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">ENTITY</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Renders a single entity or view model</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">STANDALONE_COLLECTION</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Page rendered after invoking an action that returns a collection of entites</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">VALUE</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">After invoking an action that returns a value type (though not URLs or Blob/Clobs, as these are handled appropriately automatically).</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">VOID_RETURN</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">After invoking an action that is <code>void</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">ACTION_PROMPT</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">(No longer used).</p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>The <code>PageClassList</code> interface declares which class (subclass of <code>org.apache.wicket.Page</code> is used to render for each of these types.
For example, Apache Isis' <code>WicketSignInPage</code> renders the signin page.</p>
</div>
<div class="paragraph">
<p>To specify a different page class, create a custom subclass of <code>PageClassList</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Singleton
public class MyPageClassList extends PageClassListDefault {
protected Class&lt;? extends Page&gt; getSignInPageClass() {
return MySignInPage.class;
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>You then need to register your custom <code>PageClassList</code>.
This is done by subclassing <code>IsisWicketApplication</code> (register the subclass in <code>web.xml</code>) and adjusting the Guice bindings of the guice <code>Module</code> returned by the <code>newIsisWicketModule()</code> method:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public class MyAppApplication extends IsisWicketApplication {
@Override
protected Module newIsisWicketModule() {
final Module isisDefaults = super.newIsisWicketModule();
final Module myAppOverrides = new AbstractModule() {
@Override
protected void configure() {
...
bind(PageClassList.class).to(MyPageClassList.class);
...
}
};
return Modules.override(isisDefaults).with(myAppOverrides);
}
}</code></pre>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="login-via-query-args-prototyping"><a class="anchor" href="#login-via-query-args-prototyping"></a>Login via Query Args (for Prototyping)</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This section describes a (slightly hacky) way of allowing logins using query args, eg <a href="http://localhost:8080/?user=sven&amp;pass=pass">http://localhost:8080/?user=sven&amp;pass=pass</a>. This might be useful while prototyping or demonstrating a scenario involving multiple different interacting users.</p>
</div>
<div class="paragraph">
<p>First, you&#8217;ll need to subclass <code>IsisWicketApplication</code> (and register in <code>web.xml</code>).
Then, override <code>newSession()</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-ini hljs" data-lang="ini">private final static boolean DEMO_MODE_USING_CREDENTIALS_AS_QUERYARGS = false;
@Override
public Session newSession(final Request request, final Response response) {
if(!DEMO_MODE_USING_CREDENTIALS_AS_QUERYARGS) {
return super.newSession(request, response);
}
// else demo mode
final AuthenticatedWebSessionForIsis s = (AuthenticatedWebSessionForIsis) super.newSession(request, response);
IRequestParameters requestParameters = request.getRequestParameters();
final org.apache.wicket.util.string.StringValue user = requestParameters.getParameterValue("user");
final org.apache.wicket.util.string.StringValue password = requestParameters.getParameterValue("pass");
s.signIn(user.toString(), password.toString());
return s;
}
@Override
public WebRequest newWebRequest(HttpServletRequest servletRequest, String filterPath) {
if(!DEMO_MODE_USING_CREDENTIALS_AS_QUERYARGS) {
return super.newWebRequest(servletRequest, filterPath);
}
// else demo mode
try {
String uname = servletRequest.getParameter("user");
if (uname != null) {
servletRequest.getSession().invalidate();
}
} catch (Exception e) {
}
WebRequest request = super.newWebRequest(servletRequest, filterPath);
return request;
}</code></pre>
</div>
</div>
</div>
</div>
</article>
<aside class="article-aside toc" role="navigation">
<p class="toc-title">On this page</p>
<div id="article-toc"></div>
</aside>
</main>
</div>
<footer class="footer">
<div class="content">
<div class="copyright">
<p>
Copyright © 2010~2020 The Apache Software Foundation, licensed under the Apache License, v2.0.
<br/>
Apache, the Apache feather logo, Apache Isis, and the Apache Isis project logo are all trademarks of The Apache Software Foundation.
</p>
</div>
<div class="revision">
<p>Revision: SNAPSHOT</p>
</div>
</div>
</footer>
<script src="../../_/js/site.js"></script>
<script async src="../../_/js/vendor/highlight.js"></script>
<script src="../../_/js/vendor/jquery-3.4.1.min.js"></script>
<script src="../../_/js/vendor/jquery-ui-1.12.1.custom.widget-only.min.js"></script>
<script src="../../_/js/vendor/jquery.tocify.min.js"></script>
<script>
$(function() {
$("#article-toc").tocify( {
showEffect: "slideDown",
hashGenerator: "pretty",
hideEffect: "slideUp",
selectors: "h2, h3",
scrollTo: 120,
smoothScroll: true,
theme: "jqueryui",
highlightOnScroll: true
} );
});
</script>
</body>
</html>