Showtime
My Blackberry Enterprise Server Push Utility for the Lotus Notes Client, allows you to create Jobs for individual Channel, Message, and Browser Content Pushes, as well as allows you to delete Pushed Channel Icons from defined recipient devices.
Contact Information
Blogger, podcaster, writer, and geek Chris Toohey covers topics from application development to the latest must-have-gadgets.
Latest Updates

More on Mailer...
More on Junction Lite...
More on Remote Console...

More on Controller API Utility...
Products & Applications
Time Tracker
The idea is simple. At the start of your day - upon completion of your first task - create an entry highlighting what you did and whether you feel it was an efficient or inefficient use of your time. Based on several requests, you can also select the priority, apply categories, or even align your time against a project.
For Lotus Notes Client v8.0 and above, you can use the Time Tracker Widget to make this process even easier!
Zephyr
My Configuration-based Rich Text Mail Merge and Emailing Utility, Zephyr allows you to create rich, data-driven emails to support automated workflow - all via Microsoft Word Mail Merge-like architecture. Dear <firstname> allows you to personalize each email message not only to the individual recipient, but also to the individual application workflow event!
xCopy
xCopy is a simple configurable xCopy client for the Lotus Notes client. By creating and defining xCopy Profiles, you can batch process your file backup or remote upload jobs. With the addition of the xCopy sidebar widget, you can easily kick-off these jobs, and modify both the xCopy Profiles and xCopy itself.
Community & Resources
Lotus Technical Information & Education Community
The Lotus Technical Information & Education community is comprised of IBM, business partner, and customer subject matter experts who use product wikis, published articles, white papers, community blogs and the latest in social media to build and share high quality technical content.
OpenNTF.org - Open Source Community for Lotus Notes Domino
OpenNTF is devoted to enabling groups of individuals all over the world to collaborate on IBM Lotus Notes/Domino applications and release them as open source.
developerWorks Lotus : Wikis
Share your deployment experiences and best practices in our wikis and help IBM to create scenarios for successful deployments. Contribute to the community by collaborating on shared content and leverage the shared knowledge from that community.
More Nested IBM XPage Panels + viewPanel Controls in viewColumns (with an online demo: XBook)
10/28/2011 02:08:00 PM by Chris Toohey
Sometimes I like to play with a given technique to see how far I can take it.
Thus said, I thought I'd mess around with the techniques discussed in Inline Buttons for XPage View Controls, Nested XPage viewPanels in viewColumns (with meme-themed demo), and IBM XPages + Google Maps Mashup (or Surfacing traditional Views as NotesViewEntryCollections with a viewPanel Control), and see just how far I could take them.
I'll admit, I didn't go that far - mostly because it got to the point where I confirmed my theories: it works, and works really well.
My playground was a simple NotesDatabase that contains no Form Design Elements, only 2 View Design Elements (used for NotesViewEntryCollection sorting more than anything...), and uses only core XPage controls.
(I originally used the OpenNTF.org Extension Library dialog Control for the Status Update form -- in fact, you can see it in the banner image in this article -- but decided just now [midway through writing this article] that it's a better showcase using only the core XPage controls.)
Since each panel Control can have it's own unqiue data source, and a viewColumn control can house literally anything -- including, of course, a panel Control -- I thought that I'd create a Facebook-esque demo application that showed off the technique.
Here's a quick visual overview of what the architecture of the "XBooks" demo:
XBooks Demo XPage Application architecture wireframe
I have a simple inline form panel Control ("newupdate") that allows me to submit a new update, then a viewPanel Control ("updates") that displays all of my previous updates - whose data source is set to pull the "documents" from the documents_bytype_descending View Design Element.
The first (and only) viewColumn of the "updates" viewPanel contains an "update" pane Control.
The "update" panel Control contains the "comments" viewPanel Control -- which loads all of the response NotesDocuments based on the @Text($Ref) category of the documents_bytype_ascending View Design Element.
The "update" panel Control also contains the "newcomment" pane Control, which allows you to submit a new comment to an update (and uses the UniversalID of the viewPanel entry NotesDocument to populate the "$Ref" NotesItem of the new comment).
It's all pretty simple, really.
Here's the Home.xsp XPage markup, where all of this exists:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view
xmlns:xp="http://www.ibm.com/xsp/core"
pageTitle="#{javascript:@DbTitle()}">
<xp:this.resources>
<xp:styleSheet
href="/screen.css" />
</xp:this.resources>
<xp:this.beforePageLoad><![CDATA[#{javascript:sessionScope.remote_addr = facesContext.getExternalContext().getRequest().getRemoteAddr();}]]></xp:this.beforePageLoad>
<xp:div
styleClass="container_main"
id="container_main">
<xp:div
styleClass="container_header">
<xp:div
styleClass="blueBar">
<xp:div
styleClass="container_content">
<ul
class="navigation">
<li>
<xp:link
escape="true"
text="#{javascript:@DbTitle()}"
id="link1"
styleClass="h1"
value="/Home.xsp">
</xp:link>
<xp:link
escape="true"
text=""
id="link2"
styleClass="icon icon1"
value="/Home.xsp">
</xp:link>
<xp:link
escape="true"
text=""
id="link3"
styleClass="icon icon2"
value="/Home.xsp">
</xp:link>
<xp:link
escape="true"
text=""
id="link4"
styleClass="icon icon3"
value="/Home.xsp">
</xp:link>
</li>
</ul>
<input
type="text"
id="query"
name="query"
placeholder="Search" />
<button
id="runsearch"
type="submit" />
</xp:div>
</xp:div>
</xp:div>
<xp:div
styleClass="container_body">
<xp:div
styleClass="container_content">
<xp:div
styleClass="col0 column">
<xp:image
url="/batman_facebook.jpg"
id="image1"
style="width:175px">
</xp:image>
<xp:div
styleClass="buildinfo">
<xp:paragraph
id="paragraph1">
This application is designed to be as simple
as possible while
illustrating several
extremely powerful techniques that any
XPages
Developer can employ.
</xp:paragraph>
<xp:paragraph
id="paragraph2">
Here's an overview of the app:
</xp:paragraph>
<h3>Design Elements</h3>
<xp:paragraph>
<xp:table>
<xp:tr>
<xp:td
styleClass="col0">
Views
</xp:td>
<xp:td
styleClass="col1">2</xp:td>
</xp:tr>
<xp:tr>
<xp:td
styleClass="col0">
XPages
</xp:td>
<xp:td
styleClass="col1">1</xp:td>
</xp:tr>
<xp:tr>
<xp:td
styleClass="col0">
Image Resources
</xp:td>
<xp:td
styleClass="col1">2</xp:td>
</xp:tr>
<xp:tr>
<xp:td
styleClass="col0">
Stylesheets
</xp:td>
<xp:td
styleClass="col1">1</xp:td>
</xp:tr>
<xp:tr>
<xp:td
styleClass="col0">
Forms
</xp:td>
<xp:td
styleClass="col1">0</xp:td>
</xp:tr>
</xp:table>
</xp:paragraph>
<h3>NotesData Information</h3>
<xp:paragraph>
<xp:table>
<xp:tr>
<xp:td
styleClass="col0">
"document" Documents
</xp:td>
<xp:td
styleClass="col1">
<xp:text>
<xp:this.value><![CDATA[#{javascript:var lu = @DbLookup(@DbName(), 'documents_bytype_ascending', 'document', 2);
return @Elements(lu).toString()}]]></xp:this.value>
</xp:text>
</xp:td>
</xp:tr>
<xp:tr>
<xp:td
styleClass="col0">
"comment" Documents
</xp:td>
<xp:td
styleClass="col1">
<xp:text>
<xp:this.value><![CDATA[#{javascript:var lu = @DbLookup(@DbName(), 'documents_bytype_ascending', 'comment', 2);
return @Elements(lu).toString()}]]></xp:this.value>
</xp:text>
</xp:td>
</xp:tr>
</xp:table>
</xp:paragraph>
<h3>Using this app</h3>
<xp:paragraph>
This should be fairly obvious to anyone who
uses
Facebook. Simply click "Update Status"
to add a new status update.
From each Status
Update, you'll be able to add a comment via
the
inline comment form.
</xp:paragraph>
<h3>Under the hood...</h3>
<xp:paragraph>
If you want to see exactly how the NotesData
is
stored within the application, simply
open the views:
</xp:paragraph>
<xp:paragraph>
<xp:link
escape="true"
text="documents_bytype_ascending"
id="link5"
value="http://guru.gbs.com/demo/XBook.nsf/documents_bytype_ascending">
</xp:link>
<xp:br />
<xp:br />
View showing all documents, second column
sorted in ascending
order.
</xp:paragraph>
<xp:paragraph>
<xp:link
escape="true"
text="documents_bytype_descending"
id="link6"
value="http://guru.gbs.com/demo/XBook.nsf/documents_bytype_descending">
</xp:link>
<xp:br />
<xp:br />
View showing all documents, second column
sorted in descending
order.
</xp:paragraph>
</xp:div>
</xp:div>
<xp:div
styleClass="col1 column">
<xp:panel
styleClass="newupdate">
<xp:this.data>
<xp:dominoDocument
var="newupdate"
formName="document"
ignoreRequestParams="true">
</xp:dominoDocument>
</xp:this.data>
<h3>Update Status</h3>
<xp:inputText
value="#{newupdate.REMOTE_ADDR}"
styleClass="hide"
defaultValue="#{javascript:sessionScope.remote_addr}" />
<xp:inputTextarea
styleClass="body"
value="#{newupdate.body}" />
<xp:button
value="Post"
id="button2">
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="complete"
immediate="false"
save="false">
<xp:this.action><![CDATA[#{javascript:newupdate.save();
context.redirectToPage("/Home.xsp")}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
</xp:panel>
<xp:div
styleClass="updates">
<xp:viewPanel
rows="5"
id="viewPanel_updates"
var="entry_update"
showColumnHeader="false">
<xp:this.facets>
<xp:pager
partialRefresh="true"
layout="Previous Group Next"
xp:key="headerPager"
id="pager1">
</xp:pager>
<xp:viewTitle
xp:key="viewTitle"
id="viewTitle1"
value="Status Updates">
</xp:viewTitle>
</xp:this.facets>
<xp:this.data>
<xp:dominoView
var="updates"
viewName="documents_bytype_descending"
keys="document"
keysExactMatch="true"
ignoreRequestParams="true">
</xp:dominoView>
</xp:this.data>
<xp:viewColumn
id="viewColumn_contents"
displayAs="hidden">
<xp:this.value><![CDATA[#{javascript:return ""}]]></xp:this.value>
<xp:panel>
<xp:this.data>
<xp:dominoDocument
var="thisupdate"
action="openDocument"
documentId="#{javascript:entry_update.getDocument().getUniversalID();}"
ignoreRequestParams="true">
</xp:dominoDocument>
</xp:this.data>
<xp:div
styleClass="update">
<xp:div
styleClass="update_body">
<xp:text
value="#{thisupdate.body}" />
</xp:div>
<xp:div
styleClass="update_timestamp">
<xp:text
value="#{javascript:thisupdate.getDocument().getCreated().toString()}" />
</xp:div>
</xp:div>
<xp:div
styleClass="comments">
<xp:viewPanel
rows="100"
id="viewPanel_comments"
showColumnHeader="false"
viewStyleClass="viewPanel_comments"
var="entry_comment">
<xp:this.data>
<xp:dominoView
var="comments"
viewName="documents_bytype_ascending"
keys="#{javascript:thisupdate.getDocument().getUniversalID()}"
keysExactMatch="true">
</xp:dominoView>
</xp:this.data>
<xp:viewColumn
id="viewColumn_comments"
displayAs="hidden">
<xp:this.value><![CDATA[#{javascript:return ""}]]></xp:this.value>
<xp:panel>
<xp:this.data>
<xp:dominoDocument
var="thiscomment"
action="openDocument"
documentId="#{javascript:entry_comment.getDocument().getUniversalID();}"
ignoreRequestParams="true">
</xp:dominoDocument>
</xp:this.data>
<xp:div
styleClass="comment">
<xp:div
styleClass="comment_body">
<xp:text
value="#{thiscomment.body}" />
</xp:div>
<xp:div
styleClass="comment_timestamp">
<xp:text>
<xp:this.value><![CDATA[#{javascript:var dt = thiscomment.getDocument().getCreated();
dt.getDateOnly().toString() + ' ' + dt.getTimeOnly().toString()}]]></xp:this.value>
</xp:text>
</xp:div>
</xp:div>
</xp:panel>
<xp:viewColumnHeader
value=""
id="viewColumnHeader1">
</xp:viewColumnHeader>
</xp:viewColumn>
</xp:viewPanel>
</xp:div>
<xp:div
styleClass="newcomment">
<xp:panel>
<xp:this.data>
<xp:dominoDocument
var="newcomment"
formName="comment"
parentId="#{javascript:thisupdate.getDocument().getUniversalID()}"
ignoreRequestParams="true">
</xp:dominoDocument>
</xp:this.data>
<fieldset>
<legend>
New Comment
</legend>
<xp:inputText
value="#{newcomment.REMOTE_ADDR}"
styleClass="hide"
defaultValue="#{javascript:sessionScope.remote_addr}" />
<xp:inputText
styleClass="body"
value="#{newcomment.body}" />
<xp:button
value="Post"
id="button3">
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="complete"
immediate="false"
save="false">
<xp:this.action><![CDATA[#{javascript:newcomment.save();
context.redirectToPage("/Home.xsp")}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
</fieldset>
</xp:panel>
</xp:div>
</xp:panel>
<xp:viewColumnHeader
value="" />
</xp:viewColumn>
</xp:viewPanel>
</xp:div>
</xp:div>
<xp:div
styleClass="col2 column">
<xp:viewPanel
rows="10"
id="viewPanel_latestcomments"
var="entry_comment"
showColumnHeader="false"
viewStyleClass="viewPanel_latestcomments"
viewStyle="width:100px">
<xp:this.facets>
<xp:viewTitle
xp:key="viewTitle"
id="viewTitle3"
value="Latest Comments">
</xp:viewTitle>
</xp:this.facets>
<xp:this.data>
<xp:dominoView
var="updates"
viewName="documents_bytype_descending"
keys="comment"
keysExactMatch="true">
</xp:dominoView>
</xp:this.data>
<xp:viewColumn
id="viewColumn_latestcomments"
displayAs="hidden">
<xp:this.value><![CDATA[#{javascript:return ""}]]></xp:this.value>
<xp:panel>
<xp:this.data>
<xp:dominoDocument
var="thiscomment"
action="openDocument"
documentId="#{javascript:entry_comment.getDocument().getUniversalID();}">
</xp:dominoDocument>
</xp:this.data>
<xp:div
styleClass="comment">
<xp:div
styleClass="comment_body">
<xp:text
value="#{thiscomment.body}" />
</xp:div>
<xp:div
styleClass="comment_timestamp">
<xp:text>
<xp:this.value><![CDATA[#{javascript:var dt = thiscomment.getDocument().getCreated();
dt.getDateOnly().toString() + ' ' + dt.getTimeOnly().toString()}]]></xp:this.value>
</xp:text>
</xp:div>
</xp:div>
</xp:panel>
<xp:viewColumnHeader
value="" />
</xp:viewColumn>
</xp:viewPanel>
</xp:div>
</xp:div>
</xp:div>
<xp:div
styleClass="container_footer">
<xp:div
styleClass="container_content">
</xp:div>
</xp:div>
</xp:div>
</xp:view>
View fullscreen: XBook Home.xsp XPage Markup
The rest is just a stylesheet:
body {
margin: 0px;
padding: 0px;
}
div.container_main {
text-align: center;
margin: 0px;
padding: 0px;
}
div.container_content {
text-align: left;
width: 975px;
margin: 0px auto 0px auto;
padding: 0px;
}
div.blueBar {
background-color: #3B5998;
padding: 4px 0px 4px 0px;
}
div.blueBar ul.navigation {
display: inline;
margin: 0px;
padding: 0px;
list-style: none;
}
div.blueBar ul.navigation li {
margin: 0px;
padding: 0px;
list-style: none;
display: inline;
}
div.blueBar ul.navigation li a {
display: inline-block;
vertical-align: middle;
color: #fff;
font-weight: normal;
padding: 0px;
margin: 0px;
}
div.blueBar ul.navigation li .h1 {
font-size: 11pt;
font-weight: bold;
color: #fff;
padding: 0px;
margin: 0px 8px 0px 0px;
}
div.blueBar ul.navigation li a.icon {
display: inline-block;
width: 24px;
height: 24px;
margin-left: 2px;
background-image: url(http://static.ak.fbcdn.net/rsrc.php/v1/yv/r/mTnxuar3oIS.png);
background-repeat: no-repeat;
}
div.blueBar ul.navigation li a.icon1 {
background-position: -31px -6px;
}
div.blueBar ul.navigation li a.icon2 {
background-position: -56px -76px;
}
div.blueBar ul.navigation li a.icon3 {
background-position: -25px -251px;
}
div.blueBar input#query {
width: 325px;
border: 1px solid #eee;
margin-left: 8px;
font-size: 8pt;
}
div.blueBar button#runsearch {
background-image: url(http://static.ak.fbcdn.net/rsrc.php/v1/yv/r/mTnxuar3oIS.png);
background-repeat: no-repeat;
background-position: -81px 0;
background-color: white;
border: 0;
cursor: pointer;
display: inline-block;
height: 18px;
padding: 0;
width: 22px;
position: relative;
}
div.container_body {
margin-top: 25px;
}
div.container_body div.column {
display: inline-block;
vertical-align: top;
}
div.container_body div.col0 {
width: 175px;
}
div.container_body div.col0 p {
margin: 6px 0px 6px 0px;
}
div.container_body div.col0 table {
width: 100%;
}
div.container_body div.col0 table tr td.col0 {
font-weight: bold;
}
div.container_body div.col1 {
width: 650px;
padding: 0px 5px 0px 5px;
}
div.container_body div.col2 {
width: 100px;
}
div.comment {
margin-bottom: 8px;
}
div.comment_body span.xspTextComputedField {
font-size: 10pt;
}
.newupdate {
text-align: right;
margin-bottom: 15px;
}
.newupdate h3 {
text-align: left;
padding: 4px 0px 4px 10px;
background-color: #F3F3F3;
margin: 0px 0px 4px 0px;
}
.newupdate .body {
display: block;
height: 50px;
width: 640px;
}
.newcomment .body {
width: 550px;
display: inline;
}
.newupdate .xspButtonCommand, .newcomment .xspButtonCommand, .xspButtonSubmit {
border: 1px solid #666;
background-color: #5B74A8;
color: #fff;
font-size: 8pt;
font-weight: bold;
padding: 2px 8px 2px 8px !important;
height: auto;
}
.xspDataTable, .xspDataTableViewPanel, .viewPanel_comments {
width: 100%;
}
.xspDataTable td {
border: none !important;
}
div.update_body {
font-size: 12pt;
color: #333;
}
div.update_timestamp {
font-size: 8pt;
color: #999;
}
.viewPanel_latestcomments div.comment_body {
font-size: 8pt;
color: #333;
}
.viewPanel_latestcomments div.comment_timestamp {
font-size: 7pt;
color: #666;
padding: 0px 0px 0px 0px;
margin: 4px 0px 4px 0px;
}
.viewPanel_latestcomments .xspTextViewColumn {
display: none;
}
.xspDataTableViewPanelHeaderStart,
.xspDataTableViewPanelHeaderMiddle,
.xspDataTableViewPanelHeaderEnd,
.xspDataTableViewPanelFooterStart,
.xspDataTableViewPanelFooterMiddle,
.xspDataTableViewPanelFooterEnd {
background-color: transparent;
border: none;
}
.xspDataTableViewPanel .xspDataTableViewPanelHeaderStart,
.xspDataTableViewPanel .xspDataTableViewPanelHeaderMiddle,
.xspDataTableViewPanel .xspDataTableViewPanelHeaderEnd {
background-color: #F3F3F3;
}
.viewPanel_comments .xspDataTableViewPanelHeaderStart,
.viewPanel_comments .xspDataTableViewPanelHeaderMiddle,
.viewPanel_comments .xspDataTableViewPanelHeaderEnd,
.viewPanel_comments .xspDataTableViewPanelFooterStart,
.viewPanel_comments .xspDataTableViewPanelFooterMiddle,
.viewPanel_comments .xspDataTableViewPanelFooterEnd {
background-color: transparent;
border: none;
}
.button_update {
background-color: transparent;
border: none;
text-align: center;
margin: 10px 0px 0px 25px;
font-weight: bold;
font-size: 8pt;
}
.button_update:hover {
text-decoration: underline;
cursor: pointer;
}
fieldset {
border: none;
}
legend {
text-align: left;
}
.viewPanel_latestcomments {
width: 100px;
}
.hide {
display: none !important;
}
View fullscreen: XBook screen.css CSS Markup
Online Demo
So, wanna take this demo for a spin? Check out the XBook online demo at http://guru.gbs.com/demo/XBook.nsf!
Conclusion
Two words really: Have. Fun. Play around with this stuff and see what you can make it do. You're smart. You're creative. You just have to forget the pre-conceived circular logic of "this is how you do this because this is how you do this..." because no one innovates by following how everyone else does it.
OK, enough of a pep talk. Go code something!



great stuff! time to put some controls on openntf?
Well, these are all custom controls... but there are a few controls I could submit. Perhaps when I get a free moment or two ;-)