Recent Bloggers
Bryan Cheung Posts: 9
Stars: 41
Date: 1/5/09
Minhchau Dang Posts: 6
Stars: 5
Date: 12/30/08
Nate Cavanaugh Posts: 23
Stars: 60
Date: 12/28/08
Eduardo Lundgren Posts: 1
Stars: 5
Date: 12/20/08
Brian Chan Posts: 20
Stars: 125
Date: 12/18/08
Steven Cao Posts: 4
Stars: 11
Date: 12/17/08
Jorge Ferrer Posts: 23
Stars: 102
Date: 12/16/08
Alice Cheng Posts: 116
Stars: 0
Date: 12/10/08
Michelle Kawaye Posts: 2
Stars: 8
Date: 12/10/08
Michael Young Posts: 9
Stars: 48
Date: 11/21/08
Blogs
Liferay wallpaper

There are a ton of amazingly creative people here at Liferay, and I'm always stunned with the stuff that people create.

Last night I was doing a ton of coding and some inspiration struck to make some Liferay "fan art". I don't get the time to do as much artwork as I used to, so it was a nice treat for me.

So, it totally is not "corporate" and doesn't really follow the Liferay branding guidelines, but like I said, it's "fan art" and normal rules don't apply. I wanted to make something a bit edgier than we're used to having.

I've created a package with common wallpaper sizes that you can download here.

Here is a brief preview:

Average (0 Votes)
328 Views, 6 Comments
Is this site running Liferay?

It's been a while, but trust me, we've been working like crazy to get 5.2 out for your using pleasure.

But, I arrive with a bit of a gift, especially for the marketing folks (or anyone curious). Have you ever wondered if a website was running Liferay?

Well guess no more. I've written a Greasemonkey script that will tell you if a website is running Liferay or not. What's Greasemonkey you ask?
Only about the absolute coolest extension for Firefox known to man (yes it requires Firefox).

So what do you do?

If you have Greasemonkey installed, you should just skip to Step 3.

Step 1: Go to https://addons.mozilla.org/en-US/firefox/addon/748

Step 2: Click the Add to Firefox button, and install the plugin. It will notify you to restart firefox.

Step 3: Once you've restarted, go here: http://userscripts.org/scripts/show/37259

Step 4: Press the Install button at the upper right

Step 5: Agree to the install

 

Step 6: Visit a website (like http://liferay.com)

 

Step 7: Enjoy the info:


And that's it. You can hide the display (and it will popup on every page load, or you can hide it for the domain, which will hide it for 1 year. If you want it to show up again, just clear your cookies, and it will popup again for any domain you have hidden it for.

 

Enjoi!

Average (3 Votes)
1060 Views, 4 Comments
The new Liferay.com

Hi all, it's been a while since I've blogged, and there has been good reasons. Liferay is taking off and we are growing like crazy, and as such, I've gotten less time to blog.
But there has been a special occasion for this blog, and that is the official release of Liferay.com, and I wanted to talk a bit about it.

So, back in May we started talking about our current website internally amongst a few of us planning it out. We have long known internally that there were some issues with our different sites that no matter how much Bmiller would polish it up, they wouldn't go away. And this wasn't Bmiller's fault, always gave us more than we asked for, but there were always limitations of time and resources that would keep us from making deeper changes.

So back in May, there wasn't any internal pressure to get a new website out, and we were content with the branding for the site, and instead of waiting until we HAD to redesign the site, Alice, Bmiller and I got on a conference call together on a cloudy May day and hashed out how we would design the site.
One thing we didn't like about the old site was that it would hit you with a lot of information when you first visited, and a common complaint was that people would visit and not quite sure what our company provided.

Part of this was due to information overload, part was due to just general information architecture issues.

So out of that first day came our initial wireframe for the front page.



We knew we wanted to be direct and to the point with our message and branding, and also consolidate our the amount of pertinent information on the front page. In fact, we wanted all top level pages to be succint and concise, and to help navigate most users to where they would like to go, but also make the deeper areas of the site discoverable.

So, in short order, Bmiller delivered this to my inbox to show me the progress:



A little after this, Bcheung and Cecilia got involved and we started getting all sorts of pressure to make all kinds of changes.

So one little tangent here that I'd like to touch on: Conflict is great.
In a lot of companies, organizations and even personal relationships, conflict is looked at as a bad thing, and avoided quite often. Let me just say Liferay is not that kind of company. Conflict can be constructive or destructive, depending on how it's handled.

And for this site, there was a lot of (good) conflict. There were literally days where we would argue for a couple of hours over the tiniest of details, each of us representing a different opinion, each of us having our own unique perspective.
And what came out of it was a truly superior product. I know for a fact that I argued for different things that, had they been unquestioned, would have kept the site from being as amazing as it now looks.

Or as Bchan told me during my interview almost 2 years ago: "The best idea wins."

Bcheung and Bchan both really pushed this version of the site and without their constant driving and pushing for improvements and changes, it wouldn't be a fraction as great as the final product is. It's because of their leadership, patience, high standards, and determination that we were able to get this site done.

Bmiller outdid himself again. Liferay is so blessed to have an art director on his level, and his passion for what he does is inspiring, and his patience with the rest of us whose eyes are less refined has made this process go so much smoother than it could have.

Alice deserves a TON of credit. She has been pushing us for the longest time to improve the site, and to do it right, and her input and marketing chops really provided so much to the site's development. She worked incredibly hard on the site's IA and put in a ton of time making sure the site's content was easy to find.

Ryan Park is a machine. I think he knows how to use Liferay's CMS better than most of us internally, and he has cranked away. Thanks so much Ryan for your hard work, insights and always upbeat attitude no matter how much IE6 was trying to beat you down.

And of course, there is so many more people to thank, and so many contributed ideas, opinions and constructive criticisms that truly brought out an amazing site.
If I'm forgetting to give proper credit, please let me know.

All I can say is awesome job everybody, this truly is the best version yet, and I am blown away with how great it is :)

Average (7 Votes)
1021 Views, 7 Comments
Oh yeah, about the auto-save

I just wanted to drop a couple of notes about the auto-save that I didn't mention before....

1. I didn't do the bulk of the work
That honor goes to Jonathan Neal. Bchan did a lot and I made a couple of javascript changes and tweaks, but the work load credit goes to Jon for actually laying the foundation and getting it done. Go hit up his wall and tell him great job :)

2. There was a race condition
There was a bug where you had to time your save JUST so, otherwise it could stay in the state of perpetual draft. We've fixed it in trunk, and will be pushing it live here to the site very soon.

3. We changed the interval time
Instead of 10 seconds, it now saves every 30. This seems a bit more realistic to me, but we're willing to hear you guys out if someone HAS to have it saving every 10 (and can make a good general case for it).

4. It now is smarter
It now checks if you're editing a draft, to save only if the content or title has actually been edited. Otherwise it will patiently wait until you have edited.

It's stuff like this that really just makes my day, getting to work with so many smart people who can develop rapidly and get stuff out the door.
 

Average (3 Votes)
1196 Views, 1 Comments
Autosave comes to blogs

This is a feature I know many of us have been desperately wanting for quite a while. In fact, it's something that many of us miss from other blogging apps, and we now have it.

So, to kind of show off what we have, I'll include screenshots from this very blogging session.
Isn't this awesome?!

Notice the time? Every ten seconds, we automatically save your draft until you click publish. This means that if your browser locks up (thanks a ton, Firefox 3 b5), or your whole system wonks out (*cough*Vista*cough*) you'll be assured that you'll have an up to date copy of your blog entry waiting for you to go back to it.

You know what else is great about it? Let's say you go back to your draft, and you decide the title isn't right, it automatically updates the friendly URL for you as well.

Is that more bang for your buck or what?

Now what happens if you go to see your blogs, how do you know which ones are your drafts? Wonder no longer. Take a look:

Man, Nate is one snazzy UI designer ;)

See how it has a grey box around it with the dark grey text and the icon? That's how you know it's not published yet.

We're contemplating a few ways to mark it as a draft manually, but we shall see.

Another question that might come up, what happens if you're editing an already published blog entry, does it autosave that? No. Mainly because of the big issue of let's say you accidentally type something up that the world wasn't meant to see, you definitely wouldn't want the world to see it, let alone see it in a state of flux. Or if you go in and edit, and accidentally delete everything you wrote, you sure wouldn't want that autosaved.

We have so much awesome stuff coming down the pipe, but I don't want to mention it just yet. But let's just say that I think it will just solidify even more why we're the number 1 open source portal.

 

Average (12 Votes)
1216 Views, 6 Comments
How can jQuery help me today? pt. 1

So, when we adopted jQuery, I think I should have done more to evangelize it within Liferay. For most web developers in the world, it's really taken off in popularity because the concept is tied to an existing development paradigm, eg. CSS.

However, that doesn't mean everyone in Liferay is familiar with CSS. But the benefit for the backend folks is that it let's you focus on only having to learn one set of selectors based on an existing and widely supported standard, rather than having to relearn a whole new language paradigm.

So first things first, I'll do a quick lay of the land with jQuery that will help you get up to speed, and then I'll launch into examples that can show you some useful tips that can help you get stuff done today.

First, the concept behind jQuery is that you operate on the DOM (the structure that represents HTML elements on the page).
In normal Javascript development you do everything on the DOM elements directly.
For example:
document.getElementById('banner').style.display = 'none';
document.getElementsByTagName('body')[0].className += ' classic';

Those snippets there find the elements in the DOM, and modify their properties directly. Now, there are a lot of ways to do things like this, and honestly, a lot of them get confusing really quickly. For instance, if you want to change an attribute on an element, some people set the property directly, some people use setAttribute(attr), etc. And every browser has quirks related to this.

So the purpose behind jQuery is to "query" the DOM, and return you back a set of elements (even if its only one element) and operate on that collection.

You can think of every jQuery object as a bucket of DOM elements, and every jQuery method you do on that object is automatically done on every element in the bucket.

One thing commonly done in web dev is to get all elements that match some criteria (querying the DOM), and often, you want to grab everything with a certain class name.

In CSS you would do it like so:
.liferay-element {}

In normal JS you would do it like this:

var allElements = document.getElementsByTagName('*');
var matchedElements = [];
for (var i=0; i < allElements.length; i++) {
    var el = allElements[i];
    if (el.className.indexOf('liferay-element') > -1) {
        matchedElements[i] = el;
    }
};

And then you would have your collection of matched elements.

So how would you do this in jQuery?

var matchedElements = jQuery('.liferay-element');

Looks pretty familiar, right?

So now that we have matched elements, what could we do with this? A whole ton of good stuff.

Let's say we wanted to fire an alert box when we click on each of those elements, how would we do it?

matchedElements.click(function(){
    alert('you clicked me!');
})

Or what about adding another class to each element?

matchedElements.addClass('new-class');

Or wait, let's say that we have a collection, and we want to make all of them have a red border, BUT, if the element is a span, we want a blue border?
Easy-peasy-lemon-squeezy.

matchedElements.css('border', '1px solid #f00');
matchedElements.filter('a').css('border-color', '#00c');

Notice the filter portion? The filter method reduces a current collection down to a smaller set based on a jQuery selector (or other things, but you can look at the documentation [http://docs.jquery.com] for more info).

So you may be saying "Okay Nate, that's great and all, but you're really boring me here. I don't want to add ugly borders to my elements, and I don't want to hear you ever say that easy-peasy line again, I want to DO STUFF!".

So let's do stuff.

One common thing that we've all done numerous times is use a checkbox to select all checkboxes in a set, sort of a select/deselect all option.

So let's assume we have a group of checkboxes, that don't have a classname, don't have an id, and don't have the same name.
But we know the name attrbiute all starts with the same thing, in this case:
"<portlet:namespace />check"

So, we have our checkbox that acts as the trigger, and but it doesn't start with the same name.

Our example HTML would be this:

<input type="checkbox" id="<portlet: namespace />trigger" />
<input type="checkbox" name="<portlet: namespace />check1" />
<input type="checkbox" name="<portlet: namespace />check2" />
<input type="checkbox" name="<portlet: namespace />check3" />
<input type="checkbox" name="<portlet: namespace />check4" />

Here is how we would toggle all of the checkboxes in jQuery:

var trigger = jQuery('#<portlet:namespace />trigger');
trigger.click(
    function(event){
        jQuery('[@name^=<portlet:namespace />check]').attr('checked', this.checked);
    }
);


So let's go by that, line by line, so we know what we're doing:

var trigger = jQuery('#<portlet:namespace />trigger');

The # sign in CSS signifies an ID, so in this case, we're getting an element by it's ID.

trigger.click(

We're now assigning a click event to our trigger. The events that we add need a function passed in, and the function that is passed in has two special things about it, 1 is that the argument it gets is the event object. I won't go into detail here about what the event object has on it, but it let's you do all kinds of things.
2, however, is that the scope of the function is changed a bit so that "this" points to the element that you're working with. So in this case, this points to the DOM element of our trigger.

function(event){
As mentioned above, here is the start of our function, with the event parameter.

And here is where the magic happens:

jQuery('[@name^=<portlet:namespace />check]').attr('checked', this.checked);

That's kinda nuts right?

Well, jQuery lets you query objects based on parameters, and you can also do minor regular expressions in it. CSS also allows you to do this (in every browser, of course, except IE 6).
It's not a direct port of CSS in this case, but of xpath, in that you have to use the @ sign. The newer versions of jQuery don't require the @ sign, but in Liferay pre-5.1, we have to use the @ sign.

So I'll break this line up:

jQuery('[@name^=<portlet:namespace />check]')

Find every element whose name attribute begins with <portlet:namespace />check. The begins with is done by this part:
^=
if we wanted to say every element whos name ENDS with, we would do:
$=

.attr('checked', this.checked)
This sets the checked attribute of every element we found to whatever the checked state is of the current element.
So if the current element's checked attribute is set to false (unchecked) all these elements will be unchecked. If it is checked, so will all of those elements.

Okay, but if you ask me, that's kind of a lame example. That's something we've been doing since time immemorial(like since 1999 when Ben Franklin gathered all the animals on the Ark and crossed the Delaware river), and while it's fast, it's not like everyone is screaming "HELP ME CHECK LITTLE BOXES!"

But what if we wanted to do an ajax call on a page that updated a div with the results and show a loading animation so the user isn't wondering what's going on?
Well first, we need to make sure the URL that returning the HTML we need.
Secondly, let's assume the div we want to update has an id of portletBox, and the link we're clicking points to the URL resource, and has an ID of linkTrigger.

Here's our HTML:

<div id="portletBox">
    Existing Text is here....
</div>

<a href="http://liferay.com/test/?p_p_state=exclusive" id="linkTrigger">Click me to update our text</a>.

Here's how we'd do it:

var linkTrigger = jQuery('#linkTrigger');
var updateDiv = jQuery('#portletBox');

linkTrigger.click(
    function(event) {
        updateDiv.html('<div class="loading-animation"></div>').load(this.href);
        return false;
    }
);

Let's go down a bit at a time:

This of course grabs our elements to work with.

var linkTrigger = jQuery('#linkTrigger');
var updateDiv = jQuery('#portletBox');

Now we'll add a click handler
linkTrigger.click(
    function(event){}

This is where we do our work
updateDiv.html('<div class="loading-animation"></div>').load(this.href);
return false;

Let's analyze this a tiny bit. When we click the link, we're first grabbing updateDiv and replacing all of it's HTML with a div that handles the loading-animation.
Right on the end of it, we're doing .load(this.href), which performs an AJAX call and updates the jQuery elements with the results of the AJAX call.

Lastly, we have "return false;". What does this do exactly?
Well, in every browser event, there is a default action. In the case of a link, the browsers default action is to follow the link. However, in our case, we don't want to follow that link, but instead stay on the current page.
When you return false, it prevents the default action from ever taking place.

This also works with every event, for instance, with forms, if you want to do some stuff when you submit the browser, but want to prevent the actual form from submitting, you would return false.

So, that about does it for right now. I'm going to think up some more (useful) examples of things jQuery can do to make your development life a lot easier.

Is there anything you'd like me to cover, for instance, doing animations, or manipulating html elements, etc?

Average (5 Votes)
1677 Views, 4 Comments
China

Hey there, O faithful reader...
Man, what a long time since actually posting. Since I last posted, quite a lot has happened, and included in that was the fact that my wife and I visited China with Brian and Caris, Bryan Cheung, Alice, and Dave, to go spend time with Mark, Ivan, and Shepherd.

What an experience! There are a few times in everyones life when travel truly changes you in ways you were completely not expecting.
The China trip was one of those times.

To begin with, nothing really changed my view of flying during this trip. Except maybe that I need a height-ectomy. Flights both ways were pretty much par for the course of my normal flight experiences: painful.

But the actual trip more than made up for it. I will also say that the pleasure of this trip is completely due to the grace of the people we were visiting with. Had Jessica and I gone by ourselves, we no doubt would have come back with a vastly different experience.
We were made to feel completely at home, and even though our Mandarin is limited to Ni hao and Xie Xie (Hello and Thank you), we were able to get by because of the patience of the people we were with, and the patience of the Chinese :)

The first day, I will say, was really rough. We landed with too little sleep, and the city was very overwhelming at first. Luckily though, we fell into sync with the local timezone the night we got there, and were not jet lagged at all. Jan--March2008 072



This was also the very first time in my life that I've traveled internationally and did not get sick, and that is quite a big deal to me :)

I got to me a lot of really great guys, and I will always remember Steven, Gavin, Sai, and Dale because their patience with the language barrier, and their hard work learning the Liferay theming system. They were incredibly friendly, and I do miss being out there.

Surprisingly, I don't think my comfort level was stretched too far. More than anything, I was really curious about things, but the odd thing was that while I thought the whole personal space thing would cause me grief, it's strange how oddly liberating it is to be squashed in a moving metal can with other people and have no concept of moving your arms, let alone "personal space".
DSC01246
The food there was quite good. Some things, not so much, but for the most part, most everything I tried, I really enjoyed (of course, the same is said when I eat anywhere, which suggests I'm not so much a worldly foodie, but more just a person who likes eating).
Of course, eating at a Chinese McDonalds and Pizza Hut were actually pretty strange.
The food outside of the chains were where the best food was found, though. Dumplings, wontons, and the normal Chinese fare we're all so familiar with was there, but so was live sashimi (where they cut off the flesh of the fish and leave it there on the plate moving its mouth...), chicken-head-kabobs, and all sorts of different and exciting items.
Jan--March2008 030
DSC01283
I DID manage to get my Diet Coke fix. It was glorious.
However, the first day went a bit rough in getting it, but due to the persistence of our friends, we did manage to complete the quest, as you can see below. The first machine I tried took my money, but wouldn't give me the Diet Coke. I had to settle for a freakin Fanta! But we would prevail:

Also, our office there is freaking AWESOME. I don't envy the internet speeds, but the office is so nice.
dalian, China Mar08 042
dalian, China Mar08 051
I could go on and on with vivid detail, and I might in future posts, but I will say that I learned some really amazing things while I was there.

One is that how I think I am coming across to people is often very different than how I actually am. I learned that when I interact with people, a lazy tongue and an eager ear go further than they're often given credit for.

I also learned that the people I am lucky enough to not only call co-workers, but friends, are insanely patient, loving, and kind, which humbles me that they deign to consider me a friend as well.
DSC01290 (2)
Lastly, I learned that my wife is much more adventurous and patient than I really knew, and I'm not only glad she got to go for this trip, but just overall incredibly grateful for the opportunity to experience it.
Jan--March2008 027

Average (8 Votes)
1232 Views, 4 Comments
Random theme related tips

People seemed to like the "hey buddy, want to hear a secret?" nature of my last post, and in that tradition, I will try to drop some tips about some of the lesser known ways of playing around with the Liferay themes.

Browser Selectors
In Liferay since 4.3, we've used a method of browser detection called Browser Selectors. What this does is allows you to style your theme for different browsers, different operating systems, and whether or not the users have javascript on or not.

This allows you to style for not only a certain browser, but also a certain browser version.
 

The selector class always exists on the html tag, which means that the browser metadata used in the class name will always be first in the selector chain.
For example:

.js #navigation{}
This styles the navigation element only if the user has javascript turned on.

.ie #navigation{}
This would style the navigation element only if the user is using (any version of) Internet Explorer.

Also:

.win.firefox2 #navigation{}
This would style the navigation element only if the user is using windows, and Firefox version 2.

This is much cleaner than using hacks in your css to target a browser.
Mind you, these will only work if the user has Javascript turned on.

Turning off portlet title editing
Sometimes you don't want your theme to have editable titles (you know when you click on a title and the admin user can edit the name?)

The way to do this is to open up your portlet.vm in your theme's templates/ directory, and add this class name "not-editable"

So the HTML would look something like this:
<span class="portlet-title not-editable">

Turning off dragging and dropping of navigation, and turning off the Add Pages functionality
Sometimes you don't want this added functionality. It's great sometimes, but sometimes, it can be a pain. There are two classes that exist on the navigation div that control this functionality.
These classes are sort-pages and modify-pages.

"sort-pages" adds the ability to drag and drop your navigation elements, and if you remove this class, you'll be able to add new pages on the fly, but you won't be able to sort them.
"modify-pages" adds the ability to edit the page's title, and adds the "Add Page" button to the navigation as well.

Turning off the Drop Down dock
Sometimes, you don't want the interactive dock that drops down with a list of actions. In the Classic theme, for instance, you can have it display as a friendly horizontal list of icons and text. To do this, just open up dock.vm and remove the class of "interactive-mode" from the dock div.

I think that's about it for now. I'll try to blog a few more as they come to me, but overall, this might/should/maybe help out some people who have been curious :)

Average (4 Votes)
987 Views, 410 Comments
Hooking into Liferay's Javascript functions

Have you ever wanted to plugin to Liferay's javascript functions, but didn't want to modify the original Javascript, and didn't want to branch off of it? I know I have.

For instance, let's say that there is a Liferay javascript function that always adds a class, but you don't want to add that class, and instead want to use your own. Well, there is a little known method that does this that's in Liferay 4.3.x.

The method is called Liferay.Util.actsAsAspect. This little method adds AOP (aspect oriented programming) features, that allow you to hook into existing Javascript without a plugin framework being in place.

So, I'll use a good example of this. In Liferay, if you don't want the dock to be a drop down, you can go into dock.vm in your theme, and remove the class name of interactive-mode off of the main dock container.
This allows you to easily disable that feature.
But recently, we needed to remove this class, but didn't want to keep a floating copy of dock.vm where the only difference is one little class name.

So we could either modify the file anyways, or we could use Javascript to remove the class name. But then there become a race condition, where you're trying to race against the Dock javascript to remove the class before it can find the class and begin the transformation.

Well, that's where Liferay.Util.actsAsAspect comes in.

What it does is it takes the object you pass to it, and augments it to contain three new methods: before, after, and around.

So, in our case, we needed to modify Liferay.Dock (the class that creates the dock), and we needed a way to guarantee that the class name interactive-mode was removed, thereby preventing the Dock class from even seeing that there is a dock to modify.

So, here is what we did:

Liferay.Util.actsAsAspect(Liferay.Dock);

Liferay.Dock.before(
    'init',
    function(){
        jQuery('.interactive-mode').removeClass('interactive-mode');
    }
);

So, you see, now every time Liferay.Dock.init() runs, it will always execute our little function we passed into it first.

We can also run Javascript after, using Liferay.Dock.after() and passing in the name of our method, and a function we wish to execute.

We can also wrap the init function with code, like so:

Liferay.Dock.around(
'init',
function () {
    var dock = jQuery('.interactive-mode');
    dock.removeClass('interactive-mode');
   
    this.yield(); // This line executes the original function
   
    dock.addClass('interactive-mode'); // This line now adds the class back after the function fires
}
);

Now, the next question you may be asking is, how do you do this for classically defined functions (you know the ones that look something like: onLoad() or _88_updatePage()), and don't have a parent object that we can manipulate.

Well, we actually can manipulate because all functions defined like this: function _88_updatePage() {}, all exist inside of the "window" object. The "window" object is the global namespace where normal functions are added by default.

So, here is how we would execute a function before _88_updatePage get's executed:

Liferay.Util.actsAsAspect(window);

window.before(
'_88_updatePage',
function (month, day, year){
    alert('I am just about to update the page');
}
);

So, anyways, I thought that this would be helpful to people who need to hook into our existing Javascript, and can't wait till we implement our formal JS event system :)
 

Average (3 Votes)
1424 Views, 9 Comments
New Liferay Desktop Theme

A few months ago, Peter "The Man" Shin started working on a new theme for Liferay, called, appropriately enough, Desktop.

Yes, I know we already had a theme called Desktop, but to be honest, it wasn't the greatest theme in the world. The different "OS's" was actually just different backgrounds on the same theme.
So, to the rescue, Peter went through and created a new theme, with 3 real OS versions included in it, which I've shown below.

If you get a chance, tell Peter how much the theme rocks by emailing him at "P shin @ liferay [dot] com" (use your mental regex engine to parse that one out :) ).

How do you get the theme? Download the latest bundle of Liferay (4.4.0), and go to your Plugins page, which is located in the Welcome dock > My Places > My Community > Private Pages. Click on the Theme Plugins tab, and you should see the Desktop theme.

Click on the link, and press the Install button, and you're all set.

I'll be blogging on the best way to do this soon, so users can see what the process looks like.

Click on a thumbnail to see the theme in full size:

Vista

Vista

Mac OS X

Linux

Average (3 Votes)
1531 Views, 5 Comments
Ahh, the travails of travel

I don't know how Bryan Cheung does it. I believe he flew over 100,000 miles last year, and I'm really quite surprised he's still sane.
Why do I say that? Because if any of his trips are anything like the one I am currently on, then I have to give him some sympathy and a newfound respect.

Right now I am in Frankfurt Germany with Mike Young (who actually had to take a train to Paris last night), and so far this trip has been a bit of a comedy of errors for me.

I am loathe to complain, because this is all stuff that can and has happened to anyone, so I don't think I'm a martyr for it happening, but it has been a pretty rough ride so far.

How it starts

It all started Wednesday night, when Amazon emailed me to let me know that my Kindle (I got one for Christmas, ordered it December 2nd, but have been waiting forever to even get an approximate ship date) would arrive for me THE DAY AFTER I left. A small thing, but if anyone knows me, they know I've had an unhealthy obsession with getting one.

Thursday morning, the day of our flight, I wake up to find out that some plumbing problem in our apartment building had flooded our toilet and shower (hopefully with different water ;) ), and I wouldn't be able to use the bathroom or take a shower. I called Mike and he said it was okay that I take a shower at his place.
But, alas, in the rush that ensued after that, I forgot a camera for the trip, AND my international converters. Power might be nice, considering 99.9998% of my work is done on one of those fancy computers the kids rave about these days.

I get to Mike's, and I'm able to take a shower, and we head to the airport. About 40 minutes into the drive I realize I forgot my jacket.
Not good news considering that it's about 30-40 degrees Fahrenheit where we're going. But, I remind myself that I packed a sweatshirt, long sleeve shirts and plenty of thermals, so I should be fine.
Oh boy, was I naive.

The plane ride

We get to the airport and our plane is delayed, so they move us to another flight leaving about a half an hour earlier so we won't miss our connection.

I'm 6'4, so flying in economy is rather painful for me, as it inevitably means that my knees are next to someones ears, or they're spread out into the aisle so that the Panzer that they've converted into a drink cart slams into my knees.

Thankfully the first flight was pretty short, only about 2 hours. But the second flight, on a beautiful Lufthansa plane, was a ride, let me tell you.

The lady in front of me decided she wanted to sleep the entire way. I don't blame her. Except that she was like 5'4 and the seats were made just right for her.
So she reclines her seat so far back that it literally forced my knees into the aisle, and her headrest was about 3 inches from my forehead. I actually heard her stretch and yawn to the guy next to her "Life is great". For you, lady, but the giant, lumbering white guy behind you is getting his legs amputated.

I should have just asked her not to lean so far back, but on a 10 hour plane ride, I needed something to do in between chapters of my book, so after the meal was served, and she tried leaning back again, I just used my hand to hold her seat up. She kept trying for what seemed like 30 minutes, at random times. Over the next couple of hours she tried slamming the seat back, but by now I was having fun with this ridiculous passive aggressive game of "What's stronger, her back or my arm?".

I do regret not just asking her to stop leaning back so far, but at least I have a somewhat humorous lesson to share with people who believe that leaning back in their airplane seat affects nobody else.

After we land, we decide go to baggage claim, and fairly quickly, Mikes bag comes. I wait for mine, as he goes out to meet Paul Bakaus, who's picking us up.

But, guess what? They lost my bag. Yep, the bag I packed with Paul's laptop that we're delivering to him. The bag with my thermals and sweater, and fresh clean clothes.

So I go to baggage claim, but because Mike checked both bags in his name, and he's not with me, there's only so much they can do. They try paging Mike and Paul, but they didn't hear it. So I go out to the main area and try to find them both. I page both Mike and Paul again, and again they didn't hear it.
So after searching for 20 minutes, I finally find them, we go back in, give them the appropriate numbers, and they tell me they'll keep doing a search, and deliver it when they find it.
Thank God Paul was able to take delivery of it to his place.

Thank you, Netherlands

Mike and I planned to take a quick weekend trip to Amsterdam to do some sight seeing, so we board our train, and get into Amsterdam around 6pm.

We eat dinner and mill around. Frankly, it's freezing cold. All I have is a tshirt and jeans on, and I am hurting.

The next day we make it to C&A which is like some European Kohls, and I was able to pick up a jacket, a pair of jeans, some more shirts, some socks and such, and for a fairly reasonable price.

We decide to rent some bikes and tour around Amsterdam, and check out a cheese farm where they make, you guessed it, cheese but also clogs.

We ride back, and go out to dinner again later that night (which was a really delicious Indonesian place). We head back to the hotel and go to sleep.

At about 4am I wake up freezing cold, and having massive chills. This is a strangely familiar feeling, considering that just a few weeks ago I recovered from the flu.
And now, I have it again.

For the past 2 days I've had a really bad fever and have been struggling to even move out of the bed. And even though I've been taking sleeping aids, I can't stay asleep for more than 4 hours.

So, I'm sick, jetlagged, and dressed like a homeless person.

Luckily, though, I think the fever finally broke last night. I didn't wake up in chills (just drenched in sweat), and I'm beginning to feel a bit better.

The point. There is one, and I got it

Thankfully, though, God has used this time to teach me some valuable lessons.

For one, that He will provide for all my NEEDS. During this entire time, all of my basic needs were met.
Another lesson was exactly how few my needs are. A lot of times I get hung up on the creature comforts of life, and I forget just how little I actually need.

Another lesson I learned was from a verse that I haven't read in a while. It's from Proverbs 3:11 & 12:
"My son, do not reject the discipline of the LORD
Or loathe His reproof,
For whom the LORD loves He reproves,
Even as a father corrects the son in whom he delights."

Many times (and I should probably say MOST of the time) I do resent it when my life doesn't go quite as I had planned it.
I need to change that.

One other thing that has really hit me again and again while being at Liferay, and that is just how small my problems in life really are, especially compared to the leadership in the company.
Business is tiring, stressful, hectic, and demanding. Those of us who don't carry the daily burden of billing, cashflow, HR, employee management, client relationship management, etc, are spared the daily anxiety of dealing with it. But somebody does deal with all of those things.
And while I was able to get my luggage back, and my flu will go away, and plane seats will one day become comfortable, the painful struggle of pioneering a company with 60 people dependent on you is tough.
So my griping comes with the understanding that it's rather mild in comparison to other's real struggles.

I also learned I am incredibly blessed to even be in the position that I am. Yes, I miss my wife desperately, yes I feel like the devil kicked me in the gonads, and yes, I probably stink like the Los Angeles "river", but in the end, here I am in my fourth European city since joining Liferay, working at a company with and for people so insanely talented that it makes me giddy, and with a life thankfully rich with a wonderful, if somewhat fractured, family, rich in quality friendships, and overall, I am quite content with my life.

The other lesson I've learned, and Mike actually came up with this, and it would be to have the option to be tranquilized on long flights and placed in a tube, a la the Japanese capsule hotels I used to watch about on "That's Incredible!".
You would get a shot, be placed in the tube, and wake up in 12-15 hours later in your city of choice.
Maybe they wake you up half way through so you can avoid the DVT's, and maybe use the restroom.

I don't know about you, but I'd be up for it :)

Average (5 Votes)
876 Views, 7 Comments
OS X and Liferay tip

It's been exactly 10 years since I got my very first computer, which was on Christmas of '97, and was a $400 dollar IBM clone, running Windows  95. I didn't even know how to change my wallpaper, or use my computer for anything more than a CD player/Calculator/Solitaire machine.

It's odd that exactly 10 years after getting my first computer, I am reaching another first, and that is my first Mac.

I have made the switch, and I am sold.

There has been some small issues, like a lack of decent freeware, and a few little oddities, but overall, it's been incredibly pleasent.

One of the things I love is the fact that it's based on FreeBSD, and it means I have a file system that actually closely mirrors the web servers I've been used to developing on.

But one thing I missed from Windows was shortcuts to my different Liferay installations. Clicking on startup.bat would load a new command propmpt window, which would show the log in that window.
Unfortunately, in OS X, when you run startup.sh from the command line, it doesn't do that. Basically, the normal workflow is:
Navigation to your Tomcat/bin directory
run startup.sh
run tail -f ../logs/catalina.out

Kind of a pain, if you ask me. Brian Chan also taught me that you can shorten that a bit by just using './catalina.sh run', however, I think this could be improved a bit too.

So, what I did was created an alias called liferay that runs that does just that. This way, I can just run the command from anywhere, and it will launch Liferay right there, and show the catalina.out log.

Of course, there could be better ways to do this, and I would love to hear them. I am a relative n00b on the Mac, so I am sure there will be some cool optimizations and the like that I won't get.

So here's how you can quickly create the alias:

  1. Open Terminal
  2. Type 'pico .bash_profile'
  3. If there's anything in the file that opens up place the following line at the end, if not, just place it at the first line:
    alias liferay='~/Desktop/Development/Tomcat/bin/catalina.sh run'
  4. Replace the path above with the path to your tomcat directory.
  5. Press Ctrl X, and then hit Y when it asks you if you want to save, then press enter
  6. At the command line, type 'source .bash_profile' to refresh your profile
  7. Type liferay to launch Liferay from the command line.

Now, you can always just use the liferay command, and just press Ctrl C to exit out.

I will be playing with some ways to manage a bunch of different bundles and be able to launch them easily without having to navigate the file system via CLI everytime I need to launch one.

 

Average (1 Vote)
1100 Views, 2 Comments
CSS Inheritance

So yes, there has been a long delay from the last time I blogged, but I thought I would come back with something that I have actually been asked a lot about recently, which is CSS Inheritance.

I'm finding out that front end technologies are like Sanskrit to many server side developers, which is strange, because CSS and Javascript, to me, are easier to understand.

But issues like inheritance in CSS (and OOP in Javascript) break convention so much that it's hard carrying over knowledge of the server side to the front end.

Many people assume that inheritance is controlled by a few factors, one of them being source order. And, it's true, that all things being equal, the last line of code in the source takes precedence.

But that's assuming all things are equal, which, as we'll see, is not often the case.

Let's take a look. Assuming that we have a body tag like this: <body class="my-class">, and this CSS in our file:

body.my-class { background: #000; }
.my-class { background: #f00; }

What color will the background be? Most people might assume that it's #f00 (red). But alas, it is not.

The background color of the body would be black (#000). Why? Because of one of the more confusing (but powerful) features of CSS, and that is CSS Specificity.

The idea behind it is that the more specific your selector, the more precedence it's going to have.

So what about a code setup like this:

<body class="my-class" id="myId">

and CSS like so:

#myId { background: #000; }
html > body.my-class { background: #f00; }

Again, the body background color would be black. Confused yet?

The reason is because each piece of the selector has a given weight, but they're not all equal. Each selector is actually a calculation of it's pieces weight, so that every selector has a certain number of points.

This is news to most people, but it's true. Each selector has browser given weight to it, and if you add the points up, you can tell which selector will win out.
So what are the points?

Elements (li, p, a, table, etc) all have 1 point.
Classes (.my-class, .banner, etc) all have 10 points.
Psuedo classes and attribute selectors (:link, :hover, :focus, [rel=external], [type=text]) all have 10 points
ID's (#myId, #wrapper, etc) all have 100 points
Inline styles (style="background: #0c0;") all have 1000 points.

So if you want to calculate why a certain style isn't getting applied, some quick math, you would see that a setup like this:

a[rel=external].my-class { background: #f00; }
a.my-class { background: #0c0; }

Would result in a red background (#f00) because of the points.
In the first selector we get 1 point for a, 10 points for [rel=external], and 10 points for .my-class for a total of 21 points.
In the second selector, we get 1 point for a, and 10 points for .my-class, so 11 points for that selector.

But, the confusion doesn't end there. There are some caveats.
For instance, an ID, even though only containing 100 points, will win out even over a selector with a point value greater than 100 points.

So an ID always has precendence over all other selectors, except if there is another ID in that selector. And an inline style will always win out over an ID, even if there is a 100 id's in the selector.

Now, to pile on the confusion even more, you can give weight toa certain property that will always be obeyed, no matter where the  selector is defined.
You would do that with !important.

Here's how it would work:
body { background: #fff !important; }
body#myId { background: #f00; }

The body would get a background color of #fff, even if there is an inline style, or some other heavy selector. 2 caveats, however.

1. This doesn't work in IE 6.
2. This can cause problems in styling and should only be used in rare cases.

Personally, it's stuff like CSS specificity and inheritance that cause confusion, and keep people from using the full power of CSS to their advantage.

 

 

Average (4 Votes)