The mobile project helps to build multi-device websites on a single infrastructure Sites. It allows for editors to select a single layout (template) for an asset, and gives a developer the control to build device-specific Templates if needed. This can be used in conjunction with Responsive Design techniques.
The basic premise here is that the device specific parts of the website can be grouped at a high level, either for a desktop, a mobile or a tablet. The consequence is that device detection can be grouped into 3 families. The idea is that an editor selects a generic LayoutTemplate and that a developer implements device specific templates, LayoutTemplate_desktop, LayoutTemplate_mobile and LayoutTemplate_tablet. If a device-specific template is not found the original template will be used.
The algorithm used for the device detection is from mobiforge's PHP implementatation. Other implementation might use WURFL or you can roll your own.
In device specific template you can do your device specific code, like
The idea is that within the same editorial site content for specific devices can be managed.
Below is an Action that renders a page for a specific device based on 'normal' GSF dispatching rules. This Action is to be used in a Controller (ie Wrapper/Dispatcher). As you can see the implementation overwrites 3 methods of the RenderPageAction, one is to select to correct translation for the requested assets, and the other 2 to change the editor selected template to a device specific template.
package com.fatwire.gst.foundation.mobile.action; /** * RenderPage action extension that handles translations as well as device * detection to direct the visitor to the device specific template. </p> The * rule is straight forward. If the template is X then a lookup is done if there * is a Template with the name X_mobile and that is used if the visitor is using * a mobile device. The same for _desktop and _tablet. If such a template does * not exist, the 'normal' template is used. </p> * * * @author Dolf Dijkstra * */ public class DeviceAwareRenderPageAction extends RenderPage { @InjectForRequest public DeviceDetector detector; @InjectForRequest public LocaleService localeService; @Override protected AssetIdWithSite resolveAssetId() { return findTranslation(super.resolveAssetId()); } @Override protected void callTemplate(AssetIdWithSite id, String tname) { DeviceType type = detector.detectDeviceType(ics); if (LOG.isDebugEnabled()) LOG.debug("detected device type: " + type); String dtname = checkForDeviceTName(id, tname, type); super.callTemplate(id, dtname); } @Override protected void callPage(AssetIdWithSite id, String pagename, String packedArgs) { DeviceType type = detector.detectDeviceType(ics); if (LOG.isDebugEnabled()) LOG.debug("detected device type: " + type); String nn = checkForDevicePagename(id, pagename, type); super.callPage(id, nn, packedArgs); } protected AssetIdWithSite findTranslation(AssetIdWithSite id) { AssetIdWithSite n = id; DimensionFilterInstance df = localeService.getDimensionFilter(id.getSite()); if (df != null) { AssetId translated = localeService.findTranslation(id, df); if (translated != null) n = new AssetIdWithSite(translated, id.getSite()); } return n; } protected String getPostfix(DeviceType type) { switch (type) { case MOBILE: return "_mobile"; case TABLET: return "_tablet"; default: return "_desktop"; } } protected String checkForDeviceTName(AssetIdWithSite id, String tname, DeviceType type) { if (StringUtils.endsWith(tname, "_mobile") || StringUtils.endsWith(tname, "_tablet") || StringUtils.endsWith(tname, "_desktop")) { return tname; } String pf = getPostfix(type); final String targetPagename = tname.startsWith("/") ? (id.getSite() + tname + pf) : (id.getSite() + "/" + id.getType() + "/" + tname + pf); try { if (ics.getPageData(targetPagename).isRegistered()) { return tname + pf; } else if (LOG.isTraceEnabled()) { log.trace("There is no special template for " + type + " at template " + tname); } } catch (IllegalArgumentException e) { LOG.warn(e.getMessage()); // ignore } return tname; } protected String checkForDevicePagename(AssetIdWithSite id, String pagename, DeviceType type) { if (StringUtils.endsWith(pagename, "_mobile") || StringUtils.endsWith(pagename, "_tablet") || StringUtils.endsWith(pagename, "_desktop")) { return pagename; } String pf = getPostfix(type); final String targetPagename = pagename + pf; try { if (ics.getPageData(targetPagename).isRegistered()) { return targetPagename; } else if (LOG.isTraceEnabled()) { log.trace("There is no device specific pagename for " + type + " at page " + pagename); } } catch (NullPointerException e) { // ignore } catch (IllegalArgumentException e) { LOG.warn(e.getMessage()); // ignore } return pagename; } }
The device detection service needs to be added to the ObjectFactory chain. This is done by adding (or modifying) the file WEB-INF/gsf-groovy/gsf/ObjectFactory.groovy with the content below.
package gsf import COM.FutureTense.Interfaces.ICS import com.fatwire.gst.foundation.controller.action.Factory import com.fatwire.gst.foundation.mobile.DeviceDetector import com.fatwire.gst.foundation.mobile.mobiforge.MobiForgeDeviceDetector import com.fatwire.gst.foundation.controller.annotation.ServiceProducer public class ObjectFactory { @ServiceProducer(cache = true) static DeviceDetector createDeviceDetector(ICS ics, Factory f){ return new MobiForgeDeviceDetector() } }