Manage tours and tour stops in CollectiveAccess

Voici mes réglages pour utiliser les « parcours » et les « points de parcours » dans CollectiveAccess/Pawtucket2.

Here are my settings to use « tours » and « tour stops » in CollectiveAccess/Pawtucket2.

This tutorial is tested for CollectiveAccess 1.7.17.

1. In pawtucket /app/conf/local/browse.conf

You should have these parts: (comments below)


# --------------------------------------------------------------------
# Configuration for tour stops browse
ca_tour_stops = {
name = _("Tour stops"),
facets = {
tour_facet = {
type = authority,
table = ca_tours,
relationship_table = ca_tours_x_tour_stops,
restrict_to_types = [],
group_mode = alphabetical,
label_singular = _("Tour stop"),
label_plural = _("Tour stops")
},
type_facet = {
type = fieldList,
field = type_id,
group_mode = alphabetical,
label_singular = _("Type"),
label_plural = _("Types")
},
props_facet = {
type = attribute,
element_code = tour_stop_props,
group_mode = alphabetical,
label_singular = _("Property of tour stop"),
label_plural = _("Properties of tour stops")
},
place_facet = {
type = authority,
table = ca_places,
relationship_table = ca_tour_stops_x_places,
restrict_to_types = [],
group_mode = alphabetical,
label_singular = _("Place"),
label_plural = _("Places")
},
collection_facet = {
type = authority,
table = ca_collections,
relationship_table = ca_tours_x_collections,
restrict_to_types = [],
group_mode = alphabetical,
label_singular = _("Collection"),
label_plural = _("Collections")
},
term_facet = {
type = authority,
table = ca_list_items,
relationship_table = ca_tours_x_vocabulary_terms,
restrict_to_types = [],
group_mode = alphabetical,
label_singular = _("Term"),
label_plural = _("Terms")
},
}
}
# --------------------------------------------------------------------
# Configuration for tours browse
ca_tours = {
name = _("Tours"),
facets = {
type_facet = {
type = fieldList,
field = type_id,
group_mode = alphabetical,
label_singular = _("Type"),
label_plural = _("Types")
},
}
}

Comments: you can remove facets you don’t need. The « tour_facet » allows to filter tour stops by tours. The « props_facet » is based on tour_stop_props metadata I added to describe some properties of my tour stops, via a linked list of vocabulary (indoors, outdoors, accessible to people with reduced mobility, accessible in Braille). The « place_facet » and « collection_facet » allow browsing tour stops related to places and collections… The « term_facet » allows browsing tour stops related to terms, scientific or other themes…

2. In pawtucket /themes/<your_theme>/conf/browse.conf

In the « browseTypes = {…} » block, at the same level of « objects = {…} », « collections = {…} », etc., you should have these blocks:


tours = {
displayName = _("Tours"),
labelSingular = _("tour"),
labelPlural = _("tours"),
table = ca_tours,
restrictToTypes = []
views = {
map = {
data = ca_tours.georeference,
minZoomLevel = 2,
maxZoomLevel = 18,
display = {
labelTemplate = <l>^ca_tours.preferred_labels</l>,
contentTemplate = ,
}
},
},
sortBy = {
Title = ca_tours.preferred_labels.name,
},
sortDirection = {
Title = asc,
},
sortControlType = dropdown
},

tour_stops = {
displayName = _("Tour stops"),
labelSingular = _("tour stop"),
labelPlural = _("tour stops"),
table = ca_tour_stops,
restrictToTypes = []
views = {
images = {
title_template = <b>^ca_tours.preferred_labels.name</b><br/><l>^ca_tour_stops.preferred_labels</l><br/>(^ca_tour_stops.type_id),
},
list = {
title_template = <l>^ca_tour_stops.preferred_labels</l><br/>(^ca_tour_stops.type_id),
},
map = {
data = ca_tour_stops.georeference,
minZoomLevel = 2,
maxZoomLevel = 18,
display = {
labelTemplate = <b>^ca_tours.preferred_labels.name</b><br/><l>^ca_tour_stops.preferred_labels</l>,
contentTemplate = ,
}
},
},
sortBy = {
Title = ca_tour_stops.preferred_labels.name,
},
sortDirection = {
Title = asc,
},
sortControlType = dropdown
},

Comments: for tours, the only browsing view I managed to get working is « map »; I couldn’t manage to use « images, « list »… For tour stops, it’s ok with « images », « list » and « map » to browse them, but the with the images (object_representations) from an object in relationship with the tour stop. (Take a look [here](https://collections.univ-pau.fr/pub/index.php/Browse/tours) and [here](https://collections.univ-pau.fr/pub/index.php/Browse/tour_stops))

3. In pawtucket /themes/<your_theme>/conf/detail.conf

In the « detailTypes = {…} » block, at the same level as  » objects = {…} », etc., you should add these blocks:


tours = {
displayName = _(Tours),
table = ca_tours,
restrictToTypes = [],
options = {
nextLink = <i class='fa fa-angle-right'></i><div class='small'>_(Next)</div>,
previousLink = <i class='fa fa-angle-left'></i><div class='small'>_(Prev)</div>,
resultsLink = <i class='fa fa-angle-double-left'></i><div class='small'>_(Back)</div>,
enableComments = 0,
enableShare = 0,
enablePDF = 0,
mapAttributes = [ca_tour_stops.georeference],
map_width = 100%,
map_height = 500,
mapZoomLevel = 17,
}
},
tour_stops = {
displayName = _(Tour stops),
table = ca_tour_stops,
restrictToTypes = [],
options = {
nextLink = ,
previousLink = ,
resultsLink = ,
enableComments = 0,
enableShare = 0,
enablePDF = 0,
mapAttributes = [ca_tour_stops.georeference],
map_width = 100%,
map_height = 250,
mapZoomLevel = 20,
}
},

Comments: to be located on the map, you need to define a metadata named (for example) « georeference » for tours and tour stops, if it doesn’t exist yet. It may exist for object, places, etc. but not allowed for tours and tour stops. So check that…

4. In pawtucket /themes/<your_theme>/conf/search.conf

If you want tours and stops to be searchable, add in the « multisearchTypes = {…} » block, at the same level as  » objects = {…} », etc., these blocks:


tour_stops = {
displayName = _(Tour stops),
table = ca_tour_stops,
restrictToTypes = [],
view = Search/ca_tour_stops_search_subview_html.php,
itemsPerPage = 12,
itemsPerColumn = 3,
sortControlType = list,
sortBy = {
Title = ca_tour_stops.name
},
sortDirection = {
Title = asc,
},
},

tours = {
displayName = _(Tours),
table = ca_tours,
restrictToTypes = [],
view = Search/ca_tours_search_subview_html.php,
itemsPerPage = 12,
itemsPerColumn = 3,
sortControlType = list,
sortBy = {
Title = ca_tours.name
},
sortDirection = {
Title = asc,
},
},

Comments: you see here that it will need the files Search/ca_tour_stops_search_subview_html.php and Search/ca_tours_search_subview_html.php

5. In pawtucket /themes/<your_theme>/views/Search/

So add these files:

ca_tour_stops_search_subview_html.php


<?php
/* ----------------------------------------------------------------------
* themes/default/views/Search/ca_tours_search_subview_html.php :
* ----------------------------------------------------------------------
* CollectiveAccess
* Open-source collections management software
* ----------------------------------------------------------------------
*
* Software by Whirl-i-Gig (http://www.whirl-i-gig.com)
* Copyright 2013-2014 Whirl-i-Gig
*
* For more information visit http://www.CollectiveAccess.org
*
* This program is free software; you may redistribute it and/or modify it under
* the terms of the provided license as published by Whirl-i-Gig
*
* CollectiveAccess is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTIES whatsoever, including any implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* This source code is free and modifiable under the terms of
* GNU General Public License. (http://www.gnu.org/copyleft/gpl.html). See
* the "license.txt" file for details, or visit the CollectiveAccess web site at
* http://www.CollectiveAccess.org
*
* ----------------------------------------------------------------------
*/

$qr_results = $this->getVar('result');
$va_block_info = $this->getVar('blockInfo');
$vs_block = $this->getVar('block');
$vn_start = (int)$this->getVar('start'); // offset to seek to before outputting results
$vn_hits_per_block = (int)$this->getVar('itemsPerPage');
$vn_items_per_column = (int)$this->getVar('itemsPerColumn');
$vb_has_more = (bool)$this->getVar('hasMore');
$vs_search = (string)$this->getVar('search');
$vn_init_with_start = (int)$this->getVar('initializeWithStart');
$o_config = $this->getVar("config");
$o_browse_config = caGetBrowseConfig();
$va_browse_types = array_keys($o_browse_config->get("browseTypes"));

if ($qr_results->numHits() > 0) {
if (!$this->request->isAjax()) {
?>
<small class="pull-right sortValues">
<?php
if(in_array($vs_block, $va_browse_types)){
?>
<span class='multisearchFullResults'><?php print caNavLink($this->request, '<span class="glyphicon glyphicon-list"></span> '._t('Résultats complets'), '', '', 'Search', '{{{block}}}', array('search' => $vs_search)); ?></span> |
<?php
}
?>
<span class='multisearchSort'><?php print _t("trier par :"); ?> {{{sortByControl}}}</span>
{{{sortDirectionControl}}}
</small>
<?php
if(in_array($vs_block, $va_browse_types)){
?>
<?php print '<H3>'.caNavLink($this->request, $va_block_info['displayName'].' ('.$qr_results->numHits().')', '', '', 'Search', '{{{block}}}', array('search' => $vs_search)).'</H3>'; ?>
<?php
}else{
?>
<H3><?php print $va_block_info['displayName']." (".$qr_results->numHits().")"; ?></H3>
<?php
}
?>
<div class='blockResults authorityBlock'>
<div id="{{{block}}}scrollButtonPrevious" class="scrollButtonPrevious"><i class="fa fa-angle-left"></i></div><div id="{{{block}}}scrollButtonNext" class="scrollButtonNext"><i class="fa fa-angle-right"></i></div>
<div id='{{{block}}}Results' class='multiSearchResults'>
<div class='blockResultsScroller'>
<?php
}
$vn_count = 0;
$vn_i = 0;
$vb_div_open = false;
while($qr_results->nextHit()) {
if ($vn_i == 0) { print "<div class='{{{block}}}Set authoritySet'>\n"; $vb_div_open = true;}
print "<div class='{{{block}}}Result authorityResult'>".$qr_results->get('ca_tour_stops.preferred_labels.name', array('returnAsLink' => true))."</div><!-- end Result -->";
$vn_count++;
$vn_i++;
if ($vn_i == $vn_items_per_column) {
print "</div><!-- end set -->";
$vb_div_open = false;
$vn_i = 0;
}
if ((!$vn_init_with_start && ($vn_count == $vn_hits_per_block)) || ($vn_init_with_start && ($vn_count >= $vn_init_with_start))) {break;}
}

if ($vb_div_open) {print "</div><!-- end set -->";}

if (!$this->request->isAjax()) {
?>
</div><!-- end blockResultsScroller -->
</div>
</div><!-- end blockResults -->
<?php
if ($qr_results->numHits() > 3) {
?>
<div class='allLink'><?php print caNavLink($this->request, 'See '.($qr_results->numHits() - 3)." more ".$va_block_info['displayName'].($qr_results->numHits() == 4 ? ' result' : ' results'), '', '', 'Search', '{{{block}}}', array('search' => $vs_search));?></div>
<?php
}
?>
<script type="text/javascript">
jQuery(document).ready(function() {
jQuery('#{{{block}}}Results').hscroll({
name: '{{{block}}}',
itemCount: <?php print $qr_results->numHits(); ?>,
preloadCount: <?php print $vn_count; ?>,

itemsPerColumn: <?php print $vn_items_per_column; ?>,
itemWidth: jQuery('.{{{block}}}Set').outerWidth(true),
itemsPerLoad: <?php print $vn_hits_per_block; ?>,
itemLoadURL: '<?php print caNavUrl($this->request, '*', '*', '*', array('block' => $vs_block, 'search'=> $vs_search)); ?>',
itemContainerSelector: '.blockResultsScroller',

sortParameter: '{{{block}}}Sort',
sortControlSelector: '#{{{block}}}_sort',

sortDirection: '{{{sortDirection}}}',
sortDirectionParameter: '{{{block}}}SortDirection',
sortDirectionSelector: '#{{{block}}}_sort_direction',

scrollPreviousControlSelector: '#{{{block}}}scrollButtonPrevious',
scrollNextControlSelector: '#{{{block}}}scrollButtonNext',
cacheKey: '{{{cacheKey}}}'
});
});
</script>
<?php
}else{
# --- need to change sort direction to catch default setting for direction when sort order has changed
if($this->getVar("sortDirection") == "desc"){
?>
<script type="text/javascript">
jQuery('#<?php print $vs_block; ?>_sort_direction').find('span').removeClass('glyphicon-sort-by-alphabet').addClass('glyphicon-sort-by-alphabet-alt');
</script>
<?php
}else{
?>
<script type="text/javascript">
jQuery('#<?php print $vs_block; ?>_sort_direction').find('span').removeClass('glyphicon-sort-by-alphabet-alt').addClass('glyphicon-sort-by-alphabet');
</script>
<?php
}
}
}
?>

ca_tours_search_subview_html.php


<?php
/* ----------------------------------------------------------------------
* themes/default/views/Search/ca_tours_search_subview_html.php :
* ----------------------------------------------------------------------
* CollectiveAccess
* Open-source collections management software
* ----------------------------------------------------------------------
*
* Software by Whirl-i-Gig (http://www.whirl-i-gig.com)
* Copyright 2013-2014 Whirl-i-Gig
*
* For more information visit http://www.CollectiveAccess.org
*
* This program is free software; you may redistribute it and/or modify it under
* the terms of the provided license as published by Whirl-i-Gig
*
* CollectiveAccess is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTIES whatsoever, including any implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* This source code is free and modifiable under the terms of
* GNU General Public License. (http://www.gnu.org/copyleft/gpl.html). See
* the "license.txt" file for details, or visit the CollectiveAccess web site at
* http://www.CollectiveAccess.org
*
* ----------------------------------------------------------------------
*/

$qr_results = $this->getVar('result');
$va_block_info = $this->getVar('blockInfo');
$vs_block = $this->getVar('block');
$vn_start = (int)$this->getVar('start'); // offset to seek to before outputting results
$vn_hits_per_block = (int)$this->getVar('itemsPerPage');
$vn_items_per_column = (int)$this->getVar('itemsPerColumn');
$vb_has_more = (bool)$this->getVar('hasMore');
$vs_search = (string)$this->getVar('search');
$vn_init_with_start = (int)$this->getVar('initializeWithStart');
$o_config = $this->getVar("config");
$o_browse_config = caGetBrowseConfig();
$va_browse_types = array_keys($o_browse_config->get("browseTypes"));

if ($qr_results->numHits() > 0) {
if (!$this->request->isAjax()) {
?>
<small class="pull-right sortValues">
<?php
if(in_array($vs_block, $va_browse_types)){
?>
<span class='multisearchFullResults'><?php print caNavLink($this->request, '<span class="glyphicon glyphicon-list"></span> '._t('Résultats complets'), '', '', 'Search', '{{{block}}}', array('search' => $vs_search)); ?></span> |
<?php
}
?>
<span class='multisearchSort'><?php print _t("trier par :"); ?> {{{sortByControl}}}</span>
{{{sortDirectionControl}}}
</small>
<?php
if(in_array($vs_block, $va_browse_types)){
?>
<?php print '<H3>'.caNavLink($this->request, $va_block_info['displayName'].' ('.$qr_results->numHits().')', '', '', 'Search', '{{{block}}}', array('search' => $vs_search)).'</H3>'; ?>
<?php
}else{
?>
<H3><?php print $va_block_info['displayName']." (".$qr_results->numHits().")"; ?></H3>
<?php
}
?>
<div class='blockResults authorityBlock'>
<div id="{{{block}}}scrollButtonPrevious" class="scrollButtonPrevious"><i class="fa fa-angle-left"></i></div><div id="{{{block}}}scrollButtonNext" class="scrollButtonNext"><i class="fa fa-angle-right"></i></div>
<div id='{{{block}}}Results' class='multiSearchResults'>
<div class='blockResultsScroller'>
<?php
}
$vn_count = 0;
$vn_i = 0;
$vb_div_open = false;
while($qr_results->nextHit()) {
if ($vn_i == 0) { print "<div class='{{{block}}}Set authoritySet'>\n"; $vb_div_open = true;}
print "<div class='{{{block}}}Result authorityResult'>".$qr_results->get('ca_tours.preferred_labels.name', array('returnAsLink' => true))."</div><!-- end Result -->";
$vn_count++;
$vn_i++;
if ($vn_i == $vn_items_per_column) {
print "</div><!-- end set -->";
$vb_div_open = false;
$vn_i = 0;
}
if ((!$vn_init_with_start && ($vn_count == $vn_hits_per_block)) || ($vn_init_with_start && ($vn_count >= $vn_init_with_start))) {break;}
}

if ($vb_div_open) {print "</div><!-- end set -->";}

if (!$this->request->isAjax()) {
?>
</div><!-- end blockResultsScroller -->
</div>
</div><!-- end blockResults -->
<?php
if ($qr_results->numHits() > 3) {
?>
<div class='allLink'><?php print caNavLink($this->request, 'See '.($qr_results->numHits() - 3)." more ".$va_block_info['displayName'].($qr_results->numHits() == 4 ? ' result' : ' results'), '', '', 'Search', '{{{block}}}', array('search' => $vs_search));?></div>
<?php
}
?>
<script type="text/javascript">
jQuery(document).ready(function() {
jQuery('#{{{block}}}Results').hscroll({
name: '{{{block}}}',
itemCount: <?php print $qr_results->numHits(); ?>,
preloadCount: <?php print $vn_count; ?>,

itemsPerColumn: <?php print $vn_items_per_column; ?>,
itemWidth: jQuery('.{{{block}}}Set').outerWidth(true),
itemsPerLoad: <?php print $vn_hits_per_block; ?>,
itemLoadURL: '<?php print caNavUrl($this->request, '*', '*', '*', array('block' => $vs_block, 'search'=> $vs_search)); ?>',
itemContainerSelector: '.blockResultsScroller',

sortParameter: '{{{block}}}Sort',
sortControlSelector: '#{{{block}}}_sort',

sortDirection: '{{{sortDirection}}}',
sortDirectionParameter: '{{{block}}}SortDirection',
sortDirectionSelector: '#{{{block}}}_sort_direction',

scrollPreviousControlSelector: '#{{{block}}}scrollButtonPrevious',
scrollNextControlSelector: '#{{{block}}}scrollButtonNext',
cacheKey: '{{{cacheKey}}}'
});
});
</script>
<?php
}else{
# --- need to change sort direction to catch default setting for direction when sort order has changed
if($this->getVar("sortDirection") == "desc"){
?>
<script type="text/javascript">
jQuery('#<?php print $vs_block; ?>_sort_direction').find('span').removeClass('glyphicon-sort-by-alphabet').addClass('glyphicon-sort-by-alphabet-alt');
</script>
<?php
}else{
?>
<script type="text/javascript">
jQuery('#<?php print $vs_block; ?>_sort_direction').find('span').removeClass('glyphicon-sort-by-alphabet-alt').addClass('glyphicon-sort-by-alphabet');
</script>
<?php
}
}
}
?>

6. In pawtucket /themes/<your_theme>/views/Details/

And now, you need the detail pages, so add these files:

ca_tours_default_html.php


<div class="row">
<div class='col-xs-12 navTop'><!--- only shown at small screen size -->
{{{previousLink}}}{{{resultsLink}}}{{{nextLink}}}
</div><!-- end detailTop -->
<div class='navLeftRight col-xs-1 col-sm-1 col-md-1 col-lg-1'>
<div class="detailNavBgLeft">
{{{previousLink}}}{{{resultsLink}}}
</div><!-- end detailNavBgLeft -->
</div><!-- end col -->
<div class='col-xs-12 col-sm-10 col-md-10 col-lg-10'>
<div class="container">
<div class="row">
<div class='col-md-12 col-lg-12'>
<H4>{{{^ca_tours.preferred_labels.name}}}</H4>
<p><b>({{{^ca_tours.type_id}}})</b></p>
</div><!-- end col -->
</div><!-- end row -->
<div class="row">
<div class='col-md-12 col-lg-12'>
{{{<ifdef code="ca_tours.tour_description"><p><b>^ca_tours.tour_description</b></p></ifdef>}}}
{{{<ifdef code="ca_tours.description"><p>^ca_tours.description</p></ifdef>}}}
{{{<ifdef code="ca_tours.description_source"><p><small>Source:<br /> ^ca_tours.description_source</small></p></ifdef>}}}

{{{<ifcount code="ca_tour_stops" min="1" max="1"><H6>Tour stop:</H6></ifcount>}}}
{{{<ifcount code="ca_tour_stops" min="2"><H6>Tour stops</H6></ifcount>}}}
<ul>
{{{<unit relativeTo="ca_tour_stops" delimiter=" " sort="ca_tour_stops.idno" sortDirection="ASC" restrictToTypes="primary,secondary" ><li><l>^ca_tour_stops.preferred_labels</l></li></unit>}}}
</ul>
<hr>
{{{<ifcount code="ca_entities" min="1" max="1"><H6>Related entity:</H6></ifcount>}}}
{{{<ifcount code="ca_entities" min="2"><H6>Related entities:</H6></ifcount>}}}
{{{<unit relativeTo="ca_entities" delimiter="<br/>"><l>^ca_entities.preferred_labels</l> (^relationship_typename)</unit>}}}

{{{<ifcount code="ca_places" min="1" max="1"><H6>Related place:</H6></ifcount>}}}
{{{<ifcount code="ca_places" min="2"><H6>Related places:</H6></ifcount>}}}
{{{<unit relativeTo="ca_places" delimiter="<br/>"><l>^ca_places.preferred_labels</l> (^relationship_typename)</unit>}}}

{{{<ifcount code="ca_collections" min="1" max="1"><H6>Related collection:</H6></ifcount>}}}
{{{<ifcount code="ca_collections" min="2"><H6>Related collections:</H6></ifcount>}}}
{{{<unit relativeTo="ca_collections" delimiter="<br/>"><l>^ca_collections.preferred_labels</l> (^relationship_typename)</unit>}}}

{{{<ifcount code="ca_tours.external_link" min="1" max="1"><H6>Web link:</H6></ifcount>}}}
{{{<ifcount code="ca_tours.external_link" min="2"><H6>Web links:</H6></ifcount>}}}
{{{<unit relativeTo="ca_tours.external_link" delimiter="<br/>"><a href="^ca_tours.external_link.url_entry" target="_blank" >^ca_tours.external_link.url_source</a></unit>}}}

</div><!-- end col -->
</div><!-- end row -->
<div class="row">
<div class='col-md-12 col-lg-12'>
<p>This tour is located here:</p>
{{{map}}}
</div><!-- end col -->
</div><!-- end row -->
</div><!-- end container -->
</div><!-- end col -->
<div class='navLeftRight col-xs-1 col-sm-1 col-md-1 col-lg-1'>
<div class="detailNavBgRight">
{{{nextLink}}}
</div><!-- end detailNavBgLeft -->
</div><!-- end col -->
</div><!-- end row -->

ca_tour_stops_default_html.php


<div class="row">
<div class='col-xs-12 col-sm-10 col-md-10 col-lg-10'>
<div class="container" style="font-size: 16px;font-family: lato; color:black;">
<div class="row">
<div class='col-xs-12 col-md-12 col-lg-12'>
<H6>Parcours :</H6>
{{{<unit relativeTo="ca_tours" delimiter=" ; "><l>^ca_tours.preferred_labels</l></unit>}}}</p>
<h6>{{{^ca_tour_stops.type_id}}}:</h6>
<H4>{{{^ca_tour_stops.preferred_labels.name}}}</H4>
</div><!-- end col -->
</div><!-- end row -->
<div class="row">
<div class='col-xs-12 col-md-12 col-lg-12'>
{{{<ifdef code="ca_tour_stops.georeference"><small><b>Location: </b></small>}}}
{{{<ifdef code="ca_tour_stops.tour_stop_props"><small>^ca_tour_stops.tour_stop_props%delimiter=_-_</small></ifdef>}}}
{{{map}}}
</div><!-- end col -->
</div><!-- end row -->

<div class="row">
<div class='col-xs-12 col-md-12 col-lg-12'>
{{{<ifdef code="ca_tour_stops.tour_stop_description"><H6><b>^ca_tour_stops.tour_stop_description</b></h6></ifdef>}}}
{{{<ifdef code="ca_tour_stops.description"><div>^ca_tour_stops.description</div></ifdef>}}}
<hr>
{{{<ifdef code="ca_tour_stops.description_source"><H6>Source:</H6><p>^ca_tour_stops.description_source</p></ifdef>}}}
</div><!-- end col -->
</div><!-- end row -->

<div class="row">
<div class='col-xs-12 col-md-12 col-lg-12'>

{{{<ifcount code="ca_objects" min="1" max="1"><H6>Related object:</H6></ifcount>}}}
{{{<ifcount code="ca_objects" min="2"><H6>Related objects:</H6></ifcount>}}}
{{{<unit relativeTo="ca_objects" delimiter="<br/>"><l>^ca_objects.preferred_labels</l> (^relationship_typename)</unit>}}}

{{{<ifcount code="ca_entities" min="1" max="1"><H6>Related entity:</H6></ifcount>}}}
{{{<ifcount code="ca_entities" min="2"><H6>Related entities:</H6></ifcount>}}}
{{{<unit relativeTo="ca_entities" delimiter="<br/>"><l>^ca_entities.preferred_labels</l> (^relationship_typename)</unit>}}}

{{{<ifcount code="ca_places" min="1" max="1"><H6>Related place:</H6></ifcount>}}}
{{{<ifcount code="ca_places" min="2"><H6>Related places:</H6></ifcount>}}}
{{{<unit relativeTo="ca_places" delimiter="<br/>"><l>^ca_places.preferred_labels</l> (^relationship_typename)</unit>}}}
</div><!-- end col -->
</div><!-- end row -->
</div><!-- end container -->
</div><!-- end col -->
</div><!-- end row -->

Comments: I’ve removed some parts specific to my use that use customized metadatas or types… Also, the display style (css) is not the same as other detail pages (objects…) because in my case, these tours and tour stops are made to be displayed on a smartphone, walking through the campus… You will need to adapt for your case.