name : HtmlView.php
<?php
/**
 * @package   admintools
 * @copyright Copyright (c)2010-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace Akeeba\Component\AdminTools\Administrator\View\Scanalert;

defined('_JEXEC') or die;

use Akeeba\Component\AdminTools\Administrator\Model\ScanalertModel;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Form\Form;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\View\GenericDataException;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Toolbar\ToolbarHelper;

class HtmlView extends BaseHtmlView
{
	/**
	 * File status
	 *
	 * @var  string
	 */
	public $fstatus = 'modified';

	/**
	 * Is this file suspicious?
	 *
	 * @var  bool
	 */
	public $suspiciousFile = false;

	/**
	 * The Form object
	 *
	 * @var    Form
	 * @since  1.5
	 */
	protected $form;

	/**
	 * The active item
	 *
	 * @var    object
	 * @since  1.5
	 */
	protected $item;

	/**
	 * The model state
	 *
	 * @var    object
	 * @since  1.5
	 */
	protected $state;

	/**
	 * Should I generate DIFFs for files?
	 *
	 * @var  bool
	 */
	protected $generateDiff;

	/**
	 * Size threshold for reading file contents. To calculate the score we have to read the whole file, with large ones
	 * (ie log files) we could run out of memory, causing a fatal error.
	 *
	 * @var int
	 */
	private $filesizeThreshold = 5242880;

	public function display($tpl = null): void
	{
		/** @var ScanalertModel $model */
		$model       = $this->getModel();
		$this->form  = $model->getForm();
		$this->item  = $model->getItem();
		$this->state = $model->getState();

		// Check for errors.
		if (method_exists($this->getModel(), 'getErrors'))
		{
			/** @noinspection PhpDeprecationInspection */
			$errors = $this->getModel()->getErrors();

			if (is_countable($errors) && count($errors))
			{
				throw new GenericDataException(implode("\n", $errors), 500);
			}
		}

		$this->item->newfile    = empty($this->item->diff);
		$this->item->suspicious = substr($this->item->diff, 0, 21) == '###SUSPICIOUS FILE###';

		$this->generateDiff = ComponentHelper::getParams('com_admintools')->get('scandiffs', false);

		// File status
		if ($this->item->newfile)
		{
			$this->fstatus = 'new';
		}
		elseif ($this->item->suspicious)
		{
			$this->fstatus = 'suspicious';
		}

		// Should I render a diff?
		if (!empty($this->item->diff))
		{
			$diffLines = explode("\n", $this->item->diff);
			$firstLine = array_shift($diffLines);

			if ($firstLine == '###SUSPICIOUS FILE###')
			{
				$this->suspiciousFile = true;
				$this->item->diff     = '';
			}
			elseif ($firstLine == '###MODIFIED FILE###')
			{
				$this->item->diff = '';
			}

			if ($this->suspiciousFile && (count($diffLines) > 4))
			{
				array_shift($diffLines);
				array_shift($diffLines);
				array_shift($diffLines);
				array_shift($diffLines);

				$this->item->diff = implode("\n", $diffLines);
			}

			unset($diffLines);
		}

		// Load highlight.js
		$this->getDocument()->getWebAssetManager()->registerAndUseScript(
			'com_admintools.dynamic.highlight_js',
			'//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.5.0/highlight.min.js'
		);
		$this->getDocument()->getWebAssetManager()->registerAndUseStyle(
			'com_admintools.dynamic.highlight_js',
			'//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.5.0/styles/default.min.css'
		);
		//$this->getDocument()->addScript('//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.5.0/highlight.min.js');
		//$this->getDocument()->addStyleSheet('//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.5.0/styles/default.min.css');
		//$this->getDocument()->addStyleSheet('//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.5.0/styles/dracula.min.css', $this->container->mediaVersion, 'text/css', 'screen and (prefers-color-scheme: dark)');

		$js = <<< JS

document.addEventListener('DOMContentLoaded', function() {
    document.querySelectorAll('pre.highlightCode').forEach(function (block) {
        hljs.highlightBlock(block);
    });
})

JS;

		$this->addToolbar();

		parent::display($tpl);
	}

	protected function addToolbar(): void
	{
		Factory::getApplication()->getInput()->set('hidemainmenu', true);

		$isNew = empty($this->item->id);

		ToolbarHelper::title(Text::sprintf('COM_ADMINTOOLS_TITLE_SCANALERT_EDIT', $this->item->scan_id), 'icon-admintools');

		ToolbarHelper::apply('scanalert.apply');
		ToolbarHelper::save('scanalert.save');

		ToolbarHelper::cancel('scanalert.cancel', $isNew ? 'JTOOLBAR_CANCEL' : 'JTOOLBAR_CLOSE');

		ToolbarHelper::help(null, false, 'https://www.akeeba.com/documentation/admin-tools-joomla/php-file-scanner-scan.html');
	}

	public function getFileSourceForDisplay($highlight = false)
	{
		$item = $this->item;

		if (!file_exists(JPATH_ROOT . '/' . $item->path))
		{
			return null;
		}

		$filepath = JPATH_ROOT . '/' . $item->path;
		$filesize = @filesize($filepath);

		// With very large files do not display the whole contents, but instead show a placeholder
		if ($filesize > $this->filesizeThreshold)
		{
			return Text::sprintf('COM_ADMINTOOLS_SCANS_ERR_FILE_TOO_LARGE', round($filesize / 1024 / 1024, 2));
		}

		$filedata = @file_get_contents($filepath);

		if (!$highlight)
		{
			return htmlentities($filedata);
		}

		$highlightPrefixSuspicious = "%*!*[[###  ";
		$highlightSuffixSuspicious = "  ###]]*!*%";
		$highlightPrefixKnownHack  = "%*{{!}}*[[###  ";
		$highlightSuffixKnownHack  = "  ###]]*{{!}}*%";

		/** @var string $encodedConfig Defined in the included file */
		require_once JPATH_ADMINISTRATOR . '/components/com_admintools/src/Scanner/encodedconfig.php';

		$zipped = pack('H*', $encodedConfig);
		unset($encodedConfig);

		$json_encoded = gzinflate($zipped);
		unset($zipped);

		$new_list = json_decode($json_encoded, true);
		extract($new_list);

		unset($new_list);

		/** @var array $suspiciousWords Simple array of words that are suspicious */
		/** @var array $knownHackSignatures Known hack signatures, $signature => $weight */
		/** @var array $suspiciousRegEx Suspicious constructs' RegEx, $regex => $weight */

		foreach ($suspiciousWords as $word)
		{
			$replacement = $highlightPrefixSuspicious . $word . $highlightSuffixSuspicious;
			$filedata    = str_replace($word, $replacement, $filedata);
		}

		foreach ($knownHackSignatures as $signature => $sigscore)
		{
			$replacement = $highlightPrefixKnownHack . $signature . $highlightSuffixKnownHack;
			$filedata    = str_replace($signature, $replacement, $filedata);
		}

		$i = 0;

		foreach ($suspiciousRegEx as $pattern => $value)
		{
			$i++;
			$count = preg_match_all($pattern, $filedata, $matches);

			if (!$count)
			{
				continue;
			}

			$filedata = preg_replace_callback($pattern, function ($m) use ($highlightPrefixSuspicious, $highlightSuffixSuspicious, $i) {
				return $highlightPrefixSuspicious . $m[0] . $highlightSuffixSuspicious;
			}, $filedata);
		}

		$filedata = htmlentities($filedata);

		$filedata = str_replace([
			$highlightPrefixSuspicious,
			$highlightSuffixSuspicious,
		], [
			'<mark class="bg-warning text-black fw-bold px-2 rounded-pill">',
			'</mark>',
		], $filedata);

		$filedata = str_replace([
			$highlightPrefixKnownHack,
			$highlightSuffixKnownHack,
		], [
			'<mark class="bg-danger text-white fw-bold px-2 rounded-pill">',
			'</mark>',
		], $filedata);

		return $filedata;
	}

}

© 2025 Cubjrnet7