About

This site deals with technicalities of web development and content management systems. I also try to touch upon content strategy, ECM, DMS, and other related fields. 

I try to publish something new every two weeks alternating between topics.

Navigation
Social
Monday
May282012

When to Proof-of-Concept, Prototype, or Implement

It should be really clear when to prototype, when to use a proof-of-concept, or when to start building the solution to a problem but unfortunately it's often not clear cut enough when considering the options for the first stages of development.

In order of vagueness-to-precision, the requirements for a Proof-of-Concept are the least defined, and a full system implementation is completely specified. Understanding where your project lies in this spectrum will help you gauge how much effort you need to put into non-coding activities and help manage your expectations when receiving deliverables.

Proof-of-Concept

Commonly known as a PoC, proof-of-concept work should determine if a given approach is appropriate or to find out if integration between two systems makes sense from a technical point-of-view. It can also be used to uncover deficiencies in the architecture or the system design. It's often used as a non-committal way of exploring the capabilities of a system and as such usually lacks unit tests, non-functional criteria, documentation, strict design constraints, visual design, and other aspects required for the creation of a reliable usable system.

Often the PoC work will really be a prototype and not intended to prove that something works. The only thing that should be carried forward from a PoC is library code and tools created to assist in creating these libraries such as code generation tools.

PoC code should usually be discarded at the end.

Prototypes

Prototyping is often used as a mechanism for discovery and quite rightly so. It's use implies that the solution is known but the form it will take is subject to too many uncertainties that the only realistic way forward is to create something with limited functionality to show the way forward.

Very often the prototype will form the bulk of the groundwork for the finished product. Many might argue that this shouldn't happen but in the real world the work done is often too valuable. This is true if the prototype is successful and covers all the needs of the problem at hand. It's also true when the prototype is an extension of an existing product or an implementation using a specific platform where the limitations of the prototype are in fact the constraints of the underlying technology.

Prototypes should be built in the same way as production code, but with less constraints on the developer. They need the freedom to hack and create in the fastest possible way, and yet still create all the secondary outputs that are required such as unit tests, documentation, release notes, correct code styles, and follow the quality assurance guidelines that are in place.

It used to be said that prototypes should be discarded at their end but more often than not they are released as 'beta' products or further developed into complete solutions. Prototypes should be rolled into the final solution if they prove successful.

Implementation

If you already know the majority of the elements of the UI and the solution architecture the most appropriate strategy is to start implementation. This doesn't prevent exploring the technical options for subsections, modules, and components of the system particularly in areas that interface with other systems and for parts of the UI that have been loosely defined.

Obviously final implementation is subject to the most rigorous testing and ratification. It must conform to the requirements but this doesn't preclude that components cannot be developed as prototypes.

Conclusion

If you know a lot, build your solution to a specification and explore the gaps with proof of concept work. If you don't know that something will work, prove it. If you don't know if the parts you intend to use will fit together in a logical way, do a prototype. Don't mix the terminology of prototype and proof-of-concept - one is inherently more rapid to do and is generally unsuitable for production use. Expect to throw a small percentage of prototype code away even if it's successful, and a high percentage of PoC code or face the penalty of adding documentation and unit tests later.

Tuesday
May152012

Multi-page forms in CQ5

Sometimes it's necessary to create a form that has multiple steps. Examples of this might include user registration, surveys, and any kind of form where the selections influence the options available on subsequent pages. Application integration can have predefined sets of steps that are required are risky to leave in the hands of content authors who may miss things out or misinterpret the way they need to be set up.

It's much better in some cases to roll these together and have a single self contained component than worry about training content editors and dealing with the potential for strange configurations that can have unexpected results. The penalty is a reduction in flexibility, but for cases such as user registration there is a minimum of modifications that can be made if the form has to support a service that lives outside of the CMS.

This is a design and sample code for a wizard-style form in CQ5. I'll walk though it and highlight the areas where it could be extended to take better advantage of the facilities in CQ5. The basis of this design is that the form uses HTTP POST requests throughout, and that all fields in the form are always present between requests. If the data is not visible on the current page, they are added to the form as hidden fields. It uses a 'mock MVC' style, with a controller, views, and data modelling aspects.

GET.jsp: the view controller

This JSP script does some basic initialisation of the request attributes in order to prevent 'null' appearing in the form (unless null was deliberately typed in), and to select the first page in the wizard if this is the first access to the page. Finally, it selects the view script to use based on the value of 'displayPage'. There are two variants - an input form and a summary - which it can use. To illustrate the view logic I've used a simple string contains call to use the 'input.jsp' script if the display page attribute contains 'input', otherwise display the summary.

<%@page import="java.util.List" %><%
%><%@page import="com.antonyh.multipageform.*" %><%
%><%@include file="/libs/foundation/global.jsp"%>
<%
	//init; set first page
	if (request.getAttribute("multipageform/components/sample/form/displayPage") == null) {
		request.setAttribute("multipageform/components/sample/form/displayPage", "inputPage1");
	}

	//init; set field defaults
	List fields = com.antonyh.multipageform.SampleUtil.getFieldNames();
	for (FormField field: fields) {
		if (request.getAttribute("multipageform/components/sample/form/" + field.getName()) == null) {
			request.setAttribute("multipageform/components/sample/form/" + field.getName(), "");
		}
	}

	//-------
	//very simple display logic. If it's an 'input' page use the input script, otherwise display a summary.
	//extend this as needed.
	
	String displayPage = (String) request.getAttribute("multipageform/components/sample/form/displayPage");
	if (displayPage == null || displayPage.startsWith("input")) {
%>

<%
	} else /*if (displayPage.equals("summary"))*/ {
%>

<%
	}
%>

This script would be extended if there are more types of view or if additional components are needed under certain circumstances. It would be trivial to include additional code branches here or to load extra data.

The Utility Class: the data model

For the sake of this demo, I've implemented this as a static utility class with some POJO objects to define the data model. The utility class can get all the pages in the form, all the fields in all the form pages, and a static text field with a label for the form. It sets up the data using a static block.

 

package com.antonyh.multipageform;

import java.util.ArrayList;
import java.util.List;

/**
 * Utility class used by script.
 * 
 */
public class SampleUtil {

	//example only; this wouldn't be held in the object in a real application
	static final List FIELDNAMES = new ArrayList();

	//example only; this wouldn't be held in the object in a real application
	static final List FORMPAGES = new ArrayList();
	
	
	/* 
	 *  This block initialises the lists. In reality this would source data 
	 *  from webservices and/or CRX.
	 * 
	 */
	static{
		
		List fields1 = new ArrayList();
		fields1.add(new FormField("FieldA1"));
		FORMPAGES.add( new FormPage("inputPage1", "inputPage2", fields1));
		
		List fields2 = new ArrayList();
		fields2.add(new FormField("FieldB1"));
		FORMPAGES.add(new FormPage("inputPage2", "inputPage3", fields2));
		
		List fields3 = new ArrayList();
		fields3.add(new FormField("FieldC1"));
		FORMPAGES.add(new FormPage("inputPage3", "summary", fields3));

		for(FormPage formPage: FORMPAGES){
			FIELDNAMES.addAll(formPage.getFields());
		}
		
	}
	
	
	public static String getText() {
		return "Form Example";
	}

	
	public static List getFieldNames() {
		ArrayList x = new ArrayList();
		x.addAll(FIELDNAMES); 
		return x;
	}

	
	public static List getFormPages() {
		ArrayList x = new ArrayList();
		x.addAll(FORMPAGES); 
		return x;
	}

} 

 

In a real world example this would be changed to an OSGi service and draw data dynamically from external sources or other OSGi services. It might be used to get information from the component configuration if there are editable sections on the form. The number of pages and their contents could be managed through the author interface with a service to obtain the settings and return them to the calling code.

The Servlet: the post controller

The servlet accepts all POST requests with a resource type of 'multipageform/components/sample'. This is in the ':formtype' hidden field in the form. It works by taking all the submitted form fields except those that start with ':' and adding them to the request parameters. The Sling default POST servlet uses parameters prefixed with a colon as control values; these include :redirect, :formtype, and :formpath. We remove them because we don't need to store them as they are already in our scripts.

Once it has stored the field values for use by the next view script, it works out the action it needs to take. In the example there are two actions, 'back' and 'next'. The back button submits the form with a value 'submit=back' and the servlet looks up the previous page in the sequence by finding the page with a matching next page. It sets this value as the display page and issues a redirect. Conversely, for the 'next' action, it finds the current page and sets the display page to that pages next page value before redirecting.

package com.antonyh.multipageform.impl;

import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.api.servlets.OptingServlet;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.List;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.apache.sling.api.resource.Resource;

import com.antonyh.multipageform.FormPage;

@Service
@Component
public class MultipageFormSampleServlet extends SlingAllMethodsServlet implements OptingServlet, Filter {

	static final long serialVersionUID = 1l;

	protected static final Logger log = LoggerFactory.getLogger(MultipageFormSampleServlet.class);

	@Property(value = "multipageform/components/sample")
	static final String SLING_SERVLET_RESOURCETYPES = "sling.servlet.resourceTypes";

	@Property(value = "POST")
	static final String SLING_SERVLET_METHODS = "sling.servlet.methods";

	@Property(value = "request", propertyPrivate = true)
	static final String FILTER_SCOPE = "filter.scope";

	@Property(value = "-600", propertyPrivate = true)
	static final String FILTER_ORDER = "filter.order";

	static final String ATTR_RESOURCE = "multipageform/components/sample/resource";

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.apache.sling.api.servlets.SlingAllMethodsServlet#doPost(org.apache
	 * .sling.api.SlingHttpServletRequest,
	 * org.apache.sling.api.SlingHttpServletResponse)
	 */
	protected void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException, IOException {

		log.debug("POST into MultipageFormSampleServlet.");

		//put all the field values in the request attributes
		java.util.Enumeration fields = request.getParameterNames();
		while (fields.hasMoreElements()) {
			String field = (String) fields.nextElement();
			if (field.startsWith(":")) {
				continue; //skip parameters that start with a ':' - we don't need to keep these
			}

			request.setAttribute("multipageform/components/sample/form/" + field, request.getParameter(field));
		}

	 	List formPages = com.antonyh.multipageform.SampleUtil.getFormPages();
		
	 	
	 	//display page logic. defaults to first page in the list
		String displayPage = formPages.iterator().next().getPageName(); 
		if (request.getParameter("displayPage") != null) {
			displayPage = request.getParameter("displayPage");
		}
		if (displayPage.equals("null")) {
			displayPage = formPages.iterator().next().getPageName(); 
		}
		
		
		//Back & forward button logic
		
		if (request.getParameter("back") != null) {
			for(FormPage formPage : formPages){
				if (displayPage.equals(formPage.getNextPageName())) {
					displayPage=formPage.getPageName();
					break;
				}
			}
		}else if (request.getParameter("next") != null){
			for(FormPage formPage : formPages){
				if (displayPage.equals(formPage.getPageName())) {
					displayPage=formPage.getNextPageName();
					break;
				}
			}
		}
		
		request.setAttribute("multipageform/components/sample/form/displayPage", displayPage);
	

		// take user back to the form page.
		final Resource rsrc = (Resource) request.getAttribute(ATTR_RESOURCE);
		request.removeAttribute(ATTR_RESOURCE);
		SlingHttpServletRequest formsRequest = new FormsHandlingRequest(request);
		request.getRequestDispatcher(rsrc).forward(formsRequest, response);
		return;

	}

	
	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.apache.sling.api.servlets.OptingServlet#accepts(org.apache.sling.
	 * api.SlingHttpServletRequest)
	 */
	public boolean accepts(SlingHttpServletRequest request) {
		//only accept .html requests
		return "html".equals(request.getRequestPathInfo().getExtension());
	}

	
	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,
	 * javax.servlet.ServletResponse, javax.servlet.FilterChain)
	 */
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

		if (request instanceof SlingHttpServletRequest) {
			final SlingHttpServletRequest req = (SlingHttpServletRequest) request;

			if ("POST".equalsIgnoreCase(req.getMethod()) && req.getParameter(":formtype") != null && req.getParameter(":formtype").equals("multipageform/components/sample")) {

				// store original resource as request attribute
				req.setAttribute(ATTR_RESOURCE, req.getResource());
				final StringBuilder sb = new StringBuilder();

				// forward to the path where the form component is, so that it
				// matches the "sling.servlet.resourceTypes" property define at
				// the beginning of this class. As a result, the doPost() method
				// is executed.
				final String formPath = req.getParameter(":formpath");
				if (!formPath.startsWith("/")) {
					sb.append(req.getResource().getPath());
					sb.append('/');
				}
				sb.append(formPath);
				sb.append(".html");

				// forward to forms handling servlet
				final String forwardPath = sb.toString();
				req.getRequestDispatcher(forwardPath).forward(request, response);

				return;
			}
		}

		chain.doFilter(request, response);
	}

	
	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
	 */
	public void init(FilterConfig arg0) throws ServletException {
		// no init required.
	}

}

I've opted to use a copy of the 'FormsHandlingRequest' class to convert the request object back to a GET.

The servlet is the main controller for the form. Validation should be placed here as it affects the flow of the application. It would also need to be extended to take an action at the end of the sequence such as sending an email, saving a node, or submitting the data to a webservice.

input.jsp: the main view

The basic form view.

<%@page import="com.antonyh.multipageform.*"%>
<%@page import="java.util.List"%>
<%@include file="/libs/foundation/global.jsp"%>
<%@taglib prefix="sling" uri="http://sling.apache.org/taglibs/sling/1.0"%>
<sling:defineObjects />
<%@ page contentType="text/html;charset=UTF-8"%><%
    String displayPage = (String) request.getAttribute("multipageform/components/sample/form/displayPage");
    //get form defintions
    List<FormField> allfields = com.antonyh.multipageform.SampleUtil.getFieldNames();
    List<FormPage> formPages = com.antonyh.multipageform.SampleUtil.getFormPages();
    //get the form object for the current form page
    FormPage formPage = formPages.iterator().next();
    for (FormPage formPageObj: formPages) {
        if (formPageObj.getPageName().equals(displayPage)){
            formPage = formPageObj;
            break;
        }
    }
 %>
<div><%=com.antonyh.multipageform.SampleUtil.getText()%>  <%=displayPage%>
<form name="sampleform" id="sampleform" action="<%=resourceResolver.map(currentPage.getPath())%>.html" method="POST" enctype="multipart/form-data">
<input type="hidden" name=":formpath" value="<%=currentNode.getPath()%>" /> 
<input type="hidden" name=":formtype" value="<%=component.getResourceType()%>" /> 
<input type="hidden" name=":redirect" value="<%=resourceResolver.map(currentPage.getPath())%>.html" /> 
<input type="hidden" name="_charset_" value="utf-8" /> 
<input type="hidden" name="displayPage" value="<%=displayPage%>" /> 
<%
  //hide fields not on this page.
  //show fields on this page.
  //all fields are always present.
  allfields.removeAll(formPage.getFields());
  for (FormField field: formPage.getFields()) {
%> Field <%=field.getName()%> <input type="text" name="<%=field.getName()%>" required="required"
value="<%=request.getAttribute("multipageform/components/sample/form/" + field.getName())%>" /><br /><%
}
    for (FormField field: allfields) {
%><input type="hidden" name="<%=field.getName()%>"
   value="<%=request.getAttribute("multipageform/components/sample/form/" + field.getName())%>" /><%
   }  
%> 
<input type="submit" name="back" value="back" /> 
<input type="submit" name="next" value="next" />
</form>
</div>

This does contain some aspects of logic that could easily be moved into Java code, but in general should be fairly self-explanatory.

summary.jsp: a secondary view

A summary view that is not editable. This view is intended as a demonstration of how to add additional scripts with different display and without a next button. In the example, it is shown at the end of the sequence.

<%@page import="com.antonyh.multipageform.*"%>
<%@page import="java.util.List"%>
<%@include file="/libs/foundation/global.jsp" %>
<%@taglib prefix="sling" uri="http://sling.apache.org/taglibs/sling/1.0"%>
<sling:defineObjects/>
<%@ page contentType="text/html;charset=UTF-8" %><%
  List<FormField> fields = com.antonyh.multipageform.SampleUtil.getFieldNames();
  %>
<div>
  <%= com.antonyh.multipageform.SampleUtil.getText() %> SUMMARY

<form name="sampleform" id="sampleform" action="<%=resourceResolver.map(currentPage.getPath())%>.html" method="POST" enctype="multipart/form-data">
    <input type="hidden" name=":formpath" value="<%= currentNode.getPath() %>" />
    <input type="hidden" name=":formtype" value="<%= component.getResourceType() %>" />
    <input type="hidden" name=":redirect" value="<%= resourceResolver.map(currentPage.getPath())%>.thankyou.html" />
    <input type="hidden" name="_charset_" value="utf-8" />
    
    <input type="hidden" name="displayPage" value="<%=request.getAttribute("multipageform/components/sample/form/displayPage") %>"/>

<%

//simple display of the values as a summary
//use hidden fields to keep the data for use by the back button
for (FormField field: fields) { 
%>
     Field : 
     <input type="hidden" name="<%=field.getName() %>" value="<%=request.getAttribute("multipageform/components/sample/form/" + field.getName() ) %>"/>
    <%=request.getAttribute("multipageform/components/sample/form/" + field.getName() ) %><br/>
<%
} 
%>

<input type="submit" name="back" value="back"/>
   
  </form>
</div> 

This is almost the same as the input.jsp, except that it is able to make the assumption that all the field data is in hidden fields. It needs to have the form and all the fields so that the back button is used - if this was a final page from which it was not possible to go back it wouldn't need this.

Conclusion

The form is sent via a POST request to a servlet. This stores the values in request attributes, and forwards the request back to the form. There is a view controller in the component called GET.jsp that does some basic work and includes a view script depending on the page.

This code was built in CQ5.4 but is expected to work in CQ5.5. There is a lot of things that could go into improving this before using it in a production application. It does demonstrate one approach to implement a  multi-page form wizard in CQ5. It needs a lot more work to create a working form especially if it is complex or needs a lot of flexibility but it's a good starting point for when a programatic solution is required.

The display of the source here isn't very useful or clever but might save you some time. If you need the code, you can download the source as a CQ5 package from http://antonyh.co.uk/storage/cq5/packages/multipage-form.zip

Monday
May142012

No More Analytics

I'm dropping Google Analytics from all my web properties. Why? It's partly because of the UK privacy legislation and partly because the metrics they give are not really useful to me.

Cookies and Privacy

The short version of the UK regulations that will come into play on the 26th May 2012 is that users need to explicitly opt-in to cookies that are not an essential part of the functionality of the site. Rather than waste my time trying to make my web site comply with the law I have opted to drop all cookie-based analytics.

More Useful Metrics

I love looking at the nice graphs the people at Google make for me that track visitors to my sites. Unfortunately that it's really of any practical use for my non-ecommerce sites. There are only two key metrics that are really useful for me: 

  1. Number of comments on pages
  2. Frequency of contact form submission

These are reinforced by two off-web events that occur as a result of the websites: 

  1. Phone calls
  2. Email received as a result of my website  

This is followed up with secondary results that can also be monitored:

  1. New followers, mentions, and direct messages on Twitter
  2. New connections and messages via LinkedIn

None of these are monitorable using traditional web analytics yet in many ways they are a more tangible result.

Monday
Apr302012

Java CMS Roundup

I'm looking for a new CMS to replace my company site to replace Google Sites hosting that I currently use. I decided that it needs to be Java JDK1.6 compatible, use a Postgresql 9 database, and run on Tomcat 7. Smaller memory footprints are preferred, with a target maximum of 1gb ram for the cloud server hosted with Rackspace here in London.

I also wanted a good digital asset feature in the product so that I could make it more visual and introduce more style into the site. I looked into a number of possible products, all open source and all free to use.

dotCMS

At the time of writing dotCMS is previewing version 2. 

Pros

  • It looks good. The demo of version 2 plays very well on their site.
  • It's open source, free to use and licenced under GPL v2.
  • Tomcat is supported, as is PostgreSQL.
  • It has multi-tenent hosting, so several sites can be hosted completely separately on the same server.

Cons

  • I tried and tried and tried to install this into a vanilla Tomcat 7 server without any success. I even tried to use Tomcat 6 and it still doesn't run. It turns out the only way of launching it is to use the bundled version of Tomcat.
  • The image editor is only available with the Enterprise edition.

I gave up after spending far too long attempting to get this to work properly. Even with the stable v1.9 version running with the modified Tomcat it never successfully completely started.

Magnolia CMS

I've used Magnolia before. It's currently on version 4.5.

Pros

  • It's open source, free to use and licenced under GPL v3.
  • It all seems to make sense. Create a page, edit a page, publish a page. 
  • The standard templating kit (STK) gives some nice components that work well.
  • It uses an installer, but the WAR files can be used to redeploy it elsewhere.
  • As standard, it's possible to create an intelligent, good looking site really quickly.

Cons

  • The image editor was only available with the Enterprise edition with v4.4. Version 4.5 has this included in the community edition.
  • I want to try something different, something I've not used before.
  • Currently it relies too much on right-click actions, making it unusable on tablet devices.

Magnolia is a solid reliable CMS that I have no hesitation in recommending. In general it's a good product.

Jahia xCM

For some reason, Jahia isn't listed as a CMS on Wikipedia. I tried verson 6.6.0.

Pros

  • Jahia Studio allows development through a web interface.
  • It's got a great feature list and a very good demo.

Cons

  • Jahia Studio is complex and hard to use. There is no obvious starting point.
  • It's GPL v2; but not for commercial use.

The lack of affordable licensing for commercial use eliminates this as an option.

Weceem

Version 1.1.2 of Weceem is built on Java, Groovy, Grails, and Spring. It shows some promise, but it's ready to use.

Pros

  • It's really lightweight.
  • It's open source, and under the Apache 2 Licence.
  • It appears to be highly extensible
  • A little fiddly to set up initially, but fairly simple. Packaged as a WAR file but it needs an external configuration file to start up.
  • The documentation is lovely and light. There's not much but it cuts to the chase.

Cons

  • It's very rough around the edges.
  • It has the worst demo of all the CMS I have looked at
  • I'm not convinced that it deserves the v1.1 moniker - it feels more like a pre-release
  • It has some questionable UI elements; most notably there are features which lead to pages that state that they haven't been implemented yet.
  • Some of the documentation is already out of date.

I'm not convinced that this is mature enough for my taste. I would certainly struggle to recommend it except perhaps to developers who like to hack together solutions. Weceem is embeddable into other applications and this is where it's real value lies.

Hippo CMS

Hippo CMS community edition is version 7.7. Out of the box, this product offers the most.

Pros

  • Open source, Apache 2 licence
  • Includes all the major features I will need
  • It's context aware, so I can create structured content and reuse it
  • It appears to have great media handling with a good image editor
  • Installation was simple; install two WAR files and some JAR files.

Cons

  • I have to fill in a form to download. Why?
  • As it's icon based it is less than intuitive to use
  • The structuring of the content by context also adds a learning curve
  • No obvious way of removing all demo content, nor does the demo yield many clues of how to create a new site

This is a very good product, but it might be too much for needs of a site with a single author.

OpenCMS

Despite being forced to register for a demo, I looked at version 8 of OpenCMS.

Pros

  • It's FOSS.
  • The menus pop up on left click, so it stands a chance of working on a tablet.

Cons

  • I hate being asked for contact information just to demo a free product.
  • It's designed to look like something from 1995. Bleugh.
  • The demo is as bad as the admin screens, really ugly and tired.

I suspect that even if I could live with the ancient design of the admin screens I would struggle to make the output of the site look good.

Other choices

Flexive was rejected purely on the basis of horrific green icons all over the authoring and admin screen. It really is that bad.

RiotFamily was rejected as although it is beautiflly executed, it seems to have no navigation management, UI to create new pages, or image management features. It seems to be more of a page editor.

Alfresco & Nuxeo are document management or case management systems. Although they have the ability, it would be a strange choice to abuse them to create a website. 

Disclaimer

Although I am not affiliated with the product vendor, my daily work is with Adobe Day CQ5. Unfortunately there is no free licence available for this product making it unsuitable to power the website for my own company.

Monday
Apr162012

Web Development Options for Mobile and Desktop

There are several options when developing web sites and applications that will need to be used on mobile devices. Here are some options.

HTML, CSS, & JavaScript

This is the easy way to make websites that will work on both mobile and desktop. Websites are normally made from using these technologies.

Pros; It should work on all modern browsers. You can reuse a lot of existing code. There is a lot of information on this. It's stable and predictable. Any changes you make to the HTML are reflected instantly. It's compatible with other services like Instapaper, and can be sent as links via email or tweeted.

Cons; You can't sense device rotation, take action on certain gestures, or access camera or other aspects of mobile devices. It is what it is.

Native Applications

This is an obvious choice, it gives you more control over the application in theory. 

Pros; it's native. Access to APIs that are not normally available. It's a great choice if you want certain features or need to do high performance things like 3D.

Cons; You have to build at least three or four versions if you want to cover all the mainstream platforms, all of which use different frameworks and languages. You are at the mercy of app store regulations. People like me won't install if you get the permission requirements wrong. Star ratings suck, especially if it doesn't work as expected (which isn't the same as broken). Development time may be longer. You have to manage things like memory allocation carefully. You will also have challenges with display sizes and shapes.

Sencha Touch

Pros; it's JavaScript. Fairly quick ramp-up and shallow learning curve. There's plenty of resources online, and a few printed books too. It seems mature and stable.

Cons; I found it ugly and hard to make it work correctly with changes in device orientation. It's not a fault with the framework, but with my approach to it. It's a framework applied to HTML. It doesn't work on desktop browsers as it takes some liberties assuming it will be used on a WebKit browser. Code-wise it's a culture shock if you are used to using other JavaScript libraries such as jQuery; it's very different.

jQuery Mobile

I've not tried this one yet and it seems like a great option. It's been claimed that because it is based on jQuery it is instantly familiar and so very easy to use if you have already used jQuery for desktop browsers. Again, there are lots of great resources for jQuery.

Phone Gap

This seems like a neat idea. It compiles HTML, CSS, and JS into native applications.

Pros; one source for seven targets. Consistency across platforms. Reduced development time and cost. It can leverage jQuery or Sencha Touch as the layout framework to add serious power and further cut time by reusing existing work. It can be marketted as a native application.

Cons; Does it really work as well as they claim? What strange hoops do I need to jump through to achieve X? It's still at the mercy of app store approval. How fine grained is the permission control for Android? What older versions of devices does it support?

Conclusion

Every project is different, so pick the style that fits you.