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

/**
 * CRUD module (Create/Update/Delete), for operations on content types.
 *
 * @package        core
 */
abstract class Standard_crud_module
{
    public $module_type;
    public $redirect_type = null;
    public $code_require = null;
    public $javascript_require = null;
    public $skip_webstandards = false;
    public $default_type = null;
    public $author = 'Chris Graham';
    public $organisation = 'ocProducts';
    public $user_facing = false;
    public $send_validation_request = true; // Only applies if $user_facing
    public $check_validation = true; // Only applies if $user_facing
    public $archive_entry_point = null;
    public $archive_label = null;
    public $view_entry_point = null;
    public $view_label = null;
    public $protect_first = 0;
    public $privilege_page = null; // Usually just get_page_name()
    public $permission_module = null; // E.g. 'catalogues_catalogue' if we are CRUDing a catalogue
    public $permissions_require = null;  // E.g. 'mid'
    public $permissions_cat_require = null; // E.g. 'catalogues_catalogue' if we are CRUDing a catalogue entry
    public $permissions_cat_name = null; // E.g. 'catalogue_name' if we are CRUDing a catalogue entry
    public $permissions_cat_require_b = null; // E.g. 'catalogues_category' if we are CRUDing a catalogue entry
    public $permissions_cat_name_b = null; // E.g. 'cat'
    public $add_text = '';
    public $edit_text = '';
    public $extra_donext_entries = null;
    public $extra_donext_categories = null;
    public $extra_donext_whatever = null;
    public $extra_donext_whatever_title = null;
    public $do_next_editing_categories = false;
    public $special_edit_frontend = false;
    public $upload = null;
    public $possibly_some_kind_of_upload = false;
    public $cat_crud_module = null; // Allows chaining of a secondary CRUD module on, to listen for cat CRUD (c)
    public $alt_crud_module = null; // Allows chaining of a secondary CRUD module on, to listen for some other CRUD (v)
    public $content_type = null;
    public $posting_form_title = null;
    public $posting_form_text = '';
    public $posting_form_description = '';
    public $posting_form_text_parsed = null;
    public $posting_form_tabindex = null;
    public $javascript = null;
    public $javascript_for_choose = null;
    public $form_id = 'selection_table';
    public $type_code = '';
    public $catalogue = false;
    public $non_integer_id = false;
    public $output_of_action_is_confirmation = false;
    public $second_stage_preview = false;
    public $add_submit_name = null;
    public $edit_submit_name = null;
    public $do_preview = true; // true or null (null means false here)
    public $add_one_label = null;
    public $add_one_cat_label = null;
    public $edit_this_label = null;
    public $edit_this_cat_label = null;
    public $edit_one_label = null;
    public $edit_one_cat_label = null;
    public $entries_title = null;
    public $categories_title = null;
    public $do_next_description = null;
    public $appended_actions_already = false;
    public $select_name_description = null;
    public $select_name = null;
    public $menu_label = null;
    public $care_please = true;
    public $new_id = null;
    public $no_blank_ids = true;
    public $posting_field_required = true;
    public $donext_type = null;
    public $donext_category_id = null;
    public $donext_entry_content_type = null;
    public $donext_category_content_type = null;
    public $cached_entry_rows = null;
    public $cached_max_rows = null;
    public $lang_type = null;
    public $privilege_page_name = null;
    public $edit_keep_validation = false;
    public $supports_mass_delete = false;
    public $title;

    // These only needed if we are generate create_selection_list_entries automatically
    public $table_prefix = '';
    public $array_key = 'id';
    public $title_is_multi_lang = true;
    public $orderer = null;
    public $table = null; // Actually, this is used by choose_feedback_fields_statistically also

    /**
     * Find details of the module.
     *
     * @return ?array Map of module info (null: module is disabled).
     */
    public function info()
    {
        $info = array();
        $info['author'] = $this->author;
        $info['organisation'] = $this->organisation;
        $info['hacked_by'] = null;
        $info['hack_version'] = null;
        $info['version'] = 2;
        $info['locked'] = false;
        return $info;
    }

    /**
     * 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();
        if (method_exists($this, 'add_actualisation')) {
            if (($this->permissions_require === null) || (has_privilege($member_id, 'submit_' . $this->permissions_require . 'range_content', $this->privilege_page_name))) {
                $ret += array(
                    'add' => array('ADD_' . $this->lang_type, 'menu/_generic_admin/add_one'),
                );
            }

            if (($this->permissions_require === null) || (has_privilege($member_id, 'edit_' . $this->permissions_require . 'range_content', $this->privilege_page_name))) {
                $ret += array(
                    'edit' => array('EDIT_' . $this->lang_type, 'menu/_generic_admin/edit_one'),
                );
            }
        }
        if (!is_null($this->cat_crud_module)) {
            if (($this->cat_crud_module->permissions_require === null) || (has_privilege($member_id, 'submit_' . $this->cat_crud_module->permissions_require . 'range_content', $this->cat_crud_module->privilege_page_name))) {
                $ret += array(
                    'add_category' => array('ADD_' . $this->cat_crud_module->lang_type, 'menu/_generic_admin/add_one_category'),
                );
            }

            if (($this->cat_crud_module->permissions_require === null) || (has_privilege($member_id, 'edit_' . $this->cat_crud_module->permissions_require . 'range_content', $this->cat_crud_module->privilege_page_name))) {
                $ret += array(
                    'edit_category' => array('EDIT_' . $this->cat_crud_module->lang_type, 'menu/_generic_admin/edit_one_category'),
                );
            }
        }
        if (!is_null($this->alt_crud_module)) {
            if (($this->alt_crud_module->permissions_require === null) || (has_privilege($member_id, 'submit_' . $this->alt_crud_module->permissions_require . 'range_content', $this->alt_crud_module->privilege_page_name))) {
                $ret += array(
                    'add_other' => array('ADD_' . $this->alt_crud_module->lang_type, 'menu/_generic_admin/add_one'),
                );
            }

            if (($this->alt_crud_module->permissions_require === null) || (has_privilege($member_id, 'edit_' . $this->alt_crud_module->permissions_require . 'range_content', $this->alt_crud_module->privilege_page_name))) {
                $ret += array(
                    'edit_other' => array('EDIT_' . $this->alt_crud_module->lang_type, 'menu/_generic_admin/edit_one'),
                );
            }
        }
        return $ret;
    }

    public $doing;
    public $success_message_str;

    /**
     * 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)
    {
        if (is_null($this->default_type)) {
            $this->default_type = method_exists($this, 'browse') ? 'browse' : 'add';
        }
        if (is_null($type)) {
            $type = get_param_string('type', $this->default_type);
        }

        if ($top_level) {
            // Load lang file if it exists
            $this->module_type = get_class($this);
            if (strtolower(substr($this->module_type, 0, 11)) == 'module_cms_') {
                $this->module_type = substr($this->module_type, 11);
            }
            if (strtolower(substr($this->module_type, 0, 13)) == 'module_admin_') {
                $this->module_type = substr($this->module_type, 13);
            }
            if (strtolower(substr($this->module_type, 0, 7)) == 'module_') {
                $this->module_type = substr($this->module_type, 7);
            }
            if (substr($this->module_type, 0, 4) == 'cns_') {
                $this->module_type = 'f_' . substr($this->module_type, 4);
            }
            if ((file_exists(get_file_base() . '/lang/' . fallback_lang() . '/' . $this->module_type . '.ini'))
                || (file_exists(get_custom_file_base() . '/lang_custom/' . fallback_lang() . '/' . $this->module_type . '.ini'))
            ) {
                require_lang($this->module_type);
            }

            if (!is_null($this->cat_crud_module)) {
                $this->cat_crud_module->type_code = 'category';
            }
            if (!is_null($this->alt_crud_module)) {
                $this->alt_crud_module->type_code = 'other';
            }

            if (($type == 'add_category') || ($type == '_add_category') || ($type == 'edit_category') || ($type == '_edit_category') || ($type == '__edit_category') || ($type == 'add_category') || ($type == '_add_category') || ($type == 'edit_category') || ($type == '_edit_category') || ($type == '__edit_category')) {
                if ($this->cat_crud_module !== null) {
                    $this->cat_crud_module->pre_run(false, $type);
                }
            } elseif (($type == 'add_other') || ($type == '_add_other') || ($type == 'edit_other') || ($type == '_edit_other') || ($type == '__edit_other') || ($type == 'add_catalogue') || ($type == '_add_catalogue') || ($type == 'edit_catalogue') || ($type == '_edit_catalogue') || ($type == '__edit_catalogue')) {
                if ($this->alt_crud_module !== null) {
                    $this->alt_crud_module->pre_run(false, $type);
                }
            } else {
                self::pre_run(false, $type);
            }
        } else {
            if ((((method_exists($this, 'browse')) && ($type != 'browse')) || ((isset($this->is_chained_with_parent_browse)) && ($this->is_chained_with_parent_browse)))) {
                global $BREADCRUMB_SET_PARENTS;
                $has_browse_already = false;
                foreach ($BREADCRUMB_SET_PARENTS as $set_parent) {
                    if ($set_parent[0] == '_SELF:_SELF:browse') {
                        $has_browse_already = true;
                    }
                }
                if (!$has_browse_already) {
                    if (($this->special_edit_frontend) && (($type == '_edit') || ($type == '_edit_category'))) {
                        breadcrumb_set_parents(array_merge(array(array('_SELF:_SELF:browse', do_lang_tempcode(is_null($this->menu_label) ? 'MENU' : $this->menu_label)), array('_SELF:_SELF:' . substr($type, 1), do_lang_tempcode('CHOOSE'))), $BREADCRUMB_SET_PARENTS));
                    } else {
                        if (($this->catalogue) && (either_param_string('catalogue_name', '') != '')) {
                            $_catalogue_title = $GLOBALS['SITE_DB']->query_select_value_if_there('catalogues', 'c_title', array('c_name' => either_param_string('catalogue_name')));
                            if ($_catalogue_title === null) {
                                warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'catalogue'));
                            }
                            $catalogue_title = get_translated_text($_catalogue_title);
                            breadcrumb_set_parents(array_merge(array(array('_SELF:_SELF:browse:catalogue_name=' . either_param_string('catalogue_name', ''), $catalogue_title)), $BREADCRUMB_SET_PARENTS));
                        } else {
                            breadcrumb_set_parents(array_merge(array(array('_SELF:_SELF:browse', do_lang_tempcode(is_null($this->menu_label) ? 'MENU' : $this->menu_label))), $BREADCRUMB_SET_PARENTS));
                        }
                    }
                }
            } else {
                if (($this->special_edit_frontend) && (($type == '_edit') || ($type == '_edit_category'))) {
                    breadcrumb_set_parents(array_merge(array(array('_SELF:_SELF:' . substr($type, 1), do_lang_tempcode('CHOOSE'))), $BREADCRUMB_SET_PARENTS));
                }
            }

            if ($type == '_add' || $type == '_add_other' || $type == '_add_category' || $type == '_add_entry' || $type == '_add_category' || $type == '__edit' || $type == '__edit_other' || $type == '__edit_category' || $type == '__edit_entry' || $type == '__edit_category') {
                breadcrumb_set_self(do_lang_tempcode('DONE'));
            }

            if ($type == 'add' || $type == 'add_other' || $type == 'add_category' || $type == 'add_entry' || $type == 'add_category') {
                if (multi_lang()) {
                    require_code('lang2');
                    $switch_url = get_self_url(false, false, array('keep_lang' => get_site_default_lang()));
                    if ((has_actual_page_access(get_member(), 'admin_lang')) && (user_lang() != get_site_default_lang())) {
                        set_helper_panel_text(do_lang_tempcode('lang:ADDING_CONTENT_IN_LANGUAGE_STAFF', escape_html(lookup_language_full_name(user_lang())), escape_html(lookup_language_full_name(get_site_default_lang())), escape_html($switch_url->evaluate())));
                    } else {
                        set_helper_panel_text(do_lang_tempcode('lang:ADDING_CONTENT_IN_LANGUAGE', escape_html(lookup_language_full_name(user_lang()))));
                    }
                }

                $doing = 'ADD_' . $this->lang_type;

                if (($this->catalogue) && (get_param_string('catalogue_name', '') != '')) {
                    $_catalogue_title = $GLOBALS['SITE_DB']->query_select_value_if_there('catalogues', 'c_title', array('c_name' => get_param_string('catalogue_name')));
                    if (!is_null($_catalogue_title)) {
                        $catalogue_title = get_translated_text($_catalogue_title);
                        if ($this->type_code == '') {
                            $doing = do_lang('CATALOGUE_GENERIC_ADD', escape_html($catalogue_title));
                        } elseif ($this->type_code == 'category') {
                            $doing = do_lang('CATALOGUE_GENERIC_ADD_CATEGORY', escape_html($catalogue_title));
                        }
                    }
                }

                $this->title = get_screen_title($doing);

                $this->doing = $doing;
            }

            if ($type == '_add' || $type == '_add_other' || $type == '_add_category' || $type == '_add_entry' || $type == '_add_category') {
                $doing = 'ADD_' . $this->lang_type;

                $success_message_str = 'SUCCESS_MESSAGE__' . $doing;
                if (get_param_string('catalogue_name', '') != '') {
                    $success_message_str .= '_' . get_param_string('catalogue_name', '');
                }
                if (is_null(do_lang($success_message_str, null, null, null, null, false))) {
                    $success_message_str = 'SUCCESS';
                }

                if (($this->catalogue) && (get_param_string('catalogue_name', '') != '')) {
                    $_catalogue_title = $GLOBALS['SITE_DB']->query_select_value_if_there('catalogues', 'c_title', array('c_name' => get_param_string('catalogue_name')));
                    if (!is_null($_catalogue_title)) {
                        $catalogue_title = get_translated_text($_catalogue_title);
                        if ($this->type_code == '') {
                            $doing = do_lang('CATALOGUE_GENERIC_ADD', escape_html($catalogue_title));
                        } elseif ($this->type_code == 'category') {
                            $doing = do_lang('CATALOGUE_GENERIC_ADD_CATEGORY', escape_html($catalogue_title));
                        }
                    }
                }

                $this->title = get_screen_title($doing);

                breadcrumb_set_parents(array_merge($GLOBALS['BREADCRUMB_SET_PARENTS'], array(array('_SELF:_SELF:' . $this->get_screen_type_for('add', $this->type_code), (strpos($doing, ' ') !== false) ? protect_from_escaping($doing) : do_lang_tempcode($doing)))));

                $this->doing = $doing;
                $this->success_message_str = $success_message_str;
            }

            if ($type == 'edit' || $type == 'edit_other' || $type == 'edit_category' || $type == 'edit_entry' || $type == 'edit_category') {
                $doing = 'EDIT_' . $this->lang_type;

                if (($this->catalogue) && (get_param_string('catalogue_name', '') != '')) {
                    $_catalogue_title = $GLOBALS['SITE_DB']->query_select_value_if_there('catalogues', 'c_title', array('c_name' => get_param_string('catalogue_name')));
                    if (!is_null($_catalogue_title)) {
                        $catalogue_title = get_translated_text($_catalogue_title);
                        if ($this->type_code == '') {
                            $doing = do_lang('CATALOGUE_GENERIC_EDIT', escape_html($catalogue_title));
                        } elseif ($this->type_code == 'category') {
                            $doing = do_lang('CATALOGUE_GENERIC_EDIT_CATEGORY', escape_html($catalogue_title));
                        }
                    }
                }

                $this->title = get_screen_title($doing);

                $this->doing = $doing;
            }

            if ($type == '_edit' || $type == '_edit_other' || $type == '_edit_category' || $type == '_edit_entry' || $type == '_edit_category') {
                $doing = 'EDIT_' . $this->lang_type;

                if ((multi_lang()) && (has_actual_page_access(get_member(), 'admin_lang')) && (user_lang() != get_site_default_lang())) {
                    require_code('lang2');
                    $switch_url = get_self_url(false, false, array('keep_lang' => get_site_default_lang()));
                    set_helper_panel_text(do_lang_tempcode('lang:EDITING_CONTENT_IN_LANGUAGE_STAFF', escape_html(lookup_language_full_name(user_lang())), escape_html(lookup_language_full_name(get_site_default_lang())), escape_html($switch_url->evaluate())));
                }

                if (($this->catalogue) && (get_param_string('catalogue_name', '') != '')) {
                    $_catalogue_title = $GLOBALS['SITE_DB']->query_select_value_if_there('catalogues', 'c_title', array('c_name' => get_param_string('catalogue_name')));
                    if (!is_null($_catalogue_title)) {
                        $catalogue_title = get_translated_text($_catalogue_title);
                        if ($this->type_code == '') {
                            $doing = do_lang('CATALOGUE_GENERIC_EDIT', escape_html($catalogue_title));
                        } elseif ($this->type_code == 'category') {
                            $doing = do_lang('CATALOGUE_GENERIC_EDIT_CATEGORY', escape_html($catalogue_title));
                        }
                    }
                }

                $this->title = get_screen_title($doing);

                $this->doing = $doing;
            }

            if ($type == '__edit' || $type == '__edit_other' || $type == '__edit_category' || $type == '__edit_entry' || $type == '__edit_category') {
                $id = get_param_string('id', '');

                $delete = post_param_integer('delete', 0);
                if (($delete == 1) || ($delete == 2)) { //1=partial,2=full,...=unknown,thus handled as an edit
                    $doing = 'DELETE_' . $this->lang_type;
                    if (($this->catalogue) && (get_param_string('catalogue_name', '') != '')) {
                        $_catalogue_title = $GLOBALS['SITE_DB']->query_select_value_if_there('catalogues', 'c_title', array('c_name' => get_param_string('catalogue_name')));
                        if (!is_null($_catalogue_title)) {
                            $catalogue_title = get_translated_text($_catalogue_title);
                            if ($this->type_code == '') {
                                $doing = do_lang('CATALOGUE_GENERIC_DELETE', escape_html($catalogue_title));
                            } elseif ($this->type_code == 'category') {
                                $doing = do_lang('CATALOGUE_GENERIC_DELETE_CATEGORY', escape_html($catalogue_title));
                            }
                        }
                    }

                    $this->title = get_screen_title($doing);
                } else {
                    $doing = 'EDIT_' . $this->lang_type;

                    if (($this->catalogue) && (get_param_string('catalogue_name', '') != '')) {
                        $_catalogue_title = $GLOBALS['SITE_DB']->query_select_value_if_there('catalogues', 'c_title', array('c_name' => get_param_string('catalogue_name')));
                        if (!is_null($_catalogue_title)) {
                            $catalogue_title = get_translated_text($_catalogue_title);
                            if ($this->type_code == '') {
                                $doing = do_lang('CATALOGUE_GENERIC_EDIT', escape_html($catalogue_title));
                            } elseif ($this->type_code == 'category') {
                                $doing = do_lang('CATALOGUE_GENERIC_EDIT_CATEGORY', escape_html($catalogue_title));
                            }
                        }
                    }

                    $this->title = get_screen_title($doing);
                }

                breadcrumb_set_parents(array_merge($GLOBALS['BREADCRUMB_SET_PARENTS'], array(array('_SELF:_SELF:' . $this->get_screen_type_for('_edit', $this->type_code) . ':' . $id, (strpos($doing, ' ') !== false) ? protect_from_escaping($doing) : do_lang_tempcode($doing)))));

                $success_message_str = 'SUCCESS_MESSAGE__' . $doing;
                if (get_param_string('catalogue_name', '') != '') {
                    $success_message_str .= '_' . get_param_string('catalogue_name', '');
                }
                if (is_null(do_lang($success_message_str, null, null, null, null, false))) {
                    $success_message_str = 'SUCCESS';
                }

                $this->doing = $doing;
                $this->success_message_str = $success_message_str;
            }

            if ($type == 'mass_delete') {
                $this->title = get_screen_title('MASS_DELETE');
            }
        }

        return null;
    }

    /**
     * Execute the module.
     *
     * @return Tempcode The result of execution.
     */
    public function run()
    {
        @ignore_user_abort(true); // Must keep going till completion

        require_code('input_filter_2');
        rescue_shortened_post_request();

        require_code('form_templates');
        require_code('feedback');
        require_code('permissions2');
        require_code('users2');

        if (!is_null($this->code_require)) {
            require_code($this->code_require);
        }
        if (!is_null($this->javascript_require)) {
            require_javascript($this->javascript_require);
        }

        if (is_null($this->default_type)) {
            $this->default_type = method_exists($this, 'browse') ? 'browse' : 'add';
        }
        $type = get_param_string('type', $this->default_type);

        // Metadata etc
        require_code('content2');
        if (addon_installed('content_reviews')) {
            require_code('content_reviews2');
        }

        if (method_exists($this, 'run_start')) {
            $ret = $this->run_start($type);
            if ((!is_null($ret)) && (!$ret->is_empty())) {
                return $ret;
            }
        }

        if (get_value('xhtml_strict') === '1') {
            $this->second_stage_preview = true;
            if (!is_null($this->cat_crud_module)) {
                $this->second_stage_preview = true;
            }
            if (!is_null($this->alt_crud_module)) {
                $this->second_stage_preview = true;
            }
        }

        if (get_option('edit_under') == '0') {
            $this->special_edit_frontend = true;
            if (!is_null($this->cat_crud_module)) {
                $this->special_edit_frontend = true;
            }
            if (!is_null($this->alt_crud_module)) {
                $this->special_edit_frontend = true;
            }
        }

        if ($type == 'add') {
            return $this->add();
        }
        if ($type == '_add') {
            return $this->_add();
        }
        if ($type == 'edit') {
            return $this->edit();
        }
        if ($type == '_edit') {
            return $this->_edit();
        }
        if ($type == '__edit') {
            return $this->__edit();
        }
        if (!is_null($this->cat_crud_module)) {
            if ($type == 'add_category') {
                return $this->cat_crud_module->add();
            }
            if ($type == '_add_category') {
                return $this->cat_crud_module->_add();
            }
            if ($type == 'edit_category') {
                return $this->cat_crud_module->edit();
            }
            if ($type == '_edit_category') {
                return $this->cat_crud_module->_edit();
            }
            if ($type == '__edit_category') {
                return $this->cat_crud_module->__edit();
            }
        }
        if (!is_null($this->alt_crud_module)) {
            if ($type == 'add_other') {
                return $this->alt_crud_module->add();
            }
            if ($type == '_add_other') {
                return $this->alt_crud_module->_add();
            }
            if ($type == 'edit_other') {
                return $this->alt_crud_module->edit();
            }
            if ($type == '_edit_other') {
                return $this->alt_crud_module->_edit();
            }
            if ($type == '__edit_other') {
                return $this->alt_crud_module->__edit();
            }
        }

        // These ones are for catalogues only...
        if ($type == 'add_entry') {
            return $this->add();
        }
        if ($type == '_add_entry') {
            return $this->_add();
        }
        if ($type == 'edit_entry') {
            return $this->edit();
        }
        if ($type == '_edit_entry') {
            return $this->_edit();
        }
        if ($type == '__edit_entry') {
            return $this->__edit();
        }
        if (!is_null($this->cat_crud_module)) {
            if ($type == 'add_category') {
                return $this->cat_crud_module->add();
            }
            if ($type == '_add_category') {
                return $this->cat_crud_module->_add();
            }
            if ($type == 'edit_category') {
                return $this->cat_crud_module->edit();
            }
            if ($type == '_edit_category') {
                return $this->cat_crud_module->_edit();
            }
            if ($type == '__edit_category') {
                return $this->cat_crud_module->__edit();
            }
        }
        if (!is_null($this->alt_crud_module)) {
            if ($type == 'add_catalogue') {
                return $this->alt_crud_module->add();
            }
            if ($type == '_add_catalogue') {
                return $this->alt_crud_module->_add();
            }
            if ($type == 'edit_catalogue') {
                return $this->alt_crud_module->edit();
            }
            if ($type == '_edit_catalogue') {
                return $this->alt_crud_module->_edit();
            }
            if ($type == '__edit_catalogue') {
                return $this->alt_crud_module->__edit();
            }
        }

        // Recursed across all types
        if ($type == 'mass_delete') {
            return $this->mass_delete();
        }

        return new Tempcode();
    }

    /**
     * Work out a screen type code.
     *
     * @param  string $type_code General-purpose screen type code (e.g. 'add')
     * @param  string $stub Type code for current flow (e.g. '' or 'category')
     * @return string Complete screen type code
     */
    private function get_screen_type_for($type_code, $stub)
    {
        if ($stub != '') {
            return $type_code . '_' . $stub;
        }
        return $type_code;
    }

    /**
     * Statistically work out defaults for feedback fields, if not currently set.
     *
     * @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)
     * @return array Array of all input parameters, converted
     */
    public function choose_feedback_fields_statistically($allow_rating, $allow_comments, $allow_trackbacks)
    {
        if (is_null($allow_rating)) {
            $query = 'SELECT allow_rating,count(allow_rating) AS qty FROM ' . get_table_prefix() . $this->table;
            if ($this->table == 'catalogue_entries') {
                $catalogue_name = get_param_string('catalogue_name', null);
                if (!is_null($catalogue_name)) {
                    $query .= ' WHERE ' . db_string_equal_to('c_name', $catalogue_name);
                }
            }
            $query .= ' GROUP BY allow_rating ORDER BY qty DESC';
            $val = $GLOBALS['SITE_DB']->query_value_if_there($query); // We need the mode here, not the mean
            $allow_rating = is_null($val) ? 1 : $val;
        }

        if (is_null($allow_comments)) {
            $val = $GLOBALS['SITE_DB']->query_value_if_there('SELECT allow_comments,count(allow_comments) AS qty FROM ' . get_table_prefix() . $this->table . ' GROUP BY allow_comments ORDER BY qty DESC', false, true); // We need the mode here, not the mean
            $allow_comments = is_null($val) ? 1 : $val;
        }

        if (is_null($allow_trackbacks)) {
            $val = $GLOBALS['SITE_DB']->query_select_value($this->table, 'AVG(allow_trackbacks)');
            $allow_trackbacks = is_null($val) ? 1 : intval(round($val));
        }

        return array($allow_rating, $allow_comments, $allow_trackbacks);
    }

    /**
     * Standard CRUD-module permission chooser.
     *
     * @param  ?ID_TEXT $category_id The category ID the permissions are being chosen for (null: new category)
     * @param  ?Tempcode $help Extra help to show in interface (null: none)
     * @param  boolean $new_category Whether this is a new category (don't load permissions, default to on)
     * @param  ?Tempcode $pinterface_view Label for view permissions (null: default)
     * @return Tempcode The permission fields
     */
    public function get_permission_fields($category_id, $help = null, $new_category = false, $pinterface_view = null)
    {
        return get_category_permissions_for_environment($this->permission_module, $category_id, $this->privilege_page, $help, $new_category, $pinterface_view);
    }

    /**
     * Set permissions of the news category from POST parameters.
     *
     * @param  ID_TEXT $id The category to set permissions for
     */
    public function set_permissions($id)
    {
        set_category_permissions_from_environment($this->permission_module, $id, $this->privilege_page);
    }

    /**
     * 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  ?ID_TEXT $id The ID of whatever we are working with (null: deleted)
     * @return Tempcode The UI
     */
    public function do_next_manager($title, $description, $id)
    {
        $archive_url = null;
        if (!is_null($this->archive_entry_point)) {
            list($zone, $attributes,) = page_link_decode($this->archive_entry_point);
            $page = $attributes['page'];
            unset($attributes['page']);
            $archive_url = array($page, $attributes, $zone, (!isset($this->archive_label)) ? null : do_lang_tempcode($this->archive_label));
        }
        $view_url = null;
        if (!is_null($this->view_entry_point)) {
            list($zone, $attributes,) = page_link_decode(str_replace('_ID', $id, $this->view_entry_point));
            $page = $attributes['page'];
            unset($attributes['page']);
            $view_url = array($page, $attributes, $zone, (!isset($this->view_label)) ? null : $this->view_label);
        }

        require_code('templates_donext');
        return do_next_manager($title, $description,
            null,
            null,
            /* TYPED-ORDERED LIST OF 'LINKS'  */
            $this->do_next_editing_categories ? null : array('_SELF', array('type' => $this->get_screen_type_for('add', $this->type_code)), '_SELF', !is_null($this->add_one_label) ? $this->add_one_label : null), // Add one
            $this->do_next_editing_categories ? null : ((is_null($id) || ((!is_null($this->permissions_require)) && (!has_privilege(get_member(), 'edit_own_' . $this->permissions_require . 'range_content', is_null($this->privilege_page_name) ? get_page_name() : $this->privilege_page_name)))) ? null : array('_SELF', array('type' => $this->get_screen_type_for('_edit', $this->type_code), 'id' => $id), '_SELF', !is_null($this->edit_this_label) ? $this->edit_this_label : null)), // Edit this
            $this->do_next_editing_categories ? null : (((!is_null($this->permissions_require)) && (!has_privilege(get_member(), 'edit_own_' . $this->permissions_require . 'range_content', is_null($this->privilege_page_name) ? get_page_name() : $this->privilege_page_name))) ? null : array('_SELF', array('type' => $this->get_screen_type_for('edit', $this->type_code)), '_SELF', !is_null($this->edit_one_label) ? $this->edit_one_label : null)), // Edit one
            $this->do_next_editing_categories ? null : ((is_null($id)) ? null : $view_url), // View this
            $archive_url, // View archive
            null, // Add to category
            (!$this->do_next_editing_categories) ? null : array('_SELF', array('type' => $this->get_screen_type_for('add', $this->type_code)), '_SELF', !is_null($this->add_one_cat_label) ? $this->add_one_cat_label : null), // Add one category
            (!$this->do_next_editing_categories) ? null : (((!is_null($this->permissions_require)) && (!has_privilege(get_member(), 'edit_own_' . $this->permissions_require . 'range_content', is_null($this->privilege_page_name) ? get_page_name() : $this->privilege_page_name))) ? null : array('_SELF', array('type' => $this->get_screen_type_for('edit', $this->type_code)), '_SELF', !is_null($this->edit_one_cat_label) ? $this->edit_one_cat_label : null)), // Edit one category
            (!$this->do_next_editing_categories) ? null : ((is_null($id) || ((!is_null($this->permissions_require)) && (!has_privilege(get_member(), 'edit_own_' . $this->permissions_require . 'range_content', is_null($this->privilege_page_name) ? get_page_name() : $this->privilege_page_name)))) ? null : array('_SELF', array('type' => $this->get_screen_type_for('_edit', $this->type_code), 'id' => $id), '_SELF', !is_null($this->edit_this_cat_label) ? $this->edit_this_cat_label : null)), // Edit this category
            (!$this->do_next_editing_categories) ? null : $view_url, // View this category
            $this->extra_donext_entries,
            $this->extra_donext_categories,
            $this->extra_donext_whatever,
            $this->extra_donext_whatever_title,
            null,
            $this->entries_title,
            $this->categories_title,
            $this->donext_entry_content_type,
            $this->donext_category_content_type
        );
    }

    /**
     * If a confirmation is needed, and not been given, ask for one.
     *
     * @param  Tempcode $title The page title for what's being done
     * @return ?Tempcode The confirmation UI (null: all is clear - no confirmation needed)
     */
    public function handle_confirmations($title)
    {
        if (!method_exists($this, 'get_preview')) {
            return null;
        }

        if (get_param_integer('confirmed', 0) == 1) {
            return null;
        }

        $preview = $this->get_preview();
        $fields = build_keep_post_fields();
        $url = get_self_url(false, false, array('confirmed' => 1));

        return do_template('CONFIRM_SCREEN', array('_GUID' => '802e21c2beb889385141bb2ed2f27976' . get_class($this), 'TITLE' => $title, 'PREVIEW' => $preview, 'URL' => $url, 'FIELDS' => $fields));
    }

    /**
     * Get some XHTML for a form to choose a catalogue out of all the available ones.
     *
     * @param  Tempcode $title The get_screen_title converted title for this page
     * @return ?Tempcode The Tempcode for the catalogue chooser (null: already chosen)
     */
    public function choose_catalogue($title)
    {
        if (!$this->catalogue) {
            return null;
        }

        $catalogue_name = get_param_string('catalogue_name', '');
        if ($catalogue_name != '') {
            return null;
        }

        $tree = create_selection_list_catalogues(null, false, true);
        if ($tree->is_empty()) {
            return inform_screen($title, do_lang_tempcode('NO_ENTRIES', 'catalogue'));
        }

        require_code('form_templates');
        $fields = form_input_list(do_lang_tempcode('CATALOGUE'), '', 'catalogue_name', $tree, null, true);
        $post_url = get_self_url(false, false, null, false, true);
        $submit_name = do_lang_tempcode('PROCEED');
        $hidden = build_keep_post_fields();

        return do_template('FORM_SCREEN', array(
            '_GUID' => '22812ac8056903811f444682d45ee448' . get_class($this),
            'GET' => true,
            'HIDDEN' => $hidden,
            'TITLE' => $title,
            'TEXT' => paragraph(do_lang_tempcode('CHOOSE_CATALOGUE')),
            'URL' => $post_url,
            'FIELDS' => $fields,
            'SUBMIT_ICON' => 'buttons__proceed',
            'SUBMIT_NAME' => $submit_name,
            'SKIP_WEBSTANDARDS' => true,
        ));
    }

    /**
     * Standard CRUD-module UI for a separate preview.
     *
     * @param  Tempcode $title The page title
     * @return Tempcode The UI
     */
    public function preview_intercept($title)
    {
        require_code('preview');
        $output = build_preview();

        $url = get_self_url(false, false, array('preview' => 0));

        $fields = build_keep_post_fields();

        return do_template('CONFIRM_SCREEN', array('_GUID' => '06a3eb06338a7f821676d8ca3eb66aa6', 'TITLE' => $title, 'PREVIEW' => $output, 'URL' => $url, 'FIELDS' => $fields));
    }

    /**
     * Find whether this content type has a tied catalogue.
     *
     * @return boolean Whether it has
     */
    public function has_tied_catalogue()
    {
        if (!is_null($this->content_type)) {
            require_code('fields');
            return has_tied_catalogue($this->content_type);
        }
        return false;
    }

    /**
     * Standard CRUD-module UI to add a resource.
     *
     * @return Tempcode The UI
     */
    public function add()
    {
        if ((!is_null($this->permissions_require)) && (is_null($this->permissions_cat_require))) {
            check_submit_permission($this->permissions_require, $this->privilege_page_name);
        }

        $test = $this->choose_catalogue($this->title);
        if (!is_null($test)) {
            return $test;
        }

        $submit_name = (strpos($this->doing, ' ') !== false) ? protect_from_escaping($this->doing) : do_lang($this->doing);
        if (!is_null($this->add_submit_name)) {
            $submit_name = $this->add_submit_name;
        }

        $map = array('page' => '_SELF', 'type' => $this->get_screen_type_for('_add', $this->type_code));
        if (get_param_string('catalogue_name', '') != '') {
            $map['catalogue_name'] = get_param_string('catalogue_name');
        }
        if (get_param_string('type', '') == 'add_catalogue') {
            $map['type'] = '_add_catalogue';
        }
        if (!is_null(get_param_string('auto__add_one', null))) {
            $map['auto__add_one'] = get_param_string('auto__add_one');
        }
        if (!is_null(get_param_string('auto__add_one_category', null))) {
            $map['auto__add_one_category'] = get_param_string('auto__add_one_category');
        }
        if (!is_null(get_param_string('redirect', null))) {
            $map['redirect'] = get_param_string('redirect');
        }
        if (!is_null(get_param_string('continue', null))) {
            $map['continue'] = get_param_string('continue');
        }
        if ((!is_null($this->upload)) || ($this->possibly_some_kind_of_upload)) {
            $map['uploading'] = 1;
        }
        $post_url = build_url($map, '_SELF');

        $tie_in_custom_form_fields = $this->has_tied_catalogue();

        url_default_parameters__enable();

        $bits = $this->get_form_fields();
        if (is_object($bits)) {
            return $bits;
        }

        $fields2 = new Tempcode();
        $posting_form_tabindex = null;
        $extra_tpl_params = array();
        if (is_array($bits)) {
            $fields = $bits[0];
            $hidden = array_key_exists(1, $bits) ? $bits[1] : new Tempcode();
            if ((array_key_exists(6, $bits)) && (!is_null($bits[6]))) {
                $fields2 = $bits[6];
            }
            if ((array_key_exists(7, $bits)) && (!is_null($bits[7]))) {
                $posting_form_tabindex = $bits[7];
            }
            if ((array_key_exists(8, $bits)) && (!is_null($bits[8]))) {
                $extra_tpl_params += $bits[8];
            }
            if ((array_key_exists(9, $bits)) && (!is_null($bits[9]))) {
                $tie_in_custom_form_fields = $bits[9];
            }
        } else {
            $fields = $bits;
            $hidden = new Tempcode();
        }

        // Add in custom fields
        if ($tie_in_custom_form_fields) {
            if ($this->posting_form_title !== null) {
                global $BLOCK_EXTRA_POSTING_FIELDS;
                $BLOCK_EXTRA_POSTING_FIELDS = true;
            }

            require_code('fields');
            append_form_custom_fields($this->content_type, null, $fields, $hidden, null, true, strpos($fields->evaluate(), 'form_table_field_spacer') !== false);
        }

        // Awards?
        if (addon_installed('awards')) {
            if (!is_null($this->content_type)) {
                require_code('awards');
                $fields2->attach(get_award_fields($this->content_type));
            }
        }

        if (!is_object($this->add_text)) {
            $this->add_text = make_string_tempcode(is_null($this->add_text) ? '' : $this->add_text);
        }
        if ($this->user_facing) {
            //I think people know how web systems work by now if (($this->care_please) && (do_lang('CARE_PLEASE')!='')) $this->add_text->attach(paragraph(do_lang_tempcode('CARE_PLEASE')));
            if (addon_installed('points')) {
                $login_url = build_url(array('page' => 'login', 'type' => 'browse', 'redirect' => get_self_url(true, true)), get_module_zone('login'));
                $_login_url = escape_html($login_url->evaluate());
                if ((is_guest()) && ((get_forum_type() != 'cns') || (has_actual_page_access(get_member(), 'join')))) {
                    $this->add_text->attach(paragraph(do_lang_tempcode('NOT_LOGGED_IN_NO_CREDIT', $_login_url)));
                }
            }
        }
        if (!is_null($this->upload)) {
            if ($this->upload == 'image') {
                require_code('images');
                $max = floatval(get_max_image_size()) / floatval(1024 * 1024);
                if ($max < 3.0) {
                    require_code('files2');
                    $config_url = get_upload_limit_config_url();
                    $this->add_text->attach(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 {
                require_code('files2');
                $max = floatval(get_max_file_size()) / floatval(1024 * 1024);
                if ($max < 30.0) {
                    $config_url = get_upload_limit_config_url();
                    $this->add_text->attach(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))));
                }
            }
        }

        url_default_parameters__disable();

        $submit_icon = ($this->type_code == 'category') ? 'menu___generic_admin__add_one_category' : 'menu___generic_admin__add_one';

        if (get_param_string('type', 'add') == 'add_catalogue') {
            require_javascript('catalogues');

            // New field
            $fields_new = new Tempcode();
            for ($i = 0; $i < 10; $i++) { // Up to 10 new fields for catalogue, although this number is arbitrary
                list($_fields_new, $_hidden_new) = $this->get_field_fields((($i == 0) && (substr(get_param_string('id', ''), 0, 1) != '_')), 10, 'new_field_' . strval($i) . '_', $i);
                $temp = do_template('FORM_FIELD_SET_GROUPER', array('_GUID' => '3eba3a73d1fbdf922707d63216e13e03' . get_class($this), 'VISIBLE' => ($i == 0) ? true : null, 'NAME' => do_lang_tempcode('NEW_FIELD', strval($i + 1)), 'ID' => 'NEW_FIELD_' . strval($i + 1), 'FIELDS' => $_fields_new->evaluate()/*FUDGE*/));
                $fields_new->attach($temp);
                $hidden->attach($_hidden_new);
            }

            $fields->attach($fields2);
            return do_template('CATALOGUE_ADDING_SCREEN', array(
                '_GUID' => 'adf73fdfccb387640340f15d5d6dae54' . get_class($this),
                'HIDDEN' => $hidden,
                'TITLE' => $this->title,
                'PREVIEW' => $this->do_preview,
                'TEXT' => $this->add_text,
                'URL' => $post_url,
                'FIELDS' => $fields->evaluate()/*FUDGE*/,
                'FIELDS_NEW' => $fields_new->evaluate()/*FUDGE*/,
                'SUBMIT_ICON' => 'menu__cms__catalogues__add_one_catalogue',
                'SUBMIT_NAME' => $submit_name,
                'JAVASCRIPT' => $this->javascript,
            ) + $extra_tpl_params);
        } elseif (!is_null($this->posting_form_title)) {
            $posting_form = get_posting_form($submit_name, $submit_icon, $this->posting_form_text, $post_url, $hidden, $fields, $this->posting_form_title, '', $fields2, $this->posting_form_text_parsed, $this->javascript, $posting_form_tabindex, $this->posting_field_required, /*$has_preview = */true, /*$avoid_wysiwyg = */false, /*$support_autosave = */true, /*$specialisation2_hidden = */false, $this->posting_form_description);
            return do_template('POSTING_SCREEN', array(
                '_GUID' => '15930ba8cc02634ed3a225c9714c3eac' . get_class($this),
                'TITLE' => $this->title,
                'PREVIEW' => $this->do_preview,
                'SEPARATE_PREVIEW' => $this->second_stage_preview,
                'TEXT' => $this->add_text,
                'POSTING_FORM' => $posting_form->evaluate()/*FUDGE*/,
                'JAVASCRIPT' => $this->javascript,
                'SUPPORT_AUTOSAVE' => true,
            ) + $extra_tpl_params);
        } else {
            $fields->attach($fields2);
            return do_template('FORM_SCREEN', array(
                '_GUID' => '1df73fdfccb387640340f15d5d6dae54' . get_class($this),
                'PREVIEW' => $this->do_preview,
                'SEPARATE_PREVIEW' => $this->second_stage_preview,
                'HIDDEN' => $hidden,
                'TITLE' => $this->title,
                'SKIP_WEBSTANDARDS' => $this->skip_webstandards,
                'TEXT' => $this->add_text,
                'URL' => $post_url,
                'FIELDS' => $fields->evaluate()/*FUDGE*/,
                'SUBMIT_ICON' => $submit_icon,
                'SUBMIT_NAME' => $submit_name,
                'JAVASCRIPT' => $this->javascript,
                'SUPPORT_AUTOSAVE' => true,
            ) + $extra_tpl_params);
        }
    }

    /**
     * Standard CRUD-module UI/actualiser to add a resource.
     *
     * @return Tempcode The UI
     */
    public function _add()
    {
        if (!is_null($this->permissions_require)) {
            check_submit_permission($this->permissions_require, array($this->permissions_cat_require, is_null($this->permissions_cat_name) ? '' : post_param_string($this->permissions_cat_name), $this->permissions_cat_require_b, is_null($this->permissions_cat_name_b) ? '' : post_param_string($this->permissions_cat_name_b)), $this->privilege_page_name);
        }

        if (($this->second_stage_preview) && (get_param_integer('preview', 0) == 1)) {
            return $this->preview_intercept($this->title);
        }

        $test = $this->handle_confirmations($this->title);
        if (!is_null($test)) {
            return $test;
        }

        if (($this->user_facing) && (!is_null($this->permissions_require))) {
            require_code('antispam');
            inject_action_spamcheck();
            if (addon_installed('unvalidated')) {
                if (!has_privilege(get_member(), 'bypass_validation_' . $this->permissions_require . 'range_content', $this->privilege_page_name, array($this->permissions_cat_require, is_null($this->permissions_cat_name) ? '' : post_param_string($this->permissions_cat_name), $this->permissions_cat_require_b, is_null($this->permissions_cat_name_b) ? '' : post_param_string($this->permissions_cat_name_b)))) {
                    $_POST['validated'] = '0';
                }
            }
        }

        if (!is_null($this->upload)) {
            require_code('uploads');
        }
        $temp = $this->add_actualisation();

        $description = is_null($this->do_next_description) ? paragraph(do_lang_tempcode($this->success_message_str)) : $this->do_next_description;

        if (is_array($temp)) {
            list($id, $text) = $temp;
            if (!is_null($text)) {
                $description->attach($text);
            }
        } else {
            $id = $temp;
        }

        // Save custom fields
        if ($this->has_tied_catalogue()) {
            require_code('fields');
            save_form_custom_fields($this->content_type, $id);
        }

        if ($this->user_facing) {
            require_code('submit');
            if (($this->check_validation) && (addon_installed('unvalidated')) && (post_param_integer('validated', 0) == 0)) {
                if ($this->send_validation_request) {
                    $edit_url = build_url(array('page' => '_SELF', 'type' => $this->get_screen_type_for('_edit', $this->type_code), 'id' => $id, 'validated' => 1), '_SELF', null, false, false, true);
                    send_validation_request($this->doing, $this->table, $this->non_integer_id, $id, $edit_url);
                }

                $description->attach(paragraph(do_lang_tempcode('SUBMIT_UNVALIDATED', $this->content_type)));
            }
            $submitter = get_member();
            if (method_exists($this, 'get_submitter')) {
                list($submitter,) = $this->get_submitter($id);
            }
            give_submit_points($this->doing, $submitter);
        }

        if (addon_installed('awards')) {
            if (!is_null($this->content_type)) {
                require_code('awards');
                handle_award_setting($this->content_type, $id);
            }
        }

        //if ($this->redirect_type=='!') {
        $url = get_param_string('redirect', null);
        if (!is_null($url)) {
            $url = str_replace('__ID__', $id, $url);
            return redirect_screen($this->title, $url, $description);
        }
        //}

        return $this->do_next_manager($this->title, $description, $id);
    }

    /**
     * Standard CRUD-module entry function to get rows for selection from.
     *
     * @param  boolean $recache Whether to force a recache
     * @param  ?ID_TEXT $orderer Order to use (null: automatic)
     * @param  ?array $where Extra where clauses (null: none)
     * @param  boolean $force_site_db Whether to always access using the site database
     * @param  string $join Extra join clause for our query (blank: none)
     * @param  ?integer $max Maximum to show (null: standard)
     * @return array A pair: Rows for selection from, Total results
     */
    public function get_entry_rows($recache = false, $orderer = null, $where = null, $force_site_db = false, $join = '', $max = null)
    {
        if ((!$recache) && (is_null($orderer)) && (is_null($where))) {
            if (isset($this->cached_entry_rows)) {
                return array($this->cached_entry_rows, $this->cached_max_rows);
            }
        }

        $select_field = !is_null($this->orderer) ? $this->orderer : ($this->table_prefix . strtolower($this->select_name));

        $table_raw = (is_null($this->table) ? $this->module_type : $this->table);
        $table = $table_raw . ' r';
        $db = ((substr($table, 0, 2) == 'f_') && ($table != 'f_welcome_emails r') && (!$force_site_db) && (get_forum_type() == 'cns')) ? $GLOBALS['FORUM_DB'] : $GLOBALS['SITE_DB'];

        if (is_null($orderer)) {
            $orderer = $select_field;
        }
        $orderer_is_multi_lang = isset($GLOBALS['TABLE_LANG_FIELDS_CACHE'][$table_raw][preg_replace('# (ASC|DESC)$#', '', $orderer)]);

        if ($orderer_is_multi_lang) {
            $_orderer = $GLOBALS['SITE_DB']->translate_field_ref(preg_replace('# (ASC|DESC)$#', '', $orderer));
            if (substr($orderer, -5) == ' DESC') {
                $_orderer .= ' DESC';
            }
            $orderer = $_orderer;
        } elseif ((substr($orderer, 0, 1) != '(') && (strpos($orderer, '.') === false)) { // If not a subquery and not already dotted
            $orderer = 'r.' . $orderer;
        }

        if ($force_site_db) {
            $dbs_bak = $GLOBALS['NO_DB_SCOPE_CHECK'];
            $GLOBALS['NO_DB_SCOPE_CHECK'] = true;
        }

        $max_rows = $db->query_select_value($table . $join, 'COUNT(*)', $where, '', false, isset($GLOBALS['TABLE_LANG_FIELDS_CACHE'][$table_raw]) ? $GLOBALS['TABLE_LANG_FIELDS_CACHE'][$table_raw] : null);
        if ($max_rows == 0) {
            return array(array(), 0);
        }
        $start = get_param_integer('start', 0);
        if ($max === null) {
            $max = get_param_integer('max', 20);
        }
        $rows = $db->query_select($table . $join, array('r.*'), $where, 'ORDER BY ' . $orderer, $max, $start, false, isset($GLOBALS['TABLE_LANG_FIELDS_CACHE'][$table_raw]) ? $GLOBALS['TABLE_LANG_FIELDS_CACHE'][$table_raw] : null);

        if ($force_site_db) {
            $GLOBALS['NO_DB_SCOPE_CHECK'] = $dbs_bak;
        }

        $_entries = array();
        foreach ($rows as $row) {
            $key = $row[$this->array_key];
            $readable = $row[$select_field];
            if (is_integer($readable)) {
                $readable = '#' . strval($readable);
            }
            if ($readable == '') {
                $readable = do_lang('_DEFAULT');
            }
            $row['_readable'] = $readable;
            $_entries[$key] = $row;
        }

        if ((is_null($orderer)) && (is_null($where))) {
            $this->cached_entry_rows = $_entries;
            $this->cached_max_rows = $max_rows;
        }

        return array($_entries, $max_rows);
    }

    /**
     * Standard CRUD-module entry list fetcher.
     *
     * @return Tempcode The selection list
     */
    public function create_selection_list_entries()
    {
        list($_entries,) = $this->get_entry_rows(false, null, null, false, '', intval(get_option('general_safety_listing_limit')));

        $entries = new Tempcode();
        foreach ($_entries as $key => $row) {
            $readable = $row['_readable'];
            $entries->attach(form_input_list_entry($key, $key === get_param_string('id', null, true), $readable));
        }
        return $entries;
    }

    /**
     * Standard CRUD-module UI to choose a resource to edit.
     *
     * @return Tempcode The UI
     */
    public function edit()
    {
        if ((!is_null($this->permissions_require)) && (is_null($this->permissions_cat_require))) {
            check_some_edit_permission($this->permissions_require, null, $this->privilege_page_name);
        }

        $test = $this->choose_catalogue($this->title);
        if (!is_null($test)) {
            return $test;
        }

        $text = paragraph(do_lang_tempcode('CHOOSE_EDIT_LIST'));

        $next_type = $this->get_screen_type_for('_edit', $this->type_code);
        if (get_param_string('type', 'edit') == 'edit_catalogue') {
            $next_type = '_edit_catalogue';
        }
        $map = array('page' => '_SELF', 'type' => $next_type);
        if (either_param_string('catalogue_name', '') != '') {
            $map['catalogue_name'] = either_param_string('catalogue_name');
        }
        if (!is_null(get_param_string('redirect', null))) {
            $map['redirect'] = get_param_string('redirect');
        }
        if (!is_null(get_param_string('continue', null))) {
            $map['continue'] = get_param_string('continue');
        }

        $description = ($this->select_name_description != '') ? do_lang_tempcode($this->select_name_description) : new Tempcode();
        if (method_exists($this, 'create_selection_list_radio_entries')) { // For picture selection lists only
            $entries = $this->create_selection_list_radio_entries();
            if ($entries->is_empty()) {
                inform_exit(do_lang_tempcode(($this->type_code == '') ? 'NO_ENTRIES' : 'NO_CATEGORIES', $this->content_type));
            }
            $fields = form_input_radio(do_lang_tempcode($this->select_name), $description, 'id', $entries, $this->no_blank_ids, true, '');
        } elseif ((method_exists($this, 'create_selection_list_ajax_tree')) && (($_fields = $this->create_selection_list_ajax_tree()) !== null)) {
            if (is_array($_fields)) {
                $text = paragraph(do_lang_tempcode('CHOOSE_EDIT_LIST_EXTRA', escape_html($_fields[1]->evaluate()), escape_html($_fields[2]->evaluate())));
                $fields = $_fields[0];
            } else {
                $fields = $_fields;
            }
        } elseif (method_exists($this, 'create_selection_list_choose_table')) {
            list($test,) = $this->get_entry_rows();
            if (count($test) == 0) {
                inform_exit(do_lang_tempcode(($this->type_code == '') ? 'NO_ENTRIES' : 'NO_CATEGORIES', $this->content_type));
            }
            $table_result = $this->create_selection_list_choose_table($map);
            if (is_null($table_result)) {
                inform_exit(do_lang_tempcode(($this->type_code == '') ? 'NO_ENTRIES' : 'NO_CATEGORIES', $this->content_type));
            }
            $table = $table_result[0];
            $has_ordering = $table_result[1];
            if ((array_key_exists(2, $table_result)) && (!is_null($table_result[2]))) {
                $text = paragraph(do_lang_tempcode('CHOOSE_EDIT_TABLE_EXTRA', escape_html($table_result[2]->evaluate()), escape_html($table_result[3]->evaluate())));
            } elseif ((array_key_exists(3, $table_result)) && (!is_null($table_result[3]))) {
                $text = paragraph(do_lang_tempcode('_CHOOSE_EDIT_TABLE_EXTRA', escape_html($table_result[3]->evaluate())));
            } else {
                $text = paragraph(do_lang_tempcode('CHOOSE_EDIT_TABLE'));
            }

            $tpl = do_template('COLUMNED_TABLE_SCREEN', array(
                '_GUID' => '3a3e7cf1bef6ca31f8c992c69a80449e',
                'TITLE' => $this->title,
                'FORM_ID' => $this->form_id,
                'TEXT' => $text,
                'TABLE' => $table,
                'SUBMIT_ICON' => 'buttons__sort',
                'SUBMIT_NAME' => $has_ordering ? do_lang_tempcode('SORT') : null,
                'POST_URL' => get_self_url(),
                'JAVASCRIPT' => $this->javascript_for_choose,
            ));

            require_code('templates_internalise_screen');
            return internalise_own_screen($tpl);
        } else {
            $_entries = $this->create_selection_list_entries();

            if (is_array($_entries)) {
                if (!is_null($_entries[1])) {
                    $text = paragraph(do_lang_tempcode('CHOOSE_EDIT_LIST_EXTRA', escape_html($_entries[1]->evaluate()), escape_html($_entries[2]->evaluate())));
                } else {
                    $text = paragraph(do_lang_tempcode('_CHOOSE_EDIT_LIST_EXTRA', escape_html($_entries[2]->evaluate())));
                }
                $entries = $_entries[0];
            } else {
                $entries = $_entries;
            }

            if ($entries->is_empty()) {
                inform_exit(do_lang_tempcode(($this->type_code == '') ? 'NO_ENTRIES' : 'NO_CATEGORIES', $this->content_type));
            }
            $fields = form_input_huge_list(do_lang_tempcode($this->select_name), $description, 'id', $entries, null, true, $this->no_blank_ids);
        }

        $post_url = build_url($map, '_SELF', null, false, true);

        $submit_name = do_lang_tempcode('EDIT');

        $keep = symbol_tempcode('KEEP');
        $iframe_url = mixed();
        if (!$this->special_edit_frontend && (has_js())) {
            $iframe_url = find_script('iframe') . '?zone=' . urlencode(get_zone_name()) . '&opens_below=1';
            foreach ($map as $key => $val) {
                $iframe_url .= '&' . $key . '=' . urlencode(str_replace('_SELF', get_page_name(), $val));
            }
            $iframe_url .= $keep->evaluate();
        }

        $extra_buttons = mixed();
        if ($this->supports_mass_delete) {
            $extra_buttons = do_template('MASS_SELECT_FORM_BUTTONS', array('_GUID' => '21121b5a5f8beafefbd09d3e23265986', 'TYPE' => $this->content_type));
        }

        return do_template('FORM_SCREEN', array(
            '_GUID' => '228a05e24253f324ea286ea8ac3d8b02' . get_class($this),
            'GET' => true,
            'IFRAME_URL' => $iframe_url,
            'HIDDEN' => '',
            'TITLE' => $this->title,
            'TEXT' => $text,
            'URL' => $post_url,
            'FIELDS' => $fields->evaluate()/*FUDGE*/,
            'SUBMIT_ICON' => 'menu___generic_admin__edit_this',
            'SUBMIT_NAME' => $submit_name,
            'SKIP_WEBSTANDARDS' => true,
            'EXTRA_BUTTONS' => $extra_buttons,
            'JAVASCRIPT' => $this->javascript_for_choose,
        ));
    }

    /**
     * Standard CRUD-module UI to edit a resource.
     *
     * @return Tempcode The UI
     */
    public function _edit()
    {
        $submit_name = do_lang_tempcode('SAVE');

        $id = mixed(); // Define type as mixed
        $id = $this->non_integer_id ? get_param_string('id', false, true) : strval(get_param_integer('id'));

        $map = array('page' => '_SELF', 'type' => $this->get_screen_type_for('__edit', $this->type_code), 'id' => $id);
        if (get_param_string('catalogue_name', '') != '') {
            $map['catalogue_name'] = get_param_string('catalogue_name');
        }
        if (get_param_string('type', '') == '_edit_catalogue') {
            $map['type'] = '__edit_catalogue';
        }
        if (!is_null(get_param_string('redirect', null))) {
            $map['redirect'] = get_param_string('redirect');
        }
        if (!is_null(get_param_string('continue', null))) {
            $map['continue'] = get_param_string('continue');
        }
        if ((!is_null($this->upload)) || ($this->possibly_some_kind_of_upload)) {
            $map['uploading'] = 1;
        }
        $post_url = build_url($map, '_SELF');

        if (method_exists($this, 'get_submitter')) {
            list($submitter, $date_and_time) = $this->get_submitter($id);
        } else {
            $submitter = null;
            $date_and_time = null;
        }

        if (!is_null($this->permissions_require)) {
            check_edit_permission($this->permissions_require, $submitter, array($this->permissions_cat_require, is_null($this->permissions_cat_name) ? null : $this->get_cat($id), $this->permissions_cat_require_b, is_null($this->permissions_cat_name_b) ? null : $this->get_cat_b($id)), $this->privilege_page_name);
        }

        if ((!is_null($this->permissions_cat_require)) && (!has_category_access(get_member(), $this->permissions_cat_require, $this->get_cat($id)))) {
            access_denied('CATEGORY_ACCESS');
        }

        if ((!is_null($this->permissions_cat_require_b)) && (!has_category_access(get_member(), $this->permissions_cat_require_b, $this->get_cat_b($id)))) {
            access_denied('CATEGORY_ACCESS');
        }

        $tie_in_custom_form_fields = $this->has_tied_catalogue();

        $bits = $this->fill_in_edit_form($id);
        $delete_fields = new Tempcode();
        $all_delete_fields_given = false;
        $fields2 = new Tempcode();
        $extra_tpl_params = array();
        if (is_array($bits)) {
            $fields = $bits[0];
            $hidden = $bits[1];
            if ((array_key_exists(2, $bits)) && (!is_null($bits[2]))) {
                $delete_fields = $bits[2];
            }
            if ((array_key_exists(3, $bits)) && (!is_null($bits[3]))) {
                $this->edit_text = $bits[3];
            }
            if ((array_key_exists(4, $bits)) && ($bits[4])) {
                $all_delete_fields_given = true;
            }
            if ((array_key_exists(5, $bits)) && (!is_null($bits[5]))) {
                $this->posting_form_text = $bits[5];
            }
            if ((array_key_exists(6, $bits)) && (!is_null($bits[6]))) {
                $fields2 = $bits[6];
            }
            if ((array_key_exists(7, $bits)) && (!is_null($bits[7]))) {
                $this->posting_form_text_parsed = $bits[7];
            }
            if ((array_key_exists(8, $bits)) && (!is_null($bits[8]))) {
                $extra_tpl_params += $bits[8];
            }
            if ((array_key_exists(9, $bits)) && (!is_null($bits[9]))) {
                $tie_in_custom_form_fields = $bits[9];
            }
        } else {
            $fields = $bits;
            $hidden = new Tempcode();
        }

        // Add in custom fields
        if ($tie_in_custom_form_fields) {
            if ($this->posting_form_title !== null) {
                global $BLOCK_EXTRA_POSTING_FIELDS;
                $BLOCK_EXTRA_POSTING_FIELDS = true;
            }

            require_code('fields');
            append_form_custom_fields($this->content_type, $id, $fields, $hidden, null, true, strpos($fields->evaluate(), 'form_table_field_spacer') !== false);
        }

        // Awards?
        if (addon_installed('awards')) {
            if (!is_null($this->content_type)) {
                require_code('awards');
                $fields2->attach(get_award_fields($this->content_type, $id));
            }
        }

        // Action fields / deletion options
        $delete_permission = true;
        if (!is_null($this->permissions_require)) {
            $delete_permission = has_delete_permission($this->permissions_require, get_member(), $submitter, is_null($this->privilege_page_name) ? get_page_name() : $this->privilege_page_name, array($this->permissions_cat_require, is_null($this->permissions_cat_name) ? null : $this->get_cat($id), $this->permissions_cat_require_b, is_null($this->permissions_cat_name_b) ? null : $this->get_cat_b($id)));
        }
        $may_delete = (((!method_exists($this, 'may_delete_this')) || ($this->may_delete_this($id))) && ((!is_numeric($id)) || (intval($id) >= db_get_first_id() + $this->protect_first))) && ($delete_permission);

        // Deletion options
        $action_fields = new Tempcode();
        if ($may_delete) {
            if (!$all_delete_fields_given) {
                // HACKHACK: Improve with #1789
                if ($this->content_type === 'catalogue_category' && $GLOBALS['SITE_DB']->query_select_value_if_there('catalogues c JOIN ' . get_table_prefix() . 'catalogue_categories cc ON c.c_name=cc.c_name', 'c_is_tree', array('id' => $id)) == 0) {
                    $action_fields->attach(form_input_tick(do_lang_tempcode('DELETE'), do_lang_tempcode('DESCRIPTION_DELETE_LOSE_CONTENTS'), 'delete', false));
                } elseif ($this->content_type === 'gallery') {
                    $action_fields->attach(form_input_tick(do_lang_tempcode('DELETE'), do_lang_tempcode('DESCRIPTION_DELETE_LOSE_CONTENTS'), 'delete', false));
                } else {
                    $action_fields->attach(form_input_tick(do_lang_tempcode('DELETE'), do_lang_tempcode('DESCRIPTION_DELETE'), 'delete', false));
                }
            }

            if ((addon_installed('points')) && (!is_null($submitter)) && (!is_null($date_and_time))) {
                $points_test = $GLOBALS['SITE_DB']->query_select_value_if_there('gifts', 'id', array('date_and_time' => $date_and_time, 'gift_to' => $submitter, 'gift_from' => $GLOBALS['FORUM_DRIVER']->get_guest_id()));
                if (!is_null($points_test)) {
                    require_lang('points');
                    $action_fields->attach(form_input_tick(do_lang_tempcode('REVERSE_TITLE'), do_lang_tempcode('REVERSE_TITLE_DESCRIPTION', $this->content_type), 'reverse_point_transaction', false));
                }
            }
            $action_fields->attach($delete_fields);
        }

        if ((!$this->appended_actions_already) && (!$action_fields->is_empty())) {
            $fields2->attach(do_template('FORM_SCREEN_FIELD_SPACER', array('_GUID' => '65fe1edf02edab5cae619a70cfa690d0', 'TITLE' => do_lang_tempcode('ACTIONS'))));
        }
        $fields2->attach($action_fields);

        if (!is_object($this->edit_text)) {
            $this->edit_text = make_string_tempcode(is_null($this->edit_text) ? '' : $this->edit_text);
        }
        if (!is_null($this->upload)) {
            if ($this->upload == 'image') {
                require_code('images');
                $max = floatval(get_max_image_size()) / floatval(1024 * 1024);
                if ($max < 3.0) {
                    require_code('files2');
                    $config_url = get_upload_limit_config_url();
                    $this->edit_text->attach(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 {
                require_code('files2');
                $max = floatval(get_max_file_size()) / floatval(1024 * 1024);
                if ($max < 30.0) {
                    $config_url = get_upload_limit_config_url();
                    $this->edit_text->attach(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))));
                }
            }
        }

        if (get_param_string('type', '_edit') == '_edit_catalogue') {
            require_javascript('catalogues');

            // Existing fields
            $field_count = 0;
            $c_name = get_param_string('id', false, true);
            $rows = $GLOBALS['SITE_DB']->query_select('catalogue_fields', array('*'), array('c_name' => $c_name), 'ORDER BY cf_order,' . $GLOBALS['SITE_DB']->translate_field_ref('cf_name'));
            $fields_existing = new Tempcode();
            foreach ($rows as $i => $myrow) {
                $name = get_translated_text($myrow['cf_name']);
                $description = get_translated_text($myrow['cf_description']);
                $prefix = 'existing_field_' . strval($myrow['id']) . '_';
                list($_fields_existing, $_fields_hidden) = $this->get_field_fields((($i == 0) && (substr($c_name, 0, 1) != '_')), count($rows) + 10, $prefix, $field_count, $name, $description, $myrow['cf_type'], $myrow['cf_defines_order'], $myrow['cf_visible'], $myrow['cf_searchable'], $myrow['cf_default'], $myrow['cf_required'], $myrow['cf_put_in_category'], $myrow['cf_put_in_search'], $myrow['cf_options']);
                if ((!is_ecommerce_catalogue($c_name)) || ($i > 9)) {
                    $_fields_existing->attach(do_template('FORM_SCREEN_FIELD_SPACER', array('_GUID' => 'c1959d74d4226cad31629b6f24a8e4b0', 'TITLE' => do_lang_tempcode('ACTIONS'))));
                    $_fields_existing->attach(form_input_tick(do_lang_tempcode('DELETE'), do_lang_tempcode('DESCRIPTION_DELETE'), $prefix . 'delete', false));
                }
                $temp = do_template('FORM_FIELD_SET_GROUPER', array('_GUID' => '1492d973db45cbecff892ad4ac1af28f' . get_class($this), 'NAME' => $name . ' (ID #' . $myrow['id'] . ')', 'ID' => 'FIELD_' . strval($i + 1), 'FIELDS' => $_fields_existing->evaluate()/*FUDGE*/));
                $fields_existing->attach($temp);
                $hidden->attach($_fields_hidden);

                $field_count++;
            }

            // New field
            $fields_new = new Tempcode();
            for ($i = 0; $i < 5; $i++) {
                list($_fields_new, $_fields_hidden) = $this->get_field_fields(false, count($rows) + 10, 'new_field_' . strval($i) . '_', $field_count);
                $temp = do_template('FORM_FIELD_SET_GROUPER', array('_GUID' => '8b9a632eafae003ccc6b007eefb0ce3d' . get_class($this), 'NAME' => do_lang_tempcode('NEW_FIELD', strval($i + 1)), 'ID' => 'NEW_FIELD_' . strval($i + 1), 'FIELDS' => $_fields_new->evaluate()/*FUDGE*/));
                $fields_new->attach($temp);
                $hidden->attach($_fields_hidden);

                $field_count++;
            }

            $fields->attach($fields2);
            return do_template('CATALOGUE_EDITING_SCREEN', array(
                '_GUID' => '584d7dc7c2c13939626102374f13f508' . get_class($this),
                'HIDDEN' => $hidden,
                'TITLE' => $this->title,
                'PREVIEW' => $this->do_preview,
                'TEXT' => $this->add_text,
                'URL' => $post_url,
                'FIELDS' => $fields->evaluate()/*FUDGE*/,
                'FIELDS_EXISTING' => $fields_existing->evaluate()/*FUDGE*/,
                'FIELDS_NEW' => $fields_new->evaluate()/*FUDGE*/,
                'SUBMIT_ICON' => 'menu__cms__catalogues__edit_this_catalogue',
                'SUBMIT_NAME' => $submit_name,
                'JAVASCRIPT' => $this->javascript,
            ) + $extra_tpl_params);
        }

        list($warning_details, $ping_url) = handle_conflict_resolution();

        $submit_icon = ($this->type_code == 'category') ? 'menu___generic_admin__edit_one_category' : 'menu___generic_admin__edit_one';

        if (!is_null($this->posting_form_title)) {
            $posting_form = get_posting_form($submit_name, $submit_icon, $this->posting_form_text, $post_url, $hidden, $fields, $this->posting_form_title, '', $fields2, $this->posting_form_text_parsed, $this->javascript, $this->posting_form_tabindex, $this->posting_field_required, /*$has_preview = */true, /*$avoid_wysiwyg = */false, /*$support_autosave = */true, /*$specialisation2_hidden = */false, $this->posting_form_description);
            return do_template('POSTING_SCREEN', array(
                '_GUID' => '841b9af3aa80bcab86b907e4b942786a' . get_class($this),
                'PREVIEW' => $this->do_preview,
                'TITLE' => $this->title,
                'SEPARATE_PREVIEW' => $this->second_stage_preview,
                'PING_URL' => $ping_url,
                'WARNING_DETAILS' => $warning_details,
                'TEXT' => $this->add_text,
                'POSTING_FORM' => $posting_form->evaluate()/*FUDGE*/,
                'JAVASCRIPT' => $this->javascript,
                'SUPPORT_AUTOSAVE' => true,
            ) + $extra_tpl_params);
        } else {
            $fields->attach($fields2);
            return do_template('FORM_SCREEN', array(
                '_GUID' => '2d70be34595a16c6f170d966b894bfe2' . get_class($this),
                'PREVIEW' => $this->do_preview,
                'SEPARATE_PREVIEW' => $this->second_stage_preview,
                'TITLE' => $this->title,
                'SKIP_WEBSTANDARDS' => $this->skip_webstandards,
                'PING_URL' => $ping_url,
                'WARNING_DETAILS' => $warning_details,
                'HIDDEN' => $hidden,
                'TEXT' => $this->edit_text,
                'URL' => $post_url,
                'FIELDS' => $fields->evaluate()/*FUDGE*/,
                'SUBMIT_ICON' => $submit_icon,
                'SUBMIT_NAME' => $submit_name,
                'JAVASCRIPT' => $this->javascript,
                'SUPPORT_AUTOSAVE' => true,
            ) + $extra_tpl_params);
        }
    }

    /**
     * Standard CRUD-module UI/actualiser to edit a resource.
     *
     * @return Tempcode The UI
     */
    public function __edit()
    {
        $id = mixed(); // Define type as mixed
        $id = $this->non_integer_id ? get_param_string('id', false, true) : strval(get_param_integer('id'));

        if (($this->second_stage_preview) && (get_param_integer('preview', 0) == 1)) {
            return $this->preview_intercept($this->title);
        }

        if (method_exists($this, 'get_submitter')) {
            list($submitter, $date_and_time) = $this->get_submitter($id);
            if ((!is_null($date_and_time)) && (addon_installed('points'))) {
                $reverse = post_param_integer('reverse_point_transaction', 0);
                if ($reverse == 1) {
                    $points_test = $GLOBALS['SITE_DB']->query_select('gifts', array('id'), array('date_and_time' => $date_and_time, 'gift_to' => $submitter, 'gift_from' => $GLOBALS['FORUM_DRIVER']->get_guest_id()));
                    if (array_key_exists(0, $points_test)) {
                        require_code('points2');
                        reverse_point_gift_transaction($points_test[0]['id']);
                    }
                }
            }
        } else {
            $submitter = null;
        }

        $delete = post_param_integer('delete', 0);
        if (($delete == 1) || ($delete == 2)) { // Delete: 1=partial,2=full,...=unknown,thus handled as an edit
            if (!is_null($this->permissions_require)) {
                check_delete_permission($this->permissions_require, $submitter, array($this->permissions_cat_require, is_null($this->permissions_cat_name) ? null : $this->get_cat($id), $this->permissions_cat_require_b, is_null($this->permissions_cat_name_b) ? null : $this->get_cat_b($id)), $this->privilege_page_name);
            }

            $test = $this->handle_confirmations($this->title);
            if (!is_null($test)) {
                return $test;
            }

            $this->delete_actualisation($id);

            // Delete custom fields
            if ($this->has_tied_catalogue()) {
                require_code('fields');
                delete_form_custom_fields($this->content_type, $id);
            }

            $description = is_null($this->do_next_description) ? do_lang_tempcode($this->success_message_str) : $this->do_next_description;

            return $this->do_next_manager($this->title, $description, null);
        } else { // Edit
            if (!is_null($this->permissions_require)) {
                check_edit_permission($this->permissions_require, $submitter, array($this->permissions_cat_require, is_null($this->permissions_cat_name) ? null : $this->get_cat($id), $this->permissions_cat_require_b, is_null($this->permissions_cat_name_b) ? null : $this->get_cat_b($id)), $this->privilege_page_name);
            }

            $test = $this->handle_confirmations($this->title);
            if (!is_null($test)) {
                return $test;
            }

            if (($this->user_facing) && (!is_null($this->permissions_require)) && (addon_installed('unvalidated'))) {
                if (array_key_exists('validated', $_POST)) {
                    if (!has_privilege(get_member(), 'bypass_validation_' . $this->permissions_require . 'range_content', $this->privilege_page_name, array($this->permissions_cat_require, is_null($this->permissions_cat_name) ? '' : post_param_string($this->permissions_cat_name), $this->permissions_cat_require_b, is_null($this->permissions_cat_name_b) ? '' : post_param_string($this->permissions_cat_name_b)))) {
                        if (!$this->edit_keep_validation) {
                            $_POST['validated'] = '0';
                        } else {
                            $_POST['validated'] = '-1';
                        }
                    }
                }
            }

            if (!is_null($this->upload)) {
                require_code('uploads');
            }
            $description = $this->edit_actualisation($id);
            $orig_id = $id;
            if (!is_null($this->new_id)) {
                $id = $this->new_id;
            }

            // Save custom fields
            if ($this->has_tied_catalogue()) {
                require_code('fields');
                save_form_custom_fields($this->content_type, $id, $orig_id);
            }

            if (($this->output_of_action_is_confirmation) && (!is_null($description))) {
                return $description;
            }

            if (is_null($description)) {
                $description = do_lang_tempcode($this->success_message_str);
            }

            if (addon_installed('awards')) {
                if (!is_null($this->content_type)) {
                    require_code('awards');
                    handle_award_setting($this->content_type, $id);
                }
            }

            if ($this->user_facing) {
                if (($this->check_validation) && (post_param_integer('validated', 0) == 0) && (post_param_integer('tick_on_form__validated', null) === null)) {
                    require_code('submit');
                    if (($this->send_validation_request) && (addon_installed('unvalidated'))) {
                        $edit_url = build_url(array('page' => '_SELF', 'type' => $this->get_screen_type_for('_edit', $this->type_code), 'id' => $id, 'validated' => 1), '_SELF', null, false, false, true);
                        send_validation_request($this->doing, $this->table, $this->non_integer_id, $id, $edit_url);
                    }

                    $description->attach(paragraph(do_lang_tempcode('SUBMIT_UNVALIDATED', $this->content_type)));
                }
            }
        }

        if ((!is_null($this->redirect_type)) || (((!is_null(get_param_string('redirect', null)) && ($orig_id == $id))))) {
            $url = (($this->redirect_type == '!') || (is_null($this->redirect_type))) ? make_string_tempcode(get_param_string('redirect')) : build_url(array('page' => '_SELF', 'type' => $this->redirect_type), '_SELF');

            return redirect_screen($this->title, $url, do_lang_tempcode($this->success_message_str));
        }

        return $this->do_next_manager($this->title, $description, $id);
    }

    /**
     * Mass delete some entries/categories.
     *
     * @param  boolean $top_level Whether this is a top level mass delete op (i.e. not a recursion)
     * @return ?Tempcode The UI (null: not top level)
     */
    public function mass_delete($top_level = true)
    {
        $delete = array();
        if ($this->supports_mass_delete) {
            foreach ($_POST as $key => $val) {
                $matches = array();
                if (($val === '1') && (preg_match('#^' . preg_quote($this->content_type) . '_(.*)$#', $key, $matches) != 0)) {
                    $id = $matches[1];

                    if ($this->permissions_require !== null) {
                        if (method_exists($this, 'get_submitter')) {
                            list($submitter, $date_and_time) = $this->get_submitter($id);
                        } else {
                            $submitter = null;
                        }
                        check_delete_permission($this->permissions_require, $submitter, array($this->permissions_cat_require, is_null($this->permissions_cat_name) ? null : $this->get_cat($id), $this->permissions_cat_require_b, is_null($this->permissions_cat_name_b) ? null : $this->get_cat_b($id)), $this->privilege_page_name);
                    }

                    $delete[] = $id; // Don't do right away, we want to check all permissions first so that we don't do a partial action
                }
            }
            foreach ($delete as $id) {
                $this->delete_actualisation($id);
            }
        }
        if ((!is_null($this->cat_crud_module)) && (!is_null($this->cat_crud_module->content_type))) {
            foreach ($_POST as $key => $val) {
                if (($val === '1') && (strpos($key, '_') !== false)) {
                    $this->cat_crud_module->mass_delete(false);
                }
            }
        }
        if ((!is_null($this->alt_crud_module)) && (!is_null($this->alt_crud_module->content_type))) {
            foreach ($_POST as $key => $val) {
                if (($val === '1') && (strpos($key, '_') !== false)) {
                    $this->alt_crud_module->mass_delete(false);
                }
            }
        }

        // UI
        if ($top_level) {
            if ((!is_null($this->redirect_type)) || ((!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'));
            }

            $description = do_lang_tempcode('SUCCESS');

            return $this->do_next_manager($this->title, $description, null);
        }

        // Not top level
        return null;
    }
}
