<?php 
/**
* @version		$Id: plugin.class.php 2348 2020-06-23 15:45:31Z IOS $
* @package		Elxis
* @subpackage	Component Content / Plugins
* @copyright	Copyright (c) 2006-2020 Elxis CMS (https://www.elxis.org). All rights reserved.
* @license		Elxis Public License ( https://www.elxis.org/elxis-public-license.html )
* @author		Elxis Team ( https://www.elxis.org )
* @description 	Elxis CMS is free software. Read the license for copyright notices and details
*/

defined('_ELXIS_') or die ('Direct access to this location is not allowed');


class elxisPlugin {

	private $plugins = array();
	private $allplugins = array();
	private $isloaded = false;
	private $runtimes = 0;
	private $plugintimes = 0;
	private $executed = array();

	/********************/
	/* MAGIC CONTRUCTOR */
	/********************/
	public function __construct() {
		$this->loadPlugins();
	}


	/****************/
	/* LOAD PLUGINS */
	/****************/
	private function loadPlugins() {
		if ($this->isloaded) { return; }
		$this->isloaded = true;

		$db = eFactory::getDB();
		$elxis = eFactory::getElxis();

		$sql = "SELECT ".$db->quoteId('id').", ".$db->quoteId('plugin').", ".$db->quoteId('alevel').", ".$db->quoteId('published').", ".$db->quoteId('params')
		."\n FROM ".$db->quoteId('#__plugins')." ORDER BY ".$db->quoteId('ordering')." ASC";
		$stmt = $db->prepare($sql);
		$stmt->execute();
		$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
		if (!$rows) { return; }

		$lowlev = $elxis->acl()->getLowLevel();
		$exactlev = $elxis->acl()->getExactLevel();

		foreach ($rows as $row) {
			$plugin = $row['plugin'];
			$alevel = (int)$row['alevel'];
			if (!file_exists(ELXIS_PATH.'/components/com_content/plugins/'.$plugin.'/'.$plugin.'.plugin.php')) { continue; }
			$this->allplugins[] = $plugin;
			if ($row['published'] == 0) {
				$this->plugins[$plugin] = array('published' => false, 'params' => null);
				continue;
			}
			if (($row['alevel'] > $lowlev) && ($row['alevel'] != $exactlev)) {
				$this->plugins[$plugin] = array('published' => false, 'params' => null);
				continue;
			}
			$this->plugins[$plugin] = array('published' => true, 'params' => $row['params']);
		}
	}


	/*************************************************/
	/* RUN ALL PROVIDED/AVAILABLE PLUGINS ON AN ITEM */
	/*************************************************/
	public function process(&$row) {
		if (!$this->allplugins) { return true; }
		//$avplugins = $this->usedPlugins($row->text); //= $this->allplugins for global execution (slower)
		$avplugins = $this->usedAndPublishedPlugins($row->text);//Elxis 5.2. This allows executing plugins without editor integration
		if (!$avplugins) { return true; }

		if ($this->runtimes == 0) {
			elxisLoader::loadFile('includes/libraries/elxis/parameters.class.php');
			elxisLoader::loadFile('components/com_content/plugins/plugin.interface.php');
		}

		$this->runtimes++;
		$result = array();

		$cregex1 = '#<code(.*?)>(.*?)</code>#';
		$cregex2 = '#<p>{(.*?)}</p>#';
		foreach ($avplugins as $plugin) {
			if (!in_array($plugin, $this->executed)) {
				if (!isset($this->plugins[$plugin])) { continue; }
				$this->plugintimes++;
				$published = $this->plugins[$plugin]['published'];
				if (!$published) {
					$params = null;
				} else {
					$this->loadPluginLang($plugin);
					$params = new elxisParameters($this->plugins[$plugin]['params'], '', 'plugin');
				}
				elxisLoader::loadFile('components/com_content/plugins/'.$plugin.'/'.$plugin.'.plugin.php');
				$this->plugins[$plugin]['params'] = $params; //converted to object, ready for later use
				$this->executed[] = $plugin;
			} else {
				$published = $this->plugins[$plugin]['published'];
				$params = $this->plugins[$plugin]['params'];
			}

			$row->text = preg_replace($cregex1, "$2", $row->text); //remove <code> tags that surrounds plugins
			$row->text = preg_replace($cregex2, '{$1}', $row->text); //remove <p> tags that surrounds plugins

			$classname = $plugin.'Plugin';
			$plg = new $classname();
			$result = $plg->process($row, $published, $params);
		}

		return $result;
	}


	/************************/
	/* LOAD PLUGIN LANGUAGE */
	/************************/
	private function loadPluginLang($plugin) {
		$eLang = eFactory::getLang();

		$clang = $eLang->currentLang();
		if (file_exists(ELXIS_PATH.'/language/'.$clang.'/'.$clang.'.plugin_'.$plugin.'.php')) {
			$langfile = ELXIS_PATH.'/language/'.$clang.'/'.$clang.'.plugin_'.$plugin.'.php';
		} else if (file_exists(ELXIS_PATH.'/language/en/en.plugin_'.$plugin.'.php')) {
			$langfile = ELXIS_PATH.'/language/en/en.plugin_'.$plugin.'.php';
		} else if (file_exists(ELXIS_PATH.'/components/com_content/plugins/'.$plugin.'/language/'.$clang.'.plugin_'.$plugin.'.php')) {
			$langfile = ELXIS_PATH.'/components/com_content/plugins/'.$plugin.'/language/'.$clang.'.plugin_'.$plugin.'.php';
		} else if (file_exists(ELXIS_PATH.'/components/com_content/plugins/'.$plugin.'/language/en.plugin_'.$plugin.'.php')) {
			$langfile = ELXIS_PATH.'/components/com_content/plugins/'.$plugin.'/language/en.plugin_'.$plugin.'.php';
		} else {
			$langfile = '';
		}

		if ($langfile != '') { $eLang->loadFile($langfile); }
	}


	/********************************/
	/* REMOVE ALL PLUGINS FROM TEXT */
	/********************************/
	public function removePlugins($text) {
		$cregex1 = '#<p><code(.*?)>(.*?)</code></p>#';
		$cregex2 = '#<code>(.*?)</code>#';
		$regex = '#{[^}]*}(?:.+?{\/[^}]*})?#';
		$eregex = '~href="#elink:(.*?)"~';
		$newtext = preg_replace($cregex1, '', $text);
		$newtext = preg_replace($cregex2, '', $newtext);
		$newtext = preg_replace($regex, '', $newtext);
    	$newtext = preg_replace($eregex, 'href="javascript:void(null);"', $newtext);

		return $newtext;
	}


	/********************************/
	/* GET ALL USED PLUGINS IN TEXT */
	/********************************/
	public function usedPlugins($text) {
		$regex = '#\{([^\/](.*?))[\}|\s]#';
		preg_match_all($regex, $text, $matches);
		if (isset($matches[1]) && (count($matches[1]) > 0)) {
			$avplugins = array_unique($matches[1]);
		} else {
			$avplugins = array();
		}

		if (strpos($text, '#elink:') !== false) { $avplugins[] = 'elink'; }
		return $avplugins;
	}


	/********************************************************/
	/* GET ALL USED PLUGINS IN TEXT AND ALSO PUBLISHED ONES */
	/********************************************************/
	public function usedAndPublishedPlugins($text) {
		$regex = '#\{([^\/](.*?))[\}|\s]#';
		preg_match_all($regex, $text, $matches);
		if (isset($matches[1]) && (count($matches[1]) > 0)) {
			$avplugins = array_unique($matches[1]);
		} else {
			$avplugins = array();
		}

		if (strpos($text, '#elink:') !== false) { $avplugins[] = 'elink'; }

		if ($this->plugins) {
			foreach ($this->plugins as $plugin => $data) {
				if ($data['published'] == true) {
					if (!in_array($plugin, $avplugins)) { $avplugins[] = $plugin; }
				}
			}
		}

		return $avplugins;
	}


	/**********************************************/
	/* CONVERT AN ATTRIBUTES STRING INTO AN ARRAY */
	/**********************************************/
	public function parseAttributes($string) {
		$attrs = array();
		if (trim($string) == '') { return $attrs; }
		$string = html_entity_decode($string);
		$pattern = '/(\\w+)\s*=\\s*("[^"]*"|\'[^\']*\'|[^"\'\\s>]*)/';
		preg_match_all($pattern, $string, $matches, PREG_SET_ORDER);
		if (!$matches) { return $attrs; }
		foreach ($matches as $match) {
    		if (($match[2][0] == '"' || $match[2][0] == "'") && ($match[2][0] == $match[2][strlen($match[2])-1])) {
    			$match[2] = substr($match[2], 1, -1);
			}
    		$name = $match[1];
			$attrs[$name] = $match[2];
		}
		return $attrs;
	}


	/*************************/
	/* GET DEBUG INFORMATION */
	/*************************/
	public function runData() {
		$rundata = array(
			'plugins' => $this->allplugins,
			'runtimes' => $this->runtimes,
			'plugintimes' => $this->plugintimes
		);
		return $rundata;
	}

}

?>