diff --git a/openbis-ipad/ipad-example-data/ipad-ui/1/dss/drop-boxes/ipad-image/data-set-handler.py b/openbis-ipad/ipad-example-data/ipad-ui/1/dss/drop-boxes/ipad-image/data-set-handler.py
new file mode 100644
index 0000000000000000000000000000000000000000..b5ed53e1639c6a0a4d63adfc506aa35acf60d3c5
--- /dev/null
+++ b/openbis-ipad/ipad-example-data/ipad-ui/1/dss/drop-boxes/ipad-image/data-set-handler.py
@@ -0,0 +1,48 @@
+"""Take a directory of JPEG images and register them for all HT_PROBE samples, cycling through the images in the directory."""
+
+from ch.systemsx.cisd.openbis.generic.shared.api.v1.dto import SearchCriteria
+import os
+
+
+def process(tr):
+  search_service = tr.getSearchService()
+  sc = SearchCriteria()
+  sc.addMatchClause(SearchCriteria.MatchClause.createAttributeMatch(SearchCriteria.MatchClauseAttribute.TYPE, '5HT_PROBE'))
+  five_ht_samps = search_service.searchForSamples(sc)
+  five_ht_exp = get_or_create_experiment(tr)
+  data_set = create_image_data_set(tr, five_ht_exp)
+
+  incoming_folder = tr.getIncoming().getPath()
+  incoming_filenames = os.listdir(incoming_folder)
+  count = len(incoming_filenames)
+  i = 0
+  # match samples to images, cycling through the images
+  for samp in five_ht_samps:
+    samp = tr.makeSampleMutable(samp)
+    samp.setExperiment(five_ht_exp)
+    index = i % count
+    i = i + 1
+    add_image_to_folder(tr, samp, incoming_folder, incoming_filenames[index])
+
+  tr.moveFile(incoming_folder, data_set, "images/")
+
+def get_or_create_experiment(tr):
+  exp = tr.getExperiment("/PROBES/PROBE/5HT-EXP")
+  if exp:
+    return exp
+
+  proj = tr.createNewProject("/PROBES/PROBE")
+  proj.setDescription("Project for speculative 5HT experiments")
+
+  exp = tr.createNewExperiment("/PROBES/PROBE/5HT-EXP", "5HT_EXP")
+  return exp
+
+def create_image_data_set(tr, exp):
+  ds = tr.createNewDataSet('5HT_IMAGE')
+  ds.setExperiment(exp)
+  return ds
+
+def add_image_to_folder(tr, samp, folder, filename):
+  new_filename = samp.getCode() + ".jpg"
+  linked_path = os.path.join(folder, new_filename)
+  os.link(os.path.join(folder, filename), linked_path)
diff --git a/openbis-ipad/ipad-example-data/ipad-ui/1/dss/drop-boxes/ipad-image/plugin.properties b/openbis-ipad/ipad-example-data/ipad-ui/1/dss/drop-boxes/ipad-image/plugin.properties
new file mode 100644
index 0000000000000000000000000000000000000000..65dbee83c6a5ee854f4f507b790546ec711c3f01
--- /dev/null
+++ b/openbis-ipad/ipad-example-data/ipad-ui/1/dss/drop-boxes/ipad-image/plugin.properties
@@ -0,0 +1,6 @@
+incoming-dir = ${root-dir}/incoming-ipad-image
+incoming-data-completeness-condition = auto-detection
+top-level-data-set-handler = ch.systemsx.cisd.etlserver.registrator.api.v2.JythonTopLevelDataSetHandlerV2
+storage-processor = ch.systemsx.cisd.etlserver.DefaultStorageProcessor
+script-path = data-set-handler.py
+development-mode = true
\ No newline at end of file
diff --git a/openbis-ipad/ipad-example-data/ipad-ui/1/dss/reporting-plugins/ipad-read-service-v1/ipad_read.py b/openbis-ipad/ipad-example-data/ipad-ui/1/dss/reporting-plugins/ipad-read-service-v1/ipad_read.py
index 264f2dd57014eb6f8c5bfc49931d88858d3845a1..576bca53b93ac31e0b36e782a84163e958ee55e3 100644
--- a/openbis-ipad/ipad-example-data/ipad-ui/1/dss/reporting-plugins/ipad-read-service-v1/ipad_read.py
+++ b/openbis-ipad/ipad-example-data/ipad-ui/1/dss/reporting-plugins/ipad-read-service-v1/ipad_read.py
@@ -1,7 +1,7 @@
 from ch.systemsx.cisd.openbis.dss.generic.shared.api.internal.v1 import MaterialIdentifierCollection
 from ch.systemsx.cisd.openbis.generic.shared.basic.dto import MaterialIdentifier
 from com.fasterxml.jackson.databind import ObjectMapper 
-from ch.systemsx.cisd.openbis.generic.shared.api.v1.dto import SearchCriteria
+from ch.systemsx.cisd.openbis.generic.shared.api.v1.dto import SearchCriteria, SearchSubCriteria
 
 #
 # BEGIN Infrastructure
@@ -130,6 +130,8 @@ class DetailRequestHandler(RequestHandler):
 # END Infrastructure
 #
 
+DSS_DOWNLOAD_URL = 'https://localhost:8444/datastore_server/'
+
 #
 # Helper Methods
 # 
@@ -184,10 +186,11 @@ def material_to_dict(material):
 
 	prop_names = ["NAME", "PROT_NAME", "GENE_NAME", "LENGTH", "CHEMBL", "DESC", "FORMULA", "WEIGHT", "SMILES"]
 	properties = dict((name, material.getPropertyValue(name)) for name in prop_names if material.getPropertyValue(name) is not None)
+	properties['VERY_LONG_PROPERTY_NAME'] = "This is a very long text that should span multiple lines to see if this thing works, you know, the thing that causes the other thing to place text on multiple lines and stuff like that, etc., etc., so on and so forth."
 	material_dict['PROPERTIES'] = json_encoded_value(properties)
 	return material_dict
 
-def sample_to_dict(five_ht_sample, material_by_perm_id):
+def sample_to_dict(five_ht_sample, material_by_perm_id, data_sets):
 	sample_dict = {}
 	sample_dict['SUMMARY_HEADER'] = five_ht_sample.getCode()
 	sample_dict['SUMMARY'] = five_ht_sample.getPropertyValue("DESC")
@@ -200,7 +203,7 @@ def sample_to_dict(five_ht_sample, material_by_perm_id):
 	sample_dict['REFCON'] = json_encoded_value(refcon)
 	sample_dict['CATEGORY'] = five_ht_sample.getSampleType()
 	compound = material_by_perm_id[five_ht_sample.getPropertyValue("COMPOUND")]
-	sample_dict['IMAGE_URL'] = image_url_for_compound(compound)
+	sample_dict['IMAGE_URL'] = image_url_for_sample(five_ht_sample, data_sets, compound)
 
 	children = [five_ht_sample.getPropertyValue("TARGET"), five_ht_sample.getPropertyValue("COMPOUND")]
 	sample_dict['CHILDREN'] = json_encoded_value(children)
@@ -212,6 +215,18 @@ def sample_to_dict(five_ht_sample, material_by_perm_id):
 	# Need to handle the material links as entity links: "TARGET", "COMPOUND"
 	return sample_dict
 
+def image_url_for_sample(five_ht_sample, data_sets, compound):
+	image_data_set = None
+	for data_set in data_sets:
+	    if data_set.getExperiment().getExperimentIdentifier() == five_ht_sample.getExperiment().getExperimentIdentifier():
+	        image_data_set = data_set
+	        break
+	if image_data_set is None:
+		return image_url_for_compound(compound)
+	image_url = DSS_DOWNLOAD_URL + image_data_set.getDataSetCode() + '/original/images/'
+	image_url = image_url + five_ht_sample.getCode() + '.jpg'
+	return image_url
+
 
 def add_material_to_collection(code, collection):
 	material_id = MaterialIdentifier.tryParseIdentifier(code)
@@ -229,7 +244,8 @@ def materials_to_dict(materials):
 	return result
 
 def samples_to_dict(samples, material_by_perm_id):
-	result = [sample_to_dict(sample, material_by_perm_id) for sample in samples]
+	data_sets = retrieve_data_sets_for_samples(samples)
+	result = [sample_to_dict(sample, material_by_perm_id, data_sets) for sample in samples]
 	return result
 
 def retrieve_samples(sample_perm_ids_and_ref_cons):
@@ -242,6 +258,23 @@ def retrieve_samples(sample_perm_ids_and_ref_cons):
 		sc.addMatchClause(sc.MatchClause.createAttributeMatch(sc.MatchClauseAttribute.CODE, code))
 	return searchService.searchForSamples(sc)
 
+def retrieve_data_sets_for_samples(samples):
+	experiment_codes = set()
+	for sample in samples:
+		if sample.getExperiment() is None:
+			continue
+		tokens = sample.getExperiment().getExperimentIdentifier().split('/')
+		experiment_code = tokens[len(tokens) - 1]
+		experiment_codes.add(experiment_code)
+	sc = SearchCriteria()
+	sc.setOperator(sc.SearchOperator.MATCH_ANY_CLAUSES)
+	for code in experiment_codes:
+		sc.addMatchClause(sc.MatchClause.createAttributeMatch(sc.MatchClauseAttribute.CODE, code))
+	data_set_sc = SearchCriteria()
+	data_set_sc.addMatchClause(data_set_sc.MatchClause.createAttributeMatch(data_set_sc.MatchClauseAttribute.TYPE, "5HT_IMAGE"))
+	data_set_sc.addSubCriteria(SearchSubCriteria.createExperimentCriteria(sc))
+	return searchService.searchForDataSets(data_set_sc)
+
 class ExampleAllDataRequestHandler(AllDataRequestHandler):
 	"""Handler for the ALLDATA request."""