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

/**
 * Module page class.
 */
class Module_admin_addons
{
    /**
     * 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'] = 4;
        $info['locked'] = true;
        $info['update_require_upgrade'] = true;
        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)
    {
        return array(
            'browse' => array('ADDONS', 'menu/adminzone/structure/addons'),
            'modules' => array('MODULE_MANAGEMENT', 'menu/_generic_admin/component'),
            'addon_import' => array('IMPORT_ADDON', 'menu/_generic_admin/import'),
            'addon_export' => array('EXPORT_ADDON', 'menu/_generic_admin/export'),
        );
    }

    /**
     * Uninstall the module.
     */
    public function uninstall()
    {
        $GLOBALS['SITE_DB']->drop_table_if_exists('addons');
        $GLOBALS['SITE_DB']->drop_table_if_exists('addons_files');
        $GLOBALS['SITE_DB']->drop_table_if_exists('addons_dependencies');

        if (!$GLOBALS['DEV_MODE']) {
            require_code('files');
            //deldir_contents(get_custom_file_base() . '/exports/addons', true);    This just messes with the build process and is not useful
        }
    }

    /**
     * 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)
    {
        if (is_null($upgrade_from)) {
            $GLOBALS['SITE_DB']->create_table('addons', array(
                'addon_name' => '*SHORT_TEXT',
                'addon_author' => 'SHORT_TEXT',
                'addon_organisation' => 'SHORT_TEXT',
                'addon_version' => 'SHORT_TEXT',
                'addon_category' => 'SHORT_TEXT',
                'addon_copyright_attribution' => 'SHORT_TEXT',
                'addon_licence' => 'SHORT_TEXT',
                'addon_description' => 'LONG_TEXT',
                'addon_install_time' => 'TIME'
            ), false, false, true);

            $GLOBALS['SITE_DB']->create_table('addons_files', array(
                'id' => '*AUTO', // Because two SHORT_TEXT's as keys exceeds the 500 mysql key limit
                'addon_name' => 'SHORT_TEXT',
                'filename' => 'SHORT_TEXT'
            ));

            $GLOBALS['SITE_DB']->create_table('addons_dependencies', array(
                'id' => '*AUTO', // Because two SHORT_TEXT's as keys exceeds the 500 mysql key limit
                'addon_name' => 'SHORT_TEXT',
                'addon_name_dependant_upon' => 'SHORT_TEXT',
                'addon_name_incompatibility' => 'BINARY' // 0=dependency,1=incompatibility
            ));
        }

        if ((!is_null($upgrade_from)) && ($upgrade_from < 4)) {
            $GLOBALS['SITE_DB']->add_table_field('addons', 'addon_category', 'SHORT_TEXT');
            $GLOBALS['SITE_DB']->add_table_field('addons', 'addon_copyright_attribution', 'SHORT_TEXT');
            $GLOBALS['SITE_DB']->add_table_field('addons', 'addon_licence', 'SHORT_TEXT');
        }
    }

    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()
    {
        $type = get_param_string('type', 'browse');

        require_lang('addons');

        if ($type == 'browse') {
            set_helper_panel_tutorial('tut_adv_configuration');

            $this->title = get_screen_title('ADDONS');
        }

        if ($type == 'addon_export') {
            breadcrumb_set_parents(array(array('_SELF:_SELF:browse', do_lang_tempcode('ADDONS'))));

            $this->title = get_screen_title('EXPORT_ADDON');
        }

        if ($type == '_addon_export') {
            $is_theme = (get_param_string('exp', 'custom') == 'theme');
            if ($is_theme) {
                set_helper_panel_tutorial('tut_releasing_themes');
            }

            breadcrumb_set_parents(array(array('_SELF:_SELF:browse', do_lang_tempcode('ADDONS')), array('_SELF:_SELF:browse', do_lang_tempcode('EXPORT_ADDON'))));
            breadcrumb_set_self(do_lang_tempcode('CONFIRM'));

            $this->title = get_screen_title('EXPORT_ADDON');
        }

        if ($type == '__addon_export') {
            breadcrumb_set_parents(array(array('_SELF:_SELF:browse', do_lang_tempcode('ADDONS'))));
            breadcrumb_set_self(do_lang_tempcode('DONE'));

            $this->title = get_screen_title('EXPORT_ADDON');
        }

        if ($type == 'addon_import') {
            breadcrumb_set_parents(array(array('_SELF:_SELF:browse', do_lang_tempcode('ADDONS'))));

            $this->title = get_screen_title('IMPORT_ADDON');
        }

        if ($type == '_addon_import') {
            breadcrumb_set_parents(array(array('_SELF:_SELF:browse', do_lang_tempcode('ADDONS'))));
            breadcrumb_set_self(do_lang_tempcode('DONE'));

            $this->title = get_screen_title('IMPORT_ADDON');
        }

        if ($type == 'addon_install') {
            breadcrumb_set_parents(array(array('_SELF:_SELF:browse', do_lang_tempcode('ADDONS'))));

            $this->title = get_screen_title('INSTALL_ADDON');
        }

        if ($type == '_addon_install') {
            breadcrumb_set_parents(array(array('_SELF:_SELF:browse', do_lang_tempcode('ADDONS'))));
            breadcrumb_set_self(do_lang_tempcode('DONE'));

            $this->title = get_screen_title('INSTALL_ADDON');
        }

        if ($type == 'addon_uninstall') {
            breadcrumb_set_parents(array(array('_SELF:_SELF:browse', do_lang_tempcode('ADDONS'))));

            $this->title = get_screen_title('UNINSTALL_ADDON');
        }

        if ($type == '_addon_uninstall') {
            breadcrumb_set_parents(array(array('_SELF:_SELF:browse', do_lang_tempcode('ADDONS'))));
            breadcrumb_set_self(do_lang_tempcode('DONE'));

            $this->title = get_screen_title('UNINSTALL_ADDON');
        }

        if ($type == 'addon_delete') {
            breadcrumb_set_parents(array(array('_SELF:_SELF:browse', do_lang_tempcode('ADDONS'))));

            $this->title = get_screen_title('DELETE');
        }

        if ($type == '_addon_delete') {
            breadcrumb_set_parents(array(array('_SELF:_SELF:browse', do_lang_tempcode('ADDONS'))));
            breadcrumb_set_self(do_lang_tempcode('DONE'));

            $this->title = get_screen_title('DELETE');
        }

        if ($type == 'multi_action') {
            breadcrumb_set_parents(array(array('_SELF:_SELF:browse', do_lang_tempcode('ADDONS'))));

            $this->title = get_screen_title('INSTALL_AND_UNINSTALL');
        }

        if ($type == '_multi_action') {
            breadcrumb_set_parents(array(array('_SELF:_SELF:browse', do_lang_tempcode('ADDONS'))));
            breadcrumb_set_self(do_lang_tempcode('DONE'));

            $this->title = get_screen_title('INSTALL_AND_UNINSTALL');
        }

        if ($type == 'reinstall') {
            breadcrumb_set_parents(array(array('_SELF:_SELF:browse', do_lang_tempcode('ADDONS'))));
            breadcrumb_set_self(do_lang_tempcode('DONE'));

            $this->title = get_screen_title('REINSTALL_MODULE');
        }

        if ($type == 'uninstall') {
            breadcrumb_set_parents(array(array('_SELF:_SELF:browse', do_lang_tempcode('ADDONS'))));
            breadcrumb_set_self(do_lang_tempcode('DONE'));

            $this->title = get_screen_title('UNINSTALL_MODULE');
        }

        if ($type == 'upgrade') {
            breadcrumb_set_parents(array(array('_SELF:_SELF:browse', do_lang_tempcode('ADDONS'))));
            breadcrumb_set_self(do_lang_tempcode('DONE'));

            $this->title = get_screen_title('UPGRADE_MODULE');
        }

        if ($type == 'modules') {
            breadcrumb_set_parents(array(array('_SELF:_SELF:browse', do_lang_tempcode('ADDONS'))));

            $this->title = get_screen_title('MODULE_MANAGEMENT');
        }

        if ($type == 'view') {
            breadcrumb_set_parents(array(array('_SELF:_SELF:browse', do_lang_tempcode('ADDONS')), array('_SELF:_SELF:modules', do_lang_tempcode('MODULE_MANAGEMENT'))));
            $zone = get_param_string('id');
            breadcrumb_set_self(($zone == '') ? do_lang('_WELCOME') : $zone);

            $this->title = get_screen_title('MODULE_MANAGEMENT');
        }

        return null;
    }

    /**
     * Execute the module.
     *
     * @return Tempcode The result of execution.
     */
    public function run()
    {
        if (!is_null($GLOBALS['CURRENT_SHARE_USER'])) {
            warn_exit(do_lang_tempcode('SHARED_INSTALL_PROHIBIT'));
        }

        require_code('addons2');
        require_code('menus2');
        require_css('addons_editor');

        disable_php_memory_limit(); // Choice of what to export, or tricky import

        // Decide what we're doing
        $type = get_param_string('type', 'browse');

        if ($type == 'browse') {
            return $this->gui();
        }
        if ($type == 'addon_export') {
            return $this->addon_export();
        }
        if ($type == '_addon_export') {
            return $this->_addon_export();
        }
        if ($type == '__addon_export') {
            return $this->__addon_export();
        }
        if ($type == 'addon_import') {
            return $this->addon_import();
        }
        if ($type == '_addon_import') {
            return $this->_addon_import();
        }
        if ($type == 'addon_install') {
            return $this->addon_install();
        }
        if ($type == '_addon_install') {
            return $this->_addon_install();
        }
        if ($type == 'addon_uninstall') {
            return $this->addon_uninstall();
        }
        if ($type == '_addon_uninstall') {
            return $this->_addon_uninstall();
        }
        if ($type == 'addon_delete') {
            return $this->addon_delete();
        }
        if ($type == '_addon_delete') {
            return $this->_addon_delete();
        }
        if ($type == 'multi_action') {
            return $this->multi_action();
        }
        if ($type == '_multi_action') {
            return $this->_multi_action();
        }
        if ($type == 'reinstall') {
            return $this->reinstall_module();
        }
        if ($type == 'uninstall') {
            return $this->uninstall_module();
        }
        if ($type == 'upgrade') {
            return $this->upgrade_module();
        }
        if ($type == 'modules') {
            return $this->modules_interface();
        }
        if ($type == 'view') {
            return $this->modules_view();
        }

        return new Tempcode();
    }

    /**
     * The main UI.
     *
     * @return Tempcode The UI
     */
    public function gui()
    {
        if (php_function_allowed('set_time_limit')) {
            @set_time_limit(180); // So it can scan inside addons
        }
        send_http_output_ping();

        $GLOBALS['NO_QUERY_LIMIT'] = true;

        $addons_installed = find_installed_addons(false, false);
        $addons_available_for_installation = find_available_addons(false);

        $_tpl_addons = array('red' => array(), 'green' => array());

        $updated_addons_arr = find_updated_addons();
        $updated_addons = '';
        foreach ($updated_addons_arr as $updated_addon) {
            if ($updated_addons != '') {
                $updated_addons .= ',';
            }
            $updated_addons .= strval($updated_addon[0]);
        }

        $do_caching = has_caching_for('block');

        // Show installed addons
        foreach ($addons_installed as $name => $row) {
            if (substr($name, 0, 5) == 'core_' || $name == 'core') {
                continue;
            }

            $colour = null;
            $addon_tpl = null;

            if ($do_caching) {
                $cache_identifier = $name;
                $test = get_cache_entry('_addon_installed_tpl', $cache_identifier, CACHE_AGAINST_NOTHING_SPECIAL, 10000);
                if (is_array($test)) {
                    list($colour, $addon_tpl) = $test;
                }
            }

            if ($addon_tpl === null) {
                if ($row === null) {
                    $row = read_addon_info($name);
                }

                $actions = do_template('COLUMNED_TABLE_ACTION_DELETE_ENTRY', array('_GUID' => '5a65c9aa87291ecfe46f75e9b2949246', 'GET' => true, 'NAME' => $name, 'VERB' => do_lang_tempcode('UNINSTALL'), 'URL' => build_url(array('page' => '_SELF', 'type' => 'addon_uninstall', 'name' => $name), '_SELF')));
                $updated = array_key_exists($name, $updated_addons_arr);
                $status = do_lang_tempcode($updated ? 'STATUS_OUTOFDATE' : 'STATUS_INSTALLED');
                $colour = $updated ? 'red' : 'green';
                $description = $row['description'];
                $file_list = $row['files'];
                $pretty_name = do_template('ADDON_NAME', array('_GUID' => '86b940f63744eb0690059efd69c1d58c', 'IMAGE_URL' => find_addon_icon($name, false, null), 'NAME' => $name));

                $addon_tpl = static_evaluate_tempcode(do_template('ADDON_SCREEN_ADDON', array(
                    '_GUID' => '9a06f5a9c9e3085c10ab7fb17c3efcd1',
                    'UPDATED_ADDONS' => $updated,
                    'DESCRIPTION' => $description,
                    'DESCRIPTION_PARSED' => static_evaluate_tempcode(comcode_to_tempcode($description)),
                    'FILE_LIST' => $file_list,
                    'COLOUR' => $colour,
                    'STATUS' => $status,
                    'PRETTY_NAME' => $pretty_name,
                    'NAME' => $name,
                    'FILENAME' => null,
                    'AUTHOR' => $row['author'],
                    'ORGANISATION' => $row['organisation'],
                    'CATEGORY' => $row['category'],
                    'COPYRIGHT_ATTRIBUTION' => implode("\n", $row['copyright_attribution']),
                    'LICENCE' => $row['licence'],
                    'VERSION' => $row['version'],
                    'ACTIONS' => $actions,
                    'TYPE' => 'uninstall',
                    'PASSTHROUGH' => $name,
                    'BUNDLED' => in_array('sources/hooks/systems/addon_registry/' . $name . '.php', $file_list),
                )));

                if ($do_caching) {
                    require_code('caches2');
                    put_into_cache('_addon_installed_tpl', 60 * 24, $cache_identifier, null, null, '', null, '', array($colour, $addon_tpl));
                }
            }

            $_tpl_addons[$colour][$name] = $addon_tpl;
        }

        // Show addons available for installation
        foreach ($addons_available_for_installation as $filename => $addon) {
            if (!array_key_exists($addon['name'], $addons_installed)) {
                $colour = null;
                $addon_tpl = null;

                if ($do_caching) {
                    $cache_identifier = $addon['name'];
                    $test = get_cache_entry('_addon_available_tpl', $cache_identifier, CACHE_AGAINST_NOTHING_SPECIAL, 10000);
                    if (is_array($test)) {
                        list($colour, $addon_tpl) = $test;
                    }
                }

                if ($addon_tpl === null) {
                    $file_list = $addon['files'];
                    $bundled = in_array('sources/hooks/systems/addon_registry/' . $addon['name'] . '.php', $file_list);

                    $actions = new Tempcode();
                    $actions->attach(do_template('COLUMNED_TABLE_ACTION_INSTALL_ENTRY', array(
                        '_GUID' => 'e6e2bdac62c0d3afcd5251b3d525a1c9',
                        'GET' => true,
                        'NAME' => $addon['name'],
                        'HIDDEN' => '',
                        'URL' => build_url(array('page' => '_SELF', 'type' => 'addon_install', 'file' => $filename), '_SELF'),
                    )));
                    if (!$bundled) {
                        $actions->attach(do_template('COLUMNED_TABLE_ACTION_DELETE_ENTRY', array(
                            '_GUID' => 'f6e2bdac62c0d3afcd5251b3d525a1c9',
                            'GET' => true,
                            'NAME' => $addon['name'],
                            'VERB' => do_lang_tempcode('DELETE'),
                            'HIDDEN' => '',
                            'URL' => build_url(array('page' => '_SELF', 'type' => 'addon_delete', 'file' => $filename), '_SELF'),
                        )));
                    }

                    $status = do_lang_tempcode('STATUS_NOT_INSTALLED');
                    $description = $addon['description'];
                    if ($addon['version'] == '(version-synched)') {
                        $addon['version'] = float_to_raw_string(cms_version_number());
                    }
                    $pretty_name = do_template('ADDON_NAME', array('_GUID' => '4802523382da01432bf04120ad01c677', 'IMAGE_URL' => find_addon_icon($addon['name'], false, $addon['tar_path']), 'NAME' => $addon['name']));

                    $addon_tpl = static_evaluate_tempcode(do_template('ADDON_SCREEN_ADDON', array(
                        '_GUID' => 'cb61bdb9ce0cef5cd520440c5f62008f',
                        'UPDATED_ADDONS' => false,
                        'DESCRIPTION' => $description,
                        'DESCRIPTION_PARSED' => comcode_to_tempcode($description),
                        'FILE_LIST' => $file_list,
                        'COLOUR' => 'orange',
                        'STATUS' => $status,
                        'PRETTY_NAME' => $pretty_name,
                        'NAME' => $addon['name'],
                        'FILENAME' => $filename,
                        'AUTHOR' => $addon['author'],
                        'ORGANISATION' => $addon['organisation'],
                        'CATEGORY' => $addon['category'],
                        'COPYRIGHT_ATTRIBUTION' => implode("\n", $addon['copyright_attribution']),
                        'LICENCE' => $addon['licence'],
                        'VERSION' => $addon['version'],
                        'ACTIONS' => $actions,
                        'TYPE' => 'install',
                        'PASSTHROUGH' => $filename,
                        'BUNDLED' => $bundled,
                    )));

                    if ($do_caching) {
                        require_code('caches2');
                        put_into_cache('_addon_available_tpl', 60 * 24, $cache_identifier, null, null, '', null, '', array($colour, $addon_tpl));
                    }
                }

                $_tpl_addons[$colour][$addon['name']] = $addon_tpl;
            }
        }

        $tpl_addons = new Tempcode();
        foreach ($_tpl_addons as $__tpl_addons) {
            ksort($__tpl_addons);

            foreach ($__tpl_addons as $t) {
                $tpl_addons->attach($t);
            }
        }

        $multi_action = build_url(array('page' => '_SELF', 'type' => 'multi_action'), '_SELF');

        return do_template('ADDON_SCREEN', array('_GUID' => 'ed6c80c29fcae333323ef03619954b6b', 'TITLE' => $this->title, 'ADDONS' => $tpl_addons, 'MULTI_ACTION' => $multi_action, 'UPDATED_ADDONS' => $updated_addons));
    }

    /**
     * The UI to get an addon from some source.
     *
     * @return Tempcode The UI
     */
    public function addon_import()
    {
        appengine_live_guard();

        require_code('form_templates');

        $fields = new Tempcode();
        $set_name = 'addon';
        $required = true;
        $set_title = do_lang_tempcode('SOURCE');
        $field_set = alternate_fields_set__start($set_name);

        $to_import = get_param_string('to_import', null);

        $field_set->attach(form_input_tree_list(do_lang_tempcode('DOWNLOAD'), do_lang_tempcode('DESCRIPTION_DOWNLOAD_COMPOSR_HOMESITE', escape_html(get_brand_page_url(array('page' => 'community'), 'site'))), 'url', null, 'choose_composr_homesite_addon', array(), false, $to_import, false, null, true));

        $field_set->attach(form_input_upload(do_lang_tempcode('UPLOAD'), do_lang_tempcode('DESCRIPTION_UPLOAD'), 'file', false, null, null, true, 'tar'));

        $fields->attach(alternate_fields_set__end($set_name, $set_title, '', $field_set, $required));

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

        $submit_name = do_lang_tempcode('IMPORT_ADDON');

        $post_url = build_url(array('page' => '_SELF', 'type' => '_addon_import', 'uploading' => 1), '_SELF');

        $text = new Tempcode();
        $text->attach(paragraph(do_lang_tempcode('HELP_IMPORTING_ADDON')));

        require_code('files2');
        $max = floatval(get_max_file_size()) / floatval(1024 * 1024);
        if ($max < 30.0) {
            $config_url = get_upload_limit_config_url();
            $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))));
        }

        return do_template('FORM_SCREEN', array('_GUID' => '7f50130c5a46e0f6e8a95e936ce7bf47', 'SKIP_WEBSTANDARDS' => true, 'HIDDEN' => $hidden, 'TITLE' => $this->title, 'SUBMIT_ICON' => 'menu___generic_admin__import', 'SUBMIT_NAME' => $submit_name, 'FIELDS' => $fields, 'TEXT' => $text, 'URL' => $post_url));
    }

    /**
     * The UI to retrieve a specified addon.
     *
     * @return Tempcode The UI
     */
    public function _addon_import()
    {
        appengine_live_guard();

        require_code('uploads');

        $url_map = array('page' => '_SELF', 'type' => 'multi_action');

        $__url = post_param_string('url', '');
        foreach (explode(',', $__url) as $i => $url) {
            if (is_numeric($url)) {
                $_POST['url'] = 'http://compo.sr/site/dload.php?id=' . $url;
            } else {
                $_POST['url'] = $url; // In case it was submitted in array form, which is possible on some UAs (based on an automated bug report)
            }

            $urls = get_url('url', 'file', 'imports/addons', 0, CMS_UPLOAD_ANYTHING, false, '', '', true);

            $full = get_custom_file_base() . '/' . $urls[0];
            if (strtolower(substr($full, -4)) != '.tar') {
                return warn_screen(get_screen_title('ERROR_OCCURRED'), do_lang_tempcode('ADDON_NOT_TAR'));
            }

            $url_map['install_' . strval($i)] = basename($urls[0]);
        }

        // Show it worked / Refresh
        $_url = build_url($url_map, '_SELF');
        return redirect_screen($this->title, $_url, do_lang_tempcode('ADDON_IMPORTED'));
    }

    /**
     * The UI to confirm a combined action on addons.
     *
     * @return Tempcode The UI
     */
    public function multi_action()
    {
        appengine_live_guard();

        $warnings = new Tempcode();
        $install_files = new Tempcode();
        $uninstall_files = new Tempcode();

        $installing = array();
        $uninstalling = array();

        $hidden = new Tempcode();

        foreach ($_POST + $_GET as $key => $passed) {
            if (substr($key, 0, 8) == 'install_') {
                $installing[] = $passed;
                $hidden->attach(form_input_hidden($key, $passed));
            }

            if (substr($key, 0, 10) == 'uninstall_') {
                $uninstalling[] = $passed;
                $hidden->attach(form_input_hidden($key, $passed));
            }
        }

        foreach ($uninstalling as $name) {
            list($_warnings, $_files) = inform_about_addon_uninstall($name, $uninstalling);
            $warnings->attach($_warnings);
            $uninstall_files->attach($_files);
        }

        foreach ($installing as $file) {
            list($_warnings, $_files, $info) = inform_about_addon_install($file, $uninstalling, $installing);
            $warnings->attach($_warnings);
            $install_files->attach($_files);
        }

        if ((count($installing) == 0) && (count($uninstalling) == 0)) {
            warn_exit(do_lang_tempcode('NOTHING_SELECTED'));
        }

        $url = build_url(array('page' => '_SELF', 'type' => '_multi_action'), '_SELF');

        return do_template('ADDON_MULTI_CONFIRM_SCREEN', array('_GUID' => 'bd6b7e012825bb0c873a76a9f4b19cf1', 'TITLE' => $this->title, 'HIDDEN' => $hidden, 'URL' => $url, 'INSTALL_FILES' => $install_files, 'UNINSTALL_FILES' => $uninstall_files, 'WARNINGS' => $warnings));
    }

    /**
     * The actualiser to perform a combined action on addons.
     *
     * @return Tempcode The UI
     */
    public function _multi_action()
    {
        appengine_live_guard();

        if (php_function_allowed('set_time_limit')) {
            @set_time_limit(0);
        }
        send_http_output_ping();

        require_code('abstract_file_manager');
        force_have_afm_details();

        $addons_to_remove = array();

        foreach ($_POST as $key => $passed) {
            if (substr($key, 0, 8) == 'install_') {
                // Files pass
                install_addon($passed, null, true, false);
            }

            if (substr($key, 0, 10) == 'uninstall_') {
                $name = $passed;

                if (
                    (!file_exists(get_file_base() . '/sources_custom/hooks/systems/addon_registry/' . filter_naughty_harsh($name, true) . '.php')) &&
                    (!file_exists(get_file_base() . '/sources/hooks/systems/addon_registry/' . filter_naughty_harsh($name, true) . '.php')) &&
                    (is_null($GLOBALS['SITE_DB']->query_select_value_if_there('addons', 'addon_name', array('addon_name' => $name))))
                ) {
                    continue;
                }

                $addons_to_remove[] = $name;
            }
        }

        foreach ($_POST as $key => $passed) {
            if (substr($key, 0, 8) == 'install_') {
                // DB pass
                install_addon($passed, null, false, true);
            }
        }

        foreach ($addons_to_remove as $i => $name) {
            $addon_info = read_addon_info($name);

            // Archive it off to exports/addons
            $file = preg_replace('#^[\_\.\-]#', 'x', preg_replace('#[^\w\.\-]#', '_', $name)) . '.tar';
            create_addon(
                $file,
                $addon_info['files'],
                $addon_info['name'],
                implode(',', $addon_info['incompatibilities']),
                implode(',', $addon_info['dependencies']),
                $addon_info['author'],
                $addon_info['organisation'],
                $addon_info['version'],
                $addon_info['category'],
                implode("\n", $addon_info['copyright_attribution']),
                $addon_info['licence'],
                $addon_info['description'],
                'imports/addons'
            );

            uninstall_addon($name, $i == count($addons_to_remove) - 1);
        }

        // Clear some caching
        require_code('caches3');
        erase_comcode_page_cache();
        erase_block_cache(true);
        //persistent_cache_delete('OPTIONS');  Done by set_option
        erase_persistent_cache();
        erase_cached_templates(false, null, TEMPLATE_DECACHE_WITH_ADDON);

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

    /**
     * The UI to confirm the install of an addon.
     *
     * @return Tempcode The UI
     */
    public function addon_install()
    {
        appengine_live_guard();

        $file = get_param_string('file');
        list($warnings, $files, $info) = inform_about_addon_install($file);

        $url = build_url(array('page' => '_SELF', 'type' => '_addon_install'), '_SELF');

        $_description = comcode_to_tempcode($info['description'], $GLOBALS['FORUM_DRIVER']->get_guest_id());
        if ($info['version'] == '(version-synched)') {
            $info['version'] = float_to_raw_string(cms_version_number());
        }

        return do_template('ADDON_INSTALL_CONFIRM_SCREEN', array(
            '_GUID' => '79b8c0e900a498cfb166392163295a07',
            'TITLE' => $this->title,
            'FILE' => $file,
            'URL' => $url,
            'FILES' => $files,
            'WARNINGS' => $warnings,
            'NAME' => $info['name'],
            'AUTHOR' => $info['author'],
            'ORGANISATION' => $info['organisation'],
            'VERSION' => $info['version'],
            'CATEGORY' => $info['category'],
            'COPYRIGHT_ATTRIBUTION' => implode("\n", $info['copyright_attribution']),
            'LICENCE' => $info['licence'],
            'DESCRIPTION' => $_description,
        ));
    }

    /**
     * The actualiser to install an addon.
     *
     * @return Tempcode The UI
     */
    public function _addon_install()
    {
        appengine_live_guard();

        require_code('abstract_file_manager');
        force_have_afm_details();

        $file = filter_naughty(post_param_string('file'));

        $theme = mixed();

        $files = array();
        foreach (array_keys($_POST) as $key) {
            if (substr($key, 0, 5) == 'file_') {
                $value = post_param_string($key);
                $files[] = $value;

                $matches = array();
                if (preg_match('#^themes/([^/]+)/#', $value, $matches) != 0) {
                    $theme = $matches[1];
                }
            }
        }

        install_addon($file, $files);

        // Show it worked / Refresh
        if ((!is_null($theme)) && ($theme != 'default')) {
            $url = build_url(array('page' => 'admin_themes', 'type' => 'edit_theme', 'theme' => $theme), 'adminzone');
            return redirect_screen($this->title, $url, do_lang_tempcode('INSTALL_THEME_SUCCESS'));
        }
        $url = build_url(array('page' => '_SELF', 'type' => 'browse'), '_SELF');
        return redirect_screen($this->title, $url, do_lang_tempcode('SUCCESS'));
    }

    /**
     * The UI to uninstall an addon.
     *
     * @return Tempcode The UI
     */
    public function addon_uninstall()
    {
        appengine_live_guard();

        $name = get_param_string('name');

        list($warnings, $files) = inform_about_addon_uninstall($name);

        $url = build_url(array('page' => '_SELF', 'type' => '_addon_uninstall'), '_SELF');

        return do_template('ADDON_UNINSTALL_CONFIRM_SCREEN', array('_GUID' => 'fe96098c1f09d091fc10785134803135', 'TITLE' => $this->title, 'URL' => $url, 'NAME' => $name, 'WARNINGS' => $warnings, 'FILES' => $files));
    }

    /**
     * The UI to uninstall an addon.
     *
     * @return Tempcode The UI
     */
    public function _addon_uninstall()
    {
        appengine_live_guard();

        require_code('abstract_file_manager');
        force_have_afm_details();

        $name = post_param_string('name');

        $addon_info = read_addon_info($name);

        // Archive it off to exports/addons
        $file = preg_replace('#^[\_\.\-]#', 'x', preg_replace('#[^\w\.\-]#', '_', $name)) . '.tar';

        $new_addon_files = array();
        foreach ($addon_info['files'] as $_file) {
            if (substr($_file, -9) != '.editfrom') {// This would have been added back in automatically
                $new_addon_files[] = $_file;
            }
        }

        create_addon(
            $file,
            $new_addon_files,
            $addon_info['name'],
            implode(',', $addon_info['incompatibilities']),
            implode(',', $addon_info['dependencies']),
            $addon_info['author'],
            $addon_info['organisation'],
            $addon_info['version'],
            $addon_info['category'],
            implode(',', $addon_info['copyright_attribution']),
            $addon_info['licence'],
            $addon_info['description'],
            'imports/addons'
        );

        uninstall_addon($name);

        // Clear some caching
        require_code('caches3');
        erase_comcode_page_cache();
        erase_block_cache(true);
        //persistent_cache_delete('OPTIONS');  Done by set_option
        erase_persistent_cache();
        erase_cached_templates(false, null, TEMPLATE_DECACHE_WITH_ADDON);

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

    /**
     * The UI to delete an addon TAR file.
     *
     * @return Tempcode The UI
     */
    public function addon_delete()
    {
        appengine_live_guard();

        $file = get_param_string('file');

        $url = build_url(array('page' => '_SELF', 'type' => '_addon_delete'), '_SELF');

        $preview = protect_from_escaping(do_lang('DELETE') . ': ' . escape_html($file));

        $hidden = form_input_hidden('file', $file);

        return do_template('CONFIRM_SCREEN', array('_GUID' => '1445d1204cc561ecb643b016d1528cf7', 'TITLE' => $this->title, 'URL' => $url, 'PREVIEW' => $preview, 'HIDDEN' => $hidden, 'FIELDS' => ''));
    }

    /**
     * The UI to delete an addon TAR file.
     *
     * @return Tempcode The UI
     */
    public function _addon_delete()
    {
        appengine_live_guard();

        require_code('abstract_file_manager');
        force_have_afm_details();

        $file = filter_naughty(post_param_string('file'));

        unlink(get_custom_file_base() . '/imports/addons/' . $file);

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

    /**
     * The UI to export an addon (1).
     *
     * @return Tempcode The UI
     */
    public function addon_export()
    {
        appengine_live_guard();

        require_code('files');

        // Lang packs
        $url = build_url(array('page' => '_SELF', 'type' => '_addon_export', 'exp' => 'lang'), '_SELF');
        $all_langs = find_all_langs();
        ksort($all_langs);
        $i = 0;
        $tpl_langs = new Tempcode();
        $_lang_file_map = get_custom_file_base() . '/lang_custom/langs.ini';
        if (!file_exists($_lang_file_map)) {
            $_lang_file_map = get_file_base() . '/lang/langs.ini';
        }
        $lang_file_map = better_parse_ini_file($_lang_file_map);
        foreach ($all_langs as $lang => $dir) {
            if ($dir == 'lang_custom') {
                if ($lang != fallback_lang() || get_param_integer('test', 0) == 1) {
                    $nice_name = array_key_exists($lang, $lang_file_map) ? $lang_file_map[$lang] : $lang;
                    $frm_langs = new Tempcode();
                    $frm_langs->attach(form_input_hidden('lang', $lang));
                    $tpl_langs->attach(do_template('ADDON_EXPORT_LINE', array('_GUID' => '4e2f56799bdb3c4930396315236e2383', 'NAME' => $nice_name, 'URL' => $url, 'FILES' => $frm_langs)));
                }
            }
        }

        // Theme packs
        $url = build_url(array('page' => '_SELF', 'type' => '_addon_export', 'exp' => 'theme'), '_SELF');
        require_code('themes2');
        $all_themes = find_all_themes();
        ksort($all_themes);
        $i = 0;
        $tpl_themes = new Tempcode();
        foreach ($all_themes as $theme => $theme_title) {
            if ($theme != 'default' && $theme != 'admin' || get_param_integer('test', 0) == 1) {
                $frm_themes = new Tempcode();
                $frm_themes->attach(form_input_hidden('theme', $theme));
                $tpl_themes->attach(do_template('ADDON_EXPORT_LINE', array('_GUID' => '9c1dab6d6e6c13b5e01c86c83c3acde1', 'NAME' => $theme_title, 'URL' => $url, 'FILES' => $frm_themes)));
            }
        }

        // Files for choice export
        $url = build_url(array('page' => '_SELF', 'type' => '_addon_export', 'exp' => 'custom'), '_SELF');
        $files = $this->do_dir('');
        ksort($files);
        $frm_files = new Tempcode();
        $i = 0;
        foreach (array_keys($files) as $file) {
            if (!is_string($file)) {
                $file = strval($file);
            }

            $frm_files->attach(do_template('ADDON_EXPORT_FILE_CHOICE', array('_GUID' => '77a91b947259c5e0cc7b5240b24425ca', 'ID' => strval($i), 'PATH' => $file)));
            $i++;
        }

        return do_template('ADDON_EXPORT_SCREEN', array('_GUID' => 'd89367c0bbc3d6b8bd19f736d9474dfa', 'TITLE' => $this->title, 'LANGUAGES' => $tpl_langs, 'URL' => $url, 'FILES' => $frm_files, 'THEMES' => $tpl_themes));
    }

    /**
     * (Recursively) find all files we can choose to export.
     *
     * @param  PATH $dir The directory to search
     * @return array A map, path=>true (inverted list)
     */
    private function do_dir($dir)
    {
        $full = get_file_base() . '/' . (($dir == '') ? '' : ($dir . '/'));
        $temp = array();
        $_dir = @opendir($full);
        if ($_dir !== false) {
            require_code('files');

            while (false !== ($file = readdir($_dir))) {
                if (!should_ignore_file((($dir == '') ? '' : ($dir . '/')) . $file, IGNORE_EDITFROM_FILES | IGNORE_REVISION_FILES | IGNORE_UPLOADS | IGNORE_ACCESS_CONTROLLERS)) {
                    $temp[$file] = true;
                }
            }
            closedir($_dir);
        }

        $out = array();
        foreach (array_keys($temp) as $file) {
            if (is_integer($file)) {
                $file = strval($file);
            }

            if (is_dir($full . $file)) {
                if ((!array_key_exists($file . '_custom', $temp)) || ((substr($dir, 0, 7) == 'themes/') && (substr($dir . '/', 0, 15) != 'themes/default/'))) { // If there is a custom equiv we don't do it: we only do custom, or indeterminate-custom
                    $under = $this->do_dir((($dir == '') ? '' : ($dir . '/')) . $file);
                    $out = array_merge($out, $under);
                }
            } else {
                $out[(($dir == '') ? '' : ($dir . '/')) . $file] = true;
            }
        }

        return $out;
    }

    /**
     * The UI to export an addon (2).
     *
     * @return Tempcode The UI
     */
    public function _addon_export()
    {
        appengine_live_guard();

        $hidden = build_keep_post_fields();

        $is_lang = (get_param_string('exp', 'custom') == 'lang');
        $lang = either_param_string('lang', null);

        $is_theme = (get_param_string('exp', 'custom') == 'theme');
        $theme = either_param_string('theme', null);

        require_code('files');

        // Default metadata
        $name = '';
        $author = $GLOBALS['FORUM_DRIVER']->get_username(get_member(), true);
        $organisation = get_site_name();
        $description = '';
        $version = '1.0';
        $copyright_attribution = '';
        $licence = 'Creative Commons Attribution-ShareAlike';
        $dependencies = '';
        $incompatibilities = '';

        // ... but the theme might already define some of this
        if (!is_null($theme)) {
            $ini_file = (($theme == 'default' || $theme == 'admin') ? get_file_base() : get_custom_file_base()) . '/themes/' . filter_naughty($theme) . '/theme.ini';
            if (file_exists($ini_file)) {
                $details = better_parse_ini_file($ini_file);
                if (array_key_exists('title', $details)) {
                    $name = $details['title'];
                }
                if (array_key_exists('description', $details)) {
                    $description = $details['description'];
                }
                if (array_key_exists('author', $details)) {
                    $author = $details['author'];
                }
            }
        } elseif ($is_lang) {
            $lang = post_param_string('lang');
            $ini_file = get_custom_file_base() . '/lang_custom/langs.ini';
            if (!file_exists($ini_file)) {
                $ini_file = get_file_base() . '/lang/langs.ini';
            }
            if (file_exists($ini_file)) {
                $details = better_parse_ini_file($ini_file);
                if (array_key_exists($lang, $details)) {
                    $name = $details[$lang];
                    $description = $details[$lang];
                }
            }
        } else {
            // Get from existing addon_registry hook if selected
            $files = array();
            foreach ($_POST as $key => $val) {
                if (preg_match('#^file_\d+$#', $key) != 0) {
                    $files[] = $val;
                }
            }
            sort($files);
            foreach ($files as $file) {
                if (preg_match('#^sources(_custom)?/hooks/systems/addon_registry/[^/]+\.php$#', $file) != 0) {
                    if ($name != '') {
                        attach_message('Multiple addon_registry hooks were selected - that is a bad idea.', 'warn');
                    } else {
                        attach_message('Defaults have been selected from the chosen addon_registry hook. If you change them the hook itself will not be changed, so it will become inconsistent - edit the hook instead and refresh.', 'warn');
                    }

                    $existing_addon_info = read_addon_info(basename($file, '.php'), false, null, null, $file);

                    sort($existing_addon_info['files']);

                    if ($existing_addon_info['files'] != $files) {
                        if (count($files) == 1) {
                            if (count($existing_addon_info['files']) > 1) {
                                attach_message('You only selected the addon_registry hook - automatically selecting the ' . integer_format(count($existing_addon_info['files'])) . ' files referenced by that hook.', 'inform');

                                foreach ($existing_addon_info['files'] as $i => $_file) {
                                    if ($_file != $file) {
                                        if (!is_file($_file)) {
                                            warn_exit('Missing file referenced by addon registry hook: ' . $_file);
                                        }

                                        $hidden->attach(form_input_hidden('file_' . strval(10000000 + $i), $_file));
                                    }
                                }
                            }
                        } else {
                            warn_exit('The selected files differ from those specified by the addon_registry hook.');
                        }
                    }

                    $name = $existing_addon_info['name'];
                    $author = $existing_addon_info['author'];
                    $organisation = $existing_addon_info['organisation'];
                    $description = $existing_addon_info['description'];
                    $version = $existing_addon_info['version'];
                    $copyright_attribution = $existing_addon_info['copyright_attribution'];
                    $licence = $existing_addon_info['licence'];
                    $dependencies = empty($existing_addon_info['dependencies']['requires']) ? '' : implode(',', $existing_addon_info['dependencies']['requires']);
                    $incompatibilities = empty($existing_addon_info['dependencies']['conflicts_with']) ? '' : implode(',', $existing_addon_info['dependencies']['conflicts_with']);
                }
            }
        }

        $fields = ''; /*XHTMLXHTML*/
        require_code('form_templates');
        $field = form_input_line(do_lang_tempcode('NAME'), do_lang_tempcode('DESCRIPTION_NAME'), 'name', $name, true);
        $fields .= $field->evaluate();
        $field = form_input_line(do_lang_tempcode('AUTHOR'), do_lang_tempcode('DESCRIPTION_AUTHOR', do_lang_tempcode('ADDON')), 'author', $author, true);
        $fields .= $field->evaluate();
        $field = form_input_line(do_lang_tempcode('ORGANISATION'), do_lang_tempcode('DESCRIPTION_ORGANISATION'), 'organisation', $organisation, false);
        $fields .= $field->evaluate();
        $field = form_input_line(do_lang_tempcode('VERSION'), do_lang_tempcode('DESCRIPTION_VERSION'), 'version', $version, true);
        $fields .= $field->evaluate();
        if (!is_null($theme)) {
            $categories = array(
                'Themes',
            );
            $category = 'Themes';
        }
        elseif ($is_lang) {
            $categories = array(
                'Translations',
            );
            $category = 'Translations';
        } else {
            $categories = array(
                'Admin Utilities',
                'Development',
                'Fun and Games',
                'Graphical',
                'Information Display',
                'New Features',
                'Third Party Integration',
                'Uncategorised/Alpha',
            );
            $category = 'Uncategorised/Alpha';
        }
        $_categories = new Tempcode();
        foreach ($categories as $_category) {
            $_categories->attach(form_input_list_entry($_category, $_category == $category));
        }
        $field = form_input_list(do_lang_tempcode('CATEGORY'), do_lang_tempcode('DESCRIPTION_ADDON_CATEGORY'), 'category', $_categories);
        $fields .= $field->evaluate();
        $field = form_input_line(do_lang_tempcode('COPYRIGHT_ATTRIBUTION'), do_lang_tempcode('DESCRIPTION_COPYRIGHT_ATTRIBUTION'), 'copyright_attribution', $copyright_attribution, false);
        $fields .= $field->evaluate();
        $field = form_input_line(do_lang_tempcode('LICENCE'), do_lang_tempcode('DESCRIPTION_ADDON_LICENCE'), 'licence', $licence, true);
        $fields .= $field->evaluate();
        $field = form_input_text(do_lang_tempcode('DESCRIPTION'), do_lang_tempcode('DESCRIPTION_DESCRIPTION'), 'description', $description, true);
        $fields .= $field->evaluate();
        $field = form_input_line(do_lang_tempcode('DEPENDENCIES'), do_lang_tempcode('DESCRIPTION_DEPENDENCIES'), 'dependencies', $dependencies, false);
        $fields .= $field->evaluate();
        $field = form_input_line(do_lang_tempcode('INCOMPATIBILITIES'), do_lang_tempcode('DESCRIPTION_INCOMPATIBILITIES'), 'incompatibilities', $incompatibilities, false);
        $fields .= $field->evaluate();

        if (($is_theme && !is_null($theme)) || ($is_lang && !is_null($lang))) {
            // Option for selecting exactly what files are used
            $field = do_template('FORM_SCREEN_FIELD_SPACER', array('_GUID' => '40cf0d0d5b6fee74fbd476e720a59675', 'SECTION_HIDDEN' => false, 'TITLE' => do_lang_tempcode('COUNT_FILES')));
            $fields .= $field->evaluate();
            $files = array();
            if ($is_lang && !is_null($lang)) {
                $files = $this->do_dir('lang_custom/' . $lang);
                ksort($files);

                $_files = $this->do_dir('');
                foreach (array_keys($_files) as $file) {
                    if (strpos($file, '/' . $lang . '/') !== false) {
                        $files[$file] = true;
                    }
                }
            }
            if ($is_theme && !is_null($theme)) {
                $files = $this->do_dir('themes/' . $theme);
                ksort($files);
            }
            $i = 0;
            foreach (array_keys($files) as $file) {
                if ($is_lang && !is_null($lang)) {
                    $is_selected = (substr($file, 0, 12) == 'lang_custom/');
                    $written_file = preg_replace('#^lang_custom/' . preg_quote($lang, '#') . '/#', '', $file);
                } else {
                    $is_selected = true;
                    $written_file = preg_replace('#^themes/' . preg_quote($theme, '#') . '/#', '', $file);
                }

                $field = form_input_tick($written_file, '', 'file_' . strval($i), $is_selected, null, $file);
                $fields .= $field->evaluate();
                $i++;
            }

            if ($is_theme && !is_null($theme)) {
                // Option for selecting Comcode pages
                $_fields = '';
                $fields_after = '';
                require_lang('themes');
                $files = $this->do_dir('');
                ksort($files);
                foreach (array_keys($files) as $file) {
                    if ((substr($file, 0, strlen($theme) + 2) == $theme . '__')) {
                        $file = substr($file, strlen($theme) + 2); // Per-theme Comcode page version
                    }

                    if ((substr($file, -4) == '.txt') && (strpos($file, '/comcode_custom/') !== false)) {
                        $matches = array();
                        if ((preg_match('#^/((\w+)/)?pages/comcode_custom/[^/]*/(\w+)\.txt$#', $file, $matches) != 0) && ($matches[1] != 'docs')) {
                            $auto_ticked = false;
                            if ($matches[1] == '') {
                                $auto_ticked = ($matches[3] == 'start') || (substr($matches[3], 0, 6) == 'panel_');
                            }
                            $page_link = $matches[1] . ':' . $matches[3];
                            $field = form_input_tick($page_link, '', 'file_' . strval($i), $auto_ticked, null, $file);
                            if ($auto_ticked) {
                                $_fields .= $field->evaluate();
                            } else {
                                $fields_after .= $field->evaluate();
                            }
                            $i++;
                        }
                    }
                }
                if ($_fields != '' || $fields_after != '') {
                    $field = do_template('FORM_SCREEN_FIELD_SPACER', array('_GUID' => 'e3f8c43ac243a7666a318319e8b6dd60', 'SECTION_HIDDEN' => false, 'TITLE' => do_lang_tempcode('PAGES'), 'HELP' => do_lang_tempcode('THEME_ALSO_INCLUDE_PAGES')));
                    $fields .= $field->evaluate();
                    $fields .= $_fields;
                    $fields .= $fields_after;
                }
            }
        }

        $submit_name = do_lang_tempcode('EXPORT_ADDON');

        $map = array('page' => '_SELF', 'type' => '__addon_export');
        if (!is_null($theme)) {
            $_redirect = build_url(array('page' => 'admin_themes', 'type' => 'browse'), 'adminzone');
            $redirect = $_redirect->evaluate();
            $map['redirect'] = $redirect;
            $map['theme'] = $theme;
        }
        $post_url = build_url($map, '_SELF');

        return do_template('FORM_SCREEN', array('_GUID' => 'dd8bea111b0dfc7df7ddc7e2246f0ef9', 'HIDDEN' => $hidden, 'TITLE' => $this->title, 'SUBMIT_ICON' => 'menu___generic_admin__export', 'SUBMIT_NAME' => $submit_name, 'FIELDS' => $fields, 'TEXT' => '', 'URL' => $post_url, 'SUPPORT_AUTOSAVE' => true));
    }

    /**
     * The actualiser to export an addon.
     *
     * @return Tempcode The UI
     */
    public function __addon_export()
    {
        appengine_live_guard();

        $file = preg_replace('#^[\_\.\-]#', 'x', preg_replace('#[^\w\.\-]#', '_', post_param_string('name'))) . date('-dmY-Hi', time()) . '.tar';

        $files = array();
        foreach ($_POST as $key => $val) {
            if (!is_string($val)) {
                continue;
            }

            if (@get_magic_quotes_gpc()) {
                $val = stripslashes($val);
            }

            if (substr($key, 0, 5) == 'file_') {
                $files[] = $val;
            }
        }

        create_addon(
            $file,
            $files,
            post_param_string('name'),
            post_param_string('incompatibilities'),
            post_param_string('dependencies'),
            post_param_string('author'),
            post_param_string('organisation'),
            post_param_string('version'),
            post_param_string('category'),
            post_param_string('copyright_attribution'),
            post_param_string('licence'),
            post_param_string('description')
        );

        $download_url = get_custom_base_url() . '/exports/addons/' . $file;

        log_it('EXPORT_ADDON', $file);

        // Show it worked / Refresh
        $_url = build_url(array('page' => '_SELF', 'type' => 'browse'), '_SELF');
        $url = $_url->evaluate();
        $url = get_param_string('redirect', $url);
        return redirect_screen($this->title, $url, do_lang_tempcode('ADDON_CREATED', escape_html($download_url)));
    }

    /**
     * The UI to choose a zone (or blocks) to manage.
     *
     * @return Tempcode The UI
     */
    public function modules_interface()
    {
        require_code('form_templates');
        require_code('zones2');
        require_code('zones3');
        $list = create_selection_list_zones();
        $list->attach(form_input_list_entry('_block', false, do_lang_tempcode('BLOCKS')));

        $post_url = build_url(array('page' => '_SELF', 'type' => 'view'), '_SELF', null, false, true);
        $fields = form_input_huge_list(do_lang_tempcode('ZONE_OR_BLOCKS'), '', 'id', $list, null, true);
        $submit_name = do_lang_tempcode('PROCEED');

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

    /**
     * The UI to manage the modules (or blocks).
     *
     * @return Tempcode The UI
     */
    public function modules_view()
    {
        $zone = get_param_string('id');
        $tpl_modules = array();

        require_code('templates_columned_table');
        require_code('zones2');

        if ($zone != '_block') {
            $module_rows = list_to_map('module_the_name', $GLOBALS['SITE_DB']->query_select('modules', array('*')));
        } else {
            $module_rows = list_to_map('block_name', $GLOBALS['SITE_DB']->query_select('blocks', array('*')));
        }

        if ($zone == '_block') {
            require_code('zones3');
            $modules = find_all_blocks();
        } else {
            $modules = find_all_modules($zone);
        }
        ksort($modules);
        foreach ($modules as $module => $type) {
            if ($zone != '_block') {
                $module_path = zone_black_magic_filterer(($zone == '') ? ('pages/' . $type . '/' . filter_naughty_harsh($module) . '.php') : ($zone . '/pages/' . $type . '/' . filter_naughty_harsh($module) . '.php'), true);
                $prefix = 'module';
            } else {
                $module_path = $type . '/blocks/' . filter_naughty_harsh($module) . '.php';
                $prefix = 'block';
            }

            $info = extract_module_info(get_file_base() . '/' . $module_path);
            if (is_null($info)) {
                continue;
            }
            if (get_param_integer('keep_module_dangerous', 0) == 1) {
                $info['locked'] = false;
            }

            $author = $info['author'];
            $organisation = $info['organisation'];
            $version = $info['version'];
            $hacked_by = $info['hacked_by'];
            $hack_version = $info['hack_version'];

            $actions = new Tempcode();
            $status = new Tempcode();

            if (array_key_exists($module, $module_rows)) {
                $row = $module_rows[$module];
                if (!$info['locked']) {
                    $hidden = new Tempcode();
                    $hidden->attach(form_input_hidden('zone', $zone));
                    $hidden->attach(form_input_hidden('module', $module));
                    $actions->attach(do_template('COLUMNED_TABLE_ACTION_DELETE_ENTRY', array('_GUID' => '331afd26f5e62a6a4cdc4e2c520a4114', 'HIDDEN' => $hidden, 'NAME' => $module, 'VERB' => do_lang_tempcode('UNINSTALL'), 'URL' => build_url(array('page' => '_SELF', 'type' => 'uninstall'), '_SELF'))));
                }
                if ($row[$prefix . '_version'] < $version) {
                    $status = do_lang_tempcode('STATUS_TO_UPGRADE');
                    $hidden = new Tempcode();
                    $hidden->attach(form_input_hidden('zone', $zone));
                    $hidden->attach(form_input_hidden('module', $module));
                    $actions->attach(do_template('COLUMNED_TABLE_ACTION_UPGRADE_ENTRY', array('_GUID' => 'e5d012cb8c839e0e869f1edfa008dacd', 'HIDDEN' => $hidden, 'NAME' => $module, 'URL' => build_url(array('page' => '_SELF', 'type' => 'upgrade'), '_SELF'))));
                } elseif ((!is_null($hack_version)) && ($row[$prefix . '_hack_version'] < $hack_version)) {
                    $status = do_lang_tempcode('STATUS_TO_HACK');
                    $hidden = new Tempcode();
                    $hidden->attach(form_input_hidden('zone', $zone));
                    $hidden->attach(form_input_hidden('module', $module));
                    $actions->attach(do_template('COLUMNED_TABLE_ACTION_UPGRADE_ENTRY', array('_GUID' => '42c4473bf31dfd329e921e443ccc2ec3', 'HIDDEN' => $hidden, 'NAME' => $module, 'URL' => build_url(array('page' => '_SELF', 'type' => 'upgrade'), '_SELF'))));
                } else {
                    $status = do_lang_tempcode('STATUS_CURRENT');
                }
                if (!$info['locked']) {
                    $hidden = new Tempcode();
                    $hidden->attach(form_input_hidden('zone', $zone));
                    $hidden->attach(form_input_hidden('module', $module));
                    $actions->attach(do_template('COLUMNED_TABLE_ACTION_REINSTALL_ENTRY', array('_GUID' => 'c2d820af4b9a2f8633f6f5a4e3de76bc', 'HIDDEN' => $hidden, 'NAME' => $module, 'URL' => build_url(array('page' => '_SELF', 'type' => 'reinstall'), '_SELF'))));
                }
            } else {
                $hidden = new Tempcode();
                $hidden->attach(form_input_hidden('zone', $zone));
                $hidden->attach(form_input_hidden('module', $module));
                $actions->attach(do_template('COLUMNED_TABLE_ACTION_INSTALL_ENTRY', array('_GUID' => '6b438e07cfe154afc21439479fd76978', 'HIDDEN' => $hidden, 'NAME' => $module, 'URL' => build_url(array('page' => '_SELF', 'type' => 'reinstall'), '_SELF'))));
            }

            if (is_null($hacked_by)) {
                $hacked_by = do_lang_tempcode('NA_EM');
            }
            if (is_null($hack_version)) {
                $hack_version = do_lang_tempcode('NA_EM');
            }
            $tpl_modules[] = array('STATUS' => $status, 'NAME' => $module, 'AUTHOR' => $author, 'ORGANISATION' => $organisation, 'VERSION' => strval($version), 'HACKED_BY' => $hacked_by, 'HACK_VERSION' => $hack_version, 'ACTIONS' => $actions);
        }

        return do_template('MODULE_SCREEN', array('_GUID' => '132b23107b49a23e0b11db862de1dd56', 'TITLE' => $this->title, 'MODULES' => $tpl_modules));
    }

    /**
     * The actualiser to upgrade a module.
     *
     * @return Tempcode The UI
     */
    public function upgrade_module()
    {
        $module = post_param_string('module');
        $zone = post_param_string('zone');

        require_code('zones2');

        if ($zone != '_block') {
            upgrade_module($zone, $module);
        } else {
            upgrade_block($module);
        }

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

    /**
     * The actualiser to uninstall a module.
     *
     * @return Tempcode The UI
     */
    public function uninstall_module()
    {
        $module = post_param_string('module');
        $zone = post_param_string('zone');

        require_code('zones2');

        if ($zone != '_block') {
            uninstall_module($zone, $module);
        } else {
            uninstall_block($module);
        }

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

    /**
     * The actualiser to reinstall a module.
     *
     * @return Tempcode The UI
     */
    public function reinstall_module()
    {
        $module = post_param_string('module');
        $zone = post_param_string('zone');

        require_code('zones2');

        if ($zone != '_block') {
            reinstall_module($zone, $module);
        } else {
            reinstall_block($module);
        }

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