Category Archives: Projects

Eternal Image Cache Based on Demandware eShop via Simple API for External Image Usage

Here is another pragmatic web-technology based solution I would like to share with you. We had a given Demandware eShop with 35k variants at the time (in 2014). Where a variant represents a ‘product’ in a unique color and ‘articles’ being the children of the variants as real orderable items – a variant in a certain size. A big eShopping site.


Call of an image URL in the browser.

Call of an image URL in the browser.

Our goal was to have any image of articles in the eShop under a very simple URL that can be generated only by knowing a ‘number’ (SKU or EAN if available) leading to an existing article like this:<sku>. The response would be a html image header and the streamed content of the image itself or if not available a placeholder image.

Fortunately Demandware offers a comprehensive, well documented REST based API (we used  the Open Commerce API 13.6) to interact with the eShop returning results in pure JSON.

The initial use case was to add pictures of the articles to help logistics people picking the purchased items from the warehouse. They use mobile devices with a screen and item lists on it. Another use case was to add article images on various internal reports based on the business intelligence system informing management of what is currently being sold.

Article images used in a report.

Article images used in a report.

The workflow of the application works like this:

  1. The image is stored in an ‘eternal cache’ in a directory path that is generated from the ‘number’ – e.g. 4054781891957 converts to this path data/400/405/478/189/195/7/4054781891957.jpg (400 being the width of the cached image version).
    This way we did not have to use a database. We use just use is_file($path) to find out if we have a valid cache hit.
  2. If a cached file does not yet exist (first call on a ‘number’), depending on the type of the given ‘number’ we check a bunch things via the API in the background. This is like a regular search on the eShop, fetch the URL of the article’s main image, download it and save it to the local cache converted to 400px, 200px and 100px width.

Drawback: OCAPI only offers information about articles that are ‘online’ at the time of the API call.

Optional parameters:

  • debug: Outputs the differnets actions along the way to the image.
  • nocache: Forces re-fetch from Demandware OCAPI.
  • w: Default width is 200px. Possible Values 400, 200, 100

The application is hosted in our own infrastructure for the customer. The application is based on Symfony2.

wsdl with remote methods

Update: The application now has also a SOAP/WSDL API to a) integrate images into other enterprise applications via base64-stream and b) force cache refresh of an image (happens on recycled article numbers).

OCI Interface Integration into your eShop via Punch-Out from SAP/SRM

If it comes to integration of eShops like Magento, OXID or even your cusom made eShops into a corporate buying infrastructure using SAP, you need to deal with the SRM module (Supplier Relationship Management) using the OCI interface.

On the shop side we had a Gambio eShop in this case, which is a commercial fork of XT-Commerce. At first we checked if there where any affordable ready-to-use modules available. We found some for around 1500-2000EUR with additional (unclear) service cost. To be honest, what we found looked a bit outdated and after some contacts with the responsible sales people not even very trust worthy. After skipping over the documentation we decided to integrate OCI on our own – the custom hacked way. After all, it is just simple HTTP.

If you check the documentation and your first thought is, you could easily do it using cURL at the checkout, be warned – OCI does NOT work using a cURL call from a remote server! We did it this way and failed the first try as soon as we discovered how OCI mechanics really worked (see below). The documentation is a bit ambiguous in that respect.

The main problem is, that only your customers customer (!) with whom you have to co-operate closely and directly can help you through testing. People there are not that technical, even though their job title says so. They own the SRM testsystem (you don’t) and you always have to ask for a punch-out-click in order to find new insights integrating OCI into your eShop. It took us many weeks instead of a couple of minutes due to vacation and corporate politics and overhead. Finally we integrated an all-encompassing logging for each minor step and response to reduce the communication to ‘please click again’ – and bamm, 3 days later we had a bunch of logging information powering the next development step.

Here is a SVN diff showing all files we added or modified in the gambio release with a description what we modified:

Changed files in original Gambio distribution:
M  checkout_shipping.php (added optical info, we're in OCI-mode)
M  includes/configure.php (added a switch to work on live and development server)
A  includes/modules/oci/oci.php (Main functionality wrapped in a class)
A  includes/modules/oci/ociParameters.php (Configuration with additional parameters for SRM)
A  includes/modules/oci/logger.php (Own simple logger to see what happens during test calls)
A  oci_out.php (exit sending real hidden html form onLoad via JavaScript, redirecting to SRM)
M  admin/includes/configure.php (added a switch to work on live and development server)
A  oci.php (entry point for calling Punch-Out incl authentication)
M  checkout_confirmation.php (added optical info, we're in OCI-mode)
M  checkout_process.php (redirect to exit oci_out.php instead to successpage after checkout)
M  templates/EyeCandy/module/checkout_shipping.html (added optical info, we're in OCI-mode)
M  templates/EyeCandy/module/checkout_confirmation.html (added optical info, we're in OCI-mode)
M  templates/EyeCandy/module/checkout_success.html (added optical info, we're in OCI-mode)

OCI mechanics work like this:

At first the purchase department selects a supplier in their SRM. As soon as the order agent starts shopping, SRM performs a ‘Punch-Out’, which opens a browser window with a URL like this one:–KMDRyVbbH6pX0000mAWtxW–%253fsap-ep-tstamp%253d20130419102708%2526sap-wd-tstamp%253d20130419102708%2526sap-ep-version%253d7%26NavigationTarget%3dportal_content%2fnwbc_back%26NavMode%3d3%26UsePost%3dTrue%26SAPSRM_RESUME_ID%3dSAPSRM_OCI%26sap-nwbc-has_post_params%3dX%26sap-client%3d010%26sap-language%3dDE%26sap-nwbc-node%3dresume_appl

This call goes to your /oci.php. It needs to do the following:

  • It checks if there is a customer/user in your eShop that can be authenticated with the userid, password and Z_EMAIL parameters. If so /oci.php logs the user in to the eShop. Of course you have created the customer maually before. Send the SRM owner the values of the parameters for their punch-out and test if auto-login works via punch-out.
  • /oci.php must remember the value of the parameter HOOK_URL in the session. It is only valid for this purchase and the contents of the shopping cart must be posted back to this URL – it is your value for your form’s action attribute. Remember: It can NOT be done via cURL.

The user is now navigating your eShop and fills his shopping cart and proceeds to the checkout and clicks the final checkout button. The purchase is saved in the shopping application by your eShop system. Instead of redirection to the shop-success page a hidden form is created (see OCI documentation for fieldnames and fixed field values necessary) with the shopping information for the SRM and it is sumbitted immedately by javascript. You need to intercept the redirection to the success page and redirect to your /oci_out.php instead providing this form. The action url has been handed over via the HOOK_URL parameter on punch-out.

The hidden form looks like this:

<!– OCI autosubmit –>
<script type=”text/javascript” language=”javascript”>

<form id=”srm_form” action=”–KN2O2NIP8fdX0000mAWtxW–%3fsap-ep-tstamp%3d20130419104715%26sap-wd-tstamp%3d20130419104715%26sap-ep-version%3d7&NavigationTarget=portal_content/nwbc_back&NavMode=3&UsePost=True&SAPSRM_RESUME_ID=SAPSRM_OCI&sap-nwbc-has_post_params=X&sap-client=010&sap-language=DE&sap-nwbc-node=resume_appl” method=”POST”><code> <input name=”NEW_ITEM-LINE[1]” type=”hidden” value=”00001″ />
<input name=”NEW_ITEM-DESCRIPTION[1]” type=”hidden” value=”Herren Fleece-Jacke Langley Rot” />
<input name=”NEW_ITEM-LONGTEXT_1:132[]” type=”hidden” value=”Herren Fleece-Jacke Langley Rot” />
<input name=”NEW_ITEM-QUANTITY[1]” type=”hidden” value=”1″ />
<input name=”NEW_ITEM-UNIT[1]” type=”hidden” value=”C62″ />
<input name=”NEW_ITEM-PRICE[1]” type=”hidden” value=”41.87″ />
<input name=”NEW_ITEM-CURRENCY[1]” type=”hidden” value=”EUR” />
<input name=”NEW_ITEM-PRICEUNIT[1]” type=”hidden” value=”1″ />
<input name=”NEW_ITEM-LEADTIME[1]” type=”hidden” value=”21″ />
<input name=”NEW_ITEM-VENDOR[1]” type=”hidden” value=”16222″ />
<input name=”NEW_ITEM-VENDORMAT[1]” type=”hidden” value=”287{2}5{1}23{5}17″ />
<input name=”NEW_ITEM-MATGROUP[1]” type=”hidden” value=”41010000″ />
<input name=”NEW_ITEM-LINE[2]” type=”hidden” value=”00002″ />
<input name=”NEW_ITEM-DESCRIPTION[2]” type=”hidden” value=”Pique Polo Safran” />
<input name=”NEW_ITEM-LONGTEXT_2:132[]” type=”hidden” value=”Pique Polo Safran” />
<input name=”NEW_ITEM-QUANTITY[2]” type=”hidden” value=”1″ />
<input name=”NEW_ITEM-UNIT[2]” type=”hidden” value=”C62″ />
<input name=”NEW_ITEM-PRICE[2]” type=”hidden” value=”16.95″ />
<input name=”NEW_ITEM-CURRENCY[2]” type=”hidden” value=”EUR” />
<input name=”NEW_ITEM-PRICEUNIT[2]” type=”hidden” value=”1″ />
<input name=”NEW_ITEM-LEADTIME[2]” type=”hidden” value=”21″ />
<input name=”NEW_ITEM-VENDOR[2]” type=”hidden” value=”16222″ />
<input name=”NEW_ITEM-VENDORMAT[2]” type=”hidden” value=”112{2}4{1}1{5}16″ />
<input name=”NEW_ITEM-MATGROUP[2]” type=”hidden” value=”41010000″ />
<input id=”srm_form_submit” style=”display: none;” type=”submit” value=”Go” />

After this form has been submitted, SRM generates another page also including a hidden form, additional internal information and lots of JavaScript logic also with auto-submit. Please note: a) The array counter must start at 1 not at 0. And b) note the syntax of the longtext field.

The page is generated and ‘executed’ on the server side of the SRM system! This is the reason why it could not be done via cURL: The action to the HOOK_URL opens a page on the SRM server submitting another (!) form which finally writes the purchase to SRM – not your initial POST. It also closes the browser window for the order agent. If SRM can not close the browser window something went wrong.

LSIS Channels is out!

I’m proud to announce the release of some new features in our LSIS Life Science Search Engine – ‘Save Queries‘, ‘Browse Channels‘ and ‘ORed terms‘.

Saving queries to channels works like this:

  1. Perform a search on our frontend. Then the button ‘Save Query‘ appears:
  2. Propose a title for your query and chose whether the resulting channel should be public or only private to you. If you select public the admin categorizes your query into the topic tree and approves it. As a result (a) a daily RSS feed is generated which can be used from your feed reader and (b) people subscribing to the email alert will be sent teasers by email if there are new hits added to our search index, fitting the query. The purpose is to make important news come to you without having to check what happened every day in your fields of interest.Public channels can be browsed on the channel page:

Another new feature is that you can OR search terms by entering them comma-separated as one search ‘term’. This makes searching and saving queries more accurate.

Feedback very welcome!

And if you find LSIS useful, please tell your friends.

Our new LSIS Release is out!

I am glad to announce: Our new release of LSIS – The Life Science Search Engine is online.

Check it out:

  • Frontend: Flex
  • We eliminated the boolean AND for the user utilizing term-boosing, result-scoring depending on search terms and intelligent sorting instead.
  • No paging through results, just scrolling

Feedback very welcome!