<?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
 */

/**
 * Module page class.
 */
class Module_galleries
{
    /**
     * Find details of the module.
     *
     * @return ?array Map of module info (null: module is disabled).
     */
    public function info()
    {
        $info = array();
        $info['author'] = 'Chris Graham';
        $info['organisation'] = 'ocProducts';
        $info['hacked_by'] = null;
        $info['hack_version'] = null;
        $info['version'] = 10;
        $info['update_require_upgrade'] = true;
        $info['locked'] = false;
        return $info;
    }

    /**
     * Uninstall the module.
     */
    public function uninstall()
    {
        $GLOBALS['SITE_DB']->drop_table_if_exists('galleries');
        $GLOBALS['SITE_DB']->drop_table_if_exists('images');
        $GLOBALS['SITE_DB']->drop_table_if_exists('videos');
        $GLOBALS['SITE_DB']->drop_table_if_exists('video_transcoding');

        delete_privilege('may_download_gallery');
        delete_privilege('high_personal_gallery_limit');
        delete_privilege('no_personal_gallery_limit');

        delete_privilege('autocomplete_keyword_gallery');
        delete_privilege('autocomplete_title_gallery');
        delete_privilege('autocomplete_keyword_image');
        delete_privilege('autocomplete_title_image');
        delete_privilege('autocomplete_keyword_videos');
        delete_privilege('autocomplete_title_videos');

        $GLOBALS['SITE_DB']->query_delete('group_category_access', array('module_the_name' => 'galleries'));

        $GLOBALS['SITE_DB']->query_delete('trackbacks', array('trackback_for_type' => 'galleries'));

        require_code('files');
        if (!$GLOBALS['DEV_MODE']) {
            deldir_contents(get_custom_file_base() . '/uploads/galleries', true, true);
            deldir_contents(get_custom_file_base() . '/uploads/galleries_thumbs', true);
        }
    }

    /**
     * Install the module.
     *
     * @param  ?integer $upgrade_from What version we're upgrading from (null: new install)
     * @param  ?integer $upgrade_from_hack What hack version we're upgrading from (null: new-install/not-upgrading-from-a-hacked-version)
     */
    public function install($upgrade_from = null, $upgrade_from_hack = null)
    {
        require_code('galleries');
        require_lang('galleries');
        require_code('galleries2');

        if (is_null($upgrade_from)) {
            $GLOBALS['SITE_DB']->create_table('galleries', array(
                'name' => '*ID_TEXT',
                'description' => 'LONG_TRANS__COMCODE',
                'fullname' => 'SHORT_TRANS__COMCODE',
                'add_date' => 'TIME',
                'rep_image' => 'URLPATH',
                'parent_id' => 'ID_TEXT',
                'watermark_top_left' => 'URLPATH',
                'watermark_top_right' => 'URLPATH',
                'watermark_bottom_left' => 'URLPATH',
                'watermark_bottom_right' => 'URLPATH',
                'accept_images' => 'BINARY',
                'accept_videos' => 'BINARY',
                'allow_rating' => 'BINARY',
                'allow_comments' => 'SHORT_INTEGER',
                'notes' => 'LONG_TEXT',
                'is_member_synched' => 'BINARY',
                'flow_mode_interface' => 'BINARY',
                'gallery_views' => 'INTEGER',
                'g_owner' => '?MEMBER',
            ));
            $GLOBALS['SITE_DB']->create_index('galleries', 'watermark_top_left', array('watermark_top_left'));
            $GLOBALS['SITE_DB']->create_index('galleries', 'watermark_top_right', array('watermark_top_right'));
            $GLOBALS['SITE_DB']->create_index('galleries', 'watermark_bottom_left', array('watermark_bottom_left'));
            $GLOBALS['SITE_DB']->create_index('galleries', 'watermark_bottom_right', array('watermark_bottom_right'));
            $GLOBALS['SITE_DB']->create_index('galleries', 'gadd_date', array('add_date'));
            $GLOBALS['SITE_DB']->create_index('galleries', 'parent_id', array('parent_id'));
            $GLOBALS['SITE_DB']->create_index('galleries', 'ftjoin_gfullname', array('fullname'));
            $GLOBALS['SITE_DB']->create_index('galleries', 'ftjoin_gdescrip', array('description'));

            $GLOBALS['SITE_DB']->create_table('images', array(
                'id' => '*AUTO',
                'cat' => 'ID_TEXT',
                'url' => 'URLPATH',
                'thumb_url' => 'URLPATH',
                'description' => 'LONG_TRANS__COMCODE',
                'allow_rating' => 'BINARY',
                'allow_comments' => 'SHORT_INTEGER',
                'allow_trackbacks' => 'BINARY',
                'notes' => 'LONG_TEXT',
                'submitter' => 'MEMBER',
                'validated' => 'BINARY',
                'add_date' => 'TIME',
                'edit_date' => '?TIME',
                'image_views' => 'INTEGER',
                'title' => 'SHORT_TRANS',
            ));
            $GLOBALS['SITE_DB']->create_index('images', 'image_views', array('image_views'));
            $GLOBALS['SITE_DB']->create_index('images', 'category_list', array('cat'));
            $GLOBALS['SITE_DB']->create_index('images', 'i_validated', array('validated'));
            $GLOBALS['SITE_DB']->create_index('images', 'xis', array('submitter'));
            $GLOBALS['SITE_DB']->create_index('images', 'iadd_date', array('add_date'));
            $GLOBALS['SITE_DB']->create_index('images', 'ftjoin_dtitle', array('title'));

            $GLOBALS['SITE_DB']->create_table('videos', array(
                'id' => '*AUTO',
                'cat' => 'ID_TEXT',
                'url' => 'URLPATH',
                'thumb_url' => 'URLPATH',
                'description' => 'LONG_TRANS__COMCODE',
                'allow_rating' => 'BINARY',
                'allow_comments' => 'SHORT_INTEGER',
                'allow_trackbacks' => 'BINARY',
                'notes' => 'LONG_TEXT',
                'submitter' => 'MEMBER',
                'validated' => 'BINARY',
                'add_date' => 'TIME',
                'edit_date' => '?TIME',
                'video_views' => 'INTEGER',
                'video_width' => 'INTEGER',
                'video_height' => 'INTEGER',
                'video_length' => 'INTEGER',
                'title' => 'SHORT_TRANS',
            ));
            $GLOBALS['SITE_DB']->create_index('videos', 'video_views', array('video_views'));
            $GLOBALS['SITE_DB']->create_index('videos', 'vs', array('submitter'));
            $GLOBALS['SITE_DB']->create_index('videos', 'v_validated', array('validated'));
            $GLOBALS['SITE_DB']->create_index('videos', 'category_list', array('cat'));
            $GLOBALS['SITE_DB']->create_index('videos', 'vadd_date', array('add_date'));
            $GLOBALS['SITE_DB']->create_index('videos', 'ftjoin_dtitle', array('title'));

            add_privilege('GALLERIES', 'may_download_gallery', false);
            add_privilege('GALLERIES', 'high_personal_gallery_limit', false);
            add_privilege('GALLERIES', 'no_personal_gallery_limit', false);

            // Add root gallery
            add_gallery('root', do_lang('GALLERIES_HOME'), '', '', '', 1, 1, 0, 1);
            require_code('permissions2');
            set_global_category_access('galleries', 'root');
        }

        if ((is_null($upgrade_from)) || ($upgrade_from < 7)) {
            delete_config_option('ffmpeg_path');

            $GLOBALS['SITE_DB']->create_table('video_transcoding', array(
                't_id' => '*ID_TEXT',
                't_local_id' => '?AUTO_LINK',
                't_local_id_field' => 'ID_TEXT',
                't_error' => 'LONG_TEXT',
                't_url' => 'URLPATH',
                't_table' => 'ID_TEXT',
                't_url_field' => 'ID_TEXT',
                't_orig_filename_field' => 'ID_TEXT',
                't_width_field' => 'ID_TEXT',
                't_height_field' => 'ID_TEXT',
                't_output_filename' => 'ID_TEXT', // This is the filename in uploads/galleries where the transcoder is expected to place new files. If blank, this means that the post-transcode API call will be explicitly fed a URL instead.
            ));
        }

        if ((!is_null($upgrade_from)) && ($upgrade_from < 7)) {
            $GLOBALS['SITE_DB']->add_table_field('videos', 'title', 'SHORT_TRANS');
            $GLOBALS['SITE_DB']->add_table_field('images', 'title', 'SHORT_TRANS');
            $GLOBALS['SITE_DB']->create_index('images', 'ftjoin_dtitle', array('title'));
            $GLOBALS['SITE_DB']->create_index('videos', 'ftjoin_dtitle', array('title'));
            $GLOBALS['SITE_DB']->add_table_field('galleries', 'gallery_views', 'INTEGER');
            $GLOBALS['SITE_DB']->add_table_field('galleries', 'g_owner', '?MEMBER');
        }

        if ((!is_null($upgrade_from)) && ($upgrade_from < 9)) {
            $GLOBALS['SITE_DB']->alter_table_field('images', 'comments', 'LONG_TRANS', 'description');
            $GLOBALS['SITE_DB']->alter_table_field('videos', 'comments', 'LONG_TRANS', 'description');
            $GLOBALS['SITE_DB']->delete_table_field('galleries', 'teaser');
        }

        if ((!is_null($upgrade_from)) && ($upgrade_from < 10)) {
            $GLOBALS['SITE_DB']->add_table_field('video_transcoding', 't_local_id', '?AUTO_LINK', null);
            $GLOBALS['SITE_DB']->add_table_field('video_transcoding', 't_local_id_field', 'ID_TEXT', '');

            $GLOBALS['SITE_DB']->delete_index_if_exists('images', 'ftjoin_icomments');
            $GLOBALS['SITE_DB']->delete_index_if_exists('videos', 'ftjoin_vcomments');
        }

        if ((is_null($upgrade_from)) || ($upgrade_from < 10)) {
            $GLOBALS['SITE_DB']->create_index('video_transcoding', 't_local_id', array('t_local_id'));

            $GLOBALS['SITE_DB']->create_index('galleries', '#gallery_search__combined', array('fullname', 'description'));
            $GLOBALS['SITE_DB']->create_index('images', '#image_search__combined', array('description', 'title'));
            $GLOBALS['SITE_DB']->create_index('videos', '#video_search__combined', array('description', 'title'));

            add_privilege('SEARCH', 'autocomplete_keyword_gallery', false);
            add_privilege('SEARCH', 'autocomplete_title_gallery', false);
            add_privilege('SEARCH', 'autocomplete_keyword_image', false);
            add_privilege('SEARCH', 'autocomplete_title_image', false);
            add_privilege('SEARCH', 'autocomplete_keyword_videos', false);
            add_privilege('SEARCH', 'autocomplete_title_videos', false);

            $GLOBALS['SITE_DB']->create_index('images', 'ftjoin_idescription', array('description'));
            $GLOBALS['SITE_DB']->create_index('videos', 'ftjoin_vdescription', array('description'));
        }
    }

    /**
     * Find entry-points available within this module.
     *
     * @param  boolean $check_perms Whether to check permissions.
     * @param  ?MEMBER $member_id The member to check permissions as (null: current user).
     * @param  boolean $support_crosslinks Whether to allow cross links to other modules (identifiable via a full-page-link rather than a screen-name).
     * @param  boolean $be_deferential Whether to avoid any entry-point (or even return null to disable the page in the Sitemap) if we know another module, or page_group, is going to link to that entry-point. Note that "!" and "browse" entry points are automatically merged with container page nodes (likely called by page-groupings) as appropriate.
     * @return ?array A map of entry points (screen-name=>language-code/string or screen-name=>[language-code/string, icon-theme-image]) (null: disabled).
     */
    public function get_entry_points($check_perms = true, $member_id = null, $support_crosslinks = true, $be_deferential = false)
    {
        return array(
            'browse' => array('GALLERIES_HOME', 'menu/rich_content/galleries'),
        );
    }

    public $title;
    public $cat;
    public $id;
    public $myrow;
    public $root;
    public $implied_existence;
    public $fullname;
    public $rep_image;
    public $title_to_use;
    public $title_to_use_2;
    public $url;
    public $thumb_url;
    public $true_category_name;
    public $category_name;

    /**
     * Module pre-run function. Allows us to know metadata for <head> before we start streaming output.
     *
     * @return ?Tempcode Tempcode indicating some kind of exceptional output (null: none).
     */
    public function pre_run()
    {
        $type = get_param_string('type', 'browse');

        require_code('galleries');
        require_lang('galleries');

        inform_non_canonical_parameter('days');
        inform_non_canonical_parameter('select');
        inform_non_canonical_parameter('video_select');

        if ($type == 'image' || $type == 'video') {
            if (get_param_integer('slideshow', 0) == 1) {
                attach_to_screen_header('<meta name="robots" content="noindex" />'); // XHTMLXHTML
            }
        }

        if ($type == 'browse') {
            $cat = get_param_string('id', 'root');
            set_feed_url('?mode=galleries&select=' . urlencode($cat));

            $root = get_param_string('keep_gallery_root', 'root');

            // Info on our gallery
            $gallery_rows = $GLOBALS['SITE_DB']->query_select('galleries', array('*'), array('name' => $cat), '', 1);
            $implied_existence = false;
            if (!array_key_exists(0, $gallery_rows)) {
                // Possibly we will 'imply' it's existence if this is a member gallery; allows normal rendering to finish, with add links
                $matches = array();
                if (preg_match('#^member\_(\d+)\_(.*)$#', $cat, $matches) != 0) {
                    $username = $GLOBALS['FORUM_DRIVER']->get_username(intval($matches[1]));
                    if (!is_null($username)) {
                        $gallery_rows = $GLOBALS['SITE_DB']->query_select('galleries', array('*'), array('name' => $matches[2]), '', 1);
                    }
                }
                if (!array_key_exists(0, $gallery_rows)) {
                    return warn_screen(get_screen_title('ERROR_OCCURRED'), do_lang_tempcode('MISSING_RESOURCE', 'gallery'));
                }
                $myrow = $gallery_rows[0];
                $myrow['is_member_synched'] = 0;
                $fullname = do_lang('PERSONAL_GALLERY_OF', $username, get_translated_text($myrow['fullname']));
                $myrow['parent_id'] = $matches[2];
                $implied_existence = true;
            } else {
                $myrow = $gallery_rows[0];
                $fullname = get_translated_text($myrow['fullname']);
            }
            if ($fullname == '') {
                $fullname = $cat;
            }

            $rep_image = $myrow['rep_image'];

            if (!has_category_access(get_member(), 'galleries', $cat)) {
                access_denied('CATEGORY_ACCESS');
            }

            // Breadcrumbs
            $breadcrumbs = gallery_breadcrumbs($cat, $root, true, get_module_zone('galleries'), true);
            if ((has_privilege(get_member(), 'open_virtual_roots')) && ($cat != $root)) {
                $page_link = build_page_link(array('page' => '_SELF', 'type' => 'browse', 'id' => $cat, 'keep_gallery_root' => $cat), '_SELF');
                $breadcrumbs[] = array($page_link, $fullname, do_lang_tempcode('VIRTUAL_ROOT'));
            } else {
                $breadcrumbs[] = array('', $fullname);
            }
            breadcrumb_set_parents($breadcrumbs);

            // Page title
            if ((get_value('no_awards_in_titles') !== '1') && (addon_installed('awards'))) {
                require_code('awards');
                $awards = find_awards_for('gallery', $cat);
            } else {
                $awards = array();
            }
            $title_to_use = do_lang_tempcode('_GALLERY', make_fractionable_editable('gallery', $cat, $fullname));
            $title_to_use_2 = do_lang('_GALLERY', $fullname);
            $this->title = get_screen_title($title_to_use, false, null, null, $awards);

            // Metadata
            seo_meta_load_for('gallery', $cat, $title_to_use_2);
            set_extra_request_metadata(array(
                'identifier' => '_SEARCH:galleries:browse:' . $cat,
            ), $myrow, 'gallery', $cat);

            $this->cat = $cat;
            $this->root = $root;
            $this->implied_existence = $implied_existence;
            $this->myrow = $myrow;
            $this->fullname = $fullname;
            $this->rep_image = $rep_image;
            $this->title_to_use = $title_to_use;
            $this->title_to_use_2 = $title_to_use_2;
        }

        if ($type == 'image' || $type == 'video') {
            $id = get_param_integer('id');

            if (addon_installed('content_privacy')) {
                require_code('content_privacy');
                check_privacy($type, strval($id));
            }

            if ((get_value('no_awards_in_titles') !== '1') && (addon_installed('awards'))) {
                require_code('awards');
                $awards = find_awards_for($type, strval($id));
            } else {
                $awards = array();
            }

            // Pic up some info
            $rows = $GLOBALS['SITE_DB']->query_select($type . 's', array('*'), array('id' => $id), '', 1);
            if (!array_key_exists(0, $rows)) {
                return warn_screen(get_screen_title('ERROR_OCCURRED'), do_lang_tempcode('MISSING_RESOURCE', escape_html($type)));
            }
            $myrow = $rows[0];
            $cat = $myrow['cat'];
            set_feed_url('?mode=galleries&select=' . urlencode($cat));

            $url = $myrow['url'];
            if (url_is_local($url)) {
                $url = get_custom_base_url() . '/' . $url;
            }
            $thumb_url = $myrow['thumb_url'];
            if (url_is_local($thumb_url)) {
                $thumb_url = get_custom_base_url() . '/' . $thumb_url;
            }

            if (!has_category_access(get_member(), 'galleries', $cat)) {
                access_denied('CATEGORY_ACCESS');
            }

            $category_name = get_translated_text($GLOBALS['SITE_DB']->query_select_value('galleries', 'fullname', array('name' => $cat)));

            if (get_param_integer('slideshow', 0) == 1) {
                $this->title = get_screen_title('VIEW_SLIDESHOW', true, array(make_fractionable_editable('gallery', $cat, $category_name)));
            } else {
                $fe = directive_tempcode(
                    'FRACTIONAL_EDITABLE',
                    escape_html(get_translated_text($myrow['title'])),
                    array(
                        make_string_tempcode(get_translated_text($myrow['title'])),
                        make_string_tempcode('title'),
                        make_string_tempcode((($type == 'video') ? '_SEARCH:cms_galleries:__edit_other' : '_SEARCH:cms_galleries:__edit:') . strval($id)),
                        make_string_tempcode('1'),
                        make_string_tempcode('1'),
                        make_string_tempcode(has_edit_permission('mid', get_member(), $myrow['submitter'], 'cms_galleries', array('galleries', $cat)) ? '1' : '0'),
                    )
                );
                $this->title = get_screen_title((((get_translated_text($myrow['title']) == '') ? 'VIEW_' : '_VIEW_') . strtoupper($type)), true, array($fe), null, $awards);
            }

            $title_plain = do_lang((((get_translated_text($myrow['title']) == '') ? 'VIEW_' : '_VIEW_') . strtoupper($type)), get_translated_text($myrow['title']));

            $root = get_param_string('keep_gallery_root', 'root');

            seo_meta_load_for($type, strval($id));

            $breadcrumbs = gallery_breadcrumbs($cat, $root, false, get_module_zone('galleries'));
            $breadcrumbs[] = array('', $title_plain);
            breadcrumb_set_parents($breadcrumbs);

            if ($type == 'video') {
                $extension = get_file_extension($url);
                require_code('mime_types');
                $mime_type = get_mime_type($extension, has_privilege($myrow['submitter'], 'comcode_dangerous'));
                set_extra_request_metadata(array(
                    'identifier' => '_SEARCH:galleries:video:' . strval($id),
                    'image' => $thumb_url,
                    'video' => $url,
                    'video:height' => strval($myrow['video_height']),
                    'video:width' => strval($myrow['video_width']),
                    'video:type' => $mime_type,
                ), $myrow, 'video', strval($id));
            } else {
                set_extra_request_metadata(array(
                    'identifier' => '_SEARCH:galleries:' . $type . ':' . strval($id),
                    'image' => $url,
                ), $myrow, 'image', strval($id));
            }

            $this->id = $id;
            $this->myrow = $myrow;
            $this->cat = $cat;
            $this->url = $url;
            $this->thumb_url = $thumb_url;
            $this->category_name = $category_name;
            $this->root = $root;
        }

        return null;
    }

    /**
     * Execute the module.
     *
     * @return Tempcode The result of execution.
     */
    public function run()
    {
        require_code('feedback');
        require_css('galleries');
        require_javascript('galleries');

        // What are we doing?
        $type = get_param_string('type', 'browse');
        if ($type == 'browse') {
            return $this->do_gallery();
        }
        if ($type == 'image') {
            return $this->show_image();
        }
        if ($type == 'video') {
            return $this->show_video();
        }

        return new Tempcode();
    }

    /**
     * The UI to show a gallery.
     *
     * @return Tempcode The UI
     */
    public function do_gallery()
    {
        $cat = $this->cat;
        $root = $this->root;
        $implied_existence = $this->implied_existence;
        $myrow = $this->myrow;
        $fullname = $this->fullname;
        $rep_image = $this->rep_image;
        $title_to_use = $this->title_to_use;
        $title_to_use_2 = $this->title_to_use_2;

        require_code('images');

        $just_gallery_row = db_map_restrict($myrow, array('name', 'description'));
        $description = get_translated_tempcode('galleries', $just_gallery_row, 'description');
        $may_download_gallery = has_privilege(get_member(), 'may_download_gallery', 'galleries', array('galleries', $cat));

        // Management links
        if ((has_actual_page_access(null, 'cms_galleries', null, null)) && (has_submit_permission('mid', get_member(), get_ip_address(), 'cms_galleries', array('galleries', $cat))) && (can_submit_to_gallery($cat))) {
            $submit_cat = ($myrow['is_member_synched'] == 1) ? ('member_' . strval(get_member()) . '_' . $cat) : $cat;
            $submit_image_url = ($myrow['accept_images'] == 0) ? new Tempcode() : build_url(array('page' => 'cms_galleries', 'type' => 'add', 'cat' => $submit_cat), get_module_zone('cms_galleries'));
            $submit_video_url = ($myrow['accept_videos'] == 0) ? new Tempcode() : build_url(array('page' => 'cms_galleries', 'type' => 'add_other', 'cat' => $submit_cat), get_module_zone('cms_galleries'));
        } else {
            $submit_image_url = new Tempcode();
            $submit_video_url = new Tempcode();
        }
        if ((!$implied_existence) && (has_actual_page_access(null, 'cms_galleries', null, null)) && (has_edit_permission('cat_mid', get_member(), get_member_id_from_gallery_name($cat), 'cms_galleries', array('galleries', $cat)))) {
            $edit_url = build_url(array('page' => 'cms_galleries', 'type' => '_edit_category', 'id' => $cat), get_module_zone('cms_galleries'));
        } else {
            $edit_url = new Tempcode();
        }
        if ((!$implied_existence) && (has_actual_page_access(null, 'cms_galleries', null, null)) && (has_submit_permission('cat_mid', get_member(), get_ip_address(), 'cms_galleries'))) {
            $add_gallery_url = build_url(array('page' => 'cms_galleries', 'type' => 'add_category', 'parent_id' => $cat), get_module_zone('cms_galleries'));
        } else {
            $add_gallery_url = new Tempcode();
        }

        // Flow mode puts emphasis on subgalleries, rather than entries; it is subgalleries that there are a lot of, rather than entries
        $myrow['flow_mode_interface'] = get_param_integer('flow_mode_interface', $myrow['flow_mode_interface']); // Allow override via URL
        if ($myrow['flow_mode_interface'] == 1) {
            $max = get_param_integer('module_max', get_default_gallery_max());
            if ($max < 1) {
                $max = 1;
            }
            $start = get_param_integer('module_start', 0);
        } else {
            $max = null;
            $start = null;
        }

        // Subgalleries
        // Not done via main_multi_content block due to need to filter out special galleries
        $order = 'add_date DESC';
        if (get_option('gallery_name_order') == '1') {
            $order = 'name ASC';
        }
        $rows_children = $GLOBALS['SITE_DB']->query_select('galleries', array('*'), array('parent_id' => $cat), 'AND name NOT LIKE \'download%\' ORDER BY ' . $order, intval(get_option('subgallery_link_limit'))/*reasonable limit*/);
        $children = new Tempcode();
        if (count($rows_children) == intval(get_option('subgallery_link_limit'))) {
            $rows_children = array(); // Lots of personal galleries. Will need to be reached via member profiles
        }
        foreach ($rows_children as $child) {
            if (!has_category_access(get_member(), 'galleries', $child['name'])) {
                continue;
            }

            if ((get_option('show_empty_galleries') == '1') || (gallery_has_content($child['name']))) {
                $children->attach(render_gallery_box($child, $root, true, get_zone_name(), get_option('show_empty_galleries') != '1', false, false, false, true));
            }
        }

        // Views
        if ((get_db_type() != 'xml') && (get_value('no_view_counts') !== '1') && (is_null(get_bot_type()))) {
            $myrow['gallery_views']++;
            if (!$GLOBALS['SITE_DB']->table_is_locked('galleries')) {
                $GLOBALS['SITE_DB']->query_update('galleries', array('gallery_views' => $myrow['gallery_views']), array('name' => $cat), '', 1, null, false, true);
            }
        }

        // Feedback
        list($rating_details, $comment_details, $trackback_details) = embed_feedback_systems(
            get_page_name(),
            $cat,
            $myrow['allow_rating'],
            $myrow['allow_comments'],
            0,
            1,
            null,
            build_url(array('page' => '_SELF'), '_SELF', null, true, false, true),
            $title_to_use_2,
            find_overridden_comment_forum('galleries', $cat),
            $myrow['add_date']
        );

        // Sorting
        list($sort, $sort_backwards, $sql_suffix_images, $sql_suffix_videos) = $this->get_sort_order();
        $_selectors = array();
        if (get_option('is_on_rating') == '1') {
            $_selectors['average_rating DESC'] = 'RATING';
            $_selectors['compound_rating DESC'] = 'POPULARITY';
        }
        $_selectors = array_merge($_selectors, array(
            'url ASC' => 'FILENAME',
            'add_date ASC' => 'OLDEST_FIRST',
            'add_date DESC' => 'NEWEST_FIRST',
            'title ASC' => 'TITLE'
        ));
        $selectors = new Tempcode();
        foreach ($_selectors as $selector_value => $selector_name) {
            $selected = ($sort == $selector_value);
            $selectors->attach(do_template('PAGINATION_SORTER', array('_GUID' => '2cda0eb8456ba50801d803f33b2e1e9b', 'SELECTED' => $selected, 'NAME' => do_lang_tempcode($selector_name), 'VALUE' => $selector_value)));
        }
        $sort_url = get_self_url(false, false, array('sort' => null), false, true);
        $sorting = do_template('PAGINATION_SORT', array('_GUID' => '148c9f69ea1640fb2a6d1f6ca2e201f2', 'SORT' => 'sort', 'URL' => $sort_url, 'SELECTORS' => $selectors));

        if ($myrow['flow_mode_interface'] == 1) {
            return $this->do_gallery_flow_mode($rating_details, $comment_details, $cat, $root, $description, $children, $may_download_gallery, $edit_url, $add_gallery_url, $submit_image_url, $submit_video_url, $this->title, $rep_image, $start, $max, $fullname, $sorting, $myrow);
        } else {
            return $this->do_gallery_regular_mode($rating_details, $comment_details, $cat, $root, $description, $children, $may_download_gallery, $edit_url, $add_gallery_url, $submit_image_url, $submit_video_url, $this->title, $fullname, $sorting, $myrow);
        }
    }

    /**
     * The UI for a "flow mode" gallery.
     *
     * @param  Tempcode $rating_details Rating area
     * @param  Tempcode $comment_details Commenting area
     * @param  ID_TEXT $cat Our gallery ID
     * @param  ID_TEXT $root Virtual root gallery
     * @param  Tempcode $description The description of the gallery
     * @param  Tempcode $children The Tempcode for our visible child galleries
     * @param  boolean $may_download Whether may "download this gallery"
     * @param  Tempcode $edit_url The URL to "edit this gallery"
     * @param  Tempcode $add_gallery_url The URL to "add a gallery"
     * @param  Tempcode $submit_image_url The URL to "submit an image to this gallery"
     * @param  Tempcode $submit_video_url The URL to "submit a video to this gallery"
     * @param  Tempcode $title The title of the page (our of get_screen_title)
     * @param  URLPATH $rep_image The representative image for the gallery
     * @param  integer $start The start position we are in browsing through child galleries
     * @param  integer $max The maximum number of child galleries we can display per page
     * @param  string $fullname The gallery title
     * @param  Tempcode $sorting Sorting UI
     * @param  array $gallery_row The gallery row
     * @return Tempcode The UI
     */
    public function do_gallery_flow_mode($rating_details, $comment_details, $cat, $root, $description, $children, $may_download, $edit_url, $add_gallery_url, $submit_image_url, $submit_video_url, $title, $rep_image, $start, $max, $fullname, $sorting, $gallery_row)
    {
        list($sort, $sort_backwards, $sql_suffix_images, $sql_suffix_videos) = $this->get_sort_order();

        $image_select = get_param_string('select', '*');
        $video_select = get_param_string('video_select', '*');

        // View current entry
        $row = null;
        $current_entry = new Tempcode();
        $view_url = new Tempcode();
        $full_url = '';
        $entry_edit_url = new Tempcode();
        $entry_rating_details = new Tempcode();
        $entry_comment_details = new Tempcode();
        $entry_trackback_details = new Tempcode();
        $entry_add_date_raw = '';
        $entry_edit_date_raw = '';
        $entry_views = '';
        $entry_title = '';
        $entry_submitter = null;
        $entry_description = new Tempcode();
        $probe_type = get_param_string('probe_type', 'first');
        $probe_id = get_param_integer('probe_id', 0);

        $extra_join_image = '';
        $extra_join_video = '';
        $extra_where_image = '';
        $extra_where_video = '';

        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');
            $extra_join_image .= $privacy_join_image;
            $extra_join_video .= $privacy_join_video;
            $extra_where_image .= $privacy_where_image;
            $extra_where_video .= $privacy_where_video;
        }

        if (get_option('filter_regions') == '1') {
            require_code('locations');
            $extra_where_image .= sql_region_filter('image', 'r.id');
            $extra_where_video .= sql_region_filter('video', 'r.id');
        }

        if ($probe_type == 'first') {
            $where = db_string_equal_to('cat', $cat);
            if ((!has_privilege(get_member(), 'see_unvalidated')) && (addon_installed('unvalidated'))) {
                $where .= ' AND validated=1';
            }
            if (get_param_string('days', '') != '') {
                $where .= ' AND add_date>' . strval(time() - get_param_integer('days') * 60 * 60 * 24);
            }
            $first_video = $GLOBALS['SITE_DB']->query('SELECT *' . $sql_suffix_videos . ' FROM ' . get_table_prefix() . 'videos r ' . $extra_join_video . ' WHERE ' . $where . $extra_where_video . ' ORDER BY ' . $sort, 1, null, false, true);
            if (array_key_exists(0, $first_video)) {
                $row = $first_video[0];
                $probe_type = 'video';
                $probe_id = $row['id'];
            } else {
                $where = db_string_equal_to('cat', $cat);
                if ((!has_privilege(get_member(), 'see_unvalidated')) && (addon_installed('unvalidated'))) {
                    $where .= ' AND validated=1';
                }
                if (get_param_string('days', '') != '') {
                    $where .= ' AND add_date>' . strval(time() - get_param_integer('days') * 60 * 60 * 24);
                }
                $first_image = $GLOBALS['SITE_DB']->query('SELECT *' . $sql_suffix_images . ' FROM ' . get_table_prefix() . 'images r ' . $extra_join_image . ' WHERE ' . $where . $extra_where_image . ' ORDER BY ' . $sort, 1, null, false, true);
                if (array_key_exists(0, $first_image)) {
                    $row = $first_image[0];
                    $probe_type = 'image';
                    $probe_id = $row['id'];
                } // If else, then we have no probe_type, and thus won't be able to show anything
            }
        }
        if ((!is_null($row)) && ($row['validated'] == 0) && (addon_installed('unvalidated'))) {
            if ((!has_privilege(get_member(), 'jump_to_unvalidated')) && ((is_guest()) || ($row['submitter'] != get_member()))) {
                access_denied('PRIVILEGE', 'jump_to_unvalidated');
            }

            $warning_details = do_template('WARNING_BOX', array(
                '_GUID' => '5500ce574232db1e1577b3d69bbc0d6d',
                'WARNING' => do_lang_tempcode((get_param_integer('redirected', 0) == 1) ? 'UNVALIDATED_TEXT_NON_DIRECT' : 'UNVALIDATED_TEXT', 'gallery'),
            ));
        } else {
            $warning_details = new Tempcode();
        }

        switch ($probe_type) {
            case 'video':
                if (is_null($row)) {
                    $map = array('cat' => $cat, 'id' => $probe_id);
                    if (addon_installed('content_privacy')) {
                        require_code('content_privacy');
                        check_privacy('video', strval($probe_id));
                    }
                    if ((!has_privilege(get_member(), 'see_unvalidated')) && (addon_installed('unvalidated'))) {
                        $map['validated'] = 1;
                    }
                    $rows = $GLOBALS['SITE_DB']->query_select('videos', array('*'), $map, '', 1);
                    if (!array_key_exists(0, $rows)) {
                        attach_message(do_lang_tempcode('MISSING_RESOURCE', 'video'), 'warn');
                        break;
                    }
                    $row = $rows[0];
                }

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

                if ((has_actual_page_access(null, 'cms_galleries', null, null)) && (has_edit_permission('mid', get_member(), $row['submitter'], 'cms_galleries', array('galleries', $cat)))) {
                    $entry_edit_url = build_url(array('page' => 'cms_galleries', 'type' => '_edit_other', 'id' => $row['id']), get_module_zone('cms_galleries'));
                }

                // Video HTML
                $thumb_url = $row['thumb_url'];
                $url = $row['url'];
                $video_player = show_gallery_video_media($url, $thumb_url, $row['video_width'], $row['video_height'], $row['video_length'], $row['submitter']);
                $view_url = build_url(array('page' => '_SELF', 'type' => 'video', 'id' => $row['id'], 'days' => (get_param_string('days', '') == '') ? null : get_param_string('days'), 'sort' => ($sort == 'add_date DESC') ? null : $sort, 'select' => ($image_select == '*') ? null : $image_select, 'video_select' => ($video_select == '*') ? null : $video_select), '_SELF');

                // Some extra variables relating to the currently selected entry
                $entry_title = get_translated_text($row['title']);
                $entry_rating_details = get_rating_box(get_self_url(true), $entry_title, 'videos', strval($row['id']), $row['allow_rating'] == 1, $row['submitter']);
                $entry_comment_details = get_comments('videos', $row['allow_comments'] == 1, strval($row['id']), false, find_overridden_comment_forum('videos', $cat), null, null, false, null, $row['submitter'], $row['allow_comments'] == 2);
                $entry_trackback_details = get_trackbacks('videos', strval($row['id']), $row['allow_trackbacks'] == 1);
                $entry_add_date_raw = is_null($row['add_date']) ? '' : strval($row['add_date']);
                $entry_edit_date_raw = is_null($row['edit_date']) ? '' : strval($row['edit_date']);
                $entry_views = integer_format($row['video_views']);
                $current_entry = do_template('GALLERY_FLOW_MODE_VIDEO', array(
                    '_GUID' => 'b6a795dc3853789df2a2951293d0fb26',
                    '_TITLE' => get_translated_text($row['title']),
                    'EDIT_URL' => $entry_edit_url,
                    'MAIN' => true,
                    'RATING_DETAILS' => $entry_rating_details,
                    'DESCRIPTION' => get_translated_tempcode('videos', $just_row, 'description'),
                    'CAT' => $cat,
                    'THUMB_URL' => $url,
                    'FULL_URL' => $full_url,
                    'ID' => strval($row['id']),
                    'VIEWS' => strval($row['video_views']),
                    'ADD_DATE_RAW' => strval($row['add_date']),
                    'EDIT_DATE_RAW' => is_null($row['edit_date']) ? '' : strval($row['edit_date']),
                    'SUBMITTER' => strval($row['submitter']),
                    'VIDEO_PLAYER' => $video_player,
                    'VIEW_URL' => $view_url,
                    'VIDEO_DETAILS' => show_video_details($row),
                    'COMMENT_COUNT' => ((get_option('is_on_comments') == '1') && (!has_no_forum()) && ($row['allow_comments'] >= 1)),
                ));

                $GLOBALS['SITE_DB']->query_update('videos', array('video_views' => $row['video_views'] + 1), array('id' => $row['id']), '', 1, null, false, true);

                break;
            case 'image':
                if (is_null($row)) {
                    $map = array('cat' => $cat, 'id' => $probe_id);
                    if (addon_installed('content_privacy')) {
                        require_code('content_privacy');
                        check_privacy('image', strval($probe_id));
                    }
                    if ((!has_privilege(get_member(), 'see_unvalidated')) && (addon_installed('unvalidated'))) {
                        $map['validated'] = 1;
                    }
                    $rows = $GLOBALS['SITE_DB']->query_select('images', array('*'), $map, '', 1);
                    if (!array_key_exists(0, $rows)) {
                        attach_message(do_lang_tempcode('MISSING_RESOURCE', 'image'), 'warn');
                        break;
                    }
                    $row = $rows[0];
                }

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

                if ((has_actual_page_access(null, 'cms_galleries', null, null)) && (has_edit_permission('mid', get_member(), $row['submitter'], 'cms_galleries', array('galleries', $cat)))) {
                    $entry_edit_url = build_url(array('page' => 'cms_galleries', 'type' => '_edit', 'id' => $row['id']), get_module_zone('cms_galleries'));
                }

                $thumb_url = $row['url'];
                if (url_is_local($thumb_url)) {
                    $file_size = file_exists(get_custom_file_base() . '/' . rawurldecode($thumb_url)) ? strval(filesize(get_custom_file_base() . '/' . rawurldecode($thumb_url))) : '';
                    $thumb_url = get_custom_base_url() . '/' . $thumb_url;
                } else {
                    $file_size = '';
                }
                $full_url = $thumb_url;
                $view_url = build_url(array('page' => '_SELF', 'type' => 'image', 'id' => $row['id'], 'days' => (get_param_string('days', '') == '') ? null : get_param_string('days'), 'sort' => ($sort == 'add_date DESC') ? null : $sort, 'select' => ($image_select == '*') ? null : $image_select, 'video_select' => ($video_select == '*') ? null : $video_select), '_SELF');

                // Some extra variables relatin to the currently selected entry
                $entry_title = get_translated_text($row['title']);
                $entry_rating_details = get_rating_box(get_self_url(true), $entry_title, 'images', strval($row['id']), $row['allow_rating'] == 1, $row['submitter']);
                $entry_comment_details = get_comments('images', $row['allow_comments'] == 1, strval($row['id']), false, find_overridden_comment_forum('images', $cat), null, null, false, null, $row['submitter'], $row['allow_comments'] == 2);
                $entry_trackback_details = get_trackbacks('images', strval($row['id']), $row['allow_trackbacks'] == 1);
                $entry_add_date_raw = is_null($row['add_date']) ? '' : strval($row['add_date']);
                $entry_edit_date_raw = is_null($row['edit_date']) ? '' : strval($row['edit_date']);
                $entry_views = integer_format($row['image_views']);

                $current_entry = do_template('GALLERY_FLOW_MODE_IMAGE', array(
                    '_GUID' => 'fd486cf9a3338bc277a7170a1961089b',
                    '_TITLE' => get_translated_text($row['title']),
                    'EDIT_URL' => $entry_edit_url,
                    'MAIN' => true,
                    'RATING_DETAILS' => $entry_rating_details,
                    'DESCRIPTION' => get_translated_tempcode('images', $just_row, 'description'),
                    'FILE_SIZE' => $file_size,
                    'CAT' => $cat,
                    'THUMB_URL' => $thumb_url,
                    'FULL_URL' => $full_url,
                    'ID' => strval($row['id']),
                    'VIEWS' => strval($row['image_views']),
                    'ADD_DATE_RAW' => strval($row['add_date']),
                    'EDIT_DATE_RAW' => is_null($row['edit_date']) ? '' : strval($row['edit_date']),
                    'SUBMITTER' => strval($row['submitter']),
                    'THUMB' => $thumb_url,
                    'VIEW_URL' => $view_url,
                    'COMMENT_COUNT' => ((get_option('is_on_comments') == '1') && (!has_no_forum()) && ($row['allow_comments'] >= 1)),
                ));

                $GLOBALS['SITE_DB']->query_update('images', array('image_views' => $row['image_views'] + 1), array('id' => $row['id']), '', 1, null, false, true);

                break;
        }

        $entries = new Tempcode();

        // Display entries
        $where = db_string_equal_to('cat', $cat);
        if ((!has_privilege(get_member(), 'see_unvalidated')) && (addon_installed('unvalidated'))) {
            $where .= ' AND validated=1';
        }
        if (get_param_string('days', '') != '') {
            $where .= ' AND add_date>' . strval(time() - get_param_integer('days') * 60 * 60 * 24);
        }
        $max_entries = intval(get_option('gallery_entries_flow_per_page'));
        $query_rows_videos = $GLOBALS['SITE_DB']->query('SELECT *,r.id AS r_id' . $sql_suffix_videos . ' FROM ' . get_table_prefix() . 'videos r ' . $extra_join_video . ' WHERE ' . $where . $extra_where_video . ' ORDER BY ' . $sort, $max_entries, null, false, true, array('title' => 'SHORT_TRANS', 'description' => 'LONG_TRANS__COMCODE'));
        $query_rows_images = $GLOBALS['SITE_DB']->query('SELECT *,r.id AS r_id' . $sql_suffix_images . ' FROM ' . get_table_prefix() . 'images r ' . $extra_join_image . ' WHERE ' . $where . $extra_where_image . ' ORDER BY ' . $sort, $max_entries, null, false, true, array('title' => 'SHORT_TRANS', 'description' => 'LONG_TRANS__COMCODE'));

        // See if there is a numbering system to sort by
        $all_are = null;
        foreach ($query_rows_images as $q) {
            $this_are = strtolower(preg_replace('#\d#', '', $q['url']));
            if (is_null($all_are)) {
                $all_are = $this_are;
            }
            if ($all_are != $this_are) {
                $all_are = null;
                break;
            }
        }
        if (!is_null($all_are)) {
            sort_maps_by($query_rows_images, 'url');
        }

        // Show media
        $query_rows = array_merge($query_rows_images, $query_rows_videos);
        foreach ($query_rows as $row) {
            $type = array_key_exists('video_length', $row) ? 'video' : 'image';

            if (($type == $probe_type) && ($row['r_id'] == $probe_id)) {
                continue;
            }

            $just_row = db_map_restrict($row, array('id', 'description'), array('id' => 'r_id'));

            $entry_title = get_translated_text($row['title']);
            $entry_description = get_translated_tempcode($type . 's', $just_row, 'description');

            $probe_url = build_url(array('page' => '_SELF', 'type' => 'browse', 'id' => $cat, 'flow_mode_interface' => get_param_integer('flow_mode_interface', null), 'probe_type' => $type, 'probe_id' => $row['r_id'], 'days' => (get_param_string('days', '') == '') ? null : get_param_string('days'), 'sort' => ($sort == 'add_date DESC') ? null : $sort, 'select' => ($image_select == '*') ? null : $image_select, 'video_select' => ($video_select == '*') ? null : $video_select), '_SELF');
            $view_url_2 = build_url(array('page' => '_SELF', 'type' => $type, 'id' => $row['r_id'], 'days' => (get_param_string('days', '') == '') ? null : get_param_string('days'), 'sort' => ($sort == 'add_date DESC') ? null : $sort, 'select' => ($image_select == '*') ? null : $image_select, 'video_select' => ($video_select == '*') ? null : $video_select), '_SELF');
            if (array_key_exists('url', $row)) {
                $thumb_url = ensure_thumbnail($row['url'], $row['thumb_url'], 'galleries', 'images', $row['r_id']);
            } else {
                $thumb_url = $row['thumb_url'];
                if ($thumb_url == '') {
                    $thumb_url = find_theme_image('na');
                }
                if (url_is_local($thumb_url)) {
                    $thumb_url = get_custom_base_url() . '/' . $thumb_url;
                }
            }

            $_edit_url = new Tempcode();
            if (has_delete_permission('mid', get_member(), $row['submitter'], 'cms_galleries', array('gallery', $row['cat']))) {
                $_edit_url = build_url(array('page' => 'cms_galleries', 'type' => ($type == 'image') ? '__edit' : '__edit_other', 'id' => $row['r_id'], 'redirect' => get_self_url(true)), get_module_zone('cms_galleries'));
            }

            $class = (($type == $probe_type) && ($row['r_id'] == $probe_id)) ? 'currentimage' : '';
            $thumb = do_image_thumb($thumb_url, $entry_title);
            $entries->attach(do_template('GALLERY_FLOW_ENTRY', array(
                '_GUID' => 'ff75c904ba7fe857484874483ed65f5a',
                'DESCRIPTION' => $entry_description,
                '_TITLE' => $entry_title,
                'TYPE' => $type,
                'ID' => strval($row['r_id']),
                'VIEWS' => strval($row[$type . '_views']),
                'ADD_DATE_RAW' => strval($row['add_date']),
                'EDIT_DATE_RAW' => is_null($row['edit_date']) ? '' : strval($row['edit_date']),
                'SUBMITTER' => strval($row['submitter']),
                'CLASS' => $class,
                'THUMB' => $thumb,
                'VIEW_URL' => $probe_url,
                'VIEW_URL_2' => $view_url_2,
                '_EDIT_URL' => $_edit_url,
                'CAT' => $cat,
            )));
        }

        // Member details
        $member_id = get_member_id_from_gallery_name($cat, null, true);
        if (get_forum_type() == 'cns') {
            require_code('cns_members');
            require_code('cns_members2');
        }
        $member_details = ((is_null($member_id)) || (get_forum_type() != 'cns')) ? new Tempcode() : render_member_box($member_id, true, null, null, true, null, false);

        // Rep-image
        $rep_image_url = '';
        if (($rep_image != '') && (url_is_local($rep_image))) {
            $rep_image_url = get_custom_base_url() . '/' . $rep_image;
        } elseif ($rep_image != '') {
            $rep_image_url = $rep_image;
        }

        // Navigation
        list(, , , $first_entry_id, , , $first_type) = $this->build_set_navigation(db_string_equal_to('cat', $cat), '', '', $probe_id, $root, $probe_type, get_param_integer('slideshow', 0), get_param_integer('wide_high', 0), $start, $max, $cat, $sort, $sort_backwards, $sql_suffix_images, $sql_suffix_videos, get_param_string('select', '*'), get_param_string('video_select', '*'));

        // Render
        return do_template('GALLERY_FLOW_MODE_SCREEN', array(
            '_GUID' => '73728cb94579f06ab487627101011d43',
            'FIRST_ENTRY_ID' => is_null($first_entry_id) ? '' : ($first_type . ':' . strval($first_entry_id)),
            'SORTING' => $sorting,
            '_TITLE' => $fullname,
            'MEMBER_ID' => is_null($member_id) ? '' : strval($member_id),
            'VIEW_URL' => $view_url,
            'FULL_URL' => $full_url,
            'PROBE_TYPE' => $probe_type,
            'PROBE_ID' => is_null($probe_id) ? '' : strval($probe_id),
            'ENTRY_TITLE' => $entry_title,
            'ENTRY_DESCRIPTION' => $entry_description,
            'ENTRY_SUBMITTER' => is_null($entry_submitter) ? '' : strval($entry_submitter),
            'ENTRY_VIEWS' => $entry_views,
            'ENTRY_ADD_DATE_RAW' => $entry_add_date_raw,
            'ENTRY_EDIT_DATE_RAW' => $entry_edit_date_raw,
            'ENTRY_TRACKBACK_DETAILS' => $entry_trackback_details,
            'ENTRY_RATING_DETAILS' => $entry_rating_details,
            'ENTRY_COMMENT_DETAILS' => $entry_comment_details,
            'ENTRY_EDIT_URL' => $entry_edit_url,
            'WARNING_DETAILS' => $warning_details,
            'TAGS' => get_loaded_tags('galleries'),
            'RATING_DETAILS' => $rating_details,
            'COMMENT_DETAILS' => $comment_details,
            'REP_IMAGE_URL' => $rep_image_url,
            'TITLE' => $title,
            'MEMBER_DETAILS' => $member_details,
            'DESCRIPTION' => $description,
            'CHILDREN' => $children,
            'CURRENT_ENTRY' => $current_entry,
            'ENTRIES' => $entries,
            'ADD_GALLERY_URL' => $add_gallery_url,
            'EDIT_URL' => $edit_url,
            'IMAGE_URL' => $submit_image_url,
            'VIDEO_URL' => $submit_video_url,
            'MAY_DOWNLOAD' => $may_download,
            'CAT' => $cat,
            'ACCEPT_IMAGES' => ($gallery_row['accept_images'] == 1),
            'ACCEPT_VIDEOS' => ($gallery_row['accept_videos'] == 1),
            'VIEWS' => strval($gallery_row['gallery_views']),
            'OWNER' => ($gallery_row['g_owner'] === null) ? null : strval($gallery_row['g_owner']),
        ));
    }

    /**
     * The UI for an "regular mode" gallery.
     *
     * @param  Tempcode $rating_details Rating area
     * @param  Tempcode $comment_details Commenting area
     * @param  ID_TEXT $cat Our gallery ID
     * @param  ID_TEXT $root Virtual root gallery
     * @param  Tempcode $description The description of the gallery
     * @param  Tempcode $children The Tempcode for our visible child galleries
     * @param  boolean $may_download Whether may "download this gallery"
     * @param  Tempcode $edit_url The URL to "edit this gallery"
     * @param  Tempcode $add_gallery_url The URL to "add a gallery"
     * @param  Tempcode $submit_image_url The URL to "submit an image to this gallery"
     * @param  Tempcode $submit_video_url The URL to "submit a video to this gallery"
     * @param  Tempcode $title The title of the page (our of get_screen_title)
     * @param  string $fullname The gallery title
     * @param  Tempcode $sorting Sorting UI
     * @param  array $gallery_row The gallery row
     * @return Tempcode The UI
     */
    public function do_gallery_regular_mode($rating_details, $comment_details, $cat, $root, $description, $children, $may_download, $edit_url, $add_gallery_url, $submit_image_url, $submit_video_url, $title, $fullname, $sorting, $gallery_row)
    {
        // Entries
        if (get_option('galleries_subcat_narrowin') == '1') {
            $cat_select = $cat . '*';
        } else {
            $cat_select = $cat . '#';
        }
        $days = get_param_string('days', '');
        $image_select = get_param_string('select', '*');
        $video_select = get_param_string('video_select', '*');
        $sort = get_param_string('sort', get_option('galleries_default_sort_order'));
        $filter = either_param_string('active_filter', '');
        $entries = do_block('main_gallery_embed', array('param' => $cat_select, 'zone' => get_zone_name(), 'sort' => $sort, 'days' => $days, 'max' => get_option('gallery_entries_regular_per_page'), 'pagination' => '1', 'select' => $image_select, 'video_select' => $video_select, 'filter' => $filter, 'video_filter' => $filter, 'block_id' => 'module', 'render_if_empty' => '1'));

        // Member gallery?
        $member_id = get_member_id_from_gallery_name($cat, null, true);
        if (get_forum_type() == 'cns') {
            require_code('cns_members');
            require_code('cns_members2');
        }
        $member_details = ((is_null($member_id)) || (get_forum_type() != 'cns')) ? new Tempcode() : render_member_box($member_id, true, null, null, true, null, false);

        // Render
        return do_template('GALLERY_REGULAR_MODE_SCREEN', array(
            '_GUID' => 'cec405597f47f5079b7c7f581fa6b5c2',
            'SORTING' => $sorting,
            '_TITLE' => $fullname,
            'MEMBER_ID' => is_null($member_id) ? '' : strval($member_id),
            'TAGS' => get_loaded_tags('galleries'),
            'CAT' => $cat,
            'MEMBER_DETAILS' => $member_details,
            'RATING_DETAILS' => $rating_details,
            'COMMENT_DETAILS' => $comment_details,
            'ADD_GALLERY_URL' => $add_gallery_url,
            'EDIT_URL' => $edit_url,
            'CHILDREN' => $children,
            'TITLE' => $title,
            'DESCRIPTION' => $description,
            'IMAGE_URL' => $submit_image_url,
            'VIDEO_URL' => $submit_video_url,
            'MAY_DOWNLOAD' => $may_download,
            'ENTRIES' => $entries,
            'ACCEPT_IMAGES' => ($gallery_row['accept_images'] == 1),
            'ACCEPT_VIDEOS' => ($gallery_row['accept_videos'] == 1),
            'VIEWS' => strval($gallery_row['gallery_views']),
            'OWNER' => ($gallery_row['g_owner'] === null) ? null : strval($gallery_row['g_owner']),
        ));
    }

    /**
     * The UI to show an image.
     *
     * @param  ?string $category_name Alternate category name to use (null: use standard one). This is useful if you are overriding this code to show images in virtual galleries.
     * @param  ?Tempcode $breadcrumbs Breadcrumbs (null: derive in this function).
     * @return Tempcode The UI
     */
    public function show_image($category_name = null, $breadcrumbs = null)
    {
        $id = $this->id;
        $myrow = $this->myrow;
        $cat = $this->cat;
        $url = $this->url;
        $thumb_url = $this->thumb_url;
        $true_category_name = $this->category_name;
        if (is_null($category_name)) {
            $category_name = $true_category_name;
        }
        $root = $this->root;

        list($sort, $sort_backwards, $sql_suffix_images, $sql_suffix_videos) = $this->get_sort_order();

        if ((get_value('no_individual_gallery_view') === '1') && ($GLOBALS['SITE_DB']->query_select_value('galleries', 'flow_mode_interface', array('name' => $cat)) == '1')) {
            require_code('site2');
            assign_refresh(build_url(array('page' => '_SELF', 'type' => 'browse', 'id' => $cat, 'probe_id' => $id, 'probe_type' => 'image'), '_SELF'), 0.0);
        }

        // Views
        if ((get_db_type() != 'xml') && (get_value('no_view_counts') !== '1') && (is_null(get_bot_type()))) {
            $myrow['image_views']++;
            if (!$GLOBALS['SITE_DB']->table_is_locked('images')) {
                $GLOBALS['SITE_DB']->query_update('images', array('image_views' => $myrow['image_views']), array('id' => $id), '', 1, null, false, true);
            }
        }

        list($rating_details, $comment_details, $trackback_details) = embed_feedback_systems(
            'images',
            strval($id),
            $myrow['allow_rating'],
            $myrow['allow_comments'],
            $myrow['allow_trackbacks'],
            $myrow['validated'],
            $myrow['submitter'],
            build_url(array('page' => '_SELF', 'type' => 'image', 'id' => $id), '_SELF', null, false, false, true),
            (get_translated_text($myrow['title']) == '') ? do_lang('VIEW_IMAGE_IN', $true_category_name) : get_translated_text($myrow['title']),
            find_overridden_comment_forum('images', $cat),
            $myrow['add_date']
        );

        // Description
        $just_row = db_map_restrict($myrow, array('cat', 'description'));
        $description = get_translated_tempcode('images', $just_row, 'description');

        // Validation
        if (($myrow['validated'] == 0) && (addon_installed('unvalidated'))) {
            if ((!has_privilege(get_member(), 'jump_to_unvalidated')) && ((is_guest()) || ($myrow['submitter'] != get_member()))) {
                access_denied('PRIVILEGE', 'jump_to_unvalidated');
            }

            $warning_details = do_template('WARNING_BOX', array(
                '_GUID' => 'c32faacba974e648a67e5e91ffd3d8e5',
                'WARNING' => do_lang_tempcode((get_param_integer('redirected', 0) == 1) ? 'UNVALIDATED_TEXT_NON_DIRECT' : 'UNVALIDATED_TEXT', 'image'),
            ));
        } else {
            $warning_details = new Tempcode();
        }

        if ((has_actual_page_access(null, 'cms_galleries', null, null)) && (has_edit_permission('mid', get_member(), $myrow['submitter'], 'cms_galleries', array('galleries', $cat)))) {
            $edit_url = build_url(array('page' => 'cms_galleries', 'type' => '_edit', 'id' => $id), get_module_zone('cms_galleries'));
        } else {
            $edit_url = new Tempcode();
        }

        $add_date = get_timezoned_date($myrow['add_date']);
        $edit_date = is_null($myrow['edit_date']) ? '' : get_timezoned_date($myrow['edit_date']);

        list($n, $x, $nav) = $this->build_set_navigation(db_string_equal_to('cat', $cat), '', $category_name, $id, $root, 'image', get_param_integer('slideshow', 0), get_param_integer('wide_high', 0), get_param_integer('module_start', 0), get_param_integer('module_max', get_default_gallery_max()), $cat, $sort, $sort_backwards, $sql_suffix_images, $sql_suffix_videos, get_param_string('select', '*'), get_param_string('video_select', '*'));

        $member_id = get_member_id_from_gallery_name($cat, null, true);
        if (get_forum_type() == 'cns') {
            require_code('cns_members');
            require_code('cns_members2');
        }
        $member_details = ((is_null($member_id)) || (get_forum_type() != 'cns')) ? new Tempcode() : render_member_box($member_id, true, null, null, true, null, false);

        return do_template('GALLERY_ENTRY_SCREEN', array(
            '_GUID' => '332a19b6a72505f8e1eb4d288df247ce',
            'MEDIA_TYPE' => 'image',
            'E_TITLE' => get_translated_text($myrow['title']),
            'CAT' => $cat,
            'SLIDESHOW' => (get_param_integer('slideshow', 0) == 1),
            'TRUE_GALLERY_TITLE' => $true_category_name,
            'GALLERY_TITLE' => $category_name,
            'MEMBER_ID' => is_null($member_id) ? '' : strval($member_id),
            'ID' => strval($id),
            'TAGS' => get_loaded_tags('images'),
            'TITLE' => $this->title,
            'SUBMITTER' => strval($myrow['submitter']),
            'MEMBER_DETAILS' => $member_details,
            'X' => integer_format($x),
            'N' => integer_format($n),
            'VIEWS' => integer_format($myrow['image_views']),
            'ADD_DATE_RAW' => strval($myrow['add_date']),
            'EDIT_DATE_RAW' => is_null($myrow['edit_date']) ? '' : strval($myrow['edit_date']),
            'ADD_DATE' => $add_date,
            'EDIT_DATE' => $edit_date,
            'TRACKBACK_DETAILS' => $trackback_details,
            'RATING_DETAILS' => $rating_details,
            'COMMENT_DETAILS' => $comment_details,
            'EDIT_URL' => $edit_url,
            'NAV' => $nav,
            'DESCRIPTION' => $description,
            'URL' => $url,
            'THUMB_URL' => $thumb_url,
            'WARNING_DETAILS' => $warning_details,
        ));
    }

    /**
     * The UI to show a video.
     *
     * @param  ?string $category_name Alternate category name to use (null: use standard one). This is useful if you are overriding this code to show images in virtual galleries.
     * @param  ?Tempcode $breadcrumbs Breadcrumbs (null: derive in this function).
     * @return Tempcode The UI
     */
    public function show_video($category_name = null, $breadcrumbs = null)
    {
        $id = $this->id;
        $myrow = $this->myrow;
        $cat = $this->cat;
        $url = $this->url;
        $thumb_url = $this->thumb_url;
        $true_category_name = $this->category_name;
        if (is_null($category_name)) {
            $category_name = $true_category_name;
        }
        $root = $this->root;

        list($sort, $sort_backwards, $sql_suffix_images, $sql_suffix_videos) = $this->get_sort_order();

        if ((get_value('no_individual_gallery_view') === '1') && ($GLOBALS['SITE_DB']->query_select_value('galleries', 'flow_mode_interface', array('name' => $cat)) == '1')) {
            require_code('site2');
            assign_refresh(build_url(array('page' => '_SELF', 'type' => 'browse', 'id' => $cat, 'probe_id' => $id, 'probe_type' => 'video'), '_SELF'), 0.0);
        }

        // Views
        if ((get_db_type() != 'xml') && (get_value('no_view_counts') !== '1') && (is_null(get_bot_type()))) {
            $myrow['video_views']++;
            if (!$GLOBALS['SITE_DB']->table_is_locked('videos')) {
                $GLOBALS['SITE_DB']->query_update('videos', array('video_views' => $myrow['video_views']), array('id' => $id), '', 1, null, false, true);
            }
        }

        list($rating_details, $comment_details, $trackback_details) = embed_feedback_systems(
            'videos',
            strval($id),
            $myrow['allow_rating'],
            $myrow['allow_comments'],
            $myrow['allow_trackbacks'],
            $myrow['validated'],
            $myrow['submitter'],
            build_url(array('page' => '_SELF', 'type' => 'video', 'id' => $id), '_SELF', null, false, false, true),
            (get_translated_text($myrow['title']) == '') ? do_lang('VIEW_VIDEO_IN', $true_category_name) : get_translated_text($myrow['title']),
            find_overridden_comment_forum('videos', $cat),
            $myrow['add_date']
        );

        // Description
        $just_row = db_map_restrict($myrow, array('cat', 'description'));
        $description = get_translated_tempcode('videos', $just_row, 'description');

        // Validation
        if (($myrow['validated'] == 0) && (addon_installed('unvalidated'))) {
            if ((!has_privilege(get_member(), 'jump_to_unvalidated')) && ((is_guest()) || ($myrow['submitter'] != get_member()))) {
                access_denied('PRIVILEGE', 'jump_to_unvalidated');
            }

            $warning_details = do_template('WARNING_BOX', array(
                '_GUID' => 'b32faacba974e648a67e5e91ffd3d8e5',
                'WARNING' => do_lang_tempcode((get_param_integer('redirected', 0) == 1) ? 'UNVALIDATED_TEXT_NON_DIRECT' : 'UNVALIDATED_TEXT', 'video'),
            ));
        } else {
            $warning_details = new Tempcode();
        }

        if ((has_actual_page_access(null, 'cms_galleries', null, null)) && (has_edit_permission('mid', get_member(), $myrow['submitter'], 'cms_galleries', array('galleries', $cat)))) {
            $edit_url = build_url(array('page' => 'cms_galleries', 'type' => '_edit_other', 'id' => $id), get_module_zone('cms_galleries'));
        } else {
            $edit_url = new Tempcode();
        }

        $add_date = get_timezoned_date($myrow['add_date']);
        $edit_date = is_null($myrow['edit_date']) ? '' : get_timezoned_date($myrow['edit_date']);

        // Video HTML
        $video = show_gallery_video_media($url, $thumb_url, $myrow['video_width'], $myrow['video_height'], $myrow['video_length'], $myrow['submitter']);

        list($n, $x, $nav) = $this->build_set_navigation(db_string_equal_to('cat', $cat), '', $category_name, $id, $root, 'video', get_param_integer('slideshow', 0), get_param_integer('wide_high', 0), get_param_integer('module_start', 0), get_param_integer('module_max', get_default_gallery_max()), $cat, $sort, $sort_backwards, $sql_suffix_images, $sql_suffix_videos, get_param_string('select', '*'), get_param_string('video_select', '*'));

        $member_id = get_member_id_from_gallery_name($cat, null, true);
        if (get_forum_type() == 'cns') {
            require_code('cns_members');
            require_code('cns_members2');
        }
        $member_details = ((is_null($member_id)) || (get_forum_type() != 'cns')) ? new Tempcode() : render_member_box($member_id, true, null, null, true, null, false);

        $video_details = show_video_details($myrow);

        return do_template('GALLERY_ENTRY_SCREEN', array(
            '_GUID' => '91e231906ed899513ec2db8a2974dddf',
            'MEDIA_TYPE' => 'video',
            'E_TITLE' => get_translated_text($myrow['title']),
            'CAT' => $cat,
            'SLIDESHOW' => (get_param_integer('slideshow', 0) == 1),
            'TRUE_GALLERY_TITLE' => $true_category_name,
            'GALLERY_TITLE' => $category_name,
            'MEMBER_ID' => is_null($member_id) ? '' : strval($member_id),
            'ID' => strval($id),
            'TAGS' => get_loaded_tags('videos'),
            'TITLE' => $this->title,
            'SUBMITTER' => strval($myrow['submitter']),
            'URL' => $url,
            'VIDEO_DETAILS' => $video_details,
            'MEMBER_DETAILS' => $member_details,
            'X' => integer_format($x),
            'N' => integer_format($n),
            'VIEWS' => integer_format($myrow['video_views']),
            'ADD_DATE_RAW' => strval($myrow['add_date']),
            'EDIT_DATE_RAW' => is_null($myrow['edit_date']) ? '' : strval($myrow['edit_date']),
            'ADD_DATE' => $add_date,
            'EDIT_DATE' => $edit_date,
            'RATING_DETAILS' => $rating_details,
            'TRACKBACK_DETAILS' => $trackback_details,
            'COMMENT_DETAILS' => $comment_details,
            'EDIT_URL' => $edit_url,
            'NAV' => $nav,
            'DESCRIPTION' => $description,
            'VIDEO' => $video,
            'WARNING_DETAILS' => $warning_details,
            'THUMB_URL' => $thumb_url,
        ));
    }

    /**
     * Sort out gallery back/next navigation.
     *
     * @param  string $where Where clause for doing set query
     * @param  string $join Join clause for doing set query
     * @param  Tempcode $category_name The actual title for the gallery we are using
     * @param  ?AUTO_LINK $current_id The ID of the current entry of the type we are browsing in the gallery we are using (null: assume first)
     * @param  ID_TEXT $root The root gallery (the gallery we are considering as an ad hoc root, to allow gallery splitting-up)
     * @param  ?ID_TEXT $current_type The current type being browsed (null: assume first)
     * @set image video
     * @param  BINARY $slideshow If in slideshow
     * @param  BINARY $wide_high If panels hidden
     * @param  integer $start Start position when returning to category
     * @param  integer $max Max to show when returning to category
     * @param  ID_TEXT $cat Category
     * @param  ID_TEXT $sort Sort order
     * @param  ID_TEXT $sort_backwards Sort order for reverse sorting
     * @param  string $sql_suffix_images Select clause query suffix for images
     * @param  string $sql_suffix_videos Select clause query suffix for videos
     * @param  string $image_select Selectcode for limiting images displayed
     * @param  string $video_select Selectcode for limiting videos displayed
     * @return array A tuple of the navigation bits
     */
    public function build_set_navigation($where, $join, $category_name, $current_id, $root, $current_type, $slideshow, $wide_high, $start, $max, $cat, $sort, $sort_backwards, $sql_suffix_images, $sql_suffix_videos, $image_select, $video_select)
    {
        $sv = (has_privilege(get_member(), 'see_unvalidated') && (addon_installed('unvalidated')));
        if (!$sv) {
            $where .= ' AND validated=1';
        }

        $days = get_param_string('days', '');
        if ($days != '') {
            $where .= ' AND add_date>=' . strval(time() - intval($days) * 60 * 60 * 24);
        }

        $where_images = $where;
        $where_videos = $where;
        if ($image_select != '*' || $video_select != '*') {
            require_code('selectcode');
            $image_select_sql = selectcode_to_sqlfragment($image_select, 'r.id');
            $where_images .= ' AND ' . $image_select_sql;
            $video_select_sql = selectcode_to_sqlfragment($video_select, 'r.id');
            $where_videos .= ' AND ' . $video_select_sql;
        }

        $extra_join_image = '';
        $extra_join_video = '';
        $extra_where_image = '';
        $extra_where_video = '';

        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');
            $extra_join_image .= $privacy_join_image;
            $extra_join_video .= $privacy_join_video;
            $extra_where_image .= $privacy_where_image;
            $extra_where_video .= $privacy_where_video;
        }

        if (get_option('filter_regions') == '1') {
            require_code('locations');
            $extra_where_image .= sql_region_filter('image', 'r.id');
            $extra_where_video .= sql_region_filter('video', 'r.id');
        }

        $total_images = $GLOBALS['SITE_DB']->query_value_if_there('SELECT COUNT(*) FROM ' . get_table_prefix() . 'images r' . $join . $extra_join_image . ' WHERE ' . $where_images . $extra_where_image, false, true);
        $total_videos = $GLOBALS['SITE_DB']->query_value_if_there('SELECT COUNT(*) FROM ' . get_table_prefix() . 'videos r' . $join . $extra_join_video . ' WHERE ' . $where_videos . $extra_where_video, false, true);

        // These will hopefully be replaced with proper values
        $position = 1;
        $back_id = null;
        $back_type = $current_type;
        $next_id = null;
        $next_type = $current_type;
        $first_id = $current_id;
        $first_type = $current_type;

        $total = $total_videos + $total_images;
        if ($total < 500) { // Not too many to navigate through
            $rows_images = $GLOBALS['SITE_DB']->query('SELECT r.id,add_date,url' . $sql_suffix_images . ',title FROM ' . get_table_prefix() . 'images r' . $join . $extra_join_image . ' WHERE ' . $where_images . $extra_where_image . ' ORDER BY ' . $sort, null, null, false, true);
            $rows_videos = $GLOBALS['SITE_DB']->query('SELECT r.id,add_date,url' . $sql_suffix_videos . ',title FROM ' . get_table_prefix() . 'videos r' . $join . $extra_join_video . ' WHERE ' . $where_videos . $extra_where_video . ' ORDER BY ' . $sort, null, null, false, true);

            list($_sort, $_dir) = explode(' ', $sort, 2);

            // Sort
            $combined = array();
            foreach ($rows_images as $row_image) {
                $combined[] = array($row_image, 'image', $row_image[$_sort]);
            }
            foreach ($rows_videos as $row_video) {
                $combined[] = array($row_video, 'video', $row_video[$_sort]);
            }
            sort_maps_by($combined, ($_dir == 'DESC') ? '!2' : '2');

            // Find current position in navigation set, and neighbour positions
            foreach ($combined as $position => $c) {
                if ($position == 0) {
                    $first_id = $c[0]['id'];
                    $first_type = $c[1];
                }

                if ((is_null($current_id)) || (($c[1] == $current_type) && ($c[0]['id'] == $current_id))) {
                    if ($position != 0) {
                        $back_id = $combined[$position - 1][0]['id'];
                        $back_type = $combined[$position - 1][1];
                    }

                    if ($position != count($combined) - 1) {
                        $next_id = $combined[$position + 1][0]['id'];
                        $next_type = $combined[$position + 1][1];
                    }

                    $position++; // We're not counting from zero

                    break;
                }
            }
        }

        // UI for navigating all this
        $nav_ui = is_null($first_id) ? new Tempcode() : $this->show_nav($category_name, $where, $join, $current_id, $first_id, $back_id, $next_id, $root, $position, $total, $current_type, $first_type, $back_type, $next_type, $slideshow, $wide_high, $start, $max, $cat, $sort, $sort_backwards, $sql_suffix_images, $sql_suffix_videos, $image_select, $video_select);

        return array($total/*aka $n*/, $position/*aka $x*/, $nav_ui, $first_id, $back_id, $next_id, $first_type, $back_type, $next_type);
    }

    /**
     * Show gallery navigation bits used when viewing images/videos.
     *
     * @param  Tempcode $category_name The actual title for the gallery we are using
     * @param  string $where Where clause for doing set query
     * @param  string $join Join clause for doing set query
     * @param  AUTO_LINK $current_id The ID of the current entry of the type we are browsing in the gallery we are using
     * @param  ?AUTO_LINK $first_id The ID of the first entry of the type we are browsing in the gallery we are using (null: no first)
     * @param  ?AUTO_LINK $back_id The ID of the previous entry of the type we are browsing in the gallery we are using (null: no previous)
     * @param  ?AUTO_LINK $next_id As above, except next entry (null: no next)
     * @param  ID_TEXT $root The root gallery (the gallery we are considering as an ad hoc root, to allow gallery splitting-up)
     * @param  integer $x Position in collection
     * @param  integer $n Total in collection
     * @param  ID_TEXT $current_type The first type being browsed
     * @set image video
     * @param  ID_TEXT $first_type The current type being browsed
     * @set image video
     * @param  ID_TEXT $back_type The type for the back link
     * @set image video
     * @param  ID_TEXT $next_type The type for the next link
     * @set image video
     * @param  BINARY $slideshow If in slideshow
     * @param  BINARY $wide_high If panels hidden
     * @param  integer $start Start position when returning to category
     * @param  integer $max Max to show when returning to category
     * @param  ?ID_TEXT $cat Category (null: not in a category)
     * @param  ID_TEXT $sort Sort order
     * @param  ID_TEXT $sort_backwards Sort order for reverse sorting
     * @param  string $sql_suffix_images Select clause query suffix for images
     * @param  string $sql_suffix_videos Select clause query suffix for videos
     * @param  string $image_select Selectcode for limiting images displayed
     * @param  string $video_select Selectcode for limiting videos displayed
     * @return Tempcode The navigation bits
     */
    public function show_nav($category_name, $where, $join, $current_id, $first_id, $back_id, $next_id, $root, $x, $n, $current_type, $first_type, $back_type, $next_type, $slideshow, $wide_high, $start, $max, $cat, $sort, $sort_backwards, $sql_suffix_images, $sql_suffix_videos, $image_select, $video_select)
    {
        if (!is_null($back_id)) {
            $slideshow_previous_url = build_url(array('page' => '_SELF', 'type' => $back_type, 'wide_high' => 1, 'id' => $back_id, 'slideshow' => 1, 'days' => (get_param_string('days', '') == '') ? null : get_param_string('days'), 'sort' => ($sort == get_option('galleries_default_sort_order')) ? null : $sort, 'select' => ($image_select == '*') ? null : $image_select, 'video_select' => ($video_select == '*') ? null : $video_select) + propagate_filtercode(), '_SELF', null, true); // Continues, but as slideshow
            $back_url = build_url(array('page' => '_SELF', 'type' => $back_type, 'id' => $back_id, 'slideshow' => ($slideshow == 0) ? null : $slideshow, 'wide_high' => ($wide_high == 0) ? null : $wide_high, 'days' => (get_param_string('days', '') == '') ? null : get_param_string('days'), 'sort' => ($sort == get_option('galleries_default_sort_order')) ? null : $sort, 'select' => ($image_select == '*') ? null : $image_select, 'video_select' => ($video_select == '*') ? null : $video_select) + propagate_filtercode(), '_SELF', null, true);
        } else {
            $slideshow_previous_url = new Tempcode();
            $back_url = new Tempcode();
        }
        if (!is_null($next_id)) {
            $slideshow_next_url = build_url(array('page' => '_SELF', 'type' => $next_type, 'wide_high' => 1, 'id' => $next_id, 'slideshow' => 1, 'days' => (get_param_string('days', '') == '') ? null : get_param_string('days'), 'sort' => ($sort == get_option('galleries_default_sort_order')) ? null : $sort, 'select' => ($image_select == '*') ? null : $image_select, 'video_select' => ($video_select == '*') ? null : $video_select) + propagate_filtercode(), '_SELF', null, true); // Continues, but as slideshow
            $next_url = build_url(array('page' => '_SELF', 'type' => $next_type, 'id' => $next_id, 'slideshow' => ($slideshow == 0) ? null : $slideshow, 'wide_high' => ($wide_high == 0) ? null : $wide_high, 'days' => (get_param_string('days', '') == '') ? null : get_param_string('days'), 'sort' => ($sort == get_option('galleries_default_sort_order')) ? null : $sort, 'select' => ($image_select == '*') ? null : $image_select, 'video_select' => ($video_select == '*') ? null : $video_select) + propagate_filtercode(), '_SELF', null, true);
        } else {
            $slideshow_next_url = new Tempcode();
            $next_url = new Tempcode();
        }

        // Link to show more. Preserve info about where we were
        $slideshow_url = build_url(array('page' => '_SELF', 'type' => $first_type, 'wide_high' => 1, 'id' => $first_id, 'slideshow' => 1, 'days' => (get_param_string('days', '') == '') ? null : get_param_string('days'), 'sort' => ($sort == get_option('galleries_default_sort_order')) ? null : $sort, 'select' => ($image_select == '*') ? null : $image_select, 'video_select' => ($video_select == '*') ? null : $video_select) + propagate_filtercode(), '_SELF', null, true);
        $more_url = is_null($cat) ? null : build_url(array('page' => '_SELF', 'type' => 'browse', 'id' => $cat, 'module_start' => ($start == 0) ? null : $start, 'module_max' => ($max == get_default_gallery_max()) ? null : $max, 'days' => (get_param_string('days', '') == '') ? null : get_param_string('days'), 'sort' => ($sort == get_option('galleries_default_sort_order')) ? null : $sort, 'select' => ($image_select == '*') ? null : $image_select, 'video_select' => ($video_select == '*') ? null : $video_select) + propagate_filtercode(), '_SELF');

        // Only one entry?
        if ($n == 1) {
            $slideshow_url = new Tempcode();
        }

        return do_template('GALLERY_NAV', array(
            '_GUID' => 'efd58f97d9c6254bd727125dfabb5a95',
            'X' => integer_format($x),
            'N' => integer_format($n),
            '_X' => strval($x),
            '_N' => strval($n),
            'SLIDESHOW_URL' => $slideshow_url,
            'SLIDESHOW_NEXT_URL' => $slideshow_next_url,
            'SLIDESHOW_PREVIOUS_URL' => $slideshow_previous_url,
            'BACK_URL' => $back_url,
            'NEXT_URL' => $next_url,
            'MORE_URL' => $more_url,
            'CATEGORY_NAME' => $category_name,
            'SLIDESHOW' => ($slideshow == 1),
        ));
    }

    /**
     * Read in the sort order currently active, and a suffix to the select statement to allow it to work. Apply security.
     *
     * @return array A quarter: The sort order, The sort order (for backwards sorting), SQL suffix for select statement for images, SQL suffix for select statement for videos
     */
    public function get_sort_order()
    {
        $sort = get_param_string('sort', get_option('galleries_default_sort_order'));
        if ($sort == 'random ASC') {
            $sort = 'add_date ASC';
        }
        if (($sort != 'fixed_random ASC') && ($sort != 'average_rating DESC') && ($sort != 'average_rating ASC') && ($sort != 'compound_rating DESC') && ($sort != 'compound_rating ASC') && ($sort != 'add_date DESC') && ($sort != 'add_date ASC') && ($sort != 'url DESC') && ($sort != 'url ASC') && ($sort != 'title DESC') && ($sort != 'title ASC')) {
            log_hack_attack_and_exit('ORDERBY_HACK');
        }
        list($_sort, $_dir) = explode(' ', $sort, 2);
        $sort_backwards = $_sort . ' ' . (($_dir == 'ASC') ? 'DESC' : 'ASC');
        if (($sort == 'compound_rating ASC') || ($sort == 'compound_rating DESC')) {
            $suffix_images = ',(SELECT SUM(rating-1) FROM ' . get_table_prefix() . 'rating WHERE ' . db_string_equal_to('rating_for_type', 'images') . ' AND rating_for_id=' . db_cast('r.id', 'CHAR') . ') AS compound_rating';
            $suffix_videos = ',(SELECT SUM(rating-1) FROM ' . get_table_prefix() . 'rating WHERE ' . db_string_equal_to('rating_for_type', 'videos') . ' AND rating_for_id=' . db_cast('r.id', 'CHAR') . ') AS compound_rating';
        } elseif (($sort == 'average_rating ASC') || ($sort == 'average_rating DESC')) {
            $suffix_images = ',(SELECT AVG(rating) FROM ' . get_table_prefix() . 'rating WHERE ' . db_string_equal_to('rating_for_type', 'images') . ' AND rating_for_id=' . db_cast('r.id', 'CHAR') . ') AS average_rating';
            $suffix_videos = ',(SELECT AVG(rating) FROM ' . get_table_prefix() . 'rating WHERE ' . db_string_equal_to('rating_for_type', 'videos') . ' AND rating_for_id=' . db_cast('r.id', 'CHAR') . ') AS average_rating';
        } elseif ($sort == 'fixed_random ASC') {
            $suffix_images = ',(' . db_function('MOD', array('r.id', date('d'))) . ') AS fixed_random';
            $suffix_videos = ',(' . db_function('MOD', array('r.id', date('d'))) . ') AS fixed_random';
        } else {
            $suffix_images = '';
            $suffix_videos = '';
        }
        return array($sort, $sort_backwards, $suffix_images, $suffix_videos);
    }
}
