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

require_code('crud_module');

/**
 * Module page class.
 */
class Module_cms_galleries extends Standard_crud_module
{
    public $lang_type = 'IMAGE';
    public $select_name_description = 'DESCRIPTION_IMAGE';
    public $select_name = 'IMAGE';
    public $permissions_require = 'mid';
    public $permissions_cat_require = 'galleries';
    public $permissions_cat_name = 'cat';
    public $user_facing = true;
    public $seo_type = 'image';
    public $upload = 'image';
    public $content_type = 'image';
    public $menu_label = 'GALLERIES';
    public $table = 'images';
    public $supports_mass_delete = true;

    public $donext_type = null;

    /**
     * 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)
    {
        if ($member_id === null) {
            $member_id = get_member();
        }

        $ret = array(
            'browse' => array('MANAGE_GALLERIES', 'menu/rich_content/galleries'),
        );

        if (has_privilege($member_id, 'submit_midrange_content', 'cms_galleries')) {
            $ret += array(
                'add' => array('ADD_IMAGE', 'menu/cms/galleries/add_one_image'),
            );
        }

        if (has_privilege($member_id, 'edit_midrange_content', 'cms_galleries')) {
            $ret += array(
                'edit' => array('EDIT_IMAGE', 'menu/cms/galleries/edit_one_image'),
            );
        }

        if (has_privilege($member_id, 'submit_midrange_content', 'cms_galleries')) {
            $ret += array(
                'add_other' => array('ADD_VIDEO', 'menu/cms/galleries/add_one_video'),
            );
        }

        if (has_privilege($member_id, 'edit_midrange_content', 'cms_galleries')) {
            $ret += array(
                'edit_other' => array('EDIT_VIDEO', 'menu/cms/galleries/edit_one_video'),
            );
        }

        if (has_privilege($member_id, 'mass_import', 'cms_galleries')) {
            $ret['import'] = array('GALLERY_IMPORT', 'menu/_generic_admin/import');
        }

        $this->cat_crud_module = class_exists('Mx_cms_galleries_cat') ? new Mx_cms_galleries_cat() : new Module_cms_galleries_cat();
        $this->alt_crud_module = class_exists('Mx_cms_galleries_alt') ? new Mx_cms_galleries_alt() : new Module_cms_galleries_alt();

        $ret += parent::get_entry_points();

        if ($support_crosslinks) {
            require_code('fields');
            $ret += manage_custom_fields_entry_points('image') + manage_custom_fields_entry_points('video') + manage_custom_fields_entry_points('gallery');
        }

        return $ret;
    }

    /**
     * Find privileges defined as overridable by this module.
     *
     * @return array A map of privileges that are overridable; privilege to 0 or 1. 0 means "not category overridable". 1 means "category overridable".
     */
    public function get_privilege_overrides()
    {
        require_lang('galleries');
        return array(
            'view_private_content' => 0,
            'mass_import' => 0,
            'have_personal_category' => array(0, 'HAVE_PERSONAL_GALLERIES'),
            'submit_cat_midrange_content' => array(0, 'ADD_GALLERY'),
            'edit_own_cat_midrange_content' => array(0, 'EDIT_OWN_GALLERY'),
            'edit_cat_midrange_content' => array(0, 'EDIT_GALLERY'),
            'delete_own_cat_midrange_content' => array(0, 'DELETE_OWN_GALLERY'),
            'delete_cat_midrange_content' => array(0, 'DELETE_GALLERY'),
            'may_download_gallery' => 1,
            'submit_midrange_content' => array(1, 'ADD_MEDIA'),
            'bypass_validation_midrange_content' => array(1, 'BYPASS_VALIDATION_MEDIA'),
            'edit_own_midrange_content' => array(1, 'EDIT_OWN_MEDIA'),
            'edit_midrange_content' => array(1, 'EDIT_MEDIA'),
            'delete_own_midrange_content' => array(1, 'DELETE_OWN_MEDIA'),
            'delete_midrange_content' => array(1, 'DELETE_MEDIA'),
        );
    }

    public $title;

    /**
     * Module pre-run function. Allows us to know metadata for <head> before we start streaming output.
     *
     * @param  boolean $top_level Whether this is running at the top level, prior to having sub-objects called.
     * @param  ?ID_TEXT $type The screen type to consider for metadata purposes (null: read from environment).
     * @return ?Tempcode Tempcode indicating some kind of exceptional output (null: none).
     */
    public function pre_run($top_level = true, $type = null)
    {
        $this->cat_crud_module = class_exists('Mx_cms_galleries_cat') ? new Mx_cms_galleries_cat() : new Module_cms_galleries_cat();
        $this->alt_crud_module = class_exists('Mx_cms_galleries_alt') ? new Mx_cms_galleries_alt() : new Module_cms_galleries_alt();
        $GLOBALS['MODULE_CMS_GALLERIES'] = $this;

        $type = get_param_string('type', 'browse');

        require_lang('galleries');

        inform_non_canonical_parameter('cat');
        inform_non_canonical_parameter('validated');
        inform_non_canonical_parameter('parent_id');

        if ($type == 'import') {
            inform_non_canonical_parameter('member_id');

            breadcrumb_set_parents(array(array('_SELF:_SELF:browse', do_lang_tempcode('MANAGE_GALLERIES'))));
        }

        if ($type == '_import') {
            breadcrumb_set_parents(array(array('_SELF:_SELF:browse', do_lang_tempcode('MANAGE_GALLERIES')), array('_SELF:_SELF:import', do_lang_tempcode('GALLERY_IMPORT'))));
            breadcrumb_set_self(do_lang_tempcode('UPLOAD'));
        }

        if ($type == '__import') {
            $cat = get_param_string('cat');
            breadcrumb_set_parents(array(array('_SELF:_SELF:browse', do_lang_tempcode('MANAGE_GALLERIES')), array('_SELF:_SELF:import', do_lang_tempcode('GALLERY_IMPORT')), array('_SELF:_SELF:_import:name=' . $cat, do_lang_tempcode('DETAILS'))));
            breadcrumb_set_self(do_lang_tempcode('DONE'));
        }

        if (($type == 'orphaned') || ($type == 'orphan_add') || ($type == 'orphan_delete')) {
            $cat = post_param_string('ss_cat');
            breadcrumb_set_parents(array(array('_SELF:_SELF:browse', do_lang_tempcode('MANAGE_GALLERIES')), array('_SELF:_SELF:import', do_lang_tempcode('GALLERY_IMPORT')), array('_SELF:_SELF:_import:name=' . $cat, do_lang_tempcode('DETAILS'))));
            breadcrumb_set_self(do_lang_tempcode('DONE'));
        }

        if ($type == 'add') {
            if (has_privilege(get_member(), 'mass_import', 'cms_galleries')) {
                $cat = get_param_string('cat', null);
                $mass_url = build_url(array('page' => '_SELF', 'type' => 'import', 'cat' => $cat), '_SELF');
                attach_message(do_lang_tempcode('CAN_MASS_UPLOAD', escape_html($mass_url->evaluate())), 'inform', true);
            }
        }

        if ($type == 'import' || $type == '_import' || $type == '__import' || $type == 'orphaned') {
            $this->title = get_screen_title('GALLERY_IMPORT');
        }

        if ($type == 'orphan_add') {
            $this->title = get_screen_title('ADD_IMAGE');
        }

        if ($type == 'orphan_delete') {
            $this->title = get_screen_title('DELETE_IMAGE');
        }

        set_helper_panel_tutorial('tut_galleries');

        return parent::pre_run($top_level);
    }

    /**
     * Standard crud_module run_start.
     *
     * @param  ID_TEXT $type The type of module execution
     * @return Tempcode The output of the run
     */
    public function run_start($type)
    {
        require_code('galleries');
        require_code('galleries2');
        require_code('uploads');
        require_css('galleries');
        require_lang('dearchive');

        $this->alt_crud_module->add_text = new Tempcode();

        $cat = get_param_string('cat', '');
        if ($cat != '') {
            if (strpos($type, '_other') !== false) { // Video
                $remaining = $this->alt_crud_module->check_videos_allowed($cat, true);
                if (!is_null($remaining)) {
                    $this->alt_crud_module->add_text->attach(paragraph(do_lang_tempcode('X_ENTRIES_REMAINING', escape_html(integer_format($remaining)))));
                }
            } elseif (strpos($type, '_category') === false) { // Image
                $remaining = $this->check_images_allowed($cat, true);
                if (!is_null($remaining)) {
                    $this->add_text = paragraph(do_lang_tempcode('X_ENTRIES_REMAINING', escape_html(integer_format($remaining))));
                }
            }
        }
        if (get_option('allow_audio_videos') == '1') {
            $this->alt_crud_module->add_text->attach(paragraph(do_lang_tempcode('ALSO_AUDIO')));
        }

        if ($type == 'add_category') {
            require_javascript('ajax');
            $script = find_script('snippet');
            $this->cat_crud_module->javascript .= "
                    var form=document.getElementById('main_form');
                    form.old_submit=form.onsubmit;
                    form.onsubmit=function() {
                        document.getElementById('submit_button').disabled=true;
                        var url='" . addslashes($script) . "?snippet=exists_gallery&name='+window.encodeURIComponent(form.elements['name'].value);
                        if (!do_ajax_field_test(url))
                        {
                            document.getElementById('submit_button').disabled=false;
                            return false;
                        }
                        document.getElementById('submit_button').disabled=false;
                        if (typeof form.old_submit!='undefined' && form.old_submit) return form.old_submit();
                        return true;
                    };
            ";
        }

        // Decide what to do
        if ($type == 'browse') {
            return $this->browse();
        }
        if ($type == 'import') {
            return $this->import();
        }
        if ($type == '_import') {
            return $this->_import();
        }
        if ($type == '__import') {
            return $this->__import();
        }
        if ($type == 'orphaned') {
            return $this->orphaned();
        }
        if ($type == 'orphan_add') {
            return $this->orphan_add();
        }
        if ($type == 'orphan_delete') {
            return $this->orphan_delete();
        }

        return new Tempcode();
    }

    /**
     * The do-next manager for before content management.
     *
     * @return Tempcode The UI
     */
    public function browse()
    {
        $allow_images = ($GLOBALS['SITE_DB']->query_select_value('galleries', 'COUNT(*)', array('accept_images' => 1)) > 0);
        $allow_videos = ($GLOBALS['SITE_DB']->query_select_value('galleries', 'COUNT(*)', array('accept_videos' => 1)) > 0);

        require_code('templates_donext');
        require_code('fields');
        return do_next_manager(get_screen_title('MANAGE_GALLERIES'), comcode_lang_string('DOC_GALLERIES'),
            array_merge(array(
                has_privilege(get_member(), 'submit_cat_midrange_content', 'cms_galleries') ? array('menu/_generic_admin/add_one_category', array('_SELF', array('type' => 'add_category'), '_SELF'), do_lang('ADD_GALLERY')) : null,
                has_privilege(get_member(), 'edit_own_cat_midrange_content', 'cms_galleries') ? array('menu/_generic_admin/edit_one_category', array('_SELF', array('type' => 'edit_category'), '_SELF'), do_lang('EDIT_GALLERY')) : null,
                has_privilege(get_member(), 'mass_import', 'cms_galleries') ? array('menu/_generic_admin/import', array('_SELF', array('type' => 'import'), '_SELF'), do_lang('GALLERY_IMPORT'), 'DOC_GALLERY_IMPORT') : null,
                (!$allow_images) ? null : (has_privilege(get_member(), 'submit_midrange_content', 'cms_galleries') ? array('menu/cms/galleries/add_one_image', array('_SELF', array('type' => 'add'), '_SELF'), do_lang('ADD_IMAGE')) : null),
                (!$allow_images) ? null : (has_privilege(get_member(), 'edit_own_midrange_content', 'cms_galleries') ? array('menu/cms/galleries/edit_one_image', array('_SELF', array('type' => 'edit'), '_SELF'), do_lang('EDIT_IMAGE')) : null),
                (!$allow_videos) ? null : (has_privilege(get_member(), 'submit_midrange_content', 'cms_galleries') ? array('menu/cms/galleries/add_one_video', array('_SELF', array('type' => 'add_other'), '_SELF'), do_lang('ADD_VIDEO')) : null),
                (!$allow_videos) ? null : (has_privilege(get_member(), 'edit_own_midrange_content', 'cms_galleries') ? array('menu/cms/galleries/edit_one_video', array('_SELF', array('type' => 'edit_other'), '_SELF'), do_lang('EDIT_VIDEO')) : null),
            ), manage_custom_fields_donext_link('image'), manage_custom_fields_donext_link('video'), manage_custom_fields_donext_link('gallery')),
            do_lang('MANAGE_GALLERIES')
        );
    }

    /**
     * The UI to choose a gallery to import into.
     *
     * @return Tempcode The UI
     */
    public function import()
    {
        check_privilege('mass_import');

        $condition = 'only_member_galleries_of_id';
        $member_id = get_param_integer('member_id', -1);
        if ($member_id == -1) {
            $condition = mixed();
            $member_id = null;
        }

        $cat = get_param_string('cat', '');

        $post_url = build_url(array('page' => '_SELF', 'type' => '_import'), '_SELF', null, false, true);

        require_code('form_templates');

        $fields = new Tempcode();

        $fields->attach(form_input_tree_list(do_lang_tempcode('GALLERY'), '', 'name', null, 'choose_gallery', array('must_accept_something' => true, 'purity' => false, 'addable_filter' => true, 'filter' => $condition, 'member_id' => $member_id), true, $cat));

        $submit_name = do_lang_tempcode('GALLERY_IMPORT');

        return do_template('FORM_SCREEN', array('_GUID' => '5213edb75c8d534c121b587c555a3b9a', 'TITLE' => $this->title, 'SKIP_WEBSTANDARDS' => true, 'GET' => true, 'HIDDEN' => '', 'TEXT' => '', 'FIELDS' => $fields, 'URL' => $post_url, 'SUBMIT_ICON' => 'buttons__proceed', 'SUBMIT_NAME' => $submit_name));
    }

    /**
     * The UI to import into a gallery.
     *
     * @return Tempcode The UI
     */
    public function _import()
    {
        $cat = get_param_string('name', 'root');
        check_privilege('mass_import'/*Not currently scoped to categories, array('galleries', $cat)*/);

        if (substr($cat, 0, 7) != 'member_') {
            $test = $GLOBALS['SITE_DB']->query_select_value_if_there('galleries', 'name', array('name' => $cat));
            if (is_null($test)) {
                warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'gallery'));
            }
        }

        require_code('form_templates');
        require_code('files');
        require_lang('trackbacks');

        // To choose to batch import from an attached TAR or ZIP file (ZIP file only supported if ZIP module running on php install)
        $post_url = build_url(array('page' => '_SELF', 'type' => '__import', 'cat' => $cat, 'uploading' => 1, 'redirect' => get_param_string('redirect', null)), '_SELF');
        $fields = new Tempcode();
        $supported = 'tar';
        if ((function_exists('zip_open')) || (get_option('unzip_cmd') != '')) {
            $supported .= ', zip';
        }
        $fields->attach(form_input_upload_multi(do_lang_tempcode('UPLOAD'), do_lang_tempcode('DESCRIPTION_ARCHIVE_MEDIA', escape_html($supported), escape_html(str_replace(',', ', ', get_option('valid_images') . ',' . get_allowed_video_file_types()))), 'file', true, null, null, true, str_replace(' ', '', get_option('valid_images') . ',' . $supported)));
        $fields->attach(form_input_line(do_lang_tempcode('TITLE'), do_lang_tempcode('DESCRIPTION_GALLERY_IMPORT_TITLE'), 'set_title', '', false/*Is multi-upload and may get from EXIF [besides, this is set_title not title] get_option('gallery_media_title_required') == '1'*/));
        $hidden = new Tempcode();
        handle_max_file_size($hidden);
        if (function_exists('imagepng')) {
            if ($this->has_at_least_one_watermark($cat)) {
                $fields->attach(form_input_tick(do_lang_tempcode('WATERMARK'), do_lang_tempcode('DESCRIPTION_WATERMARK'), 'watermark', true));
            }
        }
        // Feedback
        require_code('feedback2');
        $fields->attach(feedback_fields($this->content_type, true, true, true, false, ''));
        // Privacy
        if (addon_installed('content_privacy')) {
            require_code('content_privacy2');
            $fields->attach(get_privacy_form_fields(null));
        }
        // Max upload limit
        require_code('files2');
        $max = floatval(get_max_file_size()) / floatval(1024 * 1024);
        if ($max < 30.0) {
            $config_url = get_upload_limit_config_url();
            $text = paragraph(do_lang_tempcode(is_null($config_url) ? 'MAXIMUM_UPLOAD' : 'MAXIMUM_UPLOAD_STAFF', escape_html(($max > 10.0) ? integer_format(intval($max)) : float_format($max)), escape_html(is_null($config_url) ? '' : $config_url)));
        } else {
            $text = new Tempcode();
        }
        // Show form
        $hidden->attach(form_input_hidden('test', '1'));
        $form = do_template('FORM', array('_GUID' => '05de2bc134ed0b1ab703ac8bfbfabce4', 'TABINDEX' => strval(get_form_field_tabindex()), 'FIELDS' => $fields, 'SUBMIT_ICON' => 'menu___generic_admin__import', 'SUBMIT_NAME' => do_lang_tempcode('BATCH_IMPORT_ARCHIVE_CONTENTS'), 'URL' => $post_url, 'TEXT' => $text, 'HIDDEN' => $hidden));

        // Orphaned upload form
        // To choose to batch import what already exists in gallery directory, but is orphaned
        $orphaned_content = new Tempcode();
        if (($GLOBALS['FORUM_DRIVER']->is_staff(get_member())) && ($GLOBALS['SITE_DB']->query_select_value('images', 'COUNT(*)') + $GLOBALS['SITE_DB']->query_select_value('videos', 'COUNT(*)') < 4000)) {
            require_code('images');
            $there = array();
            $_dir = opendir(get_custom_file_base() . '/uploads/galleries/');
            while (false !== ($file = readdir($_dir))) {
                if ((!should_ignore_file($file, IGNORE_ACCESS_CONTROLLERS)) && (!is_dir(get_custom_file_base() . '/uploads/galleries/' . $file)) && ((is_image($file)) || (is_video($file, has_privilege(get_member(), 'comcode_dangerous'))))) {
                    $there[$file] = filemtime(get_custom_file_base() . '/uploads/galleries/' . $file);
                }
            }
            closedir($_dir);
            $_dir = @opendir(get_custom_file_base() . '/uploads/galleries/' . filter_naughty($cat));
            if ($_dir !== false) {
                while (false !== ($file = readdir($_dir))) {
                    if ((!should_ignore_file($file, IGNORE_ACCESS_CONTROLLERS)) && (!is_dir(get_custom_file_base() . '/uploads/galleries/' . $cat . '/' . $file)) && ((is_image($file)) || (is_video($file, has_privilege(get_member(), 'comcode_dangerous'))))) {
                        $there[$cat . '/' . $file] = filemtime(get_custom_file_base() . '/uploads/galleries/' . $cat . '/' . $file);
                    }
                }
                closedir($_dir);
            }
            if (count($there) != 0) {
                asort($there);
                $test1 = collapse_1d_complexity('url', $GLOBALS['SITE_DB']->query_select('images', array('url')));
                $test2 = collapse_1d_complexity('url', $GLOBALS['SITE_DB']->query_select('videos', array('url')));
                arsort($there);
                $temp_keys = array_keys($there);
                $last_time = $there[$temp_keys[0]];
                foreach ($there as $file => $time) {
                    if (!is_string($file)) {
                        $file = strval($file);
                    }

                    if ((!in_array(cms_rawurlrecode('uploads/galleries/' . str_replace('%2F', '/', rawurlencode($file))), $test1)) && (!in_array(cms_rawurlrecode('uploads/galleries/' . str_replace('%2F', '/', rawurlencode($file))), $test2))) {
                        $orphaned_content->attach(form_input_list_entry($file, ($time >= $last_time - 60 * 60 * 3) || (strpos($file, '/') !== false), $file));
                    }
                }
            }
            $add_url = build_url(array('page' => '_SELF', 'type' => 'orphaned'), '_SELF');
            if (!$orphaned_content->is_empty()) {
                $fields_2 = new Tempcode();
                $hidden_2 = new Tempcode();

                $hidden_2->attach(form_input_hidden('ss_cat', $cat));
                $fields_2->attach(form_input_multi_list(do_lang_tempcode('ENTRIES'), '', 'ss_files', $orphaned_content));
                if ($this->has_at_least_one_watermark($cat)) {
                    $fields_2->attach(form_input_various_ticks(array(array(do_lang_tempcode('WATERMARK'), 'ss_watermark', true, '')), '', null, do_lang_tempcode('OPTIONS')));
                }
                $radios = new Tempcode();
                $radios->attach(form_input_radio_entry('type', 'orphan_add', true, do_lang_tempcode('ADD_GALLERY_SELECTION')));
                $radios->attach(form_input_radio_entry('type', 'orphan_delete', false, do_lang_tempcode('DELETE_SELECTION')));
                $fields_2->attach(form_input_radio(do_lang_tempcode('ACTION'), '', 'type', $radios, true));
                // Feedback
                require_code('feedback2');
                $fields_2->attach(feedback_fields($this->content_type, true, true, true, false, '', null, false, true, true, 'ss_'));

                $form2 = do_template('FORM', array('_GUID' => '79c9fd4f29197460f08443bf2ffdf8b2', 'SECONDARY_FORM' => true, 'TABINDEX' => strval(get_form_field_tabindex()), 'FIELDS' => $fields_2, 'SUBMIT_ICON' => 'menu___generic_admin__import', 'SUBMIT_NAME' => do_lang_tempcode('PROCEED'), 'URL' => $add_url, 'TEXT' => '', 'HIDDEN' => $hidden_2));
            } else {
                $form2 = new Tempcode();
            }
        } else {
            $form2 = new Tempcode();
        }

        return do_template('GALLERY_IMPORT_SCREEN', array('_GUID' => '607c819ff751268294e5e590a0d41533', 'TITLE' => $this->title, 'FORM2' => $form2, 'FORM' => $form));
    }

    /**
     * The actualiser to import a zip/tar into a gallery.
     *
     * @return Tempcode The UI
     */
    public function __import()
    {
        if (php_function_allowed('set_time_limit')) {
            @set_time_limit(1000);
        }

        $cat = get_param_string('cat');

        require_code('images');
        require_code('files');
        require_code('files2');

        check_privilege('mass_import'/*Not currently scoped to categories, array('galleries', $cat)*/);

        post_param_string('test'); // To pick up on max file size exceeded errors

        make_member_gallery_if_needed($cat);

        if ((!is_plupload(true)) && ((!array_key_exists('file_1', $_FILES)) || (!is_uploaded_file($_FILES['file_1']['tmp_name'])))) {
            warn_exit(do_lang_tempcode('NO_PARAMETER_SENT', 'file_1'));
        }

        $media_imported = array();

        foreach ($_FILES as $attach_name => $__file) {
            $tmp_name = $__file['tmp_name'];
            $file = $__file['name'];
            if ($file == '') {
                continue; // Not filled in this one
            }

            switch (get_file_extension($file)) {
                case 'zip':
                    if ((!function_exists('zip_open')) && (get_option('unzip_cmd') == '')) {
                        warn_exit(do_lang_tempcode('ZIP_NOT_ENABLED'));
                    }
                    if (!function_exists('zip_open')) {
                        require_code('m_zip');
                        $mzip = true;
                    } else {
                        $mzip = false;
                    }
                    $myfile = zip_open($tmp_name);
                    if (!is_integer($myfile)) {
                        $directory = array();
                        while (false !== ($entry = zip_read($myfile))) {
                            // Strip off our slash to import right
                            $_file = zip_entry_name($entry);
                            $slash = strrpos($_file, '/');
                            if ($slash === false) {
                                $slash = strrpos($_file, "\\");
                            }
                            if ($slash !== false) {
                                $_file = substr($_file, $slash + 1);
                            }
                            if (empty($_file)) {
                                continue;
                            }

                            $directory[] = array('path' => $_file, 'resource' => $entry);
                        }
                        $this->_sort_media($directory);

                        foreach ($directory as $d) {
                            $entry = $d['resource'];
                            $_file = $d['path'];

                            if (should_ignore_file($_file)) {
                                continue;
                            }

                            if (!check_extension($_file, false, null, true)) {
                                continue;
                            }
                            if (strtolower(substr($_file, -5)) == '.phar') { // TODO: Remove in v11, as check_extension now covers this (only added in case of partial patching of v10)
                                continue;
                            }

                            // Load in file
                            zip_entry_open($myfile, $entry);
                            $tmp_name_2 = cms_tempnam();
                            $myfile2 = fopen($tmp_name_2, 'wb') or intelligent_write_error($tmp_name_2);
                            $more = mixed();
                            do {
                                $more = zip_entry_read($entry);
                                if ($more !== false) {
                                    if (fwrite($myfile2, $more) < strlen($more)) {
                                        warn_exit(do_lang_tempcode('COULD_NOT_SAVE_FILE', escape_html($tmp_name_2)));
                                    }
                                }
                            } while (($more !== false) && ($more != ''));
                            fclose($myfile2);

                            if ((is_image($_file)) || (is_video($_file, has_privilege(get_member(), 'comcode_dangerous')))) {
                                $ret = $this->store_from_archive($_file, $tmp_name_2, $cat);
                                if (!is_null($ret)) {
                                    $media_imported[] = $ret;
                                }
                            }

                            zip_entry_close($entry);
                        }

                        zip_close($myfile);
                    } else {
                        require_code('failure');
                        warn_exit(zip_error($myfile, $mzip));
                    }
                    break;
                case 'tar':
                    require_code('tar');
                    $myfile = tar_open($tmp_name, 'rb');
                    if ($myfile !== false) {
                        $directory = tar_get_directory($myfile);
                        $this->_sort_media($directory);

                        foreach ($directory as $entry) {
                            if (should_ignore_file($entry['path'])) {
                                continue;
                            }

                            if (!check_extension($entry['path'], false, null, true)) {
                                continue;
                            }
                            if (strtolower(substr($entry['path'], -5)) == '.phar') { // TODO: Remove in v11, as check_extension now covers this (only added in case of partial patching of v10)
                                continue;
                            }

                            $tmp_name_2 = cms_tempnam();

                            // Load in file
                            $_in = tar_get_file($myfile, $entry['path'], false, $tmp_name_2);

                            // Strip off our slash to import right
                            $_file = $entry['path'];
                            $slash = strrpos($_file, '/');
                            if ($slash === false) {
                                $slash = strrpos($_file, "\\");
                            }
                            if ($slash !== false) {
                                $_file = substr($_file, $slash + 1);
                            }

                            if ((is_image($_file)) || (is_video($_file, has_privilege(get_member(), 'comcode_dangerous')))) {
                                $ret = $this->store_from_archive($_file, $tmp_name_2, $cat);
                                if (!is_null($ret)) {
                                    $media_imported[] = $ret;
                                }
                            }
                            unset($_in);
                        }

                        tar_close($myfile);
                    }
                    break;
                default:
                    if (!check_extension($file, false, null, true)) {
                        break;
                    }
                    if (strtolower(substr($file, -5)) == '.phar') { // TODO: Remove in v11, as check_extension now covers this (only added in case of partial patching of v10)
                        break;
                    }

                    if ((is_image($file)) || (is_video($file, has_privilege(get_member(), 'comcode_dangerous')))) {
                        $tmp_name_2 = cms_tempnam();

                        if ($__file['type'] != 'plupload') {
                            $test = @move_uploaded_file($tmp_name, $tmp_name_2);
                        } else {
                            $test = @copy($tmp_name, $tmp_name_2); // We could rename, but it would hurt integrity of refreshes
                        }

                        $ret = $this->store_from_archive($file, $tmp_name_2, $cat);
                        if (!is_null($ret)) {
                            $media_imported[] = $ret;
                        }
                    } else {
                        attach_message(do_lang_tempcode('BAD_ARCHIVE_FORMAT'), 'warn');
                    }
            }
        }

        $set_title = post_param_string('set_title', '');
        if ($set_title != '') {
            foreach ($media_imported as $i => $media_file) {
                list($media_type, $media_id) = $media_file;
                if (count($media_imported) == 1) {
                    $media_title = $set_title;
                } else {
                    $media_title = do_lang('MEDIA_FILE_IN_SET', $set_title, integer_format($i + 1), integer_format(count($media_imported)));
                }
                $lang_value = $GLOBALS['SITE_DB']->query_select_value($media_type . 's', 'title', array('id' => $media_id));
                $GLOBALS['SITE_DB']->query_update($media_type . 's', lang_remap('title', $lang_value, $media_title), array('id' => $media_id), '', 1);
            }
        }

        if (!is_null(get_param_string('redirect', null))) {
            $url = make_string_tempcode(get_param_string('redirect'));
            return redirect_screen($this->title, $url, do_lang_tempcode('SUCCESS'));
        }

        return $this->cat_crud_module->_do_next_manager($this->title, do_lang_tempcode('GALLERY_IMPORT_SUCCESS_EDIT_TITLES_INLINE', escape_html(integer_format(count($media_imported)))), $cat);
    }

    /**
     * Sort a directory of gallery media being imported.
     *
     * @param  array $directory The unsorted media, will be saved by reference
     */
    public function _sort_media(&$directory)
    {
        // See if there is a numbering system to sort by
        $all_are = null;
        foreach ($directory as $entry) {
            $this_are = strtolower(preg_replace('#\d#', '', $entry['path']));
            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($directory, 'path');
        }

        $directory = array_reverse($directory);
    }

    /**
     * The actualiser to simple add/delete orphaned files in a gallery.
     *
     * @return Tempcode The UI
     */
    public function orphaned()
    {
        $cat = post_param_string('ss_cat');

        require_code('images');
        require_code('exif');

        check_privilege('mass_import'/*Not currently scoped to categories, array('galleries', $cat)*/);

        if (!$GLOBALS['FORUM_DRIVER']->is_staff(get_member())) {
            return new Tempcode();
        }

        make_member_gallery_if_needed($cat);

        if (array_key_exists('ss_files', $_POST)) {
            foreach ($_POST['ss_files'] as $file) {
                $url = cms_rawurlrecode('uploads/galleries/' . str_replace('%2F', '/', rawurlencode($file)));
                $thumb_url = cms_rawurlrecode('uploads/galleries_thumbs/' . str_replace('%2F', '/', rawurlencode($file)));
                if (substr($thumb_url, -4, 4) == '.gif') {
                    $thumb_url = substr($thumb_url, 0, strlen($thumb_url) - 4) . '.png';
                }
                if (!is_image($url)) {
                    $ret = get_video_details(get_custom_file_base() . '/uploads/galleries/' . filter_naughty($file), $file, true);
                    if ($ret !== false) {
                        list($width, $height, $length) = $ret;
                        if (is_null($width)) {
                            $width = intval(get_option('default_video_width'));
                        }
                        if (is_null($height)) {
                            $height = intval(get_option('default_video_height'));
                        }
                        if (is_null($length)) {
                            $length = 0;
                        }
                        $exif = url_is_local($url) ? get_exif_data(get_custom_file_base() . '/' . rawurldecode($url), $file) : array();
                        $id = add_video($exif['UserComment'], $cat, '', $url, '', 1, post_param_integer('ss_allow_rating', 0), post_param_integer('ss_allow_reviews', post_param_integer('ss_allow_comments', 0)), post_param_integer('ss_allow_trackbacks', 0), '', $length, $width, $height);
                        store_exif('video', strval($id), $exif);

                        require_code('users2');
                        if ((has_actual_page_access(get_modal_user(), 'galleries')) && (has_category_access(get_modal_user(), 'galleries', $cat))) {
                            $privacy_ok = true;
                            if (addon_installed('content_privacy')) {
                                require_code('content_privacy');
                                $privacy_ok = has_privacy_access('video', strval($id), $GLOBALS['FORUM_DRIVER']->get_guest_id());
                            }
                            if ($privacy_ok) {
                                require_code('activities');
                                syndicate_described_activity('galleries:ACTIVITY_ADD_VIDEO', ($exif['UserComment'] == '') ? basename($url) : $exif['UserComment'], '', '', '_SEARCH:galleries:video:' . strval($id), '', '', 'galleries');
                            }
                        }
                    }
                } else {
                    $ok = true;
                    if (function_exists('imagepng')) {
                        $path = dirname(get_custom_file_base() . '/' . rawurldecode($thumb_url));
                        if (!file_exists($path)) {
                            require_code('files2');
                            make_missing_directory($path);
                        }
                        $ok = convert_image(get_custom_base_url() . '/' . $url, get_custom_file_base() . '/' . rawurldecode($thumb_url), -1, -1, intval(get_option('thumb_width')), false);
                    }
                    if ($ok) {
                        $exif = url_is_local($url) ? get_exif_data(get_custom_file_base() . '/' . rawurldecode($url), $file) : array();

                        // Images cleanup pipeline
                        $maximum_dimension = intval(get_option('maximum_image_size'));
                        $watermark = (post_param_integer('ss_watermark', 0) == 1);
                        $watermarks = $watermark ? find_gallery_watermarks($cat) : null;
                        if (url_is_local($url)) {
                            handle_images_cleanup_pipeline(get_custom_file_base() . '/' . rawurldecode($url), null, IMG_RECOMPRESS_LOSSLESS, $maximum_dimension, $watermarks);
                        }

                        $id = add_image($exif['UserComment'], $cat, '', $url, $thumb_url, 1, post_param_integer('ss_allow_rating', 0), post_param_integer('ss_allow_reviews', post_param_integer('ss_allow_comments', 0)), post_param_integer('ss_allow_trackbacks', 0), '');
                        store_exif('image', strval($id), $exif);

                        require_code('users2');
                        if ((has_actual_page_access(get_modal_user(), 'galleries')) && (has_category_access(get_modal_user(), 'galleries', $cat))) {
                            $privacy_ok = true;
                            if (addon_installed('content_privacy')) {
                                require_code('content_privacy');
                                $privacy_ok = has_privacy_access('image', strval($id), $GLOBALS['FORUM_DRIVER']->get_guest_id());
                            }
                            if ($privacy_ok) {
                                require_code('activities');
                                syndicate_described_activity('galleries:ACTIVITY_ADD_IMAGE', ($exif['UserComment'] == '') ? basename($url) : $exif['UserComment'], '', '', '_SEARCH:galleries:image:' . strval($id), '', '', 'galleries');
                            }
                        }
                    }
                }
            }
        }

        return $this->cat_crud_module->_do_next_manager($this->title, do_lang_tempcode('SUCCESS'), $cat);
    }

    /**
     * Take some data and write it to be a file in the gallery uploads directory, and add it to a gallery.
     *
     * @param  string $file The filename
     * @param  PATH $in Path to data file (will be copied from)
     * @param  ID_TEXT $cat The gallery to add to
     * @param  ?TIME $time Timestamp to use (null: now)
     * @return ?array A pair: The media type, The media ID (null: error)
     */
    public function store_from_archive($file, &$in, $cat, $time = null)
    {
        // Find where to store on server
        //  Hunt with sensible names until we don't get a conflict
        $place = get_custom_file_base() . '/uploads/galleries/' . filter_naughty($file);
        $i = 2;
        $_file = filter_naughty($file);
        while (file_exists($place)) {
            $ext = '.' . get_file_extension($file);
            $_file = basename($file, $ext) . '_' . strval($i) . $ext;
            $place = get_custom_file_base() . '/uploads/galleries/' . $_file;
            $i++;
        }
        if (@file_put_contents($place, '') === false) { // Lock it in ASAP, to stop race conditions
            intelligent_write_error($place);
        }
        sync_file($place);
        $place_thumb = get_custom_file_base() . '/uploads/galleries_thumbs/' . filter_naughty($file);
        $i = 2;
        $_file_thumb = filter_naughty($file);
        while (file_exists($place_thumb)) {
            $ext = '.' . get_file_extension($file);
            $_file_thumb = basename($file, $ext) . '_' . strval($i) . $ext;
            $place_thumb = get_custom_file_base() . '/uploads/galleries_thumbs/' . $_file_thumb;
            $i++;
        }
        if (@file_put_contents($place_thumb, '') === false) { // Lock it in ASAP, to stop race conditions
            intelligent_write_error($place_thumb);
        }
        sync_file($place_thumb);

        // Store on server
        if (@rename($in, $place) === false) {
            intelligent_write_error($place);
        }
        fix_permissions($place);
        sync_file($place);

        $aurl = cms_rawurlrecode('uploads/galleries/' . rawurlencode($_file));
        $thumb_url = cms_rawurlrecode('uploads/galleries_thumbs/' . rawurlencode($_file_thumb));

        // Add to database
        return $this->simple_add($aurl, $thumb_url, $_file, $cat, $time);
    }

    /**
     * The actualiser to simple add an orphan file to a gallery.
     *
     * @return Tempcode The UI
     */
    public function orphan_add()
    {
        $cat = post_param_string('cat');

        check_privilege('mass_import'/*Not currently scoped to categories, array('galleries', $cat)*/);

        make_member_gallery_if_needed($cat);

        foreach ($_POST as $x => $file) {
            if (!is_string($file)) {
                continue;
            }

            if (substr($x, 0, 5) == 'file_') {
                $aurl = cms_rawurlrecode('uploads/galleries/' . rawurlencode($file));

                $place_thumb = get_custom_file_base() . '/uploads/galleries_thumbs/' . $file;
                $i = 2;
                $_file_thumb = $file;
                while (file_exists($place_thumb)) {
                    $ext = '.' . get_file_extension($file);
                    $_file_thumb = basename($file, $ext) . '_' . strval($i) . $ext;
                    $place_thumb = get_custom_file_base() . '/uploads/galleries_thumbs/' . $_file_thumb;
                    $i++;
                }
                if (@file_put_contents($place_thumb, '') === false) { // Lock it in ASAP, to stop race conditions
                    intelligent_write_error($place_thumb);
                }
                sync_file($place_thumb);
                $thumb_url = cms_rawurlrecode('uploads/galleries_thumbs/' . rawurlencode($_file_thumb));

                $this->simple_add($aurl, $thumb_url, $file, $cat);
            }
        }

        // Show it worked / Refresh
        $url = build_url(array('page' => '_SELF', 'type' => '_import', 'name' => $cat), '_SELF');
        return redirect_screen($this->title, $url, do_lang_tempcode('SUCCESS'));
    }

    /**
     * See if a gallery has any watermarks to use, or all galleries.
     *
     * @param  ?ID_TEXT $cat The gallery (null: all the current user has access to)
     * @return boolean Whether watermarks are available
     */
    public function has_at_least_one_watermark($cat = null)
    {
        $where = '';
        if (!is_null($cat)) {
            $where = db_string_equal_to('name', $cat) . ' AND ';
        }
        $where .= '(' . db_string_not_equal_to('watermark_top_left', '');
        $where .= ' OR ' . db_string_not_equal_to('watermark_top_left', '');
        $where .= ' OR ' . db_string_not_equal_to('watermark_top_right', '');
        $where .= ' OR ' . db_string_not_equal_to('watermark_bottom_left', '');
        $where .= ' OR ' . db_string_not_equal_to('watermark_bottom_right', '') . ')';
        $gals = $GLOBALS['SITE_DB']->query('SELECT name FROM ' . get_table_prefix() . 'galleries WHERE ' . $where);
        foreach ($gals as $guy) {
            $cat = $guy['name'];
            if (has_category_access(get_member(), 'galleries', $cat)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Take a file in the gallery uploads directory, and add it to a gallery.
     *
     * @param  URLPATH $url The URL to the file
     * @param  URLPATH $thumb_url The thumb URL to the file
     * @param  string $file The filename
     * @param  ID_TEXT $cat The gallery to add to
     * @param  ?TIME $time Timestamp to use (null: now)
     * @return ?array A pair: The media type, The media ID (null: error)
     */
    public function simple_add($url, $thumb_url, $file, $cat, $time = null)
    {
        require_code('exif');

        if (is_null($time)) {
            $time = time();
        }

        if (substr($thumb_url, -4, 4) == '.gif') {
            $thumb_url = substr($thumb_url, 0, strlen($thumb_url) - 4) . '.png';
        }
        if (!is_image($url)) {
            $ret = url_is_local($url) ? get_video_details(get_custom_file_base() . '/' . rawurldecode($url), $file, true) : false;
            if ($ret !== false) {
                list($width, $height, $length) = $ret;
                if (is_null($width)) {
                    $width = 100;
                }
                if (is_null($height)) {
                    $height = 100;
                }
                if (is_null($length)) {
                    $length = 0;
                }
                $exif = url_is_local($url) ? get_exif_data(get_custom_file_base() . '/' . rawurldecode($url), $file) : array();
                $id = add_video($exif['UserComment'], $cat, '', $url, '', 1, post_param_integer('allow_rating', 0), post_param_integer('allow_reviews', post_param_integer('allow_comments', 0)), post_param_integer('allow_trackbacks', 0), post_param_string('notes', ''), $length, $width, $height, null, $time);
                store_exif('video', strval($id), $exif);
                if (addon_installed('content_privacy')) {
                    require_code('content_privacy2');
                    list($privacy_level, $additional_access) = read_privacy_fields();
                    save_privacy_form_fields('video', strval($id), $privacy_level, $additional_access);
                }

                require_code('users2');
                if ((has_actual_page_access(get_modal_user(), 'galleries')) && (has_category_access(get_modal_user(), 'galleries', $cat))) {
                    $privacy_ok = true;
                    if (addon_installed('content_privacy')) {
                        require_code('content_privacy');
                        $privacy_ok = has_privacy_access('video', strval($id), $GLOBALS['FORUM_DRIVER']->get_guest_id());
                    }
                    if ($privacy_ok) {
                        require_code('activities');
                        syndicate_described_activity('galleries:ACTIVITY_ADD_VIDEO', ($exif['UserComment'] == '') ? basename($url) : $exif['UserComment'], '', '', '_SEARCH:galleries:video:' . strval($id), '', '', 'galleries');
                    }
                }

                return array('video', $id);
            }
        } else {
            $ok = true;
            if (function_exists('imagepng')) {
                require_code('images');
                $ok = convert_image(get_custom_base_url() . '/' . $url, get_custom_file_base() . '/' . rawurldecode($thumb_url), -1, -1, intval(get_option('thumb_width')), true);
            }
            if ($ok) {
                $exif = url_is_local($url) ? get_exif_data(get_custom_file_base() . '/' . rawurldecode($url), $file) : array();

                // Images cleanup pipeline
                $maximum_dimension = intval(get_option('maximum_image_size'));
                $watermark = (post_param_integer('watermark', 0) == 1);
                $watermarks = $watermark ? find_gallery_watermarks($cat) : null;
                if (url_is_local($url)) {
                    handle_images_cleanup_pipeline(get_custom_file_base() . '/' . rawurldecode($url), null, IMG_RECOMPRESS_LOSSLESS, $maximum_dimension, $watermarks);
                }

                $id = add_image($exif['UserComment'], $cat, '', $url, $thumb_url, 1, post_param_integer('allow_rating', 0), post_param_integer('allow_reviews', post_param_integer('allow_comments', 0)), post_param_integer('allow_trackbacks', 0), post_param_string('notes', ''), null, $time);
                store_exif('image', strval($id), $exif);
                if (addon_installed('content_privacy')) {
                    require_code('content_privacy2');
                    list($privacy_level, $additional_access) = read_privacy_fields();
                    save_privacy_form_fields('image', strval($id), $privacy_level, $additional_access);
                }

                require_code('users2');
                if ((has_actual_page_access(get_modal_user(), 'galleries')) && (has_category_access(get_modal_user(), 'galleries', $cat))) {
                    $privacy_ok = true;
                    if (addon_installed('content_privacy')) {
                        require_code('content_privacy');
                        $privacy_ok = has_privacy_access('image', strval($id), $GLOBALS['FORUM_DRIVER']->get_guest_id());
                    }
                    if ($privacy_ok) {
                        require_code('activities');
                        syndicate_described_activity('galleries:ACTIVITY_ADD_IMAGE', ($exif['UserComment'] == '') ? basename($url) : $exif['UserComment'], '', '', '_SEARCH:galleries:image:' . strval($id), '', '', 'galleries');
                    }
                }

                return array('image', $id);
            }
        }

        return null;
    }

    /**
     * The actualiser to simple delete an orphan file.
     *
     * @return Tempcode The UI
     */
    public function orphan_delete()
    {
        $cat = post_param_string('cat');

        check_privilege('mass_import'/*Not currently scoped to categories, array('galleries', $cat)*/);

        foreach ($_POST as $x => $file) {
            if (!is_string($file)) {
                continue;
            }

            if (substr($x, 0, 5) == 'file_') {
                $path = get_custom_file_base() . '/uploads/galleries/' . filter_naughty($file);
                @unlink($path) or intelligent_write_error($path);
                sync_file($path);
            }
        }

        // Show it worked / Refresh
        $url = build_url(array('page' => '_SELF', 'type' => '_import', 'name' => $cat), '_SELF');
        return redirect_screen($this->title, $url, do_lang_tempcode('SUCCESS'));
    }

    /**
     * Kills Composr if someone is trying to add an image to a gallery that does not support images.
     *
     * @param  ID_TEXT $cat The gallery
     * @param  boolean $tolerate_non_exist Whether to tolerate the non-existence of the gallery
     * @return ?integer Number allowed remaining (null: no limit)
     */
    public function check_images_allowed($cat, $tolerate_non_exist = false)
    {
        // Check this cat allows images
        $gallery = $GLOBALS['SITE_DB']->query_select('galleries', array('accept_videos', 'accept_images', 'is_member_synched'), array('name' => $cat), '', 1);
        $owner = get_member_id_from_gallery_name($cat);
        if (!array_key_exists(0, $gallery)) {
            if ($tolerate_non_exist) {
                $limit = mixed();
                if ((!has_privilege(get_member(), 'no_personal_gallery_limit')) && ($owner == get_member())) {
                    $limit = has_privilege(get_member(), 'high_personal_gallery_limit') ? intval(get_option('max_personal_gallery_images_high')) : intval(get_option('max_personal_gallery_images_low'));
                }
                return $limit;
            }
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'gallery'));
        }
        if (($gallery[0]['accept_images'] == 0) || ($gallery[0]['is_member_synched'] == 1)) {
            warn_exit(do_lang_tempcode(($gallery[0]['accept_images'] == 1) ? 'ERROR_NOT_ACCEPT_CONTAINER' : 'ERROR_NOT_ACCEPT_IMAGES'));
        }

        if ((!has_privilege(get_member(), 'no_personal_gallery_limit')) && ($owner == get_member())) {
            $limit = has_privilege(get_member(), 'high_personal_gallery_limit') ? intval(get_option('max_personal_gallery_images_high')) : intval(get_option('max_personal_gallery_images_low'));
            $count_already = $GLOBALS['SITE_DB']->query_select_value('images', 'COUNT(*)', array('cat' => $cat));
            if ($count_already >= $limit) {
                warn_exit(do_lang_tempcode('TOO_MANY_GALLERY_IMAGES'));
            }

            return $limit - $count_already;
        }

        return null;
    }

    /**
     * Standard crud_module list function.
     *
     * @return array A triple: The tree field (Tempcode), Search URL, Archive URL
     */
    public function create_selection_list_ajax_tree()
    {
        if ($GLOBALS['SITE_DB']->query_select_value('images', 'COUNT(*)') == 0) {
            inform_exit(do_lang_tempcode('NO_ENTRIES', 'image'));
        }

        $search_url = build_url(array('page' => 'search', 'id' => 'images'), get_module_zone('search'));
        $archive_url = build_url(array('page' => 'galleries'), get_module_zone('galleries'));

        $only_owned = has_privilege(get_member(), 'edit_midrange_content', 'cms_galleries') ? null : get_member();
        $tree = form_input_tree_list(do_lang_tempcode('IMAGE'), do_lang_tempcode('DESCRIPTION_IMAGE'), 'id', null, 'choose_image', array('filter' => 'only_conventional_galleries', 'only_owned' => $only_owned, 'editable_filter' => true), true, null, false, null, has_js() && $this->supports_mass_delete);
        return array($tree, $search_url, $archive_url);
    }

    /**
     * Get Tempcode for an image adding/editing form.
     *
     * @param  ?AUTO_LINK $id The ID of the image (null: new)
     * @param  SHORT_TEXT $title The title
     * @param  ID_TEXT $cat The gallery
     * @param  LONG_TEXT $description The image description
     * @param  URLPATH $url URL to the image
     * @param  URLPATH $thumb_url URL to the image thumbnail
     * @param  BINARY $validated Image validation status
     * @param  ?BINARY $allow_rating Whether rating is allowed (null: decide statistically, based on existing choices)
     * @param  ?SHORT_INTEGER $allow_comments Whether comments are allowed (0=no, 1=yes, 2=review style) (null: decide statistically, based on existing choices)
     * @param  ?BINARY $allow_trackbacks Whether trackbacks are allowed (null: decide statistically, based on existing choices)
     * @param  LONG_TEXT $notes Notes for the image
     * @param  ?array $regions The regions (empty: not region-limited) (null: same as empty)
     * @return array A pair: the Tempcode for the visible fields, and the Tempcode for the hidden fields
     */
    public function get_form_fields($id = null, $title = '', $cat = '', $description = '', $url = '', $thumb_url = '', $validated = 1, $allow_rating = null, $allow_comments = null, $allow_trackbacks = null, $notes = '', $regions = null)
    {
        list($allow_rating, $allow_comments, $allow_trackbacks) = $this->choose_feedback_fields_statistically($allow_rating, $allow_comments, $allow_trackbacks);

        $num_galleries = $GLOBALS['SITE_DB']->query_select_value('galleries', 'COUNT(*)', array('accept_images' => 1));

        $adding = is_null($id);

        if ($adding) {
            $cat = get_param_string('cat', '');
            if ($cat != '') {
                if ($GLOBALS['SITE_DB']->query_select_value_if_there('galleries', 'is_member_synched', array('name' => $cat)) === 1) {
                    $cat = 'member_' . strval(get_member()) . '_' . $cat;
                }
            }
        }

        require_code('images');

        $fields = new Tempcode();
        $hidden = new Tempcode();

        require_code('form_templates');

        if (strpos($cat, '?') !== false) {
            $cat = str_replace('?', strval(get_member()), $cat);
        }
        $filters = array('must_accept_images' => true, 'addable_filter' => true);
        if (substr($cat, 0, 9) != 'download_') {
            $filters['filter'] = 'only_conventional_galleries';
        }

        $fields->attach(form_input_line(do_lang_tempcode('TITLE'), do_lang_tempcode('DESCRIPTION_TITLE'), 'title', $title, get_option('gallery_media_title_required') == '1'));

        $gallery_title = '';
        if ($cat != '') {
            $gallery_title = get_potential_gallery_title($cat);
            if ($gallery_title === null) {
                $gallery_title = '';
            }
        } else {
            if ($num_galleries == 1 && $GLOBALS['SITE_DB']->query_select_value('galleries', 'is_member_synched', array('accept_images' => 1)) == 0) {
                $cat = 'root'; // Only option!
            }
        }
        if (((get_value('no_confirm_url_spec_cats') !== '1') && (($id !== null) || (substr($cat, 0, 9) != 'download_'))) || ($cat == '')) {
            $root_cat = get_value('root_cat__images', null, true);
            if ((!is_null($root_cat)) && (preg_match('#^[' . URL_CONTENT_REGEXP . ']+$#', $root_cat) == 0)) {
                $filters['filter'] = $root_cat;
                $root_cat = '';
            }
            if (empty($root_cat) && $GLOBALS['SITE_DB']->query_select_value('galleries', 'accept_images', array('name' => 'root')) == 0) {
                $root_cat = 'root'; // Don't show 'root' itself
            }
            if ($num_galleries == 1 && $GLOBALS['SITE_DB']->query_select_value('galleries', 'is_member_synched', array('accept_images' => 1)) == 0) {
                $cat = $GLOBALS['SITE_DB']->query_select_value('galleries', 'name', array('accept_images' => 1));
                $hidden->attach(form_input_hidden('cat', $cat));
            } else {
                $fields->attach(form_input_tree_list(do_lang_tempcode('GALLERY'), do_lang_tempcode('DESCRIPTION_GALLERY'), 'cat', $root_cat, 'choose_gallery', $filters, true, $cat, false, null, false, $gallery_title));
            }
        } else {
            $hidden->attach(form_input_hidden('cat', $cat));
        }

        $fields->attach(form_input_upload_multi_source(do_lang_tempcode('IMAGE'), '', $hidden, 'image', null, true, $url));

        if (!function_exists('imagetypes')) {
            $thumb_width = get_option('thumb_width');
            $fields->attach(form_input_upload_multi_source(do_lang_tempcode('THUMBNAIL'), do_lang_tempcode('DESCRIPTION_THUMBNAIL', escape_html($thumb_width)), $hidden, 'image__thumb', null, true, $thumb_url));
        }
        $fields->attach(form_input_text_comcode(do_lang_tempcode('DESCRIPTION'), do_lang_tempcode('DESCRIPTION_DESCRIPTION_ACCESSIBILITY'), 'description', $description, false));
        if ($validated == 0) {
            $validated = get_param_integer('validated', 0);
            if (($validated == 1) && (addon_installed('unvalidated'))) {
                attach_message(do_lang_tempcode('WILL_BE_VALIDATED_WHEN_SAVING'));
            }
        }
        if (has_some_cat_privilege(get_member(), 'bypass_validation_' . $this->permissions_require . 'range_content', null, $this->permissions_cat_require)) {
            $fields->attach(form_input_tick(do_lang_tempcode('VALIDATED'), do_lang_tempcode($GLOBALS['FORUM_DRIVER']->is_super_admin(get_member()) ? 'DESCRIPTION_VALIDATED_SIMPLE' : 'DESCRIPTION_VALIDATED', 'image'), 'validated', $validated == 1));
        }

        $do_watermark = ($this->has_at_least_one_watermark($cat)) && (function_exists('imagetypes'));
        $do_rep_image = ((get_option('gallery_rep_image') == '1') && (($cat == '') || (has_edit_permission('cat_mid', get_member(), get_member_id_from_gallery_name($cat), 'cms_galleries', array('galleries', $cat)))));
        if (($do_watermark) || ($do_rep_image)) {
            $fields->attach(do_template('FORM_SCREEN_FIELD_SPACER', array('_GUID' => '971e7db21c3b9d2c8cfbd6a910711514', 'SECTION_HIDDEN' => true, 'TITLE' => do_lang_tempcode('ADVANCED'))));
            if ($do_watermark) {
                $fields->attach(form_input_tick(do_lang_tempcode('WATERMARK'), do_lang_tempcode('DESCRIPTION_WATERMARK'), 'watermark', true)); // Only applies to new uploads, so can't be watermarked twice
            }
            if ($do_rep_image) {
                $fields->attach(form_input_tick(do_lang_tempcode('REPRESENTATIVE_IMAGE'), do_lang_tempcode('_DESCRIPTION_REPRESENTATIVE_IMAGE'), 'rep_image', false));
            }
        }

        if (get_option('filter_regions') == '1') {
            require_code('locations');
            $fields->attach(form_input_regions($regions));
        }

        // Metadata
        require_code('seo2');
        $seo_fields = seo_get_fields($this->seo_type, is_null($id) ? null : strval($id), false);
        if (get_option('gallery_feedback_fields') == '1') {
            require_code('feedback2');
            $feedback_fields = feedback_fields($this->content_type, $allow_rating == 1, $allow_comments == 1, $allow_trackbacks == 1, false, $notes, $allow_comments == 2, false, true, false);
        } else {
            $hidden->attach(form_input_hidden('allow_rating', strval($allow_rating)));
            $hidden->attach(form_input_hidden('allow_comments', strval($allow_comments)));
            $hidden->attach(form_input_hidden('allow_trackbacks', strval($allow_trackbacks)));
            $feedback_fields = new Tempcode();
        }
        $fields->attach(metadata_get_fields('image', is_null($id) ? null : strval($id), false, null, ($seo_fields->is_empty() && $feedback_fields->is_empty()) ? METADATA_HEADER_YES : METADATA_HEADER_FORCE));
        $fields->attach($seo_fields);
        $fields->attach($feedback_fields);

        if (addon_installed('content_privacy')) {
            require_code('content_privacy2');
            if (is_null($id)) {
                $fields->attach(get_privacy_form_fields('image'));
            } else {
                $fields->attach(get_privacy_form_fields('image', strval($id)));
            }
        }

        if (addon_installed('content_reviews')) {
            $fields->attach(content_review_get_fields('image', is_null($id) ? null : strval($id)));
        }

        return array($fields, $hidden);
    }

    /**
     * Standard crud_module submitter getter.
     *
     * @param  AUTO_LINK $id The entry for which the submitter is sought
     * @return array The submitter, and the time of submission (null submission time implies no known submission time)
     */
    public function get_submitter($id)
    {
        $rows = $GLOBALS['SITE_DB']->query_select('images', array('submitter', 'add_date'), array('id' => intval($id)), '', 1);
        if (!array_key_exists(0, $rows)) {
            return array(null, null);
        }
        return array($rows[0]['submitter'], $rows[0]['add_date']);
    }

    /**
     * Standard crud_module cat getter.
     *
     * @param  ID_TEXT $id The entry for which the cat is sought
     * @return mixed The cat
     */
    public function get_cat($id)
    {
        $temp = $GLOBALS['SITE_DB']->query_select_value_if_there('images', 'cat', array('id' => intval($id)));
        if (is_null($temp)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'image'));
        }
        return $temp;
    }

    /**
     * Standard crud_module edit form filler.
     *
     * @param  ID_TEXT $_id The entry being edited
     * @return array A tuple of lots of info
     */
    public function fill_in_edit_form($_id)
    {
        $id = intval($_id);

        $rows = $GLOBALS['SITE_DB']->query_select('images', array('*'), array('id' => $id), '', 1);
        if (!array_key_exists(0, $rows)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'image'));
        }
        $myrow = $rows[0];
        $description = get_translated_text($myrow['description']);
        $cat = $myrow['cat'];
        $validated = $myrow['validated'];

        $delete_fields = mixed();
        if (get_option('cleanup_files') == '1') {
            if (has_delete_permission('mid', get_member(), $myrow['submitter'], 'cms_galleries', array('galleries', $cat))) {
                if ($GLOBALS['FORUM_DRIVER']->is_staff(get_member())) {
                    $radios = form_input_radio_entry('delete', '0', true, do_lang_tempcode('LEAVE'));
                    $radios->attach(form_input_radio_entry('delete', '1', false, do_lang_tempcode('DELETE_PARTIAL')));
                    $radios->attach(form_input_radio_entry('delete', '2', false, do_lang_tempcode('DELETE_FULL')));
                    $delete_fields = form_input_radio(do_lang_tempcode('DELETE_STATUS'), do_lang_tempcode('DESCRIPTION_DELETE_STATUS'), 'delete', $radios);
                } else {
                    $delete_fields = form_input_tick(do_lang_tempcode('DELETE'), do_lang_tempcode('DESCRIPTION_DELETE'), 'delete', false);
                }
            } else {
                $delete_fields = new Tempcode();
            }
        }

        $regions = collapse_1d_complexity('region', $GLOBALS['SITE_DB']->query_select('content_regions', array('region'), array('content_type' => 'image', 'content_id' => strval($id))));

        $ret = $this->get_form_fields($id, get_translated_text($myrow['title']), $cat, $description, $myrow['url'], $myrow['thumb_url'], $validated, $myrow['allow_rating'], $myrow['allow_comments'], $myrow['allow_trackbacks'], $myrow['notes'], $regions);

        $ret[2] = $delete_fields;
        $ret[3] = '';
        $ret[4] = !is_null($delete_fields);
        return $ret;
    }

    /**
     * Standard crud_module add actualiser.
     *
     * @return ID_TEXT The ID of the entry added
     */
    public function add_actualisation()
    {
        $title = post_param_string('title', '');
        $description = post_param_string('description', '');

        $cat = post_param_string('cat');

        if (can_submit_to_gallery($cat) === false) {
            access_denied('SUBMIT_HERE');
        }
        make_member_gallery_if_needed($cat);
        $this->check_images_allowed($cat);

        $validated = post_param_integer('validated', 0);
        $allow_rating = post_param_integer('allow_rating', 0);
        $allow_comments = post_param_integer('allow_comments', 0);
        $notes = post_param_string('notes', '');
        $allow_trackbacks = post_param_integer('allow_trackbacks', 0);

        // Images cleanup pipeline
        $maximum_dimension = intval(get_option('maximum_image_size'));
        $watermark = (post_param_integer('watermark', 0) == 1);
        $watermarks = $watermark ? find_gallery_watermarks($cat) : null;
        set_images_cleanup_pipeline_settings(IMG_RECOMPRESS_LOSSLESS, $maximum_dimension, $watermarks, get_value('keep_gallery_gps', '0') == '0');

        $filename = '';
        $thumb_url = '';
        require_code('themes2');
        $url = post_param_image('image', 'uploads/galleries', null, true, false, $filename, $thumb_url);

        reset_images_cleanup_pipeline_settings();

        require_code('upload_syndication');
        $url = handle_upload_syndication('image__upload', $title, $description, $url, $filename, false);

        $this->donext_type = $cat;

        $metadata = actual_metadata_get_fields('image', null);

        $regions = isset($_POST['regions']) ? $_POST['regions'] : array();

        $id = add_image($title, $cat, $description, $url, $thumb_url, $validated, $allow_rating, $allow_comments, $allow_trackbacks, $notes, $metadata['submitter'], $metadata['add_time'], $metadata['edit_time'], $metadata['views'], null, '', '', $regions);

        set_url_moniker('image', strval($id));

        if (addon_installed('content_privacy')) {
            require_code('content_privacy2');
            list($privacy_level, $additional_access) = read_privacy_fields();
            save_privacy_form_fields('image', strval($id), $privacy_level, $additional_access);
        }

        if (($validated == 1) || (!addon_installed('unvalidated'))) {
            require_code('users2');
            if ((has_actual_page_access(get_modal_user(), 'galleries')) && (has_category_access(get_modal_user(), 'galleries', $cat))) {
                $privacy_ok = true;
                if (addon_installed('content_privacy')) {
                    require_code('content_privacy');
                    $privacy_ok = has_privacy_access('image', strval($id), $GLOBALS['FORUM_DRIVER']->get_guest_id());
                }
                if ($privacy_ok) {
                    require_code('activities');
                    syndicate_described_activity('galleries:ACTIVITY_ADD_IMAGE', ($title == '') ? $filename : $title, '', '', '_SEARCH:galleries:image:' . strval($id), '', '', 'galleries');
                }
            }
        }

        if ((has_edit_permission('cat_mid', get_member(), get_member_id_from_gallery_name($cat), 'cms_galleries', array('galleries', $cat))) && (post_param_integer('rep_image', 0) == 1)) {
            $GLOBALS['SITE_DB']->query_update('galleries', array('rep_image' => $thumb_url), array('name' => $cat), '', 1);
        }

        if (addon_installed('content_reviews')) {
            content_review_set('image', strval($id));
        }

        return strval($id);
    }

    /**
     * Standard crud_module edit actualiser.
     *
     * @param  ID_TEXT $_id The entry being edited
     */
    public function edit_actualisation($_id)
    {
        $id = intval($_id);

        $title = post_param_string('title');
        $description = post_param_string('description', STRING_MAGIC_NULL);

        $cat = post_param_string('cat', STRING_MAGIC_NULL);
        if ($cat != STRING_MAGIC_NULL) {
            if (can_submit_to_gallery($cat) === false) {
                access_denied('SUBMIT_HERE');
            }

            make_member_gallery_if_needed($cat);
            $this->check_images_allowed($cat);
        }

        $validated = post_param_integer('validated', fractional_edit() ? INTEGER_MAGIC_NULL : 0);

        // Images cleanup pipeline
        $maximum_dimension = intval(get_option('maximum_image_size'));
        $watermark = (post_param_integer('watermark', 0) == 1);
        $watermarks = $watermark ? find_gallery_watermarks($cat) : null;
        set_images_cleanup_pipeline_settings(IMG_RECOMPRESS_LOSSLESS, $maximum_dimension, $watermarks, get_value('keep_gallery_gps', '0') == '0');

        if (!fractional_edit()) {
            $filename = '';
            $thumb_url = '';
            require_code('themes2');
            $url = post_param_image('image', 'uploads/galleries', null, true, true, $filename, $thumb_url);

            require_code('upload_syndication');
            $url = handle_upload_syndication('image__upload', $title, $description, $url, $filename, false);
        } else {
            $url = STRING_MAGIC_NULL;
            $thumb_url = STRING_MAGIC_NULL;
        }

        reset_images_cleanup_pipeline_settings();

        $allow_rating = post_param_integer('allow_rating', fractional_edit() ? INTEGER_MAGIC_NULL : 0);
        $allow_comments = post_param_integer('allow_comments', fractional_edit() ? INTEGER_MAGIC_NULL : 0);
        $notes = post_param_string('notes', fractional_edit() ? STRING_MAGIC_NULL : '');
        $allow_trackbacks = post_param_integer('allow_trackbacks', fractional_edit() ? INTEGER_MAGIC_NULL : 0);

        $this->donext_type = $cat;

        if (addon_installed('content_privacy')) {
            require_code('content_privacy2');
            list($privacy_level, $additional_access) = read_privacy_fields();
            save_privacy_form_fields('image', strval($id), $privacy_level, $additional_access);
        }

        if (($validated == 1) && ($GLOBALS['SITE_DB']->query_select_value_if_there('images', 'validated', array('id' => $id)) === 0)) { // Just became validated, syndicate as just added
            $submitter = $GLOBALS['SITE_DB']->query_select_value('images', 'submitter', array('id' => $id));

            require_code('users2');
            if ((has_actual_page_access(get_modal_user(), 'galleries')) && (has_category_access(get_modal_user(), 'galleries', $cat))) {
                $privacy_ok = true;
                if (addon_installed('content_privacy')) {
                    require_code('content_privacy');
                    $privacy_ok = has_privacy_access('image', strval($id), $GLOBALS['FORUM_DRIVER']->get_guest_id());
                }
                if ($privacy_ok) {
                    require_code('activities');
                    syndicate_described_activity(($submitter != get_member()) ? 'galleries:ACTIVITY_VALIDATE_IMAGE' : 'galleries:ACTIVITY_ADD_IMAGE', ($title == '') ? $filename : $title, '', '', '_SEARCH:galleries:image:' . strval($id), '', '', 'galleries', 1, null/*$submitter*/);
                }
            }
        }

        $metadata = actual_metadata_get_fields('image', strval($id));

        $regions = isset($_POST['regions']) ? $_POST['regions'] : array();

        edit_image($id, $title, $cat, $description, $url, $thumb_url, $validated, $allow_rating, $allow_comments, $allow_trackbacks, $notes, post_param_string('meta_keywords', ''), post_param_string('meta_description', ''), $metadata['edit_time'], $metadata['add_time'], $metadata['views'], $metadata['submitter'], $regions, true);

        if ((!fractional_edit()) && (has_edit_permission('cat_mid', get_member(), get_member_id_from_gallery_name($cat), 'cms_galleries', array('galleries', $cat))) && (post_param_integer('rep_image', 0) == 1)) {
            $GLOBALS['SITE_DB']->query_update('galleries', array('rep_image' => $thumb_url), array('name' => $cat), '', 1);
        }

        if (addon_installed('content_reviews')) {
            content_review_set('image', strval($id));
        }
    }

    /**
     * Standard crud_module delete actualiser.
     *
     * @param  ID_TEXT $_id The entry being deleted
     */
    public function delete_actualisation($_id)
    {
        $id = intval($_id);

        $delete_status = post_param_string('delete', '1');

        $cat = post_param_string('cat', null);
        if (!is_null($cat)) {
            $this->donext_type = $cat;
        }

        delete_image($id, ($delete_status == '2') || (get_option('cleanup_files') == '1'));

        if (addon_installed('content_privacy')) {
            require_code('content_privacy2');
            delete_privacy_form_fields('image', strval($id));
        }
    }

    /**
     * The do-next manager for after content management.
     *
     * @param  Tempcode $title The title (output of get_screen_title)
     * @param  Tempcode $description Some description to show, saying what happened
     * @param  ?AUTO_LINK $id The ID of whatever was just handled (null: N/A)
     * @return Tempcode The UI
     */
    public function do_next_manager($title, $description, $id)
    {
        return $this->cat_crud_module->_do_next_manager($title, $description, $this->donext_type, is_null($id) ? null : intval($id));
    }
}

/**
 * Module page class.
 */
class Module_cms_galleries_alt extends Standard_crud_module
{
    public $lang_type = 'VIDEO';
    public $select_name = 'NAME';
    public $permissions_require = 'mid';
    public $permissions_cat_require = 'galleries';
    public $permissions_cat_name = 'cat';
    public $user_facing = true;
    public $seo_type = 'video';
    public $upload = 'file';
    public $javascript = '';
    public $content_type = 'video';
    public $menu_label = 'GALLERIES';
    public $table = 'videos';
    public $supports_mass_delete = true;
    public $is_chained_with_parent_browse = true;

    public $donext_type = null;

    /**
     * Kills Composr if someone is trying to add a video to a gallery that does not support videos.
     *
     * @param  ID_TEXT $cat The gallery
     * @param  boolean $tolerate_non_exist Whether to tolerate the non-existence of the gallery
     * @return ?integer Number allowed remaining (null: no limit)
     */
    public function check_videos_allowed($cat, $tolerate_non_exist = false)
    {
        // Check this cat allows images
        $gallery = $GLOBALS['SITE_DB']->query_select('galleries', array('accept_videos', 'accept_images', 'is_member_synched'), array('name' => $cat), '', 1);
        $owner = get_member_id_from_gallery_name($cat);
        if (!array_key_exists(0, $gallery)) {
            if ($tolerate_non_exist) {
                $limit = mixed();
                if ((!has_privilege(get_member(), 'no_personal_gallery_limit')) && ($owner == get_member())) {
                    $limit = has_privilege(get_member(), 'high_personal_gallery_limit') ? intval(get_option('max_personal_gallery_videos_high')) : intval(get_option('max_personal_gallery_videos_low'));
                }
                return $limit;
            }
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'gallery'));
        }
        if (($gallery[0]['accept_videos'] == 0) || ($gallery[0]['is_member_synched'] == 1)) {
            warn_exit(do_lang_tempcode(($gallery[0]['accept_videos'] == 1) ? 'ERROR_NOT_ACCEPT_CONTAINER' : 'ERROR_NOT_ACCEPT_VIDEOS'));
        }

        if ((!has_privilege(get_member(), 'no_personal_gallery_limit')) && ($owner == get_member())) {
            $limit = has_privilege(get_member(), 'high_personal_gallery_limit') ? intval(get_option('max_personal_gallery_videos_high')) : intval(get_option('max_personal_gallery_videos_low'));
            $count_already = $GLOBALS['SITE_DB']->query_select_value('videos', 'COUNT(*)', array('cat' => $cat));
            if ($count_already >= $limit) {
                warn_exit(do_lang_tempcode('TOO_MANY_GALLERY_VIDEOS'));
            }

            return $limit - $count_already;
        }

        return null;
    }

    /**
     * Extract video meta info from any uploaded video.
     *
     * @return array A triplet of 3 "?integer"'s: video width, video height, video length
     */
    public function get_special_video_info()
    {
        $video_length = post_param_integer('video_length', 0);
        $video_width = post_param_integer('video_width', 0);
        $video_height = post_param_integer('video_height', 0);
        if (($video_width == 0) || ($video_height == 0) || ($video_length == 0)) {
            if (((is_plupload(true)) && (array_key_exists('video__upload', $_FILES))) || ((array_key_exists('video__upload', $_FILES)) && (is_uploaded_file($_FILES['video__upload']['tmp_name'])))) {
                $filename = $_FILES['video__upload']['name'];
                list($_video_width, $_video_height, $_video_length) = get_video_details($_FILES['video__upload']['tmp_name'], $filename);
            } else {
                $url = post_param_string('video__url', '');
                if ($url == '') {
                    return array(null, null, null);
                }

                // We should use compliant encoding
                require_code('urls_simplifier');
                $coder_ob = new HarmlessURLCoder(); // TODO: This should move into post_param_string in v11, filter based
                $url = $coder_ob->encode($url);

                $_video_width = null;
                $_video_height = null;

                // Try oEmbed
                require_code('media_renderer');
                require_code('files2');
                $meta_details = get_webpage_meta_details($url);
                require_code('hooks/systems/media_rendering/oembed');
                $oembed_ob = object_factory('Hook_media_rendering_oembed');
                if ($oembed_ob->recognises_mime_type($meta_details['t_mime_type'], $meta_details) || $oembed_ob->recognises_url($url)) {
                    $oembed = $oembed_ob->get_oembed_data_result($url, array('width' => get_option('video_width_setting'), 'height' => get_option('video_height_setting')));
                    if (isset($oembed['width'])) {
                        $_video_width = intval($oembed['width']);
                    }
                    if (isset($oembed['height'])) {
                        $_video_height = intval($oembed['height']);
                    }
                    $_video_length = null;
                }

                $filename = mixed();

                // Try get_video_details
                if (!isset($_video_width)) {
                    $download_test = null;
                    $temp_path = '';
                    if ($url != '') {
                        $temp_path = cms_tempnam();
                        $write_to_file = fopen($temp_path, 'wb');
                        $download_test = http_download_file($url, 1024 * 50, false, false, 'Composr', null, null, null, null, null, $write_to_file);
                        rewind($write_to_file);
                        fclose($write_to_file);
                    }
                    if (!is_null($download_test)) {
                        $filename = is_null($GLOBALS['HTTP_FILENAME']) ? basename(urldecode($url)) : $GLOBALS['HTTP_FILENAME'];
                        list($_video_width, $_video_height, $_video_length) = get_video_details($temp_path, $filename);
                    } else {
                        list($_video_width, $_video_height, $_video_length) = array(null, null, null);
                    }

                    if ($temp_path != '') {
                        @unlink($temp_path);
                    }
                }
            }

            if (!is_null($filename)) {
                if (substr(strtolower($filename), -4) == '.mp3') {
                    $_video_width = 300;
                    $_video_height = 60;
                }
            }

            if ($video_width == 0) {
                $video_width = (is_null($_video_width)) ? intval(get_option('default_video_width')) : $_video_width;
            }
            if ($video_height == 0) {
                $video_height = (is_null($_video_height)) ? intval(get_option('default_video_height')) : $_video_height;
            }
            if ($video_length == 0) {
                $video_length = (is_null($_video_length)) ? 0 : $_video_length;
            }
        }

        return array($video_width, $video_height, $video_length);
    }

    /**
     * Standard crud_module list function.
     *
     * @return array A triple: The tree field (Tempcode), Search URL, Archive URL
     */
    public function create_selection_list_ajax_tree()
    {
        if ($GLOBALS['SITE_DB']->query_select_value('videos', 'COUNT(*)') == 0) {
            inform_exit(do_lang_tempcode('NO_ENTRIES', 'video'));
        }

        $search_url = build_url(array('page' => 'search', 'id' => 'videos'), get_module_zone('search'));
        $archive_url = build_url(array('page' => 'galleries'), get_module_zone('galleries'));

        $only_owned = has_privilege(get_member(), 'edit_midrange_content', 'cms_galleries') ? null : get_member();
        $tree = form_input_tree_list(do_lang_tempcode('VIDEO'), '', 'id', null, 'choose_video', array('only_owned' => $only_owned, 'editable_filter' => true), true, null, false, null, has_js() && $this->supports_mass_delete);
        return array($tree, $search_url, $archive_url);
    }

    /**
     * Get Tempcode for a video adding/editing form.
     *
     * @param  ?AUTO_LINK $id The ID of the video (null: new)
     * @param  SHORT_TEXT $title The title
     * @param  ID_TEXT $cat The gallery
     * @param  LONG_TEXT $description The video description
     * @param  URLPATH $url The URL to the video file (blank: not yet added)
     * @param  URLPATH $thumb_url The URL to the thumbnail
     * @param  BINARY $validated Video validation status
     * @param  ?BINARY $allow_rating Whether rating is allowed (null: decide statistically, based on existing choices)
     * @param  ?SHORT_INTEGER $allow_comments Whether comments are allowed (0=no, 1=yes, 2=review style) (null: decide statistically, based on existing choices)
     * @param  ?BINARY $allow_trackbacks Whether trackbacks are allowed (null: decide statistically, based on existing choices)
     * @param  LONG_TEXT $notes Notes for the video
     * @param  ?integer $video_length The length of the video (null: not yet added, so not yet known)
     * @param  ?integer $video_width The width of the video (null: not yet added, so not yet known)
     * @param  ?integer $video_height The height of the video (null: not yet added, so not yet known)
     * @param  ?array $regions The regions (empty: not region-limited) (null: same as empty)
     * @return array A pair: the Tempcode for the visible fields, and the Tempcode for the hidden fields
     */
    public function get_form_fields($id = null, $title = '', $cat = '', $description = '', $url = '', $thumb_url = '', $validated = 1, $allow_rating = null, $allow_comments = null, $allow_trackbacks = null, $notes = '', $video_length = null, $video_width = null, $video_height = null, $regions = null)
    {
        list($allow_rating, $allow_comments, $allow_trackbacks) = $this->choose_feedback_fields_statistically($allow_rating, $allow_comments, $allow_trackbacks);

        $no_thumb_needed = (get_option('ffmpeg_path') != '') || (class_exists('ffmpeg_movie'));

        $num_galleries = $GLOBALS['SITE_DB']->query_select_value('galleries', 'COUNT(*)', array('accept_videos' => 1));

        if ($cat == '') {
            $cat = get_param_string('cat', '');
            if ($cat != '') {
                if ($GLOBALS['SITE_DB']->query_select_value_if_there('galleries', 'is_member_synched', array('name' => $cat)) === 1) {
                    $cat = 'member_' . strval(get_member()) . '_' . $cat;
                }
            }
        }

        $fields = new Tempcode();
        $hidden = new Tempcode();
        require_code('form_templates');
        handle_max_file_size($hidden);
        if (strpos($cat, '?') !== false) {
            $cat = str_replace('?', strval(get_member()), $cat);
        }
        $filters = array('filter' => 'only_conventional_galleries', 'must_accept_videos' => true, 'addable_filter' => true);

        $fields->attach(form_input_line(do_lang_tempcode('TITLE'), do_lang_tempcode('DESCRIPTION_TITLE'), 'title', $title, get_option('gallery_media_title_required') == '1'));

        $gallery_title = '';
        if ($cat != '') {
            $gallery_title = get_potential_gallery_title($cat);
            if ($gallery_title === null) {
                $gallery_title = '';
            }
        } else {
            if ($num_galleries == 1 && $GLOBALS['SITE_DB']->query_select_value('galleries', 'is_member_synched', array('accept_videos' => 1)) == 0) {
                $cat = 'root'; // Only option!
            }
        }
        if ((get_value('no_confirm_url_spec_cats') !== '1') || ($cat == '')) {
            $root_cat = get_value('root_cat__videos', null, true);
            if ((!is_null($root_cat)) && (preg_match('#^[' . URL_CONTENT_REGEXP . ']+$#', $root_cat) == 0)) {
                $filters['filter'] = $root_cat;
                $root_cat = '';
            }
            if (empty($root_cat) && $GLOBALS['SITE_DB']->query_select_value('galleries', 'accept_videos', array('name' => 'root')) == 0) {
                $root_cat = 'root'; // Don't show 'root' itself
            }
            if ($num_galleries == 1 && $GLOBALS['SITE_DB']->query_select_value('galleries', 'is_member_synched', array('accept_videos' => 1)) == 0) {
                $cat = $GLOBALS['SITE_DB']->query_select_value('galleries', 'name', array('accept_videos' => 1));
                $hidden->attach(form_input_hidden('cat', $cat));
            } else {
                $fields->attach(form_input_tree_list(do_lang_tempcode('GALLERY'), do_lang_tempcode('DESCRIPTION_GALLERY'), 'cat', $root_cat, 'choose_gallery', $filters, true, $cat, false, null, false, $gallery_title));
            }
        } else {
            $hidden->attach(form_input_hidden('cat', $cat));
        }

        $supported = get_allowed_video_file_types();

        $fields->attach(form_input_upload_multi_source(do_lang_tempcode('VIDEO'), '', $hidden, 'video', null, true, $url, false, null, false));

        if ($validated == 0) {
            $validated = get_param_integer('validated', 0);
            if (($validated == 1) && (addon_installed('unvalidated'))) {
                attach_message(do_lang_tempcode('WILL_BE_VALIDATED_WHEN_SAVING'));
            }
        }
        $thumb_width = get_option('thumb_width');
        $validated_field = new Tempcode();
        if (has_some_cat_privilege(get_member(), 'bypass_validation_' . $this->permissions_require . 'range_content', null, $this->permissions_cat_require)) {
            if (addon_installed('unvalidated')) {
                $validated_field = form_input_tick(do_lang_tempcode('VALIDATED'), do_lang_tempcode($GLOBALS['FORUM_DRIVER']->is_super_admin(get_member()) ? 'DESCRIPTION_VALIDATED_SIMPLE' : 'DESCRIPTION_VALIDATED', 'video'), 'validated', $validated == 1);
            }
        }

        $description_field = form_input_text_comcode(do_lang_tempcode('DESCRIPTION'), do_lang_tempcode('DESCRIPTION_DESCRIPTION_ACCESSIBILITY'), 'description', $description, false);
        if ($no_thumb_needed) {
            $fields->attach($description_field);
            $fields->attach($validated_field);
            $temp = do_template('FORM_SCREEN_FIELD_SPACER', array('_GUID' => '9843d9412a6659d06b4667c2fa0f0e45', 'TITLE' => do_lang_tempcode('ADVANCED'), 'SECTION_HIDDEN' => true));
            $fields->attach($temp);
        }

        $thumbnail_required = false;//(!$no_thumb_needed) && (get_option('allow_audio_videos')=='0') && (find_theme_image('video_thumb',true)==''); YouTube won't require one for example
        $fields->attach(form_input_upload_multi_source(do_lang_tempcode('THUMBNAIL'), do_lang_tempcode('_DESCRIPTION_THUMBNAIL', escape_html($thumb_width)), $hidden, 'video__thumb', null, $thumbnail_required, $thumb_url));

        if (!$no_thumb_needed) {
            $fields->attach($description_field);
        }
        $fields->attach(form_input_integer(do_lang_tempcode('VIDEO_LENGTH'), do_lang_tempcode('DESCRIPTION_VIDEO_LENGTH'), 'video_length', $video_length, false));
        $fields->attach(form_input_dimensions(do_lang_tempcode('DIMENSIONS'), do_lang_tempcode('DESCRIPTION_VIDEO_DIMENSIONS'), 'video_width', 'video_height', $video_width, $video_height, false));
        if (!$no_thumb_needed) {
            $fields->attach($validated_field);
        }

        if (get_option('filter_regions') == '1') {
            require_code('locations');
            $fields->attach(form_input_regions($regions));
        }

        // Metadata
        require_code('seo2');
        $seo_fields = seo_get_fields($this->seo_type, is_null($id) ? null : strval($id), false);
        if (get_option('gallery_feedback_fields') == '1') {
            require_code('feedback2');
            $feedback_fields = feedback_fields($this->content_type, $allow_rating == 1, $allow_comments == 1, $allow_trackbacks == 1, false, $notes, $allow_comments == 2, false, true, false);
        } else {
            $hidden->attach(form_input_hidden('allow_rating', strval($allow_rating)));
            $hidden->attach(form_input_hidden('allow_comments', strval($allow_comments)));
            $hidden->attach(form_input_hidden('allow_trackbacks', strval($allow_trackbacks)));
            $feedback_fields = new Tempcode();
        }
        $fields->attach(metadata_get_fields('video', is_null($id) ? null : strval($id), false, null, ($seo_fields->is_empty() && $feedback_fields->is_empty()) ? METADATA_HEADER_YES : METADATA_HEADER_FORCE));
        $fields->attach($seo_fields);
        $fields->attach($feedback_fields);

        if (addon_installed('content_privacy')) {
            require_code('content_privacy2');
            if (is_null($id)) {
                $fields->attach(get_privacy_form_fields('video'));
            } else {
                $fields->attach(get_privacy_form_fields('video', strval($id)));
            }
        }

        if (addon_installed('content_reviews')) {
            $fields->attach(content_review_get_fields('video', is_null($id) ? null : strval($id)));
        }

        return array($fields, $hidden);
    }

    /**
     * Standard crud_module submitter getter.
     *
     * @param  ID_TEXT $id The entry for which the submitter is sought
     * @return array The submitter, and the time of submission (null submission time implies no known submission time)
     */
    public function get_submitter($id)
    {
        $rows = $GLOBALS['SITE_DB']->query_select('videos', array('submitter', 'add_date'), array('id' => intval($id)), '', 1);
        if (!array_key_exists(0, $rows)) {
            return array(null, null);
        }
        return array($rows[0]['submitter'], $rows[0]['add_date']);
    }

    /**
     * Standard crud_module cat getter.
     *
     * @param  ID_TEXT $id The entry for which the cat is sought
     * @return mixed The cat
     */
    public function get_cat($id)
    {
        $temp = $GLOBALS['SITE_DB']->query_select_value_if_there('videos', 'cat', array('id' => intval($id)));
        if (is_null($temp)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'video'));
        }
        return $temp;
    }

    /**
     * Standard crud_module edit form filler.
     *
     * @param  ID_TEXT $_id The entry being edited
     * @return array A tuple of lots of info
     */
    public function fill_in_edit_form($_id)
    {
        $id = intval($_id);

        $rows = $GLOBALS['SITE_DB']->query_select('videos', array('*'), array('id' => $id), '', 1);
        if (!array_key_exists(0, $rows)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'video'));
        }
        $myrow = $rows[0];
        $description = get_translated_text($myrow['description']);
        $url = $myrow['url'];
        $cat = $myrow['cat'];
        $validated = $myrow['validated'];

        $delete_fields = mixed();
        if (get_option('cleanup_files') == '1') {
            if (has_delete_permission('mid', get_member(), $myrow['submitter'], 'cms_galleries', array('galleries', $cat))) {
                if ($GLOBALS['FORUM_DRIVER']->is_staff(get_member())) {
                    $radios = form_input_radio_entry('delete', '0', true, do_lang_tempcode('LEAVE'));
                    $radios->attach(form_input_radio_entry('delete', '1', false, do_lang_tempcode('DELETE_PARTIAL')));
                    $radios->attach(form_input_radio_entry('delete', '2', false, do_lang_tempcode('DELETE_FULL')));
                    $delete_fields = form_input_radio(do_lang_tempcode('DELETE_STATUS'), do_lang_tempcode('DESCRIPTION_DELETE_STATUS'), 'delete', $radios);
                } else {
                    $delete_fields = form_input_tick(do_lang_tempcode('DELETE'), do_lang_tempcode('DESCRIPTION_DELETE'), 'delete', false);
                }
            } else {
                $delete_fields = new Tempcode();
            }
        }

        $regions = collapse_1d_complexity('region', $GLOBALS['SITE_DB']->query_select('content_regions', array('region'), array('content_type' => 'video', 'content_id' => strval($id))));

        $ret = $this->get_form_fields($id, get_translated_text($myrow['title']), $cat, $description, $url, $myrow['thumb_url'], $validated, $myrow['allow_rating'], $myrow['allow_comments'], $myrow['allow_trackbacks'], $myrow['notes'], $myrow['video_length'], $myrow['video_width'], $myrow['video_height'], $regions);

        $ret[2] = $delete_fields;
        $ret[3] = '';
        $ret[4] = !is_null($delete_fields);
        return $ret;
    }

    /**
     * Standard crud_module add actualiser.
     *
     * @return ID_TEXT The ID of the entry added
     */
    public function add_actualisation()
    {
        $title = post_param_string('title');
        $description = post_param_string('description', '');

        $cat = post_param_string('cat');

        if (can_submit_to_gallery($cat) === false) {
            access_denied('SUBMIT_HERE');
        }
        make_member_gallery_if_needed($cat);
        $this->check_videos_allowed($cat);

        $validated = post_param_integer('validated', 0);
        $allow_rating = post_param_integer('allow_rating', 0);
        $allow_comments = post_param_integer('allow_comments', 0);
        $notes = post_param_string('notes', '');
        $allow_trackbacks = post_param_integer('allow_trackbacks', 0);

        list($video_width, $video_height, $video_length) = $this->get_special_video_info();
        if (is_null($video_width)) {
            $video_width = intval(get_option('default_video_width'));
        }
        if (is_null($video_height)) {
            $video_height = intval(get_option('default_video_height'));
        }
        if (is_null($video_length)) {
            $video_length = 0;
        }

        $filename = '';
        $thumb_url = '';
        $url = post_param_multi_source_upload('video', 'uploads/galleries', false, false, $filename, $thumb_url);

        if (($thumb_url == '') && ($url != '')) {
            $thumb_url = create_video_thumb($url);
            if ($thumb_url == '') {
                warn_exit(do_lang_tempcode('IMPROPERLY_FILLED_IN_THUMBNAIL'));
            }
        }

        if (($url == '') || ($thumb_url == '')) {
            warn_exit(do_lang_tempcode('IMPROPERLY_FILLED_IN_UPLOAD'));
        }

        $this->donext_type = $cat;

        $metadata = actual_metadata_get_fields('video', null);

        $regions = isset($_POST['regions']) ? $_POST['regions'] : array();

        $id = add_video($title, $cat, $description, $url, $thumb_url, $validated, $allow_rating, $allow_comments, $allow_trackbacks, $notes, $video_length, $video_width, $video_height, $metadata['submitter'], $metadata['add_time'], $metadata['edit_time'], $metadata['views'], null, '', '', $regions);

        set_url_moniker('video', strval($id));

        if (addon_installed('content_privacy')) {
            require_code('content_privacy2');
            list($privacy_level, $additional_access) = read_privacy_fields();
            save_privacy_form_fields('video', strval($id), $privacy_level, $additional_access);
        }

        if (($validated == 1) || (!addon_installed('unvalidated'))) {
            require_code('users2');
            if ((has_actual_page_access(get_modal_user(), 'galleries')) && (has_category_access(get_modal_user(), 'galleries', $cat))) {
                $privacy_ok = true;
                if (addon_installed('content_privacy')) {
                    require_code('content_privacy');
                    $privacy_ok = has_privacy_access('video', strval($id), $GLOBALS['FORUM_DRIVER']->get_guest_id());
                }
                if ($privacy_ok) {
                    require_code('activities');
                    syndicate_described_activity('galleries:ACTIVITY_ADD_VIDEO', ($title == '') ? $filename : $title, '', '', '_SEARCH:galleries:video:' . strval($id), '', '', 'galleries');
                }
            }
        }

        if (addon_installed('content_reviews')) {
            content_review_set('video', strval($id));
        }

        return strval($id);
    }

    /**
     * Standard crud_module edit actualiser.
     *
     * @param  ID_TEXT $_id The entry being edited
     */
    public function edit_actualisation($_id)
    {
        $id = intval($_id);

        $title = post_param_string('title');
        $description = post_param_string('description', STRING_MAGIC_NULL);

        $cat = post_param_string('cat', STRING_MAGIC_NULL);
        if ($cat != STRING_MAGIC_NULL) {
            if (can_submit_to_gallery($cat) === false) {
                access_denied('SUBMIT_HERE');
            }
            make_member_gallery_if_needed($cat);
            $this->check_videos_allowed($cat);
        }

        $validated = post_param_integer('validated', fractional_edit() ? INTEGER_MAGIC_NULL : 0);

        if (!fractional_edit()) {
            $filename = '';
            $thumb_url = '';
            $url = post_param_multi_source_upload('video', 'uploads/galleries', true, true, $filename, $thumb_url, CMS_UPLOAD_VIDEO);

            if (($thumb_url == '') && ($url != '')) {
                $thumb_url = create_video_thumb($url);
            }

            if ($thumb_url == '') {
                $rows = $GLOBALS['SITE_DB']->query_select('videos', array('url', 'thumb_url'), array('id' => $id), '', 1);
                $thumb_url = $rows[0]['thumb_url'];
            }
            if (($url == '') || ($thumb_url == '')) {
                warn_exit(do_lang_tempcode('IMPROPERLY_FILLED_IN_UPLOAD'));
            }
        } else {
            $url = STRING_MAGIC_NULL;
            $thumb_url = STRING_MAGIC_NULL;
        }

        $allow_rating = post_param_integer('allow_rating', fractional_edit() ? INTEGER_MAGIC_NULL : 0);
        $allow_comments = post_param_integer('allow_comments', fractional_edit() ? INTEGER_MAGIC_NULL : 0);
        $notes = post_param_string('notes', fractional_edit() ? STRING_MAGIC_NULL : '');
        $allow_trackbacks = post_param_integer('allow_trackbacks', fractional_edit() ? INTEGER_MAGIC_NULL : 0);
        if (!fractional_edit()) {
            list($video_width, $video_height, $video_length) = $this->get_special_video_info();
        } else {
            list($video_width, $video_height, $video_length) = array(INTEGER_MAGIC_NULL, INTEGER_MAGIC_NULL, INTEGER_MAGIC_NULL);
        }
        if (is_null($video_width)) {
            $video_width = intval(get_option('default_video_width'));
        }
        if (is_null($video_height)) {
            $video_height = intval(get_option('default_video_height'));
        }
        if (is_null($video_length)) {
            $video_length = 0;
        }

        $this->donext_type = $cat;

        if (addon_installed('content_privacy')) {
            require_code('content_privacy2');
            list($privacy_level, $additional_access) = read_privacy_fields();
            save_privacy_form_fields('video', strval($id), $privacy_level, $additional_access);
        }

        if (($validated == 1) && ($GLOBALS['SITE_DB']->query_select_value_if_there('videos', 'validated', array('id' => $id)) === 0)) { // Just became validated, syndicate as just added
            $submitter = $GLOBALS['SITE_DB']->query_select_value('videos', 'submitter', array('id' => $id));

            require_code('users2');
            if ((has_actual_page_access(get_modal_user(), 'galleries')) && (has_category_access(get_modal_user(), 'galleries', $cat))) {
                $privacy_ok = true;
                if (addon_installed('content_privacy')) {
                    require_code('content_privacy');
                    $privacy_ok = has_privacy_access('video', strval($id), $GLOBALS['FORUM_DRIVER']->get_guest_id());
                }
                if ($privacy_ok) {
                    require_code('activities');
                    syndicate_described_activity(($submitter != get_member()) ? 'galleries:ACTIVITY_VALIDATE_VIDEO' : 'galleries:ACTIVITY_ADD_VIDEO', ($title == '') ? $filename : $title, '', '', '_SEARCH:galleries:video:' . strval($id), '', '', 'galleries', 1, null/*$submitter*/);
                }
            }
        }

        $metadata = actual_metadata_get_fields('video', strval($id));

        $regions = isset($_POST['regions']) ? $_POST['regions'] : array();

        edit_video($id, $title, $cat, $description, $url, $thumb_url, $validated, $allow_rating, $allow_comments, $allow_trackbacks, $notes, $video_length, $video_width, $video_height, post_param_string('meta_keywords', ''), post_param_string('meta_description', ''), $metadata['edit_time'], $metadata['add_time'], $metadata['views'], $metadata['submitter'], $regions, true);

        if (addon_installed('content_reviews')) {
            content_review_set('video', strval($id));
        }
    }

    /**
     * Standard crud_module delete actualiser.
     *
     * @param  ID_TEXT $_id The entry being deleted
     */
    public function delete_actualisation($_id)
    {
        $id = intval($_id);

        $delete_status = post_param_string('delete', '1');

        $cat = post_param_string('cat', null);
        if (!is_null($cat)) {
            $this->donext_type = $cat;
        }

        delete_video($id, ($delete_status == '2') || (get_option('cleanup_files') == '1'));

        if (addon_installed('content_privacy')) {
            require_code('content_privacy2');
            delete_privacy_form_fields('video', strval($id));
        }
    }

    /**
     * The do-next manager for after content management.
     *
     * @param  Tempcode $title The title (output of get_screen_title)
     * @param  Tempcode $description Some description to show, saying what happened
     * @param  ?AUTO_LINK $id The ID of whatever was just handled (null: N/A)
     * @return Tempcode The UI
     */
    public function do_next_manager($title, $description, $id)
    {
        return $GLOBALS['MODULE_CMS_GALLERIES']->cat_crud_module->_do_next_manager($title, $description, $this->donext_type, is_null($id) ? null : intval($id), true);
    }
}

/**
 * Module page class.
 */
class Module_cms_galleries_cat extends Standard_crud_module
{
    public $lang_type = 'GALLERY';
    public $select_name = 'NAME';
    public $permissions_require = 'cat_mid';
    public $permission_module = 'galleries';
    public $seo_type = 'gallery';
    public $upload = 'image';
    public $non_integer_id = true;
    public $content_type = 'gallery';
    public $menu_label = 'GALLERIES';
    public $table = 'galleries';
    public $javascript = "var fn=document.getElementById('fullname'); if (fn) { var form=fn.form; fn.onchange=function() { if ((form.elements['name']) && (form.elements['name'].value=='')) form.elements['name'].value=fn.value.toLowerCase().replace(/[^\w\-\\u0080-\\uFFFF]/g,'_').replace(/\_+\$/,'').substr(0,80); }; }";
    public $is_chained_with_parent_browse = true;

    /**
     * Standard crud_module list function.
     *
     * @return array A triple: The tree field (Tempcode), Search URL, Archive URL
     */
    public function create_selection_list_ajax_tree()
    {
        $purity = true;
        $_member_id = null;

        $search_url = build_url(array('page' => 'search', 'id' => 'galleries'), get_module_zone('search'));
        $archive_url = build_url(array('page' => 'galleries'), get_module_zone('galleries'));

        $tree = form_input_tree_list(do_lang_tempcode('GALLERY'), '', 'id', null, 'choose_gallery', array('purity' => $purity, 'member_id' => $_member_id, 'editable_filter' => true), true);
        return array($tree, $search_url, $archive_url);
    }

    /**
     * Get Tempcode for a gallery adding/editing form.
     *
     * @param  ID_TEXT $name The gallery codename (blank: new)
     * @param  SHORT_TEXT $fullname The full human-readeable name of the gallery
     * @param  LONG_TEXT $description The description of the gallery
     * @param  LONG_TEXT $notes Hidden notes associated with the gallery
     * @param  ID_TEXT $parent_id The parent gallery (blank: no parent)
     * @param  ?BINARY $accept_images Whether images may be put in this gallery (null: work out statistically)
     * @param  ?BINARY $accept_videos Whether videos may be put in this gallery (null: work out statistically)
     * @param  BINARY $is_member_synched Whether the gallery serves as a container for automatically created member galleries
     * @param  ?BINARY $flow_mode_interface Whether the gallery uses the flow mode interface (null: pick statistically based on current usage of other galleries)
     * @param  ?URLPATH $rep_image The representative image of the gallery (null: none)
     * @param  ?URLPATH $watermark_top_left Watermark (null: none)
     * @param  ?URLPATH $watermark_top_right Watermark (null: none)
     * @param  ?URLPATH $watermark_bottom_left Watermark (null: none)
     * @param  ?URLPATH $watermark_bottom_right Watermark (null: none)
     * @param  ?BINARY $allow_rating Whether rating is allowed (null: decide statistically, based on existing choices)
     * @param  ?SHORT_INTEGER $allow_comments Whether comments are allowed (0=no, 1=yes, 2=review style) (null: decide statistically, based on existing choices)
     * @return array A pair: the Tempcode for the visible fields, and the Tempcode for the hidden fields
     */
    public function get_form_fields($name = '', $fullname = '', $description = '', $notes = '', $parent_id = '', $accept_images = null, $accept_videos = null, $is_member_synched = 0, $flow_mode_interface = null, $rep_image = null, $watermark_top_left = null, $watermark_top_right = null, $watermark_bottom_left = null, $watermark_bottom_right = null, $allow_rating = null, $allow_comments = null)
    {
        list($allow_rating, $allow_comments,) = $this->choose_feedback_fields_statistically($allow_rating, $allow_comments, 1);

        require_code('form_templates');

        $accept_images = take_param_int_modeavg($accept_images, 'accept_images', 'galleries', 1);
        $accept_videos = take_param_int_modeavg($accept_videos, 'accept_videos', 'galleries', 1);

        $hidden = new Tempcode();

        if (is_null($flow_mode_interface)) {
            $cnt = $GLOBALS['SITE_DB']->query_select_value('galleries', 'COUNT(*)');
            if ($cnt < 5000) {
                $flow_mode_interface = intval(round($GLOBALS['SITE_DB']->query_select_value('galleries', 'AVG(flow_mode_interface)'))); // Determine default based on what is 'the norm' currently. Sometimes maths is beautiful :)
            } else {
                $flow_mode_interface = intval(round($GLOBALS['SITE_DB']->query_select_value('galleries', 'AVG(flow_mode_interface)', array('parent_id' => 'root')))); // Determine default based on what is 'the norm' currently. Sometimes maths is beautiful :)
            }
        }

        $fields = new Tempcode();
        $fields->attach(form_input_line(do_lang_tempcode('TITLE'), do_lang_tempcode('DESCRIPTION_TITLE'), 'fullname', $fullname, true));
        if ($name != 'root') {
            if (get_option('manual_gallery_codename') == '1') {
                $fields->attach(form_input_codename(do_lang_tempcode('CODENAME'), do_lang_tempcode('DESCRIPTION_CODENAME'), 'name', $name, true));
            } else {
                $hidden->attach(form_input_hidden('name', $name));
            }
        } else {
            $hidden->attach(form_input_hidden('name', 'root'));
        }
        $fields->attach(form_input_text_comcode(do_lang_tempcode('DESCRIPTION'), do_lang_tempcode('DESCRIPTION_DESCRIPTION'), 'description', $description, false));
        if ($parent_id == '') {
            $parent_id = get_param_string('parent_id', '');
        }
        if ($name != 'root') {
            if ((get_option('manual_gallery_parent') == '1') || ($parent_id == '')) {
                $parent_gallery_title = '';
                if ($parent_id != '') {
                    $_parent_gallery_title = $GLOBALS['SITE_DB']->query_select_value_if_there('galleries', 'fullname', array('name' => $parent_id));
                    if (!is_null($_parent_gallery_title)) {
                        $parent_gallery_title = get_translated_text($_parent_gallery_title);
                    }
                }
                $fields->attach(form_input_tree_list(do_lang_tempcode('PARENT'), do_lang_tempcode('DESCRIPTION_PARENT', 'gallery'), 'parent_id', null, 'choose_gallery', array('filter' => 'only_conventional_galleries', 'addable_filter' => true/*FUDGE: A little naughty, but it encodes roughly what we want and doesn't hurt staff; we have separate enable/disable images/videos settings in galleries, so permissions for adding entries can reasonably be a base requirement for permissions for adding categories*/, 'purity' => true), true, $parent_id, false, null, false, $parent_gallery_title));
            } else {
                $hidden->attach(form_input_hidden('parent_id', $parent_id));
            }
        }
        if ((get_option('manual_gallery_media_types') == '1') || ($accept_images == 0) || ($accept_videos == 0)) {
            $fields->attach(form_input_various_ticks(array(array(do_lang_tempcode('ACCEPT_IMAGES'), 'accept_images', $accept_images == 1, do_lang_tempcode('DESCRIPTION_ACCEPT_IMAGES')), array(do_lang_tempcode('ACCEPT_VIDEOS'), 'accept_videos', $accept_videos == 1, do_lang_tempcode('DESCRIPTION_ACCEPT_VIDEOS'))), new Tempcode(), null, do_lang_tempcode('ACCEPTED_MEDIA_TYPES')));
        } else {
            $hidden->attach(form_input_hidden('accept_images', '1'));
            $hidden->attach(form_input_hidden('accept_videos', '1'));
        }
        $gallery_mode_is = get_option('gallery_mode_is');
        if (($name != '') && ($gallery_mode_is != 'choice') && ($flow_mode_interface != (($gallery_mode_is == 'flow') ? 1 : 0))) {
            $gallery_mode_is = 'choice'; // Continue current but allow a choice to change
        }
        if ($gallery_mode_is != 'choice') {
            $hidden->attach(form_input_hidden('flow_mode_interface', ($gallery_mode_is == 'flow') ? '1' : '0'));
        } else {
            $fields->attach(form_input_tick(do_lang_tempcode('FLOW_MODE_INTERFACE'), do_lang_tempcode('DESCRIPTION_FLOW_MODE_INTERFACE'), 'flow_mode_interface', $flow_mode_interface == 1));
        }
        $request_rep_image = (get_option('gallery_rep_image') == '1') || ($rep_image != '');
        $request_member_synced = (get_option('gallery_member_synced') == '1') || ($is_member_synched == 1) || ($name == 'root');
        if ($request_rep_image || $request_member_synced) {
            $fields->attach(do_template('FORM_SCREEN_FIELD_SPACER', array('_GUID' => '94d1f77eb9fdca010cb9d1eac5d19b9b', 'SECTION_HIDDEN' => ($rep_image == '') && ($is_member_synched == 0), 'TITLE' => do_lang_tempcode('ADVANCED'))));
            if ($request_rep_image) {
                $fields->attach(form_input_upload_multi_source(do_lang_tempcode('REPRESENTATIVE_IMAGE'), do_lang_tempcode('DESCRIPTION_REPRESENTATIVE_IMAGE_GALLERY'), $hidden, 'image', null, false, $rep_image));
            }
            if ($request_member_synced) {
                $fields->attach(form_input_tick(do_lang_tempcode('IS_MEMBER_SYNCHED', 'gallery'), do_lang_tempcode('DESCRIPTION_IS_MEMBER_SYNCHED_GALLERY', 'gallery'), 'is_member_synched', $is_member_synched == 1));
            }
        }

        if (get_option('gallery_watermarks') == '1') {
            $fields->attach(do_template('FORM_SCREEN_FIELD_SPACER', array('_GUID' => '555320228b5a1ff1effb8a1bf9c15d8e', 'SECTION_HIDDEN' => is_null($watermark_top_left) && is_null($watermark_top_right) && is_null($watermark_bottom_left) && is_null($watermark_bottom_right), 'TITLE' => do_lang_tempcode('WATERMARKING'))));
            $fields->attach(form_input_upload(do_lang_tempcode('_WATERMARK', do_lang_tempcode('TOP_LEFT')), do_lang_tempcode('_DESCRIPTION_WATERMARK', do_lang_tempcode('TOP_LEFT')), 'watermark_top_left', false, $watermark_top_left, null, true, str_replace(' ', '', get_option('valid_images'))));
            $fields->attach(form_input_upload(do_lang_tempcode('_WATERMARK', do_lang_tempcode('TOP_RIGHT')), do_lang_tempcode('_DESCRIPTION_WATERMARK', do_lang_tempcode('TOP_RIGHT')), 'watermark_top_right', false, $watermark_top_right, null, true, str_replace(' ', '', get_option('valid_images'))));
            $fields->attach(form_input_upload(do_lang_tempcode('_WATERMARK', do_lang_tempcode('BOTTOM_LEFT')), do_lang_tempcode('_DESCRIPTION_WATERMARK', do_lang_tempcode('BOTTOM_LEFT')), 'watermark_bottom_left', false, $watermark_bottom_left, null, true, str_replace(' ', '', get_option('valid_images'))));
            $fields->attach(form_input_upload(do_lang_tempcode('_WATERMARK', do_lang_tempcode('BOTTOM_RIGHT')), do_lang_tempcode('_DESCRIPTION_WATERMARK', do_lang_tempcode('BOTTOM_RIGHT')), 'watermark_bottom_right', false, $watermark_bottom_right, null, true, str_replace(' ', '', get_option('valid_images'))));
        }
        handle_max_file_size($hidden, 'image');

        // Permissions
        if (get_option('gallery_permissions') == '1') {
            $fields->attach($this->get_permission_fields($name, null, ($name == '')));
        } else {
            require_code('permissions2');
            $hidden->attach(get_category_permissions_hidden_on());
        }

        // Metadata
        require_code('seo2');
        $seo_fields = seo_get_fields($this->seo_type, ($name == '') ? null : $name, false);
        if (get_option('gallery_feedback_fields') == '1') {
            require_code('feedback2');
            $feedback_fields = feedback_fields($this->content_type, $allow_rating == 1, $allow_comments == 1, null, false, $notes, $allow_comments == 2, true, true, false);
        } else {
            $hidden->attach(form_input_hidden('allow_rating', strval($allow_rating)));
            $hidden->attach(form_input_hidden('allow_comments', strval($allow_comments)));
            $feedback_fields = new Tempcode();
        }
        $fields->attach(metadata_get_fields('gallery', ($name == '') ? null : $name, true, null, ($seo_fields->is_empty() && $feedback_fields->is_empty()) ? METADATA_HEADER_YES : METADATA_HEADER_FORCE));
        $fields->attach($seo_fields);
        $fields->attach($feedback_fields);

        if (addon_installed('content_reviews')) {
            $fields->attach(content_review_get_fields('gallery', ($name == '') ? null : $name));
        }

        return array($fields, $hidden);
    }

    /**
     * Standard crud_module submitter getter.
     *
     * @param  ID_TEXT $id The entry for which the submitter is sought
     * @return array The submitter, and the time of submission (null submission time implies no known submission time)
     */
    public function get_submitter($id)
    {
        $rows = $GLOBALS['SITE_DB']->query_select('galleries', array('add_date', 'g_owner'), array('name' => $id), '', 1);
        if (!array_key_exists(0, $rows)) {
            return array(get_member_id_from_gallery_name($id), null);
        }
        return array(get_member_id_from_gallery_name($id, $rows[0]), $rows[0]['add_date']);
    }

    /**
     * Standard crud_module edit form filler.
     *
     * @param  ID_TEXT $id The entry being edited
     * @return array A pair: the Tempcode for the visible fields, and the Tempcode for the hidden fields
     */
    public function fill_in_edit_form($id)
    {
        $rows = $GLOBALS['SITE_DB']->query_select('galleries', array('*'), array('name' => $id), '', 1);
        if (!array_key_exists(0, $rows)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'gallery'));
        }
        $myrow = $rows[0];

        return $this->get_form_fields($id, get_translated_text($myrow['fullname']), get_translated_text($myrow['description']), $myrow['notes'], $myrow['parent_id'], $myrow['accept_images'], $myrow['accept_videos'], $myrow['is_member_synched'], $myrow['flow_mode_interface'], $myrow['rep_image'], $myrow['watermark_top_left'], $myrow['watermark_top_right'], $myrow['watermark_bottom_left'], $myrow['watermark_bottom_right'], $myrow['allow_rating'], $myrow['allow_comments']);
    }

    /**
     * Standard crud_module add actualiser.
     *
     * @return ID_TEXT The entry added
     */
    public function add_actualisation()
    {
        $name = post_param_string('name', '');
        $fullname = post_param_string('fullname');
        if ($name == '') {
            $name = preg_replace('#[^' . URL_CONTENT_REGEXP . ']#', '', $fullname);
        }
        $description = post_param_string('description');
        $notes = post_param_string('notes', '');
        $parent_id = post_param_string('parent_id');
        $accept_images = post_param_integer('accept_images', 0);
        $accept_videos = post_param_integer('accept_videos', 0);
        $is_member_synched = post_param_integer('is_member_synched', 0);
        $flow_mode_interface = post_param_integer('flow_mode_interface', 0);

        require_code('themes2');
        $url = resize_rep_image(post_param_image('image', 'uploads/repimages', null, false));

        $watermark_top_left = get_url('', 'watermark_top_left', 'uploads/watermarks', 0, CMS_UPLOAD_IMAGE);
        $watermark_top_right = get_url('', 'watermark_top_right', 'uploads/watermarks', 0, CMS_UPLOAD_IMAGE);
        $watermark_bottom_left = get_url('', 'watermark_bottom_left', 'uploads/watermarks', 0, CMS_UPLOAD_IMAGE);
        $watermark_bottom_right = get_url('', 'watermark_bottom_right', 'uploads/watermarks', 0, CMS_UPLOAD_IMAGE);

        $allow_rating = post_param_integer('allow_rating', 0);
        $allow_comments = post_param_integer('allow_comments', 0);

        $metadata = actual_metadata_get_fields('gallery', null);

        add_gallery($name, $fullname, $description, $notes, $parent_id, $accept_images, $accept_videos, $is_member_synched, $flow_mode_interface, $url, $watermark_top_left[0], $watermark_top_right[0], $watermark_bottom_left[0], $watermark_bottom_right[0], $allow_rating, $allow_comments, false, $metadata['add_time'], $metadata['submitter']);

        set_url_moniker('gallery', $name);

        $this->set_permissions($name);

        if (addon_installed('content_reviews')) {
            content_review_set('gallery', $name);
        }

        return $name;
    }

    /**
     * Standard crud_module edit actualiser.
     *
     * @param  ID_TEXT $id The entry being edited
     */
    public function edit_actualisation($id)
    {
        $name = post_param_string('name', fractional_edit() ? $id : '');
        $fullname = post_param_string('fullname');
        if ($name == '') {
            $name = preg_replace('#[^' . URL_CONTENT_REGEXP . ']#', '', $fullname);
        }
        $parent_id = post_param_string('parent_id', STRING_MAGIC_NULL);
        $accept_images = post_param_integer('accept_images', fractional_edit() ? INTEGER_MAGIC_NULL : 0);
        $accept_videos = post_param_integer('accept_videos', fractional_edit() ? INTEGER_MAGIC_NULL : 0);
        $is_member_synched = post_param_integer('is_member_synched', fractional_edit() ? INTEGER_MAGIC_NULL : 0);
        $flow_mode_interface = post_param_integer('flow_mode_interface', fractional_edit() ? INTEGER_MAGIC_NULL : 0);

        if (!fractional_edit()) {
            require_code('themes2');
            $url = resize_rep_image(post_param_image('image', 'uploads/repimages', null, false, true));

            $watermark_top_left = get_url('', 'watermark_top_left', 'uploads/watermarks', 0, CMS_UPLOAD_IMAGE);
            if (($watermark_top_left[0] == '') && (post_param_integer('watermark_top_left_unlink', 0) != 1)) {
                $watermark_top_left[0] = null;
            }
            $watermark_top_right = get_url('', 'watermark_top_right', 'uploads/watermarks', 0, CMS_UPLOAD_IMAGE);
            if (($watermark_top_right[0] == '') && (post_param_integer('watermark_top_right_unlink', 0) != 1)) {
                $watermark_top_right[0] = null;
            }
            $watermark_bottom_left = get_url('', 'watermark_bottom_left', 'uploads/watermarks', 0, CMS_UPLOAD_IMAGE);
            if (($watermark_bottom_left[0] == '') && (post_param_integer('watermark_bottom_left_unlink', 0) != 1)) {
                $watermark_bottom_left[0] = null;
            }
            $watermark_bottom_right = get_url('', 'watermark_bottom_right', 'uploads/watermarks', 0, CMS_UPLOAD_IMAGE);
            if (($watermark_bottom_right[0] == '') && (post_param_integer('watermark_bottom_right_unlink', 0) != 1)) {
                $watermark_bottom_right[0] = null;
            }
        } else {
            $url = STRING_MAGIC_NULL;
            $watermark_top_left = STRING_MAGIC_NULL;
            $watermark_top_right = STRING_MAGIC_NULL;
            $watermark_bottom_left = STRING_MAGIC_NULL;
            $watermark_bottom_right = STRING_MAGIC_NULL;
        }
        $allow_rating = post_param_integer('allow_rating', fractional_edit() ? INTEGER_MAGIC_NULL : 0);
        $allow_comments = post_param_integer('allow_comments', fractional_edit() ? INTEGER_MAGIC_NULL : 0);

        $metadata = actual_metadata_get_fields('gallery', $id, null, $name);

        edit_gallery(
            $id,
            $name,
            $fullname,
            post_param_string('description', STRING_MAGIC_NULL),
            post_param_string('notes', STRING_MAGIC_NULL),
            $parent_id,
            $accept_images,
            $accept_videos,
            $is_member_synched,
            $flow_mode_interface,
            $url,
            $watermark_top_left[0],
            $watermark_top_right[0],
            $watermark_bottom_left[0],
            $watermark_bottom_right[0],
            post_param_string('meta_keywords', STRING_MAGIC_NULL),
            post_param_string('meta_description', STRING_MAGIC_NULL),
            $allow_rating,
            $allow_comments,
            $metadata['submitter'],
            $metadata['add_time'],
            true
        );

        if ($id != $name) {
            unset($_GET['redirect']);
        }

        $this->new_id = $name;

        if (addon_installed('content_reviews')) {
            content_review_set('gallery', $name, $id);
        }

        if (!fractional_edit()) {
            $this->set_permissions($name);
        }
    }

    /**
     * Standard crud_module delete possibility checker.
     *
     * @param  ID_TEXT $id The entry being potentially deleted
     * @return boolean Whether it may be deleted
     */
    public function may_delete_this($id)
    {
        return $id != 'root';
    }

    /**
     * Standard crud_module delete actualiser.
     *
     * @param  ID_TEXT $id The entry being deleted
     */
    public function delete_actualisation($id)
    {
        delete_gallery($id);
    }

    /**
     * The do-next manager for after download content management (event types only).
     *
     * @param  Tempcode $title The title (output of get_screen_title)
     * @param  Tempcode $description Some description to show, saying what happened
     * @param  ?ID_TEXT $id The ID of whatever was just handled (null: N/A)
     * @return Tempcode The UI
     */
    public function do_next_manager($title, $description, $id)
    {
        return $this->_do_next_manager($title, $description, is_null($id) ? null : $id);
    }

    /**
     * The do-next manager for after image/video content management.
     *
     * @param  Tempcode $title The title (output of get_screen_title)
     * @param  Tempcode $description Some description to show, saying what happened
     * @param  ?ID_TEXT $cat The gallery we were working in (null: deleted)
     * @param  ?AUTO_LINK $id The ID of whatever was just handled (null: N/A)
     * @param  boolean $video Whether we were working with a video
     * @return Tempcode The UI
     */
    public function _do_next_manager($title, $description, $cat = null, $id = null, $video = false)
    {
        $extra = array();
        if (!is_null($cat)) {
            $hooks = find_all_hooks('modules', 'galleries_users');
            foreach (array_keys($hooks) as $hook) {
                require_code('hooks/modules/galleries_users/' . filter_naughty_harsh($hook));
                $object = object_factory('Hook_gu_' . filter_naughty_harsh($hook), true);
                if (is_null($object)) {
                    continue;
                }
                $extra = array_merge($extra, $object->new_donext_icons($cat));
            }
        }

        require_code('templates_donext');

        if ((is_null($id)) && (is_null($cat))) {
            return do_next_manager($title, $description,
                null,
                null,
                /* TYPED-ORDERED LIST OF 'LINKS'    */
                null, // Add one
                null, // Edit this
                null, // Edit one
                null, // View this
                array('galleries', array('type' => 'browse'), get_module_zone('galleries'), do_lang_tempcode('GALLERIES')), // View archive
                null, // Add to category
                has_privilege(get_member(), 'submit_cat_midrange_content', 'cms_galleries') ? array('_SELF', array('type' => 'add_category'), '_SELF', do_lang('ADD_GALLERY')) : null, // Add one category
                has_privilege(get_member(), 'edit_own_cat_midrange_content', 'cms_galleries') ? array('_SELF', array('type' => 'edit_category'), '_SELF', do_lang('EDIT_GALLERY')) : null, // Edit one category
                is_null($cat) ? null : array('_SELF', array('type' => '_edit_category', 'id' => $cat), '_SELF', do_lang_tempcode('EDIT_THIS_GALLERY')), // Edit this category
                is_null($cat) ? null : array('galleries', array('type' => 'browse', 'id' => $cat), get_module_zone('galleries'), do_lang_tempcode('VIEW_THIS_GALLERY')), // View this category
                /* SPECIALLY TYPED 'LINKS' */
                array_merge($extra, array(
                    array('menu/cms/galleries/add_one_image', array('_SELF', array('type' => 'add'), '_SELF'), do_lang('ADD_IMAGE')),
                    array('menu/cms/galleries/add_one_video', array('_SELF', array('type' => 'add_other'), '_SELF'), do_lang('ADD_VIDEO')),
                    has_privilege(get_member(), 'edit_own_midrange_content', 'cms_galleries') ? array('menu/cms/galleries/edit_one_image', array('_SELF', array('type' => 'edit'), '_SELF'), do_lang('EDIT_IMAGE')) : null,
                    has_privilege(get_member(), 'edit_own_midrange_content', 'cms_galleries') ? array('menu/cms/galleries/edit_one_video', array('_SELF', array('type' => 'edit_other'), '_SELF'), do_lang('EDIT_VIDEO')) : null,
                )),
                null,
                null,
                null,
                null,
                null,
                do_lang_tempcode('GALLERIES'),
                $video ? 'video' : 'image',
                'gallery'
            );
        }

        $support_videos = false;
        $support_images = false;
        if (!is_null($cat)) {
            $cat_row = $GLOBALS['SITE_DB']->query_select('galleries', array('accept_images', 'accept_videos', 'is_member_synched'), array('name' => $cat), '', 1);
            if ((array_key_exists(0, $cat_row)) && ($cat_row[0]['is_member_synched'] == 0)) {
                if ($cat_row[0]['accept_videos'] == 1) {
                    $support_videos = true;
                }
                if ($cat_row[0]['accept_images'] == 1) {
                    $support_images = true;
                }
            }
        }

        return do_next_manager($title, $description,
            null,
            null,
            /* TYPED-ORDERED LIST OF 'LINKS'  */
            null, // Add one
            (is_null($id) || (!has_privilege(get_member(), 'edit_own_midrange_content', 'cms_galleries', array('galleries', $cat)))) ? null : array('_SELF', array('type' => $video ? '_edit_other' : '_edit', 'id' => $id), '_SELF'), // Edit this
            null, // Edit one
            is_null($id) ? null : array('galleries', array('type' => $video ? 'video' : 'image', 'id' => $id, 'wide' => 1), get_module_zone('galleries')), // View this
            array('galleries', array('type' => 'browse'), get_module_zone('galleries'), do_lang_tempcode('GALLERIES')), // View archive
            null, // Add to category
            has_privilege(get_member(), 'submit_cat_midrange_content', 'cms_galleries') ? array('_SELF', array('type' => 'add_category'), '_SELF', do_lang('ADD_GALLERY')) : null, // Add one category
            has_privilege(get_member(), 'edit_own_cat_midrange_content', 'cms_galleries') ? array('_SELF', array('type' => 'edit_category'), '_SELF', do_lang('EDIT_GALLERY')) : null, // Edit one category
            has_privilege(get_member(), 'edit_own_cat_midrange_content', 'cms_galleries') ? array('_SELF', array('type' => '_edit_category', 'id' => $cat), '_SELF', do_lang_tempcode('EDIT_THIS_GALLERY')) : null, // Edit this category
            array('galleries', array('type' => 'browse', 'id' => $cat), get_module_zone('galleries'), do_lang_tempcode('VIEW_THIS_GALLERY')), // View this category
            /* SPECIALLY TYPED 'LINKS' */
            array_merge($extra, array(
                array($video ? 'menu/cms/galleries/add_one_video' : 'menu/cms/galleries/add_one_image', array('_SELF', array('type' => $video ? 'add_other' : 'add', 'cat' => ((!$video && !$support_images) || ($video && !$support_videos) || (is_null($cat))) ? null : $cat), '_SELF'), do_lang($video ? 'ADD_VIDEO' : 'ADD_IMAGE')),
                array($video ? 'menu/cms/galleries/add_one_image' : 'menu/cms/galleries/add_one_video', array('_SELF', array('type' => $video ? 'add' : 'add_other', 'cat' => (($video && !$support_images) || (!$video && !$support_videos) || (is_null($cat))) ? null : $cat), '_SELF'), do_lang($video ? 'ADD_IMAGE' : 'ADD_VIDEO')),
                (has_privilege(get_member(), 'edit_own_midrange_content', 'cms_galleries') ? array($video ? 'menu/cms/galleries/edit_one_video' : 'menu/cms/galleries/edit_one_image', array('_SELF', array('type' => $video ? 'edit_other' : 'edit'), '_SELF'), do_lang($video ? 'EDIT_VIDEO' : 'EDIT_IMAGE')) : null), // Edit one
                (has_privilege(get_member(), 'edit_own_midrange_content', 'cms_galleries') ? array($video ? 'menu/cms/galleries/edit_one_image' : 'menu/cms/galleries/edit_one_video', array('_SELF', array('type' => $video ? 'edit' : 'edit_other'), '_SELF'), do_lang($video ? 'EDIT_IMAGE' : 'EDIT_VIDEO')) : null), // Edit one
                has_privilege(get_member(), 'mass_import', 'cms_galleries') ? array('menu/_generic_admin/import', array('_SELF', array('type' => '_import', 'name' => $cat), '_SELF'), do_lang('GALLERY_IMPORT')) : null
            )),
            null,
            null,
            null,
            null,
            null,
            do_lang_tempcode('GALLERIES'),
            $video ? 'video' : 'image',
            'gallery'
        );
    }
}
