<?php
/*
 *   $Id$
 *
 *   AbanteCart, Ideal OpenSource Ecommerce Solution
 *   http://www.AbanteCart.com
 *
 *   Copyright © 2011-2025 Belavier Commerce LLC
 *
 *   This source file is subject to Open Software License (OSL 3.0)
 *   License details are bundled with this package in the file LICENSE.txt.
 *   It is also available at this URL:
 *   <http://www.opensource.org/licenses/OSL-3.0>
 *
 *  UPGRADE NOTE:
 *    Do not edit or add to this file if you wish to upgrade AbanteCart to newer
 *    versions in the future. If you wish to customize AbanteCart for your
 *    needs, please refer to http://www.AbanteCart.com for more information.
 */
if (!defined('DIR_CORE') || !IS_ADMIN) {
    header('Location: static_pages/');
}

/**
 * Class ControllerResponsesListingGridExtension
 */
class ControllerResponsesListingGridExtension extends AController
{
    public function main()
    {
        //init controller data
        $this->extensions->hk_InitData($this, __FUNCTION__);

        $this->loadLanguage('extension/extensions');
        /** @var ModelToolMPAPI $mpModel */
        $mpModel = $this->loadModel('tool/mp_api');

        $page = $this->request->post['page']; // get the requested page
        if ((int)$page < 0) {
            $page = 0;
        }
        $limit = $this->request->post['rows']; // get how many rows we want to have into the grid
        $sidx = $this->request->post['sidx']; // get index row - i.e. user click to sort
        $sord = $this->request->post['sord']; // get the direction

        $search_str = '';
        if (isset($this->request->post['_search']) && $this->request->post['_search'] == 'true') {
            $searchData = json_decode(htmlspecialchars_decode($this->request->post['filters']), true);
            $search_str = trim($searchData['rules'][0]['data']);
        }

        $store_id = (int)$this->config->get('current_store_id');
        if ($this->request->get_or_post('store_id')) {
            $store_id = $this->request->get_or_post('store_id');
        }

        //sort
        $allowedSort = array_merge(
            [
                1 => 'key',
                'name',
                'category',
                'date_modified',
                'e.status',
                'store_name',
            ],
            (array)$this->data['allowed_sort']
        );
        if (!in_array($sidx, $allowedSort)) {
            $sidx = 'date_modified';
        }

        $allowedDirection = [SORT_ASC => 'asc', SORT_DESC => 'desc'];
        if (!in_array($sord, $allowedDirection)) {
            $sord = 'asc';
        }

        //extensions that has record in DB but missing files
        $missing_extensions = $this->extensions->getMissingExtensions();

        $data = [
            'store_id'   => $store_id,
            'search'     => $search_str,
            'filter'     => $this->session->data['extension_filter'],
            'sort_order' => [$sidx, $sord],
        ];
        if ($this->config->get('current_store_id')) {
            $data['store_id'] = (int)$this->config->get('current_store_id');
        }
        //extensions list. NOTE: set "force" mode to get data from db
        $extensions = $this->extension_manager->getExtensionsList($data, 'force');

        $total = $extensions->total;
        if ($total > 0) {
            $total_pages = ceil($total / $limit);
        } else {
            $total_pages = 0;
        }

        $response = new stdClass();
        $response->rows = [];
        $response->page = $page;
        $response->total = $total_pages;
        $response->records = $total;
        $response->userdata = new stdClass();

        $i = 0;
        $for_push = $push = [];

        // get extensions for install
        $ready_to_install = $this->session->data['ready_to_install'];
        $to_install = $to_inst_keys = [];

        if (!has_value($this->session->data['extension_filter'])
            || $this->session->data['extension_filter'] == 'extensions'
        ) {
            if ($ready_to_install && is_array($ready_to_install)) {
                foreach ($ready_to_install as $pack) {
                    $to_install[$pack['extension_name']] =
                        [
                            'remote_install'    => true,
                            'key'               => $pack['extension_name'],
                            'name'              => $pack['download_name'],
                            'extension_version' => $pack['extension_version'],
                            'installation_key'  => $pack['installation_key'],
                            'date_modified'     => $pack['date_modified'],
                        ];
                    $to_inst_keys[] = $pack['extension_name'];
                }
            }
        }

        //filter already installed from remote list (ignores new versions too)
        foreach ($extensions->rows as $row) {
            if (in_array($row['key'], $to_inst_keys)) {
                unset($to_install[$row['key']]);
            }
        }

        $rows = array_merge($to_install, $extensions->rows);
        $updates = $this->session->data['extensions_updates'];

        //get popular extensions
        $popular = $mpModel->getPopularity();
        foreach ($rows as $row) {
            $extension = $row['key'];
            if (!$extension) {
                continue;
            }
            if (count(explode('.', $row['version'])) == 2) {
                $row['version'] .= '.0';
            }
            $expired = false;
            $response->rows[$i]['id'] = str_replace(' ', '-', $row['key']) . '_' . (int)$row['store_id'];
            $id = $response->rows[$i]['id'];

            $response->userdata->extension_id[$id] = $extension;
            $response->userdata->store_id[$id] = $row['store_id'];
            $response->userdata->popular[$id] = [
                'status'      => (!empty($popular) && in_array($extension, array_keys($popular))),
                'description' => $popular[$extension]
            ];

            $name = !isset($row['name']) ? trim($this->extensions->getExtensionName($extension)) : $row['name'];

            //for new extensions
            if ($row['remote_install']) {
                $response->userdata->installation_key[$id] = $row['installation_key'];
                $icon = '<img src="' . RDIR_TEMPLATE . 'image/default_extension.png' . '" alt="" border="0" />';
                $category = '';
                $status = $this->language->get('text_ready_to_install');
                $response->userdata->classes[$id] = 'success disable-edit disable-delete '
                    . 'disable-uninstall disable-install';
            } //missing extension (removed manually)
            elseif (in_array($extension, $missing_extensions)) {
                $response->userdata->classes[$id] = 'warning disable-edit disable-install '
                    . 'disable-uninstall disable-remote-install';

                $icon = '<img src="' . RDIR_TEMPLATE . 'image/default_extension.png' . '" alt="' . $extension . '" />';
                $name = sprintf($this->language->get('text_missing_extension'), $extension);
                $category = $status = '';
                // change it for show it in list first by default sorting
                $row['date_modified'] = date('Y-m-d H:i:s', time());
            } //corrupted extension
            elseif (!file_exists(DIR_EXT . $extension . DS . 'main.php') || !file_exists(DIR_EXT . $extension . DS . 'config.xml')) {
                $response->userdata->classes[$id] = 'warning disable-edit disable-install '
                    . 'disable-uninstall disable-remote-install';
                $icon = '<img src="' . RDIR_TEMPLATE . 'image/default_extension.png' . '" alt="' . $extension . '" />';
                $name = sprintf($this->language->get('text_broken_extension'), $extension);
                $category = $status = '';
                // change it for show it in list first by default sorting
                $row['date_modified'] = date('Y-m-d H:i:s', time());
            } else {
                if (!$this->config->has($extension . '_status')) {
                    $response->userdata->classes[$id] = 'disable-edit disable-uninstall disable-remote-install';
                    $status = $this->language->get('text_not_installed');
                } else {
                    $response->userdata->classes[$id] = 'disable-delete disable-install disable-remote-install';
                    $status = $this->html->buildCheckbox(
                        [
                            'name'  => $extension . '[' . $extension . '_status]',
                            'value' => $row['status'],
                            'style' => 'btn_switch',
                        ]
                    );
                }

                $iconFileName = 'icon.png';
                $icon_ext_img_url = HTTPS_EXT . $extension . '/image/';
                $icon_ext_dir = DIR_EXT . $extension . DS . 'image' . DS;
                //if icon.png not found - looking for other "icon" images
                if (!is_file($icon_ext_dir . $iconFileName)) {
                    $files = glob($icon_ext_dir . 'icon.{png,webp,jpg,jpeg}', GLOB_BRACE);
                    if ($files) {
                        $icon = $icon_ext_img_url . pathinfo($files[0], PATHINFO_BASENAME);
                    } else {
                        $icon = RDIR_TEMPLATE . 'image/default_extension.png';
                    }
                } else {
                    $icon = $icon_ext_img_url . $iconFileName;
                }

                if (!$this->config->has($extension . '_status')) {
                    $icon = '<img src="' . $icon . '" alt="' . $extension . '"/>';
                } else {
                    $icon = '<a href="' . $this->html->getSecureURL(
                            'extension/extensions/edit',
                            $this->data['url'] . '&extension=' . $extension
                        )
                        . '">'
                        . '<img src="' . $icon . '" alt="' . $extension . '"/></a>';
                }

                $category = $row['category'];
                // if update available
                if (is_array($updates) && isset($updates[$extension])) {
                    if (version_compare($updates[$extension]['version'], $row['version'], '>')) {
                        if ($updates[$extension]['installation_key']) {
                            $update_now_url = $this->html->getSecureURL(
                                'tool/package_installer',
                                '&extension_key=' . $updates[$extension]['installation_key']
                            );
                        } else {
                            $update_now_url = $updates[$extension]['url'];
                        }
                        $name = '<p class="alert-info">'
                            . $name
                            . '<br>'
                            . sprintf(
                                $this->language->get('text_update_available'),
                                $updates[$extension]['version'],
                                $update_now_url
                            )
                            . '</p>';
                        $push[] = $i;
                    }
                }
                //when support period expired
                if (($updates
                        && isset($updates[$extension]['support_expiration'])
                        && $updates[$extension]['support_expiration']
                        && dateISO2Int($updates[$extension]['support_expiration']) < time())
                    || //if no update info - takes date from extension table
                    ($row['support_expiration'] && dateISO2Int($row['support_expiration']) < time())
                    || //if extension purchased but store disconnected
                    ($row['license_key'] && !$this->config->get('mp_token'))
                ) {
                    $expired = true;
                    $response->userdata->classes[$id] = 'expired ' . $response->userdata->classes[$id];
                }
                //check of minor cart version
                if (!is_null($this->config->get($extension . "_status"))) {
                    $cfg = getExtensionConfigXml($extension);
                    if ($cfg->cartversions->item) {
                        $allSupportedCartVersions = (array)$cfg->cartversions->item;
                        $checks = isExtensionSupportsCart($allSupportedCartVersions);
                        if (!$checks['minor_check']) {
                            $name = '<p class="alert-info">'
                                . $name
                                . '<br>'
                                . $this->language->get('text_compatibility', 'extension/extensions_store')
                                . '</p>';
                        }
                    }
                }
            }

            if (!$expired) {
                $response->userdata->classes[$id] .= ' disable-expired ';
            }
            $response->userdata->mp_product_url[$id] = $row['mp_product_url'] ?: $updates[$extension]['url'];
            //if url of product page still empty - send to search result page
            if (!$response->userdata->mp_product_url[$id]) {
                $response->userdata->mp_product_url[$id] = $mpModel->getMPURL()
                    . '?rt=product/search&keyword=' . $extension;
            }

            $response->rows[$i]['cell'] = [
                $icon,
                $name,
                $extension,
                $category,
                dateISO2Display($row['date_modified'], $this->language->get('date_format_short'))
            ];
            if (!$this->config->get('current_store_id')) {
                $response->rows[$i]['cell'][] = $row['store_name'] ?: $this->language->get('text_default');
            }
            $response->rows[$i]['cell'][] = $status;
            if ($push) {
                if (in_array($i, $push)) {
                    $for_push[] = $response->rows[$i];
                    unset($response->rows[$i]);
                }
            }
            $i++;
        }

        if ($push) {
            foreach ($for_push as $ext) {
                array_unshift($response->rows, $ext);
            }
        }

        $response->rows = array_slice($response->rows, (int)($page - 1) * $limit, $limit);
        $this->data['response'] = $response;

        //update controller data
        $this->extensions->hk_UpdateData($this, __FUNCTION__);
        $this->load->library('json');
        $this->response->setOutput(AJson::encode($this->data['response']));
    }

    public function update()
    {
        //init controller data
        $this->extensions->hk_InitData($this, __FUNCTION__);

        if (!$this->user->canModify('listing_grid/extension')) {
            $error = new AError('');
            $error->toJSONResponse(
                'NO_PERMISSIONS_402',
                [
                    'error_text'  => sprintf(
                        $this->language->get('error_permission_modify'),
                        'listing_grid/extension'
                    ),
                    'reset_value' => true,
                ]
            );
            return;
        }

        $this->loadLanguage('extension/extensions');
        $store_id = $this->request->post_or_get('store_id');
        if ($this->session->data['current_store_id']) {
            $store_id = $this->session->data['current_store_id'];
        }

        if (empty($this->request->get['id'])) {
            foreach ($this->request->post as $ext => $val) {
                $val['store_id'] = $store_id;
                $this->extension_manager->editSetting($ext, $val);
            }
        } else {
            $val = $this->request->post;
            $val['store_id'] = $store_id;
            $val['one_field'] = true; // sign that we change only one setting
            $this->extension_manager->editSetting($this->request->get['id'], $val);
        }

        //update controller data
        $this->extensions->hk_UpdateData($this, __FUNCTION__);
        $this->load->library('json');
        if ($this->extension_manager->errors) {
            $error = new AError('');
            $error->toJSONResponse(
                'VALIDATION_ERROR_406',
                [
                    'error_text'  => '<br>' . implode('<br>', $this->extension_manager->errors),
                    'reset_value' => true,
                ]
            );
        }
    }

    public function license()
    {
        //init controller data
        $this->extensions->hk_InitData($this, __FUNCTION__);
        $this->loadLanguage('extension/extensions');

        // first of all we need check dependencies
        $config = getExtensionConfigXml($this->request->get['extension']);
        $result = $this->extension_manager->validateDependencies($this->request->get['extension'], $config);
        $this->data = ['license_text' => '', 'error_text' => ''];
        if ($result) {
            // if all fine show license agreement
            if (file_exists(DIR_EXT . $this->request->get['extension'] . DS . "license.txt")) {
                $this->data['license_text'] = file_get_contents(
                    DIR_EXT . $this->request->get['extension'] . DS . "license.txt"
                );
                $this->data['license_text'] = htmlentities($this->data['license_text'], ENT_QUOTES, 'UTF-8');
                $this->data['license_text'] = nl2br($this->data['license_text']);
            }
        } else {
            $error_text = $this->extension_manager->errors;
            end($error_text);
            $this->data['error_text'] = current($error_text);
        }
        //update controller data
        $this->extensions->hk_UpdateData($this, __FUNCTION__);
        $this->load->library('json');
        $this->response->setOutput(AJson::encode($this->data));
    }

    /**
     * method check is enabled dependants of extension presents
     */
    public function dependants()
    {
        //init controller data
        $this->extensions->hk_InitData($this, __FUNCTION__);
        $this->loadLanguage('extension/extensions');

        $result = $this->extension_manager->getChildrenExtensions($this->request->get['extension']);
        if ($result) {
            $this->data['result'] = $result;
            $this->view->batchAssign($this->language->getASet('extension/extensions'));

            //update controller data
            $this->extensions->hk_UpdateData($this, __FUNCTION__);

            $this->view->batchAssign($this->data);
            $this->processTemplate('responses/extension/extensions_dependants_dialog.tpl');
        }
    }
}
