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

require_code('crud_module');

/**
 * Module page class.
 */
class Module_cms_banners extends Standard_crud_module
{
    public $lang_type = 'BANNER';
    public $view_entry_point = '_SEARCH:banners:view:source=_ID';
    public $user_facing = true;
    public $permissions_require = 'mid';
    public $select_name = 'NAME';
    public $select_name_description = 'DESCRIPTION_BANNER_NAME';
    public $upload = 'image';
    public $non_integer_id = true;
    public $permission_module = 'banners';
    public $menu_label = 'BANNERS';
    public $array_key = 'name';
    public $title_is_multi_lang = false;

    public $do_next_type = null;

    public $title;

    /**
     * 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)
    {
        $this->cat_crud_module = class_exists('Mx_cms_banners_cat') ? new Mx_cms_banners_cat() : new Module_cms_banners_cat();

        return array(
            'browse' => array('MANAGE_BANNERS', 'menu/cms/banners'),
        ) + parent::get_entry_points();
    }

    /**
     * Find privileges defined as overridable by this module.
     *
     * @return array A map of privileges that are overridable; privilege to 0 or 1. 0 means "not category overridable". 1 means "category overridable".
     */
    public function get_privilege_overrides()
    {
        require_lang('banners');
        return array('submit_cat_highrange_content' => array(0, 'ADD_BANNER_TYPE'), 'edit_cat_highrange_content' => array(0, 'EDIT_BANNER_TYPE'), 'delete_cat_highrange_content' => array(0, 'DELETE_BANNER_TYPE'), 'submit_midrange_content' => array(0, 'ADD_BANNER'), 'bypass_validation_midrange_content' => array(0, 'BYPASS_VALIDATION_BANNER'), 'edit_own_midrange_content' => array(0, 'EDIT_OWN_BANNER'), 'edit_midrange_content' => array(0, 'EDIT_BANNER'), 'delete_own_midrange_content' => array(0, 'DELETE_OWN_BANNER'), 'delete_midrange_content' => array(0, 'DELETE_BANNER'));
    }

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

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

        require_lang('banners');

        inform_non_canonical_parameter('b_type');

        set_helper_panel_tutorial('tut_banners');

        if ((has_privilege(get_member(), 'banner_free')) && (get_option('admin_banners') == '0')) {
            attach_message(do_lang_tempcode('PERMISSION_BANNER_SKIP'), 'inform', true);
        }

        if ($type == 'browse') {
            if (has_actual_page_access(get_member(), 'admin_banners')) {
                $also_url = build_url(array('page' => 'admin_banners'), get_module_zone('admin_banners'));
                attach_message(do_lang_tempcode('menus:ALSO_SEE_ADMIN', escape_html($also_url->evaluate())), 'inform', true);
            }
        }

        return parent::pre_run($top_level);
    }

    /**
     * Standard crud_module run_start.
     *
     * @param  ID_TEXT $type The type of module execution
     * @return Tempcode The output of the run
     */
    public function run_start($type)
    {
        //if (!is_null($GLOBALS['CURRENT_SHARE_USER'])) warn_exit(do_lang_tempcode('SHARED_INSTALL_PROHIBIT'));

        require_code('banners');
        require_code('banners2');

        if ($type == 'browse') {
            return $this->browse();
        }

        if ($type == 'export_csv') {
            return $this->export_csv();
        }

        $this->javascript = '
            document.getElementById("importancemodulus").onkeyup=function() {
                var _im_here=document.getElementById("im_here");
                if (_im_here)
                {
                    var _im_total=document.getElementById("im_total");
                    var im_here=window.parseInt(document.getElementById("importancemodulus").value);
                    var im_total=window.parseInt(_im_total.className.replace("im_",""))+im_here;
                    set_inner_html(_im_here,im_here);
                    set_inner_html(document.getElementById("im_here_2"),im_here);
                    set_inner_html(_im_total,im_total);
                    set_inner_html(document.getElementById("im_total_2"),im_total);
                }
            }
        ';

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

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

        return new Tempcode();
    }

    /**
     * The do-next manager for before content management.
     *
     * @return Tempcode The UI
     */
    public function browse()
    {
        require_code('templates_donext');
        return do_next_manager(get_screen_title('MANAGE_BANNERS'), comcode_lang_string('DOC_BANNERS'),
            array(
                has_privilege(get_member(), 'submit_cat_highrange_content', 'cms_banners') ? array('menu/_generic_admin/add_one_category', array('_SELF', array('type' => 'add_category'), '_SELF'), do_lang('ADD_BANNER_TYPE')) : null,
                has_privilege(get_member(), 'edit_cat_highrange_content', 'cms_banners') ? array('menu/_generic_admin/edit_one_category', array('_SELF', array('type' => 'edit_category'), '_SELF'), do_lang('EDIT_BANNER_TYPE')) : null,
                has_privilege(get_member(), 'submit_midrange_content', 'cms_banners') ? array('menu/_generic_admin/add_one', array('_SELF', array('type' => 'add'), '_SELF'), do_lang('ADD_BANNER')) : null,
                has_privilege(get_member(), 'edit_own_midrange_content', 'cms_banners') ? array('menu/_generic_admin/edit_one', array('_SELF', array('type' => 'edit'), '_SELF'), do_lang('EDIT_BANNER')) : null,
                array('menu/_generic_admin/export', array('_SELF', array('type' => 'export_csv'), '_SELF'), do_lang('EXPORT_CSV_BANNERS')),
            ),
            do_lang('MANAGE_BANNERS')
        );
    }

    /**
     * Standard crud_module table function.
     *
     * @param  array $url_map Details to go to build_url for link to the next screen.
     * @return array A pair: The choose table, Whether reordering is supported from this screen.
     */
    public function create_selection_list_choose_table($url_map)
    {
        require_code('templates_results_table');

        $current_ordering = get_param_string('sort', 'b_type ASC');
        if (strpos($current_ordering, ' ') === false) {
            warn_exit(do_lang_tempcode('INTERNAL_ERROR'));
        }
        list($sortable, $sort_order) = explode(' ', $current_ordering, 2);
        if ($current_ordering == 'b_type ASC') {
            $current_ordering = 'b_type ASC,name ASC';
        }
        $sortables = array(
            'name' => do_lang_tempcode('CODENAME'),
            'b_type' => do_lang_tempcode('BANNER_TYPE'),
            'the_type' => do_lang_tempcode('DEPLOYMENT_AGREEMENT'),
            //'campaign_remaining' => do_lang_tempcode('HITS_ALLOCATED'),
            'importance_modulus' => do_lang_tempcode('IMPORTANCE_MODULUS'),
            'expiry_date' => do_lang_tempcode('EXPIRY_DATE'),
            'add_date' => do_lang_tempcode('ADDED'),
        );
        if (addon_installed('unvalidated')) {
            $sortables['validated'] = do_lang_tempcode('VALIDATED');
        }
        if (((strtoupper($sort_order) != 'ASC') && (strtoupper($sort_order) != 'DESC')) || (!array_key_exists($sortable, $sortables))) {
            log_hack_attack_and_exit('ORDERBY_HACK');
        }

        $only_owned = has_privilege(get_member(), 'edit_midrange_content', 'cms_banners') ? null : get_member();
        list($rows, $max_rows) = $this->get_entry_rows(false, $current_ordering, is_null($only_owned) ? null : array('submitter' => $only_owned));

        $has_expiry_dates = false; // Save space by default
        foreach ($rows as $row) {
            if (!is_null($row['expiry_date'])) {
                $has_expiry_dates = true;
            }
        }

        $hr = array(
            do_lang_tempcode('CODENAME'),
            do_lang_tempcode('TYPE'),
            do_lang_tempcode('DEPLOYMENT_AGREEMENT'),
            //do_lang_tempcode('HITS_ALLOCATED'),     Save space by not putting in
            do_lang_tempcode('_IMPORTANCE_MODULUS'),
        );
        if ($has_expiry_dates) {
            $hr[] = do_lang_tempcode('EXPIRY_DATE');
        }
        $hr[] = do_lang_tempcode('ADDED');
        if (addon_installed('unvalidated')) {
            $hr[] = protect_from_escaping(do_template('COMCODE_ABBR', array('_GUID' => 'b8f1372bcdfb90f5c2fa8bf3329caf5f', 'TITLE' => do_lang_tempcode('VALIDATED'), 'CONTENT' => do_lang_tempcode('VALIDATED_SHORT'))));
        }
        $hr[] = do_lang_tempcode('ACTIONS');
        $header_row = results_field_title($hr, $sortables, 'sort', $sortable . ' ' . $sort_order);

        $fields = new Tempcode();

        require_code('form_templates');
        foreach ($rows as $row) {
            $edit_link = build_url($url_map + array('id' => $row['name']), '_SELF');

            $deployment_agreement = new Tempcode();
            switch ($row['the_type']) {
                case BANNER_PERMANENT:
                    $deployment_agreement = do_lang_tempcode('BANNER_PERMANENT');
                    break;
                case BANNER_CAMPAIGN:
                    $deployment_agreement = do_lang_tempcode('BANNER_CAMPAIGN');
                    break;
                case BANNER_FALLBACK:
                    $deployment_agreement = do_lang_tempcode('BANNER_FALLBACK');
                    break;
            }

            $fr = array(
                hyperlink(build_url(array('page' => 'banners', 'type' => 'view', 'source' => $row['name']), get_module_zone('banners')), do_template('COMCODE_TELETYPE', array('_GUID' => '25c57dd13c0801ad64f5bb8e6c9860f3', 'CONTENT' => escape_html($row['name']))), false, false),
                ($row['b_type'] == '') ? do_lang('_DEFAULT') : $row['b_type'],
                $deployment_agreement,
                //integer_format($row['campaign_remaining']),
                strval($row['importance_modulus']),
            );
            if ($has_expiry_dates) {
                $fr[] = is_null($row['expiry_date']) ? protect_from_escaping(do_lang_tempcode('NA_EM')) : make_string_tempcode(get_timezoned_date($row['expiry_date']));
            }
            $fr[] = get_timezoned_date($row['add_date'], false);
            if (addon_installed('unvalidated')) {
                $fr[] = ($row['validated'] == 1) ? do_lang_tempcode('YES') : do_lang_tempcode('NO');
            }
            $fr[] = protect_from_escaping(hyperlink($edit_link, do_lang_tempcode('EDIT'), false, true, $row['name']));

            $fields->attach(results_entry($fr, true));
        }

        return array(results_table(do_lang($this->menu_label), get_param_integer('start', 0), 'start', either_param_integer('max', 20), 'max', $max_rows, $header_row, $fields, $sortables, $sortable, $sort_order), false);
    }

    /**
     * Standard crud_module list function.
     *
     * @return Tempcode The selection list
     */
    public function create_selection_list_entries()
    {
        $only_owned = has_privilege(get_member(), 'edit_midrange_content', 'cms_banners') ? null : get_member();
        return create_selection_list_banners(null, $only_owned);
    }

    /**
     * Get the Tempcode for the form to add a banner, with the information passed along to it via the parameters already added in.
     *
     * @param  ID_TEXT $name The name of the banner (blank: new)
     * @param  URLPATH $image_url The URL to the banner image
     * @param  URLPATH $site_url The URL to the site the banner leads to
     * @param  SHORT_TEXT $caption The caption of the banner
     * @param  LONG_TEXT $direct_code Complete HTML/PHP for the banner
     * @param  LONG_TEXT $notes Any notes associated with the banner
     * @param  integer $importancemodulus The banners "importance modulus"
     * @range  1 max
     * @param  ?integer $campaignremaining The number of hits the banner may have (null: not applicable for this banner type)
     * @range  0 max
     * @param  SHORT_INTEGER $the_type The type of banner (a BANNER_* constant)
     * @set    0 1 2
     * @param  ?TIME $expiry_date The banner expiry date (null: never expires)
     * @param  ?MEMBER $submitter The banners submitter (null: current member)
     * @param  BINARY $validated Whether the banner has been validated
     * @param  ID_TEXT $b_type The banner type (can be anything, where blank means 'normal')
     * @param  ?array $b_types The secondary banner types (empty: no secondary banner types) (null: same as empty)
     * @param  ?array $regions The regions (empty: not region-limited) (null: same as empty)
     * @param  SHORT_TEXT $title_text The title text for the banner (only used for text banners, and functions as the 'trigger text' if the banner type is shown inline)
     * @return array Bits
     */
    public function get_form_fields($name = '', $image_url = '', $site_url = '', $caption = '', $direct_code = '', $notes = '', $importancemodulus = 3, $campaignremaining = 50, $the_type = 0, $expiry_date = null, $submitter = null, $validated = 1, $b_type = '', $b_types = null, $regions = null, $title_text = '')
    {
        if ($b_type == '') {
            $b_type = get_param_string('b_type', '');
        }

        list($fields, $_javascript) = get_banner_form_fields(false, $name, $image_url, $site_url, $caption, $direct_code, $notes, $importancemodulus, $campaignremaining, $the_type, $expiry_date, $submitter, $validated, $b_type, $b_types, $regions, $title_text);
        $this->javascript .= $_javascript;

        $fields->attach(metadata_get_fields('banner', $name));

        if (addon_installed('content_reviews')) {
            $fields->attach(content_review_get_fields('banner', $name));
        }

        // Permissions
        if (get_option('use_banner_permissions') == '1') {
            $fields->attach($this->get_permission_fields($name, null, ($name == '')));
        }

        $edit_text = ($name == '') ? new Tempcode() : do_template('BANNER_PREVIEW', array('_GUID' => 'b7c58bc13ff317870b6823716fd36f0c', 'PREVIEW' => show_banner($name, $title_text, comcode_to_tempcode($caption, $submitter), $direct_code, $image_url, '', $site_url, $b_type, is_null($submitter) ? get_member() : $submitter)));

        $hidden = new Tempcode();
        handle_max_file_size($hidden, 'image');

        return array($fields, $hidden, null, $edit_text);
    }

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

    /**
     * Standard crud_module edit form filler.
     *
     * @param  ID_TEXT $id The entry being edited
     * @return array Bits
     */
    public function fill_in_edit_form($id)
    {
        $rows = $GLOBALS['SITE_DB']->query_select('banners', array('*'), array('name' => $id), '', 1);
        if (!array_key_exists(0, $rows)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'banner'));
        }
        $myrow = $rows[0];

        $b_types = collapse_1d_complexity('b_type', $GLOBALS['SITE_DB']->query_select('banners_types', array('b_type'), array('name' => $id)));

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

        return $this->get_form_fields($id, $myrow['img_url'], $myrow['site_url'], get_translated_text($myrow['caption']), $myrow['b_direct_code'], $myrow['notes'], $myrow['importance_modulus'], $myrow['campaign_remaining'], $myrow['the_type'], $myrow['expiry_date'], $myrow['submitter'], $myrow['validated'], $myrow['b_type'], $b_types, $regions, $myrow['b_title_text']);
    }

    /**
     * Standard crud_module add actualiser.
     *
     * @return array A pair: The entry added, Description about usage
     */
    public function add_actualisation()
    {
        $name = post_param_string('name');
        $caption = post_param_string('caption');
        $direct_code = post_param_string('direct_code', '');
        $campaignremaining = post_param_integer('campaignremaining', 0);
        $site_url = fixup_protocolless_urls(post_param_string('site_url', ''));
        $importancemodulus = post_param_integer('importancemodulus', 3);
        $notes = post_param_string('notes', '');
        $the_type = post_param_integer('the_type', 1);
        $expiry_date = post_param_date('expiry_date');
        $validated = post_param_integer('validated', 0);
        $b_type = post_param_string('b_type');
        $b_types = isset($_POST['b_types']) ? $_POST['b_types'] : array();
        $regions = isset($_POST['regions']) ? $_POST['regions'] : array();
        $title_text = post_param_string('title_text', '');

        $this->donext_type = $b_type;

        list($url, $title_text) = check_banner($title_text, $direct_code, $b_type, $b_types);

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

        add_banner($name, $url, $title_text, $caption, $direct_code, $campaignremaining, $site_url, $importancemodulus, $notes, $the_type, $expiry_date, $metadata['submitter'], $validated, $b_type, $b_types, $regions, $metadata['add_time'], 0, 0, 0, 0, $metadata['edit_time']);

        $_banner_type_row = $GLOBALS['SITE_DB']->query_select('banner_types', array('t_image_width', 't_image_height'), array('id' => $b_type), '', 1);
        if (array_key_exists(0, $_banner_type_row)) {
            $banner_type_row = $_banner_type_row[0];
        } else {
            $banner_type_row = array('t_image_width' => 728, 't_image_height' => 90);
        }
        $stats_url = build_url(array('page' => 'banners', 'type' => 'view', 'source' => $name), get_module_zone('banners'));
        $banner_code = do_template('BANNER_SHOW_CODE', array('_GUID' => '745d555fcca3a1320123ad3a5a04418b', 'TYPE' => $b_type, 'NAME' => $name, 'WIDTH' => strval($banner_type_row['t_image_width']), 'HEIGHT' => strval($banner_type_row['t_image_height'])));
        $tpl = do_template('BANNER_ADDED_SCREEN', array('_GUID' => '897bab3e444f0d3c909e7a95b84d4396', 'DO_NEXT' => '', 'TEXT' => '', 'TITLE' => '', 'BANNER_CODE' => $banner_code, 'STATS_URL' => $stats_url));

        if (get_option('use_banner_permissions') == '1') {
            $this->set_permissions($name);
        }

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

        require_code('templates_donext');
        if (has_simplified_donext()) {
            $tpl = null;
        }

        return array($name, $tpl);
    }

    /**
     * Standard crud_module edit actualiser.
     *
     * @param  ID_TEXT $id The entry being edited
     */
    public function edit_actualisation($id)
    {
        $orig_submitter = $GLOBALS['SITE_DB']->query_select_value_if_there('banners', 'submitter', array('name' => $id));
        if (is_null($orig_submitter)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'banner'));
        }

        $title_text = post_param_string('title_text', '');
        $direct_code = post_param_string('direct_code', '');
        $validated = post_param_integer('validated', 0);
        $b_type = post_param_string('b_type');
        $b_types = isset($_POST['b_types']) ? $_POST['b_types'] : array();
        $regions = isset($_POST['regions']) ? $_POST['regions'] : array();

        $this->donext_type = $b_type;

        list($url, $title_text) = check_banner($title_text, $direct_code, $b_type);

        $new_id = post_param_string('name');

        $metadata = actual_metadata_get_fields('banner', $id, null, $new_id);

        edit_banner($id, $new_id, $url, $title_text, post_param_string('caption'), $direct_code, post_param_integer('campaignremaining', 0), fixup_protocolless_urls(post_param_string('site_url')), post_param_integer('importancemodulus'), post_param_string('notes', ''), post_param_integer('the_type', 1), post_param_date('expiry_date'), $metadata['submitter'], $validated, $b_type, $b_types, $regions, $metadata['edit_time'], $metadata['add_time'], true);

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

        $this->new_id = post_param_string('name');

        if (get_option('use_banner_permissions') == '1') {
            $this->set_permissions($id);
        }

        if (addon_installed('content_reviews')) {
            content_review_set('banner', $new_id, $id);
        }
    }

    /**
     * Standard crud_module delete actualiser.
     *
     * @param  ID_TEXT $id The entry being deleted
     */
    public function delete_actualisation($id)
    {
        $b_type = post_param_string('b_type');
        $this->donext_type = $b_type;

        delete_banner($id);
    }

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

    /**
     * The actualiser to export a banners CSV.
     *
     * @return Tempcode The UI
     */
    public function export_csv()
    {
        $csv_rows = array();

        $has_banner_network = $GLOBALS['SITE_DB']->query_select_value('banners', 'SUM(views_from)') != 0.0;

        $only_owned = has_privilege(get_member(), 'edit_midrange_content', 'cms_banners') ? null : get_member();
        $rows = $GLOBALS['SITE_DB']->query_select('banners', array('*'), is_null($only_owned) ? null : array('submitter' => $only_owned), 'ORDER BY name');

        $has_title_text = false;
        $has_caption = false;
        $has_deployment_agreement = false;
        foreach ($rows as $row) {
            if ($row['b_title_text'] != '') {
                $has_title_text = true;
            }
            if (get_translated_text($row['caption']) != '') {
                $has_caption = true;
            }
            if ($row['the_type'] != BANNER_PERMANENT) {
                $has_deployment_agreement = true;
            }
        }

        foreach ($rows as $row) {
            $csv_row = array();

            // Basic details...

            $csv_row[do_lang('CODENAME')] = $row['name'];

            $csv_row[do_lang('BANNER_TYPE')] = ($row['b_type'] == '') ? do_lang('_DEFAULT') : $row['b_type'];

            $banner_types = implode(', ', collapse_1d_complexity('b_type', $GLOBALS['SITE_DB']->query_select('banners_types', array('b_type'), array('name' => $row['name']))));
            $csv_row[do_lang('SECONDARY_CATEGORIES')] = $banner_types;

            if ($has_title_text) {
                $csv_row[do_lang('BANNER_TITLE_TEXT')] = $row['b_title_text'];
            }

            if ($has_caption) {
                $csv_row[do_lang('DESCRIPTION')] = get_translated_text($row['caption']);
            }

            $csv_row[do_lang('IMAGE')] = (url_is_local($row['img_url']) ? (get_custom_base_url() . '/') : '') . $row['img_url'];

            $csv_row[do_lang('DESTINATION_URL')] = $row['site_url'];

            // Basic stats...

            if ($has_banner_network) {
                $csv_row[strip_html(do_lang('BANNER_HITS_FROM'))] = integer_format($row['hits_from']);
                $csv_row[strip_html(do_lang('BANNER_VIEWS_FROM'))] = integer_format($row['views_from']);
            }
            $csv_row[strip_html(do_lang('BANNER_HITS_TO'))] = ($row['site_url'] == '') ? strip_html(do_lang('CANT_TRACK')) : integer_format($row['hits_to']);
            $csv_row[strip_html(do_lang('BANNER_VIEWS_TO'))] = ($row['site_url'] == '') ? strip_html(do_lang('CANT_TRACK')) : integer_format($row['views_to']);

            if ($row['views_to'] != 0) {
                $click_through = float_format(100.0 * (floatval($row['hits_to']) / floatval($row['views_to'])));
            } else {
                $click_through = do_lang('NA');
            }
            $csv_row[strip_html(do_lang('BANNER_CLICKTHROUGH'))] = ($row['site_url'] == '') ? strip_html(do_lang('CANT_TRACK')) : $click_through;

            // Display determination details...

            if ($has_deployment_agreement) {
                $deployment_agreement = '';
                $campaign_remaining = '';
                switch ($row['the_type']) {
                    case BANNER_PERMANENT:
                        $deployment_agreement = do_lang('BANNER_PERMANENT');
                        $campaign_remaining = integer_format($row['campaign_remaining']);
                        break;
                    case BANNER_CAMPAIGN:
                        $deployment_agreement = do_lang('BANNER_CAMPAIGN');
                        break;
                    case BANNER_FALLBACK:
                        $deployment_agreement = do_lang('BANNER_FALLBACK');
                        break;
                }
                $csv_row[do_lang('DEPLOYMENT_AGREEMENT')] = $deployment_agreement;

                $csv_row[do_lang('HITS_ALLOCATED')] = $campaign_remaining;
            }

            $csv_row[do_lang('IMPORTANCE_MODULUS')] = strval($row['importance_modulus']);

            if (addon_installed('stats')) {
                $banners_regions = implode(', ', collapse_1d_complexity('region', $GLOBALS['SITE_DB']->query_select('content_regions', array('region'), array('content_type' => 'banner', 'content_id' => $row['name']))));
                $csv_row[do_lang('FILTER_REGIONS')] = $banners_regions;
            }

            $csv_row[do_lang('EXPIRY_DATE')] = is_null($row['expiry_date']) ? do_lang('NA') : get_timezoned_date($row['expiry_date']);

            if (addon_installed('unvalidated')) {
                $csv_row[do_lang('VALIDATED')] = ($row['validated'] == 1) ? do_lang('YES') : do_lang('NO');
            }

            // Meta details...

            $username = $GLOBALS['FORUM_DRIVER']->get_username($row['submitter']);
            if ($username === null) {
                $username = do_lang('UNKNOWN');
            }
            $csv_row[do_lang('SUBMITTER')] = $username;

            $csv_row[do_lang('ADDED')] = get_timezoned_date($row['add_date']);
            $csv_row[do_lang('EDITED')] = is_null($row['edit_date']) ? '' : date('Y-m-d', $row['edit_date']);

            $csv_rows[] = $csv_row;
        }

        require_code('files2');
        make_csv($csv_rows, 'banners.csv');

        return new Tempcode();
    }
}

/**
 * Module page class.
 */
class Module_cms_banners_cat extends Standard_crud_module
{
    public $lang_type = 'BANNER_TYPE';
    public $select_name = 'BANNER_TYPE';
    public $select_name_description = '_DESCRIPTION_BANNER_TYPE';
    public $orderer = 'id';
    public $array_key = 'id';
    public $title_is_multi_lang = false;
    public $non_integer_id = true;
    public $protect_first = 1;
    public $table = 'banner_types';
    public $permissions_require = 'cat_high';
    public $menu_label = 'BANNERS';
    public $no_blank_ids = false;
    public $is_chained_with_parent_browse = true;
    public $do_preview = null;

    /**
     * Standard crud_module table function.
     *
     * @param  array $url_map Details to go to build_url for link to the next screen.
     * @return array A pair: The choose table, Whether reordering is supported from this screen.
     */
    public function create_selection_list_choose_table($url_map)
    {
        require_code('templates_results_table');

        $current_ordering = get_param_string('sort', 'id ASC', true);
        list($sortable, $sort_order) = array(substr($current_ordering, 0, strrpos($current_ordering, ' ')), substr($current_ordering, strrpos($current_ordering, ' ') + 1));
        $sortables = array(
            'id' => do_lang_tempcode('CODENAME'),
            't_is_textual' => do_lang_tempcode('BANNER_IS_TEXTUAL'),
            't_image_width' => do_lang_tempcode('WIDTH'),
            't_image_height' => do_lang_tempcode('HEIGHT'),
            't_max_file_size' => do_lang_tempcode('FILE_SIZE'),
            't_comcode_inline' => do_lang_tempcode('COMCODE_INLINE'),
        );
        if (db_has_subqueries($GLOBALS['SITE_DB']->connection_read)) {
            $sortables['(SELECT COUNT(*) FROM ' . get_table_prefix() . 'banners WHERE b_type=r.id)'] = do_lang_tempcode('COUNT_TOTAL');
        }
        if (((strtoupper($sort_order) != 'ASC') && (strtoupper($sort_order) != 'DESC')) || (!array_key_exists($sortable, $sortables))) {
            log_hack_attack_and_exit('ORDERBY_HACK');
        }

        $header_row = results_field_title(array(
            do_lang_tempcode('CODENAME'),
            do_lang_tempcode('BANNER_IS_TEXTUAL'),
            do_lang_tempcode('WIDTH'),
            do_lang_tempcode('HEIGHT'),
            do_lang_tempcode('FILE_SIZE'),
            do_lang_tempcode('COMCODE_INLINE'),
            do_lang_tempcode('COUNT_TOTAL'),
            do_lang_tempcode('ACTIONS'),
        ), $sortables, 'sort', $sortable . ' ' . $sort_order);

        $fields = new Tempcode();

        require_code('form_templates');
        require_code('files');
        list($rows, $max_rows) = $this->get_entry_rows(false, $current_ordering);
        foreach ($rows as $row) {
            $edit_link = build_url($url_map + array('id' => $row['id']), '_SELF');

            $total = integer_format($GLOBALS['SITE_DB']->query_select_value('banners', 'COUNT(*)', array('b_type' => $row['id'])));

            $fields->attach(results_entry(array(($row['id'] == '') ? do_lang('_DEFAULT') : $row['id'], ($row['t_is_textual'] == 1) ? do_lang_tempcode('YES') : do_lang_tempcode('NO'), escape_html(integer_format($row['t_image_width'])), escape_html(integer_format($row['t_image_height'])), clean_file_size($row['t_max_file_size'] * 1024), ($row['t_comcode_inline'] == 1) ? do_lang_tempcode('YES') : do_lang_tempcode('NO'), $total, protect_from_escaping(hyperlink($edit_link, do_lang_tempcode('EDIT'), false, true, '#' . $row['id']))), true));
        }

        return array(results_table(do_lang($this->menu_label), get_param_integer('start', 0), 'start', get_param_integer('max', 20), 'max', $max_rows, $header_row, $fields, $sortables, $sortable, $sort_order), false);
    }

    /**
     * Get Tempcode for a post template adding/editing form.
     *
     * @param  ?ID_TEXT $id The ID of the banner type (null: new)
     * @param  BINARY $is_textual Whether this is a textual banner
     * @param  integer $image_width The image width (ignored for textual banners)
     * @param  integer $image_height The image height (ignored for textual banners)
     * @param  integer $max_file_size The maximum file size for the banners (this is a string length for textual banners)
     * @param  BINARY $comcode_inline Whether the banner will be automatically shown via Comcode hot-text (this can only happen if banners of the title are given title-text)
     * @return array A pair: the Tempcode for the visible fields, and the Tempcode for the hidden fields
     */
    public function get_form_fields($id = null, $is_textual = 0, $image_width = 160, $image_height = 600, $max_file_size = 250, $comcode_inline = 0)
    {
        $fields = new Tempcode();
        $hidden = new Tempcode();

        $fields->attach(form_input_codename(do_lang_tempcode('CODENAME'), do_lang_tempcode('DESCRIPTION_BANNER_TYPE_2'), 'new_id', $id, false));
        if ($id != '') {
            $hidden->attach(form_input_hidden('is_textual', strval($is_textual)));
        } else {
            $fields->attach(form_input_tick(do_lang_tempcode('BANNER_IS_TEXTUAL'), do_lang_tempcode('DESCRIPTION_BANNER_IS_TEXTUAL'), 'is_textual', $is_textual == 1));
        }
        $fields->attach(form_input_dimensions(do_lang_tempcode('DIMENSIONS'), do_lang_tempcode('DESCRIPTION_BANNER_DIMENSIONS'), 'image_width', 'image_height', $image_width, $image_height, true));
        $fields->attach(form_input_integer(do_lang_tempcode('FILE_SIZE'), do_lang_tempcode('DESCRIPTION_BANNER_FILE_SIZE'), 'max_file_size', $max_file_size, true));
        $fields->attach(form_input_tick(do_lang_tempcode('COMCODE_INLINE'), do_lang_tempcode('DESCRIPTION_COMCODE_INLINE'), 'comcode_inline', $comcode_inline == 1));

        $fields->attach(metadata_get_fields('banner_type', ($id === null) ? null : $id));

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

        return array($fields, $hidden);
    }

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

        list($fields, $hidden) = $this->get_form_fields($id, $r['t_is_textual'], $r['t_image_width'], $r['t_image_height'], $r['t_max_file_size'], $r['t_comcode_inline']);

        return array($fields, $hidden);
    }

    /**
     * Standard crud_module add actualiser.
     *
     * @return array A pair: The entry added, description about usage
     */
    public function add_actualisation()
    {
        $id = post_param_string('new_id');
        $is_textual = post_param_integer('is_textual', 0);
        $image_width = post_param_integer('image_width');
        $image_height = post_param_integer('image_height');
        $max_file_size = post_param_integer('max_file_size');
        $comcode_inline = post_param_integer('comcode_inline', 0);

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

        add_banner_type($id, $is_textual, $image_width, $image_height, $max_file_size, $comcode_inline);

        set_url_moniker('banner_type', $id);

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

        $tpl = do_lang_tempcode('ADD_BANNER_TEMPLATING');

        require_code('templates_donext');
        if (has_simplified_donext()) {
            $tpl = null;
        }

        return array($id, $tpl);
    }

    /**
     * Standard crud_module edit actualiser.
     *
     * @param  ID_TEXT $id The entry being edited
     * @return Tempcode Description about usage
     */
    public function edit_actualisation($id)
    {
        $is_textual = post_param_integer('is_textual', 0);
        $image_width = post_param_integer('image_width');
        $image_height = post_param_integer('image_height');
        $max_file_size = post_param_integer('max_file_size');
        $comcode_inline = post_param_integer('comcode_inline', 0);

        $new_id = post_param_string('new_id');

        $metadata = actual_metadata_get_fields('banner_type', $id, null, $new_id);

        edit_banner_type($id, $new_id, $is_textual, $image_width, $image_height, $max_file_size, $comcode_inline);

        $this->new_id = post_param_string('new_id');

        if (addon_installed('content_reviews')) {
            content_review_set('banner_type', $new_id, $id);
        }

        return do_lang_tempcode('ADD_BANNER_TEMPLATING');
    }

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

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

    /**
     * The do-next manager for after banner content management.
     *
     * @param  Tempcode $title The title (output of get_screen_title)
     * @param  Tempcode $description Some description to show, saying what happened
     * @param  ?AUTO_LINK $id The ID of whatever was just handled (null: N/A)
     * @param  ID_TEXT $type The type ID we were working in
     * @return Tempcode The UI
     */
    public function _do_next_manager($title, $description, $id, $type)
    {
        require_code('templates_donext');

        if ((is_null($id)) && (is_null($type))) {
            return do_next_manager($title, $description,
                null,
                null,
                /* TYPED-ORDERED LIST OF 'LINKS'    */
                array('_SELF', array('type' => 'add'), '_SELF', do_lang_tempcode('ADD_BANNER')), // Add one
                null, // Edit this
                has_privilege(get_member(), 'edit_own_lowrange_content', 'cms_banners') ? array('_SELF', array('type' => 'edit'), '_SELF', do_lang_tempcode('EDIT_BANNER')) : null, // Edit one
                null, // View this
                null, // View archive
                null, // Add to category
                has_privilege(get_member(), 'submit_cat_highrange_content', 'cms_banners') ? array('_SELF', array('type' => 'add_category'), '_SELF', do_lang_tempcode('ADD_BANNER_TYPE')) : null, // Add one category
                has_privilege(get_member(), 'edit_cat_highrange_content', 'cms_banners') ? array('_SELF', array('type' => 'edit_category'), '_SELF', do_lang_tempcode('EDIT_BANNER_TYPE')) : null, // Edit one category
                null, // Edit this category
                null, // View this category
                null,
                null,
                null,
                null,
                null,
                null,
                do_lang_tempcode('BANNER_TYPES'),
                'banner',
                'banner_type'
            );
        }

        return do_next_manager($title, $description,
            null,
            null,
            /* TYPED-ORDERED LIST OF 'LINKS'  */
            array('_SELF', array('type' => 'add', 'b_type' => $type), '_SELF', do_lang_tempcode('ADD_BANNER')), // Add one
            (is_null($id) || (!has_privilege(get_member(), 'edit_own_lowrange_content', 'cms_banners'))) ? null : array('_SELF', array('type' => '_edit', 'id' => $id), '_SELF', do_lang_tempcode('EDIT_THIS_BANNER')), // Edit this
            has_privilege(get_member(), 'edit_own_lowrange_content', 'cms_banners') ? array('_SELF', array('type' => 'edit'), '_SELF', do_lang_tempcode('EDIT_BANNER')) : null, // Edit one
            ((is_null($id)) || (/*Don't go direct to view if simplified do-next on as too unnatural*/get_option('simplified_donext') == '1')) ? null : array('banners', array('type' => 'view', 'source' => $id), get_module_zone('banners')), // View this
            array('admin_banners', array('type' => 'browse'), get_module_zone('admin_banners')), // View archive
            null, // Add to category
            has_privilege(get_member(), 'submit_cat_highrange_content', 'cms_banners') ? array('_SELF', array('type' => 'add_category'), '_SELF', do_lang_tempcode('ADD_BANNER_TYPE')) : null, // Add one category
            has_privilege(get_member(), 'edit_cat_highrange_content', 'cms_banners') ? array('_SELF', array('type' => 'edit_category'), '_SELF', do_lang_tempcode('EDIT_BANNER_TYPE')) : null, // Edit one category
            has_privilege(get_member(), 'edit_cat_highrange_content', 'cms_banners') ? array('_SELF', array('type' => '_edit_category', 'id' => $type), '_SELF', do_lang_tempcode('EDIT_THIS_BANNER_TYPE')) : null, // Edit this category
            null, // View this category
            null,
            null,
            null,
            null,
            null,
            null,
            do_lang_tempcode('BANNER_TYPES'),
            'banner',
            'banner_type'
        );
    }
}
