Prerequisite knowledge
 
This article assumes knowledge of JavaScript, HTML and XML and Adobe Digital Publishing Suite.
 
User level: Intermediate
 
Required products
 
Additional required other products (third-party/labs/open source)
 
Sample files
 
By downloading software from the Adobe Web site you agree to the terms of our license agreement. Please read it before downloading.
 
 
 
 
A Digital Publishing Suite viewer application optionally can contain a number of custom navigation slots beyond the standard "Library" and "Viewer". These slots implement webviews for rendering of HTML5, CSS3, and JavaScript. Each of these slots has its DOM modified at run time to provide a bridge to services exposed by the application. These services are detailed in the JavaScript Viewer API for Digital Publishing Suite. By using these services, a publisher can create a custom, branded and highly designed merchandising view to present folio or other digital content for sale. The webpage is hosted by the publisher and loaded at runtime allowing the use of server technologies (PHP, JSP, CMS). Publishers are free to create any visual design or technical functionality of their choosing; however, for purposes of this article we assume a merchandising view of Digital Publishing Suite folio content available for in app purchase.
 
This article will cover the basics for creating a custom storefront.
 
Note: Only Digital Publishing Suite Enterprise Licensees can add Custom Navigation slots.
 
 
 
Custom Stores can include animated banners to capture and retain visitor attention.
Figure 1: Custom Stores can include animated banners to capture and retain visitor attention.
 
 
Figure 2: Publishers can include cross-sell or upsell offers, like a subscription call-to-action, to drive incremental revenue.
Figure 2: Publishers can include cross-sell or upsell offers, like a subscription call-to-action, to drive incremental revenue.
 
 

 
Getting started

You implement custom storefront code using HTML5, CSS3, and Javascript in conjunction with the Javascript Viewer APIs for DPS. You package your custom storefront with your application when you create a viewer application using Viewer Builder. Specifically, you provide an archive (zip) file of the code that implements your storefront as the "Icon xxx HTML Resources ZIP".
 
  1. Your archive must contain an index.html file. This will be the top level of your store.
  2. Your archive should be flat. All files should be in the same folder.
  3. Only include files that are unlikely to change. Load all others at runtime through HTTP.
 
Initialization
Your code can only begin using the JavaScript Viewer APIs after receiving the onadobedpscontextloaded event from the window object. To ensure that no method calls are used before they should be, postpone any initialization until this event is received.
 
$(document).ready(function() { var init = function() { // Define a function in case the main JavaScript file has not loaded yet. if (!window.onnetworkconnection) { window.onnetworkconnection = function() { window.connected = true; } } } if (navigator.userAgent.toLowerCase().indexOf("ipad") == -1) { // On desktop so call init() immediately. init(); } else { window.onadobedpscontextloaded = init; } });
The above code initializes based upon whether the code is executing within the iPad or within a desktop browser (Safari on a Mac). The latter being very useful for development purposes. After init() has been executed, it is safe to begin store operations.
 
 
Existing folios
Check for the presence of existing folios using the getFolioData() and updateLibrary() methods. The getFolioData() retrieves an array of folio descriptors known to the Viewer's library. Valid folioState values:
 
folioState value
 
Description
 
0
 
Invalid
 
50
 
Unavailable. The folio is not yet available for purchase
 
100
 
Purchasable. Can be purchased and downloaded
 
101
 
Purchasing. There is an active or paused Purchase Transaction for the folio
 
200
 
Downloadable. The folio is free, or its Purchase Transaction completed
 
201
 
Downloading. There is an active or paused Download Transaction for the folio.
 
400
 
Extractable. The folio content is stored locally.
 
401
 
Extracting. There is an active or pause Extraction Transaction for the folio.
 
1000
 
Viewable. The folio is can be loaded in the viewer.
 
1500
 
Update available. The folio is viewable but can be updated.
 
1501
 
Downloading update. There is an active update download for this folio
 
1502
 
Update extractable. The folio is viewable but has an update that can be installed.
 
1503
 
Extracting update. There is an active update extraction for this folio.
 
 
Buying (or viewing) folios
There are two methods for initiating the purchase: download and/or viewing of a folio.
 
Method
 
Description
 
buyFolio
 
Initiates the eCommerce with the AppStore to begin purchase of a folio. Actual purchasing is all handled by the viewer, AppStore and iOS. Once complete, the application will present it's Library view and the folio will start downloading.
 
viewFolio
 
Initiates the download and/or viewing of a Folio. If the folio has been published as FREE, then it is downloaded. Once the folio has been downloaded, calling viewFolio() will open the folio for viewing within the viewer application.
 
For RETAIL folios, pricing information from iTunesConnect are contained within the folio description objects returned from getFolioData().
 

 
Example

Let's walk through an example of a basic storefront. This example showcases the JavaScript required to implement a storefront. The templates provide basic implementations. This example will walk through:
 
  1. Check for network connectivity.
  2. Wait for the API to be available.
  3. Request an update of the library.
  4. Create a visual representation of the folio and a button to purchase it.
  5. Download (or purchase) and view a folio.
 
Setup
  1. Download the source archive.
  2. Create a multi-issue viewer. Customize the navigation toolbar with a custom icon. Use the archive file (example_devnet.zip) as the archive for the "Icon xxx HTML Resources.zip" resource.
  3. Create a folio and publish it as PUBLIC/FREE with the product ID storefolio1.
 
Network_detection.js
$(document).ready(function() { var init = function() { // Define a function in case the main JavaScript file has not loaded yet. if (!window.onnetworkconnection) { window.onnetworkconnection = function() { window.connected = true; } } } $.ajax({ type: "GET", url: "http://www.google.com", success: function() { window.onnetworkconnection(); }, error: function() { $("body").append("<div id='offline'>You must be connected to the Internet to access the store.</div>"); } }) if (navigator.userAgent.toLowerCase().indexOf("ipad") == -1) { // On desktop so call init() immediately. init(); } else { window.onadobedpscontextloaded = init; } });
The above file first uses JQuery's .ajax function to fetch a well known web address to determine if the device is currently on-line. If successful, it sets a property window.connected for later inspection.
 
 
basic_store1.js

 
You implement the core of this example in the basic_store1.js file.
 
First, define the productID we will be offering for purchase/download.
 
var productID = 'storefolio1';
 
The next block of code sets up callback for receiving notification upon an update library event. Whenever the library is updated, the code fetches descriptions of all the folios in the account. Depending upon the state 'folioState' of the folio, the label on the purchase button is set to "Buy", "View" (for downloaded or free content), or "Invalid" (shouldn't happen).
 
var onFolioData = function (data) { var len = data.length; // The library returns an array of folio descriptor objects. Put them into a hash indexed by something more convenient (ie productId) for (var i = 0; i < len; i++) { folioDataHash[data[i].productId] = data[i]; console("Folio: "+data[i].productId); } var folio = folioDataHash[productID]; if (folio) { // Also fetch our preview image. adobe.dps.store.getPreviewImage(productID, true, 768, 1024, onPreviewImage); } else { console("The productId you have defined for purchase/download does not exist within your Fulfillment account"); } }; var onPreviewImage = function(data) { var s=""; var folio = folioDataHash[productID]; if (!folio) return; // un-known folio...nothing to show. //// // At this point, we have valid folio data representing our folios and a folio image for our target product ID. //// // Create the image s += "<img src='"+data['path']+"' width='160' height='222' />"; // Create the Title s += "<div id='title'>"+folio.title+"</div>"; // Create the Folio Number s += "<div id='manifestXRef'>"+folio.description+"</div>"; // Create the buy|view button and then create the 'buy' or 'view' label and assign a click handler, if valid s += "<div class='buyButton' id='buyButton1'>"; if (folio.folioState=='100') { // buy folio s +="Buy"; } else { s += "View"; } s += "</div>"; var node = document.getElementById("ourItem"); if (node) { node.innerHTML = s; node.onclick = node.onclick=buy; } }; var onLibraryUpdate = function () { adobe.dps.store.getFolioData(onFolioData); // Unregister for library updates unregisterUpdateLibraryHandler(); } var registerUpdateLibraryHandler = function () { if (window.adobedpscontextloaded) { // call into the updateLibrary API adobe.dps.store.registerLibraryUpdateCompleteHandler(onLibraryUpdate); adobe.dps.store.updateLibrary(); } else { console("Failed to find Javascript API"); } }; var unregisterUpdateLibraryHandler = function () { if (window.adobedpscontextloaded) { // call into the updateLibrary API adobe.dps.store.unregisterLibraryUpdateCompleteHandler(onLibraryUpdate); } } registerUpdateLibraryHandler();

 
JavaScript Viewer API for Digital Publishing Suite

Previously referred to as "Custom HTML Store" or "Store APIs", the JavaScript Viewer API for Digital Publishing Suite provides a bridge between web pages hosted within a custom navigation slot or entitlement banner. Both areas have access to the same functionality. However, pages opened (the 'slide-up webview') by either the entitlement banner or the custom navigation slot, do not have access to these APIs.
 
  1. eCommerce features within the viewer application. The code can initiate transactions to purchase individual folios or subscriptions.
  2. Introspection of folio descriptions within the library.
  3. Retrieve and set authentication for a specific user (entitlements only).
  4. Access to cached versions of the cover images displayed in the library.
  5. Entitlement information for folios.
The API is an asynchronous interface. This means you must provide callback handlers to receive content at future time, as in the following example:
 
adobe.dps.store.getFolioData(onFolioData);
The getFolioData() method passes a reference to the onFolioData() method. When the folio data has been collected by the viewer, it will invoke the callback onFolioData() and provide an array object containing the data.
 
Note: At the time of this writing (v19), the JS API can only support a single outstanding callback at a time. You should serialize all operations and ensure to have only a single outstanding callback active at any one time.
 

 
Connecting to fulfillment

The examples included with this article fetch information about all available folios directly from the viewer. The viewer gets this information from your Adobe Fulfillment account. This account contains your published, public content. When you develop your storefront on a desktop or test within a browser, you can access this data from the fulfillment server.
 
It remains a best practice to allow only the viewer to fetch this data – and to have the storefront code fetch it from the viewer (with a call to getFolioData). Not only does this pattern eliminate potentially costly and redundant web calls, it also ensures that the correct and consistent rendition logic is applied to the folio list.
 
The following steps help you access your fulfillment account if you are required to.
 
Each Adobe ID references a specific Adobe Digital Publishing Suite fulfillment account. Likewise, every magazine application pulls folios from a specific Adobe Digital Publishing Suite fulfillment account. Fulfillment accounts are identified by a GUID (for example, 943864f0a0d2431aa2992987caf78cb0).
 
For applications that want to create a customized view of their folios, access the data within the fulfillment account can be very useful.
 
To access your fulfillment account associated with your Adobe ID (or your magazine's AdobeID), you need to discover your GUID. This can be found by inspecting the LibraryConfig.plist file within your application's contents, as follows:
 
  1. Locate your IPA file produced and downloaded by Viewer Builder.
  2. Open with an Archive utility.
  3. Locate the Application file (for example, viewer.app) and control-click to choose "Show Package Contents".
  4. Within the file LibraryConfig.plist, the value at serviceOptions->fulfillment->loginOptions->accountId is your GUID.

 

LibraryConfig.plist
Figure 3: LibraryConfig.plist
 
 
You can then retrieve an XML description of all the public folios within your fulfillment account at the following URL:
 
http://edge.adobe-dcfs.com/ddp/issueServer/issues?accountId=<your GUID>&targetDimension=1024x768,768x1024,1024x748,748x1024
or:
 
http://edge.adobe-dcfs.com/ddp/issueServer/issues?accountId=<your GUID>&targetDimension=all
for example:
 
http://edge.adobe-dcfs.com/ddp/issueServer/issues?accountId=b8c8d8164adb4dcf8e69e94fae1c58f6&targetDimension=1024x768
The following is an example response containing a single folio:
 
<results status="SUCCESS" message="Success"> <issues> <issue id="bdfb4254-1276-4393-abc6-3578540b750e" productId="look14" formatVersion="1.7.0" version="4" subpath=""> <magazineTitle>Daily Fashion</magazineTitle> <issueNumber>14</issueNumber> <publicationDate>2011-05-01T07:00:00Z</publicationDate> <description>bracelet</description> <manifestXRef>May 2011</manifestXRef> <state>production</state> <libraryPreviewUrl landscapeVersion="1" portraitVersion="1">http://edge.adobe-dcfs.com/ddp/issueServer/issues/bdfb4254-1276-4393-abc6-3578540b750e/libraryPreview</libraryPreviewUrl> <brokers> <broker>noChargeStore</broker> </brokers> </issue> </issues> </results>
Note: Multiple <issue> nodes will probably exist.
 
 
Accessing Cover images from your fulfillment account
Within the XML returned from fulfillment, each <issue> node contains a <libraryPreviewUrl> element. Use this URL with either portrait or landscape appended to access your images.
 
Eg. http://edge.adobe-dcfs.com/ddp/issueServer/issues/bdfb4254-1276-4393-abc6-3578540b750e/libraryPreview/portrait

 
Development and maintenance considerations

While all the code for a store implementation can be embedded within the viewer, this makes updates to the code dependent upon updates to the viewer. Instead, only code that is not likely to change (JQuery.js) should be included. Also, the archive you provide to Viewer Builder for inclusion must contain an index.html file. Keep this simple.
 
From our example:
 
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Banner</title> <script src="jquery-1.5.min.js"></script> <script src="network_detection.js"></script> <script src="basic_store1.js"></script> <link rel="stylesheet" href="offline.css" type="text/css"> <link rel="stylesheet" href="styles.css" type="text/css"> </head> <body> <p id="console"/> <div id="ourItem" /> </body> </html>
Modify the file to allow rapid iterations and web updates by addressing the implementation files on a server.
 
<html><head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Banner</title> <script src="jquery-1.5.min.js"></script> <script src="network_detection.js"></script> <script src="http://<yourserver>/basic_store1.js"></script> <link rel="stylesheet" href="offline.css" type="text/css"> <link rel="stylesheet" href="http://<yourserver>/styles.css" type="text/css"> </head> <body> <p id="console"/> <div id="ourItem" /> </body></html>

 
Templates

Adobe's Digital Publishing Suite Customer Enablement team has created template implementations of storefronts that can be leveraged as turnkey implementations, or as a bootstrap to your own custom storefront. These all implement a grid layout of folios with various extra modules added as examples.
 
An archive of these implementations can be found in the Custom_store_templates.zip file.
 
 
Marketing
The Marketing template provides a Grid view of folios and a rotating banner area at the top.
 
Marketing template
Figure 4: Marketing template
 
 
 
Subscribe
The Subscribe template provides a Grid view with a transient subscription image. Once subscribed, this image disappears.
 
Figure 5: Subscribe template
Figure 5: Subscribe template
 
 
 
Rotating banner
The rotating banner template provides a Grid view with a rotating promotional area above that contains a featured folio.
 
Figure 6: Rotating Banner template
Figure 6: Rotating Banner template
 
 
 
Twitter feed
It is important to realize that these storefront templates are simply HTML5+JavaScript+CSS3. You can add anything, even a Twitter feed.
 
Figure 7: Twitter template
Figure 7: Twitter template
 
 

 
Where to go from here

Read the "Merchandising with Adobe Digital Publishing Suite" white paper for more information about how to drive additional revenue through a Custom Store.