Your IP : 216.73.217.142


Current Path : /var/www/consult-e-syn/public_html/plugins/system/modulesanywhere/src/
Upload File :
Current File : /var/www/consult-e-syn/public_html/plugins/system/modulesanywhere/src/Replace.php

<?php
/**
 * @package         Modules Anywhere
 * @version         7.18.0PRO
 * 
 * @author          Peter van Westen <info@regularlabs.com>
 * @link            https://regularlabs.com
 * @copyright       Copyright © 2023 Regular Labs All Rights Reserved
 * @license         GNU General Public License version 2 or later
 */

namespace RegularLabs\Plugin\System\ModulesAnywhere;

defined('_JEXEC') or die;

use Joomla\CMS\Factory as JFactory;
use Joomla\CMS\Helper\ModuleHelper as JModuleHelper;
use Joomla\CMS\Language\Text as JText;
use Joomla\CMS\Layout\LayoutHelper as JLayoutHelper;
use Joomla\CMS\Plugin\PluginHelper as JPluginHelper;
use Joomla\Registry\AbstractRegistryFormat as JRegistryFormat;
use PlgSystemAdvancedModuleHelper;
use PlgSystemAdvancedModulesPrepareModuleList;
use RegularLabs\Library\Document as RL_Document;
use RegularLabs\Library\Html as RL_Html;
use RegularLabs\Library\PluginTag as RL_PluginTag;
use RegularLabs\Library\Protect as RL_Protect;
use RegularLabs\Library\RegEx as RL_RegEx;
use RegularLabs\Library\StringHelper as RL_String;

class Replace
{
    static $message       = '';
    static $protect_end   = '<!-- END: MA_PROTECT -->';
    static $protect_start = '<!-- START: MA_PROTECT -->';

    public static function processModules(&$string, $area = 'article', $context = '', $article = null)
    {
        if ($area == 'article' && $article && ! RL_Protect::articlePassesSecurity($article, Params::get()->articles_security_level))
        {
            self::$message = JText::_('MA_OUTPUT_REMOVED_SECURITY');
        }

        // Check if tags are in the text snippet used for the search component
        if (strpos($context, 'com_search.') === 0)
        {
            $limit = explode('.', $context, 2);
            $limit = (int) array_pop($limit);

            $string_check = substr($string, 0, $limit);

            if ( ! RL_String::contains($string_check, Params::getTags(true)))
            {
                return;
            }
        }

        $params = Params::get();

        if (
            $area == 'article' && ! $params->articles_enable
            || $area == 'components' && ! $params->components_enable
            || $area == 'other' && ! $params->other_enable
        )
        {
            self::$message = JText::_('MA_OUTPUT_REMOVED_NOT_ENABLED');
        }

        if ( ! RL_String::contains($string, Params::getTags(true)))
        {
            return;
        }

        jimport('joomla.application.module.helper');

        if ( ! RL_Document::isFeed())
        {
            JPluginHelper::importPlugin('content');
        }

        self::replace($string, $area);
    }

    public static function replaceTags(&$string, $area = 'article', $context = '')
    {
        if ( ! is_string($string) || $string == '')
        {
            return false;
        }

        $params = Params::get();

        if ( ! RL_String::contains($string, Params::getTags(true)))
        {
            return false;
        }

        // allow in component?
        if (RL_Protect::isRestrictedComponent($params->disabled_components ?? [], $area))
        {
            if ( ! $params->disable_components_remove)
            {
                Protect::protectTags($string);

                return true;
            }

            Protect::_($string);

            self::removeAll($string, $area);

            RL_Protect::unprotect($string);

            return true;
        }

        Protect::_($string);

        // COMPONENT
        if (RL_Document::isFeed())
        {
            $string = RL_RegEx::replace('(<item[^>]*>)', '\1<!-- START: MODA_COMPONENT -->', $string);
            $string = str_replace('</item>', '<!-- END: MODA_COMPONENT --></item>', $string);
        }

        if (strpos($string, '<!-- START: MODA_COMPONENT -->') === false)
        {
            Area::tag($string, 'component');
        }

        self::$message = '';
        $option = JFactory::getApplication()->input->get('option');

        if (in_array($option, $params->disabled_components))
        {
            // For all components that are selected, set the message
            self::$message = JText::_('MA_OUTPUT_REMOVED_NOT_ENABLED');
        }

        $components = Area::get($string, 'component');

        foreach ($components as $component)
        {
            if (strpos($string, $component[0]) === false)
            {
                continue;
            }

            self::processModules($component[1], 'components');
            $string = str_replace($component[0], $component[1], $string);
        }

        // EVERYWHERE
        self::processModules($string, 'other');

        RL_Protect::unprotect($string);

        return true;
    }

    private static function addFrontendEditing(&$module, &$html)
    {
        $user = JFactory::getApplication()->getIdentity() ?: JFactory::getUser();

        if (
            trim($html) == ''
            || ! RL_Document::isClient('site')
            || ! $user->id
            || ! $user->authorise('core.edit', 'com_modules')
            || ! $user->authorise('core.edit', 'com_modules.module.' . $module->id)
        )
        {
            return;
        }

        if ( ! $frontediting = JFactory::getApplication()->get('frontediting', 1))
        {
            return;
        }

        $displayData = ['moduleHtml' => &$html, 'module' => $module, 'position' => '---', 'menusediting' => ($frontediting == 2)];
        JLayoutHelper::render('joomla.edit.frontediting_modules', $displayData);

        $position_tip = htmlspecialchars(sprintf(JText::_('JLIB_HTML_EDIT_MODULE_IN_POSITION'), '---'));

        $html = RL_RegEx::replace(
            '(?:\s*<br ?/?>)?\s*' . RL_RegEx::quote($position_tip),
            '',
            $html);
    }

    private static function applyAssignments(&$module)
    {
        if (empty($module))
        {
            return;
        }

        self::setModulePublishState($module);

        if (empty($module->published))
        {
            $module = null;
        }
    }

    private static function convertLoadModuleSyntax($string)
    {
        [$type, $title, $style] = explode(',', $string . ',,');

        $id = self::getFirstModuleIdByType($type, $title);

        if ($style)
        {
            return $id;
        }

        return 'id="' . $id . '" style="' . trim($style) . '"';
    }

    private static function convertLoadPositionSyntax($string)
    {
        [$id, $style] = explode(',', $string . ',');

        if ($style)
        {
            return trim($id);
        }

        return 'id="' . trim($id) . '" style="' . trim($style) . '"';
    }

    private static function convertTagToNewSyntax($string, $tag_type)
    {
        RL_PluginTag::protectSpecialChars($string);

        if (strpos($string, '|') === false && strpos($string, ':') === false)
        {
            RL_PluginTag::unprotectSpecialChars($string);

            return $string;
        }

        RL_PluginTag::protectSpecialChars($string);

        $sets = explode('|', $string);

        foreach ($sets as $i => &$set)
        {
            if ($i == 0)
            {
                $set = 'id="' . $set . '"';
                continue;
            }

            if (strpos($set, '=') == false)
            {
                $set = 'style="' . $set . '"';
                continue;
            }

            $key_val = explode('=', $set, 2);

            $set = $key_val[0] . '="' . $key_val[1] . '"';
        }

        return implode(' ', $sets);
    }

    private static function getFirstModuleIdByType($type, $title = '')
    {
        $db = JFactory::getDbo();

        $query = $db->getQuery(true)
            ->select('id')
            ->from('#__modules')
            ->where($db->quoteName('client_id') . ' = 0')
            ->where($db->quoteName('module') . ' = ' . $db->quote(trim($type)));

        if ($title)
        {
            $query->where($db->quoteName('title') . ' = ' . $db->quote(trim($title)));
        }

        $db->setQuery($query);

        return $db->loadResult();
    }

    private static function getModuleFromDatabase($id, $ignores = [])
    {
        $params = Params::get();

        $ignore_access      = $ignores['ignore_access'] ?? $params->ignore_access;
        $ignore_state       = $ignores['ignore_state'] ?? $params->ignore_state;
        $ignore_assignments = $ignores['ignore_assignments'] ?? $params->ignore_assignments;

        if (RL_RegEx::match('^[0-9]+[\:\#]', $id))
        {
            $id = (int) $id;
        }

        $db    = JFactory::getDbo();
        $query = $db->getQuery(true)
            ->select('m.*')
            ->from('#__modules AS m')
            ->where('m.client_id = 0')
            ->where(is_numeric($id)
                ? 'm.id = ' . (int) $id
                : 'm.title = ' . $db->quote(RL_String::html_entity_decoder($id))
            );

        if ( ! $ignore_access)
        {
            $user   = JFactory::getApplication()->getIdentity() ?: JFactory::getUser();
            $levels = $user->getAuthorisedViewLevels();
            $query->where('m.access IN (' . implode(',', $levels) . ')');
        }

        if ( ! $ignore_state)
        {
            $query->where('m.published = 1')
                ->join('LEFT', '#__extensions AS e ON e.element = m.module AND e.client_id = m.client_id')
                ->where('e.enabled = 1');
        }

        if ( ! $ignore_assignments)
        {
            $date     = JFactory::getDate();
            $now      = $date->toSql();
            $nullDate = $db->getNullDate();
            $query->where('(m.publish_up IS NULL OR m.publish_up = ' . $db->quote($nullDate) . ' OR m.publish_up <= ' . $db->quote($now) . ')')
                ->where('(m.publish_down IS NULL OR m.publish_down = ' . $db->quote($nullDate) . ' OR m.publish_down >= ' . $db->quote($now) . ')');

            if (RL_Document::isClient('site') && JFactory::getApplication()->getLanguageFilter())
            {
                $query->where('m.language IN (' . $db->quote(JFactory::getLanguage()->getTag()) . ',' . $db->quote('*') . ')');
            }
        }

        $query->order('m.ordering');
        $db->setQuery($query);

        return $db->loadObject();
    }

    private static function getSettings(&$module, $overrides = [], $chrome = '')
    {
        $settings = (object) [];

        if ( ! empty($module->params))
        {
            $settings = substr(trim($module->params), 0, 1) == '{'
                ? json_decode($module->params)
                // Old ini style. Needed for crappy old style modules like swMenuPro
                : JRegistryFormat::getInstance('INI')->stringToObject($module->params);
        }

        if ( ! empty($chrome))
        {
            self::setSettingsChrome($chrome, $settings);
        }

        if ( ! empty($overrides))
        {
            self::setSettingsFromOverrides($overrides, $settings, $module);
        }

        return $settings;
    }

    private static function getTagValues($data)
    {
        $string = RL_String::html_entity_decoder($data['id']);

        if (strpos($string, '="') == false && strpos($string, '=\'') == false)
        {
            $string = self::convertTagToNewSyntax($string, $data['type']);
        }

        $known_boolean_keys = [
            'ignore_access', 'ignore_state', 'ignore_assignments', 'ignore_caching',
            'showtitle',
        ];

        // Get the values from the tag
        $set = RL_PluginTag::getAttributesFromString($string, 'id', $known_boolean_keys);

        $key_aliases = [
            'id'      => ['ids', 'module', 'position', 'title', 'alias'],
            'style'   => ['module_style', 'html_style', 'chrome'],
            'fixhtml' => ['fix_html', 'html_fix', 'htmlfix'],
        ];

        RL_PluginTag::replaceKeyAliases($set, $key_aliases);

        return $set;
    }

    private static function processMatch(&$string, &$data, $area = 'article')
    {
        $params = Params::get();

        if ( ! empty(self::$message))
        {
            $html = '';

            if ($params->place_comments)
            {
                $html = Protect::getMessageCommentTag(self::$message);
            }

            $string = str_replace($data[0], $html, $string);

            return true;
        }

        $data['type'] = ! empty($data['type_core']) ? trim($data['type_core']) : trim($data['type']);
        $type         = $data['type'];

        if ( ! empty($data['type_core']))
        {
            switch ($type)
            {
                // Convert core loadmodule tag
                case 'loadmodule':
                    $data['id'] = self::convertLoadModuleSyntax($data['id_core']);
                    $type       = $params->tag_module;
                    break;

                // Convert core loadmoduleid tag
                case 'loadmoduleid':
                    $data['id'] = $data['id_core'];
                    $type       = $params->tag_module;
                    break;

                // Convert core loadposition tag
                case 'loadposition':
                    $data['id'] = self::convertLoadPositionSyntax($data['id_core']);
                    $type       = $params->tag_pos;
                    break;

                default:
                    break;
            }

            unset($data['id_core']);
            unset($data['type_core']);
        }

        $tag = self::getTagValues($data);

        $id = trim($tag->id);

        $chrome     = '';
        $forcetitle = 0;

        $ignores   = [];
        $overrides = [];

        foreach ($tag as $key => $val)
        {
            switch ($key)
            {
                case 'id':
                case 'fixhtml':
                    break;

                case 'style':
                    $chrome = $val;
                    break;

                case 'ignore_access':
                case 'ignore_state':
                case 'ignore_assignments':
                case 'ignore_caching':
                    $ignores[$key] = $val;
                    break;

                case 'showtitle':
                    $overrides['showtitle'] = $val;
                    $forcetitle             = $val;
                    break;

                default:
                    if ($params->override_settings)
                    {
                        $overrides[$key] = RL_String::html_entity_decoder($val);
                    }
                    break;
            }
        }

        if ($type == $params->tag_module)
        {
            if ( ! $chrome)
            {
                $chrome = ($forcetitle && $params->style == 'none') ? 'xhtml' : $params->style;
            }

            // module
            $html = self::processModule($id, $chrome, $ignores, $overrides, $area);

            if ($html == 'MA_IGNORE')
            {
                return false;
            }
        }
        else
        {
            if ( ! $chrome)
            {
                $chrome = ($forcetitle) ? 'xhtml' : '';
            }

            // module position
            $html = self::processPosition($id, $chrome);
        }

        [$pre, $post] = RL_Html::cleanSurroundingTags(
            [$data['pre'], $data['post']],
            ['p', 'span']
        );

        $html = $pre . $html . $post;

        if (self::shouldFixHtml($tag, $pre, $post))
        {
            $html = RL_Html::fix($html);
        }

        if ($params->place_comments)
        {
            $html = Protect::wrapInCommentTags($html);
        }

        $string = str_replace($data[0], $html, $string);
        unset($data);

        return $id;
    }

    private static function processModule($id, $chrome = '', $ignores = [], $overrides = [], $area = 'article')
    {
        $params = Params::get();

        $ignore_assignments = $ignores['ignore_assignments'] ?? $params->ignore_assignments;
        $ignore_caching     = $ignores['ignore_caching'] ?? $params->ignore_caching;

        $module = self::getModuleFromDatabase($id, $ignores);

        if ( ! $ignore_assignments)
        {
            self::applyAssignments($module);
        }

        if (empty($module))
        {
            if ($params->place_comments)
            {
                return Protect::getMessageCommentTag(JText::_('MA_OUTPUT_REMOVED_NOT_PUBLISHED'));
            }

            return '';
        }

        //determine if this is a custom module
        $module->user = (substr($module->module, 0, 4) == 'mod_') ? 0 : 1;

        // set style
        $module->style = $chrome ?: 'none';

        $settings = self::getSettings($module, $overrides, $chrome);

        $user   = JFactory::getApplication()->getIdentity() ?: JFactory::getUser();
        $levels = $user->getAuthorisedViewLevels();

        if (isset($module->access) && ! in_array($module->access, $levels))
        {
            if ($params->place_comments)
            {
                return Protect::getMessageCommentTag(JText::_('MA_OUTPUT_REMOVED_ACCESS'));
            }

            return '';
        }

        $module->params = json_encode($settings);

        $document = clone JFactory::getDocument();
        $renderer = $document->setType('html')->loadRenderer('module');
        $html     = $renderer->render($module, ['style' => $module->style, 'name' => '']);

        $show_edit = $overrides['show_edit'] ?? $params->show_edit;

        if ($show_edit)
        {
            self::addFrontendEditing($module, $html);
        }

        // don't return html on article level when caching is set
        if (
            $area == 'article'
            && ! $ignore_caching
            && (
                (isset($settings->cache) && ! $settings->cache)
                || (isset($settings->owncache) && ! $settings->owncache) // for stupid modules like RAXO that mess about with default params
            )
        )
        {
            return 'MA_IGNORE';
        }

        return $html;
    }

    private static function processPosition($position, $chrome = 'none')
    {
        $params = Params::get();

        $document = clone JFactory::getDocument();
        $renderer = $document->setType('html')->loadRenderer('module');

        $html = [];

        foreach (JModuleHelper::getModules($position) as $module)
        {
            $module_html = $renderer->render($module, ['style' => $chrome]);

            if ($params->show_edit)
            {
                self::addFrontendEditing($module, $module_html);
            }

            $html[] = $module_html;
        }

        return implode('', $html);
    }

    private static function removeAll(&$string, $area = 'article')
    {
        self::$message = JText::_('MA_OUTPUT_REMOVED_NOT_ENABLED');
        self::processModules($string, $area);
    }

    private static function replace(&$full_string, $area = 'article')
    {
        [$start_tags, $end_tags] = Params::getTags();

        [$pre_string, $string, $post_string] = RL_Html::getContentContainingSearches(
            $full_string,
            $start_tags,
            $end_tags
        );

        if ($string == '' || ! RL_String::contains($string, Params::getTags(true)))
        {
            return;
        }

        $regex = Params::getRegex();

        if ( ! RL_RegEx::match($regex, $string))
        {
            return;
        }

        $matches   = [];
        $break     = 0;
        $max_loops = 5;

        while (
            $break++ < $max_loops
            && RL_String::contains($string, Params::getTags(true))
            && RL_RegEx::matchAll($regex, $string, $matches)
        )
        {
            self::replaceMatches($string, $matches, $area);
            $break++;
        }

        $full_string = $pre_string . $string . $post_string;
    }

    private static function replaceMatches(&$string, $matches, $area = 'article')
    {
        $protects = [];

        foreach ($matches as $match)
        {
            if (strpos($string, $match[0]) === false)
            {
                continue;
            }

            if (self::processMatch($string, $match, $area))
            {
                continue;
            }

            $protected  = self::$protect_start . base64_encode($match[0]) . self::$protect_end;
            $string     = str_replace($match[0], $protected, $string);
            $protects[] = [$match[0], $protected];
        }

        foreach ($protects as $protect)
        {
            if (strpos($string, $protect[1]) === false)
            {
                continue;
            }

            $string = str_replace($protect[1], $protect[0], $string);
        }
    }

    private static function setModulePublishState(&$module)
    {
        if (empty($module->id))
        {
            return;
        }

        $module->published = true;
        // for old Advanced Module Manager versions
        if (function_exists('PlgSystemAdvancedModulesPrepareModuleList'))
        {
            $modules = [$module->id => $module];
            PlgSystemAdvancedModulesPrepareModuleList($modules);
            $module = array_shift($modules);

            return;
        }

        // for new Advanced Module Manager versions
        if (class_exists('PlgSystemAdvancedModuleHelper'))
        {
            $module->use_amm_cache = false;
            $modules               = [$module->id => $module];
            $helper                = new PlgSystemAdvancedModuleHelper;
            $helper->onPrepareModuleList($modules);
            $module = array_shift($modules);

            return;
        }

        // for core Joomla
        $db    = JFactory::getDbo();
        $query = $db->getQuery(true)
            ->select('mm.moduleid')
            ->from('#__modules_menu AS mm')
            ->where('mm.moduleid = ' . (int) $module->id)
            ->where('(mm.menuid = ' . ((int) JFactory::getApplication()->input->getInt('Itemid')) . ' OR mm.menuid <= 0)');
        $db->setQuery($query);
        $result = $db->loadResult();

        $module->published = ! empty($result);
    }

    private static function setSettingsChrome($chrome, &$settings)
    {
        // Set style in params to override the chrome override in module settings

        if (isset($settings->style) && $pos = strrpos($settings->style, '-'))
        {
            // Get part before the last '-'
            $settings->style = substr($settings->style, 0, $pos);
        }

        $settings->style = ! empty($settings->style)
            ? $settings->style . '-' . $chrome
            : $chrome;
    }

    private static function setSettingsFromOverrides($overrides, &$settings, &$module)
    {
        // override module parameters
        foreach ($overrides as $key => $value)
        {
            // Key is found in main module attributes
            if (isset($module->{$key}))
            {
                $module->{$key} = $value;
                continue;
            }

            // Key is found in advancedparams (Advanced Module Manager)
            if (
                isset($module->advancedparams)
                && isset($module->advancedparams->{$key})
            )
            {
                $module->advancedparams->{$key} = $value;
                continue;
            }

            // Key is an Advanced Module Manager assignment
            if (
                isset($module->advancedparams)
                && isset($module->advancedparams->conditions)
                && strpos($key, 'assignto_') === 0
            )
            {
                $module->advancedparams->conditions[substr($key, 9)] = $value;
                continue;
            }

            // Else just add to the $settings object

            // Value is a json formatted array
            if ( ! empty($value)
                && is_string($value)
                && $value[0] == '['
                && $value[strlen($value) - 1] == ']'
            )
            {
                $value            = json_decode('{"val":' . $value . '}');
                $settings->{$key} = $value->val;
                continue;
            }

            // Value is found in the module params and should be an array
            if (
                isset($settings->{$key})
                && is_array($settings->{$key})
            )
            {
                $settings->{$key} = explode(',', $value);
                continue;
            }

            $settings->{$key} = $value;
        }
    }

    private static function shouldFixHtml($tag, $pre, $post)
    {
        $page_type = RL_Document::get()->getType();

        if ($page_type == 'raw')
        {
            return false;
        }

        if (isset($tag->fixhtml))
        {
            return $tag->fixhtml;
        }

        $params = Params::get();

        if ( ! $params->fix_html)
        {
            return false;
        }

        $pre  = trim($pre);
        $post = trim($post);

        if (empty($pre) && empty($post))
        {
            return false;
        }

        // Ignore if pre/post is a surrounding div
        [$pre, $post] = RL_Html::cleanSurroundingTags(
            [$pre, $post],
            ['div']
        );

        if (empty($pre) && empty($post))
        {
            return false;
        }

        return true;
    }
}