/** * Returns HTML for editable Geocode attribute, suitable for inclusion in a bundleable editing form * * @param array $pa_element_info Array of information about the element the bundle is being generate for * @param array $pa_options Options are: * width = Width of map + controls in pixels; default is 690 * height = Height of map + controls in pixels; default is 300 * baseLayer = Tiles to user for base layer. Should be full class name with optional constructor string (Eg. OpenLayers.Layer.Stamen('toner')); default is OpenLayers.Layer.OSM() * pointRadius = Radius, in pixels, of plotted points. Default is 5 pixels * fillColor = Color (in hex format with leading "#") to fill regions and points with * strokeWidth = Width of plotted paths, in pixels. Default is 2 * strokeColor = Color of plotted paths, in hex format with leading "#" * fillColorSelected = Color to fill regions with when selected, in hex format with leading "#" * strokeColorSelected = Color of plotted paths when selected, in hex format with leading "#" * * @return string HTML output */ public function getAttributeBundleHTML($pa_element_info, $pa_options = null) { AssetLoadManager::register('openlayers'); $o_config = Configuration::load(); $va_element_width = caParseFormElementDimension($pa_element_info['settings']['fieldWidth']); $vn_element_width = $va_element_width['dimension']; $va_element_height = caParseFormElementDimension($pa_element_info['settings']['fieldHeight']); $vn_element_height = $va_element_height['dimension']; $va_options = caGetOptions($pa_options, array('width' => $vn_element_width, 'height' => $vn_element_height)); if (($vn_width = $va_options['width']) < 100) { $vn_width = 690; } if (($vn_height = $va_options['height']) < 100) { $vn_height = 300; } if (!($vs_base_layer = $va_options['baseLayer'])) { if (!($vs_base_layer = $o_config->get('openlayers_base_layer'))) { $vs_base_layer = 'OpenLayers.Layer.OSM()'; } } if (($vn_point_radius = $va_options['pointRadius']) < 1) { if (!($vn_point_radius = $o_config->get('openlayers_point_radius'))) { $vn_point_radius = 5; } } if (($vs_fill_color = $va_options['fillColor']) < 1) { if (!($vs_fill_color = $o_config->get('openlayers_fill_color'))) { $vs_fill_color = '#ffcc66'; } } if (($vn_stroke_width = $va_options['strokeWidth']) < 1) { if (!($vn_stroke_width = $o_config->get('openlayers_stroke_width'))) { $vn_stroke_width = 2; } } if (($vs_stroke_color = $va_options['strokeColor']) < 1) { if (!($vs_stroke_color = $o_config->get('openlayers_stroke_color'))) { $vs_stroke_color = '#ff9933'; } } if (($vs_fill_color_selected = $va_options['fillColorSelected']) < 1) { if (!($vs_fill_color_selected = $o_config->get('openlayers_fill_color_selected'))) { $vs_fill_color_selected = '#66ccff'; } } if (($vs_stroke_color_selected = $va_options['strokeColorSelected']) < 1) { if (!($vs_stroke_color_selected = $o_config->get('openlayers_stroke_color_selected'))) { $vs_stroke_color_selected = '#3399ff'; } } $po_request = isset($pa_options['request']) ? $pa_options['request'] : null; $vs_id = $pa_element_info['element_id']; $vs_element = '<div id="{fieldNamePrefix}mapholder_' . $vs_id . '_{n}" class="mapholder" style="width:' . $vn_width . 'spx; height:' . ($vn_height + 40) . 'px; float: left; margin:-18px 0 0 0;">'; $vs_element .= '<div class="olMapSearchControls" id="{fieldNamePrefix}Controls_{n}">'; if ($po_request) { $vs_element .= '<div class="olMapSearchBox">'; $vs_element .= '<input type="text" class="olMapSearchText" name="{fieldNamePrefix}' . $pa_element_info['element_id'] . '_{n}_search" id="{fieldNamePrefix}' . $pa_element_info['element_id'] . '_{n}_search" size="30" value="" autocomplete="off" onfocus="this.value = \'\';" onkeypress="return map_geocode_' . $vs_id . '(event);"/>'; $vs_element .= "<a href='#' onclick='map_geocode_{$vs_id}();'>" . caGetThemeGraphic($po_request, 'buttons/glass.png', array('alt' => _t('Search'), 'class' => 'olMapSearchBoxIcon', 'id' => '{fieldNamePrefix}' . $pa_element_info['element_id'] . '_{n}_search_button')) . "</a>"; $vs_element .= '</div>'; } $vs_element .= '<div class="olMapKmlControl" id="{fieldNamePrefix}showKmlControl_{n}">'; $vs_element .= '<div style="position: absolute; bottom: 0px; left: 0px;"><a href="#" class="button" id="{fieldNamePrefix}showKmlControl_{n}_button">' . _t('Upload KML file') . ' ›</a></div>'; $vs_element .= '</div>'; $vs_element .= '</div>'; $vs_element .= '<div class="olMapKMLInput" id="{fieldNamePrefix}KmlControl_{n}">'; $vs_element .= _t("Select KML or KMZ file") . ': <input type="file" name="{fieldNamePrefix}' . $pa_element_info['element_id'] . '_{n}"/><a href="#" class="button" id="{fieldNamePrefix}hideKmlControl_{n}_button">' . _t('Use map') . ' ›</a>'; $vs_element .= '</div>'; $vs_element .= '<div class="olMap" id="{fieldNamePrefix}map_' . $vs_id . '_{n}" style="width:' . $vn_width . 'px; height:' . $vn_height . 'px;"> </div>'; $vs_element .= '</div>'; $vs_element .= "<script type='text/javascript'>\n\t\t\n\tvar map_{$vs_id};\n\tvar points_{$vs_id};\n\tjQuery(document).ready(function() {\n\t\t// Styles\n\t\tvar styles_{$vs_id} = new OpenLayers.StyleMap({\n\t\t\t'default': new OpenLayers.Style({\n\t\t\t\tpointRadius: '{$vn_point_radius}',\n\t\t\t\tfillColor: '{$vs_fill_color}',\n\t\t\t\tstrokeColor: '{$vs_stroke_color}',\n\t\t\t\tstrokeWidth: '{$vn_stroke_width}',\n\t\t\t\tgraphicZIndex: 1\n\t\t\t}),\n\t\t\t'select': new OpenLayers.Style({\n\t\t\t\tfillColor: '{$vs_fill_color_selected}',\n\t\t\t\tstrokeColor: '{$vs_stroke_color_selected}',\n\t\t\t\tgraphicZIndex: 2\n\t\t\t})\n\t\t});\n\n\t\tvar map_{$vs_id}_editing_toolbar;\n\t\t\n\t\tfunction map_serialize_features_{$vs_id}() {\n\t\t\t// get all points\n\t\t\tvar features = [];\n\t\t\tfor(var i=0; i < points_{$vs_id}.features.length; i++) {\n\t\t\t\tvar pl = [];\n\t\t\t\tvar geometry_type = points_{$vs_id}.features[i].geometry.CLASS_NAME;\n\t\t\t\tvar n = points_{$vs_id}.features[i].geometry.getVertices();\n\t\t\t\tfor (var j=0; j<n.length; j++) {\n\t\t\t\t\tvar np = n[j].clone();\n\t\t\t\t\tnp.transform(map_{$vs_id}.getProjectionObject(), new OpenLayers.Projection('EPSG:4326'));\n\t\t\t\t\tpl.push(np.y + ',' + np.x);\n\t\t\t\t}\n\t\t\t\tif ((pl.length > 1) && (geometry_type == 'OpenLayers.Geometry.Polygon')) { pl.push(pl[0]); } // close polygons\n\t\t\t\tfeatures.push(pl.join(';'));\n\t\t\t}\n\t\t\t\n\t\t\tjQuery('#{fieldNamePrefix}" . $pa_element_info['element_id'] . "_{n}').val('[' + features.join(':') + ']');\n\t\t}\n\t\t\n\t\t// Set up layer for added points/paths\n\t\tpoints_{$vs_id} = new OpenLayers.Layer.Vector('Points', {\n\t\t\tstyleMap: styles_{$vs_id},\n\t\t\trendererOptions: {zIndexing: true},\n\t\t\teventListeners: {\n\t\t\t\t'featureadded': map_serialize_features_{$vs_id}\n\t\t\t}\n\t\t});\n\t\t\n\t\tmap_{$vs_id}_editing_toolbar = new OpenLayers.Control.EditingToolbar(points_{$vs_id});\n\t\t\n\t\t// Set up map\n\t\tmap_{$vs_id} = new OpenLayers.Map({\n\t\t\tdiv: '{fieldNamePrefix}map_{$vs_id}_{n}',\n\t\t\tlayers: [new {$vs_base_layer}],\n\t\t\tcontrols: [\n\t\t\t\tnew OpenLayers.Control.Navigation({\n\t\t\t\t\tdragPanOptions: {\n\t\t\t\t\t\tenableKinetic: true\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t\t\tnew OpenLayers.Control.Zoom()\n\t\t\t],\n\t\t\tcenter: [0,0],\n\t\t\tzoom: 1\n\t\t});\n\t\t\n\t\tvar map_{$vs_id}_drag_ctrl = new OpenLayers.Control.DragFeature(points_{$vs_id}, {\n\t\t\tonComplete: function(f) { map_serialize_features_{$vs_id}(f); }\n\t\t});\n\t\tmap_{$vs_id}.addControl(map_{$vs_id}_drag_ctrl);\n\t\tmap_{$vs_id}_drag_ctrl.activate();\n\t\t\n\t\t// add delete control\n\t\tvar map_{$vs_id}_delete_button = new OpenLayers.Control.Button ({displayClass: 'olControlDelete', trigger: function() { \n\t\t\tif (points_{$vs_id}.selectedFeatures) { \n\t\t\t\tpoints_{$vs_id}.removeFeatures(points_{$vs_id}.selectedFeatures);\n\t\t\t\tmap_serialize_features_{$vs_id}();\n\t\t\t}\n\t\t}, title: '" . _t('Remove') . "'});\n\t\tvar map_{$vs_id}_delete_panel = new OpenLayers.Control.Panel({type: OpenLayers.Control.TYPE_BUTTON, displayClass: 'olControlDeletePanel'});\n\t\tmap_{$vs_id}_delete_panel.addControls([map_{$vs_id}_delete_button]);\n\t\tmap_{$vs_id}.addControl(map_{$vs_id}_delete_panel);\n\t\tmap_{$vs_id}_delete_button.activate();\n\t\t\n\t\t// Grab current map coordinates from input\n\t\tvar map_{$ps_id}_loc_str = '{" . $pa_element_info['element_id'] . "}';\n\t\tvar map_{$ps_id}_loc_features = map_{$ps_id}_loc_str.match(/\\[([\\d\\,\\-\\.\\:\\;]+)\\]/)\n\t\tif (map_{$ps_id}_loc_features && (map_{$ps_id}_loc_features.length > 1)) {\n\t\t\tmap_{$ps_id}_loc_features = map_{$ps_id}_loc_features[1].split(/:/);\n\t\t} else {\n\t\t\tmap_{$ps_id}_loc_features = [];\n\t\t}\n\t\tvar features_{$vs_id} = [];\n\t\t\n\t\tvar i, j, c=0;\n\t\tfor(i=0; i < map_{$ps_id}_loc_features.length; i++) {\n\t\t\tvar ptlist = map_{$ps_id}_loc_features[i].split(/;/);\n\t\t\t\n\t\t\tif (ptlist.length > 1) {\n\t\t\t\t// path\n\t\t\t\tvar ptolist = [];\n\t\t\t\tfor(j=0; j < ptlist.length; j++) {\n\t\t\t\t\tvar pt = ptlist[j].split(/,/);\n\t\t\t\t\tptolist.push(new OpenLayers.Geometry.Point(pt[1], pt[0]));\n\t\t\t\t\t\n\t\t\t\t}\n\t\t\t\tfeatures_{$vs_id}.push(new OpenLayers.Feature.Vector(\n\t\t\t\t\tnew OpenLayers.Geometry.LineString(ptolist).transform(new OpenLayers.Projection('EPSG:4326'),map_{$vs_id}.getProjectionObject()), {}\n\t\t\t\t));\n\t\t\t} else {\n\t\t\t\t// point\n\t\t\t\tvar pt = ptlist[0].split(/,/);\n\t\t\t\tfeatures_{$vs_id}.push(new OpenLayers.Feature.Vector(\n\t\t\t\t\tnew OpenLayers.Geometry.Point(pt[1], pt[0]).transform(new OpenLayers.Projection('EPSG:4326'),map_{$vs_id}.getProjectionObject()), {}\n\t\t\t\t));\n\t\t\t}\n\t\t\tc++;\n\t\t}\n\t\n\t\tpoints_{$vs_id}.addFeatures(features_{$vs_id});\n\t\t\n\t\tvar map_{$vs_id}_highlight_ctrl = new OpenLayers.Control.SelectFeature(points_{$vs_id}, {\n\t\t\thover: false,\n\t\t\trenderIntent: 'temporary',\n\t\t\tmultiple: true, clickout: true, toggle: true, box: true,\n\t\t\teventListeners: {}\n\t\t});\n\t\tmap_{$vs_id}.addControl(map_{$vs_id}_highlight_ctrl);\n\t\tmap_{$vs_id}_highlight_ctrl.activate();\n\t\t\n\t\tmap_{$vs_id}.addControl(map_{$vs_id}_editing_toolbar);\n\t\tmap_{$vs_id}_editing_toolbar.activate();\n \n\t\tmap_{$vs_id}.addLayer(points_{$vs_id});\n\t\t\n\t\tif (c > 0) {\n\t\t\tmap_{$vs_id}.zoomToExtent(points_{$vs_id}.getDataExtent());\n\t\t\tif (map_{$vs_id}.zoom > 14) { map_{$vs_id}.zoomTo(14); }\n\t\t}\n\t\t\n\t\tjQuery('#{fieldNamePrefix}showKmlControl_{n}_button').click(function(event) {\n\t\t\tevent.preventDefault();\n\t\t\tjQuery('#{fieldNamePrefix}Controls_{n}').hide(200, function() {\n\t\t\t\tjQuery('#{fieldNamePrefix}KmlControl_{n}').slideDown(200);\n\t\t\t});\n\t\t});\n\t\t\n\t\tjQuery('#{fieldNamePrefix}hideKmlControl_{n}_button').click(function(event) {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tjQuery(this).parent().hide(200, function() {\n\t\t\t\t\tjQuery('#{fieldNamePrefix}Controls_{n}').slideDown(200);\n\t\t\t\t});\n\t\t\t});\n\t});\n\t\tfunction map_geocode_{$vs_id}(e) {\n\t\t\tif (e && ((e.keyCode || e.which || e.charCode || 0) !== 13)) { return true; }\n\t\t\tvar t = jQuery('#{fieldNamePrefix}" . $pa_element_info['element_id'] . "_{n}_search').val();\n\t\t\tjQuery('#{fieldNamePrefix}" . $pa_element_info['element_id'] . "_{n}_search_button').attr('src', '" . caGetThemeGraphicURL($po_request, '/icons/indicator.gif') . "');\n\t\t\tvar geocoder = new google.maps.Geocoder();\n\t\t\tgeocoder.geocode( { 'address': t}, function(results, status) {\n\t\t\t\tjQuery('#{fieldNamePrefix}" . $pa_element_info['element_id'] . "_{n}_search_button').attr('src', '" . caGetThemeGraphicURL($po_request, '/buttons/glass.png') . "');\n\t\t\t\tif (status == google.maps.GeocoderStatus.OK) {\n\t\t\t\t\tvar loc = results[0]['geometry']['location'];\n\t\t\t\t\tvar pt = new OpenLayers.LonLat(loc.lng(), loc.lat()).transform(new OpenLayers.Projection('EPSG:4326'),map_{$vs_id}.getProjectionObject());\n\t\t\t\t\tmap_{$vs_id}.panTo(pt);\n\t\t\t\t\tmap_{$vs_id}.zoomTo((results[0]['geometry']['location_type'] == 'APPROXIMATE') ? 10 : 14);\n\t\t\t\t\tpoints_{$vs_id}.addFeatures([new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(loc.lng(), loc.lat()).transform(new OpenLayers.Projection('EPSG:4326'),map_{$vs_id}.getProjectionObject()))]);\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn false;\n\t\t}\n\t</script>"; $vs_element .= '<input class="coordinates mapCoordinateDisplay" type="hidden" name="{fieldNamePrefix}' . $pa_element_info['element_id'] . '_{n}" id="{fieldNamePrefix}' . $pa_element_info['element_id'] . '_{n}"/>'; return $vs_element; }
/** * Returns an associative array of relationship types for the relationship * organized by the sub_type_id specified by $ps_orientation. If $ps_orientation is the name of the "right" table * then sub_type_left_id is used for keys in the array, if $ps_orientation is the name of the "left" table * then sub_type_right_id is used for keys. * * For example, for ca_objects_x_entities, if $ps_orientation is ca_objects then then sub_type_right_id is * used as the key; if ca_entities is passed then sub_type_left_id is used; if a table name is passed that * is not either side of the relation then an empty array is returned * */ public function getRelationshipTypesBySubtype($ps_orientation, $pn_type_id, $pa_options = null) { unset($pa_options['request']); if (!$this->hasField('type_id')) { return array(); } $vs_left_table_name = $this->getLeftTableName(); $vs_right_table_name = $this->getRightTableName(); $vb_dont_include_subtypes_in_type_restriction = caGetOptions('dont_include_subtypes_in_type_restriction', $pa_options, false); $o_db = $this->getDb(); $t_rel_type = new ca_relationship_types(); $vs_restrict_to_relationship_type_sql = ''; if (isset($pa_options['restrict_to_relationship_types']) && $pa_options['restrict_to_relationship_types']) { if (!is_array($pa_options['restrict_to_relationship_types'])) { $pa_options['restrict_to_relationship_types'] = array($pa_options['restrict_to_relationship_types']); } if (sizeof($pa_options['restrict_to_relationship_types'])) { $va_restrict_to_type_list = array(); foreach ($pa_options['restrict_to_relationship_types'] as $vs_type_code) { if (!strlen(trim($vs_type_code))) { continue; } $va_criteria = array('table_num' => $this->tableNum()); if (is_numeric($vs_type_code)) { $va_criteria['type_id'] = (int) $vs_type_code; } else { $va_criteria['type_code'] = $vs_type_code; } if ($t_rel_type->load($va_criteria)) { $va_restrict_to_type_list[] = "(crt.hier_left >= " . $t_rel_type->get('hier_left') . " AND crt.hier_right <= " . $t_rel_type->get('hier_right') . ")"; } } if (sizeof($va_restrict_to_type_list)) { $vs_restrict_to_relationship_type_sql = " AND (" . join(' OR ', $va_restrict_to_type_list) . ")"; } } } $qr_res = $o_db->query("\n\t\t\t\tSELECT *\n\t\t\t\tFROM ca_relationship_types crt\n\t\t\t\tINNER JOIN ca_relationship_type_labels AS crtl ON crt.type_id = crtl.type_id\n\t\t\t\tWHERE\n\t\t\t\t\t(crt.table_num = ?)\n\t\t\t\t\t{$vs_restrict_to_relationship_type_sql}\n\t\t\t", $this->tableNum()); // Support hierarchical subtypes - if the subtype restriction is a type with parents then include those as well // Allows subtypes to "inherit" bindings from parent types $t_list_item = new ca_list_items($pn_type_id); if (!$vb_dont_include_subtypes_in_type_restriction) { if (!is_array($va_ancestor_ids = $t_list_item->getHierarchyAncestors(null, array('idsOnly' => true, 'includeSelf' => true)))) { $va_ancestor_ids = array(); } // remove hierarchy root from ancestor list, otherwise invalid bindings // from root nodes (which are not "real" rel types) may be inherited array_pop($va_ancestor_ids); } else { $va_ancestor_ids = array($pn_type_id); } $va_types = array(); $va_parent_ids = array(); $vn_l = 0; $vn_root_id = $t_rel_type->load(array('parent_id' => null, 'table_num' => $this->tableNum())) ? $t_rel_type->getPrimaryKey() : null; $va_hier = array(); if ($vs_left_table_name === $vs_right_table_name) { // ---------------------------------------------------------------------------------------- // self relationship while ($qr_res->nextRow()) { $va_row = $qr_res->getRow(); $vn_parent_id = $va_row['parent_id']; $va_hier[$vn_parent_id][] = $va_row['type_id']; // skip type if it has a subtype set and it's not in our list $vs_subtype_orientation = null; $vs_subtype = null; if ($va_row['sub_type_left_id'] && !in_array($va_row['sub_type_left_id'], $va_ancestor_ids)) { // not left if ($va_row['sub_type_right_id'] && !in_array($va_row['sub_type_right_id'], $va_ancestor_ids)) { // not left and not right continue; } else { // not left and right $vs_subtype = $va_row['sub_type_left_id']; $vs_subtype_orientation = "left"; } } else { if ($va_row['sub_type_left_id'] && in_array($va_row['sub_type_left_id'], $va_ancestor_ids)) { // left if ($va_row['sub_type_right_id'] && in_array($va_row['sub_type_right_id'], $va_ancestor_ids)) { // left and right $vs_subtype = $va_row['sub_type_right_id']; $vs_subtype_orientation = ""; } else { // left and not right $vs_subtype_orientation = "right"; $vs_subtype = $va_row['sub_type_right_id']; } } } if (!$vs_subtype) { $vs_subtype = 'NULL'; } switch ($vs_subtype_orientation) { case 'left': $va_tmp = $va_row; $vs_key = strlen($va_tmp['rank']) > 0 ? sprintf("%08d", (int) $va_tmp['rank']) . preg_replace('![^A-Za-z0-9_]+!', '_', $va_tmp['typename_reverse']) : preg_replace('![^A-Za-z0-9_]+!', '_', $va_tmp['typename_reverse']); $va_tmp['typename'] = $va_tmp['typename_reverse']; unset($va_tmp['typename_reverse']); // we pass the typename adjusted for direction in 'typename', so there's no need to include typename_reverse in the returned values $va_types[$vn_parent_id][$vs_subtype][$vs_key][$va_row['type_id']][$va_row['locale_id']] = $va_tmp; break; case 'right': $va_tmp = $va_row; $vs_key = strlen($va_tmp['rank']) > 0 ? sprintf("%08d", (int) $va_tmp['rank']) . preg_replace('![^A-Za-z0-9_]+!', '_', $va_tmp['typename']) : preg_replace('![^A-Za-z0-9_]+!', '_', $va_tmp['typename']); unset($va_tmp['typename_reverse']); // we pass the typename adjusted for direction in 'typename', so there's no need to include typename_reverse in the returned values $va_types[$vn_parent_id][$vs_subtype][$vs_key][$va_row['type_id']][$va_row['locale_id']] = $va_tmp; break; default: $va_tmp = $va_row; if (trim($va_tmp['typename']) == trim($va_tmp['typename_reverse'])) { // // If the sides of the self-relationship are the same then treat it like a normal relationship type: one entry in the // list and a plain type_id value // unset($va_tmp['typename_reverse']); // we pass the typename adjusted for direction in 'typename', so there's no need to include typename_reverse in the returned values $va_tmp['direction'] = null; $vs_key = strlen($va_tmp['rank']) > 0 ? sprintf("%08d", (int) $va_tmp['rank']) . preg_replace('![^A-Za-z0-9_]+!', '_', $va_tmp['typename']) : preg_replace('![^A-Za-z0-9_]+!', '_', $va_tmp['typename']); $va_types[$vn_parent_id][$vs_subtype][$vs_key][$va_row['type_id']][$va_row['locale_id']] = $va_tmp; } else { // // If each side of the self-relationship type are different then add both to the list with special type_id values that // indicate the directionality of the typename (ltor = left to right = "typename"; rtor = right to left = "typename_reverse") // $va_tmp = $va_row; unset($va_tmp['typename_reverse']); // we pass the typename adjusted for direction in 'typename', so there's no need to include typename_reverse in the returned values $vs_key = strlen($va_tmp['rank']) > 0 ? sprintf("%08d", (int) $va_tmp['rank']) . preg_replace('![^A-Za-z0-9_]+!', '_', $va_tmp['typename']) : preg_replace('![^A-Za-z0-9_]+!', '_', $va_tmp['typename']); $va_tmp['direction'] = 'ltor'; $va_types[$vn_parent_id][$vs_subtype][$vs_key]['ltor_' . $va_row['type_id']][$va_row['locale_id']] = $va_tmp; $va_tmp = $va_row; $va_tmp['typename'] = $va_tmp['typename_reverse']; unset($va_tmp['typename_reverse']); // we pass the typename adjusted for direction in 'typename', so there's no need to include typename_reverse in the returned values $vs_key = strlen($va_tmp['rank']) > 0 ? sprintf("%08d", (int) $va_tmp['rank']) . preg_replace('![^A-Za-z0-9_]+!', '_', $va_tmp['typename_reverse']) : preg_replace('![^A-Za-z0-9_]+!', '_', $va_tmp['typename_reverse']); $va_tmp['direction'] = 'rtol'; $va_types[$vn_parent_id][$vs_subtype][$vs_key]['rtol_' . $va_row['type_id']][$va_row['locale_id']] = $va_tmp; } break; } } $va_types = $this->_processRelationshipHierarchy($vn_root_id, $va_hier, $va_types, 1); $va_processed_types = array('_type_map' => array()); $va_subtype_lookups = array(); foreach ($va_types as $vs_subtype => $va_types_by_subtype) { $va_types_by_locale = array(); foreach ($va_types_by_subtype as $vs_key => $va_types_by_key) { foreach ($va_types_by_key as $vs_k => $va_v) { foreach ($va_v as $vs_k2 => $vs_v2) { $va_types_by_locale[$vs_k][$vs_k2] = $vs_v2; } } } if (!$vb_dont_include_subtypes_in_type_restriction) { // include mapping from parent type used in restriction to child types that inherit the binding if ($vs_subtype != 'NULL' && (!isset($va_subtype_lookups[$vs_subtype]) || !$va_subtype_lookups[$vs_subtype])) { $va_children = $t_list_item->getHierarchyChildren($vs_subtype, array('idsOnly' => true)); foreach ($va_children as $vn_child) { $va_processed_types['_type_map'][$vn_child] = $vs_subtype; } $va_subtype_lookups[$vs_subtype] = true; } } $va_processed_types[$vs_subtype] = caExtractValuesByUserLocale($va_types_by_locale, null, null, array('returnList' => true)); } } else { // ---------------------------------------------------------------------------------------- // regular relationship if (!in_array($ps_orientation, array($vs_left_table_name, $vs_right_table_name))) { return array(); } while ($qr_res->nextRow()) { $va_row = $qr_res->getRow(); $vn_parent_id = $va_row['parent_id']; $va_hier[$vn_parent_id][] = $va_row['type_id']; if ($ps_orientation == $vs_left_table_name) { // right-to-left // expand subtype $va_subtypes_to_check = $va_row['sub_type_left_id'] > 0 ? caMakeTypeIDList($vs_left_table_name, array($va_row['sub_type_left_id'])) : null; // skip type if it has a subtype set and it's not in our list if (!(!$va_subtypes_to_check || sizeof(array_intersect($va_subtypes_to_check, $va_ancestor_ids)))) { continue; } $vs_subtype = $va_row['sub_type_right_id']; $vs_key = strlen($va_row['rank']) > 0 ? sprintf("%08d", (int) $va_row['rank']) . preg_replace('![^A-Za-z0-9_]+!', '_', $va_row['typename']) : preg_replace('![^A-Za-z0-9_]+!', '_', $va_row['typename']); } else { // left-to-right // expand subtype $va_subtypes_to_check = $va_row['sub_type_right_id'] > 0 ? caMakeTypeIDList($vs_right_table_name, array($va_row['sub_type_right_id'])) : null; // skip type if it has a subtype set and it's not in our list if (!(!$va_subtypes_to_check || sizeof(array_intersect($va_subtypes_to_check, $va_ancestor_ids)))) { continue; } $vs_subtype = $va_row['sub_type_left_id']; $va_row['typename'] = $va_row['typename_reverse']; $vs_key = strlen($va_row['rank']) > 0 ? sprintf("%08d", (int) $va_row['rank']) . preg_replace('![^A-Za-z0-9_]+!', '_', $va_row['typename_reverse']) : preg_replace('![^A-Za-z0-9_]+!', '_', $va_row['typename_reverse']); } unset($va_row['typename_reverse']); // we pass the typename adjusted for direction in '_display', so there's no need to include typename_reverse in the returned values if (!$vs_subtype) { $vs_subtype = 'NULL'; } $vn_type_id = $va_row['type_id']; $va_types[$vn_parent_id][$vs_subtype][$vs_key][$vn_type_id][$va_row['locale_id']] = $va_row; } $va_types = $this->_processRelationshipHierarchy($vn_root_id, $va_hier, $va_types, 1); $va_processed_types = array('_type_map' => array()); $va_subtype_lookups = array(); foreach ($va_types as $vs_subtype => $va_types_by_subtype) { $va_types_by_locale = array(); foreach ($va_types_by_subtype as $vs_key => $va_types_by_key) { foreach ($va_types_by_key as $vn_locale_id => $va_t) { if (!is_array($va_types_by_locale[$vn_locale_id])) { $va_types_by_locale[$vn_locale_id] = array(); } $va_types_by_locale[$vn_locale_id] += $va_t; } } if (!$vb_dont_include_subtypes_in_type_restriction) { // include mapping from parent type used in restriction to child types that inherit the binding if ($vs_subtype != 'NULL' && (!isset($va_subtype_lookups[$vs_subtype]) || !$va_subtype_lookups[$vs_subtype])) { $va_children = $t_list_item->getHierarchyChildren($vs_subtype, array('idsOnly' => true)); foreach ($va_children as $vn_child) { $va_processed_types['_type_map'][$vn_child] = $vs_subtype; } $va_subtype_lookups[$vs_subtype] = true; } } $va_processed_types[$vs_subtype] = caExtractValuesByUserLocale($va_types_by_locale, null, null, array('returnList' => true)); } } return $va_processed_types; }