« Quickr Domino 8.5.1 Fixpack 3: Run, Don't Walk - and - Quickr is Dead, Long Live Quickr | Main| Quickr 8.5.1 Fixpack 4 is out--with one of my favorite SPR descriptions ever »

Quickr: The wonderful thing about widgets...is widgets are wonderful things!

QuickImage Category Quickr
For those unfamiliar with this phrase, it's a rip-off from Winnie the Pooh, a children's book in which the character Tigger says the same thing (substituting Tiggers for widgets), only he bounces quite a bit more than I do when I say it!

In my previous entry, I advocated moving up to Quickr 8.5.1 Fixpack 3, which in my opinion is the first stable release for anyone who has done customizations. Oh, yes, you still have a lot of work to do to migrate some of those customizations from a prior release - especially UI constructs - but let's just say it's getting there and much better than the initial release of 8.5. There are still quite a few bugs to work out, and we're finding them as we upgrade enterprise clients and help with their customized places. We've worked on everything from themes to combination template places to new blogs and wikis to widgets, which is the topic of this post.

Widgets are, pretty much, the foundation upon which Quickr for Domino 8.5x is built. Because it relies heavily on Dojo, much of the UI and dynamic functionality is controlled by Dojo widgets. Interesting to the developer, however, is the fact that you can -- in a way that will not be overridden by fixpacks -- insert your own Dojo widgets, replace existing functionality, or extend the existing functionality of the product. We have written about 20 widgets for various customers that perform a number of interesting functions -- or fix problems with the core product...! Internet Explorer (esp. 6), the bane of our existence, is the cause of many of these.

Let me introduce you to one widget we've written. Early in the pre-release of Quickr 8.5, it became apparent that Imported Files would henceforth be previewed using the KeyView viewer. Many more kinds of files could be visualized inline than in previous versions of Quickr, which was limited to HTML, graphics and Microsoft Office file types. While they still haven't added back in HTML (and yes, many an open PMR on this!), somewhere in an 8.5.1 Fixpack (I think 2) they did add in PDF previews. You do have to modify the qpconfig.xml file for this, so follow the example!

However, it's kinda ugly unless the original PDF is either a) plain text with few text styles, or b) a scanned image. This is because PDF previews are done by converting them to HTML first on the back end, then putting it on the page in the Preview tab (sometimes...I've seen it go haywire and splat all over the screen on some really complex ones). In the case of complex PDFs -- say four color brochures or white papers originally created in Illustrator or Photoshop with lots of layers, etc. -- the preview feature loses a lot of fidelity. For example, here's just a little bit of a moderately complex PDF being rendered by KeyView:

page10preview.jpg

And here is the original (scaled down a little more):

page10original.jpg

As you can see, HTML previews of PDF files can lose colors, shading, font styles, have issues with spacing, etc. Not all of them, but many PDFs can behave this way.

So what can we do about this? A widget...!

As I mentioned before, developers can use widgets to override features based on whatever criteria might be available to them. So in this case, we wanted to tell the preview tab the following (plain English):

"Hey page, if your attachment is a PDF, and there's only one, then don't convert using KeyView but instead use the in-line Adobe Acrobat preview feature."

And while we're at it, let's do this without restarting the server.

How-To
In this case, there are two files we need to modify to enable the feature. First is the widget registry, a construct in Quickr that stores all the references to widgets that the server uses. We don't actually want to modify the registry itself, but rather extend it. IBM has provided a method to do this by adding an additional JavaScript file and referencing it internally. This file, located way way down in the file system, is called widgetRegistryConfig_ext.js and initially is empty. In my test system, it's located here:

C:\Lotus\Domino\data\domino\html\qphtml\widgets\resources\widgetRegistryConfig_ext.js

As I mentioned, it's initially empty. So we have two things to do - register a module path (which tells Quickr where to start looking for our new widget) and then tell it what widget to use once it knows the path. This is as follows:

{
        registerWidgets: (open square bracket)
        {
        type: 'REGISTERMODULEPATH',
        name: "snapps",
        path: "/snapps"
        },

        //Register renderPreview module to allow PDF inline view, instead of the KeyView PDF preview.
        {
        type: 'globalreplace',
        source: 'quickr.widgets.page.field.renderPreview',
        use: 'snapps.widgets.page.field.renderPreview'
        }
(close square bracket)
}

Sorry about the square bracket instructions - the blog template seems to not like them as typed. So in English, this says that the path is \snapps -- which on a Domino server is under (in my case) C:\Lotus\Domino\data\domino\html\. Meaning, we add the \snapps subdirectory right there.

Next, we tell it what to do - discover the original widget that is called, and replace (which can mean totally replace, or extend) it with ours. The way widgets are named, everything is a folder until that last part of the name which is a JavaScript file. So in this case, we are telling Quickr to replace the standard renderPreview widget with one we will place right here: C:\Lotus\Domino\data\domino\html\snapps\widgets\page\field\renderPreview.js

That's all there is to the widget registry. You can do other things with it besides replace features, and having a look at IBM samples and the original widgetRegistryConfig.js file (in the same location) will give you some clues.

But now we have a widget to write! The best way to do this, when replacing an existing one, is to copy and paste all the content from the original into the new widget. Then, you can write your own JavaScript to give the widget your own instructions. You can remove functions you don't change, paring it down just to your changes and/or additions. You might change one line, as I mentioned over on Luis Benitez's blog entry on showing the HTML source in the ckEditor, or you might write several of your own functions. In our case, this was what was necessary.

Now I'm not going to explain everything about this code, because I've commented above each function. I will however point out the first three lines - they comprise the structure that widgets take. You tell Dojo you want to provide your widget, require the original (since your widget acts as an inherited thing, and like I said you may only have one replacement function in it!), and then declare that your widget is an extension of the original. I won't get into the intricacies of syntax or the underscores either, that's a pure Dojo topic. So here's the widget code:

dojo.provide("snapps.widgets.page.field.renderPreview");

dojo.require("quickr.widgets.page.field.renderPreview");

dojo.declare("snapps.widgets.page.field.renderPreview",
        uickr.widgets.page.field._renderPreview
        {
                getWidgetLocation: function() {
                        return "/qphtml/widgets/page/field";
                },
               
                //Are we about to preview a PDF?
                isPdfPreview: function(){
                        var sAttName = this._attachments
                        if(this._attachments.length == 1 && this._isPdf(sAttName)){
                                return true;
                        }else{
                                return false;
                        }
                },
               
                //Is the thing we want to put in the preview tab a PDF?
                _isPdf: function(sName) {
                        var aTypes = pdf"
                       
                        return dojo.indexOf(aTypes, this._getFileExtension(sName)) > -1;
                },
               
                //We need the file to determine the extension
                _getSrc: function(sFileName) {
                        return this.getRootUrl() + this.getBaseUrl() + "/h_Index/" + this._pageWidget.unid + "/$file/" + sFileName;
                },

                //Initially tried to dynamically resize based on PDF height, this did not work so we just return 1000. Left in the if statements for study.
                _getWindowHeight: function(){
                        var myHeight = 0;
                        if( typeof( window.innerHeight ) == 'number' ) {
                                //Non-IE
                                myHeight = window.innerHeight;
                        } else if( document.documentElement && document.documentElement.clientHeight ) {
                                //IE 6+ in 'standards compliant mode'
                                myHeight = document.documentElement.clientHeight;
                        } else if( document.body && document.body.clientHeight ) {
                                //IE 4 compatible
                                myHeight = document.body.clientHeight;
                        }
                        //return myHeight;
                        return 1000;
                },
               
                //Determine whether we are about to render a PDF object
                _renderPreview: function(sPreviewLoc, ii, bForceDisabled) {
                        if(this.isPdfPreview()){
                                this.renderRead();
                        }else{
                                this.inherited(arguments);
                        }
                },
               
                //Function to tell Dojo to treat the PDF as an object.
                renderRead: function() {
                       
                        if(this.isPdfPreview()){
                               
                                var sAttName = this._attachments
                                var oOuterContainer = dojo.create("div");
                                var oContainer = dojo.create("div", {style: "overflow:auto"}, oOuterContainer);
                               
                                var pdfSrc = this._getSrc(sAttName);
                               
                                var pdfHeight = this._getWindowHeight() - 250;
                               
                                var oPreviewDiv = dojo.create("div", {className: "qkrFileSection", style:{"padding":"10px","textAlign":"center"}}, oContainer);
                                var oPreviewFile = dojo.create("object", {type:"application/pdf", width:"100%", height: pdfHeight + "px", data:pdfSrc}, oPreviewDiv);
                               
                                //this is a workaround; IE was not rendering the PDF object if we used oContainer = dojo.create("div", this.domNode);
                                dojo.attr(this.domNode, "innerHTML", oOuterContainer.innerHTML);
                                try{
                                        window.resizeIFrame(this._getWindowHeight());
                                }catch(e){}
                               
                        }else{
                                var containerNode = dojo.doc.createElement("div");
                                containerNode.style.overflow = "auto";

                                var newNode;
                                for (var ii = 0; ii < this._attachments.length; ii++) {
                                        newNode = dojo.doc.createElement("div");
                                        newNode.className = "qkrPreview";
                                        newNode.setAttribute("id", this.id + "_preview_" + ii);
                               
                                        containerNode.appendChild(newNode);
                                }
                               
                                this.replaceTargetNodeMarkup ( this.domNode, containerNode);        
                               
                                for (var ii = 0; ii < this._attachments.length; ii++) {
                                        this.generatePreview(ii);
                                }
                        }        
                }
        }
);

Now this could be cleaned up just slightly - especially in the _getWindowHeight function where we essentially gave up and set it to 1000. However, reading the comments you should get the gist of what it's trying to do. And the best part is, it works! After implementing this widget, without a server restart, here is what that same Page 10 corner looks like in the browser, this time with more context around the PDF to prove it's in Quickr:

inlnePDFwidget.jpg

Voila, not only can we preview PDFs, but in their native format. The benefits are pretty obvious -- it doesn't clutter up the server with remnants of preview cache files, it loads progressively (Acrobat handles this), it can include attachments on the PDF itself, the fidelity is perfect, and you can scale and print it from here without having to detach it to the file system first.

There is one minor drawback to this method as is, without a custom theme. Because Acrobat Reader is now an in-line object, using the "More Actions" drop-down menu actually falls "behind" the object for some of the lower menu items. So, we wrote -- you guessed it -- another widget to force the tab back to "About" if the More Actions button is clicked.

So there's one useful example of a Quickr widget. Here are descriptions of several others we as SNAPPS have written:
  • Resize the content pane when presented through WebSphere Portal (as it doesn't know how tall it is until the content feed is rendered)
  • Show the title of the current room in the Navigator (formerly Table of Contents)
  • Allow an additional option to sort attachments by file date
  • Modify the size of the dialog windows for Members and Customize, as they were deemed too large
  • Modify the subject lines in notifications to match corporate standards
  • Allow sorting of attachments three ways in read mode
  • Hide all references to Connectors for a client who doesn't use them
  • Handle internal references to http for a client using a reverse proxy for SSL
  • Fix a bug in upgraded places whereby the content pane added an iFrame around content in some cases
  • Add a description to the Index view to let users know what it is and not to delete items (thinking they were in folders anyway...)
  • Add back the drag-and-drop applet that was in Quickr 8.2 (documented in the Quickr wiki, but we modified it)
  • Modify the wording and graphics in informational messages in the product
  • ...and many more

Two final things to mention about widgets and the widget registry. Widgets that you write do NOT get written over by system upgrades, and neither does the widgetRegistryConfig_ext.js file. And, given a little ingenuity, you can even set the module path to a Domino database, making your widgets portable and clustered without having to touch the file system every time. In fact, you can (as we have with several of the above) localize your widget text to any of the 28 languages supported by Quickr.

If your organization uses Quickr for Domino and "just wish it would do things a little differently," widgets may be the answer. It's a very flexible, customizable product that has and continues to evolve and serve organizations and user worldwide, and this little window into its customization should hopefully get you thinking about what you could do with Quickr! And of course, if you need help, the folks at SNAPPS are happy to talk to you!

Credits:
PDF in-line widget: Troy Reimer, SNAPPS
PDF Example File: Karen Hobert, Collaboration Strategy Guild

@Copyright 2011 Rob Novak, SNAPPS

Comments

Gravatar Image1 - Very nice Rob!

Gravatar Image2 - I have not been able to get this to work and it is EXACTLY what I need.
It does not look like I can attach any files so if you would contact me I could send them to you. Thanks.

Gravatar Image3 - Dynamic events listing page of all published events and the ability to register for multiple events with a single invitation.Correct, well-timed and smooth data sharing among your various databases and an Event, Training or Class Management Software can actually drive your organizational objectives. Acteva has built powerful Connectors for its customers to accelerate the deployment of links between Acteva solutions and enterprise applications - CRM, Member Management database, and Learning Management solutions. This flexible information transfer engine helps lower costs and leverage existing investments.

Post A Comment

:-D:-o:-p:-x:-(:-):-\:angry::cool::cry::emb::grin::huh::laugh::lips::rolleyes:;-)

Backstage Pass to Ask

Be With the Band

Follow me on Twitter!


Opt in to receive Rob's semi-regular newsletter about Quickr, Sametime, Free Stuff and Conferences. Just enter your email address below, you can unsubscribe at any time.

Subscribe to my newsletters...
Email:

On With The Show

Here is a list of the SNAPPS templates for Lotus Quickr and other free resources on QuickrTemplates.com:
Templates:
QContacts
QIdeas
QIssues
QMeeting
QPhotos
QPresent
QProject
QSite
QSurvey

Utilities:
AnyPlace SiteMap
AnyPlace ServerMap
AnyPlace Designer for Dreamweaver

Apps:
PandaBear: Cross-Platform File Management
Flippr: Lightweight Quickr Admin Client
SnappFiles: iPhone Client for Quickr, Filenet, ICM...

Downloads: 118,490
Countries: 162
Read about the templates in Intranet Journal
NEW: Some of the templates are now bona-fide products for Quickr 8.5.1! Check out my Sep 23 2011 entry for more!

Search

Googleage

  • No Search Referers