/ src / views / helpers / IconlinkHelper.php
<?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\views\helpers;

/**
 * This is a helper class is used to draw
 * icon buttons and links
 *
 * @author Chris Pollett
 */
class IconlinkHelper extends Helper
{
    /**
     * Types of icons supported by this helper
     * @var array
     */
    public $icon_possibilities;
    /**
     * Initials mapping between names of icons and the actual icons
     * to be used as links or buttons
     */
    public function __construct()
    {
         $this->icon_possibilities = [
            "account_home" => [ tl('iconlink_helper_account_home'), '🏠'],
            "account_settings" => [ tl('iconlink_helper_account_settings'),'βš™'],
            "add" => [ tl('iconlink_helper_add'), 'βž•'],
            "ai_summary" => [tl('iconlink_helper_ai_summary'), 'πŸ“'],
            "ask_join_group" => [ tl('iconlink_helper_ask_join_group'), "πŸ™‹"],
            "audio_call" => [ tl('iconlink_helper_audio_call'), 'πŸ“ž'],
            "audio_call_end" => [ tl('iconlink_helper_end_audio_call'), 'πŸ“ž'],
            "back" => [tl('iconlink_helper_back'), '⬅️'],
            "close" => [ tl('iconlink_helper_close'), 'X'],
            "create_group" => [ tl('iconlink_helper_create_group'), 'βž•'],
            "combined_discussions" => [
                tl('iconlink_helper_combined_discussions'), 'πŸ“ƒ'],
            "compose" => [tl('iconlink_helper_compose'),
                '<svg viewBox="0 0 16 16" width="14" ' .
                'height="14" aria-hidden="true">' .
                '<path d="M14,2 L2,7 L7,9 L9,14 Z" ' .
                'fill="none" stroke="currentColor" ' .
                'stroke-width="1.5" ' .
                'stroke-linejoin="round" />' .
                '<path d="M14,2 L7,9" ' .
                'fill="none" stroke="currentColor" ' .
                'stroke-width="1.5" ' .
                'stroke-linejoin="round" />' .
                '</svg>'],
            "clip_copy" => [tl('iconlink_helper_clip_copy'),
                '<svg viewBox="0 0 16 16" width="14" ' .
                'height="14" aria-hidden="true">' .
                '<rect x="5" y="5" width="8" height="9" rx="1" ' .
                'fill="none" stroke="currentColor" ' .
                'stroke-width="1.5" />' .
                '<path d="M3,11 L3,2 L10,2" ' .
                'fill="none" stroke="currentColor" ' .
                'stroke-width="1.5" stroke-linejoin="round" ' .
                'stroke-linecap="round" />' .
                '</svg>'],
            "clip_cut" => [tl('iconlink_helper_clip_cut'),
                '<svg viewBox="0 0 16 16" width="14" ' .
                'height="14" aria-hidden="true">' .
                '<circle cx="4" cy="11" r="2" fill="none" ' .
                'stroke="currentColor" stroke-width="1.5" />' .
                '<circle cx="4" cy="5" r="2" fill="none" ' .
                'stroke="currentColor" stroke-width="1.5" />' .
                '<path d="M5.7,9.7 L14,3 M5.7,6.3 L14,13" ' .
                'fill="none" stroke="currentColor" ' .
                'stroke-width="1.5" stroke-linecap="round" />' .
                '</svg>'],
            "clone_copy" => [tl('iconlink_helper_clone_copy'), 'πŸ“‹'],
            "copy" => [tl('iconlink_helper_copy'),  'πŸ“·'],
            "crawls" => [tl('iconlink_helper_manage_crawls'),  'πŸ•ΈοΈ'],
            "cut" => [tl('iconlink_helper_cut'),  'βœ‚οΈ'],
            "delete" => [tl('iconlink_helper_delete'), 'πŸ—‘οΈ',
                tl('iconlink_helper_delete_confirm')],
            "flag" => [tl('iconlink_helper_flag'), '🏳️',
                tl('iconlink_helper_flag_confirm')],
            "discuss_page" => [
                tl('iconlink_helper_discuss_page'), 'πŸ—£οΈ'],
            "edit" => [ tl('iconlink_helper_edit'),  '✏️'],
            "groups" => [ tl('iconlink_helper_groups'), '🏘️'],
            "group_feed" => [ tl('iconlink_helper_group_discussions'), 'πŸ“ƒ'],
            "filter" => [tl('iconlink_helper_filter'),
                '<svg viewBox="0 0 16 16" width="14" ' .
                'height="14" aria-hidden="true">' .
                '<path d="M2,3 H14 L10,8 V13 L6,13 V8 Z" ' .
                'fill="none" stroke="currentColor" ' .
                'stroke-width="1.5" stroke-linejoin="round" />' .
                '</svg>'],
            "history" => [tl('iconlink_helper_history'),  'πŸ“–'],
            "information" => [tl('iconlink_helper_information'),  'ℹ️'],
            "issue_tracker" => [tl('iconlink_helper_issue_tracker'),
                'πŸ—£οΈ'],
            "new_issue" => [tl('iconlink_helper_new_issue'), 'πŸͺ²'],
            "join_group" => [ tl('iconlink_helper_join_group'), "πŸ›¬"],
            "join_group_fee" => [ tl('iconlink_helper_join_group'), "πŸ›¬",
                 tl('iconlink_helper_fee_warning')],
            "join_invited_group" => [ tl('iconlink_helper_join_invited_group'),
                "🎟️"],
            "join_groups" => [ tl('iconlink_helper_join_groups'), "πŸ›¬"],
            "leave_group" => [ tl('iconlink_helper_leave_group'), "πŸ›«"],
            "localize" => [ tl('iconlink_helper_localize'), '🌐'],
            "machines" => [tl('iconlink_helper_manage_machines'), 'πŸ€–'],
            "mail" => [tl('iconlink_helper_mail'), 'βœ‰οΈ'],
            "media_jobs" => [tl('iconlink_helper_media_jobs'), 'πŸ’Ό'],
            "media_sources" => [tl('iconlink_helper_media_sources'),
                'πŸ“'],
            "mail_filter_unread" => [
                tl('iconlink_helper_mail_filter_unread'),
                '<svg viewBox="0 0 16 16" width="14" ' .
                'height="14" aria-hidden="true">' .
                '<path d="M2,3 H14 L10,8 V13 L6,13 V8 Z" ' .
                'fill="none" stroke="currentColor" ' .
                'stroke-width="1.5" ' .
                'stroke-linejoin="round" />' .
                '</svg>'],
            "mail_filter_all" => [
                tl('iconlink_helper_mail_filter_all'),
                '<svg viewBox="0 0 16 16" width="14" ' .
                'height="14" aria-hidden="true">' .
                '<path d="M2,3 H14 L10,8 V13 L6,13 V8 Z" ' .
                'fill="currentColor" stroke="currentColor" ' .
                'stroke-width="1.5" ' .
                'stroke-linejoin="round" />' .
                '</svg>'],
            "mail_reply" => [tl('iconlink_helper_mail_reply'),
                '<svg viewBox="0 0 16 16" width="14" ' .
                'height="14" aria-hidden="true">' .
                '<path d="M7,4 L3,8 L7,12 M3,8 H10 ' .
                'C12,8 13,9 13,11 V13" ' .
                'fill="none" stroke="currentColor" ' .
                'stroke-width="1.5" ' .
                'stroke-linejoin="round" ' .
                'stroke-linecap="round" />' .
                '</svg>'],
            "mail_reply_all" => [
                tl('iconlink_helper_mail_reply_all'),
                '<svg viewBox="0 0 16 16" width="14" ' .
                'height="14" aria-hidden="true">' .
                '<path d="M4,4 L1,8 L4,12 M8,4 L5,8 L8,12 ' .
                'M5,8 H10 ' .
                'C12,8 13,9 13,11 V13" ' .
                'fill="none" stroke="currentColor" ' .
                'stroke-width="1.5" ' .
                'stroke-linejoin="round" ' .
                'stroke-linecap="round" />' .
                '</svg>'],
            "mail_forward" => [tl('iconlink_helper_mail_forward'),
                '<svg viewBox="0 0 16 16" width="14" ' .
                'height="14" aria-hidden="true">' .
                '<path d="M9,4 L13,8 L9,12 M13,8 H6 ' .
                'C4,8 3,9 3,11 V13" ' .
                'fill="none" stroke="currentColor" ' .
                'stroke-width="1.5" ' .
                'stroke-linejoin="round" ' .
                'stroke-linecap="round" />' .
                '</svg>'],
            "mixes" => [tl('iconlink_helper_manage_mixes'), 'πŸ₯¨'],
            "messages" => [ tl('iconlink_helper_messages'), 'πŸ’¬'],
            "pages" => [tl('iconlink_helper_group_pages'), 'πŸ“‘'],
            "pending" => [ tl('iconlink_helper_request_pending'), "⏳",
                tl('iconlink_helper_cancel_request')],
            "play" => [tl('iconlink_helper_group_play'), '▢️'],
            "query_stats" => [ tl('iconlink_helper_query_stats'), 'πŸ“ˆ'],
            "refresh" => [tl('iconlink_helper_refresh'), 'πŸ”„'],
            "rename" => [tl('iconlink_helper_rename'),
                '<svg viewBox="0 0 16 16" width="14" ' .
                'height="14" aria-hidden="true">' .
                '<rect x="1" y="3.5" width="14" height="9" rx="2.5" ' .
                'fill="none" stroke="currentColor" ' .
                'stroke-width="1.3" />' .
                '<path d="M2.8,10.4 L4.3,5.6 L5.8,10.4 M3.4,9 L5.2,9" ' .
                'fill="none" stroke="currentColor" stroke-width="1" ' .
                'stroke-linejoin="round" stroke-linecap="round" />' .
                '<path d="M7,5.6 L7,10.4 M7,5.6 L8.3,5.6 ' .
                'A0.9,0.9 0 0 1 8.3,7.7 L7,7.7 ' .
                'M7,7.7 L8.6,7.7 A1.35,1.35 0 0 1 8.6,10.4 L7,10.4" ' .
                'fill="none" stroke="currentColor" stroke-width="1" ' .
                'stroke-linejoin="round" stroke-linecap="round" />' .
                '<path d="M12,1.5 L12,14.5 M11,1.5 L13,1.5 ' .
                'M11,14.5 L13,14.5" ' .
                'fill="none" stroke="currentColor" ' .
                'stroke-width="1.1" stroke-linecap="round" />' .
                '</svg>'],
            "reset" => [ tl('iconlink_helper_reset'), 'X'],
            "resources" => [ tl('iconlink_helper_resources'), 'πŸ—„οΈ'],
            "search" => [ tl('iconlink_helper_search'), 'πŸ”'],
            "settings" => [ tl('iconlink_helper_settings'),
                "<b class='black medium-font'>βš™</b>"],
            "statistics" => [ tl('iconlink_helper_statistics'), "πŸ“ˆ"],
            "thread_followed" => [tl('iconlink_helper_unfollow_thread'),
                'πŸ””'],
            "thread_unfollowed" => [tl('iconlink_helper_follow_thread'),
                'πŸ”•'],
            "video_call" => [ tl('iconlink_helper_video_call'), '🎦'],
            "video_call_end" => [ tl('iconlink_helper_end_video_call'), '⏹️'],
            "view_read" => [ tl('iconlink_helper_view_read'), '↩️'],
            "view_source" => [ tl('iconlink_helper_view_source'), 'πŸ‘οΈ'],
            "wiki" => [ tl('iconlink_helper_group_main_page'),
                '🏁'],
            "wiki_syntax" => [ tl('iconlink_helper_wiki_syntax'), 'πŸ“š'],
        ];
    }
    /**
     * Draws a link button suitable to indicate the activity $type
     *
     * @param string $url  url that is sent when one clicks and confirms
     * @param string $type  one of "edit", ...
     * @param string $badge_text if not empty, text to use to draw a badge
     *  on the top right of the icon
     * @param bool $as_string whether to return a string or html
     * @param string $div_classes space separated, additional css classes
     *      to add to outermost div
     * @param string $anchor_classes space separated, additional css classes
     *      to add to anchor tag
     * @param bool $replace_css whether to append css in $div_classes and
     *      $anchor_classes to existing or to replace.
     * @param string $tab_target whether to open link in a new tab with target
     *      $tab_target
     * @return mixed either void, or a string of html if $as_string = true
     */
    public function renderButton($url, $type = "edit", $badge_text = "",
        $as_string = false, $div_classes = "", $anchor_classes = "",
        $replace_css = false, $tab_target = "")
    {
        if (is_array($url)) {
            list($id, $url,) = $url;
            $id_attr = " id='$id' ";
        } else {
            $id_attr = "";
        }
        if ($as_string) {
            ob_start();
        }
        $div_classes = ($replace_css) ? $div_classes : "icon-button-container ".
            $div_classes;
        ?><div <?=$id_attr ?> class='<?= $div_classes ?>'><?=
            $this->renderLink($url, $type, $badge_text, $as_string,
            $anchor_classes, $replace_css, $tab_target); ?></div><?php
        if ($as_string) {
            $button = ob_get_contents();
            ob_end_clean();
            return $button;
        }
    }
    /**
     * Draws a button for a form that uses an icon as its main text
     * @param string $button_type value for type attribute of html button
     * @param string $type of icon one of "edit", ...
     * @param bool $as_string whether to return a string or html
     * @param string $anchor_classes space separated, additional css classes
     *      to add to anchor
     * @param string $additional_attributes a string to enclose with the open
     *      button tag with additional attributes
     * @return mixed either void, or a string of html if $as_string = true
     */
    public function renderFormButton($button_type = "submit" , $type = "edit",
        $as_string = false, $anchor_classes = "",
        $additional_attributes = "")
    {
        $type = (isset($this->icon_possibilities[$type])) ? $type : "submit";
        $icon_info = $this->icon_possibilities[$type];
        list($icon_text, $icon, ) = $icon_info;
        if ($as_string) {
            ob_start();
        }
        ?><button type='<?=$button_type ?>' class='<?=$anchor_classes
            ?>' <?=$additional_attributes ?> ><span role='img'
            class='icon-glyph' aria-label='<?=
        $icon_text ?>' ><?=$icon ?></span></button><?php
        if ($as_string) {
            $button = ob_get_contents();
            ob_end_clean();
            return $button;
        }
    }
    /**
     * Draws a link suitable to indicate the activity $type
     *
     * @param string $url  url that is sent when one clicks and confirms
     * @param string $type  one of "edit", ...
     * @param string $badge_text if not empty, text to use to draw a badge
     *  on the top right of the icon
     * @param bool $as_string whether to return a string or html
     * @param string $anchor_classes space separated, additional css classes
     *      to add to anchor
     * @param bool $replace_css whether to replace default anchor css or
     *      append to it
     * @param string $tab_target whether to open link in a new tab with target
     *      $tab_target
     * @return mixed either void, or a string of html if $as_string = true
     */
    public function renderLink($url, $type = "edit", $badge_text = "",
        $as_string = false, $anchor_classes = "", $replace_css = false,
        $tab_target = "")
    {
        $type = (isset($this->icon_possibilities[$type])) ? $type : "edit";
        $icon_info = $this->icon_possibilities[$type];
        if (empty($tab_target)) {
            $target = (empty($_REQUEST['noscript'])) ? "" :
                " target='_parent' rel='noopener' ";
        } else {
            $target = " target='$tab_target' rel='noopener' ";
        }
        list($icon_text, $icon, ) = $icon_info;
        $confirm = "";
        if (is_array($url) && !empty($url[1]) && !empty($icon_info[2])) {
            list($url, $translate_aux,) = $url;
             $icon_info[2] = tl($icon_info[2], $translate_aux);
             $icon = "<div class='inline align-opposite'>$icon<br>".
                "<span class='tiny-font' style='position:relative; top: -8px;'".
                ">$translate_aux</span></div>";
        }
        if (!empty($icon_info[2])) {
            $confirm = " onclick='javascript:return ".
                "confirm(\"{$icon_info[2]}\")' ";
        }
        if ($as_string) {
            ob_start();
        }
        $anchor_classes = ($replace_css) ? $anchor_classes :
            "icon-anchor-button ". $anchor_classes;
        $badge_attribute = (empty($badge_text)) ? "" :
            " data-badge='$badge_text' ";
        ?><div class='position-context <?=$anchor_classes?>' <?=$badge_attribute
            ?> ><a <?=$confirm ?> class='<?= $anchor_classes
            ?>' <?=$target?> href='<?= $url ?>'><span role='img'
            class='icon-glyph' aria-label='<?=
            $icon_text ?>' ><?=$icon ?></span></a></div><?php
        if ($as_string) {
            $button = ob_get_contents();
            ob_end_clean();
            return $button;
        }
    }
}
X