query_select('catalogues', array('*'), array('c_name' => $catalogue_name), '', 1); if (!array_key_exists(0, $catalogue_rows)) { if ($fail_ok) { return null; } warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'catalogue')); } $catalogues_cache[$catalogue_name] = $catalogue_rows[0]; } return $catalogues_cache[$catalogue_name]; } /** * Render a catalogue box. * * @param array $row Catalogue row * @param ID_TEXT $zone Zone to link through to * @param boolean $give_context Whether to include context (i.e. say WHAT this is, not just show the actual content) * @param boolean $include_breadcrumbs Whether to include breadcrumbs (if there are any) * @param ?AUTO_LINK $root Virtual root to use (null: none) * @param ID_TEXT $guid Overridden GUID to send to templates (blank: none) * @return Tempcode The catalogue box */ function render_catalogue_entry_box($row, $zone = '_SEARCH', $give_context = true, $include_breadcrumbs = true, $root = null, $guid = '') { if (is_null($row)) { // Should never happen, but we need to be defensive return new Tempcode(); } require_lang('catalogues'); require_css('catalogues'); global $SEARCH_CATALOGUE_ENTRIES_CATALOGUES_CACHE; $catalogue_name = $row['c_name']; if (array_key_exists($catalogue_name, $SEARCH_CATALOGUE_ENTRIES_CATALOGUES_CACHE)) { $catalogue = $SEARCH_CATALOGUE_ENTRIES_CATALOGUES_CACHE[$catalogue_name]; } else { $catalogue = load_catalogue_row($catalogue_name); } $tpl_set = $catalogue_name; $_breadcrumbs = null; $display = get_catalogue_entry_map($row, $catalogue, 'SEARCH', $tpl_set, $root, null, null, false, true, null, $_breadcrumbs, false, $zone); $breadcrumbs = mixed(); if ($include_breadcrumbs) { $_breadcrumbs = catalogue_category_breadcrumbs($row['cc_id'], ($root === null) ? get_param_integer('keep_catalogue_' . $catalogue['c_name'] . '_root', null) : $root, false); $breadcrumbs = breadcrumb_segments_to_tempcode($_breadcrumbs); } $tpl_set = $catalogue_name; return do_template('CATALOGUE_' . $tpl_set . '_FIELDMAP_ENTRY_WRAP', array('_GUID' => ($guid != '') ? $guid : 'dfg3rergt5g433f', 'GIVE_CONTEXT' => $give_context, 'BREADCRUMBS' => $breadcrumbs) + $display, null, false, 'CATALOGUE_DEFAULT_FIELDMAP_ENTRY_WRAP'); } /** * Get Tempcode for a catalogue category 'feature box' for the given row * * @param array $row The database field row of it * @param ID_TEXT $zone The zone to use * @param boolean $give_context Whether to include context (i.e. say WHAT this is, not just show the actual content) * @param boolean $include_breadcrumbs Whether to include breadcrumbs (if there are any) * @param ?AUTO_LINK $root Virtual root to use (null: none) * @param boolean $attach_to_url_filter Whether to copy through any filter parameters in the URL, under the basis that they are associated with what this box is browsing * @param ID_TEXT $guid Overridden GUID to send to templates (blank: none) * @return Tempcode A box for it, linking to the full page */ function render_catalogue_category_box($row, $zone = '_SEARCH', $give_context = true, $include_breadcrumbs = true, $root = null, $attach_to_url_filter = false, $guid = '') { if (is_null($row)) { // Should never happen, but we need to be defensive return new Tempcode(); } require_lang('catalogues'); $just_category_row = db_map_restrict($row, array('id', 'cc_description')); // URL $map = array('page' => 'catalogues', 'type' => 'category', 'id' => $row['id']); if ($root !== null) { $map['keep_catalogue_' . $row['c_name'] . '_root'] = $root; } if ($attach_to_url_filter) { $map += propagate_filtercode(); } $url = build_url($map, $zone); // Title $_title = get_translated_text($row['cc_title']); $title = $_title; if ($give_context) { $catalogue_title = get_translated_text($GLOBALS['SITE_DB']->query_select_value('catalogues', 'c_title', array('c_name' => $row['c_name']))); $title = do_lang('CONTENT_IS_OF_TYPE', do_lang('CATALOGUE_GENERIC_CATEGORY', $catalogue_title), $_title); } // Description $content = get_translated_tempcode('catalogue_categories', $just_category_row, 'cc_description'); // Breadcrumbs $breadcrumbs = mixed(); if ($include_breadcrumbs) { $breadcrumbs = breadcrumb_segments_to_tempcode(catalogue_category_breadcrumbs($row['id'], ($root === null) ? get_param_integer('keep_catalogue_' . $row['c_name'] . '_root', null) : $root, $attach_to_url_filter)); } // Image $rep_image = mixed(); $_rep_image = mixed(); if ($row['rep_image'] != '') { $_rep_image = $row['rep_image']; if (url_is_local($_rep_image)) { $_rep_image = get_custom_base_url() . '/' . $_rep_image; } require_code('images'); $rep_image = do_image_thumb($row['rep_image'], $_title, false); } // Metadata $child_counts = count_catalogue_category_children($row['id']); $num_children = $child_counts['num_children_children']; $num_entries = $child_counts['num_entries_children']; $entry_details = do_lang_tempcode('CATEGORY_SUBORDINATE', escape_html(integer_format($num_entries)), escape_html(integer_format($num_children))); // Render return do_template('SIMPLE_PREVIEW_BOX', array( '_GUID' => ($guid != '') ? $guid : ('e3fbbe807f75c0aa24626e06082ae731_' . $row['c_name']), 'ID' => strval($row['id']), 'TITLE' => $title, 'TITLE_PLAIN' => $_title, '_REP_IMAGE' => $_rep_image, 'REP_IMAGE' => $rep_image, 'BREADCRUMBS' => $breadcrumbs, 'SUMMARY' => $content, 'ENTRY_DETAILS' => $entry_details, 'URL' => $url, 'FRACTIONAL_EDIT_FIELD_NAME' => $give_context ? null : 'title', 'FRACTIONAL_EDIT_FIELD_URL' => $give_context ? null : '_SEARCH:cms_catalogues:__edit_catalogue:' . $row['c_name'], 'RESOURCE_TYPE' => 'catalogue_category', )); } /** * Render a catalogue box. * * @param array $row Catalogue row * @param ID_TEXT $zone Zone to link through to * @param boolean $give_context Whether to include context (i.e. say WHAT this is, not just show the actual content) * @param ID_TEXT $guid Overridden GUID to send to templates (blank: none) * @return Tempcode The catalogue box */ function render_catalogue_box($row, $zone = '_SEARCH', $give_context = true, $guid = '') { if (is_null($row)) { // Should never happen, but we need to be defensive return new Tempcode(); } require_lang('catalogues'); $just_catalogue_row = db_map_restrict($row, array('c_name', 'c_description')); if ($row['c_is_tree'] == 1) { $url = build_url(array('page' => 'catalogues', 'type' => 'category', 'catalogue_name' => $row['c_name']), $zone); } else { $url = build_url(array('page' => 'catalogues', 'type' => 'index', 'id' => $row['c_name'], 'tree' => $row['c_is_tree']), $zone); } $_title = get_translated_text($row['c_title']); $title = $give_context ? do_lang('CONTENT_IS_OF_TYPE', do_lang('CATALOGUE'), $_title) : $_title; $summary = get_translated_tempcode('catalogues', $just_catalogue_row, 'c_description'); $num_children = $GLOBALS['SITE_DB']->query_select_value('catalogue_categories', 'COUNT(*)', array('c_name' => $row['c_name'])); $num_entries = $GLOBALS['SITE_DB']->query_select_value('catalogue_entries', 'COUNT(*)', array('c_name' => $row['c_name'])); $entry_details = do_lang_tempcode(($row['c_is_tree'] == 1) ? 'CATEGORY_SUBORDINATE' : 'CATEGORY_SUBORDINATE_2', escape_html(integer_format($num_entries)), escape_html(integer_format($num_children))); return do_template('SIMPLE_PREVIEW_BOX', array( '_GUID' => ($guid != '') ? $guid : ('8d7eaf6bb3170a92fd6a4876462e6f2e_' . $row['c_name']), 'ID' => $row['c_name'], 'TITLE' => $title, 'TITLE_PLAIN' => $_title, 'SUMMARY' => $summary, 'ENTRY_DETAILS' => $entry_details, 'URL' => $url, 'FRACTIONAL_EDIT_FIELD_NAME' => $give_context ? null : 'title', 'FRACTIONAL_EDIT_FIELD_URL' => $give_context ? null : '_SEARCH:cms_catalogues:__edit_catalogue:' . $row['c_name'], 'RESOURCE_TYPE' => 'catalogue', )); } /** * Count the entries and subcategories underneath the specified category, recursively. * * @param AUTO_LINK $category_id The ID of the category for which count details are collected * @return array The number of entries is returned in $output['num_entries'], and the number of subcategories is returned in $output['num_children'], the (possibly recursive) number of subcategories in $output['num_children_children'], and the (possibly recursive) number of entries is returned in $output['num_entries_children']. */ function count_catalogue_category_children($category_id) { static $total_categories = null; if ($total_categories === null) { $total_categories = $GLOBALS['SITE_DB']->query_select_value('catalogue_categories', 'COUNT(*)'); } $out = array(); $out['num_children'] = $GLOBALS['SITE_DB']->query_select_value('catalogue_categories', 'COUNT(*)', array('cc_parent_id' => $category_id)); $out['num_entries'] = $GLOBALS['SITE_DB']->query_select_value('catalogue_entries', 'COUNT(*)', array('cc_id' => $category_id, 'ce_validated' => 1)); $rec_record = $GLOBALS['SITE_DB']->query_select('catalogue_childcountcache', array('c_num_rec_children', 'c_num_rec_entries'), array('cc_id' => $category_id), '', 1); if (!array_key_exists(0, $rec_record)) { $rec_record[0] = array('c_num_rec_children' => $out['num_children'], 'c_num_rec_entries' => $out['num_entries']); } $out['num_children_children'] = $rec_record[0]['c_num_rec_children']; $out['num_entries_children'] = $rec_record[0]['c_num_rec_entries']; return $out; } /** * Get an ordered array of all the entries in the specified catalogue. * * @param ?AUTO_LINK $category_id The ID of the category for which the entries are being collected (null: no limitation) * @param ID_TEXT $catalogue_name The name of the catalogue * @param ?array $catalogue A database row of the catalogue we are working with (null: read it in) * @param ID_TEXT $view_type The view type we're doing * @set PAGE SEARCH CATEGORY * @param ID_TEXT $tpl_set The template set we are rendering this category using * @param ?integer $max The maximum number of entries to show on a single page of this this category (null: all) * @param ?integer $start The entry number to start at (null: all) * @param ?mixed $select The entries to show, may be from other categories. Can either be SQL fragment (produced from Selectcode?), or array (null: use $start and $max) * @param ?AUTO_LINK $root The virtual root for display of this category (null: default) * @param ?SHORT_INTEGER $display_type The display type to use (null: lookup from $catalogue) * @param boolean $do_sorting Whether to perform sorting * @param ?array $entries A list of entry rows (null: select them normally) * @param string $filter Filtercode to apply (blank: none). * @param ?ID_TEXT $order_by_high_level Orderer (null: read from environment) * @param ID_TEXT $ordering_param Environment param used for ordering. You should pass in $order_by_high_level if it is set. * @param ?MEMBER $viewing_member_id Viewing member ID (null: current user) * @return array An array containing our built up entries (renderable Tempcode), our sorting interface, and our entries (entry records from database, with an additional 'map' field), and the max rows, and the display type used */ function render_catalogue_category_entry_buildup($category_id, $catalogue_name, $catalogue, $view_type, $tpl_set, $max, $start, $select, $root, $display_type = null, $do_sorting = true, $entries = null, $filter = '', $order_by_high_level = null, $ordering_param = 'sort', $viewing_member_id = null) { if ($filter != '') { require_code('filtercode'); $filtercode = parse_filtercode($filter); } else { $filtercode = mixed(); } // How to display if ($display_type === null) { $display_type = get_param_integer('keep_cat_display_type', $catalogue['c_display_type']); } // Find catalogue data $is_ecomm = is_ecommerce_catalogue($catalogue_name); if ($catalogue === null) { $catalogue = load_catalogue_row($catalogue_name); } require_code('fields'); $fields = get_catalogue_fields($catalogue_name); // Find $order_by/$direction which are semantically quite different to $order_by_high_level $order_by = mixed(); $direction = 'ASC'; if ($do_sorting) { inform_non_canonical_parameter($ordering_param); if ((!empty($order_by_high_level)) && (strpos($order_by_high_level, ' ') !== false/*if false probably some bot probing URLs -- sorting always has a space between sorter and direction*/)) { // Find order by URL parameter list($order_by, $direction) = explode(' ', $order_by_high_level); if (($direction != 'ASC') && ($direction != 'DESC')) { log_hack_attack_and_exit('ORDERBY_HACK'); } if (((!is_numeric($order_by)) || (!isset($fields[intval($order_by)]))) && (!in_array($order_by, array('fixed_random', 'average_rating', 'compound_rating', 'add_date', 'distance')))) { $order_by = null; // Invalid } } if ($order_by === null) { // Find default order for catalogue $order_by = '0'; $direction = 'ASC'; foreach ($fields as $i => $field) { if ($field['cf_defines_order'] != 0) { $order_by = strval($i); $direction = ($field['cf_defines_order'] == 1) ? 'ASC'/*1*/ : 'DESC'/*2*/; $order_by_high_level = strval($i) . ' ' . $direction; break; } } } } // Get entries in this category if ($select === '1=1') { $select = null; } if ($entries === null) { list($in_db_sorting, $num_entries, $entries) = get_catalogue_entries($catalogue_name, $category_id, $max, $start, $select, $do_sorting, $filtercode, $order_by, $direction); } else { // Oh, we already have $entries $num_entries = count($entries); $in_db_sorting = false; } disable_php_memory_limit(); // Work out the actual rendering, but only for those results in our selection scope (for performance) foreach ($entries as $i => $entry) { if (($in_db_sorting /*Only select rows were grabbed so $i is not the first entry, it is the $start entry*/) || (!$in_db_sorting /*Needs data to do manual sort*/) || ((($start === null) || ($i >= $start) && (($max === null) || ($i < $start + $max))) && ((!is_array($select)) || ((is_array($select)) && (in_array($entry['id'], $select)))))) { $entries[$i]['map'] = get_catalogue_entry_map($entry, $catalogue, $view_type, $tpl_set, $root, $fields, (($display_type == C_DT_TITLELIST) && (!$is_ecomm) && ($order_by !== null)) ? array(0, intval($order_by)) : null, false, true, intval($order_by)); } } if ($do_sorting) { // Render sort change dropdown $selectors = new Tempcode(); foreach ($fields as $i => $field) { if ($field['cf_searchable'] == 1) { $potential_sorter_name = get_translated_text($field['cf_name']); foreach (array('ASC' => '_ASCENDING', 'DESC' => '_DESCENDING') as $dir_code => $dir_lang) { $sort_sel = (($order_by == strval($i)) && ($direction == $dir_code)); $_potential_sorter_name = new Tempcode(); $_potential_sorter_name->attach(escape_html($potential_sorter_name)); $_potential_sorter_name->attach(do_lang_tempcode($dir_lang)); $selectors->attach(do_template('PAGINATION_SORTER', array('_GUID' => 'dfdsfdsusd0fsd0dsf', 'SELECTED' => $sort_sel, 'NAME' => protect_from_escaping($_potential_sorter_name), 'VALUE' => strval($i) . ' ' . $dir_code))); } } } $extra_sorts = array(); $extra_sorts['add_date'] = 'ADDED'; if (get_option('is_on_rating') == '0') { $has_ratings = false; } else { if (!is_null($entries)) { $has_ratings = false; foreach ($entries as $entry) { if ($entry['allow_rating'] == 1) { $has_ratings = true; } } if ($has_ratings) { $extra_sorts['average_rating'] = 'RATING'; $extra_sorts['compound_rating'] = 'POPULARITY'; } } else { $has_ratings = true; } } $extra_sorts['fixed_random'] = 'RANDOM'; foreach ($extra_sorts as $extra_sort_code => $extra_sort_lang) { foreach (array('ASC' => '_ASCENDING', 'DESC' => '_DESCENDING') as $dir_code => $dir_lang) { if (($extra_sort_code == 'fixed_random') && ($dir_code == 'DESC')) { continue; } $sort_sel = (($order_by == $extra_sort_code) && ($direction == $dir_code)); $_potential_sorter_name = new Tempcode(); $_potential_sorter_name->attach(do_lang_tempcode($extra_sort_lang)); if ($extra_sort_code != 'fixed_random') { $_potential_sorter_name->attach(do_lang_tempcode($dir_lang)); } $selectors->attach(do_template('PAGINATION_SORTER', array('_GUID' => 'xfdsfdsusd0fsd0dsf', 'SELECTED' => $sort_sel, 'NAME' => protect_from_escaping($_potential_sorter_name), 'VALUE' => $extra_sort_code . ' ' . $dir_code))); } } $sort_url = get_self_url(false, false, array($ordering_param => null), false, true); $sorting = do_template('PAGINATION_SORT', array('_GUID' => '9fgjfdklgjdfgkjlfdjgd90', 'SORT' => $ordering_param, 'URL' => $sort_url, 'SELECTORS' => $selectors)); // Sort entries manually if (!$in_db_sorting) { catalogue_entries_manual_sort($fields, $entries, $order_by, $direction); } } else { $sorting = new Tempcode(); } // Build up entries $entry_buildup = new Tempcode(); // Possibly some extra stuff for shopping carts $extra_map = array(); if ($is_ecomm) { require_lang('shopping'); $i = 0; for ($i = 0; $i < $num_entries; $i++) { if (!array_key_exists($i, $entries)) { break; } if (!array_key_exists('map', $entries[$i])) { continue; } $entry = $entries[$i]; $extra_map[$i]['ADD_TO_CART'] = build_url(array('page' => 'shopping', 'type' => 'add_item', 'product_id' => $entry['id'], 'hook' => 'catalogue_items'), get_module_zone('shopping')); } } // Now render the correct layout style switch ($display_type) { case C_DT_FIELDMAPS: for ($i = 0; $i < $num_entries; $i++) { if (!array_key_exists($i, $entries)) { break; } if (!array_key_exists('map', $entries[$i])) { continue; } $entry = $entries[$i]; if (($max === null) || (($start === null) || ($in_db_sorting) || ($i >= $start) && (($max === null) || ($i < $start + $max))) && ((!is_array($select)) || ((is_array($select)) && (in_array($entry['id'], $select))))) { $entry_buildup->attach(do_template('CATALOGUE_' . $tpl_set . '_FIELDMAP_ENTRY_WRAP', $entry['map'] + array('GIVE_CONTEXT' => false) + (array_key_exists($i, $extra_map) ? $extra_map[$i] : array()), null, false, 'CATALOGUE_DEFAULT_FIELDMAP_ENTRY_WRAP')); } } break; case C_DT_TITLELIST: for ($i = 0; $i < $num_entries; $i++) { if (!array_key_exists($i, $entries)) { break; } if (!array_key_exists('map', $entries[$i])) { continue; } $entry = $entries[$i]; if ((($start === null) || ($in_db_sorting) || ($i >= $start) && (($max === null) || ($i < $start + $max))) && ((!is_array($select)) || ((is_array($select)) && (in_array($entry['id'], $select))))) { $entry_buildup->attach(do_template('CATALOGUE_' . $tpl_set . '_TITLELIST_ENTRY', $entry['map'] + (array_key_exists($i, $extra_map) ? $extra_map[$i] : array()), null, false, 'CATALOGUE_DEFAULT_TITLELIST_ENTRY')); } } if (!$entry_buildup->is_empty()) { $entry_buildup = do_template('CATALOGUE_' . $tpl_set . '_TITLELIST_WRAP', $entry['map'] + array('CATALOGUE' => $catalogue_name, 'CONTENT' => $entry_buildup), null, false, 'CATALOGUE_DEFAULT_TITLELIST_WRAP'); } break; case C_DT_TABULAR: // Pre-scan to know if view URLs will be used $has_view_screens = false; for ($i = 0; $i < $num_entries; $i++) { if (!array_key_exists($i, $entries)) { break; } if (!array_key_exists('map', $entries[$i])) { continue; } $entry = $entries[$i]; if ((get_option('is_on_comments') == '1') && ($entry['allow_comments'] >= 1) || (get_option('is_on_rating') == '1') && ($entry['allow_rating'] == 1) || (get_option('is_on_trackbacks') == '1') && ($entry['allow_trackbacks'] == 1)) { $has_view_screens = true; } } // Main scan for ($i = 0; $i < $num_entries; $i++) { if (!array_key_exists($i, $entries)) { break; } if (!array_key_exists('map', $entries[$i])) { continue; } $entry = $entries[$i]; if ((($start === null) || ($in_db_sorting) || ($i >= $start) && (($max === null) || ($i < $start + $max))) && ((!is_array($select)) || (is_array($select)) && (in_array($entry['id'], $select)))) { $tab_entry_map = $entry['map'] + (array_key_exists($i, $extra_map) ? $extra_map[$i] : array()); if ($has_view_screens) { $url_map = array('page' => 'catalogues', 'type' => 'entry', 'id' => $entry['id']); if ($root !== null) { $url_map['keep_catalogue_' . $catalogue_name . '_root'] = $root; } $tab_entry_map['VIEW_URL'] = build_url($url_map, get_module_zone('catalogues')); } else { $tab_entry_map['VIEW_URL'] = ''; } $entry_buildup->attach(/*Preserve memory*/static_evaluate_tempcode(do_template('CATALOGUE_' . $tpl_set . '_TABULAR_ENTRY_WRAP', $tab_entry_map, null, false, 'CATALOGUE_DEFAULT_TABULAR_ENTRY_WRAP'))); } if (($start !== null) && ($i >= $start + $max)) { break; } } // Put it together if (!$entry_buildup->is_empty()) { $head = new Tempcode(); $field_count = 0; foreach ($fields as $i => $field) { if (((($field['cf_put_in_category'] == 1) && ($view_type == 'CATEGORY')) || (($field['cf_put_in_search'] == 1) && ($view_type == 'SEARCH'))) && ($field['cf_visible'] == 1)) { if ($field['cf_searchable'] == 1) { $sort_url_asc = get_self_url(false, false, array($ordering_param => strval($i) . ' ASC'), true); $sort_url_desc = get_self_url(false, false, array($ordering_param => strval($i) . ' DESC'), true); $sort_asc_selected = (($order_by == strval($field['id'])) && ($direction == 'ASC')); $sort_desc_selected = (($order_by == strval($field['id'])) && ($direction == 'DESC')); } else { $sort_url_asc = ''; $sort_url_desc = ''; $sort_asc_selected = false; $sort_desc_selected = false; } $head->attach(do_template( 'CATALOGUE_' . $tpl_set . '_TABULAR_HEADCELL', array( 'SORT_ASC_SELECTED' => $sort_asc_selected, 'SORT_DESC_SELECTED' => $sort_desc_selected, 'SORT_URL_ASC' => $sort_url_asc, 'SORT_URL_DESC' => $sort_url_desc, 'CATALOGUE' => $catalogue_name, 'FIELDID' => strval($i), '_FIELDID' => strval($field['id']), 'FIELD' => get_translated_text($field['cf_name']), 'FIELDTYPE' => $field['cf_type'] ), null, false, 'CATALOGUE_DEFAULT_TABULAR_HEADCELL' ) ); $field_count++; } } $entry_buildup = do_template('CATALOGUE_' . $tpl_set . '_TABULAR_WRAP', array('CATALOGUE' => $catalogue_name, 'HEAD' => $head, 'CONTENT' => $entry_buildup, 'FIELD_COUNT' => strval($field_count)), null, false, 'CATALOGUE_DEFAULT_TABULAR_WRAP'); } break; case C_DT_GRID: for ($i = 0; $i < $num_entries; $i++) { if (!array_key_exists($i, $entries)) { break; } $entry = $entries[$i]; if (($max === null) || (($start === null) || ($in_db_sorting) || ($i >= $start) && (($max === null) || ($i < $start + $max))) && ((!is_array($select)) || ((is_array($select)) && (in_array($entry['id'], $select))))) { $entry_buildup->attach(do_template('CATALOGUE_' . $tpl_set . '_GRID_ENTRY_WRAP', $entry['map'] + (array_key_exists($i, $extra_map) ? $extra_map[$i] : array()), null, false, 'CATALOGUE_DEFAULT_GRID_ENTRY_WRAP')); } } break; default: warn_exit(do_lang_tempcode('INTERNAL_ERROR')); } return array($entry_buildup, $sorting, $entries, $num_entries, $display_type); } /** * Make sure we are doing necessary join to be able to access the given field * * @param object $db Database connection * @param array $info Content type info * @param ?ID_TEXT $catalogue_name Name of the catalogue (null: unknown; reduces performance) * @param array $extra_join List of joins (passed as reference) * @param array $extra_select List of selects (passed as reference) * @param ID_TEXT $filter_key The field to get * @param string $filter_val The field value for this * @param array $db_fields Database field data * @param string $table_join_code What the database will join the table with * @return ?array A triple: Proper database field name to access with, The fields API table type (blank: no special table), The new filter value (null: error) * @ignore */ function _catalogues_filtercode($db, $info, $catalogue_name, &$extra_join, &$extra_select, $filter_key, $filter_val, $db_fields, $table_join_code) { if (preg_match('#^((.*)\.)?field\_(\d+)#', $filter_key) != 0) { // This is by field ID, not field sequence # $ret = _fields_api_filtercode($db, $info, $catalogue_name, $extra_join, $extra_select, $filter_key, $filter_val, $db_fields, $table_join_code); if (!is_null($ret)) { return $ret; } } // Named $ret = _fields_api_filtercode_named($db, $info, $catalogue_name, $extra_join, $extra_select, $filter_key, $filter_val, $db_fields, $table_join_code); if (!is_null($ret)) { return $ret; } return _default_conv_func($db, $info, $catalogue_name, $extra_join, $extra_select, $filter_key, $filter_val, $db_fields, $table_join_code); } /** * Fetch entries from database, with sorting if possible. * * @param ID_TEXT $catalogue_name Name of the catalogue * @param ?AUTO_LINK $category_id The ID of the category for which the entries are being collected (null: no limitation) * @param ?integer $max The maximum number of entries to show on a single page of this this category (ignored if $select is not null) (null: all) * @param ?integer $start The entry number to start at (ignored if $select is not null) (null: all) * @param ?mixed $select The entries to show, may be from other categories. Can either be SQL fragment (produced from Selectcode?), or array (null: use $start and $max) * @param boolean $do_sorting Whether to perform sorting * @param ?array $filtercode List of filters to apply (null: none). Each filter is a triple: ORd comparison key(s) [separated by pipe symbols], comparison type (one of '<', '>', '<=', '>=', '=', '~=', or '~'), comparison value * @param ID_TEXT $order_by Orderer * @param ID_TEXT $direction Order direction * @param string $extra_where Additional WHERE SQL to add on to query * @param ?MEMBER $viewing_member_id Viewing member ID (null: current user) * @return array A tuple: whether sorting was done, number of entries returned, list of entries */ function get_catalogue_entries($catalogue_name, $category_id, $max, $start, $select, $do_sorting, $filtercode, $order_by, $direction, $extra_where = '', $viewing_member_id = null) { $where_clause = '1=1' . $extra_where; if ($category_id !== null) { // WHERE clause $where_clause .= ' AND r.cc_id=' . strval($category_id); } if ((!has_privilege(get_member(), 'see_unvalidated')) && (addon_installed('unvalidated'))) { $where_clause .= ' AND r.ce_validated=1'; } // Convert the filters to SQL require_code('filtercode'); list($extra_select, $extra_join, $extra_where) = filtercode_to_sql($GLOBALS['SITE_DB'], $filtercode, 'catalogue_entry', $catalogue_name); $where_clause .= $extra_where . ' AND ' . db_string_equal_to('r.c_name', $catalogue_name); $privacy_join = ''; $privacy_where = ''; if (addon_installed('content_privacy')) { require_code('content_privacy'); list($privacy_join, $privacy_where) = get_privacy_where_clause('catalogue_entry', 'r', $viewing_member_id); } $extra_join[] = $privacy_join; $where_clause .= $privacy_where; // If we're listing what IDs to look at, work out SQL for this if (($category_id === null) && ($select !== null)) { if (((is_array($select)) && (count($select) == 0)) || ((is_string($select)) && ($select == ''))) { $entries = array(); // This is saying we are selecting nothing, so just say that - it'll save us a query } else { // Put together some SQL for defining what to select if (!is_array($select)) { $or_list = $select; } else { $or_list = ''; foreach ($select as $s) { if ($or_list != '') { $or_list .= ' OR '; } $or_list .= 'r.id=' . strval($s); } } $where_clause .= ' AND (' . $or_list . ')'; } } require_code('fields'); $fields = get_catalogue_fields($catalogue_name); $num_entries = mixed(); if ($order_by == 'rating') { // LEGACY $order_by = 'average_rating'; } $can_do_db_sorting = ($order_by != 'distance'); require_code('hooks/systems/content_meta_aware/catalogue_entry'); $cma_ob = object_factory('Hook_content_meta_aware_catalogue_entry'); if (($do_sorting) && ($can_do_db_sorting)) { $virtual_order_by = $order_by; if ($order_by == 'add_date') { $virtual_order_by = 'r.ce_add_date'; } elseif (($order_by == 'compound_rating') || ($order_by == 'average_rating') || ($order_by == 'fixed_random')) { $ob = object_factory('Hook_content_meta_aware_catalogue_entry'); $info = $ob->info(); if ($info === null) { fatal_exit(do_lang_tempcode('INTERNAL_ERROR')); } $bits = _catalogues_filtercode($GLOBALS['SITE_DB'], $info, $catalogue_name, $extra_join, $extra_select, $order_by, '', array(), 'r'); // Used to get JOIN for ordering if ($bits !== null) { list($virtual_order_by,) = $bits; } else { $virtual_order_by = 'r.ce_add_date'; // Should not happen } } elseif ((is_numeric($order_by)) && (isset($fields[intval($order_by)]))) { // Ah, so it's saying the nth field of this catalogue $ob = object_factory('Hook_content_meta_aware_catalogue_entry'); $info = $ob->info(); if ($info === null) { fatal_exit(do_lang_tempcode('INTERNAL_ERROR')); } $order_by_field_id = $fields[intval($order_by)]['id']; $bits = _catalogues_filtercode($GLOBALS['SITE_DB'], $info, $catalogue_name, $extra_join, $extra_select, 'field_' . $order_by_field_id, '', array(), 'r'); // Used to get JOIN for ordering if ($bits !== null) { list($new_key,) = $bits; if ((strpos($new_key, '.text_original') !== false) && (multi_lang_content())) { $num_entries = $GLOBALS['SITE_DB']->query_value_if_there('SELECT COUNT(*) FROM ' . get_table_prefix() . 'catalogue_entries r' . implode('', $extra_join) . ' WHERE ' . $where_clause, false, true); if ($num_entries > 300) { // For large data sets too slow as after two MySQL joins it can't then use index for ordering $virtual_order_by = 'r.id'; unset($extra_join[$new_key]); } else { $virtual_order_by = $new_key; } } else { $virtual_order_by = $new_key; } } else { $virtual_order_by = 'r.id'; } } else { $virtual_order_by = 'r.id'; } } else { $virtual_order_by = 'r.id'; } if ($num_entries === null) { $num_entries = $GLOBALS['SITE_DB']->query_value_if_there('SELECT COUNT(*) FROM ' . get_table_prefix() . 'catalogue_entries r' . implode('', $extra_join) . ' WHERE ' . $where_clause, false, true); } if (get_value('force_memory_sort__' . $catalogue_name) === '1') { $can_do_db_sorting = false; } $in_db_sorting = $do_sorting && $can_do_db_sorting; // This defines whether $virtual_order_by can actually be used in SQL (if not, we have to sort manually) if (($num_entries > 300) && (!$in_db_sorting)) { // Needed to stop huge slow down, so reduce to sorting by ID $in_db_sorting = true; $virtual_order_by = 'r.id'; } $sql = 'SELECT r.*' . implode('', $extra_select) . ' FROM ' . get_table_prefix() . 'catalogue_entries r' . implode('', $extra_join) . ' WHERE ' . $where_clause; if ($in_db_sorting && $do_sorting) { $sql .= ' ORDER BY ' . $virtual_order_by . ' ' . $direction; } if ($max > 0) { $entries = $GLOBALS['SITE_DB']->query($sql, $in_db_sorting ? $max : null, $in_db_sorting ? $start : 0); } else { $entries = array(); } return array($in_db_sorting, $num_entries, $entries); } /** * Manually sort some catalogue entries. * * @param array $fields Fields array for catalogue * @param array $entries Entries to sort (by reference) * @param ID_TEXT $order_by What to sort by * @param ID_TEXT $direction Sort direction * @return array Entries */ function catalogue_entries_manual_sort($fields, &$entries, $order_by, $direction) { $num_entries = count($entries); for ($i = 0; $i < $num_entries; $i++) { // Bubble sort for ($j = $i + 1; $j < $num_entries; $j++) { if ($order_by == 'distance') { $considered_field = 'DISTANCE_PLAIN'; // Not in there by default, but addons might make it } else { $considered_field = 'FIELD_' . $order_by; } $a = $entries[$j]['map'][$considered_field]; if (array_key_exists($considered_field . '_PLAIN', $entries[$j]['map'])) { $a = $entries[$j]['map'][$considered_field . '_PLAIN']; } $b = $entries[$i]['map'][$considered_field]; if (array_key_exists($considered_field . '_PLAIN', $entries[$i]['map'])) { $b = $entries[$i]['map'][$considered_field . '_PLAIN']; } if (is_object($a)) { $a = $a->evaluate(); } if (is_object($b)) { $b = $b->evaluate(); } if ((isset($fields[$order_by])) && ($fields[$order_by]['cf_type'] == 'date')) { // Special case for dates $bits = explode(' ', $a, 2); $date_bits = explode((strpos($bits[0], '-') !== false) ? '-' : '/', $bits[0], 3); if (!array_key_exists(1, $date_bits)) { $date_bits[1] = date('m'); } if (!array_key_exists(2, $date_bits)) { $date_bits[2] = date('Y'); } $time_bits = explode(':', $bits[1], 3); if (!array_key_exists(1, $time_bits)) { $time_bits[1] = '00'; } if (!array_key_exists(2, $time_bits)) { $time_bits[2] = '00'; } $time_a = mktime(intval($time_bits[0]), intval($time_bits[1]), intval($time_bits[2]), intval($date_bits[1]), intval($date_bits[2]), intval($date_bits[0])); $bits = explode(' ', $b, 2); $date_bits = explode((strpos($bits[0], '-') !== false) ? '-' : '/', $bits[0], 3); if (!array_key_exists(1, $date_bits)) { $date_bits[1] = date('m'); } if (!array_key_exists(2, $date_bits)) { $date_bits[2] = date('Y'); } $time_bits = explode(':', $bits[1], 3); if (!array_key_exists(1, $time_bits)) { $time_bits[1] = '00'; } if (!array_key_exists(2, $time_bits)) { $time_bits[2] = '00'; } $time_b = mktime(intval($time_bits[0]), intval($time_bits[1]), intval($time_bits[2]), intval($date_bits[1]), intval($date_bits[2]), intval($date_bits[0])); $r = ($time_a < $time_b) ? -1 : (($time_a == $time_b) ? 0 : 1); } elseif ($order_by == 'distance') { // By distance if (($a === null) || ($b === null)) { $r = 0; } else { $r = (floatval($a) < floatval($b)) ? -1 : 1; } } else { // Normal case $r = strnatcmp(strtolower($a), strtolower($b)); } if ((($r < 0) && ($direction == 'ASC')) || (($r > 0) && ($direction == 'DESC'))) { $temp = $entries[$i]; $entries[$i] = $entries[$j]; $entries[$j] = $temp; } } } return $entries; } /** * Get a map of the fields for the given entry. * * @param array $entry A database row of the entry we are working with * @param ?array $catalogue A database row of the catalogue we are working with (null: read it in here) * @param ID_TEXT $view_type The view type we're doing * @set PAGE SEARCH CATEGORY * @param ID_TEXT $tpl_set The template set we are rendering this category using * @param ?AUTO_LINK $root The virtual root for display of this category (null: none) * @param ?array $fields The database rows for the fields for this catalogue (null: find them) * @param ?array $only_fields A list of fields (sequence numbers) that we are limiting ourselves to (null: get ALL fields) * @param boolean $feedback_details Whether to grab the feedback details * @param boolean $breadcrumbs_details Whether to grab the breadcrumbs details * @param ?integer $order_by Field index to order by (null: none) * @param ?array $_breadcrumbs Write breadcrumbs into here (null: don't bother) * @param boolean $force_view_all Whether to render everything * @param ID_TEXT $zone The zone to display in * @return array A map of information relating to the entry. The map contains 'FIELDS' (Tempcode for all accumulated fields), 'FIELD_x' (for each field x applying to the entry), STAFF_DETAILS, COMMENT_DETAILS, RATING_DETAILS, VIEW_URL, BREADCRUMBS */ function get_catalogue_entry_map($entry, $catalogue, $view_type, $tpl_set, $root = null, $fields = null, $only_fields = null, $feedback_details = false, $breadcrumbs_details = false, $order_by = null, &$_breadcrumbs = null, $force_view_all = false, $zone = '_SEARCH') { $id = $entry['id']; $all_visible = true; require_code('fields'); // Load catalogue if needed if ($catalogue === null) { $catalogue = load_catalogue_row($entry['c_name']); } // Get values $catalogue_name = $catalogue['c_name']; $fields = get_catalogue_entry_field_values($catalogue_name, $entry, $only_fields, $fields, false, $view_type); // Prepare output map $map = array(); $map['FIELDS'] = new Tempcode(); $map['FIELDS_GRID'] = new Tempcode(); $map['FIELDS_TABULAR'] = new Tempcode(); $map['fields'] = $fields; $no_catalogue_field_assembly = (get_value('no_catalogue_field_assembly') === '1'); $no_catalogue_field_assembly_fieldmaps__this = (get_value('no_catalogue_field_assembly_fieldmaps__' . $catalogue['c_name']) === '1'); $no_catalogue_field_assembly_fieldmaps = (get_value('no_catalogue_field_assembly_fieldmaps') === '1'); $no_catalogue_field_assembly_grid__this = (get_value('no_catalogue_field_assembly_grid__' . $catalogue['c_name']) === '1'); $no_catalogue_field_assembly_grid = (get_value('no_catalogue_field_assembly_grid') === '1'); $no_catalogue_field_assembly_tabular__this = (get_value('no_catalogue_field_assembly_tabular__' . $catalogue['c_name']) === '1'); $no_catalogue_field_assembly_tabular = (get_value('no_catalogue_field_assembly_tabular') === '1'); // Loop over all fields foreach ($fields as $i => $field) { if (!isset($field['effective_value'])) { // No field value. Should actually never happen, as e.g. {!INTERNAL_ERROR} is put in if field values are missing $all_visible = false; continue; } $str_i = strval($i); $str_id = strval($field['id']); // Value to show $ev = $field['effective_value']; $ev_pure = $field['effective_value_pure']; $ob = get_fields_hook($field['cf_type']); list(, , $storage_type) = $ob->get_field_value_row_bits($field); if (($i == 0) && ($catalogue['c_display_type'] == C_DT_TITLELIST)) { $use_ev = $ev; } else { $use_ev = $ob->render_field_value($field, $ev, $i, $only_fields, 'catalogue_efv_' . $storage_type, $id, 'ce_id', 'cf_id', 'cv_value', $entry['ce_submitter'], $ev_pure); } // Special case for access to raw thumbnail if ($field['cf_type'] == 'picture') { if (($ev !== null) && ($ev_pure != '')) { require_code('images'); $map['FIELD_' . $str_i . '_THUMB'] = do_image_thumb($ev_pure, ($i == 0) ? new Tempcode() : (is_object($map['FIELD_0']) ? $map['FIELD_0'] : protect_from_escaping(escape_html($map['FIELD_0']))), false, false); } else { $map['FIELD_' . $str_i . '_THUMB'] = new Tempcode(); } $map['_FIELD_' . $str_id . '_THUMB'] = $map['FIELD_' . $str_i . '_THUMB']; } // Different ways of accessing the main field value, and pure version of it $field_name = get_translated_text($field['cf_name']); $field_type = $field['cf_type']; $map['FIELD_' . $str_i] = $use_ev; $map['_FIELD_' . $str_id] = &$map['FIELD_' . $str_i]; if ($use_ev === $ev) { $map['FIELD_' . $str_i . '_PLAIN'] = &$map['FIELD_' . $str_i]; } else { $map['FIELD_' . $str_i . '_PLAIN'] = $ev; } $map['_FIELD_' . $str_id . '_PLAIN'] = &$map['FIELD_' . $str_i . '_PLAIN']; if ($ev === $field['effective_value_pure']) { $map['FIELD_' . $str_i . '_PURE'] = &$map['FIELD_' . $str_i . '_PLAIN']; } else { $map['FIELD_' . $str_i . '_PURE'] = $field['effective_value_pure']; } $map['_FIELD_' . $str_id . '_PURE'] = &$map['FIELD_' . $str_i . '_PURE']; // If the field should be shown, show it $is_visible_in_view_type = ($view_type == 'PAGE') || (($field['cf_put_in_category'] == 1) && ($view_type == 'CATEGORY')) || (($field['cf_put_in_search'] == 1) && ($view_type == 'SEARCH')) || ($force_view_all); if (($is_visible_in_view_type) && ($field['cf_visible'] == 1)) { if ((!$no_catalogue_field_assembly) || (!$feedback_details/*no feedback details implies wants all field data*/) || ($force_view_all)) { $f = array('ENTRYID' => strval($id), 'CATALOGUE' => $catalogue_name, 'TYPE' => $field['cf_type'], 'FIELD' => $field_name, 'FIELDID' => $str_i, '_FIELDID' => $str_id, 'FIELDTYPE' => $field_type, 'VALUE_PLAIN' => $ev, 'VALUE' => $use_ev); if (!$no_catalogue_field_assembly_fieldmaps__this) { if ((!$no_catalogue_field_assembly_fieldmaps) || (!$feedback_details/*no feedback details implies wants all field data [as is a category view]*/)) { $_field = do_template('CATALOGUE_' . $tpl_set . '_FIELDMAP_ENTRY_FIELD', $f, null, false, 'CATALOGUE_DEFAULT_FIELDMAP_ENTRY_FIELD'); $map['FIELDS']->attach($_field); } } if (!$no_catalogue_field_assembly_grid__this) { if (!$no_catalogue_field_assembly_grid) { $_field = do_template('CATALOGUE_' . $tpl_set . '_GRID_ENTRY_FIELD', $f, null, false, 'CATALOGUE_DEFAULT_GRID_ENTRY_FIELD'); $map['FIELDS_GRID']->attach($_field); } } if (!$no_catalogue_field_assembly_tabular__this) { if (!$no_catalogue_field_assembly_tabular) { $_field = do_template('CATALOGUE_' . $tpl_set . '_TABULAR_ENTRY_FIELD', $f, null, false, 'CATALOGUE_DEFAULT_TABULAR_ENTRY_FIELD'); $map['FIELDS_TABULAR']->attach($_field); } } } } else { $all_visible = false; } } // Admin functions if ((has_actual_page_access(null, 'cms_catalogues', null, null)) && (has_edit_permission('mid', get_member(), $entry['ce_submitter'], 'cms_catalogues', array('catalogues_catalogue', $catalogue_name) + ((get_value('disable_cat_cat_perms') !== '1') ? array('catalogues_category', $entry['cc_id']) : array())))) { $map['EDIT_URL'] = build_url(array('page' => 'cms_catalogues', 'type' => '_edit_entry', 'catalogue_name' => $catalogue_name, 'id' => $id), get_module_zone('cms_catalogues')); } else { $map['EDIT_URL'] = ''; } // Various bits of metadata $map['SUBMITTER'] = strval($entry['ce_submitter']); $map['VIEWS'] = strval($entry['ce_views']); $map['ADD_DATE_RAW'] = strval($entry['ce_add_date']); $map['EDIT_DATE_RAW'] = ($entry['ce_edit_date'] === null) ? '' : strval($entry['ce_edit_date']); $map['ADD_DATE'] = get_timezoned_date_tempcode($entry['ce_add_date']); $map['EDIT_DATE'] = is_null($entry['ce_edit_date']) ? new Tempcode() : get_timezoned_date_tempcode($entry['ce_edit_date']); $map['ID'] = strval($id); $map['CATALOGUE'] = $catalogue_name; $map['CATALOGUE_TITLE'] = array_key_exists('c_title', $catalogue) ? get_translated_text($catalogue['c_title']) : ''; $map['CATEGORY_ID'] = strval($entry['cc_id']); $map['CAT'] = strval($entry['cc_id']); if ((get_option('is_on_comments') == '1') && (!has_no_forum()) && ($entry['allow_comments'] >= 1)) { $map['COMMENT_COUNT'] = '1'; } $separate_view_screen = (get_option('is_on_comments') == '1') && ($entry['allow_comments'] >= 1) || /*(get_option('is_on_rating') == '1') && ($entry['allow_rating'] == 1) || We'll just allow inline rating */ (get_option('is_on_trackbacks') == '1') && ($entry['allow_trackbacks'] == 1) || (!$all_visible); // Feedback $c_value = isset($map['FIELD_0_PLAIN_PURE']) ? $map['FIELD_0_PLAIN_PURE'] : (isset($map['FIELD_0_PLAIN']) ? $map['FIELD_0_PLAIN'] : do_lang('UNKNOWN')); if (is_object($c_value)) { $c_value = $c_value->evaluate(); } $url_map = array('page' => 'catalogues', 'type' => 'entry', 'id' => $id); $self_url = build_url($url_map, $zone, null, false, false, true); if (($feedback_details) || ($only_fields !== array(0))) { require_code('feedback'); $map['RATING'] = ($entry['allow_rating'] == 1) ? display_rating($self_url, $c_value, 'catalogues__' . $catalogue_name, strval($id), $separate_view_screen ? 'RATING_INLINE_STATIC' : 'RATING_INLINE_DYNAMIC', $entry['ce_submitter']) : new Tempcode(); } $map['ALLOW_RATING'] = ($entry['allow_rating'] == 1); if ($feedback_details) { require_code('feedback'); list($map['RATING_DETAILS'], $map['COMMENT_DETAILS'], $map['TRACKBACK_DETAILS']) = embed_feedback_systems( 'catalogues__' . $catalogue_name, strval($id), $entry['allow_rating'], $entry['allow_comments'], $entry['allow_trackbacks'], $entry['ce_validated'], $entry['ce_submitter'], $self_url, $c_value, find_overridden_comment_forum('catalogues__' . $catalogue_name, strval($entry['cc_id'])), $entry['ce_add_date'] ); } // Link to view entry if ($separate_view_screen) { $url_map = array('page' => 'catalogues', 'type' => 'entry', 'id' => $id); if ($root !== null) { $url_map['keep_catalogue_' . $catalogue_name . '_root'] = $root; } $map['VIEW_URL'] = build_url($url_map, $zone); } else { $map['VIEW_URL'] = ''; } // Breadcrumbs if ($breadcrumbs_details) { $map['BREADCRUMBS'] = ''; if ($only_fields === null) { $_breadcrumbs = catalogue_category_breadcrumbs($entry['cc_id'], $root, false); $breadcrumbs = breadcrumb_segments_to_tempcode($_breadcrumbs); $map['BREADCRUMBS'] = $breadcrumbs; } } return $map; } /** * Get the values for the specified fields, for the stated catalogue entry. * * @param ?ID_TEXT $catalogue_name The catalogue name we are getting an entry in (null: lookup) * @param mixed $entry_id The ID of the entry we are getting OR the row * @param ?array $only_fields A list of fields that we are limiting ourselves to (null: get ALL fields) * @param ?array $fields The database rows for the fields for this catalogue (null: find them) * @param boolean $natural_order Whether to order the fields in their natural database order. This is only used for shopping catalogues as a defence against webmaster field reordering and not a strong guarantee * @param ID_TEXT $view_type The view type we're doing * @set PAGE SEARCH CATEGORY * @param ?LANGUAGE_NAME $lang Language codename (null: current user's language) * @return array A list of maps (each field for the entry gets a map), where each map contains 'effective_value' (the value for the field). Some maps get additional fields (effective_value_pure), depending on the field type */ function get_catalogue_entry_field_values($catalogue_name, $entry_id, $only_fields = null, $fields = null, $natural_order = false, $view_type = 'PAGE', $lang = null) { global $CAT_FIELDS_CACHE; if ($fields === null) { if ($catalogue_name === null) { $catalogue_name = $GLOBALS['SITE_DB']->query_select_value('catalogue_entries', 'c_name', array('id' => $entry_id)); } if ((isset($CAT_FIELDS_CACHE[$catalogue_name])) && (!$natural_order)) { $fields = $CAT_FIELDS_CACHE[$catalogue_name]; } else { $order_by = (($natural_order && (strpos(get_db_type(), 'mysql')/*assumption about sequential order*/ !== false)) ? 'id' : ('cf_order,' . $GLOBALS['SITE_DB']->translate_field_ref('cf_name'))); $fields = $GLOBALS['SITE_DB']->query_select('catalogue_fields', array('*'), array('c_name' => $catalogue_name), 'ORDER BY ' . $order_by); } } if (!$natural_order) { $CAT_FIELDS_CACHE[$catalogue_name] = $fields; } require_code('fields'); if ($only_fields !== null) { $only_fields = array_flip($only_fields); } // Work out an ID filter for what fields to show $only_field_ids = mixed(); if (get_value('catalogue_limit_cat_field_load__' . $catalogue_name) === '1') { $only_field_ids = array(); foreach ($fields as $i => $field) { $field_id = $field['id']; if (($only_fields !== null) && (!isset($only_fields[$i]))) { continue; } if ($field['cf_defines_order'] == 0) { if (($view_type == 'CATEGORY') && ($field['cf_put_in_category'] == 0)) { continue; } if (($view_type == 'SEARCH') && ($field['cf_put_in_search'] == 0)) { continue; } } $only_field_ids[] = $field_id; } } foreach ($fields as $i => $field) { $field_id = $field['id']; if (($only_fields !== null) && (!isset($only_fields[$i]))) { continue; } _resolve_catalogue_entry_field($field, $entry_id, $only_field_ids, $fields[$i], $lang); } return $fields; } /** * Get the standardised details for a catalogue entry field. * * @param array $field The field row * @param mixed $entry_id The ID of the entry we are getting OR the row * @param ?array $only_field_ids A list of field IDs that we are limiting ourselves to (null: get ALL fields) * @param array $target Save the result into here * @param ?LANGUAGE_NAME $lang Language codename (null: current user's language) * * @ignore */ function _resolve_catalogue_entry_field($field, $entry_id, $only_field_ids, &$target, $lang = null) { $ob = get_fields_hook($field['cf_type']); list($raw_type, , $type) = $ob->get_field_value_row_bits($field); if (is_null($raw_type)) { $raw_type = $field['cf_type']; } switch ($raw_type) { case 'long_trans': case 'short_trans': $temp = _get_catalogue_entry_field($field['id'], $entry_id, 'short_trans', $only_field_ids); if ($temp['cv_value'] === null) { $target['effective_value'] = ''; $target['effective_value_pure'] = ''; } else { $just_row = db_map_restrict($temp, array('cv_value')) + array('ce_id' => is_array($entry_id) ? $entry_id['id'] : $entry_id, 'cf_id' => $field['id']); if (multi_lang_content()) { $just_row['cv_value'] = intval($just_row['cv_value']); } $target['effective_value'] = get_translated_tempcode('catalogue_efv_' . $raw_type, $just_row, 'cv_value', null, $lang); $target['effective_value_pure'] = get_translated_text($just_row['cv_value'], null, $lang); } break; case 'long_text': case 'short_text': case 'long_unescaped': case 'short_unescaped': $temp = _get_catalogue_entry_field($field['id'], $entry_id, $type, $only_field_ids); if ($temp['cv_value'] === null) { $target['effective_value'] = ''; $target['effective_value_pure'] = ''; break; } else { $target['effective_value'] = $temp['cv_value']; $target['effective_value_pure'] = $temp['cv_value']; } break; case 'float_unescaped': $temp = _get_catalogue_entry_field($field['id'], $entry_id, $type, $only_field_ids); if ($temp['cv_value'] === null) { $target['effective_value'] = do_lang_tempcode('NA_EM'); $target['effective_value_pure'] = do_lang('NA'); break; } else { $target['effective_value'] = float_to_raw_string($temp['cv_value'], 30, true); $target['effective_value_pure'] = $target['effective_value']; } break; case 'integer_unescaped': $temp = _get_catalogue_entry_field($field['id'], $entry_id, $type, $only_field_ids); if ($temp['cv_value'] === null) { $target['effective_value'] = do_lang_tempcode('NA_EM'); $target['effective_value_pure'] = do_lang('NA'); break; } else { $target['effective_value'] = strval($temp['cv_value']); $target['effective_value_pure'] = $target['effective_value']; } break; default: warn_exit(do_lang_tempcode('INTERNAL_ERROR')); } } /** * Get the value for the specified field, for the stated catalogue entry. * * @param AUTO_LINK $field_id The ID of the field we are getting * @param mixed $entry_id The ID of the entry we are getting for OR the row * @param ID_TEXT $type The type of field * @set short long * @param ?array $only_field_ids A list of field IDs that we are limiting ourselves to (null: get ALL fields) * @return ?array The row (null: not found) * @ignore */ function _get_catalogue_entry_field($field_id, $entry_id, $type = 'short', $only_field_ids = null) { if (is_array($entry_id)) { $entry_id = $entry_id['id']; } // Pre-caching of whole entry static $catalogue_entry_cache = array(); if ((!isset($catalogue_entry_cache[$entry_id])) || (class_exists('Resource_fs_base')/*Implies resource-fs import*/)) { $catalogue_entry_cache[$entry_id] = array(); $only_fields_sql = ''; if ($only_field_ids !== null) { $only_fields_sql .= ' AND ('; if ($only_field_ids != array()) { foreach ($only_field_ids as $i => $_field_id) { if ($i != 0) { $only_fields_sql .= ' OR '; } $only_fields_sql .= 'f.id=' . strval($_field_id); } } else { $only_fields_sql .= '1=0'; } $only_fields_sql .= ')'; } $tables = array('catalogue_efv_float' => true, 'catalogue_efv_integer' => true, 'catalogue_efv_long' => false, 'catalogue_efv_long_trans' => multi_lang_content(), 'catalogue_efv_short' => false, 'catalogue_efv_short_trans' => multi_lang_content(),); if (strpos(get_db_type(), 'mysql') !== false) { // Optimised for MySQL $query = ''; foreach ($tables as $table => $needs_casting) { if ($query != '') { $query .= ' UNION '; } $_cv_value = $needs_casting ? db_cast('v.cv_value', 'CHAR') : 'v.cv_value'; $query .= 'SELECT f.id AS f_id,' . $_cv_value . ' AS cv_value'; if (!multi_lang_content()) { if (strpos($table, '_trans') !== false) { $query .= ',v.cv_value__text_parsed,v.cv_value__source_user'; } else { $query .= ',NULL AS cv_value__text_parsed,NULL AS cv_value__source_user'; } } $query .= ' FROM ' . get_table_prefix() . 'catalogue_fields f JOIN ' . get_table_prefix() . $table . ' v ON v.cf_id=f.id'; $query .= ' WHERE v.ce_id=' . strval($entry_id); $query .= $only_fields_sql; } $rows = $GLOBALS['SITE_DB']->query($query, null, null, false, true); foreach ($rows as $row) { $catalogue_entry_cache[$entry_id][$row['f_id']] = $row; } } else { // Other databases may not support unions with different data types, even if we do casting (PostgreSQL definitely doesn't) foreach ($tables as $table) { $query = 'SELECT f.id AS f_id,v.cv_value'; if (!multi_lang_content()) { if (strpos($table, '_trans') !== false) { $query .= ',v.cv_value__text_parsed,v.cv_value__source_user'; } else { $query .= ',NULL AS cv_value__text_parsed,NULL AS cv_value__source_user'; } } $query .= ' FROM ' . get_table_prefix() . 'catalogue_fields f JOIN ' . get_table_prefix() . $table . ' v ON v.cf_id=f.id'; $query .= ' WHERE v.ce_id=' . strval($entry_id); $query .= $only_fields_sql; $rows = $GLOBALS['SITE_DB']->query($query, null, null, false, true); foreach ($rows as $row) { $catalogue_entry_cache[$entry_id][$row['f_id']] = $row; } } } $value = isset($catalogue_entry_cache[$entry_id][$field_id]) ? $catalogue_entry_cache[$entry_id][$field_id] : null; if (class_exists('Resource_fs_base')) { $catalogue_entry_cache = array(); } } else { if (!isset($catalogue_entry_cache[$entry_id][$field_id])) { switch ($type) { case 'float': case 'integer': return array('cv_value' => null); default: return array('cv_value' => '', 'cv_value__text_parsed' => '', 'cv_value__source_user' => null); } } $value = $catalogue_entry_cache[$entry_id][$field_id]; } if (is_string($value['cv_value'])) { // UNION will coerce types to strings, undo that switch ($type) { case 'float': $value['cv_value'] = ($value['cv_value'] == '') ? null : floatval($value['cv_value']); break; case 'integer': $value['cv_value'] = ($value['cv_value'] == '') ? null : intval($value['cv_value']); break; } } return $value; } /** * Get a nice, formatted, XHTML list of all the catalogues. * * @param ?ID_TEXT $it The name of the currently selected catalogue (null: none selected) * @param boolean $prefer_ones_with_entries If there are too many to list prefer to get ones with entries rather than just the newest * @param boolean $only_submittable Whether to only show catalogues that can be submitted to * @param ?TIME $updated_since Time from which content must be updated (null: no limit). * @return Tempcode Catalogue selection list */ function create_selection_list_catalogues($it = null, $prefer_ones_with_entries = false, $only_submittable = false, $updated_since = null) { $query = 'SELECT c.* FROM ' . get_table_prefix() . 'catalogues c'; if (!is_null($updated_since)) { $privacy_join = ''; $privacy_where = ''; if (addon_installed('content_privacy')) { require_code('content_privacy'); list($privacy_join, $privacy_where) = get_privacy_where_clause('catalogue_entry', 'e', $GLOBALS['FORUM_DRIVER']->get_guest_id()); } $query .= ' WHERE EXISTS(SELECT * FROM ' . get_table_prefix() . 'catalogue_entries e' . $privacy_join . ' WHERE ce_validated=1 AND ce_add_date>' . strval($updated_since) . $privacy_where . ')'; } else { if ($prefer_ones_with_entries) { if (can_arbitrary_groupby()) { $query .= ' JOIN ' . get_table_prefix() . 'catalogue_entries e ON e.c_name=c.c_name GROUP BY c.c_name'; } } } $query .= ' ORDER BY ' . $GLOBALS['SITE_DB']->translate_field_ref('c_title') . ' ASC'; $rows = $GLOBALS['SITE_DB']->query($query, intval(get_option('general_safety_listing_limit'))/*reasonable limit*/, null, false, false, array('c_title' => 'SHORT_TRANS')); if (count($rows) == intval(get_option('general_safety_listing_limit'))) { attach_message(do_lang_tempcode('TOO_MUCH_CHOOSE__ALPHABETICAL', escape_html(integer_format(intval(get_option('general_safety_listing_limit'))))), 'warn'); } $catalogues = array(); foreach ($rows as $row) { if (substr($row['c_name'], 0, 1) == '_') { continue; } if (!has_category_access(get_member(), 'catalogues_catalogue', $row['c_name'])) { continue; } if (($only_submittable) && (!has_privilege(get_member(), 'submit_midrange_content', 'cms_catalogues', array('catalogues_catalogue', $row['c_name'])))) { continue; } if (($row['c_ecommerce'] == 0) || (addon_installed('shopping'))) { $catalogues[$row['c_name']] = get_translated_text($row['c_title']); } } asort($catalogues); $out = new Tempcode(); foreach ($catalogues as $name => $title) { $selected = ($name == $it); $out->attach(form_input_list_entry($name, $selected, $title)); } return $out; } /** * Get a nice, formatted XHTML list extending from the root, and showing all subcategories, and their subcategories (ad infinitum). * * @param ID_TEXT $catalogue_name The catalogue name * @param ?AUTO_LINK $it The currently selected entry (null: none) * @param boolean $addable_filter Whether to only show for what may be added to by the current member * @param boolean $use_compound_list Whether to make the list elements store comma-separated child lists instead of IDs * @return Tempcode The list of categories */ function create_selection_list_catalogue_category_tree($catalogue_name, $it = null, $addable_filter = false, $use_compound_list = false) { if ($GLOBALS['SITE_DB']->query_select_value('catalogue_categories', 'COUNT(*)', array('c_name' => $catalogue_name)) > intval(get_option('general_safety_listing_limit'))) { return new Tempcode(); // Too many! } $tree = array(); $temp_rows = $GLOBALS['SITE_DB']->query_select('catalogue_categories', array('id', 'cc_title'), array('c_name' => $catalogue_name, 'cc_parent_id' => null), 'ORDER BY cc_order,' . $GLOBALS['SITE_DB']->translate_field_ref('cc_title'), intval(get_option('general_safety_listing_limit'))/*reasonable limit to stop it dying*/); if (count($temp_rows) == intval(get_option('general_safety_listing_limit'))) { attach_message(do_lang_tempcode('TOO_MUCH_CHOOSE__ALPHABETICAL', escape_html(integer_format(intval(get_option('general_safety_listing_limit'))))), 'warn'); } foreach ($temp_rows as $row) { $category_id = $row['id']; $subtree = get_catalogue_category_tree($catalogue_name, $category_id, '', $row, null, $addable_filter, $use_compound_list); if (($use_compound_list) && (array_key_exists(0, $subtree))) { $subtree = $subtree[0]; } $tree = array_merge($tree, $subtree); } $out = new Tempcode(); foreach ($tree as $category) { if (($addable_filter) && (!$category['addable'])) { continue; } $selected = ($category['id'] == $it); $line = do_template('CATALOGUE_CATEGORIES_LIST_LINE', array('_GUID' => '9f6bfc4f28c154c8f5d8887ce0d47c1c', 'BREADCRUMBS' => $category['breadcrumbs'], 'COUNT' => integer_format($category['entries_count']))); $out->attach(form_input_list_entry(!$use_compound_list ? strval($category['id']) : $category['compound_list'], $selected, protect_from_escaping($line->evaluate()))); } return $out; } /** * Get a list of maps containing all the subcategories, and path information, of the specified category - and those beneath it, recursively. * * @param ID_TEXT $catalogue_name The catalogue name * @param ?AUTO_LINK $category_id The category being at the root of our recursion (null: true root category) * @param string $breadcrumbs The breadcrumbs up to this point in the recursion * @param ?array $category_details The category details of the $category_id we are currently going through (null: look it up). This is here for efficiency reasons, as finding children IDs to recurse to also reveals the childs details * @param ?integer $levels The number of recursive levels to search (null: all) * @param boolean $addable_filter Whether to only show for what may be added to by the current member * @param boolean $use_compound_list Whether to make the list elements store comma-separated child lists instead of IDs * @param boolean $do_stats Whether to collect entry counts with our tree information * @return array A list of maps for all subcategories. Each map entry containins the fields 'id' (category ID) and 'breadcrumbs' (path to the category, including the categories own title), and 'entries_count' (the number of entries in the category). */ function get_catalogue_category_tree($catalogue_name, $category_id, $breadcrumbs = '', $category_details = null, $levels = null, $addable_filter = false, $use_compound_list = false, $do_stats = false) { if (!$use_compound_list) { if ($levels == -1) { return $use_compound_list ? array(array(), '') : array(); } } if (!has_category_access(get_member(), 'catalogues_catalogue', $catalogue_name)) { return $use_compound_list ? array(array(), '') : array(); } if (($category_id !== null) && (get_value('disable_cat_cat_perms') !== '1') && (!has_category_access(get_member(), 'catalogues_category', strval($category_id)))) { return $use_compound_list ? array(array(), '') : array(); } if ($category_details === null && $category_id !== null) { $_category_details = $GLOBALS['SITE_DB']->query_select('catalogue_categories', array('cc_title'), array('id' => $category_id), '', 1); if (!array_key_exists(0, $_category_details)) { warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'catalogue_category')); } $category_details = $_category_details[0]; } $title = ($category_details === null) ? do_lang('HOME') : get_translated_text($category_details['cc_title']); $breadcrumbs .= $title; // We'll be putting all children in this entire tree into a single list $children = array(); $is_tree = $GLOBALS['SITE_DB']->query_select_value_if_there('catalogues', 'c_is_tree', array('c_name' => $catalogue_name)); if ($is_tree === null) { warn_exit(do_lang_tempcode('_MISSING_RESOURCE', escape_html($catalogue_name), 'catalogue')); } if ($category_id !== null) { $children[0]['id'] = $category_id; $children[0]['title'] = $title; $children[0]['breadcrumbs'] = $breadcrumbs; $children[0]['compound_list'] = strval($category_id) . ','; $children[0]['entries_count'] = $GLOBALS['SITE_DB']->query_select_value('catalogue_entries', 'COUNT(*)', array('cc_id' => $category_id)); if ($addable_filter) { $children[0]['addable'] = has_submit_permission('mid', get_member(), get_ip_address(), 'cms_catalogues', array('catalogues_catalogue', $catalogue_name) + ((get_value('disable_cat_cat_perms') !== '1') ? array('catalogues_category', $category_id) : array())); } } // Children of this category $rows = $GLOBALS['SITE_DB']->query_select('catalogue_categories', array('id', 'cc_title'), array('c_name' => $catalogue_name, 'cc_parent_id' => $category_id), 'ORDER BY cc_order,' . $GLOBALS['SITE_DB']->translate_field_ref('cc_title'), intval(get_option('general_safety_listing_limit'))/*reasonable limit to stop it dying*/); if (get_page_name() == 'cms_catalogues') { if (count($rows) == intval(get_option('general_safety_listing_limit'))) { attach_message(do_lang_tempcode('TOO_MUCH_CHOOSE__ALPHABETICAL', escape_html(integer_format(intval(get_option('general_safety_listing_limit'))))), 'warn'); } } $no_root = !array_key_exists(0, $children); if (!$no_root) { $children[0]['child_count'] = count($rows); } $child_breadcrumbs = $breadcrumbs . ' > '; if (($levels !== 0) || ($use_compound_list)) { foreach ($rows as $child) { $child_id = $child['id']; $child_children = get_catalogue_category_tree($catalogue_name, $child_id, $child_breadcrumbs, $child, ($levels === null) ? null : ($levels - 1), $addable_filter, $use_compound_list, $do_stats); if ($child_children != array()) { if ($use_compound_list) { list($child_children, $_compound_list) = $child_children; if (!$no_root) { $children[0]['compound_list'] .= $_compound_list; } } if ($levels !== 0) { $children = array_merge($children, $child_children); } } } } return $use_compound_list ? array($children, $no_root ? '' : $children[0]['compound_list']) : $children; } /** * Get a nice, formatted XHTML list of entries, in catalogue category tree structure * * @param ID_TEXT $catalogue_name The catalogue name * @param ?AUTO_LINK $it The currently selected entry (null: none selected) * @param ?AUTO_LINK $submitter Only show entries submitted by this member (null: no filter) * @param boolean $editable_filter Whether to only show for what may be edited by the current member * @return Tempcode The list of entries */ function create_selection_list_catalogue_entries_tree($catalogue_name, $it = null, $submitter = null, $editable_filter = false) { $tree = get_catalogue_entries_tree($catalogue_name, $submitter, null, null, null, null, $editable_filter); $out = ''; // XHTMLXHTML foreach ($tree as $category) { foreach ($category['entries'] as $eid => $etitle) { $selected = ($eid == $it); $line = do_template('CATALOGUE_ENTRIES_LIST_LINE', array('_GUID' => '0ccffeff5b80b1840188b83aaee8d9f2', 'BREADCRUMBS' => $category['breadcrumbs'], 'NAME' => $etitle)); $out .= '' . "\n"; } } if ($GLOBALS['XSS_DETECT']) { ocp_mark_as_escaped($out); } return make_string_tempcode($out); } /** * Get a list of maps containing all the catalogue entries, and path information, under the specified category - and those beneath it, recursively. * * @param ID_TEXT $catalogue_name The catalogue name * @param ?AUTO_LINK $submitter Only show entries submitted by this member (null: no filter) * @param ?AUTO_LINK $category_id The category being at the root of our recursion (null: true root) * @param ?string $breadcrumbs The breadcrumbs up to this point in the recursion (null: blank, as we are starting the recursion) * @param ?ID_TEXT $title The name of the $category_id we are currently going through (null: look it up). This is here for efficiency reasons, as finding children IDs to recurse to also reveals the childs title * @param ?integer $levels The number of recursive levels to search (null: all) * @param boolean $editable_filter Whether to only show for what may be edited by the current member * @return array A list of maps for all categories. Each map entry containins the fields 'id' (category ID) and 'breadcrumbs' (path to the category, including the categories own title), and more. */ function get_catalogue_entries_tree($catalogue_name, $submitter = null, $category_id = null, $breadcrumbs = null, $title = null, $levels = null, $editable_filter = false) { if (($category_id === null) && ($levels === null)) { if ($GLOBALS['SITE_DB']->query_select_value('catalogue_categories', 'COUNT(*)', array('c_name' => $catalogue_name)) > intval(get_option('general_safety_listing_limit'))) { return array(); // Too many! } } if ($category_id === null) { $is_tree = $GLOBALS['SITE_DB']->query_select_value_if_there('catalogues', 'c_is_tree', array('c_name' => $catalogue_name)); if ($is_tree === null) { return array(); } if ($is_tree == 0) { $temp_rows = $GLOBALS['SITE_DB']->query_select('catalogue_categories', array('id', 'cc_title'), array('c_name' => $catalogue_name, 'cc_parent_id' => null), 'ORDER BY cc_order,' . $GLOBALS['SITE_DB']->translate_field_ref('cc_title'), intval(get_option('general_safety_listing_limit'))/*reasonable limit to stop it dying*/); if (get_page_name() == 'cms_catalogues') { if (count($temp_rows) == intval(get_option('general_safety_listing_limit'))) { attach_message(do_lang_tempcode('TOO_MUCH_CHOOSE__ALPHABETICAL', escape_html(integer_format(intval(get_option('general_safety_listing_limit'))))), 'warn'); } } $children = array(); foreach ($temp_rows as $row) { $children = array_merge(get_catalogue_entries_tree($catalogue_name, $submitter, $row['id'], null, get_translated_text($row['cc_title']), 1, $editable_filter), $children); } return $children; } $temp_rows = $GLOBALS['SITE_DB']->query_select('catalogue_categories', array('id', 'cc_title'), array('c_name' => $catalogue_name, 'cc_parent_id' => null), 'ORDER BY cc_order,' . $GLOBALS['SITE_DB']->translate_field_ref('cc_title'), 1); if (!array_key_exists(0, $temp_rows)) { return array(); } $category_id = $temp_rows[0]['id']; $title = get_translated_text($temp_rows[0]['cc_title']); } if ($breadcrumbs === null) { $breadcrumbs = ''; } if (!has_category_access(get_member(), 'catalogues_catalogue', $catalogue_name)) { return array(); } if ((get_value('disable_cat_cat_perms') !== '1') && (!has_category_access(get_member(), 'catalogues_category', strval($category_id)))) { return array(); } // Put our title onto our breadcrumbs if ($title === null) { $_title = $GLOBALS['SITE_DB']->query_select_value_if_there('catalogue_categories', 'cc_title', array('id' => $category_id)); if ($_title === null) { return array(); } $title = get_translated_text($_title); } $breadcrumbs .= $title; // We'll be putting all children in this entire tree into a single list $children = array(); $children[0] = array(); $children[0]['id'] = $category_id; $children[0]['title'] = $title; $children[0]['breadcrumbs'] = $breadcrumbs; // Children of this category $rows = $GLOBALS['SITE_DB']->query_select('catalogue_categories', array('id', 'cc_title'), array('cc_parent_id' => $category_id), '', intval(get_option('general_safety_listing_limit'))/*reasonable limit to stop it dying*/); if (get_page_name() == 'cms_catalogues') { if (count($rows) == intval(get_option('general_safety_listing_limit'))) { attach_message(do_lang_tempcode('TOO_MUCH_CHOOSE__RECENT_ONLY', escape_html(integer_format(intval(get_option('general_safety_listing_limit'))))), 'warn'); } } $where = array('cc_id' => $category_id); if ($submitter !== null) { $where['ce_submitter'] = $submitter; } $erows = $GLOBALS['SITE_DB']->query_select('catalogue_entries', array('id', 'ce_submitter'), $where, 'ORDER BY ce_add_date DESC', intval(get_option('general_safety_listing_limit'))/*reasonable limit*/); if (get_page_name() == 'cms_catalogues') { if (count($erows) == intval(get_option('general_safety_listing_limit'))) { attach_message(do_lang_tempcode('TOO_MUCH_CHOOSE__RECENT_ONLY', escape_html(integer_format(intval(get_option('general_safety_listing_limit'))))), 'warn'); } } $children[0]['entries'] = array(); foreach ($erows as $row) { if (($editable_filter) && (!has_edit_permission('mid', get_member(), $row['ce_submitter'], 'cms_catalogues', array('catalogues_catalogue', $catalogue_name) + ((get_value('disable_cat_cat_perms') !== '1') ? array('catalogues_category', $category_id) : array())))) { continue; } $entry_fields = get_catalogue_entry_field_values($catalogue_name, $row['id'], array(0)); $name = $entry_fields[0]['effective_value_pure']; // 'Name' is value of first field $children[0]['entries'][$row['id']] = $name; } asort($children[0]['entries']); $children[0]['child_entry_count'] = count($children[0]['entries']); if ($levels === 0) { // We throw them away now because they're not on the desired level $children[0]['entries'] = array(); } $children[0]['child_count'] = count($rows); $breadcrumbs .= ' > '; if ($levels !== 0) { foreach ($rows as $child) { $child_id = $child['id']; $child_title = get_translated_text($child['cc_title']); $child_breadcrumbs = $breadcrumbs; $child_children = get_catalogue_entries_tree($catalogue_name, $submitter, $child_id, $child_breadcrumbs, $child_title, ($levels === null) ? null : ($levels - 1), $editable_filter); $children = array_merge($children, $child_children); } } return $children; } /** * Get a formatted XHTML string of the route back to the specified root, from the specified category. * * @param AUTO_LINK $category_id The category we are finding for * @param ?AUTO_LINK $root The root of the tree (null: the true root) * @param boolean $no_link_for_me_sir Whether to include category links at this level (the recursed levels will always contain links - the top level is optional, hence this parameter) * @param boolean $attach_to_url_filter Whether to copy through any filter parameters in the URL, under the basis that they are associated with what this box is browsing * @return array The breadcrumbs */ function catalogue_category_breadcrumbs($category_id, $root = null, $no_link_for_me_sir = true, $attach_to_url_filter = false) { if ($category_id === null) { return array(); } $map = array('page' => 'catalogues', 'type' => 'category', 'id' => $category_id); if (get_page_name() == 'catalogues') { $map += propagate_filtercode(); } $page_link = build_page_link($map, get_module_zone('catalogues')); if (($category_id != $root) || (!$no_link_for_me_sir)) { global $PT_PAIR_CACHE; if (!array_key_exists($category_id, $PT_PAIR_CACHE)) { $category_rows = $GLOBALS['SITE_DB']->query_select('catalogue_categories', array('cc_parent_id', 'cc_title'), array('id' => $category_id), '', 1); if (!array_key_exists(0, $category_rows)) { attach_message(do_lang_tempcode('CAT_NOT_FOUND', escape_html(strval($category_id)), 'catalogue_category'), 'warn'); return array(); } $PT_PAIR_CACHE[$category_id] = $category_rows[0]; } if ($PT_PAIR_CACHE[$category_id]['cc_parent_id'] == $category_id) { fatal_exit(do_lang_tempcode('RECURSIVE_TREE_CHAIN', escape_html(strval($category_id)), 'catalogue_category')); } } if ($category_id == $root) { $below = array(); } else { $below = catalogue_category_breadcrumbs($PT_PAIR_CACHE[$category_id]['cc_parent_id'], $root, false, $attach_to_url_filter); } $segments = array(); if (!$no_link_for_me_sir) { $title = get_translated_text($PT_PAIR_CACHE[$category_id]['cc_title']); $segments[] = array($page_link, $title); } return array_merge($below, $segments); } /** * Check the current catalogue is an ecommerce catalogue * * @param SHORT_TEXT $catalogue_name Catalogue name * @param ?array $catalogue Catalogue row (null: look up) * @return boolean Status of ecommerce catalogue check */ function is_ecommerce_catalogue($catalogue_name, $catalogue = null) { if (($catalogue !== null) && ($catalogue['c_ecommerce'] == 0)) { return false; } if (!addon_installed('ecommerce')) { return false; } if (!addon_installed('shopping')) { return false; } if ($GLOBALS['SITE_DB']->query_select_value_if_there('catalogues', 'c_name', array('c_name' => $catalogue_name, 'c_ecommerce' => 1)) === null) { return false; } else { return true; } } /** * Check selected entry is an ecommerce catalogue entry * * @param AUTO_LINK $entry_id Entry ID * @return boolean Status of entry type check */ function is_ecommerce_catalogue_entry($entry_id) { $catalogue_name = $GLOBALS['SITE_DB']->query_select_value('catalogue_entries', 'c_name', array('id' => $entry_id)); return is_ecommerce_catalogue($catalogue_name); } /** * Display a catalogue entry * * @param AUTO_LINK $id Entry ID * @param boolean $no_title Whether to skip rendering a title * @param boolean $attach_to_url_filter Whether to copy through any filter parameters in the URL, under the basis that they are associated with what this box is browsing * @return Tempcode Tempcode interface to display an entry */ function render_catalogue_entry_screen($id, $no_title = false, $attach_to_url_filter = true) { if (addon_installed('content_privacy')) { require_code('content_privacy'); check_privacy('catalogue_entry', strval($id)); } require_code('feedback'); if (addon_installed('ecommerce')) { require_code('ecommerce'); } require_code('images'); require_css('catalogues'); require_lang('catalogues'); $entries = $GLOBALS['SITE_DB']->query_select('catalogue_entries', array('*'), array('id' => $id), '', 1); if (!array_key_exists(0, $entries)) { return warn_screen(get_screen_title('CATALOGUES'), do_lang_tempcode('MISSING_RESOURCE', 'catalogue_entry')); } $entry = $entries[0]; $categories = $GLOBALS['SITE_DB']->query_select('catalogue_categories', array('*'), array('id' => $entry['cc_id']), '', 1); if (!array_key_exists(0, $categories)) { warn_exit(do_lang_tempcode('CAT_NOT_FOUND', escape_html(strval($entry['cc_id'])), 'catalogue_category')); } $category = $categories[0]; require_code('site'); set_feed_url('?mode=catalogues&select=' . strval($entry['cc_id'])); $catalogue_name = $category['c_name']; $catalogue = load_catalogue_row($catalogue_name); // Permission for here? if (!has_category_access(get_member(), 'catalogues_catalogue', $catalogue_name)) { access_denied('CATALOGUE_ACCESS'); } if ((get_value('disable_cat_cat_perms') !== '1') && (!has_category_access(get_member(), 'catalogues_category', strval($entry['cc_id'])))) { access_denied('CATEGORY_ACCESS'); } $ecommerce = is_ecommerce_catalogue($catalogue_name); if ($ecommerce) { $tpl_set = 'products'; } else { $tpl_set = $catalogue_name; } $count_views = (get_db_type() != 'xml') && (get_value('no_view_counts') !== '1') && (is_null(get_bot_type())); if ($count_views) { $entry['ce_views']++; } $root = get_param_integer('keep_catalogue_' . $catalogue_name . '_root', null); $breadcrumbs = array(); $map = get_catalogue_entry_map($entry, $catalogue, 'PAGE', $tpl_set, $root, null, null, true, true, null, $breadcrumbs); if ($count_views) { if (!$GLOBALS['SITE_DB']->table_is_locked('catalogue_entries')) { $GLOBALS['SITE_DB']->query_update('catalogue_entries', array('ce_views' => $entry['ce_views']), array('id' => $id), '', 1, null, false, true); } } // Validation if (($entry['ce_validated'] == 0) && (addon_installed('unvalidated'))) { if ((!has_privilege(get_member(), 'jump_to_unvalidated')) && ((is_guest()) || ($entry['ce_submitter'] != get_member()))) { access_denied('PRIVILEGE', 'jump_to_unvalidated'); } $map['WARNINGS'] = do_template('WARNING_BOX', array( '_GUID' => 'bf604859a572ca53e969bec3d91f9cfb', 'WARNING' => do_lang_tempcode((get_param_integer('redirected', 0) == 1) ? 'UNVALIDATED_TEXT_NON_DIRECT' : 'UNVALIDATED_TEXT', 'catalogue_entry'), )); } else { $map['WARNINGS'] = ''; } // Finding any hook exists for this product if (addon_installed('ecommerce')) { $object = find_product(strval($id)); if (is_object($object) && method_exists($object, 'get_custom_product_map_fields')) { $object->get_custom_product_map_fields($id, $map); } } // Main rendering... $map['ENTRY'] = do_template('CATALOGUE_' . $tpl_set . '_FIELDMAP_ENTRY_WRAP', $map + array('ENTRY_SCREEN' => true, 'GIVE_CONTEXT' => false), null, false, 'CATALOGUE_DEFAULT_FIELDMAP_ENTRY_WRAP'); $map['ADD_DATE'] = get_timezoned_date_tempcode($entry['ce_add_date']); $map['ADD_DATE_RAW'] = strval($entry['ce_add_date']); $map['EDIT_DATE'] = ($entry['ce_edit_date'] === null) ? '' : get_timezoned_date_tempcode($entry['ce_edit_date']); $map['EDIT_DATE_RAW'] = ($entry['ce_edit_date'] === null) ? '' : strval($entry['ce_edit_date']); $map['VIEWS'] = integer_format($entry['ce_views']); $title_to_use = do_lang_tempcode($catalogue_name . '__CATALOGUE_ENTRY', $map['FIELD_0']); $title_to_use_2 = do_lang($catalogue_name . '__CATALOGUE_ENTRY', $map['FIELD_0_PLAIN'], null, null, null, false); if ($title_to_use_2 === null) { $title_to_use = do_lang_tempcode('DEFAULT__CATALOGUE_ENTRY', make_fractionable_editable('catalogue_entry', $id, is_object($map['FIELD_0']) ? $map['FIELD_0'] : make_string_tempcode($map['FIELD_0']))); $title_to_use_2 = do_lang('DEFAULT__CATALOGUE_ENTRY', is_object($map['FIELD_0']) ? $map['FIELD_0']->evaluate() : $map['FIELD_0']); $len = strlen(trim(strip_html($title_to_use_2))); if (($len > 20) || ($len < 3)) {// We revert to raw ID if it appeared the rendered one was not strippable back from HTML to text; raw ID is possibly cryptic unfortunately $title_to_use_2 = do_lang('DEFAULT__CATALOGUE_ENTRY', $map['FIELD_0_PLAIN']); } } if ($no_title) { $map['TITLE'] = new Tempcode(); } else { if ((get_value('no_awards_in_titles') !== '1') && (addon_installed('awards'))) { require_code('awards'); $awards = find_awards_for('catalogue_entry', strval($id)); } else { $awards = array(); } $map['TITLE'] = get_screen_title($title_to_use, false, null, null, $awards); } $map['SUBMITTER'] = strval($entry['ce_submitter']); require_code('seo2'); if (is_object($title_to_use_2)) { $title_to_use_2 = $title_to_use_2->evaluate(); } seo_meta_load_for('catalogue_entry', strval($id), strip_html($title_to_use_2)); $map['CATEGORY_TITLE'] = get_translated_text($category['cc_title']); $map['CAT'] = strval($entry['cc_id']); $map['TAGS'] = get_loaded_tags('catalogue_entries'); $_breadcrumbs = array(); if ($root === null) { $_breadcrumbs[] = array('_SELF:_SELF:browse' . ($ecommerce ? ':ecommerce=1' : ''), do_lang_tempcode('CATALOGUES')); } if ($catalogue['c_is_tree'] == 1) { $breadcrumbs = array_merge($_breadcrumbs, $breadcrumbs); } else { if ($root === null) { $page_link = build_page_link(array('page' => '_SELF', 'type' => 'index', 'id' => $catalogue_name, 'tree' => $catalogue['c_is_tree']), '_SELF'); $_breadcrumbs[] = array($page_link, get_translated_text($catalogue['c_title'])); } $breadcrumbs = array_merge($_breadcrumbs, $breadcrumbs); } $breadcrumbs[] = array('', $title_to_use); breadcrumb_set_parents($breadcrumbs); set_extra_request_metadata(array( 'type' => get_translated_text($catalogue['c_title']) . ' entry', 'title' => comcode_escape($title_to_use_2), 'identifier' => '_SEARCH:catalogues:entry:' . strval($id), ), $entry, 'catalogue_entry', strval($id)); return do_template('CATALOGUE_' . $tpl_set . '_ENTRY_SCREEN', $map, null, false, 'CATALOGUE_DEFAULT_ENTRY_SCREEN'); }