<?php /*

 Composr
 Copyright (c) ocProducts, 2004-2016

 See text/EN/licence.txt for full licencing information.


 NOTE TO PROGRAMMERS:
   Do not edit this file. If you need to make changes, save your changed file to the appropriate *_custom folder
   **** If you ignore this advice, then your website upgrades (e.g. for bug fixes) will likely kill your changes ****

*/

/**
 * @license    http://opensource.org/licenses/cpal_1.0 Common Public Attribution License
 * @copyright  ocProducts Ltd
 * @package    galleries
 */

/**
 * Standard code module initialisation function.
 *
 * @ignore
 */
function init__galleries()
{
    global $GALLERY_ENTRIES_CATS_USED_CACHE;
    $GALLERY_ENTRIES_CATS_USED_CACHE = null;
    global $GALLERY_PAIRS_CACHE;
    $GALLERY_PAIRS_CACHE = null;
    global $PT_PAIR_CACHE_G;
    $PT_PAIR_CACHE_G = array();

    require_code('images');
}

/**
 * Render an image box.
 *
 * @param  array $row The image row
 * @param  ID_TEXT $zone The zone the galleries module is in
 * @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  ?ID_TEXT $root Virtual root to use (null: none)
 * @param  ID_TEXT $guid Overridden GUID to send to templates (blank: none)
 * @return Tempcode The rendered box
 */
function render_image_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('galleries');
    require_css('galleries');
    require_code('images');

    $just_image_row = db_map_restrict($row, array('id', 'description'));

    // URL
    $map = array('page' => 'galleries', 'type' => 'image', 'id' => $row['id']);
    if ((!is_null($root)) && ($root != 'root')) {
        $map['keep_gallery_root'] = $root;
    }
    $url = build_url($map, $zone);

    // Breadcrumbs
    $breadcrumbs = new Tempcode();
    if ($include_breadcrumbs) {
        $breadcrumbs = breadcrumb_segments_to_tempcode(gallery_breadcrumbs($row['cat'], is_null($root) ? get_param_string('keep_gallery_root', 'root') : $root, false, $zone));
    }

    // Title
    $title = $GLOBALS['SITE_DB']->query_select_value_if_there('galleries', 'fullname', array('name' => $row['cat']));
    if (is_null($title)) {
        $gallery_title = do_lang('UNKNOWN');
    } else {
        $gallery_title = get_translated_text($title);
    }

    // Description
    $description = get_translated_tempcode('images', $just_image_row, 'description');

    // Images
    $thumb_url = ensure_thumbnail($row['url'], $row['thumb_url'], 'galleries', 'images', $row['id']);
    $thumb = do_image_thumb($thumb_url, $description, true);
    $image_url = $row['url'];
    if (url_is_local($image_url)) {
        $image_url = get_custom_base_url() . '/' . $image_url;
    }

    // Render
    return do_template('GALLERY_IMAGE_BOX', array(
        '_GUID' => ($guid != '') ? $guid : '733c22f14649705418ac0b155d11a661',
        'GIVE_CONTEXT' => $give_context,
        'ADD_DATE_RAW' => strval($row['add_date']),
        'ID' => strval($row['id']),
        'TITLE' => get_translated_text($row['title']),
        'NOTES' => $row['notes'],
        'GALLERY_TITLE' => $gallery_title,
        'CAT' => $row['cat'],
        'VIEWS' => strval($row['image_views']),
        'BREADCRUMBS' => $breadcrumbs,
        'URL' => $url,
        'IMAGE_URL' => $image_url,
        'DESCRIPTION' => $description,
        'THUMB' => $thumb,
        'THUMB_URL' => $thumb_url,
    ));
}

/**
 * Render a video box.
 *
 * @param  array $row The video row
 * @param  ID_TEXT $zone The zone the galleries module is in
 * @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  ?ID_TEXT $root Virtual root to use (null: none)
 * @param  ID_TEXT $guid Overridden GUID to send to templates (blank: none)
 * @return Tempcode The rendered box
 */
function render_video_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('galleries');
    require_css('galleries');
    require_code('images');

    $just_video_row = db_map_restrict($row, array('id', 'description'));

    // URL
    $map = array('page' => 'galleries', 'type' => 'video', 'id' => $row['id']);
    if ((!is_null($root)) && ($root != 'root')) {
        $map['keep_gallery_root'] = $root;
    }
    $url = build_url($map, $zone);

    // Breadcrumbs
    $breadcrumbs = new Tempcode();
    if ($include_breadcrumbs) {
        $breadcrumbs = breadcrumb_segments_to_tempcode(gallery_breadcrumbs($row['cat'], is_null($root) ? get_param_string('keep_gallery_root', 'root') : $root, false, $zone));
    }

    // Title
    $title = $GLOBALS['SITE_DB']->query_select_value_if_there('galleries', 'fullname', array('name' => $row['cat']));
    if (is_null($title)) {
        $gallery_title = do_lang('UNKNOWN');
    } else {
        $gallery_title = get_translated_text($title);
    }

    // Description
    $description = get_translated_tempcode('videos', $just_video_row, 'description');

    // Images
    $thumb_url = ensure_thumbnail($row['url'], $row['thumb_url'], 'galleries', 'videos', $row['id']);
    $thumb = do_image_thumb($thumb_url, $description, true);
    $video_url = $row['url'];
    if (url_is_local($video_url)) {
        $video_url = get_custom_base_url() . '/' . $video_url;
    }

    // Render
    return do_template('GALLERY_VIDEO_BOX', array(
        '_GUID' => ($guid != '') ? $guid : '22ef89b03900cbb264bd945b5bade75d',
        'GIVE_CONTEXT' => $give_context,
        'ADD_DATE_RAW' => strval($row['add_date']),
        'ID' => strval($row['id']),
        'TITLE' => get_translated_text($row['title']),
        'NOTES' => $row['notes'],
        'GALLERY_TITLE' => $gallery_title,
        'CAT' => $row['cat'],
        'VIEWS' => strval($row['video_views']),
        'BREADCRUMBS' => $breadcrumbs,
        'URL' => $url,
        'VIDEO_URL' => $video_url,
        'DESCRIPTION' => $description,
        'THUMB' => $thumb,
        'THUMB_URL' => $thumb_url,
        'VIDEO_WIDTH' => strval($row['video_width']),
        'VIDEO_HEIGHT' => strval($row['video_height']),
        'VIDEO_LENGTH' => strval($row['video_length']),
    ));
}

/**
 * Get preview detailing for a gallery.
 *
 * @param  array $myrow The database row of the gallery
 * @param  ID_TEXT $root The virtual root of the gallery
 * @param  boolean $show_member_stats_if_appropriate Whether to show member stats if it is a member owned gallery
 * @param  ID_TEXT $zone The zone that the gallery module we are linking to is in
 * @param  boolean $quit_if_empty Whether to not show anything if the gallery is empty
 * @param  boolean $preview Whether only to show 'preview' details
 * @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  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 The preview
 */
function render_gallery_box($myrow, $root = 'root', $show_member_stats_if_appropriate = false, $zone = '_SEARCH', $quit_if_empty = true, $preview = false, $give_context = true, $include_breadcrumbs = true, $attach_to_url_filter = false, $guid = '')
{
    if (is_null($myrow)) { // Should never happen, but we need to be defensive
        return new Tempcode();
    }

    require_lang('galleries');
    require_css('galleries');

    $just_gallery_row = db_map_restrict($myrow, array('name', 'description'));

    $member_id = get_member_id_from_gallery_name($myrow['name'], $myrow, true);
    $is_member = !is_null($member_id);

    // URL
    $map = array('page' => 'galleries', 'type' => 'browse', 'keep_gallery_root' => ($root == 'root') ? null : $root, 'id' => $myrow['name']);
    if ((!is_null($root)) && ($root != 'root')) {
        $map['keep_gallery_root'] = $root;
    }
    if ($attach_to_url_filter) {
        $map += propagate_filtercode();
    }
    $url = build_url($map, $zone);

    // Basic details
    $_title = get_translated_text($myrow['fullname']);
    $add_date = get_timezoned_date_tempcode($myrow['add_date'], false);
    $description = get_translated_tempcode('galleries', $just_gallery_row, 'description');

    // Member details
    if ($show_member_stats_if_appropriate) {
        if (($is_member) && (get_forum_type() == 'cns')) {
            require_code('cns_members');
            require_code('cns_members2');
            $member_info = render_member_box($member_id, true, null, null, true, null, false);
        } else {
            $member_info = new Tempcode();
        }
    } else {
        $member_info = new Tempcode();
    }

    // Metadata
    list($num_children, $num_images, $num_videos) = get_recursive_gallery_details($myrow['name']);
    if ($num_children == 0) {
        if ($myrow['accept_videos'] == 0) {
            $lang = do_lang_tempcode('_SUBGALLERY_BITS_IMAGES', escape_html(integer_format($num_images)), escape_html(integer_format($num_videos)), escape_html(integer_format($num_images + $num_videos)));
        } elseif ($myrow['accept_images'] == 0) {
            $lang = do_lang_tempcode('_SUBGALLERY_BITS_VIDEOS', escape_html(integer_format($num_images)), escape_html(integer_format($num_videos)), escape_html(integer_format($num_images + $num_videos)));
        } else {
            $lang = do_lang_tempcode('_SUBGALLERY_BITS', escape_html(integer_format($num_images)), escape_html(integer_format($num_videos)), escape_html(integer_format($num_images + $num_videos)));
        }
    } else {
        if ($myrow['accept_videos'] == 0) {
            $lang = do_lang_tempcode('SUBGALLERY_BITS_IMAGES', escape_html(integer_format($num_children)), escape_html(integer_format($num_images)), array(integer_format($num_videos), escape_html(integer_format($num_images + $num_videos))));
        } elseif ($myrow['accept_images'] == 0) {
            $lang = do_lang_tempcode('SUBGALLERY_BITS_VIDEOS', escape_html(integer_format($num_children)), escape_html(integer_format($num_images)), array(integer_format($num_videos), escape_html(integer_format($num_images + $num_videos))));
        } else {
            $lang = do_lang_tempcode('SUBGALLERY_BITS', escape_html(integer_format($num_children)), escape_html(integer_format($num_images)), array(integer_format($num_videos), escape_html(integer_format($num_images + $num_videos))));
        }
    }

    // If empty gallery (special case for galleries - as galleries may often be empty, due to Personal Gallery system)
    if (($quit_if_empty) && ($num_images == 0) && ($num_videos == 0) && ($num_children == 0)) {
        return new Tempcode();
    }

    // Image
    $pic = $myrow['rep_image'];
    if (($pic == '') && ($is_member)) {
        $pic = $GLOBALS['FORUM_DRIVER']->get_member_avatar_url($member_id);
    }
    $thumb_order = 'ORDER BY add_date ASC';
    if (get_option('reverse_thumb_order') == '1') {
        $thumb_order = 'ORDER BY add_date DESC';
    }
    if ($pic == '') {
        if (addon_installed('content_privacy')) {
            require_code('content_privacy');
            list($privacy_join_video, $privacy_where_video) = get_privacy_where_clause('video', 'r');
            list($privacy_join_image, $privacy_where_image) = get_privacy_where_clause('image', 'r');
        } else {
            $privacy_join_video = '';
            $privacy_where_video = '';
            $privacy_join_image = '';
            $privacy_where_image = '';
        }

        $pic = $GLOBALS['SITE_DB']->query_select_value_if_there('images r' . $privacy_join_image, 'thumb_url', array('cat' => $myrow['name'], 'validated' => 1), $privacy_where_image . ' ' . $thumb_order);
        if ($pic === '') {
            require_code('images');
            $temp = $GLOBALS['SITE_DB']->query_select('images r' . $privacy_join_image, array('id', 'url'), array('cat' => $myrow['name'], 'validated' => 1),  $privacy_where_image . ' ' . $thumb_order, 1);
            $thumb_url = ensure_thumbnail($temp[0]['url'], '', 'galleries', 'images', $temp[0]['id']);
        }
        if ($pic === null) {
            $pic = $GLOBALS['SITE_DB']->query_select_value_if_there('videos r' . $privacy_join_video, 'thumb_url', array('cat' => $myrow['name'], 'validated' => 1),  $privacy_where_video . ' ' . $thumb_order);
        }
        if ($pic === null) {
            $pic = '';
        }
    }
    if (($pic != '') && (url_is_local($pic))) {
        $pic = get_custom_base_url() . '/' . $pic;
    }
    if ($pic != '') {
        require_code('images');
        $thumb = do_image_thumb($pic, '');
    } else {
        $thumb = new Tempcode();
    }

    // Breadcrumbs
    $breadcrumbs = mixed();
    if ($include_breadcrumbs) {
        $breadcrumbs = breadcrumb_segments_to_tempcode(gallery_breadcrumbs($myrow['name'], is_null($root) ? get_param_string('keep_gallery_root', 'root') : $root, $attach_to_url_filter));
    }

    // Render
    return do_template('GALLERY_BOX', array(
        '_GUID' => ($guid != '') ? $guid : '0dbec2f11de63b0402471fe5c8b32865',
        'GIVE_CONTEXT' => $give_context,
        'NUM_VIDEOS' => strval($num_videos),
        'NUM_IMAGES' => strval($num_images),
        'NUM_CHILDREN' => strval($num_children),
        'ID' => $myrow['name'],
        'LANG' => $lang,
        'ADD_DATE' => $add_date,
        'ADD_DATE_RAW' => strval($myrow['add_date']),
        'MEMBER_INFO' => $member_info,
        'URL' => $url,
        'THUMB' => $thumb,
        'PIC' => $pic,
        'TITLE' => $_title,
        'DESCRIPTION' => $description,
        'BREADCRUMBS' => $breadcrumbs,
    ));
}

/**
 * Find the default number of images per page in the galleries.
 *
 * @return integer Images per page
 */
function get_default_gallery_max()
{
    $option = get_option('gallery_selectors');
    if ($option == '') {
        return 12;
    }
    $selectors = explode(',', $option);
    return intval($selectors[0]);
}

/**
 * Find whether a certain gallery has any content (images, videos, or subgalleries).
 * Doesn't do a recursive search.
 *
 * @param  ID_TEXT $name The name of the gallery
 * @return boolean The answer
 */
function gallery_has_content($name)
{
    $num_galleries = null;

    global $GALLERY_ENTRIES_CATS_USED_CACHE;
    if (is_null($GALLERY_ENTRIES_CATS_USED_CACHE)) {
        $num_galleries = $GLOBALS['SITE_DB']->query_select_value('galleries', 'COUNT(*)');

        $GALLERY_ENTRIES_CATS_USED_CACHE = array();
        $images_cats = $GLOBALS['SITE_DB']->query_select('images', array('DISTINCT cat'), ($num_galleries < intval(get_option('general_safety_listing_limit'))) ? array('validated' => 1) : array('validated' => 1, 'cat' => $name));
        foreach ($images_cats as $images_cat) {
            $GALLERY_ENTRIES_CATS_USED_CACHE[$images_cat['cat']] = true;
        }
        $videos_cats = $GLOBALS['SITE_DB']->query_select('videos', array('DISTINCT cat'), ($num_galleries < intval(get_option('general_safety_listing_limit'))) ? array('validated' => 1) : array('validated' => 1, 'cat' => $name));
        foreach ($videos_cats as $videos_cat) {
            $GALLERY_ENTRIES_CATS_USED_CACHE[$videos_cat['cat']] = true;
        }
    }
    if (array_key_exists($name, $GALLERY_ENTRIES_CATS_USED_CACHE)) {
        if ($num_galleries >= intval(get_option('general_safety_listing_limit'))) {
            $GALLERY_ENTRIES_CATS_USED_CACHE = null; // It's not right so reset it
        }
        return true;
    }
    if ($num_galleries >= intval(get_option('general_safety_listing_limit'))) {
        $GALLERY_ENTRIES_CATS_USED_CACHE = null; // It's not right so reset it
    }

    global $GALLERY_PAIRS_CACHE;
    if (is_null($GALLERY_PAIRS_CACHE)) {
        if (is_null($num_galleries)) {
            $num_galleries = $GLOBALS['SITE_DB']->query_select_value('galleries', 'COUNT(*)');
        }

        if ($num_galleries < intval(get_option('general_safety_listing_limit'))) {
            $GALLERY_PAIRS_CACHE = collapse_2d_complexity('name', 'parent_id', $GLOBALS['SITE_DB']->query_select('galleries', array('name', 'parent_id')));
        } else {
            return !is_null($GLOBALS['SITE_DB']->query_select_value_if_there('galleries', 'name', array('parent_id' => $name)));
        }
    }
    foreach ($GALLERY_PAIRS_CACHE as $_parent_id) {
        if ($_parent_id == $name) {
            return true;
        }
    }

    return false;
}

/**
 * Find the owner of a gallery.
 *
 * @param  ID_TEXT $gallery_name The name of the gallery
 * @param  ?array $row Gallery row (null: look it up)
 * @param  boolean $only_if_personal_gallery Only non-null if it is a personal gallery
 * @return ?MEMBER The owner of the gallery (null: not a member owned gallery)
 */
function get_member_id_from_gallery_name($gallery_name, $row = null, $only_if_personal_gallery = false)
{
    $is_member = (substr($gallery_name, 0, 7) == 'member_');
    if (!$is_member) {
        if ($only_if_personal_gallery) {
            return null;
        }

        if (is_null($row)) {
            $rows = $GLOBALS['SITE_DB']->query_select('galleries', array('g_owner'), array('name' => $gallery_name));
            if (!isset($rows[0])) {
                warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'gallery'));
            }
            $row = $rows[0];
        }

        $ret = $row['g_owner'];
    } else {
        $ret = intval(substr($gallery_name, 7, strpos($gallery_name, '_', 7) - 7));
    }

    if ($ret !== null) {
        $username = $GLOBALS['FORUM_DRIVER']->get_username($ret, true);
        if ($username === null) {
            $ret = null;
        }
    }

    return $ret;
}

/**
 * Get preview detailing for a video.
 *
 * @param  array $myrow The database row of the video
 * @return Tempcode The preview
 */
function show_video_details($myrow)
{
    return do_template('GALLERY_VIDEO_INFO', array(
        '_GUID' => '46d32c84cb2f6ab77d825ad711e24e13',
        'WIDTH' => integer_format($myrow['video_width']),
        'HEIGHT' => integer_format($myrow['video_height']),
        'LENGTH' => strval($myrow['video_length']),
    ));
}

/**
 * Get details of the contents of a gallery.
 *
 * @param  ID_TEXT $name The name of the gallery
 * @param  boolean $test_videos Whether to test for videos when making counts (ignore this parameter - used internally)
 * @param  boolean $test_images Whether to test for images when making counts (ignore this parameter - used internally)
 * @return array A triplet: (num children, num images, num videos)
 */
function get_recursive_gallery_details($name, $test_videos = true, $test_images = true)
{
    static $total_categories = null;
    if (is_null($total_categories)) {
        $total_categories = $GLOBALS['SITE_DB']->query_select_value('galleries', 'COUNT(*)');
    }

    $num_images = $test_images ? $GLOBALS['SITE_DB']->query_select_value('images', 'COUNT(*)', array('cat' => $name)) : 0;
    $num_videos = $test_videos ? $GLOBALS['SITE_DB']->query_select_value('videos', 'COUNT(*)', array('cat' => $name)) : 0;

    if ($total_categories < 200) { // Make sure not too much, performance issue
        $children = (strpos($name, 'member_') !== false) ? array() : $GLOBALS['SITE_DB']->query_select('galleries', array('name', 'accept_images', 'accept_videos'), array('parent_id' => $name));
        $num_children = 0;
        foreach ($children as $child) {
            list($_num_children, $_num_images, $_num_videos) = get_recursive_gallery_details($child['name'], $child['accept_videos'] == 1, $child['accept_images'] == 1);
            $num_images += $_num_images;
            $num_videos += $_num_videos;
            if (get_option('show_empty_galleries') == '1') {
                $num_children += $_num_children + 1;
            } else {
                $num_children += $_num_children + ((($_num_images != 0) || ($_num_videos != 0)) ? 1 : 0);
            }
        }
        return array($num_children, $num_images, $num_videos);
    }

    $num_children = (strpos($name, 'member_') !== false) ? 0 : $GLOBALS['SITE_DB']->query_select_value('galleries', 'COUNT(*)', array('parent_id' => $name));
    return array($num_children, $num_images, $num_videos);
}

/**
 * See whether a gallery is a download gallery (designed as a filter).
 *
 * @param  ID_TEXT $cat The gallery name
 * @return boolean Whether the gallery is a download gallery
 */
function only_download_galleries($cat)
{
    return (substr($cat, 0, 9) == 'download_');
}

/**
 * See whether a gallery is NOT a download gallery (designed as a filter).
 *
 * @param  ID_TEXT $cat The gallery name
 * @return boolean Whether the gallery is NOT a download gallery
 */
function only_conventional_galleries($cat)
{
    return (substr($cat, 0, 9) != 'download_');
}

/**
 * See whether a gallery accepts some media (designed as a filter).
 *
 * @param  ID_TEXT $cat The gallery name
 * @return boolean Whether the gallery accepts some media
 */
function only_galleries_accepting_media($cat)
{
    $_gallery_info = $GLOBALS['SITE_DB']->query_select('galleries', array('accept_images', 'accept_videos'), array('name' => $cat), '', 1);
    if (!array_key_exists(0, $_gallery_info)) {
        return false;
    }
    $gallery_info = $_gallery_info[0];
    return ($gallery_info['accept_images'] == 1 || $gallery_info['accept_videos'] == 1);
}

/**
 * See whether the GET parameter 'id' is of a gallery that is a member gallery of the given member gallery container, or just a normal gallery.
 *
 * @param  ID_TEXT $cat The gallery name
 * @param  ?MEMBER $member_id Member we are filtering for (null: not needed)
 * @param  integer $child_count The number of children for this gallery
 * @return boolean The answer
 */
function only_member_galleries_of_id($cat, $member_id, $child_count)
{
    if (substr($cat, 0, 7) != 'member_') {
        return ($child_count != 0);
    }
    return (substr($cat, 7, strlen(strval($member_id)) + 1) == strval($member_id) . '_');
}

/**
 * Gets a gallery selection tree list, extending deeper from the given gallery, showing all sub(sub...)galleries.
 *
 * @param  ?ID_TEXT $it The gallery to select by default (null: no specific default)
 * @param  ?string $filter A function name to filter galleries with (null: no filter)
 * @param  boolean $must_accept_images Whether displayed galleries must support images
 * @param  boolean $must_accept_videos Whether displayed galleries must support videos
 * @param  boolean $purity Whether to NOT show member galleries that do not exist yet
 * @param  boolean $use_compound_list Whether to get a list of child galleries (not just direct ones, recursively), instead of just IDs
 * @param  ?MEMBER $member_id Member we are filtering for (null: not needed)
 * @param  boolean $addable_filter Whether to only show for what may be added to by the current member
 * @param  boolean $editable_filter Whether to only show for what may be edited by the current member
 * @param  ?TIME $updated_since Time from which content must be updated (null: no limit).
 * @return Tempcode The tree list
 */
function create_selection_list_gallery_tree($it = null, $filter = null, $must_accept_images = false, $must_accept_videos = false, $purity = false, $use_compound_list = false, $member_id = null, $addable_filter = false, $editable_filter = false, $updated_since = null)
{
    $tree = get_gallery_tree('root', '', null, $updated_since !== null, $filter, $must_accept_images, $must_accept_videos, $purity, $use_compound_list, null, $member_id, $addable_filter, $editable_filter);
    if ($use_compound_list) {
        $tree = $tree[0];
    }

    $out = ''; // XHTMLXHTML
    foreach ($tree as $category) {
        if (($addable_filter) && (!$category['addable'])) {
            continue;
        }

        if (($updated_since !== null) && (($category['updated_since'] === null) || ($category['updated_since'] < $updated_since))) {
            continue;
        }

        $selected = ($category['id'] == $it);
        $out .= '<option value="' . (!$use_compound_list ? $category['id'] : $category['compound_list']) . '"' . ($selected ? ' selected="selected"' : '') . '>' . escape_html($category['breadcrumbs']) . '</option>' . "\n";
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($out);
    }

    return make_string_tempcode($out);
}

/**
 * Gets a gallery selection tree list, extending deeper from the given gallery, showing all sub(sub...)galleries.
 *
 * @param  ?ID_TEXT $gallery The gallery we are getting the tree starting from (null: root)
 * @param  string $breadcrumbs The parent breadcrumbs at this point of the recursion
 * @param  ?array $gallery_info The database row for the $gallery gallery (null: get it from the DB)
 * @param  boolean $do_stats Whether to include video/image statistics in the returned tree
 * @param  ?string $filter A function name to filter galleries with OR a Selectcode string (null: no filter)
 * @param  boolean $must_accept_images Whether displayed galleries must support images
 * @param  boolean $must_accept_videos Whether displayed galleries must support videos
 * @param  boolean $purity Whether to NOT show member galleries that do not exist yet
 * @param  boolean $use_compound_list Whether to get a list of child galleries (not just direct ones, recursively), instead of just IDs
 * @param  ?integer $levels The number of recursive levels to search (null: all)
 * @param  ?MEMBER $member_id Member we are filtering for (null: not needed)
 * @param  boolean $addable_filter Whether to only show for what may be added to by the current member
 * @param  boolean $editable_filter Whether to only show for what may be edited by the current member
 * @return array The tree structure, or if $use_compound_list, the tree structure built with pairs containing the compound list in addition to the child branches
 */
function get_gallery_tree($gallery = 'root', $breadcrumbs = '', $gallery_info = null, $do_stats = false, $filter = null, $must_accept_images = false, $must_accept_videos = false, $purity = false, $use_compound_list = false, $levels = null, $member_id = null, $addable_filter = false, $editable_filter = false)
{
    if ($levels == -1) {
        return $use_compound_list ? array(array(), '') : array();
    }

    if (is_null($gallery)) {
        $gallery = 'root';
    }

    if (!has_category_access(get_member(), 'galleries', $gallery)) {
        return $use_compound_list ? array(array(), '') : array();
    }

    if (is_null($gallery_info)) {
        $_gallery_info = $GLOBALS['SITE_DB']->query_select('galleries', array('fullname', 'is_member_synched', 'accept_images', 'accept_videos', 'parent_id'), array('name' => $gallery), '', 1);
        if (!array_key_exists(0, $_gallery_info)) {
            warn_exit(do_lang_tempcode('_MISSING_RESOURCE', escape_html($gallery), 'gallery'));
        }
        $gallery_info = $_gallery_info[0];
    }

    $title = get_translated_text($gallery_info['fullname']);
    $breadcrumbs .= $title;

    $is_member_synched = ($gallery_info['is_member_synched'] == 1);
    $accept_images = ($gallery_info['accept_images'] == 1);
    $accept_videos = ($gallery_info['accept_videos'] == 1);

    $children = array();
    $sub = false;
    $query = 'FROM ' . get_table_prefix() . 'galleries g WHERE ' . db_string_equal_to('parent_id', $gallery);
    if ((!is_null($filter)) && (!is_callable($filter))) {
        require_code('selectcode');
        $selectcode = selectcode_to_sqlfragment($filter, 'name', 'galleries', 'parent_id', 'parent_id', 'name', false, false);
        $query .= ' AND ' . $selectcode;
    }
    $num_children = $GLOBALS['SITE_DB']->query_value_if_there('SELECT COUNT(*) ' . $query);
    if ($num_children == 0) {
        $rows = array();
    } else {
        if ($num_children >= intval(get_option('general_safety_listing_limit'))) {
            $rows = $GLOBALS['SITE_DB']->query('SELECT name,fullname,accept_images,accept_videos,is_member_synched,g.fullname,parent_id ' . $query . ' ORDER BY add_date', intval(get_option('general_safety_listing_limit')), null, false, false, array('fullname' => 'SHORT_TRANS__COMCODE'));
        } else {
            $rows = $GLOBALS['SITE_DB']->query('SELECT name,fullname,accept_images,accept_videos,is_member_synched,g.fullname,parent_id ' . $query . ' ORDER BY ' . $GLOBALS['SITE_DB']->translate_field_ref('fullname') . ' ASC', null, null, false, false, array('fullname' => 'SHORT_TRANS__COMCODE'));
        }
    }
    if (((is_null($filter)) || (!is_callable($filter)) || (call_user_func_array($filter, array($gallery, $member_id, count($rows))))) && ((!$must_accept_images) || (($accept_images) && (!$is_member_synched))) && ((!$must_accept_videos) || (($accept_videos) && (!$is_member_synched)))) {
        // We'll be putting all children in this entire tree into a single list
        $children[0]['id'] = $gallery;
        $children[0]['title'] = $title;
        $children[0]['breadcrumbs'] = $breadcrumbs;
        $children[0]['accept_images'] = $accept_images;
        $children[0]['accept_videos'] = $accept_videos;
        $children[0]['is_member_synched'] = $gallery_info['is_member_synched'];
        if ($addable_filter) {
            $children[0]['addable'] = (can_submit_to_gallery($gallery, $gallery_info) !== false) && (has_submit_permission('mid', get_member(), get_ip_address(), 'cms_galleries', array('galleries', $gallery)));
        }
        if ($editable_filter) {
            $can_submit = can_submit_to_gallery($gallery, $gallery_info);
            $children[0]['editable'] = has_edit_permission('cat_mid', get_member(), ($can_submit === false) ? null : $can_submit, 'cms_galleries', array('galleries', $gallery));
        }
        if ($do_stats) {
            $good_row_count = 0;
            foreach ($rows as $row) {
                if (((is_null($filter)) || (!is_callable($filter)) || (call_user_func_array($filter, array($row['name'], $member_id, 1)))) && ((!$must_accept_images) || (($row['accept_images']) && ($row['is_member_synched'] == 0))) && ((!$must_accept_videos) || (($row['accept_videos']) && ($row['is_member_synched'] == 0)))) {
                    $good_row_count++;
                }
            }
            $children[0]['child_count'] = $good_row_count;
            if (($good_row_count == 0) && (!$purity) && ($gallery_info['is_member_synched'])) {
                $children[0]['child_count'] = 1;
            }
            $video_stats = $GLOBALS['SITE_DB']->query_select('videos', array('COUNT(*) AS video_count', 'MAX(add_date) AS updated_since'), array('cat' => $gallery));
            $image_stats = $GLOBALS['SITE_DB']->query_select('images', array('COUNT(*) AS image_count', 'MAX(add_date) AS updated_since'), array('cat' => $gallery));
            $children[0]['video_count'] = $video_stats[0]['video_count'];
            $children[0]['image_count'] = $image_stats[0]['image_count'];
            $children[0]['updated_since'] = @max($video_stats[0]['updated_since'], $image_stats[0]['updated_since']);
        }
        $sub = true;
    }

    $can_submit = mixed();

    // Children of this category
    $child_breadcrumbs = ($breadcrumbs == '') ? '' : ($breadcrumbs . ' > ');
    $found_own_gallery = false;
    $found_member_galleries = array($GLOBALS['FORUM_DRIVER']->get_guest_id() => 1);
    $compound_list = $gallery . ',';
    foreach ($rows as $child) {
        if ($child['name'] == 'root') {
            continue;
        }

        $can_submit = can_submit_to_gallery($child['name'], $child);
        if (($can_submit !== false) && ($can_submit > 0)) {
            $found_own_gallery = true;
            $found_member_galleries[$can_submit] = 1;
        }

        if (($levels !== 0) || ($use_compound_list)) {
            $child_id = $child['name'];

            $child_children = get_gallery_tree($child_id, $child_breadcrumbs, $child, $do_stats, $filter, $must_accept_images, $must_accept_videos, $purity, $use_compound_list, is_null($levels) ? null : ($levels - 1), $member_id, $addable_filter, $editable_filter);
            if ($use_compound_list) {
                list($child_children, $_compound_list) = $child_children;
                $compound_list .= $_compound_list;
            }

            if ($levels !== 0) {
                $children = array_merge($children, $child_children);
            }
        }
    }
    if (($sub) && (array_key_exists(0, $children))) {
        $children[0]['compound_list'] = $compound_list;
    }
    $done_for_all = false;
    if (($is_member_synched) && (!$purity) && ($levels !== 0)) {
        if ((has_privilege(get_member(), 'can_submit_to_others_categories')) && (get_forum_type() == 'cns')) {
            cns_require_all_forum_stuff();

            $members = $GLOBALS['FORUM_DB']->query_select('f_members', array('id', 'm_username', 'm_primary_group'), null, 'ORDER BY m_username', 100);
            if (count($members) != 100) { // Performance tweak. Only do if we don't have too many results
                $done_for_all = true;
                $group_membership = $GLOBALS['FORUM_DB']->query_select('f_group_members', array('gm_group_id', 'gm_member_id'), array('gm_validated' => 1));
                $group_permissions = $GLOBALS['SITE_DB']->query('SELECT group_id,the_page,the_value FROM ' . $GLOBALS['SITE_DB']->get_table_prefix() . 'group_privileges WHERE ' . db_string_equal_to('privilege', 'have_personal_category') . ' AND (' . db_string_equal_to('the_page', '') . ' OR ' . db_string_equal_to('the_page', 'cms_galleries') . ')');
                $is_super_admin = $GLOBALS['FORUM_DRIVER']->is_super_admin(get_member());
                foreach ($members as $_member) {
                    $member = $_member['id'];
                    $username = $_member['m_username'];

                    $this_gallery = 'member_' . strval($member) . '_' . $gallery;
                    if ($member == get_member()) {
                        $has_permission = true;
                    } else {
                        $a = (in_array(array('group_id' => $_member['m_primary_group'], 'the_page' => '', 'the_value' => 1), $group_permissions));
                        $b = (in_array(array('group_id' => $_member['m_primary_group'], 'the_page' => 'cms_galleries', 'the_value' => 0), $group_permissions));
                        $c = (in_array(array('group_id' => $_member['m_primary_group'], 'the_page' => 'cms_galleries', 'the_value' => 1), $group_permissions));
                        $has_permission = $is_super_admin;
                        if ((($a) && (!$b)) || ($c)) {
                            $has_permission = true;
                        }
                        if (!$has_permission) {
                            foreach ($group_membership as $_g) {
                                if ($_g['gm_member_id'] == $member) {
                                    $a = (in_array(array('group_id' => $_g['gm_group_id'], 'the_page' => '', 'the_value' => 1), $group_permissions));
                                    $b = (in_array(array('group_id' => $_g['gm_group_id'], 'the_page' => 'cms_galleries', 'the_value' => 0), $group_permissions));
                                    $c = (in_array(array('group_id' => $_g['gm_group_id'], 'the_page' => 'cms_galleries', 'the_value' => 1), $group_permissions));
                                    if ((($a) && (!$b)) || ($c)) {
                                        $has_permission = true;
                                    }
                                    break;
                                }
                            }
                        }
                    }
                    if (($has_permission) && (!array_key_exists($member, $found_member_galleries)) && ((is_null($filter)) || (!is_callable($filter)) || (call_user_func_array($filter, array($this_gallery, $member_id, 0))))) {
                        $own_gallery = array();
                        $own_gallery['id'] = $this_gallery;
                        if ($title == do_lang('GALLERIES_HOME')) {
                            $title = do_lang('GALLERY');
                        }
                        $this_title = do_lang('NEW_PERSONAL_GALLERY_OF', $username, $title);
                        $own_gallery['breadcrumbs'] = $breadcrumbs . $this_title;
                        if ($do_stats) {
                            $own_gallery['video_count'] = 0;
                            $own_gallery['image_count'] = 0;
                            $own_gallery['child_count'] = 0;
                            $own_gallery['updated_since'] = 0;
                        }
                        $own_gallery['title'] = $this_title;
                        $own_gallery['accept_images'] = $gallery_info['accept_images'];
                        $own_gallery['accept_videos'] = $gallery_info['accept_videos'];
                        $own_gallery['is_member_synched'] = 0;
                        $own_gallery['compound_list'] = $compound_list;
                        $own_gallery['addable'] = true;
                        $own_gallery['editable'] = false;
                        $children[] = $own_gallery;
                        if ($member == get_member()) {
                            $found_own_gallery = true;
                        }
                    }
                }
            }
        }

        if (((!$done_for_all) || (!$found_own_gallery)) && (!array_key_exists(get_member(), $found_member_galleries)) && (!is_guest()) && (!$purity) && (has_privilege(get_member(), 'have_personal_category'))) {
            $this_gallery = 'member_' . strval(get_member()) . '_' . $gallery;
            if ((is_null($filter)) || (!is_callable($filter)) || (call_user_func_array($filter, array($this_gallery, $member_id, 0)))) {
                $own_gallery = array();
                $own_gallery['id'] = $this_gallery;
                if ($title == do_lang('GALLERIES_HOME')) {
                    $title = do_lang('GALLERY');
                }
                $this_title = do_lang('NEW_PERSONAL_GALLERY_OF', $GLOBALS['FORUM_DRIVER']->get_username(get_member(), true), $title);
                $own_gallery['breadcrumbs'] = $breadcrumbs . $this_title;
                if ($do_stats) {
                    $own_gallery['video_count'] = 0;
                    $own_gallery['image_count'] = 0;
                    $own_gallery['child_count'] = 0;
                    $own_gallery['updated_since'] = 0;
                }
                $own_gallery['title'] = $this_title;
                $own_gallery['accept_images'] = $gallery_info['accept_images'];
                $own_gallery['accept_videos'] = $gallery_info['accept_videos'];
                $own_gallery['is_member_synched'] = 0;
                $own_gallery['addable'] = true;
                $own_gallery['editable'] = false;
                $own_gallery['compound_list'] = $compound_list;
                $children[] = $own_gallery;
            }
        }
    }

    return $use_compound_list ? array($children, $compound_list) : $children;
}

/**
 * See whether the current member can submit to the named *member* gallery. Note - this function assumes that members have general submit permission, and does not check for gallery read access.
 *
 * @param  ID_TEXT $name The gallery name
 * @param  ?array $gallery_info Gallery database row (null: lookup)
 * @return ~integer The owner of the gallery (false: we aren't allowed to submit to it) (-2: not a member gallery)
 */
function can_submit_to_gallery($name, $gallery_info = null)
{
    if (substr($name, 0, 7) != 'member_') {
        if ($name == 'root') {
            return (-2);
        }
        if (isset($gallery_info['parent_id'])) {
            $parent_id = $gallery_info['parent_id'];
        } else {
            $parent_id = $GLOBALS['SITE_DB']->query_select_value_if_there('galleries', 'parent_id', array('name' => $name));
        }
        if (is_null($parent_id)) {
            return false; // No, does not even exist (probably a block was given a bad parameter)
        }

        return can_submit_to_gallery($parent_id);
    }

    $parts = explode('_', $name);
    if (intval($parts[1]) == get_member()) {
        return intval($parts[1]);
    }

    if (has_privilege(get_member(), 'can_submit_to_others_categories')) {
        return intval($parts[1]);
    }

    return false;
}

/**
 * Get a route from a known gallery back to the declared root of the tree.
 *
 * @param  ID_TEXT $gallery The gallery name
 * @param  ?ID_TEXT $root The virtual root (null: none)
 * @param  boolean $no_link_for_me_sir Whether not to put a link at this point in the breadcrumbs (usually, because the viewer is already at it)
 * @param  ID_TEXT $zone The zone that the linked to gallery module is in
 * @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 navigation element
 */
function gallery_breadcrumbs($gallery, $root = 'root', $no_link_for_me_sir = true, $zone = '', $attach_to_url_filter = false)
{
    if (is_null($root)) {
        $root = 'root';
    }

    if ($gallery == '') {
        $gallery = 'root'; // To fix corrupt data
    }

    $url_map = array('page' => 'galleries', 'type' => 'browse', 'id' => $gallery, 'keep_gallery_root' => ($root == 'root') ? null : $root);
    if (get_page_name() == 'galleries') {
        $url_map += propagate_filtercode();
    }
    $page_link = build_page_link($url_map, $zone);

    if (($gallery == $root) || ($gallery == 'root')) {
        if ($no_link_for_me_sir) {
            return array();
        }
        $title = get_translated_text($GLOBALS['SITE_DB']->query_select_value('galleries', 'fullname', array('name' => $gallery)));
        return array(array($page_link, $title));
    }

    global $PT_PAIR_CACHE_G;
    if (!array_key_exists($gallery, $PT_PAIR_CACHE_G)) {
        $category_rows = $GLOBALS['SITE_DB']->query_select('galleries', array('parent_id', 'fullname'), array('name' => $gallery), '', 1);
        if (!array_key_exists(0, $category_rows)) {
            return array();
        }//fatal_exit(do_lang_tempcode('CAT_NOT_FOUND',escape_html($gallery), 'gallery'));
        $PT_PAIR_CACHE_G[$gallery] = $category_rows[0];
    }

    $segments = array();

    $title = get_translated_text($PT_PAIR_CACHE_G[$gallery]['fullname']);
    if (!$no_link_for_me_sir) {
        $segments[] = array($page_link, $title);
    }

    if ($PT_PAIR_CACHE_G[$gallery]['parent_id'] == $gallery) {
        fatal_exit(do_lang_tempcode('RECURSIVE_TREE_CHAIN', escape_html($gallery), 'gallery'));
    }

    if ((get_option('personal_under_members') == '1') && (get_forum_type() == 'cns')) {
        $owner = get_member_id_from_gallery_name($gallery, null, true);
        if (!is_null($owner)) {
            $below = array();

            $personal_gallery_breadcrumb_parts = array(
                array(
                    '_SEARCH:members:browse',
                    do_lang_tempcode('MEMBERS')
                ),
                array(
                    '_SEARCH:members:view:' . strval($owner) . '#tab__galleries',
                    do_lang_tempcode('cns:MEMBER_ACCOUNT', escape_html($GLOBALS['FORUM_DRIVER']->get_username($owner, true)))
                ),
            );
            foreach ($personal_gallery_breadcrumb_parts as $bits) {
                list($page_link, $label) = $bits;
                list($zone, $map, $hash) = page_link_decode($page_link);
                if (get_page_name() == 'galleries') {
                    $map += propagate_filtercode();
                }
                $page_link = build_page_link($map, $zone, $hash);
                $below[] = array($page_link, $label);
            }
            return array_merge($below, $segments);
        }
    }

    $below = gallery_breadcrumbs($PT_PAIR_CACHE_G[$gallery]['parent_id'], $root, false, $zone, $attach_to_url_filter);

    return array_merge($below, $segments);
}

/**
 * Get a nice, formatted XHTML list of gallery entries, in gallery tree structure
 *
 * @param  ID_TEXT $table The table we are working with
 * @set    images videos
 * @param  ?ID_TEXT $it The currently selected entry (null: none selected)
 * @param  ?AUTO_LINK $submitter Only show images/videos submitted by this member (null: no filter)
 * @param  boolean $use_compound_list Whether to get a list of child galleries (not just direct ones, recursively), instead of just IDs
 * @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_gallery_content_tree($table, $it = null, $submitter = null, $use_compound_list = false, $editable_filter = false)
{
    $tree = get_gallery_content_tree($table, $submitter, null, null, null, null, $use_compound_list, $editable_filter);
    if ($use_compound_list) {
        $tree = $tree[0];
    }

    $out = ''; // XHTMLXHTML
    foreach ($tree as $gallery) {
        foreach ($gallery['entries'] as $eid => $etitle) {
            $selected = ($eid == $it);
            $line = do_template('GALLERY_ENTRY_LIST_LINE', array('_GUID' => '5a6fac8a768e049f9cc6c2d4ec77eeca', 'BREADCRUMBS' => $gallery['breadcrumbs'], 'URL' => $etitle));
            $out .= '<option value="' . (!$use_compound_list ? strval($eid) : $gallery['compound_list']) . '"' . ($selected ? 'selected="selected"' : '') . '>' . $line->evaluate() . '</option>' . "\n";
        }
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($out);
    }

    return make_string_tempcode($out);
}

/**
 * Get a list of maps containing all the gallery entries, and path information, under the specified gallery - and those beneath it, recursively.
 *
 * @param  ID_TEXT $table The table we are working with
 * @set    images videos
 * @param  ?AUTO_LINK $submitter Only show images/videos submitted by this member (null: no filter)
 * @param  ?ID_TEXT $gallery The gallery 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 $gallery 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 $use_compound_list Whether to get a list of child galleries (not just direct ones, recursively), instead of just IDs
 * @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 galleries. Each map entry containins the fields 'id' (gallery ID) and 'breadcrumbs' (path to the category, including the categories own title), and more. Or if $use_compound_list, the tree structure built with pairs containing the compound list in addition to the child branches
 */
function get_gallery_content_tree($table, $submitter = null, $gallery = null, $breadcrumbs = null, $title = null, $levels = null, $use_compound_list = false, $editable_filter = false)
{
    if (is_null($gallery)) {
        $gallery = 'root';
    }

    if (!has_category_access(get_member(), 'galleries', $gallery)) {
        return array();
    }

    if (is_null($breadcrumbs)) {
        $breadcrumbs = '';
    }

    // Put our title onto our breadcrumbs
    if (is_null($title)) {
        $title = get_translated_text($GLOBALS['SITE_DB']->query_select_value('galleries', 'fullname', array('name' => $gallery)));
    }
    $breadcrumbs .= $title;

    // We'll be putting all children in this entire tree into a single list
    $children = array();
    $children[0] = array();
    $children[0]['id'] = $gallery;
    $children[0]['title'] = $title;
    $children[0]['breadcrumbs'] = $breadcrumbs;

    $compound_list = $gallery . ',';

    // Children of this category
    $rows = $GLOBALS['SITE_DB']->query_select('galleries', array('name', 'fullname'), array('parent_id' => $gallery), 'ORDER BY ' . $GLOBALS['SITE_DB']->translate_field_ref('fullname') . ' ASC', intval(get_option('general_safety_listing_limit')));
    if (count($rows) == intval(get_option('general_safety_listing_limit'))) {
        $rows = $GLOBALS['SITE_DB']->query_select('galleries', array('name', 'fullname'), array('parent_id' => $gallery), 'ORDER BY add_date DESC', intval(get_option('general_safety_listing_limit')));
    }
    $where = array('cat' => $gallery);
    if (!is_null($submitter)) {
        $where['submitter'] = $submitter;
    }
    $erows = $GLOBALS['SITE_DB']->query_select($table, array('id', 'url', 'submitter', 'title', 'thumb_url'), $where, 'ORDER BY ' . $GLOBALS['SITE_DB']->translate_field_ref('title') . ' ASC', intval(get_option('general_safety_listing_limit')));
    if (count($erows) == intval(get_option('general_safety_listing_limit'))) {
        $erows = $GLOBALS['SITE_DB']->query_select($table, array('id', 'url', 'submitter', 'title', 'thumb_url'), $where, 'ORDER BY add_date DESC', intval(get_option('general_safety_listing_limit')));
    }
    $children[0]['entries'] = array();
    $children[0]['entries_rows'] = array();
    foreach ($erows as $row) {
        if (($editable_filter) && (!has_edit_permission('mid', get_member(), $row['submitter'], 'cms_galleries', array('galleries', $gallery)))) {
            continue;
        }
        $e_title = get_translated_text($row['title']);
        if ($e_title == '') {
            $e_title = basename($row['url']);
        }
        $children[0]['entries'][$row['id']] = $e_title;
        $children[0]['entries_rows'][$row['id']] = $row;
    }
    $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['name'];
            $child_title = get_translated_text($child['fullname']);
            $child_breadcrumbs = $breadcrumbs;

            $child_children = get_gallery_content_tree($table, $submitter, $child_id, $child_breadcrumbs, $child_title, is_null($levels) ? null : ($levels - 1), $use_compound_list, $editable_filter);
            if ($use_compound_list) {
                list($child_children, $_compound_list) = $child_children;
                $compound_list .= $_compound_list;
            }

            $children = array_merge($children, $child_children);
        }
    }
    $children[0]['compound_list'] = $compound_list;

    return $use_compound_list ? array($children, $compound_list) : $children;
}

/**
 * Show a gallery media entry (not an image, something more complex); all these will render under the 'video' type even if they're technically not.
 *
 * @param  URLPATH $url URL to media
 * @param  URLPATH $thumb_url URL to thumbnail
 * @param  integer $width Width
 * @param  integer $height Height
 * @param  integer $length Length
 * @param  MEMBER $submitter The entry submitter
 * @return Tempcode Displayed media
 */
function show_gallery_video_media($url, $thumb_url, $width, $height, $length, $submitter)
{
    require_code('media_renderer');
    require_code('mime_types');
    require_code('files');

    $as_admin = has_privilege($submitter, 'comcode_dangerous');

    $attributes = array(
        'thumb_url' => $thumb_url,
        'width' => strval($width),
        'height' => strval($height),
        'length' => strval($length),
        'mime_type' => get_mime_type(get_file_extension($url), $as_admin), // will not render as dangerous stuff (swf's etc), unless admin
        'context' => 'gallery_video',
    );

    if (get_option('allow_audio_videos') == '1') {
        $media_type = MEDIA_TYPE_VIDEO | MEDIA_TYPE_OTHER | MEDIA_TYPE_AUDIO;
    } else {
        $media_type = MEDIA_TYPE_VIDEO;
    }

    // Render
    return render_media_url($url, $url, $attributes, $as_admin, $submitter, $media_type);
}
