Jake Of All Trades: CMTI’s Jake Ochs

I’ve been tinkering a little under the hood of this blog/website. As some of you may know, this site is based on the awesome Drupal CMS. I was browsing some blogs and came across an interesting ratings and recommendation service called Outbrain. A little digging and sure enough, There’s a Drupal module for that! I installed the module and played with the widget settings from my Outbrain account. Outbrain wins extra love for supporting openid, which is my identity verification method of choice. It turns out that the recommendation engine’s recommendations for my blog were either irrelevant (in my humble opinion) politically charged (I have no idea how that happens as I remain a blogging centrist politically) or just links to my other content, which is easy enough to find without the recommendation engine’s obtrusiveness. So I turned it off and went with the ratings engine alone for the time being. Maybe when my blog content fills out more and/or the recommendation engine becomes a little more sophisticated I’ll give it another whirl. Meanwhile, please rate my individual blog posts by clicking on the appropriate star.

Another change that’s been a long time coming is to abstract my blog feed to Feedburner. It’s good practice to do this in case your native blog URL changes for any reason. It’s the blogger’s version of a universal phone number (Unified Feedopathy?) Say you want to change your web address or even just want to switch blogging platforms, having your subscriber base fetching your feed from Feedburner or Friendfeed means they don’t have to suffer a link breakage. You just have to repoint your Feedburner feed to the new location. To make a long story short, there’s a module for that too! I just installed the Feedburner module, which integrates nicely with Feedburner’s feed flare and your own site’s content types. Let me know if you are experiencing problems with my site feed.

by jake

Just a quick note on yesterday’s Chrome OS reveal by Google. Unless I’m missing something, there’s nothing new under the covers here. It seems as if the whole point of the “OS” is to provide a stripped down device-like environment for hosting a web browser. Essentially, all the “apps” are Google’s Cloud-based offerings. There must be a vertical play here, otherwise why would Google go through all of the trouble of creating a complete stack for what we can essentially get today? What I’m not seeing is the strategic vision of offering Chrome OS for devices when variations on Android will play in the same space. Remember folks, Android OS is not necessarily just for phones and, in fact, has begun turning up in non-phone applications as well. I do see a potential competitive space opening up between Chrome OS, Ubuntu’s Netbook remix and Moblin for tablet-ish devices. A major differentiator being Google’s support of Gears for offline access to data and Ubuntu’s increasing use of CouchDB as a data repository –both on the device and in it’s own branded cloud offerings. Being a fan of Damien Katz and IBM (CouchDB’s creator and corporate sponsor, respectively) through my Lotus connection (Damien redesigned the Notes formula language engine back in the R6 timeframe) as well as my fondness for the Ubuntu distro, you could say I’m rooting for Ubuntu –if not to outright win, then not to fail. What’s interesting in comparing Gears to CouchDB is that it’s a repeat of the age-old question of whether general-purpose data fits best into a relational or a B-tree/document/hierarchical mold. In a nutshell, there’s not much to see until the whole NetOS/Netbook picture firms up and hardware and software vendors start to bed down to produce tangible product, probably 12-18 months out. Like I said, move along, folks, move along.

by jake

So it occured to me that every few years we reinvent the wheel in enterprise filesharing. Workgroups used to “collaborate” by dropping files into a shared network folder. Then there came Exchange Public Folders and Notes document libraries. Then there were complicated document management systems. Now there’s Quickr and Sharepoint. The reality of the enterprise deployments I’ve seen of these applications is that most spaces are used primarily as glorified fileshares.* Essentially, aside from the versioning and locking (check in-check out) ability of these tools, they are being used to dump files, with the added bonus of being nicely integrated in to the productivity apps (Office, Outlook, Notes) in vogue. Which makes me wonder: Why haven’t versioning filesystems become more popular for this sort of thing? I can easily envision an application that uses such a filesystem as the data store for an enterprise. Imagine for a moment what such an animal would look like: A filesystem that accounts for granular file permissions, versioning and file locking and is LDAP/enterprise directory-aware. You’d have the basis for perhaps 80% of the common usage of the aforementioned applications right there. Making good use of this versioning filesystem would basically entail a protocol implementing plugin. Such a system would remove a common dependency between messaging and collaboration that tends to cause frustrating vendor lock-in. Filesystems being what they are, HA and DR would probably also amount to a much simpler endeavor. I know of Wayback FS-which operates on top of traditional filesystems and proves my point that HA and DR should be a straightforward task- but I don’t know of any commercial tools that implement a client for Wayback. Why hasn’t this meme gained more traction?

*I’m not detracting from any of these products’ ability to be so much more than glorified fileshares, just how they seem to be mostly used.

by jake

I had an incredible time at the Tri-State Lotus User Group meetup today. It was an all-day event with a keynote by Lotus GM Bob Picciano and a whole mini-Lotusphere roster of speakers. I was also was fortunate to meet up with old friends and colleagues Steven Pivnik, Vitaliy Dubinsky and catch up with Bob Balaban, whom I haven’t spoken to face to face in ten years or so. I was also fortunate to meet personally some real Yellowverse heroes such as Bruce & Gayle Elgort, David Leedy, John Head and Rob Novak. Of course major props to organizers Kathleen McGivney, Mitch Cohen and Jim Casale.

My biggest takeaways from a technical perspective are twofold: One is that the next version of Symphony will ship on the same Expeditor platform and JRE as the 8.5.1 version of Notes and that I just verified that it does indeed fix the crypto issues I’ve blogged about in the past. Hooray for standards compliance! (I’d attribute the source of the information, but I’m not sure that’s a good idea…) The other is how underrepresented true hard-core developers are at Tri-LUG meetings. You know who they are: The ones who breathe metadata, XML formats, JSON, CSS, languages, interop and –of course- understand workflow as if it were mother’s milk. While I’m not knocking those in attendance, I KNOW the rocket scientists are out there, I just never get to see them. (Presenters excluded.) My theory is that there’s a significant population of rock star Notes developers that just don’t feel they can learn anything from these events. Maybe the advertised session tracks aren’t technical enough; maybe they feel they have an inside ear at IBM so they have all the answers at their disposal or maybe they’re just hermetic, arrogant shut ins. I don’t know. I just want them to come out and PLAY. Perhaps the best way to lure these guys out of their holes is to offer them all presentation slots? Teachable moments are a powerful incentive to a developer. It’s a Jedi thing. (I hate myself for the Star Wars reference, but the analogy is apt.)

As always, feedback is appreciated.

by jake

This is part 5 of a series on drag and drop with Xpages and Dojo.

Our last installment showed how to drop two data containers on a page that can accept Dojo drag and drop elements between them and set up the data structure to facilitate the processing of these events –namely associating the items with Notes document UNIDs. We also left off with a teaser question that we’ll get to later in this installment.

Collecting our drag & drop data

Now that we’ve instrumented our table rows so that we can semantically determine their relationship to backend Notes documents, we need to collect the rows we’ve acted upon in a UI drag & drop event and the target container that they have been dropped on. The event we’ll be listening to is the target container’s (the table we drop tables rows in to) “onDndDrop” event, an event that is published as part of the Dojo.dnd container class. We subscribe the target container by appending a dojo.connect call (line 5, see code block below) to the initialization function for the table from the previous lesson, passing the connect function the parameters of the subscribing object (the container) the event to listen to and the function to be called on the event:

  1. function initDndTable(tid) {
  2. setUNIDS(tid,true,true,2);
  3. var tVar = new dojo.dnd.Source(tid);
  4. dojo.parser.parse();
  5. dojo.connect(tVar,"onDndDrop", movedata);
  6. }

We then create a function “movedata” that collects the information we need when the event is triggered. Note that the event gets passed the source Dojo object of DnD operation, the nodes being moved and a boolean to indicate whether this is a copy operation or a move as parameters:

  1. function movedata(source, nodes, copy){
  2. if(dojo.dnd.manager().target !== this){ return; }
  3. targetName = dojo.dnd.manager().target.node.id;
  4. tNameArr = targetName.split(":");
  5. targetName = tNameArr[tNameArr.length - 1];
  6. var docsArr = new Array();
  7. for (i=0;i<nodes.length;i++){
  8. docsArr.push(nodes[i].id);
  9. }
  10. var obj = {};
  11. obj.table = targetName;
  12. obj.docs = docsArr;
  13. var tableDataJSON = dojo.toJson(obj);
  14. var stashField = document.getElementById(hiddenFldId);
  15. stashField.value = tableDataJSON;
  16. }//movedata

We’ll finish up this function in a moment, but for now take note that line 2 is to make sure that we’re only executing the code when the container that has subscribed to the event is the target of the DnD operation as the “onDndDrop” event is fired no mater where the nodes are dropped (a peculiarity of the Dojo event topic system.) On Line 3 we get the name of the receiving container from the dojo.dnd.Manager object, as this information is not passed directly to the event. The receiver is useful for us to determine the eventual operation to perform on the backend with the collection of document UNIDs we’re gathering. Note that we’re only interested in the least significant component of targetName, which corresponds to the IDs of our XPages’ data tables. Lastly, lines 10-14 take the collected data and place it in a Javascript object that we serialize to JSON to tuck neatly into our hidden field.

Performing the Partial Submit

Next we need to perform the partial submit to update the Domino backend with our move. Like the R3 days of yore, the simplest way to execute server side code on a page refresh is to embed it in a computed field, which we’ll do. Let’s drop a panel onto our XPage and drop a computed field into the panel. Then move our existing hidden field into the panel as well, taking care to place it after the computed field. Like the old days, order of placement matters for order of execution. (See screenshot.)

The goal here is to trigger a partial refresh of the panel that contains the computed field and hidden field components on the drop event. Placing the computed field with the ServerSide Javascript (SSJS) logic for updating the Domino backend before the hidden field ensures that the hidden field will be recomputed for browser display properly. The logic for the move operation in SSJS gets put into the computed field’s value:

  1. try {
  2. dragdata = getComponent("inputHidden1").getValue();
  3. if (dragdata == "") return;
  4. ddObject = fromJson(dragdata);
  5. archiveAction = (ddObject.table == "dataTable2") ? "Y" : "N";
  6. for (i=0;i<ddObject.docs.length;i++){
  7. doc = database.getDocumentByUNID(ddObject.docs[i]);
  8. doc.replaceItemValue("Archive",archiveAction);
  9. doc.save(true,true);
  10. }
  11. getComponent("inputHidden1").setValue("")
  12. }
  13. catch (e) {
  14. print(e)
  15. }

Lines 2 & 3 (obviously) halt execution of the script if there’s nothing to do, especially on initial page load. Line 4 reconstitutes the Javascript object from the JSON representation we stashed in the hidden field in our earlier client-side script. In our example the archive action is determined by the target table name. If we dropped documents onto the archive table, we’ll set their “Archive” field value to “Y.” We then cycle through the UNIDs, grab the document objects from the database and perform the updates’ Lastly, we reset the hidden field back to empty. Note that if we didn’t place the computed field before the hidden field, the hidden field would not be updated and the client value after the refresh would still contain the JSON data we stashed there.

Now the only thing we need to do to complete the demo is to perform the partial submit on the drag and drop event. We can programmatically perform the submit from the client using the XSP.PartialRefreshPost method (hat tip: Jeremy Hodge) by appending the code to our movedata function:

  1. XSP.partialRefreshPost(computePanelId, {
  2. onStart: null,
  3. onComplete: null,
  4. onError: null
  5. })

The variable computePanelId refers to the panel that contains our computed field and hidden (stash) field. We’ll add this variable to the script block component’s code in our XPage as follows:

  1. var computePanelId = "#{id:computePanel1}"

Saving and running the completed XPage will result in a drag and drop operation that will update the backend in real-time on the drop event in the client browser!

The UI Gotcha (paginators)

If you’ve been following along, you’ll know that there’s a UI “gotcha” that I’ve alluded to at the end of the last lesson. Our drag and drop functionality is predicated on a Javascript initialization that creates the dojo.dnd containers and preps them for DnD operations. If we use a partially refreshing data component such as a view or data table, however, the table rows only partially refresh, and the dojo containers are not reinitialized with the new data. Thus using your XPage components’ pagers to page through a data container breaks the functionality. In theory, Dojo provides an “onComplete” event to fire off a script after a partial occurs that should allow us to trigger our initializations and reset our DnD functionality. As Domino is computing the partial refresh events for us, however, how to do this is not obvious. Once again, Jeremy Hodge to the rescue with a method to overrode the partial refresh event listener on the page to add a custom onComplete handler that calls our initDndTable function with the table ID we need to refresh taken from the event’s refresh ID parameter:

  1. XSP._inheritedPartialRefresh = XSP._partialRefresh;
  2. XSP._partialRefresh = function(method,form,refreshId,options){
  3. dojo.publish("/XSP/partialRefresh", new Array(method,form,refreshId,options));
  4. this._inheritedPartialRefresh(method,form,refreshId,options);
  5. }
  6. dojo.subscribe("/XSP/partialRefresh", null, function(method,form,refreshId,options) {
  7. if ((refreshId == mainTableId) || (refreshId == archiveTableId)){
  8. if (options.onComplete)
  9. options._inheritedOnComplete = options.onComplete;
  10. options.onComplete = function() {
  11. initDndTable(refreshId);
  12. if (this.inheritedOnComplete)
  13. this.inheritedOnComplete();
  14. }
  15. }
  16. });

The script overrides the XSP._partialrefresh method, taking care to call the inherited functionality when done with out custom code. Line 7 adds the custom onComplete functionality specifically to our two data tables and consists of triggering the initDndTable function with the table id to refresh. Remember in our last lesson when we alluded to the value of breaking out the table initialization into a separate function to be fired for each table? This is the reason. Drop this override code into your script library to complete the demo and generate a fully functioning drag and drop environment with paged data tables. Read Jeremy’s whole post on the XPages Blog for more information on how this works.

The complete source for this demo can be downloaded here.

Learning More

We’ev only touched on the capabilities of Dojo’s DND framework in this series. For example, you may want to learn how to enable or disable the copy feature of your DnD elements or add custom event handlers and triggers. Sitepen’s Dojo Drag and Drop, Part 1: dojo.dnd is an excellent starting point for learning DnD and contains links to other DnD related articles on the sitepen blog. The Dojocampus reference guide on Dojo.dnd is also useful, as is the API reference.

Rate me as an author on Scribnia!

SHAMELESS PLUG: Like what you see? Hire Me! Contact [email protected] or use the contact page to learn more about engaging Jake Ochs for your development needs.

by jake

This is part 4 of a series on drag and drop with Xpages and Dojo.

Apologies for the long delay between part 3 of this series and part 4. In my defense, I was holding out for release 8.5.1 to see if any new functionality was brought to the table that eased Dojo DND development in XPages. I’d also like to acknowledge Paul Withers’ and Declan Lynch’s help in bouncing ideas around and answering some of my more boneheaded XPages questions. Lastly, Jeremy Hodge’s sage advice on the Dojo and XSP script library helped resolve one last thorny issue that was preventing the publication of this tutorial installment.

Dragging between containers

We ended our last session by properly instrumenting our XPage data table and view controls to serve as Dojo drag and drop containers. After throwing in some Dojo setup code, the containers then receive drag and drop behavioral functionality. Now we’d like to drag and drop between multiple containers. A typical use case would be to drag documents from one view or folder to another. In our example, we’ll be dragging documents from a main view into one that represents an archive. To accomplish this, we embed two containers, in our case data tables attached to views:

  1. Drag two data table components onto the page
  2. Associate the data tables with view data sources.
  3. Right-click inside the data tables to insert/append a column
  4. Drag a computed field into the middle row of the column and bind it to a value in your data source.
  5. Add the Dojo.dnd tags to the view containers by drilling down to the All Properties tab of the data tables’ properties view, setting the rowClasses property to “dojoDndItem” and the styleClass property to “Container.”
  6. Add the Dojo.dnd prerequisite resources:
    1. DojoModule “dojo.dnd.Source”
    2. DojoModule “dojo.parser”
    3. DojoModule “dojo.dnd.Move”
    4. Dojo’s native dnd.css, called using the technique outlined here and in part 2 of this tutorial. Note that if you’re using release 8.5.1, IBM has updated the Dojo library and the reference should be to dojo-1.3.2 instead of dojo-1.1.1.
    5. dnd.css stylesheet (our custom stylesheet from the previous exercises, used to add interactive cues to our drag and drop operation)

So far, we’ve done nothing different than in our previous examples. Now that we’ve got two DND containers to work with though, we’ll be dealing with more complex Javascript so we’ll move most of our code to a script library for code sanity. One neat trick I’ve picked up is to embed an output script block component (new 8.5.1 feature) on the page and create Javascript variables that contain the generated DOM IDs of components we need to reference in the client. In source mode, the script block should look like this:

  1. <xp:scriptBlock id="scriptBlock1">
  2. <xp:this.value><![CDATA[var mainTableId = "#{id:dataTable1}";
  3. var archiveTableId = "#{id:dataTable2}"]]></xp:this.value>
  4. </xp:scriptBlock>

Domino will render the EL {id:dataTable1} as the true DOM ID of the dataTable1 component, which we can use to build our DND containers after the page completes loading. (Hat tip to Matt White via Paul Withers.)

Let’s create a new Javascript code library and create a new function that will take a DOM ID and create a Dojo.dnd source container object from it:

  1. function initDndTable(tid) {
  2. var tVar = new dojo.dnd.Source(tid)
  3. }//initDndTable

We can then call this function twice from another function:

  1. function initDndTables(){
  2. initDndTable(mainTableId);
  3. initDndTable(archiveTableId);
  4. }//initDndTables

with the calling parameters being the global table id variables we’ve defined in the script block on the XPage previously.

(You may be wondering why I’m overcomplicating this with separate functions. In truth, at this stage, there’s no need to break out the initialization of the Dojo.dnd containers, but the time will soon come when the need to initialize a specific container will arise.)

Lastly, we kick off the whole process by instructing Dojo to initialize and parse the tables in its onLoad event thusly:

  1. XSP.addOnLoad(initDndTables);

Notice the use of the XSP object instead of the Dojo (Dojo.addOnLoad) object. The XSP object is IBM’s override of Dojo, with extensions for providing custom XPages functionality. You can browse the uncompressed XSPClientDojo.js file in your NOTESDATAdominojsdojo-1.3.2ibmxspwidgetlayout directory.

Let’s improve the look of our page a bit by styling the containers. To begin with, put a CSS .container selector in your dnd.css file (or any CSS that you’re referencing) and set it to float: left. You may also have multiple style classes in your StyleClass property, so in addition to having the “container” styleClass for the data tables, add “archive_table” to the styleClass for the second table and set it’s margin-left property to 50px to space it a bit horizontally from the first table. This is what the tail of your referenced CSS file should look like:

  1. .container { float: left; }
  2. .archive_table { margin-left: 50px; }

I also added a thin black border around the data tables. This is what the two table should look like in your browser:

Done! You now have two data tables associated with Notes views that can interchange rows via drag and drop.

TIP: Unhide new controls (hidden input, output script) available in 8.5.1 on the palette by editing the palette from the Domino Designer preferences in the global Notes preferences dialog. (Hat Tip: Declan Lynch)

Associating nodes with Notes documents

The next hurdle to clear is that we need some way of associating the table rows in the DOM with the backend documents in Notes so that we can perform a drag & drop operation and then instruct the server to update the database with the information reflected in the browser. Let’s do this by appending a column to each of our data tables with hidden HTML fields containing the row documents’ Unique IDs. Note that 8.5.1 has a new hidden field control which we will use in the next iinstallment but that does not seem to work with data inside of repeat controls. So, for the time being, we’ll drop a computed field into our column and use Javascript to emit the HTML for the hidden field:

  1. '<input type="hidden" value="' + mainData.getDocument().getUniversalID() + '">'

MainData is the collection name specified in the Data Table’s main property tab. To complete this operation the field’s “escape” property (in the All Properties tab of the field’s properties view) must be set to false so the HTML is passed through unaltered. Additionally, make sure the field’s name or ID is also blanked out and the field’s “disableTheme” property is set to true. Nulling the field’s ID property and disabling the theme prevents Domino from wrapping the field in a span tag -making it that much easier to programmatically reach for later manipulation.

Now that we have data to allow us to associate the view elements with documents, lets take the process one step further and assign the id of each table row, which is our visual representation of a document, the DOM ID of the document’s UNID. We can create a function that will walk a datatable, extract the ID from the column number containing the IDs and assign the ID value to the row’s ID:

  1. function setUNIDS(tableID,hasTitle,hasTH,UNIDRow){
  2. table1 = dojo.byId(tableID);
  3. var childrenIndex = 0;
  4. if (hasTitle) childrenIndex++;
  5. if (hasTH) childrenIndex++;
  6. var tRows = table1.children[childrenIndex].children;
  7. //walk rows
  8. for (var i=0;i<tRows.length;i++){
  9. UNID = tRows[i].children[UNIDRow - 1].children[0].value;
  10. tRows[i].id = UNID;
  11. }
  12. }//setUNIDs

Of note is that the function params include booleans indicating whether the table has a table header row and whether you’ve enabled the table title by filling in the “caption” property of the table. The function will compute the offset accordingly to access the rows containing actual data. Also note that the UNIDRow param is not zero based. In our case, the UNIDs are in row 2 of our view, so that will be the value passed to the function.

if we now add our setUNIDs function to the initDndTable function, we’ll have our table rows’ DOM ID’s set to the documents’ UNIDs for easier manipulation:

  1. function initDndTable(tid) {
  2. setUNIDS(tid,true,true,2);
  3. var tVar = new dojo.dnd.Source(tid)
  4. }//initDndTable

Inspecting the HTML in Firebug will verify that the table row ID’s are now UNIDs:

Get the raw XSP code for our example thus far here and the Javascript library here.

In our next installment we’ll finish up this example by wiring the drop event to a function that collects the data from the event and submits a partial update request to the server to make the Domino application’s data consistent with the browser representation. We’ll finish up with a discussion of a nagging UI issue (can you spot it?) and how to fix it.

Rate me as an author on Scribnia!

SHAMELESS PLUG: Like what you see? Hire Me! Contact [email protected] or use the contact page to learn more about engaging Jake Ochs for your development needs.

by jake

Reading all the press and Yellowverse comments about release 8.5.1 of Notes/Domino is illuminating. As a developer, yes, 8.5.1 is the answer to a great many prayers. In particular, XPages support is being fleshed out nicely, with an upgrade to the native Dojo JavaScript libraries and enhanced documentation. A definitive XSP tag dictionary would be nice, though. I still eagerly await a proper XPages debugger, which, I am happy to report, seems to be in the works. A complete rewrite of the creaky LotusScript editor based upon the modern goodness that is Eclipse is a welcome addition.

You know I’m leading up to something, though. As much as I love Notes, the only question that Notes answers completely and satisfactorily (IMHO) is “If I had to pick one and only one program to run my entire business, which one would it be?” In that case, the answer is overwhelmingly Notes. Nothing else even comes close. Of course, no one asks that question because no sane person has to make that choice! In which case, what is it that Notes is lacking that the competition isn’t? Well, for email the answer seems to be mostly the synergy that Windows shops get with the complete MS and third-party stack that –truth be told- does exist in Notes, but not to the seamless or widespread effect that it does with the Exchange/Outlook combination. For collaboration, MOSS seems to have a unified set of API’s to program to that is bifurcated (trifurcated?) by Notes/Quickr and Sametime, in addition to the massive third party and developer support. Of course, that last point is a chicken and egg problem, but it’s still worth noting. Moneyquote: More evidence that Sharepoint is the new VB: SP 2009 conference sold out at 7000 attendees. Wow. http://bit.ly/KzGuj (@john_lam) Let’s face it, Notes will never be that popular, but what really sucks is when what is commonly considered your biggest competitor is.

I’m not the magic bullet type and I don’t pretend to know what the answers for Lotus or us loyal third-party developers are other than to continue churn out quality product. A thought: Could releasing the XPages engine into the wild as a free standalone server seed mindshare? There are already several excellent free and/or open source JSP servers out there, why not get some folks hooked on our flavor?

Sidebar: Multiple choice question:

Further installments on my Dojo DND tutorial series have been delayed by:

a) circumstances  beyond my control?
b) a desire to see what release 8.5.1 brings to the XPages/Dojo table?
c) a dearth of sufficient choices in persisting a link between a table row and a back end Notes object using native XPages controls?
d) all of the above.

The correct answer is d.

by jake

I’ve been thinking a lot about Pubsubhubbub and the applicability of efficient but unreliable messaging services in the course of application development that affects me. Pubsubhubbuub, if you don’t know, is a Google-authored protocol that piggybacks onto ATOM RSS feeds to provide efficient updates of new content in the publish-subscribe model. It’s efficient because it specifies a mechanism to push updates out to subscribed entities, avoiding lame (Google’s words, not mine) polling for updates that is the de facto standard for RSS consumption these days. It’s unreliable because there is no mechanism in the spec that guarantees delivery of the message. Of course, this is not a failing of the spec as much as it is a narrow definition of how the simple design is supposed to work to solve simple problems. For twitter-like real time updates of content on an aggregate website, Google Reader for example, network bandwidth is saved as Google Reader wouldn’t have to poll all its subscriptions repeatedly and a user would potentially see updates to a subscribed resource in a more timely fashion. There are two issues with this system, as I can see:

  1. All of the intelligence is on the “hub” server that publishers send update notifications to and that pushes out the updates to the subscribers. This is -again- by design, and keeps the other pieces of the puzzle, subscribers and publishers as little changed as possible. However, an issue with the hub means subscribers and publishers are cut off from each other. Is there a specification that tells the subscriber to resume polling in the absence of new data within an expected time period? In essence, there may be a need to build fault-tolerance into the system.
  2. Ideally, I’d like to be able to extend the metaphor down to a desktop client. It would be grand if I could write a client that could subscribe to a resource with a central hub server and then keep a port open and listening for publication notifications. The problem is that such a client would not be a persistent subscriber in the way that a website would be and can be expected to be on and offline at random intervals. Part of the problem can be solved by using the hub.lease_seconds parameter in the spec to specify how long a hub should service a subscriber before receiving a new lease request (like an ip lease.) However this approach is inefficient if a client subscribes to many feeds on the hub and needs to resubscribe to all of them at the end of every lease period. I wish the spec could batch up a subscriber’s feeds into one group for leasing. Of course, one could augment the spec to provide this ability, but I wish it were baked in for standardization purposes. (Also to potentially take advantage of spec-adhering code libraries.)

Dave Winer, visionary technologist, understands the plumbing of the internet like few others and envisions pubsubhubbub playing a part (message brokering) in a decentralized version of twitter. Certainly, there’s hope, but unless a means to accommodate transient participants (issue #2) is addressed, I don’t see Pubsubhubbub gaining traction outside of a few large producers and consumers such as the Bloggers, Yahoos and WordPresses of the world. The smaller pieces of the food chain will have to go it alone or augment the spec for their own use. My biggest take-away from this whole exercise is having the idea of leaving a port open in a client app that just listens for HTTP requests and responds to them in an event-driven manner bubbling up to the forefront of my consciousness.

Now I’m off to finish my tutorial series on XPages and Dojo Drag and Drop…

UPDATE: I am (clearly) not alone.

by jake

Just a quick tip/reminder to anyone out there who may have had to use the LotusScript (nee VisualBasic) Dir() or Dir$() function. The function IS NOT reentrant. If, as is a typical use-case, you want to deep traverse a directory tree by recursively calling a function that examines a directory using the Dir() function, take care not to nest calls within the recursion. For example:

  1. Function traverse(searchMe$)
  2. dirName$ = Dir$(searchMe$, ATTR_DIRECTORY%)
  3. Do While dirName$ <> ""
  4. If Not(dirName$ = ".") And Not(dirName$ = "..") Then
  5. If Getattr(searchMe$ & delim$ & dirName$) = ATTR_DIRECTORY% then traverse(searchMe$ & delim$ & dirName$)
  6. End If
  7. dirName$ = Dir$()
  8. Loop
  9. 'do something
  10. print dirName$
  11. End Function

Would be incorrect as the nested Dir call would advance the pointer for the caller’s Dir(). The correct usage to perform this task would be to build the complete list before making the recursive call, like so:

  1. Function traverse(searchMe$)
  2. dirName$ = Dir$(searchMe$, ATTR_DIRECTORY%)
  3. Dim dirList List As Integer
  4. Do While dirName$ <> ""
  5. If Not(dirName$ = ".") And Not(dirName$ = "..") Then
  6. If Getattr(searchMe$ & delim$ & dirName$) = ATTR_DIRECTORY% Then dirList(dirName$) = 1
  7. End If
  8. dirName$ = Dir$()
  9. Loop
  10. 'now we can loop and recurse through the list
  11. Forall de In dirList
  12. Call traverse(searchMe$ & delim$ & Listtag(de))
  13. End Forall
  14. 'do something
  15. Print searchMe$
  16. End Function

Note to Lotus: While this may be obvious to some, it wouldn’t hurt to document this in the Designer help file. Also a quick shout out to Jeff Dayton for reminding me about the runtime security level setting in the security tab of the Agent Infobox. What can I say? I get old, I forget things.

Also, check out David Leedy’s latest episode (#5) of his entertaining and informative Notes in Nine screencasts. It’s about time the LotusScript editor got some love!

by jake

This is part 3 of a series on drag and drop with XPages and Dojo. Part 1 is here. Part 2 is here.

Rearranging a data table

In our previous lesson, we showed how to programmatically add the elements we need to the DOM to successfully morph an HTML data structure (a list) into a Dojo drag and drop (Dojo.dnd) container. We will build on this concept to successfully embellish a more complex structure -a data table- and culminate with a view, which is in reality a nested table when displayed in the web browser.

Let’s begin by creating a new XPage in our application (see example application.) We’ll call this XPage “Draggable_Table.” We’ll drag a repeat control onto the XPage and set the data source as our “Produce” (alias “vProduce”) view. Let’s drag a table control into the repeat control, give it a dimension of 2×2 and type in our column headers of “name” and “type” respectively into the two cells of the first row of our table. Now we’re going to place computed fields into the two cells in the second row of our table control and bind their values to the rowData data source and the “Name” and “Type”columns of our data source respectively. Your XPage design view should look similar to this:

Going back to our table’s property editor, let’s name our table something descriptive, say “dndtable” (we’ll reference the name in the Javascript Dojo.dnd activation phase of our example in a bit) and give the table a style of container. Recall that Dojo looks for HTML elements with the style of container to manipulate their contents. In the outline editor of our XPage, drill down to and select the second table row element and, using the properties editor, give it a style of dojoDndItem. This row corresponds to the row containing our repeating data that we want to dynamically rearrange using Dojo.dnd. Recall that dojoDndItem is a style class that Dojo.dnd looks for to denote an element that can be dragged and dropped. Lastly, drill down in the outline editor to the first table cell of the second row of our table and give the cell a styleClass of dojoDndHandle. We want the first cell of our repeating data to serve as the “drag handle” for our dragging operations and styling the cell as such will accomplish this.

Before we switch to our XPage’s Source view, we must make sure we have specified all the resources we need to enable the Dojo.dnd behavior. Recall from our previous example that we needed to instruct Dojo to load the modules Dojo.dnd.source and Dojo.parser, the CSS for Dojo.dnd (using our Javascript technique to reference the CSS residing on our Domino server) and some custom CSS in our application that further styles some Dojo.dnd elements for a more descriptive user interface. To review:

TIP: The quickest way to copy a series of resources from one XPage to another is to copy them from the source editor. To copy all the required resources for Dojo.dnd enablement, copy everything between <xp:this.resources> and </xp:this.resources>, inclusive.
  1. From the “basics” section of the “All Properties” tab of our XPage’s property browser, add a resource using the “plus” sign of type xp:dojoModule and name dojo.dnd.Source.
  2. Add another resource of type xp:dojoModule and name it dojo.parser.
  3. Add another resource of type xp:styleSheet and set its href property to the computed value of the dnd.css file located on our Domino server using the technique and script described in Part 2. (Also describe in detail in the article REFERENCING WEB SERVER FILE SYSTEM RESOURCES IN XPAGES.)
  4. Add another resource of type xp:script and set its clientSide property to true. We’ll populate this script in the source editor in the next step.
  5. From the “Style” tab of our XPage’s property browser add the dnd.css stylesheet from our application’s style sheets resources.

Now we need to switch to our XPage’s source editor to rearrange the repeat control’s domain and add the final bits of Javascript that will enable Dojo.dnd for our example. First, recall that the repeat control is repeating everything inside of it, which is not what we want. We only want the second table row of our table definition to repeat, using the sequential values of our view data source. (See Declan Lynch’s post on repeat controls in tables in his excellent XPages tutorial for more on this.) To do this, we’ll put the opening tab of our repeat control <xp:repeat… in between the xp:tr tags of the two rows of our table. We’ll similarly put the closing tag of our repeat control after the closing xp:tr tag of our second row before the xp:table closing tag. Your table’s source should look like this:

  1. <xp:table styleClass="container" id="dndtable">
  2. <xp:tr>
  3. <xp:td>name</xp:td>
  4. <xp:td>type</xp:td>
  5. </xp:tr>
  6. <xp:repeat id="repeat1" rows="30" value="#{vProduce}"
  7. var="rowData" indexVar="rowIndex">
  8. <xp:tr styleClass="dojoDndItem">
  9. <xp:td styleClass="dojoDndHandle">
  10. <xp:text escape="true" id="computedField1"
  11. value="#{rowData.Name}">
  12. </xp:text>
  13. </xp:td>
  14. <xp:td>
  15. <xp:text escape="true" id="computedField2"
  16. value="#{rowData.Type}">
  17. </xp:text>
  18. </xp:td>
  19. </xp:tr>
  20. </xp:repeat>
  21. </xp:table>

Notice the start of the repeat control on line 6 and the closing of the repeat control on line 20. Also note (and correct if necessary) the various styleClass tags that translate to style tags when rendered in the browser.

The last step in our example is to add, via Javascript, the tagging we are not able to declare in the XPages design environment and activate Dojo.dnd. As in the example in part 2, we need to determine what the id of our container is. We’ve named our container -the table- dndtable which is merely the least significant component of the name that Domino will assign to it when rendered in the browser. You can render the page to your browser and inspect the source code (easiest way) or just know that, by convention, Domino will name your control view:_id1:dndtable. We use this information to obtain a handle to our Dojo.dnd container and add the missing required attributes to it. Once done, we can instruct Dojo to parse the page and “turn on” any functionality it finds. We put this code inside the xp:script element in our xpage that we set to clientSide=true like so:

  1. dojo.addOnLoad(function() {
  2. var wl = document.getElementById("view:_id1:dndtable");
  3. wl.setAttribute('dojoType','dojo.dnd.Source');
  4. wl.setAttribute('withHandles','true');
  5. dojo.parser.parse();
  6. });

Our page is now ready to be saved and viewed in a web browser. Notice that because only the first cell of any given data row is set to be a drag handle you cannot drag a row from the second cell. You may set the entire row to be a drag handle if that is your desired behavior.

Rearranging view items

A view element in XPages is rendered as a table-within-a-table. The outer table contains view embellishments such as navigation controls (prev|next) and view name and the inner table contains the actual view data. As a result, converting a standard XPages view control in a Dojo.dnd-enabled one is quite similar to the exercise of converting a table. The major differences being the targeting of the Dojo.dnd container element and the lack of any need to rearrange repeat control logic in our source editor (as the repeat logic is intrinsic in the view control.)

Let’s create an XPage called “Draggable_View” and add all the resource requirements we need to enable Dojo.dnd on our page. Next we’ll drag a view control onto our page and wire it to our “Produce” view. Accept the defaults to allow all the columns in our view to display. Now we drill down to our view element in our XPage’s outline and view the view’s “All Properties” tab in the properties browser. To tag our view’s datatable (the embedded table with the view data within the view table) as a Dojo container, set dataTableStyleClass to container. To set the datatable’s rows as draggable Dojo.dnd items, set rowClasses to dojoDndItem.

Lastly, to set our Dojo.dnd drag handles, select the first View Column by either drilling down to the first View Column element in the Outline or by clicking on the view column representation in Designer and set its styleClass property to dojoDndHandle. The last step is to switch over to our XPage’s source view and specify the name of the container in our Dojo.dnd Javascript initialization routine (by convention view:_id1:viewPanel1) in the same manner as we’ve done in previous examples.

Link to example application (zip file, on skydrive) for this tutorial.

By now you’ve seen that the basic steps in getting Dojo.dnd to work in Xpages is to properly identify the dragging containers, elements and handles and initialize the behavior using a combination of declarative steps and programming.  Our next tutorial will will be on how to drag elements from one container to another with the simple use case of being able to drag elements from one folder representation into another. We’ll (hopefully) finish up with the advanced topic of saving our browser’s state (after we’ve rearranged elements using Dojo.dnd) back to the server without having to submit and refresh the entire page.

SHAMELESS PLUG: Like what you see? Hire Me! Contact [email protected] or use the contact page to learn more about engaging Jake Ochs for your development needs.
by jake
Syndicate content