<?php
/**
 * SeekQuarry/Yioop --
 * Open Source Pure PHP Search Engine, Crawler, and Indexer
 *
 * Copyright (C) 2009 - 2026  Chris Pollett chris@pollett.org
 *
 * LICENSE:
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * END LICENSE
 *
 * @author Chris Pollett chris@pollett.org
 * @license https://www.gnu.org/licenses/ GPL3
 * @link https://www.seekquarry.com/
 * @copyright 2009 - 2026
 * @filesource
 */
namespace seekquarry\yioop\controllers\components;

use seekquarry\yioop\configs as C;
use seekquarry\yioop\library as L;
use seekquarry\yioop\library\mail as ML;

/**
 * Translate the supplied arguments into the current locale.
 *
 * This function is a convenience copy of the same function
 * @see seekquarry\yioop\library\tl() to this subnamespace
 *
 * @param string string_identifier  identifier to be translated
 * @param mixed additional_args  used for interpolation in translated string
 * @return string  translated string
 */
function tl()
{
    return call_user_func_array(C\NS_LIB . "tl", func_get_args());
}
/**
 * shorthand for echo
 *
 * @param string $text string to send to the current output
 */
function e($text)
{
    echo $text;
}
/**
 * Base component class for all components on
 * the SeekQuarry site. A component consists of a collection of
 * activities and their auxiliary methods that can be used by a controller
 *
 * @author Chris Pollett
 */
class Component
{
    /**
     * Reference to the controller this component lives on
     *
     * @var object
     */
    public $parent = null;

    /**
     * Sets up this component by storing in its parent field a reference to
     *  controller this component lives on
     *
     * @param object $parent_controller reference to the controller this
     *      component lives on
     */
    public function __construct($parent_controller)
    {
        $this->parent = $parent_controller;
    }
    /**
     * Called to include the Javascript Wiki Editor (wiki.js) on a page
     * and to send any localizations needed from PHP to Javascript-land
     * It is used by both Crawl and SocialComponent
     *
     * @param array &$data an asscoiative array of data to be used by the
     *     view and layout that the wiki editor will be drawn on
     *     This method tacks on to INCLUDE_SCRIPTS to make the layout load
     *     wiki.js.
     * @param $id if "" then all textareas on page will get editor buttons,
     *     if -1 then sets up translations, but does not add any button,
     *     otherwise, add buttons to textarea $id will. (Can call this method
     *     multiple times, if want more than one but not all)
     */
    public function initializeWikiEditor(&$data, $id = "")
    {
        if (!isset($data["WIKI_INITIALIZED"]) || !$data["WIKI_INITIALIZED"]) {
            $data["INCLUDE_SCRIPTS"] ??= [];
            $data["INCLUDE_SCRIPTS"][] = "editor";
            $data["WIKI_INITIALIZED"] = true;
            //set up an array of translation for javascript-land
            $data['SCRIPT'] ??= "";
            $data['SCRIPT'] .= "\ntl = {".
                'wiki_js_small :"'. tl('wiki_js_small') .'",' .
                'wiki_js_medium :"'. tl('wiki_js_medium').'",'.
                'wiki_js_large :"'. tl('wiki_js_large').'",'.
                'wiki_js_search_size :"'. tl('wiki_js_search_size').'",'.
                'wiki_js_prompt_heading :"'. tl('wiki_js_prompt_heading').'",'.
                'wiki_js_example :"'. tl('wiki_js_example').'",'.
                'wiki_js_table_title :"'. tl('wiki_js_table_title').'",'.
                'wiki_js_submit :"'. tl('wiki_js_submit').'",'.
                'wiki_js_cancel :"'. tl('wiki_js_cancel').'",'.
                'wiki_js_bold :"'. tl('wiki_js_bold') . '",' .
                'wiki_js_italic :"'. tl('wiki_js_italic').'",'.
                'wiki_js_underline :"'. tl('wiki_js_underline').'",'.
                'wiki_js_strike :"'. tl('wiki_js_strike').'",'.
                'wiki_js_hr :"'. tl('wiki_js_hr').'",'.
                'wiki_js_heading :"'. tl('wiki_js_heading').'",'.
                'wiki_js_heading1 :"'. tl('wiki_js_heading1').'",'.
                'wiki_js_heading2 :"'. tl('wiki_js_heading2').'",'.
                'wiki_js_heading3 :"'. tl('wiki_js_heading3').'",'.
                'wiki_js_heading4 :"'. tl('wiki_js_heading4').'",'.
                'wiki_js_bullets :"'. tl('wiki_js_bullet').'",'.
                'wiki_js_bullet :"'. tl('wiki_js_bullet').'",'.
                'wiki_js_numbers :"'. tl('wiki_js_enum') .'",'.
                'wiki_js_enum :"'. tl('wiki_js_enum') .'",'.
                'wiki_js_nowiki :"'. tl('wiki_js_nowiki') .'",'.
                'wiki_js_add_search :"'.tl('wiki_js_add_search') .'",'.
                'wiki_js_more_syntax :"'.tl('wiki_js_more_syntax') .'",'.
                'wiki_js_syntax_textfield :"' .
                    tl('wiki_js_syntax_textfield') . '",'.
                'wiki_js_syntax_required_textfield :"' .
                    tl('wiki_js_syntax_required_textfield') . '",'.
                'wiki_js_syntax_textarea :"' .
                    tl('wiki_js_syntax_textarea') . '",'.
                'wiki_js_syntax_required_textarea :"' .
                    tl('wiki_js_syntax_required_textarea') . '",'.
                'wiki_js_syntax_checkbox :"' .
                    tl('wiki_js_syntax_checkbox') . '",'.
                'wiki_js_syntax_left_checkbox :"' .
                    tl('wiki_js_syntax_left_checkbox') . '",'.
                'wiki_js_syntax_radio :"'.tl('wiki_js_syntax_radio') .'",'.
                'wiki_js_syntax_left_radio :"' .
                    tl('wiki_js_syntax_left_radio') . '",'.
                'wiki_js_syntax_dropdown :"' .
                    tl('wiki_js_syntax_dropdown') . '",'.
                'wiki_js_syntax_submit :"'.tl('wiki_js_syntax_submit') .'",'.
                'wiki_js_syntax_keyword_captcha :"' .
                    tl('wiki_js_syntax_keyword_captcha') . '",'.
                'wiki_js_syntax_require_signin :"' .
                    tl('wiki_js_syntax_require_signin') . '",'.
                'wiki_js_syntax_username :"' .
                    tl('wiki_js_syntax_username') . '",'.
                'wiki_js_syntax_date :"'.tl('wiki_js_syntax_date') .'",'.
                'wiki_js_syntax_timestamp :"' .
                    tl('wiki_js_syntax_timestamp') . '",'.
                'wiki_js_syntax_presubmit :"' .
                    tl('wiki_js_syntax_presubmit') . '",'.
                'wiki_js_syntax_same_unit :"' .
                    tl('wiki_js_syntax_same_unit') . '",'.
                'wiki_js_syntax_category :"' .
                    tl('wiki_js_syntax_category') . '",'.
                'wiki_js_syntax_category_list :"' .
                    tl('wiki_js_syntax_category_list') . '",'.
                'wiki_js_syntax_class :"'.tl('wiki_js_syntax_class') .'",'.
                'wiki_js_syntax_no_contents :"' .
                    tl('wiki_js_syntax_no_contents') . '",'.
                'wiki_js_syntax_preformatted :"' .
                    tl('wiki_js_syntax_preformatted') . '",'.
                'wiki_js_syntax_math :"'.tl('wiki_js_syntax_math') .'",'.
                'wiki_js_syntax_indent :"'.tl('wiki_js_syntax_indent') .'",'.
                'wiki_js_lead_story :"' .
                    tl('wiki_js_lead_story') . '",'.
                'wiki_js_category_list :"' .
                    tl('wiki_js_category_list') . '",'.
                'wiki_element_advanced_settings :"' .
                    tl('wiki_element_advanced_settings') . '",'.
                'wiki_element_basic_settings :"' .
                    tl('wiki_element_basic_settings') . '",'.
                'wiki_js_syntax_recent_places :"' .
                    tl('wiki_js_syntax_recent_places') . '",'.
                'wiki_js_syntax_resource :"' .
                    tl('wiki_js_syntax_resource') . '",'.
                'wiki_js_syntax_resource_link :"' .
                    tl('wiki_js_syntax_resource_link') . '",'.
                'wiki_js_syntax_resource_thumb :"' .
                    tl('wiki_js_syntax_resource_thumb') . '",'.
                'wiki_js_syntax_resource_qr :"' .
                    tl('wiki_js_syntax_resource_qr') . '",'.
                'wiki_js_syntax_resource_bargraph :"' .
                    tl('wiki_js_syntax_resource_bargraph') . '",'.
                'wiki_js_syntax_resource_data :"' .
                    tl('wiki_js_syntax_resource_data') . '",'.
                'wiki_js_syntax_text_field_placeholder :"' .
                    tl('wiki_js_syntax_text_field_placeholder') . '",'.
                'wiki_js_syntax_text_area_placeholder :"' .
                    tl('wiki_js_syntax_text_area_placeholder') . '",'.
                'wiki_js_syntax_option :"' .
                    tl('wiki_js_syntax_option') . '",'.
                'wiki_js_syntax_required_dropdown :"' .
                    tl('wiki_js_syntax_required_dropdown') . '",'.
                'wiki_js_syntax_after_submit :"' .
                    tl('wiki_js_syntax_after_submit') . '",'.
                'wiki_js_syntax_checkbox_short :"' .
                    tl('wiki_js_syntax_checkbox_short') . '",'.
                'wiki_js_syntax_required_checkbox_short :"' .
                    tl('wiki_js_syntax_required_checkbox_short') . '",'.
                'wiki_js_syntax_abbreviation :"' .
                    tl('wiki_js_syntax_abbreviation') . '",'.
                'wiki_js_syntax_abbreviation_meaning :"' .
                    tl('wiki_js_syntax_abbreviation_meaning') . '",'.
                'wiki_js_syntax_alias :"' .
                    tl('wiki_js_syntax_alias') . '",'.
                'wiki_js_syntax_alias_meaning :"' .
                    tl('wiki_js_syntax_alias_meaning') . '",'.
                'wiki_js_syntax_block :"' .
                    tl('wiki_js_syntax_block') . '",'.
                'wiki_js_syntax_inline_block :"' .
                    tl('wiki_js_syntax_inline_block') . '",'.
                'wiki_js_syntax_data_block :"' .
                    tl('wiki_js_syntax_data_block') . '",'.
                'wiki_js_syntax_toggle :"' .
                    tl('wiki_js_syntax_toggle') . '",'.
                'wiki_js_syntax_session_set :"' .
                    tl('wiki_js_syntax_session_set') . '",'.
                'wiki_js_syntax_session_check :"' .
                    tl('wiki_js_syntax_session_check') . '",'.
                'wiki_js_syntax_request_check :"' .
                    tl('wiki_js_syntax_request_check') . '",'.
                'wiki_js_syntax_secret_ballot :"' .
                    tl('wiki_js_syntax_secret_ballot') . '",'.
                'wiki_js_syntax_one_row_per_user :"' .
                    tl('wiki_js_syntax_one_row_per_user') . '",'.
                'wiki_js_syntax_one_row_per_hash :"' .
                    tl('wiki_js_syntax_one_row_per_hash') . '",'.
                'wiki_js_syntax_one_row_shared :"' .
                    tl('wiki_js_syntax_one_row_shared') . '",'.
                'wiki_js_autosave :"'.tl('wiki_js_autosave') .'",'.
                'wiki_js_autosave_reason :"'.
                    tl('wiki_js_autosave_reason') .'",'.
                'wiki_js_save_as_name :"'.tl('wiki_js_save_as_name') .'",'.
                'wiki_js_new_image_size :"'.
                    tl('wiki_js_new_image_size') .'",'.
                'wiki_js_save_as_relative :"'.
                    tl('wiki_js_save_as_relative') .'",'.
                'wiki_js_autosave_off :"'.tl('wiki_js_autosave_off') .'",'.
                'wiki_js_saving :"'.tl('wiki_js_saving') .'",'.
                'wiki_js_search_size :"'. tl('wiki_js_search_size') .'",'.
                'wiki_js_add_wiki_table :"'. tl('wiki_js_add_wiki_table').'",'.
                'wiki_js_for_table_cols :"'. tl('wiki_js_for_table_cols').'",'.
                'wiki_js_for_table_rows :"'. tl('wiki_js_for_table_rows').'",'.
                'wiki_js_hyperlink :"'. tl('wiki_js_add_hyperlink').'",'.
                'wiki_js_add_hyperlink :"'. tl('wiki_js_add_hyperlink').'",'.
                'wiki_js_link_text :"'. tl('wiki_js_link_text').'",'.
                'wiki_js_link_url :"'. tl('wiki_js_link_url').'",'.
                'wiki_js_placeholder :"'. tl('wiki_js_placeholder').'",'.
                'wiki_js_centeraligned :"'. tl('wiki_js_centeraligned').'",'.
                'wiki_js_rightaligned :"'. tl('wiki_js_rightaligned').'",'.
                'wiki_js_leftaligned :"'. tl('wiki_js_leftaligned').'",'.
                'wiki_js_definitionlist :"'.
                tl('wiki_js_definitionlist').'",'.
                'wiki_js_definitionlist_item :"'.
                tl('wiki_js_definitionlist_item').'",'.
                'wiki_js_definitionlist_definition :"'.
                tl('wiki_js_definitionlist_definition').'",'.
                'wiki_js_slide :"'.
                tl('wiki_js_slide').'",'.
                'wiki_js_slide_sample_title :"'.
                    tl('wiki_js_slide_sample_title').'",'.
                'wiki_js_slide_sample_bullet :"'.
                    tl('wiki_js_slide_sample_bullet').'",'.
                'wiki_js_resource_description : "'.
                tl('wiki_js_resource_description').'", '.
                'basic_js_no_transcript :"'.
                tl('basic_js_no_transcript').'",'.
                'basic_js_show_transcript :"'.
                tl('basic_js_show_transcript').'",'.
                'wiki_element_list_icon :"'. tl('wiki_element_list_icon').'",'.
                'wiki_element_grid_icon :"'. tl('wiki_element_grid_icon').'",'.
                'wiki_js_summarizing :"'. tl('wiki_js_summarizing').'",'.
                'wiki_js_summary_unavailable :"'.
                tl('wiki_js_summary_unavailable').'",'.
                'wiki_js_render_engine:"'.
                    ($data['RENDER_ENGINE'] ?? 'mediawiki') .'",'.
                '};';
        }
        if ($id != -1) {
            if ($id == "") {
                $data['SCRIPT'] .= "editorizeAll();\n";
                /* A file shown as a grid has no writing area for the
                   editor to find, so its keeping is set up separately. */
                $data['SCRIPT'] .= "initSpreadsheetKeeping();\n";
            } else {
                $data['SCRIPT'] .= "editorize('$id');\n";
            }
        }
    }
    /**
     * Used to compute statistics for badges related for the manage crawls,
     * mix crawls, and manage machine buttons shown typically to admin accounts
     *
     * @param int $user_id of user - used to determin the mix crawl list
     * @param array &$data associative array of data to send to the view.
     *  This method adds three new field NUM_MIXES, CRAWL_MANAGER,
     *  "NUM_MACHINES, CRAWLS_RUNNING, and NUM_CLOSED_CRAWLS
     */
    public function initCrawlBadges($user_id, &$data)
    {
        $parent = $this->parent;
        $crawl_model = $parent->model("crawl");
        $data['NUM_MIXES'] = count($crawl_model->getMixList($user_id));
        $data["CRAWL_MANAGER"] = true;
        $machine_urls = $parent->model("machine")->getQueueServerUrls();
        $data["NUM_MACHINES"] = count($machine_urls);
        list($stalled, $status, $recent_crawls) =
            $crawl_model->combinedCrawlInfo($machine_urls, true);
        $data["CRAWLS_RUNNING"] = 0;
        $data["NUM_CLOSED_CRAWLS"] = count($recent_crawls);
        foreach ($status as $channel => $channel_status) {
            if (!empty($channel_status['CRAWL_TIME'])) {
                $data["CRAWLS_RUNNING"]++;
                $data["NUM_CLOSED_CRAWLS"]--;
            }
        }
    }
    /**
     * Used to compute the impression statistics for badges on the
     * social controls button for $user_id.  These badges
     * display the number of unread messages, the number of unread group post
     * and the number of groups the user belongs to
     * @param int $user_id id of user to compute statistics for
     * @param array &$data associative array of data to send to the view.
     *  This method adds four new fields NUM_GROUPS, UNREAD_POSTS,
     *  UNREAD_MESSAGES, and UNREAD_MAIL
     */
    public function initSocialBadges($user_id, &$data)
    {
        $parent = $this->parent;
        $group_model = $parent->model("group");
        $feed_model = $parent->model("feed");
        $impression_model = $parent->model("impression");
        $data['NUM_GROUPS'] = $group_model->countUserGroups($user_id);
        $timestamp = $impression_model->mostRecentGroupView($user_id);
        $data['UNREAD_POSTS'] = $feed_model->getUserPostCount($user_id,
            $timestamp);
        $personal_group_id = $group_model->getPersonalGroupId($user_id);
        $contact_ids = array_diff($group_model->getGroupUserIds(
            $personal_group_id), [$user_id]);
        $chat_ids = [];
        $unread_count = 0;
        foreach ($contact_ids as $contact_id) {
            $chat_id = $feed_model->getGroupThreadId(
                $personal_group_id, $user_id,
                $feed_model->getMessagesThreadTitle(
                [$user_id, $contact_id]));
            if (empty($chat_id)) {
                continue;
            }
            $chat_stamp = $impression_model->mostRecentThreadView($user_id,
                $chat_id);
            $unread_count += floor($feed_model->getThreadPostCount($chat_id,
                $chat_stamp) / 2);
        }
        $data['UNREAD_MESSAGES'] = $unread_count;
        $data['UNREAD_MAIL'] = ML\MailUnreadProbe::cachedCount(
            $user_id);
    }
}
