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

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

    /**
     * Uninstall the module.
     */
    public function uninstall()
    {
        $GLOBALS['SITE_DB']->drop_table_if_exists('url_id_monikers');
        $GLOBALS['SITE_DB']->drop_table_if_exists('cache');
        $GLOBALS['SITE_DB']->drop_table_if_exists('cache_on');
        $GLOBALS['SITE_DB']->drop_table_if_exists('captchas');
        $GLOBALS['SITE_DB']->drop_table_if_exists('rating');
        $GLOBALS['SITE_DB']->drop_table_if_exists('member_tracking');
        $GLOBALS['SITE_DB']->drop_table_if_exists('trackbacks');
        $GLOBALS['SITE_DB']->drop_table_if_exists('menu_items');
        $GLOBALS['SITE_DB']->drop_table_if_exists('values_elective');
        $GLOBALS['SITE_DB']->drop_table_if_exists('tutorial_links');
        $GLOBALS['SITE_DB']->drop_table_if_exists('edit_pings');
        $GLOBALS['SITE_DB']->drop_table_if_exists('webstandards_checked_once');
        $GLOBALS['SITE_DB']->drop_table_if_exists('member_privileges');
        $GLOBALS['SITE_DB']->drop_table_if_exists('member_zone_access');
        $GLOBALS['SITE_DB']->drop_table_if_exists('member_page_access');
        $GLOBALS['SITE_DB']->drop_table_if_exists('member_category_access');
        $GLOBALS['SITE_DB']->drop_table_if_exists('tracking');
        $GLOBALS['SITE_DB']->drop_table_if_exists('autosave');
        $GLOBALS['SITE_DB']->drop_table_if_exists('messages_to_render');
        $GLOBALS['SITE_DB']->drop_table_if_exists('url_title_cache');
        $GLOBALS['SITE_DB']->drop_table_if_exists('review_supplement');
        $GLOBALS['SITE_DB']->drop_table_if_exists('logged_mail_messages');
        $GLOBALS['SITE_DB']->drop_table_if_exists('link_tracker');
        $GLOBALS['SITE_DB']->drop_table_if_exists('incoming_uploads');
        $GLOBALS['SITE_DB']->drop_table_if_exists('f_group_member_timeouts');
        $GLOBALS['SITE_DB']->drop_table_if_exists('temp_block_permissions');
        $GLOBALS['SITE_DB']->drop_table_if_exists('cron_caching_requests');
        $GLOBALS['SITE_DB']->drop_table_if_exists('notifications_enabled');
        $GLOBALS['SITE_DB']->drop_table_if_exists('digestives_tin');
        $GLOBALS['SITE_DB']->drop_table_if_exists('digestives_consumed');
        $GLOBALS['SITE_DB']->drop_table_if_exists('unbannable_ip');
        $GLOBALS['SITE_DB']->drop_table_if_exists('alternative_ids');
        $GLOBALS['SITE_DB']->drop_table_if_exists('content_privacy');
        $GLOBALS['SITE_DB']->drop_table_if_exists('content_privacy__members');
        $GLOBALS['SITE_DB']->drop_table_if_exists('task_queue');
        $GLOBALS['SITE_DB']->drop_table_if_exists('comcode_pages');
        $GLOBALS['SITE_DB']->drop_table_if_exists('cached_comcode_pages');
        $GLOBALS['SITE_DB']->drop_table_if_exists('email_bounces');
        $GLOBALS['SITE_DB']->drop_table_if_exists('sitemap_cache');
        $GLOBALS['SITE_DB']->drop_table_if_exists('urls_checked');
        $GLOBALS['SITE_DB']->drop_table_if_exists('content_regions');
        $GLOBALS['SITE_DB']->drop_table_if_exists('post_tokens');

        /* We don't want to get rid of on-disk data when reinstalling
        $zones = find_all_zones(true);
        require_code('files');
        $langs = find_all_langs(true);
        foreach ($zones as $zone) {
            foreach (array_keys($langs) as $lang) {
                deldir_contents(zone_black_magic_filterer(get_custom_file_base() . (($zone == '') ? '' : '/') . $zone . '/pages/comcode_custom/' . $lang, true), true);
            }
        }
        */

        delete_attachments('comcode_page');

        delete_privilege('edit_meta_fields');
        delete_privilege('view_private_content');
        delete_privilege('perform_webstandards_check_by_default');
    }

    /**
     * Install the module.
     *
     * @param  ?integer $upgrade_from What version we're upgrading from (null: new install)
     * @param  ?integer $upgrade_from_hack What hack version we're upgrading from (null: new-install/not-upgrading-from-a-hacked-version)
     */
    public function install($upgrade_from = null, $upgrade_from_hack = null)
    {
        // A lot of "peripheral architectural" tables are defined here. Central ones are defined in the installer -- as they need to be installed before any module.
        // This is always the first module to be installed.

        if (is_null($upgrade_from)) { // These are only for fresh installs
            set_value('version', float_to_raw_string(cms_version_number()));
            set_value('cns_version', float_to_raw_string(cms_version_number()));

            $GLOBALS['SITE_DB']->create_table('menu_items', array(
                'id' => '*AUTO',
                'i_menu' => 'ID_TEXT', // Foreign key in the future - currently it just binds together
                'i_order' => 'INTEGER',
                'i_parent' => '?AUTO_LINK',
                'i_caption' => 'SHORT_TRANS__COMCODE',
                'i_caption_long' => 'SHORT_TRANS__COMCODE',
                'i_url' => 'SHORT_TEXT', // Supports page-links
                'i_check_permissions' => 'BINARY',
                'i_expanded' => 'BINARY',
                'i_new_window' => 'BINARY',
                'i_include_sitemap' => 'SHORT_INTEGER',
                'i_page_only' => 'ID_TEXT', // Only show up if the page is this (allows page specific menus)
                'i_theme_img_code' => 'ID_TEXT',
            ));
            $GLOBALS['SITE_DB']->create_index('menu_items', 'menu_extraction', array('i_menu'));

            $GLOBALS['SITE_DB']->create_table('trackbacks', array(
                'id' => '*AUTO',
                'trackback_for_type' => 'ID_TEXT',
                'trackback_for_id' => 'ID_TEXT',
                'trackback_ip' => 'IP',
                'trackback_time' => 'TIME',
                'trackback_url' => 'SHORT_TEXT',
                'trackback_title' => 'SHORT_TEXT',
                'trackback_excerpt' => 'LONG_TEXT',
                'trackback_name' => 'SHORT_TEXT'
            ));
            $GLOBALS['SITE_DB']->create_index('trackbacks', 'trackback_for_type', array('trackback_for_type'));
            $GLOBALS['SITE_DB']->create_index('trackbacks', 'trackback_for_id', array('trackback_for_id'));
            $GLOBALS['SITE_DB']->create_index('trackbacks', 'trackback_time', array('trackback_time'));

            $GLOBALS['SITE_DB']->create_table('captchas', array(
                'si_session_id' => '*ID_TEXT',
                'si_time' => 'TIME',
                'si_code' => 'ID_TEXT'
            ));
            $GLOBALS['SITE_DB']->create_index('captchas', 'si_time', array('si_time'));

            $GLOBALS['SITE_DB']->create_table('member_tracking', array(
                'mt_member_id' => '*MEMBER',
                'mt_cache_username' => 'ID_TEXT',
                'mt_time' => '*TIME',
                'mt_page' => '*ID_TEXT',
                'mt_type' => '*ID_TEXT',
                'mt_id' => '*ID_TEXT'
            ));
            $GLOBALS['SITE_DB']->create_index('member_tracking', 'mt_page', array('mt_page'));
            $GLOBALS['SITE_DB']->create_index('member_tracking', 'mt_id', array('mt_page', 'mt_id', 'mt_type'));

            $GLOBALS['SITE_DB']->create_table('cache_on', array(
                'cached_for' => '*ID_TEXT',
                'cache_on' => 'LONG_TEXT',
                'special_cache_flags' => 'INTEGER',
                'cache_ttl' => 'INTEGER',
            ));

            $GLOBALS['SITE_DB']->create_table('webstandards_checked_once', array(
                'hash' => '*SHORT_TEXT'
            ), false, false, true);

            $GLOBALS['SITE_DB']->create_table('edit_pings', array(
                'id' => '*AUTO',
                'the_page' => 'ID_TEXT',
                'the_type' => 'ID_TEXT',
                'the_id' => 'ID_TEXT',
                'the_time' => 'TIME',
                'the_member' => 'MEMBER'
            ));

            $GLOBALS['SITE_DB']->create_table('values_elective', array(
                'the_name' => '*ID_TEXT',
                'the_value' => 'LONG_TEXT',
                'date_and_time' => 'TIME',
            ));
            set_value('call_home', strval(post_param_integer('advertise_on', 0)), true); // Relayed from installer

            $GLOBALS['SITE_DB']->create_table('tutorial_links', array(
                'the_name' => '*ID_TEXT',
                'the_value' => 'LONG_TEXT',
            ));

            $GLOBALS['SITE_DB']->create_table('member_privileges', array(
                'member_id' => '*INTEGER',
                'privilege' => '*ID_TEXT',
                'the_page' => '*ID_TEXT',
                'module_the_name' => '*ID_TEXT',
                'category_name' => '*ID_TEXT',
                'the_value' => 'BINARY',
                'active_until' => '?TIME',
            ), false, false, true);

            $GLOBALS['SITE_DB']->create_table('member_zone_access', array(
                'zone_name' => '*ID_TEXT',
                'member_id' => '*MEMBER',
                'active_until' => '?TIME',
            ));
            $GLOBALS['SITE_DB']->create_index('member_zone_access', 'mzazone_name', array('zone_name'));
            $GLOBALS['SITE_DB']->create_index('member_zone_access', 'mzamember_id', array('member_id'));

            $GLOBALS['SITE_DB']->create_table('member_page_access', array(
                'page_name' => '*ID_TEXT',
                'zone_name' => '*ID_TEXT',
                'member_id' => '*MEMBER',
                'active_until' => '?TIME',
            ));
            $GLOBALS['SITE_DB']->create_index('member_page_access', 'mzaname', array('page_name', 'zone_name'));
            $GLOBALS['SITE_DB']->create_index('member_page_access', 'mzamember_id', array('member_id'));

            $GLOBALS['SITE_DB']->create_table('member_category_access', array(
                'module_the_name' => '*ID_TEXT',
                'category_name' => '*ID_TEXT',
                'member_id' => '*MEMBER',
                'active_until' => '?TIME',
            ));
            $GLOBALS['SITE_DB']->create_index('member_category_access', 'mcaname', array('module_the_name', 'category_name'));
            $GLOBALS['SITE_DB']->create_index('member_category_access', 'mcamember_id', array('member_id'));

            $GLOBALS['SITE_DB']->create_table('autosave', array(
                'id' => '*AUTO',
                'a_member_id' => 'MEMBER',
                'a_key' => 'LONG_TEXT',
                'a_value' => 'LONG_TEXT',
                'a_time' => 'TIME',
            ));
            $GLOBALS['SITE_DB']->create_index('autosave', 'myautosaves', array('a_member_id'));

            $GLOBALS['SITE_DB']->create_table('messages_to_render', array(
                'id' => '*AUTO',
                'r_session_id' => 'ID_TEXT',
                'r_message' => 'LONG_TEXT',
                'r_type' => 'ID_TEXT',
                'r_time' => 'TIME',
            ));
            $GLOBALS['SITE_DB']->create_index('messages_to_render', 'forsession', array('r_session_id'));

            $GLOBALS['SITE_DB']->create_table('url_title_cache', array(
                'id' => '*AUTO',
                't_url' => 'URLPATH',
                't_title' => 'SHORT_TEXT',
                't_meta_title' => 'LONG_TEXT',
                't_keywords' => 'LONG_TEXT',
                't_description' => 'LONG_TEXT',
                't_image_url' => 'URLPATH',
                't_mime_type' => 'ID_TEXT',
                // oEmbed...
                't_json_discovery' => 'URLPATH',
                't_xml_discovery' => 'URLPATH',
            ));
            $GLOBALS['SITE_DB']->create_index('url_title_cache', 't_url', array('t_url'));

            $GLOBALS['SITE_DB']->create_table('rating', array(
                'id' => '*AUTO',
                'rating_for_type' => 'ID_TEXT',
                'rating_for_id' => 'ID_TEXT',
                'rating_member' => 'MEMBER',
                'rating_ip' => 'IP',
                'rating_time' => 'TIME',
                'rating' => 'SHORT_INTEGER'
            ));
            $GLOBALS['SITE_DB']->create_index('rating', 'alt_key', array('rating_for_type', 'rating_for_id'));
            $GLOBALS['SITE_DB']->create_index('rating', 'rating_for_id', array('rating_for_id'));

            $GLOBALS['SITE_DB']->create_table('comcode_pages', array(
                'the_zone' => '*ID_TEXT',
                'the_page' => '*ID_TEXT',
                'p_parent_page' => 'ID_TEXT',
                'p_validated' => 'BINARY',
                'p_edit_date' => '?TIME',
                'p_add_date' => 'TIME',
                'p_submitter' => 'MEMBER',
                'p_show_as_edit' => 'BINARY',
                'p_order' => 'INTEGER',
            ));
            $GLOBALS['SITE_DB']->create_index('comcode_pages', 'p_submitter', array('p_submitter'));
            $GLOBALS['SITE_DB']->create_index('comcode_pages', 'p_add_date', array('p_add_date'));
            $GLOBALS['SITE_DB']->create_index('comcode_pages', 'p_validated', array('p_validated'));

            $fields = array(
                'the_zone' => '*ID_TEXT',
                'the_page' => '*ID_TEXT',
                'string_index' => 'LONG_TRANS__COMCODE',
                'the_theme' => '*ID_TEXT',
                'cc_page_title' => '?SHORT_TRANS'
            );
            if (strpos(get_db_type(), 'sqlserver') !== false) { // Full-text search requires a single key
                $fields = array(
                        'id' => '*AUTO',
                        'the_zone' => 'ID_TEXT',
                        'the_page' => 'ID_TEXT',
                        'the_theme' => 'ID_TEXT',
                    ) + $fields;
            }
            $GLOBALS['SITE_DB']->create_table('cached_comcode_pages', $fields);

            $GLOBALS['SITE_DB']->create_index('cached_comcode_pages', 'ftjoin_ccpt', array('cc_page_title'));
            $GLOBALS['SITE_DB']->create_index('cached_comcode_pages', 'ftjoin_ccsi', array('string_index'));
            $GLOBALS['SITE_DB']->create_index('cached_comcode_pages', 'ccp_join', array('the_page', 'the_zone'));
        }

        // A lot of core upgrade is also here. When absolutely necessary it is put in upgrade.php.

        if (($upgrade_from < 10) || (is_null($upgrade_from))) {
            $GLOBALS['SITE_DB']->create_table('url_id_monikers', array(
                'id' => '*AUTO',
                'm_resource_page' => 'ID_TEXT',
                'm_resource_type' => 'ID_TEXT',
                'm_resource_id' => 'ID_TEXT',
                'm_moniker' => 'SHORT_TEXT',
                'm_moniker_reversed' => 'SHORT_TEXT', // For indexed tail-end searching
                'm_deprecated' => 'BINARY',
                'm_manually_chosen' => 'BINARY',
            ));
            $GLOBALS['SITE_DB']->create_index('url_id_monikers', 'uim_moniker', array('m_moniker'));
            $GLOBALS['SITE_DB']->create_index('url_id_monikers', 'uim_monrev', array('m_moniker_reversed'));

            $GLOBALS['SITE_DB']->create_table('review_supplement', array(
                'r_post_id' => '*AUTO_LINK',
                'r_rating_type' => '*ID_TEXT',
                'r_rating' => 'SHORT_INTEGER',
                'r_topic_id' => 'AUTO_LINK',
                'r_rating_for_id' => 'ID_TEXT',
                'r_rating_for_type' => 'ID_TEXT',
            ));
            $GLOBALS['SITE_DB']->create_index('review_supplement', 'rating_for_id', array('r_rating_for_id'));

            $GLOBALS['SITE_DB']->create_table('logged_mail_messages', array(
                'id' => '*AUTO',
                'm_subject' => 'LONG_TEXT', // While data for a subject would be tied to SHORT_TEXT, a language string could bump it up higher
                'm_message' => 'LONG_TEXT',
                'm_to_email' => 'LONG_TEXT',
                'm_extra_cc_addresses' => 'LONG_TEXT',
                'm_extra_bcc_addresses' => 'LONG_TEXT',
                'm_join_time' => '?TIME',
                'm_to_name' => 'LONG_TEXT',
                'm_from_email' => 'SHORT_TEXT',
                'm_from_name' => 'SHORT_TEXT',
                'm_priority' => 'SHORT_INTEGER',
                'm_attachments' => 'LONG_TEXT',
                'm_no_cc' => 'BINARY',
                'm_as' => 'MEMBER',
                'm_as_admin' => 'BINARY',
                'm_in_html' => 'BINARY',
                'm_date_and_time' => 'TIME',
                'm_member_id' => 'MEMBER',
                'm_url' => 'LONG_TEXT',
                'm_queued' => 'BINARY',
                'm_template' => 'ID_TEXT',
            ));
            $GLOBALS['SITE_DB']->create_index('logged_mail_messages', 'recentmessages', array('m_date_and_time'));
            $GLOBALS['SITE_DB']->create_index('logged_mail_messages', 'queued', array('m_queued'));
            $GLOBALS['SITE_DB']->create_index('logged_mail_messages', 'combo', array('m_date_and_time', 'm_queued')); // Used for number-sent-within querying

            $GLOBALS['SITE_DB']->create_table('link_tracker', array(
                'id' => '*AUTO',
                'c_date_and_time' => 'TIME',
                'c_member_id' => 'MEMBER',
                'c_ip_address' => 'IP',
                'c_url' => 'URLPATH',
            ));

            $GLOBALS['SITE_DB']->create_table('incoming_uploads', array(
                'id' => '*AUTO',
                'i_submitter' => 'MEMBER',
                'i_date_and_time' => 'TIME',
                'i_orig_filename' => 'URLPATH',
                'i_save_url' => 'SHORT_TEXT'
            ));
        }

        if (($upgrade_from < 11) && (!is_null($upgrade_from))) {
            $GLOBALS['SITE_DB']->query_update('comcode_pages', array('p_submitter' => 2), array('p_submitter' => $GLOBALS['FORUM_DRIVER']->get_guest_id()));
        }

        if ((is_null($upgrade_from)) || ($upgrade_from < 12)) {
            $GLOBALS['SITE_DB']->drop_table_if_exists('cache');
            $GLOBALS['SITE_DB']->create_table('cache', array(
                'id' => '*AUTO',
                'cached_for' => 'ID_TEXT',
                'identifier' => 'MINIID_TEXT',
                'the_theme' => 'MINIID_TEXT', // *Always* set
                'staff_status' => '?BINARY', // May be null
                'the_member' => '?MEMBER', // May be null
                'groups' => 'SHORT_TEXT', // May be blank
                'is_bot' => '?BINARY', // May be null
                'timezone' => 'MINIID_TEXT', // May be blank
                'lang' => 'LANGUAGE_NAME', // *Always* set
                'the_value' => 'LONG_TEXT',
                'dependencies' => 'LONG_TEXT',
                'date_and_time' => 'TIME',
            ));
            $GLOBALS['SITE_DB']->create_index('cache', 'cached_ford', array('date_and_time'));
            $GLOBALS['SITE_DB']->create_index('cache', 'cached_fore', array('cached_for'));
            $GLOBALS['SITE_DB']->create_index('cache', 'cached_forh', array('the_theme'));
        }

        if ((is_null($upgrade_from)) || ($upgrade_from < 13)) {
            if (!$GLOBALS['SITE_DB']->table_exists('f_group_member_timeouts')) {
                $GLOBALS['SITE_DB']->create_table('f_group_member_timeouts', array(
                    'member_id' => '*MEMBER',
                    'group_id' => '*GROUP',
                    'timeout' => 'TIME',
                ));
            }
        }

        if ((!is_null($upgrade_from)) && ($upgrade_from < 13)) {
            $GLOBALS['SITE_DB']->create_index('translate', 'equiv_lang', array('text_original(4)'));
            $GLOBALS['SITE_DB']->create_index('translate', 'decache', array('text_parsed(2)'));
        }

        if ((!is_null($upgrade_from)) && ($upgrade_from >= 10) && ($upgrade_from < 14)) {
            $GLOBALS['SITE_DB']->drop_table_if_exists('tracking');
            $GLOBALS['SITE_DB']->add_table_field('logged_mail_messages', 'm_template', 'ID_TEXT');
        }

        if ((!is_null($upgrade_from)) && ($upgrade_from == 14)) {
            $GLOBALS['SITE_DB']->alter_table_field('digestives_tin', 'd_from_member_id', '?MEMBER');
        }

        if ((is_null($upgrade_from)) || ($upgrade_from < 14)) {
            $GLOBALS['SITE_DB']->create_table('temp_block_permissions', array(
                'id' => '*AUTO',
                'p_session_id' => 'ID_TEXT',
                'p_block_constraints' => 'LONG_TEXT',
                'p_time' => 'TIME',
            ));
            $GLOBALS['SITE_DB']->create_index('temp_block_permissions', 'p_session_id', array('p_session_id'));

            $GLOBALS['SITE_DB']->create_table('cron_caching_requests', array(
                'id' => '*AUTO',
                'c_codename' => 'ID_TEXT',
                'c_map' => 'LONG_TEXT',
                'c_lang' => 'LANGUAGE_NAME',
                'c_theme' => 'ID_TEXT',
                'c_staff_status' => '?BINARY',
                'c_member' => '?MEMBER',
                'c_groups' => 'SHORT_TEXT',
                'c_is_bot' => '?BINARY',
                'c_timezone' => 'MINIID_TEXT',
                'c_store_as_tempcode' => 'BINARY',
            ));
            $GLOBALS['SITE_DB']->create_index('cron_caching_requests', 'c_compound', array('c_codename', 'c_theme', 'c_lang', 'c_timezone'));
            $GLOBALS['SITE_DB']->create_index('cron_caching_requests', 'c_is_bot', array('c_is_bot'));
            $GLOBALS['SITE_DB']->create_index('cron_caching_requests', 'c_store_as_tempcode', array('c_store_as_tempcode'));

            $GLOBALS['SITE_DB']->create_table('notifications_enabled', array(
                'id' => '*AUTO',
                'l_member_id' => 'MEMBER',
                'l_notification_code' => 'ID_TEXT',
                'l_code_category' => 'SHORT_TEXT',
                'l_setting' => 'INTEGER',
            ));
            $GLOBALS['SITE_DB']->create_index('notifications_enabled', 'l_member_id', array('l_member_id', 'l_notification_code'));
            $GLOBALS['SITE_DB']->create_index('notifications_enabled', 'l_code_category', array('l_code_category'));
            $GLOBALS['SITE_DB']->create_index('notifications_enabled', 'l_notification_code', array('l_notification_code'));

            $GLOBALS['SITE_DB']->create_table('digestives_tin', array( // Notifications queued up ready for the regular digest email
                'id' => '*AUTO',
                'd_subject' => 'LONG_TEXT',
                'd_message' => 'LONG_TRANS__COMCODE',
                'd_from_member_id' => '?MEMBER',
                'd_to_member_id' => 'MEMBER',
                'd_priority' => 'SHORT_INTEGER',
                'd_no_cc' => 'BINARY',
                'd_date_and_time' => 'TIME',
                'd_notification_code' => 'ID_TEXT',
                'd_code_category' => 'SHORT_TEXT',
                'd_frequency' => 'INTEGER', // e.g. A_DAILY_EMAIL_DIGEST
                'd_read' => 'BINARY',
            ));
            $GLOBALS['SITE_DB']->create_index('digestives_tin', 'd_date_and_time', array('d_date_and_time'));
            $GLOBALS['SITE_DB']->create_index('digestives_tin', 'd_frequency', array('d_frequency'));
            $GLOBALS['SITE_DB']->create_index('digestives_tin', 'd_to_member_id', array('d_to_member_id'));
            $GLOBALS['SITE_DB']->create_index('digestives_tin', 'd_read', array('d_read'));
            $GLOBALS['SITE_DB']->create_index('digestives_tin', 'unread', array('d_to_member_id', 'd_read'));
            $GLOBALS['SITE_DB']->create_table('digestives_consumed', array(
                'c_member_id' => '*MEMBER',
                'c_frequency' => '*INTEGER', // e.g. A_DAILY_EMAIL_DIGEST
                'c_time' => 'TIME',
            ));
        }

        if ((!is_null($upgrade_from)) && ($upgrade_from < 16)) {
            $GLOBALS['SITE_DB']->delete_index_if_exists('cron_caching_requests', 'c_in_panel');
            $GLOBALS['SITE_DB']->delete_index_if_exists('cron_caching_requests', 'c_interlock');
            $GLOBALS['SITE_DB']->delete_table_field('cron_caching_requests', 'c_interlock');
            $GLOBALS['SITE_DB']->delete_table_field('cron_caching_requests', 'c_in_panel');

            $GLOBALS['SITE_DB']->delete_index_if_exists('rating', 'rating_for_id');
            $GLOBALS['SITE_DB']->create_index('rating', 'rating_for_id', array('rating_for_id'));
        }

        if ((!is_null($upgrade_from)) && ($upgrade_from < 17)) {
            $comcode_lang_fields = array(
                'award_types' => array(
                    'a_description',
                ),
                'text' => array(
                    'the_message',
                ),
                'community_billboard' => array(
                    'the_message',
                ),
                'match_key_messages' => array(
                    'k_message',
                ),
                'menu_items' => array(
                    'i_caption',
                    'i_caption_long',
                ),
                'cached_comcode_pages' => array(
                    'string_index',
                ),
                'authors' => array(
                    'description',
                    'skills'
                ),
                'banners' => array(
                    'caption',
                ),
                'calendar_types' => array(
                    't_title',
                ),
                'calendar_events' => array(
                    'e_title',
                    'e_content',
                ),
                'catalogue_efv_long_trans' => array(
                    'cv_value',
                ),
                'catalogue_efv_short_trans' => array(
                    'cv_value',
                ),
                'catalogues' => array(
                    'c_description',
                ),
                'catalogue_categories' => array(
                    'cc_description',
                ),
                'wiki_posts' => array(
                    'the_message',
                ),
                'wiki_pages' => array(
                    'description',
                ),
                'cedi_posts' => array(
                    'the_message',
                ),
                'cedi_pages' => array(
                    'description',
                ),
                'chat_messages' => array(
                    'the_message',
                ),
                'download_downloads' => array(
                    'description',
                    'comments',
                    'additional_details',
                ),
                'download_categories' => array(
                    'description',
                ),
                'videos' => array(
                    'comments',
                    'description',
                ),
                'galleries' => array(
                    'description',
                    'teaser',
                    'fullname',
                ),
                'images' => array(
                    'comments',
                    'description',
                ),
                'iotd' => array(
                    'i_title',
                    'caption',
                ),
                'news' => array(
                    'title',
                    'news',
                    'news_article',
                ),
                'chargelog' => array(
                    'reason',
                ),
                'gifts' => array(
                    'reason',
                ),
                'pstore_customs' => array(
                    'c_description',
                ),
                'pstore_permissions' => array(
                    'p_description',
                ),
                'poll' => array(
                    'question',
                    'option1',
                    'option2',
                    'option3',
                    'option4',
                    'option5',
                    'option6',
                    'option7',
                    'option8',
                    'option9',
                    'option10',
                ),
                'quiz_question_answers' => array(
                    'q_answer_text',
                ),
                'quizzes' => array(
                    'q_start_text',
                    'q_end_text',
                    'q_end_text_fail',
                ),
                'bookable' => array(
                    'title',
                    'description',
                    'categorisation',
                ),
                'bookable_blacked' => array(
                    'blacked_explanation',
                ),
                'f_posts' => array(
                    'p_post',
                ),
                'f_topics' => array(
                    't_cache_first_post',
                ),
                'f_members' => array(
                    'm_signature',
                    'm_pt_rules_text',
                ),
                'f_forums' => array(
                    'f_description',
                    'f_intro_question',
                ),
                'f_usergroup_subs' => array(
                    's_description',
                ),
            );
            $GLOBALS['NO_DB_SCOPE_CHECK'] = true;
            $custom_fields = $GLOBALS['SITE_DB']->query_select('f_custom_fields', array('id'), null, 'WHERE cf_type IN (\'short_trans\',\'long_trans\')');
            $comcode_lang_fields['f_member_custom_fields'] = array();
            foreach ($custom_fields as $custom_field) {
                $comcode_lang_fields['f_member_custom_fields'][] = 'field_' . strval($custom_field['id']);
            }
            foreach ($comcode_lang_fields as $table => $fields) {
                foreach ($fields as $field) {
                    $sql = 'UPDATE ' . $GLOBALS['SITE_DB']->get_table_prefix() . 'db_meta SET m_type=' . db_function('CONCAT', array('m_type', '\'__COMCODE\'')) . ' WHERE ' . db_string_equal_to('m_table', $table) . ' AND ' . db_string_equal_to('m_name', $field) . ' AND m_type NOT LIKE \'' . db_encode_like('%__COMCODE') . '\'';
                    $GLOBALS['SITE_DB']->query($sql);
                }
            }

            $GLOBALS['SITE_DB']->rename_table('security_images', 'captchas');
            $GLOBALS['SITE_DB']->rename_table('validated_once', 'webstandards_checked_once');
            $GLOBALS['SITE_DB']->rename_table('long_values', 'values_elective');

            $GLOBALS['SITE_DB']->change_primary_key('member_privileges', array('member_id', 'privilege', 'the_page', 'module_the_name', 'category_name'));
            $GLOBALS['SITE_DB']->change_primary_key('member_zone_access', array('member_id', 'zone_name'));
            $GLOBALS['SITE_DB']->change_primary_key('member_page_access', array('member_id', 'page_name', 'zone_name'));
            $GLOBALS['SITE_DB']->change_primary_key('member_category_access', array('member_id', 'module_the_name', 'category_name'));

            $GLOBALS['SITE_DB']->alter_table_field('member_privileges', 'active_until', '?TIME');
            $GLOBALS['SITE_DB']->alter_table_field('member_zone_access', 'active_until', '?TIME');
            $GLOBALS['SITE_DB']->alter_table_field('member_page_access', 'active_until', '?TIME');
            $GLOBALS['SITE_DB']->alter_table_field('member_category_access', 'active_until', '?TIME');

            $GLOBALS['SITE_DB']->query_update('values', array('the_name' => 'setupwizard_completed'), array('the_name' => 'setup_wizard_completed'), '', 1);

            $GLOBALS['SITE_DB']->promote_text_field_to_comcode('digestives_tin', 'd_message', 'id', 4);
            $GLOBALS['SITE_DB']->add_table_field('digestives_tin', 'd_read', 'BINARY');
            $GLOBALS['SITE_DB']->query('UPDATE ' . get_table_prefix() . 'notifications_enabled SET l_setting=l_setting+' . strval(A_WEB_NOTIFICATION) . ' WHERE l_setting<>0');
            $GLOBALS['SITE_DB']->create_index('digestives_tin', 'd_read', array('d_read'));
            $GLOBALS['SITE_DB']->create_index('digestives_tin', 'unread', array('d_to_member_id', 'd_read'));

            rename_config_option('ocp_show_conceded_mode_link', 'show_conceded_mode_link');
            rename_config_option('ocp_show_personal_adminzone_link', 'show_personal_adminzone_link');
            rename_config_option('ocp_show_personal_last_visit', 'show_personal_last_visit');
            rename_config_option('ocp_show_personal_sub_links', 'show_personal_sub_links');
            rename_config_option('ocp_show_personal_usergroup', 'show_personal_usergroup');
            rename_config_option('ocp_show_staff_page_actions', 'show_staff_page_actions');
            rename_config_option('ocp_show_su', 'show_su');
            rename_config_option('ocp_show_avatar', 'show_avatar');

            $GLOBALS['SITE_DB']->add_table_field('logged_mail_messages', 'm_extra_cc_addresses', 'LONG_TEXT', serialize(array()));
            $GLOBALS['SITE_DB']->add_table_field('logged_mail_messages', 'm_extra_bcc_addresses', 'LONG_TEXT', serialize(array()));
            $GLOBALS['SITE_DB']->add_table_field('logged_mail_messages', 'm_join_time', '?TIME');

            $GLOBALS['SITE_DB']->query_delete('url_title_cache');
            $GLOBALS['SITE_DB']->add_table_field('url_title_cache', 't_meta_title', 'LONG_TEXT');
            $GLOBALS['SITE_DB']->add_table_field('url_title_cache', 't_keywords', 'LONG_TEXT');
            $GLOBALS['SITE_DB']->add_table_field('url_title_cache', 't_description', 'LONG_TEXT');
            $GLOBALS['SITE_DB']->add_table_field('url_title_cache', 't_image_url', 'URLPATH');
            $GLOBALS['SITE_DB']->add_table_field('url_title_cache', 't_mime_type', 'ID_TEXT');
            $GLOBALS['SITE_DB']->add_table_field('url_title_cache', 't_json_discovery', 'URLPATH');
            $GLOBALS['SITE_DB']->add_table_field('url_title_cache', 't_xml_discovery', 'URLPATH');

            $GLOBALS['SITE_DB']->add_table_field('menu_items', 'i_include_sitemap', 'SHORT_INTEGER', 0);

            $GLOBALS['SITE_DB']->delete_table_field('zones', 'zone_displayed_in_menu');
            $GLOBALS['SITE_DB']->delete_table_field('zones', 'zone_wide');

            $GLOBALS['SITE_DB']->add_table_field('url_id_monikers', 'm_manually_chosen', 'BINARY');
            $GLOBALS['SITE_DB']->add_table_field('url_id_monikers', 'm_moniker_reversed', 'SHORT_TEXT');
            if (strpos(get_db_type(), 'mysql') !== false) {
                $GLOBALS['SITE_DB']->query('UPDATE ' . get_table_prefix() . 'url_id_monikers SET m_moniker_reversed=REVERSE(m_moniker)');
            } else {
                $start = 0;
                $max = 500;
                do {
                    $url_id_monikers = $GLOBALS['SITE_DB']->query_select('url_id_monikers', array('DISTINCT m_moniker'), null, '', $max, $start);
                    foreach ($url_id_monikers as $url_id_moniker) {
                        $GLOBALS['SITE_DB']->query_update('url_id_monikers', array('m_moniker_reversed' => strrev($url_id_moniker['m_moniker'])), array('m_moniker' => $url_id_moniker['m_moniker']));
                    }
                    $start += $max;
                } while (count($url_id_monikers) == $max);
            }
            $GLOBALS['SITE_DB']->create_index('url_id_monikers', 'uim_monrev', array('m_moniker_reversed'));

            $GLOBALS['SITE_DB']->alter_table_field('captchas', 'si_session_id', '*ID_TEXT');
            $GLOBALS['SITE_DB']->alter_table_field('captchas', 'si_code', 'ID_TEXT');
            $GLOBALS['SITE_DB']->alter_table_field('messages_to_render', 'r_session_id', 'ID_TEXT');
            $GLOBALS['SITE_DB']->alter_table_field('temp_block_permissions', 'p_session_id', 'ID_TEXT');

            $GLOBALS['SITE_DB']->delete_index_if_exists('cache', 'cached_fore2');
            $GLOBALS['SITE_DB']->delete_index_if_exists('cache', 'cached_forf');
            $GLOBALS['SITE_DB']->delete_index_if_exists('cache', 'cached_forg');
            $GLOBALS['SITE_DB']->alter_table_field('cache', 'langs_required', 'LONG_TEXT', 'dependencies');
            $GLOBALS['SITE_DB']->add_table_field('cache', 'staff_status', '?BINARY');
            $GLOBALS['SITE_DB']->add_table_field('cache', 'the_member', '?MEMBER');
            $GLOBALS['SITE_DB']->add_table_field('cache', 'groups', 'SHORT_TEXT');
            $GLOBALS['SITE_DB']->add_table_field('cache', 'is_bot', '?BINARY');
            $GLOBALS['SITE_DB']->add_table_field('cache', 'timezone', 'MINIID_TEXT');
            $GLOBALS['SITE_DB']->add_auto_key('cache');

            $GLOBALS['SITE_DB']->add_table_field('cron_caching_requests', 'c_staff_status', '?BINARY');
            $GLOBALS['SITE_DB']->add_table_field('cron_caching_requests', 'c_member', '?MEMBER');
            $GLOBALS['SITE_DB']->add_table_field('cron_caching_requests', 'c_groups', 'SHORT_TEXT');
            $GLOBALS['SITE_DB']->alter_table_field('cron_caching_requests', 'c_is_bot', '?BINARY');

            $GLOBALS['SITE_DB']->add_table_field('cache_on', 'special_cache_flags', 'INTEGER');

            $GLOBALS['SITE_DB']->add_table_field('comcode_pages', 'p_order', 'INTEGER');

            $GLOBALS['SITE_DB']->create_table('seo_meta_keywords', array(
                'id' => '*AUTO',
                'meta_for_type' => 'ID_TEXT',
                'meta_for_id' => 'ID_TEXT',
                'meta_keyword' => 'SHORT_TRANS',
            ));

            $start = 0;
            $max = 300;
            do {
                $keywords = $GLOBALS['SITE_DB']->query_select('seo_meta', array('meta_for_type', 'meta_for_id', 'meta_keywords'), null, '', $max, $start);
                if (!is_null($keywords)) {
                    foreach ($keywords as $_keyword) {
                        $_keywords = array_unique(explode(',', trim(get_translated_text($_keyword['meta_keywords']))));
                        foreach ($_keywords as $keyword) {
                            if (trim($keyword) == '') {
                                continue;
                            }

                            $GLOBALS['SITE_DB']->query_insert('seo_meta_keywords', array(
                                    'meta_for_type' => $_keyword['meta_for_type'],
                                    'meta_for_id' => $_keyword['meta_for_id'],
                                ) + insert_lang('meta_keyword', $keyword, 2));
                        }
                        delete_lang($_keyword['meta_keywords']);
                    }
                    $start += $max;
                }
            } while (count($keywords) > 0);

            $GLOBALS['SITE_DB']->delete_table_field('seo_meta', 'meta_keywords');

            $GLOBALS['SITE_DB']->drop_table_if_exists('translate_history');

            $GLOBALS['SITE_DB']->rename_table('sitewatchlist', 'staff_website_monitoring');
            $GLOBALS['SITE_DB']->alter_table_field('staff_website_monitoring', 'siteurl', 'URLPATH', 'site_url');
            $GLOBALS['SITE_DB']->rename_table('stafflinks', 'staff_links');
            $GLOBALS['SITE_DB']->rename_table('customtasks', 'staff_checklist_cus_tasks');
            $GLOBALS['SITE_DB']->alter_table_field('staff_checklist_cus_tasks', 'tasktitle', 'SHORT_TEXT', 'task_title');
            $GLOBALS['SITE_DB']->alter_table_field('staff_checklist_cus_tasks', 'datetimeadded', 'TIME', 'add_date');
            $GLOBALS['SITE_DB']->alter_table_field('staff_checklist_cus_tasks', 'recurinterval', 'INTEGER', 'recur_interval');
            $GLOBALS['SITE_DB']->alter_table_field('staff_checklist_cus_tasks', 'recurevery', 'ID_TEXT', 'recur_every');
            $GLOBALS['SITE_DB']->alter_table_field('staff_checklist_cus_tasks', 'taskisdone', '?TIME', 'task_is_done');

            $GLOBALS['SITE_DB']->delete_index_if_exists('member_privileges', 'mspname');
            $GLOBALS['SITE_DB']->delete_index_if_exists('member_privileges', 'mspmember_id');

            $GLOBALS['SITE_DB']->delete_index_if_exists('sessions', 'the_user');
            $GLOBALS['SITE_DB']->create_index('sessions', 'member_id', array('member_id'));

            $GLOBALS['SITE_DB']->delete_index_if_exists('url_id_monikers', 'uim_pagelink');

            $GLOBALS['SITE_DB']->delete_index_if_exists('translate', '#search');
            $GLOBALS['SITE_DB']->create_index('translate', '#tsearch', array('text_original'));

            $GLOBALS['SITE_DB']->delete_index_if_exists('sessions', 'userat');
            $GLOBALS['SITE_DB']->create_index('sessions', 'userat', array('the_zone', 'the_page', 'the_id'));

            $GLOBALS['SITE_DB']->create_index('seo_meta_keywords', 'keywords_alt_key', array('meta_for_type', 'meta_for_id'));
            $GLOBALS['SITE_DB']->create_index('seo_meta_keywords', 'ftjoin_dmeta_keywords', array('meta_keyword'));

            $GLOBALS['SITE_DB']->create_index('group_privileges', 'group_id', array('group_id'));

            $GLOBALS['SITE_DB']->delete_index_if_exists('cache', 'cached_forf');

            $GLOBALS['SITE_DB']->delete_index_if_exists('url_title_cache', 't_url');
            $GLOBALS['SITE_DB']->create_index('url_title_cache', 't_url', array('t_url'));
        }

        if ((is_null($upgrade_from)) || ($upgrade_from < 17)) {
            $GLOBALS['SITE_DB']->create_table('alternative_ids', array( // Needs to be first, as install_create_custom_field needs it
                'resource_type' => '*ID_TEXT',
                'resource_id' => '*ID_TEXT',
                'resource_moniker' => 'ID_TEXT',
                'resource_label' => 'SHORT_TEXT',
                'resource_guid' => 'ID_TEXT',
                'resource_resource_fs_hook' => 'ID_TEXT',
            ));
            $GLOBALS['SITE_DB']->create_index('alternative_ids', 'resource_guid', array('resource_guid'));
            $GLOBALS['SITE_DB']->create_index('alternative_ids', 'resource_label', array('resource_label'/*, 'resource_type'key would be too long*/));
            $GLOBALS['SITE_DB']->create_index('alternative_ids', 'resource_moniker', array('resource_moniker', 'resource_type'));
            //$GLOBALS['SITE_DB']->create_index('alternative_ids', 'resource_label_uniqueness', array('resource_label', 'resource_resource_fs_hook'));key would be too long
            $GLOBALS['SITE_DB']->create_index('alternative_ids', 'resource_moniker_uniq', array('resource_moniker', 'resource_resource_fs_hook'));

            add_privilege('SUBMISSION', 'edit_meta_fields');
            add_privilege('SUBMISSION', 'perform_webstandards_check_by_default');
            $GLOBALS['FORUM_DRIVER']->install_create_custom_field('smart_topic_notification', 20, /*locked=*/1, /*viewable=*/0, /*settable=*/1, /*required=*/0, '', 'tick', 0, '0');

            $GLOBALS['SITE_DB']->create_table('email_bounces', array(
                'id' => '*AUTO',
                'b_email_address' => 'SHORT_TEXT',
                'b_time' => 'TIME',
                'b_subject' => 'SHORT_TEXT',
                'b_body' => 'LONG_TEXT',
            ));
            $GLOBALS['SITE_DB']->create_index('email_bounces', 'b_email_address', array('b_email_address'));
            $GLOBALS['SITE_DB']->create_index('email_bounces', 'b_time', array('b_time'));

            $GLOBALS['SITE_DB']->create_table('content_privacy', array(
                'content_type' => '*ID_TEXT',
                'content_id' => '*ID_TEXT',
                'guest_view' => 'BINARY',
                'member_view' => 'BINARY',
                'friend_view' => 'BINARY'
            ));
            $GLOBALS['SITE_DB']->create_index('content_privacy', 'guest_view', array('guest_view'));
            $GLOBALS['SITE_DB']->create_index('content_privacy', 'member_view', array('member_view'));
            $GLOBALS['SITE_DB']->create_index('content_privacy', 'friend_view', array('friend_view'));
            $GLOBALS['SITE_DB']->create_table('content_privacy__members', array(
                'content_type' => '*ID_TEXT',
                'content_id' => '*ID_TEXT',
                'member_id' => '*MEMBER',
            ));
            add_privilege('SUBMISSION', 'view_private_content', false, true);

            $GLOBALS['SITE_DB']->create_table('task_queue', array(
                'id' => '*AUTO',
                't_title' => 'SHORT_TEXT',
                't_hook' => 'ID_TEXT',
                't_args' => 'LONG_TEXT',
                't_member_id' => 'MEMBER',
                't_secure_ref' => 'ID_TEXT', // Used like a temporary password to initiate the task
                't_send_notification' => 'BINARY',
                't_locked' => 'BINARY',
            ));

            require_code('users_active_actions');
            $admin_user = get_first_admin_user();

            $GLOBALS['SITE_DB']->query_delete('comcode_pages', array(
                'the_zone' => 'site',
                'the_page' => 'userguide_comcode',
            ), '', 1);
            $GLOBALS['SITE_DB']->query_insert('comcode_pages', array(
                'the_zone' => 'site',
                'the_page' => 'userguide_comcode',
                'p_parent_page' => 'help',
                'p_validated' => 1,
                'p_edit_date' => null,
                'p_add_date' => time(),
                'p_submitter' => $admin_user,
                'p_show_as_edit' => 0,
                'p_order' => 0,
            ));

            $GLOBALS['SITE_DB']->query_delete('comcode_pages', array(
                'the_zone' => '',
                'the_page' => 'keymap',
            ), '', 1);
            $GLOBALS['SITE_DB']->query_insert('comcode_pages', array(
                'the_zone' => '',
                'the_page' => 'keymap',
                'p_parent_page' => 'help',
                'p_validated' => 1,
                'p_edit_date' => null,
                'p_add_date' => time(),
                'p_submitter' => $admin_user,
                'p_show_as_edit' => 0,
                'p_order' => 0,
            ));

            $GLOBALS['SITE_DB']->create_index('cached_comcode_pages', '#page_search__combined', array('cc_page_title', 'string_index'));

            $GLOBALS['SITE_DB']->create_table('sitemap_cache', array(
                'page_link' => '*SHORT_TEXT',
                'set_number' => 'INTEGER',
                'add_date' => '?TIME',
                'edit_date' => '?TIME',
                'last_updated' => 'TIME',
                'is_deleted' => 'BINARY',
                'priority' => 'REAL',
                'refreshfreq' => 'ID_TEXT',
                'guest_access' => 'BINARY',
            ), false, false, true);
            $GLOBALS['SITE_DB']->create_index('sitemap_cache', 'set_number', array('set_number', 'last_updated'));
            $GLOBALS['SITE_DB']->create_index('sitemap_cache', 'last_updated', array('last_updated'));
            $GLOBALS['SITE_DB']->create_index('sitemap_cache', 'is_deleted', array('is_deleted'));

            $GLOBALS['SITE_DB']->create_table('urls_checked', array(
                'id' => '*AUTO',
                'url' => 'LONG_TEXT', // Support arbitrary length
                'url_exists' => 'BINARY',
                'url_check_time' => 'TIME',
            ));
            $GLOBALS['SITE_DB']->create_index('urls_checked', 'url', array('url(200)'));

            $GLOBALS['SITE_DB']->create_table('content_regions', array(
                'content_type' => '*ID_TEXT',
                'content_id' => '*ID_TEXT',
                'region' => '*ID_TEXT',
            ));

            $GLOBALS['SITE_DB']->create_table('unbannable_ip', array(
                'ip' => '*IP',
                'note' => 'SHORT_TEXT',
            ));

            $GLOBALS['SITE_DB']->create_index('member_privileges', 'member_privileges_name', array('privilege', 'the_page', 'module_the_name', 'category_name'));
            $GLOBALS['SITE_DB']->create_index('member_privileges', 'member_privileges_member', array('member_id'));

            $GLOBALS['SITE_DB']->create_index('url_id_monikers', 'uim_page_link', array('m_resource_page', 'm_resource_type', 'm_resource_id'));

            $GLOBALS['SITE_DB']->create_index('member_tracking', 'mt_time', array('mt_time'));

            $GLOBALS['SITE_DB']->create_index('edit_pings', 'edit_pings_on', array('the_page', 'the_type', 'the_id'));

            $GLOBALS['SITE_DB']->create_index('comcode_pages', 'p_order', array('p_order'));

            $GLOBALS['SITE_DB']->create_index('cache', 'cached_forf', array('cached_for', 'identifier', 'the_theme', 'lang', 'staff_status', 'the_member'/*, 'groups'So key is not too long*/, 'is_bot'/*, 'timezone'So key is not too long*/));

            $GLOBALS['SITE_DB']->create_index('link_tracker', 'c_url', array('c_url'));

            $GLOBALS['SITE_DB']->create_table('post_tokens', array(
                'token' => '*ID_TEXT',
                'generation_time' => 'TIME',
                'member_id' => 'MEMBER',
                'session_id' => 'ID_TEXT',
                'ip_address' => 'IP',
                'usage_tally' => 'INTEGER',
            ));
            $GLOBALS['SITE_DB']->create_index('post_tokens', 'generation_time', array('generation_time'));
        }

        if ((!is_null($upgrade_from)) && ($upgrade_from < 18)) { // LEGACY 10.0.46
            $GLOBALS['FORUM_DRIVER']->install_edit_custom_field('smart_topic_notification', 'smart_topic_notification', 20, /*locked=*/1, /*viewable=*/0, /*settable=*/1, /*required=*/0, '', 'tick', 0, '0');
        }
    }

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

    public $title;

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

        return null;
    }

    /**
     * Execute the module.
     *
     * @return Tempcode The result of execution.
     */
    public function run()
    {
        $level = get_param_integer('level', 50);

        require_code('json');
        $patreons = json_decode(http_download_file('http://compo.sr/data_custom/patreons.php?level=' . strval($level)));

        $_patreons = array();
        foreach ($patreons as $patron) {
            $_patreons[] = array(
                'NAME' => $patron['name'],
                'USERNAME' => $patron['username'],
                'MONTHLY' => strval($patron['monthly']),
            );
        }

        return do_template('SPONSORS_SCREEN', array('_GUID' => '504a3975e168ac7d32ed78f3fadf2cbe', 'TITLE' => $this->title, 'PATREONS' => $_patreons));
    }
}
