12/16/2010

How to access Liferays connection tables like Users_Orgs

Ok, this is something that has been bugging me for weeks :


How do you create a DynamicQuery that is able to access all the n- to m tables that Liferay offers ?
Tables like Users_Orgs or Users_Roles ?  Normally you would have to retrieve the User and then retrieve the Orgs by calling a method on that user that uses a Finder which has some custom sql and ... well: it´s not very performant. You have to do it for every individual user, you´re out of your DynamicQuery and your application gets slower.


You could write some custom sql, or you might do the follwing:


Use the Liferay Service Builder to access the tables, Liferay hides from you.


Create a new service.xml or add the following to an already existing one :



<entity name="Users_Orgs" local-service="true" remote-service="false">
<column name="userId" type="long" primary="true" />
<column name="organizationId" type="long" />
</entity>

Your name has to match the table you want to access. Then generate your service, and Liferay creates a Users_Orgs class for you. This you can use to access the no-longer hidden table by a DynamicQuery:



DynamicQuery blogsQuery = DynamicQueryFactoryUtil.forClass( Users_Orgs.class, PortalClassLoaderUtil.getClassLoader());


On the downside I have to say that there is a reason, that those tables aren´t accessible. So if I were you I´d remove all methods from the newly created Service that writes to that table.



If you liked this tutorial it would be very nice, if you could click on some of the google ads you see on the right side. It helps me run this block and motivates me ;)

If you have any questions, feel free to leave a comment.

12/14/2010

ICEFaces and scrollable tables ... especially in Liferay

Problem : If you add the attribute  scrollable="true" to an ICEFaces dataTable, ICEFaces can´t align the header width and the width of the columns.That´s because ICEFaces renders the header and the columns as individual tables and somehow mixes up the widths.

Solution 1:

The first thing you could do is to use fixed wisth: Give the table and the columns the concrete width (in pixels) -the table should have the width of all columns added together. Sadly it is not possible to use procentual values (see tag description).

Solution 2 (Non - Internet Explorer): This works when your table is always rendered at the same size. But what do you do in Portlets, especially in Liferay ? Portlets tend to change their sizes ... fixed widths don´t work here very well.
Style the table to use 100 % of the surrounding container. Then make the table columns veeeery long - longer than the biggest screen size you could expect.  The combination of 100 % and the big pixel values leads in firefox and chrome to the table taking 100 % of the container, but not more. And the table headers are scaled correctly. I am still looking for a way to determine a portlets width ...

Example
<ice:dataTable id="selectedDocuments"
style="width:100%;border-width:0px" columnWidths="900px, 900px;"
value="#{bean.werte}" var="wert"  scrollable="true" resizable="false">

<ice:column style="width: 900px;">
<f:facet name="header">
<ice:outputText value="Title" />
</f:facet>
<ice:outputText value="text" />
</ice:column>
<ice:column style="width: 900px;">
<f:facet name="header">
<ice:outputText value="Titel" />
</f:facet>
<ice:outputText value="text2" />
</ice:column>
</ice:dataTable>



Hint

Of course this is not working for IE6 & IE8 ...


If you liked this tutorial it would be very nice, if you could click on some of the google ads you see on the right side. It helps me run this block and motivates me ;)

If you have any questions, feel free to leave a comment.

12/12/2010

RSS working again

There were some troubles with my RSS feed which relates back to me renaming my blog from "berufsblog" to "liferay-blogging". Anyway, you can now subscribe to my feed again.

Here is the feedburner url : http://feeds.feedburner.com/liferayblogging

12/11/2010

How to create a link to a document in Liferay

One thing I really often had to do was to provide download links to documents.
There were always people who didn´t wanted to browse through the portal.

So ... here is the solution:


public String createDocumentLink(DLFileEntry fileEntry) {


long folderId = fileEntry.getFolderId();
String filename = fileEntry.getName();


StringBuilder builder = new StringBuilder();
builder.append(themeDisplay.getPortalURL());
builder.append(themeDisplay.getPathMain());
builder.append("/document_library/get_file?folderId=");
builder.append(folderId);
builder.append("&name=");
builder.append(filename);


String result = builder.toString();
return result;
}


That´s all :) Now you can directly point to a document you uploaded.


If you like this tutorial it would be very nice, if you could click on some of the google ads you see on the right side. It helps me run this block and motivates me ;)

If you have any questions, feel free to leave a comment.

AlloyUI Tutorial Part 2 : Explaining Hello World and digging deeper

Part 1   Part 2   Part 3   Useful Links   Useful Literature

To really fully understand what happened in Part 1, you need to understand what AlloyUI is.
AlloyUI is built on top of YUI3 (http://developer.yahoo.com/yui/3/) and provides a lot of Widgets to be used in your web applications / portlets. Well ... not only Widgets, but Widgets provide a covenient way to see results very fast.

If you want to know what to do to get the AlloyUI examples to run, you need to understand how Widgets in YUI3 work. The guys at yahoo! have a pretty good page set up which explains the object model and functions all widgets need to use and implement. Please take a look at http://developer.yahoo.com/yui/3/widget/.

By the way : I just found out, that the guys at Liferay also have some absolutely fantastic blogs about AlloyUI that go into another direction then my step - by - step tutorial here. If you want to take a technical dive into AlloyUI, look here: http://www.liferay.com/de/web/nathan.cavanaugh/blog/-/blogs/4717591/maximized

So - what did we do in Part 1 ?
We first created a div and gave it a name. Thats all - nothing but an anchor that we use in our javascript statements. Then we started writing some mysterious AlloyUI code. Let´s review it step by step:

AUI().use('aui-autocomplete', function(A)


What´s this ? We declare that we want to use the AUI function "aui-autocomplete" and register a callback (our function). We now have access to all functions and classes the autocomplete package provides. The "A" allows us to access the autocomplete functions. If we wanted to use more than one package, we could have specified it like this:


AUI().use('packag1', 'package2', function(A)




var people = [


       ['Uncle Bob','Car Repairing'],
       ['Dad','Finance'],
       ['Mum','Cooking'],
       ['Maria','Information Technology']
    ];


This is nothing but POJS (Plain old JavaScript) : A two - dimensional array holding the information we want to proceed.  

var auto  = new A.AutoComplete(

Here we create a new auto complete object and pass parameters to it.We assign it to our div by using the contentBox parameter and render it upon page load. The behaviour of the auto complete widget is controlled by its parameters.

xyz.render();


Render displays the widget - because we didn´t implement restrictions, the widget is displaye upon page load.



So - that´s the end of Part 2 of the tutorial.
You now know how to use Widgets, how to create a callback and how to display widgets.



If you liked this tutorial it would be very nice, if you could click on some of the google ads you see on the right side. It helps me run this block and motivates me ;)

If you have any questions, feel free to leave a comment.

12/10/2010

AlloyUI Tutorial Part 1 : Hello World & First Steps.

Part 1   Part 2   Part 3   Useful Links   Useful Literature

About AlloyUI

What is AlloyUI ? AlloyUI is the new and preferred way to create GUIs and GUI communication in Liferay6.
It offers a rich set of components, ready to be used by you. This allows to create GUIs in Liferay Portlets and Web Contents very fast and reduces the amount of backing bean code you have to program yourself.
It is built on top of a javascript library called YUI3.
If you want to know more, look here :
http://www.liferay.com/de/web/nathan.cavanaugh/blog/-/blogs/alloyui

Setup

How do you get a first AlloyUI example to run?

At first you need Liferay 6 - download the CE at
http://www.liferay.com/de/downloads/liferay-portal/overview.
Then it would be great for you to download the plugins SDK which you can use to easily create a blank portlet.
You can get it here:
http://sourceforge.net/projects/lportal/files/Liferay%20Portal/6.0.5/liferay-plugins-sdk-6.0.5.zip/download

Unzip the portal, unzip the plugins SDK and modify the build.properties to point to your tomcat installation.

Go to your plugins SDKs directory and into the folder "portlets".
Run create.bat "Hello World" hello-world to create your Hello World Portlet.

This all for the setup. Your Portlet is ready to be deployed.

First Steps

Change the liferay-portlet.xml.
Add the required javascript libraries to your portlets xml file like this:


<?xml version="1.0"?>
<!DOCTYPE liferay-portlet-app PUBLIC "-//Liferay//DTD Portlet Application 6.0.0//EN" "http://www.liferay.com/dtd/liferay-portlet-app_6_0_0.dtd">


<liferay-portlet-app>
<portlet>
 <portlet-name>hello-world</portlet-name>
 <icon>/icon.png</icon>
 <instanceable>true</instanceable>
 <header-portlet-css>/html/js/aui/aui-skin-classic/css/aui-skin-classic-all-min.css</header-portlet-css>
 <header-portlet-javascript>/html/js/aui/aui/aui.min.js</header-portlet-javascript>
 <footer-portlet-javascript>/js/main.js</footer-portlet-javascript> 
 <css-class-wrapper>hello-world-portlet</css-class-wrapper>
</portlet>
<role-mapper>
 <role-name>administrator</role-name>
 <role-link>Administrator</role-link>
</role-mapper>
<role-mapper>
 <role-name>guest</role-name>
 <role-link>Guest</role-link>
</role-mapper>
<role-mapper>
 <role-name>power-user</role-name>
 <role-link>Power User</role-link>
</role-mapper>
 <role-mapper>
 <role-name>user</role-name>
 <role-link>User</role-link>
</role-mapper>
</liferay-portlet-app>



Your portlet is now ready to execute some fine AlloyUI code ;)

For our first example, we will use the autocomplete Feature of AlloyUI:

At first, add a div to your portlet:

<div id="autoCompleteField"></div> 

This div we will later address within our AlloyUI code.
Then add the following AlloyUI code:


<script type="text/javascript" charset="utf-8"> 

AUI().use('aui-autocomplete', function(A) {

var people = [['Uncle Bob','Car Repairing'],['Dad','Finance'],['Mum','Cooking'],['Maria','Information Technology']];

var auto = new A.AutoComplete(
{
dataSource: people,schema: {resultFields: ['name', 'job']},matchKey: 'name',delimChar: ',',typeAhead: true,
minQueryLength:'3',contentBox: '#autoCompleteField'}

);
auto.render();
});
</script> 
We will go through it step by step:

At first we will create the data structure, that is used by the autocomplete feature:


[['Uncle Bob','Car Repairing'],
['Dad','Finance'],
['Mum','Cooking'],
['Maria','Information Technology']];

This you can replace later by JSP iteration. For now, we added some names with theier profession.
The next step is to create a new AutoComplete object and to configure it properly:


window.AC = new A.AutoComplete(
{
dataSource: people,
schema: { resultFields: ['name', 'job']},
matchKey: 'name',
delimChar: ',',
typeAhead: true,
minQueryLength:'3',
contentBox: '#autoCompleteField'
}
);
AC.render();

dataSource points to our javascript array.
schema maps the array to the internal autocomplete handling.
matchKey specifies the index the input field listens on.
minQueryLength specifies the length your query must have so that auto completion starts to work.

And that´s all for the first steps ... you now have your first AlloyUI component working:

If you liked this tutorial it would be very nice, if you could click on some of the google ads you see on the right side. It helps me run this block and motivates me ;)

If you have any questions, feel free to leave a comment.

12/09/2010

Tutorial: Creating your own reusable JSF Component (JSF 1.2)

Creating your Own Component


Ok, here is a little tutorial on how to create a reusable component in JSF that you can use with various beans. Not quite as comfortable as with JSF 2.0 but you can almost get the same results.

1) Create a Reference for the taglib

Add the following to your web.xml:

<context-param>
<param-name>facelets.LIBRARIES</param-name>
<param-value>/ui/templates/taglib.xml</param-value>
</context-param>
Now your taglib can be accessed in your application

2) Give the taglib some content to reference your files

Here is an examle entry in the taglib (/ui/templates/taglib.xm) that shows to a component: 
<?xml version="1.0"?>
<!DOCTYPE facelet-taglib PUBLIC
  "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN"
  "facelet-taglib_1_0.dtd">
<facelet-taglib>
<namespace>http://some.namespace.com</namespace>
<tag>
<tag-name>beanComponent</tag-name>
<source>beanComponent.xhtml</source>
</tag>
</facelet-taglib>

If you want to access your component , you can do that by using the namespace  (some.namespace.com) and the tag name (beanComponent).

3) Call your component 

For the call within a facelets file, add the namespace to the list of references name spaces in the header. 
xmlns:some="http://some.namespace.com"
This is a lot easier in JSF 2.0 ... well , anyway. You can access your component now:
<some:beanComponent bean=${backingbean}/>

In this example we pass the backing bean into the reusable component by using a jstl expression. Don´t try to use the JSF standard like #{backingbean}.

4) The component

The component first has the usual header definitions, starts a ui:composition, and then accesses the bean:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:z="http://www.qualcomm.com/jsf/core"
xmlns:c="http://java.sun.com/jstl/core"
xmlns:fn="http://java.sun.com/jsp/jstl/functions">
<ui:composition>
<h:outputText value="#{backingBean.wert}"/>
</ui:composition>
</html>


You can access the bean (as always) by using #{backingBean.wert}. This allows you to use your component with different beans.
5) Extension
If you want to make your component even more flexible and dynamic, you can also pass thenames of methods into your component and access them dynamically. You can do so by creating your component like this:
<some:beanComponent bean=${backingbean} method="wert"/>
... and access it like that:
<h:outputText value="#{backingBean[method]}"/>

You can´t get any more flexible ;)



If you like this tutorial it would be very nice, if you could click on some of the google ads you see on the right side. It helps me run this block and motivates me ;)

If you have any questions, feel free to leave a comment.

12/07/2010

Starting with AlloyUI

I am starting to write down ressources to provide you an understandable Tutorial for ...


AlloyUI !


Stay tuned ... the first content will appear soon !!!

12/04/2010

I am Offering Custom Portlet Development over the Internet

Are you looking for someone, that creates a custom portlet or a JSf application for you ?
Do you just want to "get the job done" as quickly as possible ?
Do you want to save as much money as possible without taking too much risk ?


Then contact me ! I am offering you to develop your portlet / your JSF application / your Java program over the internet, starting at just 99€ for a Liferay Portlet.
To do so I am using the following (simplified) process:


























First step : Send me the specs

I will look over them, think about the time needed to implement it and send a first proposal back to you.

Second Step : Iterative development


I will make a plan for several iterations of development for your custom portlet. After every iteration I will send the work done to you. So you are well informed and you don´t run the risk of paying for something the clearly goes the wrong way


Third Step: Completion


After I sent you the last iteration you will receive everything yuo need to have to let your developers extend the portlet in the future - yuo will get all sources, eclipse projects and documentation.




Are your interested ? Then either leave me a comment or send me a mail to daniel.breitner32@googlemail.com



12/02/2010

How to create a link to a WebContent in Liferay

Problem

You want to have a link to a WebContent in Liferay, to send to a colleague or a friend. This friend should then look at the WebContent without having to navigate through Liferay. And : This should be as flexible as possible, without having to create a WebContent Display Portlet for each created WebContent.

Solution

There are actually two solutions:

1 Linking diretly to the WebContent:

If you link diretly to the WebContent, the WebContent is shown without showing the Portal itself. Just as you would link directly to an html page. This is what you need to do to get the link:

1.1

Add portalURL and pathMain together to create a link to the Server Liferay is running on :



String pathToWebContent = themeDisplay.getPortalURL() +  themeDisplay.getPathMain();

2.1

Add the Path to directly view a WebContent:
pathToWebContent += "/journal/view_article_content?groupId="
3.1

Add GroupId, ArticleId and Version of the WebContent you want to display :
pathToWebContent += journalArticle.getGroupId() + "&articleId=" + journalArticle.getArticleId() + "&version=" +journalArticle.getVersion();
This creates something like the following which you can send by email:


http://localhost:8080/c/journal/view_article_content?groupId=12041&articleId=73245&version=1.0

2 Linking to an asset publisher


If you want to display the WebContent IN the portal, so that the User afterwards can start to explore your portal (maybe you want to lure some customers onto your website) you can do the following :

To summarize it : We deploy an Asset Publisher Portlet to the portal, search this Asset Publisher in our code and pass the Web Content data to it.

2.1 Prepare the portal

At first, create a new site in your portal, that will only take an asset publisher. Then deploy an asset publisher somewhere on that page. This asset publisher will now be the place where all your web contents will be displayed. Enter the Configuration of the asset publisher and change "asset selection" to "manual". That´s all for the asset publisher.
The last thing you have to do is to remember the friendly url that leads to your asset publisher. Look at the status bar of your browser : http://localhost:8080/user/admin/test. In this case it would be "test".

2.2 Navigate to the asset publisher

What your method needs is the friendly url ("test") and the journalArticle (= Web Content) to navigate to the asset publisher. So how do we navigate there ?

First, retrieve all Layouts and inspect the friendlyURL property if it equals the friendly URL you passed into the method:
List<Layout> layouts = LayoutLocalServiceUtil.getLayouts(QueryUtil.ALL_POS, QueryUtil.ALL_POS); 
[...]
 if (friendlyURL.equals("/" + friendlyUrl)) ...
What you then need to do is to retrieve the UnicodeProperties containing the properties you need to inspect for an asset publisher ID. The asset publisher ID is 101 :
UnicodeProperties typeSettingsProperties = layout.getTypeSettingsProperties();
String property= typeSettingsProperties.getProperty("column-2");

If you have this property, you need the instance ID of the asset publisher to build the link correctly:
 String assetId= property.subString(property.indexOf("_INSTANCE_") + 10);
And the last step is to combine all that to the link :
String url = getUserPage() + "/" + friendlyUrl + "/-/asset_publisher/" + assetId+ "/content/" + journalArticle.getUrlTitle();

Thats all :) Now you can see the Web Content in your Portal by clicking on a link.


If you like this tutorial it would be very nice, if you could click on some of the google ads you see on the right side. It helps me run this block and motivates me ;)

If you have any questions, feel free to leave a comment.