Portal/Portlet work continues

Posted by Warner Onstine Sat, 29 Mar 2008 15:32:00 GMT

So I thought I’d update everyone on my progress so far with the Portal/Portlet component I’ve been working on, as well as some of the issues I’ve run into.

Normally I would have posted this to my Tapestry 101 blog but I’m going to try and consolidate my blogs together and I’m extremely tired of JRoller’s broken Textile formatting, such a pain in the ass (I have fallen in love with Markdown and code markup, it rocks!). I’ll be crossposting this, but soon I will stop updating the JRoller blog.

I now have a basic implementation of the Portal component complete and it consists of the following classes:

  • Portal.java - Stores a TreeSet of columns
  • Portal.html - Adds in the portlet controls div and some custom CSS stuff (which I’ll talk about shortly)
  • PortalColumn - Stores a TreeSet of Portlets plus some configuration for viewing (width and background color)
  • Portlet - Stores the Portlet title, content and it’s location
  • portal/scripts/
    • Portal.script - includes all the JavaScript files and does some dynamic stuff (coming up shortly)
    • prototype/scriptaculous scripts
    • portal_hooks.js - the hooks for the portal actions (close, move, etc.)
    • portal.js - the actual JavaScript for the portal functionality that can be called by the portal_hooks.js file
  • portal/css - portal.css
  • portal/images - all the images for the portal controls and rendering (rounded corners)

Ok, so that’s the basics here are some of the specifics.

Portal.script

<input-symbol key="columns" required="yes" />
<initialization>
 function init() {    
 portal = new Xilinus.Portal("#portal div", {onOverWidget: onOverWidget, onOutWidget: onOutWidget, onChange: onChange, onUpdate: onUpdate, removeEffect: Effect.SwitchOff}); 
   <foreach key="column" expression="columns">
    <foreach key="portlet" expression="column.portlets">
    portal.add(new Xilinus.Widget().setTitle('${portlet.title}').setContent('${portlet.content}'), ${column.index});
    </foreach>
   </foreach> 
   // Add controls buttons
   portal.addWidgetControls("control_buttons");
 }
 Event.observe(window, "load", init);
</initialization>

What I’m doing here is creating a new Portal (from Xilinus) and then I’m looping through each column and then each portlet inside of the column to add it’s content.

I dive into the hackish parts after the break.

Portal.html

This part contains some of the CSS hackish-ness I was hoping to avoid.

<span jwcid="@Script" script="Portal.script" columns="ognl:columns"/>
<div id="portal">
<div jwcid="@For" value="ognl:column" source="ognl:columns">

    <div jwcid="@Any" id="ognl:'widget_col_' + column.index" />
</div>
</div>

This first part just adds in the Portal.script and then declares the html div element we want to use (this element is used throughout the CSS and JavaScript files. The next piece loops over the TreeSet of columns and outputs one specially-named div for each column.

This just creates the HTML for the portlet controls (don’t know how customizable I’m going to make this yet).

Now comes the hack.

<style type="text/css">
<span jwcid="@For" value="ognl:column" source="ognl:columns" renderTag="false">
#widget_col_<span jwcid="@Insert" value="ognl:column.index"/> { 
  float:left;
  width: <span jwcid="@Insert" value="ognl:column.width"/>;  
  background:<span jwcid="@Insert" value="ognl:column.backgroundColor"/>;
}
</span>
#edit_button {
  background: url(<span jwcid="@Insert" value="ognl:editButton.buildURL()"/>);
}
#delete_button {
  background: url(<span jwcid="@Insert" value="ognl:removeButton.buildURL()"/>);
}
</style>

Here you see I had to do some inline styles so that I could add in some new attributes for the columns, as well as images for the control buttons. I posted a query to the mailing list about 2 weeks ago, asking if there was anything like RCSS (Ruby CSS) where you could actually use Ruby to generate CSS stylesheets - which is exactly what I need here.

After talking with Howard there did not appear to be any good way of going about doing this. One solution discussed was to let the user drop in a replacement stylesheet, but to me this seems silly, they don’t need to recreate the whole style sheet.

Another solution was to create a Tapestry page that acted like a CSS file. This still holds some promise, but is technically going to be a bear to implement due to the fact that we have to change the mime-type. The kicker in all of this though is that this isn’t going to be easier in Tapestry 5, at least not right away.

The other issue that I have with implementing this is what a pain it is to work with JavaScript in this environment and now I completely understand why it wasn’t really worked with before. Customizing the JavaScript that I need to output is a truly a pain, using XML to do my looping for me feels…wrong. If there are two areas I would like to contribute to these are it - making JavaScript and CSS more first-class citizens. Outputting HTML isn’t everything you do in a Web application.

What’s left?

Not quite done with this yet. I still have the following items to clear up:

  • Need to make the Portal, Portlet, and PortalColumn classes persistable
  • Once that’s done I need to modify the JavaScript hooks so that some kind of Ajax call happens whenever a user moves a portlet, adds or removes a portlet so that their selected portlets are saved in the database
  • Need a new Page so that a user can see all possible portlets to add to their custom page
    • For this page was kind of thinking of doing a slick sliding door effect, dunno yet
  • Add in some further customizations
  • Replace default button images with ones that are easier to read/understand and fit with our internal scheme a bit better (these are customizable)

I’m trying to get this code Open-Sourced, so it may take a little bit to get this code available but it shouldn’t be too much of a problem (and we have some other cool little items we’re going to release as well - just need to clean them up).