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

require_code('crud_module');

/**
 * Module page class.
 */
class Module_cms_news extends Standard_crud_module
{
    public $lang_type = 'NEWS';
    public $select_name = 'TITLE';
    public $code_require = 'news';
    public $permissions_require = 'high';
    public $permissions_cat_require = 'news';
    public $permissions_cat_name = 'main_news_category';
    public $user_facing = true;
    public $seo_type = 'news';
    public $content_type = 'news';
    public $possibly_some_kind_of_upload = true;
    public $upload = 'image';
    public $menu_label = 'NEWS';
    public $table = 'news';
    public $orderer = 'title';
    public $title_is_multi_lang = true;

    public $donext_type = null;

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

        $ret = array(
            'browse' => array('MANAGE_NEWS', 'menu/rich_content/news'),
        ) + parent::get_entry_points();

        if ($support_crosslinks) {
            require_code('fields');
            $ret += manage_custom_fields_entry_points('news_category') + manage_custom_fields_entry_points('news');
        }

        return $ret;
    }

    /**
     * Find privileges defined as overridable by this module.
     *
     * @return array A map of privileges that are overridable; privilege to 0 or 1. 0 means "not category overridable". 1 means "category overridable".
     */
    public function get_privilege_overrides()
    {
        require_lang('news');
        return array(
            'view_private_content' => 0,

            'mass_import' => 0,

            'have_personal_category' => array(0, 'HAVE_BLOG'),

            'submit_cat_highrange_content' => array(0, 'ADD_NEWS_CATEGORY'),
            'edit_own_cat_highrange_content' => array(0, 'EDIT_OWN_NEWS_CATEGORY'),
            'edit_cat_highrange_content' => array(0, 'EDIT_NEWS_CATEGORY'),
            'delete_own_cat_highrange_content' => array(0, 'DELETE_OWN_NEWS_CATEGORY'),
            'delete_cat_highrange_content' => array(0, 'DELETE_NEWS_CATEGORY'),

            'submit_highrange_content' => array(1, 'ADD_NEWS'),
            'bypass_validation_highrange_content' => array(1, 'BYPASS_NEWS_VALIDATION'),
            'edit_own_highrange_content' => array(1, 'EDIT_OWN_NEWS'),
            'edit_highrange_content' => array(1, 'EDIT_NEWS'),
            'delete_own_highrange_content' => array(1, 'DELETE_OWN_NEWS'),
            'delete_highrange_content' => array(1, 'DELETE_NEWS'),

            // These are set as non-overridable. Technically they are, but that makes for confusion!
            //  Best to just keep the idea that only a blog owner or some vague 'staff' can edit a member's blog
            //   i.e. no need to make this editable per-blog! Just use a regular news category if you need that control.
            'submit_midrange_content' => array(0, 'ADD_NEWS_BLOG'),
            'bypass_validation_midrange_content' => array(0, 'BYPASS_NEWS_BLOG_VALIDATION'),
            'edit_own_midrange_content' => array(0, 'EDIT_OWN_NEWS_BLOG'),
            'edit_midrange_content' => array(0, 'EDIT_NEWS_BLOG'),
            'delete_own_midrange_content' => array(0, 'DELETE_OWN_NEWS_BLOG'),
            'delete_midrange_content' => array(0, 'DELETE_NEWS_BLOG'),
        );
    }

    public $title;

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

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

        require_lang('news');

        inform_non_canonical_parameter('cat');

        set_helper_panel_tutorial('tut_news');

        if ($type == 'add' || $type == '_edit') {
            set_helper_panel_text(comcode_lang_string('DOC_NEWS'));
        }

        if ($type == 'import' || $type == '_import_news') {
            $this->title = get_screen_title('IMPORT_NEWS');
        }

        if ($type == '_import_news') {
            breadcrumb_set_parents(array(array('_SELF:_SELF:import', do_lang_tempcode('IMPORT_NEWS'))));
            breadcrumb_set_self(do_lang_tempcode('DONE'));
        }

        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)
    {
        $this->posting_form_title = do_lang_tempcode('NEWS_ARTICLE');

        require_code('input_filter_2');
        modsecurity_workaround_enable();

        require_code('news2');
        require_css('news');

        // Decide what to do
        if ($type == 'browse') {
            return $this->browse();
        }
        if ($type == 'import') {
            return $this->import_news();
        }
        if ($type == '_import_news') {
            return $this->_import_news();
        }

        return new Tempcode();
    }

    /**
     * The do-next manager for before content management.
     *
     * @return Tempcode The UI
     */
    public function browse()
    {
        require_code('templates_donext');
        require_code('fields');
        return do_next_manager(get_screen_title('MANAGE_NEWS'), comcode_lang_string('DOC_NEWS'),
            array_merge(array(
                has_privilege(get_member(), 'submit_cat_highrange_content', 'cms_news') ? array('menu/_generic_admin/add_one_category', array('_SELF', array('type' => 'add_category'), '_SELF'), do_lang('ADD_NEWS_CATEGORY')) : null,
                has_privilege(get_member(), 'edit_own_cat_highrange_content', 'cms_news') ? array('menu/_generic_admin/edit_one_category', array('_SELF', array('type' => 'edit_category'), '_SELF'), do_lang('EDIT_NEWS_CATEGORY')) : null,
                has_privilege(get_member(), 'submit_highrange_content', 'cms_news') ? array('menu/_generic_admin/add_one', array('_SELF', array('type' => 'add'), '_SELF'), do_lang('ADD_NEWS')) : null,
                has_privilege(get_member(), 'edit_own_highrange_content', 'cms_news') ? array('menu/_generic_admin/edit_one', array('_SELF', array('type' => 'edit'), '_SELF'), do_lang('EDIT_NEWS')) : null,
                has_privilege(get_member(), 'mass_import', 'cms_news') ? array('menu/_generic_admin/import', array('_SELF', array('type' => 'import'), '_SELF'), do_lang('IMPORT_NEWS')) : null,
            ), manage_custom_fields_donext_link('news')),
            do_lang('MANAGE_NEWS')
        );
    }

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

        $current_ordering = get_param_string('sort', 'date_and_time DESC');
        if (strpos($current_ordering, ' ') === false) {
            warn_exit(do_lang_tempcode('INTERNAL_ERROR'));
        }
        list($sortable, $sort_order) = explode(' ', $current_ordering, 2);
        $sortables = array(
            'title' => do_lang_tempcode('TITLE'),
            'news_category' => do_lang_tempcode('MAIN_CATEGORY'),
            'date_and_time' => do_lang_tempcode('ADDED'),
            'news_views' => do_lang_tempcode('COUNT_VIEWS'),
            'submitter' => do_lang_tempcode('metadata:OWNER'),
        );
        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');
        }

        $fh = array(do_lang_tempcode('TITLE'), do_lang_tempcode('MAIN_CATEGORY'));
        $fh[] = do_lang_tempcode('ADDED');
        $fh[] = do_lang_tempcode('COUNT_VIEWS');
        if (addon_installed('unvalidated')) {
            $fh[] = protect_from_escaping(do_template('COMCODE_ABBR', array('_GUID' => '5e5269c25a2eb44fd92b554a50f6007f', 'TITLE' => do_lang_tempcode('VALIDATED'), 'CONTENT' => do_lang_tempcode('VALIDATED_SHORT'))));
        }
        $fh[] = do_lang_tempcode('metadata:OWNER');
        $fh[] = do_lang_tempcode('ACTIONS');
        $header_row = results_field_title($fh, $sortables, 'sort', $sortable . ' ' . $sort_order);

        $fields = new Tempcode();

        require_code('form_templates');
        $only_owned = has_privilege(get_member(), 'edit_highrange_content', 'cms_news') ? null : get_member();
        list($rows, $max_rows) = $this->get_entry_rows(false, $current_ordering, is_null($only_owned) ? null : array('submitter' => $only_owned));
        $news_cat_titles = array();
        foreach ($rows as $row) {
            $edit_link = build_url($url_map + array('id' => $row['id']), '_SELF');

            $fr = array();
            $fr[] = protect_from_escaping(hyperlink(build_url(array('page' => 'news', 'type' => 'view', 'id' => $row['id']), get_module_zone('news')), get_translated_text($row['title']), false, true));
            if (array_key_exists($row['news_category'], $news_cat_titles)) {
                $nc_title = $news_cat_titles[$row['news_category']];
            } else {
                $nc_title = $GLOBALS['SITE_DB']->query_select_value_if_there('news_categories', 'nc_title', array('id' => $row['news_category']));
                $news_cat_titles[$row['news_category']] = $nc_title;
            }
            if (!is_null($nc_title)) {
                $fr[] = protect_from_escaping(hyperlink(build_url(array('page' => 'news', 'type' => 'browse', 'select' => $row['news_category']), get_module_zone('news')), get_translated_text($nc_title), false, true));
            } else {
                $fr[] = do_lang('UNKNOWN');
            }
            $fr[] = get_timezoned_date($row['date_and_time']);
            $fr[] = integer_format($row['news_views']);
            if (addon_installed('unvalidated')) {
                $fr[] = ($row['validated'] == 1) ? do_lang_tempcode('YES') : do_lang_tempcode('NO');
            }
            $username = protect_from_escaping($GLOBALS['FORUM_DRIVER']->member_profile_hyperlink($row['submitter']));
            $fr[] = $username;
            $fr[] = protect_from_escaping(hyperlink($edit_link, do_lang_tempcode('EDIT'), false, true, do_lang('EDIT') . ' #' . strval($row['id'])));

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

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

        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, $search_url, $archive_url);
    }

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

    /**
     * Get Tempcode for a news adding/editing form.
     *
     * @param  ?AUTO_LINK $id The news ID (null: new)
     * @param  ?AUTO_LINK $main_news_category The primary category for the news (null: personal)
     * @param  ?array $news_category A list of categories the news is in (null: not known)
     * @param  SHORT_TEXT $title The news title
     * @param  LONG_TEXT $news The news summary
     * @param  SHORT_TEXT $author The name of the author
     * @param  BINARY $validated Whether the news is validated
     * @param  ?BINARY $allow_rating Whether rating is allowed (null: decide statistically, based on existing choices)
     * @param  ?SHORT_INTEGER $allow_comments Whether comments are allowed (0=no, 1=yes, 2=review style) (null: decide statistically, based on existing choices)
     * @param  ?BINARY $allow_trackbacks Whether trackbacks are allowed (null: decide statistically, based on existing choices)
     * @param  BINARY $send_trackbacks Whether to show the "send trackback" field
     * @param  LONG_TEXT $notes Notes for the video
     * @param  URLPATH $image URL to the image for the news entry (blank: use cat image)
     * @param  ?array $scheduled Scheduled go-live time (null: N/A)
     * @param  ?array $regions The regions (empty: not region-limited) (null: same as empty)
     * @return array A tuple of lots of info (fields, hidden fields, trailing fields, tabindex for posting form)
     */
    public function get_form_fields($id = null, $main_news_category = null, $news_category = null, $title = '', $news = '', $author = '', $validated = 1, $allow_rating = null, $allow_comments = null, $allow_trackbacks = null, $send_trackbacks = 1, $notes = '', $image = '', $scheduled = null, $regions = null)
    {
        if (is_null($id)) {
            // Cloning support
            $id = get_param_integer('id', null);
            if (!is_null($id)) {
                if (method_exists($this, 'get_submitter')) {
                    list($submitter) = $this->get_submitter($id);
                } else {
                    $submitter = null;
                }

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

                $ret = $this->fill_in_edit_form($id);

                $ret[2] = null;
                $ret[3] = null;
                $ret[4] = null; // These strictly only relate to edits

                $this->posting_form_text = $ret[5];
                $ret[5] = null;

                $this->posting_form_text_parsed = $ret[7];
                $ret[7] = null;

                return $ret;
            }
        }

        list($allow_rating, $allow_comments, $allow_trackbacks) = $this->choose_feedback_fields_statistically($allow_rating, $allow_comments, $allow_trackbacks);

        if (is_null($main_news_category)) {
            $param_cat = get_param_string('cat', '');
            if ($param_cat == '') {
                $news_category = array();
                $main_news_category = null;
            } elseif (strpos($param_cat, ',') === false) {
                $news_category = array();
                $main_news_category = intval($param_cat);
            } else {
                require_code('selectcode');
                $_param_cat = explode(',', $param_cat);
                $_main_news_category = array_shift($_param_cat);
                $param_cat = implode(',', $_param_cat);
                $main_news_category = ($_main_news_category == '') ? null : intval($_main_news_category);
                $news_category = selectcode_to_idlist_using_db($param_cat, 'id', 'news_categories', 'news_categories', null, 'id', 'id');
            }

            $author = $GLOBALS['FORUM_DRIVER']->get_username(get_member());
        }

        $cats1 = create_selection_list_news_categories($main_news_category, false, true, is_integer($main_news_category), null, true);
        if ($cats1->is_empty()) {
            warn_exit(do_lang_tempcode('NO_CATEGORIES', 'news_category'));
        }
        $cats2 = create_selection_list_news_categories(is_null($news_category) ? array() : $news_category, false, true, is_integer($main_news_category), null, true);

        $fields = new Tempcode();
        $fields2 = new Tempcode();
        require_code('form_templates');
        $fields->attach(form_input_line_comcode(do_lang_tempcode('TITLE'), do_lang_tempcode('DESCRIPTION_TITLE'), 'title', $title, true));
        $fields->attach(form_input_list(do_lang_tempcode('MAIN_CATEGORY'), do_lang_tempcode('DESCRIPTION_MAIN_CATEGORY'), 'main_news_category', $cats1));
        if (addon_installed('authors')) {
            $fields->attach(form_input_author(do_lang_tempcode('SOURCE'), do_lang_tempcode('DESCRIPTION_SOURCE'), 'author', $author, true));
        }

        require_code('feedback2');

        $posting_form_tabindex = get_form_field_tabindex(null);

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

        $fields2->attach(do_template('FORM_SCREEN_FIELD_SPACER', array('_GUID' => '90e0f1f4557eb78d58b9a13c3e1e65dc', 'SECTION_HIDDEN' => $news == '' && $image == '' && (is_null($scheduled)) && ($title == ''/*=new entry and selected news cats was from URL*/ || is_null($news_category) || $news_category == array()), 'TITLE' => do_lang_tempcode('ADVANCED'))));

        $news_summary_required = (get_option('news_summary_required') == '1');
        $_summary_field = form_input_text_comcode(do_lang_tempcode('NEWS_SUMMARY'), $news_summary_required ? new Tempcode() : do_lang_tempcode('DESCRIPTION_NEWS_SUMMARY'), 'news', $news, $news_summary_required);
        if ($news_summary_required) {
            $fields->attach($_summary_field);
        } else {
            $fields2->attach($_summary_field);
        }

        $fields2->attach(form_input_multi_list(do_lang_tempcode('SECONDARY_CATEGORIES'), do_lang_tempcode('DESCRIPTION_SECONDARY_CATEGORIES', 'news'), 'news_category', $cats2));

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

        $hidden = new Tempcode();

        $fields2->attach(form_input_upload_multi_source(do_lang_tempcode('REPRESENTATIVE_IMAGE'), do_lang_tempcode('DESCRIPTION_NEWS_IMAGE_OVERRIDE'), $hidden, 'image', 'newscats', false, $image));

        if ((addon_installed('calendar')) && (has_privilege(get_member(), 'scheduled_publication_times'))) {
            $fields2->attach(form_input_date__scheduler(do_lang_tempcode('PUBLICATION_TIME'), do_lang_tempcode('DESCRIPTION_PUBLICATION_TIME'), 'schedule', false, ($scheduled === null), true, $scheduled, intval(date('Y')) - 1970 + 2, 1970));
        }

        require_code('activities');
        $fields2->attach(get_syndication_option_fields('news'));

        // Metadata
        require_code('seo2');
        $seo_fields = seo_get_fields($this->seo_type, is_null($id) ? null : strval($id), false);
        require_code('feedback2');
        $feedback_fields = feedback_fields($this->content_type, $allow_rating == 1, $allow_comments == 1, $allow_trackbacks == 1, $send_trackbacks == 1, $notes, $allow_comments == 2, false, true, false);
        $fields2->attach(metadata_get_fields('news', is_null($id) ? null : strval($id), false, null, ($seo_fields->is_empty() && $feedback_fields->is_empty()) ? METADATA_HEADER_YES : METADATA_HEADER_FORCE));
        $fields2->attach($seo_fields);
        $fields2->attach($feedback_fields);

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

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

        return array($fields, $hidden, null, null, null, null, make_string_tempcode($fields2->evaluate())/*XHTMLXHTML*/, $posting_form_tabindex);
    }

    /**
     * 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('news', array('submitter', 'date_and_time'), array('id' => intval($id)), '', 1);
        if (!array_key_exists(0, $rows)) {
            return array(null, null);
        }
        return array($rows[0]['submitter'], $rows[0]['date_and_time']);
    }

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

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

        require_lang('menus');
        require_lang('zones');

        $rows = $GLOBALS['SITE_DB']->query_select('news', array('*'), array('id' => intval($id)), '', 1);
        if (!array_key_exists(0, $rows)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'news'));
        }
        $myrow = $rows[0];

        $cat = $myrow['news_category'];

        $categories = array();
        $category_query = $GLOBALS['SITE_DB']->query_select('news_category_entries', array('news_entry_category'), array('news_entry' => $id));

        foreach ($category_query as $value) {
            $categories[] = $value['news_entry_category'];
        }

        $scheduled = mixed();

        if (addon_installed('calendar')) {
            $schedule_code = ':$GLOBALS[\'SITE_DB\']->query_update(\'news\',array(\'date_and_time\'=>$GLOBALS[\'_EVENT_TIMESTAMP\'],\'validated\'=>1),array(\'id\'=>' . strval($id) . '),\'\',1); require_code(\'sitemap_xml\'); notify_sitemap_node_edit(\'_SEARCH:news:view:' . strval($id) . '\'); decache(\'main_news\'); decache(\'main_image_fader_news\'); decache(\'side_news\'); decache(\'side_news_archive\'); decache(\'bottom_news\'); decache(\'side_news_categories\'); erase_static_cache();';
            $past_event = $GLOBALS['SITE_DB']->query_select('calendar_events', array('*'), array($GLOBALS['SITE_DB']->translate_field_ref('e_content') => $schedule_code), '', 1);
            $scheduled = array_key_exists(0, $past_event) ? array($past_event[0]['e_start_minute'], $past_event[0]['e_start_hour'], $past_event[0]['e_start_month'], $past_event[0]['e_start_day'], $past_event[0]['e_start_year']) : null;
            if ((!is_null($scheduled)) && (mktime($scheduled[1], $scheduled[0], 0, $scheduled[2], $scheduled[3], $scheduled[4]) < time())) {
                $scheduled = null;
            }
        } else {
            $scheduled = null;
        }

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

        $ret = $this->get_form_fields($id, $cat, $categories, get_translated_text($myrow['title']), get_translated_text($myrow['news']), $myrow['author'], $myrow['validated'], $myrow['allow_rating'], $myrow['allow_comments'], $myrow['allow_trackbacks'], 0, $myrow['notes'], $myrow['news_image'], $scheduled, $regions);

        $ret[2] = new Tempcode();
        $ret[3] = '';
        $ret[4] = false;
        $ret[5] = get_translated_text($myrow['news_article']);
        $ret[7] = get_translated_tempcode('news', $myrow, 'news_article');
        return $ret;
    }

    /**
     * Standard crud_module add actualiser.
     *
     * @return ID_TEXT The ID of the entry added
     */
    public function add_actualisation()
    {
        $author = post_param_string('author', $GLOBALS['FORUM_DRIVER']->get_username(get_member()));
        $news = post_param_string('news');
        $title = post_param_string('title');
        $validated = post_param_integer('validated', 0);
        $news_article = post_param_string('post');
        if (post_param_string('main_news_category') != 'personal') {
            $main_news_category = post_param_integer('main_news_category');
        } else {
            $main_news_category = null;
        }

        $news_category = array();
        if (array_key_exists('news_category', $_POST)) {
            foreach ($_POST['news_category'] as $val) {
                $news_category[] = ($val == 'personal') ? null : intval($val);
            }
        }

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

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

        $schedule = post_param_date('schedule');
        if ((addon_installed('calendar')) && (has_privilege(get_member(), 'scheduled_publication_times')) && (!is_null($schedule)) && ($schedule > time())) {
            $validated = 0;
        } else {
            $schedule = null;
        }

        if (!is_null($main_news_category)) {
            $owner = $GLOBALS['SITE_DB']->query_select_value('news_categories', 'nc_owner', array('id' => intval($main_news_category)));
            if ((!is_null($owner)) && ($owner != get_member())) {
                check_privilege('can_submit_to_others_categories', array('news', $main_news_category));
            }
        }

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

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

        $id = add_news($title, $news, $author, $validated, $allow_rating, $allow_comments, $allow_trackbacks, $notes, $news_article, $main_news_category, $news_category, $metadata['add_time'], $metadata['submitter'], $metadata['views'], null, null, $image, '', '', $regions);

        require_code('feedback2');
        send_trackbacks(post_param_string('send_trackbacks', ''), $title, $news);

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

        $main_news_category = $GLOBALS['SITE_DB']->query_select_value('news', 'news_category', array('id' => $id));
        $this->donext_type = $main_news_category;

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

        if (($validated == 1) || (!addon_installed('unvalidated'))) {
            $is_blog = !is_null($GLOBALS['SITE_DB']->query_select_value('news_categories', 'nc_owner', array('id' => $main_news_category)));

            if (has_actual_page_access(get_modal_user(), 'news')) { // NB: no category permission check, as syndication choice was explicit, and news categorisation is a bit more complex
                $privacy_ok = true;
                if (addon_installed('content_privacy')) {
                    require_code('content_privacy');
                    $privacy_ok = has_privacy_access('news', strval($id), $GLOBALS['FORUM_DRIVER']->get_guest_id());
                }
                if ($privacy_ok) {
                    require_code('activities');
                    syndicate_described_activity($is_blog ? 'news:ACTIVITY_ADD_NEWS_BLOG' : 'news:ACTIVITY_ADD_NEWS', $title, '', '', '_SEARCH:news:view:' . strval($id), '', '', 'news', 1, null, true);
                }
            }
        }

        if (!is_null($schedule)) {
            require_code('calendar');
            $schedule_code = ':$GLOBALS[\'SITE_DB\']->query_update(\'news\',array(\'date_and_time\'=>$GLOBALS[\'_EVENT_TIMESTAMP\'],\'validated\'=>1),array(\'id\'=>' . strval($id) . '),\'\',1); require_code(\'sitemap_xml\'); notify_sitemap_node_edit(\'_SEARCH:news:view:' . strval($id) . '\'); decache(\'main_news\'); decache(\'main_image_fader_news\'); decache(\'side_news\'); decache(\'side_news_archive\'); decache(\'bottom_news\'); decache(\'side_news_categories\'); erase_static_cache();';
            $start_year = intval(date('Y', $schedule));
            $start_month = intval(date('m', $schedule));
            $start_day = intval(date('d', $schedule));
            $start_hour = intval(date('H', $schedule));
            $start_minute = intval(date('i', $schedule));
            require_code('calendar2');
            $event_id = add_calendar_event(db_get_first_id(), '', null, 0, do_lang('PUBLISH_NEWS', $title), $schedule_code, 3, $start_year, $start_month, $start_day, 'day_of_month', $start_hour, $start_minute);
            regenerate_event_reminder_jobs($event_id, true);
        }

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

        return strval($id);
    }

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

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

        $news_article = post_param_string('post', STRING_MAGIC_NULL);
        if (post_param_string('main_news_category') != 'personal') {
            $main_news_category = post_param_integer('main_news_category', fractional_edit() ? INTEGER_MAGIC_NULL : false);
        } else {
            warn_exit(do_lang_tempcode('INTERNAL_ERROR'));
        }

        $news_category = mixed();
        $news_category = array();
        if (array_key_exists('news_category', $_POST)) {
            foreach ($_POST['news_category'] as $val) {
                $news_category[] = intval($val);
            }
        }
        if (fractional_edit()) {
            $news_category = null;
        }

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

        $this->donext_type = $main_news_category;

        if (!fractional_edit()) {
            require_code('themes2');
            $image = resize_rep_image(post_param_image('image', 'uploads/repimages', 'newscats', false));
        } else {
            $image = STRING_MAGIC_NULL;
        }

        $owner = $GLOBALS['SITE_DB']->query_select_value_if_there('news_categories', 'nc_owner', array('id' => $main_news_category)); // if_there in case somehow category setting corrupted
        if ((!is_null($owner)) && ($owner != get_member())) {
            check_privilege('can_submit_to_others_categories', array('news', $main_news_category));
        }

        $schedule = post_param_date('schedule');

        if ((addon_installed('calendar')) && (!fractional_edit()) && (has_privilege(get_member(), 'scheduled_publication_times'))) {
            require_code('calendar2');
            $schedule_code = ':$GLOBALS[\'SITE_DB\']->query_update(\'news\',array(\'date_and_time\'=>$GLOBALS[\'_EVENT_TIMESTAMP\'],\'validated\'=>1),array(\'id\'=>' . strval($id) . '),\'\',1); require_code(\'sitemap_xml\'); notify_sitemap_node_edit(\'_SEARCH:news:view:' . strval($id) . '\'); decache(\'main_news\'); decache(\'main_image_fader_news\'); decache(\'side_news\'); decache(\'side_news_archive\'); decache(\'bottom_news\'); decache(\'side_news_categories\'); erase_static_cache();';
            $past_event = $GLOBALS['SITE_DB']->query_select_value_if_there('calendar_events', 'id', array($GLOBALS['SITE_DB']->translate_field_ref('e_content') => $schedule_code));
            require_code('calendar');
            if (!is_null($past_event)) {
                delete_calendar_event($past_event);
            }

            if ((!is_null($schedule)) && ($schedule > time())) {
                $validated = 0;

                $start_year = intval(date('Y', $schedule));
                $start_month = intval(date('m', $schedule));
                $start_day = intval(date('d', $schedule));
                $start_hour = intval(date('H', $schedule));
                $start_minute = intval(date('i', $schedule));
                $event_id = add_calendar_event(db_get_first_id(), 'none', null, 0, do_lang('PUBLISH_NEWS', post_param_string('title')), $schedule_code, 3, $start_year, $start_month, $start_day, 'day_of_month', $start_hour, $start_minute);
                regenerate_event_reminder_jobs($event_id, true);
            }
        }

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

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

        if (($validated == 1) && ($GLOBALS['SITE_DB']->query_select_value('news', 'validated', array('id' => intval($id))) == 0)) { // Just became validated, syndicate as just added
            $is_blog = !is_null($GLOBALS['SITE_DB']->query_select_value('news_categories', 'nc_owner', array('id' => $main_news_category)));

            $submitter = $GLOBALS['SITE_DB']->query_select_value('news', 'submitter', array('id' => $id));
            $activity_title = ($is_blog ? 'news:ACTIVITY_ADD_NEWS_BLOG' : 'news:ACTIVITY_ADD_NEWS');
            $activity_title_validate = ($is_blog ? 'news:ACTIVITY_VALIDATE_NEWS_BLOG' : 'news:ACTIVITY_VALIDATE_NEWS');

            if (has_actual_page_access(get_modal_user(), 'news')) {
                $privacy_ok = true;
                if (addon_installed('content_privacy')) {
                    require_code('content_privacy');
                    $privacy_ok = has_privacy_access('news', strval($id), $GLOBALS['FORUM_DRIVER']->get_guest_id());
                }
                if ($privacy_ok) {
                    require_code('activities');
                    syndicate_described_activity(($submitter != get_member()) ? $activity_title_validate : $activity_title, $title, '', '', '_SEARCH:news:view:' . strval($id), '', '', 'news', 1, null/*$submitter*/, true);
                }
            }
        }

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

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

        edit_news($id, $title, post_param_string('news', STRING_MAGIC_NULL), post_param_string('author', STRING_MAGIC_NULL), $validated, $allow_rating, $allow_comments, $allow_trackbacks, $notes, $news_article, $main_news_category, $news_category, post_param_string('meta_keywords', STRING_MAGIC_NULL), post_param_string('meta_description', STRING_MAGIC_NULL), $image, $metadata['add_time'], $metadata['edit_time'], $metadata['views'], $metadata['submitter'], $regions, true);

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

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

        delete_news($id);

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

    /**
     * The do-next manager for after download content management (events 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, is_null($id) ? null : intval($id), $this->donext_type);
    }

    /**
     * The UI to import news
     *
     * @return Tempcode The UI
     */
    public function import_news()
    {
        check_privilege('mass_import');

        $lang = post_param_string('lang', user_lang());

        $post_url = build_url(array('page' => '_SELF', 'type' => '_import_news', 'old_type' => get_param_string('type', '')), '_SELF');
        $submit_name = do_lang_tempcode('IMPORT_NEWS');

        require_code('form_templates');

        require_code('news2');
        $fields = import_rss_fields(false);

        $hidden = new Tempcode();
        $hidden->attach(form_input_hidden('lang', $lang));
        handle_max_file_size($hidden);

        return do_template('FORM_SCREEN', array('_GUID' => '4ac8c667fa38c1e6338eedcb138e7fd4', 'TITLE' => $this->title, 'TEXT' => do_lang_tempcode('IMPORT_NEWS_TEXT'), 'HIDDEN' => $hidden, 'FIELDS' => $fields, 'SUBMIT_ICON' => 'menu___generic_admin__import', 'SUBMIT_NAME' => $submit_name, 'URL' => $post_url));
    }

    /**
     * The actualiser to import news
     *
     * @return Tempcode The UI
     */
    public function _import_news()
    {
        check_privilege('mass_import');

        $is_validated = post_param_integer('auto_validate', 0);
        if (!addon_installed('unvalidated')) {
            $is_validated = 1;
        }
        $download_images = post_param_integer('download_images', 0);
        if (!has_privilege(get_member(), 'draw_to_server')) {
            $download_images = 0;
        }
        $to_own_account = post_param_integer('add_to_own', 0);
        if (!$GLOBALS['FORUM_DRIVER']->is_super_admin(get_member())) {
            $to_own_account = 1;
        }
        $import_blog_comments = post_param_integer('import_blog_comments', 0);
        $import_to_blog = post_param_integer('import_to_blog', 0);

        require_code('uploads');
        if (((is_plupload(true)) && (array_key_exists('file_anytype', $_FILES))) || ((array_key_exists('file_anytype', $_FILES)) && (is_uploaded_file($_FILES['file_anytype']['tmp_name'])))) {
            $rss_feed = $_FILES['file_anytype']['tmp_name'];
            $is_filesystem_path = true;
        } else {
            $rss_feed = post_param_string('rss_feed_url', null);
            $is_filesystem_path = false;
        }
        if (is_null($rss_feed)) {
            warn_exit(do_lang_tempcode('IMPROPERLY_FILLED_IN'));
        }

        require_code('rss');
        $rss = new CMS_RSS($rss_feed, $is_filesystem_path);

        // Cleanup
        if ($is_filesystem_path) { // Means it is a temp file
            @unlink($rss_feed);
        }

        require_code('tasks');
        $ret = call_user_func_array__long_task(do_lang('IMPORT_NEWS'), $this->title, 'import_rss', array($is_validated, $download_images, $to_own_account, $import_blog_comments, $import_to_blog, $rss));

        return $ret;
    }
}

/**
 * Module page class.
 */
class Module_cms_news_cat extends Standard_crud_module
{
    public $lang_type = 'NEWS_CATEGORY';
    public $select_name = 'TITLE';
    public $permissions_require = 'cat_high';
    public $permission_module = 'news';
    public $menu_label = 'NEWS';
    public $table = 'news_categories';
    public $orderer = 'nc_title';
    public $title_is_multi_lang = true;
    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', 'nc_title ASC', true);
        list($sortable, $sort_order) = array(substr($current_ordering, 0, strrpos($current_ordering, ' ')), substr($current_ordering, strrpos($current_ordering, ' ') + 1));
        $sortables = array(
            'nc_title' => do_lang_tempcode('TITLE'),
        );
        if (db_has_subqueries($GLOBALS['SITE_DB']->connection_read)) {
            $sortables['((SELECT COUNT(*) FROM ' . get_table_prefix() . 'news WHERE news_category=r.id) + (SELECT COUNT(*) FROM ' . get_table_prefix() . 'news_category_entries WHERE news_entry_category=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('TITLE'),
            do_lang_tempcode('IMAGE'),
            do_lang_tempcode('COUNT_TOTAL'),
            do_lang_tempcode('ACTIONS'),
        ), $sortables, 'sort', $sortable . ' ' . $sort_order);

        $fields = new Tempcode();

        require_code('form_templates');
        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 = $GLOBALS['SITE_DB']->query_select_value('news', 'COUNT(*)', array('news_category' => $row['id']));
            $total += $GLOBALS['SITE_DB']->query_select_value('news_category_entries', 'COUNT(*)', array('news_entry_category' => $row['id']));

            $fields->attach(results_entry(array(
                protect_from_escaping(hyperlink(build_url(array('page' => 'news', 'type' => 'browse', 'select' => $row['id']), get_module_zone('news')), get_translated_text($row['nc_title']), false, true)),
                protect_from_escaping(($row['nc_img'] == '') ? '' : ('<img width="15" alt="' . do_lang('IMAGE') . '" src="' . escape_html(get_news_category_image_url($row['nc_img'])) . '" />')), // XHTMLXHTML
                integer_format($total),
                protect_from_escaping(hyperlink($edit_link, do_lang_tempcode('EDIT'), false, true, do_lang('EDIT') . ' #' . strval($row['id'])))
            ), 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()
    {
        return create_selection_list_news_categories(null, false, false, true);
    }

    /**
     * Get Tempcode for a news category adding/editing form.
     *
     * @param  ?AUTO_LINK $id The news category ID (null: new)
     * @param  SHORT_TEXT $title The title of the news category
     * @param  SHORT_TEXT $img The news category image
     * @param  LONG_TEXT $notes Notes relating to the news category
     * @param  ?MEMBER $owner The owner of the news category (null: public)
     * @param  ?AUTO_LINK $category_id The ID of this news category (null: we haven't added it yet)
     * @return array A pair: The input fields, Hidden fields
     */
    public function get_form_fields($id = null, $title = '', $img = '', $notes = '', $owner = null, $category_id = null)
    {
        $fields = new Tempcode();
        $hidden = new Tempcode();

        require_code('form_templates');
        $fields->attach(form_input_line_comcode(do_lang_tempcode('TITLE'), do_lang_tempcode('DESCRIPTION_TITLE'), 'title', $title, true));

        $fields->attach(form_input_upload_multi_source(do_lang_tempcode('IMAGE'), '', $hidden, 'image', 'newscats', false, $img));

        if (get_option('enable_staff_notes') == '1') {
            $fields->attach(do_template('FORM_SCREEN_FIELD_SPACER', array('_GUID' => 'b88f1b286b05e18991ad51538812f7b2', 'SECTION_HIDDEN' => $notes == '', 'TITLE' => do_lang_tempcode('ADVANCED'))));
            $fields->attach(form_input_text(do_lang_tempcode('NOTES'), do_lang_tempcode('DESCRIPTION_NOTES'), 'notes', $notes, false));
        }

        $fields->attach(metadata_get_fields('news_category', is_null($id) ? null : strval($id)), true);

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

        $fields->attach($this->get_permission_fields(is_null($category_id) ? '' : strval($category_id), null, ($title == '')));

        return array($fields, $hidden);
    }

    /**
     * Standard crud_module edit form filler.
     *
     * @param  ID_TEXT $_id The entry being edited
     * @return array A pair: The input fields, Hidden fields
     */
    public function fill_in_edit_form($_id)
    {
        $id = intval($_id);

        $rows = $GLOBALS['SITE_DB']->query_select('news_categories', array('*'), array('id' => $id), '', 1);
        if (!array_key_exists(0, $rows)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'news_category'));
        }
        $myrow = $rows[0];

        return $this->get_form_fields($id, get_translated_text($myrow['nc_title']), $myrow['nc_img'], $myrow['notes'], $myrow['nc_owner'], $myrow['id']);
    }

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

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

        $notes = post_param_string('notes', '');

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

        $id = add_news_category($title, $img, $notes);

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

        $this->set_permissions(strval($id));

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

        return strval($id);
    }

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

        if (!fractional_edit()) {
            require_code('themes2');
            $img = resize_rep_image(post_param_image('image', 'uploads/repimages', 'newscats', false, true));
        } else {
            $img = STRING_MAGIC_NULL;
        }

        $notes = post_param_string('notes', STRING_MAGIC_NULL);

        $metadata = actual_metadata_get_fields('news_category', $id);

        if (is_null($metadata['submitter'])) { // We need to interpret this - if we didn't have specification permission, we need to copy through existing setting, as a null would imply a de-set
            $metadata['submitter'] = $GLOBALS['SITE_DB']->query_select_value_if_there('news_categories', 'nc_owner', array('id' => intval($id)));
        }

        edit_news_category(intval($id), $title, $img, $notes, $metadata['submitter']);

        $this->set_permissions(intval($id));

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

    /**
     * Standard crud_module submitter getter.
     *
     * @param  ID_TEXT $id The entry for which the submitter is sought
     * @return ?MEMBER The submitter (null: none)
     */
    public function get_submitter($id)
    {
        return $GLOBALS['SITE_DB']->query_select_value_if_there('news_categories', 'nc_owner', array('id' => intval($id)));
    }

    /**
     * Standard crud_module delete actualiser.
     *
     * @param  ID_TEXT $id The entry being deleted
     */
    public function delete_actualisation($id)
    {
        delete_news_category(intval($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, is_null($id) ? null : intval($id));
    }

    /**
     * The do-next manager for after news 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  ?AUTO_LINK $cat The category ID we were working in (null: deleted)
     * @return Tempcode The UI
     */
    public function _do_next_manager($title, $description, $id = null, $cat = null)
    {
        require_code('templates_donext');

        if ((is_null($id)) && (is_null($cat))) {
            return do_next_manager($title, $description,
                null,
                null,
                /* TYPED-ORDERED LIST OF 'LINKS'    */
                array('_SELF', array('type' => 'add'), '_SELF'), // Add one
                null, // Edit this
                has_privilege(get_member(), 'edit_own_highrange_content', 'cms_news') ? array('_SELF', array('type' => 'edit'), '_SELF') : null, // Edit one
                null, // View this
                array('news', array('type' => 'browse'), get_module_zone('news')), // View archive
                null, // Add to category
                has_privilege(get_member(), 'submit_cat_highrange_content', 'cms_news') ? array('_SELF', array('type' => 'add_category'), '_SELF') : null, // Add one category
                has_privilege(get_member(), 'edit_own_cat_highrange_content', 'cms_news') ? array('_SELF', array('type' => 'edit_category'), '_SELF') : null, // Edit one category
                null, // Edit this category
                null, // View this category
                null,
                null,
                null,
                null,
                null,
                null,
                null,
                'news',
                'news_category'
            );
        }

        return do_next_manager($title, $description,
            null,
            null,
            /* TYPED-ORDERED LIST OF 'LINKS'  */
            array('_SELF', array('type' => 'add', 'cat' => $cat), '_SELF'), // Add one
            (is_null($id) || (!has_privilege(get_member(), 'edit_own_highrange_content', 'cms_news', array('news', $cat)))) ? null : array('_SELF', array('type' => '_edit', 'id' => $id), '_SELF'), // Edit this
            has_privilege(get_member(), 'edit_own_highrange_content', 'cms_news') ? array('_SELF', array('type' => 'edit'), '_SELF') : null, // Edit one
            is_null($id) ? null : array('news', array('type' => 'view', 'id' => $id), get_module_zone('news')), // View this
            array('news', array('type' => 'browse'), get_module_zone('news')), // View archive
            null, // Add to category
            has_privilege(get_member(), 'submit_cat_highrange_content', 'cms_news') ? array('_SELF', array('type' => 'add_category'), '_SELF') : null, // Add one category
            has_privilege(get_member(), 'edit_own_cat_highrange_content', 'cms_news') ? array('_SELF', array('type' => 'edit_category'), '_SELF') : null, // Edit one category
            is_null($cat) ? null : (has_privilege(get_member(), 'edit_own_cat_highrange_content', 'cms_news') ? array('_SELF', array('type' => '_edit_category', 'id' => $cat), '_SELF') : null), // Edit this category
            null, // View this category
            null,
            null,
            null,
            null,
            null,
            null,
            null,
            'news',
            'news_category'
        );
    }
}
