Last week I had to change browse.jsp view in alfresco and I need to add custom columns – attributes from my own data model. It’s pretty simple and I’ll explain that in this blogpost.

I tried to do is as much simple as possible, so I removed all other view types but details and in this view I added some boolean properties, which have own action handler – so are clickable and after click change their state. Result of this changes is on following screenshot.

How does it look like

How does it look like

You can see I added two boolean properties (in Czech – Ano = true, Ne = false) among others (Type). To do that I needed to override BrowseBean and create own NodePropertyResolver. Now from the begining:

  • I created custom document model,
  • wrote custom browse.jsp (just copied and edited original one),
  • overrode outcomes to new view,
  • overwrote BrowseBean and wrote custom NodePropertyResolver.

Creating custom document model is well described everywhere else (at least in alfresco wiki), so I’m not going to describe it here, also overriding views. Lets start with altering browse.jsp.

Items in list are created by RichList component, in which we are interested in just some attributes: viewMode and value. ViewMode is a String value (details, icons, list) with default view mode. When I tried to change ViewMode value to details, it doesn’t work correctly, so I had to override getBrowseViewMode() to return „details“.

public class BrowseTeletextBean extends BrowseBean {
 . . .
private NodePropertyResolver pageExpiredResolver = new BooleanNodePropertyResolver(TxtModel.txtExpired, getNodeService());
 . . .
@Override
public String getBrowseViewMode(){
	return "details";
}

@Override
public List<Node> getContent() {
	List<Node> list = super.getContent();

	for(Node node : list){
		node.addPropertyResolver("pageExpired", pageExpiredResolver);
		 . . .
	}

	return list;
}
 . . .

Value should return list of Node type, in which I wanted to add custom fields, so I needed to override it too. I put there some propertyResolvers – I did my own BooleanPropertyResolver:

public class BooleanNodePropertyResolver implements NodePropertyResolver {
	private NodeService nodeService;
	private QName qName;

	public BooleanNodePropertyResolver(QName qName, NodeService nodeService){
		this.qName = qName;
		this.nodeService = nodeService;
	}

	@Override
	public Object get(Node node) {
		boolean expired = NodeUtils.getBoolProperty(node.getNodeRef(), qName, nodeService);
		return (expired ? "Ano" : "Ne");
	}
}

Now it’s possible to build a list in view like this:

<a:richList id="contentRichList" binding="#{BrowseTeletextBean.contentRichList}" viewMode="#{BrowseTeletextBean.browseViewMode}" pageSize="#{BrowseTeletextBean.pageSizeContent}" styleClass="recordSet" headerStyleClass="recordSetHeader" rowStyleClass="recordSetRow" altRowStyleClass="recordSetRowAlt" width="100%" value="#{BrowseTeletextBean.content}" refreshOnBind="true" var="r">
 . . .
  <a:column id="col14" style="text-align:left">
      <f:facet name="header">
          <a:sortLink id="col14-sort" label="Expired" value="expired" styleClass="header"/>
      </f:facet>
      <a:actionLink id="col14-act1" value="#{r.pageExpired}" actionListener="#{BrowseTeletextBean.clickPageExpired}">
          <f:param name="id" value="#{r.id}" id="param14-1" />
      </a:actionLink>
  </a:column>
 . . .

Here you can see actionLink with value defined by custom NodePropertyResolver (r.pageExpired), which renders either „Ano“ or „Ne“ based on value in TxtModel.txtExpired (QName) property in model.

public static final QName txtExpired = QName.createQName("http://www.alfresco.org/model/txt/1.0", "expired");

ActionLink has an action handler – actionListener parameter – this handler just change value of the expired property and is as simple as this:

public void clickPageExpired(ActionEvent event){
    NodeRef nodeRef = getNodeRefFromParams(event);
    if(nodeRef != null) {
        boolean status = NodeUtils.getBoolProperty(nodeRef, TxtModel.txtExpired, getNodeService());
        getNodeService().setProperty(nodeRef, qName, !status);
        UIContextService.getInstance(FacesContext.getCurrentInstance()).notifyBeans();
    }
}

Note that UIContext…notifyBeans() is needed to notify beans, that some change had been done and we need to refresh view. For this refresh also refreshOnBind parameter on richList is needed to be true.

It was easy to put it together, but like everything in Alfresco – badly documented. So I hope this post help someone. Feel free to ask questions, if something is not clear enough ;-).